From 34610b46e9f16ef0f69941180fd3fa2b1eb695db Mon Sep 17 00:00:00 2001 From: Roman Belyakovsky Date: Tue, 28 Nov 2023 14:05:45 +0200 Subject: [PATCH] PTT: added menu, new apps and improved controls --- .../hid_app/assets/BrokenButton_15x15.png | Bin 0 -> 340 bytes base_pack/hid_app/assets/BtnBackV_9x9.png | Bin 0 -> 362 bytes base_pack/hid_app/assets/BtnLeft_9x9.png | Bin 0 -> 362 bytes base_pack/hid_app/assets/Hand_8x10.png | Bin 0 -> 156 bytes base_pack/hid_app/assets/Help_exit_64x9.png | Bin 0 -> 404 bytes base_pack/hid_app/assets/Help_top_64x17.png | Bin 0 -> 470 bytes base_pack/hid_app/assets/Hold_15x5.png | Bin 0 -> 356 bytes base_pack/hid_app/assets/Mic_7x11.png | Bin 0 -> 356 bytes base_pack/hid_app/assets/Mic_btn_8x10.png | Bin 141 -> 0 bytes .../assets/MicrophoneCrossed_16x16.png | Bin 0 -> 341 bytes .../assets/MicrophonePressedBtn_16x16.png | Bin 0 -> 329 bytes .../MicrophonePressedCrossedBtn_16x16.png | Bin 0 -> 339 bytes .../assets/RoundButtonPressed_16x16.png | Bin 0 -> 320 bytes .../assets/RoundButtonUnpressed_16x16.png | Bin 0 -> 323 bytes base_pack/hid_app/assets/for_help_27x5.png | Bin 0 -> 162 bytes base_pack/hid_app/hid.c | 29 +- base_pack/hid_app/hid.h | 4 +- base_pack/hid_app/hid_usb_10px.png | Bin 174 -> 969 bytes base_pack/hid_app/views.h | 4 +- base_pack/hid_app/views/hid_ptt.c | 842 +++++++++++------- base_pack/hid_app/views/hid_ptt.h | 15 +- base_pack/hid_app/views/hid_ptt_menu.c | 413 +++++++++ base_pack/hid_app/views/hid_ptt_menu.h | 27 + 23 files changed, 1014 insertions(+), 320 deletions(-) create mode 100644 base_pack/hid_app/assets/BrokenButton_15x15.png create mode 100644 base_pack/hid_app/assets/BtnBackV_9x9.png create mode 100644 base_pack/hid_app/assets/BtnLeft_9x9.png create mode 100644 base_pack/hid_app/assets/Hand_8x10.png create mode 100644 base_pack/hid_app/assets/Help_exit_64x9.png create mode 100644 base_pack/hid_app/assets/Help_top_64x17.png create mode 100644 base_pack/hid_app/assets/Hold_15x5.png create mode 100644 base_pack/hid_app/assets/Mic_7x11.png delete mode 100644 base_pack/hid_app/assets/Mic_btn_8x10.png create mode 100644 base_pack/hid_app/assets/MicrophoneCrossed_16x16.png create mode 100644 base_pack/hid_app/assets/MicrophonePressedBtn_16x16.png create mode 100644 base_pack/hid_app/assets/MicrophonePressedCrossedBtn_16x16.png create mode 100644 base_pack/hid_app/assets/RoundButtonPressed_16x16.png create mode 100644 base_pack/hid_app/assets/RoundButtonUnpressed_16x16.png create mode 100644 base_pack/hid_app/assets/for_help_27x5.png create mode 100644 base_pack/hid_app/views/hid_ptt_menu.c create mode 100644 base_pack/hid_app/views/hid_ptt_menu.h diff --git a/base_pack/hid_app/assets/BrokenButton_15x15.png b/base_pack/hid_app/assets/BrokenButton_15x15.png new file mode 100644 index 0000000000000000000000000000000000000000..a627d16ffc617ea2f1098f8dca541e01d85500a0 GIT binary patch literal 340 zcmeAS@N?(olHy`uVBq!ia0vp^{2I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9f$?sa@Dd=8y~NYkmHj!ll$e0Z!)`qz zppb>9i(?4Kbw*u=OaB@E{bTs?kKyk*h8LR{zE?5)wPW~K$MF9j!~1Ux?|v~%pT=N) zj^Y14hW~Ovm6qHRVcFZaDFDq-Epd$~Nl7e8wMs5Z1yT$~21drZhK9OEW+6sKR)(fl z#wOYZ237_JcV?cKMbVI(pOTqYiLAlU+{(b%%Gdy+;Y(=NU!VpJxD6$lxv9k^iMa*H ddO((#Ss9x_^t4s}2?6S1@O1TaS?83{1OTx;W4r(W literal 0 HcmV?d00001 diff --git a/base_pack/hid_app/assets/BtnBackV_9x9.png b/base_pack/hid_app/assets/BtnBackV_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..6aff407a89c47f70758ce705dee33dd2304d8e8b GIT binary patch literal 362 zcmeAS@N?(olHy`uVBq!ia0vp^oFL2yBp6P-vfc}%6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9f$?sa@Dd=8v%n*=n1O+B4+t|(-BaWV z6l5>)^mS!_&MhS_qiX%zdOlD{AUV;m3`jQsu>%m71F`&T6N70$TGrFWF@)oKvc-)9 z8`d{2oYAN#-g8G%;*+F<61y}*!e(|Rm)@oFK!vI$t`Q|Ei6yC4$wjF^iowXh$XM6V zP}j&T#K_pn$lS`rT-(6F%D`Z`>1G8K4Y~O#nQ4`{HC)T!o&(gN0k@$fGdH!kBr&%D bU5|y8G1QW(%@x;ydKf%i{an^LB{Ts596MX< literal 0 HcmV?d00001 diff --git a/base_pack/hid_app/assets/BtnLeft_9x9.png b/base_pack/hid_app/assets/BtnLeft_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..1082423acab6aa2de19fafd57cc2b804a01ef352 GIT binary patch literal 362 zcmeAS@N?(olHy`uVBq!ia0vp^oFL2yBp6P-vfc}%6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9f$?sa@Dd=8v%n*=n1O+B4+t|(-BaWV z6l5>)^mS!_&MhS_tv@B-#SJJVkeujO2BaN;xB-aeftahSJDwFt%X+#vhHzX@wzzR% z!}`XBvl1G8K4Y~O#nQ4`{HC)T!o&(gN0k@$fGdH!kBr&%D Vx1PpYEoYz}22WQ%mvv4FO#p5XTgd#0g7>k44ofy`glX(f`gn7C+hG+!$ zP7LH?P~dRB`&tTBi21sKj)Sb7h~uWU0*U(Vc$SlOj$jaEv z%EV0Dz`)AD;M0t&?@=`5=BH$)Rbpx|Hi2l6n_rp?)Sv;kp(HamwYVfPw*WFVdQ&MBb@0P@UvPyhe` literal 0 HcmV?d00001 diff --git a/base_pack/hid_app/assets/Help_top_64x17.png b/base_pack/hid_app/assets/Help_top_64x17.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc0e66474383bdef50f6f342c00e67885afe24e GIT binary patch literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^4nQo(2qYL>4@_4CQVPi)LB0$ORcZ_j4J`}|zkosw zFBlj~4Hy_+B``2p&0t^21sKj)Sb7gDm8Y2E}B5=c(;D+AIFK->VtAdseGU!)48J3U<-LpZJ{XZ+x2 zz@SY;J9ze*I^^|NsAg zc9l)Lh{TY+Q$*H8NYpON8TaIAAgv(`1Bqf|>=BT7;dOH!?p zi&B9UgOP!ev96(^u8~=Yk&%_LnU#r|wt<0_fx)L4SKp&($jwj5OsmAyU~B@>AUD4> s8K^-6ZbM0CZfbE!Vr~J79%Cy*3oDqOOFLP-fqED`UHx3vIVCg!0H%+cNdN!< literal 0 HcmV?d00001 diff --git a/base_pack/hid_app/assets/Hold_15x5.png b/base_pack/hid_app/assets/Hold_15x5.png new file mode 100644 index 0000000000000000000000000000000000000000..102d0bd7a964aea46997aebf8f0748b4159ac92b GIT binary patch literal 356 zcmeAS@N?(olHy`uVBq!ia0vp^{6Ngg2qYLbnRPb;DTQQ@AYTTCDm4a%h86~fUqGRT z7Yq!g1`G_Z5*Qe)W-u^_7tGleXakgBO7eDhVPL%5CAVt>_A*8EWY>vkQVoJaSY+Op4_oL zvE$xBgBQ;SOya|=-P d7+V>eTbUR_^e8JI*a_6b;OXk;vd$@?2>^sUS`7dI literal 0 HcmV?d00001 diff --git a/base_pack/hid_app/assets/Mic_7x11.png b/base_pack/hid_app/assets/Mic_7x11.png new file mode 100644 index 0000000000000000000000000000000000000000..49223394b4a6c1024244b8a1033a3ab9270263ad GIT binary patch literal 356 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)T2qYM0zh1=(q!f}pf_xbms?-=58d?|_egTCV zUNA6}8Za=tN?>5Hn!&&zUNC1@pbb!hDaqU2g@N&Im+%rGkF&rdvY3H^Zx0AFPTf=F z2^3^6@$_|Nf6gr>rllP+_w6m9kU(;xUm1{g0OAH9HU(lPe#HlqfV8-$i(?4K_2i1< z1w1^l4F>Ir;uBa!7#RZhv1dxXP2~Y9Q7v(eC`m~yNwrEYN(E93Mg~U4x`u|jMrI*K zMpnjVRwicJ1_o9J2A^hJeUG9cH$NpatrAm%u?a+j-2BpHpau=N4JDbmsl_FUxdmu? bOpL5dj3IiKSzNmW)WhKE>gTe~DWM4fZ^Bs2 literal 0 HcmV?d00001 diff --git a/base_pack/hid_app/assets/Mic_btn_8x10.png b/base_pack/hid_app/assets/Mic_btn_8x10.png deleted file mode 100644 index a08bb35dd8bc620aac8b5f5ec9a2638bfe3e0fcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^96-#)!3HEdkIOdzDaPU;cPEB*=VV?2Iqse=jv*Ss zy%Pet7z{X^KL2YkyOQR@vUYs||JFv8x4aEEx>5udxF2jPdth}*js3dK7yb`#;*4ag oK6AOBoENWuQhQm)g>C&-mySEV|FAk~InYQ3Pgg&ebxsLQ01&z{WB>pF diff --git a/base_pack/hid_app/assets/MicrophoneCrossed_16x16.png b/base_pack/hid_app/assets/MicrophoneCrossed_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..0e91d2f86d5e0db4dc025cfc3f00311c432a9448 GIT binary patch literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx?BpA#)4xIr~3dtTpz6=aiY77hwEes65fI`k0jPyGmLm`c;l(zjeE&6QcoM~owo4& zq=z4p+t}am*gs)xZs))9T)9UGXozZwYeY#(Vo9o1a#1RfVlXl=GS)RT)HN~-F*33; zGPE+b&^9ozGBD^iNJiC=o1c=IR*74~uiV?~fHr8rZ79jiO)V}-%q>9IV`ODu01G@N literal 0 HcmV?d00001 diff --git a/base_pack/hid_app/assets/MicrophonePressedBtn_16x16.png b/base_pack/hid_app/assets/MicrophonePressedBtn_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..911fe1b22fe06fa837d51fbda320bd13a25fc035 GIT binary patch literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx?BpA#)4xIr~3dtTpz6=aiY77hwEes65fIzX;D#n%wH3I|91{WzQ$0v+w6E?|zVko!Z z*S&Gcej8AuYKdz^NlIc#s#S7PDv)9@GB7gMH8j*UG7B*>vNE)^GB(gQFt9Q(FnYz^ zfTAHcKP5A*61N8D?VCYv*MQqll9`)YT#}eufTG9P%D~df#0+9d;`$Y>Ks^keu6{1- HoD!MT1I*f zP{`cV#W95Adh!p&^B)@KK6Eg4e(c$HtedBYTOxv4B0|r=z%L;oC?Ui4#D$9|E>ui- z$YAVolfRN7<161?+%p{13v zfwqBxm4SiLEA9pq4Y~O#nQ4`{H8^kI4Dy8r+=i0O+|=Td#M}ZDJ;qjs25?JE6%*C~ P^)Pt4`njxgN@xNAngU$S literal 0 HcmV?d00001 diff --git a/base_pack/hid_app/assets/RoundButtonPressed_16x16.png b/base_pack/hid_app/assets/RoundButtonPressed_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c4892e6420289e0361dd4978da2dc3c4f836e5b8 GIT binary patch literal 320 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx?BpA#)4xIr~3dtTpz6=aiY77hwEes65fIJRdP`(kYX@0Ff!IPG}JXR3o$aZGBC0-GSoIOure^XGua~;MMG|WN@iLm zvIawQD+6OIVoHu+Bs+WW;SRlfxr z0kx=>xJHzuB$lLFB^RXvDF!10BV%1dLtP`Y5F-OCQ%fro3vB}fD+7buu3hFR8glbf zGSe!NH5i&(85mm`8$mQApQ^qB)Sv;kp(HamwYVfPw*Xm>k&%_5rIoP(M32a#Nsobg O7(8A5T-G@yGywo*m|J21 literal 0 HcmV?d00001 diff --git a/base_pack/hid_app/assets/for_help_27x5.png b/base_pack/hid_app/assets/for_help_27x5.png new file mode 100644 index 0000000000000000000000000000000000000000..20bb30a08682194abaeb6a6021357e50c805a035 GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^(m>40!3HF^a7^C^q!^2X+?^QKos)S99WdZ%`YnH}b*bKw%(wov3pjc@7#Jp5EWGsk_J-j3N~_Q6iC4>A*8LpSTl~#^ z+JRrYL%v#l$~%@lt19=;jsCgPGefM>g5I^SHM^%H{nqGdUnPUhmzuAf%ie%2W$<+M Kb6Mw<&;$T{m_9)O literal 0 HcmV?d00001 diff --git a/base_pack/hid_app/hid.c b/base_pack/hid_app/hid.c index b0bbceecdad..bf7c3991429 100644 --- a/base_pack/hid_app/hid.c +++ b/base_pack/hid_app/hid.c @@ -16,7 +16,7 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexMouse, HidSubmenuIndexMouseClicker, HidSubmenuIndexMouseJiggler, - HidSubmenuIndexPtt, + HidSubmenuIndexPushToTalk, }; static void hid_submenu_callback(void* context, uint32_t index) { @@ -54,9 +54,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexMouseJiggler) { app->view_id = HidViewMouseJiggler; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); - } else if(index == HidSubmenuIndexPtt) { - app->view_id = HidViewPtt; - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewPtt); + } else if(index == HidSubmenuIndexPushToTalk) { + app->view_id = HidViewPushToTalkMenu; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewPushToTalkMenu); } } @@ -93,6 +93,11 @@ static uint32_t hid_exit(void* context) { return VIEW_NONE; } +static uint32_t hid_ptt_menu_view(void* context) { + UNUSED(context); + return HidViewPushToTalkMenu; +} + Hid* hid_alloc(HidTransport transport) { Hid* app = malloc(sizeof(Hid)); app->transport = transport; @@ -151,7 +156,7 @@ Hid* hid_alloc(HidTransport transport) { hid_submenu_callback, app); submenu_add_item( - app->device_type_submenu, "PTT", HidSubmenuIndexPtt, hid_submenu_callback, app); + app->device_type_submenu, "PushToTalk", HidSubmenuIndexPushToTalk, hid_submenu_callback, app); view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); view_dispatcher_add_view( app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); @@ -224,11 +229,15 @@ Hid* hid_app_alloc_view(void* context) { HidViewMouseJiggler, hid_mouse_jiggler_get_view(app->hid_mouse_jiggler)); - // Ptt view + // PushToTalk view + app->hid_ptt_menu = hid_ptt_menu_alloc(app); + view_set_previous_callback(hid_ptt_menu_get_view(app->hid_ptt_menu), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewPushToTalkMenu, hid_ptt_menu_get_view(app->hid_ptt_menu)); app->hid_ptt = hid_ptt_alloc(app); - view_set_previous_callback(hid_ptt_get_view(app->hid_ptt), hid_menu_view); + view_set_previous_callback(hid_ptt_get_view(app->hid_ptt), hid_ptt_menu_view); view_dispatcher_add_view( - app->view_dispatcher, HidViewPtt, hid_ptt_get_view(app->hid_ptt)); + app->view_dispatcher, HidViewPushToTalk, hid_ptt_get_view(app->hid_ptt)); return app; } @@ -260,7 +269,9 @@ void hid_free(Hid* app) { hid_mouse_clicker_free(app->hid_mouse_clicker); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); hid_mouse_jiggler_free(app->hid_mouse_jiggler); - view_dispatcher_remove_view(app->view_dispatcher, HidViewPtt); + view_dispatcher_remove_view(app->view_dispatcher, HidViewPushToTalkMenu); + hid_ptt_menu_free(app->hid_ptt_menu); + view_dispatcher_remove_view(app->view_dispatcher, HidViewPushToTalk); hid_ptt_free(app->hid_ptt); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikShorts); hid_tikshorts_free(app->hid_tikshorts); diff --git a/base_pack/hid_app/hid.h b/base_pack/hid_app/hid.h index 3570c38c8fa..ccbbb02d726 100644 --- a/base_pack/hid_app/hid.h +++ b/base_pack/hid_app/hid.h @@ -26,6 +26,7 @@ #include "views/hid_mouse_jiggler.h" #include "views/hid_tikshorts.h" #include "views/hid_ptt.h" +#include "views/hid_ptt_menu.h" #define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" @@ -52,7 +53,8 @@ struct Hid { HidMouseClicker* hid_mouse_clicker; HidMouseJiggler* hid_mouse_jiggler; HidTikShorts* hid_tikshorts; - HidPtt* hid_ptt; + HidPushToTalk* hid_ptt; + HidPushToTalkMenu* hid_ptt_menu; HidTransport transport; uint32_t view_id; diff --git a/base_pack/hid_app/hid_usb_10px.png b/base_pack/hid_app/hid_usb_10px.png index 7649138eb70ee33ccc98aba2252999969c7569a4..415de7d2304fe982c025b2b9a942abbf0a2b6dd0 100644 GIT binary patch literal 969 zcmaJ=J#W)M7(OY0prTdS3e(9IQYsOjefe;0)l|h!Xha;MG(g5)>`P;{_8I$1oJb(V z#>Co{9{@i91|(QuX5%*?v9pwOnxqT_55D(az0dQ0J@>lZy1%+|YXtzX+Ss!@;>_%o zt2y!i@N?&lIBxPQduOaNrjU+e?;YX%)UR2L%LyN@}>atRF6-9xXE~}dAVr@YBcOX_U zM#>gat3`~BQpG5%aP~Eh*gj89{x|#<%&i_M$ zU=f}04!x-NpTtRb98uJv2|I~hvAe-WmMSu=m=ez7E@Q{@LAHmCvt-C3h|9793l4Gp zF!O9qA&z4-!i1C1r48GZ1c~hXo`JDuS-aJ0wOrd$)taqWN;ON>tZH4WV;fs@tj*k$ zfQEdI^)9g5QfwxOAQG8v8vD3;Z_1q-{ zl$i_hipxU&G!&YTg}8q|eDGX6j4SPCw{~`RCd@~lzrPU2?S{SEO@H(cJnv<$o(G-l ph0_~rZ>7^_`EovpzT_W+OY7j;8rXcd{ #include +#include #include "../hid.h" #include "../views.h" #include "hid_icons.h" -#define TAG "HidPtt" +#define TAG "HidPushToTalk" -struct HidPtt { +struct HidPushToTalk { View* view; Hid* hid; + Widget* help; }; +typedef void (*PushToTalkActionCallback)(HidPushToTalk* hid_ptt); + typedef struct { bool left_pressed; bool up_pressed; @@ -21,24 +26,377 @@ typedef struct { bool muted; bool ptt_pressed; bool mic_pressed; - bool mic_sync_pressed; bool connected; - bool is_mac_os; - uint32_t appIndex; + FuriString *os; + FuriString *app; + size_t osIndex; + size_t appIndex; + size_t window_position; HidTransport transport; -} HidPttModel; - -enum HidPttAppIndex { - HidPttAppIndexGoogleMeet, - HidPttAppIndexZoom, - HidPttAppIndexFaceTime, - HidPttAppIndexSkype, - HidPttAppIndexSize, + PushToTalkActionCallback callback_trigger_mute; + PushToTalkActionCallback callback_trigger_camera; + PushToTalkActionCallback callback_trigger_hand; + PushToTalkActionCallback callback_start_ptt; + PushToTalkActionCallback callback_stop_ptt; +} HidPushToTalkModel; + +enum HidPushToTalkAppIndex { + HidPushToTalkAppIndexGoogleMeet, + HidPushToTalkAppIndexZoom, + HidPushToTalkAppIndexFaceTime, + HidPushToTalkAppIndexSkype, + HidPushToTalkAppIndexDiscord, + HidPushToTalkAppIndexTeamSpeak, + HidPushToTalkAppIndexTeams, + HidPushToTalkAppIndexJamulus, + HidPushToTalkAppIndexSize, }; +// meet, zoom +static void hid_ptt_start_ptt_meet_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_stop_ptt_meet_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_trigger_mute_macos_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); +} +static void hid_ptt_trigger_mute_linux_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); +} +static void hid_ptt_trigger_camera_macos_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); +} +static void hid_ptt_trigger_camera_linux_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E ); +} +static void hid_ptt_trigger_hand_macos_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL |HID_KEYBOARD_H); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL |HID_KEYBOARD_H); +} +static void hid_ptt_trigger_hand_linux_meet(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT |HID_KEYBOARD_H); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT |HID_KEYBOARD_H); +} +static void hid_ptt_trigger_mute_macos_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); +} +static void hid_ptt_trigger_mute_linux_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_A); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_A); +} +static void hid_ptt_trigger_camera_macos_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); +} +static void hid_ptt_trigger_camera_linux_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); +} +static void hid_ptt_trigger_hand_macos_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| HID_KEYBOARD_Y); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| HID_KEYBOARD_Y); +} +static void hid_ptt_trigger_hand_linux_zoom(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y); +} + +// this one is widely used across different apps +static void hid_ptt_trigger_cmd_shift_m(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); +} + +// skype +static void hid_ptt_start_ptt_linux_skype(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M ); +} +static void hid_ptt_stop_ptt_linux_skype(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); +} +static void hid_ptt_trigger_mute_linux_skype(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); +} +static void hid_ptt_trigger_camera_macos_skype(HidPushToTalk* hid_ptt) { // and hand in teams + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); +} +static void hid_ptt_trigger_camera_linux_skype(HidPushToTalk* hid_ptt) { // and hand in teams + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); +} + +// discord +static void hid_ptt_trigger_mute_macos_discord(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_start_ptt_macos_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_stop_ptt_macos_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_trigger_mute_linux_discord(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_start_ptt_linux_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_stop_ptt_linux_discord(HidPushToTalk* hid_ptt) { // and TeamSpeak + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_P); +} + +// teamspeak +static void hid_ptt_trigger_mute_macos_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_start_ptt_macos_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_stop_ptt_macos_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_trigger_mute_linux_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_start_ptt_linux_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); +} +static void hid_ptt_stop_ptt_linux_teamspeak(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_CTRL | KEY_MOD_RIGHT_ALT | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_P); +} + +// teams +static void hid_ptt_start_ptt_macos_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI|HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_start_ptt_linux_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL|HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_stop_ptt_macos_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI|HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_stop_ptt_linux_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL|HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_trigger_mute_linux_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_trigger_camera_macos_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); +} +static void hid_ptt_trigger_camera_linux_teams(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_O); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT |HID_KEYBOARD_O); +} + +// Jamulus +static void hid_ptt_trigger_mute_jamulus(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_M); +} +static void hid_ptt_start_ptt_jamulus(HidPushToTalk* hid_ptt) { + hid_ptt_trigger_mute_jamulus(hid_ptt); +} +static void hid_ptt_stop_ptt_jamulus(HidPushToTalk* hid_ptt) { + hid_ptt_trigger_mute_jamulus(hid_ptt); +} + +static void hid_ptt_menu_callback(void* context, uint32_t osIndex, FuriString* osLabel, uint32_t appIndex, FuriString* appLabel) { + furi_assert(context); + HidPushToTalk* hid_ptt = context; + with_view_model( + hid_ptt->view, HidPushToTalkModel * model, { + furi_string_set(model->os, osLabel); + furi_string_set(model->app, appLabel); + model->osIndex = osIndex; + model->appIndex = appIndex; + model->callback_trigger_mute = NULL; + model->callback_trigger_camera = NULL; + model->callback_trigger_hand = NULL; + model->callback_start_ptt = NULL; + model->callback_stop_ptt = NULL; + FURI_LOG_E(TAG, "appIndex: %lu", appIndex); + if(osIndex == HidPushToTalkMacOS) { + switch(appIndex) { + case HidPushToTalkAppIndexGoogleMeet: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_meet; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_meet; + model->callback_trigger_hand = hid_ptt_trigger_hand_macos_meet; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + break; + case HidPushToTalkAppIndexZoom: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_zoom; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_zoom; + model->callback_trigger_hand = hid_ptt_trigger_hand_macos_zoom; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + break; + case HidPushToTalkAppIndexFaceTime: + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; + break; + case HidPushToTalkAppIndexSkype: + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_skype; + model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; + break; + case HidPushToTalkAppIndexDiscord: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_discord; + model->callback_start_ptt = hid_ptt_start_ptt_macos_discord; + model->callback_stop_ptt = hid_ptt_stop_ptt_macos_discord; + break; + case HidPushToTalkAppIndexTeamSpeak: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_teamspeak; + model->callback_start_ptt = hid_ptt_start_ptt_macos_teamspeak; + model->callback_stop_ptt = hid_ptt_stop_ptt_macos_teamspeak; + break; + case HidPushToTalkAppIndexTeams: + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_teams; + model->callback_trigger_hand = hid_ptt_trigger_camera_macos_skype; + model->callback_start_ptt = hid_ptt_start_ptt_macos_teams; + model->callback_stop_ptt = hid_ptt_stop_ptt_macos_teams; + break; + case HidPushToTalkAppIndexJamulus: + model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; + model->callback_start_ptt = hid_ptt_start_ptt_jamulus; + model->callback_stop_ptt = hid_ptt_stop_ptt_jamulus; + break; + } + } else if (osIndex == HidPushToTalkLinux) { + switch(appIndex) { + case HidPushToTalkAppIndexGoogleMeet: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_meet; + model->callback_trigger_camera = hid_ptt_trigger_camera_linux_meet; + model->callback_trigger_hand = hid_ptt_trigger_hand_linux_meet; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + break; + case HidPushToTalkAppIndexZoom: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_zoom; + model->callback_trigger_camera = hid_ptt_trigger_camera_linux_zoom; + model->callback_trigger_hand = hid_ptt_trigger_hand_linux_zoom; + model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; + model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; + break; + case HidPushToTalkAppIndexSkype: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_skype; + model->callback_trigger_camera = hid_ptt_trigger_camera_linux_skype; + model->callback_start_ptt = hid_ptt_start_ptt_linux_skype; + model->callback_stop_ptt = hid_ptt_stop_ptt_linux_skype; + break; + case HidPushToTalkAppIndexDiscord: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_discord; + model->callback_start_ptt = hid_ptt_start_ptt_linux_discord; + model->callback_stop_ptt = hid_ptt_stop_ptt_linux_discord; + break; + case HidPushToTalkAppIndexTeamSpeak: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_teamspeak; + model->callback_start_ptt = hid_ptt_start_ptt_linux_teamspeak; + model->callback_stop_ptt = hid_ptt_stop_ptt_linux_teamspeak; + break; + case HidPushToTalkAppIndexTeams: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_teams; + model->callback_trigger_camera = hid_ptt_trigger_camera_linux_teams; + model->callback_trigger_hand = hid_ptt_trigger_camera_linux_skype; + model->callback_start_ptt = hid_ptt_start_ptt_linux_teams; + model->callback_stop_ptt = hid_ptt_stop_ptt_linux_teams; + break; + case HidPushToTalkAppIndexJamulus: + model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; + model->callback_start_ptt = hid_ptt_start_ptt_jamulus; + model->callback_stop_ptt = hid_ptt_stop_ptt_jamulus; + break; + } + } + + char *app_specific_help = ""; + switch(appIndex) { + case HidPushToTalkAppIndexGoogleMeet: + app_specific_help = + "Google Meet:\n" + "This feature is off by default in your audio settings " + "and may not work for Windows users who use their screen " + "reader. In this situation, the spacebar performs a different action.\n\n" + ; + break; + case HidPushToTalkAppIndexDiscord: + app_specific_help = + "Discord:\n" + "1. Under App Settings, click Voice & Video. Under Input Mode, " + "check the box next to Push to Talk.\n" + "2. Scroll down to SHORTCUT, click Record Keybinder.\n" + "3. Press PTT in the app to bind it." + "4. Go to Keybinds and assign mute button.\n\n" + ; + break; + case HidPushToTalkAppIndexTeamSpeak: + app_specific_help = + "TeamSpeak:\n" + "To make keys working bind them in discord settings.\n\n" + ; + break; + case HidPushToTalkAppIndexTeams: + app_specific_help = + "Teams:\n" + "Go to Settings > Privacy. Make sure Keyboard shortcut to unmute is toggled on.\n\n" + ; + break; + } + + FuriString *msg = furi_string_alloc(); + furi_string_cat_printf(msg, + "%sGeneral:\n" + "To operate properly flipper microphone " + "status must be in sync with your computer.\n" + "Hold > to change mic status.\n" + "Hold < to open this help.\n" + "Press BACK to switch mic on/off.\n" + "Hold 'o' for PTT mode (mic will be off once you release 'o')\n" + "Hold BACK to exit.", app_specific_help); + widget_add_text_scroll_element(hid_ptt->help, 0, 0, 128, 64, furi_string_get_cstr(msg)); + furi_string_free(msg); + }, true); + view_dispatcher_switch_to_view(hid_ptt->hid->view_dispatcher, HidViewPushToTalk); +} + +static void hid_ptt_draw_camera(Canvas* canvas, uint8_t x, uint8_t y) { + canvas_draw_icon(canvas, x + 7, y, &I_ButtonLeft_4x7); + canvas_draw_box(canvas, x, y, 7, 7); +} + +static void hid_ptt_draw_text_centered(Canvas* canvas, uint8_t y, FuriString* str) { + FuriString* disp_str; + disp_str = furi_string_alloc_set(str); + elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); + uint8_t x_pos = (canvas_width(canvas) - canvas_string_width(canvas,furi_string_get_cstr(disp_str))) / 2; + canvas_draw_str(canvas,x_pos,y,furi_string_get_cstr(disp_str)); + furi_string_free(disp_str); +} + static void hid_ptt_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - HidPttModel* model = context; + HidPushToTalkModel* model = context; // Header canvas_set_font(canvas, FontPrimary); @@ -50,127 +408,96 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { } } - // App selection - const uint8_t y_app = 78; + // OS and App labels canvas_set_font(canvas, FontSecondary); - canvas_draw_icon(canvas, 0, y_app, &I_ButtonLeft_4x7); - if(model->appIndex == HidPttAppIndexGoogleMeet) { - elements_multiline_text_aligned(canvas, 7, y_app, AlignLeft, AlignTop, "Google Meet"); - } else if(model->appIndex == HidPttAppIndexZoom) { - elements_multiline_text_aligned(canvas, 7, y_app, AlignLeft, AlignTop, "Zoom"); - } else if(model->appIndex == HidPttAppIndexFaceTime) { - elements_multiline_text_aligned(canvas, 7, y_app, AlignLeft, AlignTop, "FaceTime"); - } else if(model->appIndex == HidPttAppIndexSkype) { - elements_multiline_text_aligned(canvas, 7, y_app, AlignLeft, AlignTop, "Skype"); - } - canvas_draw_icon(canvas, 60, y_app, &I_ButtonRight_4x7); - - // OS selection - const uint8_t y_os = 88; - const uint8_t x_os = 7; - // elements_slightly_rounded_box(canvas, model->is_mac_os ? 0 : 26, y_os, model->is_mac_os ? 21 : 26, 11); - elements_slightly_rounded_box(canvas, model->is_mac_os ? x_os : x_os + 26, y_os, model->is_mac_os ? 21 : 26, 11); - canvas_set_color(canvas, model->is_mac_os ? ColorWhite : ColorBlack); - elements_multiline_text_aligned(canvas, x_os + 2, y_os + 1, AlignLeft, AlignTop, "Mac"); - canvas_set_color(canvas, ColorBlack); - if (model->appIndex != HidPttAppIndexFaceTime) { - elements_multiline_text_aligned(canvas, x_os + 23, y_os + 2, AlignLeft, AlignTop, "|"); - canvas_set_color(canvas, model->is_mac_os ? ColorBlack : ColorWhite); - elements_multiline_text_aligned(canvas, x_os + 28, y_os + 2, AlignLeft, AlignTop, "Linux"); - canvas_set_color(canvas, ColorBlack); - } - - // Mic label - const uint8_t y_mic = 102; - canvas_draw_icon(canvas, 0, y_mic - 1, &I_Pin_back_arrow_rotated_8x10); - canvas_draw_icon(canvas, 18, y_mic - 1, &I_Ok_btn_9x9); - elements_multiline_text_aligned(canvas, 11, y_mic, AlignLeft, AlignTop, "+ to sync"); - elements_multiline_text_aligned(canvas, 20, y_mic+10, AlignLeft, AlignTop, "mic status"); - - // Exit label - canvas_draw_icon(canvas, 20, 121, &I_ButtonLeft_4x7); - elements_multiline_text_aligned(canvas, 0, 121, AlignLeft, AlignTop, "Hold to exit"); + hid_ptt_draw_text_centered(canvas, 73, model->app); + hid_ptt_draw_text_centered(canvas, 84, model->os); + + // Help label + canvas_draw_icon(canvas, 0, 88, &I_Help_top_64x17); + canvas_draw_line(canvas, 4, 105, 4, 114); + canvas_draw_line(canvas, 63, 105, 63, 114); + canvas_draw_icon(canvas, 7, 107, &I_Hold_15x5); + canvas_draw_icon(canvas, 24, 105, &I_BtnLeft_9x9); + canvas_draw_icon(canvas, 34, 108, &I_for_help_27x5); + canvas_draw_icon(canvas, 0, 115, &I_Help_exit_64x9); + canvas_draw_icon(canvas, 24, 115, &I_BtnBackV_9x9); + const uint8_t x_1 = 0; const uint8_t x_2 = x_1 + 19 + 4; const uint8_t x_3 = x_1 + 19 * 2 + 8; - const uint8_t y_1 = 19; + const uint8_t y_1 = 3; const uint8_t y_2 = y_1 + 19; const uint8_t y_3 = y_2 + 19; - if(!model->ptt_pressed || model->mic_pressed || model->up_pressed || model->down_pressed || model->left_pressed || model->right_pressed || model->mic_sync_pressed) { - // Up - canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); - if(model->up_pressed) { - elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - if(model->mic_pressed) { - if (model->appIndex != HidPttAppIndexFaceTime) { - elements_multiline_text_aligned(canvas, x_2 + 4, y_1 + 5, AlignLeft, AlignTop, "OS"); - } - } else { - canvas_draw_icon(canvas, x_2 + 5, y_1 + 5, &I_Volup_8x6); - } - canvas_set_color(canvas, ColorBlack); - - // Down - canvas_draw_icon(canvas, x_2, y_3, &I_Button_18x18); - if(model->down_pressed) { - elements_slightly_rounded_box(canvas, x_2 + 3, y_3 + 2, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - if(!model->mic_pressed) { - canvas_draw_icon(canvas, x_2 + 6, y_3 + 5, &I_Voldwn_6x6); - } - canvas_set_color(canvas, ColorBlack); + + // Up + canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, x_2 + 5, y_1 + 5, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); - // Left - canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); - if(model->left_pressed) { - elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - if (model->mic_pressed) { - canvas_draw_icon(canvas, x_1 + 7, y_2 + 5, &I_ButtonLeft_4x7); - } else { - canvas_draw_icon(canvas, x_1 + 4, y_2 + 5, &I_Pin_back_arrow_10x8); - } - canvas_set_color(canvas, ColorBlack); + // Down + canvas_draw_icon(canvas, x_2, y_3, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_3 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, x_2 + 6, y_3 + 5, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); - // Right / Camera - canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); - if(model->right_pressed) { - elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - if(!model->mic_pressed) { - if (model->appIndex != HidPttAppIndexFaceTime) { - canvas_draw_icon(canvas, x_3 + 11, y_2 + 5, &I_ButtonLeft_4x7); - canvas_draw_box(canvas, x_3 + 4, y_2 + 5, 7, 7); - } - } else { - canvas_draw_icon(canvas, x_3 + 8, y_2 + 5, &I_ButtonRight_4x7); - } - canvas_set_color(canvas, ColorBlack); + // Left / Help + canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + if (model->callback_trigger_hand) { + canvas_draw_icon(canvas, x_1 + 4, y_2 + 3, &I_Hand_8x10); + } else { + canvas_draw_icon(canvas, x_1 + 2, y_2 + 1, &I_BrokenButton_15x15); + } + canvas_set_color(canvas, ColorBlack); + // Right / Camera + canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); } + if (model->callback_trigger_camera) { + hid_ptt_draw_camera(canvas, x_3 + 4, y_2 + 5); + } else { + canvas_draw_icon(canvas, x_3 + 2, y_2 + 1, &I_BrokenButton_15x15); + } + canvas_set_color(canvas, ColorBlack); + + // Back / Mic const uint8_t x_mic = x_3; - canvas_draw_icon(canvas, x_mic, 0, &I_Button_18x18); - if(model->mic_pressed) { - elements_slightly_rounded_box(canvas, x_mic + 3, 2, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, x_mic + 5, 4, &I_Mic_btn_8x10); + canvas_draw_icon(canvas, x_mic, 0, &I_RoundButtonUnpressed_16x16); - if (!(!model->muted || (model->ptt_pressed && !model->mic_sync_pressed))) { - canvas_draw_line(canvas, x_mic + 3, 2 , x_mic + 3 + 13, 2 + 13); - canvas_draw_line(canvas, x_mic + 2, 2 , x_mic + 2 + 13, 2 + 13); - canvas_draw_line(canvas, x_mic + 3, 2 + 13, x_mic + 3 + 13, 2); - canvas_draw_line(canvas, x_mic + 2, 2 + 13, x_mic + 2 + 13, 2); + if (!(!model->muted || (model->ptt_pressed))) { + // show muted + if(model->mic_pressed) { + // canvas_draw_icon(canvas, x_mic + 1, 0, &I_MicrophonePressedCrossed_15x15); + canvas_draw_icon(canvas, x_mic, 0, &I_MicrophonePressedCrossedBtn_16x16); + } else { + canvas_draw_icon(canvas, x_mic, 0, &I_MicrophoneCrossed_16x16); + } + } else { + // show unmuted + if(model->mic_pressed) { + // canvas_draw_icon(canvas, x_mic + 1, 0, &I_MicrophonePressed_15x15); + canvas_draw_icon(canvas, x_mic, 0, &I_MicrophonePressedBtn_16x16); + } else { + canvas_draw_icon(canvas, x_mic + 5, 2, &I_Mic_7x11); + } } - canvas_set_color(canvas, ColorBlack); // Ok / PTT const uint8_t x_ptt_margin = 4; @@ -182,175 +509,37 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { canvas_draw_line(canvas, x_ptt + 3 , y_2 + 16, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2 + 16); canvas_draw_line(canvas, x_ptt + 3 , y_2 + 17, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2 + 17); - if (!model->ptt_pressed && !model->muted && model->mic_pressed) { - elements_slightly_rounded_box(canvas, x_ptt + 3, y_2 + 2, (x_ptt_width + x_ptt_margin) / 2, 13); - canvas_set_color(canvas, ColorWhite); - } - if (model->mic_pressed) { - canvas_draw_icon(canvas, x_ptt + 4, y_2 + 4, &I_Mic_btn_8x10); - } - canvas_set_color(canvas, ColorBlack); - if(!model->ptt_pressed && model->muted && model->mic_pressed) { - elements_slightly_rounded_box(canvas, x_ptt + 3 + (x_ptt_width + x_ptt_margin) / 2, y_2 + 2, (x_ptt_width + x_ptt_margin) / 2, 13); + + if (model->ptt_pressed) { + elements_slightly_rounded_box(canvas, x_ptt + 3, y_2 + 2, x_ptt_width + x_ptt_margin, 13); canvas_set_color(canvas, ColorWhite); } - if (model->mic_pressed) { - canvas_draw_icon(canvas, x_ptt + 14, y_2 + 4, &I_Mic_btn_8x10); - canvas_draw_line(canvas, x_ptt + 13, y_2 + 3 , x_ptt + 22, y_2 + 14); - canvas_draw_line(canvas, x_ptt + 13, y_2 + 14, x_ptt + 22, y_2 + 3); - } else { - if (model->ptt_pressed && !model->mic_sync_pressed) { - elements_slightly_rounded_box(canvas, x_ptt + 3, y_2 + 2, x_ptt_width + x_ptt_margin, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, x_ptt + 2 + x_ptt_margin / 2, y_2 + 13, AlignLeft, AlignBottom, "PTT"); - canvas_set_font(canvas, FontSecondary); - } + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, x_ptt + 2 + x_ptt_margin / 2, y_2 + 13, AlignLeft, AlignBottom, "PTT"); + canvas_set_font(canvas, FontSecondary); canvas_set_color(canvas, ColorBlack); } -static void hid_ptt_trigger_mute(HidPtt* hid_ptt, HidPttModel * model) { - if(model->appIndex == HidPttAppIndexGoogleMeet && model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D ); - } else if(model->appIndex == HidPttAppIndexGoogleMeet && !model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D ); - } else if(model->appIndex == HidPttAppIndexZoom && model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A ); - } else if(model->appIndex == HidPttAppIndexFaceTime) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); - } else if(model->appIndex == HidPttAppIndexSkype && model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); - } else if(model->appIndex == HidPttAppIndexSkype && !model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M ); - } -} - -static void hid_ptt_trigger_camera(HidPtt* hid_ptt, HidPttModel * model) { - if(model->appIndex == HidPttAppIndexGoogleMeet && model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E ); - } else if(model->appIndex == HidPttAppIndexGoogleMeet && !model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E ); - } else if(model->appIndex == HidPttAppIndexZoom && model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V ); - } else if(model->appIndex == HidPttAppIndexZoom && !model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V ); - } else if(model->appIndex == HidPttAppIndexSkype && model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K ); - } else if(model->appIndex == HidPttAppIndexSkype && !model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K ); - } -} - -static void hid_ptt_start_ptt(HidPtt* hid_ptt, HidPttModel * model) { - if(model->appIndex == HidPttAppIndexGoogleMeet) { - hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); - } else if(model->appIndex == HidPttAppIndexZoom) { - hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); - } else if(model->appIndex == HidPttAppIndexFaceTime) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); - } else if(model->appIndex == HidPttAppIndexSkype && model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); - } else if(model->appIndex == HidPttAppIndexSkype && !model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M ); - } -} - -static void hid_ptt_stop_ptt(HidPtt* hid_ptt, HidPttModel * model) { - if(model->appIndex == HidPttAppIndexGoogleMeet) { - hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); - } else if(model->appIndex == HidPttAppIndexZoom) { - hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); - } else if(model->appIndex == HidPttAppIndexFaceTime) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); - } else if(model->appIndex == HidPttAppIndexSkype && model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); - } else if(model->appIndex == HidPttAppIndexSkype && !model->is_mac_os) { - hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); - hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M ); - } -} - -// Supports only ±1 -static void hid_ptt_shift_app(HidPttModel * model, int shift) { - int i = (short) model->appIndex; - if (i + shift >= HidPttAppIndexSize) { - model->appIndex = 0; - } else if(i + shift <= 0) { - model->appIndex = HidPttAppIndexSize - 1; - } else { - model->appIndex += shift; - } - // Avoid showing facetime if not macos - if (model->appIndex == HidPttAppIndexFaceTime && !model->is_mac_os) { - hid_ptt_shift_app(model, shift); - } -} - -static void hid_ptt_process(HidPtt* hid_ptt, InputEvent* event) { +static void hid_ptt_process(HidPushToTalk* hid_ptt, InputEvent* event) { with_view_model( hid_ptt->view, - HidPttModel * model, + HidPushToTalkModel * model, { if(event->type == InputTypePress && !model->ptt_pressed) { if(event->key == InputKeyUp) { model->up_pressed = true; - if (!model->mic_pressed){ - hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_INCREMENT); - } else { - if (model->appIndex != HidPttAppIndexFaceTime) { - model->is_mac_os = !model->is_mac_os; - notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); - } - } + hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyDown) { model->down_pressed = true; - if (!model->mic_pressed){ - hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT); - } else if (!model->mic_pressed) { - hid_ptt_shift_app(model, - 1); - notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); - } + hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyLeft) { model->left_pressed = true; - if (model->mic_pressed){ - hid_ptt_shift_app(model, 1); - notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); - } } else if(event->key == InputKeyRight) { model->right_pressed = true; - if (model->mic_pressed){ - hid_ptt_shift_app(model, - 1); - notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); - } } else if(event->key == InputKeyOk) { model->ptt_pressed = true; - if (model->mic_pressed){ - // Change local mic status - model->muted = !model->muted; - model->mic_sync_pressed = true; - notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); - } else { - if (model->muted) { - hid_ptt_start_ptt(hid_ptt, model); - } + if (!model->mic_pressed && model->muted){ + model->callback_start_ptt ? model->callback_start_ptt(hid_ptt):0; } } else if(event->key == InputKeyBack) { model->mic_pressed = true; @@ -358,12 +547,12 @@ static void hid_ptt_process(HidPtt* hid_ptt, InputEvent* event) { } else if(event->type == InputTypeRelease) { if(event->key == InputKeyUp) { model->up_pressed = false; - if (!model->mic_pressed && !model->ptt_pressed){ + if (!model->ptt_pressed){ hid_hal_consumer_key_release(hid_ptt->hid, HID_CONSUMER_VOLUME_INCREMENT); } } else if(event->key == InputKeyDown) { model->down_pressed = false; - if (!model->mic_pressed && !model->ptt_pressed){ + if (!model->ptt_pressed){ hid_hal_consumer_key_release(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT); } } else if(event->key == InputKeyLeft) { @@ -373,38 +562,36 @@ static void hid_ptt_process(HidPtt* hid_ptt, InputEvent* event) { } else if(event->key == InputKeyOk) { model->ptt_pressed = false; - if(!model->mic_pressed && !model->mic_sync_pressed) { + if(!model->mic_pressed) { if (model->muted) { - hid_ptt_stop_ptt(hid_ptt, model); + model->callback_stop_ptt ? model->callback_stop_ptt(hid_ptt):0; } else { - hid_ptt_trigger_mute(hid_ptt, model); + model->callback_trigger_mute ? model->callback_trigger_mute(hid_ptt):0; model->muted = true; } } - model->mic_sync_pressed = false; } else if(event->key == InputKeyBack) { model->mic_pressed = false; } } else if(event->type == InputTypeShort && !model->ptt_pressed) { if(event->key == InputKeyBack ) { // no changes if PTT is pressed model->muted = !model->muted; - hid_ptt_trigger_mute(hid_ptt, model); + model->callback_trigger_mute ? model->callback_trigger_mute(hid_ptt):0; } else if(event->key == InputKeyRight) { - if (!model->mic_pressed){ - hid_ptt_trigger_camera(hid_ptt, model); - } + model->callback_trigger_camera ? model->callback_trigger_camera(hid_ptt):0; + } else if(event->key == InputKeyLeft) { + model->callback_trigger_hand ? model->callback_trigger_hand(hid_ptt):0; } + } else if(event->type == InputTypeLong && event->key == InputKeyRight) { + model->muted = !model->muted; + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); } else if(event->type == InputTypeLong && event->key == InputKeyLeft) { + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); model->left_pressed = false; - if (!model->ptt_pressed){ - hid_hal_keyboard_release_all(hid_ptt->hid); - view_dispatcher_switch_to_view(hid_ptt->hid->view_dispatcher, HidViewSubmenu); - // sequence_double_vibro to notify that we quit PTT - notification_message(hid_ptt->hid->notifications, &sequence_double_vibro); - } + view_dispatcher_switch_to_view(hid_ptt->hid->view_dispatcher, HidViewPushToTalkHelp); } //LED - if (!model->muted || (model->ptt_pressed && !model->mic_sync_pressed)) { + if (!model->muted || (model->ptt_pressed)) { notification_message(hid_ptt->hid->notifications, &sequence_set_red_255); } else { notification_message(hid_ptt->hid->notifications, &sequence_reset_red); @@ -415,46 +602,93 @@ static void hid_ptt_process(HidPtt* hid_ptt, InputEvent* event) { static bool hid_ptt_input_callback(InputEvent* event, void* context) { furi_assert(context); - HidPtt* hid_ptt = context; - bool consumed = true; - hid_ptt_process(hid_ptt, event); + HidPushToTalk* hid_ptt = context; + bool consumed = false; + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_ptt->hid); + notification_message(hid_ptt->hid->notifications, &sequence_double_vibro); + widget_reset(hid_ptt->help); + } else { + consumed = true; + hid_ptt_process(hid_ptt, event); + } return consumed; } -HidPtt* hid_ptt_alloc(Hid* hid) { - HidPtt* hid_ptt = malloc(sizeof(HidPtt)); - hid_ptt->view = view_alloc(); +View* hid_ptt_get_view(HidPushToTalk* hid_ptt) { + furi_assert(hid_ptt); + return hid_ptt->view; +} + +static uint32_t hid_ptt_view(void* context) { + UNUSED(context); + return HidViewPushToTalk; +} + +HidPushToTalk* hid_ptt_alloc(Hid* hid) { + HidPushToTalk* hid_ptt = malloc(sizeof(HidPushToTalk)); hid_ptt->hid = hid; + hid_ptt->view = view_alloc(); view_set_context(hid_ptt->view, hid_ptt); - view_allocate_model(hid_ptt->view, ViewModelTypeLocking, sizeof(HidPttModel)); + view_allocate_model(hid_ptt->view, ViewModelTypeLocking, sizeof(HidPushToTalkModel)); view_set_draw_callback(hid_ptt->view, hid_ptt_draw_callback); view_set_input_callback(hid_ptt->view, hid_ptt_input_callback); view_set_orientation(hid_ptt->view, ViewOrientationVerticalFlip); with_view_model( - hid_ptt->view, HidPttModel * model, { + hid_ptt->view, HidPushToTalkModel * model, { model->transport = hid->transport; model->muted = true; // assume we're muted - model->is_mac_os = true; - model->mic_sync_pressed = false; + model->os = furi_string_alloc(); + model->app = furi_string_alloc(); }, true); + + FURI_LOG_I(TAG, "Calling adding list"); + ptt_menu_add_list(hid->hid_ptt_menu, "macOS", HidPushToTalkMacOS); + ptt_menu_add_list(hid->hid_ptt_menu, "Win/Linux", HidPushToTalkLinux); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Google Meet", HidPushToTalkAppIndexGoogleMeet, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Google Meet", HidPushToTalkAppIndexGoogleMeet, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Discord", HidPushToTalkAppIndexDiscord, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Discord", HidPushToTalkAppIndexDiscord, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "FaceTime", HidPushToTalkAppIndexFaceTime, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Jamulus", HidPushToTalkAppIndexJamulus, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Jamulus", HidPushToTalkAppIndexJamulus, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Skype", HidPushToTalkAppIndexSkype, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Skype", HidPushToTalkAppIndexSkype, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "TeamSpeak", HidPushToTalkAppIndexTeamSpeak, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "TeamSpeak", HidPushToTalkAppIndexTeamSpeak, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Teams", HidPushToTalkAppIndexTeams, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Teams", HidPushToTalkAppIndexTeams, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkMacOS, "Zoom", HidPushToTalkAppIndexZoom, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list(hid->hid_ptt_menu, HidPushToTalkLinux, "Zoom", HidPushToTalkAppIndexZoom, hid_ptt_menu_callback, hid_ptt); + + hid_ptt->help = widget_alloc(); + view_set_previous_callback(widget_get_view(hid_ptt->help), hid_ptt_view); + view_dispatcher_add_view(hid->view_dispatcher, HidViewPushToTalkHelp, widget_get_view(hid_ptt->help)); return hid_ptt; } -void hid_ptt_free(HidPtt* hid_ptt) { +void hid_ptt_free(HidPushToTalk* hid_ptt) { furi_assert(hid_ptt); notification_message(hid_ptt->hid->notifications, &sequence_reset_red); + with_view_model( + hid_ptt->view, HidPushToTalkModel * model, { + furi_string_free(model->os); + furi_string_free(model->app); + }, true); + view_dispatcher_remove_view(hid_ptt->hid->view_dispatcher, HidViewPushToTalkHelp); + widget_free(hid_ptt->help); view_free(hid_ptt->view); free(hid_ptt); } -View* hid_ptt_get_view(HidPtt* hid_ptt) { - furi_assert(hid_ptt); - return hid_ptt->view; -} - -void hid_ptt_set_connected_status(HidPtt* hid_ptt, bool connected) { +void hid_ptt_set_connected_status(HidPushToTalk* hid_ptt, bool connected) { furi_assert(hid_ptt); with_view_model( - hid_ptt->view, HidPttModel * model, { model->connected = connected; }, true); + hid_ptt->view, HidPushToTalkModel * model, { + model->connected = connected; + if (!connected) { + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + } + }, true); } diff --git a/base_pack/hid_app/views/hid_ptt.h b/base_pack/hid_app/views/hid_ptt.h index eb205c20e15..44883edd2bc 100644 --- a/base_pack/hid_app/views/hid_ptt.h +++ b/base_pack/hid_app/views/hid_ptt.h @@ -3,12 +3,17 @@ #include typedef struct Hid Hid; -typedef struct HidPtt HidPtt; +typedef struct HidPushToTalk HidPushToTalk; -HidPtt* hid_ptt_alloc(Hid* bt_hid); +HidPushToTalk* hid_ptt_alloc(Hid* bt_hid); -void hid_ptt_free(HidPtt* hid_ptt); +void hid_ptt_free(HidPushToTalk* hid_ptt); -View* hid_ptt_get_view(HidPtt* hid_ptt); +View* hid_ptt_get_view(HidPushToTalk* hid_ptt); -void hid_ptt_set_connected_status(HidPtt* hid_ptt, bool connected); +void hid_ptt_set_connected_status(HidPushToTalk* hid_ptt, bool connected); + +enum HidPushToTalkOSes { + HidPushToTalkMacOS, + HidPushToTalkLinux, +}; diff --git a/base_pack/hid_app/views/hid_ptt_menu.c b/base_pack/hid_app/views/hid_ptt_menu.c new file mode 100644 index 00000000000..d84a394f4e1 --- /dev/null +++ b/base_pack/hid_app/views/hid_ptt_menu.c @@ -0,0 +1,413 @@ +#include "hid_ptt_menu.h" +#include "hid_ptt.h" +#include +#include +#include "../hid.h" +#include "../views.h" + +#define TAG "HidPushToTalkMenu" + +struct HidPushToTalkMenu { + View* view; + Hid* hid; +}; + +typedef struct { + FuriString* label; + uint32_t index; + PushToTalkMenuItemCallback callback; + void* callback_context; +} PushToTalkMenuItem; + +// Menu item +static void PushToTalkMenuItem_init(PushToTalkMenuItem* item) { + item->label = furi_string_alloc(); + item->index = 0; +} + +static void PushToTalkMenuItem_init_set(PushToTalkMenuItem* item, const PushToTalkMenuItem* src) { + item->label = furi_string_alloc_set(src->label); + item->index = src->index; +} + +static void PushToTalkMenuItem_set(PushToTalkMenuItem* item, const PushToTalkMenuItem* src) { + furi_string_set(item->label, src->label); + item->index = src->index; +} + +static void PushToTalkMenuItem_clear(PushToTalkMenuItem* item) { + furi_string_free(item->label); +} + +ARRAY_DEF( + PushToTalkMenuItemArray, + PushToTalkMenuItem, + (INIT(API_2(PushToTalkMenuItem_init)), + SET(API_6(PushToTalkMenuItem_set)), + INIT_SET(API_6(PushToTalkMenuItem_init_set)), + CLEAR(API_2(PushToTalkMenuItem_clear)))) + +// Menu list (horisontal, 2d array) +typedef struct { + FuriString* label; + uint32_t index; + PushToTalkMenuItemArray_t items; +} PushToTalkMenuList; + +typedef struct { + size_t list_position; + size_t position; + size_t window_position; + PushToTalkMenuList *lists; + int lists_count; +} HidPushToTalkMenuModel; + +static void hid_ptt_menu_draw_list(Canvas* canvas, void* context, const PushToTalkMenuItemArray_t items) { + furi_assert(context); + HidPushToTalkMenuModel* model = context; + const uint8_t item_height = 16; + uint8_t item_width = canvas_width(canvas) - 5; + + canvas_set_font(canvas, FontSecondary); + size_t position = 0; + PushToTalkMenuItemArray_it_t it; + for(PushToTalkMenuItemArray_it(it, items); !PushToTalkMenuItemArray_end_p(it); PushToTalkMenuItemArray_next(it)) { + const size_t item_position = position - model->window_position; + const size_t items_on_screen = 3; + uint8_t y_offset = 16; + + if(item_position < items_on_screen) { + if(position == model->position) { + canvas_set_color(canvas, ColorBlack); + elements_slightly_rounded_box( + canvas, + 0, + y_offset + (item_position * item_height) + 1, + item_width, + item_height - 2); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + FuriString* disp_str; + disp_str = furi_string_alloc_set(PushToTalkMenuItemArray_cref(it)->label); + elements_string_fit_width(canvas, disp_str, item_width - (6 * 2)); + + canvas_draw_str( + canvas, + 6, + y_offset + (item_position * item_height) + item_height - 4, + furi_string_get_cstr(disp_str)); + + furi_string_free(disp_str); + } + + position++; + } + elements_scrollbar_pos(canvas, 128 , 17, 46, model->position, PushToTalkMenuItemArray_size(items)); +} + +PushToTalkMenuList * hid_ptt_menu_get_list_at_index( + void* context, + uint32_t index) { + furi_assert(context); + HidPushToTalkMenuModel* model = context; + for (int i = 0; i < model->lists_count; i++) { + PushToTalkMenuList* list = &model->lists[i]; + if(index == list->index) { + return list; + } + } + return NULL; +} + +static void hid_ptt_menu_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidPushToTalkMenuModel* model = context; + if (model->lists_count == 0){ + return; + } + uint8_t item_width = canvas_width(canvas) - 5; + + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 11, "<"); + canvas_draw_str(canvas, 121, 11, ">"); + + PushToTalkMenuList* list = &model->lists[model->list_position]; + FuriString* disp_str; + disp_str = furi_string_alloc_set(list->label); + elements_string_fit_width(canvas, disp_str, item_width - (6 * 2)); + uint8_t x_pos = (canvas_width(canvas) - canvas_string_width(canvas,furi_string_get_cstr(disp_str))) / 2; + canvas_draw_str(canvas,x_pos,11,furi_string_get_cstr(disp_str)); + furi_string_free(disp_str); + canvas_set_font(canvas, FontSecondary); + hid_ptt_menu_draw_list( + canvas, + context, + list->items + ); +} + +void ptt_menu_add_list( + HidPushToTalkMenu* hid_ptt_menu, + const char* label, + uint32_t index) { + furi_assert(label); + furi_assert(hid_ptt_menu); + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + if (model->lists_count == 0) { + model->lists = (PushToTalkMenuList *)malloc(sizeof(PushToTalkMenuList)); + } else { + model->lists = (PushToTalkMenuList *)realloc(model->lists, (model->lists_count + 1) * sizeof(PushToTalkMenuList)); + } + if (model->lists == NULL) { + FURI_LOG_E(TAG, "Memory reallocation failed (%i)", model->lists_count); + return; + } + PushToTalkMenuList* list = &model->lists[model->lists_count]; + PushToTalkMenuItemArray_init(list->items); + list->label = furi_string_alloc_set(label); + list->index = index; + model->lists_count += 1; + }, + true); +} + + +void ptt_menu_add_item_to_list( + HidPushToTalkMenu* hid_ptt_menu, + uint32_t list_index, + const char* label, + uint32_t index, + PushToTalkMenuItemCallback callback, + void* callback_context) { + PushToTalkMenuItem* item = NULL; + furi_assert(label); + furi_assert(hid_ptt_menu); + UNUSED(list_index); + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + PushToTalkMenuList* list = hid_ptt_menu_get_list_at_index(model, list_index); + if (list == NULL){ + FURI_LOG_E(TAG, "Adding item %s to unknown index %li", label, list_index); + return; + } + item = PushToTalkMenuItemArray_push_new(list->items); + furi_string_set_str(item->label, label); + item->index = index; + item->callback = callback; + item->callback_context = callback_context; + }, + true); +} + +void ptt_menu_shift_list(HidPushToTalkMenu* hid_ptt_menu, int shift){ + size_t new_position = 0; + uint32_t index = 0; + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + int new_list_position = (short) model->list_position + shift; + if (new_list_position >= model->lists_count) { + new_list_position = 0; + } else if (new_list_position < 0) { + new_list_position = model->lists_count - 1; + } + PushToTalkMenuList* list = &model->lists[model->list_position]; + PushToTalkMenuList* new_list = &model->lists[new_list_position]; + size_t new_window_position = model->window_position; + const size_t items_size = PushToTalkMenuItemArray_size(new_list->items); + size_t position = 0; + // Find item index from current list + PushToTalkMenuItemArray_it_t it; + for(PushToTalkMenuItemArray_it(it, list->items); !PushToTalkMenuItemArray_end_p(it); PushToTalkMenuItemArray_next(it)) { + if (position == model->position){ + index = PushToTalkMenuItemArray_cref(it)->index; + break; + } + position++; + } + // Try to find item with the same index in a new list + position = 0; + bool item_exists_in_new_list = false; + for(PushToTalkMenuItemArray_it(it, new_list->items); !PushToTalkMenuItemArray_end_p(it); PushToTalkMenuItemArray_next(it)) { + if (PushToTalkMenuItemArray_cref(it)->index == index) { + item_exists_in_new_list = true; + new_position = position; + break; + } + position++; + } + + // This list item is not presented in a new list, let's try to keep position as is. + // If it's out of range for the new list set it to the end + if (!item_exists_in_new_list) { + new_position = items_size - 1 < model->position ? items_size - 1 : model->position; + } + + // Tune window position. As we have 3 items on screen, keep focus centered + const size_t items_on_screen = 3; + + if (new_position >= items_size - 1) { + if (items_size < items_on_screen + 1) { + new_window_position = 0; + } else { + new_window_position = items_size - items_on_screen; + } + } else if (new_position < items_on_screen - 1) { + new_window_position = 0; + } else { + new_window_position = new_position - 1; + } + model->list_position = new_list_position; + model->position = new_position; + model->window_position = new_window_position; + }, + true); +} + +void ptt_menu_process_up(HidPushToTalkMenu* hid_ptt_menu) { + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + PushToTalkMenuList* list = &model->lists[model->list_position]; + const size_t items_on_screen = 3; + const size_t items_size = PushToTalkMenuItemArray_size(list->items); + + if(model->position > 0) { + model->position--; + if((model->position == model->window_position) && (model->window_position > 0)) { + model->window_position--; + } + } else { + model->position = items_size - 1; + if(model->position > items_on_screen - 1) { + model->window_position = model->position - (items_on_screen - 1); + } + } + }, + true); +} + +void ptt_menu_process_down(HidPushToTalkMenu* hid_ptt_menu) { + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + PushToTalkMenuList* list = &model->lists[model->list_position]; + const size_t items_on_screen = 3; + const size_t items_size = PushToTalkMenuItemArray_size(list->items); + + if(model->position < items_size - 1) { + model->position++; + if((model->position - model->window_position > items_on_screen - 2) && + (model->window_position < items_size - items_on_screen)) { + model->window_position++; + } + } else { + model->position = 0; + model->window_position = 0; + } + }, + true); +} + +void ptt_menu_process_ok(HidPushToTalkMenu* hid_ptt_menu) { + PushToTalkMenuList* list = NULL; + PushToTalkMenuItem* item = NULL; + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, + { + list = &model->lists[model->list_position]; + const size_t items_size = PushToTalkMenuItemArray_size(list->items); + if(model->position < items_size) { + item = PushToTalkMenuItemArray_get(list->items, model->position); + } + }, + true); + if(item && list && item->callback) { + item->callback(item->callback_context, list->index, list->label, item->index, item->label); + } +} + +static bool hid_ptt_menu_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidPushToTalkMenu* hid_ptt_menu = context; + bool consumed = false; + if(event->type == InputTypeShort) { + switch(event->key) { + case InputKeyUp: + consumed = true; + ptt_menu_process_up(hid_ptt_menu); + break; + case InputKeyDown: + consumed = true; + ptt_menu_process_down(hid_ptt_menu); + break; + case InputKeyLeft: + consumed = true; + ptt_menu_shift_list(hid_ptt_menu, -1); + break; + case InputKeyRight: + consumed = true; + ptt_menu_shift_list(hid_ptt_menu, +1); + break; + case InputKeyOk: + consumed = true; + ptt_menu_process_ok(hid_ptt_menu); + break; + default: + break; + } + } else if(event->type == InputTypeRepeat) { + if(event->key == InputKeyUp) { + consumed = true; + ptt_menu_process_up(hid_ptt_menu); + } else if(event->key == InputKeyDown) { + consumed = true; + ptt_menu_process_down(hid_ptt_menu); + } + } + return consumed; +} + +View* hid_ptt_menu_get_view(HidPushToTalkMenu* hid_ptt_menu) { + furi_assert(hid_ptt_menu); + return hid_ptt_menu->view; +} + +HidPushToTalkMenu* hid_ptt_menu_alloc(Hid* hid) { + HidPushToTalkMenu* hid_ptt_menu = malloc(sizeof(HidPushToTalkMenu)); + hid_ptt_menu->hid = hid; + hid_ptt_menu->view = view_alloc(); + view_set_context(hid_ptt_menu->view, hid_ptt_menu); + view_allocate_model(hid_ptt_menu->view, ViewModelTypeLocking, sizeof(HidPushToTalkMenuModel)); + view_set_draw_callback(hid_ptt_menu->view, hid_ptt_menu_draw_callback); + view_set_input_callback(hid_ptt_menu->view, hid_ptt_menu_input_callback); + + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, { + model->lists_count = 0; + model->position = 0; + model->window_position = 0; + }, true); + return hid_ptt_menu; +} + +void hid_ptt_menu_free(HidPushToTalkMenu* hid_ptt_menu) { + furi_assert(hid_ptt_menu); + with_view_model( + hid_ptt_menu->view, HidPushToTalkMenuModel * model, { + for (int i = 0; i < model->lists_count; i++) { + PushToTalkMenuItemArray_clear(model->lists[i].items); + furi_string_free(model->lists[i].label); + } + free(model->lists); + }, true); + view_free(hid_ptt_menu->view); + free(hid_ptt_menu); +} diff --git a/base_pack/hid_app/views/hid_ptt_menu.h b/base_pack/hid_app/views/hid_ptt_menu.h new file mode 100644 index 00000000000..b273ab74d30 --- /dev/null +++ b/base_pack/hid_app/views/hid_ptt_menu.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidPushToTalkMenu HidPushToTalkMenu; + +typedef void (*PushToTalkMenuItemCallback)(void* context, uint32_t listIndex, FuriString* listLabel, uint32_t itemIndex, FuriString* itemLabel ); + +HidPushToTalkMenu* hid_ptt_menu_alloc(Hid* bt_hid); + +void hid_ptt_menu_free(HidPushToTalkMenu* hid_ptt_menu); + +View* hid_ptt_menu_get_view(HidPushToTalkMenu* hid_ptt_menu); + +void ptt_menu_add_item_to_list( + HidPushToTalkMenu* hid_ptt_menu, + uint32_t list_index, + const char* label, + uint32_t index, + PushToTalkMenuItemCallback callback, + void* callback_context); + +void ptt_menu_add_list( + HidPushToTalkMenu* hid_ptt_menu, + const char* label, + uint32_t index); \ No newline at end of file