Merge branch 'ul-dev' into xfw-dev
@@ -64,6 +64,7 @@ openocd.log
|
|||||||
# PVS Studio temporary files
|
# PVS Studio temporary files
|
||||||
.PVS-Studio/
|
.PVS-Studio/
|
||||||
PVS-Studio.log
|
PVS-Studio.log
|
||||||
|
*.PVS-Studio.*
|
||||||
|
|
||||||
.gdbinit
|
.gdbinit
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,6 @@ static void avr_isp_app_tick_event_callback(void* context) {
|
|||||||
AvrIspApp* avr_isp_app_alloc() {
|
AvrIspApp* avr_isp_app_alloc() {
|
||||||
AvrIspApp* app = malloc(sizeof(AvrIspApp));
|
AvrIspApp* app = malloc(sizeof(AvrIspApp));
|
||||||
|
|
||||||
// Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering
|
|
||||||
uint8_t attempts = 0;
|
|
||||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
|
||||||
furi_hal_power_enable_otg();
|
|
||||||
furi_delay_ms(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
app->file_path = furi_string_alloc();
|
app->file_path = furi_string_alloc();
|
||||||
furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX);
|
furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX);
|
||||||
app->error = AvrIspErrorNoError;
|
app->error = AvrIspErrorNoError;
|
||||||
@@ -102,6 +95,13 @@ AvrIspApp* avr_isp_app_alloc() {
|
|||||||
AvrIspViewChipDetect,
|
AvrIspViewChipDetect,
|
||||||
avr_isp_chip_detect_view_get_view(app->avr_isp_chip_detect_view));
|
avr_isp_chip_detect_view_get_view(app->avr_isp_chip_detect_view));
|
||||||
|
|
||||||
|
// Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering
|
||||||
|
uint8_t attempts = 0;
|
||||||
|
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||||
|
furi_hal_power_enable_otg();
|
||||||
|
furi_delay_ms(10);
|
||||||
|
}
|
||||||
|
|
||||||
scene_manager_next_scene(app->scene_manager, AvrIspSceneStart);
|
scene_manager_next_scene(app->scene_manager, AvrIspSceneStart);
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
@@ -110,6 +110,11 @@ AvrIspApp* avr_isp_app_alloc() {
|
|||||||
void avr_isp_app_free(AvrIspApp* app) {
|
void avr_isp_app_free(AvrIspApp* app) {
|
||||||
furi_assert(app);
|
furi_assert(app);
|
||||||
|
|
||||||
|
// Disable 5v power
|
||||||
|
if(furi_hal_power_is_otg_enabled()) {
|
||||||
|
furi_hal_power_disable_otg();
|
||||||
|
}
|
||||||
|
|
||||||
// Submenu
|
// Submenu
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewSubmenu);
|
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewSubmenu);
|
||||||
submenu_free(app->submenu);
|
submenu_free(app->submenu);
|
||||||
@@ -159,11 +164,6 @@ void avr_isp_app_free(AvrIspApp* app) {
|
|||||||
// Path strings
|
// Path strings
|
||||||
furi_string_free(app->file_path);
|
furi_string_free(app->file_path);
|
||||||
|
|
||||||
// Disable 5v power
|
|
||||||
if(furi_hal_power_is_otg_enabled()) {
|
|
||||||
furi_hal_power_disable_otg();
|
|
||||||
}
|
|
||||||
|
|
||||||
free(app);
|
free(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,10 +51,10 @@ static void dap_main_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
if(model->dap_active) {
|
if(model->dap_active) {
|
||||||
canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18);
|
canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18);
|
||||||
canvas_draw_icon(canvas, 28, 16, &I_ArrowDownFilled_12x18);
|
canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpFilled_12x18, IconRotation180);
|
||||||
} else {
|
} else {
|
||||||
canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18);
|
canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18);
|
||||||
canvas_draw_icon(canvas, 28, 16, &I_ArrowDownEmpty_12x18);
|
canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpEmpty_12x18, IconRotation180);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(model->mode) {
|
switch(model->mode) {
|
||||||
@@ -76,9 +76,9 @@ static void dap_main_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(model->rx_active) {
|
if(model->rx_active) {
|
||||||
canvas_draw_icon(canvas, 101, 16, &I_ArrowDownFilled_12x18);
|
canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpFilled_12x18, IconRotation180);
|
||||||
} else {
|
} else {
|
||||||
canvas_draw_icon(canvas, 101, 16, &I_ArrowDownEmpty_12x18);
|
canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpEmpty_12x18, IconRotation180);
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART");
|
canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART");
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 160 B |
|
Before Width: | Height: | Size: 168 B |
@@ -80,9 +80,9 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);
|
canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);
|
||||||
|
|
||||||
if(model->rx_active)
|
if(model->rx_active)
|
||||||
canvas_draw_icon(canvas, 48, 34, &I_ArrowDownFilled_14x15);
|
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
|
||||||
else
|
else
|
||||||
canvas_draw_icon(canvas, 48, 34, &I_ArrowDownEmpty_14x15);
|
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
|
static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
App(
|
||||||
|
appid="ir_scope",
|
||||||
|
name="IR Scope",
|
||||||
|
apptype=FlipperAppType.EXTERNAL,
|
||||||
|
entry_point="ir_scope_app",
|
||||||
|
cdefines=["APP_IR_SCOPE"],
|
||||||
|
requires=["gui"],
|
||||||
|
stack_size=2 * 1024,
|
||||||
|
fap_icon="ir_scope.png",
|
||||||
|
fap_category="Tools",
|
||||||
|
)
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
// Author: github.com/kallanreed
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <infrared.h>
|
||||||
|
#include <infrared_worker.h>
|
||||||
|
#include <furi_hal_infrared.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
|
||||||
|
#define TAG "IR Scope"
|
||||||
|
#define COLS 128
|
||||||
|
#define ROWS 8
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool autoscale;
|
||||||
|
uint16_t us_per_sample;
|
||||||
|
size_t timings_cnt;
|
||||||
|
uint32_t* timings;
|
||||||
|
uint32_t timings_sum;
|
||||||
|
FuriMutex* mutex;
|
||||||
|
} IRScopeState;
|
||||||
|
|
||||||
|
static void state_set_autoscale(IRScopeState* state) {
|
||||||
|
if(state->autoscale) state->us_per_sample = state->timings_sum / (ROWS * COLS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void canvas_draw_str_outline(Canvas* canvas, int x, int y, const char* str) {
|
||||||
|
canvas_set_color(canvas, ColorWhite);
|
||||||
|
for(int y1 = -1; y1 <= 1; ++y1)
|
||||||
|
for(int x1 = -1; x1 <= 1; ++x1) canvas_draw_str(canvas, x + x1, y + y1, str);
|
||||||
|
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_draw_str(canvas, x, y, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void render_callback(Canvas* canvas, void* ctx) {
|
||||||
|
const IRScopeState* state = (IRScopeState*)ctx;
|
||||||
|
|
||||||
|
furi_mutex_acquire(state->mutex, FuriWaitForever);
|
||||||
|
|
||||||
|
canvas_clear(canvas);
|
||||||
|
canvas_draw_frame(canvas, 0, 0, 128, 64);
|
||||||
|
|
||||||
|
// Draw the signal chart.
|
||||||
|
bool on = false;
|
||||||
|
bool done = false;
|
||||||
|
size_t ix = 0;
|
||||||
|
int timing_cols = -1; // Count of columns used to draw the current timing
|
||||||
|
for(size_t row = 0; row < ROWS && !done; ++row) {
|
||||||
|
for(size_t col = 0; col < COLS && !done; ++col) {
|
||||||
|
done = ix >= state->timings_cnt;
|
||||||
|
|
||||||
|
if(!done && timing_cols < 0) {
|
||||||
|
timing_cols = state->timings[ix] / state->us_per_sample;
|
||||||
|
on = !on;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(timing_cols == 0) ++ix;
|
||||||
|
|
||||||
|
int y = row * 8 + 7;
|
||||||
|
canvas_draw_line(canvas, col, y, col, y - (on ? 5 : 0));
|
||||||
|
--timing_cols;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
if(state->autoscale)
|
||||||
|
canvas_draw_str_outline(canvas, 100, 64, "Auto");
|
||||||
|
else {
|
||||||
|
char buf[20];
|
||||||
|
snprintf(buf, sizeof(buf), "%uus", state->us_per_sample);
|
||||||
|
canvas_draw_str_outline(canvas, 100, 64, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_mutex_release(state->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||||
|
FuriMessageQueue* event_queue = ctx;
|
||||||
|
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ir_received_callback(void* ctx, InfraredWorkerSignal* signal) {
|
||||||
|
furi_check(signal);
|
||||||
|
IRScopeState* state = (IRScopeState*)ctx;
|
||||||
|
|
||||||
|
furi_mutex_acquire(state->mutex, FuriWaitForever);
|
||||||
|
|
||||||
|
const uint32_t* timings;
|
||||||
|
infrared_worker_get_raw_signal(signal, &timings, &state->timings_cnt);
|
||||||
|
|
||||||
|
if(state->timings) {
|
||||||
|
free(state->timings);
|
||||||
|
state->timings_sum = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->timings = malloc(state->timings_cnt * sizeof(uint32_t));
|
||||||
|
|
||||||
|
// Copy and sum.
|
||||||
|
for(size_t i = 0; i < state->timings_cnt; ++i) {
|
||||||
|
state->timings[i] = timings[i];
|
||||||
|
state->timings_sum += timings[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
state_set_autoscale(state);
|
||||||
|
|
||||||
|
furi_mutex_release(state->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ir_scope_app(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
|
||||||
|
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||||
|
furi_check(event_queue);
|
||||||
|
|
||||||
|
if(furi_hal_infrared_is_busy()) {
|
||||||
|
FURI_LOG_E(TAG, "Infrared is busy.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRScopeState state = {
|
||||||
|
.autoscale = false, .us_per_sample = 200, .timings = NULL, .timings_cnt = 0, .mutex = NULL};
|
||||||
|
state.mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
|
if(!state.mutex) {
|
||||||
|
FURI_LOG_E(TAG, "Cannot create mutex.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewPort* view_port = view_port_alloc();
|
||||||
|
view_port_draw_callback_set(view_port, render_callback, &state);
|
||||||
|
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||||
|
|
||||||
|
Gui* gui = furi_record_open("gui");
|
||||||
|
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||||
|
|
||||||
|
InfraredWorker* worker = infrared_worker_alloc();
|
||||||
|
infrared_worker_rx_enable_signal_decoding(worker, false);
|
||||||
|
infrared_worker_rx_enable_blink_on_receiving(worker, true);
|
||||||
|
infrared_worker_rx_set_received_signal_callback(worker, ir_received_callback, &state);
|
||||||
|
infrared_worker_rx_start(worker);
|
||||||
|
|
||||||
|
InputEvent event;
|
||||||
|
bool processing = true;
|
||||||
|
while(processing &&
|
||||||
|
furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||||
|
if(event.type == InputTypeRelease) {
|
||||||
|
furi_mutex_acquire(state.mutex, FuriWaitForever);
|
||||||
|
|
||||||
|
if(event.key == InputKeyBack) {
|
||||||
|
processing = false;
|
||||||
|
} else if(event.key == InputKeyUp) {
|
||||||
|
state.us_per_sample = MIN(1000, state.us_per_sample + 25);
|
||||||
|
state.autoscale = false;
|
||||||
|
} else if(event.key == InputKeyDown) {
|
||||||
|
state.us_per_sample = MAX(25, state.us_per_sample - 25);
|
||||||
|
state.autoscale = false;
|
||||||
|
} else if(event.key == InputKeyOk) {
|
||||||
|
state.autoscale = !state.autoscale;
|
||||||
|
if(state.autoscale)
|
||||||
|
state_set_autoscale(&state);
|
||||||
|
else
|
||||||
|
state.us_per_sample = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
view_port_update(view_port);
|
||||||
|
furi_mutex_release(state.mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up.
|
||||||
|
infrared_worker_rx_stop(worker);
|
||||||
|
infrared_worker_free(worker);
|
||||||
|
|
||||||
|
if(state.timings) free(state.timings);
|
||||||
|
|
||||||
|
view_port_enabled_set(view_port, false);
|
||||||
|
gui_remove_view_port(gui, view_port);
|
||||||
|
furi_record_close("gui");
|
||||||
|
view_port_free(view_port);
|
||||||
|
furi_message_queue_free(event_queue);
|
||||||
|
furi_mutex_free(state.mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 169 B |
@@ -386,17 +386,35 @@ bool subghz_remote_key_load(
|
|||||||
FURI_LOG_E(TAG, "Could not read Protocol.");
|
FURI_LOG_E(TAG, "Could not read Protocol.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!furi_string_cmp_str(preset->protocol, "RAW")) {
|
if(!furi_string_cmp_str(preset->protocol, "RAW")) {
|
||||||
subghz_protocol_raw_gen_fff_data(fff_data, path);
|
subghz_protocol_raw_gen_fff_data(fff_data, path);
|
||||||
|
// repeat
|
||||||
|
if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &preset->repeat, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to insert or update Repeat");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_rewind(fff_data)) {
|
||||||
|
FURI_LOG_E(TAG, "Rewind error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
stream_copy_full(
|
stream_copy_full(
|
||||||
flipper_format_get_raw_stream(fff_file), flipper_format_get_raw_stream(fff_data));
|
flipper_format_get_raw_stream(fff_file), flipper_format_get_raw_stream(fff_data));
|
||||||
|
// repeat
|
||||||
|
if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &preset->repeat, 1)) {
|
||||||
|
FURI_LOG_E(TAG, "Unable to insert or update Repeat");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!flipper_format_rewind(fff_data)) {
|
||||||
|
FURI_LOG_E(TAG, "Rewind error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// repeat
|
if(!flipper_format_rewind(fff_file)) {
|
||||||
if(!flipper_format_insert_or_update_uint32(fff_file, "Repeat", &preset->repeat, 1)) {
|
FURI_LOG_E(TAG, "Rewind error");
|
||||||
FURI_LOG_E(TAG, "Unable to insert or update Repeat");
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
preset->decoder = subghz_receiver_search_decoder_base_by_name(
|
preset->decoder = subghz_receiver_search_decoder_base_by_name(
|
||||||
@@ -435,9 +453,6 @@ bool subghz_remote_save_protocol_to_file(FlipperFormat* fff_file, const char* de
|
|||||||
|
|
||||||
path_extract_dirname(dev_file_name, file_dir);
|
path_extract_dirname(dev_file_name, file_dir);
|
||||||
do {
|
do {
|
||||||
flipper_format_delete_key(fff_file, "Repeat");
|
|
||||||
//flipper_format_delete_key(fff_file, "Manufacture");
|
|
||||||
|
|
||||||
if(!storage_simply_mkdir(storage, furi_string_get_cstr(file_dir))) {
|
if(!storage_simply_mkdir(storage, furi_string_get_cstr(file_dir))) {
|
||||||
FURI_LOG_E(TAG, "(save) Cannot mkdir");
|
FURI_LOG_E(TAG, "(save) Cannot mkdir");
|
||||||
break;
|
break;
|
||||||
@@ -475,14 +490,18 @@ void subghz_remote_tx_stop(SubGHzRemote* app) {
|
|||||||
//FURI_LOG_I(TAG, "TX Done!");
|
//FURI_LOG_I(TAG, "TX Done!");
|
||||||
subghz_transmitter_stop(app->tx_transmitter);
|
subghz_transmitter_stop(app->tx_transmitter);
|
||||||
|
|
||||||
FURI_LOG_D(TAG, "Checking if protocol is dynamic");
|
//FURI_LOG_D(TAG, "Checking if protocol is dynamic");
|
||||||
const SubGhzProtocolRegistry* protocol_registry_items =
|
const SubGhzProtocolRegistry* protocol_registry_items =
|
||||||
subghz_environment_get_protocol_registry(app->environment);
|
subghz_environment_get_protocol_registry(app->environment);
|
||||||
const SubGhzProtocol* proto = subghz_protocol_registry_get_by_name(
|
const SubGhzProtocol* proto = subghz_protocol_registry_get_by_name(
|
||||||
protocol_registry_items, furi_string_get_cstr(app->txpreset->protocol));
|
protocol_registry_items, furi_string_get_cstr(app->txpreset->protocol));
|
||||||
FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type);
|
//FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type);
|
||||||
|
|
||||||
if(proto && proto->type == SubGhzProtocolTypeDynamic) {
|
if(proto && proto->type == SubGhzProtocolTypeDynamic) {
|
||||||
FURI_LOG_D(TAG, "Protocol is dynamic. Saving key");
|
//FURI_LOG_D(TAG, "Protocol is dynamic. Saving key");
|
||||||
|
// Remove repeat if it was present
|
||||||
|
flipper_format_delete_key(app->tx_fff_data, "Repeat");
|
||||||
|
|
||||||
subghz_remote_save_protocol_to_file(app->tx_fff_data, app->tx_file_path);
|
subghz_remote_save_protocol_to_file(app->tx_fff_data, app->tx_file_path);
|
||||||
|
|
||||||
keeloq_reset_mfname();
|
keeloq_reset_mfname();
|
||||||
@@ -682,15 +701,15 @@ static void render_callback(Canvas* canvas, void* ctx) {
|
|||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
||||||
canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_down_7x9);
|
canvas_draw_icon_ex(canvas, 116, 17, &I_Pin_arrow_up_7x9, IconRotation180);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
||||||
canvas_draw_icon(canvas, 115, 18, &I_Pin_arrow_right_9x7);
|
canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation90);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
||||||
canvas_draw_icon(canvas, 115, 18, &I_Pin_arrow_left_9x7);
|
canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation270);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ App(
|
|||||||
name="base32",
|
name="base32",
|
||||||
),
|
),
|
||||||
Lib(
|
Lib(
|
||||||
name="list",
|
name="base64",
|
||||||
|
),
|
||||||
|
Lib(
|
||||||
|
name="linked_list"
|
||||||
),
|
),
|
||||||
Lib(
|
Lib(
|
||||||
name="timezone_utils",
|
name="timezone_utils",
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input) {
|
|||||||
} else if(c == CliSymbolAsciiETX) {
|
} else if(c == CliSymbolAsciiETX) {
|
||||||
cli_nl();
|
cli_nl();
|
||||||
return false;
|
return false;
|
||||||
} else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
|
} else if(
|
||||||
|
(c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||||
|
c == '/' || c == '=' || c == '+') {
|
||||||
if(mask_user_input) {
|
if(mask_user_input) {
|
||||||
putc('*', stdout);
|
putc('*', stdout);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,24 +14,13 @@
|
|||||||
#define DOCOPT_OPTIONS "[options]"
|
#define DOCOPT_OPTIONS "[options]"
|
||||||
#define DOCOPT_DEFAULT(val) "[default: " val "]"
|
#define DOCOPT_DEFAULT(val) "[default: " val "]"
|
||||||
|
|
||||||
#define TOTP_CLI_PRINTF(format, ...) \
|
#define TOTP_CLI_PRINTF(format, ...) printf(format, ##__VA_ARGS__)
|
||||||
do { \
|
|
||||||
_Pragma(STRINGIFY(GCC diagnostic push)) \
|
|
||||||
_Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \
|
|
||||||
printf(format, ##__VA_ARGS__); \
|
|
||||||
_Pragma(STRINGIFY(GCC diagnostic pop)) \
|
|
||||||
} while(false)
|
|
||||||
|
|
||||||
#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \
|
#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \
|
||||||
do { \
|
printf("\e[%s", color); \
|
||||||
_Pragma(STRINGIFY(GCC diagnostic push)) \
|
printf(format, ##__VA_ARGS__); \
|
||||||
_Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \
|
printf("\e[0m"); \
|
||||||
printf("\e[%s", color); \
|
fflush(stdout)
|
||||||
printf(format, ##__VA_ARGS__); \
|
|
||||||
printf("\e[0m"); \
|
|
||||||
fflush(stdout); \
|
|
||||||
_Pragma(STRINGIFY(GCC diagnostic pop)) \
|
|
||||||
} while(false)
|
|
||||||
|
|
||||||
#define TOTP_CLI_COLOR_ERROR "91m"
|
#define TOTP_CLI_COLOR_ERROR "91m"
|
||||||
#define TOTP_CLI_COLOR_WARNING "93m"
|
#define TOTP_CLI_COLOR_WARNING "93m"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "add.h"
|
#include "add.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include "../../../lib/list/list.h"
|
#include <linked_list.h>
|
||||||
#include "../../../types/token_info.h"
|
#include "../../../types/token_info.h"
|
||||||
#include "../../../services/config/config.h"
|
#include "../../../services/config/config.h"
|
||||||
#include "../../../services/convert/convert.h"
|
#include "../../../services/convert/convert.h"
|
||||||
@@ -17,11 +17,11 @@ void totp_cli_command_add_docopt_commands() {
|
|||||||
void totp_cli_command_add_docopt_usage() {
|
void totp_cli_command_add_docopt_usage() {
|
||||||
TOTP_CLI_PRINTF(
|
TOTP_CLI_PRINTF(
|
||||||
" " TOTP_CLI_COMMAND_NAME
|
" " TOTP_CLI_COMMAND_NAME
|
||||||
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_ADD " | " TOTP_CLI_COMMAND_ADD_ALT " | " TOTP_CLI_COMMAND_ADD_ALT2) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(
|
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_ADD " | " TOTP_CLI_COMMAND_ADD_ALT " | " TOTP_CLI_COMMAND_ADD_ALT2) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING))) " " DOCOPT_OPTIONAL(
|
||||||
DOCOPT_OPTION(
|
DOCOPT_OPTION(
|
||||||
TOTP_CLI_COMMAND_ARG_DURATION_PREFIX,
|
TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
|
||||||
DOCOPT_ARGUMENT(
|
DOCOPT_ARGUMENT(
|
||||||
TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n");
|
TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_cli_command_add_docopt_arguments() {
|
void totp_cli_command_add_docopt_arguments() {
|
||||||
@@ -35,11 +35,21 @@ void totp_cli_command_add_docopt_options() {
|
|||||||
TOTP_CLI_COMMAND_ARG_ALGO)) " Token hashing algorithm. Must be one of: " TOTP_TOKEN_ALGO_SHA1_NAME
|
TOTP_CLI_COMMAND_ARG_ALGO)) " Token hashing algorithm. Must be one of: " TOTP_TOKEN_ALGO_SHA1_NAME
|
||||||
", " TOTP_TOKEN_ALGO_SHA256_NAME
|
", " TOTP_TOKEN_ALGO_SHA256_NAME
|
||||||
", " TOTP_TOKEN_ALGO_SHA512_NAME
|
", " TOTP_TOKEN_ALGO_SHA512_NAME
|
||||||
|
", " TOTP_TOKEN_ALGO_STEAM_NAME
|
||||||
" " DOCOPT_DEFAULT(TOTP_TOKEN_ALGO_SHA1_NAME) "\r\n");
|
" " DOCOPT_DEFAULT(TOTP_TOKEN_ALGO_SHA1_NAME) "\r\n");
|
||||||
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
|
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
|
||||||
TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
|
TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
|
||||||
DOCOPT_ARGUMENT(
|
DOCOPT_ARGUMENT(
|
||||||
TOTP_CLI_COMMAND_ARG_DIGITS)) " Number of digits to generate, one of: 6, 8 " DOCOPT_DEFAULT("6") "\r\n");
|
TOTP_CLI_COMMAND_ARG_DIGITS)) " Number of digits to generate, one of: 5, 6, 8 " DOCOPT_DEFAULT("6") "\r\n");
|
||||||
|
|
||||||
|
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
|
||||||
|
TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX,
|
||||||
|
DOCOPT_ARGUMENT(
|
||||||
|
TOTP_CLI_COMMAND_ARG_SECRET_ENCODING)) " Token secret encoding, one of " PLAIN_TOKEN_ENCODING_BASE32_NAME
|
||||||
|
", " PLAIN_TOKEN_ENCODING_BASE64_NAME
|
||||||
|
" " DOCOPT_DEFAULT(
|
||||||
|
PLAIN_TOKEN_ENCODING_BASE32_NAME) "\r\n");
|
||||||
|
|
||||||
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
|
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
|
||||||
TOTP_CLI_COMMAND_ARG_DURATION_PREFIX,
|
TOTP_CLI_COMMAND_ARG_DURATION_PREFIX,
|
||||||
DOCOPT_ARGUMENT(
|
DOCOPT_ARGUMENT(
|
||||||
@@ -83,13 +93,16 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
|
|||||||
|
|
||||||
// Read optional arguments
|
// Read optional arguments
|
||||||
bool mask_user_input = true;
|
bool mask_user_input = true;
|
||||||
|
PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32;
|
||||||
while(args_read_string_and_trim(args, temp_str)) {
|
while(args_read_string_and_trim(args, temp_str)) {
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
if(!totp_cli_try_read_algo(token_info, temp_str, args, &parsed) &&
|
if(!totp_cli_try_read_algo(token_info, temp_str, args, &parsed) &&
|
||||||
!totp_cli_try_read_digits(token_info, temp_str, args, &parsed) &&
|
!totp_cli_try_read_digits(token_info, temp_str, args, &parsed) &&
|
||||||
!totp_cli_try_read_duration(token_info, temp_str, args, &parsed) &&
|
!totp_cli_try_read_duration(token_info, temp_str, args, &parsed) &&
|
||||||
!totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) &&
|
!totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) &&
|
||||||
!totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed)) {
|
!totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) &&
|
||||||
|
!totp_cli_try_read_plain_token_secret_encoding(
|
||||||
|
temp_str, args, &parsed, &token_secret_encoding)) {
|
||||||
totp_cli_printf_unknown_argument(temp_str);
|
totp_cli_printf_unknown_argument(temp_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,31 +128,34 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
|
|||||||
|
|
||||||
TOTP_CLI_DELETE_LAST_LINE();
|
TOTP_CLI_DELETE_LAST_LINE();
|
||||||
|
|
||||||
if(!token_info_set_secret(
|
|
||||||
token_info,
|
|
||||||
furi_string_get_cstr(temp_str),
|
|
||||||
furi_string_size(temp_str),
|
|
||||||
plugin_state->iv)) {
|
|
||||||
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
|
|
||||||
furi_string_secure_free(temp_str);
|
|
||||||
token_info_free(token_info);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
furi_string_secure_free(temp_str);
|
|
||||||
|
|
||||||
bool load_generate_token_scene = false;
|
bool load_generate_token_scene = false;
|
||||||
if(plugin_state->current_scene == TotpSceneGenerateToken) {
|
if(plugin_state->current_scene == TotpSceneGenerateToken) {
|
||||||
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
|
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
|
||||||
load_generate_token_scene = true;
|
load_generate_token_scene = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check);
|
bool secret_set = token_info_set_secret(
|
||||||
plugin_state->tokens_count++;
|
token_info,
|
||||||
if(totp_config_file_save_new_token(token_info) == TotpConfigFileUpdateSuccess) {
|
furi_string_get_cstr(temp_str),
|
||||||
TOTP_CLI_PRINTF_SUCCESS("Token \"%s\" has been successfully added\r\n", token_info->name);
|
furi_string_size(temp_str),
|
||||||
|
token_secret_encoding,
|
||||||
|
plugin_state->iv);
|
||||||
|
|
||||||
|
furi_string_secure_free(temp_str);
|
||||||
|
|
||||||
|
if(secret_set) {
|
||||||
|
TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check);
|
||||||
|
plugin_state->tokens_count++;
|
||||||
|
|
||||||
|
if(totp_config_file_save_new_token(token_info) == TotpConfigFileUpdateSuccess) {
|
||||||
|
TOTP_CLI_PRINTF_SUCCESS(
|
||||||
|
"Token \"%s\" has been successfully added\r\n", token_info->name);
|
||||||
|
} else {
|
||||||
|
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
|
token_info_free(token_info);
|
||||||
|
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(load_generate_token_scene) {
|
if(load_generate_token_scene) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include "../../../lib/list/list.h"
|
#include <linked_list.h>
|
||||||
#include "../../../services/config/config.h"
|
#include "../../../services/config/config.h"
|
||||||
#include "../../cli_helpers.h"
|
#include "../../cli_helpers.h"
|
||||||
#include "../../../ui/scene_director.h"
|
#include "../../../ui/scene_director.h"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "details.h"
|
#include "details.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include "../../../lib/list/list.h"
|
#include <linked_list.h>
|
||||||
#include "../../../types/token_info.h"
|
#include "../../../types/token_info.h"
|
||||||
#include "../../../services/config/constants.h"
|
#include "../../../services/config/constants.h"
|
||||||
#include "../../cli_helpers.h"
|
#include "../../cli_helpers.h"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "../../../lib/list/list.h"
|
#include <linked_list.h>
|
||||||
#include "../../../types/token_info.h"
|
#include "../../../types/token_info.h"
|
||||||
#include "../../../services/config/constants.h"
|
#include "../../../services/config/constants.h"
|
||||||
#include "../../cli_helpers.h"
|
#include "../../cli_helpers.h"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include "../../../lib/list/list.h"
|
#include <linked_list.h>
|
||||||
#include "../../../types/token_info.h"
|
#include "../../../types/token_info.h"
|
||||||
#include "../../../services/config/config.h"
|
#include "../../../services/config/config.h"
|
||||||
#include "../../cli_helpers.h"
|
#include "../../cli_helpers.h"
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
|
#include <linked_list.h>
|
||||||
#include "../../../types/token_info.h"
|
#include "../../../types/token_info.h"
|
||||||
#include "../../../types/user_pin_codes.h"
|
#include "../../../types/user_pin_codes.h"
|
||||||
#include "../../../services/config/config.h"
|
#include "../../../services/config/config.h"
|
||||||
#include "../../cli_helpers.h"
|
#include "../../cli_helpers.h"
|
||||||
#include "../../../lib/polyfills/memset_s.h"
|
#include <memset_s.h>
|
||||||
#include "../../../services/crypto/crypto.h"
|
#include "../../../services/crypto/crypto.h"
|
||||||
#include "../../../ui/scene_director.h"
|
#include "../../../ui/scene_director.h"
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg
|
|||||||
if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) {
|
if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) {
|
||||||
plugin_state->timezone_offset = tz;
|
plugin_state->timezone_offset = tz;
|
||||||
if(totp_config_file_update_timezone_offset(tz) == TotpConfigFileUpdateSuccess) {
|
if(totp_config_file_update_timezone_offset(tz) == TotpConfigFileUpdateSuccess) {
|
||||||
TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", tz);
|
TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", (double)tz);
|
||||||
} else {
|
} else {
|
||||||
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
|
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,8 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg
|
|||||||
TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n");
|
TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TOTP_CLI_PRINTF_INFO("Current timezone offset is %f\r\n", plugin_state->timezone_offset);
|
TOTP_CLI_PRINTF_INFO(
|
||||||
|
"Current timezone offset is %f\r\n", (double)plugin_state->timezone_offset);
|
||||||
}
|
}
|
||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "update.h"
|
#include "update.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include "../../../lib/list/list.h"
|
#include <linked_list.h>
|
||||||
#include "../../../types/token_info.h"
|
#include "../../../types/token_info.h"
|
||||||
#include "../../../services/config/config.h"
|
#include "../../../services/config/config.h"
|
||||||
#include "../../../services/convert/convert.h"
|
#include "../../../services/convert/convert.h"
|
||||||
@@ -18,17 +18,20 @@ void totp_cli_command_update_docopt_commands() {
|
|||||||
void totp_cli_command_update_docopt_usage() {
|
void totp_cli_command_update_docopt_usage() {
|
||||||
TOTP_CLI_PRINTF(
|
TOTP_CLI_PRINTF(
|
||||||
" " TOTP_CLI_COMMAND_NAME
|
" " TOTP_CLI_COMMAND_NAME
|
||||||
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_UPDATE) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_NAME_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME))) " " DOCOPT_OPTIONAL(
|
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_UPDATE) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_NAME_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME))) " " DOCOPT_OPTIONAL(
|
||||||
DOCOPT_OPTION(
|
DOCOPT_OPTION(
|
||||||
TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
|
TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
|
||||||
DOCOPT_ARGUMENT(
|
DOCOPT_ARGUMENT(
|
||||||
TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n");
|
TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void totp_cli_command_update_docopt_options() {
|
void totp_cli_command_update_docopt_options() {
|
||||||
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
|
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
|
||||||
TOTP_CLI_COMMAND_ARG_NAME_PREFIX,
|
TOTP_CLI_COMMAND_ARG_NAME_PREFIX,
|
||||||
DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME)) " Token name\r\n");
|
DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME)) " Token name\r\n");
|
||||||
|
|
||||||
|
TOTP_CLI_PRINTF(" " DOCOPT_SWITCH(
|
||||||
|
TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) " Update token secret\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@@ -85,6 +88,7 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args,
|
|||||||
// Read optional arguments
|
// Read optional arguments
|
||||||
bool mask_user_input = true;
|
bool mask_user_input = true;
|
||||||
bool update_token_secret = false;
|
bool update_token_secret = false;
|
||||||
|
PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32;
|
||||||
while(args_read_string_and_trim(args, temp_str)) {
|
while(args_read_string_and_trim(args, temp_str)) {
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
if(!totp_cli_try_read_name(token_info, temp_str, args, &parsed) &&
|
if(!totp_cli_try_read_name(token_info, temp_str, args, &parsed) &&
|
||||||
@@ -93,7 +97,9 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args,
|
|||||||
!totp_cli_try_read_duration(token_info, temp_str, args, &parsed) &&
|
!totp_cli_try_read_duration(token_info, temp_str, args, &parsed) &&
|
||||||
!totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) &&
|
!totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) &&
|
||||||
!totp_cli_try_read_change_secret_flag(temp_str, &parsed, &update_token_secret) &&
|
!totp_cli_try_read_change_secret_flag(temp_str, &parsed, &update_token_secret) &&
|
||||||
!totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed)) {
|
!totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) &&
|
||||||
|
!totp_cli_try_read_plain_token_secret_encoding(
|
||||||
|
temp_str, args, &parsed, &token_secret_encoding)) {
|
||||||
totp_cli_printf_unknown_argument(temp_str);
|
totp_cli_printf_unknown_argument(temp_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,54 +111,55 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool token_secret_read = false;
|
||||||
if(update_token_secret) {
|
if(update_token_secret) {
|
||||||
// Reading token secret
|
// Reading token secret
|
||||||
furi_string_reset(temp_str);
|
furi_string_reset(temp_str);
|
||||||
TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n");
|
TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n");
|
||||||
if(!totp_cli_read_line(cli, temp_str, mask_user_input) ||
|
token_secret_read = totp_cli_read_line(cli, temp_str, mask_user_input);
|
||||||
!totp_cli_ensure_authenticated(plugin_state, cli)) {
|
|
||||||
TOTP_CLI_DELETE_LAST_LINE();
|
|
||||||
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
|
|
||||||
furi_string_secure_free(temp_str);
|
|
||||||
token_info_free(token_info);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TOTP_CLI_DELETE_LAST_LINE();
|
TOTP_CLI_DELETE_LAST_LINE();
|
||||||
|
if(!token_secret_read) {
|
||||||
if(token_info->token != NULL) {
|
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
|
||||||
free(token_info->token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!token_info_set_secret(
|
|
||||||
token_info,
|
|
||||||
furi_string_get_cstr(temp_str),
|
|
||||||
furi_string_size(temp_str),
|
|
||||||
plugin_state->iv)) {
|
|
||||||
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
|
|
||||||
furi_string_secure_free(temp_str);
|
|
||||||
token_info_free(token_info);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_string_secure_free(temp_str);
|
|
||||||
|
|
||||||
bool load_generate_token_scene = false;
|
bool load_generate_token_scene = false;
|
||||||
if(plugin_state->current_scene == TotpSceneGenerateToken) {
|
if(plugin_state->current_scene == TotpSceneGenerateToken) {
|
||||||
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
|
totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
|
||||||
load_generate_token_scene = true;
|
load_generate_token_scene = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_item->data = token_info;
|
bool token_secret_set = false;
|
||||||
|
if(update_token_secret && token_secret_read) {
|
||||||
|
if(token_info->token != NULL) {
|
||||||
|
free(token_info->token);
|
||||||
|
}
|
||||||
|
token_secret_set = token_info_set_secret(
|
||||||
|
token_info,
|
||||||
|
furi_string_get_cstr(temp_str),
|
||||||
|
furi_string_size(temp_str),
|
||||||
|
token_secret_encoding,
|
||||||
|
plugin_state->iv);
|
||||||
|
if(!token_secret_set) {
|
||||||
|
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) {
|
furi_string_secure_free(temp_str);
|
||||||
TOTP_CLI_PRINTF_SUCCESS(
|
|
||||||
"Token \"%s\" has been successfully updated\r\n", token_info->name);
|
if(!update_token_secret || (token_secret_read && token_secret_set)) {
|
||||||
token_info_free(existing_token_info);
|
list_item->data = token_info;
|
||||||
|
|
||||||
|
if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) {
|
||||||
|
TOTP_CLI_PRINTF_SUCCESS(
|
||||||
|
"Token \"%s\" has been successfully updated\r\n", token_info->name);
|
||||||
|
token_info_free(existing_token_info);
|
||||||
|
} else {
|
||||||
|
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
|
||||||
|
list_item->data = existing_token_info;
|
||||||
|
token_info_free(token_info);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
|
|
||||||
list_item->data = existing_token_info;
|
|
||||||
token_info_free(token_info);
|
token_info_free(token_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,5 +108,34 @@ bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool*
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool totp_cli_try_read_plain_token_secret_encoding(
|
||||||
|
FuriString* arg,
|
||||||
|
FuriString* args,
|
||||||
|
bool* parsed,
|
||||||
|
PlainTokenSecretEncoding* secret_encoding) {
|
||||||
|
if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX) == 0) {
|
||||||
|
if(!args_read_string_and_trim(args, arg)) {
|
||||||
|
totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX);
|
||||||
|
} else {
|
||||||
|
if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE32_NAME) == 0) {
|
||||||
|
*secret_encoding = PLAIN_TOKEN_ENCODING_BASE32;
|
||||||
|
*parsed = true;
|
||||||
|
} else if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE64_NAME) == 0) {
|
||||||
|
*secret_encoding = PLAIN_TOKEN_ENCODING_BASE64;
|
||||||
|
*parsed = true;
|
||||||
|
} else {
|
||||||
|
TOTP_CLI_PRINTF_ERROR(
|
||||||
|
"\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX
|
||||||
|
"\"\r\n",
|
||||||
|
furi_string_get_cstr(arg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,8 @@
|
|||||||
#define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX "-b"
|
#define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX "-b"
|
||||||
#define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE "feature"
|
#define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE "feature"
|
||||||
#define TOTP_CLI_COMMAND_ARG_INDEX "index"
|
#define TOTP_CLI_COMMAND_ARG_INDEX "index"
|
||||||
|
#define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX "-e"
|
||||||
|
#define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING "encoding"
|
||||||
|
|
||||||
void totp_cli_printf_unknown_argument(const FuriString* arg);
|
void totp_cli_printf_unknown_argument(const FuriString* arg);
|
||||||
void totp_cli_printf_missed_argument_value(char* arg);
|
void totp_cli_printf_missed_argument_value(char* arg);
|
||||||
@@ -34,4 +36,10 @@ bool totp_cli_try_read_automation_features(
|
|||||||
FuriString* arg,
|
FuriString* arg,
|
||||||
FuriString* args,
|
FuriString* args,
|
||||||
bool* parsed);
|
bool* parsed);
|
||||||
bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag);
|
bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag);
|
||||||
|
|
||||||
|
bool totp_cli_try_read_plain_token_secret_encoding(
|
||||||
|
FuriString* arg,
|
||||||
|
FuriString* args,
|
||||||
|
bool* parsed,
|
||||||
|
PlainTokenSecretEncoding* secret_encoding);
|
||||||
@@ -1,2 +1,14 @@
|
|||||||
|
// Include Bluetooth token input automation
|
||||||
#define TOTP_BADBT_TYPE_ENABLED
|
#define TOTP_BADBT_TYPE_ENABLED
|
||||||
#define TOTP_AUTOMATION_ICONS_ENABLED
|
|
||||||
|
// Include token input automation icons on the main screen
|
||||||
|
#define TOTP_AUTOMATION_ICONS_ENABLED
|
||||||
|
|
||||||
|
// List of compatible firmwares
|
||||||
|
#define TOTP_FIRMWARE_OFFICIAL_STABLE 1
|
||||||
|
#define TOTP_FIRMWARE_OFFICIAL_DEV 2
|
||||||
|
#define TOTP_FIRMWARE_XTREME 3
|
||||||
|
// End of list
|
||||||
|
|
||||||
|
// Target firmware to build for
|
||||||
|
#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
#include "base32.h"
|
#include "base32.h"
|
||||||
|
|
||||||
int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) {
|
size_t base32_decode(const uint8_t* encoded, uint8_t* result, size_t bufSize) {
|
||||||
int buffer = 0;
|
int buffer = 0;
|
||||||
int bitsLeft = 0;
|
int bitsLeft = 0;
|
||||||
int count = 0;
|
size_t count = 0;
|
||||||
for(const uint8_t* ptr = encoded; count < bufSize && *ptr; ++ptr) {
|
for(const uint8_t* ptr = encoded; count < bufSize && *ptr; ++ptr) {
|
||||||
uint8_t ch = *ptr;
|
uint8_t ch = *ptr;
|
||||||
if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') {
|
if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') {
|
||||||
@@ -43,7 +43,7 @@ int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) {
|
|||||||
} else if(ch >= '2' && ch <= '7') {
|
} else if(ch >= '2' && ch <= '7') {
|
||||||
ch -= '2' - 26;
|
ch -= '2' - 26;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer |= ch;
|
buffer |= ch;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,6 +35,6 @@
|
|||||||
* @param encoded Base-32 encoded bytes
|
* @param encoded Base-32 encoded bytes
|
||||||
* @param[out] result result output buffer
|
* @param[out] result result output buffer
|
||||||
* @param bufSize result output buffer size
|
* @param bufSize result output buffer size
|
||||||
* @return Decoded result length in bytes if successfully decoded; \c -1 otherwise
|
* @return Decoded result length in bytes if successfully decoded; \c 0 otherwise
|
||||||
*/
|
*/
|
||||||
int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize);
|
size_t base32_decode(const uint8_t* encoded, uint8_t* result, size_t bufSize);
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Base64 encoding/decoding (RFC1341)
|
||||||
|
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Alternatively, this software may be distributed under the terms of BSD
|
||||||
|
* license.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const uint8_t dtable[] = {0x3e, 0x80, 0x80, 0x80, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38,
|
||||||
|
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x0, 0x80,
|
||||||
|
0x80, 0x80, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
|
||||||
|
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11,
|
||||||
|
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80,
|
||||||
|
0x80, 0x80, 0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||||
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
|
||||||
|
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33};
|
||||||
|
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
static uint8_t get_dtable_value(uint8_t index) {
|
||||||
|
return (index < 43 || index > 122) ? 0x80 : dtable[index - 43];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len, size_t* out_size) {
|
||||||
|
uint8_t *out;
|
||||||
|
uint8_t *pos;
|
||||||
|
uint8_t in[4];
|
||||||
|
uint8_t block[4];
|
||||||
|
uint8_t tmp;
|
||||||
|
size_t i;
|
||||||
|
size_t count;
|
||||||
|
size_t olen;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
if(get_dtable_value(src[i]) != 0x80) count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count == 0 || count % 4) return NULL;
|
||||||
|
olen = count / 4 * 3;
|
||||||
|
pos = out = malloc(olen);
|
||||||
|
*out_size = olen;
|
||||||
|
if(out == NULL) return NULL;
|
||||||
|
count = 0;
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
tmp = get_dtable_value(src[i]);
|
||||||
|
if(tmp == 0x80) continue;
|
||||||
|
in[count] = src[i];
|
||||||
|
block[count] = tmp;
|
||||||
|
count++;
|
||||||
|
if(count == 4) {
|
||||||
|
*pos++ = (block[0] << 2) | (block[1] >> 4);
|
||||||
|
*pos++ = (block[1] << 4) | (block[2] >> 2);
|
||||||
|
*pos++ = (block[2] << 6) | block[3];
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(pos > out) {
|
||||||
|
if(in[2] == '=')
|
||||||
|
pos -= 2;
|
||||||
|
else if(in[3] == '=')
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
*out_len = pos - out;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decodes Base-64 encoded bytes into plain bytes.
|
||||||
|
* @param src Base-64 encoded bytes
|
||||||
|
* @param len Base-64 encoded bytes count
|
||||||
|
* @param[out] out_len decoded buffer length
|
||||||
|
* @param[out] out_size decoded buffer allocated size
|
||||||
|
* @return Decoded result buffer if successfully decoded; \c NULL otherwise
|
||||||
|
*/
|
||||||
|
uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len, size_t* out_size);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "list.h"
|
#include "linked_list.h"
|
||||||
|
|
||||||
ListNode* list_init_head(void* data) {
|
ListNode* list_init_head(void* data) {
|
||||||
ListNode* new = malloc(sizeof(ListNode));
|
ListNode* new = malloc(sizeof(ListNode));
|
||||||
@@ -84,19 +84,15 @@ ListNode* list_insert_at(ListNode* head, uint16_t index, void* data);
|
|||||||
void list_free(ListNode* head);
|
void list_free(ListNode* head);
|
||||||
|
|
||||||
#define TOTP_LIST_INIT_OR_ADD(head, item, assert) \
|
#define TOTP_LIST_INIT_OR_ADD(head, item, assert) \
|
||||||
do { \
|
if(head == NULL) { \
|
||||||
if(head == NULL) { \
|
head = list_init_head(item); \
|
||||||
head = list_init_head(item); \
|
assert(head != NULL); \
|
||||||
assert(head != NULL); \
|
} else { \
|
||||||
} else { \
|
assert(list_add(head, item) != NULL); \
|
||||||
assert(list_add(head, item) != NULL); \
|
}
|
||||||
} \
|
|
||||||
} while(false)
|
|
||||||
|
|
||||||
#define TOTP_LIST_FOREACH(head, node, action) \
|
#define TOTP_LIST_FOREACH(head, node, action) \
|
||||||
do { \
|
ListNode* node = head; \
|
||||||
ListNode* node = head; \
|
while(node != NULL) { \
|
||||||
while(node != NULL) { \
|
action node = node->next; \
|
||||||
action node = node->next; \
|
}
|
||||||
} \
|
|
||||||
} while(false)
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "../list/list.h"
|
#include <linked_list.h>
|
||||||
#include "../../types/common.h"
|
#include "../../types/common.h"
|
||||||
#include "../../types/token_info.h"
|
#include "../../types/token_info.h"
|
||||||
#include "../../features_config.h"
|
#include "../../features_config.h"
|
||||||
@@ -139,10 +139,11 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF
|
|||||||
|
|
||||||
furi_string_printf(
|
furi_string_printf(
|
||||||
temp_str,
|
temp_str,
|
||||||
" # Token hashing algorithm to use during code generation. Supported options are %s, %s and %s. If you are not use which one to use - use %s",
|
" # Token hashing algorithm to use during code generation. Supported options are %s, %s, %s, and %s. If you are not use which one to use - use %s",
|
||||||
TOTP_TOKEN_ALGO_SHA1_NAME,
|
TOTP_TOKEN_ALGO_SHA1_NAME,
|
||||||
TOTP_TOKEN_ALGO_SHA256_NAME,
|
TOTP_TOKEN_ALGO_SHA256_NAME,
|
||||||
TOTP_TOKEN_ALGO_SHA512_NAME,
|
TOTP_TOKEN_ALGO_SHA512_NAME,
|
||||||
|
TOTP_TOKEN_ALGO_STEAM_NAME,
|
||||||
TOTP_TOKEN_ALGO_SHA1_NAME);
|
TOTP_TOKEN_ALGO_SHA1_NAME);
|
||||||
flipper_format_write_comment(fff_data_file, temp_str);
|
flipper_format_write_comment(fff_data_file, temp_str);
|
||||||
furi_string_printf(
|
furi_string_printf(
|
||||||
@@ -152,7 +153,7 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF
|
|||||||
|
|
||||||
flipper_format_write_comment_cstr(
|
flipper_format_write_comment_cstr(
|
||||||
fff_data_file,
|
fff_data_file,
|
||||||
"# How many digits there should be in generated code. Available options are 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6");
|
"# How many digits there should be in generated code. Available options are 5, 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6");
|
||||||
furi_string_printf(temp_str, "%s: 6", TOTP_CONFIG_KEY_TOKEN_DIGITS);
|
furi_string_printf(temp_str, "%s: 6", TOTP_CONFIG_KEY_TOKEN_DIGITS);
|
||||||
flipper_format_write_comment(fff_data_file, temp_str);
|
flipper_format_write_comment(fff_data_file, temp_str);
|
||||||
flipper_format_write_comment_cstr(fff_data_file, " ");
|
flipper_format_write_comment_cstr(fff_data_file, " ");
|
||||||
@@ -686,6 +687,7 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state)
|
|||||||
tokenInfo,
|
tokenInfo,
|
||||||
furi_string_get_cstr(temp_str),
|
furi_string_get_cstr(temp_str),
|
||||||
furi_string_size(temp_str),
|
furi_string_size(temp_str),
|
||||||
|
PLAIN_TOKEN_ENCODING_BASE32,
|
||||||
&plugin_state->iv[0])) {
|
&plugin_state->iv[0])) {
|
||||||
FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name);
|
FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -26,12 +26,9 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef WORDS_BIGENDIAN
|
|
||||||
#define SWAP(n) (n)
|
|
||||||
#else
|
|
||||||
#include "byteswap.h"
|
#include "byteswap.h"
|
||||||
|
|
||||||
#define SWAP(n) swap_uint64(n)
|
#define SWAP(n) swap_uint64(n)
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This array contains the bytes used to pad the buffer to the next
|
/* This array contains the bytes used to pad the buffer to the next
|
||||||
128-byte boundary. */
|
128-byte boundary. */
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
#include "../hmac/byteswap.h"
|
#include "../hmac/byteswap.h"
|
||||||
#include "../../lib/timezone_utils/timezone_utils.h"
|
#include "../../lib/timezone_utils/timezone_utils.h"
|
||||||
|
|
||||||
#define HMAC_MAX_SIZE 64
|
#define HMAC_MAX_RESULT_SIZE HMAC_SHA512_RESULT_SIZE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generates the timeblock for a time in seconds.
|
* @brief Generates the timeblock for a time in seconds.
|
||||||
@@ -29,19 +29,17 @@ uint64_t totp_timecode(uint8_t interval, uint64_t for_time) {
|
|||||||
/**
|
/**
|
||||||
* @brief Generates an OTP (One Time Password)
|
* @brief Generates an OTP (One Time Password)
|
||||||
* @param algo hashing algorithm to be used
|
* @param algo hashing algorithm to be used
|
||||||
* @param digits desired TOTP code length
|
|
||||||
* @param plain_secret plain token secret
|
* @param plain_secret plain token secret
|
||||||
* @param plain_secret_length plain token secret length
|
* @param plain_secret_length plain token secret length
|
||||||
* @param input input data for OTP code generation
|
* @param input input data for OTP code generation
|
||||||
* @return OTP code if code was successfully generated; 0 otherwise
|
* @return OTP code if code was successfully generated; 0 otherwise
|
||||||
*/
|
*/
|
||||||
uint32_t otp_generate(
|
uint64_t otp_generate(
|
||||||
TOTP_ALGO algo,
|
TOTP_ALGO algo,
|
||||||
uint8_t digits,
|
|
||||||
const uint8_t* plain_secret,
|
const uint8_t* plain_secret,
|
||||||
size_t plain_secret_length,
|
size_t plain_secret_length,
|
||||||
uint64_t input) {
|
uint64_t input) {
|
||||||
uint8_t hmac[HMAC_MAX_SIZE] = {0};
|
uint8_t hmac[HMAC_MAX_RESULT_SIZE] = {0};
|
||||||
|
|
||||||
uint64_t input_swapped = swap_uint64(input);
|
uint64_t input_swapped = swap_uint64(input);
|
||||||
|
|
||||||
@@ -55,14 +53,12 @@ uint32_t otp_generate(
|
|||||||
uint64_t i_code =
|
uint64_t i_code =
|
||||||
((hmac[offset] & 0x7F) << 24 | (hmac[offset + 1] & 0xFF) << 16 |
|
((hmac[offset] & 0x7F) << 24 | (hmac[offset + 1] & 0xFF) << 16 |
|
||||||
(hmac[offset + 2] & 0xFF) << 8 | (hmac[offset + 3] & 0xFF));
|
(hmac[offset + 2] & 0xFF) << 8 | (hmac[offset + 3] & 0xFF));
|
||||||
i_code %= (uint64_t)pow(10, digits);
|
|
||||||
|
|
||||||
return i_code;
|
return i_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t totp_at(
|
uint64_t totp_at(
|
||||||
TOTP_ALGO algo,
|
TOTP_ALGO algo,
|
||||||
uint8_t digits,
|
|
||||||
const uint8_t* plain_secret,
|
const uint8_t* plain_secret,
|
||||||
size_t plain_secret_length,
|
size_t plain_secret_length,
|
||||||
uint64_t for_time,
|
uint64_t for_time,
|
||||||
@@ -71,11 +67,7 @@ uint32_t totp_at(
|
|||||||
uint64_t for_time_adjusted =
|
uint64_t for_time_adjusted =
|
||||||
timezone_offset_apply(for_time, timezone_offset_from_hours(timezone));
|
timezone_offset_apply(for_time, timezone_offset_from_hours(timezone));
|
||||||
return otp_generate(
|
return otp_generate(
|
||||||
algo,
|
algo, plain_secret, plain_secret_length, totp_timecode(interval, for_time_adjusted));
|
||||||
digits,
|
|
||||||
plain_secret,
|
|
||||||
plain_secret_length,
|
|
||||||
totp_timecode(interval, for_time_adjusted));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int totp_algo_sha1(
|
static int totp_algo_sha1(
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ extern const TOTP_ALGO TOTP_ALGO_SHA512;
|
|||||||
/**
|
/**
|
||||||
* @brief Generates a OTP key using the totp algorithm.
|
* @brief Generates a OTP key using the totp algorithm.
|
||||||
* @param algo hashing algorithm to be used
|
* @param algo hashing algorithm to be used
|
||||||
* @param digits desired TOTP code length
|
|
||||||
* @param plain_secret plain token secret
|
* @param plain_secret plain token secret
|
||||||
* @param plain_secret_length plain token secret length
|
* @param plain_secret_length plain token secret length
|
||||||
* @param for_time the time the generated key will be created for
|
* @param for_time the time the generated key will be created for
|
||||||
@@ -47,9 +46,8 @@ extern const TOTP_ALGO TOTP_ALGO_SHA512;
|
|||||||
* @param interval token lifetime in seconds
|
* @param interval token lifetime in seconds
|
||||||
* @return TOTP code if code was successfully generated; 0 otherwise
|
* @return TOTP code if code was successfully generated; 0 otherwise
|
||||||
*/
|
*/
|
||||||
uint32_t totp_at(
|
uint64_t totp_at(
|
||||||
TOTP_ALGO algo,
|
TOTP_ALGO algo,
|
||||||
uint8_t digits,
|
|
||||||
const uint8_t* plain_secret,
|
const uint8_t* plain_secret,
|
||||||
size_t plain_secret_length,
|
size_t plain_secret_length,
|
||||||
uint64_t for_time,
|
uint64_t for_time,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <dialogs/dialogs.h>
|
#include <dialogs/dialogs.h>
|
||||||
#include "../features_config.h"
|
#include "../features_config.h"
|
||||||
#include "../lib/list/list.h"
|
#include <linked_list.h>
|
||||||
#include "../ui/totp_scenes_enum.h"
|
#include "../ui/totp_scenes_enum.h"
|
||||||
#include "notification_method.h"
|
#include "notification_method.h"
|
||||||
#include "automation_method.h"
|
#include "automation_method.h"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#include <furi_hal.h>
|
|
||||||
#include "token_info.h"
|
#include "token_info.h"
|
||||||
#include "stdlib.h"
|
#include <furi_hal.h>
|
||||||
|
#include <base32.h>
|
||||||
|
#include <base64.h>
|
||||||
|
#include <memset_s.h>
|
||||||
|
#include <strnlen.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "../lib/base32/base32.h"
|
|
||||||
#include "../services/crypto/crypto.h"
|
#include "../services/crypto/crypto.h"
|
||||||
#include "../lib/polyfills/memset_s.h"
|
|
||||||
#include "../lib/polyfills/strnlen.h"
|
|
||||||
|
|
||||||
TokenInfo* token_info_alloc() {
|
TokenInfo* token_info_alloc() {
|
||||||
TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));
|
TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));
|
||||||
@@ -26,15 +26,32 @@ void token_info_free(TokenInfo* token_info) {
|
|||||||
|
|
||||||
bool token_info_set_secret(
|
bool token_info_set_secret(
|
||||||
TokenInfo* token_info,
|
TokenInfo* token_info,
|
||||||
const char* base32_token_secret,
|
const char* plain_token_secret,
|
||||||
size_t token_secret_length,
|
size_t token_secret_length,
|
||||||
|
PlainTokenSecretEncoding plain_token_secret_encoding,
|
||||||
const uint8_t* iv) {
|
const uint8_t* iv) {
|
||||||
if(token_secret_length == 0) return false;
|
if(token_secret_length == 0) return false;
|
||||||
|
uint8_t* plain_secret;
|
||||||
|
size_t plain_secret_length;
|
||||||
|
size_t plain_secret_size;
|
||||||
|
if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE32) {
|
||||||
|
plain_secret_size = token_secret_length;
|
||||||
|
plain_secret = malloc(plain_secret_size);
|
||||||
|
furi_check(plain_secret != NULL);
|
||||||
|
plain_secret_length =
|
||||||
|
base32_decode((const uint8_t*)plain_token_secret, plain_secret, plain_secret_size);
|
||||||
|
} else if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE64) {
|
||||||
|
plain_secret_length = 0;
|
||||||
|
plain_secret = base64_decode(
|
||||||
|
(const uint8_t*)plain_token_secret,
|
||||||
|
token_secret_length,
|
||||||
|
&plain_secret_length,
|
||||||
|
&plain_secret_size);
|
||||||
|
furi_check(plain_secret != NULL);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* plain_secret = malloc(token_secret_length);
|
|
||||||
furi_check(plain_secret != NULL);
|
|
||||||
int plain_secret_length =
|
|
||||||
base32_decode((const uint8_t*)base32_token_secret, plain_secret, token_secret_length);
|
|
||||||
bool result;
|
bool result;
|
||||||
if(plain_secret_length > 0) {
|
if(plain_secret_length > 0) {
|
||||||
token_info->token =
|
token_info->token =
|
||||||
@@ -44,13 +61,16 @@ bool token_info_set_secret(
|
|||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset_s(plain_secret, token_secret_length, 0, token_secret_length);
|
memset_s(plain_secret, plain_secret_size, 0, plain_secret_size);
|
||||||
free(plain_secret);
|
free(plain_secret);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) {
|
bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) {
|
||||||
switch(digits) {
|
switch(digits) {
|
||||||
|
case 5:
|
||||||
|
token_info->digits = TOTP_5_DIGITS;
|
||||||
|
return true;
|
||||||
case 6:
|
case 6:
|
||||||
token_info->digits = TOTP_6_DIGITS;
|
token_info->digits = TOTP_6_DIGITS;
|
||||||
return true;
|
return true;
|
||||||
@@ -89,6 +109,11 @@ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(furi_string_cmpi_str(str, TOTP_TOKEN_ALGO_STEAM_NAME) == 0) {
|
||||||
|
token_info->algo = STEAM;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +125,8 @@ char* token_info_get_algo_as_cstr(const TokenInfo* token_info) {
|
|||||||
return TOTP_TOKEN_ALGO_SHA256_NAME;
|
return TOTP_TOKEN_ALGO_SHA256_NAME;
|
||||||
case SHA512:
|
case SHA512:
|
||||||
return TOTP_TOKEN_ALGO_SHA512_NAME;
|
return TOTP_TOKEN_ALGO_SHA512_NAME;
|
||||||
|
case STEAM:
|
||||||
|
return TOTP_TOKEN_ALGO_STEAM_NAME;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,14 @@
|
|||||||
#define TOTP_TOKEN_DURATION_DEFAULT 30
|
#define TOTP_TOKEN_DURATION_DEFAULT 30
|
||||||
|
|
||||||
#define TOTP_TOKEN_ALGO_SHA1_NAME "sha1"
|
#define TOTP_TOKEN_ALGO_SHA1_NAME "sha1"
|
||||||
|
#define TOTP_TOKEN_ALGO_STEAM_NAME "steam"
|
||||||
#define TOTP_TOKEN_ALGO_SHA256_NAME "sha256"
|
#define TOTP_TOKEN_ALGO_SHA256_NAME "sha256"
|
||||||
#define TOTP_TOKEN_ALGO_SHA512_NAME "sha512"
|
#define TOTP_TOKEN_ALGO_SHA512_NAME "sha512"
|
||||||
#define TOTP_TOKEN_MAX_LENGTH 255
|
#define TOTP_TOKEN_MAX_LENGTH 255
|
||||||
|
|
||||||
|
#define PLAIN_TOKEN_ENCODING_BASE32_NAME "base32"
|
||||||
|
#define PLAIN_TOKEN_ENCODING_BASE64_NAME "base64"
|
||||||
|
|
||||||
#define TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME "none"
|
#define TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME "none"
|
||||||
#define TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME "enter"
|
#define TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME "enter"
|
||||||
#define TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME "tab"
|
#define TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME "tab"
|
||||||
@@ -19,6 +23,7 @@
|
|||||||
typedef uint8_t TokenHashAlgo;
|
typedef uint8_t TokenHashAlgo;
|
||||||
typedef uint8_t TokenDigitsCount;
|
typedef uint8_t TokenDigitsCount;
|
||||||
typedef uint8_t TokenAutomationFeature;
|
typedef uint8_t TokenAutomationFeature;
|
||||||
|
typedef uint8_t PlainTokenSecretEncoding;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Hashing algorithm to be used to generate token
|
* @brief Hashing algorithm to be used to generate token
|
||||||
@@ -37,13 +42,23 @@ enum TokenHashAlgos {
|
|||||||
/**
|
/**
|
||||||
* @brief SHA512 hashing algorithm
|
* @brief SHA512 hashing algorithm
|
||||||
*/
|
*/
|
||||||
SHA512
|
SHA512,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Algorithm used by Steam (Valve)
|
||||||
|
*/
|
||||||
|
STEAM
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Token digits count to be generated.
|
* @brief Token digits count to be generated.
|
||||||
*/
|
*/
|
||||||
enum TokenDigitsCounts {
|
enum TokenDigitsCounts {
|
||||||
|
/**
|
||||||
|
* @brief 6 digits
|
||||||
|
*/
|
||||||
|
TOTP_5_DIGITS = 5,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 6 digits
|
* @brief 6 digits
|
||||||
*/
|
*/
|
||||||
@@ -80,6 +95,11 @@ enum TokenAutomationFeatures {
|
|||||||
TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100
|
TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum PlainTokenSecretEncodings {
|
||||||
|
PLAIN_TOKEN_ENCODING_BASE32 = 0,
|
||||||
|
PLAIN_TOKEN_ENCODING_BASE64 = 1
|
||||||
|
};
|
||||||
|
|
||||||
#define TOTP_TOKEN_DIGITS_MAX_COUNT 8
|
#define TOTP_TOKEN_DIGITS_MAX_COUNT 8
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -139,13 +159,15 @@ void token_info_free(TokenInfo* token_info);
|
|||||||
* @param token_info instance where secret should be updated
|
* @param token_info instance where secret should be updated
|
||||||
* @param base32_token_secret plain token secret in Base32 format
|
* @param base32_token_secret plain token secret in Base32 format
|
||||||
* @param token_secret_length plain token secret length
|
* @param token_secret_length plain token secret length
|
||||||
|
* @param plain_token_secret_encoding plain token secret encoding
|
||||||
* @param iv initialization vecor (IV) to be used for encryption
|
* @param iv initialization vecor (IV) to be used for encryption
|
||||||
* @return \c true if token successfully set; \c false otherwise
|
* @return \c true if token successfully set; \c false otherwise
|
||||||
*/
|
*/
|
||||||
bool token_info_set_secret(
|
bool token_info_set_secret(
|
||||||
TokenInfo* token_info,
|
TokenInfo* token_info,
|
||||||
const char* base32_token_secret,
|
const char* plain_token_secret,
|
||||||
size_t token_secret_length,
|
size_t token_secret_length,
|
||||||
|
PlainTokenSecretEncoding plain_token_secret_encoding,
|
||||||
const uint8_t* iv);
|
const uint8_t* iv);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* GENERATED BY https://github.com/pavius/the-dot-factory */
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// This structure describes a single character's display information
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t width; // width, in bits (or pixels), of the character
|
||||||
|
const uint16_t
|
||||||
|
offset; // offset of the character's bitmap, in bytes, into the the FONT_INFO's data array
|
||||||
|
|
||||||
|
} FONT_CHAR_INFO;
|
||||||
|
|
||||||
|
// Describes a single font
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t height; // height, in pages (8 pixels), of the font's characters
|
||||||
|
const uint8_t startChar; // the first character in the font (e.g. in charInfo and data)
|
||||||
|
const uint8_t endChar; // the last character in the font
|
||||||
|
const uint8_t spacePixels; // number of pixels that a space character takes up
|
||||||
|
const FONT_CHAR_INFO* charInfo; // pointer to array of char information
|
||||||
|
const uint8_t* data; // pointer to generated array of character visual representation
|
||||||
|
|
||||||
|
} FONT_INFO;
|
||||||
@@ -0,0 +1,940 @@
|
|||||||
|
#include "mode_nine.h"
|
||||||
|
|
||||||
|
/* GENERATED BY https://github.com/pavius/the-dot-factory */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Font data for ModeNine 15pt
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Character bitmaps for ModeNine 15pt */
|
||||||
|
const uint8_t modeNine_15ptBitmaps[] = {
|
||||||
|
/* @0 '-' (10 pixels wide) */
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @28 '0' (10 pixels wide) */
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x33,
|
||||||
|
0x03,
|
||||||
|
0x33,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @56 '1' (10 pixels wide) */
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x38,
|
||||||
|
0x00,
|
||||||
|
0x3C,
|
||||||
|
0x00,
|
||||||
|
0x3C,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @84 '2' (10 pixels wide) */
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x80,
|
||||||
|
0x03,
|
||||||
|
0xFC,
|
||||||
|
0x01,
|
||||||
|
0xFE,
|
||||||
|
0x00,
|
||||||
|
0x07,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
|
||||||
|
/* @112 '3' (10 pixels wide) */
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0x80,
|
||||||
|
0x03,
|
||||||
|
0xC0,
|
||||||
|
0x01,
|
||||||
|
0xE0,
|
||||||
|
0x00,
|
||||||
|
0x70,
|
||||||
|
0x00,
|
||||||
|
0xF8,
|
||||||
|
0x00,
|
||||||
|
0xFC,
|
||||||
|
0x01,
|
||||||
|
0x80,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @140 '4' (10 pixels wide) */
|
||||||
|
0xE0,
|
||||||
|
0x00,
|
||||||
|
0xF0,
|
||||||
|
0x00,
|
||||||
|
0xF8,
|
||||||
|
0x00,
|
||||||
|
0xDC,
|
||||||
|
0x00,
|
||||||
|
0xCE,
|
||||||
|
0x00,
|
||||||
|
0xC7,
|
||||||
|
0x00,
|
||||||
|
0xC3,
|
||||||
|
0x00,
|
||||||
|
0xC3,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xC0,
|
||||||
|
0x00,
|
||||||
|
0xC0,
|
||||||
|
0x00,
|
||||||
|
0xC0,
|
||||||
|
0x00,
|
||||||
|
0xC0,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @168 '5' (10 pixels wide) */
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0x80,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @196 '6' (10 pixels wide) */
|
||||||
|
0xF0,
|
||||||
|
0x00,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0x0E,
|
||||||
|
0x00,
|
||||||
|
0x06,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @224 '7' (10 pixels wide) */
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x80,
|
||||||
|
0x01,
|
||||||
|
0xC0,
|
||||||
|
0x01,
|
||||||
|
0xE0,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x18,
|
||||||
|
0x00,
|
||||||
|
0x1C,
|
||||||
|
0x00,
|
||||||
|
0x0C,
|
||||||
|
0x00,
|
||||||
|
0x0C,
|
||||||
|
0x00,
|
||||||
|
0x0C,
|
||||||
|
0x00,
|
||||||
|
0x0C,
|
||||||
|
0x00,
|
||||||
|
0x0C,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @252 '8' (10 pixels wide) */
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @280 '9' (10 pixels wide) */
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x07,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x03,
|
||||||
|
0xFC,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x80,
|
||||||
|
0x01,
|
||||||
|
0xC0,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0x3C,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @308 'B' (10 pixels wide) */
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @336 'C' (10 pixels wide) */
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @364 'D' (10 pixels wide) */
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @392 'F' (10 pixels wide) */
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @420 'G' (10 pixels wide) */
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0xC3,
|
||||||
|
0x03,
|
||||||
|
0xC3,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x07,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x03,
|
||||||
|
0xFC,
|
||||||
|
0x03,
|
||||||
|
|
||||||
|
/* @448 'H' (10 pixels wide) */
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
|
||||||
|
/* @476 'J' (10 pixels wide) */
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @504 'K' (10 pixels wide) */
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0xC3,
|
||||||
|
0x01,
|
||||||
|
0xE3,
|
||||||
|
0x00,
|
||||||
|
0x73,
|
||||||
|
0x00,
|
||||||
|
0x3B,
|
||||||
|
0x00,
|
||||||
|
0x1F,
|
||||||
|
0x00,
|
||||||
|
0x0F,
|
||||||
|
0x00,
|
||||||
|
0x0F,
|
||||||
|
0x00,
|
||||||
|
0x1F,
|
||||||
|
0x00,
|
||||||
|
0x3B,
|
||||||
|
0x00,
|
||||||
|
0x73,
|
||||||
|
0x00,
|
||||||
|
0xE3,
|
||||||
|
0x00,
|
||||||
|
0xC3,
|
||||||
|
0x01,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
|
||||||
|
/* @532 'M' (10 pixels wide) */
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xCF,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0x7B,
|
||||||
|
0x03,
|
||||||
|
0x33,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
|
||||||
|
/* @560 'N' (10 pixels wide) */
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x07,
|
||||||
|
0x03,
|
||||||
|
0x0F,
|
||||||
|
0x03,
|
||||||
|
0x1F,
|
||||||
|
0x03,
|
||||||
|
0x3B,
|
||||||
|
0x03,
|
||||||
|
0x73,
|
||||||
|
0x03,
|
||||||
|
0xE3,
|
||||||
|
0x03,
|
||||||
|
0xC3,
|
||||||
|
0x03,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
|
||||||
|
/* @588 'P' (10 pixels wide) */
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @616 'Q' (10 pixels wide) */
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x33,
|
||||||
|
0x03,
|
||||||
|
0x73,
|
||||||
|
0x03,
|
||||||
|
0xE7,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x03,
|
||||||
|
|
||||||
|
/* @644 'R' (10 pixels wide) */
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0x1F,
|
||||||
|
0x00,
|
||||||
|
0x3B,
|
||||||
|
0x00,
|
||||||
|
0x73,
|
||||||
|
0x00,
|
||||||
|
0xE3,
|
||||||
|
0x00,
|
||||||
|
0xC3,
|
||||||
|
0x01,
|
||||||
|
0x83,
|
||||||
|
0x03,
|
||||||
|
|
||||||
|
/* @672 'T' (10 pixels wide) */
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @700 'V' (10 pixels wide) */
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x86,
|
||||||
|
0x01,
|
||||||
|
0x86,
|
||||||
|
0x01,
|
||||||
|
0xCC,
|
||||||
|
0x00,
|
||||||
|
0xCC,
|
||||||
|
0x00,
|
||||||
|
0x78,
|
||||||
|
0x00,
|
||||||
|
0x78,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
|
||||||
|
/* @728 'W' (10 pixels wide) */
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x33,
|
||||||
|
0x03,
|
||||||
|
0x33,
|
||||||
|
0x03,
|
||||||
|
0x33,
|
||||||
|
0x03,
|
||||||
|
0x33,
|
||||||
|
0x03,
|
||||||
|
0x33,
|
||||||
|
0x03,
|
||||||
|
0x33,
|
||||||
|
0x03,
|
||||||
|
0xFF,
|
||||||
|
0x03,
|
||||||
|
0xFE,
|
||||||
|
0x01,
|
||||||
|
|
||||||
|
/* @756 'X' (10 pixels wide) */
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xCE,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0xCE,
|
||||||
|
0x01,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
|
||||||
|
/* @784 'Y' (10 pixels wide) */
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x87,
|
||||||
|
0x03,
|
||||||
|
0xCE,
|
||||||
|
0x01,
|
||||||
|
0xFC,
|
||||||
|
0x00,
|
||||||
|
0x78,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Character descriptors for ModeNine 15pt */
|
||||||
|
/* { [Char width in bits], [Offset into modeNine_15ptCharBitmaps in bytes] } */
|
||||||
|
const FONT_CHAR_INFO modeNine_15ptDescriptors[] = {
|
||||||
|
{10, 0}, /* - */
|
||||||
|
{0, 0}, /* . */
|
||||||
|
{0, 0}, /* / */
|
||||||
|
{10, 28}, /* 0 */
|
||||||
|
{10, 56}, /* 1 */
|
||||||
|
{10, 84}, /* 2 */
|
||||||
|
{10, 112}, /* 3 */
|
||||||
|
{10, 140}, /* 4 */
|
||||||
|
{10, 168}, /* 5 */
|
||||||
|
{10, 196}, /* 6 */
|
||||||
|
{10, 224}, /* 7 */
|
||||||
|
{10, 252}, /* 8 */
|
||||||
|
{10, 280}, /* 9 */
|
||||||
|
{0, 0}, /* : */
|
||||||
|
{0, 0}, /* ; */
|
||||||
|
{0, 0}, /* < */
|
||||||
|
{0, 0}, /* = */
|
||||||
|
{0, 0}, /* > */
|
||||||
|
{0, 0}, /* ? */
|
||||||
|
{0, 0}, /* @ */
|
||||||
|
{0, 0}, /* A */
|
||||||
|
{10, 308}, /* B */
|
||||||
|
{10, 336}, /* C */
|
||||||
|
{10, 364}, /* D */
|
||||||
|
{0, 0}, /* E */
|
||||||
|
{10, 392}, /* F */
|
||||||
|
{10, 420}, /* G */
|
||||||
|
{10, 448}, /* H */
|
||||||
|
{0, 0}, /* I */
|
||||||
|
{10, 476}, /* J */
|
||||||
|
{10, 504}, /* K */
|
||||||
|
{0, 0}, /* L */
|
||||||
|
{10, 532}, /* M */
|
||||||
|
{10, 560}, /* N */
|
||||||
|
{0, 0}, /* O */
|
||||||
|
{10, 588}, /* P */
|
||||||
|
{10, 616}, /* Q */
|
||||||
|
{10, 644}, /* R */
|
||||||
|
{0, 0}, /* S */
|
||||||
|
{10, 672}, /* T */
|
||||||
|
{0, 0}, /* U */
|
||||||
|
{10, 700}, /* V */
|
||||||
|
{10, 728}, /* W */
|
||||||
|
{10, 756}, /* X */
|
||||||
|
{10, 784}, /* Y */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Font information for ModeNine 15pt */
|
||||||
|
const FONT_INFO modeNine_15ptFontInfo = {
|
||||||
|
14, /* Character height */
|
||||||
|
'-', /* Start character */
|
||||||
|
'Y', /* End character */
|
||||||
|
2, /* Width, in pixels, of space character */
|
||||||
|
modeNine_15ptDescriptors, /* Character descriptor array */
|
||||||
|
modeNine_15ptBitmaps, /* Character bitmap array */
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* GENERATED BY https://github.com/pavius/the-dot-factory */
|
||||||
|
|
||||||
|
#include "../font_info.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Font data for ModeNine 15pt */
|
||||||
|
extern const FONT_INFO modeNine_15ptFontInfo;
|
||||||
@@ -4,17 +4,17 @@
|
|||||||
#include "../../scene_director.h"
|
#include "../../scene_director.h"
|
||||||
#include "totp_input_text.h"
|
#include "totp_input_text.h"
|
||||||
#include "../../../types/token_info.h"
|
#include "../../../types/token_info.h"
|
||||||
#include "../../../lib/list/list.h"
|
#include <linked_list.h>
|
||||||
#include "../../../services/config/config.h"
|
#include "../../../services/config/config.h"
|
||||||
#include "../../ui_controls.h"
|
#include "../../ui_controls.h"
|
||||||
#include "../../common_dialogs.h"
|
#include "../../common_dialogs.h"
|
||||||
#include "../../../lib/roll_value/roll_value.h"
|
#include <roll_value.h>
|
||||||
#include "../../../types/nullable.h"
|
#include "../../../types/nullable.h"
|
||||||
#include "../generate_token/totp_scene_generate_token.h"
|
#include "../generate_token/totp_scene_generate_token.h"
|
||||||
|
|
||||||
char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512"};
|
char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512", "Steam"};
|
||||||
char* TOKEN_DIGITS_TEXT_LIST[] = {"6 digits", "8 digits"};
|
char* TOKEN_DIGITS_TEXT_LIST[] = {"5 digits", "6 digits", "8 digits"};
|
||||||
TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_6_DIGITS, TOTP_8_DIGITS};
|
TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_5_DIGITS, TOTP_6_DIGITS, TOTP_8_DIGITS};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TokenNameTextBox,
|
TokenNameTextBox,
|
||||||
@@ -95,6 +95,8 @@ void totp_scene_add_new_token_activate(
|
|||||||
|
|
||||||
scene_state->screen_y_offset = 0;
|
scene_state->screen_y_offset = 0;
|
||||||
|
|
||||||
|
scene_state->digits_count_index = 1;
|
||||||
|
|
||||||
scene_state->input_state = NULL;
|
scene_state->input_state = NULL;
|
||||||
scene_state->duration = TOTP_TOKEN_DURATION_DEFAULT;
|
scene_state->duration = TOTP_TOKEN_DURATION_DEFAULT;
|
||||||
scene_state->duration_text = furi_string_alloc();
|
scene_state->duration_text = furi_string_alloc();
|
||||||
@@ -216,10 +218,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
|
|||||||
break;
|
break;
|
||||||
case InputKeyRight:
|
case InputKeyRight:
|
||||||
if(scene_state->selected_control == TokenAlgoSelect) {
|
if(scene_state->selected_control == TokenAlgoSelect) {
|
||||||
totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, SHA512, RollOverflowBehaviorRoll);
|
totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, STEAM, RollOverflowBehaviorRoll);
|
||||||
} else if(scene_state->selected_control == TokenLengthSelect) {
|
} else if(scene_state->selected_control == TokenLengthSelect) {
|
||||||
totp_roll_value_uint8_t(
|
totp_roll_value_uint8_t(
|
||||||
&scene_state->digits_count_index, 1, 0, 1, RollOverflowBehaviorRoll);
|
&scene_state->digits_count_index, 1, 0, 2, RollOverflowBehaviorRoll);
|
||||||
} else if(scene_state->selected_control == TokenDurationSelect) {
|
} else if(scene_state->selected_control == TokenDurationSelect) {
|
||||||
totp_roll_value_uint8_t(&scene_state->duration, 15, 15, 255, RollOverflowBehaviorStop);
|
totp_roll_value_uint8_t(&scene_state->duration, 15, 15, 255, RollOverflowBehaviorStop);
|
||||||
update_duration_text(scene_state);
|
update_duration_text(scene_state);
|
||||||
@@ -227,11 +229,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
|
|||||||
break;
|
break;
|
||||||
case InputKeyLeft:
|
case InputKeyLeft:
|
||||||
if(scene_state->selected_control == TokenAlgoSelect) {
|
if(scene_state->selected_control == TokenAlgoSelect) {
|
||||||
totp_roll_value_uint8_t(
|
totp_roll_value_uint8_t(&scene_state->algo, -1, SHA1, STEAM, RollOverflowBehaviorRoll);
|
||||||
&scene_state->algo, -1, SHA1, SHA512, RollOverflowBehaviorRoll);
|
|
||||||
} else if(scene_state->selected_control == TokenLengthSelect) {
|
} else if(scene_state->selected_control == TokenLengthSelect) {
|
||||||
totp_roll_value_uint8_t(
|
totp_roll_value_uint8_t(
|
||||||
&scene_state->digits_count_index, -1, 0, 1, RollOverflowBehaviorRoll);
|
&scene_state->digits_count_index, -1, 0, 2, RollOverflowBehaviorRoll);
|
||||||
} else if(scene_state->selected_control == TokenDurationSelect) {
|
} else if(scene_state->selected_control == TokenDurationSelect) {
|
||||||
totp_roll_value_uint8_t(
|
totp_roll_value_uint8_t(
|
||||||
&scene_state->duration, -15, 15, 255, RollOverflowBehaviorStop);
|
&scene_state->duration, -15, 15, 255, RollOverflowBehaviorStop);
|
||||||
@@ -268,6 +269,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
|
|||||||
tokenInfo,
|
tokenInfo,
|
||||||
scene_state->token_secret,
|
scene_state->token_secret,
|
||||||
scene_state->token_secret_length,
|
scene_state->token_secret_length,
|
||||||
|
PLAIN_TOKEN_ENCODING_BASE32,
|
||||||
&plugin_state->iv[0]);
|
&plugin_state->iv[0]);
|
||||||
|
|
||||||
if(token_secret_set) {
|
if(token_secret_set) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "../../constants.h"
|
#include "../../constants.h"
|
||||||
#include "../../../services/config/config.h"
|
#include "../../../services/config/config.h"
|
||||||
#include "../../../services/convert/convert.h"
|
#include "../../../services/convert/convert.h"
|
||||||
#include "../../../lib/roll_value/roll_value.h"
|
#include <roll_value.h>
|
||||||
#include "../../../types/nullable.h"
|
#include "../../../types/nullable.h"
|
||||||
#include "../../../features_config.h"
|
#include "../../../features_config.h"
|
||||||
#ifdef TOTP_BADBT_TYPE_ENABLED
|
#ifdef TOTP_BADBT_TYPE_ENABLED
|
||||||
|
|||||||
@@ -19,7 +19,9 @@
|
|||||||
#ifdef TOTP_BADBT_TYPE_ENABLED
|
#ifdef TOTP_BADBT_TYPE_ENABLED
|
||||||
#include "../../../workers/bt_type_code/bt_type_code.h"
|
#include "../../../workers/bt_type_code/bt_type_code.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "../../fonts/mode-nine/mode_nine.h"
|
||||||
|
|
||||||
|
static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
|
||||||
static const uint8_t PROGRESS_BAR_MARGIN = 3;
|
static const uint8_t PROGRESS_BAR_MARGIN = 3;
|
||||||
static const uint8_t PROGRESS_BAR_HEIGHT = 4;
|
static const uint8_t PROGRESS_BAR_HEIGHT = 4;
|
||||||
|
|
||||||
@@ -121,13 +123,21 @@ static const NotificationSequence*
|
|||||||
return (NotificationSequence*)scene_state->notification_sequence_badusb;
|
return (NotificationSequence*)scene_state->notification_sequence_badusb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount len) {
|
static void
|
||||||
|
int_token_to_str(uint64_t i_token_code, char* str, TokenDigitsCount len, TokenHashAlgo algo) {
|
||||||
if(i_token_code == OTP_ERROR) {
|
if(i_token_code == OTP_ERROR) {
|
||||||
memset(&str[0], '-', len);
|
memset(&str[0], '-', len);
|
||||||
} else {
|
} else {
|
||||||
for(int i = len - 1; i >= 0; i--) {
|
if(algo == STEAM) {
|
||||||
str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10);
|
for(uint8_t i = 0; i < len; i++) {
|
||||||
i_token_code = i_token_code / 10;
|
str[i] = STEAM_ALGO_ALPHABET[i_token_code % 26];
|
||||||
|
i_token_code = i_token_code / 26;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(int8_t i = len - 1; i >= 0; i--) {
|
||||||
|
str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10);
|
||||||
|
i_token_code = i_token_code / 10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +147,7 @@ static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount
|
|||||||
static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
|
static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) {
|
||||||
switch(algo) {
|
switch(algo) {
|
||||||
case SHA1:
|
case SHA1:
|
||||||
|
case STEAM:
|
||||||
return TOTP_ALGO_SHA1;
|
return TOTP_ALGO_SHA1;
|
||||||
case SHA256:
|
case SHA256:
|
||||||
return TOTP_ALGO_SHA256;
|
return TOTP_ALGO_SHA256;
|
||||||
@@ -161,6 +172,27 @@ static void update_totp_params(PluginState* const plugin_state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_state) {
|
||||||
|
uint8_t code_length = scene_state->current_token->digits;
|
||||||
|
uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width;
|
||||||
|
uint8_t total_length = code_length * (char_width + modeNine_15ptFontInfo.spacePixels);
|
||||||
|
uint8_t offset_x = (SCREEN_WIDTH - total_length) >> 1;
|
||||||
|
uint8_t offset_y = SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1);
|
||||||
|
for(uint8_t i = 0; i < code_length; i++) {
|
||||||
|
char ch = scene_state->last_code[i];
|
||||||
|
uint8_t char_index = ch - modeNine_15ptFontInfo.startChar;
|
||||||
|
canvas_draw_xbm(
|
||||||
|
canvas,
|
||||||
|
offset_x,
|
||||||
|
offset_y,
|
||||||
|
char_width,
|
||||||
|
modeNine_15ptFontInfo.height,
|
||||||
|
&modeNine_15ptFontInfo.data[modeNine_15ptFontInfo.charInfo[char_index].offset]);
|
||||||
|
|
||||||
|
offset_x += char_width + modeNine_15ptFontInfo.spacePixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void totp_scene_generate_token_init(const PluginState* plugin_state) {
|
void totp_scene_generate_token_init(const PluginState* plugin_state) {
|
||||||
UNUSED(plugin_state);
|
UNUSED(plugin_state);
|
||||||
}
|
}
|
||||||
@@ -274,19 +306,19 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
|
|||||||
int_token_to_str(
|
int_token_to_str(
|
||||||
totp_at(
|
totp_at(
|
||||||
get_totp_algo_impl(tokenInfo->algo),
|
get_totp_algo_impl(tokenInfo->algo),
|
||||||
tokenInfo->digits,
|
|
||||||
key,
|
key,
|
||||||
key_length,
|
key_length,
|
||||||
curr_ts,
|
curr_ts,
|
||||||
plugin_state->timezone_offset,
|
plugin_state->timezone_offset,
|
||||||
tokenInfo->duration),
|
tokenInfo->duration),
|
||||||
scene_state->last_code,
|
scene_state->last_code,
|
||||||
tokenInfo->digits);
|
tokenInfo->digits,
|
||||||
|
tokenInfo->algo);
|
||||||
memset_s(key, key_length, 0, key_length);
|
memset_s(key, key_length, 0, key_length);
|
||||||
free(key);
|
free(key);
|
||||||
} else {
|
} else {
|
||||||
furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
|
furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
|
||||||
int_token_to_str(0, scene_state->last_code, tokenInfo->digits);
|
int_token_to_str(0, scene_state->last_code, tokenInfo->digits, tokenInfo->algo);
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_mutex_release(scene_state->last_code_update_sync);
|
furi_mutex_release(scene_state->last_code_update_sync);
|
||||||
@@ -322,14 +354,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
|
|||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
draw_totp_code(canvas, scene_state);
|
||||||
canvas_draw_str_aligned(
|
|
||||||
canvas,
|
|
||||||
SCREEN_WIDTH_CENTER,
|
|
||||||
SCREEN_HEIGHT_CENTER,
|
|
||||||
AlignCenter,
|
|
||||||
AlignCenter,
|
|
||||||
scene_state->last_code);
|
|
||||||
|
|
||||||
const uint8_t TOKEN_LIFETIME = scene_state->current_token->duration;
|
const uint8_t TOKEN_LIFETIME = scene_state->current_token->duration;
|
||||||
float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME;
|
float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME;
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
#include "../../constants.h"
|
#include "../../constants.h"
|
||||||
#include "../../scene_director.h"
|
#include "../../scene_director.h"
|
||||||
#include "../../../services/config/config.h"
|
#include "../../../services/config/config.h"
|
||||||
#include "../../../lib/list/list.h"
|
#include <linked_list.h>
|
||||||
#include "../../../types/token_info.h"
|
#include "../../../types/token_info.h"
|
||||||
#include "../generate_token/totp_scene_generate_token.h"
|
#include "../generate_token/totp_scene_generate_token.h"
|
||||||
#include "../add_new_token/totp_scene_add_new_token.h"
|
#include "../add_new_token/totp_scene_add_new_token.h"
|
||||||
#include "../app_settings/totp_app_settings.h"
|
#include "../app_settings/totp_app_settings.h"
|
||||||
#include "../../../types/nullable.h"
|
#include "../../../types/nullable.h"
|
||||||
#include "../../../lib/roll_value/roll_value.h"
|
#include <roll_value.h>
|
||||||
|
|
||||||
#define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3)
|
#define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3)
|
||||||
#define SCREEN_HEIGHT_THIRD_CENTER (SCREEN_HEIGHT_THIRD >> 1)
|
#define SCREEN_HEIGHT_THIRD_CENTER (SCREEN_HEIGHT_THIRD >> 1)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "bt_type_code.h"
|
#include "bt_type_code.h"
|
||||||
#include <furi_hal_bt_hid.h>
|
#include <furi_hal_bt_hid.h>
|
||||||
|
#include <bt/bt_service/bt_i.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include "../../types/common.h"
|
#include "../../types/common.h"
|
||||||
#include "../../types/token_info.h"
|
#include "../../types/token_info.h"
|
||||||
@@ -11,6 +12,26 @@ static inline bool totp_type_code_worker_stop_requested() {
|
|||||||
return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop;
|
return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
|
||||||
|
static void totp_type_code_worker_bt_set_app_mac(uint8_t* mac) {
|
||||||
|
uint8_t max_i;
|
||||||
|
size_t uid_size = furi_hal_version_uid_size();
|
||||||
|
if(uid_size < 6) {
|
||||||
|
max_i = uid_size;
|
||||||
|
} else {
|
||||||
|
max_i = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* uid = furi_hal_version_uid();
|
||||||
|
memcpy(mac, uid, max_i);
|
||||||
|
for(uint8_t i = max_i; i < 6; i++) {
|
||||||
|
mac[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mac[0] = 0b10;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context) {
|
static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context) {
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
do {
|
do {
|
||||||
@@ -30,7 +51,7 @@ static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int32_t totp_type_code_worker_callback(void* context) {
|
static int32_t totp_type_code_worker_callback(void* context) {
|
||||||
furi_assert(context);
|
furi_check(context);
|
||||||
FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
if(context_mutex == NULL) {
|
if(context_mutex == NULL) {
|
||||||
return 251;
|
return 251;
|
||||||
@@ -74,7 +95,7 @@ void totp_bt_type_code_worker_start(
|
|||||||
char* code_buf,
|
char* code_buf,
|
||||||
uint8_t code_buf_length,
|
uint8_t code_buf_length,
|
||||||
FuriMutex* code_buf_update_sync) {
|
FuriMutex* code_buf_update_sync) {
|
||||||
furi_assert(context != NULL);
|
furi_check(context != NULL);
|
||||||
context->string = code_buf;
|
context->string = code_buf;
|
||||||
context->string_length = code_buf_length;
|
context->string_length = code_buf_length;
|
||||||
context->string_sync = code_buf_update_sync;
|
context->string_sync = code_buf_update_sync;
|
||||||
@@ -87,7 +108,7 @@ void totp_bt_type_code_worker_start(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context) {
|
void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context) {
|
||||||
furi_assert(context != NULL);
|
furi_check(context != NULL);
|
||||||
furi_thread_flags_set(furi_thread_get_id(context->thread), TotpBtTypeCodeWorkerEventStop);
|
furi_thread_flags_set(furi_thread_get_id(context->thread), TotpBtTypeCodeWorkerEventStop);
|
||||||
furi_thread_join(context->thread);
|
furi_thread_join(context->thread);
|
||||||
furi_thread_free(context->thread);
|
furi_thread_free(context->thread);
|
||||||
@@ -98,7 +119,7 @@ void totp_bt_type_code_worker_notify(
|
|||||||
TotpBtTypeCodeWorkerContext* context,
|
TotpBtTypeCodeWorkerContext* context,
|
||||||
TotpBtTypeCodeWorkerEvent event,
|
TotpBtTypeCodeWorkerEvent event,
|
||||||
uint8_t flags) {
|
uint8_t flags) {
|
||||||
furi_assert(context != NULL);
|
furi_check(context != NULL);
|
||||||
context->flags = flags;
|
context->flags = flags;
|
||||||
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
|
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
|
||||||
}
|
}
|
||||||
@@ -114,11 +135,33 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() {
|
|||||||
furi_hal_bt_reinit();
|
furi_hal_bt_reinit();
|
||||||
furi_delay_ms(200);
|
furi_delay_ms(200);
|
||||||
bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH);
|
bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH);
|
||||||
|
|
||||||
|
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
|
||||||
|
totp_type_code_worker_bt_set_app_mac(&context->bt_mac[0]);
|
||||||
|
memcpy(
|
||||||
|
&context->previous_bt_name[0],
|
||||||
|
furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard),
|
||||||
|
TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN);
|
||||||
|
memcpy(
|
||||||
|
&context->previous_bt_mac[0],
|
||||||
|
furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard),
|
||||||
|
TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN);
|
||||||
|
char new_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN];
|
||||||
|
snprintf(new_name, sizeof(new_name), "%s TOTP Auth", furi_hal_version_get_name_ptr());
|
||||||
|
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, new_name);
|
||||||
|
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, context->bt_mac);
|
||||||
|
#endif
|
||||||
|
|
||||||
if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) {
|
if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) {
|
||||||
FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to keyboard HID profile");
|
FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to keyboard HID profile");
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_hal_bt_start_advertising();
|
furi_hal_bt_start_advertising();
|
||||||
|
|
||||||
|
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
|
||||||
|
bt_enable_peer_key_update(context->bt);
|
||||||
|
#endif
|
||||||
|
|
||||||
context->is_advertising = true;
|
context->is_advertising = true;
|
||||||
bt_set_status_changed_callback(context->bt, connection_status_changed_callback, context);
|
bt_set_status_changed_callback(context->bt, connection_status_changed_callback, context);
|
||||||
|
|
||||||
@@ -126,7 +169,7 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) {
|
void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) {
|
||||||
furi_assert(context != NULL);
|
furi_check(context != NULL);
|
||||||
|
|
||||||
if(context->thread != NULL) {
|
if(context->thread != NULL) {
|
||||||
totp_bt_type_code_worker_stop(context);
|
totp_bt_type_code_worker_stop(context);
|
||||||
@@ -142,6 +185,11 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) {
|
|||||||
furi_delay_ms(200);
|
furi_delay_ms(200);
|
||||||
bt_keys_storage_set_default_path(context->bt);
|
bt_keys_storage_set_default_path(context->bt);
|
||||||
|
|
||||||
|
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
|
||||||
|
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, context->previous_bt_name);
|
||||||
|
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, context->previous_bt_mac);
|
||||||
|
#endif
|
||||||
|
|
||||||
if(!bt_set_profile(context->bt, BtProfileSerial)) {
|
if(!bt_set_profile(context->bt, BtProfileSerial)) {
|
||||||
FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to Serial profile");
|
FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to Serial profile");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
#include <furi/furi.h>
|
#include <furi/furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <bt/bt_service/bt.h>
|
#include <bt/bt_service/bt.h>
|
||||||
|
#include "../../features_config.h"
|
||||||
|
|
||||||
|
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
|
||||||
|
#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN 18
|
||||||
|
#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef uint8_t TotpBtTypeCodeWorkerEvent;
|
typedef uint8_t TotpBtTypeCodeWorkerEvent;
|
||||||
|
|
||||||
@@ -16,6 +22,11 @@ typedef struct {
|
|||||||
Bt* bt;
|
Bt* bt;
|
||||||
bool is_advertising;
|
bool is_advertising;
|
||||||
bool is_connected;
|
bool is_connected;
|
||||||
|
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
|
||||||
|
uint8_t bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
|
||||||
|
char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN + 1];
|
||||||
|
uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
|
||||||
|
#endif
|
||||||
} TotpBtTypeCodeWorkerContext;
|
} TotpBtTypeCodeWorkerContext;
|
||||||
|
|
||||||
enum TotpBtTypeCodeWorkerEvents {
|
enum TotpBtTypeCodeWorkerEvents {
|
||||||
|
|||||||
@@ -3,17 +3,15 @@
|
|||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include "../../services/convert/convert.h"
|
#include "../../services/convert/convert.h"
|
||||||
|
|
||||||
static const uint8_t hid_number_keys[10] = {
|
static const uint8_t hid_number_keys[] = {
|
||||||
HID_KEYBOARD_0,
|
HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, HID_KEYBOARD_3, HID_KEYBOARD_4,
|
||||||
HID_KEYBOARD_1,
|
HID_KEYBOARD_5, HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, HID_KEYBOARD_9,
|
||||||
HID_KEYBOARD_2,
|
HID_KEYBOARD_A, HID_KEYBOARD_B, HID_KEYBOARD_C, HID_KEYBOARD_D, HID_KEYBOARD_E,
|
||||||
HID_KEYBOARD_3,
|
HID_KEYBOARD_F, HID_KEYBOARD_G, HID_KEYBOARD_H, HID_KEYBOARD_I, HID_KEYBOARD_J,
|
||||||
HID_KEYBOARD_4,
|
HID_KEYBOARD_K, HID_KEYBOARD_L, HID_KEYBOARD_M, HID_KEYBOARD_N, HID_KEYBOARD_O,
|
||||||
HID_KEYBOARD_5,
|
HID_KEYBOARD_P, HID_KEYBOARD_Q, HID_KEYBOARD_R, HID_KEYBOARD_S, HID_KEYBOARD_T,
|
||||||
HID_KEYBOARD_6,
|
HID_KEYBOARD_U, HID_KEYBOARD_V, HID_KEYBOARD_W, HID_KEYBOARD_X, HID_KEYBOARD_Y,
|
||||||
HID_KEYBOARD_7,
|
HID_KEYBOARD_Z};
|
||||||
HID_KEYBOARD_8,
|
|
||||||
HID_KEYBOARD_9};
|
|
||||||
|
|
||||||
static uint32_t get_keystroke_delay(TokenAutomationFeature features) {
|
static uint32_t get_keystroke_delay(TokenAutomationFeature features) {
|
||||||
if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) {
|
if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) {
|
||||||
@@ -49,10 +47,18 @@ void totp_type_code_worker_execute_automation(
|
|||||||
TokenAutomationFeature features) {
|
TokenAutomationFeature features) {
|
||||||
furi_delay_ms(500);
|
furi_delay_ms(500);
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
|
totp_type_code_worker_press_key(
|
||||||
|
HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features);
|
||||||
|
|
||||||
while(i < string_length && string[i] != 0) {
|
while(i < string_length && string[i] != 0) {
|
||||||
uint8_t digit = CONVERT_CHAR_TO_DIGIT(string[i]);
|
uint8_t char_index = CONVERT_CHAR_TO_DIGIT(string[i]);
|
||||||
if(digit > 9) break;
|
if(char_index > 9) {
|
||||||
uint8_t hid_kb_key = hid_number_keys[digit];
|
char_index = string[i] - 0x41 + 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(char_index > 35) break;
|
||||||
|
|
||||||
|
uint8_t hid_kb_key = hid_number_keys[char_index];
|
||||||
totp_type_code_worker_press_key(hid_kb_key, key_press_fn, key_release_fn, features);
|
totp_type_code_worker_press_key(hid_kb_key, key_press_fn, key_release_fn, features);
|
||||||
furi_delay_ms(get_keystroke_delay(features));
|
furi_delay_ms(get_keystroke_delay(features));
|
||||||
i++;
|
i++;
|
||||||
@@ -68,4 +74,7 @@ void totp_type_code_worker_execute_automation(
|
|||||||
furi_delay_ms(get_keystroke_delay(features));
|
furi_delay_ms(get_keystroke_delay(features));
|
||||||
totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features);
|
totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totp_type_code_worker_press_key(
|
||||||
|
HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features);
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int32_t totp_type_code_worker_callback(void* context) {
|
static int32_t totp_type_code_worker_callback(void* context) {
|
||||||
furi_assert(context);
|
furi_check(context);
|
||||||
FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
if(context_mutex == NULL) {
|
if(context_mutex == NULL) {
|
||||||
return 251;
|
return 251;
|
||||||
@@ -89,7 +89,7 @@ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context) {
|
void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context) {
|
||||||
furi_assert(context != NULL);
|
furi_check(context != NULL);
|
||||||
furi_thread_flags_set(furi_thread_get_id(context->thread), TotpUsbTypeCodeWorkerEventStop);
|
furi_thread_flags_set(furi_thread_get_id(context->thread), TotpUsbTypeCodeWorkerEventStop);
|
||||||
furi_thread_join(context->thread);
|
furi_thread_join(context->thread);
|
||||||
furi_thread_free(context->thread);
|
furi_thread_free(context->thread);
|
||||||
@@ -101,7 +101,7 @@ void totp_usb_type_code_worker_notify(
|
|||||||
TotpUsbTypeCodeWorkerContext* context,
|
TotpUsbTypeCodeWorkerContext* context,
|
||||||
TotpUsbTypeCodeWorkerEvent event,
|
TotpUsbTypeCodeWorkerEvent event,
|
||||||
uint8_t flags) {
|
uint8_t flags) {
|
||||||
furi_assert(context != NULL);
|
furi_check(context != NULL);
|
||||||
context->flags = flags;
|
context->flags = flags;
|
||||||
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
|
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
|
||||||
}
|
}
|
||||||
@@ -111,11 +111,21 @@ void uart_terminal_scene_console_output_on_enter(void* context) {
|
|||||||
uart_terminal_uart_set_handle_rx_data_cb(
|
uart_terminal_uart_set_handle_rx_data_cb(
|
||||||
app->uart, uart_terminal_console_output_handle_rx_data_cb); // setup callback for rx thread
|
app->uart, uart_terminal_console_output_handle_rx_data_cb); // setup callback for rx thread
|
||||||
|
|
||||||
// Send command with newline '\n'
|
// Send command with CR+LF or newline '\n'
|
||||||
if(app->is_command && app->selected_tx_string) {
|
if(app->is_command && app->selected_tx_string) {
|
||||||
uart_terminal_uart_tx(
|
if(app->TERMINAL_MODE == 1) {
|
||||||
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
|
// char buffer[240];
|
||||||
uart_terminal_uart_tx((uint8_t*)("\n"), 1);
|
// snprintf(buffer, 240, "%s\r\n", (app->selected_tx_string));
|
||||||
|
// uart_terminal_uart_tx((unsigned char *)buffer, strlen(buffer));
|
||||||
|
uart_terminal_uart_tx(
|
||||||
|
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
|
||||||
|
uart_terminal_uart_tx((uint8_t*)("\r"), 1);
|
||||||
|
uart_terminal_uart_tx((uint8_t*)("\n"), 1);
|
||||||
|
} else {
|
||||||
|
uart_terminal_uart_tx(
|
||||||
|
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
|
||||||
|
uart_terminal_uart_tx((uint8_t*)("\n"), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,4 +154,4 @@ void uart_terminal_scene_console_output_on_exit(void* context) {
|
|||||||
//if(app->is_command) {
|
//if(app->is_command) {
|
||||||
// uart_terminal_uart_tx((uint8_t*)("exit\n"), strlen("exit\n"));
|
// uart_terminal_uart_tx((uint8_t*)("exit\n"), strlen("exit\n"));
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,7 @@ const UART_TerminalItem items[NUM_MENU_ITEMS] = {
|
|||||||
FOCUS_CONSOLE_TOGGLE,
|
FOCUS_CONSOLE_TOGGLE,
|
||||||
NO_TIP},
|
NO_TIP},
|
||||||
{"Send command", {""}, 1, {""}, INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP},
|
{"Send command", {""}, 1, {""}, INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP},
|
||||||
|
{"Send AT command", {""}, 1, {"AT"}, INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP},
|
||||||
{"Fast cmd",
|
{"Fast cmd",
|
||||||
{"help", "uptime", "date", "df -h", "ps", "dmesg", "reboot", "poweroff"},
|
{"help", "uptime", "date", "df -h", "ps", "dmesg", "reboot", "poweroff"},
|
||||||
8,
|
8,
|
||||||
|
|||||||
@@ -25,7 +25,13 @@ void uart_terminal_scene_text_input_on_enter(void* context) {
|
|||||||
// Setup view
|
// Setup view
|
||||||
UART_TextInput* text_input = app->text_input;
|
UART_TextInput* text_input = app->text_input;
|
||||||
// Add help message to header
|
// Add help message to header
|
||||||
uart_text_input_set_header_text(text_input, "Send command to UART");
|
if(0 == strncmp("AT", app->selected_tx_string, strlen("AT"))) {
|
||||||
|
app->TERMINAL_MODE = 1;
|
||||||
|
uart_text_input_set_header_text(text_input, "Send AT command to UART");
|
||||||
|
} else {
|
||||||
|
app->TERMINAL_MODE = 0;
|
||||||
|
uart_text_input_set_header_text(text_input, "Send command to UART");
|
||||||
|
}
|
||||||
uart_text_input_set_result_callback(
|
uart_text_input_set_result_callback(
|
||||||
text_input,
|
text_input,
|
||||||
uart_terminal_scene_text_input_callback,
|
uart_terminal_scene_text_input_callback,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <gui/modules/variable_item_list.h>
|
#include <gui/modules/variable_item_list.h>
|
||||||
#include "uart_text_input.h"
|
#include "uart_text_input.h"
|
||||||
|
|
||||||
#define NUM_MENU_ITEMS (4)
|
#define NUM_MENU_ITEMS (5)
|
||||||
|
|
||||||
#define UART_TERMINAL_TEXT_BOX_STORE_SIZE (4096)
|
#define UART_TERMINAL_TEXT_BOX_STORE_SIZE (4096)
|
||||||
#define UART_TERMINAL_TEXT_INPUT_STORE_SIZE (512)
|
#define UART_TERMINAL_TEXT_INPUT_STORE_SIZE (512)
|
||||||
@@ -40,6 +40,7 @@ struct UART_TerminalApp {
|
|||||||
bool focus_console_start;
|
bool focus_console_start;
|
||||||
bool show_stopscan_tip;
|
bool show_stopscan_tip;
|
||||||
int BAUDRATE;
|
int BAUDRATE;
|
||||||
|
int TERMINAL_MODE; //1=AT mode, 0=other mode
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "uart_text_input.h"
|
#include "uart_text_input.h"
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
#include "uart_terminal_icons.h"
|
#include "uart_terminal_icons.h"
|
||||||
|
#include "uart_terminal_app_i.h"
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
|
||||||
struct UART_TextInput {
|
struct UART_TextInput {
|
||||||
@@ -36,6 +37,8 @@ static const uint8_t keyboard_origin_x = 1;
|
|||||||
static const uint8_t keyboard_origin_y = 29;
|
static const uint8_t keyboard_origin_y = 29;
|
||||||
static const uint8_t keyboard_row_count = 4;
|
static const uint8_t keyboard_row_count = 4;
|
||||||
|
|
||||||
|
#define mode_AT "Send AT command to UART"
|
||||||
|
|
||||||
#define ENTER_KEY '\r'
|
#define ENTER_KEY '\r'
|
||||||
#define BACKSPACE_KEY '\b'
|
#define BACKSPACE_KEY '\b'
|
||||||
|
|
||||||
@@ -163,6 +166,47 @@ static bool char_is_lowercase(char letter) {
|
|||||||
return (letter >= 0x61 && letter <= 0x7A);
|
return (letter >= 0x61 && letter <= 0x7A);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool char_is_uppercase(char letter) {
|
||||||
|
return (letter >= 0x41 && letter <= 0x5A);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char char_to_lowercase(const char letter) {
|
||||||
|
switch(letter) {
|
||||||
|
case ' ':
|
||||||
|
return 0x5f;
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
return 0x28;
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
return 0x7b;
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
return 0x5b;
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
return 0x2f;
|
||||||
|
break;
|
||||||
|
case ':':
|
||||||
|
return 0x3b;
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
return 0x2e;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
return 0x21;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
return 0x3c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(char_is_uppercase(letter)) {
|
||||||
|
return (letter + 0x20);
|
||||||
|
} else {
|
||||||
|
return letter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static char char_to_uppercase(const char letter) {
|
static char char_to_uppercase(const char letter) {
|
||||||
switch(letter) {
|
switch(letter) {
|
||||||
case '_':
|
case '_':
|
||||||
@@ -193,7 +237,7 @@ static char char_to_uppercase(const char letter) {
|
|||||||
return 0x3e;
|
return 0x3e;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(isalpha(letter)) {
|
if(char_is_lowercase(letter)) {
|
||||||
return (letter - 0x20);
|
return (letter - 0x20);
|
||||||
} else {
|
} else {
|
||||||
return letter;
|
return letter;
|
||||||
@@ -209,7 +253,7 @@ static void uart_text_input_backspace_cb(UART_TextInputModel* model) {
|
|||||||
|
|
||||||
static void uart_text_input_view_draw_callback(Canvas* canvas, void* _model) {
|
static void uart_text_input_view_draw_callback(Canvas* canvas, void* _model) {
|
||||||
UART_TextInputModel* model = _model;
|
UART_TextInputModel* model = _model;
|
||||||
uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0;
|
//uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0;
|
||||||
uint8_t needed_string_width = canvas_width(canvas) - 8;
|
uint8_t needed_string_width = canvas_width(canvas) - 8;
|
||||||
uint8_t start_pos = 4;
|
uint8_t start_pos = 4;
|
||||||
|
|
||||||
@@ -291,15 +335,12 @@ static void uart_text_input_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
} else {
|
} else {
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
}
|
}
|
||||||
|
if(0 == strcmp(model->header, mode_AT)) {
|
||||||
if(model->clear_default_text ||
|
|
||||||
(text_length == 0 && char_is_lowercase(keys[column].text))) {
|
|
||||||
canvas_draw_glyph(
|
canvas_draw_glyph(
|
||||||
canvas,
|
canvas,
|
||||||
keyboard_origin_x + keys[column].x,
|
keyboard_origin_x + keys[column].x,
|
||||||
keyboard_origin_y + keys[column].y,
|
keyboard_origin_y + keys[column].y,
|
||||||
//char_to_uppercase(keys[column].text));
|
char_to_uppercase(keys[column].text));
|
||||||
keys[column].text);
|
|
||||||
} else {
|
} else {
|
||||||
canvas_draw_glyph(
|
canvas_draw_glyph(
|
||||||
canvas,
|
canvas,
|
||||||
@@ -372,10 +413,18 @@ static void uart_text_input_handle_ok(
|
|||||||
char selected = get_selected_char(model);
|
char selected = get_selected_char(model);
|
||||||
uint8_t text_length = strlen(model->text_buffer);
|
uint8_t text_length = strlen(model->text_buffer);
|
||||||
|
|
||||||
if(shift) {
|
if(0 == strcmp(model->header, mode_AT)) {
|
||||||
selected = char_to_uppercase(selected);
|
selected = char_to_uppercase(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(shift) {
|
||||||
|
if(0 == strcmp(model->header, mode_AT)) {
|
||||||
|
selected = char_to_lowercase(selected);
|
||||||
|
} else {
|
||||||
|
selected = char_to_uppercase(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(selected == ENTER_KEY) {
|
if(selected == ENTER_KEY) {
|
||||||
if(model->validator_callback &&
|
if(model->validator_callback &&
|
||||||
(!model->validator_callback(
|
(!model->validator_callback(
|
||||||
@@ -392,9 +441,6 @@ static void uart_text_input_handle_ok(
|
|||||||
text_length = 0;
|
text_length = 0;
|
||||||
}
|
}
|
||||||
if(text_length < (model->text_buffer_size - 1)) {
|
if(text_length < (model->text_buffer_size - 1)) {
|
||||||
if(text_length == 0 && char_is_lowercase(selected)) {
|
|
||||||
//selected = char_to_uppercase(selected);
|
|
||||||
}
|
|
||||||
model->text_buffer[text_length] = selected;
|
model->text_buffer[text_length] = selected;
|
||||||
model->text_buffer[text_length + 1] = 0;
|
model->text_buffer[text_length + 1] = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
#define TAG "WSProtocolLaCrosse_TX141THBv2"
|
#define TAG "WSProtocolLaCrosse_TX141THBv2"
|
||||||
|
|
||||||
|
#define LACROSSE_TX141TH_BV2_BIT_COUNT 41
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Help
|
* Help
|
||||||
* https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse_tx141x.c
|
* https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse_tx141x.c
|
||||||
*
|
*
|
||||||
* iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u
|
* iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u - 41 bit
|
||||||
|
* or
|
||||||
|
* iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | -40 bit
|
||||||
* - i: identification; changes on battery switch
|
* - i: identification; changes on battery switch
|
||||||
* - c: lfsr_digest8_reflect;
|
* - c: lfsr_digest8_reflect;
|
||||||
* - u: unknown;
|
* - u: unknown;
|
||||||
@@ -17,10 +21,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static const SubGhzBlockConst ws_protocol_lacrosse_tx141thbv2_const = {
|
static const SubGhzBlockConst ws_protocol_lacrosse_tx141thbv2_const = {
|
||||||
.te_short = 250,
|
.te_short = 208,
|
||||||
.te_long = 500,
|
.te_long = 417,
|
||||||
.te_delta = 120,
|
.te_delta = 120,
|
||||||
.min_count_bit_for_found = 41,
|
.min_count_bit_for_found = 40,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WSProtocolDecoderLaCrosse_TX141THBv2 {
|
struct WSProtocolDecoderLaCrosse_TX141THBv2 {
|
||||||
@@ -102,14 +106,14 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context) {
|
|||||||
static bool
|
static bool
|
||||||
ws_protocol_lacrosse_tx141thbv2_check_crc(WSProtocolDecoderLaCrosse_TX141THBv2* instance) {
|
ws_protocol_lacrosse_tx141thbv2_check_crc(WSProtocolDecoderLaCrosse_TX141THBv2* instance) {
|
||||||
if(!instance->decoder.decode_data) return false;
|
if(!instance->decoder.decode_data) return false;
|
||||||
uint8_t msg[] = {
|
uint64_t data = instance->decoder.decode_data;
|
||||||
instance->decoder.decode_data >> 33,
|
if(instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) {
|
||||||
instance->decoder.decode_data >> 25,
|
data >>= 1;
|
||||||
instance->decoder.decode_data >> 17,
|
}
|
||||||
instance->decoder.decode_data >> 9};
|
uint8_t msg[] = {data >> 32, data >> 24, data >> 16, data >> 8};
|
||||||
|
|
||||||
uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4);
|
uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4);
|
||||||
return (crc == ((instance->decoder.decode_data >> 1) & 0xFF));
|
return (crc == (data & 0xFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,14 +121,43 @@ static bool
|
|||||||
* @param instance Pointer to a WSBlockGeneric* instance
|
* @param instance Pointer to a WSBlockGeneric* instance
|
||||||
*/
|
*/
|
||||||
static void ws_protocol_lacrosse_tx141thbv2_remote_controller(WSBlockGeneric* instance) {
|
static void ws_protocol_lacrosse_tx141thbv2_remote_controller(WSBlockGeneric* instance) {
|
||||||
instance->id = instance->data >> 33;
|
uint64_t data = instance->data;
|
||||||
instance->battery_low = (instance->data >> 32) & 1;
|
if(instance->data_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) {
|
||||||
instance->btn = (instance->data >> 31) & 1;
|
data >>= 1;
|
||||||
instance->channel = ((instance->data >> 29) & 0x03) + 1;
|
}
|
||||||
instance->temp = ((float)((instance->data >> 17) & 0x0FFF) - 500.0f) / 10.0f;
|
instance->id = data >> 32;
|
||||||
instance->humidity = (instance->data >> 9) & 0xFF;
|
instance->battery_low = (data >> 31) & 1;
|
||||||
|
instance->btn = (data >> 30) & 1;
|
||||||
|
instance->channel = ((data >> 28) & 0x03) + 1;
|
||||||
|
instance->temp = ((float)((data >> 16) & 0x0FFF) - 500.0f) / 10.0f;
|
||||||
|
instance->humidity = (data >> 8) & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analysis of received data
|
||||||
|
* @param instance Pointer to a WSBlockGeneric* instance
|
||||||
|
*/
|
||||||
|
static bool ws_protocol_decoder_lacrosse_tx141thbv2_add_bit(
|
||||||
|
WSProtocolDecoderLaCrosse_TX141THBv2* instance,
|
||||||
|
uint32_t te_last,
|
||||||
|
uint32_t te_current) {
|
||||||
|
furi_assert(instance);
|
||||||
|
bool ret = false;
|
||||||
|
if(DURATION_DIFF(
|
||||||
|
te_last + te_current,
|
||||||
|
ws_protocol_lacrosse_tx141thbv2_const.te_short +
|
||||||
|
ws_protocol_lacrosse_tx141thbv2_const.te_long) <
|
||||||
|
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) {
|
||||||
|
if(te_last > te_current) {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||||
|
} else {
|
||||||
|
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||||
|
}
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration) {
|
void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
WSProtocolDecoderLaCrosse_TX141THBv2* instance = context;
|
WSProtocolDecoderLaCrosse_TX141THBv2* instance = context;
|
||||||
@@ -132,7 +165,7 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin
|
|||||||
switch(instance->decoder.parser_step) {
|
switch(instance->decoder.parser_step) {
|
||||||
case LaCrosse_TX141THBv2DecoderStepReset:
|
case LaCrosse_TX141THBv2DecoderStepReset:
|
||||||
if((level) &&
|
if((level) &&
|
||||||
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
|
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) <
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) {
|
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) {
|
||||||
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule;
|
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule;
|
||||||
instance->decoder.te_last = duration;
|
instance->decoder.te_last = duration;
|
||||||
@@ -146,33 +179,17 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin
|
|||||||
} else {
|
} else {
|
||||||
if((DURATION_DIFF(
|
if((DURATION_DIFF(
|
||||||
instance->decoder.te_last,
|
instance->decoder.te_last,
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
|
ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) <
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) &&
|
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) &&
|
||||||
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
|
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) <
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) {
|
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) {
|
||||||
//Found preambule
|
//Found preambule
|
||||||
instance->header_count++;
|
instance->header_count++;
|
||||||
} else if(instance->header_count == 4) {
|
} else if(instance->header_count == 4) {
|
||||||
if((DURATION_DIFF(
|
if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit(
|
||||||
instance->decoder.te_last,
|
instance, instance->decoder.te_last, duration)) {
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_short) <
|
instance->decoder.decode_data = instance->decoder.decode_data & 1;
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta) &&
|
instance->decoder.decode_count_bit = 1;
|
||||||
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) <
|
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta)) {
|
|
||||||
instance->decoder.decode_data = 0;
|
|
||||||
instance->decoder.decode_count_bit = 0;
|
|
||||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
|
||||||
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
|
|
||||||
} else if(
|
|
||||||
(DURATION_DIFF(
|
|
||||||
instance->decoder.te_last,
|
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_long) <
|
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta) &&
|
|
||||||
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) <
|
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta)) {
|
|
||||||
instance->decoder.decode_data = 0;
|
|
||||||
instance->decoder.decode_count_bit = 0;
|
|
||||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
|
||||||
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
|
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
|
||||||
} else {
|
} else {
|
||||||
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset;
|
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset;
|
||||||
@@ -198,37 +215,26 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin
|
|||||||
instance->decoder.te_last,
|
instance->decoder.te_last,
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
|
ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) &&
|
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) &&
|
||||||
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
|
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) <
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2))) {
|
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2))) {
|
||||||
if((instance->decoder.decode_count_bit ==
|
if((instance->decoder.decode_count_bit ==
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) &&
|
ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) ||
|
||||||
ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) {
|
(instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT)) {
|
||||||
instance->generic.data = instance->decoder.decode_data;
|
if(ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) {
|
||||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
instance->generic.data = instance->decoder.decode_data;
|
||||||
ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic);
|
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||||
if(instance->base.callback)
|
ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic);
|
||||||
instance->base.callback(&instance->base, instance->base.context);
|
if(instance->base.callback)
|
||||||
|
instance->base.callback(&instance->base, instance->base.context);
|
||||||
|
}
|
||||||
|
instance->decoder.decode_data = 0;
|
||||||
|
instance->decoder.decode_count_bit = 0;
|
||||||
|
instance->header_count = 1;
|
||||||
|
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
instance->decoder.decode_data = 0;
|
} else if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit(
|
||||||
instance->decoder.decode_count_bit = 0;
|
instance, instance->decoder.te_last, duration)) {
|
||||||
instance->header_count = 1;
|
|
||||||
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule;
|
|
||||||
break;
|
|
||||||
} else if(
|
|
||||||
(DURATION_DIFF(
|
|
||||||
instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_short) <
|
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta) &&
|
|
||||||
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) <
|
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta)) {
|
|
||||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
|
||||||
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
|
|
||||||
} else if(
|
|
||||||
(DURATION_DIFF(
|
|
||||||
instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_long) <
|
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta) &&
|
|
||||||
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) <
|
|
||||||
ws_protocol_lacrosse_tx141thbv2_const.te_delta)) {
|
|
||||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
|
||||||
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
|
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
|
||||||
} else {
|
} else {
|
||||||
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset;
|
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset;
|
||||||
|
|||||||
@@ -80,9 +80,9 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);
|
canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);
|
||||||
|
|
||||||
if(model->rx_active)
|
if(model->rx_active)
|
||||||
canvas_draw_icon(canvas, 48, 34, &I_ArrowDownFilled_14x15);
|
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
|
||||||
else
|
else
|
||||||
canvas_draw_icon(canvas, 48, 34, &I_ArrowDownEmpty_14x15);
|
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
|
static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ typedef enum {
|
|||||||
SubGhzSpeakerStateEnable,
|
SubGhzSpeakerStateEnable,
|
||||||
} SubGhzSpeakerState;
|
} SubGhzSpeakerState;
|
||||||
|
|
||||||
|
/** SubGhzStarLineIgnore state */
|
||||||
|
typedef enum {
|
||||||
|
SubGhzStarLineIgnoreDisable,
|
||||||
|
SubGhzStarLineIgnoreEnable,
|
||||||
|
} SubGhzStarLineIgnoreState;
|
||||||
|
|
||||||
/** SubGhzRxKeyState state */
|
/** SubGhzRxKeyState state */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SubGhzRxKeyStateIDLE,
|
SubGhzRxKeyStateIDLE,
|
||||||
|
|||||||
@@ -147,6 +147,16 @@ void subghz_scene_receiver_on_enter(void* context) {
|
|||||||
subghz_receiver_set_rx_callback(
|
subghz_receiver_set_rx_callback(
|
||||||
subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz);
|
subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz);
|
||||||
|
|
||||||
|
if(subghz->txrx->starline_state == SubGhzStarLineIgnoreEnable) {
|
||||||
|
SubGhzProtocolDecoderBase* protocoldecoderbase = NULL;
|
||||||
|
protocoldecoderbase =
|
||||||
|
subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "Star Line");
|
||||||
|
if(protocoldecoderbase) {
|
||||||
|
subghz_protocol_decoder_base_set_decoder_callback(
|
||||||
|
protocoldecoderbase, NULL, subghz->txrx->receiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||||
subghz_rx_end(subghz);
|
subghz_rx_end(subghz);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ enum SubGhzSettingIndex {
|
|||||||
SubGhzSettingIndexHopping,
|
SubGhzSettingIndexHopping,
|
||||||
SubGhzSettingIndexModulation,
|
SubGhzSettingIndexModulation,
|
||||||
SubGhzSettingIndexBinRAW,
|
SubGhzSettingIndexBinRAW,
|
||||||
|
SubGhzSettingIndexIgnoreStarline,
|
||||||
SubGhzSettingIndexSound,
|
SubGhzSettingIndexSound,
|
||||||
SubGhzSettingIndexLock,
|
SubGhzSettingIndexLock,
|
||||||
SubGhzSettingIndexRAWThresholdRSSI,
|
SubGhzSettingIndexRAWThresholdRSSI,
|
||||||
@@ -68,6 +69,15 @@ const uint32_t bin_raw_value[BIN_RAW_COUNT] = {
|
|||||||
SubGhzProtocolFlag_Decodable,
|
SubGhzProtocolFlag_Decodable,
|
||||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW,
|
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW,
|
||||||
};
|
};
|
||||||
|
#define STAR_LINE_COUNT 2
|
||||||
|
const char* const star_line_text[STAR_LINE_COUNT] = {
|
||||||
|
"OFF",
|
||||||
|
"ON",
|
||||||
|
};
|
||||||
|
const uint32_t star_line_value[STAR_LINE_COUNT] = {
|
||||||
|
SubGhzStarLineIgnoreDisable,
|
||||||
|
SubGhzStarLineIgnoreEnable,
|
||||||
|
};
|
||||||
|
|
||||||
uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
|
uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
@@ -217,6 +227,14 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it
|
|||||||
subghz->txrx->raw_threshold_rssi = raw_threshold_rssi_value[index];
|
subghz->txrx->raw_threshold_rssi = raw_threshold_rssi_value[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void subghz_scene_receiver_config_set_starline(VariableItem* item) {
|
||||||
|
SubGhz* subghz = variable_item_get_context(item);
|
||||||
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
|
variable_item_set_current_value_text(item, star_line_text[index]);
|
||||||
|
subghz->txrx->starline_state = star_line_value[index];
|
||||||
|
}
|
||||||
|
|
||||||
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
|
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
@@ -291,6 +309,21 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
|||||||
variable_item_set_current_value_text(item, bin_raw_text[value_index]);
|
variable_item_set_current_value_text(item, bin_raw_text[value_index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
||||||
|
SubGhzCustomEventManagerSet) {
|
||||||
|
item = variable_item_list_add(
|
||||||
|
subghz->variable_item_list,
|
||||||
|
"Ignore StarLine:",
|
||||||
|
STAR_LINE_COUNT,
|
||||||
|
subghz_scene_receiver_config_set_starline,
|
||||||
|
subghz);
|
||||||
|
|
||||||
|
value_index =
|
||||||
|
value_index_uint32(subghz->txrx->starline_state, star_line_value, STAR_LINE_COUNT);
|
||||||
|
variable_item_set_current_value_index(item, value_index);
|
||||||
|
variable_item_set_current_value_text(item, star_line_text[value_index]);
|
||||||
|
}
|
||||||
|
|
||||||
// Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :)
|
// Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :)
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
subghz->variable_item_list,
|
subghz->variable_item_list,
|
||||||
|
|||||||
@@ -558,7 +558,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
|||||||
subghz,
|
subghz,
|
||||||
"AM650",
|
"AM650",
|
||||||
433920000,
|
433920000,
|
||||||
(key & 0x00FFFFFF) | 0x04000000,
|
(key & 0x000FFFFF) | 0x04700000,
|
||||||
0x2,
|
0x2,
|
||||||
0x0021,
|
0x0021,
|
||||||
"AN-Motors")) {
|
"AN-Motors")) {
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ struct SubGhzTxRx {
|
|||||||
SubGhzTxRxState txrx_state;
|
SubGhzTxRxState txrx_state;
|
||||||
SubGhzHopperState hopper_state;
|
SubGhzHopperState hopper_state;
|
||||||
SubGhzSpeakerState speaker_state;
|
SubGhzSpeakerState speaker_state;
|
||||||
|
SubGhzStarLineIgnoreState starline_state;
|
||||||
uint8_t hopper_timeout;
|
uint8_t hopper_timeout;
|
||||||
uint8_t hopper_idx_frequency;
|
uint8_t hopper_idx_frequency;
|
||||||
SubGhzRxKeyState rx_key_state;
|
SubGhzRxKeyState rx_key_state;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ static void desktop_loader_callback(const void* message, void* context) {
|
|||||||
static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) {
|
static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) {
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
canvas_draw_icon(canvas, 0, 0, &I_Lock_8x8);
|
canvas_draw_icon(canvas, 0, 0, &I_Lock_7x8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool desktop_custom_event_callback(void* context, uint32_t event) {
|
static bool desktop_custom_event_callback(void* context, uint32_t event) {
|
||||||
@@ -216,7 +216,7 @@ Desktop* desktop_alloc() {
|
|||||||
|
|
||||||
// Lock icon
|
// Lock icon
|
||||||
desktop->lock_icon_viewport = view_port_alloc();
|
desktop->lock_icon_viewport = view_port_alloc();
|
||||||
view_port_set_width(desktop->lock_icon_viewport, icon_get_width(&I_Lock_8x8));
|
view_port_set_width(desktop->lock_icon_viewport, icon_get_width(&I_Lock_7x8));
|
||||||
view_port_draw_callback_set(
|
view_port_draw_callback_set(
|
||||||
desktop->lock_icon_viewport, desktop_lock_icon_draw_callback, desktop);
|
desktop->lock_icon_viewport, desktop_lock_icon_draw_callback, desktop);
|
||||||
view_port_enabled_set(desktop->lock_icon_viewport, false);
|
view_port_enabled_set(desktop->lock_icon_viewport, false);
|
||||||
|
|||||||
@@ -115,16 +115,18 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu
|
|||||||
} else {
|
} else {
|
||||||
switch(model->pin.data[i]) {
|
switch(model->pin.data[i]) {
|
||||||
case InputKeyDown:
|
case InputKeyDown:
|
||||||
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9);
|
canvas_draw_icon_ex(
|
||||||
|
canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation180);
|
||||||
break;
|
break;
|
||||||
case InputKeyUp:
|
case InputKeyUp:
|
||||||
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9);
|
canvas_draw_icon_ex(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation0);
|
||||||
break;
|
break;
|
||||||
case InputKeyLeft:
|
case InputKeyLeft:
|
||||||
canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7);
|
canvas_draw_icon_ex(
|
||||||
|
canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation270);
|
||||||
break;
|
break;
|
||||||
case InputKeyRight:
|
case InputKeyRight:
|
||||||
canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_right_9x7);
|
canvas_draw_icon_ex(canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation90);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
furi_assert(0);
|
furi_assert(0);
|
||||||
@@ -147,7 +149,8 @@ static void desktop_view_pin_input_draw(Canvas* canvas, void* context) {
|
|||||||
desktop_view_pin_input_draw_cells(canvas, model);
|
desktop_view_pin_input_draw_cells(canvas, model);
|
||||||
|
|
||||||
if((model->pin.length > 0) && !model->locked_input) {
|
if((model->pin.length > 0) && !model->locked_input) {
|
||||||
canvas_draw_icon(canvas, 4, 53, &I_Pin_back_full_40x8);
|
canvas_draw_icon(canvas, 4, 53, &I_Pin_back_arrow_10x8);
|
||||||
|
canvas_draw_str(canvas, 16, 60, "= clear");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(model->button_label && ((model->pin.length >= MIN_PIN_SIZE) || model->locked_input)) {
|
if(model->button_label && ((model->pin.length >= MIN_PIN_SIZE) || model->locked_input)) {
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ void canvas_draw_bitmap(
|
|||||||
y += canvas->offset_y;
|
y += canvas->offset_y;
|
||||||
uint8_t* bitmap_data = NULL;
|
uint8_t* bitmap_data = NULL;
|
||||||
compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data);
|
compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data);
|
||||||
u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data);
|
canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, IconRotation0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void canvas_draw_icon_animation(
|
void canvas_draw_icon_animation(
|
||||||
@@ -252,13 +252,138 @@ void canvas_draw_icon_animation(
|
|||||||
uint8_t* icon_data = NULL;
|
uint8_t* icon_data = NULL;
|
||||||
compress_icon_decode(
|
compress_icon_decode(
|
||||||
canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data);
|
canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data);
|
||||||
u8g2_DrawXBM(
|
canvas_draw_u8g2_bitmap(
|
||||||
&canvas->fb,
|
&canvas->fb,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
icon_animation_get_width(icon_animation),
|
icon_animation_get_width(icon_animation),
|
||||||
icon_animation_get_height(icon_animation),
|
icon_animation_get_height(icon_animation),
|
||||||
icon_data);
|
icon_data,
|
||||||
|
IconRotation0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void canvas_draw_u8g2_bitmap_int(
|
||||||
|
u8g2_t* u8g2,
|
||||||
|
u8g2_uint_t x,
|
||||||
|
u8g2_uint_t y,
|
||||||
|
u8g2_uint_t w,
|
||||||
|
u8g2_uint_t h,
|
||||||
|
bool mirror,
|
||||||
|
bool rotation,
|
||||||
|
const uint8_t* bitmap) {
|
||||||
|
u8g2_uint_t blen;
|
||||||
|
blen = w;
|
||||||
|
blen += 7;
|
||||||
|
blen >>= 3;
|
||||||
|
|
||||||
|
if(rotation && !mirror) {
|
||||||
|
x += w + 1;
|
||||||
|
} else if(mirror && !rotation) {
|
||||||
|
y += h - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(h > 0) {
|
||||||
|
const uint8_t* b = bitmap;
|
||||||
|
uint16_t len = w;
|
||||||
|
uint16_t x0 = x;
|
||||||
|
uint16_t y0 = y;
|
||||||
|
uint8_t mask;
|
||||||
|
uint8_t color = u8g2->draw_color;
|
||||||
|
uint8_t ncolor = (color == 0 ? 1 : 0);
|
||||||
|
mask = 1;
|
||||||
|
|
||||||
|
while(len > 0) {
|
||||||
|
if(u8x8_pgm_read(b) & mask) {
|
||||||
|
u8g2->draw_color = color;
|
||||||
|
u8g2_DrawHVLine(u8g2, x0, y0, 1, 0);
|
||||||
|
} else if(u8g2->bitmap_transparency == 0) {
|
||||||
|
u8g2->draw_color = ncolor;
|
||||||
|
u8g2_DrawHVLine(u8g2, x0, y0, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rotation) {
|
||||||
|
y0++;
|
||||||
|
} else {
|
||||||
|
x0++;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask <<= 1;
|
||||||
|
if(mask == 0) {
|
||||||
|
mask = 1;
|
||||||
|
b++;
|
||||||
|
}
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8g2->draw_color = color;
|
||||||
|
bitmap += blen;
|
||||||
|
|
||||||
|
if(mirror) {
|
||||||
|
if(rotation) {
|
||||||
|
x++;
|
||||||
|
} else {
|
||||||
|
y--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(rotation) {
|
||||||
|
x--;
|
||||||
|
} else {
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void canvas_draw_u8g2_bitmap(
|
||||||
|
u8g2_t* u8g2,
|
||||||
|
u8g2_uint_t x,
|
||||||
|
u8g2_uint_t y,
|
||||||
|
u8g2_uint_t w,
|
||||||
|
u8g2_uint_t h,
|
||||||
|
const uint8_t* bitmap,
|
||||||
|
IconRotation rotation) {
|
||||||
|
u8g2_uint_t blen;
|
||||||
|
blen = w;
|
||||||
|
blen += 7;
|
||||||
|
blen >>= 3;
|
||||||
|
#ifdef U8G2_WITH_INTERSECTION
|
||||||
|
if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;
|
||||||
|
#endif /* U8G2_WITH_INTERSECTION */
|
||||||
|
|
||||||
|
switch(rotation) {
|
||||||
|
case IconRotation0:
|
||||||
|
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 0, bitmap);
|
||||||
|
break;
|
||||||
|
case IconRotation90:
|
||||||
|
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 1, bitmap);
|
||||||
|
break;
|
||||||
|
case IconRotation180:
|
||||||
|
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 0, bitmap);
|
||||||
|
break;
|
||||||
|
case IconRotation270:
|
||||||
|
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 1, bitmap);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void canvas_draw_icon_ex(
|
||||||
|
Canvas* canvas,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
const Icon* icon,
|
||||||
|
IconRotation rotation) {
|
||||||
|
furi_assert(canvas);
|
||||||
|
furi_assert(icon);
|
||||||
|
|
||||||
|
x += canvas->offset_x;
|
||||||
|
y += canvas->offset_y;
|
||||||
|
uint8_t* icon_data = NULL;
|
||||||
|
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
|
||||||
|
canvas_draw_u8g2_bitmap(
|
||||||
|
&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
|
void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
|
||||||
@@ -269,7 +394,8 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
|
|||||||
y += canvas->offset_y;
|
y += canvas->offset_y;
|
||||||
uint8_t* icon_data = NULL;
|
uint8_t* icon_data = NULL;
|
||||||
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
|
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
|
||||||
u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data);
|
canvas_draw_u8g2_bitmap(
|
||||||
|
&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, IconRotation0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) {
|
void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) {
|
||||||
@@ -379,7 +505,7 @@ void canvas_draw_xbm(
|
|||||||
furi_assert(canvas);
|
furi_assert(canvas);
|
||||||
x += canvas->offset_x;
|
x += canvas->offset_x;
|
||||||
y += canvas->offset_y;
|
y += canvas->offset_y;
|
||||||
u8g2_DrawXBM(&canvas->fb, x, y, w, h, bitmap);
|
canvas_draw_u8g2_bitmap(&canvas->fb, x, y, w, h, bitmap, IconRotation0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) {
|
void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) {
|
||||||
|
|||||||
@@ -65,6 +65,22 @@ typedef struct {
|
|||||||
uint8_t descender;
|
uint8_t descender;
|
||||||
} CanvasFontParameters;
|
} CanvasFontParameters;
|
||||||
|
|
||||||
|
/** Icon flip */
|
||||||
|
typedef enum {
|
||||||
|
IconFlipNone,
|
||||||
|
IconFlipHorizontal,
|
||||||
|
IconFlipVertical,
|
||||||
|
IconFlipBoth,
|
||||||
|
} IconFlip;
|
||||||
|
|
||||||
|
/** Icon rotation */
|
||||||
|
typedef enum {
|
||||||
|
IconRotation0,
|
||||||
|
IconRotation90,
|
||||||
|
IconRotation180,
|
||||||
|
IconRotation270,
|
||||||
|
} IconRotation;
|
||||||
|
|
||||||
/** Canvas anonymous structure */
|
/** Canvas anonymous structure */
|
||||||
typedef struct Canvas Canvas;
|
typedef struct Canvas Canvas;
|
||||||
|
|
||||||
@@ -218,6 +234,22 @@ void canvas_draw_bitmap(
|
|||||||
uint8_t height,
|
uint8_t height,
|
||||||
const uint8_t* compressed_bitmap_data);
|
const uint8_t* compressed_bitmap_data);
|
||||||
|
|
||||||
|
/** Draw icon at position defined by x,y with rotation and flip.
|
||||||
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param x x coordinate
|
||||||
|
* @param y y coordinate
|
||||||
|
* @param icon Icon instance
|
||||||
|
* @param flip IconFlip
|
||||||
|
* @param rotation IconRotation
|
||||||
|
*/
|
||||||
|
void canvas_draw_icon_ex(
|
||||||
|
Canvas* canvas,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
const Icon* icon,
|
||||||
|
IconRotation rotation);
|
||||||
|
|
||||||
/** Draw animation at position defined by x,y.
|
/** Draw animation at position defined by x,y.
|
||||||
*
|
*
|
||||||
* @param canvas Canvas instance
|
* @param canvas Canvas instance
|
||||||
|
|||||||
@@ -83,6 +83,25 @@ void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation);
|
|||||||
*/
|
*/
|
||||||
CanvasOrientation canvas_get_orientation(const Canvas* canvas);
|
CanvasOrientation canvas_get_orientation(const Canvas* canvas);
|
||||||
|
|
||||||
|
/** Draw a u8g2 bitmap
|
||||||
|
*
|
||||||
|
* @param canvas Canvas instance
|
||||||
|
* @param x x coordinate
|
||||||
|
* @param y y coordinate
|
||||||
|
* @param width width
|
||||||
|
* @param height height
|
||||||
|
* @param bitmap bitmap
|
||||||
|
* @param rotation rotation
|
||||||
|
*/
|
||||||
|
void canvas_draw_u8g2_bitmap(
|
||||||
|
u8g2_t* u8g2,
|
||||||
|
uint8_t x,
|
||||||
|
uint8_t y,
|
||||||
|
uint8_t width,
|
||||||
|
uint8_t height,
|
||||||
|
const uint8_t* bitmap,
|
||||||
|
uint8_t rotation);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <flipper_application/api_hashtable/compilesort.hpp>
|
#include <flipper_application/api_hashtable/compilesort.hpp>
|
||||||
|
|
||||||
/* Generated table */
|
/* Generated table */
|
||||||
#include <symbols.h>
|
#include <firmware_api_table.h>
|
||||||
|
|
||||||
static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!");
|
static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!");
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 654 B |
|
Before Width: | Height: | Size: 669 B |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 303 B |
@@ -68,7 +68,7 @@ env = ENV.Clone(
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SDK_APISYMS=None,
|
FW_API_TABLE=None,
|
||||||
_APP_ICONS=None,
|
_APP_ICONS=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ Depends(
|
|||||||
[
|
[
|
||||||
fwenv["FW_VERSION_JSON"],
|
fwenv["FW_VERSION_JSON"],
|
||||||
fwenv["FW_ASSETS_HEADERS"],
|
fwenv["FW_ASSETS_HEADERS"],
|
||||||
fwenv["SDK_APISYMS"],
|
fwenv["FW_API_TABLE"],
|
||||||
fwenv["_APP_ICONS"],
|
fwenv["_APP_ICONS"],
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,20.0,,
|
Version,+,20.1,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
Header,+,applications/services/cli/cli_vcp.h,,
|
Header,+,applications/services/cli/cli_vcp.h,,
|
||||||
@@ -540,6 +540,7 @@ Function,+,canvas_draw_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
|||||||
Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t"
|
Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t"
|
||||||
Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*"
|
Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*"
|
||||||
Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*"
|
Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*"
|
||||||
|
Function,+,canvas_draw_icon_ex,void,"Canvas*, uint8_t, uint8_t, const Icon*, IconRotation"
|
||||||
Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||||
Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||||
Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||||
@@ -876,12 +877,6 @@ Function,-,furi_hal_clock_resume_tick,void,
|
|||||||
Function,-,furi_hal_clock_suspend_tick,void,
|
Function,-,furi_hal_clock_suspend_tick,void,
|
||||||
Function,-,furi_hal_clock_switch_to_hsi,void,
|
Function,-,furi_hal_clock_switch_to_hsi,void,
|
||||||
Function,-,furi_hal_clock_switch_to_pll,void,
|
Function,-,furi_hal_clock_switch_to_pll,void,
|
||||||
Function,-,compress_alloc,Compress*,uint16_t
|
|
||||||
Function,-,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
|
||||||
Function,-,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
|
||||||
Function,-,compress_free,void,Compress*
|
|
||||||
Function,-,compress_icon_decode,void,"const uint8_t*, uint8_t**"
|
|
||||||
Function,-,compress_icon_init,void,
|
|
||||||
Function,+,furi_hal_console_disable,void,
|
Function,+,furi_hal_console_disable,void,
|
||||||
Function,+,furi_hal_console_enable,void,
|
Function,+,furi_hal_console_enable,void,
|
||||||
Function,+,furi_hal_console_init,void,
|
Function,+,furi_hal_console_init,void,
|
||||||
|
|||||||
|
@@ -655,12 +655,14 @@ Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t"
|
|||||||
Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*"
|
Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*"
|
||||||
Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*"
|
Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*"
|
||||||
Function,+,canvas_draw_icon_bitmap,void,"Canvas*, uint8_t, uint8_t, int16_t, int16_t, const Icon*"
|
Function,+,canvas_draw_icon_bitmap,void,"Canvas*, uint8_t, uint8_t, int16_t, int16_t, const Icon*"
|
||||||
|
Function,+,canvas_draw_icon_ex,void,"Canvas*, uint8_t, uint8_t, const Icon*, IconRotation"
|
||||||
Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||||
Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||||
Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||||
Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*"
|
Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*"
|
||||||
Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*"
|
Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*"
|
||||||
Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection"
|
Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection"
|
||||||
|
Function,+,canvas_draw_u8g2_bitmap,void,"u8g2_t*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*, uint8_t"
|
||||||
Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*"
|
Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*"
|
||||||
Function,-,canvas_frame_set,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
Function,-,canvas_frame_set,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||||
Function,-,canvas_free,void,Canvas*
|
Function,-,canvas_free,void,Canvas*
|
||||||
|
|||||||
|
@@ -22,16 +22,14 @@
|
|||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include "sector_cache.h"
|
#include "sector_cache.h"
|
||||||
|
|
||||||
static volatile DSTATUS Stat = STA_NOINIT;
|
|
||||||
|
|
||||||
static DSTATUS driver_check_status(BYTE lun) {
|
static DSTATUS driver_check_status(BYTE lun) {
|
||||||
UNUSED(lun);
|
UNUSED(lun);
|
||||||
Stat = STA_NOINIT;
|
DSTATUS status = 0;
|
||||||
if(sd_get_card_state() == SdSpiStatusOK) {
|
if(sd_get_card_state() != SdSpiStatusOK) {
|
||||||
Stat &= ~STA_NOINIT;
|
status = STA_NOINIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Stat;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DSTATUS driver_initialize(BYTE pdrv);
|
static DSTATUS driver_initialize(BYTE pdrv);
|
||||||
@@ -127,6 +125,16 @@ static bool sd_device_write(uint32_t* buff, uint32_t sector, uint32_t count) {
|
|||||||
* @retval DSTATUS: Operation status
|
* @retval DSTATUS: Operation status
|
||||||
*/
|
*/
|
||||||
static DSTATUS driver_initialize(BYTE pdrv) {
|
static DSTATUS driver_initialize(BYTE pdrv) {
|
||||||
|
UNUSED(pdrv);
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets Disk Status
|
||||||
|
* @param pdrv: Physical drive number (0..)
|
||||||
|
* @retval DSTATUS: Operation status
|
||||||
|
*/
|
||||||
|
static DSTATUS driver_status(BYTE pdrv) {
|
||||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||||
|
|
||||||
@@ -138,16 +146,6 @@ static DSTATUS driver_initialize(BYTE pdrv) {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Gets Disk Status
|
|
||||||
* @param pdrv: Physical drive number (0..)
|
|
||||||
* @retval DSTATUS: Operation status
|
|
||||||
*/
|
|
||||||
static DSTATUS driver_status(BYTE pdrv) {
|
|
||||||
UNUSED(pdrv);
|
|
||||||
return Stat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reads Sector(s)
|
* @brief Reads Sector(s)
|
||||||
* @param pdrv: Physical drive number (0..)
|
* @param pdrv: Physical drive number (0..)
|
||||||
@@ -244,15 +242,15 @@ static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT coun
|
|||||||
* @retval DRESULT: Operation result
|
* @retval DRESULT: Operation result
|
||||||
*/
|
*/
|
||||||
static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
|
static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
|
||||||
UNUSED(pdrv);
|
|
||||||
DRESULT res = RES_ERROR;
|
DRESULT res = RES_ERROR;
|
||||||
SD_CardInfo CardInfo;
|
SD_CardInfo CardInfo;
|
||||||
|
|
||||||
if(Stat & STA_NOINIT) return RES_NOTRDY;
|
|
||||||
|
|
||||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||||
|
|
||||||
|
DSTATUS status = driver_check_status(pdrv);
|
||||||
|
if(status & STA_NOINIT) return RES_NOTRDY;
|
||||||
|
|
||||||
switch(cmd) {
|
switch(cmd) {
|
||||||
/* Make sure that no pending write process */
|
/* Make sure that no pending write process */
|
||||||
case CTRL_SYNC:
|
case CTRL_SYNC:
|
||||||
|
|||||||
@@ -2,29 +2,24 @@
|
|||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <flipper.h>
|
#include <flipper.h>
|
||||||
#include <alt_boot.h>
|
#include <alt_boot.h>
|
||||||
#include <u8g2_glue.h>
|
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
#include <toolbox/compress.h>
|
#include <toolbox/compress.h>
|
||||||
|
#include <gui/canvas.h>
|
||||||
|
#include <gui/canvas_i.h>
|
||||||
|
|
||||||
void flipper_boot_dfu_show_splash() {
|
void flipper_boot_dfu_show_splash() {
|
||||||
// Initialize
|
// Initialize
|
||||||
CompressIcon* compress_icon = compress_icon_alloc();
|
Canvas* canvas = canvas_init();
|
||||||
|
|
||||||
u8g2_t* fb = malloc(sizeof(u8g2_t));
|
canvas_set_color(canvas, ColorBlack);
|
||||||
memset(fb, 0, sizeof(u8g2_t));
|
canvas_set_font(canvas, FontPrimary);
|
||||||
u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
|
||||||
u8g2_InitDisplay(fb);
|
|
||||||
u8g2_SetDrawColor(fb, 0x01);
|
|
||||||
uint8_t* splash_data = NULL;
|
|
||||||
compress_icon_decode(compress_icon, icon_get_data(&I_DFU_128x50), &splash_data);
|
|
||||||
u8g2_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data);
|
|
||||||
u8g2_SetFont(fb, u8g2_font_helvB08_tr);
|
|
||||||
u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode");
|
|
||||||
u8g2_DrawStr(fb, 2, 21, "DFU Started");
|
|
||||||
u8g2_SetPowerSave(fb, 0);
|
|
||||||
u8g2_SendBuffer(fb);
|
|
||||||
|
|
||||||
compress_icon_free(compress_icon);
|
canvas_draw_icon(canvas, 0, 64 - 50, &I_DFU_128x50);
|
||||||
|
canvas_draw_str(canvas, 2, 8, "Update & Recovery Mode");
|
||||||
|
canvas_draw_str(canvas, 2, 21, "DFU Started");
|
||||||
|
canvas_commit(canvas);
|
||||||
|
|
||||||
|
canvas_free(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flipper_boot_dfu_exec() {
|
void flipper_boot_dfu_exec() {
|
||||||
|
|||||||
@@ -2,44 +2,43 @@
|
|||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <flipper.h>
|
#include <flipper.h>
|
||||||
#include <alt_boot.h>
|
#include <alt_boot.h>
|
||||||
#include <u8g2_glue.h>
|
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
#include <toolbox/compress.h>
|
#include <toolbox/compress.h>
|
||||||
|
#include <gui/canvas.h>
|
||||||
|
#include <gui/canvas_i.h>
|
||||||
|
|
||||||
#define COUNTER_VALUE (136U)
|
#define COUNTER_VALUE (136U)
|
||||||
|
|
||||||
static void flipper_boot_recovery_draw_splash(u8g2_t* fb, size_t progress) {
|
static void flipper_boot_recovery_draw_progress(Canvas* canvas, size_t progress) {
|
||||||
if(progress < COUNTER_VALUE) {
|
if(progress < COUNTER_VALUE) {
|
||||||
// Fill the progress bar while the progress is going down
|
// Fill the progress bar while the progress is going down
|
||||||
u8g2_SetDrawColor(fb, 0x01);
|
canvas_draw_rframe(canvas, 59, 41, 69, 8, 2);
|
||||||
u8g2_DrawRFrame(fb, 59, 41, 69, 8, 2);
|
|
||||||
size_t width = (COUNTER_VALUE - progress) * 68 / COUNTER_VALUE;
|
size_t width = (COUNTER_VALUE - progress) * 68 / COUNTER_VALUE;
|
||||||
u8g2_DrawBox(fb, 60, 42, width, 6);
|
canvas_draw_box(canvas, 60, 42, width, 6);
|
||||||
} else {
|
} else {
|
||||||
u8g2_SetDrawColor(fb, 0x00);
|
canvas_draw_rframe(canvas, 59, 41, 69, 8, 2);
|
||||||
u8g2_DrawRBox(fb, 59, 41, 69, 8, 2);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
|
canvas_draw_box(canvas, 60, 42, 67, 6);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8g2_SendBuffer(fb);
|
canvas_commit(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flipper_boot_recovery_draw_splash(Canvas* canvas) {
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
|
||||||
|
canvas_draw_icon(canvas, 0, 0, &I_Erase_pin_128x64);
|
||||||
|
|
||||||
|
canvas_commit(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flipper_boot_recovery_exec() {
|
void flipper_boot_recovery_exec() {
|
||||||
u8g2_t* fb = malloc(sizeof(u8g2_t));
|
Canvas* canvas = canvas_init();
|
||||||
u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
|
||||||
u8g2_InitDisplay(fb);
|
|
||||||
|
|
||||||
CompressIcon* compress_icon = compress_icon_alloc();
|
// Show recovery splashscreen
|
||||||
uint8_t* splash_data = NULL;
|
flipper_boot_recovery_draw_splash(canvas);
|
||||||
compress_icon_decode(compress_icon, icon_get_data(&I_Erase_pin_128x64), &splash_data);
|
|
||||||
|
|
||||||
u8g2_ClearBuffer(fb);
|
|
||||||
u8g2_SetDrawColor(fb, 0x01);
|
|
||||||
|
|
||||||
// Draw the recovery picture
|
|
||||||
u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data);
|
|
||||||
u8g2_SendBuffer(fb);
|
|
||||||
u8g2_SetPowerSave(fb, 0);
|
|
||||||
compress_icon_free(compress_icon);
|
|
||||||
|
|
||||||
size_t counter = COUNTER_VALUE;
|
size_t counter = COUNTER_VALUE;
|
||||||
while(counter) {
|
while(counter) {
|
||||||
@@ -53,7 +52,7 @@ void flipper_boot_recovery_exec() {
|
|||||||
counter = COUNTER_VALUE;
|
counter = COUNTER_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
flipper_boot_recovery_draw_splash(fb, counter);
|
flipper_boot_recovery_draw_progress(canvas, counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!counter) {
|
if(!counter) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from SCons.Builder import Builder
|
from SCons.Builder import Builder
|
||||||
from SCons.Action import Action
|
from SCons.Action import Action
|
||||||
from SCons.Errors import SConsEnvironmentError
|
from SCons.Errors import StopError
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -90,7 +90,7 @@ def proto_ver_generator(target, source, env):
|
|||||||
source_dir=src_dir,
|
source_dir=src_dir,
|
||||||
)
|
)
|
||||||
except (subprocess.CalledProcessError, EnvironmentError) as e:
|
except (subprocess.CalledProcessError, EnvironmentError) as e:
|
||||||
raise SConsEnvironmentError("Git: describe failed")
|
raise StopError("Git: describe failed")
|
||||||
|
|
||||||
git_major, git_minor = git_describe.split(".")
|
git_major, git_minor = git_describe.split(".")
|
||||||
version_file_data = (
|
version_file_data = (
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ from fbt.sdk.cache import SdkCache
|
|||||||
from fbt.util import extract_abs_dir_path
|
from fbt.util import extract_abs_dir_path
|
||||||
|
|
||||||
|
|
||||||
|
_FAP_META_SECTION = ".fapmeta"
|
||||||
|
_FAP_FILEASSETS_SECTION = ".fapassets"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class FlipperExternalAppInfo:
|
class FlipperExternalAppInfo:
|
||||||
app: FlipperApplication
|
app: FlipperApplication
|
||||||
@@ -234,6 +238,8 @@ def BuildAppElf(env, app):
|
|||||||
|
|
||||||
|
|
||||||
def prepare_app_metadata(target, source, env):
|
def prepare_app_metadata(target, source, env):
|
||||||
|
metadata_node = next(filter(lambda t: t.name.endswith(_FAP_META_SECTION), target))
|
||||||
|
|
||||||
sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=True)
|
sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=True)
|
||||||
|
|
||||||
if not sdk_cache.is_buildable():
|
if not sdk_cache.is_buildable():
|
||||||
@@ -242,8 +248,7 @@ def prepare_app_metadata(target, source, env):
|
|||||||
)
|
)
|
||||||
|
|
||||||
app = env["APP"]
|
app = env["APP"]
|
||||||
meta_file_name = source[0].path + ".meta"
|
with open(metadata_node.abspath, "wb") as f:
|
||||||
with open(meta_file_name, "wb") as f:
|
|
||||||
f.write(
|
f.write(
|
||||||
assemble_manifest_data(
|
assemble_manifest_data(
|
||||||
app_manifest=app,
|
app_manifest=app,
|
||||||
@@ -337,24 +342,26 @@ def embed_app_metadata_emitter(target, source, env):
|
|||||||
if app.apptype == FlipperAppType.PLUGIN:
|
if app.apptype == FlipperAppType.PLUGIN:
|
||||||
target[0].name = target[0].name.replace(".fap", ".fal")
|
target[0].name = target[0].name.replace(".fap", ".fal")
|
||||||
|
|
||||||
meta_file_name = source[0].path + ".meta"
|
target.append(env.File(source[0].abspath + _FAP_META_SECTION))
|
||||||
target.append("#" + meta_file_name)
|
|
||||||
|
|
||||||
if app.fap_file_assets:
|
if app.fap_file_assets:
|
||||||
files_section = source[0].path + ".files.section"
|
target.append(env.File(source[0].abspath + _FAP_FILEASSETS_SECTION))
|
||||||
target.append("#" + files_section)
|
|
||||||
|
|
||||||
return (target, source)
|
return (target, source)
|
||||||
|
|
||||||
|
|
||||||
def prepare_app_files(target, source, env):
|
def prepare_app_files(target, source, env):
|
||||||
|
files_section_node = next(
|
||||||
|
filter(lambda t: t.name.endswith(_FAP_FILEASSETS_SECTION), target)
|
||||||
|
)
|
||||||
|
|
||||||
app = env["APP"]
|
app = env["APP"]
|
||||||
directory = app._appdir.Dir(app.fap_file_assets)
|
directory = env.Dir(app._apppath).Dir(app.fap_file_assets)
|
||||||
if not directory.exists():
|
if not directory.exists():
|
||||||
raise UserError(f"File asset directory {directory} does not exist")
|
raise UserError(f"File asset directory {directory} does not exist")
|
||||||
|
|
||||||
bundler = FileBundler(directory.abspath)
|
bundler = FileBundler(directory.abspath)
|
||||||
bundler.export(source[0].path + ".files.section")
|
bundler.export(files_section_node.abspath)
|
||||||
|
|
||||||
|
|
||||||
def generate_embed_app_metadata_actions(source, target, env, for_signature):
|
def generate_embed_app_metadata_actions(source, target, env, for_signature):
|
||||||
@@ -367,15 +374,15 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature):
|
|||||||
objcopy_str = (
|
objcopy_str = (
|
||||||
"${OBJCOPY} "
|
"${OBJCOPY} "
|
||||||
"--remove-section .ARM.attributes "
|
"--remove-section .ARM.attributes "
|
||||||
"--add-section .fapmeta=${SOURCE}.meta "
|
"--add-section ${_FAP_META_SECTION}=${SOURCE}${_FAP_META_SECTION} "
|
||||||
)
|
)
|
||||||
|
|
||||||
if app.fap_file_assets:
|
if app.fap_file_assets:
|
||||||
actions.append(Action(prepare_app_files, "$APPFILE_COMSTR"))
|
actions.append(Action(prepare_app_files, "$APPFILE_COMSTR"))
|
||||||
objcopy_str += "--add-section .fapassets=${SOURCE}.files.section "
|
objcopy_str += "--add-section ${_FAP_FILEASSETS_SECTION}=${SOURCE}${_FAP_FILEASSETS_SECTION} "
|
||||||
|
|
||||||
objcopy_str += (
|
objcopy_str += (
|
||||||
"--set-section-flags .fapmeta=contents,noload,readonly,data "
|
"--set-section-flags ${_FAP_META_SECTION}=contents,noload,readonly,data "
|
||||||
"--strip-debug --strip-unneeded "
|
"--strip-debug --strip-unneeded "
|
||||||
"--add-gnu-debuglink=${SOURCE} "
|
"--add-gnu-debuglink=${SOURCE} "
|
||||||
"${SOURCES} ${TARGET}"
|
"${SOURCES} ${TARGET}"
|
||||||
@@ -391,6 +398,51 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature):
|
|||||||
return Action(actions)
|
return Action(actions)
|
||||||
|
|
||||||
|
|
||||||
|
def AddAppLaunchTarget(env, appname, launch_target_name):
|
||||||
|
deploy_sources, flipp_dist_paths, validators = [], [], []
|
||||||
|
run_script_extra_ars = ""
|
||||||
|
|
||||||
|
def _add_dist_targets(app_artifacts):
|
||||||
|
validators.append(app_artifacts.validator)
|
||||||
|
for _, ext_path in app_artifacts.dist_entries:
|
||||||
|
deploy_sources.append(app_artifacts.compact)
|
||||||
|
flipp_dist_paths.append(f"/ext/{ext_path}")
|
||||||
|
return app_artifacts
|
||||||
|
|
||||||
|
def _add_host_app_to_targets(host_app):
|
||||||
|
artifacts_app_to_run = env["EXT_APPS"].get(host_app.appid, None)
|
||||||
|
_add_dist_targets(artifacts_app_to_run)
|
||||||
|
for plugin in host_app._plugins:
|
||||||
|
_add_dist_targets(env["EXT_APPS"].get(plugin.appid, None))
|
||||||
|
|
||||||
|
artifacts_app_to_run = env.GetExtAppByIdOrPath(appname)
|
||||||
|
if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN:
|
||||||
|
# We deploy host app instead
|
||||||
|
host_app = env["APPMGR"].get(artifacts_app_to_run.app.requires[0])
|
||||||
|
|
||||||
|
if host_app:
|
||||||
|
if host_app.apptype == FlipperAppType.EXTERNAL:
|
||||||
|
_add_host_app_to_targets(host_app)
|
||||||
|
else:
|
||||||
|
# host app is a built-in app
|
||||||
|
run_script_extra_ars = f"-a {host_app.name}"
|
||||||
|
_add_dist_targets(artifacts_app_to_run)
|
||||||
|
else:
|
||||||
|
raise UserError("Host app is unknown")
|
||||||
|
else:
|
||||||
|
_add_host_app_to_targets(artifacts_app_to_run.app)
|
||||||
|
|
||||||
|
# print(deploy_sources, flipp_dist_paths)
|
||||||
|
env.PhonyTarget(
|
||||||
|
launch_target_name,
|
||||||
|
'${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}',
|
||||||
|
source=deploy_sources,
|
||||||
|
FLIPPER_FILE_TARGETS=flipp_dist_paths,
|
||||||
|
EXTRA_ARGS=run_script_extra_ars,
|
||||||
|
)
|
||||||
|
env.Alias(launch_target_name, validators)
|
||||||
|
|
||||||
|
|
||||||
def generate(env, **kw):
|
def generate(env, **kw):
|
||||||
env.SetDefault(
|
env.SetDefault(
|
||||||
EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}",
|
EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}",
|
||||||
@@ -410,10 +462,14 @@ def generate(env, **kw):
|
|||||||
EXT_APPS={}, # appid -> FlipperExternalAppInfo
|
EXT_APPS={}, # appid -> FlipperExternalAppInfo
|
||||||
EXT_LIBS={},
|
EXT_LIBS={},
|
||||||
_APP_ICONS=[],
|
_APP_ICONS=[],
|
||||||
|
_FAP_META_SECTION=_FAP_META_SECTION,
|
||||||
|
_FAP_FILEASSETS_SECTION=_FAP_FILEASSETS_SECTION,
|
||||||
)
|
)
|
||||||
|
|
||||||
env.AddMethod(BuildAppElf)
|
env.AddMethod(BuildAppElf)
|
||||||
env.AddMethod(GetExtAppByIdOrPath)
|
env.AddMethod(GetExtAppByIdOrPath)
|
||||||
|
env.AddMethod(AddAppLaunchTarget)
|
||||||
|
|
||||||
env.Append(
|
env.Append(
|
||||||
BUILDERS={
|
BUILDERS={
|
||||||
"FapDist": Builder(
|
"FapDist": Builder(
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ def ProcessSdkDepends(env, filename):
|
|||||||
return depends
|
return depends
|
||||||
|
|
||||||
|
|
||||||
def prebuild_sdk_emitter(target, source, env):
|
def api_amalgam_emitter(target, source, env):
|
||||||
target.append(env.ChangeFileExtension(target[0], ".d"))
|
target.append(env.ChangeFileExtension(target[0], ".d"))
|
||||||
target.append(env.ChangeFileExtension(target[0], ".i.c"))
|
target.append(env.ChangeFileExtension(target[0], ".i.c"))
|
||||||
return target, source
|
return target, source
|
||||||
|
|
||||||
|
|
||||||
def prebuild_sdk_create_origin_file(target, source, env):
|
def api_amalgam_gen_origin_header(target, source, env):
|
||||||
mega_file = env.subst("${TARGET}.c", target=target[0])
|
mega_file = env.subst("${TARGET}.c", target=target[0])
|
||||||
with open(mega_file, "wt") as sdk_c:
|
with open(mega_file, "wt") as sdk_c:
|
||||||
sdk_c.write(
|
sdk_c.write(
|
||||||
@@ -87,6 +87,7 @@ class SdkMeta:
|
|||||||
class SdkTreeBuilder:
|
class SdkTreeBuilder:
|
||||||
SDK_DIR_SUBST = "SDK_ROOT_DIR"
|
SDK_DIR_SUBST = "SDK_ROOT_DIR"
|
||||||
SDK_APP_EP_SUBST = "SDK_APP_EP_SUBST"
|
SDK_APP_EP_SUBST = "SDK_APP_EP_SUBST"
|
||||||
|
HEADER_EXTENSIONS = [".h", ".hpp"]
|
||||||
|
|
||||||
def __init__(self, env, target, source) -> None:
|
def __init__(self, env, target, source) -> None:
|
||||||
self.env = env
|
self.env = env
|
||||||
@@ -111,7 +112,10 @@ class SdkTreeBuilder:
|
|||||||
lines = LogicalLines(deps_f).readlines()
|
lines = LogicalLines(deps_f).readlines()
|
||||||
_, depends = lines[0].split(":", 1)
|
_, depends = lines[0].split(":", 1)
|
||||||
self.header_depends = list(
|
self.header_depends = list(
|
||||||
filter(lambda fname: fname.endswith(".h"), depends.split()),
|
filter(
|
||||||
|
lambda fname: any(map(fname.endswith, self.HEADER_EXTENSIONS)),
|
||||||
|
depends.split(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.header_depends.append(self.sdk_env.subst("${LINKER_SCRIPT_PATH}"))
|
self.header_depends.append(self.sdk_env.subst("${LINKER_SCRIPT_PATH}"))
|
||||||
self.header_depends.append(self.sdk_env.subst("${SDK_DEFINITION}"))
|
self.header_depends.append(self.sdk_env.subst("${SDK_DEFINITION}"))
|
||||||
@@ -180,12 +184,12 @@ class SdkTreeBuilder:
|
|||||||
self._generate_sdk_meta()
|
self._generate_sdk_meta()
|
||||||
|
|
||||||
|
|
||||||
def deploy_sdk_tree_action(target, source, env):
|
def deploy_sdk_header_tree_action(target, source, env):
|
||||||
sdk_tree = SdkTreeBuilder(env, target, source)
|
sdk_tree = SdkTreeBuilder(env, target, source)
|
||||||
return sdk_tree.deploy_action()
|
return sdk_tree.deploy_action()
|
||||||
|
|
||||||
|
|
||||||
def deploy_sdk_tree_emitter(target, source, env):
|
def deploy_sdk_header_tree_emitter(target, source, env):
|
||||||
sdk_tree = SdkTreeBuilder(env, target, source)
|
sdk_tree = SdkTreeBuilder(env, target, source)
|
||||||
return sdk_tree.emitter(target, source, env)
|
return sdk_tree.emitter(target, source, env)
|
||||||
|
|
||||||
@@ -224,7 +228,7 @@ def _check_sdk_is_up2date(sdk_cache: SdkCache):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_sdk_cache(source, target, env):
|
def validate_api_cache(source, target, env):
|
||||||
# print(f"Generating SDK for {source[0]} to {target[0]}")
|
# print(f"Generating SDK for {source[0]} to {target[0]}")
|
||||||
current_sdk = SdkCollector()
|
current_sdk = SdkCollector()
|
||||||
current_sdk.process_source_file_for_sdk(source[0].path)
|
current_sdk.process_source_file_for_sdk(source[0].path)
|
||||||
@@ -237,7 +241,7 @@ def validate_sdk_cache(source, target, env):
|
|||||||
_check_sdk_is_up2date(sdk_cache)
|
_check_sdk_is_up2date(sdk_cache)
|
||||||
|
|
||||||
|
|
||||||
def generate_sdk_symbols(source, target, env):
|
def generate_api_table(source, target, env):
|
||||||
sdk_cache = SdkCache(source[0].path)
|
sdk_cache = SdkCache(source[0].path)
|
||||||
_check_sdk_is_up2date(sdk_cache)
|
_check_sdk_is_up2date(sdk_cache)
|
||||||
|
|
||||||
@@ -249,11 +253,11 @@ def generate_sdk_symbols(source, target, env):
|
|||||||
def generate(env, **kw):
|
def generate(env, **kw):
|
||||||
if not env["VERBOSE"]:
|
if not env["VERBOSE"]:
|
||||||
env.SetDefault(
|
env.SetDefault(
|
||||||
SDK_PREGEN_COMSTR="\tPREGEN\t${TARGET}",
|
SDK_AMALGAMATE_HEADER_COMSTR="\tAPIPREP\t${TARGET}",
|
||||||
SDK_COMSTR="\tSDKSRC\t${TARGET}",
|
SDK_AMALGAMATE_PP_COMSTR="\tAPIPP\t${TARGET}",
|
||||||
SDKSYM_UPDATER_COMSTR="\tSDKCHK\t${TARGET}",
|
SDKSYM_UPDATER_COMSTR="\tSDKCHK\t${TARGET}",
|
||||||
SDKSYM_GENERATOR_COMSTR="\tSDKSYM\t${TARGET}",
|
APITABLE_GENERATOR_COMSTR="\tAPITBL\t${TARGET}",
|
||||||
SDKDEPLOY_COMSTR="\tSDKTREE\t${TARGET}",
|
SDKTREE_COMSTR="\tSDKTREE\t${TARGET}",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Filtering out things cxxheaderparser cannot handle
|
# Filtering out things cxxheaderparser cannot handle
|
||||||
@@ -274,40 +278,40 @@ def generate(env, **kw):
|
|||||||
env.AddMethod(ProcessSdkDepends)
|
env.AddMethod(ProcessSdkDepends)
|
||||||
env.Append(
|
env.Append(
|
||||||
BUILDERS={
|
BUILDERS={
|
||||||
"SDKPrebuilder": Builder(
|
"ApiAmalgamator": Builder(
|
||||||
emitter=prebuild_sdk_emitter,
|
emitter=api_amalgam_emitter,
|
||||||
action=[
|
action=[
|
||||||
Action(
|
Action(
|
||||||
prebuild_sdk_create_origin_file,
|
api_amalgam_gen_origin_header,
|
||||||
"$SDK_PREGEN_COMSTR",
|
"$SDK_AMALGAMATE_HEADER_COMSTR",
|
||||||
),
|
),
|
||||||
Action(
|
Action(
|
||||||
"$CC -o $TARGET -E -P $CCFLAGS $_CCCOMCOM $SDK_PP_FLAGS -MMD ${TARGET}.c",
|
"$CC -o $TARGET -E -P $CCFLAGS $_CCCOMCOM $SDK_PP_FLAGS -MMD ${TARGET}.c",
|
||||||
"$SDK_COMSTR",
|
"$SDK_AMALGAMATE_PP_COMSTR",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
suffix=".i",
|
suffix=".i",
|
||||||
),
|
),
|
||||||
"SDKTree": Builder(
|
"SDKHeaderTreeExtractor": Builder(
|
||||||
action=Action(
|
action=Action(
|
||||||
deploy_sdk_tree_action,
|
deploy_sdk_header_tree_action,
|
||||||
"$SDKDEPLOY_COMSTR",
|
"$SDKTREE_COMSTR",
|
||||||
),
|
),
|
||||||
emitter=deploy_sdk_tree_emitter,
|
emitter=deploy_sdk_header_tree_emitter,
|
||||||
src_suffix=".d",
|
src_suffix=".d",
|
||||||
),
|
),
|
||||||
"SDKSymUpdater": Builder(
|
"ApiTableValidator": Builder(
|
||||||
action=Action(
|
action=Action(
|
||||||
validate_sdk_cache,
|
validate_api_cache,
|
||||||
"$SDKSYM_UPDATER_COMSTR",
|
"$SDKSYM_UPDATER_COMSTR",
|
||||||
),
|
),
|
||||||
suffix=".csv",
|
suffix=".csv",
|
||||||
src_suffix=".i",
|
src_suffix=".i",
|
||||||
),
|
),
|
||||||
"SDKSymGenerator": Builder(
|
"ApiSymbolTable": Builder(
|
||||||
action=Action(
|
action=Action(
|
||||||
generate_sdk_symbols,
|
generate_api_table,
|
||||||
"$SDKSYM_GENERATOR_COMSTR",
|
"$APITABLE_GENERATOR_COMSTR",
|
||||||
),
|
),
|
||||||
suffix=".h",
|
suffix=".h",
|
||||||
src_suffix=".csv",
|
src_suffix=".csv",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import SCons.Warnings as Warnings
|
import SCons.Warnings as Warnings
|
||||||
|
from SCons.Errors import UserError
|
||||||
|
|
||||||
|
|
||||||
# from SCons.Script.Main import find_deepest_user_frame
|
# from SCons.Script.Main import find_deepest_user_frame
|
||||||
|
|
||||||
@@ -36,6 +38,11 @@ def fbt_warning(e):
|
|||||||
|
|
||||||
|
|
||||||
def generate(env):
|
def generate(env):
|
||||||
|
if env.get("UFBT_WORK_DIR"):
|
||||||
|
raise UserError(
|
||||||
|
"You're trying to use a new format SDK on a legacy ufbt version. "
|
||||||
|
"Please update ufbt to a version from PyPI: https://pypi.org/project/ufbt/"
|
||||||
|
)
|
||||||
Warnings._warningOut = fbt_warning
|
Warnings._warningOut = fbt_warning
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ class StorageErrorCode(enum.Enum):
|
|||||||
|
|
||||||
|
|
||||||
class FlipperStorageException(Exception):
|
class FlipperStorageException(Exception):
|
||||||
def __init__(self, message):
|
@staticmethod
|
||||||
super().__init__(f"Storage error: {message}")
|
def from_error_code(path: str, error_code: StorageErrorCode):
|
||||||
|
return FlipperStorageException(
|
||||||
def __init__(self, path: str, error_code: StorageErrorCode):
|
f"Storage error: path '{path}': {error_code.value}"
|
||||||
super().__init__(f"Storage error: path '{path}': {error_code.value}")
|
)
|
||||||
|
|
||||||
|
|
||||||
class BufferedRead:
|
class BufferedRead:
|
||||||
@@ -247,7 +247,9 @@ class FlipperStorage:
|
|||||||
if self.has_error(answer):
|
if self.has_error(answer):
|
||||||
last_error = self.get_error(answer)
|
last_error = self.get_error(answer)
|
||||||
self.read.until(self.CLI_PROMPT)
|
self.read.until(self.CLI_PROMPT)
|
||||||
raise FlipperStorageException(filename_to, last_error)
|
raise FlipperStorageException.from_error_code(
|
||||||
|
filename_to, last_error
|
||||||
|
)
|
||||||
|
|
||||||
self.port.write(filedata)
|
self.port.write(filedata)
|
||||||
self.read.until(self.CLI_PROMPT)
|
self.read.until(self.CLI_PROMPT)
|
||||||
@@ -319,7 +321,7 @@ class FlipperStorage:
|
|||||||
StorageErrorCode.INVALID_NAME,
|
StorageErrorCode.INVALID_NAME,
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
raise FlipperStorageException(path, error_code)
|
raise FlipperStorageException.from_error_code(path, error_code)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -333,7 +335,7 @@ class FlipperStorage:
|
|||||||
|
|
||||||
def _check_no_error(self, response, path=None):
|
def _check_no_error(self, response, path=None):
|
||||||
if self.has_error(response):
|
if self.has_error(response):
|
||||||
raise FlipperStorageException(self.get_error(response))
|
raise FlipperStorageException.from_error_code(self.get_error(response))
|
||||||
|
|
||||||
def size(self, path: str):
|
def size(self, path: str):
|
||||||
"""file size on Flipper"""
|
"""file size on Flipper"""
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ def parse_args():
|
|||||||
|
|
||||||
def get_commit_json(event):
|
def get_commit_json(event):
|
||||||
context = ssl._create_unverified_context()
|
context = ssl._create_unverified_context()
|
||||||
with urllib.request.urlopen(
|
commit_url = event["pull_request"]["base"]["repo"]["commits_url"].replace(
|
||||||
event["pull_request"]["_links"]["commits"]["href"], context=context
|
"{/sha}", f"/{event['after']}"
|
||||||
) as commit_file:
|
)
|
||||||
|
with urllib.request.urlopen(commit_url, context=context) as commit_file:
|
||||||
commit_json = json.loads(commit_file.read().decode("utf-8"))
|
commit_json = json.loads(commit_file.read().decode("utf-8"))
|
||||||
return commit_json
|
return commit_json
|
||||||
|
|
||||||
@@ -43,8 +44,8 @@ def get_details(event, args):
|
|||||||
current_time = datetime.datetime.utcnow().date()
|
current_time = datetime.datetime.utcnow().date()
|
||||||
if args.type == "pull":
|
if args.type == "pull":
|
||||||
commit_json = get_commit_json(event)
|
commit_json = get_commit_json(event)
|
||||||
data["commit_comment"] = shlex.quote(commit_json[-1]["commit"]["message"])
|
data["commit_comment"] = shlex.quote(commit_json["commit"]["message"])
|
||||||
data["commit_hash"] = commit_json[-1]["sha"]
|
data["commit_hash"] = commit_json["sha"]
|
||||||
ref = event["pull_request"]["head"]["ref"]
|
ref = event["pull_request"]["head"]["ref"]
|
||||||
data["pull_id"] = event["pull_request"]["number"]
|
data["pull_id"] = event["pull_request"]["number"]
|
||||||
data["pull_name"] = shlex.quote(event["pull_request"]["title"])
|
data["pull_name"] = shlex.quote(event["pull_request"]["title"])
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from flipper.app import App
|
import json
|
||||||
from os.path import join, exists, relpath
|
|
||||||
from os import makedirs, walk, environ
|
|
||||||
from update import Main as UpdateMain
|
|
||||||
import shutil
|
import shutil
|
||||||
import zipfile
|
|
||||||
import tarfile
|
import tarfile
|
||||||
|
import zipfile
|
||||||
|
from os import makedirs, walk, environ
|
||||||
|
from os.path import exists, join, relpath, basename, split
|
||||||
|
|
||||||
from ansi.color import fg
|
from ansi.color import fg
|
||||||
|
from flipper.app import App
|
||||||
|
from update import Main as UpdateMain
|
||||||
|
|
||||||
|
|
||||||
class ProjectDir:
|
class ProjectDir:
|
||||||
@@ -54,12 +56,19 @@ class Main(App):
|
|||||||
if project_name == "firmware" and filetype != "elf":
|
if project_name == "firmware" and filetype != "elf":
|
||||||
project_name = "full"
|
project_name = "full"
|
||||||
|
|
||||||
return self.get_dist_file_name(project_name, filetype)
|
dist_target_path = self.get_dist_file_name(project_name, filetype)
|
||||||
|
self.note_dist_component(
|
||||||
|
project_name, filetype, self.get_dist_path(dist_target_path)
|
||||||
|
)
|
||||||
|
return dist_target_path
|
||||||
|
|
||||||
|
def note_dist_component(self, component: str, extension: str, srcpath: str) -> None:
|
||||||
|
self._dist_components[f"{component}.{extension}"] = srcpath
|
||||||
|
|
||||||
def get_dist_file_name(self, dist_artifact_type: str, filetype: str) -> str:
|
def get_dist_file_name(self, dist_artifact_type: str, filetype: str) -> str:
|
||||||
return f"{self.DIST_FILE_PREFIX}{self.target}-{dist_artifact_type}-{self.args.suffix}.{filetype}"
|
return f"{self.DIST_FILE_PREFIX}{self.target}-{dist_artifact_type}-{self.args.suffix}.{filetype}"
|
||||||
|
|
||||||
def get_dist_file_path(self, filename: str) -> str:
|
def get_dist_path(self, filename: str) -> str:
|
||||||
return join(self.output_dir_path, filename)
|
return join(self.output_dir_path, filename)
|
||||||
|
|
||||||
def copy_single_project(self, project: ProjectDir) -> None:
|
def copy_single_project(self, project: ProjectDir) -> None:
|
||||||
@@ -69,32 +78,26 @@ class Main(App):
|
|||||||
if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")):
|
if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")):
|
||||||
shutil.copyfile(
|
shutil.copyfile(
|
||||||
src_file,
|
src_file,
|
||||||
self.get_dist_file_path(
|
self.get_dist_path(self.get_project_file_name(project, filetype)),
|
||||||
self.get_project_file_name(project, filetype)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
for foldertype in ("sdk", "lib"):
|
for foldertype in ("sdk_headers", "lib"):
|
||||||
if exists(sdk_folder := join(obj_directory, foldertype)):
|
if exists(sdk_folder := join(obj_directory, foldertype)):
|
||||||
self.package_zip(foldertype, sdk_folder)
|
self.note_dist_component(foldertype, "dir", sdk_folder)
|
||||||
|
|
||||||
def package_zip(self, foldertype, sdk_folder):
|
# TODO: remove this after everyone migrates to new uFBT
|
||||||
|
self.create_zip_stub("lib")
|
||||||
|
|
||||||
|
def create_zip_stub(self, foldertype):
|
||||||
with zipfile.ZipFile(
|
with zipfile.ZipFile(
|
||||||
self.get_dist_file_path(self.get_dist_file_name(foldertype, "zip")),
|
self.get_dist_path(self.get_dist_file_name(foldertype, "zip")),
|
||||||
"w",
|
"w",
|
||||||
zipfile.ZIP_DEFLATED,
|
zipfile.ZIP_DEFLATED,
|
||||||
) as zf:
|
) as _:
|
||||||
for root, _, files in walk(sdk_folder):
|
pass
|
||||||
for file in files:
|
|
||||||
zf.write(
|
|
||||||
join(root, file),
|
|
||||||
relpath(
|
|
||||||
join(root, file),
|
|
||||||
sdk_folder,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def copy(self) -> int:
|
def copy(self) -> int:
|
||||||
self.projects = dict(
|
self._dist_components: dict[str, str] = dict()
|
||||||
|
self.projects: dict[str, ProjectDir] = dict(
|
||||||
map(
|
map(
|
||||||
lambda pd: (pd.project, pd),
|
lambda pd: (pd.project, pd),
|
||||||
map(ProjectDir, self.args.project),
|
map(ProjectDir, self.args.project),
|
||||||
@@ -122,12 +125,18 @@ class Main(App):
|
|||||||
try:
|
try:
|
||||||
shutil.rmtree(self.output_dir_path)
|
shutil.rmtree(self.output_dir_path)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
pass
|
self.logger.warn(f"Failed to clean output directory: {ex}")
|
||||||
|
|
||||||
if not exists(self.output_dir_path):
|
if not exists(self.output_dir_path):
|
||||||
|
self.logger.debug(f"Creating output directory {self.output_dir_path}")
|
||||||
makedirs(self.output_dir_path)
|
makedirs(self.output_dir_path)
|
||||||
|
|
||||||
|
for folder in ("debug", "scripts"):
|
||||||
|
if exists(folder):
|
||||||
|
self.note_dist_component(folder, "dir", folder)
|
||||||
|
|
||||||
for project in self.projects.values():
|
for project in self.projects.values():
|
||||||
|
self.logger.debug(f"Copying {project.project} for {project.target}")
|
||||||
self.copy_single_project(project)
|
self.copy_single_project(project)
|
||||||
|
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
@@ -137,69 +146,140 @@ class Main(App):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.args.version:
|
if self.args.version:
|
||||||
bundle_dir_name = f"{self.target}-update-{self.args.suffix}"[
|
if bundle_result := self.bundle_update_package():
|
||||||
: self.DIST_FOLDER_MAX_NAME_LENGTH
|
return bundle_result
|
||||||
]
|
|
||||||
bundle_dir = join(self.output_dir_path, bundle_dir_name)
|
|
||||||
bundle_args = [
|
|
||||||
"generate",
|
|
||||||
"-d",
|
|
||||||
bundle_dir,
|
|
||||||
"-v",
|
|
||||||
self.args.version,
|
|
||||||
"-t",
|
|
||||||
self.target,
|
|
||||||
"--dfu",
|
|
||||||
self.get_dist_file_path(
|
|
||||||
self.get_project_file_name(self.projects["firmware"], "dfu")
|
|
||||||
),
|
|
||||||
"--stage",
|
|
||||||
self.get_dist_file_path(
|
|
||||||
self.get_project_file_name(self.projects["updater"], "bin")
|
|
||||||
),
|
|
||||||
]
|
|
||||||
if self.args.resources:
|
|
||||||
bundle_args.extend(
|
|
||||||
(
|
|
||||||
"-r",
|
|
||||||
self.args.resources,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
bundle_args.extend(self.other_args)
|
|
||||||
log_custom_fz_name = environ.get("CUSTOM_FLIPPER_NAME", None) or ""
|
|
||||||
if (
|
|
||||||
(log_custom_fz_name != "")
|
|
||||||
and (len(log_custom_fz_name) <= 8)
|
|
||||||
and (log_custom_fz_name.isalnum())
|
|
||||||
and (log_custom_fz_name.isascii())
|
|
||||||
):
|
|
||||||
self.logger.info(
|
|
||||||
f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0:
|
required_components = ("firmware.elf", "full.bin", "update.dir")
|
||||||
self.logger.info(
|
if all(
|
||||||
fg.boldgreen(
|
map(
|
||||||
f"Use this directory to self-update your Flipper:\n\t{bundle_dir}"
|
lambda c: c in self._dist_components,
|
||||||
)
|
required_components,
|
||||||
)
|
)
|
||||||
|
):
|
||||||
# Create tgz archive
|
self.bundle_sdk()
|
||||||
with tarfile.open(
|
|
||||||
join(
|
|
||||||
self.output_dir_path,
|
|
||||||
f"{self.DIST_FILE_PREFIX}{bundle_dir_name}.tgz",
|
|
||||||
),
|
|
||||||
"w:gz",
|
|
||||||
compresslevel=9,
|
|
||||||
format=tarfile.USTAR_FORMAT,
|
|
||||||
) as tar:
|
|
||||||
tar.add(bundle_dir, arcname=bundle_dir_name)
|
|
||||||
|
|
||||||
return bundle_result
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def bundle_sdk(self):
|
||||||
|
self.logger.info("Bundling SDK")
|
||||||
|
components_paths = dict()
|
||||||
|
|
||||||
|
sdk_components_keys = (
|
||||||
|
"full.bin",
|
||||||
|
"firmware.elf",
|
||||||
|
"update.dir",
|
||||||
|
"sdk_headers.dir",
|
||||||
|
"lib.dir",
|
||||||
|
"debug.dir",
|
||||||
|
"scripts.dir",
|
||||||
|
)
|
||||||
|
|
||||||
|
with zipfile.ZipFile(
|
||||||
|
self.get_dist_path(self.get_dist_file_name("sdk", "zip")),
|
||||||
|
"w",
|
||||||
|
zipfile.ZIP_DEFLATED,
|
||||||
|
) as zf:
|
||||||
|
for component_key in sdk_components_keys:
|
||||||
|
component_path = self._dist_components.get(component_key)
|
||||||
|
components_paths[component_key] = basename(component_path)
|
||||||
|
|
||||||
|
if component_key.endswith(".dir"):
|
||||||
|
for root, dirnames, files in walk(component_path):
|
||||||
|
if "__pycache__" in dirnames:
|
||||||
|
dirnames.remove("__pycache__")
|
||||||
|
for file in files:
|
||||||
|
zf.write(
|
||||||
|
join(root, file),
|
||||||
|
join(
|
||||||
|
components_paths[component_key],
|
||||||
|
relpath(
|
||||||
|
join(root, file),
|
||||||
|
component_path,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
zf.write(component_path, basename(component_path))
|
||||||
|
|
||||||
|
zf.writestr(
|
||||||
|
"components.json",
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"hw_target": self.target,
|
||||||
|
"flavor": self.flavor,
|
||||||
|
"version": self.args.version,
|
||||||
|
},
|
||||||
|
"components": components_paths,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def bundle_update_package(self):
|
||||||
|
self.logger.debug(
|
||||||
|
f"Generating update bundle with version {self.args.version} for {self.target}"
|
||||||
|
)
|
||||||
|
bundle_dir_name = f"{self.target}-update-{self.args.suffix}"[
|
||||||
|
: self.DIST_FOLDER_MAX_NAME_LENGTH
|
||||||
|
]
|
||||||
|
bundle_dir = self.get_dist_path(bundle_dir_name)
|
||||||
|
bundle_args = [
|
||||||
|
"generate",
|
||||||
|
"-d",
|
||||||
|
bundle_dir,
|
||||||
|
"-v",
|
||||||
|
self.args.version,
|
||||||
|
"-t",
|
||||||
|
self.target,
|
||||||
|
"--dfu",
|
||||||
|
self.get_dist_path(
|
||||||
|
self.get_project_file_name(self.projects["firmware"], "dfu")
|
||||||
|
),
|
||||||
|
"--stage",
|
||||||
|
self.get_dist_path(
|
||||||
|
self.get_project_file_name(self.projects["updater"], "bin")
|
||||||
|
),
|
||||||
|
]
|
||||||
|
if self.args.resources:
|
||||||
|
bundle_args.extend(
|
||||||
|
(
|
||||||
|
"-r",
|
||||||
|
self.args.resources,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
bundle_args.extend(self.other_args)
|
||||||
|
|
||||||
|
log_custom_fz_name = (
|
||||||
|
environ.get("CUSTOM_FLIPPER_NAME", None)
|
||||||
|
or ""
|
||||||
|
)
|
||||||
|
if (log_custom_fz_name != "") and (len(log_custom_fz_name) <= 8) and (log_custom_fz_name.isalnum()) and (log_custom_fz_name.isascii()):
|
||||||
|
self.logger.info(f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars")
|
||||||
|
|
||||||
|
if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0:
|
||||||
|
self.note_dist_component("update", "dir", bundle_dir)
|
||||||
|
self.logger.info(
|
||||||
|
fg.boldgreen(
|
||||||
|
f"Use this directory to self-update your Flipper:\n\t{bundle_dir}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create tgz archive
|
||||||
|
with tarfile.open(
|
||||||
|
join(
|
||||||
|
self.output_dir_path,
|
||||||
|
bundle_tgz := f"{self.DIST_FILE_PREFIX}{bundle_dir_name}.tgz",
|
||||||
|
),
|
||||||
|
"w:gz",
|
||||||
|
compresslevel=9,
|
||||||
|
format=tarfile.USTAR_FORMAT,
|
||||||
|
) as tar:
|
||||||
|
self.note_dist_component(
|
||||||
|
"update", "tgz", self.get_dist_path(bundle_tgz)
|
||||||
|
)
|
||||||
|
tar.add(bundle_dir, arcname=bundle_dir_name)
|
||||||
|
return bundle_result
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
Main()()
|
Main()()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import sys
|
|||||||
def main():
|
def main():
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
if not (port := resolve_port(logger, "auto")):
|
if not (port := resolve_port(logger, "auto")):
|
||||||
logger.error("Is Flipper connected over USB and isn't in DFU mode?")
|
logger.error("Is Flipper connected over USB and is it not in DFU mode?")
|
||||||
return 1
|
return 1
|
||||||
subprocess.call(
|
subprocess.call(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -15,10 +15,12 @@ if not ["%FBT_NOENV%"] == [""] (
|
|||||||
|
|
||||||
set "FLIPPER_TOOLCHAIN_VERSION=21"
|
set "FLIPPER_TOOLCHAIN_VERSION=21"
|
||||||
|
|
||||||
if ["%FBT_TOOLCHAIN_ROOT%"] == [""] (
|
if ["%FBT_TOOLCHAIN_PATH%"] == [""] (
|
||||||
set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows"
|
set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set "FBT_TOOLCHAIN_ROOT=%FBT_TOOLCHAIN_PATH%\toolchain\x86_64-windows"
|
||||||
|
|
||||||
set "FBT_TOOLCHAIN_VERSION_FILE=%FBT_TOOLCHAIN_ROOT%\VERSION"
|
set "FBT_TOOLCHAIN_VERSION_FILE=%FBT_TOOLCHAIN_ROOT%\VERSION"
|
||||||
|
|
||||||
if not exist "%FBT_TOOLCHAIN_ROOT%" (
|
if not exist "%FBT_TOOLCHAIN_ROOT%" (
|
||||||
|
|||||||
@@ -4,9 +4,15 @@
|
|||||||
|
|
||||||
# public variables
|
# public variables
|
||||||
DEFAULT_SCRIPT_PATH="$(pwd -P)";
|
DEFAULT_SCRIPT_PATH="$(pwd -P)";
|
||||||
SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}";
|
|
||||||
FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}";
|
FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}";
|
||||||
FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}";
|
|
||||||
|
if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then
|
||||||
|
FBT_TOOLCHAIN_PATH_WAS_SET=0;
|
||||||
|
else
|
||||||
|
FBT_TOOLCHAIN_PATH_WAS_SET=1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$DEFAULT_SCRIPT_PATH}";
|
||||||
FBT_VERBOSE="${FBT_VERBOSE:-""}";
|
FBT_VERBOSE="${FBT_VERBOSE:-""}";
|
||||||
|
|
||||||
fbtenv_show_usage()
|
fbtenv_show_usage()
|
||||||
@@ -60,7 +66,6 @@ fbtenv_restore_env()
|
|||||||
unset SAVED_PYTHONPATH;
|
unset SAVED_PYTHONPATH;
|
||||||
unset SAVED_PYTHONHOME;
|
unset SAVED_PYTHONHOME;
|
||||||
|
|
||||||
unset SCRIPT_PATH;
|
|
||||||
unset FBT_TOOLCHAIN_VERSION;
|
unset FBT_TOOLCHAIN_VERSION;
|
||||||
unset FBT_TOOLCHAIN_PATH;
|
unset FBT_TOOLCHAIN_PATH;
|
||||||
}
|
}
|
||||||
@@ -104,13 +109,14 @@ fbtenv_set_shell_prompt()
|
|||||||
return 0; # all other shells
|
return 0; # all other shells
|
||||||
}
|
}
|
||||||
|
|
||||||
fbtenv_check_script_path()
|
fbtenv_check_env_vars()
|
||||||
{
|
{
|
||||||
if [ ! -x "$SCRIPT_PATH/fbt" ] && [ ! -x "$SCRIPT_PATH/ufbt" ] ; then
|
# Return error if FBT_TOOLCHAIN_PATH is not set before script is sourced or if fbt executable is not in DEFAULT_SCRIPT_PATH
|
||||||
echo "Please source this script from [u]fbt root directory, or specify 'SCRIPT_PATH' variable manually";
|
if [ "$FBT_TOOLCHAIN_PATH_WAS_SET" -eq 0 ] && [ ! -x "$DEFAULT_SCRIPT_PATH/fbt" ] && [ ! -x "$DEFAULT_SCRIPT_PATH/ufbt" ] ; then
|
||||||
|
echo "Please source this script from [u]fbt root directory, or specify 'FBT_TOOLCHAIN_PATH' variable manually";
|
||||||
echo "Example:";
|
echo "Example:";
|
||||||
printf "\tSCRIPT_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n";
|
printf "\tFBT_TOOLCHAIN_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n";
|
||||||
echo "If current directory is right, type 'unset SCRIPT_PATH' and try again"
|
echo "If current directory is right, type 'unset FBT_TOOLCHAIN_PATH' and try again"
|
||||||
return 1;
|
return 1;
|
||||||
fi
|
fi
|
||||||
return 0;
|
return 0;
|
||||||
@@ -217,7 +223,7 @@ fbtenv_show_unpack_percentage()
|
|||||||
|
|
||||||
fbtenv_unpack_toolchain()
|
fbtenv_unpack_toolchain()
|
||||||
{
|
{
|
||||||
echo "Unpacking toolchain:";
|
echo "Unpacking toolchain to '$FBT_TOOLCHAIN_PATH/toolchain':";
|
||||||
tar -xvf "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" -C "$FBT_TOOLCHAIN_PATH/toolchain" 2>&1 | fbtenv_show_unpack_percentage;
|
tar -xvf "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" -C "$FBT_TOOLCHAIN_PATH/toolchain" 2>&1 | fbtenv_show_unpack_percentage;
|
||||||
mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1;
|
mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1;
|
||||||
mv "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_DIR" "$TOOLCHAIN_ARCH_DIR" || return 1;
|
mv "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_DIR" "$TOOLCHAIN_ARCH_DIR" || return 1;
|
||||||
@@ -225,7 +231,7 @@ fbtenv_unpack_toolchain()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fbtenv_clearing()
|
fbtenv_cleanup()
|
||||||
{
|
{
|
||||||
printf "Cleaning up..";
|
printf "Cleaning up..";
|
||||||
if [ -n "${FBT_TOOLCHAIN_PATH:-""}" ]; then
|
if [ -n "${FBT_TOOLCHAIN_PATH:-""}" ]; then
|
||||||
@@ -280,14 +286,14 @@ fbtenv_download_toolchain()
|
|||||||
fbtenv_check_tar || return 1;
|
fbtenv_check_tar || return 1;
|
||||||
TOOLCHAIN_TAR="$(basename "$TOOLCHAIN_URL")";
|
TOOLCHAIN_TAR="$(basename "$TOOLCHAIN_URL")";
|
||||||
TOOLCHAIN_DIR="$(echo "$TOOLCHAIN_TAR" | sed "s/-$FBT_TOOLCHAIN_VERSION.tar.gz//g")";
|
TOOLCHAIN_DIR="$(echo "$TOOLCHAIN_TAR" | sed "s/-$FBT_TOOLCHAIN_VERSION.tar.gz//g")";
|
||||||
trap fbtenv_clearing 2; # trap will be restored in fbtenv_clearing
|
trap fbtenv_cleanup 2; # trap will be restored in fbtenv_cleanup
|
||||||
if ! fbtenv_check_downloaded_toolchain; then
|
if ! fbtenv_check_downloaded_toolchain; then
|
||||||
fbtenv_curl_wget_check || return 1;
|
fbtenv_curl_wget_check || return 1;
|
||||||
fbtenv_download_toolchain_tar || return 1;
|
fbtenv_download_toolchain_tar || return 1;
|
||||||
fi
|
fi
|
||||||
fbtenv_remove_old_tooclhain;
|
fbtenv_remove_old_tooclhain;
|
||||||
fbtenv_unpack_toolchain || return 1;
|
fbtenv_unpack_toolchain || return 1;
|
||||||
fbtenv_clearing;
|
fbtenv_cleanup;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,8 +312,8 @@ fbtenv_main()
|
|||||||
fbtenv_restore_env;
|
fbtenv_restore_env;
|
||||||
return 0;
|
return 0;
|
||||||
fi
|
fi
|
||||||
fbtenv_check_if_sourced_multiple_times; # many source it's just a warning
|
fbtenv_check_if_sourced_multiple_times;
|
||||||
fbtenv_check_script_path || return 1;
|
fbtenv_check_env_vars || return 1;
|
||||||
fbtenv_check_download_toolchain || return 1;
|
fbtenv_check_download_toolchain || return 1;
|
||||||
fbtenv_set_shell_prompt;
|
fbtenv_set_shell_prompt;
|
||||||
fbtenv_print_version;
|
fbtenv_print_version;
|
||||||
|
|||||||
@@ -0,0 +1,393 @@
|
|||||||
|
from SCons.Platform import TempFileMunge
|
||||||
|
from SCons.Node import FS
|
||||||
|
from SCons.Errors import UserError
|
||||||
|
|
||||||
|
import os
|
||||||
|
import multiprocessing
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
SetOption("num_jobs", multiprocessing.cpu_count())
|
||||||
|
SetOption("max_drift", 1)
|
||||||
|
# SetOption("silent", False)
|
||||||
|
|
||||||
|
ufbt_state_dir = Dir(os.environ.get("UFBT_STATE_DIR", "#.ufbt"))
|
||||||
|
ufbt_script_dir = Dir(os.environ.get("UFBT_SCRIPT_DIR"))
|
||||||
|
|
||||||
|
ufbt_current_sdk_dir = ufbt_state_dir.Dir("current")
|
||||||
|
|
||||||
|
SConsignFile(ufbt_state_dir.File(".sconsign.dblite").abspath)
|
||||||
|
|
||||||
|
ufbt_variables = SConscript("commandline.scons")
|
||||||
|
|
||||||
|
forward_os_env = {
|
||||||
|
# Import PATH from OS env - scons doesn't do that by default
|
||||||
|
"PATH": os.environ["PATH"],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxying environment to child processes & scripts
|
||||||
|
variables_to_forward = [
|
||||||
|
# CI/CD variables
|
||||||
|
"WORKFLOW_BRANCH_OR_TAG",
|
||||||
|
"DIST_SUFFIX",
|
||||||
|
# Python & other tools
|
||||||
|
"HOME",
|
||||||
|
"APPDATA",
|
||||||
|
"PYTHONHOME",
|
||||||
|
"PYTHONNOUSERSITE",
|
||||||
|
"TMP",
|
||||||
|
"TEMP",
|
||||||
|
# Colors for tools
|
||||||
|
"TERM",
|
||||||
|
]
|
||||||
|
|
||||||
|
if proxy_env := GetOption("proxy_env"):
|
||||||
|
variables_to_forward.extend(proxy_env.split(","))
|
||||||
|
|
||||||
|
for env_value_name in variables_to_forward:
|
||||||
|
if environ_value := os.environ.get(env_value_name, None):
|
||||||
|
forward_os_env[env_value_name] = environ_value
|
||||||
|
|
||||||
|
# Core environment init - loads SDK state, sets up paths, etc.
|
||||||
|
core_env = Environment(
|
||||||
|
variables=ufbt_variables,
|
||||||
|
ENV=forward_os_env,
|
||||||
|
UFBT_STATE_DIR=ufbt_state_dir,
|
||||||
|
UFBT_CURRENT_SDK_DIR=ufbt_current_sdk_dir,
|
||||||
|
UFBT_SCRIPT_DIR=ufbt_script_dir,
|
||||||
|
toolpath=[ufbt_current_sdk_dir.Dir("scripts/ufbt/site_tools")],
|
||||||
|
tools=[
|
||||||
|
"ufbt_state",
|
||||||
|
("ufbt_help", {"vars": ufbt_variables}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
if "update" in BUILD_TARGETS:
|
||||||
|
SConscript(
|
||||||
|
"update.scons",
|
||||||
|
exports={"core_env": core_env},
|
||||||
|
)
|
||||||
|
|
||||||
|
if "purge" in BUILD_TARGETS:
|
||||||
|
core_env.Execute(Delete(ufbt_state_dir))
|
||||||
|
print("uFBT state purged")
|
||||||
|
Exit(0)
|
||||||
|
|
||||||
|
# Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state
|
||||||
|
|
||||||
|
from fbt.util import (
|
||||||
|
tempfile_arg_esc_func,
|
||||||
|
single_quote,
|
||||||
|
extract_abs_dir,
|
||||||
|
extract_abs_dir_path,
|
||||||
|
wrap_tempfile,
|
||||||
|
path_as_posix,
|
||||||
|
)
|
||||||
|
from fbt.appmanifest import FlipperAppType
|
||||||
|
from fbt.sdk.cache import SdkCache
|
||||||
|
|
||||||
|
# Base environment with all tools loaded from SDK
|
||||||
|
env = core_env.Clone(
|
||||||
|
toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")],
|
||||||
|
tools=[
|
||||||
|
"fbt_tweaks",
|
||||||
|
(
|
||||||
|
"crosscc",
|
||||||
|
{
|
||||||
|
"toolchain_prefix": "arm-none-eabi-",
|
||||||
|
"versions": (" 10.3",),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
"fwbin",
|
||||||
|
"python3",
|
||||||
|
"sconsrecursiveglob",
|
||||||
|
"sconsmodular",
|
||||||
|
"ccache",
|
||||||
|
"fbt_apps",
|
||||||
|
"fbt_extapps",
|
||||||
|
"fbt_assets",
|
||||||
|
("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}),
|
||||||
|
],
|
||||||
|
FBT_FAP_DEBUG_ELF_ROOT=ufbt_state_dir.Dir("build"),
|
||||||
|
TEMPFILE=TempFileMunge,
|
||||||
|
MAXLINELENGTH=2048,
|
||||||
|
PROGSUFFIX=".elf",
|
||||||
|
TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
|
||||||
|
SINGLEQUOTEFUNC=single_quote,
|
||||||
|
ABSPATHGETTERFUNC=extract_abs_dir_path,
|
||||||
|
APPS=[],
|
||||||
|
UFBT_API_VERSION=SdkCache(
|
||||||
|
core_env.subst("$SDK_DEFINITION"), load_version_only=True
|
||||||
|
).version,
|
||||||
|
APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}\n\t\tTarget: ${TARGET_HW}, API: ${UFBT_API_VERSION}",
|
||||||
|
)
|
||||||
|
|
||||||
|
wrap_tempfile(env, "LINKCOM")
|
||||||
|
wrap_tempfile(env, "ARCOM")
|
||||||
|
|
||||||
|
# print(env.Dump())
|
||||||
|
|
||||||
|
# Dist env
|
||||||
|
|
||||||
|
dist_env = env.Clone(
|
||||||
|
tools=[
|
||||||
|
"fbt_dist",
|
||||||
|
"fbt_debugopts",
|
||||||
|
"openocd",
|
||||||
|
"blackmagic",
|
||||||
|
"jflash",
|
||||||
|
"textfile",
|
||||||
|
],
|
||||||
|
ENV=os.environ,
|
||||||
|
OPENOCD_OPTS=[
|
||||||
|
"-f",
|
||||||
|
"interface/stlink.cfg",
|
||||||
|
"-c",
|
||||||
|
"transport select hla_swd",
|
||||||
|
"-f",
|
||||||
|
"${FBT_DEBUG_DIR}/stm32wbx.cfg",
|
||||||
|
"-c",
|
||||||
|
"stm32wbx.cpu configure -rtos auto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
openocd_target = dist_env.OpenOCDFlash(
|
||||||
|
dist_env["UFBT_STATE_DIR"].File("flash"),
|
||||||
|
dist_env["FW_BIN"],
|
||||||
|
OPENOCD_COMMAND=[
|
||||||
|
"-c",
|
||||||
|
"program ${SOURCE.posix} reset exit 0x08000000",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
dist_env.Alias("firmware_flash", openocd_target)
|
||||||
|
dist_env.Alias("flash", openocd_target)
|
||||||
|
if env["FORCE"]:
|
||||||
|
env.AlwaysBuild(openocd_target)
|
||||||
|
|
||||||
|
firmware_debug = dist_env.PhonyTarget(
|
||||||
|
"debug",
|
||||||
|
"${GDBPYCOM}",
|
||||||
|
source=dist_env["FW_ELF"],
|
||||||
|
GDBOPTS="${GDBOPTS_BASE}",
|
||||||
|
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||||
|
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
||||||
|
)
|
||||||
|
|
||||||
|
dist_env.PhonyTarget(
|
||||||
|
"blackmagic",
|
||||||
|
"${GDBPYCOM}",
|
||||||
|
source=dist_env["FW_ELF"],
|
||||||
|
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||||
|
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||||
|
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
||||||
|
)
|
||||||
|
|
||||||
|
dist_env.PhonyTarget(
|
||||||
|
"flash_blackmagic",
|
||||||
|
"$GDB $GDBOPTS $SOURCES $GDBFLASH",
|
||||||
|
source=dist_env["FW_ELF"],
|
||||||
|
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||||
|
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||||
|
GDBFLASH=[
|
||||||
|
"-ex",
|
||||||
|
"load",
|
||||||
|
"-ex",
|
||||||
|
"quit",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
flash_usb_full = dist_env.UsbInstall(
|
||||||
|
dist_env["UFBT_STATE_DIR"].File("usbinstall"),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
dist_env.AlwaysBuild(flash_usb_full)
|
||||||
|
dist_env.Alias("flash_usb", flash_usb_full)
|
||||||
|
dist_env.Alias("flash_usb_full", flash_usb_full)
|
||||||
|
|
||||||
|
# App build environment
|
||||||
|
|
||||||
|
appenv = env.Clone(
|
||||||
|
CCCOM=env["CCCOM"].replace("$CFLAGS", "$CFLAGS_APP $CFLAGS"),
|
||||||
|
CXXCOM=env["CXXCOM"].replace("$CXXFLAGS", "$CXXFLAGS_APP $CXXFLAGS"),
|
||||||
|
LINKCOM=env["LINKCOM"].replace("$LINKFLAGS", "$LINKFLAGS_APP $LINKFLAGS"),
|
||||||
|
COMPILATIONDB_USE_ABSPATH=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
original_app_dir = Dir(appenv.subst("$UFBT_APP_DIR"))
|
||||||
|
app_mount_point = Dir("#/app/")
|
||||||
|
app_mount_point.addRepository(original_app_dir)
|
||||||
|
|
||||||
|
appenv.LoadAppManifest(app_mount_point)
|
||||||
|
appenv.PrepareApplicationsBuild()
|
||||||
|
|
||||||
|
#######################
|
||||||
|
|
||||||
|
apps_artifacts = appenv["EXT_APPS"]
|
||||||
|
|
||||||
|
apps_to_build_as_faps = [
|
||||||
|
FlipperAppType.PLUGIN,
|
||||||
|
FlipperAppType.EXTERNAL,
|
||||||
|
]
|
||||||
|
|
||||||
|
known_extapps = [
|
||||||
|
app
|
||||||
|
for apptype in apps_to_build_as_faps
|
||||||
|
for app in appenv["APPBUILD"].get_apps_of_type(apptype, True)
|
||||||
|
]
|
||||||
|
for app in known_extapps:
|
||||||
|
app_artifacts = appenv.BuildAppElf(app)
|
||||||
|
app_src_dir = extract_abs_dir(app_artifacts.app._appdir)
|
||||||
|
app_artifacts.installer = [
|
||||||
|
appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact),
|
||||||
|
appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug),
|
||||||
|
]
|
||||||
|
|
||||||
|
if appenv["FORCE"]:
|
||||||
|
appenv.AlwaysBuild([extapp.compact for extapp in apps_artifacts.values()])
|
||||||
|
|
||||||
|
# Final steps - target aliases
|
||||||
|
|
||||||
|
install_and_check = [
|
||||||
|
(extapp.installer, extapp.validator) for extapp in apps_artifacts.values()
|
||||||
|
]
|
||||||
|
Alias(
|
||||||
|
"faps",
|
||||||
|
install_and_check,
|
||||||
|
)
|
||||||
|
Default(install_and_check)
|
||||||
|
|
||||||
|
# Compilation database
|
||||||
|
|
||||||
|
fwcdb = appenv.CompilationDatabase(
|
||||||
|
original_app_dir.Dir(".vscode").File("compile_commands.json")
|
||||||
|
)
|
||||||
|
|
||||||
|
AlwaysBuild(fwcdb)
|
||||||
|
Precious(fwcdb)
|
||||||
|
NoClean(fwcdb)
|
||||||
|
if len(apps_artifacts):
|
||||||
|
Default(fwcdb)
|
||||||
|
|
||||||
|
|
||||||
|
# launch handler
|
||||||
|
runnable_apps = appenv["APPBUILD"].get_apps_of_type(FlipperAppType.EXTERNAL, True)
|
||||||
|
|
||||||
|
app_to_launch = None
|
||||||
|
if len(runnable_apps) == 1:
|
||||||
|
app_to_launch = runnable_apps[0].appid
|
||||||
|
elif len(runnable_apps) > 1:
|
||||||
|
# more than 1 app - try to find one with matching id
|
||||||
|
app_to_launch = appenv.subst("$APPID")
|
||||||
|
|
||||||
|
|
||||||
|
def ambiguous_app_call(**kw):
|
||||||
|
raise UserError(
|
||||||
|
f"More than one app is runnable: {', '.join(app.appid for app in runnable_apps)}. Please specify an app with APPID=..."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if app_to_launch:
|
||||||
|
appenv.AddAppLaunchTarget(app_to_launch, "launch")
|
||||||
|
else:
|
||||||
|
dist_env.PhonyTarget("launch", Action(ambiguous_app_call, None))
|
||||||
|
|
||||||
|
# cli handler
|
||||||
|
|
||||||
|
appenv.PhonyTarget(
|
||||||
|
"cli",
|
||||||
|
'${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py"',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Linter
|
||||||
|
|
||||||
|
dist_env.PhonyTarget(
|
||||||
|
"lint",
|
||||||
|
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
|
||||||
|
source=original_app_dir.File(".clang-format"),
|
||||||
|
LINT_SOURCES=[original_app_dir],
|
||||||
|
)
|
||||||
|
|
||||||
|
dist_env.PhonyTarget(
|
||||||
|
"format",
|
||||||
|
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
|
||||||
|
source=original_app_dir.File(".clang-format"),
|
||||||
|
LINT_SOURCES=[original_app_dir],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Prepare vscode environment
|
||||||
|
def _path_as_posix(path):
|
||||||
|
return pathlib.Path(path).as_posix()
|
||||||
|
|
||||||
|
|
||||||
|
vscode_dist = []
|
||||||
|
project_template_dir = dist_env["UFBT_SCRIPT_ROOT"].Dir("project_template")
|
||||||
|
for template_file in project_template_dir.Dir(".vscode").glob("*"):
|
||||||
|
vscode_dist.append(
|
||||||
|
dist_env.Substfile(
|
||||||
|
original_app_dir.Dir(".vscode").File(template_file.name),
|
||||||
|
template_file,
|
||||||
|
SUBST_DICT={
|
||||||
|
"@UFBT_VSCODE_PATH_SEP@": os.path.pathsep,
|
||||||
|
"@UFBT_TOOLCHAIN_ARM_TOOLCHAIN_DIR@": pathlib.Path(
|
||||||
|
dist_env.WhereIs("arm-none-eabi-gcc")
|
||||||
|
).parent.as_posix(),
|
||||||
|
"@UFBT_TOOLCHAIN_GCC@": _path_as_posix(
|
||||||
|
dist_env.WhereIs("arm-none-eabi-gcc")
|
||||||
|
),
|
||||||
|
"@UFBT_TOOLCHAIN_GDB_PY@": _path_as_posix(
|
||||||
|
dist_env.WhereIs("arm-none-eabi-gdb-py")
|
||||||
|
),
|
||||||
|
"@UFBT_TOOLCHAIN_OPENOCD@": _path_as_posix(dist_env.WhereIs("openocd")),
|
||||||
|
"@UFBT_APP_DIR@": _path_as_posix(original_app_dir.abspath),
|
||||||
|
"@UFBT_ROOT_DIR@": _path_as_posix(Dir("#").abspath),
|
||||||
|
"@UFBT_DEBUG_DIR@": dist_env["FBT_DEBUG_DIR"],
|
||||||
|
"@UFBT_DEBUG_ELF_DIR@": _path_as_posix(
|
||||||
|
dist_env["FBT_FAP_DEBUG_ELF_ROOT"].abspath
|
||||||
|
),
|
||||||
|
"@UFBT_FIRMWARE_ELF@": _path_as_posix(dist_env["FW_ELF"].abspath),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for config_file in project_template_dir.glob(".*"):
|
||||||
|
if isinstance(config_file, FS.Dir):
|
||||||
|
continue
|
||||||
|
vscode_dist.append(dist_env.Install(original_app_dir, config_file))
|
||||||
|
|
||||||
|
dist_env.Precious(vscode_dist)
|
||||||
|
dist_env.NoClean(vscode_dist)
|
||||||
|
dist_env.Alias("vscode_dist", vscode_dist)
|
||||||
|
|
||||||
|
|
||||||
|
# Creating app from base template
|
||||||
|
|
||||||
|
dist_env.SetDefault(FBT_APPID=appenv.subst("$APPID") or "template")
|
||||||
|
app_template_dist = []
|
||||||
|
for template_file in project_template_dir.Dir("app_template").glob("*"):
|
||||||
|
dist_file_name = dist_env.subst(template_file.name)
|
||||||
|
if template_file.name.endswith(".png"):
|
||||||
|
app_template_dist.append(
|
||||||
|
dist_env.InstallAs(original_app_dir.File(dist_file_name), template_file)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
app_template_dist.append(
|
||||||
|
dist_env.Substfile(
|
||||||
|
original_app_dir.File(dist_file_name),
|
||||||
|
template_file,
|
||||||
|
SUBST_DICT={
|
||||||
|
"@FBT_APPID@": dist_env.subst("$FBT_APPID"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
AddPostAction(
|
||||||
|
app_template_dist[-1],
|
||||||
|
[
|
||||||
|
Mkdir(original_app_dir.Dir("images")),
|
||||||
|
Touch(original_app_dir.Dir("images").File(".gitkeep")),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
dist_env.Precious(app_template_dist)
|
||||||
|
dist_env.NoClean(app_template_dist)
|
||||||
|
dist_env.Alias("create", app_template_dist)
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
AddOption(
|
||||||
|
"--proxy-env",
|
||||||
|
action="store",
|
||||||
|
dest="proxy_env",
|
||||||
|
default="",
|
||||||
|
help="Comma-separated list of additional environment variables to pass to child SCons processes",
|
||||||
|
)
|
||||||
|
|
||||||
|
AddOption(
|
||||||
|
"--channel",
|
||||||
|
action="store",
|
||||||
|
dest="sdk_channel",
|
||||||
|
choices=["dev", "rc", "release"],
|
||||||
|
default="",
|
||||||
|
help="Release channel to use for SDK",
|
||||||
|
)
|
||||||
|
|
||||||
|
AddOption(
|
||||||
|
"--branch",
|
||||||
|
action="store",
|
||||||
|
dest="sdk_branch",
|
||||||
|
help="Custom main repo branch to use for SDK",
|
||||||
|
)
|
||||||
|
|
||||||
|
AddOption(
|
||||||
|
"--hw-target",
|
||||||
|
action="store",
|
||||||
|
dest="sdk_target",
|
||||||
|
help="SDK Hardware target",
|
||||||
|
)
|
||||||
|
|
||||||
|
vars = Variables("ufbt_options.py", ARGUMENTS)
|
||||||
|
|
||||||
|
vars.AddVariables(
|
||||||
|
BoolVariable(
|
||||||
|
"VERBOSE",
|
||||||
|
help="Print full commands",
|
||||||
|
default=False,
|
||||||
|
),
|
||||||
|
BoolVariable(
|
||||||
|
"FORCE",
|
||||||
|
help="Force target action (for supported targets)",
|
||||||
|
default=False,
|
||||||
|
),
|
||||||
|
# These 2 are inherited from SDK
|
||||||
|
# BoolVariable(
|
||||||
|
# "DEBUG",
|
||||||
|
# help="Enable debug build",
|
||||||
|
# default=True,
|
||||||
|
# ),
|
||||||
|
# BoolVariable(
|
||||||
|
# "COMPACT",
|
||||||
|
# help="Optimize for size",
|
||||||
|
# default=False,
|
||||||
|
# ),
|
||||||
|
PathVariable(
|
||||||
|
"OTHER_ELF",
|
||||||
|
help="Path to prebuilt ELF file to debug",
|
||||||
|
validator=PathVariable.PathAccept,
|
||||||
|
default="",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"OPENOCD_OPTS",
|
||||||
|
"Options to pass to OpenOCD",
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"BLACKMAGIC",
|
||||||
|
"Blackmagic probe location",
|
||||||
|
"auto",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"OPENOCD_ADAPTER_SERIAL",
|
||||||
|
"OpenOCD adapter serial number",
|
||||||
|
"auto",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"APPID",
|
||||||
|
"Application id",
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
PathVariable(
|
||||||
|
"UFBT_APP_DIR",
|
||||||
|
help="Application dir to work with",
|
||||||
|
validator=PathVariable.PathIsDir,
|
||||||
|
default="",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
Return("vars")
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: AlwaysBreak
|
||||||
|
AlignArrayOfStructures: None
|
||||||
|
AlignConsecutiveMacros: None
|
||||||
|
AlignConsecutiveAssignments: None
|
||||||
|
AlignConsecutiveBitFields: None
|
||||||
|
AlignConsecutiveDeclarations: None
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: Align
|
||||||
|
AlignTrailingComments: false
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortEnumsOnASingleLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
AttributeMacros:
|
||||||
|
- __capability
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
BeforeLambdaBody: false
|
||||||
|
BeforeWhile: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeConceptDeclarations: true
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakBeforeTernaryOperators: false
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BreakConstructorInitializers: BeforeComma
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: false
|
||||||
|
ColumnLimit: 99
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
QualifierAlignment: Leave
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DeriveLineEnding: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
EmptyLineAfterAccessModifier: Never
|
||||||
|
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
PackConstructorInitializers: BinPack
|
||||||
|
BasedOnStyle: ''
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
|
FixNamespaceComments: false
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IfMacros:
|
||||||
|
- KJ_IF_MAYBE
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||||
|
Priority: 3
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
|
IncludeIsMainSourceRegex: ''
|
||||||
|
IndentAccessModifiers: false
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentCaseBlocks: false
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentExternBlock: AfterExternBlock
|
||||||
|
IndentRequires: false
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: true
|
||||||
|
InsertTrailingCommas: None
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
LambdaBodyIndentation: Signature
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBinPackProtocolList: Auto
|
||||||
|
ObjCBlockIndentWidth: 4
|
||||||
|
ObjCBreakBeforeNestedBlockParam: true
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakAssignment: 10
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 30
|
||||||
|
PenaltyBreakComment: 10
|
||||||
|
PenaltyBreakFirstLessLess: 0
|
||||||
|
PenaltyBreakOpenParenthesis: 0
|
||||||
|
PenaltyBreakString: 10
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
|
PenaltyExcessCharacter: 100
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PenaltyIndentedWhitespace: 0
|
||||||
|
PointerAlignment: Left
|
||||||
|
PPIndentWidth: -1
|
||||||
|
ReferenceAlignment: Pointer
|
||||||
|
ReflowComments: false
|
||||||
|
RemoveBracesLLVM: false
|
||||||
|
SeparateDefinitionBlocks: Leave
|
||||||
|
ShortNamespaceLines: 1
|
||||||
|
SortIncludes: Never
|
||||||
|
SortJavaStaticImport: Before
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCaseColon: false
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: Never
|
||||||
|
SpaceBeforeParensOptions:
|
||||||
|
AfterControlStatements: false
|
||||||
|
AfterForeachMacros: false
|
||||||
|
AfterFunctionDefinitionName: false
|
||||||
|
AfterFunctionDeclarationName: false
|
||||||
|
AfterIfMacros: false
|
||||||
|
AfterOverloadedOperator: false
|
||||||
|
BeforeNonEmptyParentheses: false
|
||||||
|
SpaceAroundPointerQualifiers: Default
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: Never
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInLineCommentPrefix:
|
||||||
|
Minimum: 1
|
||||||
|
Maximum: -1
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
BitFieldColonSpacing: Both
|
||||||
|
Standard: c++03
|
||||||
|
StatementAttributeLikeMacros:
|
||||||
|
- Q_EMIT
|
||||||
|
StatementMacros:
|
||||||
|
- Q_UNUSED
|
||||||
|
- QT_REQUIRE_VERSION
|
||||||
|
TabWidth: 4
|
||||||
|
UseCRLF: false
|
||||||
|
UseTab: Never
|
||||||
|
WhitespaceSensitiveMacros:
|
||||||
|
- STRINGIZE
|
||||||
|
- PP_STRINGIZE
|
||||||
|
- BOOST_PP_STRINGIZE
|
||||||
|
- NS_SWIFT_NAME
|
||||||
|
- CF_SWIFT_NAME
|
||||||
|
...
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
[*.{cpp,h,c,py,sh}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[{Makefile,*.mk}]
|
||||||
|
indent_size = tab
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
dist/*
|
||||||
|
.vscode
|
||||||
|
.clang-format
|
||||||
|
.editorconfig
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "main",
|
||||||
|
"compilerPath": "@UFBT_TOOLCHAIN_GCC@",
|
||||||
|
"intelliSenseMode": "gcc-arm",
|
||||||
|
"compileCommands": "${workspaceFolder}/.vscode/compile_commands.json",
|
||||||
|
"configurationProvider": "ms-vscode.cpptools",
|
||||||
|
"cStandard": "gnu17",
|
||||||
|
"cppStandard": "c++17"
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||||
|
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||||
|
// List of extensions which should be recommended for users of this workspace.
|
||||||
|
"recommendations": [
|
||||||
|
"ms-python.black-formatter",
|
||||||
|
"ms-vscode.cpptools",
|
||||||
|
"amiralizadeh9480.cpp-helper",
|
||||||
|
"marus25.cortex-debug",
|
||||||
|
"zxh404.vscode-proto3",
|
||||||
|
"augustocdias.tasks-shell-input"
|
||||||
|
],
|
||||||
|
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"twxs.cmake",
|
||||||
|
"ms-vscode.cmake-tools"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"inputs": [
|
||||||
|
// {
|
||||||
|
// "id": "BLACKMAGIC",
|
||||||
|
// "type": "command",
|
||||||
|
// "command": "shellCommand.execute",
|
||||||
|
// "args": {
|
||||||
|
// "useSingleResult": true,
|
||||||
|
// "env": {
|
||||||
|
// "PATH": "${workspaceFolder};${env:PATH}"
|
||||||
|
// },
|
||||||
|
// "command": "./fbt get_blackmagic",
|
||||||
|
// "description": "Get Blackmagic device",
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Attach FW (ST-Link)",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"executable": "@UFBT_FIRMWARE_ELF@",
|
||||||
|
"request": "attach",
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"servertype": "openocd",
|
||||||
|
"device": "stlink",
|
||||||
|
"svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd",
|
||||||
|
"rtos": "FreeRTOS",
|
||||||
|
"configFiles": [
|
||||||
|
"interface/stlink.cfg",
|
||||||
|
"@UFBT_DEBUG_DIR@/stm32wbx.cfg"
|
||||||
|
],
|
||||||
|
"postAttachCommands": [
|
||||||
|
"source @UFBT_DEBUG_DIR@/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@"
|
||||||
|
],
|
||||||
|
// "showDevDebugOutput": "raw",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Attach FW (DAP)",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"executable": "@UFBT_FIRMWARE_ELF@",
|
||||||
|
"request": "attach",
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"servertype": "openocd",
|
||||||
|
"device": "cmsis-dap",
|
||||||
|
"svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd",
|
||||||
|
"rtos": "FreeRTOS",
|
||||||
|
"configFiles": [
|
||||||
|
"interface/cmsis-dap.cfg",
|
||||||
|
"@UFBT_DEBUG_DIR@/stm32wbx.cfg"
|
||||||
|
],
|
||||||
|
"postAttachCommands": [
|
||||||
|
"source @UFBT_DEBUG_DIR@/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@"
|
||||||
|
],
|
||||||
|
// "showDevDebugOutput": "raw",
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// "name": "Attach FW (blackmagic)",
|
||||||
|
// "cwd": "${workspaceFolder}",
|
||||||
|
// "executable": "@UFBT_FIRMWARE_ELF@",
|
||||||
|
// "request": "attach",
|
||||||
|
// "type": "cortex-debug",
|
||||||
|
// "servertype": "external",
|
||||||
|
// "gdbTarget": "${input:BLACKMAGIC}",
|
||||||
|
// "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd",
|
||||||
|
// "rtos": "FreeRTOS",
|
||||||
|
// "postAttachCommands": [
|
||||||
|
// "monitor swdp_scan",
|
||||||
|
// "attach 1",
|
||||||
|
// "set confirm off",
|
||||||
|
// "set mem inaccessible-by-default off",
|
||||||
|
// "source @UFBT_DEBUG_DIR@/flipperapps.py",
|
||||||
|
// "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@"
|
||||||
|
// ]
|
||||||
|
// // "showDevDebugOutput": "raw",
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
"name": "Attach FW (JLink)",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"executable": "@UFBT_FIRMWARE_ELF@",
|
||||||
|
"request": "attach",
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"interface": "swd",
|
||||||
|
"device": "STM32WB55RG",
|
||||||
|
"svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd",
|
||||||
|
"rtos": "FreeRTOS",
|
||||||
|
"postAttachCommands": [
|
||||||
|
"source @UFBT_DEBUG_DIR@/flipperapps.py",
|
||||||
|
"fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@"
|
||||||
|
]
|
||||||
|
// "showDevDebugOutput": "raw",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"cortex-debug.enableTelemetry": false,
|
||||||
|
"cortex-debug.variableUseNaturalFormat": false,
|
||||||
|
"cortex-debug.showRTOS": true,
|
||||||
|
"cortex-debug.armToolchainPath": "@UFBT_TOOLCHAIN_ARM_TOOLCHAIN_DIR@",
|
||||||
|
"cortex-debug.openocdPath": "@UFBT_TOOLCHAIN_OPENOCD@",
|
||||||
|
"cortex-debug.gdbPath": "@UFBT_TOOLCHAIN_GDB_PY@",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"files.associations": {
|
||||||
|
"*.scons": "python",
|
||||||
|
"SConscript": "python",
|
||||||
|
"SConstruct": "python",
|
||||||
|
"*.fam": "python"
|
||||||
|
},
|
||||||
|
"cortex-debug.registerUseNaturalFormat": false,
|
||||||
|
"python.analysis.typeCheckingMode": "off",
|
||||||
|
"[python]": {
|
||||||
|
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${workspaceFolder}@UFBT_VSCODE_PATH_SEP@${env:PATH}@UFBT_VSCODE_PATH_SEP@@UFBT_ROOT_DIR@"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Launch App on Flipper",
|
||||||
|
"group": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ufbt launch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Build",
|
||||||
|
"group": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ufbt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Flash FW (ST-Link)",
|
||||||
|
"group": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ufbt FORCE=1 flash"
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// "label": "[NOTIMPL] Flash FW (blackmagic)",
|
||||||
|
// "group": "build",
|
||||||
|
// "type": "shell",
|
||||||
|
// "command": "ufbt flash_blackmagic"
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "label": "[NOTIMPL] Flash FW (JLink)",
|
||||||
|
// "group": "build",
|
||||||
|
// "type": "shell",
|
||||||
|
// "command": "ufbt FORCE=1 jflash"
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
"label": "Flash FW (USB, with resources)",
|
||||||
|
"group": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ufbt FORCE=1 flash_usb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Update uFBT SDK",
|
||||||
|
"group": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ufbt update"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
|
||||||
|
/* generated by fbt from .png files in images folder */
|
||||||
|
#include <@FBT_APPID@_icons.h>
|
||||||
|
|
||||||
|
int32_t @FBT_APPID@_app(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
FURI_LOG_I("TEST", "Hello world");
|
||||||
|
FURI_LOG_I("TEST", "I'm @FBT_APPID@!");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||