hid_numpad.c 12 KB

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