Skip to content

Commit

Permalink
Add support for background loader
Browse files Browse the repository at this point in the history
  • Loading branch information
twisted_pear committed Sep 10, 2023
1 parent e9bedaa commit 36e14b8
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 16 deletions.
21 changes: 21 additions & 0 deletions bgloader_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <furi.h>
#include <flipper_application/flipper_application.h>

#define APP_BASE_ARGS "run_in_background"

typedef enum {
BGLoaderMessageType_AppReattached,
BGLoaderMessageType_LoaderBackground,
BGLoaderMessageType_LoaderExit,
} BGLoaderMessageType;

typedef struct {
BGLoaderMessageType type;
} BGLoaderMessage;

typedef struct {
FlipperApplication *fap;
FuriThread *thread;
FuriMessageQueue *to_app;
FuriMessageQueue *to_loader;
} BGLoaderApp;
148 changes: 132 additions & 16 deletions esubghz_chat.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "helpers/radio_device_loader.h"
#include "esubghz_chat_i.h"

#include "bgloader_api.h"

#define CHAT_LEAVE_DELAY 10
#define TICK_INTERVAL 50
#define MESSAGE_COMPLETION_TIMEOUT 500
Expand Down Expand Up @@ -252,21 +254,8 @@ static bool esubghz_chat_navigation_event_callback(void* context)
return scene_manager_handle_back_event(state->scene_manager);
}

/* Tick event callback for view dispatcher. Called every TICK_INTERVAL. Resets
* the locked message if necessary. Retrieves a received message from the
* Sub-GHz worker and calls post_rx(). Then calls the scene manager. */
static void esubghz_chat_tick_event_callback(void* context)
static void esubghz_chat_check_messages(ESubGhzChatState *state)
{
FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_tick_event_callback");

furi_assert(context);
ESubGhzChatState* state = context;

/* reset locked message if necessary */
if (kbd_lock_msg_reset_timeout(state)) {
kbd_lock_msg_reset(state, true);
}

/* if the maximum message size was reached or the
* MESSAGE_COMPLETION_TIMEOUT has expired, retrieve a message and call
* post_rx() */
Expand All @@ -284,6 +273,24 @@ static void esubghz_chat_tick_event_callback(void* context)
state->rx_buffer, RX_TX_BUFFER_SIZE);
post_rx(state, rx_size);
}
}

/* Tick event callback for view dispatcher. Called every TICK_INTERVAL. Resets
* the locked message if necessary. Retrieves a received message from the
* Sub-GHz worker and calls post_rx(). Then calls the scene manager. */
static void esubghz_chat_tick_event_callback(void* context)
{
FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_tick_event_callback");

furi_assert(context);
ESubGhzChatState* state = context;

/* reset locked message if necessary */
if (kbd_lock_msg_reset_timeout(state)) {
kbd_lock_msg_reset(state, true);
}

esubghz_chat_check_messages(state);

/* call scene manager */
scene_manager_handle_tick_event(state->scene_manager);
Expand Down Expand Up @@ -355,6 +362,18 @@ static void esubghz_hooked_input_callback(InputEvent* event, void* context)
return;
}

/* handle long press of back key to exit for real */
if (event->key == InputKeyBack) {
if (state->view_dispatcher->current_view ==
text_input_get_view(state->text_input)) {
if (event->type == InputTypeLong) {
state->exit_for_real = true;
view_dispatcher_stop(state->view_dispatcher);
return;
}
}
}

if (event->key == InputKeyOk) {
/* if we are in the chat view and no input is ongoing, allow
* locking */
Expand Down Expand Up @@ -434,6 +453,95 @@ static void esubghz_hooked_input_callback(InputEvent* event, void* context)
state->orig_input_cb(event, state->view_dispatcher);
}

static const char *esubghz_get_bgloader_app_path(const char *args)
{
size_t base_args_len = strlen(APP_BASE_ARGS);

return (args + base_args_len + 1);
}

static bool esubghz_run_with_bgloader(const char *args)
{
size_t base_args_len = strlen(APP_BASE_ARGS);

if (args == NULL) {
return false;
}

if (strncmp(args, APP_BASE_ARGS, base_args_len) != 0) {
return false;
}

if (strlen(args) < base_args_len + 2) {
return false;
}

if (args[base_args_len] != ':') {
return false;
}

const char *app_path = esubghz_get_bgloader_app_path(args);
return furi_record_exists(app_path);
}

