hid.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. #include "hid.h"
  2. #include "views.h"
  3. #include <notification/notification_messages.h>
  4. #include <dolphin/dolphin.h>
  5. #define TAG "HidApp"
  6. enum HidDebugSubmenuIndex {
  7. HidSubmenuIndexKeynote,
  8. HidSubmenuIndexKeynoteVertical,
  9. HidSubmenuIndexKeyboard,
  10. HidSubmenuIndexMedia,
  11. HidSubmenuIndexTikTok,
  12. HidSubmenuIndexMouse,
  13. HidSubmenuIndexMouseClicker,
  14. HidSubmenuIndexMouseJiggler,
  15. };
  16. static void hid_submenu_callback(void* context, uint32_t index) {
  17. furi_assert(context);
  18. Hid* app = context;
  19. if(index == HidSubmenuIndexKeynote) {
  20. app->view_id = HidViewKeynote;
  21. hid_keynote_set_orientation(app->hid_keynote, false);
  22. view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
  23. } else if(index == HidSubmenuIndexKeynoteVertical) {
  24. app->view_id = HidViewKeynote;
  25. hid_keynote_set_orientation(app->hid_keynote, true);
  26. view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
  27. } else if(index == HidSubmenuIndexKeyboard) {
  28. app->view_id = HidViewKeyboard;
  29. view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard);
  30. } else if(index == HidSubmenuIndexMedia) {
  31. app->view_id = HidViewMedia;
  32. view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia);
  33. } else if(index == HidSubmenuIndexMouse) {
  34. app->view_id = HidViewMouse;
  35. view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse);
  36. } else if(index == HidSubmenuIndexTikTok) {
  37. app->view_id = BtHidViewTikTok;
  38. view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
  39. } else if(index == HidSubmenuIndexMouseClicker) {
  40. app->view_id = HidViewMouseClicker;
  41. view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker);
  42. } else if(index == HidSubmenuIndexMouseJiggler) {
  43. app->view_id = HidViewMouseJiggler;
  44. view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler);
  45. }
  46. }
  47. static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {
  48. furi_assert(context);
  49. Hid* hid = context;
  50. bool connected = (status == BtStatusConnected);
  51. if(hid->transport == HidTransportBle) {
  52. if(connected) {
  53. notification_internal_message(hid->notifications, &sequence_set_blue_255);
  54. } else {
  55. notification_internal_message(hid->notifications, &sequence_reset_blue);
  56. }
  57. }
  58. hid_keynote_set_connected_status(hid->hid_keynote, connected);
  59. hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
  60. hid_media_set_connected_status(hid->hid_media, connected);
  61. hid_mouse_set_connected_status(hid->hid_mouse, connected);
  62. hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected);
  63. hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected);
  64. hid_tiktok_set_connected_status(hid->hid_tiktok, connected);
  65. }
  66. static void hid_dialog_callback(DialogExResult result, void* context) {
  67. furi_assert(context);
  68. Hid* app = context;
  69. if(result == DialogExResultLeft) {
  70. view_dispatcher_stop(app->view_dispatcher);
  71. } else if(result == DialogExResultRight) {
  72. view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view
  73. } else if(result == DialogExResultCenter) {
  74. view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu);
  75. }
  76. }
  77. static uint32_t hid_exit_confirm_view(void* context) {
  78. UNUSED(context);
  79. return HidViewExitConfirm;
  80. }
  81. static uint32_t hid_exit(void* context) {
  82. UNUSED(context);
  83. return VIEW_NONE;
  84. }
  85. Hid* hid_alloc(HidTransport transport) {
  86. Hid* app = malloc(sizeof(Hid));
  87. app->transport = transport;
  88. // Gui
  89. app->gui = furi_record_open(RECORD_GUI);
  90. // Bt
  91. app->bt = furi_record_open(RECORD_BT);
  92. // Notifications
  93. app->notifications = furi_record_open(RECORD_NOTIFICATION);
  94. // View dispatcher
  95. app->view_dispatcher = view_dispatcher_alloc();
  96. view_dispatcher_enable_queue(app->view_dispatcher);
  97. view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
  98. // Device Type Submenu view
  99. app->device_type_submenu = submenu_alloc();
  100. submenu_add_item(
  101. app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app);
  102. submenu_add_item(
  103. app->device_type_submenu,
  104. "Keynote Vertical",
  105. HidSubmenuIndexKeynoteVertical,
  106. hid_submenu_callback,
  107. app);
  108. submenu_add_item(
  109. app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app);
  110. submenu_add_item(
  111. app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app);
  112. submenu_add_item(
  113. app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app);
  114. if(app->transport == HidTransportBle) {
  115. submenu_add_item(
  116. app->device_type_submenu,
  117. "TikTok Controller",
  118. HidSubmenuIndexTikTok,
  119. hid_submenu_callback,
  120. app);
  121. }
  122. submenu_add_item(
  123. app->device_type_submenu,
  124. "Mouse Clicker",
  125. HidSubmenuIndexMouseClicker,
  126. hid_submenu_callback,
  127. app);
  128. submenu_add_item(
  129. app->device_type_submenu,
  130. "Mouse Jiggler",
  131. HidSubmenuIndexMouseJiggler,
  132. hid_submenu_callback,
  133. app);
  134. view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit);
  135. view_dispatcher_add_view(
  136. app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu));
  137. app->view_id = HidViewSubmenu;
  138. view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
  139. return app;
  140. }
  141. Hid* hid_app_alloc_view(void* context) {
  142. furi_assert(context);
  143. Hid* app = context;
  144. // Dialog view
  145. app->dialog = dialog_ex_alloc();
  146. dialog_ex_set_result_callback(app->dialog, hid_dialog_callback);
  147. dialog_ex_set_context(app->dialog, app);
  148. dialog_ex_set_left_button_text(app->dialog, "Exit");
  149. dialog_ex_set_right_button_text(app->dialog, "Stay");
  150. dialog_ex_set_center_button_text(app->dialog, "Menu");
  151. dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop);
  152. view_dispatcher_add_view(
  153. app->view_dispatcher, HidViewExitConfirm, dialog_ex_get_view(app->dialog));
  154. // Keynote view
  155. app->hid_keynote = hid_keynote_alloc(app);
  156. view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_exit_confirm_view);
  157. view_dispatcher_add_view(
  158. app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote));
  159. // Keyboard view
  160. app->hid_keyboard = hid_keyboard_alloc(app);
  161. view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view);
  162. view_dispatcher_add_view(
  163. app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard));
  164. // Media view
  165. app->hid_media = hid_media_alloc(app);
  166. view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view);
  167. view_dispatcher_add_view(
  168. app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media));
  169. // TikTok view
  170. app->hid_tiktok = hid_tiktok_alloc(app);
  171. view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_exit_confirm_view);
  172. view_dispatcher_add_view(
  173. app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok));
  174. // Mouse view
  175. app->hid_mouse = hid_mouse_alloc(app);
  176. view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view);
  177. view_dispatcher_add_view(
  178. app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse));
  179. // Mouse clicker view
  180. app->hid_mouse_clicker = hid_mouse_clicker_alloc(app);
  181. view_set_previous_callback(
  182. hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view);
  183. view_dispatcher_add_view(
  184. app->view_dispatcher,
  185. HidViewMouseClicker,
  186. hid_mouse_clicker_get_view(app->hid_mouse_clicker));
  187. // Mouse jiggler view
  188. app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app);
  189. view_set_previous_callback(
  190. hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_exit_confirm_view);
  191. view_dispatcher_add_view(
  192. app->view_dispatcher,
  193. HidViewMouseJiggler,
  194. hid_mouse_jiggler_get_view(app->hid_mouse_jiggler));
  195. return app;
  196. }
  197. void hid_free(Hid* app) {
  198. furi_assert(app);
  199. // Reset notification
  200. if(app->transport == HidTransportBle) {
  201. notification_internal_message(app->notifications, &sequence_reset_blue);
  202. }
  203. // Free views
  204. view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu);
  205. submenu_free(app->device_type_submenu);
  206. view_dispatcher_remove_view(app->view_dispatcher, HidViewExitConfirm);
  207. dialog_ex_free(app->dialog);
  208. view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote);
  209. hid_keynote_free(app->hid_keynote);
  210. view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard);
  211. hid_keyboard_free(app->hid_keyboard);
  212. view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia);
  213. hid_media_free(app->hid_media);
  214. view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse);
  215. hid_mouse_free(app->hid_mouse);
  216. view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker);
  217. hid_mouse_clicker_free(app->hid_mouse_clicker);
  218. view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler);
  219. hid_mouse_jiggler_free(app->hid_mouse_jiggler);
  220. view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
  221. hid_tiktok_free(app->hid_tiktok);
  222. view_dispatcher_free(app->view_dispatcher);
  223. // Close records
  224. furi_record_close(RECORD_GUI);
  225. app->gui = NULL;
  226. furi_record_close(RECORD_NOTIFICATION);
  227. app->notifications = NULL;
  228. furi_record_close(RECORD_BT);
  229. app->bt = NULL;
  230. // Free rest
  231. free(app);
  232. }
  233. void hid_hal_keyboard_press(Hid* instance, uint16_t event) {
  234. furi_assert(instance);
  235. if(instance->transport == HidTransportBle) {
  236. furi_hal_bt_hid_kb_press(event);
  237. } else if(instance->transport == HidTransportUsb) {
  238. furi_hal_hid_kb_press(event);
  239. } else {
  240. furi_crash(NULL);
  241. }
  242. }
  243. void hid_hal_keyboard_release(Hid* instance, uint16_t event) {
  244. furi_assert(instance);
  245. if(instance->transport == HidTransportBle) {
  246. furi_hal_bt_hid_kb_release(event);
  247. } else if(instance->transport == HidTransportUsb) {
  248. furi_hal_hid_kb_release(event);
  249. } else {
  250. furi_crash(NULL);
  251. }
  252. }
  253. void hid_hal_keyboard_release_all(Hid* instance) {
  254. furi_assert(instance);
  255. if(instance->transport == HidTransportBle) {
  256. furi_hal_bt_hid_kb_release_all();
  257. } else if(instance->transport == HidTransportUsb) {
  258. furi_hal_hid_kb_release_all();
  259. } else {
  260. furi_crash(NULL);
  261. }
  262. }
  263. void hid_hal_consumer_key_press(Hid* instance, uint16_t event) {
  264. furi_assert(instance);
  265. if(instance->transport == HidTransportBle) {
  266. furi_hal_bt_hid_consumer_key_press(event);
  267. } else if(instance->transport == HidTransportUsb) {
  268. furi_hal_hid_consumer_key_press(event);
  269. } else {
  270. furi_crash(NULL);
  271. }
  272. }
  273. void hid_hal_consumer_key_release(Hid* instance, uint16_t event) {
  274. furi_assert(instance);
  275. if(instance->transport == HidTransportBle) {
  276. furi_hal_bt_hid_consumer_key_release(event);
  277. } else if(instance->transport == HidTransportUsb) {
  278. furi_hal_hid_consumer_key_release(event);
  279. } else {
  280. furi_crash(NULL);
  281. }
  282. }
  283. void hid_hal_consumer_key_release_all(Hid* instance) {
  284. furi_assert(instance);
  285. if(instance->transport == HidTransportBle) {
  286. furi_hal_bt_hid_consumer_key_release_all();
  287. } else if(instance->transport == HidTransportUsb) {
  288. furi_hal_hid_kb_release_all();
  289. } else {
  290. furi_crash(NULL);
  291. }
  292. }
  293. void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) {
  294. furi_assert(instance);
  295. if(instance->transport == HidTransportBle) {
  296. furi_hal_bt_hid_mouse_move(dx, dy);
  297. } else if(instance->transport == HidTransportUsb) {
  298. furi_hal_hid_mouse_move(dx, dy);
  299. } else {
  300. furi_crash(NULL);
  301. }
  302. }
  303. void hid_hal_mouse_scroll(Hid* instance, int8_t delta) {
  304. furi_assert(instance);
  305. if(instance->transport == HidTransportBle) {
  306. furi_hal_bt_hid_mouse_scroll(delta);
  307. } else if(instance->transport == HidTransportUsb) {
  308. furi_hal_hid_mouse_scroll(delta);
  309. } else {
  310. furi_crash(NULL);
  311. }
  312. }
  313. void hid_hal_mouse_press(Hid* instance, uint16_t event) {
  314. furi_assert(instance);
  315. if(instance->transport == HidTransportBle) {
  316. furi_hal_bt_hid_mouse_press(event);
  317. } else if(instance->transport == HidTransportUsb) {
  318. furi_hal_hid_mouse_press(event);
  319. } else {
  320. furi_crash(NULL);
  321. }
  322. }
  323. void hid_hal_mouse_release(Hid* instance, uint16_t event) {
  324. furi_assert(instance);
  325. if(instance->transport == HidTransportBle) {
  326. furi_hal_bt_hid_mouse_release(event);
  327. } else if(instance->transport == HidTransportUsb) {
  328. furi_hal_hid_mouse_release(event);
  329. } else {
  330. furi_crash(NULL);
  331. }
  332. }
  333. void hid_hal_mouse_release_all(Hid* instance) {
  334. furi_assert(instance);
  335. if(instance->transport == HidTransportBle) {
  336. furi_hal_bt_hid_mouse_release_all();
  337. } else if(instance->transport == HidTransportUsb) {
  338. furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT);
  339. furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT);
  340. } else {
  341. furi_crash(NULL);
  342. }
  343. }
  344. int32_t hid_usb_app(void* p) {
  345. UNUSED(p);
  346. Hid* app = hid_alloc(HidTransportUsb);
  347. app = hid_app_alloc_view(app);
  348. FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
  349. furi_hal_usb_unlock();
  350. furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true);
  351. bt_hid_connection_status_changed_callback(BtStatusConnected, app);
  352. dolphin_deed(DolphinDeedPluginStart);
  353. view_dispatcher_run(app->view_dispatcher);
  354. furi_hal_usb_set_config(usb_mode_prev, NULL);
  355. hid_free(app);
  356. return 0;
  357. }
  358. int32_t hid_ble_app(void* p) {
  359. UNUSED(p);
  360. Hid* app = hid_alloc(HidTransportBle);
  361. app = hid_app_alloc_view(app);
  362. bt_disconnect(app->bt);
  363. // Wait 2nd core to update nvm storage
  364. furi_delay_ms(200);
  365. // Migrate data from old sd-card folder
  366. Storage* storage = furi_record_open(RECORD_STORAGE);
  367. storage_common_migrate(
  368. storage,
  369. EXT_PATH("apps/Tools/" HID_BT_KEYS_STORAGE_NAME),
  370. APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
  371. bt_keys_storage_set_storage_path(app->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
  372. furi_record_close(RECORD_STORAGE);
  373. if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) {
  374. FURI_LOG_E(TAG, "Failed to switch to HID profile");
  375. }
  376. furi_hal_bt_start_advertising();
  377. bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app);
  378. dolphin_deed(DolphinDeedPluginStart);
  379. view_dispatcher_run(app->view_dispatcher);
  380. bt_set_status_changed_callback(app->bt, NULL, NULL);
  381. bt_disconnect(app->bt);
  382. // Wait 2nd core to update nvm storage
  383. furi_delay_ms(200);
  384. bt_keys_storage_set_default_path(app->bt);
  385. if(!bt_set_profile(app->bt, BtProfileSerial)) {
  386. FURI_LOG_E(TAG, "Failed to switch to Serial profile");
  387. }
  388. hid_free(app);
  389. return 0;
  390. }