diff --git a/applications/plugins/airmouse/README.md b/applications/plugins/airmouse/README.md index 9df0d69b0..04e346e4b 100644 --- a/applications/plugins/airmouse/README.md +++ b/applications/plugins/airmouse/README.md @@ -19,6 +19,7 @@ Using it is really simple: * Up button for Left mouse click; * Down button for Right mouse click; * Center button for Middle mouse click; + * Left and Right buttons for scrolling; * Use calibration menu option if you notice significant drift (place your Flipper onto a level surface, make sure it doesn't move, run this option, wait 2 seconds, done). See early prototype [in action](https://www.youtube.com/watch?v=DdxAmmsYfMA). @@ -41,10 +42,9 @@ Reality: ![What it looks like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/flipper.jpg) - ## Software -The code is based on the original Bosch [driver](https://github.com/BoschSensortec/BMI160_driver/) and an orientation tracking implementation from the [Cardboard](https://github.com/googlevr/cardboard/tree/master/sdk/sensors) project +The code is based on the original Bosch [driver](https://github.com/BoschSensortec/BMI160_driver/) and an orientation tracking implementation from the Google [Cardboard](https://github.com/googlevr/cardboard/tree/master/sdk/sensors) project If you're familiar with Flipper applications, start in the [firmware](https://github.com/flipperdevices/flipperzero-firmware) checkout folder and do the following: ``` @@ -54,3 +54,7 @@ cd ../.. ./fbt fap_air_mouse ``` If you're not familiar with those, just grab a `fap` file from Releases. + +## License + +TL;DR: Use the code however you want, give credit where it's due, no warranty of any kind is provided. diff --git a/applications/plugins/airmouse/application.fam b/applications/plugins/airmouse/application.fam index c646ee7e1..abc3f55bb 100644 --- a/applications/plugins/airmouse/application.fam +++ b/applications/plugins/airmouse/application.fam @@ -1,5 +1,5 @@ App( - appid="BMI160_Air_Mouse", + appid="Air_Mouse", name="[BMI160] Air Mouse", apptype=FlipperAppType.EXTERNAL, entry_point="air_mouse_app", diff --git a/applications/plugins/airmouse/views/bt_mouse.c b/applications/plugins/airmouse/views/bt_mouse.c index e6e0ae45a..7d9c0e6db 100644 --- a/applications/plugins/airmouse/views/bt_mouse.c +++ b/applications/plugins/airmouse/views/bt_mouse.c @@ -44,8 +44,7 @@ struct BtMouse { #define BT_MOUSE_FLAG_KILL_THREAD (1UL << 1) #define BT_MOUSE_FLAG_ALL (BT_MOUSE_FLAG_INPUT_EVENT | BT_MOUSE_FLAG_KILL_THREAD) -#define MOUSE_MOVE_SHORT 5 -#define MOUSE_MOVE_LONG 20 +#define MOUSE_SCROLL 2 static void bt_mouse_notify_event(BtMouse* bt_mouse) { FuriThreadId thread_id = furi_thread_get_id(bt_mouse->thread); @@ -100,6 +99,14 @@ static void bt_mouse_process(BtMouse* bt_mouse, InputEvent* event) { } else if(event->type == InputTypeRelease) { bt_mouse_button_state(bt_mouse, HID_MOUSE_BTN_WHEEL, false); } + } else if(event->key == InputKeyRight) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + bt_mouse->wheel = MOUSE_SCROLL; + } + } else if(event->key == InputKeyLeft) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + bt_mouse->wheel = -MOUSE_SCROLL; + } } }, true); diff --git a/applications/plugins/airmouse/views/usb_mouse.c b/applications/plugins/airmouse/views/usb_mouse.c index 5d9ab4352..09075b566 100644 --- a/applications/plugins/airmouse/views/usb_mouse.c +++ b/applications/plugins/airmouse/views/usb_mouse.c @@ -21,6 +21,8 @@ static void usb_mouse_draw_callback(Canvas* canvas, void* context) { canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); } +#define MOUSE_SCROLL 2 + static void usb_mouse_process(UsbMouse* usb_mouse, InputEvent* event) { with_view_model( usb_mouse->view, @@ -45,6 +47,14 @@ static void usb_mouse_process(UsbMouse* usb_mouse, InputEvent* event) { } else if(event->type == InputTypeRelease) { furi_hal_hid_mouse_release(HID_MOUSE_BTN_WHEEL); } + } else if(event->key == InputKeyRight) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + furi_hal_hid_mouse_scroll(MOUSE_SCROLL); + } + } else if(event->key == InputKeyLeft) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + furi_hal_hid_mouse_scroll(-MOUSE_SCROLL); + } } }, true); diff --git a/applications/plugins/bpmtapper/README.md b/applications/plugins/bpmtapper/README.md index 224d2cb5b..8e88863ee 100644 --- a/applications/plugins/bpmtapper/README.md +++ b/applications/plugins/bpmtapper/README.md @@ -9,6 +9,6 @@ Hit any button other than back repeatedly. Calculates based on the average of th ## Compiling ``` -./fbt fap_bpm_tapper +./fbt firmware_bpm_tapper ``` diff --git a/applications/plugins/bpmtapper/bpm.c b/applications/plugins/bpmtapper/bpm.c index 323a898a4..5720ac4d2 100644 --- a/applications/plugins/bpmtapper/bpm.c +++ b/applications/plugins/bpmtapper/bpm.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "BPM_Tapper_icons.h" @@ -91,6 +90,7 @@ static float queue_avg(queue* q) { //} // typedef struct { + FuriMutex* mutex; int taps; double bpm; uint32_t last_stamp; @@ -127,42 +127,43 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu } static void render_callback(Canvas* const canvas, void* ctx) { - string_t tempStr; + furi_assert(ctx); + const BPMTapper* bpm_state = ctx; + furi_mutex_acquire(bpm_state->mutex, FuriWaitForever); - const BPMTapper* bpm_state = acquire_mutex((ValueMutex*)ctx, 25); - if(bpm_state == NULL) { - return; - } + FuriString* tempStr; // border //canvas_draw_frame(canvas, 0, 0, 128, 64); canvas_set_font(canvas, FontPrimary); - string_init(tempStr); + tempStr = furi_string_alloc(); - string_printf(tempStr, "Taps: %d", bpm_state->taps); - canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignBottom, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf(tempStr, "Taps: %d", bpm_state->taps); + canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); - string_printf(tempStr, "Queue: %d", bpm_state->tap_queue->size); - canvas_draw_str_aligned(canvas, 70, 10, AlignLeft, AlignBottom, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf(tempStr, "Queue: %d", bpm_state->tap_queue->size); + canvas_draw_str_aligned(canvas, 70, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); - string_printf(tempStr, "Interval: %dms", bpm_state->interval); - canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignBottom, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf(tempStr, "Interval: %ldms", bpm_state->interval); + canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); - string_printf(tempStr, "x2 %.2f /2 %.2f", bpm_state->bpm * 2, bpm_state->bpm / 2); - canvas_draw_str_aligned(canvas, 64, 60, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf(tempStr, "x2 %.2f /2 %.2f", bpm_state->bpm * 2, bpm_state->bpm / 2); + canvas_draw_str_aligned( + canvas, 64, 60, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); - string_printf(tempStr, "%.2f", bpm_state->bpm); + furi_string_printf(tempStr, "%.2f", bpm_state->bpm); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + canvas_draw_str_aligned( + canvas, 64, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); - string_clear(tempStr); + furi_string_free(tempStr); - release_mutex((ValueMutex*)ctx, bpm_state); + furi_mutex_release(bpm_state->mutex); } static void bpm_state_init(BPMTapper* const plugin_state) { @@ -185,8 +186,8 @@ int32_t bpm_tapper_app(void* p) { // setup bpm_state_init(bpm_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, bpm_state, sizeof(bpm_state))) { + bpm_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!bpm_state->mutex) { FURI_LOG_E("BPM-Tapper", "cannot create mutex\r\n"); free(bpm_state); return 255; @@ -197,7 +198,7 @@ int32_t bpm_tapper_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, bpm_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -207,7 +208,7 @@ int32_t bpm_tapper_app(void* p) { PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - BPMTapper* bpm_state = (BPMTapper*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(bpm_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events if(event.type == EventTypeKey) { @@ -240,19 +241,16 @@ int32_t bpm_tapper_app(void* p) { } } } - } else { - FURI_LOG_D("BPM-Tapper", "FuriMessageQueue: event timeout"); - // event timeout } view_port_update(view_port); - release_mutex(&state_mutex, bpm_state); + furi_mutex_release(bpm_state->mutex); } view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close("gui"); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(bpm_state->mutex); queue* q = bpm_state->tap_queue; free(q); free(bpm_state); diff --git a/applications/plugins/bpmtapper/images/DolphinCommon_56x48.png b/applications/plugins/bpmtapper/images/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/applications/plugins/bpmtapper/images/DolphinCommon_56x48.png differ diff --git a/applications/plugins/brainfuck/application.fam b/applications/plugins/brainfuck/application.fam index 6e2b6d1f9..716c44a6a 100644 --- a/applications/plugins/brainfuck/application.fam +++ b/applications/plugins/brainfuck/application.fam @@ -11,5 +11,4 @@ App( fap_icon="bfico.png", fap_category="Misc", fap_icon_assets="icons", - fap_icon_assets_symbol="brainfuck", ) diff --git a/applications/plugins/brainfuck/brainfuck_i.h b/applications/plugins/brainfuck/brainfuck_i.h index d3d27dcbd..3940cecad 100644 --- a/applications/plugins/brainfuck/brainfuck_i.h +++ b/applications/plugins/brainfuck/brainfuck_i.h @@ -29,7 +29,7 @@ typedef unsigned char byte; #include #include -#include +#include #include #include diff --git a/applications/plugins/brainfuck/views/bf_dev_env.c b/applications/plugins/brainfuck/views/bf_dev_env.c index c5f194500..241a4ebc3 100644 --- a/applications/plugins/brainfuck/views/bf_dev_env.c +++ b/applications/plugins/brainfuck/views/bf_dev_env.c @@ -35,9 +35,9 @@ int selectedButton = 0; int saveNotifyCountdown = 0; int execCountdown = 0; -char dspLine0[25] = {}; -char dspLine1[25] = {}; -char dspLine2[25] = {}; +char dspLine0[25] = {0x00}; +char dspLine1[25] = {0x00}; +char dspLine2[25] = {0x00}; static bMapping buttonMappings[12] = { {8, 8, 7, 1}, //0 @@ -361,6 +361,10 @@ static void bf_dev_enter_callback(void* context) { //read into the buffer appDev->dataSize = stream_size(stream); + if(appDev->dataSize > 2000) { + return; //BF file is too large + } + stream_read(stream, (uint8_t*)appDev->dataBuffer, appDev->dataSize); buffered_file_stream_close(stream); diff --git a/applications/plugins/brainfuck/worker.c b/applications/plugins/brainfuck/worker.c index 1b05ac3fd..584bb9fb8 100644 --- a/applications/plugins/brainfuck/worker.c +++ b/applications/plugins/brainfuck/worker.c @@ -1,4 +1,6 @@ #include "worker.h" +#include +#include bool killswitch = false; @@ -207,27 +209,40 @@ static const NotificationSequence led_on = { }; static const NotificationSequence led_off = { - &message_green_0, + &message_blue_0, NULL, }; +void input_kill(void* _ctx) { + UNUSED(_ctx); + killswitch = true; +} + void beginWorker() { status = 1; + + //redefined from furi_hal_resources.c + const GpioPin gpio_button_back = {.port = GPIOC, .pin = LL_GPIO_PIN_13}; + while(inst[instPtr] != 0x00) { if(runOpCount % 500 == 0) { text_box_set_text(wrkrApp->text_box, workerGetOutput()); notification_message(wrkrApp->notifications, &led_on); } + //status 2 indicates failure if(status == 2) { status = 0; break; } - if(killswitch) { + + //read back button directly to avoid weirdness in furi + if(killswitch || !furi_hal_gpio_read(&gpio_button_back)) { status = 0; killswitch = false; break; } + switch(inst[instPtr]) { case '>': rShift(); diff --git a/applications/plugins/caesarcipher/application.fam b/applications/plugins/caesarcipher/application.fam index 4f438d2b3..652585de2 100644 --- a/applications/plugins/caesarcipher/application.fam +++ b/applications/plugins/caesarcipher/application.fam @@ -1,7 +1,7 @@ App( appid="Caesar_Cipher", name="Caesar Cipher", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="caesar_cipher_app", cdefines=["APP_CAESAR_CIPHER"], requires=[ diff --git a/applications/plugins/caesarcipher/caesar_cipher.c b/applications/plugins/caesarcipher/caesar_cipher.c index 9eb93e925..d5c4b2b65 100644 --- a/applications/plugins/caesarcipher/caesar_cipher.c +++ b/applications/plugins/caesarcipher/caesar_cipher.c @@ -21,6 +21,7 @@ typedef struct { } PluginEvent; typedef struct { + FuriMutex* mutex; ViewDispatcher* view_dispatcher; TextInput* text_input; TextBox* text_box; @@ -58,7 +59,10 @@ static void build_output(char* input, char* output) { } static void text_input_callback(void* ctx) { - CaesarState* caesar_state = acquire_mutex((ValueMutex*)ctx, 25); + furi_assert(ctx); + CaesarState* caesar_state = ctx; + furi_mutex_acquire(caesar_state->mutex, FuriWaitForever); + FURI_LOG_D("caesar_cipher", "Input text: %s", caesar_state->input); // this is where we build the output. string_to_uppercase(caesar_state->input); @@ -67,13 +71,14 @@ static void text_input_callback(void* ctx) { text_box_set_text(caesar_state->text_box, caesar_state->output); view_dispatcher_switch_to_view(caesar_state->view_dispatcher, 1); - release_mutex((ValueMutex*)ctx, caesar_state); + furi_mutex_release(caesar_state->mutex); } static bool back_event_callback(void* ctx) { - const CaesarState* caesar_state = acquire_mutex((ValueMutex*)ctx, 25); + const CaesarState* caesar_state = ctx; + furi_mutex_acquire(caesar_state->mutex, FuriWaitForever); view_dispatcher_stop(caesar_state->view_dispatcher); - release_mutex((ValueMutex*)ctx, caesar_state); + furi_mutex_release(caesar_state->mutex); return true; } @@ -99,8 +104,8 @@ int32_t caesar_cipher_app() { FURI_LOG_D("caesar_cipher", "Running caesar_cipher_state_init"); caesar_cipher_state_init(caesar_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, caesar_state, sizeof(CaesarState))) { + caesar_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!caesar_state->mutex) { FURI_LOG_E("caesar_cipher", "cannot create mutex\r\n"); free(caesar_state); return 255; @@ -110,7 +115,7 @@ int32_t caesar_cipher_app() { text_input_set_result_callback( caesar_state->text_input, text_input_callback, - &state_mutex, + caesar_state, caesar_state->input, TEXT_BUFFER_SIZE, //clear default text @@ -135,12 +140,12 @@ int32_t caesar_cipher_app() { FURI_LOG_D("ceasar_cipher", "starting view dispatcher"); view_dispatcher_set_navigation_event_callback( caesar_state->view_dispatcher, back_event_callback); - view_dispatcher_set_event_callback_context(caesar_state->view_dispatcher, &state_mutex); + view_dispatcher_set_event_callback_context(caesar_state->view_dispatcher, caesar_state); view_dispatcher_switch_to_view(caesar_state->view_dispatcher, 0); view_dispatcher_run(caesar_state->view_dispatcher); furi_record_close("gui"); - delete_mutex(&state_mutex); + furi_mutex_free(caesar_state->mutex); caesar_cipher_state_free(caesar_state); return 0; diff --git a/applications/plugins/counter/application.fam b/applications/plugins/counter/application.fam index 5d164f20c..8f0147b09 100644 --- a/applications/plugins/counter/application.fam +++ b/applications/plugins/counter/application.fam @@ -1,7 +1,7 @@ App( - appid="Counter", + appid="counter", name="Counter", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="counterapp", requires=[ "gui", diff --git a/applications/plugins/counter/counter.c b/applications/plugins/counter/counter.c index 886ad3398..22fa8cd80 100644 --- a/applications/plugins/counter/counter.c +++ b/applications/plugins/counter/counter.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include #define MAX_COUNT 99 #define BOXTIME 2 diff --git a/applications/plugins/counter/media/preview.gif b/applications/plugins/counter/media/preview.gif deleted file mode 100644 index 87098b733..000000000 Binary files a/applications/plugins/counter/media/preview.gif and /dev/null differ diff --git a/applications/plugins/flipper_i2ctools/application.fam b/applications/plugins/flipper_i2ctools/application.fam index 6fafeef58..44138691d 100644 --- a/applications/plugins/flipper_i2ctools/application.fam +++ b/applications/plugins/flipper_i2ctools/application.fam @@ -10,4 +10,4 @@ App( fap_icon="i2ctools.png", fap_category="GPIO", fap_icon_assets="images", -) \ No newline at end of file +) diff --git a/applications/plugins/game_of_life/game_of_life.c b/applications/plugins/game_of_life/game_of_life.c index 65be8cb72..8629062b8 100644 --- a/applications/plugins/game_of_life/game_of_life.c +++ b/applications/plugins/game_of_life/game_of_life.c @@ -21,6 +21,7 @@ typedef struct { typedef struct { bool revive; int evo; + FuriMutex* mutex; } State; unsigned char new[TOTAL_PIXELS] = {}; @@ -92,7 +93,10 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu } static void render_callback(Canvas* canvas, void* ctx) { - State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25); + //furi_assert(ctx); + State* state = ctx; + furi_mutex_acquire(state->mutex, FuriWaitForever); + canvas_clear(canvas); for(int i = 0; i < TOTAL_PIXELS; ++i) { @@ -100,7 +104,7 @@ static void render_callback(Canvas* canvas, void* ctx) { int y = (int)(i / SCREEN_WIDTH); if(fields[current][i] == 1) canvas_draw_dot(canvas, x, y); } - release_mutex((ValueMutex*)ctx, state); + furi_mutex_release(state->mutex); } int32_t game_of_life_app(void* p) { @@ -112,8 +116,8 @@ int32_t game_of_life_app(void* p) { State* _state = malloc(sizeof(State)); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, _state, sizeof(State))) { + _state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!_state->mutex) { printf("cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(_state); @@ -121,7 +125,7 @@ int32_t game_of_life_app(void* p) { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, _state); view_port_input_callback_set(view_port, input_callback, event_queue); Gui* gui = furi_record_open(RECORD_GUI); @@ -129,23 +133,23 @@ int32_t game_of_life_app(void* p) { AppEvent event; for(bool processing = true; processing;) { - State* state = (State*)acquire_mutex_block(&state_mutex); FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); + furi_mutex_acquire(_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk && event.type == EventTypeKey && event.input.type == InputTypePress) { if(event.input.key == InputKeyBack) { // furiac_exit(NULL); processing = false; - release_mutex(&state_mutex, state); + furi_mutex_release(_state->mutex); break; } } - update_field(state); + update_field(_state); view_port_update(view_port); - release_mutex(&state_mutex, state); + furi_mutex_release(_state->mutex); } view_port_enabled_set(view_port, false); @@ -153,7 +157,7 @@ int32_t game_of_life_app(void* p) { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(_state->mutex); free(_state); return 0; diff --git a/applications/plugins/hc_sr04/application.fam b/applications/plugins/hc_sr04/application.fam index ad451a102..85b6da2df 100644 --- a/applications/plugins/hc_sr04/application.fam +++ b/applications/plugins/hc_sr04/application.fam @@ -11,4 +11,4 @@ App( order=20, fap_icon="dist_sensor10px.png", fap_category="GPIO", -) \ No newline at end of file +) diff --git a/applications/plugins/mandelbrot/mandelbrot.c b/applications/plugins/mandelbrot/mandelbrot.c index bfddc6a97..8bfe36c77 100644 --- a/applications/plugins/mandelbrot/mandelbrot.c +++ b/applications/plugins/mandelbrot/mandelbrot.c @@ -15,6 +15,7 @@ typedef struct { } PluginEvent; typedef struct { + FuriMutex* mutex; float xZoom; float yZoom; float xOffset; @@ -52,10 +53,9 @@ bool mandelbrot_pixel(int x, int y, float xZoom, float yZoom, float xOffset, flo } static void render_callback(Canvas* const canvas, void* ctx) { - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } + furi_assert(ctx); + const PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); // border around the edge of the screen canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -75,7 +75,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { } } - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -100,8 +100,8 @@ int32_t mandelbrot_app(void* p) { PluginState* plugin_state = malloc(sizeof(PluginState)); mandelbrot_state_init(plugin_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!plugin_state->mutex) { FURI_LOG_E("mandelbrot", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(plugin_state); @@ -110,7 +110,7 @@ int32_t mandelbrot_app(void* p) { // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_draw_callback_set(view_port, render_callback, plugin_state); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port @@ -120,7 +120,7 @@ int32_t mandelbrot_app(void* p) { PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -154,12 +154,9 @@ int32_t mandelbrot_app(void* p) { } } } - } else { - FURI_LOG_D("mandelbrot", "osMessageQueue: event timeout"); - // event timeout } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(plugin_state->mutex); } view_port_enabled_set(view_port, false); @@ -167,6 +164,8 @@ int32_t mandelbrot_app(void* p) { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); + furi_mutex_free(plugin_state->mutex); + free(plugin_state); return 0; } \ No newline at end of file diff --git a/applications/plugins/music_beeper/application.fam b/applications/plugins/music_beeper/application.fam index 2a83867bf..39b9babba 100644 --- a/applications/plugins/music_beeper/application.fam +++ b/applications/plugins/music_beeper/application.fam @@ -11,9 +11,9 @@ App( provides=["music_beeper_start"], stack_size=2 * 1024, order=45, - fap_icon="icons/music_10px.png", - fap_category="Music", + fap_icon="music_10px.png", fap_icon_assets="icons", + fap_category="Music", ) App( diff --git a/applications/plugins/music_beeper/music_10px.png b/applications/plugins/music_beeper/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/applications/plugins/music_beeper/music_10px.png differ diff --git a/applications/plugins/music_beeper/music_beeper_worker.c b/applications/plugins/music_beeper/music_beeper_worker.c index 4523b806e..e06e77447 100644 --- a/applications/plugins/music_beeper/music_beeper_worker.c +++ b/applications/plugins/music_beeper/music_beeper_worker.c @@ -101,8 +101,11 @@ MusicBeeperWorker* music_beeper_worker_alloc() { NoteBlockArray_init(instance->notes); - instance->thread = furi_thread_alloc_ex( - "MusicBeeperWorker", 1024, music_beeper_worker_thread_callback, instance); + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "MusicBeeperWorker"); + furi_thread_set_stack_size(instance->thread, 1024); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_callback(instance->thread, music_beeper_worker_thread_callback); instance->volume = 1.0f; diff --git a/applications/plugins/musictracker/application.fam b/applications/plugins/musictracker/application.fam index 51a0e839d..c0f7edca6 100644 --- a/applications/plugins/musictracker/application.fam +++ b/applications/plugins/musictracker/application.fam @@ -1,7 +1,7 @@ App( - appid="Zero_Tracker", + appid="zero_tracker", name="Zero Tracker", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="zero_tracker_app", requires=[ "gui", diff --git a/applications/plugins/musictracker/tracker_engine/speaker_hal.c b/applications/plugins/musictracker/tracker_engine/speaker_hal.c index 14a9c4f85..94489f1b6 100644 --- a/applications/plugins/musictracker/tracker_engine/speaker_hal.c +++ b/applications/plugins/musictracker/tracker_engine/speaker_hal.c @@ -40,7 +40,7 @@ void tracker_speaker_stop() { } void tracker_speaker_init() { - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(200.0f, 0.01f); tracker_speaker_stop(); } diff --git a/applications/plugins/nrf24scan/README.md b/applications/plugins/nrf24scan/README.md index a93ec913e..f2c50fde5 100644 --- a/applications/plugins/nrf24scan/README.md +++ b/applications/plugins/nrf24scan/README.md @@ -59,6 +59,21 @@ OK - вход в режим просмотра адресов и включен

+Схема подключения модуля nRF24l01:
+Big board:
+ +Gerber for full board (https://raw.githubusercontent.com/vad7/nrf24scan/master/Gerber_PCB_Flipper%20Zero%20nRF24%20board_v1_0.zip)
+Easyeda source (https://oshwlab.com/vad7/flipper-zero-nrf24-board). +

+Mini board:
+ +
+ +
+Gerber for mini board with 5V - 3.3V converter LM1117-3.3 (https://raw.githubusercontent.com/vad7/nrf24scan/master/Gerber_PCB_Flipper%20Zero%20nRF24%20board%20mini_v1_0.zip)
+Easyeda source (https://oshwlab.com/vad7/flipper-zero-nrf24-board_copy). +
+
_________________________________________________________________________________

diff --git a/applications/plugins/nrf24scan/nrf24scan.c b/applications/plugins/nrf24scan/nrf24scan.c index fafe37e8e..d66e7caf6 100644 --- a/applications/plugins/nrf24scan/nrf24scan.c +++ b/applications/plugins/nrf24scan/nrf24scan.c @@ -14,7 +14,7 @@ #include #define TAG "nrf24scan" -#define VERSION "2.1" +#define VERSION "2.2" #define MAX_CHANNEL 125 #define MAX_ADDR 6 @@ -71,6 +71,7 @@ uint8_t NRF_Payload = 32; // Payload len in bytes or Minimum payload in sniff mo uint8_t NRF_Payload_sniff_min = 0; uint8_t NRF_AA_OFF = 0; // Disable Auto Acknowledgement bool NRF_ERROR = 0; +bool NRF_BOARD_POWER_5V = false; struct ADDRS { uint8_t addr_P0[5]; // MSB first @@ -485,7 +486,13 @@ void check_add_addr(uint8_t* pkt) { static void prepare_nrf24(bool fsend_packet) { nrf24_write_reg(nrf24_HANDLE, REG_STATUS, 0x70); // clear interrupts - nrf24_write_reg(nrf24_HANDLE, REG_RF_SETUP, NRF_rate); + nrf24_write_reg( + nrf24_HANDLE, + REG_RF_SETUP, + (NRF_rate == 0 ? 0b00100000 : + NRF_rate == 1 ? 0 : + 0b00001000) | + 0b111); // +TX high power uint8_t erx_addr = (1 << 0); // Enable RX_P0 struct ADDRS* adr = what_to_do == 1 ? &addrs_sniff : &addrs; if(!fsend_packet) { @@ -517,14 +524,13 @@ static void prepare_nrf24(bool fsend_packet) { } if(what_to_do == 1) { // SNIFF payload = 32; - nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x70); // Mask all interrupts, NO CRC + nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x70); // Mask all interrupts nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, 0); // Automatic Retransmission nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0); // Auto acknowledgement nrf24_write_reg( nrf24_HANDLE, REG_FEATURE, 0); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload - nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel); } else if(setup_from_log) { // Scan nrf24_write_reg( nrf24_HANDLE, @@ -947,8 +953,10 @@ bool nrf24_send_packet() { } static void render_callback(Canvas* const canvas, void* ctx) { - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) return; + furi_assert(ctx); + const PluginState* plugin_state = ctx; + furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); + //canvas_draw_frame(canvas, 0, 0, 128, 64); // border around the edge of the screen if(what_doing == 0) { canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines @@ -1317,7 +1325,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas_draw_str(canvas, 0, 64, screen_buf); } } - release_mutex((ValueMutex*)ctx, plugin_state); + furi_mutex_release(plugin_state->mutex); } int32_t nrf24scan_app(void* p) { @@ -1325,8 +1333,8 @@ int32_t nrf24scan_app(void* p) { APP = malloc(sizeof(Nrf24Scan)); APP->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); APP->plugin_state = malloc(sizeof(PluginState)); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, APP->plugin_state, sizeof(PluginState))) { + APP->plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!APP->plugin_state->mutex) { furi_message_queue_free(APP->event_queue); FURI_LOG_E(TAG, "cannot create mutex"); free(APP->plugin_state); @@ -1346,11 +1354,16 @@ int32_t nrf24scan_app(void* p) { memset((uint8_t*)&addrs, 0, sizeof(addrs)); memset((uint8_t*)&addrs_sniff, 0, sizeof(addrs_sniff)); + if(!furi_hal_power_is_otg_enabled()) { + furi_hal_power_enable_otg(); + NRF_BOARD_POWER_5V = true; + furi_delay_ms(100); + } nrf24_init(); // Set system callbacks APP->view_port = view_port_alloc(); - view_port_draw_callback_set(APP->view_port, render_callback, &state_mutex); + view_port_draw_callback_set(APP->view_port, render_callback, APP->plugin_state); view_port_input_callback_set(APP->view_port, input_callback, APP->event_queue); // Open GUI and register view_port @@ -1391,7 +1404,7 @@ int32_t nrf24scan_app(void* p) { PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(APP->event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(APP->plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -1618,13 +1631,14 @@ int32_t nrf24scan_app(void* p) { } view_port_update(APP->view_port); - release_mutex(&state_mutex, plugin_state); + furi_mutex_release(APP->plugin_state->mutex); } nrf24_set_idle(nrf24_HANDLE); if(log_arr_idx && (log_to_file == 1 || log_to_file == 2)) { write_to_log_file(APP->storage, false); } nrf24_deinit(); + if(NRF_BOARD_POWER_5V) furi_hal_power_disable_otg(); view_port_enabled_set(APP->view_port, false); gui_remove_view_port(APP->gui, APP->view_port); @@ -1632,6 +1646,7 @@ int32_t nrf24scan_app(void* p) { furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_STORAGE); view_port_free(APP->view_port); + furi_mutex_free(APP->plugin_state->mutex); furi_message_queue_free(APP->event_queue); free(APP->plugin_state); if(APP->log_arr) free(APP->log_arr); diff --git a/applications/plugins/nrf24scan/nrf24scan.h b/applications/plugins/nrf24scan/nrf24scan.h index ea8d7ac04..cfc4806e7 100644 --- a/applications/plugins/nrf24scan/nrf24scan.h +++ b/applications/plugins/nrf24scan/nrf24scan.h @@ -19,8 +19,7 @@ typedef struct { } PluginEvent; typedef struct { - int x; - int y; + FuriMutex* mutex; } PluginState; struct FOUND { diff --git a/applications/plugins/ocarina/ocarina.c b/applications/plugins/ocarina/ocarina.c index 80dc1fddc..7fdfce74c 100644 --- a/applications/plugins/ocarina/ocarina.c +++ b/applications/plugins/ocarina/ocarina.c @@ -88,27 +88,27 @@ int32_t ocarina_app(void* p) { if(event.type == InputTypePress) { switch(event.key) { case InputKeyUp: - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(NOTE_UP, volume); } break; case InputKeyDown: - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(NOTE_DOWN, volume); } break; case InputKeyLeft: - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(NOTE_LEFT, volume); } break; case InputKeyRight: - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(NOTE_RIGHT, volume); } break; case InputKeyOk: - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { furi_hal_speaker_start(NOTE_OK, volume); } break; diff --git a/applications/plugins/paint/paint.c b/applications/plugins/paint/paint.c index 5cfe85155..0d4124b02 100644 --- a/applications/plugins/paint/paint.c +++ b/applications/plugins/paint/paint.c @@ -12,14 +12,17 @@ typedef struct selected_position { } selected_position; typedef struct { + FuriMutex* mutex; selected_position selected; bool board[32][16]; bool isDrawing; } PaintData; void paint_draw_callback(Canvas* canvas, void* ctx) { - const PaintData* paint_state = acquire_mutex((ValueMutex*)ctx, 25); - UNUSED(ctx); + furi_assert(ctx); + const PaintData* paint_state = ctx; + furi_mutex_acquire(paint_state->mutex, FuriWaitForever); + canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); //draw the canvas(32x16) on screen(144x64) using 4x4 tiles @@ -39,7 +42,7 @@ void paint_draw_callback(Canvas* canvas, void* ctx) { canvas, paint_state->selected.x * 4 + 1, paint_state->selected.y * 4 + 1, 2, 2); //release the mutex - release_mutex((ValueMutex*)ctx, paint_state); + furi_mutex_release(paint_state->mutex); } void paint_input_callback(InputEvent* input_event, void* ctx) { @@ -53,8 +56,9 @@ int32_t paint_app(void* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); PaintData* paint_state = malloc(sizeof(PaintData)); - ValueMutex paint_state_mutex; - if(!init_mutex(&paint_state_mutex, paint_state, sizeof(PaintData))) { + + paint_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!paint_state->mutex) { FURI_LOG_E("paint", "cannot create mutex\r\n"); free(paint_state); return -1; @@ -62,7 +66,7 @@ int32_t paint_app(void* p) { // Configure view port ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, paint_draw_callback, &paint_state_mutex); + view_port_draw_callback_set(view_port, paint_draw_callback, paint_state); view_port_input_callback_set(view_port, paint_input_callback, event_queue); // Register view port in GUI @@ -140,9 +144,10 @@ int32_t paint_app(void* p) { gui_remove_view_port(gui, view_port); view_port_free(view_port); furi_message_queue_free(event_queue); - free(paint_state); + furi_mutex_free(paint_state->mutex); furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_GUI); + free(paint_state); return 0; } diff --git a/applications/plugins/pomodoro/LICENSE b/applications/plugins/pomodoro/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/applications/plugins/pomodoro/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam index ceb8cccc9..e73427fc8 100644 --- a/applications/plugins/pomodoro/application.fam +++ b/applications/plugins/pomodoro/application.fam @@ -1,12 +1,15 @@ App( - appid="Pomodoro", - name="Pomodoro", + appid="Pomodoro_Timer", + name="Pomodoro Timer", apptype=FlipperAppType.EXTERNAL, - entry_point="flipp_pomodoro_app", - requires=["gui", "notification", "dolphin"], + entry_point="pomodoro_app", stack_size=1 * 1024, + cdefines=["APP_POMODORO"], + requires=[ + "gui", + ], + order=10, + fap_icon="pomodoro_timer.png", fap_category="Tools", - fap_icon_assets="images", - fap_icon="flipp_pomodoro_10.png", - fap_icon_assets_symbol="flipp_pomodoro", + fap_icon_assets="icons", ) diff --git a/applications/plugins/pomodoro/flipp_pomodoro_10.png b/applications/plugins/pomodoro/flipp_pomodoro_10.png deleted file mode 100644 index 977d16a58..000000000 Binary files a/applications/plugins/pomodoro/flipp_pomodoro_10.png and /dev/null differ diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app.c b/applications/plugins/pomodoro/flipp_pomodoro_app.c deleted file mode 100644 index 2ddf8cf68..000000000 --- a/applications/plugins/pomodoro/flipp_pomodoro_app.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "flipp_pomodoro_app_i.h" - -enum { - CustomEventConsumed = true, - CustomEventNotConsumed = false, -}; - -static bool flipp_pomodoro_app_back_event_callback(void* ctx) { - furi_assert(ctx); - FlippPomodoroApp* app = ctx; - return scene_manager_handle_back_event(app->scene_manager); -}; - -static void flipp_pomodoro_app_tick_event_callback(void* ctx) { - furi_assert(ctx); - FlippPomodoroApp* app = ctx; - - scene_manager_handle_custom_event(app->scene_manager, FlippPomodoroAppCustomEventTimerTick); -}; - -static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) { - furi_assert(ctx); - FlippPomodoroApp* app = ctx; - - switch(event) { - case FlippPomodoroAppCustomEventStageSkip: - flipp_pomodoro__toggle_stage(app->state); - view_dispatcher_send_custom_event( - app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated); - return CustomEventConsumed; - case FlippPomodoroAppCustomEventStageComplete: - flipp_pomodoro__toggle_stage(app->state); - notification_message( - app->notification_app, - stage_start_notification_sequence_map[flipp_pomodoro__get_stage(app->state)]); - view_dispatcher_send_custom_event( - app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated); - return CustomEventConsumed; - default: - break; - } - return scene_manager_handle_custom_event(app->scene_manager, event); -}; - -FlippPomodoroApp* flipp_pomodoro_app_alloc() { - FlippPomodoroApp* app = malloc(sizeof(FlippPomodoroApp)); - app->state = flipp_pomodoro__new(); - - app->scene_manager = scene_manager_alloc(&flipp_pomodoro_scene_handlers, app); - app->gui = furi_record_open(RECORD_GUI); - app->notification_app = furi_record_open(RECORD_NOTIFICATION); - - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, flipp_pomodoro_app_custom_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, flipp_pomodoro_app_tick_event_callback, 1000); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, flipp_pomodoro_app_back_event_callback); - - app->timer_view = flipp_pomodoro_view_timer_alloc(); - - view_dispatcher_add_view( - app->view_dispatcher, - FlippPomodoroAppViewTimer, - flipp_pomodoro_view_timer_get_view(app->timer_view)); - - scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer); - - return app; -}; - -void flipp_pomodoro_app_free(FlippPomodoroApp* app) { - view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewTimer); - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - flipp_pomodoro_view_timer_free(app->timer_view); - flipp_pomodoro__destroy(app->state); - free(app); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); -}; - -int32_t flipp_pomodoro_app(void* p) { - UNUSED(p); - FlippPomodoroApp* app = flipp_pomodoro_app_alloc(); - - view_dispatcher_run(app->view_dispatcher); - - flipp_pomodoro_app_free(app); - - return 0; -}; diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app.h b/applications/plugins/pomodoro/flipp_pomodoro_app.h deleted file mode 100644 index 54102e1f3..000000000 --- a/applications/plugins/pomodoro/flipp_pomodoro_app.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include "views/flipp_pomodoro_timer_view.h" - -#include "modules/flipp_pomodoro.h" - -typedef enum { - // Reserve first 100 events for button types and indexes, starting from 0 - FlippPomodoroAppCustomEventStageSkip = 100, - FlippPomodoroAppCustomEventStageComplete, // By Expiration - FlippPomodoroAppCustomEventTimerTick, - FlippPomodoroAppCustomEventStateUpdated, -} FlippPomodoroAppCustomEvent; - -typedef struct { - SceneManager* scene_manager; - ViewDispatcher* view_dispatcher; - Gui* gui; - NotificationApp* notification_app; - FlippPomodoroTimerView* timer_view; - FlippPomodoroState* state; -} FlippPomodoroApp; - -typedef enum { - FlippPomodoroAppViewTimer, -} FlippPomodoroAppView; \ No newline at end of file diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app_i.h b/applications/plugins/pomodoro/flipp_pomodoro_app_i.h deleted file mode 100644 index 492ef9c2d..000000000 --- a/applications/plugins/pomodoro/flipp_pomodoro_app_i.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#define FURI_DEBUG 1 - -/** - * Index of dependencies for the main app - */ - -// Platform Imports - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// App resource imports - -#include "helpers/time.h" -#include "helpers/notifications.h" -#include "modules/flipp_pomodoro.h" -#include "flipp_pomodoro_app.h" -#include "scenes/flipp_pomodoro_scene.h" -#include "views/flipp_pomodoro_timer_view.h" - -// Auto-compiled icons -#include "flipp_pomodoro_icons.h" diff --git a/applications/plugins/pomodoro/helpers/debug.h b/applications/plugins/pomodoro/helpers/debug.h deleted file mode 100644 index 13b8f2998..000000000 --- a/applications/plugins/pomodoro/helpers/debug.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -#define TAG "FlippPomodoro" \ No newline at end of file diff --git a/applications/plugins/pomodoro/helpers/notifications.c b/applications/plugins/pomodoro/helpers/notifications.c deleted file mode 100644 index 388a3f11d..000000000 --- a/applications/plugins/pomodoro/helpers/notifications.c +++ /dev/null @@ -1,49 +0,0 @@ -#include - -const NotificationSequence work_start_notification = { - &message_display_backlight_on, - - &message_vibro_on, - - &message_note_b5, - &message_delay_250, - - &message_note_d5, - &message_delay_250, - - &message_sound_off, - &message_vibro_off, - - &message_green_255, - &message_delay_1000, - &message_green_0, - &message_delay_250, - &message_green_255, - &message_delay_1000, - - NULL, -}; - -const NotificationSequence rest_start_notification = { - &message_display_backlight_on, - - &message_vibro_on, - - &message_note_d5, - &message_delay_250, - - &message_note_b5, - &message_delay_250, - - &message_sound_off, - &message_vibro_off, - - &message_red_255, - &message_delay_1000, - &message_red_0, - &message_delay_250, - &message_red_255, - &message_delay_1000, - - NULL, -}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/helpers/notifications.h b/applications/plugins/pomodoro/helpers/notifications.h deleted file mode 100644 index c6cd0428f..000000000 --- a/applications/plugins/pomodoro/helpers/notifications.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "../modules/flipp_pomodoro.h" -#include - -extern const NotificationSequence work_start_notification; -extern const NotificationSequence rest_start_notification; - -/// @brief Defines a notification sequence that should indicate start of specific pomodoro stage. -const NotificationSequence* stage_start_notification_sequence_map[] = { - [FlippPomodoroStageFocus] = &work_start_notification, - [FlippPomodoroStageRest] = &rest_start_notification, - [FlippPomodoroStageLongBreak] = &rest_start_notification, -}; diff --git a/applications/plugins/pomodoro/helpers/time.c b/applications/plugins/pomodoro/helpers/time.c deleted file mode 100644 index 7fb0d13c2..000000000 --- a/applications/plugins/pomodoro/helpers/time.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include "time.h" - -const int TIME_SECONDS_IN_MINUTE = 60; -const int TIME_MINUTES_IN_HOUR = 60; - -uint32_t time_now() { - return furi_hal_rtc_get_timestamp(); -}; - -TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) { - const uint32_t duration_seconds = end - begin; - - uint32_t minutes = (duration_seconds / TIME_MINUTES_IN_HOUR) % TIME_MINUTES_IN_HOUR; - uint32_t seconds = duration_seconds % TIME_SECONDS_IN_MINUTE; - - return ( - TimeDifference){.total_seconds = duration_seconds, .minutes = minutes, .seconds = seconds}; -}; diff --git a/applications/plugins/pomodoro/helpers/time.h b/applications/plugins/pomodoro/helpers/time.h deleted file mode 100644 index 7a7d90bf2..000000000 --- a/applications/plugins/pomodoro/helpers/time.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include - -extern const int TIME_SECONDS_IN_MINUTE; -extern const int TIME_MINUTES_IN_HOUR; - -/// @brief Container for a time period -typedef struct { - uint8_t seconds; - uint8_t minutes; - uint32_t total_seconds; -} TimeDifference; - -/// @brief Time by the moment of calling -/// @return A timestamp(seconds percision) -uint32_t time_now(); - -/// @brief Calculates difference between two provided timestamps -/// @param begin - start timestamp of the period -/// @param end - end timestamp of the period to measure -/// @return TimeDifference struct -TimeDifference time_difference_seconds(uint32_t begin, uint32_t end); diff --git a/applications/plugins/pomodoro/icons/ButtonLeft_4x7.png b/applications/plugins/pomodoro/icons/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/applications/plugins/pomodoro/icons/ButtonLeft_4x7.png differ diff --git a/applications/plugins/pomodoro/icons/Ok_btn_9x9.png b/applications/plugins/pomodoro/icons/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/applications/plugins/pomodoro/icons/Ok_btn_9x9.png differ diff --git a/applications/plugins/pomodoro/icons/Pin_back_arrow_10x8.png b/applications/plugins/pomodoro/icons/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/applications/plugins/pomodoro/icons/Pin_back_arrow_10x8.png differ diff --git a/applications/plugins/pomodoro/icons/Space_65x18.png b/applications/plugins/pomodoro/icons/Space_65x18.png new file mode 100644 index 000000000..b60ae5097 Binary files /dev/null and b/applications/plugins/pomodoro/icons/Space_65x18.png differ diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png b/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png deleted file mode 100644 index 7ead27c4e..000000000 Binary files a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png and /dev/null differ diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_01.png b/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_01.png deleted file mode 100644 index 0e36886f2..000000000 Binary files a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_01.png and /dev/null differ diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_rate b/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_rate deleted file mode 100644 index 56a6051ca..000000000 --- a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_rate +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_00.png b/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_00.png deleted file mode 100644 index 072affef3..000000000 Binary files a/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_00.png and /dev/null differ diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_01.png b/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_01.png deleted file mode 100644 index cf6358658..000000000 Binary files a/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_01.png and /dev/null differ diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_rate b/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_rate deleted file mode 100644 index 56a6051ca..000000000 --- a/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_rate +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/applications/plugins/pomodoro/modules/flipp_pomodoro.c b/applications/plugins/pomodoro/modules/flipp_pomodoro.c deleted file mode 100644 index 9915025fb..000000000 --- a/applications/plugins/pomodoro/modules/flipp_pomodoro.c +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include -#include "../helpers/time.h" -#include "flipp_pomodoro.h" - -PomodoroStage stages_sequence[] = { - FlippPomodoroStageFocus, - FlippPomodoroStageRest, - - FlippPomodoroStageFocus, - FlippPomodoroStageRest, - - FlippPomodoroStageFocus, - FlippPomodoroStageRest, - - FlippPomodoroStageFocus, - FlippPomodoroStageLongBreak, -}; - -char* current_stage_label[] = { - [FlippPomodoroStageFocus] = "Continue focus for:", - [FlippPomodoroStageRest] = "Keep rest for:", - [FlippPomodoroStageLongBreak] = "Long Break for:", -}; - -char* next_stage_label[] = { - [FlippPomodoroStageFocus] = "Focus", - [FlippPomodoroStageRest] = "Short Break", - [FlippPomodoroStageLongBreak] = "Long Break", -}; - -PomodoroStage flipp_pomodoro__stage_by_index(int index) { - const int one_loop_size = sizeof(stages_sequence); - return stages_sequence[index % one_loop_size]; -} - -void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) { - furi_assert(state); - state->current_stage_index = state->current_stage_index + 1; - state->started_at_timestamp = time_now(); -}; - -PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state) { - furi_assert(state); - return flipp_pomodoro__stage_by_index(state->current_stage_index); -}; - -char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state) { - furi_assert(state); - return current_stage_label[flipp_pomodoro__get_stage(state)]; -}; - -char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) { - furi_assert(state); - return next_stage_label[flipp_pomodoro__stage_by_index(state->current_stage_index + 1)]; -}; - -void flipp_pomodoro__destroy(FlippPomodoroState* state) { - furi_assert(state); - free(state); -}; - -uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) { - const int32_t stage_duration_seconds_map[] = { - [FlippPomodoroStageFocus] = 25 * TIME_SECONDS_IN_MINUTE, - [FlippPomodoroStageRest] = 5 * TIME_SECONDS_IN_MINUTE, - [FlippPomodoroStageLongBreak] = 30 * TIME_SECONDS_IN_MINUTE, - }; - - return stage_duration_seconds_map[flipp_pomodoro__get_stage(state)]; -}; - -uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState* state) { - return state->started_at_timestamp + flipp_pomodoro__current_stage_total_duration(state); -}; - -TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state) { - const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state); - return time_difference_seconds(time_now(), stage_ends_at); -}; - -bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state) { - const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state); - const uint8_t seamless_change_span_seconds = 1; - return (time_now() - seamless_change_span_seconds) >= expired_by; -}; - -FlippPomodoroState* flipp_pomodoro__new() { - FlippPomodoroState* state = malloc(sizeof(FlippPomodoroState)); - const uint32_t now = time_now(); - state->started_at_timestamp = now; - state->current_stage_index = 0; - return state; -}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/modules/flipp_pomodoro.h b/applications/plugins/pomodoro/modules/flipp_pomodoro.h deleted file mode 100644 index 251a77469..000000000 --- a/applications/plugins/pomodoro/modules/flipp_pomodoro.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#include "../helpers/time.h" - -/// @brief Options of pomodoro stages -typedef enum { - FlippPomodoroStageFocus, - FlippPomodoroStageRest, - FlippPomodoroStageLongBreak, -} PomodoroStage; - -/// @brief State of the pomodoro timer -typedef struct { - PomodoroStage stage; - uint8_t current_stage_index; - uint32_t started_at_timestamp; -} FlippPomodoroState; - -/// @brief Generates initial state -/// @returns A new pre-populated state for pomodoro timer -FlippPomodoroState* flipp_pomodoro__new(); - -/// @brief Extract current stage of pomodoro -/// @param state - pointer to the state of pomorodo -/// @returns Current stage value -PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state); - -/// @brief Destroys state of timer and it's dependencies -void flipp_pomodoro__destroy(FlippPomodoroState* state); - -/// @brief Get remaining stage time. -/// @param state - pointer to the state of pomorodo -/// @returns Time difference to the end of current stage -TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state); - -/// @brief Label of currently active stage -/// @param state - pointer to the state of pomorodo -/// @returns A string that explains current stage -char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state); - -/// @brief Label of transition to the next stage -/// @param state - pointer to the state of pomorodo. -/// @returns string with the label of the "skipp" button -char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state); - -/// @brief Check if current stage is expired -/// @param state - pointer to the state of pomorodo. -/// @returns expriations status - true means stage is expired -bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state); - -/// @brief Rotate stage of the timer -/// @param state - pointer to the state of pomorodo. -void flipp_pomodoro__toggle_stage(FlippPomodoroState* state); diff --git a/applications/plugins/pomodoro/pomodoro.c b/applications/plugins/pomodoro/pomodoro.c new file mode 100644 index 000000000..5b1db1984 --- /dev/null +++ b/applications/plugins/pomodoro/pomodoro.c @@ -0,0 +1,164 @@ +#include "pomodoro.h" +#include + +#define TAG "PomodoroApp" + +enum PomodoroDebugSubmenuIndex { + PomodoroSubmenuIndex10, + PomodoroSubmenuIndex25, + PomodoroSubmenuIndex50, +}; + +void pomodoro_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + Pomodoro* app = context; + if(index == PomodoroSubmenuIndex10) { + app->view_id = PomodoroView10; + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView10); + } + if(index == PomodoroSubmenuIndex25) { + app->view_id = PomodoroView25; + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView25); + } + if(index == PomodoroSubmenuIndex50) { + app->view_id = PomodoroView50; + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView50); + } +} + +void pomodoro_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + Pomodoro* app = context; + if(result == DialogExResultLeft) { + view_dispatcher_stop(app->view_dispatcher); + } else if(result == DialogExResultRight) { + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view + } else if(result == DialogExResultCenter) { + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroViewSubmenu); + } +} + +uint32_t pomodoro_exit_confirm_view(void* context) { + UNUSED(context); + return PomodoroViewExitConfirm; +} + +uint32_t pomodoro_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +Pomodoro* pomodoro_app_alloc() { + Pomodoro* app = malloc(sizeof(Pomodoro)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // Notifications + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Submenu view + app->submenu = submenu_alloc(); + submenu_add_item( + app->submenu, + "Classic: 25 work 5 rest", + PomodoroSubmenuIndex25, + pomodoro_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Long: 50 work 10 rest", + PomodoroSubmenuIndex50, + pomodoro_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Sprint: 10 work 2 rest", + PomodoroSubmenuIndex10, + pomodoro_submenu_callback, + app); + view_set_previous_callback(submenu_get_view(app->submenu), pomodoro_exit); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroViewSubmenu, submenu_get_view(app->submenu)); + + // Dialog view + app->dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->dialog, pomodoro_dialog_callback); + dialog_ex_set_context(app->dialog, app); + dialog_ex_set_left_button_text(app->dialog, "Exit"); + dialog_ex_set_right_button_text(app->dialog, "Stay"); + dialog_ex_set_center_button_text(app->dialog, "Menu"); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroViewExitConfirm, dialog_ex_get_view(app->dialog)); + + // 25 minutes view + app->pomodoro_25 = pomodoro_25_alloc(); + view_set_previous_callback(pomodoro_25_get_view(app->pomodoro_25), pomodoro_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroView25, pomodoro_25_get_view(app->pomodoro_25)); + + // 50 minutes view + app->pomodoro_50 = pomodoro_50_alloc(); + view_set_previous_callback(pomodoro_50_get_view(app->pomodoro_50), pomodoro_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroView50, pomodoro_50_get_view(app->pomodoro_50)); + + // 10 minutes view + app->pomodoro_10 = pomodoro_10_alloc(); + view_set_previous_callback(pomodoro_10_get_view(app->pomodoro_10), pomodoro_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroView10, pomodoro_10_get_view(app->pomodoro_10)); + + // TODO switch to menu after Media is done + app->view_id = PomodoroViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + + return app; +} + +void pomodoro_app_free(Pomodoro* app) { + furi_assert(app); + + // Reset notification + notification_internal_message(app->notifications, &sequence_reset_blue); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, PomodoroViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroViewExitConfirm); + dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroView25); + pomodoro_25_free(app->pomodoro_25); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroView50); + pomodoro_50_free(app->pomodoro_50); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroView10); + pomodoro_10_free(app->pomodoro_10); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Free rest + free(app); +} + +int32_t pomodoro_app(void* p) { + UNUSED(p); + // Switch profile to Hid + Pomodoro* app = pomodoro_app_alloc(); + + view_dispatcher_run(app->view_dispatcher); + + pomodoro_app_free(app); + + return 0; +} diff --git a/applications/plugins/pomodoro/pomodoro.h b/applications/plugins/pomodoro/pomodoro.h new file mode 100644 index 000000000..53dedb8e3 --- /dev/null +++ b/applications/plugins/pomodoro/pomodoro.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include "pomodoro_timer.h" +#include "views/pomodoro_10.h" +#include "views/pomodoro_25.h" +#include "views/pomodoro_50.h" + +typedef struct { + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + DialogEx* dialog; + PomodoroTimer* pomodoro_10; + PomodoroTimer* pomodoro_25; + PomodoroTimer* pomodoro_50; + uint32_t view_id; +} Pomodoro; + +typedef enum { + PomodoroViewSubmenu, + PomodoroView10, + PomodoroView25, + PomodoroView50, + PomodoroViewExitConfirm, +} PomodoroView; diff --git a/applications/plugins/pomodoro/pomodoro_timer.c b/applications/plugins/pomodoro/pomodoro_timer.c new file mode 100644 index 000000000..0fba5db42 --- /dev/null +++ b/applications/plugins/pomodoro/pomodoro_timer.c @@ -0,0 +1,242 @@ +#include "pomodoro_timer.h" +#include +#include +#include +#include +#include + +const NotificationSequence sequence_finish = { + &message_display_backlight_on, + &message_green_255, + &message_vibro_on, + &message_note_c5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_e5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_g5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_b5, + &message_delay_250, + &message_vibro_off, + &message_vibro_on, + &message_note_c6, + &message_delay_250, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_rest = { + &message_display_backlight_on, + &message_red_255, + &message_vibro_on, + &message_note_c6, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_b5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_g5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_e5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_c5, + &message_delay_250, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +void pomodoro_timer_process(PomodoroTimer* pomodoro_timer, InputEvent* event) { + with_view_model( + pomodoro_timer->view, + PomodoroTimerModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyOk) { + model->ok_pressed = true; + } else if(event->key == InputKeyLeft) { + model->reset_pressed = true; + } else if(event->key == InputKeyBack) { + model->back_pressed = true; + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyOk) { + model->ok_pressed = false; + + // START/STOP TIMER + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t current_timestamp = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + // STARTED -> PAUSED + if(model->timer_running) { + // Update stopped seconds + model->timer_stopped_seconds = + current_timestamp - model->timer_start_timestamp; + } else if(!model->time_passed) { + // INITIAL -> STARTED + model->timer_start_timestamp = current_timestamp; + model->rest_running = false; + } else { + // PAUSED -> STARTED + model->timer_start_timestamp = + current_timestamp - model->timer_stopped_seconds; + } + model->timer_running = !model->timer_running; + } else if(event->key == InputKeyLeft) { + if(!model->timer_running) { + furi_record_close(RECORD_NOTIFICATION); + model->timer_stopped_seconds = 0; + model->timer_start_timestamp = 0; + model->time_passed = 0; + model->timer_running = false; + } + model->reset_pressed = false; + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + } + }, + true); +} + +void pomodoro_draw_callback(Canvas* canvas, void* context, int max_seconds, int max_seconds_rest) { + furi_assert(context); + PomodoroTimerModel* model = context; + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t current_timestamp = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + // Header + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Pomodoro"); + + canvas_draw_icon(canvas, 68, 1, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 127, 1, AlignRight, AlignTop, "Hold to exit"); + + // Start/Pause/Continue + int txt_main_y = 34; + canvas_draw_icon(canvas, 63, 23, &I_Space_65x18); // button + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 66, 25, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + if(model->timer_running) { + model->time_passed = current_timestamp - model->timer_start_timestamp; + elements_multiline_text_aligned(canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Pause"); + canvas_draw_box(canvas, 71, 27, 2, 8); + canvas_draw_box(canvas, 75, 27, 2, 8); + } else { + if(model->time_passed) { + elements_multiline_text_aligned( + canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Continue"); + } else { + elements_multiline_text_aligned( + canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Start"); + } + canvas_draw_icon(canvas, 70, 26, &I_Ok_btn_9x9); // OK icon + } + canvas_set_color(canvas, ColorBlack); + + // Reset + if(!model->timer_running && model->time_passed) { + canvas_draw_icon(canvas, 63, 46, &I_Space_65x18); + if(model->reset_pressed) { + elements_slightly_rounded_box(canvas, 66, 48, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 72, 50, &I_ButtonLeft_4x7); + elements_multiline_text_aligned(canvas, 83, 57, AlignLeft, AlignBottom, "Reset"); + canvas_set_color(canvas, ColorBlack); + } + + char buffer[64]; + + // Time to work + int total_time_left = (max_seconds - (uint32_t)model->time_passed); + int minutes_left = total_time_left / 60; + int seconds_left = total_time_left % 60; + canvas_set_font(canvas, FontBigNumbers); + + // Play sound + if(total_time_left == 0 && !model->sound_playing) { + model->sound_playing = true; + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_finish); + } + if(total_time_left < 0) { + model->timer_running = false; + model->time_passed = 0; + model->sound_playing = false; + + model->rest_running = true; + model->rest_start_timestamp = current_timestamp; + seconds_left = 0; + model->counter += 1; + } + if(!model->rest_running) { + snprintf(buffer, sizeof(buffer), "%02d:%02d", minutes_left, seconds_left); + canvas_draw_str(canvas, 0, 39, buffer); + } + if(model->timer_running) { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 0, 50, AlignLeft, AlignTop, "Time to work"); + } + + // Time to rest + if(model->rest_running && !model->timer_running) { + canvas_set_font(canvas, FontBigNumbers); + int rest_passed = current_timestamp - model->rest_start_timestamp; + int rest_total_time_left = (max_seconds_rest - rest_passed); + int rest_minutes_left = rest_total_time_left / 60; + int rest_seconds_left = rest_total_time_left % 60; + + // Play sound + if(rest_total_time_left == 0 && !model->sound_playing) { + model->sound_playing = true; + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_rest); + } + if(rest_total_time_left < 0) { + rest_seconds_left = 0; + model->rest_running = false; + model->sound_playing = false; + } + snprintf(buffer, sizeof(buffer), "%02d:%02d", rest_minutes_left, rest_seconds_left); + canvas_draw_str(canvas, 0, 60, buffer); + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 0, 27, AlignLeft, AlignTop, "Have a rest"); + } + + // Clocks + canvas_set_font(canvas, FontSecondary); + snprintf( + buffer, + sizeof(buffer), + "%02ld:%02ld:%02ld", + ((uint32_t)current_timestamp % (60 * 60 * 24)) / (60 * 60), + ((uint32_t)current_timestamp % (60 * 60)) / 60, + (uint32_t)current_timestamp % 60); + canvas_draw_str(canvas, 0, 20, buffer); + + // Tomato counter + if(model->counter > 5) { + model->counter = 1; + } + for(int i = 0; i < model->counter; ++i) { + canvas_draw_disc(canvas, 122 - i * 10, 15, 4); + } +} diff --git a/applications/plugins/pomodoro/pomodoro_timer.h b/applications/plugins/pomodoro/pomodoro_timer.h new file mode 100644 index 000000000..284f0a6c6 --- /dev/null +++ b/applications/plugins/pomodoro/pomodoro_timer.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +typedef struct PomodoroTimer PomodoroTimer; + +struct PomodoroTimer { + View* view; +}; + +typedef struct PomodoroTimerModel PomodoroTimerModel; + +struct PomodoroTimerModel { + bool ok_pressed; + bool reset_pressed; + bool back_pressed; + bool connected; + bool timer_running; + bool rest_running; + bool sound_playing; + uint32_t timer_start_timestamp; + uint32_t timer_stopped_seconds; + uint32_t time_passed; + uint32_t rest_start_timestamp; + int counter; +}; + +void pomodoro_timer_process(PomodoroTimer* pomodoro_timer, InputEvent* event); + +void pomodoro_draw_callback(Canvas* canvas, void* context, int max_seconds, int max_seconds_rest); diff --git a/applications/plugins/pomodoro/pomodoro_timer.png b/applications/plugins/pomodoro/pomodoro_timer.png new file mode 100644 index 000000000..b25c2718e Binary files /dev/null and b/applications/plugins/pomodoro/pomodoro_timer.png differ diff --git a/applications/plugins/pomodoro/scenes/.keep b/applications/plugins/pomodoro/scenes/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h b/applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h deleted file mode 100644 index f95daeb30..000000000 --- a/applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h +++ /dev/null @@ -1 +0,0 @@ -ADD_SCENE(flipp_pomodoro, timer, Timer) \ No newline at end of file diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c deleted file mode 100644 index 5856ac947..000000000 --- a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "flipp_pomodoro_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const flipp_pomodoro_scene_on_enter_handlers[])(void*) = { -#include "config/flipp_pomodoro_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const flipp_pomodoro_scene_on_event_handlers[])(void* ctx, SceneManagerEvent event) = { -#include "config/flipp_pomodoro_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const flipp_pomodoro_scene_on_exit_handlers[])(void* ctx) = { -#include "config/flipp_pomodoro_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers flipp_pomodoro_scene_handlers = { - .on_enter_handlers = flipp_pomodoro_scene_on_enter_handlers, - .on_event_handlers = flipp_pomodoro_scene_on_event_handlers, - .on_exit_handlers = flipp_pomodoro_scene_on_exit_handlers, - .scene_num = FlippPomodoroSceneNum, -}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h deleted file mode 100644 index 3b8a9052a..000000000 --- a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h +++ /dev/null @@ -1,27 +0,0 @@ -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) FlippPomodoroScene##id, -typedef enum { -#include "config/flipp_pomodoro_scene_config.h" - FlippPomodoroSceneNum, -} FlippPomodoroScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers flipp_pomodoro_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "config/flipp_pomodoro_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* ctx, SceneManagerEvent event); -#include "config/flipp_pomodoro_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* ctx); -#include "config/flipp_pomodoro_scene_config.h" -#undef ADD_SCENE diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c deleted file mode 100644 index 2190dbdb7..000000000 --- a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include -#include -#include "../flipp_pomodoro_app.h" -#include "../views/flipp_pomodoro_timer_view.h" - -enum { SceneEventConusmed = true, SceneEventNotConusmed = false }; - -uint8_t ExitSignal = 0; - -void flipp_pomodoro_scene_timer_sync_view_state(void* ctx) { - furi_assert(ctx); - - FlippPomodoroApp* app = ctx; - - flipp_pomodoro_view_timer_set_state( - flipp_pomodoro_view_timer_get_view(app->timer_view), app->state); -}; - -void flipp_pomodoro_scene_timer_on_next_stage(void* ctx) { - furi_assert(ctx); - - FlippPomodoroApp* app = ctx; - - view_dispatcher_send_custom_event(app->view_dispatcher, FlippPomodoroAppCustomEventStageSkip); -}; - -void flipp_pomodoro_scene_timer_on_enter(void* ctx) { - furi_assert(ctx); - - FlippPomodoroApp* app = ctx; - - view_dispatcher_switch_to_view(app->view_dispatcher, FlippPomodoroAppViewTimer); - flipp_pomodoro_scene_timer_sync_view_state(app); - flipp_pomodoro_view_timer_set_on_right_cb( - app->timer_view, flipp_pomodoro_scene_timer_on_next_stage, app); -}; - -void flipp_pomodoro_scene_timer_handle_custom_event( - FlippPomodoroApp* app, - FlippPomodoroAppCustomEvent custom_event) { - if(custom_event == FlippPomodoroAppCustomEventTimerTick && - flipp_pomodoro__is_stage_expired(app->state)) { - view_dispatcher_send_custom_event( - app->view_dispatcher, FlippPomodoroAppCustomEventStageComplete); - } - - if(custom_event == FlippPomodoroAppCustomEventStateUpdated) { - flipp_pomodoro_scene_timer_sync_view_state(app); - } -}; - -bool flipp_pomodoro_scene_timer_on_event(void* ctx, SceneManagerEvent event) { - furi_assert(ctx); - FlippPomodoroApp* app = ctx; - - switch(event.type) { - case SceneManagerEventTypeCustom: - flipp_pomodoro_scene_timer_handle_custom_event(app, event.event); - return SceneEventConusmed; - case SceneManagerEventTypeBack: - return ExitSignal; - default: - break; - }; - return SceneEventNotConusmed; -}; - -void flipp_pomodoro_scene_timer_on_exit(void* ctx) { - UNUSED(ctx); -}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/views/.keep b/applications/plugins/pomodoro/views/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c b/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c deleted file mode 100644 index e8e0383b7..000000000 --- a/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c +++ /dev/null @@ -1,195 +0,0 @@ -#include "flipp_pomodoro_timer_view.h" -#include -#include -#include -#include -#include "../helpers/debug.h" -#include "../flipp_pomodoro_app.h" -#include "../modules/flipp_pomodoro.h" - -// Auto-compiled icons -#include "flipp_pomodoro_icons.h" - -enum { - ViewInputConsumed = true, - ViewInputNotConusmed = false, -}; - -struct FlippPomodoroTimerView { - View* view; - FlippPomodoroTimerViewInputCb right_cb; - void* right_cb_ctx; -}; - -typedef struct { - IconAnimation* icon; - FlippPomodoroState* state; -} FlippPomodoroTimerViewModel; - -static const Icon* stage_background_image[] = { - [FlippPomodoroStageFocus] = &A_flipp_pomodoro_focus_64, - [FlippPomodoroStageRest] = &A_flipp_pomodoro_rest_64, - [FlippPomodoroStageLongBreak] = &A_flipp_pomodoro_rest_64, -}; - -static void - flipp_pomodoro_view_timer_draw_countdown(Canvas* canvas, TimeDifference remaining_time) { - canvas_set_font(canvas, FontBigNumbers); - const uint8_t right_border_margin = 1; - - const uint8_t countdown_box_height = canvas_height(canvas) * 0.4; - const uint8_t countdown_box_width = canvas_width(canvas) * 0.5; - const uint8_t countdown_box_x = - canvas_width(canvas) - countdown_box_width - right_border_margin; - const uint8_t countdown_box_y = 15; - - elements_bold_rounded_frame( - canvas, countdown_box_x, countdown_box_y, countdown_box_width, countdown_box_height); - - FuriString* timer_string = furi_string_alloc(); - furi_string_printf(timer_string, "%02u:%02u", remaining_time.minutes, remaining_time.seconds); - const char* remaining_stage_time_string = furi_string_get_cstr(timer_string); - canvas_draw_str_aligned( - canvas, - countdown_box_x + (countdown_box_width / 2), - countdown_box_y + (countdown_box_height / 2), - AlignCenter, - AlignCenter, - remaining_stage_time_string); - - furi_string_free(timer_string); -}; - -static void draw_str_with_drop_shadow( - Canvas* canvas, - uint8_t x, - uint8_t y, - Align horizontal, - Align vertical, - const char* str) { - canvas_set_color(canvas, ColorWhite); - for(int x_off = -2; x_off <= 2; x_off++) { - for(int y_off = -2; y_off <= 2; y_off++) { - canvas_draw_str_aligned(canvas, x + x_off, y + y_off, horizontal, vertical, str); - } - } - canvas_set_color(canvas, ColorBlack); - canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, str); -} - -static void - flipp_pomodoro_view_timer_draw_current_stage_label(Canvas* canvas, FlippPomodoroState* state) { - canvas_set_font(canvas, FontPrimary); - draw_str_with_drop_shadow( - canvas, - canvas_width(canvas), - 0, - AlignRight, - AlignTop, - flipp_pomodoro__current_stage_label(state)); -} - -static void flipp_pomodoro_view_timer_draw_callback(Canvas* canvas, void* _model) { - if(!_model) { - return; - }; - - FlippPomodoroTimerViewModel* model = _model; - - canvas_clear(canvas); - if(model->icon) { - canvas_draw_icon_animation(canvas, 0, 0, model->icon); - } - - flipp_pomodoro_view_timer_draw_countdown( - canvas, flipp_pomodoro__stage_remaining_duration(model->state)); - - flipp_pomodoro_view_timer_draw_current_stage_label(canvas, model->state); - canvas_set_color(canvas, ColorBlack); - - canvas_set_font(canvas, FontSecondary); - elements_button_right(canvas, flipp_pomodoro__next_stage_label(model->state)); -}; - -bool flipp_pomodoro_view_timer_input_callback(InputEvent* event, void* ctx) { - furi_assert(ctx); - furi_assert(event); - FlippPomodoroTimerView* timer = ctx; - - const bool should_trigger_right_event_cb = (event->type == InputTypePress) && - (event->key == InputKeyRight) && - (timer->right_cb != NULL); - - if(should_trigger_right_event_cb) { - furi_assert(timer->right_cb); - furi_assert(timer->right_cb_ctx); - timer->right_cb(timer->right_cb_ctx); - return ViewInputConsumed; - }; - - return ViewInputNotConusmed; -}; - -View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer) { - furi_assert(timer); - return timer->view; -}; - -void flipp_pomodoro_view_timer_assign_animation(View* view) { - with_view_model( - view, - FlippPomodoroTimerViewModel * model, - { - furi_assert(model->state); - if(model->icon) { - icon_animation_free(model->icon); - } - model->icon = icon_animation_alloc( - stage_background_image[flipp_pomodoro__get_stage(model->state)]); - view_tie_icon_animation(view, model->icon); - icon_animation_start(model->icon); - }, - true); -} - -FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc() { - FlippPomodoroTimerView* timer = malloc(sizeof(FlippPomodoroTimerView)); - timer->view = view_alloc(); - - view_allocate_model(timer->view, ViewModelTypeLockFree, sizeof(FlippPomodoroTimerViewModel)); - view_set_context(flipp_pomodoro_view_timer_get_view(timer), timer); - view_set_draw_callback(timer->view, flipp_pomodoro_view_timer_draw_callback); - view_set_input_callback(timer->view, flipp_pomodoro_view_timer_input_callback); - - return timer; -}; - -void flipp_pomodoro_view_timer_set_on_right_cb( - FlippPomodoroTimerView* timer, - FlippPomodoroTimerViewInputCb right_cb, - void* right_cb_ctx) { - furi_assert(right_cb); - furi_assert(right_cb_ctx); - timer->right_cb = right_cb; - timer->right_cb_ctx = right_cb_ctx; -}; - -void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state) { - furi_assert(view); - furi_assert(state); - with_view_model( - view, FlippPomodoroTimerViewModel * model, { model->state = state; }, false); - flipp_pomodoro_view_timer_assign_animation(view); -}; - -void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer) { - furi_assert(timer); - with_view_model( - timer->view, - FlippPomodoroTimerViewModel * model, - { icon_animation_free(model->icon); }, - false); - view_free(timer->view); - - free(timer); -}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h b/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h deleted file mode 100644 index 929a0eba3..000000000 --- a/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include "../modules/flipp_pomodoro.h" - -typedef struct FlippPomodoroTimerView FlippPomodoroTimerView; - -typedef void (*FlippPomodoroTimerViewInputCb)(void* ctx); - -FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc(); - -View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer); - -void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer); - -void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state); - -void flipp_pomodoro_view_timer_set_on_right_cb( - FlippPomodoroTimerView* timer, - FlippPomodoroTimerViewInputCb right_cb, - void* right_cb_ctx); diff --git a/applications/plugins/pomodoro/views/pomodoro_10.c b/applications/plugins/pomodoro/views/pomodoro_10.c new file mode 100644 index 000000000..f11f96d9f --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_10.c @@ -0,0 +1,46 @@ +#include "../pomodoro_timer.h" +#include "pomodoro_10.h" +#include +#include +#include +#include + +static void pomodoro_10_draw_callback(Canvas* canvas, void* context) { + int max_seconds = 60 * 10; + int max_seconds_rest = 60 * 2; + pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest); +} + +static bool pomodoro_10_input_callback(InputEvent* event, void* context) { + furi_assert(context); + PomodoroTimer* pomodoro_10 = context; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + return false; + } else { + pomodoro_timer_process(pomodoro_10, event); + return true; + } +} + +PomodoroTimer* pomodoro_10_alloc() { + PomodoroTimer* pomodoro_10 = malloc(sizeof(PomodoroTimer)); + pomodoro_10->view = view_alloc(); + view_set_context(pomodoro_10->view, pomodoro_10); + view_allocate_model(pomodoro_10->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel)); + view_set_draw_callback(pomodoro_10->view, pomodoro_10_draw_callback); + view_set_input_callback(pomodoro_10->view, pomodoro_10_input_callback); + + return pomodoro_10; +} + +void pomodoro_10_free(PomodoroTimer* pomodoro_10) { + furi_assert(pomodoro_10); + view_free(pomodoro_10->view); + free(pomodoro_10); +} + +View* pomodoro_10_get_view(PomodoroTimer* pomodoro_10) { + furi_assert(pomodoro_10); + return pomodoro_10->view; +} diff --git a/applications/plugins/pomodoro/views/pomodoro_10.h b/applications/plugins/pomodoro/views/pomodoro_10.h new file mode 100644 index 000000000..8f27e6bd6 --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_10.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "../pomodoro_timer.h" + +PomodoroTimer* pomodoro_10_alloc(); + +void pomodoro_10_free(PomodoroTimer* pomodoro_10); + +View* pomodoro_10_get_view(PomodoroTimer* pomodoro_10); diff --git a/applications/plugins/pomodoro/views/pomodoro_25.c b/applications/plugins/pomodoro/views/pomodoro_25.c new file mode 100644 index 000000000..01c5a7125 --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_25.c @@ -0,0 +1,46 @@ +#include "../pomodoro_timer.h" +#include "pomodoro_25.h" +#include +#include +#include +#include + +static void pomodoro_25_draw_callback(Canvas* canvas, void* context) { + int max_seconds = 60 * 25; + int max_seconds_rest = 60 * 5; + pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest); +} + +static bool pomodoro_25_input_callback(InputEvent* event, void* context) { + furi_assert(context); + PomodoroTimer* pomodoro_25 = context; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + return false; + } else { + pomodoro_timer_process(pomodoro_25, event); + return true; + } +} + +PomodoroTimer* pomodoro_25_alloc() { + PomodoroTimer* pomodoro_25 = malloc(sizeof(PomodoroTimer)); + pomodoro_25->view = view_alloc(); + view_set_context(pomodoro_25->view, pomodoro_25); + view_allocate_model(pomodoro_25->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel)); + view_set_draw_callback(pomodoro_25->view, pomodoro_25_draw_callback); + view_set_input_callback(pomodoro_25->view, pomodoro_25_input_callback); + + return pomodoro_25; +} + +void pomodoro_25_free(PomodoroTimer* pomodoro_25) { + furi_assert(pomodoro_25); + view_free(pomodoro_25->view); + free(pomodoro_25); +} + +View* pomodoro_25_get_view(PomodoroTimer* pomodoro_25) { + furi_assert(pomodoro_25); + return pomodoro_25->view; +} diff --git a/applications/plugins/pomodoro/views/pomodoro_25.h b/applications/plugins/pomodoro/views/pomodoro_25.h new file mode 100644 index 000000000..c3eb43976 --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_25.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "../pomodoro_timer.h" + +PomodoroTimer* pomodoro_25_alloc(); + +void pomodoro_25_free(PomodoroTimer* pomodoro_25); + +View* pomodoro_25_get_view(PomodoroTimer* pomodoro_25); diff --git a/applications/plugins/pomodoro/views/pomodoro_50.c b/applications/plugins/pomodoro/views/pomodoro_50.c new file mode 100644 index 000000000..74f89122a --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_50.c @@ -0,0 +1,46 @@ +#include "../pomodoro_timer.h" +#include "pomodoro_50.h" +#include +#include +#include +#include + +static void pomodoro_50_draw_callback(Canvas* canvas, void* context) { + int max_seconds = 60 * 50; + int max_seconds_rest = 60 * 10; + pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest); +} + +static bool pomodoro_50_input_callback(InputEvent* event, void* context) { + furi_assert(context); + PomodoroTimer* pomodoro_50 = context; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + return false; + } else { + pomodoro_timer_process(pomodoro_50, event); + return true; + } +} + +PomodoroTimer* pomodoro_50_alloc() { + PomodoroTimer* pomodoro_50 = malloc(sizeof(PomodoroTimer)); + pomodoro_50->view = view_alloc(); + view_set_context(pomodoro_50->view, pomodoro_50); + view_allocate_model(pomodoro_50->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel)); + view_set_draw_callback(pomodoro_50->view, pomodoro_50_draw_callback); + view_set_input_callback(pomodoro_50->view, pomodoro_50_input_callback); + + return pomodoro_50; +} + +void pomodoro_50_free(PomodoroTimer* pomodoro_50) { + furi_assert(pomodoro_50); + view_free(pomodoro_50->view); + free(pomodoro_50); +} + +View* pomodoro_50_get_view(PomodoroTimer* pomodoro_50) { + furi_assert(pomodoro_50); + return pomodoro_50->view; +} diff --git a/applications/plugins/pomodoro/views/pomodoro_50.h b/applications/plugins/pomodoro/views/pomodoro_50.h new file mode 100644 index 000000000..e0246d2d2 --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_50.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "../pomodoro_timer.h" + +PomodoroTimer* pomodoro_50_alloc(); + +void pomodoro_50_free(PomodoroTimer* pomodoro_50); + +View* pomodoro_50_get_view(PomodoroTimer* pomodoro_50); diff --git a/applications/plugins/rc2014_coleco/application.fam b/applications/plugins/rc2014_coleco/application.fam index 6856b716b..fe3ee028b 100644 --- a/applications/plugins/rc2014_coleco/application.fam +++ b/applications/plugins/rc2014_coleco/application.fam @@ -1,5 +1,5 @@ App( - appid="RC2014_Coleco", + appid="coleco", name="[RC2014] ColecoVision", apptype=FlipperAppType.EXTERNAL, entry_point="coleco_app", diff --git a/applications/plugins/rc2014_coleco/coleco.c b/applications/plugins/rc2014_coleco/coleco.c index 6ae050633..d5b1b0d46 100644 --- a/applications/plugins/rc2014_coleco/coleco.c +++ b/applications/plugins/rc2014_coleco/coleco.c @@ -2,7 +2,7 @@ #include #include #include -#include "RC2014_Coleco_icons.h" +#include "coleco_icons.h" #define CODE_0 0x0A #define CODE_1 0x0D @@ -40,16 +40,16 @@ typedef struct { } PluginEvent; typedef struct { + FuriMutex* mutex; bool dpad; int row; int column; } Coleco; static void render_callback(Canvas* const canvas, void* context) { - Coleco* coleco = acquire_mutex((ValueMutex*)context, 25); - if(coleco == NULL) { - return; - } + furi_assert(context); + Coleco* coleco = context; + furi_mutex_acquire(coleco->mutex, FuriWaitForever); if(coleco->dpad) { canvas_draw_icon(canvas, 4, 16, &I_ColecoJoystick_sel_33x33); @@ -128,7 +128,7 @@ static void render_callback(Canvas* const canvas, void* context) { (coleco->row == 4 && coleco->column == 2) ? &I_ColecoPound_hvr_17x17 : &I_ColecoPound_17x17); - release_mutex((ValueMutex*)context, coleco); + furi_mutex_release(coleco->mutex); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -191,8 +191,8 @@ int32_t coleco_app(void* p) { Coleco* coleco = coleco_alloc(); - ValueMutex coleco_mutex; - if(!init_mutex(&coleco_mutex, coleco, sizeof(Coleco))) { + coleco->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!coleco->mutex) { FURI_LOG_E("Coleco", "cannot create mutex\r\n"); coleco_free(coleco); return 255; @@ -200,7 +200,7 @@ int32_t coleco_app(void* p) { // set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &coleco_mutex); + view_port_draw_callback_set(view_port, render_callback, coleco); view_port_input_callback_set(view_port, input_callback, event_queue); // open GUI and register view_port @@ -214,7 +214,7 @@ int32_t coleco_app(void* p) { for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - Coleco* coleco = (Coleco*)acquire_mutex_block(&coleco_mutex); + furi_mutex_acquire(coleco->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -346,11 +346,9 @@ int32_t coleco_app(void* p) { view_port_update(view_port); } - } else { - FURI_LOG_D("Coleco", "FuriMessageQueue: event timeout"); } - release_mutex(&coleco_mutex, coleco); + furi_mutex_release(coleco->mutex); } furi_hal_power_disable_otg(); @@ -360,7 +358,7 @@ int32_t coleco_app(void* p) { furi_record_close("gui"); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&coleco_mutex); + furi_mutex_free(coleco->mutex); coleco_free(coleco); return 0; } diff --git a/applications/plugins/sam/application.fam b/applications/plugins/sam/application.fam index 100840482..1c49eb236 100644 --- a/applications/plugins/sam/application.fam +++ b/applications/plugins/sam/application.fam @@ -1,7 +1,7 @@ App( appid="SAM", name="SAM AYBABTU", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.PLUGIN, entry_point="sam_app", requires=[ "gui", @@ -9,7 +9,7 @@ App( ], stack_size=4 * 1024, order=20, - fap_icon="icons/music_10px.png", + fap_icon="music_10px.png", fap_category="Music", fap_icon_assets="icons", ) @@ -24,7 +24,7 @@ App( ], stack_size=4 * 1024, order=20, - fap_icon="icons/music_10px.png", + fap_icon="music_10px.png", fap_category="Music", fap_icon_assets="icons", ) @@ -39,7 +39,7 @@ App( ], stack_size=4 * 1024, order=20, - fap_icon="icons/music_10px.png", + fap_icon="music_10px.png", fap_category="Music", fap_icon_assets="icons", ) @@ -54,7 +54,7 @@ App( ], stack_size=4 * 1024, order=20, - fap_icon="icons/music_10px.png", + fap_icon="music_10px.png", fap_category="Music", fap_icon_assets="icons", ) diff --git a/applications/plugins/sam/music_10px.png b/applications/plugins/sam/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/applications/plugins/sam/music_10px.png differ diff --git a/applications/plugins/sam/sam_app.cpp b/applications/plugins/sam/sam_app.cpp index c46c6d3a2..81adf3093 100644 --- a/applications/plugins/sam/sam_app.cpp +++ b/applications/plugins/sam/sam_app.cpp @@ -6,7 +6,7 @@ STM32SAM voice; extern "C" int32_t sam_app(void* p) { UNUSED(p); - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { voice.begin(); voice.say( "All your base are belong to us. You have no chance to survive make your time. ha. ha. ha. GOOD BYE. "); @@ -17,7 +17,7 @@ extern "C" int32_t sam_app(void* p) { extern "C" int32_t sam_app_yes(void* p) { UNUSED(p); - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { voice.begin(); voice.say("Yes"); furi_hal_speaker_release(); @@ -27,7 +27,7 @@ extern "C" int32_t sam_app_yes(void* p) { extern "C" int32_t sam_app_no(void* p) { UNUSED(p); - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { voice.begin(); voice.say("No"); furi_hal_speaker_release(); @@ -37,10 +37,10 @@ extern "C" int32_t sam_app_no(void* p) { extern "C" int32_t sam_app_wtf(void* p) { UNUSED(p); - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { voice.begin(); voice.say("What The Fuck"); furi_hal_speaker_release(); } return 0; -} \ No newline at end of file +} diff --git a/applications/plugins/tama_p1/README.md b/applications/plugins/tama_p1/README.md index 0c06eb712..cb58931cc 100644 --- a/applications/plugins/tama_p1/README.md +++ b/applications/plugins/tama_p1/README.md @@ -8,27 +8,32 @@ This is a tama P1 Emulator app for Flipper Zero, based on [TamaLIB](https://gith How to play ----------- Create a `tama_p1` folder in your microSD card, and put the ROM as `rom.bin`. -Use a search engine to find the Tamagotchi ROM. There is a file named `a`. +Use a search engine to find the Tamagotchi ROM. There is a file named `tama.b`. Rename this to `rom.bin`. +*Controls in portrait mode are the same as landscape mode, but turned 90 degrees.* - Left button is A. -- OK is B. +- Down or OK is B. - Right button is C. -- Holding the Up button functions the same as press both A and C, which mutes the volume. +- Up button takes you to the emulator menu. - Hold the Back button to save and exit. +![Alt Text](Screenshot1.png) +![Alt Text](Screenshot2.png) Building -------- -Move this folder into flippers applications/plugins/tama_p1. +Move this folder into flippers `applications/plugins/tama_p1`. Launching the app, directly from console to flipper: -`./fbt launch_app APPSRC=applications\plugins\tama_p1` +``` +./fbt launch_app APPSRC=applications/plugins/tama_p1 +``` Run the following to compile icons: ``` -scripts/assets.py icons applications/tama_p1/icons applications/tama_p1/compiled +scripts/assets.py icons applications/plugins/tama_p1/icons applications/plugins/tama_p1/compiled ``` Note: you may also need to add `-Wno-unused-parameter` to `CCFLAGS` in @@ -38,22 +43,34 @@ Debugging --------- Using the serial script from [FlipperScripts](https://github.com/DroomOne/FlipperScripts/blob/main/serial_logger.py) it is easy to add direct logging after running the application: +``` `python .\serial_logger.py` -`./fbt launch_app APPSRC=applications\plugins\tama_p1; python .\serial_logger.py` - +`./fbt launch_app APPSRC=applications/plugins/tama_p1; python .\serial_logger.py` +``` +Alternatively, follow the directions here: https://flipper.atmanos.com/docs/debugging/viewing/ Implemented ----------- -- Basic emulation -- Input -- Sound -- Saving/Loading emulator state (stored in `/ext/tama_p1/save.bin`) -- Mute button combo shortcut (Up = A+C) +- Menu options: + - Switch between portrait and landscape + - A+C shortcut (mute/change in-game time) + - Double / quadruple speed -To-do +![Alt Text](Screenshot3.png) + +To-Do ----- -- more than one save slot -- In-game reset -- Test mode? -- Volume adjustment +- Fix bugs: + - When not on 1x speed, after mashing buttons in quick succession, buttons stop responding for a few seconds. But the rom still runs. +- Stuff to do when bored: + - optimization and bug fixing (see above) + - add to this list + - portrait menu + - Add "loading bar" when saving + - "Advanced" settings + - saving and loading, multiple save states, with the date and time of of each save. + - Autosave and changing autosave frequency + - Save settings to /tama_p1/settings.txt + +![Alt Text](Screenshot4.png) diff --git a/applications/plugins/tama_p1/application.fam b/applications/plugins/tama_p1/application.fam index 2f089a54b..93ee53aa5 100644 --- a/applications/plugins/tama_p1/application.fam +++ b/applications/plugins/tama_p1/application.fam @@ -5,7 +5,7 @@ App( entry_point="tama_p1_app", cdefines=["APP_TAMA_P1"], requires=["gui", "storage"], - stack_size=1 * 1024, + stack_size=2 * 1024, order=215, fap_icon="tamaIcon.png", fap_category="Games", diff --git a/applications/plugins/tama_p1/tama_p1.c b/applications/plugins/tama_p1/tama_p1.c index 0e7686a06..1d7939a01 100644 --- a/applications/plugins/tama_p1/tama_p1.c +++ b/applications/plugins/tama_p1/tama_p1.c @@ -10,6 +10,21 @@ TamaApp* g_ctx; FuriMutex* g_state_mutex; +uint8_t layout_mode = 0; // 3: portrait => 4: portrait <= +// 0: landscape (small) 1: landscape (big) 2: landscape (full) +bool in_menu = false; + +uint8_t speed = 1; +const uint8_t speed_options[] = {1, 2, 4}; +const uint8_t min_speed = 1; +const uint8_t max_speed = 4; +const uint8_t speed_options_size = 3; +// = sizeof(speed_options) / sizeof(speed_options[0]); + +uint8_t menu_cursor = 3; // 0: layout mode; 1: speed; 2: A+C; +const uint8_t menu_items = 4; // 3: Close menu, Save & Exit +uint8_t sub_menu_buttons = 0; +uint8_t sub_menu_last = 0; static const Icon* icons_list[] = { &I_icon_0, @@ -22,6 +37,384 @@ static const Icon* icons_list[] = { &I_icon_7, }; +static InputKey m = InputKeyUp; +static InputKey a = InputKeyLeft; +static InputKey b = InputKeyDown; +static InputKey c = InputKeyRight; + +static void speed_up() { + switch(speed) { + case max_speed: + speed = speed_options[0]; + break; + default: + for(uint8_t i = 0; i < speed_options_size - 1; i++) { + if(speed == speed_options[i]) { + speed = speed_options[i + 1]; + break; + } + } + break; + } + tamalib_set_speed(speed); +} +static void speed_down() { + switch(speed) { + case min_speed: + speed = speed_options[speed_options_size - 1]; + break; + default: + for(uint8_t i = speed_options_size - 1; i > 0; i--) { + if(speed == speed_options[i]) { + speed = speed_options[i - 1]; + break; + } + } + break; + } + tamalib_set_speed(speed); +} + +// static void draw_landscape(Canvas* const canvas, void* cb_ctx) +static void draw_landscape(Canvas* const canvas, uint8_t scale) { + // FURI_LOG_D(TAG, "Drawing frame"); + // Calculate positioning + uint16_t canv_width = canvas_width(canvas); + uint16_t canv_height = canvas_height(canvas); + uint16_t lcd_matrix_scaled_width = 32 * scale; + uint16_t lcd_matrix_scaled_height = 16 * scale; + uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2; // 0 + uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2; + + // uint16_t lcd_icon_upper_top = lcd_matrix_top - TAMA_LCD_ICON_SIZE - TAMA_LCD_ICON_MARGIN; + // uint16_t lcd_icon_lower_top = lcd_matrix_top + lcd_matrix_scaled_height + TAMA_LCD_ICON_MARGIN; + uint16_t lcd_icon_upper_left = lcd_matrix_left; + uint16_t lcd_icon_lower_left = lcd_matrix_left; + uint16_t lcd_icon_spacing_horiz = + (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; + + uint16_t y = lcd_matrix_top; + for(uint8_t row = 0; row < 16; ++row) { + uint16_t x = lcd_matrix_left; + uint32_t row_pixels = g_ctx->framebuffer[row]; + for(uint8_t col = 0; col < 32; ++col) { + if(row_pixels & 1) { + canvas_draw_box(canvas, x, y, scale, scale); + } + x += scale; + row_pixels >>= 1; + } + y += scale; + } + + // Start drawing icons + uint8_t lcd_icons = g_ctx->icons; + + // Draw top icons + y = 0; + uint16_t x_ic = lcd_icon_upper_left; + for(uint8_t i = 0; i < 4; ++i) { + if(lcd_icons & 1) { + canvas_draw_icon(canvas, x_ic, y, icons_list[i]); + } + // x_ic += TAMA_LCD_ICON_SIZE + 4; + // if(scale == 3) { + // y += 16; + // } else { + x_ic += lcd_icon_spacing_horiz; + // } + lcd_icons >>= 1; + } + + // Draw bottom icons + y = 64 - TAMA_LCD_ICON_SIZE; + // if(scale == 3) { + // y = 0; + // x_ic = 128 - TAMA_LCD_ICON_SIZE; + // x_ic = 0; + // } else { + // y = 64 - TAMA_LCD_ICON_SIZE; + x_ic = lcd_icon_lower_left; + // } + for(uint8_t i = 4; i < 8; ++i) { + // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); + if(lcd_icons & 1) { + canvas_draw_icon(canvas, x_ic, y, icons_list[i]); + } + // if(scale == 3) { + // y += 16; + // } else { + x_ic += lcd_icon_spacing_horiz; + // } + lcd_icons >>= 1; + } +} +// static void draw_portrait_right(Canvas* const canvas, void* cb_ctx) +static void draw_portrait_right(Canvas* const canvas, uint8_t scale) { + // FURI_LOG_D(TAG, "Drawing frame"); + // Calculate positioning + // uint16_t canv_width = canvas_width(canvas); + uint16_t canv_height = canvas_height(canvas); + uint16_t lcd_matrix_scaled_width = 32 * scale; + uint16_t lcd_matrix_scaled_height = 16 * scale; + // uint16_t lcd_matrix_top = 0; + uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2; + // uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2; + uint16_t lcd_matrix_left = 64 - TAMA_LCD_ICON_SIZE; + uint16_t lcd_icon_upper_left = lcd_matrix_left; + uint16_t lcd_icon_lower_left = lcd_matrix_left; + uint16_t lcd_icon_spacing_horiz = + (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; + + uint16_t y = lcd_matrix_top; // 64 + for(uint8_t row = 0; row < 16; ++row) { + uint16_t x = 128; // lcd_matrix_left + uint32_t row_pixels = g_ctx->framebuffer[row]; + for(uint8_t col = 0; col < 32; ++col) { + if(row_pixels & 1) { + canvas_draw_box(canvas, y + 32, x - 66, scale, scale); + } + x -= scale; + row_pixels >>= 1; + } + y += scale; + } + + // Start drawing icons + uint8_t lcd_icons = g_ctx->icons; + + // Draw top icons + // y = lcd_icon_upper_top; + y = 30; + // y = 64 - TAMA_LCD_ICON_SIZE; + uint16_t x_ic = lcd_icon_upper_left; + // uint16_t x_ic = 64 - TAMA_LCD_ICON_SIZE; + for(uint8_t i = 0; i < 4; ++i) { + if(lcd_icons & 1) { + canvas_draw_icon(canvas, y, x_ic, icons_list[i]); + } + x_ic -= lcd_icon_spacing_horiz; // TAMA_LCD_ICON_SIZE + 4; + lcd_icons >>= 1; + } + + // Draw bottom icons + y = 84; // lcd_icon_lower_top + x_ic = lcd_icon_lower_left; // 64 - TAMA_LCD_ICON_SIZE + for(uint8_t i = 4; i < 8; ++i) { + // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); + if(lcd_icons & 1) { + canvas_draw_icon(canvas, y, x_ic, icons_list[i]); + } + x_ic -= lcd_icon_spacing_horiz; + lcd_icons >>= 1; + } +} +static void draw_portrait_left(Canvas* const canvas, uint8_t scale) { + // FURI_LOG_D(TAG, "Drawing frame"); + // Calculate positioning + // uint16_t canv_width = canvas_width(canvas); + // uint16_t canv_height = canvas_height(canvas); + uint16_t lcd_matrix_scaled_width = 32 * scale; + // uint16_t lcd_matrix_scaled_height = 16 * scale; + // uint16_t lcd_matrix_top = 0; + // uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2; + // uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2; + uint16_t lcd_matrix_left = 0; + uint16_t lcd_icon_upper_left = lcd_matrix_left; + uint16_t lcd_icon_lower_left = lcd_matrix_left; + uint16_t lcd_icon_spacing_horiz = + (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; + + // uint16_t y = 64 + lcd_matrix_top; + uint16_t y = 64; + for(uint8_t row = 0; row < 16; ++row) { + uint16_t x = 0; // lcd_matrix_left + uint32_t row_pixels = g_ctx->framebuffer[row]; + for(uint8_t col = 0; col < 32; ++col) { + if(row_pixels & 1) { + canvas_draw_box(canvas, y, x, scale, scale); + } + x += scale; + row_pixels >>= 1; + } + y -= scale; + } + + // Start drawing icons + uint8_t lcd_icons = g_ctx->icons; + + // Draw top icons + // y = lcd_icon_upper_top; + y = 70; + // y = 64 - TAMA_LCD_ICON_SIZE; + uint16_t x_ic = lcd_icon_upper_left; + // uint16_t x_ic = 64 - TAMA_LCD_ICON_SIZE; + for(uint8_t i = 0; i < 4; ++i) { + if(lcd_icons & 1) { + canvas_draw_icon(canvas, y, x_ic, icons_list[i]); + } + x_ic += lcd_icon_spacing_horiz; // TAMA_LCD_ICON_SIZE + 4; + lcd_icons >>= 1; + } + + // Draw bottom icons + y = 16; // lcd_icon_lower_top + x_ic = lcd_icon_lower_left; // 64 - TAMA_LCD_ICON_SIZE + for(uint8_t i = 4; i < 8; ++i) { + // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); + if(lcd_icons & 1) { + canvas_draw_icon(canvas, y, x_ic, icons_list[i]); + } + x_ic += lcd_icon_spacing_horiz; + lcd_icons >>= 1; + } +} +// static void draw_mini(Canvas* const canvas, uint16_t inX, uint16_t inY) +static void draw_mini(Canvas* const canvas) { + // Calculate positioning + // uint16_t y = inY; + const uint16_t y = 34; + const uint16_t x = 84; + + uint16_t y1 = y; + for(uint8_t row = 0; row < 16; ++row) { + // uint16_t x = inX; + uint16_t x1 = x; + uint32_t row_pixels = g_ctx->framebuffer[row]; + for(uint8_t col = 0; col < 32; ++col) { + if(row_pixels & 1) { + canvas_draw_dot(canvas, x1, y1); + } + x1 += 1; + row_pixels >>= 1; + } + y1 += 1; + } + + // Start drawing icons + uint8_t lcd_icons = g_ctx->icons; + + // Draw top icons + uint16_t y2 = y - 2; + uint16_t x_ic = x; + for(uint8_t i = 0; i < 4; ++i) { + if(lcd_icons & 1) { + // canvas_draw_icon(canvas, x_ic, y, icons_list[i]); + canvas_draw_line(canvas, x_ic, y2, x_ic + 6, y2); + } + x_ic += 8; + lcd_icons >>= 1; + } + + // Draw bottom icons + y2 = y + 17; + x_ic = x; + for(uint8_t i = 4; i < 8; ++i) { + // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); + if(lcd_icons & 1) { + // canvas_draw_icon(canvas, x_ic, y, icons_list[i]); + canvas_draw_line(canvas, x_ic, y2, x_ic + 6, y2); + } + x_ic += 8; + lcd_icons >>= 1; + } +} + +static void draw_menu(Canvas* const canvas) { + canvas_draw_frame(canvas, 0, 0, 128, 64); + canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignCenter, "Menu"); + canvas_draw_line(canvas, 0, 10, 128, 10); + draw_mini(canvas); + // draw_mini(canvas, 34, 84); + + switch(menu_cursor) { + case 0: + canvas_draw_triangle(canvas, 4, 16, 6, 6, CanvasDirectionLeftToRight); + break; + case 1: + canvas_draw_triangle(canvas, 4, 26, 6, 6, CanvasDirectionLeftToRight); + break; + case 2: + switch(sub_menu_buttons) { + case 0: + canvas_draw_triangle(canvas, 4, 36, 6, 6, CanvasDirectionLeftToRight); + break; + case 1: + canvas_draw_triangle(canvas, 47, 44, 6, 4, CanvasDirectionBottomToTop); + break; + case 2: + canvas_draw_triangle(canvas, 57, 44, 6, 4, CanvasDirectionBottomToTop); + break; + case 3: + canvas_draw_triangle(canvas, 67, 44, 6, 4, CanvasDirectionBottomToTop); + break; + default: + break; + } + break; + case menu_items - 1: + switch(sub_menu_last) { + case 0: + canvas_draw_triangle(canvas, 4, 56, 6, 6, CanvasDirectionLeftToRight); + break; + case 1: + canvas_draw_triangle(canvas, 36, 56, 6, 6, CanvasDirectionLeftToRight); + break; + case 2: + canvas_draw_triangle(canvas, 67, 56, 6, 6, CanvasDirectionLeftToRight); + break; + default: + break; + } + break; + } + switch(layout_mode) { + case 0: + canvas_draw_str(canvas, 12, 20, "Layout: Landscape (small)"); + break; + case 1: + canvas_draw_str(canvas, 12, 20, "Layout: Landscape (big)"); + break; + case 2: + canvas_draw_str(canvas, 12, 20, "Layout: Landscape (full)"); + break; + case 3: + canvas_draw_str(canvas, 12, 20, "Layout: Portrait =>"); + break; + case 4: + canvas_draw_str(canvas, 12, 20, "Layout: Portrait <="); + break; + default: + canvas_draw_str(canvas, 12, 20, "Layout: ???"); + break; + } + switch(speed) { // match with speed_options + // case 0: // freeze menu too + // canvas_draw_str(canvas, 12, 30, "Speed: 0x"); + // break; + case 1: + canvas_draw_str(canvas, 12, 30, "Speed: 1x"); + break; + case 2: + canvas_draw_str(canvas, 12, 30, "Speed: 2x"); + break; + case 4: + canvas_draw_str(canvas, 12, 30, "Speed: 4x (max)"); + break; + default: + canvas_draw_str(canvas, 12, 30, "Speed ??x"); + break; + } + canvas_draw_str(canvas, 12, 40, "A+C"); + canvas_draw_str(canvas, 45, 40, "A"); + canvas_draw_str(canvas, 55, 40, "B"); + canvas_draw_str(canvas, 65, 40, "C"); + + canvas_draw_str(canvas, 12, 60, "Close"); + canvas_draw_str(canvas, 44, 60, "Save"); + canvas_draw_str(canvas, 75, 60, "Save & Exit"); +} + static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) { furi_assert(cb_ctx); @@ -35,68 +428,32 @@ static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) { canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 30, 30, "Halted"); } else { - // FURI_LOG_D(TAG, "Drawing frame"); - // Calculate positioning - uint16_t canv_width = canvas_width(canvas); - uint16_t canv_height = canvas_height(canvas); - uint16_t lcd_matrix_scaled_width = 32 * TAMA_SCREEN_SCALE_FACTOR; - uint16_t lcd_matrix_scaled_height = 16 * TAMA_SCREEN_SCALE_FACTOR; - // uint16_t lcd_matrix_top = 0; - uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2; - uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2; - - uint16_t lcd_icon_upper_top = lcd_matrix_top - TAMA_LCD_ICON_SIZE - TAMA_LCD_ICON_MARGIN; - uint16_t lcd_icon_upper_left = lcd_matrix_left; - uint16_t lcd_icon_lower_top = - lcd_matrix_top + lcd_matrix_scaled_height + TAMA_LCD_ICON_MARGIN; - uint16_t lcd_icon_lower_left = lcd_matrix_left; - uint16_t lcd_icon_spacing_horiz = - (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; - - uint16_t y = lcd_matrix_top; - for(uint8_t row = 0; row < 16; ++row) { - uint16_t x = lcd_matrix_left; - uint32_t row_pixels = g_ctx->framebuffer[row]; - for(uint8_t col = 0; col < 32; ++col) { - if(row_pixels & 1) { - canvas_draw_box( - canvas, x, y, TAMA_SCREEN_SCALE_FACTOR, TAMA_SCREEN_SCALE_FACTOR); - } - x += TAMA_SCREEN_SCALE_FACTOR; - row_pixels >>= 1; + if(in_menu) { + // switch(layout_mode) + // draw_menu_landscape(canvas); + draw_menu(canvas); + } else { + switch(layout_mode) { + case 0: + draw_landscape(canvas, TAMA_SCREEN_SCALE_FACTOR); // 2 + break; + case 1: + draw_landscape(canvas, 3); + break; + case 2: + draw_landscape(canvas, 4); + break; + case 3: + draw_portrait_right(canvas, TAMA_SCREEN_SCALE_FACTOR); + break; + case 4: + draw_portrait_left(canvas, TAMA_SCREEN_SCALE_FACTOR); + break; + default: + break; } - y += TAMA_SCREEN_SCALE_FACTOR; - } - - // Start drawing icons - uint8_t lcd_icons = g_ctx->icons; - - // Draw top icons - y = lcd_icon_upper_top; - // y = 64 - TAMA_LCD_ICON_SIZE; - uint16_t x_ic = lcd_icon_upper_left; - for(uint8_t i = 0; i < 4; ++i) { - if(lcd_icons & 1) { - canvas_draw_icon(canvas, x_ic, y, icons_list[i]); - } - // x_ic += TAMA_LCD_ICON_SIZE + 4; - x_ic += lcd_icon_spacing_horiz; - lcd_icons >>= 1; - } - - // Draw bottom icons - y = lcd_icon_lower_top; - x_ic = lcd_icon_lower_left; - for(uint8_t i = 4; i < 8; ++i) { - // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); - if(lcd_icons & 1) { - canvas_draw_icon(canvas, x_ic, y, icons_list[i]); - } - x_ic += lcd_icon_spacing_horiz; - lcd_icons >>= 1; } } - furi_mutex_release(mutex); } @@ -345,9 +702,9 @@ static int32_t tama_p1_worker(void* context) { if(furi_thread_flags_get()) { running = false; } else { - // FURI_LOG_D(TAG, "Stepping"); + // FURI_LOG_D(TAG, "Stepping"); // enabling this cause blank screen somehow // for (int i = 0; i < 100; ++i) - tamalib_step(); + tamalib_step(); // tamalib_mainloop(); } } LL_TIM_DisableCounter(TIM2); @@ -406,15 +763,15 @@ static void tama_p1_init(TamaApp* const ctx) { // Init TamaLIB tamalib_register_hal(&ctx->hal); tamalib_init((u12_t*)ctx->rom, NULL, 64000); - tamalib_set_speed(1); + tamalib_set_speed(speed); // TODO: implement fast forwarding - ctx->fast_forward_done = true; + // ctx->fast_forward_done = true; // Start stepping thread ctx->thread = furi_thread_alloc(); furi_thread_set_name(ctx->thread, "TamaLIB"); - furi_thread_set_stack_size(ctx->thread, 1024); + furi_thread_set_stack_size(ctx->thread, 2 * 1024); furi_thread_set_callback(ctx->thread, tama_p1_worker); furi_thread_set_context(ctx->thread, g_state_mutex); furi_thread_start(ctx->thread); @@ -449,6 +806,9 @@ int32_t tama_p1_app(void* p) { furi_timer_alloc(tama_p1_update_timer_callback, FuriTimerTypePeriodic, event_queue); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30); + // in_menu = false; + // menu_cursor = 2; + for(bool running = true; running;) { TamaEvent event; FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); @@ -466,51 +826,353 @@ int32_t tama_p1_app(void* p) { event.input.sequence, event.input.key, event.input.type); - InputType input_type = event.input.type; - if(input_type == InputTypePress || input_type == InputTypeRelease) { - btn_state_t tama_btn_state = 0; - if(input_type == InputTypePress) - tama_btn_state = BTN_STATE_PRESSED; - else if(input_type == InputTypeRelease) - tama_btn_state = BTN_STATE_RELEASED; + // InputType input_type = event.input.type; // idk why this is a variable + btn_state_t tama_btn_state = 0; // BTN_STATE_RELEASED is 0 - if(event.input.key == InputKeyLeft) { - tamalib_set_button(BTN_LEFT, tama_btn_state); + if(in_menu) { + // if(menu_cursor == 2 && + // (event.input.key == InputKeyUp || event.input.key == InputKeyDown)) { + // tama_btn_state = BTN_STATE_RELEASED; + // } + if(event.input.key == InputKeyBack) { + tama_btn_state = BTN_STATE_RELEASED; + in_menu = false; + } else if(event.input.key == InputKeyUp && event.input.type == InputTypePress) { + tama_btn_state = BTN_STATE_RELEASED; + if(menu_cursor > 0) { + menu_cursor -= 1; + } else { + menu_cursor = menu_items - 1; + } + if(menu_cursor >= menu_items - 2 && sub_menu_last > 0) { + sub_menu_buttons = 1; + sub_menu_last = 1; + } else { + sub_menu_buttons = 0; + sub_menu_last = 0; + } + } else if(event.input.key == InputKeyDown && event.input.type == InputTypePress) { + tama_btn_state = BTN_STATE_RELEASED; + if(menu_cursor < menu_items - 1) { + menu_cursor += 1; + } else { + menu_cursor = 0; + } + if(menu_cursor >= menu_items - 2 && sub_menu_buttons > 0) { + sub_menu_buttons = 1; + sub_menu_last = 1; + } else { + sub_menu_buttons = 0; + sub_menu_last = 0; + } + } else if(event.input.key == InputKeyLeft && event.input.type == InputTypePress) { + switch(menu_cursor) { + case 0: + switch(layout_mode) { + case 0: + layout_mode = 4; + m = InputKeyRight; + a = InputKeyUp; + b = InputKeyLeft; + c = InputKeyDown; + break; + case 1: + layout_mode -= 1; // 0 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 2: + layout_mode -= 1; // 1 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 3: + layout_mode -= 1; // 2 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 4: + layout_mode -= 1; // 3 + m = InputKeyLeft; + a = InputKeyDown; + b = InputKeyRight; + c = InputKeyUp; + break; + } + break; + case 1: + speed_down(); + break; + case 2: + tama_btn_state = BTN_STATE_RELEASED; + switch(sub_menu_buttons) { + case 0: + sub_menu_buttons = 3; + break; + case 1: + case 2: + case 3: + sub_menu_buttons -= 1; + break; + default: + break; + } + break; + case menu_items - 1: + switch(sub_menu_last) { + case 0: + sub_menu_last = 2; + break; + case 1: + case 2: + sub_menu_last -= 1; + break; + default: + break; + } + break; + default: + break; + } + } else if(event.input.key == InputKeyRight && event.input.type == InputTypePress) { + switch(menu_cursor) { + case 0: + switch(layout_mode) { + case 0: + layout_mode += 1; // 1 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 1: + layout_mode += 1; // 2 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 2: + layout_mode += 1; // 3 + m = InputKeyLeft; + a = InputKeyDown; + b = InputKeyRight; + c = InputKeyUp; + break; + case 3: + layout_mode += 1; // 4 + m = InputKeyRight; + a = InputKeyUp; + b = InputKeyLeft; + c = InputKeyDown; + break; + case 4: + layout_mode = 0; + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + } + break; + case 1: + speed_up(); + break; + case 2: + tama_btn_state = BTN_STATE_RELEASED; + switch(sub_menu_buttons) { + case 0: + case 1: + case 2: + sub_menu_buttons += 1; + break; + case 3: + sub_menu_buttons = 0; + break; + default: + break; + } + break; + case menu_items - 1: + switch(sub_menu_last) { + case 0: + case 1: + sub_menu_last += 1; + break; + case 2: + sub_menu_last = 0; + break; + default: + break; + } + break; + default: + break; + } } else if(event.input.key == InputKeyOk) { - tamalib_set_button(BTN_MIDDLE, tama_btn_state); - } else if(event.input.key == InputKeyRight) { - tamalib_set_button(BTN_RIGHT, tama_btn_state); - } else if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) { - // TODO: pause or fast-forward tamagotchi - tama_p1_save_state(); - } else if(event.input.key == InputKeyUp) { // mute tamagotchi - tamalib_set_button(BTN_LEFT, tama_btn_state); - tamalib_set_button(BTN_RIGHT, tama_btn_state); - } else if(event.input.key == InputKeyBack && event.input.type == InputTypeShort) { + switch(menu_cursor) { + case 0: + if(event.input.type == InputTypePress) { + switch(layout_mode) { + case 0: + layout_mode += 1; // 1 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 1: + layout_mode += 1; // 2 + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + case 2: + layout_mode += 1; // 3 + m = InputKeyLeft; + a = InputKeyDown; + b = InputKeyRight; + c = InputKeyUp; + break; + case 3: + layout_mode += 1; // 4 + m = InputKeyRight; + a = InputKeyUp; + b = InputKeyLeft; + c = InputKeyDown; + break; + case 4: + layout_mode = 0; + m = InputKeyUp; + a = InputKeyLeft; + b = InputKeyDown; + c = InputKeyRight; + break; + } + } + break; + case 1: + if(event.input.type == InputTypePress) { + speed_up(); + } + break; + case 2: + if(event.input.type == InputTypePress) + tama_btn_state = BTN_STATE_PRESSED; + else if(event.input.type == InputTypeRelease) + tama_btn_state = BTN_STATE_RELEASED; + + switch(sub_menu_buttons) { + case 0: // A+C + tamalib_set_button(BTN_LEFT, tama_btn_state); + tamalib_set_button(BTN_RIGHT, tama_btn_state); + break; + case 1: // A + tamalib_set_button(BTN_LEFT, tama_btn_state); + break; + case 2: // B + tamalib_set_button(BTN_MIDDLE, tama_btn_state); + break; + case 3: // C + tamalib_set_button(BTN_RIGHT, tama_btn_state); + break; + default: + break; + } + break; + case menu_items - 1: + default: + if(event.input.type == InputTypePress) { + switch(sub_menu_last) { + case 0: // close menu + in_menu = false; + break; + case 1: // Save + if(speed != 1) { + uint8_t temp = speed; + speed = 1; + tamalib_set_speed(speed); + furi_timer_stop(timer); + tama_p1_save_state(); + furi_timer_start( + timer, furi_kernel_get_tick_frequency() / 30); + speed = temp; + tamalib_set_speed(speed); + } else { + furi_timer_stop(timer); + tama_p1_save_state(); + furi_timer_start( + timer, furi_kernel_get_tick_frequency() / 30); + } + break; + case 2: // Save & Exit + if(speed != 1) { + speed = 1; + tamalib_set_speed(speed); + } + furi_timer_stop(timer); + tama_p1_save_state(); + running = false; + break; + default: + break; + } + } + break; + } + } + } else { // out of menu // TODO: clean up code -.- + if(event.input.key == InputKeyBack && event.input.type == InputTypeLong) { + if(speed != 1) { + speed = 1; + tamalib_set_speed(speed); + } + furi_timer_stop(timer); tama_p1_save_state(); + running = false; + } else if( + event.input.type == InputTypePress || + event.input.type == InputTypeRelease) { + if(event.input.key != InputKeyBack && event.input.key != m) { + if(event.input.type == InputTypePress) + tama_btn_state = BTN_STATE_PRESSED; + else if(event.input.type == InputTypeRelease) + tama_btn_state = BTN_STATE_RELEASED; + } else { + tama_btn_state = BTN_STATE_RELEASED; + } + if(event.input.key == m && event.input.type == InputTypePress) { + in_menu = true; + } else if(event.input.key == a) { + tamalib_set_button(BTN_LEFT, tama_btn_state); + } else if(event.input.key == b) { + tamalib_set_button(BTN_MIDDLE, tama_btn_state); + } else if(event.input.key == c) { + tamalib_set_button(BTN_RIGHT, tama_btn_state); + } else if(event.input.key == InputKeyOk) { + tamalib_set_button(BTN_MIDDLE, tama_btn_state); + } } } - - if(event.input.key == InputKeyBack && event.input.type == InputTypeLong) { - furi_timer_stop(timer); - running = false; - - tama_p1_save_state(); - } } - furi_mutex_release(g_state_mutex); - } else { - // Timeout - // FURI_LOG_D(TAG, "Timed out"); } + // else { + // // Timeout + // FURI_LOG_D(TAG, "Timed out"); + // } } - if(ctx->rom != NULL) { furi_thread_flags_set(furi_thread_get_id(ctx->thread), 1); furi_thread_join(ctx->thread); } - furi_timer_free(timer); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); @@ -520,6 +1182,5 @@ int32_t tama_p1_app(void* p) { furi_mutex_free(g_state_mutex); tama_p1_deinit(ctx); free(ctx); - return 0; } diff --git a/applications/plugins/tanksgame/tanks_game.c b/applications/plugins/tanksgame/tanks_game.c index 3d67dfbb0..14ea1ce52 100644 --- a/applications/plugins/tanksgame/tanks_game.c +++ b/applications/plugins/tanksgame/tanks_game.c @@ -105,6 +105,7 @@ typedef struct { uint8_t sent; PlayerState* p1; PlayerState* p2; + FuriMutex* mutex; } TanksState; typedef enum { @@ -388,10 +389,9 @@ void tanks_game_deserialize_and_render(unsigned char* data, Canvas* const canvas } static void tanks_game_render_callback(Canvas* const canvas, void* ctx) { - const TanksState* tanks_state = acquire_mutex((ValueMutex*)ctx, 25); - if(tanks_state == NULL) { - return; - } + furi_assert(ctx); + const TanksState* tanks_state = ctx; + furi_mutex_acquire(tanks_state->mutex, FuriWaitForever); // Before the function is called, the state is set with the canvas_reset(canvas) if(tanks_state->state == GameStateMenu) { @@ -415,7 +415,7 @@ static void tanks_game_render_callback(Canvas* const canvas, void* ctx) { canvas_draw_frame(canvas, 0, 0, 128, 64); - release_mutex((ValueMutex*)ctx, tanks_state); + furi_mutex_release(tanks_state->mutex); return; } @@ -432,7 +432,7 @@ static void tanks_game_render_callback(Canvas* const canvas, void* ctx) { tanks_game_render_constant_cells(canvas); - release_mutex((ValueMutex*)ctx, tanks_state); + furi_mutex_release(tanks_state->mutex); return; } @@ -615,7 +615,7 @@ static void tanks_game_render_callback(Canvas* const canvas, void* ctx) { tanks_game_deserialize_and_render(data, canvas); // TEST enf - release_mutex((ValueMutex*)ctx, tanks_state); + furi_mutex_release(tanks_state->mutex); } static void tanks_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -1188,8 +1188,8 @@ int32_t tanks_game_app(void* p) { tanks_state->state = GameStateMenu; tanks_state->menu_state = MenuStateSingleMode; - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, tanks_state, sizeof(TanksState))) { + tanks_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!tanks_state->mutex) { FURI_LOG_E("Tanks", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(tanks_state); @@ -1197,7 +1197,7 @@ int32_t tanks_game_app(void* p) { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, tanks_game_render_callback, &state_mutex); + view_port_draw_callback_set(view_port, tanks_game_render_callback, tanks_state); view_port_input_callback_set(view_port, tanks_game_input_callback, event_queue); FuriTimer* timer = @@ -1221,7 +1221,7 @@ int32_t tanks_game_app(void* p) { for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - TanksState* tanks_state = (TanksState*)acquire_mutex_block(&state_mutex); + furi_mutex_acquire(tanks_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { // press events @@ -1422,7 +1422,7 @@ int32_t tanks_game_app(void* p) { } view_port_update(view_port); - release_mutex(&state_mutex, tanks_state); + furi_mutex_release(tanks_state->mutex); furi_delay_ms(1); } @@ -1440,7 +1440,7 @@ int32_t tanks_game_app(void* p) { furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); + furi_mutex_free(tanks_state->mutex); if(tanks_state->p1 != NULL) { free(tanks_state->p1); diff --git a/applications/plugins/timelapse/README.md b/applications/plugins/timelapse/README.md index fe3ed171a..b10955ea1 100644 --- a/applications/plugins/timelapse/README.md +++ b/applications/plugins/timelapse/README.md @@ -5,6 +5,12 @@ english version [below](#eng) + +Blog: [theageoflove.ru](https://theageoflove.ru) + +TG: [t.me/scuko_bled](https://t.me/scuko_bled) + + ![zeitraffer for flipper zero](https://theageoflove.ru/uploads/2022/11/photo_2022-11-10_15-54-25.jpg) Видео работы: https://youtu.be/VPSpRLJXYAc diff --git a/applications/plugins/timelapse/application.fam b/applications/plugins/timelapse/application.fam index f5eaae551..ba5babc7e 100644 --- a/applications/plugins/timelapse/application.fam +++ b/applications/plugins/timelapse/application.fam @@ -1,6 +1,6 @@ App( - appid="GPIO_Intervalometer", - name="[GPIO] Intervalometer", + appid="GPIO_Timelapse", + name="[GPIO] Timelapse", apptype=FlipperAppType.EXTERNAL, entry_point="zeitraffer_app", cdefines=["APP_ZEITRAFFER"], diff --git a/applications/plugins/timelapse/zeitraffer.c b/applications/plugins/timelapse/zeitraffer.c index 4e31292c6..cbae4fa44 100644 --- a/applications/plugins/timelapse/zeitraffer.c +++ b/applications/plugins/timelapse/zeitraffer.c @@ -5,10 +5,10 @@ #include #include #include "gpio_item.h" -#include "GPIO_Intervalometer_icons.h" +#include "GPIO_Timelapse_icons.h" -#define CONFIG_FILE_DIRECTORY_PATH "/ext/apps_data/intravelometer" -#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/intravelometer.conf" +#define CONFIG_FILE_DIRECTORY_PATH "/ext/apps_data/timelapse" +#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/timelapse.conf" // Часть кода покрадена из https://github.com/zmactep/flipperzero-hello-world @@ -390,7 +390,7 @@ int32_t zeitraffer_app(void* p) { } if(!flipper_format_write_comment_cstr( save, - "Zeitraffer app settings: № of frames, interval time, backlight type, Delay")) { + "Zeitraffer app settings: n of frames, interval time, backlight type, Delay")) { notification_message(notifications, &sequence_error); break; } diff --git a/applications/plugins/usb_hid_autofire/CHANGELOG.md b/applications/plugins/usb_hid_autofire/CHANGELOG.md index 62929bc8f..d0924edd3 100644 --- a/applications/plugins/usb_hid_autofire/CHANGELOG.md +++ b/applications/plugins/usb_hid_autofire/CHANGELOG.md @@ -1,8 +1,5 @@ # Changelog -## 0.5 -- Fix compatibility with Flipper Zero firmware 0.74.2 - ## 0.4 - Show active/inactive state in primary font (bold) diff --git a/applications/plugins/usb_hid_autofire/version.h b/applications/plugins/usb_hid_autofire/version.h index 669b388a5..ac1f5d0fa 100644 --- a/applications/plugins/usb_hid_autofire/version.h +++ b/applications/plugins/usb_hid_autofire/version.h @@ -1 +1 @@ -#define VERSION "0.5" +#define VERSION "0.4" diff --git a/applications/plugins/wii_ec_anal/LICENSE b/applications/plugins/wii_ec_anal/LICENSE new file mode 100644 index 000000000..95e544a06 --- /dev/null +++ b/applications/plugins/wii_ec_anal/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 BlueChip + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert.sh b/applications/plugins/wii_ec_anal/_image_tool/_convert.sh old mode 100644 new mode 100755 diff --git a/applications/plugins/wii_ec_anal/info.sh b/applications/plugins/wii_ec_anal/info.sh old mode 100644 new mode 100755 diff --git a/applications/plugins/wii_ec_anal/wii_anal.c b/applications/plugins/wii_ec_anal/wii_anal.c index f0af1c9c5..34b4f318b 100644 --- a/applications/plugins/wii_ec_anal/wii_anal.c +++ b/applications/plugins/wii_ec_anal/wii_anal.c @@ -94,10 +94,9 @@ static void cbDraw(Canvas* const canvas, void* ctx) { furi_assert(canvas); furi_assert(ctx); - state_t* state = NULL; - // Try to acquire the mutex for the plugin state variables, timeout = 25mS - if(!(state = (state_t*)acquire_mutex((ValueMutex*)ctx, 25))) return; + state_t* state = ctx; + furi_mutex_acquire(state->mutex, FuriWaitForever); switch(state->scene) { //--------------------------------------------------------------------- @@ -182,7 +181,7 @@ static void cbDraw(Canvas* const canvas, void* ctx) { } // Release the mutex - release_mutex((ValueMutex*)ctx, state); + furi_mutex_release(state->mutex); LEAVE; return; @@ -308,7 +307,6 @@ int32_t wii_ec_anal(void) { Gui* gui = NULL; ViewPort* vpp = NULL; state_t* state = NULL; - ValueMutex mutex = {0}; FuriMessageQueue* queue = NULL; const uint32_t queueSz = 20; // maximum messages in queue uint32_t tmo = (3.5f * 1000); // timeout splash screen after N seconds @@ -346,7 +344,8 @@ int32_t wii_ec_anal(void) { goto bail; } // 5. Create a mutex for (reading/writing) the plugin state variables - if(!init_mutex(&mutex, state, sizeof(state))) { + state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!state->mutex) { ERROR(wii_errs[(error = ERR_NO_MUTEX)]); goto bail; } @@ -360,7 +359,7 @@ int32_t wii_ec_anal(void) { // 7a. Register a callback for input events view_port_input_callback_set(vpp, cbInput, queue); // 7b. Register a callback for draw events - view_port_draw_callback_set(vpp, cbDraw, &mutex); + view_port_draw_callback_set(vpp, cbDraw, state); // ===== Start GUI Interface ===== // 8. Attach the viewport to the GUI @@ -435,10 +434,7 @@ int32_t wii_ec_anal(void) { // Read successful // *** Try to lock the plugin state variables *** - if(!(state = (state_t*)acquire_mutex_block(&mutex))) { - ERROR(wii_errs[(error = ERR_MUTEX_BLOCK)]); - goto bail; - } + furi_mutex_acquire(state->mutex, FuriWaitForever); // *** Handle events *** switch(msg.id) { @@ -476,10 +472,7 @@ int32_t wii_ec_anal(void) { if(redraw) view_port_update(vpp); // *** Try to release the plugin state variables *** - if(!release_mutex(&mutex, state)) { - ERROR(wii_errs[(error = ERR_MUTEX_RELEASE)]); - goto bail; - } + furi_mutex_release(state->mutex); } while(state->run); // ===== Game Over ===== @@ -514,10 +507,7 @@ bail: } // 5. Free the mutex - if(mutex.mutex) { - delete_mutex(&mutex); - mutex.mutex = NULL; - } + furi_mutex_free(state->mutex); // 4. Free up state pointer(s) // none diff --git a/applications/plugins/wii_ec_anal/wii_anal.h b/applications/plugins/wii_ec_anal/wii_anal.h index 3aae61fdc..d8997b030 100644 --- a/applications/plugins/wii_ec_anal/wii_anal.h +++ b/applications/plugins/wii_ec_anal/wii_anal.h @@ -56,6 +56,7 @@ typedef struct eventMsg { // Access to this memory is controlled by mutex // typedef struct state { + FuriMutex* mutex; bool run; // true : plugin is running bool timerEn; // controller scanning enabled