hid_ptt.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #include "hid_ptt.h"
  2. #include <gui/elements.h>
  3. #include <notification/notification_messages.h>
  4. #include "../hid.h"
  5. #include "../views.h"
  6. #include "hid_icons.h"
  7. #define TAG "HidPtt"
  8. struct HidPtt {
  9. View* view;
  10. Hid* hid;
  11. };
  12. typedef struct {
  13. bool left_pressed;
  14. bool up_pressed;
  15. bool right_pressed;
  16. bool down_pressed;
  17. bool muted;
  18. bool ptt_pressed;
  19. bool connected;
  20. bool is_mac_os;
  21. HidTransport transport;
  22. } HidPttModel;
  23. static void hid_ptt_draw_callback(Canvas* canvas, void* context) {
  24. furi_assert(context);
  25. HidPttModel* model = context;
  26. // Header
  27. canvas_set_font(canvas, FontPrimary);
  28. if(model->transport == HidTransportBle) {
  29. if(model->connected) {
  30. canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
  31. } else {
  32. canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
  33. }
  34. }
  35. canvas_set_font(canvas, FontSecondary);
  36. canvas_draw_icon(canvas, 3, 81, &I_ButtonUp_7x4);
  37. elements_multiline_text_aligned(canvas, 0, 86, AlignLeft, AlignTop, "google meet");
  38. canvas_draw_icon(canvas, 3, 96, &I_ButtonDown_7x4);
  39. // OS selection
  40. elements_slightly_rounded_box(canvas, model->is_mac_os ? 0 : 26, 106, model->is_mac_os ? 21 : 26, 11);
  41. canvas_set_color(canvas, model->is_mac_os ? ColorWhite : ColorBlack);
  42. elements_multiline_text_aligned(canvas, 2, 108, AlignLeft, AlignTop, "Mac");
  43. canvas_set_color(canvas, ColorBlack);
  44. elements_multiline_text_aligned(canvas, 23, 108, AlignLeft, AlignTop, "|");
  45. canvas_set_color(canvas, model->is_mac_os ? ColorBlack : ColorWhite);
  46. elements_multiline_text_aligned(canvas, 28, 108, AlignLeft, AlignTop, "Linux");
  47. canvas_set_color(canvas, ColorBlack);
  48. // Exit label
  49. canvas_draw_icon(canvas, 3, 121, &I_ButtonLeft_4x7);
  50. elements_multiline_text_aligned(canvas, 9, 121, AlignLeft, AlignTop, "Hold to exit");
  51. const uint8_t x_2 = 27;
  52. const uint8_t x_1 = 8;
  53. const uint8_t x_3 = 46;
  54. const uint8_t y_1 = 19;
  55. const uint8_t y_2 = 38;
  56. const uint8_t y_3 = 57;
  57. // Up
  58. canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18);
  59. if(model->up_pressed) {
  60. elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13);
  61. canvas_set_color(canvas, ColorWhite);
  62. }
  63. if(model->ptt_pressed) {
  64. canvas_draw_icon(canvas, x_2 + 6, y_1 + 7, &I_ButtonUp_7x4);
  65. } else {
  66. canvas_draw_icon(canvas, x_2 + 5, y_1 + 5, &I_Volup_8x6);
  67. }
  68. canvas_set_color(canvas, ColorBlack);
  69. // Down
  70. canvas_draw_icon(canvas, x_2, y_3, &I_Button_18x18);
  71. if(model->down_pressed) {
  72. elements_slightly_rounded_box(canvas, x_2 + 3, y_3 + 2, 13, 13);
  73. canvas_set_color(canvas, ColorWhite);
  74. }
  75. if(model->ptt_pressed) {
  76. canvas_draw_icon(canvas, x_2 + 6, y_3 + 7, &I_ButtonDown_7x4);
  77. } else {
  78. canvas_draw_icon(canvas, x_2 + 6, y_3 + 5, &I_Voldwn_6x6);
  79. }
  80. canvas_set_color(canvas, ColorBlack);
  81. // Left
  82. canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18);
  83. if(model->left_pressed) {
  84. elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13);
  85. canvas_set_color(canvas, ColorWhite);
  86. }
  87. if (model->ptt_pressed) {
  88. // canvas_draw_icon(canvas, x_1 + 8, y_2 + 5, &I_ButtonRight_4x7);
  89. canvas_set_font(canvas, FontPrimary);
  90. elements_multiline_text_aligned(canvas, x_1 + 7, y_2 + 4, AlignLeft, AlignTop, "?");
  91. canvas_set_font(canvas, FontSecondary);
  92. } else {
  93. canvas_draw_icon(canvas, x_1 + 7, y_2 + 5, &I_ButtonLeft_4x7);
  94. }
  95. canvas_set_color(canvas, ColorBlack);
  96. // Right / Camera
  97. canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18);
  98. if(model->right_pressed) {
  99. elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13);
  100. canvas_set_color(canvas, ColorWhite);
  101. }
  102. if(!model->ptt_pressed) {
  103. canvas_draw_icon(canvas, x_3 + 11, y_2 + 5, &I_ButtonLeft_4x7);
  104. canvas_draw_box(canvas, x_3 + 4, y_2 + 5, 7, 7);
  105. } else {
  106. elements_multiline_text_aligned(canvas, x_3 + 4, y_2 + 5, AlignLeft, AlignTop, "OS");
  107. }
  108. canvas_set_color(canvas, ColorBlack);
  109. // Ok / Mic
  110. canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18);
  111. canvas_draw_icon(canvas, x_2 + 5, y_2 + 4, &I_Mic_btn_8x10);
  112. if(model->muted && !model->ptt_pressed) {
  113. canvas_draw_line(canvas, x_2 + 3, y_2 + 2, x_2 + 3 + 13, y_2 + 2 + 13);
  114. canvas_draw_line(canvas, x_2 + 2, y_2 + 2, x_2 + 2 + 13, y_2 + 2 + 13);
  115. canvas_draw_line(canvas, x_2 + 3, y_2 + 2 + 13, x_2 + 3 + 13, y_2 + 2);
  116. canvas_draw_line(canvas, x_2 + 2, y_2 + 2 + 13, x_2 + 2 + 13, y_2 + 2);
  117. }
  118. canvas_set_color(canvas, ColorBlack);
  119. // Back / PTT
  120. canvas_draw_icon(canvas, x_2, 0, &I_BtnFrameLeft_3x18);
  121. canvas_draw_icon(canvas, x_2 + 35, 0, &I_BtnFrameRight_2x18);
  122. canvas_draw_line(canvas, x_2 + 3, 0, x_2 + 34, 0);
  123. canvas_draw_line(canvas, x_2 + 3, 16, x_2 + 34, 16);
  124. canvas_draw_line(canvas, x_2 + 3, 17, x_2 + 34, 17);
  125. if(model->ptt_pressed) {
  126. elements_slightly_rounded_box(canvas, x_2+3, 0+2, 32, 13);
  127. canvas_set_color(canvas, ColorWhite);
  128. }
  129. canvas_draw_icon(canvas, x_2+4, 0+4, &I_Pin_back_arrow_rotated_8x10);
  130. elements_multiline_text_aligned(canvas, x_2+16, 0+12, AlignLeft, AlignBottom, "PTT");
  131. }
  132. static void hid_ptt_process(HidPtt* hid_ptt, InputEvent* event) {
  133. with_view_model(
  134. hid_ptt->view,
  135. HidPttModel * model,
  136. {
  137. if(event->type == InputTypePress) {
  138. if(event->key == InputKeyUp) {
  139. model->up_pressed = true;
  140. hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_INCREMENT);
  141. } else if(event->key == InputKeyDown) {
  142. model->down_pressed = true;
  143. hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT);
  144. } else if(event->key == InputKeyLeft) {
  145. model->left_pressed = true;
  146. } else if(event->key == InputKeyRight) {
  147. model->right_pressed = true;
  148. } else if(event->key == InputKeyBack) {
  149. model->ptt_pressed = true;
  150. if (model->muted) {
  151. hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_SPACEBAR);
  152. }
  153. }
  154. } else if(event->type == InputTypeRelease) {
  155. if(event->key == InputKeyUp) {
  156. model->up_pressed = false;
  157. hid_hal_consumer_key_release(hid_ptt->hid, HID_CONSUMER_VOLUME_INCREMENT);
  158. } else if(event->key == InputKeyDown) {
  159. model->down_pressed = false;
  160. hid_hal_consumer_key_release(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT);
  161. } else if(event->key == InputKeyLeft) {
  162. model->left_pressed = false;
  163. } else if(event->key == InputKeyRight) {
  164. model->right_pressed = false;
  165. } else if(event->key == InputKeyBack) {
  166. model->ptt_pressed = false;
  167. if (model->muted) {
  168. hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); // release PTT
  169. } else {
  170. // mute
  171. hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D);
  172. hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D );
  173. model->muted = true;
  174. }
  175. }
  176. } else if(event->type == InputTypeShort) {
  177. if(event->key == InputKeyOk && !model->ptt_pressed ) { // no changes if PTT is pressed
  178. model->muted = !model->muted;
  179. hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D);
  180. hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D);
  181. } else if(event->key == InputKeyRight) {
  182. if (!model->ptt_pressed){
  183. hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E);
  184. hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E);
  185. } else {
  186. model->is_mac_os = !model->is_mac_os;
  187. notification_message(hid_ptt->hid->notifications, &sequence_single_vibro);
  188. }
  189. }
  190. } else if(event->type == InputTypeLong) {
  191. if(event->key == InputKeyLeft) {
  192. model->left_pressed = false;
  193. hid_hal_keyboard_release_all(hid_ptt->hid);
  194. view_dispatcher_switch_to_view(hid_ptt->hid->view_dispatcher, HidViewSubmenu);
  195. // sequence_double_vibro to notify that we quit PTT
  196. notification_message(hid_ptt->hid->notifications, &sequence_double_vibro);
  197. } else if(event->key == InputKeyOk && !model->ptt_pressed ) { // no changes if PTT is pressed
  198. // Change local mic status
  199. model->muted = !model->muted;
  200. notification_message(hid_ptt->hid->notifications, &sequence_single_vibro);
  201. }
  202. }
  203. //LED
  204. if (model->muted && !model->ptt_pressed) {
  205. notification_message(hid_ptt->hid->notifications, &sequence_reset_red);
  206. } else {
  207. notification_message(hid_ptt->hid->notifications, &sequence_set_red_255);
  208. }
  209. },
  210. true);
  211. }
  212. static bool hid_ptt_input_callback(InputEvent* event, void* context) {
  213. furi_assert(context);
  214. HidPtt* hid_ptt = context;
  215. bool consumed = true;
  216. hid_ptt_process(hid_ptt, event);
  217. return consumed;
  218. }
  219. HidPtt* hid_ptt_alloc(Hid* hid) {
  220. HidPtt* hid_ptt = malloc(sizeof(HidPtt));
  221. hid_ptt->view = view_alloc();
  222. hid_ptt->hid = hid;
  223. view_set_context(hid_ptt->view, hid_ptt);
  224. view_allocate_model(hid_ptt->view, ViewModelTypeLocking, sizeof(HidPttModel));
  225. view_set_draw_callback(hid_ptt->view, hid_ptt_draw_callback);
  226. view_set_input_callback(hid_ptt->view, hid_ptt_input_callback);
  227. view_set_orientation(hid_ptt->view, ViewOrientationVerticalFlip);
  228. with_view_model(
  229. hid_ptt->view, HidPttModel * model, {
  230. model->transport = hid->transport;
  231. model->muted = true; // assume we're muted
  232. model->is_mac_os = true;
  233. }, true);
  234. return hid_ptt;
  235. }
  236. void hid_ptt_free(HidPtt* hid_ptt) {
  237. furi_assert(hid_ptt);
  238. notification_message(hid_ptt->hid->notifications, &sequence_reset_red);
  239. view_free(hid_ptt->view);
  240. free(hid_ptt);
  241. }
  242. View* hid_ptt_get_view(HidPtt* hid_ptt) {
  243. furi_assert(hid_ptt);
  244. return hid_ptt->view;
  245. }
  246. void hid_ptt_set_connected_status(HidPtt* hid_ptt, bool connected) {
  247. furi_assert(hid_ptt);
  248. with_view_model(
  249. hid_ptt->view, HidPttModel * model, { model->connected = connected; }, true);
  250. }