static void esubghz_attach_to_gui(ESubGhzChatState *state)
{
Gui *gui = furi_record_open(RECORD_GUI);
view_dispatcher_attach_to_gui(state->view_dispatcher, gui,
ViewDispatcherTypeFullscreen);
}

static void esubghz_detach_from_gui(ESubGhzChatState *state)
{
gui_remove_view_port(state->view_dispatcher->gui,
state->view_dispatcher->view_port);
state->view_dispatcher->gui = NULL;
furi_record_close(RECORD_GUI);
}

static void esubghz_bgloader_loop(ESubGhzChatState *state, const char
*bg_app_path)
{
while (true) {
view_dispatcher_run(state->view_dispatcher);

if (state->exit_for_real) {
/* exit for real */
break;
}

BGLoaderApp *bg_app = furi_record_open(bg_app_path);

/* signal loader that we're ready to go to background */
BGLoaderMessage msg;
msg.type = BGLoaderMessageType_LoaderBackground;
furi_check(furi_message_queue_put(bg_app->to_loader, &msg,
FuriWaitForever) == FuriStatusOk);

esubghz_detach_from_gui(state);

while (true) {
/* wait for loader to wake us up again */
if (furi_message_queue_get(bg_app->to_app, &msg,
TICK_INTERVAL) != FuriStatusOk)
{
/* check for messages on timeout */
esubghz_chat_check_messages(state);
continue;
}
if (msg.type == BGLoaderMessageType_AppReattached) {
break;
} else {
furi_check(0);
}
}

furi_record_close(bg_app_path);

esubghz_attach_to_gui(state);
}
}

static bool helper_strings_alloc(ESubGhzChatState *state)
{
furi_assert(state);
Expand Down Expand Up @@ -492,7 +600,7 @@ static void chat_box_free(ESubGhzChatState *state)
furi_string_free(state->chat_box_store);
}

int32_t esubghz_chat(void)
int32_t esubghz_chat(const char *args)
{
/* init the crypto system */
crypto_init();
Expand Down Expand Up @@ -577,6 +685,9 @@ int32_t esubghz_chat(void)
/* set the default frequency */
state->frequency = DEFAULT_FREQ;

/* in the first few views there is no background support */
state->exit_for_real = true;

/* set the have_read callback of the Sub-GHz worker */
subghz_tx_rx_worker_set_callback_have_read(state->subghz_worker,
have_read_cb, state);
Expand Down Expand Up @@ -649,7 +760,12 @@ int32_t esubghz_chat(void)

/* run the view dispatcher, this call only returns when we close the
* application */
view_dispatcher_run(state->view_dispatcher);
if (!esubghz_run_with_bgloader(args)) {
view_dispatcher_run(state->view_dispatcher);
} else {
const char *bg_app_path = esubghz_get_bgloader_app_path(args);
esubghz_bgloader_loop(state, bg_app_path);
}

/* if it is running, stop the Sub-GHz worker */
if (subghz_tx_rx_worker_is_running(state->subghz_worker)) {
Expand Down
3 changes: 3 additions & 0 deletions esubghz_chat_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ typedef struct {
bool kbd_ok_input_ongoing;
bool kbd_left_input_ongoing;
bool kbd_right_input_ongoing;

// for background support
bool exit_for_real;
} ESubGhzChatState;

typedef enum {
Expand Down
2 changes: 2 additions & 0 deletions scenes/esubghz_chat_chat_box.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ bool scene_on_event_chat_box(void* context, SceneManagerEvent event)
case ESubGhzChatEvent_GotoMsgInput:
if (!scene_manager_previous_scene(
state->scene_manager)) {
/* error condition, exit for real */
state->exit_for_real = true;
view_dispatcher_stop(state->view_dispatcher);
}
consumed = true;
Expand Down
3 changes: 3 additions & 0 deletions scenes/esubghz_chat_freq_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ static void freq_input_cb(void *context)

enter_chat(state);

/* starting from here running in background is supported */
state->exit_for_real = false;

view_dispatcher_send_custom_event(state->view_dispatcher,
ESubGhzChatEvent_FreqEntered);
}
Expand Down
2 changes: 2 additions & 0 deletions scenes/esubghz_chat_key_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ bool scene_on_event_key_display(void* context, SceneManagerEvent event)
case ESubGhzChatEvent_KeyDisplayBack:
if (!scene_manager_previous_scene(
state->scene_manager)) {
/* error condition, exit for real */
state->exit_for_real = true;
view_dispatcher_stop(state->view_dispatcher);
}
consumed = true;
Expand Down

0 comments on commit 36e14b8

Please sign in to comment.