1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-13 05:06:30 +04:00

Update Protoview

https://github.com/antirez/protoview
This commit is contained in:
MX
2023-01-20 07:11:00 +03:00
parent 3174d29dfa
commit f3939a715a
4 changed files with 125 additions and 33 deletions

View File

@@ -64,10 +64,13 @@ static void render_callback(Canvas* const canvas, void* ctx) {
case ViewDirectSampling:
render_view_direct_sampling(canvas, app);
break;
case ViewLast:
furi_crash(TAG " ViewLast selected");
default:
furi_crash(TAG "Invalid view selected");
break;
}
/* Draw the alert box if set. */
ui_draw_alert_if_needed(canvas, app);
}
/* Here all we do is putting the events into the queue that will be handled
@@ -79,17 +82,23 @@ static void input_callback(InputEvent* input_event, void* ctx) {
/* Called to switch view (when left/right is pressed). Handles
* changing the current view ID and calling the enter/exit view
* callbacks if needed. */
static void app_switch_view(ProtoViewApp* app, SwitchViewDirection dir) {
* callbacks if needed.
*
* The 'switchto' parameter can be the identifier of a view, or the
* special views ViewGoNext and ViewGoPrev in order to move to
* the logical next/prev view. */
static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) {
ProtoViewCurrentView old = app->current_view;
if(dir == AppNextView) {
if(switchto == ViewGoNext) {
app->current_view++;
if(app->current_view == ViewLast) app->current_view = 0;
} else if(dir == AppPrevView) {
} else if(switchto == ViewGoPrev) {
if(app->current_view == 0)
app->current_view = ViewLast - 1;
else
app->current_view--;
} else {
app->current_view = switchto;
}
ProtoViewCurrentView new = app->current_view;
@@ -108,6 +117,7 @@ static void app_switch_view(ProtoViewApp* app, SwitchViewDirection dir) {
* the main thing. */
app->current_subview[old] = 0;
memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN);
ui_dismiss_alert(app);
}
/* Allocate the application state and initialize a number of stuff.
@@ -134,6 +144,7 @@ ProtoViewApp* protoview_app_alloc() {
app->view_dispatcher = NULL;
app->text_input = NULL;
app->show_text_input = false;
app->alert_dismiss_time = 0;
app->current_view = ViewRawPulses;
for(int j = 0; j < ViewLast; j++) app->current_subview[j] = 0;
app->direct_sampling_enabled = false;
@@ -266,18 +277,26 @@ int32_t protoview_app_entry(void* p) {
/* Handle navigation here. Then handle view-specific inputs
* in the view specific handling function. */
if(input.type == InputTypeShort && input.key == InputKeyBack) {
/* Exit the app. */
if(app->current_view != ViewRawPulses) {
/* If this is not the main app view, go there. */
app_switch_view(app, ViewRawPulses);
} else {
/* If we are in the main app view, warn the user
* they needs to long press to really quit. */
ui_show_alert(app, "Long press to exit", 1000);
}
} else if(input.type == InputTypeLong && input.key == InputKeyBack) {
app->running = 0;
} else if(
input.type == InputTypeShort && input.key == InputKeyRight &&
get_current_subview(app) == 0) {
ui_get_current_subview(app) == 0) {
/* Go to the next view. */
app_switch_view(app, AppNextView);
app_switch_view(app, ViewGoNext);
} else if(
input.type == InputTypeShort && input.key == InputKeyLeft &&
get_current_subview(app) == 0) {
ui_get_current_subview(app) == 0) {
/* Go to the previous view. */
app_switch_view(app, AppPrevView);
app_switch_view(app, ViewGoPrev);
} else {
/* This is where we pass the control to the currently
* active view input processing. */
@@ -295,8 +314,8 @@ int32_t protoview_app_entry(void* p) {
case ViewDirectSampling:
process_input_direct_sampling(app, input);
break;
case ViewLast:
furi_crash(TAG " ViewLast selected");
default:
furi_crash(TAG "Invalid view selected");
break;
}
}

View File

@@ -48,10 +48,12 @@ typedef enum {
ViewModulationSettings,
ViewDirectSampling,
ViewLast, /* Just a sentinel to wrap around. */
} ProtoViewCurrentView;
/* Used by app_switch_view() */
typedef enum { AppNextView, AppPrevView } SwitchViewDirection;
/* The following are special views that are not iterated, but
* have meaning for the API. */
ViewGoNext,
ViewGoPrev,
} ProtoViewCurrentView;
typedef struct {
const char* name; // Name to show to the user.
@@ -114,6 +116,7 @@ typedef struct ProtoViewMsgInfo {
} ProtoViewMsgInfo;
/* Our main application context. */
#define ALERT_MAX_LEN 32
struct ProtoViewApp {
/* GUI */
Gui* gui;
@@ -123,6 +126,8 @@ struct ProtoViewApp {
ProtoViewCurrentView current_view; /* Active left-right view ID. */
int current_subview[ViewLast]; /* Active up-down subview ID. */
FuriMessageQueue* event_queue; /* Keypress events go here. */
/* Input text state. */
ViewDispatcher* view_dispatcher; /* Used only when we want to show
the text_input view for a moment.
Otherwise it is set to null. */
@@ -132,6 +137,12 @@ struct ProtoViewApp {
uint32_t text_input_buffer_len;
void (*text_input_done_callback)(void*);
/* Alert state. */
uint32_t alert_dismiss_time; /* Millisecond when the alert will be
no longer shown. Or zero if the alert
is currently not set at all. */
char alert_text[ALERT_MAX_LEN]; /* Alert content. */
/* Radio related. */
ProtoViewTxRx* txrx; /* Radio state. */
SubGhzSetting* setting; /* A list of valid frequencies. */
@@ -245,9 +256,18 @@ void view_exit_direct_sampling(ProtoViewApp* app);
void view_exit_settings(ProtoViewApp* app);
/* ui.c */
int get_current_subview(ProtoViewApp* app);
void show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview);
bool process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview);
int ui_get_current_subview(ProtoViewApp* app);
void ui_show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview);
bool ui_process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview);
void ui_show_keyboard(
ProtoViewApp* app,
char* buffer,
uint32_t buflen,
void (*done_callback)(void*));
void ui_dismiss_keyboard(ProtoViewApp* app);
void ui_show_alert(ProtoViewApp* app, const char* text, uint32_t ttl);
void ui_dismiss_alert(ProtoViewApp* app);
void ui_draw_alert_if_needed(Canvas* canvas, ProtoViewApp* app);
void canvas_draw_str_with_border(
Canvas* canvas,
uint8_t x,
@@ -255,8 +275,6 @@ void canvas_draw_str_with_border(
const char* str,
Color text_color,
Color border_color);
void show_keyboard(ProtoViewApp* app, char* buffer, uint32_t buflen, void (*done_callback)(void*));
void dismiss_keyboard(ProtoViewApp* app);
/* crc.c */
uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly);

