hid_tiktok.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #include "hid_tiktok.h"
  2. #include "../hid.h"
  3. #include <gui/elements.h>
  4. #include "hid_icons.h"
  5. #define TAG "HidTikTok"
  6. struct HidTikTok {
  7. View* view;
  8. Hid* hid;
  9. };
  10. typedef struct {
  11. bool left_pressed;
  12. bool up_pressed;
  13. bool right_pressed;
  14. bool down_pressed;
  15. bool ok_pressed;
  16. bool connected;
  17. bool is_cursor_set;
  18. bool back_mouse_pressed;
  19. HidTransport transport;
  20. } HidTikTokModel;
  21. static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
  22. furi_assert(context);
  23. HidTikTokModel* model = context;
  24. // Header
  25. if(model->transport == HidTransportBle) {
  26. if(model->connected) {
  27. canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
  28. } else {
  29. canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
  30. }
  31. }
  32. canvas_set_font(canvas, FontPrimary);
  33. elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok /");
  34. elements_multiline_text_aligned(canvas, 3, 18, AlignLeft, AlignTop, "YT Shorts");
  35. canvas_set_font(canvas, FontSecondary);
  36. // Keypad circles
  37. canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51);
  38. // Pause
  39. if(model->back_mouse_pressed) {
  40. canvas_set_bitmap_mode(canvas, 1);
  41. canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
  42. canvas_set_bitmap_mode(canvas, 0);
  43. canvas_set_color(canvas, ColorWhite);
  44. }
  45. canvas_draw_icon(canvas, 113, 37, &I_Pause_icon_9x9);
  46. canvas_set_color(canvas, ColorBlack);
  47. // Up
  48. if(model->up_pressed) {
  49. canvas_set_bitmap_mode(canvas, 1);
  50. canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15);
  51. canvas_set_bitmap_mode(canvas, 0);
  52. canvas_set_color(canvas, ColorWhite);
  53. }
  54. canvas_draw_icon(canvas, 80, 8, &I_Arr_up_7x9);
  55. canvas_set_color(canvas, ColorBlack);
  56. // Down
  57. if(model->down_pressed) {
  58. canvas_set_bitmap_mode(canvas, 1);
  59. canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15);
  60. canvas_set_bitmap_mode(canvas, 0);
  61. canvas_set_color(canvas, ColorWhite);
  62. }
  63. canvas_draw_icon(canvas, 80, 40, &I_Arr_dwn_7x9);
  64. canvas_set_color(canvas, ColorBlack);
  65. // Left
  66. if(model->left_pressed) {
  67. canvas_set_bitmap_mode(canvas, 1);
  68. canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31);
  69. canvas_set_bitmap_mode(canvas, 0);
  70. canvas_set_color(canvas, ColorWhite);
  71. }
  72. canvas_draw_icon(canvas, 64, 25, &I_Voldwn_6x6);
  73. canvas_set_color(canvas, ColorBlack);
  74. // Right
  75. if(model->right_pressed) {
  76. canvas_set_bitmap_mode(canvas, 1);
  77. canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31);
  78. canvas_set_bitmap_mode(canvas, 0);
  79. canvas_set_color(canvas, ColorWhite);
  80. }
  81. canvas_draw_icon(canvas, 95, 25, &I_Volup_8x6);
  82. canvas_set_color(canvas, ColorBlack);
  83. // Ok
  84. if(model->ok_pressed) {
  85. canvas_set_bitmap_mode(canvas, 1);
  86. canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
  87. canvas_set_bitmap_mode(canvas, 0);
  88. canvas_set_color(canvas, ColorWhite);
  89. }
  90. canvas_draw_icon(canvas, 78, 25, &I_Like_def_11x9);
  91. canvas_set_color(canvas, ColorBlack);
  92. // Exit
  93. canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
  94. canvas_set_font(canvas, FontSecondary);
  95. elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
  96. }
  97. static void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) {
  98. // Set cursor to the phone's left up corner
  99. // Delays to guarantee one packet per connection interval
  100. for(size_t i = 0; i < 8; i++) {
  101. hid_hal_mouse_move(hid_tiktok->hid, -127, -127);
  102. furi_delay_ms(50);
  103. }
  104. // Move cursor from the corner
  105. hid_hal_mouse_move(hid_tiktok->hid, 20, 120);
  106. furi_delay_ms(50);
  107. }
  108. static void
  109. hid_tiktok_process_press(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) {
  110. if(event->key == InputKeyUp) {
  111. model->up_pressed = true;
  112. } else if(event->key == InputKeyDown) {
  113. model->down_pressed = true;
  114. } else if(event->key == InputKeyLeft) {
  115. model->left_pressed = true;
  116. hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT);
  117. } else if(event->key == InputKeyRight) {
  118. model->right_pressed = true;
  119. hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT);
  120. } else if(event->key == InputKeyOk) {
  121. model->ok_pressed = true;
  122. } else if(event->key == InputKeyBack) {
  123. model->back_mouse_pressed = true;
  124. }
  125. }
  126. static void
  127. hid_tiktok_process_release(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) {
  128. if(event->key == InputKeyUp) {
  129. model->up_pressed = false;
  130. } else if(event->key == InputKeyDown) {
  131. model->down_pressed = false;
  132. } else if(event->key == InputKeyLeft) {
  133. model->left_pressed = false;
  134. hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT);
  135. } else if(event->key == InputKeyRight) {
  136. model->right_pressed = false;
  137. hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT);
  138. } else if(event->key == InputKeyOk) {
  139. model->ok_pressed = false;
  140. } else if(event->key == InputKeyBack) {
  141. model->back_mouse_pressed = false;
  142. }
  143. }
  144. static bool hid_tiktok_input_callback(InputEvent* event, void* context) {
  145. furi_assert(context);
  146. HidTikTok* hid_tiktok = context;
  147. bool consumed = false;
  148. with_view_model(
  149. hid_tiktok->view,
  150. HidTikTokModel * model,
  151. {
  152. if(event->type == InputTypePress) {
  153. hid_tiktok_process_press(hid_tiktok, model, event);
  154. if(model->connected && !model->is_cursor_set) {
  155. hid_tiktok_reset_cursor(hid_tiktok);
  156. model->is_cursor_set = true;
  157. }
  158. consumed = true;
  159. } else if(event->type == InputTypeRelease) {
  160. hid_tiktok_process_release(hid_tiktok, model, event);
  161. consumed = true;
  162. } else if(event->type == InputTypeShort) {
  163. if(event->key == InputKeyOk) {
  164. hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
  165. furi_delay_ms(25);
  166. hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
  167. furi_delay_ms(100);
  168. hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
  169. furi_delay_ms(25);
  170. hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
  171. consumed = true;
  172. } else if(event->key == InputKeyUp) {
  173. // Emulate up swipe
  174. hid_hal_mouse_scroll(hid_tiktok->hid, -6);
  175. hid_hal_mouse_scroll(hid_tiktok->hid, -8);
  176. hid_hal_mouse_scroll(hid_tiktok->hid, -10);
  177. hid_hal_mouse_scroll(hid_tiktok->hid, -8);
  178. hid_hal_mouse_scroll(hid_tiktok->hid, -6);
  179. consumed = true;
  180. } else if(event->key == InputKeyDown) {
  181. // Emulate down swipe
  182. hid_hal_mouse_scroll(hid_tiktok->hid, 6);
  183. hid_hal_mouse_scroll(hid_tiktok->hid, 8);
  184. hid_hal_mouse_scroll(hid_tiktok->hid, 10);
  185. hid_hal_mouse_scroll(hid_tiktok->hid, 8);
  186. hid_hal_mouse_scroll(hid_tiktok->hid, 6);
  187. consumed = true;
  188. } else if(event->key == InputKeyBack) {
  189. // Pause
  190. hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
  191. furi_delay_ms(50);
  192. hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
  193. consumed = true;
  194. }
  195. } else if(event->type == InputTypeLong) {
  196. if(event->key == InputKeyBack) {
  197. hid_hal_consumer_key_release_all(hid_tiktok->hid);
  198. model->is_cursor_set = false;
  199. consumed = false;
  200. }
  201. }
  202. },
  203. true);
  204. return consumed;
  205. }
  206. HidTikTok* hid_tiktok_alloc(Hid* bt_hid) {
  207. HidTikTok* hid_tiktok = malloc(sizeof(HidTikTok));
  208. hid_tiktok->hid = bt_hid;
  209. hid_tiktok->view = view_alloc();
  210. view_set_context(hid_tiktok->view, hid_tiktok);
  211. view_allocate_model(hid_tiktok->view, ViewModelTypeLocking, sizeof(HidTikTokModel));
  212. view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback);
  213. view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback);
  214. with_view_model(
  215. hid_tiktok->view, HidTikTokModel * model, { model->transport = bt_hid->transport; }, true);
  216. return hid_tiktok;
  217. }
  218. void hid_tiktok_free(HidTikTok* hid_tiktok) {
  219. furi_assert(hid_tiktok);
  220. view_free(hid_tiktok->view);
  221. free(hid_tiktok);
  222. }
  223. View* hid_tiktok_get_view(HidTikTok* hid_tiktok) {
  224. furi_assert(hid_tiktok);
  225. return hid_tiktok->view;
  226. }
  227. void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected) {
  228. furi_assert(hid_tiktok);
  229. with_view_model(
  230. hid_tiktok->view,
  231. HidTikTokModel * model,
  232. {
  233. model->connected = connected;
  234. model->is_cursor_set = false;
  235. },
  236. true);
  237. }