timed-remote

Flipper Zero app for sending delayed IR commands
git clone git://src.adamsgaard.dk/timed-remote # fast
git clone https://src.adamsgaard.dk/timed-remote.git # slow
Log | Files | Refs | README | LICENSE Back to index

commit 45ffc081a50c3e5508c8bae60c0de9987c47a621
parent e6560e7f271134addcbadcf3c326feec4b76b0d8
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Tue, 17 Feb 2026 21:30:35 +0100

refactor(style): apply openbsd style(9)

Diffstat:
Mhelpers/ir_helper.c | 265++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mhelpers/ir_helper.h | 10+++++-----
Mhelpers/time_helper.c | 47+++++++++++++++++++++++++----------------------
Mhelpers/time_helper.h | 8++++----
Mscenes/scene_confirm.c | 45++++++++++++++++++++++++++++-----------------
Mscenes/scene_ir_browse.c | 83+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mscenes/scene_ir_select.c | 102+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mscenes/scene_timer_config.c | 261+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mscenes/scene_timer_running.c | 192++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mscenes/timed_remote_scene.c | 44++++++++++++++++++++++----------------------
Mscenes/timed_remote_scene.h | 56++++++++++++++++++++++++++++----------------------------
Mtimed_remote.c | 69+++++++++++++++++++++++++++++++++++++++------------------------------
Mtimed_remote.h | 14+++++++-------
13 files changed, 686 insertions(+), 510 deletions(-)

