uart_text_input.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992
  1. // from https://github.com/xMasterX/all-the-plugins/blob/dev/base_pack/uart_terminal/uart_text_input.c
  2. // all credits to xMasterX for the code
  3. #include "uart_text_input.h"
  4. #include <gui/elements.h>
  5. #include "flip_social_icons.h"
  6. #include <furi.h>
  7. #include "rpc_keyboard.h"
  8. struct UART_TextInput
  9. {
  10. View *view;
  11. FuriTimer *timer;
  12. };
  13. typedef struct
  14. {
  15. const char text;
  16. const uint8_t x;
  17. const uint8_t y;
  18. } UART_TextInputKey;
  19. typedef struct
  20. {
  21. const char *header;
  22. char *text_buffer;
  23. size_t text_buffer_size;
  24. bool clear_default_text;
  25. FuriPubSubSubscription *keyboard_subscription;
  26. bool invoke_callback;
  27. UART_TextInputCallback callback;
  28. void *callback_context;
  29. uint8_t selected_row;
  30. uint8_t selected_column;
  31. UART_TextInputValidatorCallback validator_callback;
  32. void *validator_callback_context;
  33. FuriString *validator_text;
  34. bool valadator_message_visible;
  35. } UART_TextInputModel;
  36. static const uint8_t keyboard_origin_x = 1;
  37. static const uint8_t keyboard_origin_y = 29;
  38. static const uint8_t keyboard_row_count = 4;
  39. #define mode_AT "Send AT command to UART"
  40. #define ENTER_KEY '\r'
  41. #define BACKSPACE_KEY '\b'
  42. static const UART_TextInputKey keyboard_keys_row_1[] = {
  43. {'{', 1, 0},
  44. {'(', 9, 0},
  45. {'[', 17, 0},
  46. {'|', 25, 0},
  47. {'@', 33, 0},
  48. {'&', 41, 0},
  49. {'#', 49, 0},
  50. {';', 57, 0},
  51. {'^', 65, 0},
  52. {'*', 73, 0},
  53. {'`', 81, 0},
  54. {'"', 89, 0},
  55. {'~', 97, 0},
  56. {'\'', 105, 0},
  57. {'.', 113, 0},
  58. {'/', 120, 0},
  59. };
  60. static const UART_TextInputKey keyboard_keys_row_2[] = {
  61. {'q', 1, 10},
  62. {'w', 9, 10},
  63. {'e', 17, 10},
  64. {'r', 25, 10},
  65. {'t', 33, 10},
  66. {'y', 41, 10},
  67. {'u', 49, 10},
  68. {'i', 57, 10},
  69. {'o', 65, 10},
  70. {'p', 73, 10},
  71. {'0', 81, 10},
  72. {'1', 89, 10},
  73. {'2', 97, 10},
  74. {'3', 105, 10},
  75. {'=', 113, 10},
  76. {'-', 120, 10},
  77. };
  78. static const UART_TextInputKey keyboard_keys_row_3[] = {
  79. {'a', 1, 21},
  80. {'s', 9, 21},
  81. {'d', 18, 21},
  82. {'f', 25, 21},
  83. {'g', 33, 21},
  84. {'h', 41, 21},
  85. {'j', 49, 21},
  86. {'k', 57, 21},
  87. {'l', 65, 21},
  88. {BACKSPACE_KEY, 72, 13},
  89. {'4', 89, 21},
  90. {'5', 97, 21},
  91. {'6', 105, 21},
  92. {'$', 113, 21},
  93. {'%', 120, 21},
  94. };
  95. static const UART_TextInputKey keyboard_keys_row_4[] = {
  96. {'z', 1, 33},
  97. {'x', 9, 33},
  98. {'c', 18, 33},
  99. {'v', 25, 33},
  100. {'b', 33, 33},
  101. {'n', 41, 33},
  102. {'m', 49, 33},
  103. {'_', 57, 33},
  104. {ENTER_KEY, 64, 24},
  105. {'7', 89, 33},
  106. {'8', 97, 33},
  107. {'9', 105, 33},
  108. {'!', 113, 33},
  109. {'+', 120, 33},
  110. };
  111. static uint8_t get_row_size(uint8_t row_index)
  112. {
  113. uint8_t row_size = 0;
  114. switch (row_index + 1)
  115. {
  116. case 1:
  117. row_size = sizeof(keyboard_keys_row_1) / sizeof(UART_TextInputKey);
  118. break;
  119. case 2:
  120. row_size = sizeof(keyboard_keys_row_2) / sizeof(UART_TextInputKey);
  121. break;
  122. case 3:
  123. row_size = sizeof(keyboard_keys_row_3) / sizeof(UART_TextInputKey);
  124. break;
  125. case 4:
  126. row_size = sizeof(keyboard_keys_row_4) / sizeof(UART_TextInputKey);
  127. break;
  128. }
  129. return row_size;
  130. }
  131. static const UART_TextInputKey *get_row(uint8_t row_index)
  132. {
  133. const UART_TextInputKey *row = NULL;
  134. switch (row_index + 1)
  135. {
  136. case 1:
  137. row = keyboard_keys_row_1;
  138. break;
  139. case 2:
  140. row = keyboard_keys_row_2;
  141. break;
  142. case 3:
  143. row = keyboard_keys_row_3;
  144. break;
  145. case 4:
  146. row = keyboard_keys_row_4;
  147. break;
  148. }
  149. return row;
  150. }
  151. static char get_selected_char(UART_TextInputModel *model)
  152. {
  153. return get_row(model->selected_row)[model->selected_column].text;
  154. }
  155. static bool char_is_lowercase(char letter)
  156. {
  157. return (letter >= 0x61 && letter <= 0x7A);
  158. }
  159. static bool char_is_uppercase(char letter)
  160. {
  161. return (letter >= 0x41 && letter <= 0x5A);
  162. }
  163. static char char_to_lowercase(const char letter)
  164. {
  165. switch (letter)
  166. {
  167. case ' ':
  168. return 0x5f;
  169. break;
  170. case ')':
  171. return 0x28;
  172. break;
  173. case '}':
  174. return 0x7b;
  175. break;
  176. case ']':
  177. return 0x5b;
  178. break;
  179. case '\\':
  180. return 0x2f;
  181. break;
  182. case ':':
  183. return 0x3b;
  184. break;
  185. case ',':
  186. return 0x2e;
  187. break;
  188. case '?':
  189. return 0x21;
  190. break;
  191. case '>':
  192. return 0x3c;
  193. break;
  194. }
  195. if (char_is_uppercase(letter))
  196. {
  197. return (letter + 0x20);
  198. }
  199. else
  200. {
  201. return letter;
  202. }
  203. }
  204. static char char_to_uppercase(const char letter)
  205. {
  206. switch (letter)
  207. {
  208. case '_':
  209. return 0x20;
  210. break;
  211. case '(':
  212. return 0x29;
  213. break;
  214. case '{':
  215. return 0x7d;
  216. break;
  217. case '[':
  218. return 0x5d;
  219. break;
  220. case '/':
  221. return 0x5c;
  222. break;
  223. case ';':
  224. return 0x3a;
  225. break;
  226. case '.':
  227. return 0x2c;
  228. break;
  229. case '!':
  230. return 0x3f;
  231. break;
  232. case '<':
  233. return 0x3e;
  234. break;
  235. }
  236. if (char_is_lowercase(letter))
  237. {
  238. return (letter - 0x20);
  239. }
  240. else
  241. {
  242. return letter;
  243. }
  244. }
  245. static void uart_text_input_backspace_cb(UART_TextInputModel *model)
  246. {
  247. uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer);
  248. if (text_length > 0)
  249. {
  250. model->text_buffer[text_length - 1] = 0;
  251. }
  252. }
  253. static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model)
  254. {
  255. UART_TextInputModel *model = _model;
  256. // uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0;
  257. uint8_t needed_string_width = canvas_width(canvas) - 8;
  258. uint8_t start_pos = 4;
  259. if (model->invoke_callback)
  260. {
  261. model->invoke_callback = false;
  262. if (model->validator_callback && (!model->validator_callback(model->text_buffer, model->validator_text, model->validator_callback_context)))
  263. {
  264. model->valadator_message_visible = true;
  265. }
  266. else if (model->callback != 0)
  267. {
  268. // We hijack the current thread to invoke the callback (we aren't doing a draw).
  269. // model->callback(model->callback_context);
  270. return;
  271. }
  272. }
  273. const char *text = model->text_buffer;
  274. canvas_clear(canvas);
  275. canvas_set_color(canvas, ColorBlack);
  276. canvas_draw_str(canvas, 2, 7, model->header);
  277. elements_slightly_rounded_frame(canvas, 1, 8, 126, 12);
  278. if (canvas_string_width(canvas, text) > needed_string_width)
  279. {
  280. canvas_draw_str(canvas, start_pos, 17, "...");
  281. start_pos += 6;
  282. needed_string_width -= 8;
  283. }
  284. while (text != 0 && canvas_string_width(canvas, text) > needed_string_width)
  285. {
  286. text++;
  287. }
  288. if (model->clear_default_text)
  289. {
  290. elements_slightly_rounded_box(
  291. canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10);
  292. canvas_set_color(canvas, ColorWhite);
  293. }
  294. else
  295. {
  296. canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 18, "|");
  297. canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 18, "|");
  298. }
  299. canvas_draw_str(canvas, start_pos, 17, text);
  300. canvas_set_font(canvas, FontKeyboard);
  301. for (uint8_t row = 0; row <= keyboard_row_count; row++)
  302. {
  303. const uint8_t column_count = get_row_size(row);
  304. const UART_TextInputKey *keys = get_row(row);
  305. for (size_t column = 0; column < column_count; column++)
  306. {
  307. if (keys[column].text == ENTER_KEY)
  308. {
  309. canvas_set_color(canvas, ColorBlack);
  310. if (model->selected_row == row && model->selected_column == column)
  311. {
  312. canvas_draw_icon(
  313. canvas,
  314. keyboard_origin_x + keys[column].x,
  315. keyboard_origin_y + keys[column].y,
  316. &I_KeySaveSelected_24x11);
  317. }
  318. else
  319. {
  320. canvas_draw_icon(
  321. canvas,
  322. keyboard_origin_x + keys[column].x,
  323. keyboard_origin_y + keys[column].y,
  324. &I_KeySave_24x11);
  325. }
  326. }
  327. else if (keys[column].text == BACKSPACE_KEY)
  328. {
  329. canvas_set_color(canvas, ColorBlack);
  330. if (model->selected_row == row && model->selected_column == column)
  331. {
  332. canvas_draw_icon(
  333. canvas,
  334. keyboard_origin_x + keys[column].x,
  335. keyboard_origin_y + keys[column].y,
  336. &I_KeyBackspaceSelected_16x9);
  337. }
  338. else
  339. {
  340. canvas_draw_icon(
  341. canvas,
  342. keyboard_origin_x + keys[column].x,
  343. keyboard_origin_y + keys[column].y,
  344. &I_KeyBackspace_16x9);
  345. }
  346. }
  347. else
  348. {
  349. if (model->selected_row == row && model->selected_column == column)
  350. {
  351. canvas_set_color(canvas, ColorBlack);
  352. canvas_draw_box(
  353. canvas,
  354. keyboard_origin_x + keys[column].x - 1,
  355. keyboard_origin_y + keys[column].y - 8,
  356. 7,
  357. 10);
  358. canvas_set_color(canvas, ColorWhite);
  359. }
  360. else
  361. {
  362. canvas_set_color(canvas, ColorBlack);
  363. }
  364. if (0 == strcmp(model->header, mode_AT))
  365. {
  366. canvas_draw_glyph(
  367. canvas,
  368. keyboard_origin_x + keys[column].x,
  369. keyboard_origin_y + keys[column].y,
  370. char_to_uppercase(keys[column].text));
  371. }
  372. else
  373. {
  374. canvas_draw_glyph(
  375. canvas,
  376. keyboard_origin_x + keys[column].x,
  377. keyboard_origin_y + keys[column].y,
  378. keys[column].text);
  379. }
  380. }
  381. }
  382. }
  383. if (model->valadator_message_visible)
  384. {
  385. canvas_set_font(canvas, FontSecondary);
  386. canvas_set_color(canvas, ColorWhite);
  387. canvas_draw_box(canvas, 8, 10, 110, 48);
  388. canvas_set_color(canvas, ColorBlack);
  389. canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);
  390. canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);
  391. canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);
  392. elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text));
  393. canvas_set_font(canvas, FontKeyboard);
  394. }
  395. }
  396. static void
  397. uart_text_input_handle_up(UART_TextInput *uart_text_input, UART_TextInputModel *model)
  398. {
  399. UNUSED(uart_text_input);
  400. if (model->selected_row > 0)
  401. {
  402. model->selected_row--;
  403. if (model->selected_column > get_row_size(model->selected_row) - 6)
  404. {
  405. model->selected_column = model->selected_column + 1;
  406. }
  407. }
  408. }
  409. static void
  410. uart_text_input_handle_down(UART_TextInput *uart_text_input, UART_TextInputModel *model)
  411. {
  412. UNUSED(uart_text_input);
  413. if (model->selected_row < keyboard_row_count - 1)
  414. {
  415. model->selected_row++;
  416. if (model->selected_column > get_row_size(model->selected_row) - 4)
  417. {
  418. model->selected_column = model->selected_column - 1;
  419. }
  420. }
  421. }
  422. static void
  423. uart_text_input_handle_left(UART_TextInput *uart_text_input, UART_TextInputModel *model)
  424. {
  425. UNUSED(uart_text_input);
  426. if (model->selected_column > 0)
  427. {
  428. model->selected_column--;
  429. }
  430. else
  431. {
  432. model->selected_column = get_row_size(model->selected_row) - 1;
  433. }
  434. }
  435. static void
  436. uart_text_input_handle_right(UART_TextInput *uart_text_input, UART_TextInputModel *model)
  437. {
  438. UNUSED(uart_text_input);
  439. if (model->selected_column < get_row_size(model->selected_row) - 1)
  440. {
  441. model->selected_column++;
  442. }
  443. else
  444. {
  445. model->selected_column = 0;
  446. }
  447. }
  448. static void uart_text_input_handle_ok(
  449. UART_TextInput *uart_text_input,
  450. UART_TextInputModel *model,
  451. bool shift)
  452. {
  453. char selected = get_selected_char(model);
  454. uint8_t text_length = strlen(model->text_buffer);
  455. if (0 == strcmp(model->header, mode_AT))
  456. {
  457. selected = char_to_uppercase(selected);
  458. }
  459. if (shift)
  460. {
  461. if (0 == strcmp(model->header, mode_AT))
  462. {
  463. selected = char_to_lowercase(selected);
  464. }
  465. else
  466. {
  467. selected = char_to_uppercase(selected);
  468. }
  469. }
  470. if (selected == ENTER_KEY)
  471. {
  472. if (model->validator_callback &&
  473. (!model->validator_callback(
  474. model->text_buffer, model->validator_text, model->validator_callback_context)))
  475. {
  476. model->valadator_message_visible = true;
  477. furi_timer_start(uart_text_input->timer, furi_kernel_get_tick_frequency() * 4);
  478. }
  479. else if (model->callback != 0 && text_length > 0)
  480. {
  481. model->callback(model->callback_context);
  482. }
  483. }
  484. else if (selected == BACKSPACE_KEY)
  485. {
  486. uart_text_input_backspace_cb(model);
  487. }
  488. else
  489. {
  490. if (model->clear_default_text)
  491. {
  492. text_length = 0;
  493. }
  494. if (text_length < (model->text_buffer_size - 1))
  495. {
  496. model->text_buffer[text_length] = selected;
  497. model->text_buffer[text_length + 1] = 0;
  498. }
  499. }
  500. model->clear_default_text = false;
  501. }
  502. static bool uart_text_input_view_input_callback(InputEvent *event, void *context)
  503. {
  504. UART_TextInput *uart_text_input = context;
  505. furi_assert(uart_text_input);
  506. bool consumed = false;
  507. // Acquire model
  508. UART_TextInputModel *model = view_get_model(uart_text_input->view);
  509. if ((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) &&
  510. model->valadator_message_visible)
  511. {
  512. model->valadator_message_visible = false;
  513. consumed = true;
  514. }
  515. else if (event->type == InputTypeShort)
  516. {
  517. consumed = true;
  518. switch (event->key)
  519. {
  520. case InputKeyUp:
  521. uart_text_input_handle_up(uart_text_input, model);
  522. break;
  523. case InputKeyDown:
  524. uart_text_input_handle_down(uart_text_input, model);
  525. break;
  526. case InputKeyLeft:
  527. uart_text_input_handle_left(uart_text_input, model);
  528. break;
  529. case InputKeyRight:
  530. uart_text_input_handle_right(uart_text_input, model);
  531. break;
  532. case InputKeyOk:
  533. uart_text_input_handle_ok(uart_text_input, model, false);
  534. break;
  535. default:
  536. consumed = false;
  537. break;
  538. }
  539. }
  540. else if (event->type == InputTypeLong)
  541. {
  542. consumed = true;
  543. switch (event->key)
  544. {
  545. case InputKeyUp:
  546. uart_text_input_handle_up(uart_text_input, model);
  547. break;
  548. case InputKeyDown:
  549. uart_text_input_handle_down(uart_text_input, model);
  550. break;
  551. case InputKeyLeft:
  552. uart_text_input_handle_left(uart_text_input, model);
  553. break;
  554. case InputKeyRight:
  555. uart_text_input_handle_right(uart_text_input, model);
  556. break;
  557. case InputKeyOk:
  558. uart_text_input_handle_ok(uart_text_input, model, true);
  559. break;
  560. case InputKeyBack:
  561. uart_text_input_backspace_cb(model);
  562. break;
  563. default:
  564. consumed = false;
  565. break;
  566. }
  567. }
  568. else if (event->type == InputTypeRepeat)
  569. {
  570. consumed = true;
  571. switch (event->key)
  572. {
  573. case InputKeyUp:
  574. uart_text_input_handle_up(uart_text_input, model);
  575. break;
  576. case InputKeyDown:
  577. uart_text_input_handle_down(uart_text_input, model);
  578. break;
  579. case InputKeyLeft:
  580. uart_text_input_handle_left(uart_text_input, model);
  581. break;
  582. case InputKeyRight:
  583. uart_text_input_handle_right(uart_text_input, model);
  584. break;
  585. case InputKeyBack:
  586. uart_text_input_backspace_cb(model);
  587. break;
  588. default:
  589. consumed = false;
  590. break;
  591. }
  592. }
  593. // Commit model
  594. view_commit_model(uart_text_input->view, consumed);
  595. return consumed;
  596. }
  597. void uart_text_input_timer_callback(void *context)
  598. {
  599. furi_assert(context);
  600. UART_TextInput *uart_text_input = context;
  601. with_view_model(
  602. uart_text_input->view,
  603. UART_TextInputModel * model,
  604. { model->valadator_message_visible = false; },
  605. true);
  606. }
  607. static void text_input_keyboard_callback_line(UART_TextInput *text_input, const RpcKeyboardEvent *event)
  608. {
  609. with_view_model(
  610. text_input->view,
  611. UART_TextInputModel * model,
  612. {
  613. if (model->text_buffer != NULL && model->text_buffer_size > 0)
  614. {
  615. if (event->data.length > 0)
  616. {
  617. furi_mutex_acquire(event->data.mutex, FuriWaitForever);
  618. size_t len = event->data.length;
  619. if (len >= model->text_buffer_size)
  620. {
  621. len = model->text_buffer_size - 1;
  622. }
  623. bool newline = false;
  624. bool substitutions = false;
  625. size_t copy_index = 0;
  626. for (size_t i = 0; i < len; i++)
  627. {
  628. char ch = event->data.message[i];
  629. if ((ch >= 0x20 && ch <= 0x7E) || ch == '\n' || ch == '\r')
  630. {
  631. model->text_buffer[copy_index++] = ch;
  632. if (ch == '\n' || ch == '\r')
  633. {
  634. newline = event->data.newline_enabled && !substitutions; // TODO: No min-length check?
  635. break;
  636. }
  637. }
  638. }
  639. model->text_buffer[copy_index] = '\0';
  640. furi_mutex_release(event->data.mutex);
  641. FURI_LOG_D("text_input", "copy: %d, %d, %s", len, copy_index, model->text_buffer);
  642. // Set focus on Save
  643. model->selected_row = 3;
  644. model->selected_column = 8;
  645. // Hijack the next draw to invoke the callback if newline is true.
  646. model->invoke_callback = newline;
  647. }
  648. }
  649. },
  650. true);
  651. }
  652. static void text_input_keyboard_type_key(UART_TextInput *text_input, char selected)
  653. {
  654. with_view_model(
  655. text_input->view,
  656. UART_TextInputModel * model,
  657. {
  658. size_t text_length = strlen(model->text_buffer);
  659. char search_key = isupper(selected) ? tolower(selected) : selected == ' ' ? '_'
  660. : selected;
  661. bool found = false;
  662. for (int row = 0; row < keyboard_row_count; row++)
  663. {
  664. const UART_TextInputKey *keys = get_row(row);
  665. for (int column = 0; column < get_row_size(row); column++)
  666. {
  667. if (keys[column].text == search_key)
  668. {
  669. model->selected_row = row;
  670. model->selected_column = column;
  671. found = true;
  672. }
  673. }
  674. }
  675. if (!found)
  676. {
  677. // Set focus on Backspace
  678. model->selected_row = 2;
  679. model->selected_column = 9;
  680. }
  681. if (selected == ENTER_KEY)
  682. {
  683. if (model->validator_callback && (!model->validator_callback(model->text_buffer, model->validator_text, model->validator_callback_context)))
  684. {
  685. model->valadator_message_visible = true;
  686. furi_timer_start(text_input->timer, furi_kernel_get_tick_frequency() * 4);
  687. }
  688. else if (model->callback != 0)
  689. { // TODO: no min-length check
  690. model->callback(model->callback_context);
  691. }
  692. }
  693. else if (selected == BACKSPACE_KEY)
  694. {
  695. uart_text_input_backspace_cb(model);
  696. }
  697. else
  698. {
  699. if (model->clear_default_text)
  700. {
  701. text_length = 0;
  702. }
  703. if (selected == RPC_KEYBOARD_KEY_LEFT || selected == RPC_KEYBOARD_KEY_RIGHT)
  704. {
  705. // ignore these keys for now
  706. }
  707. else if (text_length < (model->text_buffer_size - 1))
  708. {
  709. model->text_buffer[text_length] = selected;
  710. model->text_buffer[text_length + 1] = 0;
  711. }
  712. }
  713. model->clear_default_text = false;
  714. },
  715. true);
  716. }
  717. static void text_input_keyboard_callback(const void *message, void *context)
  718. {
  719. UART_TextInput *text_input = context;
  720. const RpcKeyboardEvent *event = message;
  721. if (event == NULL)
  722. {
  723. return;
  724. }
  725. switch (event->type)
  726. {
  727. case RpcKeyboardEventTypeTextEntered:
  728. text_input_keyboard_callback_line(text_input, event);
  729. break;
  730. case RpcKeyboardEventTypeCharEntered:
  731. char ch = event->data.message[0];
  732. FURI_LOG_I("text_input", "char: %c", ch);
  733. text_input_keyboard_type_key(text_input, ch);
  734. break;
  735. case RpcKeyboardEventTypeMacroEntered:
  736. furi_mutex_acquire(event->data.mutex, FuriWaitForever);
  737. FURI_LOG_I("text_input", "macro: %s", event->data.message);
  738. for (size_t i = 0; i < event->data.length; i++)
  739. {
  740. text_input_keyboard_type_key(text_input, event->data.message[i]);
  741. }
  742. furi_mutex_release(event->data.mutex);
  743. break;
  744. }
  745. }
  746. static void text_input_view_enter_callback(void *context)
  747. {
  748. furi_assert(context);
  749. UART_TextInput *text_input = context;
  750. if (furi_record_exists(RECORD_RPC_KEYBOARD))
  751. {
  752. RpcKeyboard *rpc_keyboard = furi_record_open(RECORD_RPC_KEYBOARD);
  753. FuriPubSub *rpc_keyboard_pubsub = rpc_keyboard_get_pubsub(rpc_keyboard);
  754. if (rpc_keyboard_pubsub != NULL)
  755. {
  756. with_view_model(text_input->view, UART_TextInputModel * model, { model->keyboard_subscription = furi_pubsub_subscribe(rpc_keyboard_pubsub, text_input_keyboard_callback, text_input); }, false);
  757. }
  758. furi_record_close(RECORD_RPC_KEYBOARD);
  759. }
  760. }
  761. static void text_input_view_exit_callback(void *context)
  762. {
  763. furi_assert(context);
  764. UART_TextInput *text_input = context;
  765. if (furi_record_exists(RECORD_RPC_KEYBOARD))
  766. {
  767. RpcKeyboard *rpc_keyboard = furi_record_open(RECORD_RPC_KEYBOARD);
  768. FuriPubSub *rpc_keyboard_pubsub = rpc_keyboard_get_pubsub(rpc_keyboard);
  769. if (rpc_keyboard_pubsub != NULL)
  770. {
  771. with_view_model(
  772. text_input->view,
  773. UART_TextInputModel * model,
  774. {
  775. furi_pubsub_unsubscribe(rpc_keyboard_pubsub, model->keyboard_subscription);
  776. model->keyboard_subscription = NULL;
  777. },
  778. false);
  779. }
  780. furi_record_close(RECORD_RPC_KEYBOARD);
  781. }
  782. }
  783. UART_TextInput *uart_text_input_alloc()
  784. {
  785. UART_TextInput *uart_text_input = malloc(sizeof(UART_TextInput));
  786. uart_text_input->view = view_alloc();
  787. view_set_context(uart_text_input->view, uart_text_input);
  788. view_allocate_model(uart_text_input->view, ViewModelTypeLocking, sizeof(UART_TextInputModel));
  789. view_set_draw_callback(uart_text_input->view, uart_text_input_view_draw_callback);
  790. view_set_input_callback(uart_text_input->view, uart_text_input_view_input_callback);
  791. view_set_enter_callback(uart_text_input->view, text_input_view_enter_callback);
  792. view_set_exit_callback(uart_text_input->view, text_input_view_exit_callback);
  793. uart_text_input->timer =
  794. furi_timer_alloc(uart_text_input_timer_callback, FuriTimerTypeOnce, uart_text_input);
  795. with_view_model(
  796. uart_text_input->view,
  797. UART_TextInputModel * model,
  798. { model->validator_text = furi_string_alloc(); },
  799. false);
  800. uart_text_input_reset(uart_text_input);
  801. return uart_text_input;
  802. }
  803. void uart_text_input_free(UART_TextInput *uart_text_input)
  804. {
  805. furi_assert(uart_text_input);
  806. with_view_model(
  807. uart_text_input->view,
  808. UART_TextInputModel * model,
  809. { furi_string_free(model->validator_text); },
  810. false);
  811. // Send stop command
  812. furi_timer_stop(uart_text_input->timer);
  813. // Release allocated memory
  814. furi_timer_free(uart_text_input->timer);
  815. view_free(uart_text_input->view);
  816. free(uart_text_input);
  817. }
  818. void uart_text_input_reset(UART_TextInput *uart_text_input)
  819. {
  820. furi_assert(uart_text_input);
  821. with_view_model(
  822. uart_text_input->view,
  823. UART_TextInputModel * model,
  824. {
  825. model->text_buffer_size = 0;
  826. model->header = "";
  827. model->selected_row = 0;
  828. model->selected_column = 0;
  829. model->clear_default_text = false;
  830. model->text_buffer = NULL;
  831. model->text_buffer_size = 0;
  832. model->callback = NULL;
  833. model->callback_context = NULL;
  834. model->validator_callback = NULL;
  835. model->validator_callback_context = NULL;
  836. furi_string_reset(model->validator_text);
  837. model->valadator_message_visible = false;
  838. },
  839. true);
  840. }
  841. View *uart_text_input_get_view(UART_TextInput *uart_text_input)
  842. {
  843. furi_assert(uart_text_input);
  844. return uart_text_input->view;
  845. }
  846. void uart_text_input_set_result_callback(
  847. UART_TextInput *uart_text_input,
  848. UART_TextInputCallback callback,
  849. void *callback_context,
  850. char *text_buffer,
  851. size_t text_buffer_size,
  852. bool clear_default_text)
  853. {
  854. with_view_model(
  855. uart_text_input->view,
  856. UART_TextInputModel * model,
  857. {
  858. model->callback = callback;
  859. model->callback_context = callback_context;
  860. model->text_buffer = text_buffer;
  861. model->text_buffer_size = text_buffer_size;
  862. model->clear_default_text = clear_default_text;
  863. if (text_buffer && text_buffer[0] != '\0')
  864. {
  865. // Set focus on Save
  866. model->selected_row = 3;
  867. model->selected_column = 8;
  868. }
  869. },
  870. true);
  871. }
  872. void uart_text_input_set_validator(
  873. UART_TextInput *uart_text_input,
  874. UART_TextInputValidatorCallback callback,
  875. void *callback_context)
  876. {
  877. with_view_model(
  878. uart_text_input->view,
  879. UART_TextInputModel * model,
  880. {
  881. model->validator_callback = callback;
  882. model->validator_callback_context = callback_context;
  883. },
  884. true);
  885. }
  886. UART_TextInputValidatorCallback
  887. uart_text_input_get_validator_callback(UART_TextInput *uart_text_input)
  888. {
  889. UART_TextInputValidatorCallback validator_callback = NULL;
  890. with_view_model(
  891. uart_text_input->view,
  892. UART_TextInputModel * model,
  893. { validator_callback = model->validator_callback; },
  894. false);
  895. return validator_callback;
  896. }
  897. void *uart_text_input_get_validator_callback_context(UART_TextInput *uart_text_input)
  898. {
  899. void *validator_callback_context = NULL;
  900. with_view_model(
  901. uart_text_input->view,
  902. UART_TextInputModel * model,
  903. { validator_callback_context = model->validator_callback_context; },
  904. false);
  905. return validator_callback_context;
  906. }
  907. void uart_text_input_set_header_text(UART_TextInput *uart_text_input, const char *text)
  908. {
  909. with_view_model(
  910. uart_text_input->view, UART_TextInputModel * model, { model->header = text; }, true);
  911. }