hid_tiktok.c 9.2 KB

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