diff --git a/helpers/ir_helper.c b/helpers/ir_helper.c @@ -1,123 +1,111 @@ -#include "ir_helper.h" - #include <flipper_format/flipper_format.h> #include <furi.h> #include <infrared/worker/infrared_worker.h> #include <lib/infrared/signal/infrared_error_code.h> +#include <stdlib.h> #include <storage/storage.h> +#include <string.h> + +#include "ir_helper.h" + +static bool ir_add_signal(IrSignalList *, InfraredSignal *, const char *); +static bool ir_is_file(const FileInfo *, const char *); IrSignalList * ir_list_alloc(void) { - IrSignalList *list = malloc(sizeof(IrSignalList)); - if (!list) + IrSignalList *list; + + list = malloc(sizeof(IrSignalList)); + if (list == NULL) return NULL; + list->items = NULL; list->count = 0; list->capacity = 0; + return list; } void ir_list_free(IrSignalList *list) { - if (!list) + size_t i; + + if (list == NULL) return; - for (size_t i = 0; i < list->count; i++) - { - if (list->items[i].signal) - { + for (i = 0; i < list->count; i++) { + if (list->items[i].signal != NULL) infrared_signal_free(list->items[i].signal); - } - if (list->items[i].name) - { + if (list->items[i].name != NULL) furi_string_free(list->items[i].name); - } - } - if (list->items) - { - free(list->items); } + free(list->items); free(list); } -static bool -add_sig(IrSignalList *list, InfraredSignal *signal, const char *name) -{ - if (list->count >= list->capacity) - { - size_t new_capacity = list->capacity == 0 ? 8 : list->capacity * 2; - IrSignalItem *items = realloc(list->items, new_capacity * sizeof(IrSignalItem)); - if (!items) - return false; - list->items = items; - list->capacity = new_capacity; - } - - list->items[list->count].signal = signal; - list->items[list->count].name = furi_string_alloc_set(name); - if (!list->items[list->count].name) - return false; - list->count++; - return true; -} - bool ir_load(const char *path, IrSignalList *list) { - if (!path || !list) + InfraredErrorCode err; + InfraredSignal *signal; + FlipperFormat *ff; + FuriString *filetype; + FuriString *sig; + Storage *storage; + uint32_t version; + bool header_ok; + bool success; + + if (path == NULL || list == NULL) return false; - Storage *storage = furi_record_open(RECORD_STORAGE); - if (!storage) + storage = furi_record_open(RECORD_STORAGE); + if (storage == NULL) return false; - FlipperFormat *ff = flipper_format_file_alloc(storage); - if (!ff) - { + ff = flipper_format_file_alloc(storage); + if (ff == NULL) { furi_record_close(RECORD_STORAGE); return false; } - bool success = false; - FuriString *filetype = NULL; - FuriString *sig = NULL; + success = false; + filetype = NULL; + sig = NULL; - do - { + do { if (!flipper_format_file_open_existing(ff, path)) break; filetype = furi_string_alloc(); - if (!filetype) + if (filetype == NULL) break; - uint32_t version; - bool header_ok = flipper_format_read_header(ff, filetype, &version); + header_ok = flipper_format_read_header(ff, filetype, &version); if (!header_ok) break; sig = furi_string_alloc(); - if (!sig) + if (sig == NULL) break; - while (true) - { - InfraredSignal *signal = infrared_signal_alloc(); - if (!signal) + for (;;) { + signal = infrared_signal_alloc(); + if (signal == NULL) break; - InfraredErrorCode err = infrared_signal_read(signal, ff, sig); - if (err == InfraredErrorCodeNone) - { - if (!add_sig(list, signal, furi_string_get_cstr(sig))) - { + err = infrared_signal_read(signal, ff, sig); + if (err == InfraredErrorCodeNone) { + if (!ir_add_signal( + list, + signal, + furi_string_get_cstr(sig))) { infrared_signal_free(signal); break; } - } else - { + } else { infrared_signal_free(signal); break; } @@ -125,12 +113,14 @@ ir_load(const char *path, IrSignalList *list) success = true; } while (false); - if (filetype) + if (filetype != NULL) furi_string_free(filetype); - if (sig) + if (sig != NULL) furi_string_free(sig); + flipper_format_free(ff); furi_record_close(RECORD_STORAGE); + return success; } @@ -140,103 +130,130 @@ ir_tx(InfraredSignal *signal) infrared_signal_transmit(signal); } -static bool -is_ir(const FileInfo *file_info, const char *name) -{ - if ((file_info->flags & FSF_DIRECTORY) || !name) - return false; - - size_t len = strlen(name); - return len > 3 && strcmp(name + len - 3, ".ir") == 0; -} - bool -ir_files(const char *dir_path, FuriString ***files, - size_t *count) +ir_files(const char *dir_path, FuriString ***files, size_t *count) { - if (!dir_path || !files || !count) + FuriString **next_files; + Storage *storage; + FileInfo file_info; + size_t capacity; + char name_buf[256]; + File *dir; + bool dir_open; + bool ok; + + if (dir_path == NULL || files == NULL || count == NULL) return false; *files = NULL; *count = 0; + capacity = 0; + dir_open = false; + ok = false; - Storage *storage = furi_record_open(RECORD_STORAGE); - if (!storage) + storage = furi_record_open(RECORD_STORAGE); + if (storage == NULL) return false; - File *dir = storage_file_alloc(storage); - if (!dir) - { - furi_record_close(RECORD_STORAGE); - return false; - } + dir = storage_file_alloc(storage); + if (dir == NULL) + goto done; if (!storage_dir_open(dir, dir_path)) - { - storage_file_free(dir); - furi_record_close(RECORD_STORAGE); - return false; - } - - FileInfo file_info; - char name_buf[256]; - size_t capacity = 0; + goto done; + dir_open = true; - while (storage_dir_read(dir, &file_info, name_buf, sizeof(name_buf))) - { - if (!is_ir(&file_info, name_buf)) + while (storage_dir_read(dir, &file_info, name_buf, sizeof(name_buf))) { + if (!ir_is_file(&file_info, name_buf)) continue; - if (*count >= capacity) - { - size_t new_capacity = capacity == 0 ? 8 : capacity * 2; - FuriString **next_files = realloc(*files, new_capacity * sizeof(FuriString *)); - if (!next_files) - { + if (*count >= capacity) { + size_t new_capacity; + + new_capacity = capacity == 0 ? 8 : capacity * 2; + next_files = realloc(*files, new_capacity * sizeof(FuriString *)); + if (next_files == NULL) { ir_files_free(*files, *count); *files = NULL; *count = 0; - storage_dir_close(dir); - storage_file_free(dir); - furi_record_close(RECORD_STORAGE); - return false; + goto done; } *files = next_files; capacity = new_capacity; } (*files)[*count] = furi_string_alloc_set(name_buf); - if (!(*files)[*count]) - { + if ((*files)[*count] == NULL) { ir_files_free(*files, *count); *files = NULL; *count = 0; - storage_dir_close(dir); - storage_file_free(dir); - furi_record_close(RECORD_STORAGE); - return false; + goto done; } (*count)++; } - storage_dir_close(dir); - storage_file_free(dir); + ok = true; + +done: + if (dir != NULL) { + if (dir_open) + storage_dir_close(dir); + storage_file_free(dir); + } furi_record_close(RECORD_STORAGE); - return true; + return ok; } void ir_files_free(FuriString **files, size_t count) { - if (!files) + size_t i; + + if (files == NULL) return; - for (size_t i = 0; i < count; i++) - { - if (files[i]) - { + for (i = 0; i < count; i++) { + if (files[i] != NULL) furi_string_free(files[i]); - } } free(files); } + +static bool +ir_add_signal(IrSignalList *list, InfraredSignal *signal, const char *name) +{ + IrSignalItem *items; + size_t new_capacity; + + if (list->count >= list->capacity) { + new_capacity = list->capacity == 0 ? 8 : list->capacity * 2; + items = realloc(list->items, new_capacity * sizeof(IrSignalItem)); + if (items == NULL) + return false; + list->items = items; + list->capacity = new_capacity; + } + + list->items[list->count].signal = signal; + list->items[list->count].name = furi_string_alloc_set(name); + if (list->items[list->count].name == NULL) + return false; + + list->count++; + return true; +} + +static bool +ir_is_file(const FileInfo *file_info, const char *name) +{ + size_t len; + + if ((file_info->flags & FSF_DIRECTORY) || name == NULL) + return false; + + len = strlen(name); + if (len <= 3) + return false; + + return strcmp(name + len - 3, ".ir") == 0; +} diff --git a/helpers/ir_helper.h b/helpers/ir_helper.h @@ -21,8 +21,8 @@ typedef struct } IrSignalList; IrSignalList *ir_list_alloc(void); -void ir_list_free(IrSignalList *list); -bool ir_load(const char *path, IrSignalList *list); -void ir_tx(InfraredSignal *signal); -bool ir_files(const char *dir_path, FuriString ***files, size_t *count); -void ir_files_free(FuriString **files, size_t count); +void ir_list_free(IrSignalList *); +bool ir_load(const char *, IrSignalList *); +void ir_tx(InfraredSignal *); +bool ir_files(const char *, FuriString ***, size_t *); +void ir_files_free(FuriString **, size_t); diff --git a/helpers/time_helper.c b/helpers/time_helper.c @@ -1,49 +1,44 @@ -#include "time_helper.h" - #ifndef TIMED_REMOTE_TEST_BUILD #include <furi_hal.h> #include <stdio.h> #endif +#include "time_helper.h" + uint32_t time_hms_sec(uint8_t h, uint8_t m, uint8_t s) { - return (uint32_t) h * 3600 + (uint32_t) m * 60 + (uint32_t) s; + return (uint32_t)h * 3600 + (uint32_t)m * 60 + (uint32_t)s; } void -time_sec_hms(uint32_t total_seconds, uint8_t *h, uint8_t *m, - uint8_t *s) +time_sec_hms(uint32_t total_seconds, uint8_t *h, uint8_t *m, uint8_t *s) { - /* Handle times >= 24 hours by wrapping */ - total_seconds = total_seconds % (24 * 3600); + /* Wrap values >= 24 hours into a single day. */ + total_seconds %= 24 * 3600; - *h = (uint8_t) (total_seconds / 3600); + *h = (uint8_t)(total_seconds / 3600); total_seconds %= 3600; - *m = (uint8_t) (total_seconds / 60); - *s = (uint8_t) (total_seconds % 60); + *m = (uint8_t)(total_seconds / 60); + *s = (uint8_t)(total_seconds % 60); } #ifndef TIMED_REMOTE_TEST_BUILD uint32_t -time_until(uint8_t target_h, uint8_t target_m, - uint8_t target_s) +time_until(uint8_t target_h, uint8_t target_m, uint8_t target_s) { DateTime now; + uint32_t now_seconds; + uint32_t target_seconds; + furi_hal_rtc_get_datetime(&now); - uint32_t now_seconds = - time_hms_sec(now.hour, now.minute, now.second); - uint32_t target_seconds = - time_hms_sec(target_h, target_m, target_s); + now_seconds = time_hms_sec(now.hour, now.minute, now.second); + target_seconds = time_hms_sec(target_h, target_m, target_s); if (target_seconds < now_seconds) - { - /* Roll to next day */ return (24 * 3600 - now_seconds) + target_seconds; - } - /* Target time is now or later today */ return target_seconds - now_seconds; } @@ -51,9 +46,17 @@ void time_name(char *buffer, size_t buffer_size) { DateTime now; + furi_hal_rtc_get_datetime(&now); - snprintf(buffer, buffer_size, "IR_%04d%02d%02d_%02d%02d%02d", now.year, - now.month, now.day, now.hour, now.minute, now.second); + snprintf(buffer, + buffer_size, + "IR_%04d%02d%02d_%02d%02d%02d", + now.year, + now.month, + now.day, + now.hour, + now.minute, + now.second); } #endif diff --git a/helpers/time_helper.h b/helpers/time_helper.h @@ -3,10 +3,10 @@ #include <stddef.h> #include <stdint.h> -uint32_t time_hms_sec(uint8_t h, uint8_t m, uint8_t s); -void time_sec_hms(uint32_t total_seconds, uint8_t *h, uint8_t *m, uint8_t *s); +uint32_t time_hms_sec(uint8_t, uint8_t, uint8_t); +void time_sec_hms(uint32_t, uint8_t *, uint8_t *, uint8_t *); #ifndef TIMED_REMOTE_TEST_BUILD -uint32_t time_until(uint8_t target_h, uint8_t target_m, uint8_t target_s); -void time_name(char *buffer, size_t buffer_size); +uint32_t time_until(uint8_t, uint8_t, uint8_t); +void time_name(char *, size_t); #endif diff --git a/scenes/scene_confirm.c b/scenes/scene_confirm.c @@ -2,39 +2,50 @@ #include "timed_remote_scene.h" static void -done_cb(void *context) +on_popup_done(void *context) { - TimedRemoteApp *a = context; - view_dispatcher_send_custom_event(a->vd, EvDone); + TimedRemoteApp *app; + + app = context; + view_dispatcher_send_custom_event(app->vd, EVENT_DONE); } void scene_done_enter(void *context) { - TimedRemoteApp *a = context; - popup_reset(a->popup); - popup_set_header(a->popup, "Signal Sent!", 64, 20, AlignCenter, AlignCenter); - popup_set_text(a->popup, a->sig, 64, 35, AlignCenter, AlignCenter); - popup_set_timeout(a->popup, 2000); - popup_set_context(a->popup, a); - popup_set_callback(a->popup, done_cb); - popup_enable_timeout(a->popup); - view_dispatcher_switch_to_view(a->vd, ViewPop); + TimedRemoteApp *app; + + app = context; + popup_reset(app->popup); + popup_set_header(app->popup, "Signal Sent!", 64, 20, AlignCenter, AlignCenter); + popup_set_text(app->popup, app->sig, 64, 35, AlignCenter, AlignCenter); + popup_set_timeout(app->popup, 2000); + popup_set_context(app->popup, app); + popup_set_callback(app->popup, on_popup_done); + popup_enable_timeout(app->popup); + view_dispatcher_switch_to_view(app->vd, VIEW_POP); } bool scene_done_event(void *context, SceneManagerEvent event) { - TimedRemoteApp *a = context; - if (event.type != SceneManagerEventTypeCustom || event.event != EvDone) + TimedRemoteApp *app; + + app = context; + if (event.type != SceneManagerEventTypeCustom) return false; - scene_manager_search_and_switch_to_previous_scene(a->sm, ScBrowse); + if (event.event != EVENT_DONE) + return false; + + scene_manager_search_and_switch_to_previous_scene(app->sm, SCENE_BROWSE); return true; } void scene_done_exit(void *context) { - TimedRemoteApp *a = context; - popup_reset(a->popup); + TimedRemoteApp *app; + + app = context; + popup_reset(app->popup); } diff --git a/scenes/scene_ir_browse.c b/scenes/scene_ir_browse.c @@ -1,64 +1,89 @@ +#include <stdio.h> + #include "../helpers/ir_helper.h" #include "../timed_remote.h" #include "timed_remote_scene.h" -#define IR_DIR "/ext/infrared" +#define IR_DIRECTORY "/ext/infrared" static FuriString **files; static size_t nfiles; static void -pick_file(void *context, uint32_t i) +pick_file(void *context, uint32_t index) { - TimedRemoteApp *a = context; - if (i >= nfiles) + TimedRemoteApp *app; + + app = context; + if (index >= nfiles) return; - snprintf(a->file, sizeof(a->file), "%s/%s", IR_DIR, furi_string_get_cstr(files[i])); - view_dispatcher_send_custom_event(a->vd, EvFile); + + snprintf( + app->file, + sizeof(app->file), + "%s/%s", + IR_DIRECTORY, + furi_string_get_cstr(files[index])); + view_dispatcher_send_custom_event(app->vd, EVENT_FILE_SELECTED); } void scene_browse_enter(void *context) { - TimedRemoteApp *a = context; - submenu_reset(a->submenu); - submenu_set_header(a->submenu, "Select IR File"); - - if (ir_files(IR_DIR, &files, &nfiles)) - { - if (nfiles == 0) - { - submenu_add_item(a->submenu, "(No IR files found)", 0, NULL, NULL); - } else - { - for (size_t i = 0; i < nfiles; i++) - submenu_add_item(a->submenu, furi_string_get_cstr(files[i]), i, pick_file, a); + TimedRemoteApp *app; + size_t i; + + app = context; + submenu_reset(app->submenu); + submenu_set_header(app->submenu, "Select IR File"); + + if (ir_files(IR_DIRECTORY, &files, &nfiles)) { + if (nfiles == 0) { + submenu_add_item( + app->submenu, "(No IR files found)", 0, NULL, NULL); + } else { + for (i = 0; i < nfiles; i++) { + submenu_add_item( + app->submenu, + furi_string_get_cstr(files[i]), + i, + pick_file, + app); + } } - } else - { - submenu_add_item(a->submenu, "(Error reading directory)", 0, NULL, NULL); + } else { + submenu_add_item( + app->submenu, "(Error reading directory)", 0, NULL, NULL); } - view_dispatcher_switch_to_view(a->vd, ViewMenu); + view_dispatcher_switch_to_view(app->vd, VIEW_MENU); } bool scene_browse_event(void *context, SceneManagerEvent event) { - TimedRemoteApp *a = context; - if (event.type != SceneManagerEventTypeCustom || event.event != EvFile) + TimedRemoteApp *app; + + app = context; + if (event.type != SceneManagerEventTypeCustom) + return false; + if (event.event != EVENT_FILE_SELECTED) return false; - scene_manager_next_scene(a->sm, ScSelect); + + scene_manager_next_scene(app->sm, SCENE_SELECT); return true; } void scene_browse_exit(void *context) { - TimedRemoteApp *a = context; - submenu_reset(a->submenu); - if (!files) + TimedRemoteApp *app; + + app = context; + submenu_reset(app->submenu); + if (files == NULL) return; + ir_files_free(files, nfiles); files = NULL; nfiles = 0; diff --git a/scenes/scene_ir_select.c b/scenes/scene_ir_select.c @@ -1,75 +1,95 @@ +#include <string.h> + #include "../helpers/ir_helper.h" #include "../timed_remote.h" #include "timed_remote_scene.h" -static IrSignalList *sigs; +static IrSignalList *signals; static void -pick_sig(void *context, uint32_t i) +pick_signal(void *context, uint32_t index) { - TimedRemoteApp *a = context; - if (!sigs || i >= sigs->count) + TimedRemoteApp *app; + + app = context; + if (signals == NULL || index >= signals->count) return; - if (a->ir) - infrared_signal_free(a->ir); - a->ir = infrared_signal_alloc(); - if (!a->ir) + if (app->ir != NULL) + infrared_signal_free(app->ir); + + app->ir = infrared_signal_alloc(); + if (app->ir == NULL) return; - infrared_signal_set_signal(a->ir, sigs->items[i].signal); - strncpy(a->sig, furi_string_get_cstr(sigs->items[i].name), sizeof(a->sig) - 1); - a->sig[sizeof(a->sig) - 1] = '\0'; - view_dispatcher_send_custom_event(a->vd, EvSig); + infrared_signal_set_signal(app->ir, signals->items[index].signal); + strncpy( + app->sig, + furi_string_get_cstr(signals->items[index].name), + sizeof(app->sig) - 1); + app->sig[sizeof(app->sig) - 1] = '\0'; + view_dispatcher_send_custom_event(app->vd, EVENT_SIGNAL_SELECTED); } void scene_select_enter(void *context) { - TimedRemoteApp *a = context; - submenu_reset(a->submenu); - submenu_set_header(a->submenu, "Select Signal"); - - sigs = ir_list_alloc(); - if (!sigs) - { - submenu_add_item(a->submenu, "(Out of memory)", 0, NULL, NULL); - } else if (ir_load(a->file, sigs)) - { - if (sigs->count == 0) - { - submenu_add_item(a->submenu, "(No signals in file)", 0, NULL, NULL); - } else - { - for (size_t i = 0; i < sigs->count; i++) + TimedRemoteApp *app; + size_t i; + + app = context; + submenu_reset(app->submenu); + submenu_set_header(app->submenu, "Select Signal"); + + signals = ir_list_alloc(); + if (signals == NULL) { + submenu_add_item(app->submenu, "(Out of memory)", 0, NULL, NULL); + } else if (ir_load(app->file, signals)) { + if (signals->count == 0) { + submenu_add_item( + app->submenu, "(No signals in file)", 0, NULL, NULL); + } else { + for (i = 0; i < signals->count; i++) { submenu_add_item( - a->submenu, furi_string_get_cstr(sigs->items[i].name), i, pick_sig, a); + app->submenu, + furi_string_get_cstr(signals->items[i].name), + i, + pick_signal, + app); + } } - } else - { - submenu_add_item(a->submenu, "(Error reading file)", 0, NULL, NULL); + } else { + submenu_add_item(app->submenu, "(Error reading file)", 0, NULL, NULL); } - view_dispatcher_switch_to_view(a->vd, ViewMenu); + view_dispatcher_switch_to_view(app->vd, VIEW_MENU); } bool scene_select_event(void *context, SceneManagerEvent event) { - TimedRemoteApp *a = context; - if (event.type != SceneManagerEventTypeCustom || event.event != EvSig) + TimedRemoteApp *app; + + app = context; + if (event.type != SceneManagerEventTypeCustom) + return false; + if (event.event != EVENT_SIGNAL_SELECTED) return false; - scene_manager_next_scene(a->sm, ScCfg); + + scene_manager_next_scene(app->sm, SCENE_CONFIG); return true; } void scene_select_exit(void *context) { - TimedRemoteApp *a = context; - submenu_reset(a->submenu); - if (!sigs) + TimedRemoteApp *app; + + app = context; + submenu_reset(app->submenu); + if (signals == NULL) return; - ir_list_free(sigs); - sigs = NULL; + + ir_list_free(signals); + signals = NULL; } diff --git a/scenes/scene_timer_config.c b/scenes/scene_timer_config.c @@ -1,199 +1,246 @@ +#include <stdio.h> + #include "../timed_remote.h" #include "timed_remote_scene.h" enum { - IdxMode, - IdxH, - IdxM, - IdxS, - IdxRepeat, - IdxStart, + ITEM_MODE, + ITEM_HOURS, + ITEM_MINUTES, + ITEM_SECONDS, + ITEM_REPEAT, + ITEM_START, }; -#define REP_OFF 0U -#define REP_INF 255U +#define REPEAT_OFF 0U +#define REPEAT_UNLIMITED 255U + +static void add_time_item(VariableItemList *, const char *, uint8_t, uint8_t, + VariableItemChangeCallback, TimedRemoteApp *); +static void build_items(TimedRemoteApp *); +static uint8_t index_from_repeat(uint8_t); +static void set_repeat_text(VariableItem *, uint8_t); +static uint8_t repeat_from_index(uint8_t); +static void set_two_digits(VariableItem *, uint8_t); +static uint32_t start_item_index(const TimedRemoteApp *); static void -set2(VariableItem *it, uint8_t v) +set_two_digits(VariableItem *item, uint8_t value) { - char s[4]; - snprintf(s, sizeof(s), "%02d", v); - variable_item_set_current_value_text(it, s); + char text[4]; + + snprintf(text, sizeof(text), "%02d", value); + variable_item_set_current_value_text(item, text); } static uint8_t -rep_from_idx(uint8_t i) +repeat_from_index(uint8_t index) { - if (i == 0) - return REP_OFF; - if (i == 1) - return REP_INF; - return i - 1; + if (index == 0) + return REPEAT_OFF; + if (index == 1) + return REPEAT_UNLIMITED; + return index - 1; } static uint8_t -idx_from_rep(uint8_t r) +index_from_repeat(uint8_t repeat) { - if (r == REP_OFF) + if (repeat == REPEAT_OFF) return 0; - if (r == REP_INF) + if (repeat == REPEAT_UNLIMITED) return 1; - return r + 1; + return repeat + 1; } static void -set_rep(VariableItem *it, uint8_t r) +set_repeat_text(VariableItem *item, uint8_t repeat) { - if (r == REP_OFF) - { - variable_item_set_current_value_text(it, "Off"); + char text[16]; + + if (repeat == REPEAT_OFF) { + variable_item_set_current_value_text(item, "Off"); return; } - if (r == REP_INF) - { - variable_item_set_current_value_text(it, "Unlimited"); + if (repeat == REPEAT_UNLIMITED) { + variable_item_set_current_value_text(item, "Unlimited"); return; } - char s[16]; - snprintf(s, sizeof(s), "%u", r); - variable_item_set_current_value_text(it, s); + + snprintf(text, sizeof(text), "%u", repeat); + variable_item_set_current_value_text(item, text); } static uint32_t -start_idx(const TimedRemoteApp *a) +start_item_index(const TimedRemoteApp *app) { - if (a->mode == ModeDown) - return IdxStart; - return IdxRepeat; + if (app->mode == MODE_COUNTDOWN) + return ITEM_START; + return ITEM_REPEAT; } static void -mode_chg(VariableItem *it) +on_mode_change(VariableItem *item) { - TimedRemoteApp *a = variable_item_get_context(it); - a->mode = variable_item_get_current_value_index(it) == 0 - ? ModeDown - : ModeSched; + TimedRemoteApp *app; + uint8_t mode_index; + + app = variable_item_get_context(item); + mode_index = variable_item_get_current_value_index(item); + app->mode = mode_index == 0 ? MODE_COUNTDOWN : MODE_SCHEDULED; variable_item_set_current_value_text( - it, a->mode == ModeDown ? "Countdown" : "Scheduled"); - if (a->mode == ModeSched) - a->repeat = REP_OFF; - view_dispatcher_send_custom_event(a->vd, EvMode); + item, + app->mode == MODE_COUNTDOWN ? "Countdown" : "Scheduled"); + if (app->mode == MODE_SCHEDULED) + app->repeat = REPEAT_OFF; + + view_dispatcher_send_custom_event(app->vd, EVENT_MODE_CHANGED); } static void -h_chg(VariableItem *it) +on_hours_change(VariableItem *item) { - TimedRemoteApp *a = variable_item_get_context(it); - a->h = variable_item_get_current_value_index(it); - set2(it, a->h); + TimedRemoteApp *app; + + app = variable_item_get_context(item); + app->h = variable_item_get_current_value_index(item); + set_two_digits(item, app->h); } static void -m_chg(VariableItem *it) +on_minutes_change(VariableItem *item) { - TimedRemoteApp *a = variable_item_get_context(it); - a->m = variable_item_get_current_value_index(it); - set2(it, a->m); + TimedRemoteApp *app; + + app = variable_item_get_context(item); + app->m = variable_item_get_current_value_index(item); + set_two_digits(item, app->m); } static void -s_chg(VariableItem *it) +on_seconds_change(VariableItem *item) { - TimedRemoteApp *a = variable_item_get_context(it); - a->s = variable_item_get_current_value_index(it); - set2(it, a->s); + TimedRemoteApp *app; + + app = variable_item_get_context(item); + app->s = variable_item_get_current_value_index(item); + set_two_digits(item, app->s); } static void -rep_chg(VariableItem *it) +on_repeat_change(VariableItem *item) { - TimedRemoteApp *a = variable_item_get_context(it); - a->repeat = rep_from_idx(variable_item_get_current_value_index(it)); - set_rep(it, a->repeat); + TimedRemoteApp *app; + + app = variable_item_get_context(item); + app->repeat = repeat_from_index(variable_item_get_current_value_index(item)); + set_repeat_text(item, app->repeat); } static void -enter_cb(void *ctx, uint32_t i) +on_enter(void *context, uint32_t index) { - TimedRemoteApp *a = ctx; - if (i != start_idx(a)) + TimedRemoteApp *app; + + app = context; + if (index != start_item_index(app)) return; - view_dispatcher_send_custom_event(a->vd, EvCfg); + + view_dispatcher_send_custom_event(app->vd, EVENT_TIMER_CONFIGURED); } static void -add_tm(VariableItemList *l, const char *n, uint8_t nvals, uint8_t v, VariableItemChangeCallback cb, TimedRemoteApp *a) +add_time_item(VariableItemList *list, + const char *name, + uint8_t value_count, + uint8_t value, + VariableItemChangeCallback callback, + TimedRemoteApp *app) { - VariableItem *it = variable_item_list_add(l, n, nvals, cb, a); - variable_item_set_current_value_index(it, v); - set2(it, v); + VariableItem *item; + + item = variable_item_list_add(list, name, value_count, callback, app); + variable_item_set_current_value_index(item, value); + set_two_digits(item, value); } static void -build(TimedRemoteApp *a) +build_items(TimedRemoteApp *app) { - variable_item_list_reset(a->vlist); - - VariableItem *it = variable_item_list_add(a->vlist, "Mode", 2, mode_chg, a); - variable_item_set_current_value_index(it, a->mode == ModeDown ? 0 : 1); - variable_item_set_current_value_text( - it, a->mode == ModeDown ? "Countdown" : "Scheduled"); + VariableItem *item; - add_tm(a->vlist, "Hours", 24, a->h, h_chg, a); - add_tm(a->vlist, "Minutes", 60, a->m, m_chg, a); - add_tm(a->vlist, "Seconds", 60, a->s, s_chg, a); + variable_item_list_reset(app->vlist); - if (a->mode == ModeDown) - { - it = variable_item_list_add(a->vlist, "Repeat", 101, rep_chg, a); - variable_item_set_current_value_index(it, idx_from_rep(a->repeat)); - set_rep(it, a->repeat); + item = variable_item_list_add(app->vlist, "Mode", 2, on_mode_change, app); + variable_item_set_current_value_index( + item, + app->mode == MODE_COUNTDOWN ? 0 : 1); + variable_item_set_current_value_text( + item, + app->mode == MODE_COUNTDOWN ? "Countdown" : "Scheduled"); + + add_time_item(app->vlist, "Hours", 24, app->h, on_hours_change, app); + add_time_item(app->vlist, "Minutes", 60, app->m, on_minutes_change, app); + add_time_item(app->vlist, "Seconds", 60, app->s, on_seconds_change, app); + + if (app->mode == MODE_COUNTDOWN) { + item = variable_item_list_add( + app->vlist, + "Repeat", + 101, + on_repeat_change, + app); + variable_item_set_current_value_index(item, index_from_repeat(app->repeat)); + set_repeat_text(item, app->repeat); } - variable_item_list_add(a->vlist, ">> Start Timer <<", 0, NULL, NULL); - variable_item_list_set_enter_callback(a->vlist, enter_cb, a); + variable_item_list_add(app->vlist, ">> Start Timer <<", 0, NULL, NULL); + variable_item_list_set_enter_callback(app->vlist, on_enter, app); } void scene_cfg_enter(void *context) { - TimedRemoteApp *a = context; - build(a); - view_dispatcher_switch_to_view(a->vd, ViewList); + TimedRemoteApp *app; + + app = context; + build_items(app); + view_dispatcher_switch_to_view(app->vd, VIEW_LIST); } bool scene_cfg_event(void *context, SceneManagerEvent event) { - TimedRemoteApp *a = context; + TimedRemoteApp *app; + + app = context; if (event.type != SceneManagerEventTypeCustom) return false; - if (event.event == EvMode) - { - build(a); + + if (event.event == EVENT_MODE_CHANGED) { + build_items(app); return true; } - if (event.event != EvCfg) + if (event.event != EVENT_TIMER_CONFIGURED) return false; - if (a->repeat == REP_OFF) - { - a->repeat_left = 1; - } else if (a->repeat == REP_INF) - { - a->repeat_left = REP_INF; - } else - { - a->repeat_left = a->repeat + 1; - } - scene_manager_next_scene(a->sm, ScRun); + + if (app->repeat == REPEAT_OFF) + app->repeat_left = 1; + else if (app->repeat == REPEAT_UNLIMITED) + app->repeat_left = REPEAT_UNLIMITED; + else + app->repeat_left = app->repeat + 1; + + scene_manager_next_scene(app->sm, SCENE_RUN); return true; } void scene_cfg_exit(void *context) { - TimedRemoteApp *a = context; - variable_item_list_reset(a->vlist); + TimedRemoteApp *app; + + app = context; + variable_item_list_reset(app->vlist); } diff --git a/scenes/scene_timer_running.c b/scenes/scene_timer_running.c @@ -1,132 +1,176 @@ +#include <stdio.h> + #include "../helpers/ir_helper.h" #include "../helpers/time_helper.h" #include "../timed_remote.h" #include "timed_remote_scene.h" +static uint32_t countdown_seconds(const TimedRemoteApp *); +static void render_timer(TimedRemoteApp *); + +#define REPEAT_UNLIMITED 255U + static void -tick_cb(void *context) +on_timer_tick(void *context) { - TimedRemoteApp *a = context; - view_dispatcher_send_custom_event(a->vd, EvTick); + TimedRemoteApp *app; + + app = context; + view_dispatcher_send_custom_event(app->vd, EVENT_TIMER_TICK); } static uint32_t -countdown(const TimedRemoteApp *a) +countdown_seconds(const TimedRemoteApp *app) { - return time_hms_sec(a->h, a->m, a->s); + return time_hms_sec(app->h, app->m, app->s); } static void -redraw(TimedRemoteApp *a) +render_timer(TimedRemoteApp *app) { - uint8_t h, m, s; - time_sec_hms(a->left, &h, &m, &s); - - char t[16]; - snprintf(t, sizeof(t), "%02d:%02d:%02d", h, m, s); - - widget_reset(a->widget); - widget_add_string_element(a->widget, 64, 5, AlignCenter, AlignTop, FontSecondary, a->sig); - widget_add_string_element(a->widget, 64, 25, AlignCenter, AlignTop, FontBigNumbers, t); - if (a->repeat > 0 && a->repeat < 255) - { - char r[24]; - snprintf(r, sizeof(r), "Repeat: %d/%d", a->repeat - a->repeat_left + 1, a->repeat); - widget_add_string_element(a->widget, 64, 52, AlignCenter, AlignTop, FontSecondary, r); - } else if (a->repeat == 255) - { + char timer[16]; + uint8_t h; + uint8_t m; + uint8_t s; + + time_sec_hms(app->left, &h, &m, &s); + snprintf(timer, sizeof(timer), "%02d:%02d:%02d", h, m, s); + + widget_reset(app->widget); + widget_add_string_element( + app->widget, + 64, + 5, + AlignCenter, + AlignTop, + FontSecondary, + app->sig); + widget_add_string_element( + app->widget, + 64, + 25, + AlignCenter, + AlignTop, + FontBigNumbers, + timer); + + if (app->repeat > 0 && app->repeat < REPEAT_UNLIMITED) { + char repeat[24]; + + snprintf( + repeat, + sizeof(repeat), + "Repeat: %d/%d", + app->repeat - app->repeat_left + 1, + app->repeat); + widget_add_string_element( + app->widget, + 64, + 52, + AlignCenter, + AlignTop, + FontSecondary, + repeat); + } else if (app->repeat == REPEAT_UNLIMITED) { widget_add_string_element( - a->widget, 64, 52, AlignCenter, AlignTop, FontSecondary, "Repeat: Unlimited"); + app->widget, + 64, + 52, + AlignCenter, + AlignTop, + FontSecondary, + "Repeat: Unlimited"); } } void scene_run_enter(void *context) { - TimedRemoteApp *a = context; - - if (a->mode == ModeDown) - { - a->left = countdown(a); - } else - { - a->left = time_until(a->h, a->m, a->s); - if (a->left == 0) - { - view_dispatcher_send_custom_event(a->vd, EvFire); + TimedRemoteApp *app; + + app = context; + if (app->mode == MODE_COUNTDOWN) { + app->left = countdown_seconds(app); + } else { + app->left = time_until(app->h, app->m, app->s); + if (app->left == 0) { + view_dispatcher_send_custom_event(app->vd, EVENT_FIRE_SIGNAL); return; } } - if (a->repeat == 0) - a->repeat_left = 1; + if (app->repeat == 0) + app->repeat_left = 1; - redraw(a); - view_dispatcher_switch_to_view(a->vd, ViewRun); + render_timer(app); + view_dispatcher_switch_to_view(app->vd, VIEW_RUN); - a->timer = furi_timer_alloc(tick_cb, FuriTimerTypePeriodic, a); - if (!a->timer) + app->timer = furi_timer_alloc(on_timer_tick, FuriTimerTypePeriodic, app); + if (app->timer == NULL) return; - furi_timer_start(a->timer, furi_kernel_get_tick_frequency()); + + furi_timer_start(app->timer, furi_kernel_get_tick_frequency()); } bool scene_run_event(void *context, SceneManagerEvent event) { - TimedRemoteApp *a = context; - if (event.type == SceneManagerEventTypeBack) - { - scene_manager_search_and_switch_to_previous_scene(a->sm, ScBrowse); + TimedRemoteApp *app; + + app = context; + if (event.type == SceneManagerEventTypeBack) { + scene_manager_search_and_switch_to_previous_scene( + app->sm, + SCENE_BROWSE); return true; } if (event.type != SceneManagerEventTypeCustom) return false; - if (event.event == EvTick) - { - if (a->left > 0) - { - a->left--; - redraw(a); + + if (event.event == EVENT_TIMER_TICK) { + if (app->left > 0) { + app->left--; + render_timer(app); } - if (a->left == 0) - view_dispatcher_send_custom_event(a->vd, EvFire); + if (app->left == 0) + view_dispatcher_send_custom_event(app->vd, EVENT_FIRE_SIGNAL); return true; } - if (event.event != EvFire) + if (event.event != EVENT_FIRE_SIGNAL) return false; - if (a->ir) - ir_tx(a->ir); + if (app->ir != NULL) + ir_tx(app->ir); - if (a->repeat == 255) - { - a->left = countdown(a); - redraw(a); + if (app->repeat == REPEAT_UNLIMITED) { + app->left = countdown_seconds(app); + render_timer(app); return true; } - if (a->repeat_left > 0) - a->repeat_left--; - if (a->repeat != 0 && a->repeat_left > 0) - { - a->left = countdown(a); - redraw(a); + if (app->repeat_left > 0) + app->repeat_left--; + + if (app->repeat != 0 && app->repeat_left > 0) { + app->left = countdown_seconds(app); + render_timer(app); return true; } - scene_manager_next_scene(a->sm, ScDone); + scene_manager_next_scene(app->sm, SCENE_DONE); return true; } void scene_run_exit(void *context) { - TimedRemoteApp *a = context; - if (a->timer) - { - furi_timer_stop(a->timer); - furi_timer_free(a->timer); - a->timer = NULL; + TimedRemoteApp *app; + + app = context; + if (app->timer != NULL) { + furi_timer_stop(app->timer); + furi_timer_free(app->timer); + app->timer = NULL; } - widget_reset(a->widget); + widget_reset(app->widget); } diff --git a/scenes/timed_remote_scene.c b/scenes/timed_remote_scene.c @@ -1,32 +1,32 @@ #include "timed_remote_scene.h" -static void (*const enter[])(void *) = { - [ScBrowse] = scene_browse_enter, - [ScSelect] = scene_select_enter, - [ScCfg] = scene_cfg_enter, - [ScRun] = scene_run_enter, - [ScDone] = scene_done_enter, +static void (*const scene_on_enter[])(void *) = { + [SCENE_BROWSE] = scene_browse_enter, + [SCENE_SELECT] = scene_select_enter, + [SCENE_CONFIG] = scene_cfg_enter, + [SCENE_RUN] = scene_run_enter, + [SCENE_DONE] = scene_done_enter, }; -static bool (*const event[])(void *, SceneManagerEvent) = { - [ScBrowse] = scene_browse_event, - [ScSelect] = scene_select_event, - [ScCfg] = scene_cfg_event, - [ScRun] = scene_run_event, - [ScDone] = scene_done_event, +static bool (*const scene_on_event[])(void *, SceneManagerEvent) = { + [SCENE_BROWSE] = scene_browse_event, + [SCENE_SELECT] = scene_select_event, + [SCENE_CONFIG] = scene_cfg_event, + [SCENE_RUN] = scene_run_event, + [SCENE_DONE] = scene_done_event, }; -static void (*const on_exit[])(void *) = { - [ScBrowse] = scene_browse_exit, - [ScSelect] = scene_select_exit, - [ScCfg] = scene_cfg_exit, - [ScRun] = scene_run_exit, - [ScDone] = scene_done_exit, +static void (*const scene_on_exit[])(void *) = { + [SCENE_BROWSE] = scene_browse_exit, + [SCENE_SELECT] = scene_select_exit, + [SCENE_CONFIG] = scene_cfg_exit, + [SCENE_RUN] = scene_run_exit, + [SCENE_DONE] = scene_done_exit, }; const SceneManagerHandlers scene_handlers = { - .on_enter_handlers = enter, - .on_event_handlers = event, - .on_exit_handlers = on_exit, - .scene_num = ScN, + .on_enter_handlers = scene_on_enter, + .on_event_handlers = scene_on_event, + .on_exit_handlers = scene_on_exit, + .scene_num = SCENE_COUNT, }; diff --git a/scenes/timed_remote_scene.h b/scenes/timed_remote_scene.h @@ -4,37 +4,37 @@ typedef enum { - ScBrowse, - ScSelect, - ScCfg, - ScRun, - ScDone, - ScN, + SCENE_BROWSE, + SCENE_SELECT, + SCENE_CONFIG, + SCENE_RUN, + SCENE_DONE, + SCENE_COUNT, } SceneId; typedef enum { - EvFile, - EvSig, - EvMode, - EvCfg, - EvTick, - EvFire, - EvDone, + EVENT_FILE_SELECTED, + EVENT_SIGNAL_SELECTED, + EVENT_MODE_CHANGED, + EVENT_TIMER_CONFIGURED, + EVENT_TIMER_TICK, + EVENT_FIRE_SIGNAL, + EVENT_DONE, } EvId; -void scene_browse_enter(void *context); -bool scene_browse_event(void *context, SceneManagerEvent event); -void scene_browse_exit(void *context); -void scene_select_enter(void *context); -bool scene_select_event(void *context, SceneManagerEvent event); -void scene_select_exit(void *context); -void scene_cfg_enter(void *context); -bool scene_cfg_event(void *context, SceneManagerEvent event); -void scene_cfg_exit(void *context); -void scene_run_enter(void *context); -bool scene_run_event(void *context, SceneManagerEvent event); -void scene_run_exit(void *context); -void scene_done_enter(void *context); -bool scene_done_event(void *context, SceneManagerEvent event); -void scene_done_exit(void *context); +void scene_browse_enter(void *); +bool scene_browse_event(void *, SceneManagerEvent); +void scene_browse_exit(void *); +void scene_select_enter(void *); +bool scene_select_event(void *, SceneManagerEvent); +void scene_select_exit(void *); +void scene_cfg_enter(void *); +bool scene_cfg_event(void *, SceneManagerEvent); +void scene_cfg_exit(void *); +void scene_run_enter(void *); +bool scene_run_event(void *, SceneManagerEvent); +void scene_run_exit(void *); +void scene_done_enter(void *); +bool scene_done_event(void *, SceneManagerEvent); +void scene_done_exit(void *); diff --git a/timed_remote.c b/timed_remote.c @@ -1,26 +1,37 @@ -#include "timed_remote.h" +#include <stdlib.h> +#include <string.h> + #include "scenes/timed_remote_scene.h" +#include "timed_remote.h" extern const SceneManagerHandlers scene_handlers; static bool nav_cb(void *context) { - TimedRemoteApp *app = context; + TimedRemoteApp *app; + + app = context; return scene_manager_handle_back_event(app->sm); } static bool evt_cb(void *context, uint32_t evt) { - TimedRemoteApp *app = context; + TimedRemoteApp *app; + + app = context; return scene_manager_handle_custom_event(app->sm, evt); } TimedRemoteApp * timed_remote_app_alloc(void) { - TimedRemoteApp *app = malloc(sizeof(TimedRemoteApp)); + TimedRemoteApp *app; + + app = malloc(sizeof(TimedRemoteApp)); + if (app == NULL) + return NULL; memset(app, 0, sizeof(TimedRemoteApp)); app->gui = furi_record_open(RECORD_GUI); @@ -29,27 +40,26 @@ timed_remote_app_alloc(void) view_dispatcher_set_event_callback_context(app->vd, app); view_dispatcher_set_navigation_event_callback(app->vd, nav_cb); view_dispatcher_set_custom_event_callback(app->vd, evt_cb); - view_dispatcher_attach_to_gui(app->vd, app->gui, - ViewDispatcherTypeFullscreen); + view_dispatcher_attach_to_gui( + app->vd, app->gui, ViewDispatcherTypeFullscreen); app->sm = scene_manager_alloc(&scene_handlers, app); app->submenu = submenu_alloc(); - view_dispatcher_add_view(app->vd, ViewMenu, - submenu_get_view(app->submenu)); + view_dispatcher_add_view( + app->vd, VIEW_MENU, submenu_get_view(app->submenu)); app->vlist = variable_item_list_alloc(); view_dispatcher_add_view( - app->vd, ViewList, - variable_item_list_get_view(app->vlist)); + app->vd, VIEW_LIST, variable_item_list_get_view(app->vlist)); app->widget = widget_alloc(); - view_dispatcher_add_view(app->vd, ViewRun, - widget_get_view(app->widget)); + view_dispatcher_add_view( + app->vd, VIEW_RUN, widget_get_view(app->widget)); app->popup = popup_alloc(); - view_dispatcher_add_view(app->vd, ViewPop, - popup_get_view(app->popup)); + view_dispatcher_add_view( + app->vd, VIEW_POP, popup_get_view(app->popup)); return app; } @@ -57,34 +67,31 @@ timed_remote_app_alloc(void) void timed_remote_app_free(TimedRemoteApp *app) { - if (app->timer) - { + if (app == NULL) + return; + + if (app->timer != NULL) { furi_timer_stop(app->timer); furi_timer_free(app->timer); } - if (app->ir) - { + if (app->ir != NULL) infrared_signal_free(app->ir); - } - view_dispatcher_remove_view(app->vd, ViewMenu); + view_dispatcher_remove_view(app->vd, VIEW_MENU); submenu_free(app->submenu); - view_dispatcher_remove_view(app->vd, - ViewList); + view_dispatcher_remove_view(app->vd, VIEW_LIST); variable_item_list_free(app->vlist); - view_dispatcher_remove_view(app->vd, ViewRun); + view_dispatcher_remove_view(app->vd, VIEW_RUN); widget_free(app->widget); - view_dispatcher_remove_view(app->vd, ViewPop); + view_dispatcher_remove_view(app->vd, VIEW_POP); popup_free(app->popup); scene_manager_free(app->sm); - view_dispatcher_free(app->vd); - furi_record_close(RECORD_GUI); free(app); @@ -93,14 +100,16 @@ timed_remote_app_free(TimedRemoteApp *app) int32_t timed_remote_app(void *p) { - UNUSED(p); + TimedRemoteApp *app; - TimedRemoteApp *app = timed_remote_app_alloc(); + UNUSED(p); - scene_manager_next_scene(app->sm, ScBrowse); + app = timed_remote_app_alloc(); + if (app == NULL) + return -1; + scene_manager_next_scene(app->sm, SCENE_BROWSE); view_dispatcher_run(app->vd); - timed_remote_app_free(app); return 0; diff --git a/timed_remote.h b/timed_remote.h @@ -12,16 +12,16 @@ typedef enum { - ModeDown, - ModeSched, + MODE_COUNTDOWN, + MODE_SCHEDULED, } ModeId; typedef enum { - ViewMenu, - ViewList, - ViewRun, - ViewPop, + VIEW_MENU, + VIEW_LIST, + VIEW_RUN, + VIEW_POP, } ViewId; #define SIGNAL_NAME_MAX_LEN 32 @@ -55,4 +55,4 @@ typedef struct } TimedRemoteApp; TimedRemoteApp *timed_remote_app_alloc(void); -void timed_remote_app_free(TimedRemoteApp *app); +void timed_remote_app_free(TimedRemoteApp *);