Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FL-3456] Allow for larger Infrared remotes #3164

Merged
merged 45 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ea9896d
Do not load all signals at once (Draft)
gsurkov Oct 20, 2023
aae1c7e
Minor cleanup
gsurkov Oct 20, 2023
2992c07
Minor cleanup (x2)
gsurkov Oct 20, 2023
bfc1814
Refactor remote renaming
gsurkov Oct 20, 2023
e11df87
Improve function signatures
gsurkov Oct 20, 2023
6ca330f
Rename infrared_remote functions
gsurkov Oct 20, 2023
5b2899d
Optimise signal loading
gsurkov Oct 21, 2023
0cb685f
Implement adding signals to remote
gsurkov Oct 21, 2023
ce24c21
Add read_name() method
gsurkov Oct 21, 2023
cf4ae22
Deprecate a function
gsurkov Oct 21, 2023
d9d1ada
Partially implement deleting signals (draft)
gsurkov Oct 21, 2023
3f5b32f
Use m-array instead of m-list for signal name directory
gsurkov Oct 22, 2023
f0c5854
Use plain C strings instead of furi_string
gsurkov Oct 22, 2023
489c8d0
Implement deleting signals
gsurkov Oct 22, 2023
d8295fc
Implement deleting signals via generalised callback
gsurkov Oct 22, 2023
c3df91d
Implement renaming signals
gsurkov Oct 22, 2023
ff19ad1
Rename some types
gsurkov Oct 22, 2023
ca16a37
Some more renaming
gsurkov Oct 22, 2023
436f05a
Remove unused type
gsurkov Oct 22, 2023
25232db
Implement inserting signals (internal use)
gsurkov Oct 22, 2023
2a907aa
Improve InfraredMoveView
gsurkov Oct 23, 2023
3eeab28
Send an event to move a signal
gsurkov Oct 23, 2023
5643fb4
Remove unused type
gsurkov Oct 23, 2023
a50b973
Implement moving signals
gsurkov Oct 23, 2023
9f65c07
Implement creating new remotes with one signal
gsurkov Oct 23, 2023
f90562e
Un-deprecate and rename a function
gsurkov Oct 23, 2023
46c9967
Add InfraredRemote API docs
gsurkov Oct 23, 2023
71eb52e
Merge remote-tracking branch 'origin/dev' into gsurkov/3456_ir_big_re…
gsurkov Oct 24, 2023
ef7ce17
Add InfraredSignal API docs
gsurkov Oct 24, 2023
f09de18
Fix PVS warnings
gsurkov Oct 24, 2023
6476ecb
Better error messages
gsurkov Oct 24, 2023
49fc082
Show progress pop-up when moving buttons in a remote
gsurkov Oct 24, 2023
e355fab
Copy labels to the InfraredMoveView to avoid pointer invalidation
gsurkov Oct 24, 2023
2248b39
Improve file selection scene
gsurkov Oct 24, 2023
71c263b
Fix PVS warnings
gsurkov Oct 24, 2023
351ef56
Show progress pop-up when renaming buttons in a remote
gsurkov Oct 24, 2023
4f09b20
Refactor a scene
gsurkov Oct 24, 2023
b5238be
Show progress when deleting a button from remote
gsurkov Oct 24, 2023
b204c08
Use a random name for temp files
gsurkov Oct 24, 2023
a31feb2
Add docs to infrared_brute_force.h
gsurkov Oct 25, 2023
4efa395
Rename Infrared type to InfraredApp
gsurkov Oct 25, 2023
4e40901
Fix formatting
gsurkov Oct 25, 2023
80100dc
Add docs to infrared_app_i.h
gsurkov Oct 25, 2023
6c4f291
Fix PVS warnings
gsurkov Oct 25, 2023
fe01720
Merge branch 'dev' into gsurkov/3456_ir_big_remote_oom
skotopes Oct 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions applications/main/infrared/infrared.h

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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;
}
Expand All @@ -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);
}

Expand All @@ -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));

InfraredRemoteButton* button = infrared_remote_get_button(infrared->remote, button_index);
InfraredSignal* signal = infrared_remote_button_get_signal(button);

infrared_tx_start_signal(infrared, signal);
}
void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index) {
furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote));

void infrared_tx_start_received(Infrared* infrared) {
infrared_tx_start_signal(infrared, infrared->received_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_stop(Infrared* infrared) {
void infrared_tx_stop(InfraredApp* infrared) {
if(!infrared->app_state.is_transmitting) {
return;
}
Expand All @@ -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;
Expand All @@ -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,
Expand All @@ -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);

Expand All @@ -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);
}
}

Expand Down
15 changes: 15 additions & 0 deletions applications/main/infrared/infrared_app.h
Original file line number Diff line number Diff line change
@@ -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;
Loading