diff --git a/applications/external/wifi_marauder_companion/assets/KeyKeyboardSelected_10x11.png b/applications/external/wifi_marauder_companion/assets/KeyKeyboardSelected_10x11.png new file mode 100644 index 000000000..231880386 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/KeyKeyboardSelected_10x11.png differ diff --git a/applications/external/wifi_marauder_companion/assets/KeyKeyboard_10x11.png b/applications/external/wifi_marauder_companion/assets/KeyKeyboard_10x11.png new file mode 100644 index 000000000..1f4c03478 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/KeyKeyboard_10x11.png differ diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h index ae976c6bf..d223af79a 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h @@ -12,3 +12,4 @@ ADD_SCENE(wifi_marauder, script_confirm_delete, ScriptConfirmDelete) ADD_SCENE(wifi_marauder, script_stage_edit, ScriptStageEdit) ADD_SCENE(wifi_marauder, script_stage_add, ScriptStageAdd) ADD_SCENE(wifi_marauder, script_stage_edit_list, ScriptStageEditList) +ADD_SCENE(wifi_marauder, sniffpmkid_options, SniffPmkidOptions) diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_sniffpmkid_options.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_sniffpmkid_options.c new file mode 100644 index 000000000..02869030e --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_sniffpmkid_options.c @@ -0,0 +1,117 @@ +#include "../wifi_marauder_app_i.h" + +enum SubmenuIndex { + SubmenuIndexPassive, + SubmenuIndexActive, + SubmenuIndexTargetedPassive, + SubmenuIndexTargetedActive, + SubmenuIndexChannelPassive, + SubmenuIndexChannelActive, +}; + +static void wifi_marauder_scene_sniffpmkid_options_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + app->is_custom_tx_string = false; // this will be set if needed by text input + switch(index) { + case SubmenuIndexPassive: + app->selected_tx_string = "sniffpmkid"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexActive: + app->selected_tx_string = "sniffpmkid -d"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexTargetedPassive: + app->selected_tx_string = "sniffpmkid -l"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexTargetedActive: + app->selected_tx_string = "sniffpmkid -d -l"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexChannelPassive: + app->selected_tx_string = "sniffpmkid -c"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneTextInput); + break; + case SubmenuIndexChannelActive: + app->selected_tx_string = "sniffpmkid -d -c"; + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneSniffPmkidOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneTextInput); + break; + } +} + +void wifi_marauder_scene_sniffpmkid_options_on_enter(void* context) { + WifiMarauderApp* app = context; + + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "Sniff PMKID"); + submenu_add_item( + submenu, + "Passive", + SubmenuIndexPassive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + submenu_add_item( + submenu, + "Active (Force Deauth)", + SubmenuIndexActive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + submenu_add_item( + submenu, + "Targeted Passive (List)", + SubmenuIndexTargetedPassive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + submenu_add_item( + submenu, + "Targeted Active (List)", + SubmenuIndexTargetedActive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + submenu_add_item( + submenu, + "On Channel # - Passive", + SubmenuIndexChannelPassive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + submenu_add_item( + submenu, + "On Channel # - Active", + SubmenuIndexChannelActive, + wifi_marauder_scene_sniffpmkid_options_callback, + app); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneSniffPmkidOptions)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_sniffpmkid_options_on_event(void* context, SceneManagerEvent event) { + //WifiMarauderApp* app = context; + UNUSED(context); + UNUSED(event); + bool consumed = false; + + return consumed; +} + +void wifi_marauder_scene_sniffpmkid_options_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c index 6f22d0adb..28a3ed2a9 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c @@ -97,13 +97,6 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, - {"Sniff PMKID", - {"ap", "channel"}, - 2, - {"sniffpmkid -d -l", "sniffpmkid -c"}, - TOGGLE_ARGS, - FOCUS_CONSOLE_END, - SHOW_STOPSCAN_TIP}, {"Channel", {"get", "set"}, 2, @@ -161,6 +154,14 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin return; } + if(app->selected_tx_string && + strncmp("sniffpmkid", app->selected_tx_string, strlen("sniffpmkid")) == 0) { + // sniffpmkid submenu + view_dispatcher_send_custom_event( + app->view_dispatcher, WifiMarauderEventStartSniffPmkidOptions); + return; + } + // Select automation script if(index == NUM_MENU_ITEMS - 2) { view_dispatcher_send_custom_event( @@ -254,6 +255,10 @@ bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state( app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSelect); + } else if(event.event == WifiMarauderEventStartSniffPmkidOptions) { + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneSniffPmkidOptions); } consumed = true; } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.h b/applications/external/wifi_marauder_companion/wifi_marauder_app.h index 303fb59b4..7e3856059 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.h @@ -4,7 +4,7 @@ extern "C" { #endif -#define WIFI_MARAUDER_APP_VERSION "v0.3.5" +#define WIFI_MARAUDER_APP_VERSION "v0.3.6" typedef struct WifiMarauderApp WifiMarauderApp; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h index 2a16522bb..45e1e5920 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -26,7 +26,7 @@ #include #include -#define NUM_MENU_ITEMS (18) +#define NUM_MENU_ITEMS (17) #define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096) #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h index 5acdfa38e..b6d9f8274 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h @@ -8,5 +8,6 @@ typedef enum { WifiMarauderEventSaveDestinationMac, WifiMarauderEventStartSettingsInit, WifiMarauderEventStartLogViewer, - WifiMarauderEventStartScriptSelect + WifiMarauderEventStartScriptSelect, + WifiMarauderEventStartSniffPmkidOptions } WifiMarauderCustomEvent; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c index d9eee83c9..663d0130e 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c @@ -15,149 +15,215 @@ typedef struct { const uint8_t y; } WIFI_TextInputKey; +typedef struct { + const WIFI_TextInputKey* rows[3]; + const uint8_t keyboard_index; +} Keyboard; + typedef struct { const char* header; char* text_buffer; size_t text_buffer_size; + size_t minimum_length; bool clear_default_text; + bool cursor_select; + size_t cursor_pos; + WIFI_TextInputCallback callback; void* callback_context; uint8_t selected_row; uint8_t selected_column; + uint8_t selected_keyboard; WIFI_TextInputValidatorCallback validator_callback; void* validator_callback_context; FuriString* validator_text; - bool valadator_message_visible; + bool validator_message_visible; } WIFI_TextInputModel; static const uint8_t keyboard_origin_x = 1; static const uint8_t keyboard_origin_y = 29; -static const uint8_t keyboard_row_count = 4; +static const uint8_t keyboard_row_count = 3; +static const uint8_t keyboard_count = 2; #define ENTER_KEY '\r' #define BACKSPACE_KEY '\b' +#define SWITCH_KEYBOARD_KEY 0xfe static const WIFI_TextInputKey keyboard_keys_row_1[] = { - {'{', 1, 0}, - {'(', 9, 0}, - {'[', 17, 0}, - {'|', 25, 0}, - {'@', 33, 0}, - {'&', 41, 0}, - {'#', 49, 0}, - {';', 57, 0}, - {'^', 65, 0}, - {'*', 73, 0}, - {'`', 81, 0}, - {'"', 89, 0}, - {'~', 97, 0}, - {'\'', 105, 0}, - {'.', 113, 0}, - {'/', 120, 0}, + {'q', 1, 8}, + {'w', 10, 8}, + {'e', 19, 8}, + {'r', 28, 8}, + {'t', 37, 8}, + {'y', 46, 8}, + {'u', 55, 8}, + {'i', 64, 8}, + {'o', 73, 8}, + {'p', 82, 8}, + {'0', 91, 8}, + {'1', 100, 8}, + {'2', 110, 8}, + {'3', 120, 8}, }; static const WIFI_TextInputKey keyboard_keys_row_2[] = { - {'q', 1, 10}, - {'w', 9, 10}, - {'e', 17, 10}, - {'r', 25, 10}, - {'t', 33, 10}, - {'y', 41, 10}, - {'u', 49, 10}, - {'i', 57, 10}, - {'o', 65, 10}, - {'p', 73, 10}, - {'0', 81, 10}, - {'1', 89, 10}, - {'2', 97, 10}, - {'3', 105, 10}, - {'=', 113, 10}, - {'-', 120, 10}, + {'a', 1, 20}, + {'s', 10, 20}, + {'d', 19, 20}, + {'f', 28, 20}, + {'g', 37, 20}, + {'h', 46, 20}, + {'j', 55, 20}, + {'k', 64, 20}, + {'l', 73, 20}, + {BACKSPACE_KEY, 82, 12}, + {'4', 100, 20}, + {'5', 110, 20}, + {'6', 120, 20}, }; static const WIFI_TextInputKey keyboard_keys_row_3[] = { - {'a', 1, 21}, - {'s', 9, 21}, - {'d', 18, 21}, - {'f', 25, 21}, - {'g', 33, 21}, - {'h', 41, 21}, - {'j', 49, 21}, - {'k', 57, 21}, - {'l', 65, 21}, - {BACKSPACE_KEY, 72, 13}, - {'4', 89, 21}, - {'5', 97, 21}, - {'6', 105, 21}, - {'$', 113, 21}, - {'%', 120, 21}, - + {SWITCH_KEYBOARD_KEY, 1, 23}, + {'z', 13, 32}, + {'x', 21, 32}, + {'c', 28, 32}, + {'v', 36, 32}, + {'b', 44, 32}, + {'n', 52, 32}, + {'m', 59, 32}, + {'_', 67, 32}, + {ENTER_KEY, 74, 23}, + {'7', 100, 32}, + {'8', 110, 32}, + {'9', 120, 32}, }; -static const WIFI_TextInputKey keyboard_keys_row_4[] = { - {'z', 1, 33}, - {'x', 9, 33}, - {'c', 18, 33}, - {'v', 25, 33}, - {'b', 33, 33}, - {'n', 41, 33}, - {'m', 49, 33}, - {'_', 57, 33}, - {ENTER_KEY, 64, 24}, - {'7', 89, 33}, - {'8', 97, 33}, - {'9', 105, 33}, - {'!', 113, 33}, - {'+', 120, 33}, +static const WIFI_TextInputKey symbol_keyboard_keys_row_1[] = { + {'!', 2, 8}, + {'@', 12, 8}, + {'#', 22, 8}, + {'$', 32, 8}, + {'%', 42, 8}, + {'^', 52, 8}, + {'&', 62, 8}, + {'(', 71, 8}, + {')', 81, 8}, + {'0', 91, 8}, + {'1', 100, 8}, + {'2', 110, 8}, + {'3', 120, 8}, }; -static uint8_t get_row_size(uint8_t row_index) { +static const WIFI_TextInputKey symbol_keyboard_keys_row_2[] = { + {'~', 2, 20}, + {'+', 12, 20}, + {'-', 22, 20}, + {'=', 32, 20}, + {'[', 42, 20}, + {']', 52, 20}, + {'{', 62, 20}, + {'}', 72, 20}, + {BACKSPACE_KEY, 82, 12}, + {'4', 100, 20}, + {'5', 110, 20}, + {'6', 120, 20}, +}; + +static const WIFI_TextInputKey symbol_keyboard_keys_row_3[] = { + {SWITCH_KEYBOARD_KEY, 1, 23}, + {'.', 15, 32}, + {',', 29, 32}, + {';', 41, 32}, + {'`', 53, 32}, + {'\'', 65, 32}, + {ENTER_KEY, 74, 23}, + {'7', 100, 32}, + {'8', 110, 32}, + {'9', 120, 32}, +}; + +static const Keyboard keyboard = { + .rows = + { + keyboard_keys_row_1, + keyboard_keys_row_2, + keyboard_keys_row_3, + }, + .keyboard_index = 0, +}; + +static const Keyboard symbol_keyboard = { + .rows = + { + symbol_keyboard_keys_row_1, + symbol_keyboard_keys_row_2, + symbol_keyboard_keys_row_3, + }, + .keyboard_index = 1, +}; + +static const Keyboard* keyboards[] = { + &keyboard, + &symbol_keyboard, +}; + +static void switch_keyboard(WIFI_TextInputModel* model) { + model->selected_keyboard = (model->selected_keyboard + 1) % keyboard_count; +} + +static uint8_t get_row_size(const Keyboard* keyboard, uint8_t row_index) { uint8_t row_size = 0; - - switch(row_index + 1) { - case 1: - row_size = sizeof(keyboard_keys_row_1) / sizeof(WIFI_TextInputKey); - break; - case 2: - row_size = sizeof(keyboard_keys_row_2) / sizeof(WIFI_TextInputKey); - break; - case 3: - row_size = sizeof(keyboard_keys_row_3) / sizeof(WIFI_TextInputKey); - break; - case 4: - row_size = sizeof(keyboard_keys_row_4) / sizeof(WIFI_TextInputKey); - break; + if(keyboard == &symbol_keyboard) { + switch(row_index + 1) { + case 1: + row_size = COUNT_OF(symbol_keyboard_keys_row_1); + break; + case 2: + row_size = COUNT_OF(symbol_keyboard_keys_row_2); + break; + case 3: + row_size = COUNT_OF(symbol_keyboard_keys_row_3); + break; + default: + furi_crash(NULL); + } + } else { + switch(row_index + 1) { + case 1: + row_size = COUNT_OF(keyboard_keys_row_1); + break; + case 2: + row_size = COUNT_OF(keyboard_keys_row_2); + break; + case 3: + row_size = COUNT_OF(keyboard_keys_row_3); + break; + default: + furi_crash(NULL); + } } return row_size; } -static const WIFI_TextInputKey* get_row(uint8_t row_index) { +static const WIFI_TextInputKey* get_row(const Keyboard* keyboard, uint8_t row_index) { const WIFI_TextInputKey* row = NULL; - - switch(row_index + 1) { - case 1: - row = keyboard_keys_row_1; - break; - case 2: - row = keyboard_keys_row_2; - break; - case 3: - row = keyboard_keys_row_3; - break; - case 4: - row = keyboard_keys_row_4; - break; + if(row_index < 3) { + row = keyboard->rows[row_index]; + } else { + furi_crash(NULL); } return row; } static char get_selected_char(WIFI_TextInputModel* model) { - return get_row(model->selected_row)[model->selected_column].text; + return get_row( + keyboards[model->selected_keyboard], model->selected_row)[model->selected_column] + .text; } static bool char_is_lowercase(char letter) { @@ -165,36 +231,9 @@ static bool char_is_lowercase(char letter) { } static char char_to_uppercase(const char letter) { - switch(letter) { - case '_': + if(letter == '_') { return 0x20; - break; - case '(': - return 0x29; - break; - case '{': - return 0x7d; - break; - case '[': - return 0x5d; - break; - case '/': - return 0x5c; - break; - case ';': - return 0x3a; - break; - case '.': - return 0x2c; - break; - case '!': - return 0x3f; - break; - case '<': - return 0x3e; - break; - } - if(char_is_lowercase(letter)) { + } else if(char_is_lowercase(letter)) { return (letter - 0x20); } else { return letter; @@ -202,86 +241,96 @@ static char char_to_uppercase(const char letter) { } static void wifi_text_input_backspace_cb(WIFI_TextInputModel* model) { - uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer); - if(text_length > 0) { - model->text_buffer[text_length - 1] = 0; + if(model->clear_default_text) { + model->text_buffer[0] = 0; + model->cursor_pos = 0; + } else if(model->cursor_pos > 0) { + char* move = model->text_buffer + model->cursor_pos; + memmove(move - 1, move, strlen(move) + 1); + model->cursor_pos--; } } static void wifi_text_input_view_draw_callback(Canvas* canvas, void* _model) { WIFI_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 start_pos = 4; - const char* text = model->text_buffer; + model->cursor_pos = model->cursor_pos > text_length ? text_length : model->cursor_pos; + size_t cursor_pos = model->cursor_pos; canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); - canvas_draw_str(canvas, 2, 7, model->header); - elements_slightly_rounded_frame(canvas, 1, 8, 126, 12); + canvas_draw_str(canvas, 2, 8, model->header); + elements_slightly_rounded_frame(canvas, 1, 12, 126, 15); - if(canvas_string_width(canvas, text) > needed_string_width) { - canvas_draw_str(canvas, start_pos, 17, "..."); - start_pos += 6; - needed_string_width -= 8; - } - - while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { - text++; + char buf[model->text_buffer_size + 1]; + if(model->text_buffer) { + strlcpy(buf, model->text_buffer, sizeof(buf)); } + char* str = buf; if(model->clear_default_text) { elements_slightly_rounded_box( - canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10); + canvas, start_pos - 1, 14, canvas_string_width(canvas, str) + 2, 10); canvas_set_color(canvas, ColorWhite); } else { - canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 18, "|"); - canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 18, "|"); + char* move = str + cursor_pos; + memmove(move + 1, move, strlen(move) + 1); + str[cursor_pos] = '|'; } - canvas_draw_str(canvas, start_pos, 17, text); + + if(cursor_pos > 0 && canvas_string_width(canvas, str) > needed_string_width) { + canvas_draw_str(canvas, start_pos, 22, "..."); + start_pos += 6; + needed_string_width -= 8; + for(uint32_t off = 0; + strlen(str) && canvas_string_width(canvas, str) > needed_string_width && + off < cursor_pos; + off++) { + str++; + } + } + + if(canvas_string_width(canvas, str) > needed_string_width) { + needed_string_width -= 4; + size_t len = strlen(str); + while(len && canvas_string_width(canvas, str) > needed_string_width) { + str[len--] = '\0'; + } + strcat(str, "..."); + } + + canvas_draw_str(canvas, start_pos, 22, str); canvas_set_font(canvas, FontKeyboard); - for(uint8_t row = 0; row <= keyboard_row_count; row++) { - const uint8_t column_count = get_row_size(row); - const WIFI_TextInputKey* keys = get_row(row); + for(uint8_t row = 0; row < keyboard_row_count; row++) { + const uint8_t column_count = get_row_size(keyboards[model->selected_keyboard], row); + const WIFI_TextInputKey* keys = get_row(keyboards[model->selected_keyboard], row); for(size_t column = 0; column < column_count; column++) { + bool selected = !model->cursor_select && model->selected_row == row && + model->selected_column == column; + const Icon* icon = NULL; if(keys[column].text == ENTER_KEY) { - canvas_set_color(canvas, ColorBlack); - if(model->selected_row == row && model->selected_column == column) { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeySaveSelected_24x11); - } else { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeySave_24x11); - } + icon = selected ? &I_KeySaveSelected_24x11 : &I_KeySave_24x11; + } else if(keys[column].text == SWITCH_KEYBOARD_KEY) { + icon = selected ? &I_KeyKeyboardSelected_10x11 : &I_KeyKeyboard_10x11; } else if(keys[column].text == BACKSPACE_KEY) { - canvas_set_color(canvas, ColorBlack); - if(model->selected_row == row && model->selected_column == column) { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeyBackspaceSelected_16x9); - } else { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeyBackspace_16x9); - } + icon = selected ? &I_KeyBackspaceSelected_16x9 : &I_KeyBackspace_16x9; + } + canvas_set_color(canvas, ColorBlack); + if(icon != NULL) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + icon); } else { - if(model->selected_row == row && model->selected_column == column) { - canvas_set_color(canvas, ColorBlack); + if(selected) { canvas_draw_box( canvas, keyboard_origin_x + keys[column].x - 1, @@ -289,19 +338,25 @@ static void wifi_text_input_view_draw_callback(Canvas* canvas, void* _model) { 7, 10); canvas_set_color(canvas, ColorWhite); - } else { - canvas_set_color(canvas, ColorBlack); } - canvas_draw_glyph( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - keys[column].text); + if(model->clear_default_text || text_length == 0) { + canvas_draw_glyph( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + char_to_uppercase(keys[column].text)); + } else { + canvas_draw_glyph( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + keys[column].text); + } } } } - if(model->valadator_message_visible) { + if(model->validator_message_visible) { canvas_set_font(canvas, FontSecondary); canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 8, 10, 110, 48); @@ -319,37 +374,69 @@ static void UNUSED(wifi_text_input); if(model->selected_row > 0) { model->selected_row--; - if(model->selected_column > get_row_size(model->selected_row) - 6) { + if(model->selected_row == 0 && + model->selected_column > + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 6) { model->selected_column = model->selected_column + 1; } + if(model->selected_row == 1 && + model->selected_keyboard == symbol_keyboard.keyboard_index) { + if(model->selected_column > 5) + model->selected_column += 2; + else if(model->selected_column > 1) + model->selected_column += 1; + } + } else { + model->cursor_select = true; + model->clear_default_text = false; } } static void wifi_text_input_handle_down(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { UNUSED(wifi_text_input); - if(model->selected_row < keyboard_row_count - 1) { + if(model->cursor_select) { + model->cursor_select = false; + } else if(model->selected_row < keyboard_row_count - 1) { model->selected_row++; - if(model->selected_column > get_row_size(model->selected_row) - 4) { + if(model->selected_row == 1 && + model->selected_column > + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 4) { model->selected_column = model->selected_column - 1; } + if(model->selected_row == 2 && + model->selected_keyboard == symbol_keyboard.keyboard_index) { + if(model->selected_column > 7) + model->selected_column -= 2; + else if(model->selected_column > 1) + model->selected_column -= 1; + } } } static void wifi_text_input_handle_left(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { UNUSED(wifi_text_input); - if(model->selected_column > 0) { + if(model->cursor_select) { + if(model->cursor_pos > 0) { + model->cursor_pos = CLAMP(model->cursor_pos - 1, strlen(model->text_buffer), 0u); + } + } else if(model->selected_column > 0) { model->selected_column--; } else { - model->selected_column = get_row_size(model->selected_row) - 1; + model->selected_column = + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 1; } } static void wifi_text_input_handle_right(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { UNUSED(wifi_text_input); - if(model->selected_column < get_row_size(model->selected_row) - 1) { + if(model->cursor_select) { + model->cursor_pos = CLAMP(model->cursor_pos + 1, strlen(model->text_buffer), 0u); + } else if( + model->selected_column < + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 1) { model->selected_column++; } else { model->selected_column = 0; @@ -359,35 +446,49 @@ static void static void wifi_text_input_handle_ok( WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model, - bool shift) { + InputType type) { + if(model->cursor_select) return; + bool shift = type == InputTypeLong; + bool repeat = type == InputTypeRepeat; char selected = get_selected_char(model); - uint8_t text_length = strlen(model->text_buffer); - - if(shift) { - selected = char_to_uppercase(selected); - } + size_t text_length = strlen(model->text_buffer); if(selected == ENTER_KEY) { if(model->validator_callback && (!model->validator_callback( model->text_buffer, model->validator_text, model->validator_callback_context))) { - model->valadator_message_visible = true; + model->validator_message_visible = true; furi_timer_start(wifi_text_input->timer, furi_kernel_get_tick_frequency() * 4); - } else if(model->callback != 0 && text_length > 0) { + } else if(model->callback != 0 && text_length >= model->minimum_length) { model->callback(model->callback_context); } - } else if(selected == BACKSPACE_KEY) { - wifi_text_input_backspace_cb(model); + } else if(selected == SWITCH_KEYBOARD_KEY) { + switch_keyboard(model); } else { - if(model->clear_default_text) { - text_length = 0; - } - if(text_length < (model->text_buffer_size - 1)) { - model->text_buffer[text_length] = selected; - model->text_buffer[text_length + 1] = 0; + if(selected == BACKSPACE_KEY) { + wifi_text_input_backspace_cb(model); + } else if(!repeat) { + if(model->clear_default_text) { + text_length = 0; + } + if(text_length < (model->text_buffer_size - 1)) { + if(shift != (text_length == 0)) { + selected = char_to_uppercase(selected); + } + if(model->clear_default_text) { + model->text_buffer[0] = selected; + model->text_buffer[1] = '\0'; + model->cursor_pos = 1; + } else { + char* move = model->text_buffer + model->cursor_pos; + memmove(move + 1, move, strlen(move) + 1); + model->text_buffer[model->cursor_pos] = selected; + model->cursor_pos++; + } + } } + model->clear_default_text = false; } - model->clear_default_text = false; } static bool wifi_text_input_view_input_callback(InputEvent* event, void* context) { @@ -400,8 +501,8 @@ static bool wifi_text_input_view_input_callback(InputEvent* event, void* context WIFI_TextInputModel* model = view_get_model(wifi_text_input->view); if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && - model->valadator_message_visible) { - model->valadator_message_visible = false; + model->validator_message_visible) { + model->validator_message_visible = false; consumed = true; } else if(event->type == InputTypeShort) { consumed = true; @@ -419,7 +520,7 @@ static bool wifi_text_input_view_input_callback(InputEvent* event, void* context wifi_text_input_handle_right(wifi_text_input, model); break; case InputKeyOk: - wifi_text_input_handle_ok(wifi_text_input, model, false); + wifi_text_input_handle_ok(wifi_text_input, model, event->type); break; default: consumed = false; @@ -441,7 +542,7 @@ static bool wifi_text_input_view_input_callback(InputEvent* event, void* context wifi_text_input_handle_right(wifi_text_input, model); break; case InputKeyOk: - wifi_text_input_handle_ok(wifi_text_input, model, true); + wifi_text_input_handle_ok(wifi_text_input, model, event->type); break; case InputKeyBack: wifi_text_input_backspace_cb(model); @@ -465,6 +566,9 @@ static bool wifi_text_input_view_input_callback(InputEvent* event, void* context case InputKeyRight: wifi_text_input_handle_right(wifi_text_input, model); break; + case InputKeyOk: + wifi_text_input_handle_ok(wifi_text_input, model, event->type); + break; case InputKeyBack: wifi_text_input_backspace_cb(model); break; @@ -487,7 +591,7 @@ void wifi_text_input_timer_callback(void* context) { with_view_model( wifi_text_input->view, WIFI_TextInputModel * model, - { model->valadator_message_visible = false; }, + { model->validator_message_visible = false; }, true); } @@ -505,7 +609,12 @@ WIFI_TextInput* wifi_text_input_alloc() { with_view_model( wifi_text_input->view, WIFI_TextInputModel * model, - { model->validator_text = furi_string_alloc(); }, + { + model->validator_text = furi_string_alloc(); + model->minimum_length = 1; + model->cursor_pos = 0; + model->cursor_select = false; + }, false); wifi_text_input_reset(wifi_text_input); @@ -537,11 +646,14 @@ void wifi_text_input_reset(WIFI_TextInput* wifi_text_input) { wifi_text_input->view, WIFI_TextInputModel * model, { - model->text_buffer_size = 0; model->header = ""; model->selected_row = 0; model->selected_column = 0; + model->selected_keyboard = 0; + model->minimum_length = 1; model->clear_default_text = false; + model->cursor_pos = 0; + model->cursor_select = false; model->text_buffer = NULL; model->text_buffer_size = 0; model->callback = NULL; @@ -549,7 +661,7 @@ void wifi_text_input_reset(WIFI_TextInput* wifi_text_input) { model->validator_callback = NULL; model->validator_callback_context = NULL; furi_string_reset(model->validator_text); - model->valadator_message_visible = false; + model->validator_message_visible = false; }, true); } @@ -575,15 +687,28 @@ void wifi_text_input_set_result_callback( model->text_buffer = text_buffer; model->text_buffer_size = text_buffer_size; model->clear_default_text = clear_default_text; + model->cursor_select = false; if(text_buffer && text_buffer[0] != '\0') { + model->cursor_pos = strlen(text_buffer); // Set focus on Save model->selected_row = 2; - model->selected_column = 8; + model->selected_column = 9; + model->selected_keyboard = 0; + } else { + model->cursor_pos = 0; } }, true); } +void wifi_text_input_set_minimum_length(WIFI_TextInput* wifi_text_input, size_t minimum_length) { + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { model->minimum_length = minimum_length; }, + true); +} + void wifi_text_input_set_validator( WIFI_TextInput* wifi_text_input, WIFI_TextInputValidatorCallback callback, diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h index b6b1f7bdf..f5efe9234 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h @@ -22,27 +22,27 @@ WIFI_TextInput* wifi_text_input_alloc(); /** Deinitialize and free text input * - * @param wifi_text_input WIFI_TextInput instance + * @param text_input WIFI_TextInput instance */ -void wifi_text_input_free(WIFI_TextInput* wifi_text_input); +void wifi_text_input_free(WIFI_TextInput* text_input); /** Clean text input view Note: this function does not free memory * - * @param wifi_text_input Text input instance + * @param text_input Text input instance */ -void wifi_text_input_reset(WIFI_TextInput* wifi_text_input); +void wifi_text_input_reset(WIFI_TextInput* text_input); /** Get text input view * - * @param wifi_text_input WIFI_TextInput instance + * @param text_input WIFI_TextInput instance * * @return View instance that can be used for embedding */ -View* wifi_text_input_get_view(WIFI_TextInput* wifi_text_input); +View* wifi_text_input_get_view(WIFI_TextInput* text_input); /** Set text input result callback * - * @param wifi_text_input WIFI_TextInput instance + * @param text_input WIFI_TextInput instance * @param callback callback fn * @param callback_context callback context * @param text_buffer pointer to YOUR text buffer, that we going @@ -53,7 +53,7 @@ View* wifi_text_input_get_view(WIFI_TextInput* wifi_text_input); * event */ void wifi_text_input_set_result_callback( - WIFI_TextInput* wifi_text_input, + WIFI_TextInput* text_input, WIFI_TextInputCallback callback, void* callback_context, char* text_buffer, @@ -61,22 +61,23 @@ void wifi_text_input_set_result_callback( bool clear_default_text); void wifi_text_input_set_validator( - WIFI_TextInput* wifi_text_input, + WIFI_TextInput* text_input, WIFI_TextInputValidatorCallback callback, void* callback_context); -WIFI_TextInputValidatorCallback - wifi_text_input_get_validator_callback(WIFI_TextInput* wifi_text_input); +void wifi_text_input_set_minimum_length(WIFI_TextInput* text_input, size_t minimum_length); -void* wifi_text_input_get_validator_callback_context(WIFI_TextInput* wifi_text_input); +WIFI_TextInputValidatorCallback wifi_text_input_get_validator_callback(WIFI_TextInput* text_input); + +void* wifi_text_input_get_validator_callback_context(WIFI_TextInput* text_input); /** Set text input header text * - * @param wifi_text_input WIFI_TextInput instance + * @param text_input WIFI_TextInput instance * @param text text to be shown */ -void wifi_text_input_set_header_text(WIFI_TextInput* wifi_text_input, const char* text); +void wifi_text_input_set_header_text(WIFI_TextInput* text_input, const char* text); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index 7383757d2..e57b1ac2d 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -23,6 +23,14 @@ static void subghz_remote_app_tick_event_callback(void* context) { SubGhzRemoteApp* subghz_remote_app_alloc() { SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp)); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("unirf"), SUBREM_APP_FOLDER); + + if(!storage_simply_mkdir(storage, SUBREM_APP_FOLDER)) { + //FURI_LOG_E(TAG, "Could not create folder %s", SUBREM_APP_FOLDER); + } + furi_record_close(RECORD_STORAGE); + // Enable power for External CC1101 if it is connected furi_hal_subghz_enable_ext_power(); // Auto switch to internal radio if external radio is not available diff --git a/applications/services/application.fam b/applications/services/application.fam index afa69301d..0b5009096 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -9,6 +9,6 @@ App( "desktop", "loader", "power", - "namechange_srv", + "namechanger_srv", ], ) diff --git a/applications/services/namechanger/application.fam b/applications/services/namechanger/application.fam index a4bc23938..0eaeab987 100644 --- a/applications/services/namechanger/application.fam +++ b/applications/services/namechanger/application.fam @@ -1,7 +1,7 @@ App( - appid="namechange_srv", + appid="namechanger_srv", apptype=FlipperAppType.STARTUP, - entry_point="namechange_on_system_start", + entry_point="namechanger_on_system_start", requires=["storage", "cli", "bt"], conflicts=["updater"], order=600, diff --git a/applications/services/namechanger/namechange.c b/applications/services/namechanger/namechange.c deleted file mode 100644 index 22cb0d9de..000000000 --- a/applications/services/namechanger/namechange.c +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -int32_t namechange_on_system_start(void* p) { - UNUSED(p); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { - return 0; - } - - // Wait for all required services to start and create their records - uint8_t timeout = 0; - while(!furi_record_exists(RECORD_CLI) || !furi_record_exists(RECORD_BT) || - !furi_record_exists(RECORD_STORAGE)) { - timeout++; - if(timeout > 250) { - return 0; - } - furi_delay_ms(5); - } - - // Hehe bad code now here, bad bad bad, very bad, bad example, dont take it, make it better - - if(NameChanger_Init()) { - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_close(cli); - furi_delay_ms(2); // why i added delays here - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); - - furi_delay_ms(3); - Bt* bt = furi_record_open(RECORD_BT); - if(!bt_set_profile(bt, BtProfileSerial)) { - //FURI_LOG_D(TAG, "Failed to touch bluetooth to name change"); - } - furi_record_close(RECORD_BT); - bt = NULL; - furi_delay_ms(3); - } - - return 0; -} \ No newline at end of file diff --git a/lib/toolbox/namechanger.c b/applications/services/namechanger/namechanger.c similarity index 60% rename from lib/toolbox/namechanger.c rename to applications/services/namechanger/namechanger.c index 94e08ac13..796dd2d3b 100644 --- a/lib/toolbox/namechanger.c +++ b/applications/services/namechanger/namechanger.c @@ -1,10 +1,15 @@ #include "namechanger.h" +#include #include +#include +#include +#include +#include #include #define TAG "NameChanger" -bool NameChanger_Init() { +static bool namechanger_init() { Storage* storage = furi_record_open(RECORD_STORAGE); // Kostil + velosiped = top ficha @@ -64,4 +69,43 @@ bool NameChanger_Init() { furi_string_free(str); return res; +} + +int32_t namechanger_on_system_start(void* p) { + UNUSED(p); + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + return 0; + } + + // Wait for all required services to start and create their records + uint8_t timeout = 0; + while(!furi_record_exists(RECORD_CLI) || !furi_record_exists(RECORD_BT) || + !furi_record_exists(RECORD_STORAGE)) { + timeout++; + if(timeout > 250) { + return 0; + } + furi_delay_ms(5); + } + + // Hehe bad code now here, bad bad bad, very bad, bad example, dont take it, make it better + + if(namechanger_init()) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_close(cli); + furi_delay_ms(2); // why i added delays here + cli_session_open(cli, &cli_vcp); + furi_record_close(RECORD_CLI); + + furi_delay_ms(3); + Bt* bt = furi_record_open(RECORD_BT); + if(!bt_set_profile(bt, BtProfileSerial)) { + //FURI_LOG_D(TAG, "Failed to touch bluetooth to name change"); + } + furi_record_close(RECORD_BT); + bt = NULL; + furi_delay_ms(3); + } + + return 0; } \ No newline at end of file diff --git a/applications/services/namechanger/namechanger.h b/applications/services/namechanger/namechanger.h new file mode 100644 index 000000000..dac1b7952 --- /dev/null +++ b/applications/services/namechanger/namechanger.h @@ -0,0 +1,5 @@ +#pragma once + +#define NAMECHANGER_HEADER "Flipper Name File" +#define NAMECHANGER_VERSION 1 +#define NAMECHANGER_PATH EXT_PATH("dolphin/name.settings") diff --git a/applications/settings/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c index 317d57654..7e04271f2 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c index d27613410..334aa0102 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -436,7 +436,11 @@ static bool hid_send_report(uint8_t report_id) { if((hid_semaphore == NULL) || (hid_connected == false)) return false; if((boot_protocol == true) && (report_id != ReportIdKeyboard)) return false; - furi_check(furi_semaphore_acquire(hid_semaphore, FuriWaitForever) == FuriStatusOk); + FuriStatus status = furi_semaphore_acquire(hid_semaphore, HID_INTERVAL * 2); + if(status == FuriStatusErrorTimeout) { + return false; + } + furi_check(status == FuriStatusOk); if(hid_connected == false) { return false; } diff --git a/lib/toolbox/namechanger.h b/lib/toolbox/namechanger.h deleted file mode 100644 index bfaadec15..000000000 --- a/lib/toolbox/namechanger.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#define NAMECHANGER_HEADER "Flipper Name File" -#define NAMECHANGER_VERSION 1 -#define NAMECHANGER_PATH EXT_PATH("dolphin/name.settings") - -#include "stdbool.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Initializes the name changer. (Load name file, apply changes) -bool NameChanger_Init(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index ed1654e36..eb265cee8 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -1,7 +1,7 @@ import os from dataclasses import dataclass, field from enum import Enum -from typing import Callable, List, Optional, Tuple +from typing import Callable, List, Optional, Tuple, Union class FlipperManifestException(Exception): @@ -56,7 +56,7 @@ class FlipperApplication: # .fap-specific sources: List[str] = field(default_factory=lambda: ["*.c*"]) - fap_version: str | Tuple[int] = "0.1" + fap_version: Union[str, Tuple[int]] = "0.1" fap_icon: Optional[str] = None fap_libs: List[str] = field(default_factory=list) fap_category: str = ""