From b94f3ed713c069b3769fb97b467d8e1349ecb8ad Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 24 Jul 2023 00:40:52 +0200 Subject: [PATCH] Control on-screen keyboard with PC keyboard --- .../services/gui/modules/text_input.c | 58 +++++++++++++++- .../services/gui/modules/text_input.h | 2 + .../services/gui/modules/text_input_i.h | 5 ++ applications/services/input/input_cli.c | 67 +++++++++++++++---- firmware/targets/f7/api_symbols.csv | 1 + 5 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 applications/services/gui/modules/text_input_i.h diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 3da4d9c7c..210b4cc47 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -1,4 +1,4 @@ -#include "text_input.h" +#include "text_input_i.h" #include #include #include @@ -51,7 +51,7 @@ static const uint8_t keyboard_count = 2; #define ENTER_KEY '\r' #define BACKSPACE_KEY '\b' -#define SWITCH_KEYBOARD_KEY 0xfe +#define SWITCH_KEYBOARD_KEY '\t' static const TextInputKey keyboard_keys_row_1[] = { {'q', 1, 8}, @@ -490,7 +490,7 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, I } } -static bool text_input_view_input_callback(InputEvent* event, void* context) { +bool text_input_view_input_callback(InputEvent* event, void* context) { TextInput* text_input = context; furi_assert(text_input); @@ -811,3 +811,55 @@ void text_input_set_header_text(TextInput* text_input, const char* text) { with_view_model( text_input->view, TextInputModel * model, { model->header = text; }, true); } + +bool text_input_insert_character(TextInput* text_input, char chr) { + if(chr == 0x1b) { // Arrow escape code = Select input row + with_view_model( + text_input->view, + TextInputModel * model, + { + model->cursor_select = true; + model->clear_default_text = false; + model->selected_row = 0; + }, + true); + return false; // Don't consume so CLI gives arrow input + } + if(chr == 0x01) { // Ctrl A = Select all text + with_view_model( + text_input->view, + TextInputModel * model, + { + model->clear_default_text = true; + }, + true); + return true; + } + for(size_t k = 0; k < keyboard_count; k++) { + const Keyboard* keyboard = keyboards[k]; + for(size_t r = 0; r < keyboard_row_count; r++) { + const TextInputKey* row = get_row(keyboard, r); + uint8_t size = get_row_size(keyboard, r); + for(size_t key = 0; key < size; key++) { + char lower = row[key].text; + char upper = char_to_uppercase(lower); + if(chr == lower || chr == upper) { + with_view_model( + text_input->view, + TextInputModel * model, + { + model->cursor_select = false; + model->selected_keyboard = k; + model->selected_row = r; + model->selected_column = key; + bool shift = (chr == upper) != (model->clear_default_text || strlen(model->text_buffer) == 0); + text_input_handle_ok(text_input, model, shift ? InputTypeLong : InputTypeShort); + }, + true); + return true; + } + } + } + } + return false; +} diff --git a/applications/services/gui/modules/text_input.h b/applications/services/gui/modules/text_input.h index 1ba4f1cd4..d7f9d2bb7 100644 --- a/applications/services/gui/modules/text_input.h +++ b/applications/services/gui/modules/text_input.h @@ -89,6 +89,8 @@ void* text_input_get_validator_callback_context(TextInput* text_input); */ void text_input_set_header_text(TextInput* text_input, const char* text); +bool text_input_insert_character(TextInput* text_input, char c); + #ifdef __cplusplus } #endif diff --git a/applications/services/gui/modules/text_input_i.h b/applications/services/gui/modules/text_input_i.h new file mode 100644 index 000000000..ad27778b0 --- /dev/null +++ b/applications/services/gui/modules/text_input_i.h @@ -0,0 +1,5 @@ +#pragma once + +#include "text_input.h" + +bool text_input_view_input_callback(InputEvent* event, void* context); diff --git a/applications/services/input/input_cli.c b/applications/services/input/input_cli.c index d9d8ac476..d8ab003ea 100644 --- a/applications/services/input/input_cli.c +++ b/applications/services/input/input_cli.c @@ -3,6 +3,10 @@ #include #include #include +#include +#include +#include +#include static void input_cli_usage() { printf("Usage:\r\n"); @@ -10,7 +14,7 @@ static void input_cli_usage() { printf("Cmd list:\r\n"); printf("\tdump\t\t\t - dump input events\r\n"); printf("\tsend \t - send input event\r\n"); - printf("\tkeyboard\t - read keyboard input and control flipper with it\r\n"); + printf("\tkeyboard\t\t - read keyboard input and control flipper with it\r\n"); } static void input_cli_dump_events_callback(const void* value, void* ctx) { @@ -43,39 +47,78 @@ static void input_cli_dump(Cli* cli, FuriString* args, Input* input) { static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { UNUSED(args); + Gui* gui = furi_record_open(RECORD_GUI); - printf("Press CTRL+C to stop\r\n"); + printf("Using console keyboard feedback for flipper input\r\n"); + + printf("\r\nUsage:\r\n"); + printf("\tMove = Arrows\r\n"); + printf("\tOk = Enter\r\n"); + printf("\tHold Ok = Shift + Enter\r\n"); + printf("\tBack = Backspace\r\n"); + + printf("\r\nIn Keyboard:\r\n"); + printf("\tType normally on PC Keyboard\r\n"); + printf("\tQuit = Ctrl + Q\r\n"); + printf("\tSelect All = Ctrl + A\r\n"); + printf("\tMove Cursor = Arrows\r\n"); + printf("\tSave Text = Enter\r\n"); + + printf("\r\nPress CTRL+C to stop\r\n"); while(cli_is_connected(cli)) { char in_chr = cli_getc(cli); if(in_chr == CliSymbolAsciiETX) break; - InputKey send_key = InputKeyMAX; + InputType send_type = InputTypeShort; - switch(in_chr) { - case CliSymbolAsciiEsc: + ViewPort* view_port = gui->ongoing_input_view_port; + if(view_port && view_port->input_callback == view_dispatcher_input_callback) { + ViewDispatcher* view_dispatcher = view_port->input_callback_context; + if(view_dispatcher) { + View* view = view_dispatcher->current_view; + if(view && view->input_callback == text_input_view_input_callback) { + TextInput* text_input = view->context; + if(text_input) { + if(in_chr == 0x11) { // Ctrl Q = Close text input + send_key = InputKeyBack; + } else if(text_input_insert_character(text_input, in_chr)) { + continue; + } + } + } + } + } + + if(send_key == InputKeyMAX) { + switch(in_chr) { + case CliSymbolAsciiEsc: // 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') { - send_key = in_chr - 'A'; + if(in_chr >= 'A' && in_chr <= 'D') { // Arrows = Dpad + send_key = in_chr - 'A'; // Arrows in same order as InputKey } break; - case CliSymbolAsciiBackspace: + case CliSymbolAsciiBackspace: // Backspace = Back send_key = InputKeyBack; break; - case CliSymbolAsciiCR: + case 0x4d: // Shift Enter = Hold Ok + send_type = InputTypeLong; + /* fall through */ + case CliSymbolAsciiCR: // Enter = Ok send_key = InputKeyOk; break; - case CliSymbolAsciiTab: - break; default: printf("ignoring key: %u\r\n", in_chr); break; + } } if(send_key != InputKeyMAX) { - input_fake_event(input, send_key, InputTypeShort); + input_fake_event(input, send_key, send_type); } } + + furi_record_close(RECORD_GUI); } static void input_cli_send_print_usage() { diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 91ffda1f3..ab4be83c0 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -3012,6 +3012,7 @@ Function,+,text_input_free,void,TextInput* Function,+,text_input_get_validator_callback,TextInputValidatorCallback,TextInput* Function,+,text_input_get_validator_callback_context,void*,TextInput* Function,+,text_input_get_view,View*,TextInput* +Function,+,text_input_insert_character,_Bool,"TextInput*, char" Function,+,text_input_reset,void,TextInput* Function,+,text_input_set_header_text,void,"TextInput*, const char*" Function,+,text_input_set_minimum_length,void,"TextInput*, size_t"