mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-23 13:21:44 +03:00
[FL-3456] Allow for larger Infrared remotes (#3164)
* Do not load all signals at once (Draft) * Minor cleanup * Refactor remote renaming * Improve function signatures * Rename infrared_remote functions * Optimise signal loading * Implement adding signals to remote * Add read_name() method * Deprecate a function * Partially implement deleting signals (draft) * Use m-array instead of m-list for signal name directory * Use plain C strings instead of furi_string * Implement deleting signals * Implement deleting signals via generalised callback * Implement renaming signals * Rename some types * Some more renaming * Remove unused type * Implement inserting signals (internal use) * Improve InfraredMoveView * Send an event to move a signal * Remove unused type * Implement moving signals * Implement creating new remotes with one signal * Un-deprecate and rename a function * Add InfraredRemote API docs * Add InfraredSignal API docs * Better error messages * Show progress pop-up when moving buttons in a remote * Copy labels to the InfraredMoveView to avoid pointer invalidation * Improve file selection scene * Show progress pop-up when renaming buttons in a remote * Refactor a scene * Show progress when deleting a button from remote * Use a random name for temp files * Add docs to infrared_brute_force.h * Rename Infrared type to InfraredApp * Add docs to infrared_app_i.h Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
parent
917410a0a8
commit
c8180747db
@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct Infrared Infrared;
|
@ -1,48 +1,52 @@
|
||||
#include "infrared_i.h"
|
||||
#include "infrared_app_i.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define TAG "InfraredApp"
|
||||
|
||||
#define INFRARED_TX_MIN_INTERVAL_MS 50U
|
||||
|
||||
static const NotificationSequence* infrared_notification_sequences[] = {
|
||||
&sequence_success,
|
||||
&sequence_set_only_green_255,
|
||||
&sequence_reset_green,
|
||||
&sequence_solid_yellow,
|
||||
&sequence_reset_rgb,
|
||||
&sequence_blink_start_cyan,
|
||||
&sequence_blink_start_magenta,
|
||||
&sequence_blink_stop,
|
||||
static const NotificationSequence*
|
||||
infrared_notification_sequences[InfraredNotificationMessageCount] = {
|
||||
&sequence_success,
|
||||
&sequence_set_only_green_255,
|
||||
&sequence_reset_green,
|
||||
&sequence_solid_yellow,
|
||||
&sequence_reset_rgb,
|
||||
&sequence_blink_start_cyan,
|
||||
&sequence_blink_start_magenta,
|
||||
&sequence_blink_stop,
|
||||
};
|
||||
|
||||
static void infrared_make_app_folder(Infrared* infrared) {
|
||||
static void infrared_make_app_folder(InfraredApp* infrared) {
|
||||
if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) {
|
||||
dialog_message_show_storage_error(infrared->dialogs, "Cannot create\napp folder");
|
||||
infrared_show_error_message(infrared, "Cannot create\napp folder");
|
||||
}
|
||||
}
|
||||
|
||||
static bool infrared_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
return scene_manager_handle_custom_event(infrared->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool infrared_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
return scene_manager_handle_back_event(infrared->scene_manager);
|
||||
}
|
||||
|
||||
static void infrared_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
scene_manager_handle_tick_event(infrared->scene_manager);
|
||||
}
|
||||
|
||||
static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
furi_assert(infrared->rpc_ctx);
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
@ -109,8 +113,8 @@ static void infrared_find_vacant_remote_name(FuriString* name, const char* path)
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static Infrared* infrared_alloc() {
|
||||
Infrared* infrared = malloc(sizeof(Infrared));
|
||||
static InfraredApp* infrared_alloc() {
|
||||
InfraredApp* infrared = malloc(sizeof(InfraredApp));
|
||||
|
||||
infrared->file_path = furi_string_alloc();
|
||||
|
||||
@ -139,7 +143,7 @@ static Infrared* infrared_alloc() {
|
||||
|
||||
infrared->worker = infrared_worker_alloc();
|
||||
infrared->remote = infrared_remote_alloc();
|
||||
infrared->received_signal = infrared_signal_alloc();
|
||||
infrared->current_signal = infrared_signal_alloc();
|
||||
infrared->brute_force = infrared_brute_force_alloc();
|
||||
|
||||
infrared->submenu = submenu_alloc();
|
||||
@ -184,7 +188,7 @@ static Infrared* infrared_alloc() {
|
||||
return infrared;
|
||||
}
|
||||
|
||||
static void infrared_free(Infrared* infrared) {
|
||||
static void infrared_free(InfraredApp* infrared) {
|
||||
furi_assert(infrared);
|
||||
ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
@ -229,7 +233,7 @@ static void infrared_free(Infrared* infrared) {
|
||||
scene_manager_free(infrared->scene_manager);
|
||||
|
||||
infrared_brute_force_free(infrared->brute_force);
|
||||
infrared_signal_free(infrared->received_signal);
|
||||
infrared_signal_free(infrared->current_signal);
|
||||
infrared_remote_free(infrared->remote);
|
||||
infrared_worker_free(infrared->worker);
|
||||
|
||||
@ -248,65 +252,61 @@ static void infrared_free(Infrared* infrared) {
|
||||
}
|
||||
|
||||
bool infrared_add_remote_with_button(
|
||||
Infrared* infrared,
|
||||
const InfraredApp* infrared,
|
||||
const char* button_name,
|
||||
InfraredSignal* signal) {
|
||||
const InfraredSignal* signal) {
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
|
||||
FuriString *new_name, *new_path;
|
||||
new_name = furi_string_alloc_set(INFRARED_DEFAULT_REMOTE_NAME);
|
||||
new_path = furi_string_alloc_set(INFRARED_APP_FOLDER);
|
||||
FuriString* new_name = furi_string_alloc_set(INFRARED_DEFAULT_REMOTE_NAME);
|
||||
FuriString* new_path = furi_string_alloc_set(INFRARED_APP_FOLDER);
|
||||
|
||||
infrared_find_vacant_remote_name(new_name, furi_string_get_cstr(new_path));
|
||||
furi_string_cat_printf(
|
||||
new_path, "/%s%s", furi_string_get_cstr(new_name), INFRARED_APP_EXTENSION);
|
||||
|
||||
infrared_remote_reset(remote);
|
||||
infrared_remote_set_name(remote, furi_string_get_cstr(new_name));
|
||||
infrared_remote_set_path(remote, furi_string_get_cstr(new_path));
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!infrared_remote_create(remote, furi_string_get_cstr(new_path))) break;
|
||||
if(!infrared_remote_append_signal(remote, signal, button_name)) break;
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(new_name);
|
||||
furi_string_free(new_path);
|
||||
return infrared_remote_add_button(remote, button_name, signal);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_rename_current_remote(Infrared* infrared, const char* name) {
|
||||
bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name) {
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
const char* remote_path = infrared_remote_get_path(remote);
|
||||
const char* old_path = infrared_remote_get_path(remote);
|
||||
|
||||
if(!strcmp(infrared_remote_get_name(remote), name)) {
|
||||
if(!strcmp(infrared_remote_get_name(remote), new_name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
FuriString* new_name;
|
||||
new_name = furi_string_alloc_set(name);
|
||||
FuriString* new_name_fstr = furi_string_alloc_set(new_name);
|
||||
FuriString* new_path_fstr = furi_string_alloc_set(old_path);
|
||||
|
||||
infrared_find_vacant_remote_name(new_name, remote_path);
|
||||
infrared_find_vacant_remote_name(new_name_fstr, old_path);
|
||||
|
||||
FuriString* new_path;
|
||||
new_path = furi_string_alloc_set(infrared_remote_get_path(remote));
|
||||
if(furi_string_end_with(new_path, INFRARED_APP_EXTENSION)) {
|
||||
size_t filename_start = furi_string_search_rchar(new_path, '/');
|
||||
furi_string_left(new_path, filename_start);
|
||||
if(furi_string_end_with(new_path_fstr, INFRARED_APP_EXTENSION)) {
|
||||
path_extract_dirname(old_path, new_path_fstr);
|
||||
}
|
||||
furi_string_cat_printf(
|
||||
new_path, "/%s%s", furi_string_get_cstr(new_name), INFRARED_APP_EXTENSION);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
path_append(new_path_fstr, furi_string_get_cstr(new_name_fstr));
|
||||
furi_string_cat(new_path_fstr, INFRARED_APP_EXTENSION);
|
||||
|
||||
FS_Error status = storage_common_rename(
|
||||
storage, infrared_remote_get_path(remote), furi_string_get_cstr(new_path));
|
||||
infrared_remote_set_name(remote, furi_string_get_cstr(new_name));
|
||||
infrared_remote_set_path(remote, furi_string_get_cstr(new_path));
|
||||
const bool success = infrared_remote_rename(remote, furi_string_get_cstr(new_path_fstr));
|
||||
|
||||
furi_string_free(new_name);
|
||||
furi_string_free(new_path);
|
||||
furi_string_free(new_name_fstr);
|
||||
furi_string_free(new_path_fstr);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return (status == FSE_OK || status == FSE_EXIST);
|
||||
return success;
|
||||
}
|
||||
|
||||
void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
|
||||
void infrared_tx_start(InfraredApp* infrared) {
|
||||
if(infrared->app_state.is_transmitting) {
|
||||
return;
|
||||
}
|
||||
@ -317,12 +317,12 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
if(infrared_signal_is_raw(infrared->current_signal)) {
|
||||
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(infrared->current_signal);
|
||||
infrared_worker_set_raw_signal(
|
||||
infrared->worker, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);
|
||||
} else {
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
const InfraredMessage* message = infrared_signal_get_message(infrared->current_signal);
|
||||
infrared_worker_set_decoded_signal(infrared->worker, message);
|
||||
}
|
||||
|
||||
@ -336,20 +336,20 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
|
||||
infrared->app_state.is_transmitting = true;
|
||||
}
|
||||
|
||||
void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) {
|
||||
furi_assert(button_index < infrared_remote_get_button_count(infrared->remote));
|
||||
void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index) {
|
||||
furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote));
|
||||
|
||||
InfraredRemoteButton* button = infrared_remote_get_button(infrared->remote, button_index);
|
||||
InfraredSignal* signal = infrared_remote_button_get_signal(button);
|
||||
|
||||
infrared_tx_start_signal(infrared, signal);
|
||||
if(infrared_remote_load_signal(infrared->remote, infrared->current_signal, button_index)) {
|
||||
infrared_tx_start(infrared);
|
||||
} else {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to load\n\"%s\"",
|
||||
infrared_remote_get_signal_name(infrared->remote, button_index));
|
||||
}
|
||||
}
|
||||
|
||||
void infrared_tx_start_received(Infrared* infrared) {
|
||||
infrared_tx_start_signal(infrared, infrared->received_signal);
|
||||
}
|
||||
|
||||
void infrared_tx_stop(Infrared* infrared) {
|
||||
void infrared_tx_stop(InfraredApp* infrared) {
|
||||
if(!infrared->app_state.is_transmitting) {
|
||||
return;
|
||||
}
|
||||
@ -363,25 +363,27 @@ void infrared_tx_stop(Infrared* infrared) {
|
||||
infrared->app_state.last_transmit_time = furi_get_tick();
|
||||
}
|
||||
|
||||
void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) {
|
||||
void infrared_text_store_set(InfraredApp* infrared, uint32_t bank, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, text);
|
||||
va_start(args, fmt);
|
||||
|
||||
vsnprintf(infrared->text_store[bank], INFRARED_TEXT_STORE_SIZE, text, args);
|
||||
vsnprintf(infrared->text_store[bank], INFRARED_TEXT_STORE_SIZE, fmt, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void infrared_text_store_clear(Infrared* infrared, uint32_t bank) {
|
||||
void infrared_text_store_clear(InfraredApp* infrared, uint32_t bank) {
|
||||
memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE + 1);
|
||||
}
|
||||
|
||||
void infrared_play_notification_message(Infrared* infrared, uint32_t message) {
|
||||
furi_assert(message < sizeof(infrared_notification_sequences) / sizeof(NotificationSequence*));
|
||||
void infrared_play_notification_message(
|
||||
const InfraredApp* infrared,
|
||||
InfraredNotificationMessage message) {
|
||||
furi_assert(message < InfraredNotificationMessageCount);
|
||||
notification_message(infrared->notifications, infrared_notification_sequences[message]);
|
||||
}
|
||||
|
||||
void infrared_show_loading_popup(Infrared* infrared, bool show) {
|
||||
void infrared_show_loading_popup(const InfraredApp* infrared, bool show) {
|
||||
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
|
||||
ViewStack* view_stack = infrared->view_stack;
|
||||
Loading* loading = infrared->loading;
|
||||
@ -397,19 +399,30 @@ void infrared_show_loading_popup(Infrared* infrared, bool show) {
|
||||
}
|
||||
}
|
||||
|
||||
void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
FuriString* message = furi_string_alloc_vprintf(fmt, args);
|
||||
dialog_message_show_storage_error(infrared->dialogs, furi_string_get_cstr(message));
|
||||
|
||||
furi_string_free(message);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
|
||||
if(infrared_worker_signal_is_decoded(received_signal)) {
|
||||
infrared_signal_set_message(
|
||||
infrared->received_signal, infrared_worker_get_decoded_signal(received_signal));
|
||||
infrared->current_signal, infrared_worker_get_decoded_signal(received_signal));
|
||||
} else {
|
||||
const uint32_t* timings;
|
||||
size_t timings_size;
|
||||
infrared_worker_get_raw_signal(received_signal, &timings, &timings_size);
|
||||
infrared_signal_set_raw_signal(
|
||||
infrared->received_signal,
|
||||
infrared->current_signal,
|
||||
timings,
|
||||
timings_size,
|
||||
INFRARED_COMMON_CARRIER_FREQUENCY,
|
||||
@ -422,20 +435,20 @@ void infrared_signal_received_callback(void* context, InfraredWorkerSignal* rece
|
||||
|
||||
void infrared_text_input_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeTextEditDone);
|
||||
}
|
||||
|
||||
void infrared_popup_closed_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypePopupClosed);
|
||||
}
|
||||
|
||||
int32_t infrared_app(void* p) {
|
||||
Infrared* infrared = infrared_alloc();
|
||||
InfraredApp* infrared = infrared_alloc();
|
||||
|
||||
infrared_make_app_folder(infrared);
|
||||
|
||||
@ -451,13 +464,15 @@ int32_t infrared_app(void* p) {
|
||||
rpc_system_app_send_started(infrared->rpc_ctx);
|
||||
is_rpc_mode = true;
|
||||
} else {
|
||||
furi_string_set(infrared->file_path, (const char*)p);
|
||||
is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path);
|
||||
const char* file_path = (const char*)p;
|
||||
is_remote_loaded = infrared_remote_load(infrared->remote, file_path);
|
||||
|
||||
if(!is_remote_loaded) {
|
||||
dialog_message_show_storage_error(
|
||||
infrared->dialogs, "Failed to load\nselected remote");
|
||||
infrared_show_error_message(infrared, "Failed to load\n\"%s\"", file_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
furi_string_set(infrared->file_path, file_path);
|
||||
}
|
||||
}
|
||||
|
15
applications/main/infrared/infrared_app.h
Normal file
15
applications/main/infrared/infrared_app.h
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @file infrared_app.h
|
||||
* @brief Infrared application - start here.
|
||||
*
|
||||
* @see infrared_app_i.h for the main application data structure and functions.
|
||||
* @see infrared_signal.h for the infrared signal library - loading, storing and transmitting signals.
|
||||
* @see infrared_remote.hl for the infrared remote library - loading, storing and manipulating remotes.
|
||||
* @see infrared_brute_force.h for the infrared brute force - loading and transmitting multiple signals.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief InfraredApp opaque type declaration.
|
||||
*/
|
||||
typedef struct InfraredApp InfraredApp;
|
288
applications/main/infrared/infrared_app_i.h
Normal file
288
applications/main/infrared/infrared_app_i.h
Normal file
@ -0,0 +1,288 @@
|
||||
/**
|
||||
* @file infrared_app_i.h
|
||||
* @brief Main Infrared application types and functions.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <assets_icons.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/button_menu.h>
|
||||
#include <gui/modules/button_panel.h>
|
||||
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <infrared_worker.h>
|
||||
|
||||
#include "infrared_app.h"
|
||||
#include "infrared_remote.h"
|
||||
#include "infrared_brute_force.h"
|
||||
#include "infrared_custom_event.h"
|
||||
|
||||
#include "scenes/infrared_scene.h"
|
||||
#include "views/infrared_progress_view.h"
|
||||
#include "views/infrared_debug_view.h"
|
||||
#include "views/infrared_move_view.h"
|
||||
|
||||
#include "rpc/rpc_app.h"
|
||||
|
||||
#define INFRARED_FILE_NAME_SIZE 100
|
||||
#define INFRARED_TEXT_STORE_NUM 2
|
||||
#define INFRARED_TEXT_STORE_SIZE 128
|
||||
|
||||
#define INFRARED_MAX_BUTTON_NAME_LENGTH 22
|
||||
#define INFRARED_MAX_REMOTE_NAME_LENGTH 22
|
||||
|
||||
#define INFRARED_APP_FOLDER ANY_PATH("infrared")
|
||||
#define INFRARED_APP_EXTENSION ".ir"
|
||||
|
||||
#define INFRARED_DEFAULT_REMOTE_NAME "Remote"
|
||||
#define INFRARED_LOG_TAG "InfraredApp"
|
||||
|
||||
/**
|
||||
* @brief Enumeration of invalid remote button indices.
|
||||
*/
|
||||
typedef enum {
|
||||
InfraredButtonIndexNone = -1, /**< No button is currently selected. */
|
||||
} InfraredButtonIndex;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of editing targets.
|
||||
*/
|
||||
typedef enum {
|
||||
InfraredEditTargetNone, /**< No editing target is selected. */
|
||||
InfraredEditTargetRemote, /**< Whole remote is selected as editing target. */
|
||||
InfraredEditTargetButton, /**< Single button is selected as editing target. */
|
||||
} InfraredEditTarget;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of editing modes.
|
||||
*/
|
||||
typedef enum {
|
||||
InfraredEditModeNone, /**< No editing mode is selected. */
|
||||
InfraredEditModeRename, /**< Rename mode is selected. */
|
||||
InfraredEditModeDelete, /**< Delete mode is selected. */
|
||||
} InfraredEditMode;
|
||||
|
||||
/**
|
||||
* @brief Infrared application state type.
|
||||
*/
|
||||
typedef struct {
|
||||
bool is_learning_new_remote; /**< Learning new remote or adding to an existing one. */
|
||||
bool is_debug_enabled; /**< Whether to enable or disable debugging features. */
|
||||
bool is_transmitting; /**< Whether a signal is currently being transmitted. */
|
||||
InfraredEditTarget edit_target : 8; /**< Selected editing target (a remote or a button). */
|
||||
InfraredEditMode edit_mode : 8; /**< Selected editing operation (rename or delete). */
|
||||
int32_t current_button_index; /**< Selected button index (move destination). */
|
||||
int32_t prev_button_index; /**< Previous button index (move source). */
|
||||
uint32_t last_transmit_time; /**< Lat time a signal was transmitted. */
|
||||
} InfraredAppState;
|
||||
|
||||
/**
|
||||
* @brief Infrared application type.
|
||||
*/
|
||||
struct InfraredApp {
|
||||
SceneManager* scene_manager; /**< Pointer to a SceneManager instance. */
|
||||
ViewDispatcher* view_dispatcher; /**< Pointer to a ViewDispatcher instance. */
|
||||
|
||||
Gui* gui; /**< Pointer to a Gui instance. */
|
||||
Storage* storage; /**< Pointer to a Storage instance. */
|
||||
DialogsApp* dialogs; /**< Pointer to a DialogsApp instance. */
|
||||
NotificationApp* notifications; /**< Pointer to a NotificationApp instance. */
|
||||
InfraredWorker* worker; /**< Used to send or receive signals. */
|
||||
InfraredRemote* remote; /**< Holds the currently loaded remote. */
|
||||
InfraredSignal* current_signal; /**< Holds the currently loaded signal. */
|
||||
InfraredBruteForce* brute_force; /**< Used for the Universal Remote feature. */
|
||||
|
||||
Submenu* submenu; /**< Standard view for displaying application menus. */
|
||||
TextInput* text_input; /**< Standard view for receiving user text input. */
|
||||
DialogEx* dialog_ex; /**< Standard view for displaying dialogs. */
|
||||
ButtonMenu* button_menu; /**< Custom view for interacting with IR remotes. */
|
||||
Popup* popup; /**< Standard view for displaying messages. */
|
||||
|
||||
ViewStack* view_stack; /**< Standard view for displaying stacked interfaces. */
|
||||
InfraredDebugView* debug_view; /**< Custom view for displaying debug information. */
|
||||
InfraredMoveView* move_view; /**< Custom view for rearranging buttons in a remote. */
|
||||
|
||||
ButtonPanel* button_panel; /**< Standard view for displaying control panels. */
|
||||
Loading* loading; /**< Standard view for informing about long operations. */
|
||||
InfraredProgressView* progress; /**< Custom view for showing brute force progress. */
|
||||
|
||||
FuriString* file_path; /**< Full path to the currently loaded file. */
|
||||
/** Arbitrary text storage for various inputs. */
|
||||
char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1];
|
||||
InfraredAppState app_state; /**< Application state. */
|
||||
|
||||
void* rpc_ctx; /**< Pointer to the RPC context object. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Enumeration of all used view types.
|
||||
*/
|
||||
typedef enum {
|
||||
InfraredViewSubmenu,
|
||||
InfraredViewTextInput,
|
||||
InfraredViewDialogEx,
|
||||
InfraredViewButtonMenu,
|
||||
InfraredViewPopup,
|
||||
InfraredViewStack,
|
||||
InfraredViewDebugView,
|
||||
InfraredViewMove,
|
||||
} InfraredView;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of all notification message types.
|
||||
*/
|
||||
typedef enum {
|
||||
InfraredNotificationMessageSuccess, /**< Play a short happy tune. */
|
||||
InfraredNotificationMessageGreenOn, /**< Turn green LED on. */
|
||||
InfraredNotificationMessageGreenOff, /**< Turn green LED off. */
|
||||
InfraredNotificationMessageYellowOn, /**< Turn yellow LED on. */
|
||||
InfraredNotificationMessageYellowOff, /**< Turn yellow LED off. */
|
||||
InfraredNotificationMessageBlinkStartRead, /**< Blink the LED to indicate receiver mode. */
|
||||
InfraredNotificationMessageBlinkStartSend, /**< Blink the LED to indicate transmitter mode. */
|
||||
InfraredNotificationMessageBlinkStop, /**< Stop blinking the LED. */
|
||||
InfraredNotificationMessageCount, /**< Special value equal to the message type count. */
|
||||
} InfraredNotificationMessage;
|
||||
|
||||
/**
|
||||
* @brief Add a new remote with a single signal.
|
||||
*
|
||||
* The filename will be automatically generated depending on
|
||||
* the names and number of other files in the infrared data directory.
|
||||
*
|
||||
* @param[in] infrared pointer to the application instance.
|
||||
* @param[in] name pointer to a zero-terminated string containing the signal name.
|
||||
* @param[in] signal pointer to the signal to be added.
|
||||
* @return true if the remote was successfully created, false otherwise.
|
||||
*/
|
||||
bool infrared_add_remote_with_button(
|
||||
const InfraredApp* infrared,
|
||||
const char* name,
|
||||
const InfraredSignal* signal);
|
||||
|
||||
/**
|
||||
* @brief Rename the currently loaded remote.
|
||||
*
|
||||
* @param[in] infrared pointer to the application instance.
|
||||
* @param[in] new_name pointer to a zero-terminated string containing the new remote name.
|
||||
* @return true if the remote was successfully renamed, false otherwise.
|
||||
*/
|
||||
bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name);
|
||||
|
||||
/**
|
||||
* @brief Begin transmission of the currently loaded signal.
|
||||
*
|
||||
* The signal will be repeated indefinitely until stopped.
|
||||
*
|
||||
* @param[in,out] infrared pointer to the application instance.
|
||||
*/
|
||||
void infrared_tx_start(InfraredApp* infrared);
|
||||
|
||||
/**
|
||||
* @brief Load a signal under the given index and begin transmission.
|
||||
*
|
||||
* The signal will be repeated indefinitely until stopped.
|
||||
*
|
||||
* @param[in,out] infrared pointer to the application instance.
|
||||
* @param[in] button_index index of the signal to be loaded.
|
||||
* @returns true if the signal could be loaded, false otherwise.
|
||||
*/
|
||||
void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index);
|
||||
|
||||
/**
|
||||
* @brief Stop transmission of the currently loaded signal.
|
||||
*
|
||||
* @param[in,out] infrared pointer to the application instance.
|
||||
*/
|
||||
void infrared_tx_stop(InfraredApp* infrared);
|
||||
|
||||
/**
|
||||
* @brief Set the internal text store with formatted text.
|
||||
*
|
||||
* @param[in,out] infrared pointer to the application instance.
|
||||
* @param[in] bank index of text store bank (0 or 1).
|
||||
* @param[in] fmt pointer to a zero-terminated string containing the format text.
|
||||
* @param[in] ... additional arguments.
|
||||
*/
|
||||
void infrared_text_store_set(InfraredApp* infrared, uint32_t bank, const char* fmt, ...)
|
||||
_ATTRIBUTE((__format__(__printf__, 3, 4)));
|
||||
|
||||
/**
|
||||
* @brief Clear the internal text store.
|
||||
*
|
||||
* @param[in,out] infrared pointer to the application instance.
|
||||
* @param[in] bank index of text store bank (0 or 1).
|
||||
*/
|
||||
void infrared_text_store_clear(InfraredApp* infrared, uint32_t bank);
|
||||
|
||||
/**
|
||||
* @brief Play a sound and/or blink the LED.
|
||||
*
|
||||
* @param[in] infrared pointer to the application instance.
|
||||
* @param[in] message type of the message to play.
|
||||
*/
|
||||
void infrared_play_notification_message(
|
||||
const InfraredApp* infrared,
|
||||
InfraredNotificationMessage message);
|
||||
|
||||
/**
|
||||
* @brief Show a loading pop-up screen.
|
||||
*
|
||||
* In order for this to work, a Stack view must be currently active and
|
||||
* the main view must be added to it.
|
||||
*
|
||||
* @param[in] infrared pointer to the application instance.
|
||||
* @param[in] show whether to show or hide the pop-up.
|
||||
*/
|
||||
void infrared_show_loading_popup(const InfraredApp* infrared, bool show);
|
||||
|
||||
/**
|
||||
* @brief Show a formatted error messsage.
|
||||
*
|
||||
* @param[in] infrared pointer to the application instance.
|
||||
* @param[in] fmt pointer to a zero-terminated string containing the format text.
|
||||
* @param[in] ... additional arguments.
|
||||
*/
|
||||
void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...)
|
||||
_ATTRIBUTE((__format__(__printf__, 2, 3)));
|
||||
|
||||
/**
|
||||
* @brief Common received signal callback.
|
||||
*
|
||||
* Called when the worker has received a complete infrared signal.
|
||||
*
|
||||
* @param[in,out] context pointer to the user-specified context object.
|
||||
* @param[in] received_signal pointer to the received signal.
|
||||
*/
|
||||
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal);
|
||||
|
||||
/**
|
||||
* @brief Common text input callback.
|
||||
*
|
||||
* Called when the input has been accepted by the user.
|
||||
*
|
||||
* @param[in,out] context pointer to the user-specified context object.
|
||||
*/
|
||||
void infrared_text_input_callback(void* context);
|
||||
|
||||
/**
|
||||
* @brief Common popup close callback.
|
||||
*
|
||||
* Called when the popup has been closed either by the user or after a timeout.
|
||||
*
|
||||
* @param[in,out] context pointer to the user-specified context object.
|
||||
*/
|
||||
void infrared_popup_closed_callback(void* context);
|
@ -111,7 +111,7 @@ bool infrared_brute_force_start(
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_brute_force_is_started(InfraredBruteForce* brute_force) {
|
||||
bool infrared_brute_force_is_started(const InfraredBruteForce* brute_force) {
|
||||
return brute_force->is_started;
|
||||
}
|
||||
|
||||
@ -129,7 +129,9 @@ void infrared_brute_force_stop(InfraredBruteForce* brute_force) {
|
||||
bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) {
|
||||
furi_assert(brute_force->is_started);
|
||||
const bool success = infrared_signal_search_and_read(
|
||||
brute_force->current_signal, brute_force->ff, brute_force->current_record_name);
|
||||
brute_force->current_signal,
|
||||
brute_force->ff,
|
||||
furi_string_get_cstr(brute_force->current_record_name));
|
||||
if(success) {
|
||||
infrared_signal_transmit(brute_force->current_signal);
|
||||
}
|
||||
|
@ -1,23 +1,110 @@
|
||||
/**
|
||||
* @file infrared_brute_force.h
|
||||
* @brief Infrared signal brute-forcing library.
|
||||
*
|
||||
* The BruteForce library is used to send large quantities of signals,
|
||||
* sorted by a category. It is used to implement the Universal Remote
|
||||
* feature.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief InfraredBruteForce opaque type declaration.
|
||||
*/
|
||||
typedef struct InfraredBruteForce InfraredBruteForce;
|
||||
|
||||
/**
|
||||
* @brief Create a new InfraredBruteForce instance.
|
||||
*
|
||||
* @returns pointer to the created instance.
|
||||
*/
|
||||
InfraredBruteForce* infrared_brute_force_alloc();
|
||||
|
||||
/**
|
||||
* @brief Delete an InfraredBruteForce instance.
|
||||
*
|
||||
* @param[in,out] brute_force pointer to the instance to be deleted.
|
||||
*/
|
||||
void infrared_brute_force_free(InfraredBruteForce* brute_force);
|
||||
|
||||
/**
|
||||
* @brief Set an InfraredBruteForce instance to use a signal database contained in a file.
|
||||
*
|
||||
* @param[in,out] brute_force pointer to the instance to be configured.
|
||||
* @param[in] db_filename pointer to a zero-terminated string containing a full path to the database file.
|
||||
*/
|
||||
void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename);
|
||||
|
||||
/**
|
||||
* @brief Build a signal dictionary from a previously set database file.
|
||||
*
|
||||
* This function must be called each time after setting the database via
|
||||
* a infrared_brute_force_set_db_filename() call.
|
||||
*
|
||||
* @param[in,out] brute_force pointer to the instance to be updated.
|
||||
* @returns true on success, false otherwise.
|
||||
*/
|
||||
bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force);
|
||||
|
||||
/**
|
||||
* @brief Start transmitting signals from a category stored in an InfraredBruteForce's instance dictionary.
|
||||
*
|
||||
* @param[in,out] brute_force pointer to the instance to be started.
|
||||
* @param[in] index index of the signal category in the dictionary.
|
||||
* @returns true on success, false otherwise.
|
||||
*/
|
||||
bool infrared_brute_force_start(
|
||||
InfraredBruteForce* brute_force,
|
||||
uint32_t index,
|
||||
uint32_t* record_count);
|
||||
bool infrared_brute_force_is_started(InfraredBruteForce* brute_force);
|
||||
|
||||
/**
|
||||
* @brief Determine whether the transmission was started.
|
||||
*
|
||||
* @param[in] brute_force pointer to the instance to be tested.
|
||||
* @returns true if transmission was started, false otherwise.
|
||||
*/
|
||||
bool infrared_brute_force_is_started(const InfraredBruteForce* brute_force);
|
||||
|
||||
/**
|
||||
* @brief Stop transmitting the signals.
|
||||
*
|
||||
* @param[in] brute_force pointer to the instance to be stopped.
|
||||
*/
|
||||
void infrared_brute_force_stop(InfraredBruteForce* brute_force);
|
||||
|
||||
/**
|
||||
* @brief Send the next signal from the chosen category.
|
||||
*
|
||||
* This function is called repeatedly until no more signals are left
|
||||
* in the chosen signal category.
|
||||
*
|
||||
* @warning Transmission must be started first by calling infrared_brute_force_start()
|
||||
* before calling this function.
|
||||
*
|
||||
* @param[in,out] brute_force pointer to the instance to be used.
|
||||
* @returns true if the next signal existed and could be transmitted, false otherwise.
|
||||
*/
|
||||
bool infrared_brute_force_send_next(InfraredBruteForce* brute_force);
|
||||
|
||||
/**
|
||||
* @brief Add a signal category to an InfraredBruteForce instance's dictionary.
|
||||
*
|
||||
* @param[in,out] brute_force pointer to the instance to be updated.
|
||||
* @param[in] index index of the category to be added.
|
||||
* @param[in] name name of the category to be added.
|
||||
*/
|
||||
void infrared_brute_force_add_record(
|
||||
InfraredBruteForce* brute_force,
|
||||
uint32_t index,
|
||||
const char* name);
|
||||
|
||||
/**
|
||||
* @brief Reset an InfraredBruteForce instance.
|
||||
*
|
||||
* @param[in,out] brute_force pointer to the instance to be reset.
|
||||
*/
|
||||
void infrared_brute_force_reset(InfraredBruteForce* brute_force);
|
||||
|
@ -202,7 +202,7 @@ static bool
|
||||
}
|
||||
|
||||
static bool infrared_cli_decode_raw_signal(
|
||||
InfraredRawSignal* raw_signal,
|
||||
const InfraredRawSignal* raw_signal,
|
||||
InfraredDecoderHandler* decoder,
|
||||
FlipperFormat* output_file,
|
||||
const char* signal_name) {
|
||||
@ -274,7 +274,7 @@ static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* o
|
||||
continue;
|
||||
}
|
||||
}
|
||||
InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
|
||||
const InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
|
||||
printf(
|
||||
"Raw signal: %s, %zu samples\r\n",
|
||||
furi_string_get_cstr(tmp),
|
||||
|
@ -1,146 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <assets_icons.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/loading.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/button_menu.h>
|
||||
#include <gui/modules/button_panel.h>
|
||||
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <infrared_worker.h>
|
||||
|
||||
#include "infrared.h"
|
||||
#include "infrared_remote.h"
|
||||
#include "infrared_brute_force.h"
|
||||
#include "infrared_custom_event.h"
|
||||
|
||||
#include "scenes/infrared_scene.h"
|
||||
#include "views/infrared_progress_view.h"
|
||||
#include "views/infrared_debug_view.h"
|
||||
#include "views/infrared_move_view.h"
|
||||
|
||||
#include "rpc/rpc_app.h"
|
||||
|
||||
#define INFRARED_FILE_NAME_SIZE 100
|
||||
#define INFRARED_TEXT_STORE_NUM 2
|
||||
#define INFRARED_TEXT_STORE_SIZE 128
|
||||
|
||||
#define INFRARED_MAX_BUTTON_NAME_LENGTH 22
|
||||
#define INFRARED_MAX_REMOTE_NAME_LENGTH 22
|
||||
|
||||
#define INFRARED_APP_FOLDER ANY_PATH("infrared")
|
||||
#define INFRARED_APP_EXTENSION ".ir"
|
||||
|
||||
#define INFRARED_DEFAULT_REMOTE_NAME "Remote"
|
||||
#define INFRARED_LOG_TAG "InfraredApp"
|
||||
|
||||
typedef enum {
|
||||
InfraredButtonIndexNone = -1,
|
||||
} InfraredButtonIndex;
|
||||
|
||||
typedef enum {
|
||||
InfraredEditTargetNone,
|
||||
InfraredEditTargetRemote,
|
||||
InfraredEditTargetButton,
|
||||
} InfraredEditTarget;
|
||||
|
||||
typedef enum {
|
||||
InfraredEditModeNone,
|
||||
InfraredEditModeRename,
|
||||
InfraredEditModeDelete,
|
||||
} InfraredEditMode;
|
||||
|
||||
typedef struct {
|
||||
bool is_learning_new_remote;
|
||||
bool is_debug_enabled;
|
||||
bool is_transmitting;
|
||||
InfraredEditTarget edit_target : 8;
|
||||
InfraredEditMode edit_mode : 8;
|
||||
int32_t current_button_index;
|
||||
int32_t current_button_index_move_orig;
|
||||
uint32_t last_transmit_time;
|
||||
} InfraredAppState;
|
||||
|
||||
struct Infrared {
|
||||
SceneManager* scene_manager;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
|
||||
Gui* gui;
|
||||
Storage* storage;
|
||||
DialogsApp* dialogs;
|
||||
NotificationApp* notifications;
|
||||
InfraredWorker* worker;
|
||||
InfraredRemote* remote;
|
||||
InfraredSignal* received_signal;
|
||||
InfraredBruteForce* brute_force;
|
||||
|
||||
Submenu* submenu;
|
||||
TextInput* text_input;
|
||||
DialogEx* dialog_ex;
|
||||
ButtonMenu* button_menu;
|
||||
Popup* popup;
|
||||
|
||||
ViewStack* view_stack;
|
||||
InfraredDebugView* debug_view;
|
||||
InfraredMoveView* move_view;
|
||||
|
||||
ButtonPanel* button_panel;
|
||||
Loading* loading;
|
||||
InfraredProgressView* progress;
|
||||
|
||||
FuriString* file_path;
|
||||
char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1];
|
||||
InfraredAppState app_state;
|
||||
|
||||
void* rpc_ctx;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
InfraredViewSubmenu,
|
||||
InfraredViewTextInput,
|
||||
InfraredViewDialogEx,
|
||||
InfraredViewButtonMenu,
|
||||
InfraredViewPopup,
|
||||
InfraredViewStack,
|
||||
InfraredViewDebugView,
|
||||
InfraredViewMove,
|
||||
} InfraredView;
|
||||
|
||||
typedef enum {
|
||||
InfraredNotificationMessageSuccess,
|
||||
InfraredNotificationMessageGreenOn,
|
||||
InfraredNotificationMessageGreenOff,
|
||||
InfraredNotificationMessageYellowOn,
|
||||
InfraredNotificationMessageYellowOff,
|
||||
InfraredNotificationMessageBlinkStartRead,
|
||||
InfraredNotificationMessageBlinkStartSend,
|
||||
InfraredNotificationMessageBlinkStop,
|
||||
} InfraredNotificationMessage;
|
||||
|
||||
bool infrared_add_remote_with_button(Infrared* infrared, const char* name, InfraredSignal* signal);
|
||||
bool infrared_rename_current_remote(Infrared* infrared, const char* name);
|
||||
void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal);
|
||||
void infrared_tx_start_button_index(Infrared* infrared, size_t button_index);
|
||||
void infrared_tx_start_received(Infrared* infrared);
|
||||
void infrared_tx_stop(Infrared* infrared);
|
||||
void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...);
|
||||
void infrared_text_store_clear(Infrared* infrared, uint32_t bank);
|
||||
void infrared_play_notification_message(Infrared* infrared, uint32_t message);
|
||||
void infrared_show_loading_popup(Infrared* infrared, bool show);
|
||||
|
||||
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal);
|
||||
void infrared_text_input_callback(void* context);
|
||||
void infrared_popup_closed_callback(void* context);
|
@ -1,197 +1,428 @@
|
||||
#include "infrared_remote.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#include <toolbox/m_cstr_dup.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <storage/storage.h>
|
||||
#include <core/common_defines.h>
|
||||
|
||||
#define TAG "InfraredRemote"
|
||||
|
||||
ARRAY_DEF(InfraredButtonArray, InfraredRemoteButton*, M_PTR_OPLIST);
|
||||
#define INFRARED_FILE_HEADER "IR signals file"
|
||||
#define INFRARED_FILE_VERSION (1)
|
||||
|
||||
ARRAY_DEF(StringArray, const char*, M_CSTR_DUP_OPLIST); //-V575
|
||||
|
||||
struct InfraredRemote {
|
||||
InfraredButtonArray_t buttons;
|
||||
StringArray_t signal_names;
|
||||
FuriString* name;
|
||||
FuriString* path;
|
||||
};
|
||||
|
||||
static void infrared_remote_clear_buttons(InfraredRemote* remote) {
|
||||
InfraredButtonArray_it_t it;
|
||||
for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it);
|
||||
InfraredButtonArray_next(it)) {
|
||||
infrared_remote_button_free(*InfraredButtonArray_cref(it));
|
||||
}
|
||||
InfraredButtonArray_reset(remote->buttons);
|
||||
}
|
||||
typedef struct {
|
||||
InfraredRemote* remote;
|
||||
FlipperFormat* ff_in;
|
||||
FlipperFormat* ff_out;
|
||||
FuriString* signal_name;
|
||||
InfraredSignal* signal;
|
||||
size_t signal_index;
|
||||
} InfraredBatch;
|
||||
|
||||
typedef struct {
|
||||
size_t signal_index;
|
||||
const char* signal_name;
|
||||
const InfraredSignal* signal;
|
||||
} InfraredBatchTarget;
|
||||
|
||||
typedef bool (
|
||||
*InfraredBatchCallback)(const InfraredBatch* batch, const InfraredBatchTarget* target);
|
||||
|
||||
InfraredRemote* infrared_remote_alloc() {
|
||||
InfraredRemote* remote = malloc(sizeof(InfraredRemote));
|
||||
InfraredButtonArray_init(remote->buttons);
|
||||
StringArray_init(remote->signal_names);
|
||||
remote->name = furi_string_alloc();
|
||||
remote->path = furi_string_alloc();
|
||||
return remote;
|
||||
}
|
||||
|
||||
void infrared_remote_free(InfraredRemote* remote) {
|
||||
infrared_remote_clear_buttons(remote);
|
||||
InfraredButtonArray_clear(remote->buttons);
|
||||
StringArray_clear(remote->signal_names);
|
||||
furi_string_free(remote->path);
|
||||
furi_string_free(remote->name);
|
||||
free(remote);
|
||||
}
|
||||
|
||||
void infrared_remote_reset(InfraredRemote* remote) {
|
||||
infrared_remote_clear_buttons(remote);
|
||||
StringArray_reset(remote->signal_names);
|
||||
furi_string_reset(remote->name);
|
||||
furi_string_reset(remote->path);
|
||||
}
|
||||
|
||||
void infrared_remote_set_name(InfraredRemote* remote, const char* name) {
|
||||
furi_string_set(remote->name, name);
|
||||
}
|
||||
|
||||
const char* infrared_remote_get_name(InfraredRemote* remote) {
|
||||
const char* infrared_remote_get_name(const InfraredRemote* remote) {
|
||||
return furi_string_get_cstr(remote->name);
|
||||
}
|
||||
|
||||
void infrared_remote_set_path(InfraredRemote* remote, const char* path) {
|
||||
static void infrared_remote_set_path(InfraredRemote* remote, const char* path) {
|
||||
furi_string_set(remote->path, path);
|
||||
path_extract_filename(remote->path, remote->name, true);
|
||||
}
|
||||
|
||||
const char* infrared_remote_get_path(InfraredRemote* remote) {
|
||||
const char* infrared_remote_get_path(const InfraredRemote* remote) {
|
||||
return furi_string_get_cstr(remote->path);
|
||||
}
|
||||
|
||||
size_t infrared_remote_get_button_count(InfraredRemote* remote) {
|
||||
return InfraredButtonArray_size(remote->buttons);
|
||||
size_t infrared_remote_get_signal_count(const InfraredRemote* remote) {
|
||||
return StringArray_size(remote->signal_names);
|
||||
}
|
||||
|
||||
InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index) {
|
||||
furi_assert(index < InfraredButtonArray_size(remote->buttons));
|
||||
return *InfraredButtonArray_get(remote->buttons, index);
|
||||
const char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t index) {
|
||||
furi_assert(index < infrared_remote_get_signal_count(remote));
|
||||
return *StringArray_cget(remote->signal_names, index);
|
||||
}
|
||||
|
||||
bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) {
|
||||
for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) {
|
||||
InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i);
|
||||
if(!strcmp(infrared_remote_button_get_name(button), name)) {
|
||||
bool infrared_remote_load_signal(
|
||||
const InfraredRemote* remote,
|
||||
InfraredSignal* signal,
|
||||
size_t index) {
|
||||
furi_assert(index < infrared_remote_get_signal_count(remote));
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
|
||||
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
const char* path = furi_string_get_cstr(remote->path);
|
||||
if(!flipper_format_buffered_file_open_existing(ff, path)) break;
|
||||
|
||||
const char* name = infrared_remote_get_signal_name(remote, index);
|
||||
|
||||
if(!infrared_signal_search_and_read(signal, ff, name)) {
|
||||
FURI_LOG_E(TAG, "Failed to load signal '%s' from file '%s'", name, path);
|
||||
break;
|
||||
}
|
||||
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_remote_get_signal_index(
|
||||
const InfraredRemote* remote,
|
||||
const char* name,
|
||||
size_t* index) {
|
||||
uint32_t i = 0;
|
||||
StringArray_it_t it;
|
||||
|
||||
for(StringArray_it(it, remote->signal_names); !StringArray_end_p(it);
|
||||
StringArray_next(it), ++i) {
|
||||
if(strcmp(*StringArray_cref(it), name) == 0) {
|
||||
*index = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) {
|
||||
InfraredRemoteButton* button = infrared_remote_button_alloc();
|
||||
infrared_remote_button_set_name(button, name);
|
||||
infrared_remote_button_set_signal(button, signal);
|
||||
InfraredButtonArray_push_back(remote->buttons, button);
|
||||
return infrared_remote_store(remote);
|
||||
}
|
||||
|
||||
bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index) {
|
||||
furi_assert(index < InfraredButtonArray_size(remote->buttons));
|
||||
InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, index);
|
||||
infrared_remote_button_set_name(button, new_name);
|
||||
return infrared_remote_store(remote);
|
||||
}
|
||||
|
||||
bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) {
|
||||
furi_assert(index < InfraredButtonArray_size(remote->buttons));
|
||||
InfraredRemoteButton* button;
|
||||
InfraredButtonArray_pop_at(&button, remote->buttons, index);
|
||||
infrared_remote_button_free(button);
|
||||
return infrared_remote_store(remote);
|
||||
}
|
||||
|
||||
void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest) {
|
||||
furi_assert(index_orig < InfraredButtonArray_size(remote->buttons));
|
||||
furi_assert(index_dest < InfraredButtonArray_size(remote->buttons));
|
||||
|
||||
InfraredRemoteButton* button;
|
||||
InfraredButtonArray_pop_at(&button, remote->buttons, index_orig);
|
||||
InfraredButtonArray_push_at(remote->buttons, index_dest, button);
|
||||
}
|
||||
|
||||
bool infrared_remote_store(InfraredRemote* remote) {
|
||||
bool infrared_remote_append_signal(
|
||||
InfraredRemote* remote,
|
||||
const InfraredSignal* signal,
|
||||
const char* name) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* ff = flipper_format_file_alloc(storage);
|
||||
|
||||
bool success = false;
|
||||
const char* path = furi_string_get_cstr(remote->path);
|
||||
|
||||
FURI_LOG_I(TAG, "store file: \'%s\'", path);
|
||||
|
||||
bool success = flipper_format_file_open_always(ff, path) &&
|
||||
flipper_format_write_header_cstr(ff, "IR signals file", 1);
|
||||
if(success) {
|
||||
InfraredButtonArray_it_t it;
|
||||
for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it);
|
||||
InfraredButtonArray_next(it)) {
|
||||
InfraredRemoteButton* button = *InfraredButtonArray_cref(it);
|
||||
success = infrared_signal_save(
|
||||
infrared_remote_button_get_signal(button),
|
||||
ff,
|
||||
infrared_remote_button_get_name(button));
|
||||
if(!success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flipper_format_free(ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_remote_load(InfraredRemote* remote, FuriString* path) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
|
||||
|
||||
FuriString* buf;
|
||||
buf = furi_string_alloc();
|
||||
|
||||
FURI_LOG_I(TAG, "load file: \'%s\'", furi_string_get_cstr(path));
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(path))) break;
|
||||
uint32_t version;
|
||||
if(!flipper_format_read_header(ff, buf, &version)) break;
|
||||
if(!furi_string_equal(buf, "IR signals file") || (version != 1)) break;
|
||||
if(!flipper_format_file_open_append(ff, path)) break;
|
||||
if(!infrared_signal_save(signal, ff, name)) break;
|
||||
|
||||
path_extract_filename(path, buf, true);
|
||||
infrared_remote_clear_buttons(remote);
|
||||
infrared_remote_set_name(remote, furi_string_get_cstr(buf));
|
||||
infrared_remote_set_path(remote, furi_string_get_cstr(path));
|
||||
|
||||
for(bool can_read = true; can_read;) {
|
||||
InfraredRemoteButton* button = infrared_remote_button_alloc();
|
||||
can_read = infrared_signal_read(infrared_remote_button_get_signal(button), ff, buf);
|
||||
if(can_read) {
|
||||
infrared_remote_button_set_name(button, furi_string_get_cstr(buf));
|
||||
InfraredButtonArray_push_back(remote->buttons, button);
|
||||
} else {
|
||||
infrared_remote_button_free(button);
|
||||
}
|
||||
}
|
||||
StringArray_push_back(remote->signal_names, name);
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(buf);
|
||||
flipper_format_free(ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool infrared_remote_batch_start(
|
||||
InfraredRemote* remote,
|
||||
InfraredBatchCallback batch_callback,
|
||||
const InfraredBatchTarget* target) {
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
InfraredBatch batch_context = {
|
||||
.remote = remote,
|
||||
.ff_in = flipper_format_buffered_file_alloc(storage),
|
||||
.ff_out = flipper_format_buffered_file_alloc(storage),
|
||||
.signal_name = furi_string_alloc(),
|
||||
.signal = infrared_signal_alloc(),
|
||||
.signal_index = 0,
|
||||
};
|
||||
|
||||
const char* path_in = furi_string_get_cstr(remote->path);
|
||||
const char* path_out;
|
||||
|
||||
FS_Error status;
|
||||
|
||||
do {
|
||||
furi_string_printf(tmp, "%s.temp%08x.swp", path_in, rand());
|
||||
path_out = furi_string_get_cstr(tmp);
|
||||
status = storage_common_stat(storage, path_out, NULL);
|
||||
} while(status == FSE_OK || status == FSE_EXIST);
|
||||
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_buffered_file_open_existing(batch_context.ff_in, path_in)) break;
|
||||
if(!flipper_format_buffered_file_open_always(batch_context.ff_out, path_out)) break;
|
||||
if(!flipper_format_write_header_cstr(
|
||||
batch_context.ff_out, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION))
|
||||
break;
|
||||
|
||||
const size_t signal_count = infrared_remote_get_signal_count(remote);
|
||||
|
||||
for(; batch_context.signal_index < signal_count; ++batch_context.signal_index) {
|
||||
if(!infrared_signal_read(
|
||||
batch_context.signal, batch_context.ff_in, batch_context.signal_name))
|
||||
break;
|
||||
if(!batch_callback(&batch_context, target)) break;
|
||||
}
|
||||
|
||||
if(batch_context.signal_index != signal_count) break;
|
||||
|
||||
if(!flipper_format_buffered_file_close(batch_context.ff_out)) break;
|
||||
if(!flipper_format_buffered_file_close(batch_context.ff_in)) break;
|
||||
|
||||
const FS_Error status = storage_common_rename(storage, path_out, path_in);
|
||||
success = (status == FSE_OK || status == FSE_EXIST);
|
||||
} while(false);
|
||||
|
||||
infrared_signal_free(batch_context.signal);
|
||||
furi_string_free(batch_context.signal_name);
|
||||
flipper_format_free(batch_context.ff_out);
|
||||
flipper_format_free(batch_context.ff_in);
|
||||
furi_string_free(tmp);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool infrared_remote_insert_signal_callback(
|
||||
const InfraredBatch* batch,
|
||||
const InfraredBatchTarget* target) {
|
||||
// Insert a signal under the specified index
|
||||
if(batch->signal_index == target->signal_index) {
|
||||
if(!infrared_signal_save(target->signal, batch->ff_out, target->signal_name)) return false;
|
||||
StringArray_push_at(
|
||||
batch->remote->signal_names, target->signal_index, target->signal_name);
|
||||
}
|
||||
|
||||
// Write the rest normally
|
||||
return infrared_signal_save(
|
||||
batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name));
|
||||
}
|
||||
|
||||
bool infrared_remote_insert_signal(
|
||||
InfraredRemote* remote,
|
||||
const InfraredSignal* signal,
|
||||
const char* name,
|
||||
size_t index) {
|
||||
if(index >= infrared_remote_get_signal_count(remote)) {
|
||||
return infrared_remote_append_signal(remote, signal, name);
|
||||
}
|
||||
|
||||
const InfraredBatchTarget insert_target = {
|
||||
.signal_index = index,
|
||||
.signal_name = name,
|
||||
.signal = signal,
|
||||
};
|
||||
|
||||
return infrared_remote_batch_start(
|
||||
remote, infrared_remote_insert_signal_callback, &insert_target);
|
||||
}
|
||||
|
||||
static bool infrared_remote_rename_signal_callback(
|
||||
const InfraredBatch* batch,
|
||||
const InfraredBatchTarget* target) {
|
||||
const char* signal_name;
|
||||
|
||||
if(batch->signal_index == target->signal_index) {
|
||||
// Rename the signal at requested index
|
||||
signal_name = target->signal_name;
|
||||
StringArray_set_at(batch->remote->signal_names, batch->signal_index, signal_name);
|
||||
} else {
|
||||
// Use the original name otherwise
|
||||
signal_name = furi_string_get_cstr(batch->signal_name);
|
||||
}
|
||||
|
||||
return infrared_signal_save(batch->signal, batch->ff_out, signal_name);
|
||||
}
|
||||
|
||||
bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name) {
|
||||
furi_assert(index < infrared_remote_get_signal_count(remote));
|
||||
|
||||
const InfraredBatchTarget rename_target = {
|
||||
.signal_index = index,
|
||||
.signal_name = new_name,
|
||||
.signal = NULL,
|
||||
};
|
||||
|
||||
return infrared_remote_batch_start(
|
||||
remote, infrared_remote_rename_signal_callback, &rename_target);
|
||||
}
|
||||
|
||||
static bool infrared_remote_delete_signal_callback(
|
||||
const InfraredBatch* batch,
|
||||
const InfraredBatchTarget* target) {
|
||||
if(batch->signal_index == target->signal_index) {
|
||||
// Do not save the signal to be deleted, remove it from the signal name list instead
|
||||
StringArray_remove_v(
|
||||
batch->remote->signal_names, batch->signal_index, batch->signal_index + 1);
|
||||
} else {
|
||||
// Pass other signals through
|
||||
return infrared_signal_save(
|
||||
batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index) {
|
||||
furi_assert(index < infrared_remote_get_signal_count(remote));
|
||||
|
||||
const InfraredBatchTarget delete_target = {
|
||||
.signal_index = index,
|
||||
.signal_name = NULL,
|
||||
.signal = NULL,
|
||||
};
|
||||
|
||||
return infrared_remote_batch_start(
|
||||
remote, infrared_remote_delete_signal_callback, &delete_target);
|
||||
}
|
||||
|
||||
bool infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index) {
|
||||
const size_t signal_count = infrared_remote_get_signal_count(remote);
|
||||
furi_assert(index < signal_count);
|
||||
furi_assert(new_index < signal_count);
|
||||
|
||||
if(index == new_index) return true;
|
||||
|
||||
InfraredSignal* signal = infrared_signal_alloc();
|
||||
char* signal_name = strdup(infrared_remote_get_signal_name(remote, index));
|
||||
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!infrared_remote_load_signal(remote, signal, index)) break;
|
||||
if(!infrared_remote_delete_signal(remote, index)) break;
|
||||
if(!infrared_remote_insert_signal(remote, signal, signal_name, new_index)) break;
|
||||
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
free(signal_name);
|
||||
infrared_signal_free(signal);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_remote_create(InfraredRemote* remote, const char* path) {
|
||||
FURI_LOG_I(TAG, "Creating new file: '%s'", path);
|
||||
|
||||
infrared_remote_reset(remote);
|
||||
infrared_remote_set_path(remote, path);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* ff = flipper_format_file_alloc(storage);
|
||||
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_always(ff, path)) break;
|
||||
if(!flipper_format_write_header_cstr(ff, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION))
|
||||
break;
|
||||
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_remote_load(InfraredRemote* remote, const char* path) {
|
||||
FURI_LOG_I(TAG, "Loading file: '%s'", path);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
|
||||
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_buffered_file_open_existing(ff, path)) break;
|
||||
|
||||
uint32_t version;
|
||||
if(!flipper_format_read_header(ff, tmp, &version)) break;
|
||||
|
||||
if(!furi_string_equal(tmp, INFRARED_FILE_HEADER) || (version != INFRARED_FILE_VERSION))
|
||||
break;
|
||||
|
||||
infrared_remote_set_path(remote, path);
|
||||
StringArray_reset(remote->signal_names);
|
||||
|
||||
while(infrared_signal_read_name(ff, tmp)) {
|
||||
StringArray_push_back(remote->signal_names, furi_string_get_cstr(tmp));
|
||||
}
|
||||
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(tmp);
|
||||
flipper_format_free(ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_remote_rename(InfraredRemote* remote, const char* new_path) {
|
||||
const char* old_path = infrared_remote_get_path(remote);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
const FS_Error status = storage_common_rename(storage, old_path, new_path);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
const bool success = (status == FSE_OK || status == FSE_EXIST);
|
||||
|
||||
if(success) {
|
||||
infrared_remote_set_path(remote, new_path);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_remote_remove(InfraredRemote* remote) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
FS_Error status = storage_common_remove(storage, furi_string_get_cstr(remote->path));
|
||||
infrared_remote_reset(remote);
|
||||
|
||||
const FS_Error status = storage_common_remove(storage, infrared_remote_get_path(remote));
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return (status == FSE_OK || status == FSE_NOT_EXIST);
|
||||
|
||||
const bool success = (status == FSE_OK || status == FSE_NOT_EXIST);
|
||||
|
||||
if(success) {
|
||||
infrared_remote_reset(remote);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
@ -1,30 +1,229 @@
|
||||
/**
|
||||
* @file infrared_remote.h
|
||||
* @brief Infrared remote library.
|
||||
*
|
||||
* An infrared remote contains zero or more infrared signals which
|
||||
* have a (possibly non-unique) name each.
|
||||
*
|
||||
* The current implementation does load only the names into the memory,
|
||||
* while the signals themselves are loaded on-demand one by one. In theory,
|
||||
* this should allow for quite large remotes with relatively bulky signals.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "infrared_remote_button.h"
|
||||
#include "infrared_signal.h"
|
||||
|
||||
/**
|
||||
* @brief InfraredRemote opaque type declaration.
|
||||
*/
|
||||
typedef struct InfraredRemote InfraredRemote;
|
||||
|
||||
/**
|
||||
* @brief Create a new InfraredRemote instance.
|
||||
*
|
||||
* @returns pointer to the created instance.
|
||||
*/
|
||||
InfraredRemote* infrared_remote_alloc();
|
||||
|
||||
/**
|
||||
* @brief Delete an InfraredRemote instance.
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to be deleted.
|
||||
*/
|
||||
void infrared_remote_free(InfraredRemote* remote);
|
||||
|
||||
/**
|
||||
* @brief Reset an InfraredRemote instance.
|
||||
*
|
||||
* Resetting a remote clears its signal name list and
|
||||
* the associated file path.
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to be deleted.
|
||||
*/
|
||||
void infrared_remote_reset(InfraredRemote* remote);
|
||||
|
||||
void infrared_remote_set_name(InfraredRemote* remote, const char* name);
|
||||
const char* infrared_remote_get_name(InfraredRemote* remote);
|
||||
/**
|
||||
* @brief Get an InfraredRemote instance's name.
|
||||
*
|
||||
* The name is deduced from the file path.
|
||||
*
|
||||
* The return value remains valid unless one of the following functions is called:
|
||||
* - infrared_remote_reset()
|
||||
* - infrared_remote_load()
|
||||
* - infrared_remote_create()
|
||||
*
|
||||
* @param[in] remote pointer to the instance to be queried.
|
||||
* @returns pointer to a zero-terminated string containing the name.
|
||||
*/
|
||||
const char* infrared_remote_get_name(const InfraredRemote* remote);
|
||||
|
||||
void infrared_remote_set_path(InfraredRemote* remote, const char* path);
|
||||
const char* infrared_remote_get_path(InfraredRemote* remote);
|
||||
/**
|
||||
* @brief Get an InfraredRemote instance's file path.
|
||||
*
|
||||
* Same return value validity considerations as infrared_remote_get_name().
|
||||
*
|
||||
* @param[in] remote pointer to the instance to be queried.
|
||||
* @returns pointer to a zero-terminated string containing the path.
|
||||
*/
|
||||
const char* infrared_remote_get_path(const InfraredRemote* remote);
|
||||
|
||||
size_t infrared_remote_get_button_count(InfraredRemote* remote);
|
||||
InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index);
|
||||
bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index);
|
||||
/**
|
||||
* @brief Get the number of signals listed in an InfraredRemote instance.
|
||||
*
|
||||
* @param[in] remote pointer to the instance to be queried.
|
||||
* @returns number of signals, zero or more
|
||||
*/
|
||||
size_t infrared_remote_get_signal_count(const InfraredRemote* remote);
|
||||
|
||||
bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal);
|
||||
bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index);
|
||||
bool infrared_remote_delete_button(InfraredRemote* remote, size_t index);
|
||||
void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest);
|
||||
/**
|
||||
* @brief Get the name of a signal listed in an InfraredRemote instance.
|
||||
*
|
||||
* @param[in] remote pointer to the instance to be queried.
|
||||
* @param[in] index index of the signal in question. Must be less than the total signal count.
|
||||
*/
|
||||
const char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t index);
|
||||
|
||||
bool infrared_remote_store(InfraredRemote* remote);
|
||||
bool infrared_remote_load(InfraredRemote* remote, FuriString* path);
|
||||
/**
|
||||
* @brief Get the index of a signal listed in an InfraredRemote instance by its name.
|
||||
*
|
||||
* @param[in] remote pointer to the instance to be queried.
|
||||
* @param[in] name pointer to a zero-terminated string containig the name of the signal in question.
|
||||
* @param[out] index pointer to the variable to hold the signal index.
|
||||
* @returns true if a signal with the given name was found, false otherwise.
|
||||
*/
|
||||
bool infrared_remote_get_signal_index(
|
||||
const InfraredRemote* remote,
|
||||
const char* name,
|
||||
size_t* index);
|
||||
|
||||
/**
|
||||
* @brief Load a signal listed in an InfraredRemote instance.
|
||||
*
|
||||
* As mentioned above, the signals are loaded on-demand. The user code must call this function
|
||||
* each time it wants to interact with a new signal.
|
||||
*
|
||||
* @param[in] remote pointer to the instance to load from.
|
||||
* @param[out] signal pointer to the signal to load into. Must be allocated.
|
||||
* @param[in] index index of the signal to be loaded. Must be less than the total signal count.
|
||||
* @return true if the signal was successfully loaded, false otherwise.
|
||||
*/
|
||||
bool infrared_remote_load_signal(
|
||||
const InfraredRemote* remote,
|
||||
InfraredSignal* signal,
|
||||
size_t index);
|
||||
|
||||
/**
|
||||
* @brief Append a signal to the file associated with an InfraredRemote instance.
|
||||
*
|
||||
* The file path must be somehow initialised first by calling either infrared_remote_load() or
|
||||
* infrared_remote_create(). As the name suggests, the signal will be put in the end of the file.
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to append to.
|
||||
* @param[in] signal pointer to the signal to be appended.
|
||||
* @param[in] name pointer to a zero-terminated string containing the name of the signal.
|
||||
* @returns true if the signal was successfully appended, false otherwise.
|
||||
*/
|
||||
bool infrared_remote_append_signal(
|
||||
InfraredRemote* remote,
|
||||
const InfraredSignal* signal,
|
||||
const char* name);
|
||||
|
||||
/**
|
||||
* @brief Insert a signal to the file associated with an InfraredRemote instance.
|
||||
*
|
||||
* Same behaviour as infrared_remote_append_signal(), but the user code can decide where to
|
||||
* put the signal in the file.
|
||||
*
|
||||
* Index values equal to or greater than the total signal count will result in behaviour
|
||||
* identical to infrared_remote_append_signal().
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to insert to.
|
||||
* @param[in] signal pointer to the signal to be inserted.
|
||||
* @param[in] name pointer to a zero-terminated string containing the name of the signal.
|
||||
* @param[in] index the index under which the signal shall be inserted.
|
||||
* @returns true if the signal was successfully inserted, false otherwise.
|
||||
*/
|
||||
bool infrared_remote_insert_signal(
|
||||
InfraredRemote* remote,
|
||||
const InfraredSignal* signal,
|
||||
const char* name,
|
||||
size_t index);
|
||||
|
||||
/**
|
||||
* @brief Rename a signal in the file associated with an InfraredRemote instance.
|
||||
*
|
||||
* Only changes the signal's name, but neither its position nor contents.
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to be modified.
|
||||
* @param[in] index index of the signal to be renamed. Must be less than the total signal count.
|
||||
* @param[in] new_name pointer to a zero-terminated string containig the signal's new name.
|
||||
* @returns true if the signal was successfully renamed, false otherwise.
|
||||
*/
|
||||
bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name);
|
||||
|
||||
/**
|
||||
* @brief Change a signal's position in the file associated with an InfraredRemote instance.
|
||||
*
|
||||
* Only changes the signal's position (index), but neither its name nor contents.
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to be modified.
|
||||
* @param[in] index index of the signal to be moved. Must be less than the total signal count.
|
||||
* @param[in] new_index index of the signal to be moved. Must be less than the total signal count.
|
||||
*/
|
||||
bool infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index);
|
||||
|
||||
/**
|
||||
* @brief Delete a signal in the file associated with an InfraredRemote instance.
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to be modified.
|
||||
* @param[in] index index of the signal to be deleted. Must be less than the total signal count.
|
||||
* @returns true if the signal was successfully deleted, false otherwise.
|
||||
*/
|
||||
bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index);
|
||||
|
||||
/**
|
||||
* @brief Create a new file and associate it with an InfraredRemote instance.
|
||||
*
|
||||
* The instance will be reset and given a new empty file with just the header.
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to be assigned with a new file.
|
||||
* @param[in] path pointer to a zero-terminated string containing the full file path.
|
||||
* @returns true if the file was successfully created, false otherwise.
|
||||
*/
|
||||
bool infrared_remote_create(InfraredRemote* remote, const char* path);
|
||||
|
||||
/**
|
||||
* @brief Associate an InfraredRemote instance with a file and load the signal names from it.
|
||||
*
|
||||
* The instance will be reset and fill its signal name list from the given file.
|
||||
* The file must already exist and be valid.
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to be assigned with an existing file.
|
||||
* @param[in] path pointer to a zero-terminated string containing the full file path.
|
||||
* @returns true if the file was successfully loaded, false otherwise.
|
||||
*/
|
||||
bool infrared_remote_load(InfraredRemote* remote, const char* path);
|
||||
|
||||
/**
|
||||
* @brief Rename the file associated with an InfraredRemote instance.
|
||||
*
|
||||
* Only renames the file, no signals are added, moved or deleted.
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to be modified.
|
||||
* @param[in] new_path pointer to a zero-terminated string containing the new full file path.
|
||||
* @returns true if the file was successfully renamed, false otherwise.
|
||||
*/
|
||||
bool infrared_remote_rename(InfraredRemote* remote, const char* new_path);
|
||||
|
||||
/**
|
||||
* @brief Remove the file associated with an InfraredRemote instance.
|
||||
*
|
||||
* This operation is irreversible and fully deletes the remote file
|
||||
* from the underlying filesystem.
|
||||
* After calling this function, the instance becomes invalid until
|
||||
* infrared_remote_create() or infrared_remote_load() are successfully executed.
|
||||
*
|
||||
* @param[in,out] remote pointer to the instance to be modified.
|
||||
* @returns true if the file was successfully removed, false otherwise.
|
||||
*/
|
||||
bool infrared_remote_remove(InfraredRemote* remote);
|
||||
|
@ -1,37 +0,0 @@
|
||||
#include "infrared_remote_button.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct InfraredRemoteButton {
|
||||
FuriString* name;
|
||||
InfraredSignal* signal;
|
||||
};
|
||||
|
||||
InfraredRemoteButton* infrared_remote_button_alloc() {
|
||||
InfraredRemoteButton* button = malloc(sizeof(InfraredRemoteButton));
|
||||
button->name = furi_string_alloc();
|
||||
button->signal = infrared_signal_alloc();
|
||||
return button;
|
||||
}
|
||||
|
||||
void infrared_remote_button_free(InfraredRemoteButton* button) {
|
||||
furi_string_free(button->name);
|
||||
infrared_signal_free(button->signal);
|
||||
free(button);
|
||||
}
|
||||
|
||||
void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name) {
|
||||
furi_string_set(button->name, name);
|
||||
}
|
||||
|
||||
const char* infrared_remote_button_get_name(InfraredRemoteButton* button) {
|
||||
return furi_string_get_cstr(button->name);
|
||||
}
|
||||
|
||||
void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal) {
|
||||
infrared_signal_set_signal(button->signal, signal);
|
||||
}
|
||||
|
||||
InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button) {
|
||||
return button->signal;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "infrared_signal.h"
|
||||
|
||||
typedef struct InfraredRemoteButton InfraredRemoteButton;
|
||||
|
||||
InfraredRemoteButton* infrared_remote_button_alloc();
|
||||
void infrared_remote_button_free(InfraredRemoteButton* button);
|
||||
|
||||
void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name);
|
||||
const char* infrared_remote_button_get_name(InfraredRemoteButton* button);
|
||||
|
||||
void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal);
|
||||
InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button);
|
@ -8,6 +8,8 @@
|
||||
|
||||
#define TAG "InfraredSignal"
|
||||
|
||||
#define INFRARED_SIGNAL_NAME_KEY "name"
|
||||
|
||||
struct InfraredSignal {
|
||||
bool is_raw;
|
||||
union {
|
||||
@ -24,7 +26,7 @@ static void infrared_signal_clear_timings(InfraredSignal* signal) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool infrared_signal_is_message_valid(InfraredMessage* message) {
|
||||
static bool infrared_signal_is_message_valid(const InfraredMessage* message) {
|
||||
if(!infrared_is_protocol_valid(message->protocol)) {
|
||||
FURI_LOG_E(TAG, "Unknown protocol");
|
||||
return false;
|
||||
@ -57,7 +59,7 @@ static bool infrared_signal_is_message_valid(InfraredMessage* message) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) {
|
||||
static bool infrared_signal_is_raw_valid(const InfraredRawSignal* raw) {
|
||||
if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
@ -83,7 +85,8 @@ static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) {
|
||||
static inline bool
|
||||
infrared_signal_save_message(const InfraredMessage* message, FlipperFormat* ff) {
|
||||
const char* protocol_name = infrared_get_protocol_name(message->protocol);
|
||||
return flipper_format_write_string_cstr(ff, "type", "parsed") &&
|
||||
flipper_format_write_string_cstr(ff, "protocol", protocol_name) &&
|
||||
@ -91,7 +94,7 @@ static inline bool infrared_signal_save_message(InfraredMessage* message, Flippe
|
||||
flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4);
|
||||
}
|
||||
|
||||
static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) {
|
||||
static inline bool infrared_signal_save_raw(const InfraredRawSignal* raw, FlipperFormat* ff) {
|
||||
furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT);
|
||||
return flipper_format_write_string_cstr(ff, "type", "raw") &&
|
||||
flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) &&
|
||||
@ -180,11 +183,11 @@ void infrared_signal_free(InfraredSignal* signal) {
|
||||
free(signal);
|
||||
}
|
||||
|
||||
bool infrared_signal_is_raw(InfraredSignal* signal) {
|
||||
bool infrared_signal_is_raw(const InfraredSignal* signal) {
|
||||
return signal->is_raw;
|
||||
}
|
||||
|
||||
bool infrared_signal_is_valid(InfraredSignal* signal) {
|
||||
bool infrared_signal_is_valid(const InfraredSignal* signal) {
|
||||
return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) :
|
||||
infrared_signal_is_message_valid(&signal->payload.message);
|
||||
}
|
||||
@ -218,7 +221,7 @@ void infrared_signal_set_raw_signal(
|
||||
memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) {
|
||||
const InfraredRawSignal* infrared_signal_get_raw_signal(const InfraredSignal* signal) {
|
||||
furi_assert(signal->is_raw);
|
||||
return &signal->payload.raw;
|
||||
}
|
||||
@ -230,14 +233,14 @@ void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage*
|
||||
signal->payload.message = *message;
|
||||
}
|
||||
|
||||
InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) {
|
||||
const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal) {
|
||||
furi_assert(!signal->is_raw);
|
||||
return &signal->payload.message;
|
||||
}
|
||||
|
||||
bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) {
|
||||
bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name) {
|
||||
if(!flipper_format_write_comment_cstr(ff, "") ||
|
||||
!flipper_format_write_string_cstr(ff, "name", name)) {
|
||||
!flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_NAME_KEY, name)) {
|
||||
return false;
|
||||
} else if(signal->is_raw) {
|
||||
return infrared_signal_save_raw(&signal->payload.raw, ff);
|
||||
@ -247,33 +250,30 @@ bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char*
|
||||
}
|
||||
|
||||
bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) {
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_string(ff, "name", tmp)) break;
|
||||
furi_string_set(name, tmp);
|
||||
if(!infrared_signal_read_name(ff, name)) break;
|
||||
if(!infrared_signal_read_body(signal, ff)) break;
|
||||
success = true;
|
||||
} while(0);
|
||||
|
||||
furi_string_free(tmp);
|
||||
success = true; //-V779
|
||||
} while(false);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_signal_search_and_read(
|
||||
InfraredSignal* signal,
|
||||
FlipperFormat* ff,
|
||||
const FuriString* name) {
|
||||
bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name) {
|
||||
return flipper_format_read_string(ff, INFRARED_SIGNAL_NAME_KEY, name);
|
||||
}
|
||||
|
||||
bool infrared_signal_search_and_read(InfraredSignal* signal, FlipperFormat* ff, const char* name) {
|
||||
bool success = false;
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
do {
|
||||
bool is_name_found = false;
|
||||
while(flipper_format_read_string(ff, "name", tmp)) {
|
||||
is_name_found = furi_string_equal(name, tmp);
|
||||
if(is_name_found) break;
|
||||
while(!is_name_found && infrared_signal_read_name(ff, tmp)) { //-V560
|
||||
is_name_found = furi_string_equal(tmp, name);
|
||||
}
|
||||
if(!is_name_found) break; //-V547
|
||||
if(!infrared_signal_read_body(signal, ff)) break; //-V779
|
||||
@ -284,9 +284,9 @@ bool infrared_signal_search_and_read(
|
||||
return success;
|
||||
}
|
||||
|
||||
void infrared_signal_transmit(InfraredSignal* signal) {
|
||||
void infrared_signal_transmit(const InfraredSignal* signal) {
|
||||
if(signal->is_raw) {
|
||||
InfraredRawSignal* raw_signal = &signal->payload.raw;
|
||||
const InfraredRawSignal* raw_signal = &signal->payload.raw;
|
||||
infrared_send_raw_ext(
|
||||
raw_signal->timings,
|
||||
raw_signal->timings_size,
|
||||
@ -294,7 +294,7 @@ void infrared_signal_transmit(InfraredSignal* signal) {
|
||||
raw_signal->frequency,
|
||||
raw_signal->duty_cycle);
|
||||
} else {
|
||||
InfraredMessage* message = &signal->payload.message;
|
||||
const InfraredMessage* message = &signal->payload.message;
|
||||
infrared_send(message, 1);
|
||||
}
|
||||
}
|
||||
|
@ -1,45 +1,186 @@
|
||||
/**
|
||||
* @file infrared_signal.h
|
||||
* @brief Infrared signal library.
|
||||
*
|
||||
* Infrared signals may be of two types:
|
||||
* - known to the infrared signal decoder, or *parsed* signals
|
||||
* - the rest, or *raw* signals, which are treated merely as a set of timings.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <infrared.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <infrared/encoder_decoder/infrared.h>
|
||||
|
||||
/**
|
||||
* @brief InfraredSignal opaque type declaration.
|
||||
*/
|
||||
typedef struct InfraredSignal InfraredSignal;
|
||||
|
||||
/**
|
||||
* @brief Raw signal type definition.
|
||||
*
|
||||
* Measurement units used:
|
||||
* - time: microseconds (uS)
|
||||
* - frequency: Hertz (Hz)
|
||||
* - duty_cycle: no units, fraction between 0 and 1.
|
||||
*/
|
||||
typedef struct {
|
||||
size_t timings_size;
|
||||
uint32_t* timings;
|
||||
uint32_t frequency;
|
||||
float duty_cycle;
|
||||
size_t timings_size; /**< Number of elements in the timings array. */
|
||||
uint32_t* timings; /**< Pointer to an array of timings describing the signal. */
|
||||
uint32_t frequency; /**< Carrier frequency of the signal. */
|
||||
float duty_cycle; /**< Duty cycle of the signal. */
|
||||
} InfraredRawSignal;
|
||||
|
||||
/**
|
||||
* @brief Create a new InfraredSignal instance.
|
||||
*
|
||||
* @returns pointer to the instance created.
|
||||
*/
|
||||
InfraredSignal* infrared_signal_alloc();
|
||||
|
||||
/**
|
||||
* @brief Delete an InfraredSignal instance.
|
||||
*
|
||||
* @param[in,out] signal pointer to the instance to be deleted.
|
||||
*/
|
||||
void infrared_signal_free(InfraredSignal* signal);
|
||||
|
||||
bool infrared_signal_is_raw(InfraredSignal* signal);
|
||||
bool infrared_signal_is_valid(InfraredSignal* signal);
|
||||
/**
|
||||
* @brief Test whether an InfraredSignal instance holds a raw signal.
|
||||
*
|
||||
* @param[in] signal pointer to the instance to be tested.
|
||||
* @returns true if the instance holds a raw signal, false otherwise.
|
||||
*/
|
||||
bool infrared_signal_is_raw(const InfraredSignal* signal);
|
||||
|
||||
/**
|
||||
* @brief Test whether an InfraredSignal instance holds any signal.
|
||||
*
|
||||
* @param[in] signal pointer to the instance to be tested.
|
||||
* @returns true if the instance holds raw signal, false otherwise.
|
||||
*/
|
||||
bool infrared_signal_is_valid(const InfraredSignal* signal);
|
||||
|
||||
/**
|
||||
* @brief Set an InfraredInstance to hold the signal from another one.
|
||||
*
|
||||
* Any instance's previous contents will be automatically deleted before
|
||||
* copying the source instance's contents.
|
||||
*
|
||||
* @param[in,out] signal pointer to the destination instance.
|
||||
* @param[in] other pointer to the source instance.
|
||||
*/
|
||||
void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other);
|
||||
|
||||
/**
|
||||
* @brief Set an InfraredInstance to hold a raw signal.
|
||||
*
|
||||
* Any instance's previous contents will be automatically deleted before
|
||||
* copying the raw signal.
|
||||
*
|
||||
* After this call, infrared_signal_is_raw() will return true.
|
||||
*
|
||||
* @param[in,out] signal pointer to the destination instance.
|
||||
* @param[in] timings pointer to an array containing the raw signal timings.
|
||||
* @param[in] timings_size number of elements in the timings array.
|
||||
* @param[in] frequency signal carrier frequency, in Hertz.
|
||||
* @param[in] duty_cycle signal duty cycle, fraction between 0 and 1.
|
||||
*/
|
||||
void infrared_signal_set_raw_signal(
|
||||
InfraredSignal* signal,
|
||||
const uint32_t* timings,
|
||||
size_t timings_size,
|
||||
uint32_t frequency,
|
||||
float duty_cycle);
|
||||
InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal);
|
||||
|
||||
/**
|
||||
* @brief Get the raw signal held by an InfraredSignal instance.
|
||||
*
|
||||
* @warning the instance MUST hold a *raw* signal, otherwise undefined behaviour will occur.
|
||||
*
|
||||
* @param[in] signal pointer to the instance to be queried.
|
||||
* @returns pointer to the raw signal structure held by the instance.
|
||||
*/
|
||||
const InfraredRawSignal* infrared_signal_get_raw_signal(const InfraredSignal* signal);
|
||||
|
||||
/**
|
||||
* @brief Set an InfraredInstance to hold a parsed signal.
|
||||
*
|
||||
* Any instance's previous contents will be automatically deleted before
|
||||
* copying the raw signal.
|
||||
*
|
||||
* After this call, infrared_signal_is_raw() will return false.
|
||||
*
|
||||
* @param[in,out] signal pointer to the destination instance.
|
||||
* @param[in] message pointer to the message containing the parsed signal.
|
||||
*/
|
||||
void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message);
|
||||
InfraredMessage* infrared_signal_get_message(InfraredSignal* signal);
|
||||
|
||||
bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name);
|
||||
/**
|
||||
* @brief Get the parsed signal held by an InfraredSignal instance.
|
||||
*
|
||||
* @warning the instance MUST hold a *parsed* signal, otherwise undefined behaviour will occur.
|
||||
*
|
||||
* @param[in] signal pointer to the instance to be queried.
|
||||
* @returns pointer to the parsed signal structure held by the instance.
|
||||
*/
|
||||
const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal);
|
||||
|
||||
/**
|
||||
* @brief Read a signal from a FlipperFormat file into an InfraredSignal instance.
|
||||
*
|
||||
* The file must be allocated and open prior to this call. The seek position determines
|
||||
* which signal will be read (if there is more than one in the file). Calling this function
|
||||
* repeatedly will result in all signals in the file to be read until no more are left.
|
||||
*
|
||||
* @param[in,out] signal pointer to the instance to be read into.
|
||||
* @param[in,out] ff pointer to the FlipperFormat file instance to read from.
|
||||
* @param[out] name pointer to the string to hold the signal name. Must be properly allocated.
|
||||
* @returns true if a signal was successfully read, false otherwise (e.g. no more signals to read).
|
||||
*/
|
||||
bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name);
|
||||
bool infrared_signal_search_and_read(
|
||||
InfraredSignal* signal,
|
||||
FlipperFormat* ff,
|
||||
const FuriString* name);
|
||||
|
||||
void infrared_signal_transmit(InfraredSignal* signal);
|
||||
/**
|
||||
* @brief Read a signal name from a FlipperFormat file.
|
||||
*
|
||||
* Same behaviour as infrared_signal_read(), but only the name is read.
|
||||
*
|
||||
* @param[in,out] ff pointer to the FlipperFormat file instance to read from.
|
||||
* @param[out] name pointer to the string to hold the signal name. Must be properly allocated.
|
||||
* @returns true if a signal name was successfully read, false otherwise (e.g. no more signals to read).
|
||||
*/
|
||||
bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name);
|
||||
|
||||
/**
|
||||
* @brief Read a signal with a particular name from a FlipperFormat file into an InfraredSignal instance.
|
||||
*
|
||||
* This function will look for a signal with the given name and if found, attempt to read it.
|
||||
* Same considerations apply as to infrared_signal_read().
|
||||
*
|
||||
* @param[in,out] signal pointer to the instance to be read into.
|
||||
* @param[in,out] ff pointer to the FlipperFormat file instance to read from.
|
||||
* @param[in] name pointer to a zero-terminated string containing the requested signal name.
|
||||
* @returns true if a signal was found and successfully read, false otherwise (e.g. the signal was not found).
|
||||
*/
|
||||
bool infrared_signal_search_and_read(InfraredSignal* signal, FlipperFormat* ff, const char* name);
|
||||
|
||||
/**
|
||||
* @brief Save a signal contained in an InfraredSignal instance to a FlipperFormat file.
|
||||
*
|
||||
* The file must be allocated and open prior to this call. Additionally, an appropriate header
|
||||
* must be already written into the file.
|
||||
*
|
||||
* @param[in] signal pointer to the instance holding the signal to be saved.
|
||||
* @param[in,out] ff pointer to the FlipperFormat file instance to write to.
|
||||
* @param[in] name pointer to a zero-terminated string contating the name of the signal.
|
||||
*/
|
||||
bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name);
|
||||
|
||||
/**
|
||||
* @brief Transmit a signal contained in an InfraredSignal instance.
|
||||
*
|
||||
* The transmission happens once per call using the built-in hardware (via HAL calls).
|
||||
*
|
||||
* @param[in] signal pointer to the instance holding the signal to be transmitted.
|
||||
*/
|
||||
void infrared_signal_transmit(const InfraredSignal* signal);
|
||||
|
@ -1,20 +1,21 @@
|
||||
#include "../../infrared_i.h"
|
||||
#include "../../infrared_app_i.h"
|
||||
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void infrared_scene_universal_common_item_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index);
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void infrared_scene_universal_common_progress_back_callback(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeBackPressed, -1);
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint32_t record_count) {
|
||||
static void
|
||||
infrared_scene_universal_common_show_popup(InfraredApp* infrared, uint32_t record_count) {
|
||||
ViewStack* view_stack = infrared->view_stack;
|
||||
InfraredProgressView* progress = infrared->progress;
|
||||
infrared_progress_view_set_progress_total(progress, record_count);
|
||||
@ -24,7 +25,7 @@ static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint3
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
|
||||
}
|
||||
|
||||
static void infrared_scene_universal_common_hide_popup(Infrared* infrared) {
|
||||
static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) {
|
||||
ViewStack* view_stack = infrared->view_stack;
|
||||
InfraredProgressView* progress = infrared->progress;
|
||||
view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress));
|
||||
@ -32,12 +33,12 @@ static void infrared_scene_universal_common_hide_popup(Infrared* infrared) {
|
||||
}
|
||||
|
||||
void infrared_scene_universal_common_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel));
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
bool consumed = false;
|
||||
@ -84,7 +85,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e
|
||||
}
|
||||
|
||||
void infrared_scene_universal_common_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel));
|
||||
infrared_brute_force_reset(infrared->brute_force);
|
||||
|
@ -1,12 +1,12 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void infrared_scene_ask_back_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
|
||||
if(infrared->app_state.is_learning_new_remote) {
|
||||
@ -28,7 +28,7 @@ void infrared_scene_ask_back_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
@ -54,6 +54,6 @@ bool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void infrared_scene_ask_back_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
dialog_ex_reset(infrared->dialog_ex);
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void infrared_scene_ask_retry_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
|
||||
dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop);
|
||||
@ -23,7 +23,7 @@ void infrared_scene_ask_retry_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_ask_retry_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
@ -43,6 +43,6 @@ bool infrared_scene_ask_retry_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void infrared_scene_ask_retry_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
dialog_ex_reset(infrared->dialog_ex);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
void infrared_scene_debug_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
InfraredWorker* worker = infrared->worker;
|
||||
|
||||
infrared_worker_rx_set_received_signal_callback(
|
||||
@ -14,16 +14,16 @@ void infrared_scene_debug_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeSignalReceived) {
|
||||
InfraredDebugView* debug_view = infrared->debug_view;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
InfraredSignal* signal = infrared->current_signal;
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_debug_view_set_text(debug_view, "RAW\n%d samples\n", raw->timings_size);
|
||||
|
||||
printf("RAW, %zu samples:\r\n", raw->timings_size);
|
||||
@ -33,7 +33,7 @@ bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) {
|
||||
printf("\r\n");
|
||||
|
||||
} else {
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
const InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
infrared_debug_view_set_text(
|
||||
debug_view,
|
||||
"%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n",
|
||||
@ -61,7 +61,7 @@ bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void infrared_scene_debug_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
InfraredWorker* worker = infrared->worker;
|
||||
infrared_worker_rx_stop(worker);
|
||||
infrared_worker_rx_enable_blink_on_receiving(worker, false);
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexAddButton,
|
||||
@ -10,12 +10,12 @@ typedef enum {
|
||||
} SubmenuIndex;
|
||||
|
||||
static void infrared_scene_edit_submenu_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_edit_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
|
||||
@ -64,7 +64,7 @@ void infrared_scene_edit_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
@ -106,6 +106,6 @@ bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void infrared_scene_edit_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
static void infrared_scene_edit_button_select_submenu_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_edit_button_select_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
@ -16,16 +16,16 @@ void infrared_scene_edit_button_select_on_enter(void* context) {
|
||||
"Delete Button:";
|
||||
submenu_set_header(submenu, header);
|
||||
|
||||
const size_t button_count = infrared_remote_get_button_count(remote);
|
||||
const size_t button_count = infrared_remote_get_signal_count(remote);
|
||||
for(size_t i = 0; i < button_count; ++i) {
|
||||
InfraredRemoteButton* button = infrared_remote_get_button(remote, i);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
infrared_remote_button_get_name(button),
|
||||
infrared_remote_get_signal_name(remote, i),
|
||||
i,
|
||||
infrared_scene_edit_button_select_submenu_callback,
|
||||
context);
|
||||
}
|
||||
|
||||
if(button_count && app_state->current_button_index != InfraredButtonIndexNone) {
|
||||
submenu_set_selected_item(submenu, app_state->current_button_index);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
@ -35,7 +35,7 @@ void infrared_scene_edit_button_select_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
@ -57,6 +57,6 @@ bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent
|
||||
}
|
||||
|
||||
void infrared_scene_edit_button_select_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
|
@ -1,42 +1,49 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
static void
|
||||
infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void infrared_scene_edit_delete_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
|
||||
const InfraredEditTarget edit_target = infrared->app_state.edit_target;
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
int32_t current_button_index = infrared->app_state.current_button_index;
|
||||
furi_assert(current_button_index != InfraredButtonIndexNone);
|
||||
|
||||
dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop);
|
||||
InfraredRemoteButton* current_button =
|
||||
infrared_remote_get_button(remote, current_button_index);
|
||||
InfraredSignal* signal = infrared_remote_button_get_signal(current_button);
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
const int32_t current_button_index = infrared->app_state.current_button_index;
|
||||
furi_check(current_button_index != InfraredButtonIndexNone);
|
||||
|
||||
if(!infrared_remote_load_signal(remote, infrared->current_signal, current_button_index)) {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to load\n\"%s\"",
|
||||
infrared_remote_get_signal_name(remote, current_button_index));
|
||||
scene_manager_previous_scene(infrared->scene_manager);
|
||||
return;
|
||||
}
|
||||
|
||||
if(infrared_signal_is_raw(infrared->current_signal)) {
|
||||
const InfraredRawSignal* raw =
|
||||
infrared_signal_get_raw_signal(infrared->current_signal);
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
"%s\nRAW\n%ld samples",
|
||||
infrared_remote_button_get_name(current_button),
|
||||
"%s\nRAW\n%zu samples",
|
||||
infrared_remote_get_signal_name(remote, current_button_index),
|
||||
raw->timings_size);
|
||||
|
||||
} else {
|
||||
const InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
const InfraredMessage* message = infrared_signal_get_message(infrared->current_signal);
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
"%s\n%s\nA=0x%0*lX C=0x%0*lX",
|
||||
infrared_remote_button_get_name(current_button),
|
||||
infrared_remote_get_signal_name(remote, current_button_index),
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
@ -49,9 +56,9 @@ void infrared_scene_edit_delete_on_enter(void* context) {
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
"%s\n with %lu buttons",
|
||||
"%s\n with %zu buttons",
|
||||
infrared_remote_get_name(remote),
|
||||
infrared_remote_get_button_count(remote));
|
||||
infrared_remote_get_signal_count(remote));
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
@ -63,11 +70,14 @@ void infrared_scene_edit_delete_on_enter(void* context) {
|
||||
dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_delete_dialog_result_callback);
|
||||
dialog_ex_set_context(dialog_ex, context);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal);
|
||||
view_stack_add_view(infrared->view_stack, dialog_ex_get_view(infrared->dialog_ex));
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
@ -83,18 +93,24 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event)
|
||||
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
furi_assert(app_state->current_button_index != InfraredButtonIndexNone);
|
||||
success = infrared_remote_delete_button(remote, app_state->current_button_index);
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
success = infrared_remote_delete_signal(remote, app_state->current_button_index);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
success = infrared_remote_remove(remote);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else {
|
||||
furi_assert(0);
|
||||
furi_crash(NULL);
|
||||
}
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone);
|
||||
} else {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to\ndelete %s",
|
||||
edit_target == InfraredEditTargetButton ? "button" : "file");
|
||||
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
|
||||
scene_manager_search_and_switch_to_previous_scene_one_of(
|
||||
scene_manager, possible_scenes, COUNT_OF(possible_scenes));
|
||||
@ -107,6 +123,6 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event)
|
||||
}
|
||||
|
||||
void infrared_scene_edit_delete_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
InfraredApp* infrared = context;
|
||||
view_stack_remove_view(infrared->view_stack, dialog_ex_get_view(infrared->dialog_ex));
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
void infrared_scene_edit_delete_done_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
|
||||
@ -16,7 +16,7 @@ void infrared_scene_edit_delete_done_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
@ -43,6 +43,6 @@ bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent e
|
||||
}
|
||||
|
||||
void infrared_scene_edit_delete_done_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
|
@ -1,44 +1,69 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
static void infrared_scene_move_button(uint32_t index_old, uint32_t index_new, void* context) {
|
||||
InfraredRemote* remote = context;
|
||||
furi_assert(remote);
|
||||
infrared_remote_move_button(remote, index_old, index_new);
|
||||
}
|
||||
static void infrared_scene_edit_move_button_callback(
|
||||
uint32_t index_old,
|
||||
uint32_t index_new,
|
||||
void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
furi_assert(infrared);
|
||||
|
||||
static const char* infrared_scene_get_btn_name(uint32_t index, void* context) {
|
||||
InfraredRemote* remote = context;
|
||||
furi_assert(remote);
|
||||
InfraredRemoteButton* button = infrared_remote_get_button(remote, index);
|
||||
return (infrared_remote_button_get_name(button));
|
||||
infrared->app_state.prev_button_index = index_old;
|
||||
infrared->app_state.current_button_index = index_new;
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeButtonSelected);
|
||||
}
|
||||
|
||||
void infrared_scene_edit_move_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
|
||||
infrared_move_view_set_callback(infrared->move_view, infrared_scene_move_button);
|
||||
for(size_t i = 0; i < infrared_remote_get_signal_count(remote); ++i) {
|
||||
infrared_move_view_add_item(
|
||||
infrared->move_view, infrared_remote_get_signal_name(remote, i));
|
||||
}
|
||||
|
||||
uint32_t btn_count = infrared_remote_get_button_count(remote);
|
||||
infrared_move_view_list_init(
|
||||
infrared->move_view, btn_count, infrared_scene_get_btn_name, remote);
|
||||
infrared_move_view_list_update(infrared->move_view);
|
||||
infrared_move_view_set_callback(
|
||||
infrared->move_view, infrared_scene_edit_move_button_callback, infrared);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewMove);
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal);
|
||||
view_stack_add_view(infrared->view_stack, infrared_move_view_get_view(infrared->move_view));
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
UNUSED(event);
|
||||
UNUSED(infrared);
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeButtonSelected) {
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
const bool button_moved = infrared_remote_move_signal(
|
||||
infrared->remote,
|
||||
infrared->app_state.prev_button_index,
|
||||
infrared->app_state.current_button_index);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
|
||||
if(!button_moved) {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to move\n\"%s\"",
|
||||
infrared_remote_get_signal_name(
|
||||
infrared->remote, infrared->app_state.current_button_index));
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
infrared->scene_manager, InfraredSceneRemoteList);
|
||||
}
|
||||
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_edit_move_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
infrared_remote_store(remote);
|
||||
InfraredApp* infrared = context;
|
||||
view_stack_remove_view(infrared->view_stack, infrared_move_view_get_view(infrared->move_view));
|
||||
infrared_move_view_reset(infrared->move_view);
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <toolbox/path.h>
|
||||
|
||||
void infrared_scene_edit_rename_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
TextInput* text_input = infrared->text_input;
|
||||
size_t enter_name_length = 0;
|
||||
@ -14,14 +14,12 @@ void infrared_scene_edit_rename_on_enter(void* context) {
|
||||
text_input_set_header_text(text_input, "Name the button");
|
||||
|
||||
const int32_t current_button_index = infrared->app_state.current_button_index;
|
||||
furi_assert(current_button_index != InfraredButtonIndexNone);
|
||||
furi_check(current_button_index != InfraredButtonIndexNone);
|
||||
|
||||
InfraredRemoteButton* current_button =
|
||||
infrared_remote_get_button(remote, current_button_index);
|
||||
enter_name_length = INFRARED_MAX_BUTTON_NAME_LENGTH;
|
||||
strncpy(
|
||||
infrared->text_store[0],
|
||||
infrared_remote_button_get_name(current_button),
|
||||
infrared_remote_get_signal_name(remote, current_button_index),
|
||||
enter_name_length);
|
||||
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
@ -44,7 +42,7 @@ void infrared_scene_edit_rename_on_enter(void* context) {
|
||||
|
||||
furi_string_free(folder_path);
|
||||
} else {
|
||||
furi_assert(0);
|
||||
furi_crash(NULL);
|
||||
}
|
||||
|
||||
text_input_set_result_callback(
|
||||
@ -55,11 +53,14 @@ void infrared_scene_edit_rename_on_enter(void* context) {
|
||||
enter_name_length,
|
||||
false);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal);
|
||||
view_stack_add_view(infrared->view_stack, text_input_get_view(infrared->text_input));
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
@ -72,18 +73,24 @@ bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event)
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
const int32_t current_button_index = app_state->current_button_index;
|
||||
furi_assert(current_button_index != InfraredButtonIndexNone);
|
||||
success = infrared_remote_rename_button(
|
||||
remote, infrared->text_store[0], current_button_index);
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
success = infrared_remote_rename_signal(
|
||||
remote, current_button_index, infrared->text_store[0]);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
success = infrared_rename_current_remote(infrared, infrared->text_store[0]);
|
||||
} else {
|
||||
furi_assert(0);
|
||||
furi_crash(NULL);
|
||||
}
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone);
|
||||
} else {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to\nrename %s",
|
||||
edit_target == InfraredEditTargetButton ? "button" : "file");
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
scene_manager, InfraredSceneRemoteList);
|
||||
}
|
||||
@ -95,9 +102,11 @@ bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event)
|
||||
}
|
||||
|
||||
void infrared_scene_edit_rename_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
TextInput* text_input = infrared->text_input;
|
||||
|
||||
view_stack_remove_view(infrared->view_stack, text_input_get_view(text_input));
|
||||
|
||||
void* validator_context = text_input_get_validator_callback_context(text_input);
|
||||
text_input_set_validator(text_input, NULL, NULL);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
void infrared_scene_edit_rename_done_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
@ -16,7 +16,7 @@ void infrared_scene_edit_rename_done_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
@ -33,6 +33,6 @@ bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent e
|
||||
}
|
||||
|
||||
void infrared_scene_edit_rename_done_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
void infrared_scene_error_databases_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_icon(popup, 5, 11, &I_SDQuestion_35x43);
|
||||
@ -16,7 +16,7 @@ void infrared_scene_error_databases_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_error_databases_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
@ -31,7 +31,7 @@ bool infrared_scene_error_databases_on_event(void* context, SceneManagerEvent ev
|
||||
}
|
||||
|
||||
void infrared_scene_error_databases_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
popup_reset(infrared->popup);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOff);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void infrared_scene_learn_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
InfraredWorker* worker = infrared->worker;
|
||||
|
||||
@ -21,7 +21,7 @@ void infrared_scene_learn_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
@ -37,7 +37,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void infrared_scene_learn_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL);
|
||||
infrared_worker_rx_stop(infrared->worker);
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
void infrared_scene_learn_done_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
@ -21,7 +21,7 @@ void infrared_scene_learn_done_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
@ -38,7 +38,7 @@ bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event)
|
||||
}
|
||||
|
||||
void infrared_scene_learn_done_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
infrared->app_state.is_learning_new_remote = false;
|
||||
popup_set_header(infrared->popup, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void infrared_scene_learn_enter_name_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
TextInput* text_input = infrared->text_input;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
InfraredSignal* signal = infrared->current_signal;
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_text_store_set(infrared, 0, "RAW_%d", raw->timings_size);
|
||||
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_text_store_set(infrared, 0, "RAW_%zu", raw->timings_size);
|
||||
} else {
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
const InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
@ -28,31 +28,32 @@ void infrared_scene_learn_enter_name_on_enter(void* context) {
|
||||
infrared->text_store[0],
|
||||
INFRARED_MAX_BUTTON_NAME_LENGTH,
|
||||
true);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);
|
||||
}
|
||||
|
||||
bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
InfraredApp* infrared = context;
|
||||
InfraredSignal* signal = infrared->current_signal;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeTextEditDone) {
|
||||
bool success = false;
|
||||
if(infrared->app_state.is_learning_new_remote) {
|
||||
success =
|
||||
infrared_add_remote_with_button(infrared, infrared->text_store[0], signal);
|
||||
} else {
|
||||
success =
|
||||
infrared_remote_add_button(infrared->remote, infrared->text_store[0], signal);
|
||||
}
|
||||
const char* signal_name = infrared->text_store[0];
|
||||
const bool success =
|
||||
infrared->app_state.is_learning_new_remote ?
|
||||
infrared_add_remote_with_button(infrared, signal_name, signal) :
|
||||
infrared_remote_append_signal(infrared->remote, signal, signal_name);
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneLearnDone);
|
||||
dolphin_deed(DolphinDeedIrSave);
|
||||
} else {
|
||||
dialog_message_show_storage_error(infrared->dialogs, "Failed to save file");
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to\n%s",
|
||||
infrared->app_state.is_learning_new_remote ? "create file" : "add signal");
|
||||
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
|
||||
scene_manager_search_and_switch_to_previous_scene_one_of(
|
||||
scene_manager, possible_scenes, COUNT_OF(possible_scenes));
|
||||
@ -65,6 +66,6 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e
|
||||
}
|
||||
|
||||
void infrared_scene_learn_enter_name_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
|
@ -1,26 +1,26 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
static void
|
||||
infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void infrared_scene_learn_success_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
InfraredSignal* signal = infrared->current_signal;
|
||||
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn);
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter);
|
||||
infrared_text_store_set(infrared, 0, "%d samples", raw->timings_size);
|
||||
infrared_text_store_set(infrared, 0, "%zu samples", raw->timings_size);
|
||||
dialog_ex_set_text(dialog_ex, infrared->text_store[0], 75, 23, AlignLeft, AlignTop);
|
||||
|
||||
} else {
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
const InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
uint8_t addr_digits =
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4);
|
||||
uint8_t cmd_digits =
|
||||
@ -56,7 +56,7 @@ void infrared_scene_learn_success_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
const bool is_transmitter_idle = !infrared->app_state.is_transmitting;
|
||||
bool consumed = false;
|
||||
@ -84,7 +84,7 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExPressCenter) {
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);
|
||||
infrared_tx_start_received(infrared);
|
||||
infrared_tx_start(infrared);
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExReleaseCenter) {
|
||||
infrared_tx_stop(infrared);
|
||||
@ -96,7 +96,7 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even
|
||||
}
|
||||
|
||||
void infrared_scene_learn_success_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
dialog_ex_reset(infrared->dialog_ex);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
typedef enum {
|
||||
ButtonIndexPlus = -2,
|
||||
@ -8,7 +8,7 @@ typedef enum {
|
||||
|
||||
static void
|
||||
infrared_scene_remote_button_menu_callback(void* context, int32_t index, InputType type) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
|
||||
uint16_t custom_type;
|
||||
if(type == InputTypePress) {
|
||||
@ -26,17 +26,15 @@ static void
|
||||
}
|
||||
|
||||
void infrared_scene_remote_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
ButtonMenu* button_menu = infrared->button_menu;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
|
||||
size_t button_count = infrared_remote_get_button_count(remote);
|
||||
for(size_t i = 0; i < button_count; ++i) {
|
||||
InfraredRemoteButton* button = infrared_remote_get_button(remote, i);
|
||||
for(size_t i = 0; i < infrared_remote_get_signal_count(remote); ++i) {
|
||||
button_menu_add_item(
|
||||
button_menu,
|
||||
infrared_remote_button_get_name(button),
|
||||
infrared_remote_get_signal_name(remote, i),
|
||||
i,
|
||||
infrared_scene_remote_button_menu_callback,
|
||||
ButtonMenuItemTypeCommon,
|
||||
@ -68,7 +66,7 @@ void infrared_scene_remote_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
const bool is_transmitter_idle = !infrared->app_state.is_transmitting;
|
||||
bool consumed = false;
|
||||
@ -116,6 +114,6 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void infrared_scene_remote_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
button_menu_reset(infrared->button_menu);
|
||||
}
|
||||
|
@ -1,31 +1,34 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
void infrared_scene_remote_list_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack);
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px);
|
||||
browser_options.base_path = INFRARED_APP_FOLDER;
|
||||
|
||||
bool success = dialog_file_browser_show(
|
||||
infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options);
|
||||
|
||||
if(success) {
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack);
|
||||
while(dialog_file_browser_show(
|
||||
infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options)) {
|
||||
const char* file_path = furi_string_get_cstr(infrared->file_path);
|
||||
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
success = infrared_remote_load(infrared->remote, infrared->file_path);
|
||||
const bool remote_loaded = infrared_remote_load(infrared->remote, file_path);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
|
||||
if(remote_loaded) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneRemote);
|
||||
return;
|
||||
} else {
|
||||
infrared_show_error_message(infrared, "Failed to load\n\"%s\"", file_path);
|
||||
}
|
||||
}
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneRemote);
|
||||
} else {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
|
||||
bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
#include <gui/canvas.h>
|
||||
|
||||
typedef enum {
|
||||
@ -8,7 +8,7 @@ typedef enum {
|
||||
} InfraredRpcState;
|
||||
|
||||
void infrared_scene_rpc_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
popup_set_header(popup, "Infrared", 89, 42, AlignCenter, AlignBottom);
|
||||
@ -27,7 +27,7 @@ void infrared_scene_rpc_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
@ -43,7 +43,8 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
|
||||
if(arg && (state == InfraredRpcStateIdle)) {
|
||||
furi_string_set(infrared->file_path, arg);
|
||||
result = infrared_remote_load(infrared->remote, infrared->file_path);
|
||||
result = infrared_remote_load(
|
||||
infrared->remote, furi_string_get_cstr(infrared->file_path));
|
||||
if(result) {
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
|
||||
@ -61,7 +62,7 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
|
||||
if(arg && (state == InfraredRpcStateLoaded)) {
|
||||
size_t button_index = 0;
|
||||
if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) {
|
||||
if(infrared_remote_get_signal_index(infrared->remote, arg, &button_index)) {
|
||||
infrared_tx_start_button_index(infrared, button_index);
|
||||
result = true;
|
||||
scene_manager_set_scene_state(
|
||||
@ -91,7 +92,7 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void infrared_scene_rpc_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
if(scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc) ==
|
||||
InfraredRpcStateSending) {
|
||||
infrared_tx_stop(infrared);
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexUniversalRemotes,
|
||||
@ -8,12 +8,12 @@ enum SubmenuIndex {
|
||||
};
|
||||
|
||||
static void infrared_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_start_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
|
||||
@ -50,7 +50,7 @@ void infrared_scene_start_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
|
||||
bool consumed = false;
|
||||
@ -79,6 +79,6 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void infrared_scene_start_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexUniversalTV,
|
||||
@ -8,12 +8,12 @@ typedef enum {
|
||||
} SubmenuIndex;
|
||||
|
||||
static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_universal_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
@ -47,7 +47,7 @@ void infrared_scene_universal_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
@ -72,6 +72,6 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void infrared_scene_universal_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_audio_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_projector_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_tv_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
Infrared* infrared = context;
|
||||
InfraredApp* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
#include "infrared_move_view.h"
|
||||
|
||||
#include <m-array.h>
|
||||
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <toolbox/m_cstr_dup.h>
|
||||
|
||||
#define LIST_ITEMS 4U
|
||||
#define LIST_LINE_H 13U
|
||||
@ -13,42 +14,41 @@
|
||||
|
||||
struct InfraredMoveView {
|
||||
View* view;
|
||||
InfraredMoveCallback move_cb;
|
||||
void* cb_context;
|
||||
InfraredMoveCallback callback;
|
||||
void* callback_context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char** btn_names;
|
||||
uint32_t btn_number;
|
||||
int32_t list_offset;
|
||||
int32_t item_idx;
|
||||
bool is_moving;
|
||||
ARRAY_DEF(InfraredMoveViewItemArray, const char*, M_CSTR_DUP_OPLIST); //-V575
|
||||
|
||||
InfraredMoveGetItemCallback get_item_cb;
|
||||
typedef struct {
|
||||
InfraredMoveViewItemArray_t labels;
|
||||
int32_t list_offset;
|
||||
int32_t current_idx;
|
||||
int32_t start_idx;
|
||||
bool is_moving;
|
||||
} InfraredMoveViewModel;
|
||||
|
||||
static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
InfraredMoveViewModel* model = _model;
|
||||
|
||||
UNUSED(model);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a button to move");
|
||||
|
||||
bool show_scrollbar = model->btn_number > LIST_ITEMS;
|
||||
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);
|
||||
const bool show_scrollbar = btn_number > LIST_ITEMS;
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
for(uint32_t i = 0; i < MIN(model->btn_number, LIST_ITEMS); i++) {
|
||||
int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->btn_number, 0u);
|
||||
uint8_t x_offset = (model->is_moving && model->item_idx == idx) ? MOVE_X_OFFSET : 0;
|
||||
for(uint32_t i = 0; i < MIN(btn_number, LIST_ITEMS); i++) {
|
||||
int32_t idx = CLAMP((uint32_t)(i + model->list_offset), btn_number, 0U);
|
||||
uint8_t x_offset = (model->is_moving && model->current_idx == idx) ? MOVE_X_OFFSET : 0;
|
||||
uint8_t y_offset = HEADER_H + i * LIST_LINE_H;
|
||||
uint8_t box_end_x = canvas_width(canvas) - (show_scrollbar ? 6 : 1);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
if(model->item_idx == idx) {
|
||||
if(model->current_idx == idx) {
|
||||
canvas_draw_box(canvas, x_offset, y_offset, box_end_x - x_offset, LIST_LINE_H);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
@ -60,7 +60,12 @@ static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_draw_dot(canvas, box_end_x - 1, y_offset + LIST_LINE_H - 1);
|
||||
}
|
||||
canvas_draw_str_aligned(
|
||||
canvas, x_offset + 3, y_offset + 3, AlignLeft, AlignTop, model->btn_names[idx]);
|
||||
canvas,
|
||||
x_offset + 3,
|
||||
y_offset + 3,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
*InfraredMoveViewItemArray_cget(model->labels, idx));
|
||||
}
|
||||
|
||||
if(show_scrollbar) {
|
||||
@ -69,22 +74,22 @@ static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_width(canvas),
|
||||
HEADER_H,
|
||||
canvas_height(canvas) - HEADER_H,
|
||||
model->item_idx,
|
||||
model->btn_number);
|
||||
model->current_idx,
|
||||
btn_number);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_list_offset(InfraredMoveViewModel* model) {
|
||||
int32_t bounds = model->btn_number > (LIST_ITEMS - 1) ? 2 : model->btn_number;
|
||||
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);
|
||||
const int32_t bounds = btn_number > (LIST_ITEMS - 1) ? 2 : btn_number;
|
||||
|
||||
if((model->btn_number > (LIST_ITEMS - 1)) &&
|
||||
(model->item_idx >= ((int32_t)model->btn_number - 1))) {
|
||||
model->list_offset = model->item_idx - (LIST_ITEMS - 1);
|
||||
} else if(model->list_offset < model->item_idx - bounds) {
|
||||
model->list_offset = CLAMP(
|
||||
model->item_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)model->btn_number - bounds, 0);
|
||||
} else if(model->list_offset > model->item_idx - bounds) {
|
||||
model->list_offset = CLAMP(model->item_idx - 1, (int32_t)model->btn_number - bounds, 0);
|
||||
if((btn_number > (LIST_ITEMS - 1)) && (model->current_idx >= ((int32_t)btn_number - 1))) {
|
||||
model->list_offset = model->current_idx - (LIST_ITEMS - 1);
|
||||
} else if(model->list_offset < model->current_idx - bounds) {
|
||||
model->list_offset =
|
||||
CLAMP(model->current_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)btn_number - bounds, 0);
|
||||
} else if(model->list_offset > model->current_idx - bounds) {
|
||||
model->list_offset = CLAMP(model->current_idx - 1, (int32_t)btn_number - bounds, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,117 +100,130 @@ static bool infrared_move_view_input_callback(InputEvent* event, void* context)
|
||||
|
||||
if(((event->type == InputTypeShort || event->type == InputTypeRepeat)) &&
|
||||
((event->key == InputKeyUp) || (event->key == InputKeyDown))) {
|
||||
bool is_moving = false;
|
||||
uint32_t index_old = 0;
|
||||
uint32_t index_new = 0;
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
is_moving = model->is_moving;
|
||||
index_old = model->item_idx;
|
||||
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);
|
||||
const int32_t item_idx_prev = model->current_idx;
|
||||
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->item_idx <= 0) {
|
||||
model->item_idx = model->btn_number;
|
||||
if(model->current_idx <= 0) {
|
||||
model->current_idx = btn_number;
|
||||
}
|
||||
model->item_idx--;
|
||||
model->current_idx--;
|
||||
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->item_idx++;
|
||||
if(model->item_idx >= (int32_t)(model->btn_number)) {
|
||||
model->item_idx = 0;
|
||||
model->current_idx++;
|
||||
if(model->current_idx >= (int32_t)(btn_number)) {
|
||||
model->current_idx = 0;
|
||||
}
|
||||
}
|
||||
index_new = model->item_idx;
|
||||
|
||||
if(model->is_moving) {
|
||||
InfraredMoveViewItemArray_swap_at(
|
||||
model->labels, item_idx_prev, model->current_idx);
|
||||
}
|
||||
|
||||
update_list_offset(model);
|
||||
},
|
||||
!is_moving);
|
||||
if((is_moving) && (move_view->move_cb)) {
|
||||
move_view->move_cb(index_old, index_new, move_view->cb_context);
|
||||
infrared_move_view_list_update(move_view);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
true);
|
||||
|
||||
if((event->key == InputKeyOk) && (event->type == InputTypeShort)) {
|
||||
consumed = true;
|
||||
|
||||
} else if((event->key == InputKeyOk) && (event->type == InputTypeShort)) {
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{ model->is_moving = !(model->is_moving); },
|
||||
{
|
||||
if(!model->is_moving) {
|
||||
model->start_idx = model->current_idx;
|
||||
} else if(move_view->callback) {
|
||||
move_view->callback(
|
||||
model->start_idx, model->current_idx, move_view->callback_context);
|
||||
}
|
||||
model->is_moving = !(model->is_moving);
|
||||
},
|
||||
true);
|
||||
|
||||
consumed = true;
|
||||
|
||||
} else if(event->key == InputKeyBack) {
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
if(model->is_moving && move_view->callback) {
|
||||
move_view->callback(
|
||||
model->start_idx, model->current_idx, move_view->callback_context);
|
||||
}
|
||||
model->is_moving = false;
|
||||
},
|
||||
false);
|
||||
|
||||
// Not consuming, Back event is passed thru
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void infrared_move_view_on_exit(void* context) {
|
||||
furi_assert(context);
|
||||
InfraredMoveView* move_view = context;
|
||||
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
if(model->btn_names) {
|
||||
free(model->btn_names);
|
||||
model->btn_names = NULL;
|
||||
}
|
||||
model->btn_number = 0;
|
||||
model->get_item_cb = NULL;
|
||||
},
|
||||
false);
|
||||
move_view->cb_context = NULL;
|
||||
}
|
||||
|
||||
void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback) {
|
||||
furi_assert(move_view);
|
||||
move_view->move_cb = callback;
|
||||
}
|
||||
|
||||
void infrared_move_view_list_init(
|
||||
void infrared_move_view_set_callback(
|
||||
InfraredMoveView* move_view,
|
||||
uint32_t item_count,
|
||||
InfraredMoveGetItemCallback load_cb,
|
||||
InfraredMoveCallback callback,
|
||||
void* context) {
|
||||
furi_assert(move_view);
|
||||
move_view->cb_context = context;
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
furi_assert(model->btn_names == NULL);
|
||||
model->btn_names = malloc(sizeof(char*) * item_count);
|
||||
model->btn_number = item_count;
|
||||
model->get_item_cb = load_cb;
|
||||
},
|
||||
false);
|
||||
move_view->callback = callback;
|
||||
move_view->callback_context = context;
|
||||
}
|
||||
|
||||
void infrared_move_view_list_update(InfraredMoveView* move_view) {
|
||||
furi_assert(move_view);
|
||||
void infrared_move_view_add_item(InfraredMoveView* move_view, const char* label) {
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{ InfraredMoveViewItemArray_push_back(model->labels, label); },
|
||||
true);
|
||||
}
|
||||
|
||||
void infrared_move_view_reset(InfraredMoveView* move_view) {
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
for(uint32_t i = 0; i < model->btn_number; i++) {
|
||||
if(!model->get_item_cb) break;
|
||||
model->btn_names[i] = model->get_item_cb(i, move_view->cb_context);
|
||||
}
|
||||
InfraredMoveViewItemArray_reset(model->labels);
|
||||
model->list_offset = 0;
|
||||
model->start_idx = 0;
|
||||
model->current_idx = 0;
|
||||
model->is_moving = false;
|
||||
},
|
||||
true);
|
||||
false);
|
||||
move_view->callback_context = NULL;
|
||||
}
|
||||
|
||||
InfraredMoveView* infrared_move_view_alloc(void) {
|
||||
InfraredMoveView* move_view = malloc(sizeof(InfraredMoveView));
|
||||
|
||||
move_view->view = view_alloc();
|
||||
view_allocate_model(move_view->view, ViewModelTypeLocking, sizeof(InfraredMoveViewModel));
|
||||
view_set_draw_callback(move_view->view, infrared_move_view_draw_callback);
|
||||
view_set_input_callback(move_view->view, infrared_move_view_input_callback);
|
||||
view_set_exit_callback(move_view->view, infrared_move_view_on_exit);
|
||||
view_set_context(move_view->view, move_view);
|
||||
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{ InfraredMoveViewItemArray_init(model->labels); },
|
||||
true);
|
||||
|
||||
return move_view;
|
||||
}
|
||||
|
||||
void infrared_move_view_free(InfraredMoveView* move_view) {
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{ InfraredMoveViewItemArray_clear(model->labels); },
|
||||
true);
|
||||
|
||||
view_free(move_view->view);
|
||||
free(move_view);
|
||||
}
|
||||
|
@ -6,20 +6,17 @@ typedef struct InfraredMoveView InfraredMoveView;
|
||||
|
||||
typedef void (*InfraredMoveCallback)(uint32_t index_old, uint32_t index_new, void* context);
|
||||
|
||||
typedef const char* (*InfraredMoveGetItemCallback)(uint32_t index, void* context);
|
||||
|
||||
InfraredMoveView* infrared_move_view_alloc(void);
|
||||
|
||||
void infrared_move_view_free(InfraredMoveView* debug_view);
|
||||
|
||||
View* infrared_move_view_get_view(InfraredMoveView* debug_view);
|
||||
|
||||
void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback);
|
||||
|
||||
void infrared_move_view_list_init(
|
||||
void infrared_move_view_set_callback(
|
||||
InfraredMoveView* move_view,
|
||||
uint32_t item_count,
|
||||
InfraredMoveGetItemCallback load_cb,
|
||||
InfraredMoveCallback callback,
|
||||
void* context);
|
||||
|
||||
void infrared_move_view_list_update(InfraredMoveView* move_view);
|
||||
void infrared_move_view_add_item(InfraredMoveView* move_view, const char* label);
|
||||
|
||||
void infrared_move_view_reset(InfraredMoveView* move_view);
|
||||
|
Loading…
Reference in New Issue
Block a user