hid_numpad.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. #include "hid_numpad.h"
  2. #include <furi.h>
  3. #include <gui/elements.h>
  4. #include <gui/icon_i.h>
  5. #include "../hid.h"
  6. #include "hid_icons.h"
  7. #include <assets_icons.h>
  8. #define TAG "HidNumpad"
  9. struct HidNumpad {
  10. View* view;
  11. Hid* hid;
  12. };
  13. typedef struct {
  14. uint8_t last_x;
  15. uint8_t last_y;
  16. uint8_t x;
  17. uint8_t y;
  18. uint8_t last_key_code;
  19. uint16_t modifier_code;
  20. bool ok_pressed;
  21. bool back_pressed;
  22. bool connected;
  23. char key_string[5];
  24. HidTransport transport;
  25. } HidNumpadModel;
  26. typedef struct {
  27. uint8_t width;
  28. char* key;
  29. uint8_t height;
  30. const Icon* icon;
  31. uint8_t value;
  32. } HidNumpadKey;
  33. typedef struct {
  34. int8_t x;
  35. int8_t y;
  36. } HidNumpadPoint;
  37. #define MARGIN_TOP 32
  38. #define MARGIN_LEFT 1
  39. #define KEY_WIDTH 20
  40. #define KEY_HEIGHT 15
  41. #define KEY_PADDING 1
  42. #define ROW_COUNT 6
  43. #define COLUMN_COUNT 3
  44. const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = {
  45. {
  46. {.width = 1, .height = 1, .icon = NULL, .key = "NL", .value = HID_KEYPAD_NUMLOCK},
  47. {.width = 1, .height = 1, .icon = NULL, .key = "/", .value = HID_KEYPAD_SLASH},
  48. {.width = 1, .height = 1, .icon = NULL, .key = "*", .value = HID_KEYPAD_ASTERISK},
  49. // {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
  50. },
  51. {
  52. {.width = 1, .height = 1, .icon = NULL, .key = "7", .value = HID_KEYPAD_7},
  53. {.width = 1, .height = 1, .icon = NULL, .key = "8", .value = HID_KEYBOARD_8},
  54. {.width = 1, .height = 1, .icon = NULL, .key = "9", .value = HID_KEYBOARD_9},
  55. // {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
  56. },
  57. {
  58. {.width = 1, .height = 1, .icon = NULL, .key = "4", .value = HID_KEYPAD_4},
  59. {.width = 1, .height = 1, .icon = NULL, .key = "5", .value = HID_KEYPAD_5},
  60. {.width = 1, .height = 1, .icon = NULL, .key = "6", .value = HID_KEYPAD_6},
  61. },
  62. {
  63. {.width = 1, .height = 1, .icon = NULL, .key = "1", .value = HID_KEYPAD_1},
  64. {.width = 1, .height = 1, .icon = NULL, .key = "2", .value = HID_KEYPAD_2},
  65. {.width = 1, .height = 1, .icon = NULL, .key = "3", .value = HID_KEYPAD_3},
  66. // {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
  67. },
  68. {
  69. {.width = 2, .height = 1, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
  70. {.width = 0, .height = 0, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
  71. {.width = 1, .height = 1, .icon = NULL, .key = ".", .value = HID_KEYPAD_DOT},
  72. },
  73. {
  74. {.width = 1, .height = 1, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
  75. {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
  76. {.width = 1, .height = 1, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
  77. },
  78. };
  79. static void hid_numpad_draw_key(
  80. Canvas* canvas,
  81. HidNumpadModel* model,
  82. uint8_t x,
  83. uint8_t y,
  84. HidNumpadKey key,
  85. bool selected) {
  86. if(!key.width || !key.height) return;
  87. canvas_set_color(canvas, ColorBlack);
  88. uint8_t keyWidth = KEY_WIDTH * key.width + KEY_PADDING * (key.width - 1);
  89. uint8_t keyHeight = KEY_HEIGHT * key.height + KEY_PADDING * (key.height - 1);
  90. if(selected) {
  91. elements_slightly_rounded_box(
  92. canvas,
  93. MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING),
  94. MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING),
  95. keyWidth,
  96. keyHeight);
  97. canvas_set_color(canvas, ColorWhite);
  98. } else {
  99. elements_slightly_rounded_frame(
  100. canvas,
  101. MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING),
  102. MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING),
  103. keyWidth,
  104. keyHeight);
  105. }
  106. if(key.icon != NULL) {
  107. canvas_draw_icon(
  108. canvas,
  109. MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key.icon->width / 2,
  110. MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + keyHeight / 2 - key.icon->height / 2,
  111. key.icon);
  112. } else {
  113. strcpy(model->key_string, key.key);
  114. canvas_draw_str_aligned(
  115. canvas,
  116. MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1,
  117. MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + keyHeight / 2 + 1,
  118. AlignCenter,
  119. AlignCenter,
  120. model->key_string);
  121. }
  122. }
  123. static void hid_numpad_draw_callback(Canvas* canvas, void* context) {
  124. furi_assert(context);
  125. HidNumpadModel* model = context;
  126. // Header
  127. canvas_set_font(canvas, FontPrimary);
  128. if(model->transport == HidTransportBle) {
  129. if(model->connected) {
  130. canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
  131. } else {
  132. canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
  133. elements_multiline_text_aligned(
  134. canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection...");
  135. }
  136. elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad");
  137. } else {
  138. elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad");
  139. }
  140. canvas_draw_icon(canvas, 3, 18, &I_Pin_back_arrow_10x8);
  141. canvas_set_font(canvas, FontSecondary);
  142. elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit");
  143. if(!model->connected && (model->transport == HidTransportBle)) {
  144. return;
  145. }
  146. canvas_set_font(canvas, FontKeyboard);
  147. uint8_t initY = 0; // = model->y == 0 ? 0 : 1;
  148. // if(model->y > ROW_COUNT) {
  149. // initY = model->y - (ROW_COUNT - 1);
  150. // }
  151. for(uint8_t y = initY; y < ROW_COUNT; y++) {
  152. const HidNumpadKey* numpadKeyRow = hid_numpad_keyset[y];
  153. uint8_t x = 0;
  154. for(uint8_t i = 0; i < COLUMN_COUNT; i++) {
  155. HidNumpadKey key = numpadKeyRow[i];
  156. bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y;
  157. bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE;
  158. hid_numpad_draw_key(
  159. canvas,
  160. model,
  161. x,
  162. y - initY,
  163. key,
  164. (!model->ok_pressed && keySelected) || backSelected);
  165. x += key.width;
  166. }
  167. }
  168. }
  169. static uint8_t hid_numpad_get_selected_key(HidNumpadModel* model) {
  170. HidNumpadKey key = hid_numpad_keyset[model->y][model->x];
  171. return key.value;
  172. }
  173. static void hid_numpad_get_select_key(HidNumpadModel* model, HidNumpadPoint delta) {
  174. do {
  175. const int delta_sum = model->y + delta.y;
  176. model->y = delta_sum < 0 ? ROW_COUNT - 1 : delta_sum % ROW_COUNT;
  177. } while(delta.y != 0 && hid_numpad_keyset[model->y][model->x].value == 0);
  178. do {
  179. const int delta_sum = model->x + delta.x;
  180. model->x = delta_sum < 0 ? COLUMN_COUNT - 1 : delta_sum % COLUMN_COUNT;
  181. } while(delta.x != 0 && hid_numpad_keyset[model->y][model->x].width == 0);
  182. }
  183. static void hid_numpad_process(HidNumpad* hid_numpad, InputEvent* event) {
  184. with_view_model(
  185. hid_numpad->view,
  186. HidNumpadModel * model,
  187. {
  188. if(event->key == InputKeyOk) {
  189. if(event->type == InputTypePress) {
  190. model->ok_pressed = true;
  191. } else if(event->type == InputTypeLong || event->type == InputTypeShort) {
  192. model->last_key_code = hid_numpad_get_selected_key(model);
  193. hid_hal_keyboard_press(
  194. hid_numpad->hid, model->modifier_code | model->last_key_code);
  195. } else if(event->type == InputTypeRelease) {
  196. hid_hal_keyboard_release(
  197. hid_numpad->hid, model->modifier_code | model->last_key_code);
  198. model->ok_pressed = false;
  199. }
  200. } else if(event->key == InputKeyBack) {
  201. if(event->type == InputTypePress) {
  202. model->back_pressed = true;
  203. } else if(event->type == InputTypeShort) {
  204. hid_hal_keyboard_press(hid_numpad->hid, HID_KEYBOARD_DELETE);
  205. hid_hal_keyboard_release(hid_numpad->hid, HID_KEYBOARD_DELETE);
  206. } else if(event->type == InputTypeRelease) {
  207. model->back_pressed = false;
  208. }
  209. } else if(event->type == InputTypePress || event->type == InputTypeRepeat) {
  210. if(event->key == InputKeyUp) {
  211. hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 0, .y = -1});
  212. } else if(event->key == InputKeyDown) {
  213. hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 0, .y = 1});
  214. } else if(event->key == InputKeyLeft) {
  215. if(model->last_x == 2 && model->last_y == 2 && model->y == 1 &&
  216. model->x == 3) {
  217. model->x = model->last_x;
  218. model->y = model->last_y;
  219. } else if(
  220. model->last_x == 2 && model->last_y == 4 && model->y == 3 &&
  221. model->x == 3) {
  222. model->x = model->last_x;
  223. model->y = model->last_y;
  224. } else
  225. hid_numpad_get_select_key(model, (HidNumpadPoint){.x = -1, .y = 0});
  226. model->last_x = 0;
  227. model->last_y = 0;
  228. } else if(event->key == InputKeyRight) {
  229. if(model->x == 2 && model->y == 2) {
  230. model->last_x = model->x;
  231. model->last_y = model->y;
  232. hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = -1});
  233. } else if(model->x == 2 && model->y == 4) {
  234. model->last_x = model->x;
  235. model->last_y = model->y;
  236. hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = -1});
  237. } else {
  238. hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = 0});
  239. }
  240. }
  241. }
  242. },
  243. true);
  244. }
  245. static bool hid_numpad_input_callback(InputEvent* event, void* context) {
  246. furi_assert(context);
  247. HidNumpad* hid_numpad = context;
  248. bool consumed = false;
  249. if(event->type == InputTypeLong && event->key == InputKeyBack) {
  250. hid_hal_keyboard_release_all(hid_numpad->hid);
  251. } else {
  252. hid_numpad_process(hid_numpad, event);
  253. consumed = true;
  254. }
  255. return consumed;
  256. }
  257. HidNumpad* hid_numpad_alloc(Hid* bt_hid) {
  258. HidNumpad* hid_numpad = malloc(sizeof(HidNumpad));
  259. hid_numpad->view = view_alloc();
  260. hid_numpad->hid = bt_hid;
  261. view_set_context(hid_numpad->view, hid_numpad);
  262. view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel));
  263. view_set_orientation(hid_numpad->view, ViewOrientationVertical);
  264. view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback);
  265. view_set_input_callback(hid_numpad->view, hid_numpad_input_callback);
  266. with_view_model(
  267. hid_numpad->view,
  268. HidNumpadModel * model,
  269. {
  270. model->transport = bt_hid->transport;
  271. model->y = 0;
  272. },
  273. true);
  274. return hid_numpad;
  275. }
  276. void hid_numpad_free(HidNumpad* hid_numpad) {
  277. furi_assert(hid_numpad);
  278. view_free(hid_numpad->view);
  279. free(hid_numpad);
  280. }
  281. View* hid_numpad_get_view(HidNumpad* hid_numpad) {
  282. furi_assert(hid_numpad);
  283. return hid_numpad->view;
  284. }
  285. void hid_numpad_set_connected_status(HidNumpad* hid_numpad, bool connected) {
  286. furi_assert(hid_numpad);
  287. with_view_model(
  288. hid_numpad->view, HidNumpadModel * model, { model->connected = connected; }, true);
  289. }