| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- /*!
- * @file flipper-xremote/xremote_learn.c
- @license This project is released under the GNU GPLv3 License
- * @copyright (c) 2023 Sandro Kalatozishvili (s.kalatoz@gmail.com)
- *
- * @brief Functionality to read and store input from new remote.
- */
- #include "xremote_learn.h"
- #include "views/xremote_learn_view.h"
- struct XRemoteLearnContext {
- /* XRemote context */
- XRemoteSignalReceiver* ir_receiver;
- XRemoteAppContext* app_ctx;
- XRemoteView* signal_view;
- XRemoteViewID curr_view;
- XRemoteViewID prev_view;
- /* Main infrared app context */
- InfraredRemote* ir_remote;
- InfraredSignal* ir_signal;
- /* User interactions */
- TextInput* text_input;
- DialogEx* dialog_ex;
- /* User context and clear callback */
- XRemoteClearCallback on_clear;
- void* context;
- /* Private control flags */
- char text_store[XREMOTE_APP_TEXT_MAX + 1];
- uint8_t current_button;
- bool finish_learning;
- bool stop_receiver;
- bool is_dirty;
- };
- void xremote_learn_send_event(XRemoteLearnContext* learn_ctx, XRemoteEvent event) {
- xremote_app_assert_void(learn_ctx);
- if(event == XRemoteEventSignalFinish) learn_ctx->finish_learning = true;
- ViewDispatcher* view_disp = learn_ctx->app_ctx->view_dispatcher;
- view_dispatcher_send_custom_event(view_disp, event);
- }
- const char* xremote_learn_get_curr_button_name(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert(learn_ctx, NULL);
- return xremote_button_get_name(learn_ctx->current_button);
- }
- int xremote_learn_get_curr_button_index(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert(learn_ctx, -1);
- return learn_ctx->current_button;
- }
- InfraredRemote* xremote_learn_get_ir_remote(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert(learn_ctx, NULL);
- return learn_ctx->ir_remote;
- }
- InfraredSignal* xremote_learn_get_ir_signal(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert(learn_ctx, NULL);
- return learn_ctx->ir_signal;
- }
- XRemoteSignalReceiver* xremote_learn_get_ir_receiver(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert(learn_ctx, NULL);
- return learn_ctx->ir_receiver;
- }
- XRemoteAppContext* xremote_learn_get_app_context(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert(learn_ctx, NULL);
- return learn_ctx->app_ctx;
- }
- bool xremote_learn_has_buttons(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert(learn_ctx, false);
- return infrared_remote_get_button_count(learn_ctx->ir_remote) > 0;
- }
- static void xremote_learn_switch_to_view(XRemoteLearnContext* learn_ctx, XRemoteViewID view_id) {
- xremote_app_assert_void(learn_ctx);
- learn_ctx->prev_view = learn_ctx->curr_view;
- learn_ctx->curr_view = view_id;
- ViewDispatcher* view_disp = learn_ctx->app_ctx->view_dispatcher;
- view_dispatcher_switch_to_view(view_disp, view_id);
- }
- static void xremote_learn_context_rx_stop(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert_void(learn_ctx);
- learn_ctx->stop_receiver = true;
- xremote_signal_receiver_stop(learn_ctx->ir_receiver);
- }
- static void xremote_learn_context_rx_start(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert_void(learn_ctx);
- learn_ctx->finish_learning = false;
- learn_ctx->stop_receiver = false;
- xremote_signal_receiver_start(learn_ctx->ir_receiver);
- }
- static void xremote_learn_exit_dialog_free(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert_void(learn_ctx);
- xremote_app_assert_void(learn_ctx->dialog_ex);
- ViewDispatcher* view_disp = learn_ctx->app_ctx->view_dispatcher;
- view_dispatcher_remove_view(view_disp, XRemoteViewDialogExit);
- dialog_ex_free(learn_ctx->dialog_ex);
- learn_ctx->dialog_ex = NULL;
- }
- static void xremote_learn_exit_dialog_alloc(
- XRemoteLearnContext* learn_ctx,
- DialogExResultCallback callback) {
- xremote_app_assert_void(learn_ctx);
- xremote_learn_exit_dialog_free(learn_ctx);
- ViewDispatcher* view_disp = learn_ctx->app_ctx->view_dispatcher;
- const char* dialog_text = "All unsaved data\nwill be lost!";
- const char* header_text = "Exit to XRemote Menu?";
- learn_ctx->dialog_ex = dialog_ex_alloc();
- View* view = dialog_ex_get_view(learn_ctx->dialog_ex);
- view_dispatcher_add_view(view_disp, XRemoteViewDialogExit, view);
- dialog_ex_set_header(learn_ctx->dialog_ex, header_text, 64, 11, AlignCenter, AlignTop);
- dialog_ex_set_text(learn_ctx->dialog_ex, dialog_text, 64, 25, AlignCenter, AlignTop);
- dialog_ex_set_icon(learn_ctx->dialog_ex, 0, 0, NULL);
- dialog_ex_set_left_button_text(learn_ctx->dialog_ex, "Exit");
- dialog_ex_set_center_button_text(learn_ctx->dialog_ex, "Save");
- dialog_ex_set_right_button_text(learn_ctx->dialog_ex, "Stay");
- dialog_ex_set_result_callback(learn_ctx->dialog_ex, callback);
- dialog_ex_set_context(learn_ctx->dialog_ex, learn_ctx);
- }
- static uint32_t xremote_learn_view_exit_callback(void* context) {
- UNUSED(context);
- return XRemoteViewLearn;
- }
- static void xremote_learn_dialog_exit_callback(DialogExResult result, void* context) {
- XRemoteLearnContext* learn_ctx = (XRemoteLearnContext*)context;
- xremote_learn_switch_to_view(learn_ctx, XRemoteViewSubmenu);
- if(result == DialogExResultLeft)
- xremote_learn_send_event(learn_ctx, XRemoteEventSignalExit);
- else if(result == DialogExResultRight)
- xremote_learn_send_event(learn_ctx, XRemoteEventSignalRetry);
- else if(result == DialogExResultCenter)
- xremote_learn_send_event(learn_ctx, XRemoteEventSignalFinish);
- }
- static uint32_t xremote_learn_text_input_exit_callback(void* context) {
- TextInput* text_input = context;
- XRemoteLearnContext* learn_ctx;
- learn_ctx = text_input_get_validator_callback_context(text_input);
- xremote_app_assert(learn_ctx, XRemoteViewSubmenu);
- XRemoteEvent event = learn_ctx->prev_view == XRemoteViewSignal ? XRemoteEventSignalReceived :
- XRemoteEventSignalRetry;
- if(learn_ctx->current_button >= XREMOTE_BUTTON_COUNT)
- learn_ctx->current_button = XREMOTE_BUTTON_COUNT - 1;
- learn_ctx->finish_learning = false;
- xremote_learn_send_event(learn_ctx, event);
- return XRemoteViewTextInput;
- }
- static void xremote_learn_text_input_callback(void* context) {
- xremote_app_assert_void(context);
- XRemoteLearnContext* learn_ctx = context;
- if(learn_ctx->is_dirty) {
- const char* name = xremote_learn_get_curr_button_name(learn_ctx);
- if(!infrared_remote_get_button_by_name(learn_ctx->ir_remote, name)) {
- InfraredSignal* signal = xremote_learn_get_ir_signal(learn_ctx);
- infrared_remote_push_button(learn_ctx->ir_remote, name, signal);
- }
- learn_ctx->is_dirty = false;
- }
- if(infrared_remote_get_button_count(learn_ctx->ir_remote) &&
- learn_ctx->text_store[0] != '\0') {
- char output_file[256];
- snprintf(
- output_file,
- sizeof(output_file),
- "%s/%s.ir",
- XREMOTE_APP_FOLDER,
- learn_ctx->text_store);
- infrared_remote_set_name(learn_ctx->ir_remote, learn_ctx->text_store);
- infrared_remote_set_path(learn_ctx->ir_remote, output_file);
- infrared_remote_store(learn_ctx->ir_remote);
- infrared_remote_reset(learn_ctx->ir_remote);
- }
- xremote_learn_send_event(learn_ctx, XRemoteEventSignalExit);
- }
- static void xremote_learn_signal_callback(void* context, InfraredSignal* signal) {
- XRemoteLearnContext* learn_ctx = context;
- xremote_app_assert_void(!learn_ctx->stop_receiver);
- xremote_app_assert_void(!learn_ctx->finish_learning);
- learn_ctx->stop_receiver = true;
- learn_ctx->is_dirty = true;
- infrared_signal_set_signal(learn_ctx->ir_signal, signal);
- xremote_learn_send_event(learn_ctx, XRemoteEventSignalReceived);
- }
- static void xremote_learn_exit_dialog_ask(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert_void(learn_ctx);
- if(infrared_remote_get_button_count(learn_ctx->ir_remote) || learn_ctx->is_dirty) {
- xremote_learn_exit_dialog_alloc(learn_ctx, xremote_learn_dialog_exit_callback);
- xremote_learn_switch_to_view(learn_ctx, XRemoteViewDialogExit);
- return;
- }
- learn_ctx->is_dirty = false;
- xremote_learn_switch_to_view(learn_ctx, XRemoteViewSubmenu);
- }
- static void xremote_learn_finish(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert_void(learn_ctx);
- xremote_app_assert_void(learn_ctx->text_input);
- if(infrared_remote_get_button_count(learn_ctx->ir_remote) || learn_ctx->is_dirty) {
- snprintf(learn_ctx->text_store, XREMOTE_APP_TEXT_MAX, "Remote_");
- text_input_set_header_text(learn_ctx->text_input, "Name new remote");
- text_input_set_result_callback(
- learn_ctx->text_input,
- xremote_learn_text_input_callback,
- learn_ctx,
- learn_ctx->text_store,
- XREMOTE_APP_TEXT_MAX,
- true);
- xremote_learn_switch_to_view(learn_ctx, XRemoteViewTextInput);
- return;
- }
- learn_ctx->is_dirty = false;
- xremote_learn_switch_to_view(learn_ctx, XRemoteViewSubmenu);
- }
- static bool xremote_learn_custom_event_dummy_callback(void* context, uint32_t event) {
- UNUSED(context);
- UNUSED(event);
- return true;
- }
- static bool xremote_learn_custom_event_callback(void* context, uint32_t event) {
- xremote_app_assert(context, false);
- XRemoteLearnContext* learn_ctx = context;
- if(learn_ctx->finish_learning && event != XRemoteEventSignalFinish &&
- event != XRemoteEventSignalAskExit && event != XRemoteEventSignalExit)
- return true;
- if(event == XRemoteEventSignalReceived) {
- xremote_learn_context_rx_stop(learn_ctx);
- xremote_learn_switch_to_view(learn_ctx, XRemoteViewSignal);
- } else if(event == XRemoteEventSignalSave) {
- const char* name = xremote_learn_get_curr_button_name(learn_ctx);
- infrared_remote_delete_button_by_name(learn_ctx->ir_remote, name);
- InfraredSignal* signal = xremote_learn_get_ir_signal(learn_ctx);
- infrared_remote_push_button(learn_ctx->ir_remote, name, signal);
- learn_ctx->is_dirty = false;
- if(++learn_ctx->current_button >= XREMOTE_BUTTON_COUNT) {
- xremote_learn_context_rx_stop(learn_ctx);
- xremote_learn_finish(learn_ctx);
- return true;
- }
- xremote_learn_context_rx_start(learn_ctx);
- xremote_learn_switch_to_view(learn_ctx, XRemoteViewLearn);
- } else if(event == XRemoteEventSignalSkip) {
- learn_ctx->current_button++;
- learn_ctx->is_dirty = false;
- if(learn_ctx->current_button >= XREMOTE_BUTTON_COUNT) {
- if(xremote_learn_has_buttons(learn_ctx)) {
- xremote_learn_context_rx_stop(learn_ctx);
- xremote_learn_finish(learn_ctx);
- return true;
- }
- learn_ctx->current_button = 0;
- }
- xremote_learn_context_rx_start(learn_ctx);
- xremote_learn_switch_to_view(learn_ctx, XRemoteViewLearn);
- } else if(event == XRemoteEventSignalRetry) {
- learn_ctx->is_dirty = false;
- xremote_learn_context_rx_start(learn_ctx);
- xremote_learn_switch_to_view(learn_ctx, XRemoteViewLearn);
- } else if(event == XRemoteEventSignalFinish) {
- xremote_learn_context_rx_stop(learn_ctx);
- xremote_learn_finish(learn_ctx);
- } else if(event == XRemoteEventSignalAskExit) {
- xremote_learn_context_rx_stop(learn_ctx);
- xremote_learn_exit_dialog_ask(learn_ctx);
- } else if(event == XRemoteEventSignalExit) {
- learn_ctx->is_dirty = false;
- xremote_learn_context_rx_stop(learn_ctx);
- xremote_learn_switch_to_view(learn_ctx, XRemoteViewSubmenu);
- }
- return true;
- }
- static XRemoteLearnContext* xremote_learn_context_alloc(XRemoteAppContext* app_ctx) {
- XRemoteLearnContext* learn_ctx = malloc(sizeof(XRemoteLearnContext));
- learn_ctx->ir_signal = infrared_signal_alloc();
- learn_ctx->ir_remote = infrared_remote_alloc();
- learn_ctx->app_ctx = app_ctx;
- learn_ctx->dialog_ex = NULL;
- learn_ctx->text_store[0] = 0;
- learn_ctx->current_button = 0;
- learn_ctx->curr_view = XRemoteViewLearn;
- learn_ctx->prev_view = XRemoteViewNone;
- learn_ctx->finish_learning = false;
- learn_ctx->stop_receiver = false;
- learn_ctx->is_dirty = false;
- learn_ctx->signal_view = xremote_learn_success_view_alloc(app_ctx, learn_ctx);
- View* view = xremote_view_get_view(learn_ctx->signal_view);
- view_set_previous_callback(view, xremote_learn_view_exit_callback);
- view_dispatcher_add_view(app_ctx->view_dispatcher, XRemoteViewSignal, view);
- learn_ctx->text_input = text_input_alloc();
- text_input_set_validator(learn_ctx->text_input, NULL, learn_ctx);
- view = text_input_get_view(learn_ctx->text_input);
- view_set_previous_callback(view, xremote_learn_text_input_exit_callback);
- view_dispatcher_add_view(app_ctx->view_dispatcher, XRemoteViewTextInput, view);
- view_dispatcher_set_custom_event_callback(
- app_ctx->view_dispatcher, xremote_learn_custom_event_callback);
- view_dispatcher_set_event_callback_context(app_ctx->view_dispatcher, learn_ctx);
- learn_ctx->ir_receiver = xremote_signal_receiver_alloc(app_ctx);
- xremote_signal_receiver_set_context(learn_ctx->ir_receiver, learn_ctx, NULL);
- xremote_signal_receiver_set_rx_callback(learn_ctx->ir_receiver, xremote_learn_signal_callback);
- return learn_ctx;
- }
- static void xremote_learn_context_free(XRemoteLearnContext* learn_ctx) {
- xremote_app_assert_void(learn_ctx);
- xremote_signal_receiver_stop(learn_ctx->ir_receiver);
- xremote_learn_exit_dialog_free(learn_ctx);
- ViewDispatcher* view_disp = learn_ctx->app_ctx->view_dispatcher;
- view_dispatcher_set_custom_event_callback(
- view_disp, xremote_learn_custom_event_dummy_callback);
- view_dispatcher_remove_view(view_disp, XRemoteViewTextInput);
- text_input_free(learn_ctx->text_input);
- view_dispatcher_remove_view(view_disp, XRemoteViewSignal);
- xremote_view_free(learn_ctx->signal_view);
- xremote_signal_receiver_free(learn_ctx->ir_receiver);
- infrared_signal_free(learn_ctx->ir_signal);
- infrared_remote_free(learn_ctx->ir_remote);
- free(learn_ctx);
- }
- static void xremote_learn_context_clear_callback(void* context) {
- XRemoteLearnContext* learn = context;
- xremote_learn_context_free(learn);
- }
- XRemoteApp* xremote_learn_alloc(XRemoteAppContext* app_ctx) {
- XRemoteApp* app = xremote_app_alloc(app_ctx);
- app->view_id = XRemoteViewLearn;
- XRemoteLearnContext* learn = xremote_learn_context_alloc(app_ctx);
- app->view_ctx = xremote_learn_view_alloc(app->app_ctx, learn);
- View* view = xremote_view_get_view(app->view_ctx);
- ViewDispatcher* view_disp = app_ctx->view_dispatcher;
- view_dispatcher_add_view(view_disp, app->view_id, view);
- xremote_app_view_set_previous_callback(app, xremote_learn_view_exit_callback);
- xremote_app_set_view_context(app, learn, xremote_learn_context_clear_callback);
- xremote_signal_receiver_start(learn->ir_receiver);
- return app;
- }
|