[FL-2627] Flipper applications: SDK, build and debug system (#1387)
* Added support for running applications from SD card (FAPs - Flipper Application Packages) * Added plugin_dist target for fbt to build FAPs * All apps of type FlipperAppType.EXTERNAL and FlipperAppType.PLUGIN are built as FAPs by default * Updated VSCode configuration for new fbt features - re-deploy stock configuration to use them * Added debugging support for FAPs with fbt debug & VSCode * Added public firmware API with automated versioning Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: SG <who.just.the.doctor@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
11
applications/services/dialogs/application.fam
Normal file
11
applications/services/dialogs/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="dialogs",
|
||||
name="DialogsSrv",
|
||||
apptype=FlipperAppType.SERVICE,
|
||||
entry_point="dialogs_srv",
|
||||
cdefines=["SRV_DIALOGS"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=40,
|
||||
sdk_headers=["dialogs.h"],
|
||||
)
|
54
applications/services/dialogs/dialogs.c
Normal file
54
applications/services/dialogs/dialogs.c
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "dialogs/dialogs_message.h"
|
||||
#include "dialogs_i.h"
|
||||
#include "dialogs_api_lock.h"
|
||||
#include "dialogs_module_file_browser.h"
|
||||
#include "dialogs_module_message.h"
|
||||
|
||||
void dialog_file_browser_set_basic_options(
|
||||
DialogsFileBrowserOptions* options,
|
||||
const char* extension,
|
||||
const Icon* icon) {
|
||||
options->extension = extension;
|
||||
options->skip_assets = true;
|
||||
options->icon = icon;
|
||||
options->hide_ext = true;
|
||||
options->item_loader_callback = NULL;
|
||||
options->item_loader_context = NULL;
|
||||
}
|
||||
|
||||
static DialogsApp* dialogs_app_alloc() {
|
||||
DialogsApp* app = malloc(sizeof(DialogsApp));
|
||||
app->message_queue = furi_message_queue_alloc(8, sizeof(DialogsAppMessage));
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* message) {
|
||||
UNUSED(app);
|
||||
switch(message->command) {
|
||||
case DialogsAppCommandFileBrowser:
|
||||
message->return_data->bool_value =
|
||||
dialogs_app_process_module_file_browser(&message->data->file_browser);
|
||||
break;
|
||||
case DialogsAppCommandDialog:
|
||||
message->return_data->dialog_value =
|
||||
dialogs_app_process_module_message(&message->data->dialog);
|
||||
break;
|
||||
}
|
||||
API_LOCK_UNLOCK(message->lock);
|
||||
}
|
||||
|
||||
int32_t dialogs_srv(void* p) {
|
||||
UNUSED(p);
|
||||
DialogsApp* app = dialogs_app_alloc();
|
||||
furi_record_create(RECORD_DIALOGS, app);
|
||||
|
||||
DialogsAppMessage message;
|
||||
while(1) {
|
||||
if(furi_message_queue_get(app->message_queue, &message, FuriWaitForever) == FuriStatusOk) {
|
||||
dialogs_app_process_message(app, &message);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
165
applications/services/dialogs/dialogs.h
Normal file
165
applications/services/dialogs/dialogs.h
Normal file
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include <gui/canvas.h>
|
||||
#include "m-string.h"
|
||||
#include <gui/modules/file_browser.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/****************** COMMON ******************/
|
||||
|
||||
#define RECORD_DIALOGS "dialogs"
|
||||
|
||||
typedef struct DialogsApp DialogsApp;
|
||||
|
||||
/****************** FILE BROWSER ******************/
|
||||
|
||||
/**
|
||||
* File browser dialog extra options
|
||||
* @param extension file extension to be offered for selection
|
||||
* @param skip_assets true - do not show assets folders
|
||||
* @param icon file icon pointer, NULL for default icon
|
||||
* @param hide_ext true - hide extensions for files
|
||||
* @param item_loader_callback callback function for providing custom icon & entry name
|
||||
* @param hide_ext callback context
|
||||
*/
|
||||
typedef struct {
|
||||
const char* extension;
|
||||
bool skip_assets;
|
||||
const Icon* icon;
|
||||
bool hide_ext;
|
||||
FileBrowserLoadItemCallback item_loader_callback;
|
||||
void* item_loader_context;
|
||||
} DialogsFileBrowserOptions;
|
||||
|
||||
/**
|
||||
* Initialize file browser dialog options
|
||||
* and set default values
|
||||
* @param options pointer to options structure
|
||||
* @param extension file extension to filter
|
||||
* @param icon file icon pointer, NULL for default icon
|
||||
*/
|
||||
void dialog_file_browser_set_basic_options(
|
||||
DialogsFileBrowserOptions* options,
|
||||
const char* extension,
|
||||
const Icon* icon);
|
||||
|
||||
/**
|
||||
* Shows and processes the file browser dialog
|
||||
* @param context api pointer
|
||||
* @param result_path selected file path string pointer
|
||||
* @param path preselected file path string pointer
|
||||
* @param options file browser dialog extra options, may be null
|
||||
* @return bool whether a file was selected
|
||||
*/
|
||||
bool dialog_file_browser_show(
|
||||
DialogsApp* context,
|
||||
string_ptr result_path,
|
||||
string_ptr path,
|
||||
const DialogsFileBrowserOptions* options);
|
||||
|
||||
/****************** MESSAGE ******************/
|
||||
|
||||
/**
|
||||
* Message result type
|
||||
*/
|
||||
typedef enum {
|
||||
DialogMessageButtonBack,
|
||||
DialogMessageButtonLeft,
|
||||
DialogMessageButtonCenter,
|
||||
DialogMessageButtonRight,
|
||||
} DialogMessageButton;
|
||||
|
||||
/**
|
||||
* Message struct
|
||||
*/
|
||||
typedef struct DialogMessage DialogMessage;
|
||||
|
||||
/**
|
||||
* Allocate and fill message
|
||||
* @return DialogMessage*
|
||||
*/
|
||||
DialogMessage* dialog_message_alloc();
|
||||
|
||||
/**
|
||||
* Free message struct
|
||||
* @param message message pointer
|
||||
*/
|
||||
void dialog_message_free(DialogMessage* message);
|
||||
|
||||
/**
|
||||
* Set message text
|
||||
* @param message message pointer
|
||||
* @param text text, can be NULL if you don't want to display the text
|
||||
* @param x x position
|
||||
* @param y y position
|
||||
* @param horizontal horizontal alignment
|
||||
* @param vertical vertical alignment
|
||||
*/
|
||||
void dialog_message_set_text(
|
||||
DialogMessage* message,
|
||||
const char* text,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
Align horizontal,
|
||||
Align vertical);
|
||||
|
||||
/**
|
||||
* Set message header
|
||||
* @param message message pointer
|
||||
* @param text text, can be NULL if you don't want to display the header
|
||||
* @param x x position
|
||||
* @param y y position
|
||||
* @param horizontal horizontal alignment
|
||||
* @param vertical vertical alignment
|
||||
*/
|
||||
void dialog_message_set_header(
|
||||
DialogMessage* message,
|
||||
const char* text,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
Align horizontal,
|
||||
Align vertical);
|
||||
|
||||
/**
|
||||
* Set message icon
|
||||
* @param message message pointer
|
||||
* @param icon icon pointer, can be NULL if you don't want to display the icon
|
||||
* @param x x position
|
||||
* @param y y position
|
||||
*/
|
||||
void dialog_message_set_icon(DialogMessage* message, const Icon* icon, uint8_t x, uint8_t y);
|
||||
|
||||
/**
|
||||
* Set message buttons text, button text can be NULL if you don't want to display and process some buttons
|
||||
* @param message message pointer
|
||||
* @param left left button text, can be NULL if you don't want to display the left button
|
||||
* @param center center button text, can be NULL if you don't want to display the center button
|
||||
* @param right right button text, can be NULL if you don't want to display the right button
|
||||
*/
|
||||
void dialog_message_set_buttons(
|
||||
DialogMessage* message,
|
||||
const char* left,
|
||||
const char* center,
|
||||
const char* right);
|
||||
|
||||
/**
|
||||
* Show message from filled struct
|
||||
* @param context api pointer
|
||||
* @param message message struct pointer to be shown
|
||||
* @return DialogMessageButton type
|
||||
*/
|
||||
DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* message);
|
||||
|
||||
/**
|
||||
* Show SD error message (with question sign)
|
||||
* @param context
|
||||
* @param error_text
|
||||
*/
|
||||
void dialog_message_show_storage_error(DialogsApp* context, const char* error_text);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
78
applications/services/dialogs/dialogs_api.c
Normal file
78
applications/services/dialogs/dialogs_api.c
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "dialogs/dialogs_message.h"
|
||||
#include "dialogs_i.h"
|
||||
#include "dialogs_api_lock.h"
|
||||
#include "m-string.h"
|
||||
|
||||
/****************** File browser ******************/
|
||||
|
||||
bool dialog_file_browser_show(
|
||||
DialogsApp* context,
|
||||
string_ptr result_path,
|
||||
string_ptr path,
|
||||
const DialogsFileBrowserOptions* options) {
|
||||
FuriApiLock lock = API_LOCK_INIT_LOCKED();
|
||||
furi_check(lock != NULL);
|
||||
|
||||
DialogsAppData data = {
|
||||
.file_browser = {
|
||||
.extension = options ? options->extension : "",
|
||||
.result_path = result_path,
|
||||
.file_icon = options ? options->icon : NULL,
|
||||
.hide_ext = options ? options->hide_ext : true,
|
||||
.skip_assets = options ? options->skip_assets : true,
|
||||
.preselected_filename = path,
|
||||
.item_callback = options ? options->item_loader_callback : NULL,
|
||||
.item_callback_context = options ? options->item_loader_context : NULL,
|
||||
}};
|
||||
|
||||
DialogsAppReturn return_data;
|
||||
DialogsAppMessage message = {
|
||||
.lock = lock,
|
||||
.command = DialogsAppCommandFileBrowser,
|
||||
.data = &data,
|
||||
.return_data = &return_data,
|
||||
};
|
||||
|
||||
furi_check(
|
||||
furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock);
|
||||
|
||||
return return_data.bool_value;
|
||||
}
|
||||
|
||||
/****************** Message ******************/
|
||||
|
||||
DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) {
|
||||
FuriApiLock lock = API_LOCK_INIT_LOCKED();
|
||||
furi_check(lock != NULL);
|
||||
|
||||
DialogsAppData data = {
|
||||
.dialog = {
|
||||
.message = dialog_message,
|
||||
}};
|
||||
|
||||
DialogsAppReturn return_data;
|
||||
DialogsAppMessage message = {
|
||||
.lock = lock,
|
||||
.command = DialogsAppCommandDialog,
|
||||
.data = &data,
|
||||
.return_data = &return_data,
|
||||
};
|
||||
|
||||
furi_check(
|
||||
furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock);
|
||||
|
||||
return return_data.dialog_value;
|
||||
}
|
||||
|
||||
/****************** Storage error ******************/
|
||||
|
||||
void dialog_message_show_storage_error(DialogsApp* context, const char* error_text) {
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
dialog_message_set_text(message, error_text, 88, 32, AlignCenter, AlignCenter);
|
||||
dialog_message_set_icon(message, &I_SDQuestion_35x43, 5, 6);
|
||||
dialog_message_set_buttons(message, "Back", NULL, NULL);
|
||||
dialog_message_show(context, message);
|
||||
dialog_message_free(message);
|
||||
}
|
18
applications/services/dialogs/dialogs_api_lock.h
Normal file
18
applications/services/dialogs/dialogs_api_lock.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
typedef FuriEventFlag* FuriApiLock;
|
||||
|
||||
#define API_LOCK_EVENT (1U << 0)
|
||||
|
||||
#define API_LOCK_INIT_LOCKED() furi_event_flag_alloc();
|
||||
|
||||
#define API_LOCK_WAIT_UNTIL_UNLOCK(_lock) \
|
||||
furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever);
|
||||
|
||||
#define API_LOCK_FREE(_lock) furi_event_flag_free(_lock);
|
||||
|
||||
#define API_LOCK_UNLOCK(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT);
|
||||
|
||||
#define API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(_lock) \
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK(_lock); \
|
||||
API_LOCK_FREE(_lock);
|
16
applications/services/dialogs/dialogs_i.h
Normal file
16
applications/services/dialogs/dialogs_i.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "dialogs.h"
|
||||
#include "dialogs_message.h"
|
||||
#include "view_holder.h"
|
||||
#include <gui/modules/file_browser.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
struct DialogsApp {
|
||||
FuriMessageQueue* message_queue;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
50
applications/services/dialogs/dialogs_message.h
Normal file
50
applications/services/dialogs/dialogs_message.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include "dialogs_i.h"
|
||||
#include "dialogs_api_lock.h"
|
||||
#include "m-string.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
const char* extension;
|
||||
bool skip_assets;
|
||||
bool hide_ext;
|
||||
const Icon* file_icon;
|
||||
string_ptr result_path;
|
||||
string_ptr preselected_filename;
|
||||
FileBrowserLoadItemCallback item_callback;
|
||||
void* item_callback_context;
|
||||
} DialogsAppMessageDataFileBrowser;
|
||||
|
||||
typedef struct {
|
||||
const DialogMessage* message;
|
||||
} DialogsAppMessageDataDialog;
|
||||
|
||||
typedef union {
|
||||
DialogsAppMessageDataFileBrowser file_browser;
|
||||
DialogsAppMessageDataDialog dialog;
|
||||
} DialogsAppData;
|
||||
|
||||
typedef union {
|
||||
bool bool_value;
|
||||
DialogMessageButton dialog_value;
|
||||
} DialogsAppReturn;
|
||||
|
||||
typedef enum {
|
||||
DialogsAppCommandFileBrowser,
|
||||
DialogsAppCommandDialog,
|
||||
} DialogsAppCommand;
|
||||
|
||||
typedef struct {
|
||||
FuriApiLock lock;
|
||||
DialogsAppCommand command;
|
||||
DialogsAppData* data;
|
||||
DialogsAppReturn* return_data;
|
||||
} DialogsAppMessage;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
60
applications/services/dialogs/dialogs_module_file_browser.c
Normal file
60
applications/services/dialogs/dialogs_module_file_browser.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "dialogs_i.h"
|
||||
#include "dialogs_api_lock.h"
|
||||
#include "gui/modules/file_browser.h"
|
||||
|
||||
typedef struct {
|
||||
FuriApiLock lock;
|
||||
bool result;
|
||||
} DialogsAppFileBrowserContext;
|
||||
|
||||
static void dialogs_app_file_browser_back_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DialogsAppFileBrowserContext* file_browser_context = context;
|
||||
file_browser_context->result = false;
|
||||
API_LOCK_UNLOCK(file_browser_context->lock);
|
||||
}
|
||||
|
||||
static void dialogs_app_file_browser_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DialogsAppFileBrowserContext* file_browser_context = context;
|
||||
file_browser_context->result = true;
|
||||
API_LOCK_UNLOCK(file_browser_context->lock);
|
||||
}
|
||||
|
||||
bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) {
|
||||
bool ret = false;
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
DialogsAppFileBrowserContext* file_browser_context =
|
||||
malloc(sizeof(DialogsAppFileBrowserContext));
|
||||
file_browser_context->lock = API_LOCK_INIT_LOCKED();
|
||||
|
||||
ViewHolder* view_holder = view_holder_alloc();
|
||||
view_holder_attach_to_gui(view_holder, gui);
|
||||
view_holder_set_back_callback(
|
||||
view_holder, dialogs_app_file_browser_back_callback, file_browser_context);
|
||||
|
||||
FileBrowser* file_browser = file_browser_alloc(data->result_path);
|
||||
file_browser_set_callback(
|
||||
file_browser, dialogs_app_file_browser_callback, file_browser_context);
|
||||
file_browser_configure(
|
||||
file_browser, data->extension, data->skip_assets, data->file_icon, data->hide_ext);
|
||||
file_browser_set_item_callback(file_browser, data->item_callback, data->item_callback_context);
|
||||
file_browser_start(file_browser, data->preselected_filename);
|
||||
|
||||
view_holder_set_view(view_holder, file_browser_get_view(file_browser));
|
||||
view_holder_start(view_holder);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK(file_browser_context->lock);
|
||||
|
||||
ret = file_browser_context->result;
|
||||
|
||||
view_holder_stop(view_holder);
|
||||
view_holder_free(view_holder);
|
||||
file_browser_stop(file_browser);
|
||||
file_browser_free(file_browser);
|
||||
API_LOCK_FREE(file_browser_context->lock);
|
||||
free(file_browser_context);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return ret;
|
||||
}
|
12
applications/services/dialogs/dialogs_module_file_browser.h
Normal file
12
applications/services/dialogs/dialogs_module_file_browser.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "dialogs_message.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
155
applications/services/dialogs/dialogs_module_message.c
Normal file
155
applications/services/dialogs/dialogs_module_message.c
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "dialogs_i.h"
|
||||
#include "dialogs_api_lock.h"
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
|
||||
typedef struct {
|
||||
FuriApiLock lock;
|
||||
DialogMessageButton result;
|
||||
} DialogsAppMessageContext;
|
||||
|
||||
struct DialogMessage {
|
||||
const char* header_text;
|
||||
uint8_t header_text_x;
|
||||
uint8_t header_text_y;
|
||||
Align header_horizontal;
|
||||
Align header_vertical;
|
||||
const char* dialog_text;
|
||||
uint8_t dialog_text_x;
|
||||
uint8_t dialog_text_y;
|
||||
Align dialog_text_horizontal;
|
||||
Align dialog_text_vertical;
|
||||
const Icon* icon;
|
||||
uint8_t icon_x;
|
||||
uint8_t icon_y;
|
||||
const char* left_button_text;
|
||||
const char* center_button_text;
|
||||
const char* right_button_text;
|
||||
};
|
||||
|
||||
static void dialogs_app_message_back_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DialogsAppMessageContext* message_context = context;
|
||||
message_context->result = DialogMessageButtonBack;
|
||||
API_LOCK_UNLOCK(message_context->lock);
|
||||
}
|
||||
|
||||
static void dialogs_app_message_callback(DialogExResult result, void* context) {
|
||||
furi_assert(context);
|
||||
DialogsAppMessageContext* message_context = context;
|
||||
switch(result) {
|
||||
case DialogExResultLeft:
|
||||
message_context->result = DialogMessageButtonLeft;
|
||||
break;
|
||||
case DialogExResultRight:
|
||||
message_context->result = DialogMessageButtonRight;
|
||||
break;
|
||||
case DialogExResultCenter:
|
||||
message_context->result = DialogMessageButtonCenter;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
API_LOCK_UNLOCK(message_context->lock);
|
||||
}
|
||||
|
||||
DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) {
|
||||
DialogMessageButton ret = DialogMessageButtonBack;
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
const DialogMessage* message = data->message;
|
||||
DialogsAppMessageContext* message_context = malloc(sizeof(DialogsAppMessageContext));
|
||||
message_context->lock = API_LOCK_INIT_LOCKED();
|
||||
|
||||
ViewHolder* view_holder = view_holder_alloc();
|
||||
view_holder_attach_to_gui(view_holder, gui);
|
||||
view_holder_set_back_callback(view_holder, dialogs_app_message_back_callback, message_context);
|
||||
|
||||
DialogEx* dialog_ex = dialog_ex_alloc();
|
||||
dialog_ex_set_result_callback(dialog_ex, dialogs_app_message_callback);
|
||||
dialog_ex_set_context(dialog_ex, message_context);
|
||||
dialog_ex_set_header(
|
||||
dialog_ex,
|
||||
message->header_text,
|
||||
message->header_text_x,
|
||||
message->header_text_y,
|
||||
message->header_horizontal,
|
||||
message->header_vertical);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex,
|
||||
message->dialog_text,
|
||||
message->dialog_text_x,
|
||||
message->dialog_text_y,
|
||||
message->dialog_text_horizontal,
|
||||
message->dialog_text_vertical);
|
||||
dialog_ex_set_icon(dialog_ex, message->icon_x, message->icon_y, message->icon);
|
||||
dialog_ex_set_left_button_text(dialog_ex, message->left_button_text);
|
||||
dialog_ex_set_center_button_text(dialog_ex, message->center_button_text);
|
||||
dialog_ex_set_right_button_text(dialog_ex, message->right_button_text);
|
||||
|
||||
view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex));
|
||||
view_holder_start(view_holder);
|
||||
API_LOCK_WAIT_UNTIL_UNLOCK(message_context->lock);
|
||||
|
||||
ret = message_context->result;
|
||||
|
||||
view_holder_stop(view_holder);
|
||||
view_holder_free(view_holder);
|
||||
dialog_ex_free(dialog_ex);
|
||||
API_LOCK_FREE(message_context->lock);
|
||||
free(message_context);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DialogMessage* dialog_message_alloc() {
|
||||
DialogMessage* message = malloc(sizeof(DialogMessage));
|
||||
return message;
|
||||
}
|
||||
|
||||
void dialog_message_free(DialogMessage* message) {
|
||||
free(message);
|
||||
}
|
||||
|
||||
void dialog_message_set_text(
|
||||
DialogMessage* message,
|
||||
const char* text,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
Align horizontal,
|
||||
Align vertical) {
|
||||
message->dialog_text = text;
|
||||
message->dialog_text_x = x;
|
||||
message->dialog_text_y = y;
|
||||
message->dialog_text_horizontal = horizontal;
|
||||
message->dialog_text_vertical = vertical;
|
||||
}
|
||||
|
||||
void dialog_message_set_header(
|
||||
DialogMessage* message,
|
||||
const char* text,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
Align horizontal,
|
||||
Align vertical) {
|
||||
message->header_text = text;
|
||||
message->header_text_x = x;
|
||||
message->header_text_y = y;
|
||||
message->header_horizontal = horizontal;
|
||||
message->header_vertical = vertical;
|
||||
}
|
||||
|
||||
void dialog_message_set_icon(DialogMessage* message, const Icon* icon, uint8_t x, uint8_t y) {
|
||||
message->icon = icon;
|
||||
message->icon_x = x;
|
||||
message->icon_y = y;
|
||||
}
|
||||
|
||||
void dialog_message_set_buttons(
|
||||
DialogMessage* message,
|
||||
const char* left,
|
||||
const char* center,
|
||||
const char* right) {
|
||||
message->left_button_text = left;
|
||||
message->center_button_text = center;
|
||||
message->right_button_text = right;
|
||||
}
|
12
applications/services/dialogs/dialogs_module_message.h
Normal file
12
applications/services/dialogs/dialogs_module_message.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "dialogs_message.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
150
applications/services/dialogs/view_holder.c
Normal file
150
applications/services/dialogs/view_holder.c
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "view_holder.h"
|
||||
#include <gui/view_i.h>
|
||||
|
||||
#define TAG "ViewHolder"
|
||||
|
||||
struct ViewHolder {
|
||||
View* view;
|
||||
ViewPort* view_port;
|
||||
Gui* gui;
|
||||
|
||||
FreeCallback free_callback;
|
||||
void* free_context;
|
||||
|
||||
BackCallback back_callback;
|
||||
void* back_context;
|
||||
|
||||
uint8_t ongoing_input;
|
||||
};
|
||||
|
||||
static void view_holder_draw_callback(Canvas* canvas, void* context);
|
||||
static void view_holder_input_callback(InputEvent* event, void* context);
|
||||
|
||||
ViewHolder* view_holder_alloc() {
|
||||
ViewHolder* view_holder = malloc(sizeof(ViewHolder));
|
||||
|
||||
view_holder->view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_holder->view_port, view_holder_draw_callback, view_holder);
|
||||
view_port_input_callback_set(view_holder->view_port, view_holder_input_callback, view_holder);
|
||||
view_port_enabled_set(view_holder->view_port, false);
|
||||
|
||||
return view_holder;
|
||||
}
|
||||
|
||||
void view_holder_free(ViewHolder* view_holder) {
|
||||
furi_assert(view_holder);
|
||||
|
||||
if(view_holder->gui) {
|
||||
gui_remove_view_port(view_holder->gui, view_holder->view_port);
|
||||
}
|
||||
|
||||
view_port_free(view_holder->view_port);
|
||||
|
||||
if(view_holder->free_callback) {
|
||||
view_holder->free_callback(view_holder->free_context);
|
||||
}
|
||||
|
||||
free(view_holder);
|
||||
}
|
||||
|
||||
void view_holder_set_view(ViewHolder* view_holder, View* view) {
|
||||
furi_assert(view_holder);
|
||||
if(view_holder->view) {
|
||||
view_set_update_callback(view_holder->view, NULL);
|
||||
view_set_update_callback_context(view_holder->view, NULL);
|
||||
}
|
||||
|
||||
view_holder->view = view;
|
||||
|
||||
if(view_holder->view) {
|
||||
view_set_update_callback(view_holder->view, view_holder_update);
|
||||
view_set_update_callback_context(view_holder->view, view_holder);
|
||||
}
|
||||
}
|
||||
|
||||
void view_holder_set_free_callback(
|
||||
ViewHolder* view_holder,
|
||||
FreeCallback free_callback,
|
||||
void* free_context) {
|
||||
furi_assert(view_holder);
|
||||
view_holder->free_callback = free_callback;
|
||||
view_holder->free_context = free_context;
|
||||
}
|
||||
|
||||
void* view_holder_get_free_context(ViewHolder* view_holder) {
|
||||
return view_holder->free_context;
|
||||
}
|
||||
|
||||
void view_holder_set_back_callback(
|
||||
ViewHolder* view_holder,
|
||||
BackCallback back_callback,
|
||||
void* back_context) {
|
||||
furi_assert(view_holder);
|
||||
view_holder->back_callback = back_callback;
|
||||
view_holder->back_context = back_context;
|
||||
}
|
||||
|
||||
void view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui) {
|
||||
furi_assert(gui);
|
||||
furi_assert(view_holder);
|
||||
view_holder->gui = gui;
|
||||
gui_add_view_port(gui, view_holder->view_port, GuiLayerFullscreen);
|
||||
}
|
||||
|
||||
void view_holder_start(ViewHolder* view_holder) {
|
||||
view_port_enabled_set(view_holder->view_port, true);
|
||||
}
|
||||
|
||||
void view_holder_stop(ViewHolder* view_holder) {
|
||||
while(view_holder->ongoing_input) furi_delay_tick(1);
|
||||
view_port_enabled_set(view_holder->view_port, false);
|
||||
}
|
||||
|
||||
void view_holder_update(View* view, void* context) {
|
||||
furi_assert(view);
|
||||
furi_assert(context);
|
||||
|
||||
ViewHolder* view_holder = context;
|
||||
if(view == view_holder->view) {
|
||||
view_port_update(view_holder->view_port);
|
||||
}
|
||||
}
|
||||
|
||||
static void view_holder_draw_callback(Canvas* canvas, void* context) {
|
||||
ViewHolder* view_holder = context;
|
||||
if(view_holder->view) {
|
||||
view_draw(view_holder->view, canvas);
|
||||
}
|
||||
}
|
||||
|
||||
static void view_holder_input_callback(InputEvent* event, void* context) {
|
||||
ViewHolder* view_holder = context;
|
||||
|
||||
uint8_t key_bit = (1 << event->key);
|
||||
if(event->type == InputTypePress) {
|
||||
view_holder->ongoing_input |= key_bit;
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
view_holder->ongoing_input &= ~key_bit;
|
||||
} else if(!(view_holder->ongoing_input & key_bit)) {
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
"non-complementary input, discarding key: %s, type: %s",
|
||||
input_get_key_name(event->key),
|
||||
input_get_type_name(event->type));
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_consumed = false;
|
||||
|
||||
if(view_holder->view) {
|
||||
is_consumed = view_input(view_holder->view, event);
|
||||
}
|
||||
|
||||
if(!is_consumed && event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyBack) {
|
||||
if(view_holder->back_callback) {
|
||||
view_holder->back_callback(view_holder->back_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
98
applications/services/dialogs/view_holder.h
Normal file
98
applications/services/dialogs/view_holder.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ViewHolder ViewHolder;
|
||||
|
||||
/**
|
||||
* @brief Free callback type
|
||||
*/
|
||||
typedef void (*FreeCallback)(void* free_context);
|
||||
|
||||
/**
|
||||
* @brief Back callback type
|
||||
* @warning comes from GUI thread
|
||||
*/
|
||||
typedef void (*BackCallback)(void* back_context);
|
||||
|
||||
/**
|
||||
* @brief Allocate ViewHolder
|
||||
* @return pointer to ViewHolder instance
|
||||
*/
|
||||
ViewHolder* view_holder_alloc();
|
||||
|
||||
/**
|
||||
* @brief Free ViewHolder and call Free callback
|
||||
* @param view_holder pointer to ViewHolder
|
||||
*/
|
||||
void view_holder_free(ViewHolder* view_holder);
|
||||
|
||||
/**
|
||||
* @brief Set view for ViewHolder
|
||||
*
|
||||
* @param view_holder ViewHolder instance
|
||||
* @param view View instance
|
||||
*/
|
||||
void view_holder_set_view(ViewHolder* view_holder, View* view);
|
||||
|
||||
/**
|
||||
* @brief Set Free callback
|
||||
*
|
||||
* @param view_holder ViewHolder instance
|
||||
* @param free_callback callback pointer
|
||||
* @param free_context callback context
|
||||
*/
|
||||
void view_holder_set_free_callback(
|
||||
ViewHolder* view_holder,
|
||||
FreeCallback free_callback,
|
||||
void* free_context);
|
||||
|
||||
/**
|
||||
* @brief Free callback context getter. Useful if your Free callback is a module destructor, so you can get an instance of the module using this method.
|
||||
*
|
||||
* @param view_holder ViewHolder instance
|
||||
* @return void* free callback context
|
||||
*/
|
||||
void* view_holder_get_free_context(ViewHolder* view_holder);
|
||||
|
||||
void view_holder_set_back_callback(
|
||||
ViewHolder* view_holder,
|
||||
BackCallback back_callback,
|
||||
void* back_context);
|
||||
|
||||
/**
|
||||
* @brief Attach ViewHolder to GUI
|
||||
*
|
||||
* @param view_holder ViewHolder instance
|
||||
* @param gui GUI instance to attach to
|
||||
*/
|
||||
void view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui);
|
||||
|
||||
/**
|
||||
* @brief Enable view processing
|
||||
*
|
||||
* @param view_holder
|
||||
*/
|
||||
void view_holder_start(ViewHolder* view_holder);
|
||||
|
||||
/**
|
||||
* @brief Disable view processing
|
||||
*
|
||||
* @param view_holder
|
||||
*/
|
||||
void view_holder_stop(ViewHolder* view_holder);
|
||||
|
||||
/** View Update Handler
|
||||
* @param view, View Instance
|
||||
* @param context, ViewHolder instance
|
||||
*/
|
||||
void view_holder_update(View* view, void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user