mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-08 05:39:09 -07:00
Merge remote-tracking branch 'ofw/dev' into mntm-dev
This commit is contained in:
@@ -445,13 +445,11 @@ static void bt_change_profile(Bt* bt, BtMessage* message) {
|
||||
*message->profile_instance = NULL;
|
||||
}
|
||||
}
|
||||
if(message->lock) api_lock_unlock(message->lock);
|
||||
}
|
||||
|
||||
static void bt_close_connection(Bt* bt, BtMessage* message) {
|
||||
static void bt_close_connection(Bt* bt) {
|
||||
bt_close_rpc_connection(bt);
|
||||
furi_hal_bt_stop_advertising();
|
||||
if(message->lock) api_lock_unlock(message->lock);
|
||||
}
|
||||
|
||||
static void bt_apply_settings(Bt* bt) {
|
||||
@@ -499,19 +497,13 @@ static void bt_load_settings(Bt* bt) {
|
||||
}
|
||||
|
||||
static void bt_handle_get_settings(Bt* bt, BtMessage* message) {
|
||||
furi_assert(message->lock);
|
||||
*message->data.settings = bt->bt_settings;
|
||||
api_lock_unlock(message->lock);
|
||||
}
|
||||
|
||||
static void bt_handle_set_settings(Bt* bt, BtMessage* message) {
|
||||
furi_assert(message->lock);
|
||||
bt->bt_settings = *message->data.csettings;
|
||||
|
||||
bt_apply_settings(bt);
|
||||
bt_settings_save(&bt->bt_settings);
|
||||
|
||||
api_lock_unlock(message->lock);
|
||||
}
|
||||
|
||||
static void bt_handle_reload_keys_settings(Bt* bt) {
|
||||
@@ -576,6 +568,12 @@ int32_t bt_srv(void* p) {
|
||||
while(1) {
|
||||
furi_check(
|
||||
furi_message_queue_get(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"call %d, lock 0x%p, result 0x%p",
|
||||
message.type,
|
||||
(void*)message.lock,
|
||||
(void*)message.result);
|
||||
if(message.type == BtMessageTypeUpdateStatus) {
|
||||
// Update view ports
|
||||
bt_statusbar_update(bt);
|
||||
@@ -599,7 +597,7 @@ int32_t bt_srv(void* p) {
|
||||
} else if(message.type == BtMessageTypeSetProfile) {
|
||||
bt_change_profile(bt, &message);
|
||||
} else if(message.type == BtMessageTypeDisconnect) {
|
||||
bt_close_connection(bt, &message);
|
||||
bt_close_connection(bt);
|
||||
} else if(message.type == BtMessageTypeForgetBondedDevices) {
|
||||
bt_keys_storage_delete(bt->keys_storage);
|
||||
} else if(message.type == BtMessageTypeGetSettings) {
|
||||
@@ -609,6 +607,8 @@ int32_t bt_srv(void* p) {
|
||||
} else if(message.type == BtMessageTypeReloadKeysSettings) {
|
||||
bt_handle_reload_keys_settings(bt);
|
||||
}
|
||||
|
||||
if(message.lock) api_lock_unlock(message.lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "cli_i.h"
|
||||
#include "cli_commands.h"
|
||||
#include "cli_vcp.h"
|
||||
#include "cli_ansi.h"
|
||||
#include <furi_hal_version.h>
|
||||
#include <loader/loader.h>
|
||||
|
||||
@@ -11,6 +12,8 @@
|
||||
#define TAG "CliSrv"
|
||||
|
||||
#define CLI_INPUT_LEN_LIMIT 256
|
||||
#define CLI_PROMPT ">: " // qFlipper does not recognize us if we use escape sequences :(
|
||||
#define CLI_PROMPT_LENGTH 3 // printable characters
|
||||
|
||||
Cli* cli_alloc(void) {
|
||||
Cli* cli = malloc(sizeof(Cli));
|
||||
@@ -89,7 +92,7 @@ bool cli_cmd_interrupt_received(Cli* cli) {
|
||||
char c = '\0';
|
||||
if(cli_is_connected(cli)) {
|
||||
if(cli->session->rx((uint8_t*)&c, 1, 0) == 1) {
|
||||
return c == CliSymbolAsciiETX;
|
||||
return c == CliKeyETX;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
@@ -106,7 +109,8 @@ void cli_print_usage(const char* cmd, const char* usage, const char* arg) {
|
||||
}
|
||||
|
||||
void cli_motd(void) {
|
||||
printf("\r\n"
|
||||
printf(ANSI_FLIPPER_BRAND_ORANGE
|
||||
"\r\n"
|
||||
" _.-------.._ -,\r\n"
|
||||
" .-\"```\"--..,,_/ /`-, -, \\ \r\n"
|
||||
" .:\" /:/ /'\\ \\ ,_..., `. | |\r\n"
|
||||
@@ -120,12 +124,11 @@ void cli_motd(void) {
|
||||
" _L_ _ ___ ___ ___ ___ ____--\"`___ _ ___\r\n"
|
||||
"| __|| | |_ _|| _ \\| _ \\| __|| _ \\ / __|| | |_ _|\r\n"
|
||||
"| _| | |__ | | | _/| _/| _| | / | (__ | |__ | |\r\n"
|
||||
"|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n"
|
||||
"\r\n"
|
||||
"Welcome to Flipper Zero Command Line Interface!\r\n"
|
||||
"|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n" ANSI_RESET
|
||||
"\r\n" ANSI_FG_BR_WHITE "Welcome to " ANSI_FLIPPER_BRAND_ORANGE
|
||||
"Flipper Zero" ANSI_FG_BR_WHITE " Command Line Interface!\r\n"
|
||||
"Read the manual: https://docs.flipper.net/development/cli\r\n"
|
||||
"Run `help` or `?` to list available commands\r\n"
|
||||
"\r\n");
|
||||
"Run `help` or `?` to list available commands\r\n" ANSI_RESET "\r\n");
|
||||
|
||||
const Version* firmware_version = furi_hal_version_get_firmware_version();
|
||||
if(firmware_version) {
|
||||
@@ -146,7 +149,7 @@ void cli_nl(Cli* cli) {
|
||||
|
||||
void cli_prompt(Cli* cli) {
|
||||
UNUSED(cli);
|
||||
printf("\r\n>: %s", furi_string_get_cstr(cli->line));
|
||||
printf("\r\n" CLI_PROMPT "%s", furi_string_get_cstr(cli->line));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
@@ -169,7 +172,7 @@ static void cli_handle_backspace(Cli* cli) {
|
||||
|
||||
cli->cursor_position--;
|
||||
} else {
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
cli_putc(cli, CliKeyBell);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +248,7 @@ static void cli_handle_enter(Cli* cli) {
|
||||
printf(
|
||||
"`%s` command not found, use `help` or `?` to list all available commands",
|
||||
furi_string_get_cstr(command));
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
cli_putc(cli, CliKeyBell);
|
||||
}
|
||||
|
||||
cli_reset(cli);
|
||||
@@ -309,8 +312,85 @@ static void cli_handle_autocomplete(Cli* cli) {
|
||||
cli_prompt(cli);
|
||||
}
|
||||
|
||||
static void cli_handle_escape(Cli* cli, char c) {
|
||||
if(c == 'A') {
|
||||
typedef enum {
|
||||
CliCharClassWord,
|
||||
CliCharClassSpace,
|
||||
CliCharClassOther,
|
||||
} CliCharClass;
|
||||
|
||||
/**
|
||||
* @brief Determines the class that a character belongs to
|
||||
*
|
||||
* The return value of this function should not be used on its own; it should
|
||||
* only be used for comparing it with other values returned by this function.
|
||||
* This function is used internally in `cli_skip_run`.
|
||||
*/
|
||||
static CliCharClass cli_char_class(char c) {
|
||||
if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_') {
|
||||
return CliCharClassWord;
|
||||
} else if(c == ' ') {
|
||||
return CliCharClassSpace;
|
||||
} else {
|
||||
return CliCharClassOther;
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
CliSkipDirectionLeft,
|
||||
CliSkipDirectionRight,
|
||||
} CliSkipDirection;
|
||||
|
||||
/**
|
||||
* @brief Skips a run of a class of characters
|
||||
*
|
||||
* @param string Input string
|
||||
* @param original_pos Position to start the search at
|
||||
* @param direction Direction in which to perform the search
|
||||
* @returns The position at which the run ends
|
||||
*/
|
||||
static size_t cli_skip_run(FuriString* string, size_t original_pos, CliSkipDirection direction) {
|
||||
if(furi_string_size(string) == 0) return original_pos;
|
||||
if(direction == CliSkipDirectionLeft && original_pos == 0) return original_pos;
|
||||
if(direction == CliSkipDirectionRight && original_pos == furi_string_size(string))
|
||||
return original_pos;
|
||||
|
||||
int8_t look_offset = (direction == CliSkipDirectionLeft) ? -1 : 0;
|
||||
int8_t increment = (direction == CliSkipDirectionLeft) ? -1 : 1;
|
||||
int32_t position = original_pos;
|
||||
CliCharClass start_class =
|
||||
cli_char_class(furi_string_get_char(string, position + look_offset));
|
||||
|
||||
while(true) {
|
||||
position += increment;
|
||||
if(position < 0) break;
|
||||
if(position >= (int32_t)furi_string_size(string)) break;
|
||||
if(cli_char_class(furi_string_get_char(string, position + look_offset)) != start_class)
|
||||
break;
|
||||
}
|
||||
|
||||
return MAX(0, position);
|
||||
}
|
||||
|
||||
void cli_process_input(Cli* cli) {
|
||||
CliKeyCombo combo = cli_read_ansi_key_combo(cli);
|
||||
FURI_LOG_T(TAG, "code=0x%02x, mod=0x%x\r\n", combo.key, combo.modifiers);
|
||||
|
||||
if(combo.key == CliKeyTab) {
|
||||
cli_handle_autocomplete(cli);
|
||||
|
||||
} else if(combo.key == CliKeySOH) {
|
||||
furi_delay_ms(33); // We are too fast, Minicom is not ready yet
|
||||
cli_motd();
|
||||
cli_prompt(cli);
|
||||
|
||||
} else if(combo.key == CliKeyETX) {
|
||||
cli_reset(cli);
|
||||
cli_prompt(cli);
|
||||
|
||||
} else if(combo.key == CliKeyEOT) {
|
||||
cli_reset(cli);
|
||||
|
||||
} else if(combo.key == CliKeyUp && combo.modifiers == CliModKeyNo) {
|
||||
// Use previous command if line buffer is empty
|
||||
if(furi_string_size(cli->line) == 0 && furi_string_cmp(cli->line, cli->last_line) != 0) {
|
||||
// Set line buffer and cursor position
|
||||
@@ -319,67 +399,85 @@ static void cli_handle_escape(Cli* cli, char c) {
|
||||
// Show new line to user
|
||||
printf("%s", furi_string_get_cstr(cli->line));
|
||||
}
|
||||
} else if(c == 'B') {
|
||||
} else if(c == 'C') {
|
||||
|
||||
} else if(combo.key == CliKeyDown && combo.modifiers == CliModKeyNo) {
|
||||
// Clear input buffer
|
||||
furi_string_reset(cli->line);
|
||||
cli->cursor_position = 0;
|
||||
printf("\r" CLI_PROMPT "\e[0K");
|
||||
|
||||
} else if(combo.key == CliKeyRight && combo.modifiers == CliModKeyNo) {
|
||||
// Move right
|
||||
if(cli->cursor_position < furi_string_size(cli->line)) {
|
||||
cli->cursor_position++;
|
||||
printf("\e[C");
|
||||
}
|
||||
} else if(c == 'D') {
|
||||
|
||||
} else if(combo.key == CliKeyLeft && combo.modifiers == CliModKeyNo) {
|
||||
// Move left
|
||||
if(cli->cursor_position > 0) {
|
||||
cli->cursor_position--;
|
||||
printf("\e[D");
|
||||
}
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void cli_process_input(Cli* cli) {
|
||||
char in_chr = cli_getc(cli);
|
||||
size_t rx_len;
|
||||
} else if(combo.key == CliKeyHome && combo.modifiers == CliModKeyNo) {
|
||||
// Move to beginning of line
|
||||
cli->cursor_position = 0;
|
||||
printf("\e[%uG", CLI_PROMPT_LENGTH + 1); // columns start at 1 \(-_-)/
|
||||
|
||||
} else if(combo.key == CliKeyEnd && combo.modifiers == CliModKeyNo) {
|
||||
// Move to end of line
|
||||
cli->cursor_position = furi_string_size(cli->line);
|
||||
printf("\e[%zuG", CLI_PROMPT_LENGTH + cli->cursor_position + 1);
|
||||
|
||||
if(in_chr == CliSymbolAsciiTab) {
|
||||
cli_handle_autocomplete(cli);
|
||||
} else if(in_chr == CliSymbolAsciiSOH) {
|
||||
furi_delay_ms(33); // We are too fast, Minicom is not ready yet
|
||||
cli_motd();
|
||||
cli_prompt(cli);
|
||||
} else if(in_chr == CliSymbolAsciiETX) {
|
||||
cli_reset(cli);
|
||||
cli_prompt(cli);
|
||||
} else if(in_chr == CliSymbolAsciiEOT) {
|
||||
cli_reset(cli);
|
||||
} else if(in_chr == CliSymbolAsciiEsc) {
|
||||
rx_len = cli_read(cli, (uint8_t*)&in_chr, 1);
|
||||
if((rx_len > 0) && (in_chr == '[')) {
|
||||
cli_read(cli, (uint8_t*)&in_chr, 1);
|
||||
cli_handle_escape(cli, in_chr);
|
||||
} else {
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
}
|
||||
} else if(in_chr == CliSymbolAsciiBackspace || in_chr == CliSymbolAsciiDel) {
|
||||
cli_handle_backspace(cli);
|
||||
} else if(in_chr == CliSymbolAsciiCR) {
|
||||
cli_handle_enter(cli);
|
||||
} else if(
|
||||
(in_chr >= 0x20 && in_chr < 0x7F) && //-V560
|
||||
combo.modifiers == CliModKeyCtrl &&
|
||||
(combo.key == CliKeyLeft || combo.key == CliKeyRight)) {
|
||||
// Skip run of similar chars to the left or right
|
||||
CliSkipDirection direction = (combo.key == CliKeyLeft) ? CliSkipDirectionLeft :
|
||||
CliSkipDirectionRight;
|
||||
cli->cursor_position = cli_skip_run(cli->line, cli->cursor_position, direction);
|
||||
printf("\e[%zuG", CLI_PROMPT_LENGTH + cli->cursor_position + 1);
|
||||
|
||||
} else if(combo.key == CliKeyBackspace || combo.key == CliKeyDEL) {
|
||||
cli_handle_backspace(cli);
|
||||
|
||||
} else if(combo.key == CliKeyETB) { // Ctrl + Backspace
|
||||
// Delete run of similar chars to the left
|
||||
size_t run_start = cli_skip_run(cli->line, cli->cursor_position, CliSkipDirectionLeft);
|
||||
furi_string_replace_at(cli->line, run_start, cli->cursor_position - run_start, "");
|
||||
cli->cursor_position = run_start;
|
||||
printf(
|
||||
"\e[%zuG%s\e[0K\e[%zuG", // move cursor, print second half of line, erase remains, move cursor again
|
||||
CLI_PROMPT_LENGTH + cli->cursor_position + 1,
|
||||
furi_string_get_cstr(cli->line) + run_start,
|
||||
CLI_PROMPT_LENGTH + run_start + 1);
|
||||
|
||||
} else if(combo.key == CliKeyCR) {
|
||||
cli_handle_enter(cli);
|
||||
|
||||
} else if(
|
||||
(combo.key >= 0x20 && combo.key < 0x7F) && //-V560
|
||||
(furi_string_size(cli->line) < CLI_INPUT_LEN_LIMIT)) {
|
||||
if(cli->cursor_position == furi_string_size(cli->line)) {
|
||||
furi_string_push_back(cli->line, in_chr);
|
||||
cli_putc(cli, in_chr);
|
||||
furi_string_push_back(cli->line, combo.key);
|
||||
cli_putc(cli, combo.key);
|
||||
} else {
|
||||
// Insert character to line buffer
|
||||
const char in_str[2] = {in_chr, 0};
|
||||
const char in_str[2] = {combo.key, 0};
|
||||
furi_string_replace_at(cli->line, cli->cursor_position, 0, in_str);
|
||||
|
||||
// Print character in replace mode
|
||||
printf("\e[4h%c\e[4l", in_chr);
|
||||
printf("\e[4h%c\e[4l", combo.key);
|
||||
fflush(stdout);
|
||||
}
|
||||
cli->cursor_position++;
|
||||
|
||||
} else {
|
||||
cli_putc(cli, CliSymbolAsciiBell);
|
||||
cli_putc(cli, CliKeyBell);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void cli_add_command(
|
||||
|
||||
@@ -10,26 +10,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
CliSymbolAsciiSOH = 0x01,
|
||||
CliSymbolAsciiETX = 0x03,
|
||||
CliSymbolAsciiEOT = 0x04,
|
||||
CliSymbolAsciiBell = 0x07,
|
||||
CliSymbolAsciiBackspace = 0x08,
|
||||
CliSymbolAsciiTab = 0x09,
|
||||
CliSymbolAsciiLF = 0x0A,
|
||||
CliSymbolAsciiCR = 0x0D,
|
||||
CliSymbolAsciiEsc = 0x1B,
|
||||
CliSymbolAsciiUS = 0x1F,
|
||||
CliSymbolAsciiSpace = 0x20,
|
||||
CliSymbolAsciiDel = 0x7F,
|
||||
} CliSymbols;
|
||||
|
||||
typedef enum {
|
||||
CliCommandFlagDefault = 0, /**< Default, loader lock is used */
|
||||
CliCommandFlagParallelSafe =
|
||||
(1 << 0), /**< Safe to run in parallel with other apps, loader lock is not used */
|
||||
CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */
|
||||
CliCommandFlagHidden = (1 << 2), /**< Not shown in `help` */
|
||||
} CliCommandFlag;
|
||||
|
||||
#define RECORD_CLI "cli"
|
||||
|
||||
76
applications/services/cli/cli_ansi.c
Normal file
76
applications/services/cli/cli_ansi.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "cli_ansi.h"
|
||||
|
||||
/**
|
||||
* @brief Converts a single character representing a special key into the enum
|
||||
* representation
|
||||
*/
|
||||
static CliKey cli_ansi_key_from_mnemonic(char c) {
|
||||
switch(c) {
|
||||
case 'A':
|
||||
return CliKeyUp;
|
||||
case 'B':
|
||||
return CliKeyDown;
|
||||
case 'C':
|
||||
return CliKeyRight;
|
||||
case 'D':
|
||||
return CliKeyLeft;
|
||||
case 'F':
|
||||
return CliKeyEnd;
|
||||
case 'H':
|
||||
return CliKeyHome;
|
||||
default:
|
||||
return CliKeyUnrecognized;
|
||||
}
|
||||
}
|
||||
|
||||
CliKeyCombo cli_read_ansi_key_combo(Cli* cli) {
|
||||
char ch = cli_getc(cli);
|
||||
|
||||
if(ch != CliKeyEsc)
|
||||
return (CliKeyCombo){
|
||||
.modifiers = CliModKeyNo,
|
||||
.key = ch,
|
||||
};
|
||||
|
||||
ch = cli_getc(cli);
|
||||
|
||||
// ESC ESC -> ESC
|
||||
if(ch == '\e')
|
||||
return (CliKeyCombo){
|
||||
.modifiers = CliModKeyNo,
|
||||
.key = '\e',
|
||||
};
|
||||
|
||||
// ESC <char> -> Alt + <char>
|
||||
if(ch != '[')
|
||||
return (CliKeyCombo){
|
||||
.modifiers = CliModKeyAlt,
|
||||
.key = cli_getc(cli),
|
||||
};
|
||||
|
||||
ch = cli_getc(cli);
|
||||
|
||||
// ESC [ 1
|
||||
if(ch == '1') {
|
||||
// ESC [ 1 ; <modifier bitfield> <key mnemonic>
|
||||
if(cli_getc(cli) == ';') {
|
||||
CliModKey modifiers = (cli_getc(cli) - '0'); // convert following digit to a number
|
||||
modifiers &= ~1;
|
||||
return (CliKeyCombo){
|
||||
.modifiers = modifiers,
|
||||
.key = cli_ansi_key_from_mnemonic(cli_getc(cli)),
|
||||
};
|
||||
}
|
||||
|
||||
return (CliKeyCombo){
|
||||
.modifiers = CliModKeyNo,
|
||||
.key = CliKeyUnrecognized,
|
||||
};
|
||||
}
|
||||
|
||||
// ESC [ <key mnemonic>
|
||||
return (CliKeyCombo){
|
||||
.modifiers = CliModKeyNo,
|
||||
.key = cli_ansi_key_from_mnemonic(ch),
|
||||
};
|
||||
}
|
||||
94
applications/services/cli/cli_ansi.h
Normal file
94
applications/services/cli/cli_ansi.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ANSI_RESET "\e[0m"
|
||||
#define ANSI_BOLD "\e[1m"
|
||||
#define ANSI_FAINT "\e[2m"
|
||||
|
||||
#define ANSI_FG_BLACK "\e[30m"
|
||||
#define ANSI_FG_RED "\e[31m"
|
||||
#define ANSI_FG_GREEN "\e[32m"
|
||||
#define ANSI_FG_YELLOW "\e[33m"
|
||||
#define ANSI_FG_BLUE "\e[34m"
|
||||
#define ANSI_FG_MAGENTA "\e[35m"
|
||||
#define ANSI_FG_CYAN "\e[36m"
|
||||
#define ANSI_FG_WHITE "\e[37m"
|
||||
#define ANSI_FG_BR_BLACK "\e[90m"
|
||||
#define ANSI_FG_BR_RED "\e[91m"
|
||||
#define ANSI_FG_BR_GREEN "\e[92m"
|
||||
#define ANSI_FG_BR_YELLOW "\e[93m"
|
||||
#define ANSI_FG_BR_BLUE "\e[94m"
|
||||
#define ANSI_FG_BR_MAGENTA "\e[95m"
|
||||
#define ANSI_FG_BR_CYAN "\e[96m"
|
||||
#define ANSI_FG_BR_WHITE "\e[97m"
|
||||
|
||||
#define ANSI_BG_BLACK "\e[40m"
|
||||
#define ANSI_BG_RED "\e[41m"
|
||||
#define ANSI_BG_GREEN "\e[42m"
|
||||
#define ANSI_BG_YELLOW "\e[43m"
|
||||
#define ANSI_BG_BLUE "\e[44m"
|
||||
#define ANSI_BG_MAGENTA "\e[45m"
|
||||
#define ANSI_BG_CYAN "\e[46m"
|
||||
#define ANSI_BG_WHITE "\e[47m"
|
||||
#define ANSI_BG_BR_BLACK "\e[100m"
|
||||
#define ANSI_BG_BR_RED "\e[101m"
|
||||
#define ANSI_BG_BR_GREEN "\e[102m"
|
||||
#define ANSI_BG_BR_YELLOW "\e[103m"
|
||||
#define ANSI_BG_BR_BLUE "\e[104m"
|
||||
#define ANSI_BG_BR_MAGENTA "\e[105m"
|
||||
#define ANSI_BG_BR_CYAN "\e[106m"
|
||||
#define ANSI_BG_BR_WHITE "\e[107m"
|
||||
|
||||
#define ANSI_FLIPPER_BRAND_ORANGE "\e[38;2;255;130;0m"
|
||||
|
||||
typedef enum {
|
||||
CliKeyUnrecognized = 0,
|
||||
|
||||
CliKeySOH = 0x01,
|
||||
CliKeyETX = 0x03,
|
||||
CliKeyEOT = 0x04,
|
||||
CliKeyBell = 0x07,
|
||||
CliKeyBackspace = 0x08,
|
||||
CliKeyTab = 0x09,
|
||||
CliKeyLF = 0x0A,
|
||||
CliKeyCR = 0x0D,
|
||||
CliKeyETB = 0x17,
|
||||
CliKeyEsc = 0x1B,
|
||||
CliKeyUS = 0x1F,
|
||||
CliKeySpace = 0x20,
|
||||
CliKeyDEL = 0x7F,
|
||||
|
||||
CliKeySpecial = 0x80,
|
||||
CliKeyLeft,
|
||||
CliKeyRight,
|
||||
CliKeyUp,
|
||||
CliKeyDown,
|
||||
CliKeyHome,
|
||||
CliKeyEnd,
|
||||
} CliKey;
|
||||
|
||||
typedef enum {
|
||||
CliModKeyNo = 0,
|
||||
CliModKeyAlt = 2,
|
||||
CliModKeyCtrl = 4,
|
||||
CliModKeyMeta = 8,
|
||||
} CliModKey;
|
||||
|
||||
typedef struct {
|
||||
CliModKey modifiers;
|
||||
CliKey key;
|
||||
} CliKeyCombo;
|
||||
|
||||
/**
|
||||
* @brief Reads a key or key combination
|
||||
*/
|
||||
CliKeyCombo cli_read_ansi_key_combo(Cli* cli);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "cli_commands.h"
|
||||
#include "cli_command_gpio.h"
|
||||
#include "cli_ansi.h"
|
||||
|
||||
#include <core/thread.h>
|
||||
#include <furi_hal.h>
|
||||
@@ -10,6 +11,7 @@
|
||||
#include <loader/loader.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/strint.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
// Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'`
|
||||
#define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d"
|
||||
@@ -52,35 +54,194 @@ void cli_command_info(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
void cli_command_help(Cli* cli, FuriString* args, void* context) {
|
||||
// Lil Easter egg :>
|
||||
void cli_command_neofetch(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(cli);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
static const char* const neofetch_logo[] = {
|
||||
" _.-------.._ -,",
|
||||
" .-\"```\"--..,,_/ /`-, -, \\ ",
|
||||
" .:\" /:/ /'\\ \\ ,_..., `. | |",
|
||||
" / ,----/:/ /`\\ _\\~`_-\"` _;",
|
||||
" ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ ",
|
||||
" | | | 0 | | .-' ,/` /",
|
||||
" | ,..\\ \\ ,.-\"` ,/` /",
|
||||
"; : `/`\"\"\\` ,/--==,/-----,",
|
||||
"| `-...| -.___-Z:_______J...---;",
|
||||
": ` _-'",
|
||||
};
|
||||
#define NEOFETCH_COLOR ANSI_FLIPPER_BRAND_ORANGE
|
||||
|
||||
// Determine logo parameters
|
||||
size_t logo_height = COUNT_OF(neofetch_logo), logo_width = 0;
|
||||
for(size_t i = 0; i < logo_height; i++)
|
||||
logo_width = MAX(logo_width, strlen(neofetch_logo[i]));
|
||||
logo_width += 4; // space between logo and info
|
||||
|
||||
// Format hostname delimiter
|
||||
const size_t size_of_hostname = 4 + strlen(furi_hal_version_get_name_ptr());
|
||||
char delimiter[64];
|
||||
memset(delimiter, '-', size_of_hostname);
|
||||
delimiter[size_of_hostname] = '\0';
|
||||
|
||||
// Get heap info
|
||||
size_t heap_total = memmgr_get_total_heap();
|
||||
size_t heap_used = heap_total - memmgr_get_free_heap();
|
||||
uint16_t heap_percent = (100 * heap_used) / heap_total;
|
||||
|
||||
// Get storage info
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
uint64_t ext_total, ext_free, ext_used, ext_percent;
|
||||
storage_common_fs_info(storage, "/ext", &ext_total, &ext_free);
|
||||
ext_used = ext_total - ext_free;
|
||||
ext_percent = (100 * ext_used) / ext_total;
|
||||
ext_used /= 1024 * 1024;
|
||||
ext_total /= 1024 * 1024;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
// Get battery info
|
||||
uint16_t charge_percent = furi_hal_power_get_pct();
|
||||
const char* charge_state;
|
||||
if(furi_hal_power_is_charging()) {
|
||||
if((charge_percent < 100) && (!furi_hal_power_is_charging_done())) {
|
||||
charge_state = "charging";
|
||||
} else {
|
||||
charge_state = "charged";
|
||||
}
|
||||
} else {
|
||||
charge_state = "discharging";
|
||||
}
|
||||
|
||||
// Get misc info
|
||||
uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency();
|
||||
const Version* version = version_get();
|
||||
uint16_t major, minor;
|
||||
furi_hal_info_get_api_version(&major, &minor);
|
||||
|
||||
// Print ASCII art with info
|
||||
const size_t info_height = 16;
|
||||
for(size_t i = 0; i < MAX(logo_height, info_height); i++) {
|
||||
printf(NEOFETCH_COLOR "%-*s", logo_width, (i < logo_height) ? neofetch_logo[i] : "");
|
||||
switch(i) {
|
||||
case 0: // you@<hostname>
|
||||
printf("you" ANSI_RESET "@" NEOFETCH_COLOR "%s", furi_hal_version_get_name_ptr());
|
||||
break;
|
||||
case 1: // delimiter
|
||||
printf(ANSI_RESET "%s", delimiter);
|
||||
break;
|
||||
case 2: // OS: FURI <edition> <branch> <version> <commit> (SDK <maj>.<min>)
|
||||
printf(
|
||||
"OS" ANSI_RESET ": FURI %s %s %s %s (SDK %hu.%hu)",
|
||||
version_get_version(version),
|
||||
version_get_gitbranch(version),
|
||||
version_get_version(version),
|
||||
version_get_githash(version),
|
||||
major,
|
||||
minor);
|
||||
break;
|
||||
case 3: // Host: <model> <hostname>
|
||||
printf(
|
||||
"Host" ANSI_RESET ": %s %s",
|
||||
furi_hal_version_get_model_code(),
|
||||
furi_hal_version_get_device_name_ptr());
|
||||
break;
|
||||
case 4: // Kernel: FreeRTOS <maj>.<min>.<build>
|
||||
printf(
|
||||
"Kernel" ANSI_RESET ": FreeRTOS %d.%d.%d",
|
||||
tskKERNEL_VERSION_MAJOR,
|
||||
tskKERNEL_VERSION_MINOR,
|
||||
tskKERNEL_VERSION_BUILD);
|
||||
break;
|
||||
case 5: // Uptime: ?h?m?s
|
||||
printf(
|
||||
"Uptime" ANSI_RESET ": %luh%lum%lus",
|
||||
uptime / 60 / 60,
|
||||
uptime / 60 % 60,
|
||||
uptime % 60);
|
||||
break;
|
||||
case 6: // ST7567 128x64 @ 1 bpp in 1.4"
|
||||
printf("Display" ANSI_RESET ": ST7567 128x64 @ 1 bpp in 1.4\"");
|
||||
break;
|
||||
case 7: // DE: GuiSrv
|
||||
printf("DE" ANSI_RESET ": GuiSrv");
|
||||
break;
|
||||
case 8: // Shell: CliSrv
|
||||
printf("Shell" ANSI_RESET ": CliSrv");
|
||||
break;
|
||||
case 9: // CPU: STM32WB55RG @ 64 MHz
|
||||
printf("CPU" ANSI_RESET ": STM32WB55RG @ 64 MHz");
|
||||
break;
|
||||
case 10: // Memory: <used> / <total> B (??%)
|
||||
printf(
|
||||
"Memory" ANSI_RESET ": %zu / %zu B (%hu%%)", heap_used, heap_total, heap_percent);
|
||||
break;
|
||||
case 11: // Disk (/ext): <used> / <total> MiB (??%)
|
||||
printf(
|
||||
"Disk (/ext)" ANSI_RESET ": %llu / %llu MiB (%llu%%)",
|
||||
ext_used,
|
||||
ext_total,
|
||||
ext_percent);
|
||||
break;
|
||||
case 12: // Battery: ??% (<state>)
|
||||
printf("Battery" ANSI_RESET ": %hu%% (%s)" ANSI_RESET, charge_percent, charge_state);
|
||||
break;
|
||||
case 13: // empty space
|
||||
break;
|
||||
case 14: // Colors (line 1)
|
||||
for(size_t j = 30; j <= 37; j++)
|
||||
printf("\e[%dm███", j);
|
||||
break;
|
||||
case 15: // Colors (line 2)
|
||||
for(size_t j = 90; j <= 97; j++)
|
||||
printf("\e[%dm███", j);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
printf(ANSI_RESET);
|
||||
#undef NEOFETCH_COLOR
|
||||
}
|
||||
|
||||
void cli_command_help(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(context);
|
||||
printf("Commands available:");
|
||||
|
||||
// Command count
|
||||
const size_t commands_count = CliCommandTree_size(cli->commands);
|
||||
const size_t commands_count_mid = commands_count / 2 + commands_count % 2;
|
||||
// Count non-hidden commands
|
||||
CliCommandTree_it_t it_count;
|
||||
CliCommandTree_it(it_count, cli->commands);
|
||||
size_t commands_count = 0;
|
||||
while(!CliCommandTree_end_p(it_count)) {
|
||||
if(!(CliCommandTree_cref(it_count)->value_ptr->flags & CliCommandFlagHidden))
|
||||
commands_count++;
|
||||
CliCommandTree_next(it_count);
|
||||
}
|
||||
|
||||
// Use 2 iterators from start and middle to show 2 columns
|
||||
CliCommandTree_it_t it_left;
|
||||
CliCommandTree_it(it_left, cli->commands);
|
||||
CliCommandTree_it_t it_right;
|
||||
CliCommandTree_it(it_right, cli->commands);
|
||||
for(size_t i = 0; i < commands_count_mid; i++)
|
||||
CliCommandTree_next(it_right);
|
||||
// Create iterators starting at different positions
|
||||
const size_t columns = 3;
|
||||
const size_t commands_per_column = (commands_count / columns) + (commands_count % columns);
|
||||
CliCommandTree_it_t iterators[columns];
|
||||
for(size_t c = 0; c < columns; c++) {
|
||||
CliCommandTree_it(iterators[c], cli->commands);
|
||||
for(size_t i = 0; i < c * commands_per_column; i++)
|
||||
CliCommandTree_next(iterators[c]);
|
||||
}
|
||||
|
||||
// Iterate throw tree
|
||||
for(size_t i = 0; i < commands_count_mid; i++) {
|
||||
// Print commands
|
||||
for(size_t r = 0; r < commands_per_column; r++) {
|
||||
printf("\r\n");
|
||||
// Left Column
|
||||
if(!CliCommandTree_end_p(it_left)) {
|
||||
printf("%-30s", furi_string_get_cstr(*CliCommandTree_ref(it_left)->key_ptr));
|
||||
CliCommandTree_next(it_left);
|
||||
}
|
||||
// Right Column
|
||||
if(!CliCommandTree_end_p(it_right)) {
|
||||
printf("%s", furi_string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr));
|
||||
CliCommandTree_next(it_right);
|
||||
|
||||
for(size_t c = 0; c < columns; c++) {
|
||||
if(!CliCommandTree_end_p(iterators[c])) {
|
||||
const CliCommandTree_itref_t* item = CliCommandTree_cref(iterators[c]);
|
||||
if(!(item->value_ptr->flags & CliCommandFlagHidden)) {
|
||||
printf("%-30s", furi_string_get_cstr(*item->key_ptr));
|
||||
}
|
||||
CliCommandTree_next(iterators[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,16 +562,18 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
int interval = 1000;
|
||||
args_read_int_and_trim(args, &interval);
|
||||
|
||||
if(interval) printf("\e[2J\e[?25l"); // Clear display, hide cursor
|
||||
|
||||
FuriThreadList* thread_list = furi_thread_list_alloc();
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
uint32_t tick = furi_get_tick();
|
||||
furi_thread_enumerate(thread_list);
|
||||
|
||||
if(interval) printf("\e[2J\e[0;0f"); // Clear display and return to 0
|
||||
if(interval) printf("\e[0;0f"); // Return to 0,0
|
||||
|
||||
uint32_t uptime = tick / furi_kernel_get_tick_frequency();
|
||||
printf(
|
||||
"Threads: %zu, ISR Time: %0.2f%%, Uptime: %luh%lum%lus\r\n",
|
||||
"\rThreads: %zu, ISR Time: %0.2f%%, Uptime: %luh%lum%lus\e[0K\r\n",
|
||||
furi_thread_list_size(thread_list),
|
||||
(double)furi_thread_list_get_isr_time(thread_list),
|
||||
uptime / 60 / 60,
|
||||
@@ -418,14 +581,14 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
uptime % 60);
|
||||
|
||||
printf(
|
||||
"Heap: total %zu, free %zu, minimum %zu, max block %zu\r\n\r\n",
|
||||
"\rHeap: total %zu, free %zu, minimum %zu, max block %zu\e[0K\r\n\r\n",
|
||||
memmgr_get_total_heap(),
|
||||
memmgr_get_free_heap(),
|
||||
memmgr_get_minimum_free_heap(),
|
||||
memmgr_heap_get_max_free_block());
|
||||
|
||||
printf(
|
||||
"%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s\r\n",
|
||||
"\r%-17s %-20s %-10s %5s %12s %6s %10s %7s %5s\e[0K\r\n",
|
||||
"AppID",
|
||||
"Name",
|
||||
"State",
|
||||
@@ -439,7 +602,7 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
for(size_t i = 0; i < furi_thread_list_size(thread_list); i++) {
|
||||
const FuriThreadListItem* item = furi_thread_list_get_at(thread_list, i);
|
||||
printf(
|
||||
"%-17s %-20s %-10s %5d 0x%08lx %6lu %10lu %7zu %5.1f\r\n",
|
||||
"\r%-17s %-20s %-10s %5d 0x%08lx %6lu %10lu %7zu %5.1f\e[0K\r\n",
|
||||
item->app_id,
|
||||
item->name,
|
||||
item->state,
|
||||
@@ -458,6 +621,8 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||
}
|
||||
}
|
||||
furi_thread_list_free(thread_list);
|
||||
|
||||
if(interval) printf("\e[?25h"); // Show cursor
|
||||
}
|
||||
|
||||
void cli_command_free(Cli* cli, FuriString* args, void* context) {
|
||||
@@ -509,6 +674,12 @@ void cli_commands_init(Cli* cli) {
|
||||
cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
||||
cli_add_command(cli, "info", CliCommandFlagParallelSafe, cli_command_info, NULL);
|
||||
cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
||||
cli_add_command(
|
||||
cli,
|
||||
"neofetch",
|
||||
CliCommandFlagParallelSafe | CliCommandFlagHidden,
|
||||
cli_command_neofetch,
|
||||
NULL);
|
||||
cli_add_command(cli, "src", CliCommandFlagParallelSafe, cli_command_src, NULL);
|
||||
cli_add_command(cli, "source", CliCommandFlagParallelSafe, cli_command_src, NULL);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_ansi.h>
|
||||
|
||||
void crypto_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
@@ -45,14 +46,14 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) {
|
||||
input = furi_string_alloc();
|
||||
char c;
|
||||
while(cli_read(cli, (uint8_t*)&c, 1) == 1) {
|
||||
if(c == CliSymbolAsciiETX) {
|
||||
if(c == CliKeyETX) {
|
||||
printf("\r\n");
|
||||
break;
|
||||
} else if(c >= 0x20 && c < 0x7F) {
|
||||
putc(c, stdout);
|
||||
fflush(stdout);
|
||||
furi_string_push_back(input, c);
|
||||
} else if(c == CliSymbolAsciiCR) {
|
||||
} else if(c == CliKeyCR) {
|
||||
printf("\r\n");
|
||||
furi_string_cat(input, "\r\n");
|
||||
}
|
||||
@@ -120,14 +121,14 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) {
|
||||
hex_input = furi_string_alloc();
|
||||
char c;
|
||||
while(cli_read(cli, (uint8_t*)&c, 1) == 1) {
|
||||
if(c == CliSymbolAsciiETX) {
|
||||
if(c == CliKeyETX) {
|
||||
printf("\r\n");
|
||||
break;
|
||||
} else if(c >= 0x20 && c < 0x7F) {
|
||||
putc(c, stdout);
|
||||
fflush(stdout);
|
||||
furi_string_push_back(hex_input, c);
|
||||
} else if(c == CliSymbolAsciiCR) {
|
||||
} else if(c == CliKeyCR) {
|
||||
printf("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,11 +54,14 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
|
||||
ret = file_browser_context->result;
|
||||
|
||||
view_holder_set_view(view_holder, NULL);
|
||||
view_holder_free(view_holder);
|
||||
file_browser_stop(file_browser);
|
||||
|
||||
file_browser_free(file_browser);
|
||||
view_holder_free(view_holder);
|
||||
|
||||
api_lock_free(file_browser_context->lock);
|
||||
free(file_browser_context);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -556,12 +556,10 @@ void canvas_draw_xbm(
|
||||
size_t height,
|
||||
const uint8_t* bitmap) {
|
||||
furi_check(canvas);
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap, IconRotation0);
|
||||
canvas_draw_xbm_ex(canvas, x, y, width, height, IconRotation0, bitmap);
|
||||
}
|
||||
|
||||
void canvas_draw_xbm_custom(
|
||||
void canvas_draw_xbm_ex(
|
||||
Canvas* canvas,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
|
||||
@@ -298,16 +298,15 @@ void canvas_draw_xbm(
|
||||
|
||||
/** Draw rotated XBM bitmap
|
||||
*
|
||||
* @param canvas Canvas instance
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param[in] width bitmap width
|
||||
* @param[in] height bitmap height
|
||||
* @param[in] rotation bitmap rotation
|
||||
* @param bitmap pointer to XBM bitmap data
|
||||
* @param canvas Canvas instance
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param[in] width bitmap width
|
||||
* @param[in] height bitmap height
|
||||
* @param[in] rotation bitmap rotation
|
||||
* @param bitmap_data pointer to XBM bitmap data
|
||||
*/
|
||||
|
||||
void canvas_draw_xbm_custom(
|
||||
void canvas_draw_xbm_ex(
|
||||
Canvas* canvas,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
|
||||
@@ -24,6 +24,7 @@ typedef struct {
|
||||
const char* header;
|
||||
char* text_buffer;
|
||||
size_t text_buffer_size;
|
||||
size_t minimum_length;
|
||||
bool clear_default_text;
|
||||
|
||||
TextInputCallback callback;
|
||||
@@ -37,7 +38,6 @@ typedef struct {
|
||||
FuriString* validator_text;
|
||||
bool validator_message_visible;
|
||||
|
||||
size_t minimum_length;
|
||||
char extra_symbols[9];
|
||||
bool cursor_select;
|
||||
size_t cursor_pos;
|
||||
|
||||
@@ -65,13 +65,18 @@ void text_input_set_result_callback(
|
||||
size_t text_buffer_size,
|
||||
bool clear_default_text);
|
||||
|
||||
/**
|
||||
* @brief Sets the minimum length of a TextInput
|
||||
* @param [in] text_input TextInput
|
||||
* @param [in] minimum_length Minimum input length
|
||||
*/
|
||||
void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length);
|
||||
|
||||
void text_input_set_validator(
|
||||
TextInput* text_input,
|
||||
TextInputValidatorCallback callback,
|
||||
void* callback_context);
|
||||
|
||||
void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length);
|
||||
|
||||
// Add up to 9 extra characters for symbol keyboard
|
||||
void text_input_add_extra_symbol(TextInput* text_input, char symbol);
|
||||
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
#define VIEW_DISPATCHER_QUEUE_LEN (16U)
|
||||
|
||||
ViewDispatcher* view_dispatcher_alloc(void) {
|
||||
ViewDispatcher* dispatcher = view_dispatcher_alloc_ex(furi_event_loop_alloc());
|
||||
dispatcher->is_event_loop_owned = true;
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
ViewDispatcher* view_dispatcher_alloc_ex(FuriEventLoop* loop) {
|
||||
ViewDispatcher* view_dispatcher = malloc(sizeof(ViewDispatcher));
|
||||
|
||||
view_dispatcher->view_port = view_port_alloc();
|
||||
@@ -18,7 +24,7 @@ ViewDispatcher* view_dispatcher_alloc(void) {
|
||||
|
||||
ViewDict_init(view_dispatcher->views);
|
||||
|
||||
view_dispatcher->event_loop = furi_event_loop_alloc();
|
||||
view_dispatcher->event_loop = loop;
|
||||
|
||||
view_dispatcher->input_queue =
|
||||
furi_message_queue_alloc(VIEW_DISPATCHER_QUEUE_LEN, sizeof(InputEvent));
|
||||
@@ -70,7 +76,7 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) {
|
||||
furi_message_queue_free(view_dispatcher->ascii_queue);
|
||||
furi_message_queue_free(view_dispatcher->event_queue);
|
||||
|
||||
furi_event_loop_free(view_dispatcher->event_loop);
|
||||
if(view_dispatcher->is_event_loop_owned) furi_event_loop_free(view_dispatcher->event_loop);
|
||||
// Free dispatcher
|
||||
free(view_dispatcher);
|
||||
}
|
||||
@@ -98,6 +104,7 @@ void view_dispatcher_set_tick_event_callback(
|
||||
ViewDispatcherTickEventCallback callback,
|
||||
uint32_t tick_period) {
|
||||
furi_check(view_dispatcher);
|
||||
furi_check(view_dispatcher->is_event_loop_owned);
|
||||
view_dispatcher->tick_event_callback = callback;
|
||||
view_dispatcher->tick_period = tick_period;
|
||||
}
|
||||
@@ -119,11 +126,12 @@ void view_dispatcher_run(ViewDispatcher* view_dispatcher) {
|
||||
uint32_t tick_period = view_dispatcher->tick_period == 0 ? FuriWaitForever :
|
||||
view_dispatcher->tick_period;
|
||||
|
||||
furi_event_loop_tick_set(
|
||||
view_dispatcher->event_loop,
|
||||
tick_period,
|
||||
view_dispatcher_handle_tick_event,
|
||||
view_dispatcher);
|
||||
if(view_dispatcher->is_event_loop_owned)
|
||||
furi_event_loop_tick_set(
|
||||
view_dispatcher->event_loop,
|
||||
tick_period,
|
||||
view_dispatcher_handle_tick_event,
|
||||
view_dispatcher);
|
||||
|
||||
furi_event_loop_run(view_dispatcher->event_loop);
|
||||
|
||||
|
||||
@@ -47,6 +47,15 @@ typedef void (*ViewDispatcherTickEventCallback)(void* context);
|
||||
*/
|
||||
ViewDispatcher* view_dispatcher_alloc(void);
|
||||
|
||||
/** Allocate ViewDispatcher instance with an externally owned event loop. If
|
||||
* this constructor is used instead of `view_dispatcher_alloc`, the burden of
|
||||
* freeing the event loop is placed on the caller.
|
||||
*
|
||||
* @param loop pointer to FuriEventLoop instance
|
||||
* @return pointer to ViewDispatcher instance
|
||||
*/
|
||||
ViewDispatcher* view_dispatcher_alloc_ex(FuriEventLoop* loop);
|
||||
|
||||
/** Free ViewDispatcher instance
|
||||
*
|
||||
* @warning All added views MUST be removed using view_dispatcher_remove_view()
|
||||
@@ -97,6 +106,10 @@ void view_dispatcher_set_navigation_event_callback(
|
||||
|
||||
/** Set tick event handler
|
||||
*
|
||||
* @warning Requires the event loop to be owned by the view dispatcher, i.e.
|
||||
* it should have been instantiated with `view_dispatcher_alloc`, not
|
||||
* `view_dispatcher_alloc_ex`.
|
||||
*
|
||||
* @param view_dispatcher ViewDispatcher instance
|
||||
* @param callback ViewDispatcherTickEventCallback
|
||||
* @param tick_period callback call period
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
DICT_DEF2(ViewDict, uint32_t, M_DEFAULT_OPLIST, View*, M_PTR_OPLIST) // NOLINT
|
||||
|
||||
struct ViewDispatcher {
|
||||
bool is_event_loop_owned;
|
||||
FuriEventLoop* event_loop;
|
||||
FuriMessageQueue* input_queue;
|
||||
FuriMessageQueue* event_queue;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_ansi.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
static void input_cli_usage(void) {
|
||||
@@ -73,12 +74,12 @@ static void input_cli_keyboard(Cli* cli, FuriString* args, FuriPubSub* event_pub
|
||||
FuriPubSub* ascii_pubsub = furi_record_open(RECORD_ASCII_EVENTS);
|
||||
while(cli_is_connected(cli)) {
|
||||
char in_chr = cli_getc(cli);
|
||||
if(in_chr == CliSymbolAsciiETX) break;
|
||||
if(in_chr == CliKeyETX) break;
|
||||
InputKey send_key = InputKeyMAX;
|
||||
uint8_t send_ascii = AsciiValueNUL;
|
||||
|
||||
switch(in_chr) {
|
||||
case CliSymbolAsciiEsc: // Escape code for arrows
|
||||
case CliKeyEsc: // Escape code for arrows
|
||||
if(!cli_read(cli, (uint8_t*)&in_chr, 1) || in_chr != '[') break;
|
||||
if(!cli_read(cli, (uint8_t*)&in_chr, 1)) break;
|
||||
if(in_chr >= 'A' && in_chr <= 'D') { // Arrows = Dpad
|
||||
@@ -89,8 +90,8 @@ static void input_cli_keyboard(Cli* cli, FuriString* args, FuriPubSub* event_pub
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CliSymbolAsciiBackspace: // (minicom) Backspace = Back
|
||||
case CliSymbolAsciiDel: // (putty/picocom) Backspace = Back
|
||||
case CliKeyBackspace: // (minicom) Backspace = Back
|
||||
case CliKeyDEL: // (putty/picocom) Backspace = Back
|
||||
if(hold) {
|
||||
send_key = InputKeyBack;
|
||||
} else {
|
||||
@@ -104,14 +105,14 @@ static void input_cli_keyboard(Cli* cli, FuriString* args, FuriPubSub* event_pub
|
||||
send_ascii = AsciiValueESC;
|
||||
}
|
||||
break;
|
||||
case CliSymbolAsciiCR: // Enter = Ok
|
||||
case CliKeyCR: // Enter = Ok
|
||||
if(hold) {
|
||||
send_key = InputKeyOk;
|
||||
} else {
|
||||
send_ascii = AsciiValueCR;
|
||||
}
|
||||
break;
|
||||
case CliSymbolAsciiSpace: // Space = Toggle hold next key
|
||||
case CliKeySpace: // Space = Toggle hold next key
|
||||
if(hold) {
|
||||
send_ascii = ' ';
|
||||
} else {
|
||||
|
||||
@@ -69,7 +69,7 @@ static RpcSystemCallbacks rpc_systems[] = {
|
||||
struct RpcSession {
|
||||
Rpc* rpc;
|
||||
|
||||
FuriThreadId thread_id;
|
||||
FuriThread* thread;
|
||||
|
||||
RpcHandlerDict_t handlers;
|
||||
FuriStreamBuffer* stream;
|
||||
@@ -175,7 +175,7 @@ size_t rpc_session_feed(
|
||||
|
||||
size_t bytes_sent = furi_stream_buffer_send(session->stream, encoded_bytes, size, timeout);
|
||||
|
||||
furi_thread_flags_set(session->thread_id, RpcEvtNewData);
|
||||
furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtNewData);
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
@@ -223,7 +223,7 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) {
|
||||
break;
|
||||
} else {
|
||||
/* Save disconnect flag and continue reading buffer */
|
||||
furi_thread_flags_set(session->thread_id, RpcEvtDisconnect);
|
||||
furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect);
|
||||
}
|
||||
} else if(flags & RpcEvtNewData) {
|
||||
// Just wake thread up
|
||||
@@ -350,32 +350,37 @@ static int32_t rpc_session_worker(void* context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rpc_session_thread_release_callback(
|
||||
FuriThread* thread,
|
||||
FuriThreadState thread_state,
|
||||
void* context) {
|
||||
if(thread_state == FuriThreadStateStopped) {
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
static void rpc_session_thread_pending_callback(void* context, uint32_t arg) {
|
||||
UNUSED(arg);
|
||||
RpcSession* session = (RpcSession*)context;
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) {
|
||||
if(rpc_systems[i].free) {
|
||||
(rpc_systems[i].free)(session->system_contexts[i]);
|
||||
}
|
||||
for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) {
|
||||
if(rpc_systems[i].free) {
|
||||
(rpc_systems[i].free)(session->system_contexts[i]);
|
||||
}
|
||||
free(session->system_contexts);
|
||||
free(session->decoded_message);
|
||||
RpcHandlerDict_clear(session->handlers);
|
||||
furi_stream_buffer_free(session->stream);
|
||||
}
|
||||
free(session->system_contexts);
|
||||
free(session->decoded_message);
|
||||
RpcHandlerDict_clear(session->handlers);
|
||||
furi_stream_buffer_free(session->stream);
|
||||
|
||||
furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);
|
||||
if(session->terminated_callback) {
|
||||
session->terminated_callback(session->context);
|
||||
}
|
||||
furi_mutex_release(session->callbacks_mutex);
|
||||
furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever);
|
||||
if(session->terminated_callback) {
|
||||
session->terminated_callback(session->context);
|
||||
}
|
||||
furi_mutex_release(session->callbacks_mutex);
|
||||
|
||||
furi_mutex_free(session->callbacks_mutex);
|
||||
furi_thread_free(thread);
|
||||
free(session);
|
||||
furi_mutex_free(session->callbacks_mutex);
|
||||
furi_thread_join(session->thread);
|
||||
furi_thread_free(session->thread);
|
||||
free(session);
|
||||
}
|
||||
|
||||
static void
|
||||
rpc_session_thread_state_callback(FuriThread* thread, FuriThreadState state, void* context) {
|
||||
UNUSED(thread);
|
||||
if(state == FuriThreadStateStopped) {
|
||||
furi_timer_pending_callback(rpc_session_thread_pending_callback, context, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,14 +416,12 @@ RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) {
|
||||
};
|
||||
rpc_add_handler(session, PB_Main_stop_session_tag, &rpc_handler);
|
||||
|
||||
FuriThread* thread =
|
||||
furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session);
|
||||
session->thread_id = furi_thread_get_id(thread);
|
||||
session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session);
|
||||
|
||||
furi_thread_set_state_context(thread, session);
|
||||
furi_thread_set_state_callback(thread, rpc_session_thread_release_callback);
|
||||
furi_thread_set_state_context(session->thread, session);
|
||||
furi_thread_set_state_callback(session->thread, rpc_session_thread_state_callback);
|
||||
|
||||
furi_thread_start(thread);
|
||||
furi_thread_start(session->thread);
|
||||
|
||||
rpc->sessions_count++;
|
||||
|
||||
@@ -434,7 +437,7 @@ void rpc_session_close(RpcSession* session) {
|
||||
rpc_session_set_send_bytes_callback(session, NULL);
|
||||
rpc_session_set_close_callback(session, NULL);
|
||||
rpc_session_set_buffer_is_empty_callback(session, NULL);
|
||||
furi_thread_flags_set(session->thread_id, RpcEvtDisconnect);
|
||||
furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect);
|
||||
}
|
||||
|
||||
void rpc_on_system_start(void* p) {
|
||||
|
||||
@@ -404,7 +404,7 @@ void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, Furi
|
||||
* @param storage pointer to a storage API instance.
|
||||
* @param source pointer to a zero-terminated string containing the source path.
|
||||
* @param dest pointer to a zero-terminated string containing the destination path.
|
||||
* @return FSE_OK if the migration was successfull completed, any other error code on failure.
|
||||
* @return FSE_OK if the migration was successfully completed, any other error code on failure.
|
||||
*/
|
||||
FS_Error storage_common_migrate(Storage* storage, const char* source, const char* dest);
|
||||
|
||||
@@ -452,7 +452,7 @@ bool storage_common_is_subdir(Storage* storage, const char* parent, const char*
|
||||
/******************* Error Functions *******************/
|
||||
|
||||
/**
|
||||
* @brief Get the textual description of a numeric error identifer.
|
||||
* @brief Get the textual description of a numeric error identifier.
|
||||
*
|
||||
* @param error_id numeric identifier of the error in question.
|
||||
* @return pointer to a statically allocated zero-terminated string containing the respective error text.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <cli/cli.h>
|
||||
#include <cli/cli_ansi.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/toolbox/dir_walk.h>
|
||||
#include <lib/toolbox/md5_calc.h>
|
||||
@@ -224,7 +225,7 @@ static void storage_cli_write(Cli* cli, FuriString* path, FuriString* args) {
|
||||
while(true) {
|
||||
uint8_t symbol = cli_getc(cli);
|
||||
|
||||
if(symbol == CliSymbolAsciiETX) {
|
||||
if(symbol == CliKeyETX) {
|
||||
size_t write_size = read_index % buffer_size;
|
||||
|
||||
if(write_size > 0) {
|
||||
|
||||
Reference in New Issue
Block a user