diff --git a/applications/system/hex_viewer/application.fam b/applications/system/hex_viewer/application.fam index 013ea09e5a..c67fe12c5c 100644 --- a/applications/system/hex_viewer/application.fam +++ b/applications/system/hex_viewer/application.fam @@ -8,11 +8,10 @@ App( "dialogs", ], stack_size=2 * 1024, - order=20, - fap_icon="icons/hex_10px.png", + fap_icon="icons/hex_10px.bmp", fap_icon_assets="icons", fap_category="Tools", fap_author="@QtRoS", fap_version="2.0", - fap_description="App allows to view various files as HEX.", + fap_description="App allows to view various files as HEX", ) diff --git a/applications/system/hex_viewer/helpers/hex_viewer_storage.c b/applications/system/hex_viewer/helpers/hex_viewer_storage.c index 6b4217dc26..b7eb6b0c4b 100644 --- a/applications/system/hex_viewer/helpers/hex_viewer_storage.c +++ b/applications/system/hex_viewer/helpers/hex_viewer_storage.c @@ -123,7 +123,7 @@ bool hex_viewer_open_file(void* context, const char* file_path) { // TODO Separate function? if(hex_viewer->model->stream) { buffered_file_stream_close(hex_viewer->model->stream); - stream_free(hex_viewer->model->stream); // TODO Check + stream_free(hex_viewer->model->stream); hex_viewer->model->file_offset = 0; } diff --git a/applications/system/hex_viewer/hex_viewer.c b/applications/system/hex_viewer/hex_viewer.c index 2b5fe68f25..96b0c5fe1c 100644 --- a/applications/system/hex_viewer/hex_viewer.c +++ b/applications/system/hex_viewer/hex_viewer.c @@ -87,7 +87,10 @@ HexViewer* hex_viewer_app_alloc() { void hex_viewer_app_free(HexViewer* app) { furi_assert(app); - if(app->model->stream) buffered_file_stream_close(app->model->stream); + if(app->model->stream) { + buffered_file_stream_close(app->model->stream); + stream_free(app->model->stream); + } // Scene manager scene_manager_free(app->scene_manager); diff --git a/applications/system/hex_viewer/hex_viewer.h b/applications/system/hex_viewer/hex_viewer.h index d1c1adba31..c8b98926f1 100644 --- a/applications/system/hex_viewer/hex_viewer.h +++ b/applications/system/hex_viewer/hex_viewer.h @@ -43,7 +43,6 @@ typedef struct { Stream* stream; } HexViewerModel; -// TODO Clean typedef struct { HexViewerModel* model; diff --git a/applications/system/hex_viewer/icons/hex_10px.bmp b/applications/system/hex_viewer/icons/hex_10px.bmp new file mode 100644 index 0000000000..54b78dc42b Binary files /dev/null and b/applications/system/hex_viewer/icons/hex_10px.bmp differ diff --git a/applications/system/hex_viewer/scenes/hex_viewer_scene_scroll.c b/applications/system/hex_viewer/scenes/hex_viewer_scene_scroll.c index d20e9cfcb8..9613f53904 100644 --- a/applications/system/hex_viewer/scenes/hex_viewer_scene_scroll.c +++ b/applications/system/hex_viewer/scenes/hex_viewer_scene_scroll.c @@ -13,7 +13,7 @@ void hex_viewer_scene_scroll_on_enter(void* context) { TextInput* text_input = app->text_input; - text_input_set_header_text(text_input, "Scroll to percent (0..100)"); + text_input_set_header_text(text_input, "Scroll to percentage (0..100)"); text_input_set_result_callback( text_input, hex_viewer_scene_scroll_callback, diff --git a/applications/system/hex_viewer/views/hex_viewer_startscreen.c b/applications/system/hex_viewer/views/hex_viewer_startscreen.c index ed40de8a7d..515a0b1390 100644 --- a/applications/system/hex_viewer/views/hex_viewer_startscreen.c +++ b/applications/system/hex_viewer/views/hex_viewer_startscreen.c @@ -61,7 +61,7 @@ void hex_viewer_startscreen_draw(Canvas* canvas, HexViewerStartscreenModel* mode width, 0, ROW_HEIGHT * HEX_VIEWER_LINES_ON_SCREEN, - first_line_on_screen, // TODO + first_line_on_screen, line_count - (HEX_VIEWER_LINES_ON_SCREEN - 1)); } @@ -69,6 +69,9 @@ void hex_viewer_startscreen_draw(Canvas* canvas, HexViewerStartscreenModel* mode uint32_t row_iters = model->file_read_bytes / HEX_VIEWER_BYTES_PER_LINE; if(model->file_read_bytes % HEX_VIEWER_BYTES_PER_LINE != 0) row_iters += 1; + // For the rest of drawing. + canvas_set_font(canvas, FontKeyboard); + for(uint32_t i = 0; i < row_iters; ++i) { uint32_t bytes_left_per_row = model->file_read_bytes - i * HEX_VIEWER_BYTES_PER_LINE; bytes_left_per_row = MIN(bytes_left_per_row, HEX_VIEWER_BYTES_PER_LINE); @@ -79,13 +82,13 @@ void hex_viewer_startscreen_draw(Canvas* canvas, HexViewerStartscreenModel* mode for(uint32_t j = 0; j < bytes_left_per_row; ++j) if(!isprint((int)temp_buf[j])) temp_buf[j] = '.'; - canvas_set_font(canvas, FontKeyboard); + //canvas_set_font(canvas, FontKeyboard); canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); } else { uint32_t addr = model->file_offset + i * HEX_VIEWER_BYTES_PER_LINE; snprintf(temp_buf, 32, "%04lX", addr); - canvas_set_font(canvas, FontKeyboard); + //canvas_set_font(canvas, FontKeyboard); canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); } @@ -93,7 +96,7 @@ void hex_viewer_startscreen_draw(Canvas* canvas, HexViewerStartscreenModel* mode for(uint32_t j = 0; j < bytes_left_per_row; ++j) p += snprintf(p, 32, "%02X ", model->file_bytes[i][j]); - canvas_set_font(canvas, FontKeyboard); + //canvas_set_font(canvas, FontKeyboard); canvas_draw_str(canvas, LEFT_OFFSET + 41, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); } @@ -125,7 +128,7 @@ bool hex_viewer_startscreen_input(InputEvent* event, void* context) { furi_assert(context); HexViewerStartscreen* instance = context; HexViewer* app = instance->context; // TO so good, but works - // TODO InputTypeShort? + if(event->type == InputTypeRelease || event->type == InputTypeRepeat) { switch(event->key) { case InputKeyBack: diff --git a/applications/system/hid_app/application.fam b/applications/system/hid_app/application.fam index dd3e3e98e9..25a72def1e 100644 --- a/applications/system/hid_app/application.fam +++ b/applications/system/hid_app/application.fam @@ -4,8 +4,6 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="hid_usb_app", stack_size=1 * 1024, - fap_description="Use Flipper as a HID remote control over USB", - fap_version="1.0", fap_category="USB", fap_icon="hid_usb_10px.png", fap_icon_assets="assets", @@ -19,8 +17,6 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="hid_ble_app", stack_size=1 * 1024, - fap_description="Use Flipper as a HID remote control over Bluetooth", - fap_version="1.0", fap_category="Bluetooth", fap_icon="hid_ble_10px.png", fap_icon_assets="assets", diff --git a/applications/system/hid_app/assets/BrokenButton_15x15.png b/applications/system/hid_app/assets/BrokenButton_15x15.png new file mode 100644 index 0000000000..a627d16ffc Binary files /dev/null and b/applications/system/hid_app/assets/BrokenButton_15x15.png differ diff --git a/applications/system/hid_app/assets/BtnBackV_9x9.png b/applications/system/hid_app/assets/BtnBackV_9x9.png new file mode 100644 index 0000000000..6aff407a89 Binary files /dev/null and b/applications/system/hid_app/assets/BtnBackV_9x9.png differ diff --git a/applications/system/hid_app/assets/BtnLeft_9x9.png b/applications/system/hid_app/assets/BtnLeft_9x9.png new file mode 100644 index 0000000000..1082423aca Binary files /dev/null and b/applications/system/hid_app/assets/BtnLeft_9x9.png differ diff --git a/applications/system/hid_app/assets/Hand_8x10.png b/applications/system/hid_app/assets/Hand_8x10.png new file mode 100644 index 0000000000..8defeed4d2 Binary files /dev/null and b/applications/system/hid_app/assets/Hand_8x10.png differ diff --git a/applications/system/hid_app/assets/Help_exit_64x9.png b/applications/system/hid_app/assets/Help_exit_64x9.png new file mode 100644 index 0000000000..7f40db3871 Binary files /dev/null and b/applications/system/hid_app/assets/Help_exit_64x9.png differ diff --git a/applications/system/hid_app/assets/Help_top_64x17.png b/applications/system/hid_app/assets/Help_top_64x17.png new file mode 100644 index 0000000000..ecc0e66474 Binary files /dev/null and b/applications/system/hid_app/assets/Help_top_64x17.png differ diff --git a/applications/system/hid_app/assets/Hold_15x5.png b/applications/system/hid_app/assets/Hold_15x5.png new file mode 100644 index 0000000000..102d0bd7a9 Binary files /dev/null and b/applications/system/hid_app/assets/Hold_15x5.png differ diff --git a/applications/system/hid_app/assets/Mic_7x11.png b/applications/system/hid_app/assets/Mic_7x11.png new file mode 100644 index 0000000000..49223394b4 Binary files /dev/null and b/applications/system/hid_app/assets/Mic_7x11.png differ diff --git a/applications/system/hid_app/assets/MicrophoneCrossed_16x16.png b/applications/system/hid_app/assets/MicrophoneCrossed_16x16.png new file mode 100644 index 0000000000..0e91d2f86d Binary files /dev/null and b/applications/system/hid_app/assets/MicrophoneCrossed_16x16.png differ diff --git a/applications/system/hid_app/assets/MicrophonePressedBtn_16x16.png b/applications/system/hid_app/assets/MicrophonePressedBtn_16x16.png new file mode 100644 index 0000000000..911fe1b22f Binary files /dev/null and b/applications/system/hid_app/assets/MicrophonePressedBtn_16x16.png differ diff --git a/applications/system/hid_app/assets/MicrophonePressedCrossedBtn_16x16.png b/applications/system/hid_app/assets/MicrophonePressedCrossedBtn_16x16.png new file mode 100644 index 0000000000..1c485a85b6 Binary files /dev/null and b/applications/system/hid_app/assets/MicrophonePressedCrossedBtn_16x16.png differ diff --git a/applications/system/hid_app/assets/RoundButtonUnpressed_16x16.png b/applications/system/hid_app/assets/RoundButtonUnpressed_16x16.png new file mode 100644 index 0000000000..e2fc85c19c Binary files /dev/null and b/applications/system/hid_app/assets/RoundButtonUnpressed_16x16.png differ diff --git a/applications/system/hid_app/assets/for_help_27x5.png b/applications/system/hid_app/assets/for_help_27x5.png new file mode 100644 index 0000000000..20bb30a086 Binary files /dev/null and b/applications/system/hid_app/assets/for_help_27x5.png differ diff --git a/applications/system/hid_app/hid.c b/applications/system/hid_app/hid.c index 4792211cbd..f22d8cae97 100644 --- a/applications/system/hid_app/hid.c +++ b/applications/system/hid_app/hid.c @@ -12,11 +12,11 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexNumpad, HidSubmenuIndexMedia, HidSubmenuIndexMovie, - HidSubmenuIndexTikTok, + HidSubmenuIndexTikShorts, HidSubmenuIndexMouse, HidSubmenuIndexMouseClicker, HidSubmenuIndexMouseJiggler, - HidSubmenuIndexPtt, + HidSubmenuIndexPushToTalk, }; static void hid_submenu_callback(void* context, uint32_t index) { @@ -45,18 +45,18 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexMouse) { app->view_id = HidViewMouse; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse); - } else if(index == HidSubmenuIndexTikTok) { - app->view_id = BtHidViewTikTok; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); + } else if(index == HidSubmenuIndexTikShorts) { + app->view_id = BtHidViewTikShorts; + view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikShorts); } else if(index == HidSubmenuIndexMouseClicker) { app->view_id = HidViewMouseClicker; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker); } 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); } } @@ -80,7 +80,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected); hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); hid_ptt_set_connected_status(hid->hid_ptt, connected); - hid_tiktok_set_connected_status(hid->hid_tiktok, connected); + hid_tikshorts_set_connected_status(hid->hid_tikshorts, connected); } static uint32_t hid_menu_view(void* context) { @@ -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; @@ -134,7 +139,7 @@ Hid* hid_alloc(HidTransport transport) { submenu_add_item( app->device_type_submenu, "TikTok / YT Shorts", - HidSubmenuIndexTikTok, + HidSubmenuIndexTikShorts, hid_submenu_callback, app); } @@ -151,7 +156,11 @@ 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)); @@ -163,7 +172,6 @@ Hid* hid_alloc(HidTransport transport) { Hid* hid_app_alloc_view(void* context) { furi_assert(context); Hid* app = context; - // Dialog view // Keynote view app->hid_keynote = hid_keynote_alloc(app); @@ -195,11 +203,11 @@ Hid* hid_app_alloc_view(void* context) { view_dispatcher_add_view( app->view_dispatcher, HidViewMovie, hid_movie_get_view(app->hid_movie)); - // TikTok view - app->hid_tiktok = hid_tiktok_alloc(app); - view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_menu_view); + // TikTok / YT Shorts view + app->hid_tikshorts = hid_tikshorts_alloc(app); + view_set_previous_callback(hid_tikshorts_get_view(app->hid_tikshorts), hid_menu_view); view_dispatcher_add_view( - app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok)); + app->view_dispatcher, BtHidViewTikShorts, hid_tikshorts_get_view(app->hid_tikshorts)); // Mouse view app->hid_mouse = hid_mouse_alloc(app); @@ -223,10 +231,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_dispatcher_add_view(app->view_dispatcher, HidViewPtt, hid_ptt_get_view(app->hid_ptt)); + view_set_previous_callback(hid_ptt_get_view(app->hid_ptt), hid_ptt_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewPushToTalk, hid_ptt_get_view(app->hid_ptt)); return app; } @@ -258,10 +271,12 @@ 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, BtHidViewTikTok); - hid_tiktok_free(app->hid_tiktok); + view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikShorts); + hid_tikshorts_free(app->hid_tikshorts); view_dispatcher_free(app->view_dispatcher); // Close records diff --git a/applications/system/hid_app/hid.h b/applications/system/hid_app/hid.h index 2eb224bd53..9f10243ecc 100644 --- a/applications/system/hid_app/hid.h +++ b/applications/system/hid_app/hid.h @@ -23,8 +23,9 @@ #include "views/hid_mouse.h" #include "views/hid_mouse_clicker.h" #include "views/hid_mouse_jiggler.h" -#include "views/hid_tiktok.h" +#include "views/hid_tikshorts.h" #include "views/hid_ptt.h" +#include "views/hid_ptt_menu.h" #include @@ -51,8 +52,9 @@ struct Hid { HidMouse* hid_mouse; HidMouseClicker* hid_mouse_clicker; HidMouseJiggler* hid_mouse_jiggler; - HidTikTok* hid_tiktok; - HidPtt* hid_ptt; + HidTikShorts* hid_tikshorts; + HidPushToTalk* hid_ptt; + HidPushToTalkMenu* hid_ptt_menu; HidTransport transport; uint32_t view_id; diff --git a/applications/system/hid_app/views.h b/applications/system/hid_app/views.h index 94279b9c43..961faec529 100644 --- a/applications/system/hid_app/views.h +++ b/applications/system/hid_app/views.h @@ -8,6 +8,8 @@ typedef enum { HidViewMouse, HidViewMouseClicker, HidViewMouseJiggler, - BtHidViewTikTok, - HidViewPtt, + BtHidViewTikShorts, + HidViewPushToTalk, + HidViewPushToTalkMenu, + HidViewPushToTalkHelp, } HidView; diff --git a/applications/system/hid_app/views/hid_media.c b/applications/system/hid_app/views/hid_media.c index 2fc0cfb1a2..849c511d94 100644 --- a/applications/system/hid_app/views/hid_media.c +++ b/applications/system/hid_app/views/hid_media.c @@ -188,12 +188,15 @@ static bool hid_media_input_callback(InputEvent* event, void* context) { HidMedia* hid_media = context; bool consumed = false; - if(event->type == InputTypePress) { - hid_media_process_press(hid_media, event); - consumed = true; - } else if(event->type == InputTypeRelease) { - hid_media_process_release(hid_media, event); + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_media->hid); + } else { consumed = true; + if(event->type == InputTypePress) { + hid_media_process_press(hid_media, event); + } else if(event->type == InputTypeRelease) { + hid_media_process_release(hid_media, event); + } } return consumed; } diff --git a/applications/system/hid_app/views/hid_movie.c b/applications/system/hid_app/views/hid_movie.c index 866a4c6266..229f7299a5 100644 --- a/applications/system/hid_app/views/hid_movie.c +++ b/applications/system/hid_app/views/hid_movie.c @@ -186,13 +186,19 @@ static bool hid_movie_input_callback(InputEvent* event, void* context) { HidMovie* hid_movie = context; bool consumed = false; - if(event->type == InputTypePress) { - hid_movie_process_press(hid_movie, event); - consumed = true; - } else if(event->type == InputTypeRelease) { - hid_movie_process_release(hid_movie, event); + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_movie->hid); + } else { consumed = true; + if(event->type == InputTypePress) { + hid_movie_process_press(hid_movie, event); + consumed = true; + } else if(event->type == InputTypeRelease) { + hid_movie_process_release(hid_movie, event); + consumed = true; + } } + return consumed; } diff --git a/applications/system/hid_app/views/hid_ptt.c b/applications/system/hid_app/views/hid_ptt.c index 3670cb2196..3d5879f2b7 100644 --- a/applications/system/hid_app/views/hid_ptt.c +++ b/applications/system/hid_app/views/hid_ptt.c @@ -1,18 +1,23 @@ #include "hid_ptt.h" +#include "hid_ptt_menu.h" #include #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; @@ -22,22 +27,555 @@ typedef struct { bool ptt_pressed; bool mic_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 { + HidPushToTalkAppIndexDiscord, + HidPushToTalkAppIndexFaceTime, + HidPushToTalkAppIndexGoogleMeet, + HidPushToTalkAppIndexGoogleHangouts, + HidPushToTalkAppIndexJamulus, + HidPushToTalkAppIndexSignal, + HidPushToTalkAppIndexSkype, + HidPushToTalkAppIndexSlackCall, + HidPushToTalkAppIndexSlackHubble, + HidPushToTalkAppIndexTeams, + HidPushToTalkAppIndexTeamSpeak, + HidPushToTalkAppIndexWebex, + HidPushToTalkAppIndexZoom, + 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); +} + +// Hangouts HidPushToTalkAppIndexGoogleHangouts +static void hid_ptt_trigger_mute_macos_hangouts(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_hangouts(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_hangouts(HidPushToTalk* hid_ptt) { // and hand in teams + 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_hangouts(HidPushToTalk* hid_ptt) { // and hand in teams + 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); +} + +// Signal +static void hid_ptt_trigger_mute_signal(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); +} +static void hid_ptt_trigger_camera_signal(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); +} + +// skype +static void hid_ptt_trigger_mute_linux_skype(HidPushToTalk* hid_ptt) { // and webex + 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); +} + +// slack call +static void hid_ptt_trigger_mute_slack_call(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_M); +} +static void hid_ptt_trigger_camera_slack_call(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_V); +} + +// slack hubble +static void hid_ptt_trigger_mute_macos_slack_hubble(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); +} +static void hid_ptt_trigger_mute_linux_slack_hubble(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_SPACEBAR); +} + +// 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); +} + +// webex + +static void hid_ptt_trigger_camera_webex(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); +} +static void hid_ptt_trigger_hand_macos_webex(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); +} +static void hid_ptt_trigger_hand_linux_webex(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); + hid_hal_keyboard_release( + hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R); +} + +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 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 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 HidPushToTalkAppIndexGoogleHangouts: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_hangouts; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_hangouts; + model->callback_start_ptt = hid_ptt_trigger_mute_macos_hangouts; + model->callback_stop_ptt = hid_ptt_trigger_mute_macos_hangouts; + break; + 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 HidPushToTalkAppIndexJamulus: + model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; + model->callback_start_ptt = hid_ptt_trigger_mute_jamulus; + model->callback_stop_ptt = hid_ptt_trigger_mute_jamulus; + 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 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 HidPushToTalkAppIndexSignal: + model->callback_trigger_mute = hid_ptt_trigger_mute_signal; + model->callback_trigger_camera = hid_ptt_trigger_camera_signal; + model->callback_start_ptt = hid_ptt_trigger_mute_signal; + model->callback_stop_ptt = hid_ptt_trigger_mute_signal; + 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 HidPushToTalkAppIndexSlackCall: + model->callback_trigger_mute = hid_ptt_trigger_mute_slack_call; + model->callback_trigger_camera = hid_ptt_trigger_camera_slack_call; + model->callback_start_ptt = hid_ptt_trigger_mute_slack_call; + model->callback_stop_ptt = hid_ptt_trigger_mute_slack_call; + break; + case HidPushToTalkAppIndexSlackHubble: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_slack_hubble; + model->callback_start_ptt = hid_ptt_trigger_mute_macos_slack_hubble; + model->callback_stop_ptt = hid_ptt_trigger_mute_macos_slack_hubble; + break; + case HidPushToTalkAppIndexWebex: + model->callback_trigger_mute = hid_ptt_trigger_cmd_shift_m; + model->callback_trigger_camera = hid_ptt_trigger_camera_webex; + model->callback_trigger_hand = hid_ptt_trigger_hand_macos_webex; + model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m; + model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m; + 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; + } + } else if(osIndex == HidPushToTalkLinux) { + switch(appIndex) { + 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 HidPushToTalkAppIndexGoogleHangouts: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_hangouts; + model->callback_trigger_camera = hid_ptt_trigger_camera_linux_hangouts; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_hangouts; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_hangouts; + break; + 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 HidPushToTalkAppIndexJamulus: + model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; + model->callback_start_ptt = hid_ptt_trigger_mute_jamulus; + model->callback_stop_ptt = hid_ptt_trigger_mute_jamulus; + 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 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 HidPushToTalkAppIndexSignal: + model->callback_trigger_mute = hid_ptt_trigger_mute_signal; + model->callback_trigger_camera = hid_ptt_trigger_camera_signal; + model->callback_start_ptt = hid_ptt_trigger_mute_signal; + model->callback_stop_ptt = hid_ptt_trigger_mute_signal; + 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_trigger_mute_linux_skype; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_skype; + break; + case HidPushToTalkAppIndexSlackCall: + model->callback_trigger_mute = hid_ptt_trigger_mute_slack_call; + model->callback_trigger_camera = hid_ptt_trigger_camera_slack_call; + model->callback_start_ptt = hid_ptt_trigger_mute_slack_call; + model->callback_stop_ptt = hid_ptt_trigger_mute_slack_call; + break; + case HidPushToTalkAppIndexSlackHubble: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_slack_hubble; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_slack_hubble; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_slack_hubble; + 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 HidPushToTalkAppIndexWebex: + model->callback_trigger_mute = hid_ptt_trigger_mute_linux_skype; + model->callback_trigger_camera = hid_ptt_trigger_camera_webex; + model->callback_trigger_hand = hid_ptt_trigger_hand_linux_webex; + model->callback_start_ptt = hid_ptt_trigger_mute_linux_skype; + model->callback_stop_ptt = hid_ptt_trigger_mute_linux_skype; + 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 TeamSpeak 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); @@ -49,52 +587,26 @@ 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, 19, y_mic - 1, &I_Pin_back_arrow_rotated_8x10); - elements_multiline_text_aligned(canvas, 0, y_mic, AlignLeft, AlignTop, "Hold to sync"); - elements_multiline_text_aligned(canvas, 20, y_mic + 10, AlignLeft, AlignTop, "mic status"); + hid_ptt_draw_text_centered(canvas, 73, model->app); + hid_ptt_draw_text_centered(canvas, 84, model->os); - // Exit label - canvas_draw_icon(canvas, 20, 121, &I_ButtonLeft_4x7); - elements_multiline_text_aligned(canvas, 0, 121, AlignLeft, AlignTop, "Hold to exit"); + // 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; @@ -104,13 +616,7 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); canvas_set_color(canvas, ColorWhite); } - if(model->ptt_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_draw_icon(canvas, x_2 + 5, y_1 + 5, &I_Volup_8x6); canvas_set_color(canvas, ColorBlack); // Down @@ -119,21 +625,19 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, x_2 + 3, y_3 + 2, 13, 13); canvas_set_color(canvas, ColorWhite); } - if(!model->ptt_pressed) { - canvas_draw_icon(canvas, x_2 + 6, y_3 + 5, &I_Voldwn_6x6); - } + canvas_draw_icon(canvas, x_2 + 6, y_3 + 5, &I_Voldwn_6x6); canvas_set_color(canvas, ColorBlack); - // Left + // 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->ptt_pressed) { - canvas_draw_icon(canvas, x_1 + 7, y_2 + 5, &I_ButtonLeft_4x7); + 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 + 4, y_2 + 5, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, x_1 + 2, y_2 + 1, &I_BrokenButton_15x15); } canvas_set_color(canvas, ColorBlack); @@ -143,31 +647,34 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); canvas_set_color(canvas, ColorWhite); } - if(!model->ptt_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); - } + if(model->callback_trigger_camera) { + hid_ptt_draw_camera(canvas, x_3 + 4, y_2 + 5); } else { - canvas_draw_icon(canvas, x_3 + 8, y_2 + 5, &I_ButtonRight_4x7); + 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, 0 + 2, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, x_mic + 5, 0 + 4, &I_Mic_btn_8x10); - if(model->muted && !model->ptt_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); + canvas_draw_icon(canvas, x_mic, 0, &I_RoundButtonUnpressed_16x16); + + 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; @@ -180,6 +687,7 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { 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) { elements_slightly_rounded_box(canvas, x_ptt + 3, y_2 + 2, x_ptt_width + x_ptt_margin, 13); canvas_set_color(canvas, ColorWhite); @@ -188,163 +696,29 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) { 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) { + if(event->type == InputTypePress && !model->ptt_pressed) { if(event->key == InputKeyUp) { model->up_pressed = true; - if(!model->ptt_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->ptt_pressed) { - hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT); - } else { - 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->ptt_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->ptt_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->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; @@ -367,46 +741,41 @@ static void hid_ptt_process(HidPtt* hid_ptt, InputEvent* event) { } else if(event->key == InputKeyOk) { model->ptt_pressed = false; - if(model->muted) { - hid_ptt_stop_ptt(hid_ptt, model); - } else { - hid_ptt_trigger_mute(hid_ptt, model); - model->muted = true; + if(!model->mic_pressed) { + if(model->muted) { + model->callback_stop_ptt ? model->callback_stop_ptt(hid_ptt) : 0; + } else { + model->callback_trigger_mute ? model->callback_trigger_mute(hid_ptt) : + 0; + model->muted = true; + } } } else if(event->key == InputKeyBack) { model->mic_pressed = false; } - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyBack && - !model->ptt_pressed) { // no changes if PTT is pressed + } 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->ptt_pressed) { - hid_ptt_trigger_camera(hid_ptt, model); - } - } - } else if(event->type == InputTypeLong) { - if(event->key == InputKeyLeft) { - 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); - } - } else if(event->key == InputKeyBack && !model->ptt_pressed) { // no changes if PTT is pressed - // Change local mic status - model->muted = !model->muted; - notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + 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; + view_dispatcher_switch_to_view( + hid_ptt->hid->view_dispatcher, HidViewPushToTalkHelp); } //LED - if(model->muted && !model->ptt_pressed) { - notification_message(hid_ptt->hid->notifications, &sequence_reset_red); - } else { + 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); } }, true); @@ -414,48 +783,263 @@ 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, + HidPushToTalkModel * model, { model->transport = hid->transport; model->muted = true; // assume we're muted - model->is_mac_os = true; + 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, + "Google Hangouts", + HidPushToTalkAppIndexGoogleHangouts, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Google Hangouts", + HidPushToTalkAppIndexGoogleHangouts, + 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, + "Signal", + HidPushToTalkAppIndexSignal, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Signal", + HidPushToTalkAppIndexSignal, + 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, + "Slack Call", + HidPushToTalkAppIndexSlackCall, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Slack Call", + HidPushToTalkAppIndexSlackCall, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Slack Hubble", + HidPushToTalkAppIndexSlackHubble, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Slack Hubble", + HidPushToTalkAppIndexSlackHubble, + 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); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Webex", + HidPushToTalkAppIndexWebex, + hid_ptt_menu_callback, + hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkLinux, + "Webex", + HidPushToTalkAppIndexWebex, + 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, + { + if(!connected && model->connected) { + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + } + model->connected = connected; + }, + true); } diff --git a/applications/system/hid_app/views/hid_ptt.h b/applications/system/hid_app/views/hid_ptt.h index eb205c20e1..219e1c537c 100644 --- a/applications/system/hid_app/views/hid_ptt.h +++ b/applications/system/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/applications/system/hid_app/views/hid_ptt_menu.c b/applications/system/hid_app/views/hid_ptt_menu.c new file mode 100644 index 0000000000..074c85ba72 --- /dev/null +++ b/applications/system/hid_app/views/hid_ptt_menu.c @@ -0,0 +1,423 @@ +#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/applications/system/hid_app/views/hid_ptt_menu.h b/applications/system/hid_app/views/hid_ptt_menu.h new file mode 100644 index 0000000000..c6dc53d550 --- /dev/null +++ b/applications/system/hid_app/views/hid_ptt_menu.h @@ -0,0 +1,29 @@ +#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 diff --git a/applications/system/hid_app/views/hid_tiktok.c b/applications/system/hid_app/views/hid_tikshorts.c similarity index 60% rename from applications/system/hid_app/views/hid_tiktok.c rename to applications/system/hid_app/views/hid_tikshorts.c index 0bfcaa2f4d..6965c13318 100644 --- a/applications/system/hid_app/views/hid_tiktok.c +++ b/applications/system/hid_app/views/hid_tikshorts.c @@ -1,12 +1,12 @@ -#include "hid_tiktok.h" +#include "hid_tikshorts.h" #include "../hid.h" #include #include "hid_icons.h" -#define TAG "HidTikTok" +#define TAG "HidTikShorts" -struct HidTikTok { +struct HidTikShorts { View* view; Hid* hid; }; @@ -21,11 +21,11 @@ typedef struct { bool is_cursor_set; bool back_mouse_pressed; HidTransport transport; -} HidTikTokModel; +} HidTikShortsModel; -static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { +static void hid_tikshorts_draw_callback(Canvas* canvas, void* context) { furi_assert(context); - HidTikTokModel* model = context; + HidTikShortsModel* model = context; // Header if(model->transport == HidTransportBle) { @@ -110,30 +110,32 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } -static void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) { +static void hid_tikshorts_reset_cursor(HidTikShorts* hid_tikshorts) { // Set cursor to the phone's left up corner // Delays to guarantee one packet per connection interval for(size_t i = 0; i < 8; i++) { - hid_hal_mouse_move(hid_tiktok->hid, -127, -127); + hid_hal_mouse_move(hid_tikshorts->hid, -127, -127); furi_delay_ms(50); } // Move cursor from the corner - hid_hal_mouse_move(hid_tiktok->hid, 20, 120); + hid_hal_mouse_move(hid_tikshorts->hid, 20, 120); furi_delay_ms(50); } -static void - hid_tiktok_process_press(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) { +static void hid_tikshorts_process_press( + HidTikShorts* hid_tikshorts, + HidTikShortsModel* model, + InputEvent* event) { if(event->key == InputKeyUp) { model->up_pressed = true; } else if(event->key == InputKeyDown) { model->down_pressed = true; } else if(event->key == InputKeyLeft) { model->left_pressed = true; - hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); + hid_hal_consumer_key_press(hid_tikshorts->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyRight) { model->right_pressed = true; - hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); + hid_hal_consumer_key_press(hid_tikshorts->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyOk) { model->ok_pressed = true; } else if(event->key == InputKeyBack) { @@ -141,18 +143,20 @@ static void } } -static void - hid_tiktok_process_release(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) { +static void hid_tikshorts_process_release( + HidTikShorts* hid_tikshorts, + HidTikShortsModel* model, + InputEvent* event) { if(event->key == InputKeyUp) { model->up_pressed = false; } else if(event->key == InputKeyDown) { model->down_pressed = false; } else if(event->key == InputKeyLeft) { model->left_pressed = false; - hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); + hid_hal_consumer_key_release(hid_tikshorts->hid, HID_CONSUMER_VOLUME_DECREMENT); } else if(event->key == InputKeyRight) { model->right_pressed = false; - hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); + hid_hal_consumer_key_release(hid_tikshorts->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyOk) { model->ok_pressed = false; } else if(event->key == InputKeyBack) { @@ -160,61 +164,61 @@ static void } } -static bool hid_tiktok_input_callback(InputEvent* event, void* context) { +static bool hid_tikshorts_input_callback(InputEvent* event, void* context) { furi_assert(context); - HidTikTok* hid_tiktok = context; + HidTikShorts* hid_tikshorts = context; bool consumed = false; with_view_model( - hid_tiktok->view, - HidTikTokModel * model, + hid_tikshorts->view, + HidTikShortsModel * model, { if(event->type == InputTypePress) { - hid_tiktok_process_press(hid_tiktok, model, event); + hid_tikshorts_process_press(hid_tikshorts, model, event); if(model->connected && !model->is_cursor_set) { - hid_tiktok_reset_cursor(hid_tiktok); + hid_tikshorts_reset_cursor(hid_tikshorts); model->is_cursor_set = true; } consumed = true; } else if(event->type == InputTypeRelease) { - hid_tiktok_process_release(hid_tiktok, model, event); + hid_tikshorts_process_release(hid_tikshorts, model, event); consumed = true; } else if(event->type == InputTypeShort) { if(event->key == InputKeyOk) { - hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_press(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(25); - hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(100); - hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_press(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(25); - hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - consumed = true; - } else if(event->key == InputKeyUp) { - // Emulate up swipe - hid_hal_mouse_scroll(hid_tiktok->hid, -6); - hid_hal_mouse_scroll(hid_tiktok->hid, -8); - hid_hal_mouse_scroll(hid_tiktok->hid, -10); - hid_hal_mouse_scroll(hid_tiktok->hid, -8); - hid_hal_mouse_scroll(hid_tiktok->hid, -6); + hid_hal_mouse_release(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); consumed = true; } else if(event->key == InputKeyDown) { - // Emulate down swipe - hid_hal_mouse_scroll(hid_tiktok->hid, 6); - hid_hal_mouse_scroll(hid_tiktok->hid, 8); - hid_hal_mouse_scroll(hid_tiktok->hid, 10); - hid_hal_mouse_scroll(hid_tiktok->hid, 8); - hid_hal_mouse_scroll(hid_tiktok->hid, 6); + // Swipe to next video + hid_hal_mouse_scroll(hid_tikshorts->hid, 6); + hid_hal_mouse_scroll(hid_tikshorts->hid, 8); + hid_hal_mouse_scroll(hid_tikshorts->hid, 10); + hid_hal_mouse_scroll(hid_tikshorts->hid, 8); + hid_hal_mouse_scroll(hid_tikshorts->hid, 6); + consumed = true; + } else if(event->key == InputKeyUp) { + // Swipe to previous video + hid_hal_mouse_scroll(hid_tikshorts->hid, -6); + hid_hal_mouse_scroll(hid_tikshorts->hid, -8); + hid_hal_mouse_scroll(hid_tikshorts->hid, -10); + hid_hal_mouse_scroll(hid_tikshorts->hid, -8); + hid_hal_mouse_scroll(hid_tikshorts->hid, -6); consumed = true; } else if(event->key == InputKeyBack) { // Pause - hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_press(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(50); - hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_tikshorts->hid, HID_MOUSE_BTN_LEFT); consumed = true; } } else if(event->type == InputTypeLong) { if(event->key == InputKeyBack) { - hid_hal_consumer_key_release_all(hid_tiktok->hid); + hid_hal_consumer_key_release_all(hid_tikshorts->hid); model->is_cursor_set = false; consumed = false; } @@ -225,37 +229,40 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) { return consumed; } -HidTikTok* hid_tiktok_alloc(Hid* bt_hid) { - HidTikTok* hid_tiktok = malloc(sizeof(HidTikTok)); - hid_tiktok->hid = bt_hid; - hid_tiktok->view = view_alloc(); - view_set_context(hid_tiktok->view, hid_tiktok); - view_allocate_model(hid_tiktok->view, ViewModelTypeLocking, sizeof(HidTikTokModel)); - view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback); - view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback); +HidTikShorts* hid_tikshorts_alloc(Hid* bt_hid) { + HidTikShorts* hid_tikshorts = malloc(sizeof(HidTikShorts)); + hid_tikshorts->hid = bt_hid; + hid_tikshorts->view = view_alloc(); + view_set_context(hid_tikshorts->view, hid_tikshorts); + view_allocate_model(hid_tikshorts->view, ViewModelTypeLocking, sizeof(HidTikShortsModel)); + view_set_draw_callback(hid_tikshorts->view, hid_tikshorts_draw_callback); + view_set_input_callback(hid_tikshorts->view, hid_tikshorts_input_callback); with_view_model( - hid_tiktok->view, HidTikTokModel * model, { model->transport = bt_hid->transport; }, true); + hid_tikshorts->view, + HidTikShortsModel * model, + { model->transport = bt_hid->transport; }, + true); - return hid_tiktok; + return hid_tikshorts; } -void hid_tiktok_free(HidTikTok* hid_tiktok) { - furi_assert(hid_tiktok); - view_free(hid_tiktok->view); - free(hid_tiktok); +void hid_tikshorts_free(HidTikShorts* hid_tikshorts) { + furi_assert(hid_tikshorts); + view_free(hid_tikshorts->view); + free(hid_tikshorts); } -View* hid_tiktok_get_view(HidTikTok* hid_tiktok) { - furi_assert(hid_tiktok); - return hid_tiktok->view; +View* hid_tikshorts_get_view(HidTikShorts* hid_tikshorts) { + furi_assert(hid_tikshorts); + return hid_tikshorts->view; } -void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected) { - furi_assert(hid_tiktok); +void hid_tikshorts_set_connected_status(HidTikShorts* hid_tikshorts, bool connected) { + furi_assert(hid_tikshorts); with_view_model( - hid_tiktok->view, - HidTikTokModel * model, + hid_tikshorts->view, + HidTikShortsModel * model, { model->connected = connected; model->is_cursor_set = false; diff --git a/applications/system/hid_app/views/hid_tikshorts.h b/applications/system/hid_app/views/hid_tikshorts.h new file mode 100644 index 0000000000..5604962ee8 --- /dev/null +++ b/applications/system/hid_app/views/hid_tikshorts.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidTikShorts HidTikShorts; + +HidTikShorts* hid_tikshorts_alloc(Hid* bt_hid); + +void hid_tikshorts_free(HidTikShorts* hid_tikshorts); + +View* hid_tikshorts_get_view(HidTikShorts* hid_tikshorts); + +void hid_tikshorts_set_connected_status(HidTikShorts* hid_tikshorts, bool connected); diff --git a/applications/system/hid_app/views/hid_tiktok.h b/applications/system/hid_app/views/hid_tiktok.h deleted file mode 100644 index b2efc3692d..0000000000 --- a/applications/system/hid_app/views/hid_tiktok.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -typedef struct Hid Hid; -typedef struct HidTikTok HidTikTok; - -HidTikTok* hid_tiktok_alloc(Hid* bt_hid); - -void hid_tiktok_free(HidTikTok* hid_tiktok); - -View* hid_tiktok_get_view(HidTikTok* hid_tiktok); - -void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected); diff --git a/applications/system/ir_remote/infrared_remote_app.c b/applications/system/ir_remote/infrared_remote_app.c index bc27c21491..43d7dbd2b6 100644 --- a/applications/system/ir_remote/infrared_remote_app.c +++ b/applications/system/ir_remote/infrared_remote_app.c @@ -6,7 +6,7 @@ #include #include #include -#include "ir_remote_icons.h" +#include #include #include diff --git a/applications/system/subghz_playlist/application.fam b/applications/system/subghz_playlist/application.fam index 5050197f22..4f112ff968 100644 --- a/applications/system/subghz_playlist/application.fam +++ b/applications/system/subghz_playlist/application.fam @@ -5,6 +5,7 @@ App( entry_point="playlist_app", requires=["storage", "gui", "dialogs", "subghz"], stack_size=2 * 1024, + order=14, fap_icon="subplaylist_10px.png", fap_category="Sub-GHz", fap_icon_assets="images", diff --git a/applications/system/subghz_playlist/playlist.c b/applications/system/subghz_playlist/playlist.c index 518d0ed58d..4dfc2cd69d 100644 --- a/applications/system/subghz_playlist/playlist.c +++ b/applications/system/subghz_playlist/playlist.c @@ -6,7 +6,7 @@ #include #include -#include "subghz_playlist_icons.h" +#include #include #include diff --git a/applications/system/subghz_remote/views/remote.c b/applications/system/subghz_remote/views/remote.c index 2523d19779..0774d3875e 100644 --- a/applications/system/subghz_remote/views/remote.c +++ b/applications/system/subghz_remote/views/remote.c @@ -239,7 +239,6 @@ bool subrem_view_remote_input(InputEvent* event, void* context) { return true; } - // BACK button processing end if(event->key == InputKeyUp && event->type == InputTypePress) { diff --git a/applications/system/text_viewer/application.fam b/applications/system/text_viewer/application.fam index cbbe8b2d40..c186f4a3de 100644 --- a/applications/system/text_viewer/application.fam +++ b/applications/system/text_viewer/application.fam @@ -8,6 +8,7 @@ App( "dialogs", ], stack_size=10 * 1024, + order=20, fap_icon="icons/text_10px.png", fap_category="Tools", fap_icon_assets="icons",