[FL-3618] Infrared remote button index support (#3180)
* 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 * Deliver event data via a callback * Bundle event data together with event type * Change DataExchange behaviour * Adapt RPC debug app to new API * Remove rogue output * Add Doxygen comments to rpc_app.h * Simplify rpc_app.c code * Remove superflous parameter * Do not allocate protobuf messages on the stack * Fix GetError response * Support for button indices * Comment out shallow submodules * Fix F18 api * Fix logical error and add more debug output * fbt: testing unshallow for protobuf * github: lint&checks: unshallow prior to checks * Fix a TODO * github: do not unshallow the unshallowed * fbt: assets: only attempt to unshallow if cannot describe * Do not use the name when loading a signal by index (duh) * Simplify loading infrared signals by name * Sync with protobuf release * Infrared: use compact furi_crash macros Co-authored-by: hedger <hedger@nanode.su> Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
parent
16ffa2bf75
commit
49dcf81743
@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
- name: 'Check protobuf branch'
|
||||
run: |
|
||||
git submodule update --init
|
||||
git submodule update --init;
|
||||
SUB_PATH="assets/protobuf";
|
||||
SUB_BRANCH="dev";
|
||||
SUB_COMMITS_MIN=40;
|
||||
|
||||
@ -21,22 +21,51 @@ static void rpc_debug_app_tick_event_callback(void* context) {
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void rpc_debug_app_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
static void
|
||||
rpc_debug_app_format_hex(const uint8_t* data, size_t data_size, char* buf, size_t buf_size) {
|
||||
if(data == NULL || data_size == 0) {
|
||||
strncpy(buf, "<Data empty>", buf_size);
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t byte_width = 3;
|
||||
const size_t line_width = 7;
|
||||
|
||||
data_size = MIN(data_size, buf_size / (byte_width + 1));
|
||||
|
||||
for(size_t i = 0; i < data_size; ++i) {
|
||||
char* p = buf + (i * byte_width);
|
||||
char sep = !((i + 1) % line_width) ? '\n' : ' ';
|
||||
snprintf(p, byte_width + 1, "%02X%c", data[i], sep);
|
||||
}
|
||||
|
||||
buf[buf_size - 1] = '\0';
|
||||
}
|
||||
|
||||
static void rpc_debug_app_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
RpcDebugApp* app = context;
|
||||
furi_assert(app->rpc);
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
rpc_system_app_set_callback(app->rpc, NULL, NULL);
|
||||
app->rpc = NULL;
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true);
|
||||
rpc_system_app_confirm(app->rpc, true);
|
||||
} else if(event->type == RpcAppEventTypeDataExchange) {
|
||||
furi_assert(event->data.type == RpcAppSystemEventDataTypeBytes);
|
||||
|
||||
rpc_debug_app_format_hex(
|
||||
event->data.bytes.ptr, event->data.bytes.size, app->text_store, TEXT_STORE_SIZE);
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);
|
||||
} else {
|
||||
rpc_system_app_confirm(app->rpc, event, false);
|
||||
rpc_system_app_confirm(app->rpc, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,40 +1,5 @@
|
||||
#include "../rpc_debug_app.h"
|
||||
|
||||
static void rpc_debug_app_scene_start_format_hex(
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
char* buf,
|
||||
size_t buf_size) {
|
||||
furi_assert(data);
|
||||
furi_assert(buf);
|
||||
|
||||
const size_t byte_width = 3;
|
||||
const size_t line_width = 7;
|
||||
|
||||
data_size = MIN(data_size, buf_size / (byte_width + 1));
|
||||
|
||||
for(size_t i = 0; i < data_size; ++i) {
|
||||
char* p = buf + (i * byte_width);
|
||||
char sep = !((i + 1) % line_width) ? '\n' : ' ';
|
||||
snprintf(p, byte_width + 1, "%02X%c", data[i], sep);
|
||||
}
|
||||
|
||||
buf[buf_size - 1] = '\0';
|
||||
}
|
||||
|
||||
static void rpc_debug_app_scene_receive_data_exchange_callback(
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
void* context) {
|
||||
RpcDebugApp* app = context;
|
||||
if(data) {
|
||||
rpc_debug_app_scene_start_format_hex(data, data_size, app->text_store, TEXT_STORE_SIZE);
|
||||
} else {
|
||||
strncpy(app->text_store, "<Data empty>", TEXT_STORE_SIZE);
|
||||
}
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);
|
||||
}
|
||||
|
||||
void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {
|
||||
RpcDebugApp* app = context;
|
||||
strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE);
|
||||
@ -42,8 +7,6 @@ void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {
|
||||
text_box_set_text(app->text_box, app->text_store);
|
||||
text_box_set_font(app->text_box, TextBoxFontHex);
|
||||
|
||||
rpc_system_app_set_data_exchange_callback(
|
||||
app->rpc, rpc_debug_app_scene_receive_data_exchange_callback, app);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox);
|
||||
}
|
||||
|
||||
@ -53,6 +16,7 @@ bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneMana
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == RpcDebugAppCustomEventRpcDataExchange) {
|
||||
rpc_system_app_confirm(app->rpc, true);
|
||||
notification_message(app->notifications, &sequence_blink_cyan_100);
|
||||
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||
text_box_set_text(app->text_box, app->text_store);
|
||||
@ -66,5 +30,4 @@ bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneMana
|
||||
void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) {
|
||||
RpcDebugApp* app = context;
|
||||
text_box_reset(app->text_box);
|
||||
rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL);
|
||||
}
|
||||
|
||||
@ -39,21 +39,23 @@ static void ibutton_make_app_folder(iButton* ibutton) {
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
static void ibutton_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
iButton* ibutton = context;
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
view_dispatcher_send_custom_event(
|
||||
ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose);
|
||||
rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);
|
||||
ibutton->rpc = NULL;
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);
|
||||
} else if(event == RpcAppEventLoadFile) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
|
||||
} else if(event->type == RpcAppEventTypeLoadFile) {
|
||||
furi_assert(event->data.type == RpcAppSystemEventDataTypeString);
|
||||
furi_string_set(ibutton->file_path, event->data.string);
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoadFile);
|
||||
} else {
|
||||
rpc_system_app_confirm(ibutton->rpc, event, false);
|
||||
rpc_system_app_confirm(ibutton->rpc, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ typedef enum {
|
||||
iButtonCustomEventWorkerWriteNoDetect,
|
||||
iButtonCustomEventWorkerWriteCannotWrite,
|
||||
|
||||
iButtonCustomEventRpcLoad,
|
||||
iButtonCustomEventRpcLoadFile,
|
||||
iButtonCustomEventRpcExit,
|
||||
iButtonCustomEventRpcSessionClose,
|
||||
} iButtonCustomEvent;
|
||||
|
||||
@ -23,28 +23,23 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
|
||||
if(event.event == iButtonCustomEventRpcLoad) {
|
||||
if(event.event == iButtonCustomEventRpcLoadFile) {
|
||||
bool result = false;
|
||||
const char* file_path = rpc_system_app_get_data(ibutton->rpc);
|
||||
|
||||
if(file_path && (furi_string_empty(ibutton->file_path))) {
|
||||
furi_string_set(ibutton->file_path, file_path);
|
||||
if(ibutton_load_key(ibutton)) {
|
||||
popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop);
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
|
||||
|
||||
if(ibutton_load_key(ibutton)) {
|
||||
popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop);
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
|
||||
ibutton_worker_emulate_start(ibutton->worker, ibutton->key);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
|
||||
ibutton_worker_emulate_start(ibutton->worker, ibutton->key);
|
||||
|
||||
result = true;
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
|
||||
rpc_system_app_confirm(ibutton->rpc, RpcAppEventLoadFile, result);
|
||||
rpc_system_app_confirm(ibutton->rpc, result);
|
||||
|
||||
} else if(event.event == iButtonCustomEventRpcExit) {
|
||||
rpc_system_app_confirm(ibutton->rpc, RpcAppEventAppExit, true);
|
||||
rpc_system_app_confirm(ibutton->rpc, true);
|
||||
scene_manager_stop(ibutton->scene_manager);
|
||||
view_dispatcher_stop(ibutton->view_dispatcher);
|
||||
|
||||
|
||||
@ -44,30 +44,42 @@ static void infrared_tick_event_callback(void* context) {
|
||||
scene_manager_handle_tick_event(infrared->scene_manager);
|
||||
}
|
||||
|
||||
static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
static void infrared_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
InfraredApp* infrared = context;
|
||||
furi_assert(infrared->rpc_ctx);
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose);
|
||||
rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);
|
||||
infrared->rpc_ctx = NULL;
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcExit);
|
||||
} else if(event == RpcAppEventLoadFile) {
|
||||
} else if(event->type == RpcAppEventTypeLoadFile) {
|
||||
furi_assert(event->data.type == RpcAppSystemEventDataTypeString);
|
||||
furi_string_set(infrared->file_path, event->data.string);
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad);
|
||||
} else if(event == RpcAppEventButtonPress) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress);
|
||||
} else if(event == RpcAppEventButtonRelease) {
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcLoadFile);
|
||||
} else if(event->type == RpcAppEventTypeButtonPress) {
|
||||
furi_assert(
|
||||
event->data.type == RpcAppSystemEventDataTypeString ||
|
||||
event->data.type == RpcAppSystemEventDataTypeInt32);
|
||||
if(event->data.type == RpcAppSystemEventDataTypeString) {
|
||||
furi_string_set(infrared->button_name, event->data.string);
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressName);
|
||||
} else {
|
||||
infrared->app_state.current_button_index = event->data.i32;
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressIndex);
|
||||
}
|
||||
} else if(event->type == RpcAppEventTypeButtonRelease) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease);
|
||||
} else {
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, event, false);
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,6 +129,7 @@ static InfraredApp* infrared_alloc() {
|
||||
InfraredApp* infrared = malloc(sizeof(InfraredApp));
|
||||
|
||||
infrared->file_path = furi_string_alloc();
|
||||
infrared->button_name = furi_string_alloc();
|
||||
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
app_state->is_learning_new_remote = false;
|
||||
@ -247,6 +260,7 @@ static void infrared_free(InfraredApp* infrared) {
|
||||
infrared->gui = NULL;
|
||||
|
||||
furi_string_free(infrared->file_path);
|
||||
furi_string_free(infrared->button_name);
|
||||
|
||||
free(infrared);
|
||||
}
|
||||
|
||||
@ -121,6 +121,7 @@ struct InfraredApp {
|
||||
InfraredProgressView* progress; /**< Custom view for showing brute force progress. */
|
||||
|
||||
FuriString* file_path; /**< Full path to the currently loaded file. */
|
||||
FuriString* button_name; /** Name of the button requested in RPC mode. */
|
||||
/** Arbitrary text storage for various inputs. */
|
||||
char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1];
|
||||
InfraredAppState app_state; /**< Application state. */
|
||||
|
||||
@ -128,7 +128,7 @@ 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(
|
||||
const bool success = infrared_signal_search_by_name_and_read(
|
||||
brute_force->current_signal,
|
||||
brute_force->ff,
|
||||
furi_string_get_cstr(brute_force->current_record_name));
|
||||
|
||||
@ -15,9 +15,10 @@ enum InfraredCustomEventType {
|
||||
InfraredCustomEventTypeButtonSelected,
|
||||
InfraredCustomEventTypeBackPressed,
|
||||
|
||||
InfraredCustomEventTypeRpcLoad,
|
||||
InfraredCustomEventTypeRpcLoadFile,
|
||||
InfraredCustomEventTypeRpcExit,
|
||||
InfraredCustomEventTypeRpcButtonPress,
|
||||
InfraredCustomEventTypeRpcButtonPressName,
|
||||
InfraredCustomEventTypeRpcButtonPressIndex,
|
||||
InfraredCustomEventTypeRpcButtonRelease,
|
||||
InfraredCustomEventTypeRpcSessionClose,
|
||||
};
|
||||
|
||||
@ -95,10 +95,9 @@ bool infrared_remote_load_signal(
|
||||
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);
|
||||
if(!infrared_signal_search_by_index_and_read(signal, ff, index)) {
|
||||
const char* signal_name = infrared_remote_get_signal_name(remote, index);
|
||||
FURI_LOG_E(TAG, "Failed to load signal '%s' from file '%s'", signal_name, path);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -266,19 +266,37 @@ 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 infrared_signal_search_by_name_and_read(
|
||||
InfraredSignal* signal,
|
||||
FlipperFormat* ff,
|
||||
const char* name) {
|
||||
bool success = false;
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
do {
|
||||
bool is_name_found = false;
|
||||
while(!is_name_found && infrared_signal_read_name(ff, tmp)) { //-V560
|
||||
is_name_found = furi_string_equal(tmp, name);
|
||||
while(infrared_signal_read_name(ff, tmp)) {
|
||||
if(furi_string_equal(tmp, name)) {
|
||||
success = infrared_signal_read_body(signal, ff);
|
||||
break;
|
||||
}
|
||||
if(!is_name_found) break; //-V547
|
||||
if(!infrared_signal_read_body(signal, ff)) break; //-V779
|
||||
success = true;
|
||||
} while(false);
|
||||
}
|
||||
|
||||
furi_string_free(tmp);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_signal_search_by_index_and_read(
|
||||
InfraredSignal* signal,
|
||||
FlipperFormat* ff,
|
||||
size_t index) {
|
||||
bool success = false;
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
for(uint32_t i = 0; infrared_signal_read_name(ff, tmp); ++i) {
|
||||
if(i == index) {
|
||||
success = infrared_signal_read_body(signal, ff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_free(tmp);
|
||||
return success;
|
||||
|
||||
@ -162,7 +162,26 @@ bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name);
|
||||
* @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);
|
||||
bool infrared_signal_search_by_name_and_read(
|
||||
InfraredSignal* signal,
|
||||
FlipperFormat* ff,
|
||||
const char* name);
|
||||
|
||||
/**
|
||||
* @brief Read a signal with a particular index from a FlipperFormat file into an InfraredSignal instance.
|
||||
*
|
||||
* This function will look for a signal with the given index 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] index the requested signal index.
|
||||
* @returns true if a signal was found and successfully read, false otherwise (e.g. the signal was not found).
|
||||
*/
|
||||
bool infrared_signal_search_by_index_and_read(
|
||||
InfraredSignal* signal,
|
||||
FlipperFormat* ff,
|
||||
size_t index);
|
||||
|
||||
/**
|
||||
* @brief Save a signal contained in an InfraredSignal instance to a FlipperFormat file.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include "../infrared_app_i.h"
|
||||
|
||||
typedef enum {
|
||||
ButtonIndexPlus = -2,
|
||||
ButtonIndexLearn = -2,
|
||||
ButtonIndexEdit = -1,
|
||||
ButtonIndexNA = 0,
|
||||
} ButtonIndex;
|
||||
@ -44,7 +44,7 @@ void infrared_scene_remote_on_enter(void* context) {
|
||||
button_menu_add_item(
|
||||
button_menu,
|
||||
"+",
|
||||
ButtonIndexPlus,
|
||||
ButtonIndexLearn,
|
||||
infrared_scene_remote_button_menu_callback,
|
||||
ButtonMenuItemTypeControl,
|
||||
context);
|
||||
@ -95,7 +95,7 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
|
||||
if(is_transmitter_idle) {
|
||||
scene_manager_set_scene_state(
|
||||
scene_manager, InfraredSceneRemote, (unsigned)button_index);
|
||||
if(button_index == ButtonIndexPlus) {
|
||||
if(button_index == ButtonIndexLearn) {
|
||||
infrared->app_state.is_learning_new_remote = false;
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
|
||||
consumed = true;
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include <gui/canvas.h>
|
||||
|
||||
#define TAG "InfraredApp"
|
||||
|
||||
typedef enum {
|
||||
InfraredRpcStateIdle,
|
||||
InfraredRpcStateLoaded,
|
||||
@ -38,11 +40,9 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypePopupClosed) {
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcLoad) {
|
||||
} else if(event.event == InfraredCustomEventTypeRpcLoadFile) {
|
||||
bool result = false;
|
||||
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
|
||||
if(arg && (state == InfraredRpcStateIdle)) {
|
||||
furi_string_set(infrared->file_path, arg);
|
||||
if(state == InfraredRpcStateIdle) {
|
||||
result = infrared_remote_load(
|
||||
infrared->remote, furi_string_get_cstr(infrared->file_path));
|
||||
if(result) {
|
||||
@ -56,20 +56,35 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
popup_set_text(
|
||||
infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop);
|
||||
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcButtonPress) {
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, result);
|
||||
} else if(
|
||||
event.event == InfraredCustomEventTypeRpcButtonPressName ||
|
||||
event.event == InfraredCustomEventTypeRpcButtonPressIndex) {
|
||||
bool result = false;
|
||||
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
|
||||
if(arg && (state == InfraredRpcStateLoaded)) {
|
||||
size_t button_index = 0;
|
||||
if(infrared_remote_get_signal_index(infrared->remote, arg, &button_index)) {
|
||||
infrared_tx_start_button_index(infrared, button_index);
|
||||
result = true;
|
||||
if(state == InfraredRpcStateLoaded) {
|
||||
if(event.event == InfraredCustomEventTypeRpcButtonPressName) {
|
||||
const char* button_name = furi_string_get_cstr(infrared->button_name);
|
||||
size_t index;
|
||||
const bool index_found =
|
||||
infrared_remote_get_signal_index(infrared->remote, button_name, &index);
|
||||
infrared->app_state.current_button_index =
|
||||
index_found ? (signed)index : InfraredButtonIndexNone;
|
||||
FURI_LOG_D(TAG, "Sending signal with name \"%s\"", button_name);
|
||||
} else {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Sending signal with index \"%ld\"",
|
||||
infrared->app_state.current_button_index);
|
||||
}
|
||||
if(infrared->app_state.current_button_index != InfraredButtonIndexNone) {
|
||||
infrared_tx_start_button_index(
|
||||
infrared, infrared->app_state.current_button_index);
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result);
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, result);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcButtonRelease) {
|
||||
bool result = false;
|
||||
if(state == InfraredRpcStateSending) {
|
||||
@ -78,11 +93,11 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
|
||||
}
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result);
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, result);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcExit) {
|
||||
scene_manager_stop(infrared->scene_manager);
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventAppExit, true);
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, true);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcSessionClose) {
|
||||
scene_manager_stop(infrared->scene_manager);
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
|
||||
@ -13,21 +13,23 @@ static bool lfrfid_debug_back_event_callback(void* context) {
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
|
||||
static void rpc_command_callback(const RpcAppSystemEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
LfRfid* app = (LfRfid*)context;
|
||||
|
||||
if(rpc_event == RpcAppEventSessionClose) {
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcSessionClose);
|
||||
// Detach RPC
|
||||
rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL);
|
||||
app->rpc_ctx = NULL;
|
||||
} else if(rpc_event == RpcAppEventAppExit) {
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventExit);
|
||||
} else if(rpc_event == RpcAppEventLoadFile) {
|
||||
} else if(event->type == RpcAppEventTypeLoadFile) {
|
||||
furi_assert(event->data.type == RpcAppSystemEventDataTypeString);
|
||||
furi_string_set(app->file_path, event->data.string);
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcLoadFile);
|
||||
} else {
|
||||
rpc_system_app_confirm(app->rpc_ctx, rpc_event, false);
|
||||
rpc_system_app_confirm(app->rpc_ctx, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -24,17 +24,15 @@ bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == LfRfidEventExit) {
|
||||
rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true);
|
||||
rpc_system_app_confirm(app->rpc_ctx, true);
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
} else if(event.event == LfRfidEventRpcSessionClose) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
} else if(event.event == LfRfidEventRpcLoadFile) {
|
||||
const char* arg = rpc_system_app_get_data(app->rpc_ctx);
|
||||
bool result = false;
|
||||
if(arg && (app->rpc_state == LfRfidRpcStateIdle)) {
|
||||
furi_string_set(app->file_path, arg);
|
||||
if(app->rpc_state == LfRfidRpcStateIdle) {
|
||||
if(lfrfid_load_key_data(app, app->file_path, false)) {
|
||||
lfrfid_worker_start_thread(app->lfworker);
|
||||
lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id);
|
||||
@ -48,7 +46,7 @@ bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
rpc_system_app_confirm(app->rpc_ctx, result);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
|
||||
@ -21,7 +21,7 @@ typedef enum {
|
||||
NfcCustomEventTextInputDone,
|
||||
NfcCustomEventDictAttackDone,
|
||||
|
||||
NfcCustomEventRpcLoad,
|
||||
NfcCustomEventRpcLoadFile,
|
||||
NfcCustomEventRpcExit,
|
||||
NfcCustomEventRpcSessionClose,
|
||||
|
||||
|
||||
@ -694,15 +694,17 @@ static bool nfc_protocol_support_scene_rpc_on_event(NfcApp* instance, SceneManag
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventRpcLoad && instance->rpc_state == NfcRpcStateIdle) {
|
||||
furi_string_set(instance->file_path, rpc_system_app_get_data(instance->rpc_ctx));
|
||||
const bool load_success = nfc_load_file(instance, instance->file_path, false);
|
||||
if(load_success) {
|
||||
nfc_protocol_support_scene_rpc_setup_ui_and_emulate(instance);
|
||||
if(event.event == NfcCustomEventRpcLoadFile) {
|
||||
bool success = false;
|
||||
if(instance->rpc_state == NfcRpcStateIdle) {
|
||||
if(nfc_load_file(instance, instance->file_path, false)) {
|
||||
nfc_protocol_support_scene_rpc_setup_ui_and_emulate(instance);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(instance->rpc_ctx, RpcAppEventLoadFile, load_success);
|
||||
rpc_system_app_confirm(instance->rpc_ctx, success);
|
||||
} else if(event.event == NfcCustomEventRpcExit) {
|
||||
rpc_system_app_confirm(instance->rpc_ctx, RpcAppEventAppExit, true);
|
||||
rpc_system_app_confirm(instance->rpc_ctx, true);
|
||||
scene_manager_stop(instance->scene_manager);
|
||||
view_dispatcher_stop(instance->view_dispatcher);
|
||||
} else if(event.event == NfcCustomEventRpcSessionClose) {
|
||||
|
||||
@ -14,22 +14,24 @@ bool nfc_back_event_callback(void* context) {
|
||||
return scene_manager_handle_back_event(nfc->scene_manager);
|
||||
}
|
||||
|
||||
static void nfc_app_rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
|
||||
static void nfc_app_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
NfcApp* nfc = (NfcApp*)context;
|
||||
|
||||
furi_assert(nfc->rpc_ctx);
|
||||
|
||||
if(rpc_event == RpcAppEventSessionClose) {
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose);
|
||||
rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL);
|
||||
nfc->rpc_ctx = NULL;
|
||||
} else if(rpc_event == RpcAppEventAppExit) {
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcExit);
|
||||
} else if(rpc_event == RpcAppEventLoadFile) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad);
|
||||
} else if(event->type == RpcAppEventTypeLoadFile) {
|
||||
furi_assert(event->data.type == RpcAppSystemEventDataTypeString);
|
||||
furi_string_set(nfc->file_path, event->data.string);
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoadFile);
|
||||
} else {
|
||||
rpc_system_app_confirm(nfc->rpc_ctx, rpc_event, false);
|
||||
rpc_system_app_confirm(nfc->rpc_ctx, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -33,13 +33,13 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.event == SubGhzCustomEventSceneExit) {
|
||||
scene_manager_stop(subghz->scene_manager);
|
||||
view_dispatcher_stop(subghz->view_dispatcher);
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventAppExit, true);
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, true);
|
||||
} else if(event.event == SubGhzCustomEventSceneRpcSessionClose) {
|
||||
scene_manager_stop(subghz->scene_manager);
|
||||
view_dispatcher_stop(subghz->view_dispatcher);
|
||||
} else if(event.event == SubGhzCustomEventSceneRpcButtonPress) {
|
||||
bool result = false;
|
||||
if((state == SubGhzRpcStateLoaded)) {
|
||||
if(state == SubGhzRpcStateLoaded) {
|
||||
switch(
|
||||
subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx))) {
|
||||
case SubGhzTxRxStartTxStateErrorOnlyRx:
|
||||
@ -61,7 +61,7 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result);
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, result);
|
||||
} else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) {
|
||||
bool result = false;
|
||||
if(state == SubGhzRpcStateTx) {
|
||||
@ -70,15 +70,13 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
result = true;
|
||||
}
|
||||
state = SubGhzRpcStateIdle;
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result);
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, result);
|
||||
} else if(event.event == SubGhzCustomEventSceneRpcLoad) {
|
||||
bool result = false;
|
||||
const char* arg = rpc_system_app_get_data(subghz->rpc_ctx);
|
||||
if(arg && (state == SubGhzRpcStateIdle)) {
|
||||
if(subghz_key_load(subghz, arg, false)) {
|
||||
if(state == SubGhzRpcStateIdle) {
|
||||
if(subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), false)) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateLoaded);
|
||||
furi_string_set(subghz->file_path, arg);
|
||||
result = true;
|
||||
FuriString* file_name;
|
||||
file_name = furi_string_alloc();
|
||||
@ -97,7 +95,7 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
rpc_system_app_set_error_text(subghz->rpc_ctx, "Cannot parse file");
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, result);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
|
||||
@ -20,29 +20,31 @@ void subghz_tick_event_callback(void* context) {
|
||||
scene_manager_handle_tick_event(subghz->scene_manager);
|
||||
}
|
||||
|
||||
static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
static void subghz_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
|
||||
furi_assert(subghz->rpc_ctx);
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose);
|
||||
rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL);
|
||||
subghz->rpc_ctx = NULL;
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit);
|
||||
} else if(event == RpcAppEventLoadFile) {
|
||||
} else if(event->type == RpcAppEventTypeLoadFile) {
|
||||
furi_assert(event->data.type == RpcAppSystemEventDataTypeString);
|
||||
furi_string_set(subghz->file_path, event->data.string);
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad);
|
||||
} else if(event == RpcAppEventButtonPress) {
|
||||
} else if(event->type == RpcAppEventTypeButtonPress) {
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPress);
|
||||
} else if(event == RpcAppEventButtonRelease) {
|
||||
} else if(event->type == RpcAppEventTypeButtonRelease) {
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease);
|
||||
} else {
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, event, false);
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -477,12 +477,15 @@ void rpc_send_and_release(RpcSession* session, PB_Main* message) {
|
||||
}
|
||||
|
||||
void rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_CommandStatus status) {
|
||||
furi_assert(session);
|
||||
|
||||
PB_Main message = {
|
||||
.command_id = command_id,
|
||||
.command_status = status,
|
||||
.has_next = false,
|
||||
.which_content = PB_Main_empty_tag,
|
||||
};
|
||||
|
||||
rpc_send_and_release(session, &message);
|
||||
pb_release(&PB_Main_msg, &message);
|
||||
}
|
||||
|
||||
@ -10,49 +10,85 @@
|
||||
struct RpcAppSystem {
|
||||
RpcSession* session;
|
||||
|
||||
RpcAppSystemCallback app_callback;
|
||||
void* app_context;
|
||||
RpcAppSystemCallback callback;
|
||||
void* callback_context;
|
||||
|
||||
RpcAppSystemDataExchangeCallback data_exchange_callback;
|
||||
void* data_exchange_context;
|
||||
uint32_t error_code;
|
||||
char* error_text;
|
||||
|
||||
PB_Main* state_msg;
|
||||
PB_Main* error_msg;
|
||||
|
||||
uint32_t last_id;
|
||||
char* last_data;
|
||||
uint32_t last_command_id;
|
||||
RpcAppSystemEventType last_event_type;
|
||||
};
|
||||
|
||||
#define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16
|
||||
|
||||
static void rpc_system_app_send_state_response(
|
||||
RpcAppSystem* rpc_app,
|
||||
PB_App_AppState state,
|
||||
const char* name) {
|
||||
PB_Main* response = malloc(sizeof(PB_Main));
|
||||
|
||||
response->which_content = PB_Main_app_state_response_tag;
|
||||
response->content.app_state_response.state = state;
|
||||
|
||||
FURI_LOG_D(TAG, "%s", name);
|
||||
rpc_send(rpc_app->session, response);
|
||||
|
||||
free(response);
|
||||
}
|
||||
|
||||
static void rpc_system_app_send_error_response(
|
||||
RpcAppSystem* rpc_app,
|
||||
uint32_t command_id,
|
||||
PB_CommandStatus status,
|
||||
const char* name) {
|
||||
// Not describing all possible errors as only APP_NOT_RUNNING is used
|
||||
const char* status_str = status == PB_CommandStatus_ERROR_APP_NOT_RUNNING ? "APP_NOT_RUNNING" :
|
||||
"UNKNOWN";
|
||||
FURI_LOG_E(TAG, "%s: %s, id %lu, status: %d", name, status_str, command_id, status);
|
||||
rpc_send_and_release_empty(rpc_app->session, command_id, status);
|
||||
}
|
||||
|
||||
static void rpc_system_app_set_last_command(
|
||||
RpcAppSystem* rpc_app,
|
||||
uint32_t command_id,
|
||||
const RpcAppSystemEvent* event) {
|
||||
furi_assert(rpc_app->last_command_id == 0);
|
||||
furi_assert(rpc_app->last_event_type == RpcAppEventTypeInvalid);
|
||||
|
||||
rpc_app->last_command_id = command_id;
|
||||
rpc_app->last_event_type = event->type;
|
||||
}
|
||||
|
||||
static void rpc_system_app_start_process(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
|
||||
furi_assert(request->which_content == PB_Main_app_start_request_tag);
|
||||
RpcAppSystem* rpc_app = context;
|
||||
RpcSession* session = rpc_app->session;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
furi_assert(session);
|
||||
char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE];
|
||||
|
||||
furi_assert(!rpc_app->last_id);
|
||||
furi_assert(!rpc_app->last_data);
|
||||
RpcAppSystem* rpc_app = context;
|
||||
furi_assert(rpc_app);
|
||||
furi_assert(rpc_app->last_command_id == 0);
|
||||
furi_assert(rpc_app->last_event_type == RpcAppEventTypeInvalid);
|
||||
|
||||
FURI_LOG_D(TAG, "StartProcess: id %lu", request->command_id);
|
||||
|
||||
PB_CommandStatus result;
|
||||
|
||||
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||
const char* app_name = request->content.app_start_request.name;
|
||||
|
||||
PB_CommandStatus result;
|
||||
|
||||
if(app_name) {
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
|
||||
char app_args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE];
|
||||
const char* app_args = request->content.app_start_request.args;
|
||||
|
||||
if(app_args && strcmp(app_args, "RPC") == 0) {
|
||||
// If app is being started in RPC mode - pass RPC context via args string
|
||||
snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app);
|
||||
app_args = args_temp;
|
||||
snprintf(app_args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app);
|
||||
app_args = app_args_temp;
|
||||
}
|
||||
LoaderStatus status = loader_start(loader, app_name, app_args, NULL);
|
||||
|
||||
const LoaderStatus status = loader_start(loader, app_name, app_args, NULL);
|
||||
if(status == LoaderStatusErrorAppStarted) {
|
||||
result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED;
|
||||
} else if(status == LoaderStatusErrorInternal) {
|
||||
@ -71,266 +107,271 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context)
|
||||
furi_record_close(RECORD_LOADER);
|
||||
|
||||
FURI_LOG_D(TAG, "StartProcess: response id %lu, result %d", request->command_id, result);
|
||||
rpc_send_and_release_empty(session, request->command_id, result);
|
||||
rpc_send_and_release_empty(rpc_app->session, request->command_id, result);
|
||||
}
|
||||
|
||||
static void rpc_system_app_lock_status_process(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
|
||||
furi_assert(request->which_content == PB_Main_app_lock_status_request_tag);
|
||||
|
||||
RpcAppSystem* rpc_app = context;
|
||||
furi_assert(rpc_app);
|
||||
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
FURI_LOG_D(TAG, "LockStatus");
|
||||
|
||||
PB_Main* response = malloc(sizeof(PB_Main));
|
||||
|
||||
response->command_id = request->command_id;
|
||||
response->which_content = PB_Main_app_lock_status_response_tag;
|
||||
|
||||
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||
|
||||
PB_Main response = {
|
||||
.has_next = false,
|
||||
.command_status = PB_CommandStatus_OK,
|
||||
.command_id = request->command_id,
|
||||
.which_content = PB_Main_app_lock_status_response_tag,
|
||||
};
|
||||
|
||||
response.content.app_lock_status_response.locked = loader_is_locked(loader);
|
||||
|
||||
response->content.app_lock_status_response.locked = loader_is_locked(loader);
|
||||
furi_record_close(RECORD_LOADER);
|
||||
|
||||
FURI_LOG_D(TAG, "LockStatus: response");
|
||||
rpc_send_and_release(session, &response);
|
||||
pb_release(&PB_Main_msg, &response);
|
||||
rpc_send_and_release(rpc_app->session, response);
|
||||
|
||||
free(response);
|
||||
}
|
||||
|
||||
static void rpc_system_app_exit_request(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
|
||||
furi_assert(request->which_content == PB_Main_app_exit_request_tag);
|
||||
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
furi_assert(rpc_app);
|
||||
|
||||
PB_CommandStatus status;
|
||||
|
||||
if(rpc_app->app_callback) {
|
||||
if(rpc_app->callback) {
|
||||
FURI_LOG_D(TAG, "ExitRequest: id %lu", request->command_id);
|
||||
furi_assert(!rpc_app->last_id);
|
||||
furi_assert(!rpc_app->last_data);
|
||||
rpc_app->last_id = request->command_id;
|
||||
rpc_app->app_callback(RpcAppEventAppExit, rpc_app->app_context);
|
||||
|
||||
const RpcAppSystemEvent event = {
|
||||
.type = RpcAppEventTypeAppExit,
|
||||
.data =
|
||||
{
|
||||
.type = RpcAppSystemEventDataTypeNone,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
|
||||
|
||||
rpc_app->callback(&event, rpc_app->callback_context);
|
||||
|
||||
} else {
|
||||
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
|
||||
FURI_LOG_E(
|
||||
TAG, "ExitRequest: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
|
||||
rpc_send_and_release_empty(session, request->command_id, status);
|
||||
rpc_system_app_send_error_response(
|
||||
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ExitRequest");
|
||||
}
|
||||
}
|
||||
|
||||
static void rpc_system_app_load_file(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
|
||||
furi_assert(request->which_content == PB_Main_app_load_file_request_tag);
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
PB_CommandStatus status;
|
||||
if(rpc_app->app_callback) {
|
||||
RpcAppSystem* rpc_app = context;
|
||||
furi_assert(rpc_app);
|
||||
|
||||
if(rpc_app->callback) {
|
||||
FURI_LOG_D(TAG, "LoadFile: id %lu", request->command_id);
|
||||
furi_assert(!rpc_app->last_id);
|
||||
furi_assert(!rpc_app->last_data);
|
||||
rpc_app->last_id = request->command_id;
|
||||
rpc_app->last_data = strdup(request->content.app_load_file_request.path);
|
||||
rpc_app->app_callback(RpcAppEventLoadFile, rpc_app->app_context);
|
||||
|
||||
const RpcAppSystemEvent event = {
|
||||
.type = RpcAppEventTypeLoadFile,
|
||||
.data =
|
||||
{
|
||||
.type = RpcAppSystemEventDataTypeString,
|
||||
.string = request->content.app_load_file_request.path,
|
||||
},
|
||||
};
|
||||
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
|
||||
|
||||
rpc_app->callback(&event, rpc_app->callback_context);
|
||||
|
||||
} else {
|
||||
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
|
||||
FURI_LOG_E(
|
||||
TAG, "LoadFile: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
|
||||
rpc_send_and_release_empty(session, request->command_id, status);
|
||||
rpc_system_app_send_error_response(
|
||||
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "LoadFile");
|
||||
}
|
||||
}
|
||||
|
||||
static void rpc_system_app_button_press(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(context);
|
||||
|
||||
furi_assert(request->which_content == PB_Main_app_button_press_request_tag);
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
PB_CommandStatus status;
|
||||
if(rpc_app->app_callback) {
|
||||
RpcAppSystem* rpc_app = context;
|
||||
furi_assert(rpc_app);
|
||||
|
||||
if(rpc_app->callback) {
|
||||
FURI_LOG_D(TAG, "ButtonPress");
|
||||
furi_assert(!rpc_app->last_id);
|
||||
furi_assert(!rpc_app->last_data);
|
||||
rpc_app->last_id = request->command_id;
|
||||
rpc_app->last_data = strdup(request->content.app_button_press_request.args);
|
||||
rpc_app->app_callback(RpcAppEventButtonPress, rpc_app->app_context);
|
||||
|
||||
RpcAppSystemEvent event;
|
||||
event.type = RpcAppEventTypeButtonPress;
|
||||
|
||||
if(strlen(request->content.app_button_press_request.args) != 0) {
|
||||
event.data.type = RpcAppSystemEventDataTypeString;
|
||||
event.data.string = request->content.app_button_press_request.args;
|
||||
} else {
|
||||
event.data.type = RpcAppSystemEventDataTypeInt32;
|
||||
event.data.i32 = request->content.app_button_press_request.index;
|
||||
}
|
||||
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
|
||||
|
||||
rpc_app->callback(&event, rpc_app->callback_context);
|
||||
|
||||
} else {
|
||||
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
|
||||
FURI_LOG_E(
|
||||
TAG, "ButtonPress: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
|
||||
rpc_send_and_release_empty(session, request->command_id, status);
|
||||
rpc_system_app_send_error_response(
|
||||
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ButtonPress");
|
||||
}
|
||||
}
|
||||
|
||||
static void rpc_system_app_button_release(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_app_button_release_request_tag);
|
||||
furi_assert(context);
|
||||
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
furi_assert(rpc_app);
|
||||
|
||||
PB_CommandStatus status;
|
||||
if(rpc_app->app_callback) {
|
||||
if(rpc_app->callback) {
|
||||
FURI_LOG_D(TAG, "ButtonRelease");
|
||||
furi_assert(!rpc_app->last_id);
|
||||
furi_assert(!rpc_app->last_data);
|
||||
rpc_app->last_id = request->command_id;
|
||||
rpc_app->app_callback(RpcAppEventButtonRelease, rpc_app->app_context);
|
||||
|
||||
const RpcAppSystemEvent event = {
|
||||
.type = RpcAppEventTypeButtonRelease,
|
||||
.data =
|
||||
{
|
||||
.type = RpcAppSystemEventDataTypeNone,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
|
||||
|
||||
rpc_app->callback(&event, rpc_app->callback_context);
|
||||
|
||||
} else {
|
||||
status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
|
||||
FURI_LOG_E(
|
||||
TAG, "ButtonRelease: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
|
||||
rpc_send_and_release_empty(session, request->command_id, status);
|
||||
rpc_system_app_send_error_response(
|
||||
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ButtonRelease");
|
||||
}
|
||||
}
|
||||
|
||||
static void rpc_system_app_get_error_process(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_app_get_error_request_tag);
|
||||
furi_assert(context);
|
||||
|
||||
RpcAppSystem* rpc_app = context;
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
furi_assert(rpc_app);
|
||||
|
||||
rpc_app->error_msg->command_id = request->command_id;
|
||||
PB_Main* response = malloc(sizeof(PB_Main));
|
||||
|
||||
response->command_id = request->command_id;
|
||||
response->which_content = PB_Main_app_get_error_response_tag;
|
||||
response->content.app_get_error_response.code = rpc_app->error_code;
|
||||
response->content.app_get_error_response.text = rpc_app->error_text;
|
||||
|
||||
FURI_LOG_D(TAG, "GetError");
|
||||
rpc_send(session, rpc_app->error_msg);
|
||||
rpc_send(rpc_app->session, response);
|
||||
|
||||
free(response);
|
||||
}
|
||||
|
||||
static void rpc_system_app_data_exchange_process(const PB_Main* request, void* context) {
|
||||
furi_assert(request);
|
||||
furi_assert(request->which_content == PB_Main_app_data_exchange_request_tag);
|
||||
furi_assert(context);
|
||||
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
furi_assert(rpc_app);
|
||||
|
||||
PB_CommandStatus command_status;
|
||||
pb_bytes_array_t* data = request->content.app_data_exchange_request.data;
|
||||
if(rpc_app->callback) {
|
||||
FURI_LOG_D(TAG, "DataExchange");
|
||||
|
||||
if(rpc_app->data_exchange_callback) {
|
||||
uint8_t* data_bytes = NULL;
|
||||
size_t data_size = 0;
|
||||
if(data) {
|
||||
data_bytes = data->bytes;
|
||||
data_size = data->size;
|
||||
}
|
||||
rpc_app->data_exchange_callback(data_bytes, data_size, rpc_app->data_exchange_context);
|
||||
command_status = PB_CommandStatus_OK;
|
||||
const pb_bytes_array_t* data = request->content.app_data_exchange_request.data;
|
||||
|
||||
const RpcAppSystemEvent event = {
|
||||
.type = RpcAppEventTypeDataExchange,
|
||||
.data =
|
||||
{
|
||||
.type = RpcAppSystemEventDataTypeBytes,
|
||||
.bytes =
|
||||
{
|
||||
.ptr = data ? data->bytes : NULL,
|
||||
.size = data ? data->size : 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
|
||||
|
||||
rpc_app->callback(&event, rpc_app->callback_context);
|
||||
} else {
|
||||
command_status = PB_CommandStatus_ERROR_APP_CMD_ERROR;
|
||||
rpc_system_app_send_error_response(
|
||||
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "DataExchange");
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "DataExchange");
|
||||
rpc_send_and_release_empty(session, request->command_id, command_status);
|
||||
}
|
||||
|
||||
void rpc_system_app_send_started(RpcAppSystem* rpc_app) {
|
||||
furi_assert(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED;
|
||||
|
||||
FURI_LOG_D(TAG, "SendStarted");
|
||||
rpc_send(session, rpc_app->state_msg);
|
||||
rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_STARTED, "SendStarted");
|
||||
}
|
||||
|
||||
void rpc_system_app_send_exited(RpcAppSystem* rpc_app) {
|
||||
furi_assert(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED;
|
||||
|
||||
FURI_LOG_D(TAG, "SendExit");
|
||||
rpc_send(session, rpc_app->state_msg);
|
||||
rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_CLOSED, "SendExit");
|
||||
}
|
||||
|
||||
const char* rpc_system_app_get_data(RpcAppSystem* rpc_app) {
|
||||
void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result) {
|
||||
furi_assert(rpc_app);
|
||||
furi_assert(rpc_app->last_data);
|
||||
return rpc_app->last_data;
|
||||
}
|
||||
furi_assert(rpc_app->last_command_id != 0);
|
||||
/* Ensure that only commands of these types can be confirmed */
|
||||
furi_assert(
|
||||
rpc_app->last_event_type == RpcAppEventTypeAppExit ||
|
||||
rpc_app->last_event_type == RpcAppEventTypeLoadFile ||
|
||||
rpc_app->last_event_type == RpcAppEventTypeButtonPress ||
|
||||
rpc_app->last_event_type == RpcAppEventTypeButtonRelease ||
|
||||
rpc_app->last_event_type == RpcAppEventTypeDataExchange);
|
||||
|
||||
void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result) {
|
||||
furi_assert(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
furi_assert(rpc_app->last_id);
|
||||
const uint32_t last_command_id = rpc_app->last_command_id;
|
||||
const RpcAppSystemEventType last_event_type = rpc_app->last_event_type;
|
||||
|
||||
PB_CommandStatus status = result ? PB_CommandStatus_OK : PB_CommandStatus_ERROR_APP_CMD_ERROR;
|
||||
rpc_app->last_command_id = 0;
|
||||
rpc_app->last_event_type = RpcAppEventTypeInvalid;
|
||||
|
||||
uint32_t last_id = 0;
|
||||
switch(event) {
|
||||
case RpcAppEventAppExit:
|
||||
case RpcAppEventLoadFile:
|
||||
case RpcAppEventButtonPress:
|
||||
case RpcAppEventButtonRelease:
|
||||
last_id = rpc_app->last_id;
|
||||
rpc_app->last_id = 0;
|
||||
if(rpc_app->last_data) {
|
||||
free(rpc_app->last_data);
|
||||
rpc_app->last_data = NULL;
|
||||
}
|
||||
FURI_LOG_D(TAG, "AppConfirm: event %d last_id %lu status %d", event, last_id, status);
|
||||
rpc_send_and_release_empty(session, last_id, status);
|
||||
break;
|
||||
default:
|
||||
furi_crash("RPC App state programming Error");
|
||||
break;
|
||||
}
|
||||
const PB_CommandStatus status = result ? PB_CommandStatus_OK :
|
||||
PB_CommandStatus_ERROR_APP_CMD_ERROR;
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"AppConfirm: event %d last_id %lu status %d",
|
||||
last_event_type,
|
||||
last_command_id,
|
||||
status);
|
||||
|
||||
rpc_send_and_release_empty(rpc_app->session, last_command_id, status);
|
||||
}
|
||||
|
||||
void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) {
|
||||
furi_assert(rpc_app);
|
||||
|
||||
rpc_app->app_callback = callback;
|
||||
rpc_app->app_context = ctx;
|
||||
rpc_app->callback = callback;
|
||||
rpc_app->callback_context = ctx;
|
||||
}
|
||||
|
||||
void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code) {
|
||||
furi_assert(rpc_app);
|
||||
PB_App_GetErrorResponse* content = &rpc_app->error_msg->content.app_get_error_response;
|
||||
content->code = error_code;
|
||||
rpc_app->error_code = error_code;
|
||||
}
|
||||
|
||||
void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text) {
|
||||
furi_assert(rpc_app);
|
||||
PB_App_GetErrorResponse* content = &rpc_app->error_msg->content.app_get_error_response;
|
||||
|
||||
if(content->text) {
|
||||
free(content->text);
|
||||
if(rpc_app->error_text) {
|
||||
free(rpc_app->error_text);
|
||||
}
|
||||
|
||||
content->text = error_text ? strdup(error_text) : NULL;
|
||||
rpc_app->error_text = error_text ? strdup(error_text) : NULL;
|
||||
}
|
||||
|
||||
void rpc_system_app_error_reset(RpcAppSystem* rpc_app) {
|
||||
@ -340,29 +381,13 @@ void rpc_system_app_error_reset(RpcAppSystem* rpc_app) {
|
||||
rpc_system_app_set_error_text(rpc_app, NULL);
|
||||
}
|
||||
|
||||
void rpc_system_app_set_data_exchange_callback(
|
||||
RpcAppSystem* rpc_app,
|
||||
RpcAppSystemDataExchangeCallback callback,
|
||||
void* ctx) {
|
||||
furi_assert(rpc_app);
|
||||
|
||||
rpc_app->data_exchange_callback = callback;
|
||||
rpc_app->data_exchange_context = ctx;
|
||||
}
|
||||
|
||||
void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size) {
|
||||
furi_assert(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
PB_Main message = {
|
||||
.command_id = 0,
|
||||
.command_status = PB_CommandStatus_OK,
|
||||
.has_next = false,
|
||||
.which_content = PB_Main_app_data_exchange_request_tag,
|
||||
};
|
||||
PB_Main* request = malloc(sizeof(PB_Main));
|
||||
|
||||
PB_App_DataExchangeRequest* content = &message.content.app_data_exchange_request;
|
||||
request->which_content = PB_Main_app_data_exchange_request_tag;
|
||||
PB_App_DataExchangeRequest* content = &request->content.app_data_exchange_request;
|
||||
|
||||
if(data && data_size) {
|
||||
content->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data_size));
|
||||
@ -372,7 +397,9 @@ void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, si
|
||||
content->data = NULL;
|
||||
}
|
||||
|
||||
rpc_send_and_release(session, &message);
|
||||
rpc_send_and_release(rpc_app->session, request);
|
||||
|
||||
free(request);
|
||||
}
|
||||
|
||||
void* rpc_system_app_alloc(RpcSession* session) {
|
||||
@ -381,18 +408,6 @@ void* rpc_system_app_alloc(RpcSession* session) {
|
||||
RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem));
|
||||
rpc_app->session = session;
|
||||
|
||||
// App exit message
|
||||
rpc_app->state_msg = malloc(sizeof(PB_Main));
|
||||
rpc_app->state_msg->which_content = PB_Main_app_state_response_tag;
|
||||
rpc_app->state_msg->command_status = PB_CommandStatus_OK;
|
||||
|
||||
// App error message
|
||||
rpc_app->error_msg = malloc(sizeof(PB_Main));
|
||||
rpc_app->error_msg->which_content = PB_Main_app_get_error_response_tag;
|
||||
rpc_app->error_msg->command_status = PB_CommandStatus_OK;
|
||||
rpc_app->error_msg->content.app_get_error_response.code = 0;
|
||||
rpc_app->error_msg->content.app_get_error_response.text = NULL;
|
||||
|
||||
RpcHandler rpc_handler = {
|
||||
.message_handler = NULL,
|
||||
.decode_submessage = NULL,
|
||||
@ -429,24 +444,24 @@ void* rpc_system_app_alloc(RpcSession* session) {
|
||||
void rpc_system_app_free(void* context) {
|
||||
RpcAppSystem* rpc_app = context;
|
||||
furi_assert(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
furi_assert(rpc_app->session);
|
||||
|
||||
if(rpc_app->app_callback) {
|
||||
rpc_app->app_callback(RpcAppEventSessionClose, rpc_app->app_context);
|
||||
if(rpc_app->callback) {
|
||||
const RpcAppSystemEvent event = {
|
||||
.type = RpcAppEventTypeSessionClose,
|
||||
.data =
|
||||
{
|
||||
.type = RpcAppSystemEventDataTypeNone,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
rpc_app->callback(&event, rpc_app->callback_context);
|
||||
}
|
||||
|
||||
while(rpc_app->app_callback) {
|
||||
while(rpc_app->callback) {
|
||||
furi_delay_tick(1);
|
||||
}
|
||||
|
||||
furi_assert(!rpc_app->data_exchange_callback);
|
||||
|
||||
if(rpc_app->last_data) free(rpc_app->last_data);
|
||||
|
||||
pb_release(&PB_Main_msg, rpc_app->error_msg);
|
||||
|
||||
free(rpc_app->error_msg);
|
||||
free(rpc_app->state_msg);
|
||||
free(rpc_app);
|
||||
}
|
||||
|
||||
@ -1,45 +1,213 @@
|
||||
/**
|
||||
* @file rpc_app.h
|
||||
* @brief Application RPC subsystem interface.
|
||||
*
|
||||
* The application RPC subsystem provides facilities for interacting with applications,
|
||||
* such as starting/stopping, passing parameters, sending commands and exchanging arbitrary data.
|
||||
*
|
||||
* All commands are handled asynchronously via a user-settable callback.
|
||||
*
|
||||
* For a complete description of message types handled in this subsystem,
|
||||
* see https://github.com/flipperdevices/flipperzero-protobuf/blob/dev/application.proto
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "rpc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enumeration of possible event data types.
|
||||
*/
|
||||
typedef enum {
|
||||
RpcAppEventSessionClose,
|
||||
RpcAppEventAppExit,
|
||||
RpcAppEventLoadFile,
|
||||
RpcAppEventButtonPress,
|
||||
RpcAppEventButtonRelease,
|
||||
RpcAppSystemEventDataTypeNone, /**< No data is provided by the event. */
|
||||
RpcAppSystemEventDataTypeString, /**< Event data contains a zero-terminated string. */
|
||||
RpcAppSystemEventDataTypeInt32, /**< Event data contains a signed 32-bit integer. */
|
||||
RpcAppSystemEventDataTypeBytes, /**< Event data contains zero or more bytes. */
|
||||
} RpcAppSystemEventDataType;
|
||||
|
||||
/**
|
||||
* @brief Event data structure, containing the type and associated data.
|
||||
*
|
||||
* All below fields except for type are valid only if the respective type is set.
|
||||
*/
|
||||
typedef struct {
|
||||
RpcAppSystemEventDataType
|
||||
type; /**< Type of the data. The meaning of other fields depends on this one. */
|
||||
union {
|
||||
const char* string; /**< Pointer to a zero-terminated character string. */
|
||||
int32_t i32; /**< Signed 32-bit integer value. */
|
||||
struct {
|
||||
const uint8_t* ptr; /**< Pointer to the byte array data. */
|
||||
size_t size; /**< Size of the byte array, in bytes. */
|
||||
} bytes; /**< Byte array of arbitrary length. */
|
||||
};
|
||||
} RpcAppSystemEventData;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of possible event types.
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* @brief Denotes an invalid state.
|
||||
*
|
||||
* An event of this type shall never be passed into the callback.
|
||||
*/
|
||||
RpcAppEventTypeInvalid,
|
||||
/**
|
||||
* @brief The client side has closed the session.
|
||||
*
|
||||
* After receiving this event, the RPC context is no more valid.
|
||||
*/
|
||||
RpcAppEventTypeSessionClose,
|
||||
/**
|
||||
* @brief The client has requested the application to exit.
|
||||
*
|
||||
* The application must exit after receiving this command.
|
||||
*/
|
||||
RpcAppEventTypeAppExit,
|
||||
/**
|
||||
* @brief The client has requested the application to load a file.
|
||||
*
|
||||
* This command's meaning is application-specific, i.e. the application might or
|
||||
* might not require additional commands after loading a file to do anything useful.
|
||||
*/
|
||||
RpcAppEventTypeLoadFile,
|
||||
/**
|
||||
* @brief The client has informed the application that a button has been pressed.
|
||||
*
|
||||
* This command's meaning is application-specific, e.g. to select a part of the
|
||||
* previously loaded file or to invoke a particular function within the application.
|
||||
*/
|
||||
RpcAppEventTypeButtonPress,
|
||||
/**
|
||||
* @brief The client has informed the application that a button has been released.
|
||||
*
|
||||
* This command's meaning is application-specific, e.g. to cease
|
||||
* all activities to be conducted while a button is being pressed.
|
||||
*/
|
||||
RpcAppEventTypeButtonRelease,
|
||||
/**
|
||||
* @brief The client has sent a byte array of arbitrary size.
|
||||
*
|
||||
* This command's purpose is bi-directional exchange of arbitrary raw data.
|
||||
* Useful for implementing higher-level protocols while using the RPC as a transport layer.
|
||||
*/
|
||||
RpcAppEventTypeDataExchange,
|
||||
} RpcAppSystemEventType;
|
||||
|
||||
/**
|
||||
* @brief RPC application subsystem event structure.
|
||||
*/
|
||||
typedef struct {
|
||||
RpcAppSystemEventType type; /**< Type of the event. */
|
||||
RpcAppSystemEventData data; /**< Data associated with the event. */
|
||||
} RpcAppSystemEvent;
|
||||
|
||||
typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context);
|
||||
typedef void (
|
||||
*RpcAppSystemDataExchangeCallback)(const uint8_t* data, size_t data_size, void* context);
|
||||
/**
|
||||
* @brief Callback function type.
|
||||
*
|
||||
* A function of this type must be passed to rpc_system_app_set_callback() by the user code.
|
||||
*
|
||||
* @warning The event pointer is valid ONLY inside the callback function.
|
||||
*
|
||||
* @param[in] event pointer to the event object. Valid only inside the callback function.
|
||||
* @param[in,out] context pointer to the user-defined context object.
|
||||
*/
|
||||
typedef void (*RpcAppSystemCallback)(const RpcAppSystemEvent* event, void* context);
|
||||
|
||||
/**
|
||||
* @brief RPC application subsystem opaque type declaration.
|
||||
*/
|
||||
typedef struct RpcAppSystem RpcAppSystem;
|
||||
|
||||
void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx);
|
||||
/**
|
||||
* @brief Set the callback function for use by an RpcAppSystem instance.
|
||||
*
|
||||
* @param[in,out] rpc_app pointer to the instance to be configured.
|
||||
* @param[in] callback pointer to the function to be called upon message reception.
|
||||
* @param[in,out] context pointer to the user-defined context object. Will be passed to the callback.
|
||||
*/
|
||||
void rpc_system_app_set_callback(
|
||||
RpcAppSystem* rpc_app,
|
||||
RpcAppSystemCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* @brief Send a notification that an RpcAppSystem instance has been started and is ready.
|
||||
*
|
||||
* Call this function once right after acquiring an RPC context and setting the callback.
|
||||
*
|
||||
* @param[in,out] rpc_app pointer to the instance to be used.
|
||||
*/
|
||||
void rpc_system_app_send_started(RpcAppSystem* rpc_app);
|
||||
|
||||
/**
|
||||
* @brief Send a notification that the application using an RpcAppSystem instance is about to exit.
|
||||
*
|
||||
* Call this function when the application is about to exit (usually in the *_free() function).
|
||||
*
|
||||
* @param[in,out] rpc_app pointer to the instance to be used.
|
||||
*/
|
||||
void rpc_system_app_send_exited(RpcAppSystem* rpc_app);
|
||||
|
||||
const char* rpc_system_app_get_data(RpcAppSystem* rpc_app);
|
||||
|
||||
void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result);
|
||||
/**
|
||||
* @brief Send a confirmation that the application using an RpcAppSystem instance has handled the event.
|
||||
*
|
||||
* An explicit confirmation is required for the following event types:
|
||||
* - RpcAppEventTypeAppExit
|
||||
* - RpcAppEventTypeLoadFile
|
||||
* - RpcAppEventTypeButtonPress
|
||||
* - RpcAppEventTypeButtonRelease
|
||||
* - RpcAppEventTypeDataExchange
|
||||
*
|
||||
* Not confirming these events will result in a client-side timeout.
|
||||
*
|
||||
* @param[in,out] rpc_app pointer to the instance to be used.
|
||||
* @param[in] result whether the command was successfully handled or not (true for success).
|
||||
*/
|
||||
void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result);
|
||||
|
||||
/**
|
||||
* @brief Set the error code stored in an RpcAppSystem instance.
|
||||
*
|
||||
* The error code can be retrieved by the client at any time by using the GetError request.
|
||||
* The error code value has no meaning within the subsystem, i.e. it is only passed through to the client.
|
||||
*
|
||||
* @param[in,out] rpc_app pointer to the instance to be modified.
|
||||
* @param[in] error_code arbitrary error code to be set.
|
||||
*/
|
||||
void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code);
|
||||
|
||||
/**
|
||||
* @brief Set the error text stored in an RpcAppSystem instance.
|
||||
*
|
||||
* The error text can be retrieved by the client at any time by using the GetError request.
|
||||
* The text has no meaning within the subsystem, i.e. it is only passed through to the client.
|
||||
*
|
||||
* @param[in,out] rpc_app pointer to the instance to be modified.
|
||||
* @param[in] error_text Pointer to a zero-terminated string containing the error text.
|
||||
*/
|
||||
void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text);
|
||||
|
||||
/**
|
||||
* @brief Reset the error code and text stored in an RpcAppSystem instance.
|
||||
*
|
||||
* Resets the error code to 0 and error text to "" (empty string).
|
||||
*
|
||||
* @param[in,out] rpc_app pointer to the instance to be reset.
|
||||
*/
|
||||
void rpc_system_app_error_reset(RpcAppSystem* rpc_app);
|
||||
|
||||
void rpc_system_app_set_data_exchange_callback(
|
||||
RpcAppSystem* rpc_app,
|
||||
RpcAppSystemDataExchangeCallback callback,
|
||||
void* ctx);
|
||||
|
||||
/**
|
||||
* @brief Send a byte array of arbitrary data to the client using an RpcAppSystem instance.
|
||||
*
|
||||
* @param[in,out] rpc_app pointer to the instance to be used.
|
||||
* @param[in] data pointer to the data buffer to be sent.
|
||||
* @param[in] data_size size of the data buffer, in bytes.
|
||||
*/
|
||||
void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 327163d5867c7aa3051334c93ced718d15bfe4da
|
||||
Subproject commit 23ad19a756649ed9f6677b598e5361c5cce6847b
|
||||
@ -80,22 +80,35 @@ def __invoke_git(args, source_dir):
|
||||
def _proto_ver_generator(target, source, env):
|
||||
target_file = target[0]
|
||||
src_dir = source[0].dir.abspath
|
||||
try:
|
||||
__invoke_git(
|
||||
["fetch", "--tags"],
|
||||
source_dir=src_dir,
|
||||
)
|
||||
except (subprocess.CalledProcessError, EnvironmentError):
|
||||
# Not great, not terrible
|
||||
print(fg.boldred("Git: fetch failed"))
|
||||
|
||||
try:
|
||||
git_describe = __invoke_git(
|
||||
["describe", "--tags", "--abbrev=0"],
|
||||
source_dir=src_dir,
|
||||
)
|
||||
except (subprocess.CalledProcessError, EnvironmentError):
|
||||
raise StopError("Git: describe failed")
|
||||
def fetch(unshallow=False):
|
||||
git_args = ["fetch", "--tags"]
|
||||
if unshallow:
|
||||
git_args.append("--unshallow")
|
||||
|
||||
try:
|
||||
__invoke_git(git_args, source_dir=src_dir)
|
||||
except (subprocess.CalledProcessError, EnvironmentError):
|
||||
# Not great, not terrible
|
||||
print(fg.boldred("Git: fetch failed"))
|
||||
|
||||
def describe():
|
||||
try:
|
||||
return __invoke_git(
|
||||
["describe", "--tags", "--abbrev=0"],
|
||||
source_dir=src_dir,
|
||||
)
|
||||
except (subprocess.CalledProcessError, EnvironmentError):
|
||||
return None
|
||||
|
||||
fetch()
|
||||
git_describe = describe()
|
||||
if not git_describe:
|
||||
fetch(unshallow=True)
|
||||
git_describe = describe()
|
||||
|
||||
if not git_describe:
|
||||
raise StopError("Failed to process git tags for protobuf versioning")
|
||||
|
||||
git_major, git_minor = git_describe.split(".")
|
||||
version_file_data = (
|
||||
|
||||
@ -1950,14 +1950,12 @@ Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCal
|
||||
Function,+,rpc_session_set_context,void,"RpcSession*, void*"
|
||||
Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback"
|
||||
Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback"
|
||||
Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, RpcAppSystemEvent, _Bool"
|
||||
Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, _Bool"
|
||||
Function,+,rpc_system_app_error_reset,void,RpcAppSystem*
|
||||
Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t"
|
||||
Function,+,rpc_system_app_get_data,const char*,RpcAppSystem*
|
||||
Function,+,rpc_system_app_send_exited,void,RpcAppSystem*
|
||||
Function,+,rpc_system_app_send_started,void,RpcAppSystem*
|
||||
Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*"
|
||||
Function,+,rpc_system_app_set_data_exchange_callback,void,"RpcAppSystem*, RpcAppSystemDataExchangeCallback, void*"
|
||||
Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t"
|
||||
Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*"
|
||||
Function,-,rpmatch,int,const char*
|
||||
|
||||
|
@ -2493,14 +2493,12 @@ Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCal
|
||||
Function,+,rpc_session_set_context,void,"RpcSession*, void*"
|
||||
Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback"
|
||||
Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback"
|
||||
Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, RpcAppSystemEvent, _Bool"
|
||||
Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, _Bool"
|
||||
Function,+,rpc_system_app_error_reset,void,RpcAppSystem*
|
||||
Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t"
|
||||
Function,+,rpc_system_app_get_data,const char*,RpcAppSystem*
|
||||
Function,+,rpc_system_app_send_exited,void,RpcAppSystem*
|
||||
Function,+,rpc_system_app_send_started,void,RpcAppSystem*
|
||||
Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*"
|
||||
Function,+,rpc_system_app_set_data_exchange_callback,void,"RpcAppSystem*, RpcAppSystemDataExchangeCallback, void*"
|
||||
Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t"
|
||||
Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*"
|
||||
Function,-,rpmatch,int,const char*
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user