View File

@@ -10,15 +10,15 @@
/* Return the ID of the currently selected subview, of the current
* view. */
int get_current_subview(ProtoViewApp* app) {
int ui_get_current_subview(ProtoViewApp* app) {
return app->current_subview[app->current_view];
}
/* Called by view rendering callback that has subviews, to show small triangles
* facing down/up if there are other subviews the user can access with up
* and down. */
void show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview) {
int subview = get_current_subview(app);
void ui_show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview) {
int subview = ui_get_current_subview(app);
if(subview != 0) canvas_draw_triangle(canvas, 120, 5, 8, 5, CanvasDirectionBottomToTop);
if(subview != last_subview - 1)
canvas_draw_triangle(canvas, 120, 59, 8, 5, CanvasDirectionTopToBottom);
@@ -27,8 +27,8 @@ void show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview
/* Handle up/down keys when we are in a subview. If the function catched
* such keypress, it returns true, so that the actual view input callback
* knows it can just return ASAP without doing anything. */
bool process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview) {
int subview = get_current_subview(app);
bool ui_process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview) {
int subview = ui_get_current_subview(app);
if(input.type == InputTypePress) {
if(input.key == InputKeyUp) {
if(subview != 0) app->current_subview[app->current_view]--;
@@ -57,17 +57,69 @@ bool process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subvie
*
* Note: if the buffer is not a null-termined zero string, what it contains will
* be used as initial input for the user. */
void show_keyboard(ProtoViewApp* app, char* buffer, uint32_t buflen, void (*done_callback)(void*)) {
void ui_show_keyboard(
ProtoViewApp* app,
char* buffer,
uint32_t buflen,
void (*done_callback)(void*)) {
app->show_text_input = true;
app->text_input_buffer = buffer;
app->text_input_buffer_len = buflen;
app->text_input_done_callback = done_callback;
}
void dismiss_keyboard(ProtoViewApp* app) {
void ui_dismiss_keyboard(ProtoViewApp* app) {
view_dispatcher_stop(app->view_dispatcher);
}
/* ================================= Alert ================================== */
/* Set an alert message to be shown over any currently active view, for
* the specified amount of time of 'ttl' milliseconds. */
void ui_show_alert(ProtoViewApp* app, const char* text, uint32_t ttl) {
app->alert_dismiss_time = furi_get_tick() + furi_ms_to_ticks(ttl);
snprintf(app->alert_text, ALERT_MAX_LEN, "%s", text);
}
/* Cancel the alert before its time has elapsed. */
void ui_dismiss_alert(ProtoViewApp* app) {
app->alert_dismiss_time = 0;
}
/* Show the alert if an alert is set. This is called after the currently
* active view displayed its stuff, so we overwrite the screen with the
* alert message. */
void ui_draw_alert_if_needed(Canvas* canvas, ProtoViewApp* app) {
if(app->alert_dismiss_time == 0) {
/* No active alert. */
return;
} else if(app->alert_dismiss_time < furi_get_tick()) {
/* Alert just expired. */
ui_dismiss_alert(app);
return;
}
/* Show the alert. A box with black border and a text inside. */
canvas_set_font(canvas, FontPrimary);
uint8_t w = canvas_string_width(canvas, app->alert_text);
uint8_t h = 8; // Font height.
uint8_t text_x = 64 - (w / 2);
uint8_t text_y = 32 + 4;
uint8_t padding = 3;
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(
canvas, text_x - padding, text_y - padding - h, w + padding * 2, h + padding * 2);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(
canvas,
text_x - padding + 1,
text_y - padding - h + 1,
w + padding * 2 - 2,
h + padding * 2 - 2);
canvas_set_color(canvas, ColorBlack);
canvas_draw_str(canvas, text_x, text_y, app->alert_text);
}
/* =========================== Canvas extensions ============================ */
void canvas_draw_str_with_border(

View File

@@ -103,7 +103,7 @@ void render_view_info(Canvas* const canvas, ProtoViewApp* app) {
return;
}
show_available_subviews(canvas, app, SubViewInfoLast);
ui_show_available_subviews(canvas, app, SubViewInfoLast);
switch(app->current_subview[app->current_view]) {
case SubViewInfoMain:
render_subview_main(canvas, app);
@@ -126,7 +126,7 @@ void text_input_done_callback(void* context) {
furi_string_free(save_path);
free(privdata->filename);
dismiss_keyboard(app);
ui_dismiss_keyboard(app);
}
/* Replace all the occurrences of character c1 with c2 in the specified
@@ -261,9 +261,12 @@ void notify_signal_sent(ProtoViewApp* app) {
/* Handle input for the info view. */
void process_input_info(ProtoViewApp* app, InputEvent input) {
if(process_subview_updown(app, input, SubViewInfoLast)) return;
/* If we don't have a decoded signal, we don't allow to go up/down
* in the subviews: they are only useful when a loaded signal. */
if(app->signal_decoded && ui_process_subview_updown(app, input, SubViewInfoLast)) return;
InfoViewPrivData* privdata = app->view_privdata;
int subview = get_current_subview(app);
int subview = ui_get_current_subview(app);
/* Main subview. */
if(subview == SubViewInfoMain) {
@@ -280,7 +283,7 @@ void process_input_info(ProtoViewApp* app, InputEvent input) {
} else if(input.type == InputTypeLong && input.key == InputKeyOk) {
privdata->filename = malloc(SAVE_FILENAME_LEN);
set_signal_random_filename(app, privdata->filename, SAVE_FILENAME_LEN);
show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN, text_input_done_callback);
ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN, text_input_done_callback);
} else if(input.type == InputTypeShort && input.key == InputKeyOk) {
SendSignalCtx send_state;
send_signal_init(&send_state, app);