Bluetooth Remote to HID Remote (#2039)
* WIP BT + USB Hid * Refactoring Submenus/Views * Changed to bool instead of enum * Revamp finished * Removed usb_keyboard * Renaming device_types that can do USB+BT * Removed error view * Renaming folder structure and file names * Fixed views.h * Fixed hid.h * Fixed hid_mouse.c * Fixed a accidetnal renaming * Apps: add missing view remove call in hid app destructor * Hid app: hal abstraction, split into bluetooth and usb remotes. Fbt: customizable icon symbol name. * Hid app: update usb remote icon * Hid: single status change routine * HID App: final touches * HID App: rename BtHidTikTok to HidTikTok, format sources * HID App: fix comma in keyboard Co-authored-by: あく <alleteam@gmail.com>
| @ -1,10 +0,0 @@ | |||||||
| App( |  | ||||||
|     appid="bt_hid", |  | ||||||
|     name="Bluetooth Remote", |  | ||||||
|     apptype=FlipperAppType.EXTERNAL, |  | ||||||
|     entry_point="bt_hid_app", |  | ||||||
|     stack_size=1 * 1024, |  | ||||||
|     fap_category="Tools", |  | ||||||
|     fap_icon="bt_remote_10px.png", |  | ||||||
|     fap_icon_assets="assets", |  | ||||||
| ) |  | ||||||
| @ -1,216 +0,0 @@ | |||||||
| #include "bt_hid.h" |  | ||||||
| #include <furi_hal_bt.h> |  | ||||||
| #include <notification/notification_messages.h> |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| #define TAG "BtHidApp" |  | ||||||
| 
 |  | ||||||
| enum BtDebugSubmenuIndex { |  | ||||||
|     BtHidSubmenuIndexKeynote, |  | ||||||
|     BtHidSubmenuIndexKeyboard, |  | ||||||
|     BtHidSubmenuIndexMedia, |  | ||||||
|     BtHidSubmenuIndexTikTok, |  | ||||||
|     BtHidSubmenuIndexMouse, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void bt_hid_submenu_callback(void* context, uint32_t index) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     BtHid* app = context; |  | ||||||
|     if(index == BtHidSubmenuIndexKeynote) { |  | ||||||
|         app->view_id = BtHidViewKeynote; |  | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote); |  | ||||||
|     } else if(index == BtHidSubmenuIndexKeyboard) { |  | ||||||
|         app->view_id = BtHidViewKeyboard; |  | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeyboard); |  | ||||||
|     } else if(index == BtHidSubmenuIndexMedia) { |  | ||||||
|         app->view_id = BtHidViewMedia; |  | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMedia); |  | ||||||
|     } else if(index == BtHidSubmenuIndexMouse) { |  | ||||||
|         app->view_id = BtHidViewMouse; |  | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse); |  | ||||||
|     } else if(index == BtHidSubmenuIndexTikTok) { |  | ||||||
|         app->view_id = BtHidViewTikTok; |  | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void bt_hid_dialog_callback(DialogExResult result, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     BtHid* app = context; |  | ||||||
|     if(result == DialogExResultLeft) { |  | ||||||
|         view_dispatcher_stop(app->view_dispatcher); |  | ||||||
|     } else if(result == DialogExResultRight) { |  | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view
 |  | ||||||
|     } else if(result == DialogExResultCenter) { |  | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewSubmenu); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint32_t bt_hid_exit_confirm_view(void* context) { |  | ||||||
|     UNUSED(context); |  | ||||||
|     return BtHidViewExitConfirm; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint32_t bt_hid_exit(void* context) { |  | ||||||
|     UNUSED(context); |  | ||||||
|     return VIEW_NONE; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     BtHid* bt_hid = context; |  | ||||||
|     bool connected = (status == BtStatusConnected); |  | ||||||
|     if(connected) { |  | ||||||
|         notification_internal_message(bt_hid->notifications, &sequence_set_blue_255); |  | ||||||
|     } else { |  | ||||||
|         notification_internal_message(bt_hid->notifications, &sequence_reset_blue); |  | ||||||
|     } |  | ||||||
|     bt_hid_keynote_set_connected_status(bt_hid->bt_hid_keynote, connected); |  | ||||||
|     bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected); |  | ||||||
|     bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected); |  | ||||||
|     bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected); |  | ||||||
|     bt_hid_tiktok_set_connected_status(bt_hid->bt_hid_tiktok, connected); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| BtHid* bt_hid_app_alloc() { |  | ||||||
|     BtHid* app = malloc(sizeof(BtHid)); |  | ||||||
| 
 |  | ||||||
|     // Gui
 |  | ||||||
|     app->gui = furi_record_open(RECORD_GUI); |  | ||||||
| 
 |  | ||||||
|     // Bt
 |  | ||||||
|     app->bt = furi_record_open(RECORD_BT); |  | ||||||
| 
 |  | ||||||
|     // Notifications
 |  | ||||||
|     app->notifications = furi_record_open(RECORD_NOTIFICATION); |  | ||||||
| 
 |  | ||||||
|     // View dispatcher
 |  | ||||||
|     app->view_dispatcher = view_dispatcher_alloc(); |  | ||||||
|     view_dispatcher_enable_queue(app->view_dispatcher); |  | ||||||
|     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); |  | ||||||
| 
 |  | ||||||
|     // Submenu view
 |  | ||||||
|     app->submenu = submenu_alloc(); |  | ||||||
|     submenu_add_item( |  | ||||||
|         app->submenu, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app); |  | ||||||
|     submenu_add_item( |  | ||||||
|         app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app); |  | ||||||
|     submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); |  | ||||||
|     submenu_add_item( |  | ||||||
|         app->submenu, "TikTok Controller", BtHidSubmenuIndexTikTok, bt_hid_submenu_callback, app); |  | ||||||
|     submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app); |  | ||||||
|     view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         app->view_dispatcher, BtHidViewSubmenu, submenu_get_view(app->submenu)); |  | ||||||
| 
 |  | ||||||
|     // Dialog view
 |  | ||||||
|     app->dialog = dialog_ex_alloc(); |  | ||||||
|     dialog_ex_set_result_callback(app->dialog, bt_hid_dialog_callback); |  | ||||||
|     dialog_ex_set_context(app->dialog, app); |  | ||||||
|     dialog_ex_set_left_button_text(app->dialog, "Exit"); |  | ||||||
|     dialog_ex_set_right_button_text(app->dialog, "Stay"); |  | ||||||
|     dialog_ex_set_center_button_text(app->dialog, "Menu"); |  | ||||||
|     dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         app->view_dispatcher, BtHidViewExitConfirm, dialog_ex_get_view(app->dialog)); |  | ||||||
| 
 |  | ||||||
|     // Keynote view
 |  | ||||||
|     app->bt_hid_keynote = bt_hid_keynote_alloc(); |  | ||||||
|     view_set_previous_callback( |  | ||||||
|         bt_hid_keynote_get_view(app->bt_hid_keynote), bt_hid_exit_confirm_view); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         app->view_dispatcher, BtHidViewKeynote, bt_hid_keynote_get_view(app->bt_hid_keynote)); |  | ||||||
| 
 |  | ||||||
|     // Keyboard view
 |  | ||||||
|     app->bt_hid_keyboard = bt_hid_keyboard_alloc(); |  | ||||||
|     view_set_previous_callback( |  | ||||||
|         bt_hid_keyboard_get_view(app->bt_hid_keyboard), bt_hid_exit_confirm_view); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         app->view_dispatcher, BtHidViewKeyboard, bt_hid_keyboard_get_view(app->bt_hid_keyboard)); |  | ||||||
| 
 |  | ||||||
|     // Media view
 |  | ||||||
|     app->bt_hid_media = bt_hid_media_alloc(); |  | ||||||
|     view_set_previous_callback(bt_hid_media_get_view(app->bt_hid_media), bt_hid_exit_confirm_view); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media)); |  | ||||||
| 
 |  | ||||||
|     // TikTok view
 |  | ||||||
|     app->bt_hid_tiktok = bt_hid_tiktok_alloc(); |  | ||||||
|     view_set_previous_callback( |  | ||||||
|         bt_hid_tiktok_get_view(app->bt_hid_tiktok), bt_hid_exit_confirm_view); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         app->view_dispatcher, BtHidViewTikTok, bt_hid_tiktok_get_view(app->bt_hid_tiktok)); |  | ||||||
| 
 |  | ||||||
|     // Mouse view
 |  | ||||||
|     app->bt_hid_mouse = bt_hid_mouse_alloc(); |  | ||||||
|     view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         app->view_dispatcher, BtHidViewMouse, bt_hid_mouse_get_view(app->bt_hid_mouse)); |  | ||||||
| 
 |  | ||||||
|     // TODO switch to menu after Media is done
 |  | ||||||
|     app->view_id = BtHidViewSubmenu; |  | ||||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); |  | ||||||
| 
 |  | ||||||
|     return app; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void bt_hid_app_free(BtHid* app) { |  | ||||||
|     furi_assert(app); |  | ||||||
| 
 |  | ||||||
|     // Reset notification
 |  | ||||||
|     notification_internal_message(app->notifications, &sequence_reset_blue); |  | ||||||
| 
 |  | ||||||
|     // Free views
 |  | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewSubmenu); |  | ||||||
|     submenu_free(app->submenu); |  | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewExitConfirm); |  | ||||||
|     dialog_ex_free(app->dialog); |  | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeynote); |  | ||||||
|     bt_hid_keynote_free(app->bt_hid_keynote); |  | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeyboard); |  | ||||||
|     bt_hid_keyboard_free(app->bt_hid_keyboard); |  | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMedia); |  | ||||||
|     bt_hid_media_free(app->bt_hid_media); |  | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse); |  | ||||||
|     bt_hid_mouse_free(app->bt_hid_mouse); |  | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); |  | ||||||
|     bt_hid_tiktok_free(app->bt_hid_tiktok); |  | ||||||
|     view_dispatcher_free(app->view_dispatcher); |  | ||||||
| 
 |  | ||||||
|     // Close records
 |  | ||||||
|     furi_record_close(RECORD_GUI); |  | ||||||
|     app->gui = NULL; |  | ||||||
|     furi_record_close(RECORD_NOTIFICATION); |  | ||||||
|     app->notifications = NULL; |  | ||||||
|     furi_record_close(RECORD_BT); |  | ||||||
|     app->bt = NULL; |  | ||||||
| 
 |  | ||||||
|     // Free rest
 |  | ||||||
|     free(app); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int32_t bt_hid_app(void* p) { |  | ||||||
|     UNUSED(p); |  | ||||||
|     // Switch profile to Hid
 |  | ||||||
|     BtHid* app = bt_hid_app_alloc(); |  | ||||||
|     bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); |  | ||||||
|     // Change profile
 |  | ||||||
|     if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { |  | ||||||
|         FURI_LOG_E(TAG, "Failed to switch profile"); |  | ||||||
|         bt_hid_app_free(app); |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
|     furi_hal_bt_start_advertising(); |  | ||||||
| 
 |  | ||||||
|     DOLPHIN_DEED(DolphinDeedPluginStart); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_run(app->view_dispatcher); |  | ||||||
| 
 |  | ||||||
|     bt_set_status_changed_callback(app->bt, NULL, NULL); |  | ||||||
|     // Change back profile to Serial
 |  | ||||||
|     bt_set_profile(app->bt, BtProfileSerial); |  | ||||||
| 
 |  | ||||||
|     bt_hid_app_free(app); |  | ||||||
| 
 |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| @ -1,41 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <furi.h> |  | ||||||
| #include <bt/bt_service/bt.h> |  | ||||||
| #include <gui/gui.h> |  | ||||||
| #include <gui/view.h> |  | ||||||
| #include <gui/view_dispatcher.h> |  | ||||||
| #include <notification/notification.h> |  | ||||||
| 
 |  | ||||||
| #include <gui/modules/submenu.h> |  | ||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| #include "views/bt_hid_keynote.h" |  | ||||||
| #include "views/bt_hid_keyboard.h" |  | ||||||
| #include "views/bt_hid_media.h" |  | ||||||
| #include "views/bt_hid_mouse.h" |  | ||||||
| #include "views/bt_hid_tiktok.h" |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     Bt* bt; |  | ||||||
|     Gui* gui; |  | ||||||
|     NotificationApp* notifications; |  | ||||||
|     ViewDispatcher* view_dispatcher; |  | ||||||
|     Submenu* submenu; |  | ||||||
|     DialogEx* dialog; |  | ||||||
|     BtHidKeynote* bt_hid_keynote; |  | ||||||
|     BtHidKeyboard* bt_hid_keyboard; |  | ||||||
|     BtHidMedia* bt_hid_media; |  | ||||||
|     BtHidMouse* bt_hid_mouse; |  | ||||||
|     BtHidTikTok* bt_hid_tiktok; |  | ||||||
|     uint32_t view_id; |  | ||||||
| } BtHid; |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     BtHidViewSubmenu, |  | ||||||
|     BtHidViewKeynote, |  | ||||||
|     BtHidViewKeyboard, |  | ||||||
|     BtHidViewMedia, |  | ||||||
|     BtHidViewMouse, |  | ||||||
|     BtHidViewTikTok, |  | ||||||
|     BtHidViewExitConfirm, |  | ||||||
| } BtHidView; |  | ||||||
| @ -1,13 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <gui/view.h> |  | ||||||
| 
 |  | ||||||
| typedef struct BtHidKeyboard BtHidKeyboard; |  | ||||||
| 
 |  | ||||||
| BtHidKeyboard* bt_hid_keyboard_alloc(); |  | ||||||
| 
 |  | ||||||
| void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard); |  | ||||||
| 
 |  | ||||||
| View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard); |  | ||||||
| 
 |  | ||||||
| void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected); |  | ||||||
| @ -1,13 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <gui/view.h> |  | ||||||
| 
 |  | ||||||
| typedef struct BtHidKeynote BtHidKeynote; |  | ||||||
| 
 |  | ||||||
| BtHidKeynote* bt_hid_keynote_alloc(); |  | ||||||
| 
 |  | ||||||
| void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote); |  | ||||||
| 
 |  | ||||||
| View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote); |  | ||||||
| 
 |  | ||||||
| void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected); |  | ||||||
| @ -1,13 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <gui/view.h> |  | ||||||
| 
 |  | ||||||
| typedef struct BtHidMedia BtHidMedia; |  | ||||||
| 
 |  | ||||||
| BtHidMedia* bt_hid_media_alloc(); |  | ||||||
| 
 |  | ||||||
| void bt_hid_media_free(BtHidMedia* bt_hid_media); |  | ||||||
| 
 |  | ||||||
| View* bt_hid_media_get_view(BtHidMedia* bt_hid_media); |  | ||||||
| 
 |  | ||||||
| void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected); |  | ||||||
| @ -1,13 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <gui/view.h> |  | ||||||
| 
 |  | ||||||
| typedef struct BtHidMouse BtHidMouse; |  | ||||||
| 
 |  | ||||||
| BtHidMouse* bt_hid_mouse_alloc(); |  | ||||||
| 
 |  | ||||||
| void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse); |  | ||||||
| 
 |  | ||||||
| View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse); |  | ||||||
| 
 |  | ||||||
| void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected); |  | ||||||
| @ -1,13 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <gui/view.h> |  | ||||||
| 
 |  | ||||||
| typedef struct BtHidTikTok BtHidTikTok; |  | ||||||
| 
 |  | ||||||
| BtHidTikTok* bt_hid_tiktok_alloc(); |  | ||||||
| 
 |  | ||||||
| void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok); |  | ||||||
| 
 |  | ||||||
| View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok); |  | ||||||
| 
 |  | ||||||
| void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected); |  | ||||||
							
								
								
									
										24
									
								
								applications/plugins/hid_app/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,24 @@ | |||||||
|  | App( | ||||||
|  |     appid="hid_usb", | ||||||
|  |     name="USB Remote", | ||||||
|  |     apptype=FlipperAppType.PLUGIN, | ||||||
|  |     entry_point="hid_usb_app", | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     fap_category="Tools", | ||||||
|  |     fap_icon="hid_usb_10px.png", | ||||||
|  |     fap_icon_assets="assets", | ||||||
|  |     fap_icon_assets_symbol="hid", | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="hid_ble", | ||||||
|  |     name="Bluetooth Remote", | ||||||
|  |     apptype=FlipperAppType.PLUGIN, | ||||||
|  |     entry_point="hid_ble_app", | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     fap_category="Tools", | ||||||
|  |     fap_icon="hid_ble_10px.png", | ||||||
|  |     fap_icon_assets="assets", | ||||||
|  |     fap_icon_assets_symbol="hid", | ||||||
|  | ) | ||||||
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 102 B | 
| Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB | 
| Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB | 
| Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 102 B | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										365
									
								
								applications/plugins/hid_app/hid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,365 @@ | |||||||
|  | #include "hid.h" | ||||||
|  | #include "views.h" | ||||||
|  | #include <notification/notification_messages.h> | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "HidApp" | ||||||
|  | 
 | ||||||
|  | enum HidDebugSubmenuIndex { | ||||||
|  |     HidSubmenuIndexKeynote, | ||||||
|  |     HidSubmenuIndexKeyboard, | ||||||
|  |     HidSubmenuIndexMedia, | ||||||
|  |     BtHidSubmenuIndexTikTok, | ||||||
|  |     HidSubmenuIndexMouse, | ||||||
|  | }; | ||||||
|  | typedef enum { ConnTypeSubmenuIndexBluetooth, ConnTypeSubmenuIndexUsb } ConnTypeDebugSubmenuIndex; | ||||||
|  | 
 | ||||||
|  | static void hid_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Hid* app = context; | ||||||
|  |     if(index == HidSubmenuIndexKeynote) { | ||||||
|  |         app->view_id = HidViewKeynote; | ||||||
|  |         view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); | ||||||
|  |     } else if(index == HidSubmenuIndexKeyboard) { | ||||||
|  |         app->view_id = HidViewKeyboard; | ||||||
|  |         view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard); | ||||||
|  |     } else if(index == HidSubmenuIndexMedia) { | ||||||
|  |         app->view_id = HidViewMedia; | ||||||
|  |         view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia); | ||||||
|  |     } else if(index == HidSubmenuIndexMouse) { | ||||||
|  |         app->view_id = HidViewMouse; | ||||||
|  |         view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse); | ||||||
|  |     } else if(index == BtHidSubmenuIndexTikTok) { | ||||||
|  |         app->view_id = BtHidViewTikTok; | ||||||
|  |         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Hid* hid = context; | ||||||
|  |     bool connected = (status == BtStatusConnected); | ||||||
|  |     if(connected) { | ||||||
|  |         notification_internal_message(hid->notifications, &sequence_set_blue_255); | ||||||
|  |     } else { | ||||||
|  |         notification_internal_message(hid->notifications, &sequence_reset_blue); | ||||||
|  |     } | ||||||
|  |     hid_keynote_set_connected_status(hid->hid_keynote, connected); | ||||||
|  |     hid_keyboard_set_connected_status(hid->hid_keyboard, connected); | ||||||
|  |     hid_media_set_connected_status(hid->hid_media, connected); | ||||||
|  |     hid_mouse_set_connected_status(hid->hid_mouse, connected); | ||||||
|  |     hid_tiktok_set_connected_status(hid->hid_tiktok, connected); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_dialog_callback(DialogExResult result, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Hid* app = context; | ||||||
|  |     if(result == DialogExResultLeft) { | ||||||
|  |         view_dispatcher_stop(app->view_dispatcher); | ||||||
|  |     } else if(result == DialogExResultRight) { | ||||||
|  |         view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view
 | ||||||
|  |     } else if(result == DialogExResultCenter) { | ||||||
|  |         view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint32_t hid_exit_confirm_view(void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     return HidViewExitConfirm; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint32_t hid_exit(void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     return VIEW_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Hid* hid_alloc(HidTransport transport) { | ||||||
|  |     Hid* app = malloc(sizeof(Hid)); | ||||||
|  |     app->transport = transport; | ||||||
|  | 
 | ||||||
|  |     // Gui
 | ||||||
|  |     app->gui = furi_record_open(RECORD_GUI); | ||||||
|  | 
 | ||||||
|  |     // Bt
 | ||||||
|  |     app->bt = furi_record_open(RECORD_BT); | ||||||
|  | 
 | ||||||
|  |     // Notifications
 | ||||||
|  |     app->notifications = furi_record_open(RECORD_NOTIFICATION); | ||||||
|  | 
 | ||||||
|  |     // View dispatcher
 | ||||||
|  |     app->view_dispatcher = view_dispatcher_alloc(); | ||||||
|  |     view_dispatcher_enable_queue(app->view_dispatcher); | ||||||
|  |     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||||
|  |     // Device Type Submenu view
 | ||||||
|  |     app->device_type_submenu = submenu_alloc(); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app); | ||||||
|  |     if(app->transport == HidTransportBle) { | ||||||
|  |         submenu_add_item( | ||||||
|  |             app->device_type_submenu, | ||||||
|  |             "TikTok Controller", | ||||||
|  |             BtHidSubmenuIndexTikTok, | ||||||
|  |             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)); | ||||||
|  |     app->view_id = HidViewSubmenu; | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); | ||||||
|  |     return app; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Hid* hid_app_alloc_view(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Hid* app = context; | ||||||
|  |     // Dialog view
 | ||||||
|  |     app->dialog = dialog_ex_alloc(); | ||||||
|  |     dialog_ex_set_result_callback(app->dialog, hid_dialog_callback); | ||||||
|  |     dialog_ex_set_context(app->dialog, app); | ||||||
|  |     dialog_ex_set_left_button_text(app->dialog, "Exit"); | ||||||
|  |     dialog_ex_set_right_button_text(app->dialog, "Stay"); | ||||||
|  |     dialog_ex_set_center_button_text(app->dialog, "Menu"); | ||||||
|  |     dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, HidViewExitConfirm, dialog_ex_get_view(app->dialog)); | ||||||
|  | 
 | ||||||
|  |     // Keynote view
 | ||||||
|  |     app->hid_keynote = hid_keynote_alloc(app); | ||||||
|  |     view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_exit_confirm_view); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote)); | ||||||
|  | 
 | ||||||
|  |     // Keyboard view
 | ||||||
|  |     app->hid_keyboard = hid_keyboard_alloc(app); | ||||||
|  |     view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard)); | ||||||
|  | 
 | ||||||
|  |     // Media view
 | ||||||
|  |     app->hid_media = hid_media_alloc(app); | ||||||
|  |     view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media)); | ||||||
|  | 
 | ||||||
|  |     // TikTok view
 | ||||||
|  |     app->hid_tiktok = hid_tiktok_alloc(app); | ||||||
|  |     view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_exit_confirm_view); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok)); | ||||||
|  | 
 | ||||||
|  |     // Mouse view
 | ||||||
|  |     app->hid_mouse = hid_mouse_alloc(app); | ||||||
|  |     view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); | ||||||
|  | 
 | ||||||
|  |     return app; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_free(Hid* app) { | ||||||
|  |     furi_assert(app); | ||||||
|  | 
 | ||||||
|  |     // Reset notification
 | ||||||
|  |     notification_internal_message(app->notifications, &sequence_reset_blue); | ||||||
|  | 
 | ||||||
|  |     // Free views
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu); | ||||||
|  |     submenu_free(app->device_type_submenu); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, HidViewExitConfirm); | ||||||
|  |     dialog_ex_free(app->dialog); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote); | ||||||
|  |     hid_keynote_free(app->hid_keynote); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard); | ||||||
|  |     hid_keyboard_free(app->hid_keyboard); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia); | ||||||
|  |     hid_media_free(app->hid_media); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); | ||||||
|  |     hid_mouse_free(app->hid_mouse); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); | ||||||
|  |     hid_tiktok_free(app->hid_tiktok); | ||||||
|  |     view_dispatcher_free(app->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     // Close records
 | ||||||
|  |     furi_record_close(RECORD_GUI); | ||||||
|  |     app->gui = NULL; | ||||||
|  |     furi_record_close(RECORD_NOTIFICATION); | ||||||
|  |     app->notifications = NULL; | ||||||
|  |     furi_record_close(RECORD_BT); | ||||||
|  |     app->bt = NULL; | ||||||
|  | 
 | ||||||
|  |     // Free rest
 | ||||||
|  |     free(app); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_keyboard_press(Hid* instance, uint16_t event) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_kb_press(event); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_kb_press(event); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_keyboard_release(Hid* instance, uint16_t event) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_kb_release(event); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_kb_release(event); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_keyboard_release_all(Hid* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_kb_release_all(); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_kb_release_all(); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_consumer_key_press(Hid* instance, uint16_t event) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_consumer_key_press(event); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_consumer_key_press(event); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_consumer_key_release(Hid* instance, uint16_t event) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_consumer_key_release(event); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_consumer_key_release(event); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_consumer_key_release_all(Hid* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_consumer_key_release_all(); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_kb_release_all(); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_mouse_move(dx, dy); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_mouse_move(dx, dy); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_mouse_scroll(Hid* instance, int8_t delta) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_mouse_scroll(delta); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_mouse_scroll(delta); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_mouse_press(Hid* instance, uint16_t event) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_mouse_press(event); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_mouse_press(event); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_mouse_release(Hid* instance, uint16_t event) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_mouse_release(event); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_mouse_release(event); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void hid_hal_mouse_release_all(Hid* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     if(instance->transport == HidTransportBle) { | ||||||
|  |         furi_hal_bt_hid_mouse_release_all(); | ||||||
|  |     } else if(instance->transport == HidTransportUsb) { | ||||||
|  |         furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); | ||||||
|  |         furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); | ||||||
|  |     } else { | ||||||
|  |         furi_crash(NULL); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t hid_usb_app(void* p) { | ||||||
|  |     UNUSED(p); | ||||||
|  |     Hid* app = hid_alloc(HidTransportUsb); | ||||||
|  |     app = hid_app_alloc_view(app); | ||||||
|  |     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); | ||||||
|  |     furi_hal_usb_unlock(); | ||||||
|  |     furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); | ||||||
|  | 
 | ||||||
|  |     bt_hid_connection_status_changed_callback(BtStatusConnected, app); | ||||||
|  | 
 | ||||||
|  |     DOLPHIN_DEED(DolphinDeedPluginStart); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_run(app->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     furi_hal_usb_set_config(usb_mode_prev, NULL); | ||||||
|  | 
 | ||||||
|  |     hid_free(app); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t hid_ble_app(void* p) { | ||||||
|  |     UNUSED(p); | ||||||
|  |     Hid* app = hid_alloc(HidTransportBle); | ||||||
|  |     app = hid_app_alloc_view(app); | ||||||
|  | 
 | ||||||
|  |     if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { | ||||||
|  |         FURI_LOG_E(TAG, "Failed to switch profile"); | ||||||
|  |     } | ||||||
|  |     furi_hal_bt_start_advertising(); | ||||||
|  |     bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); | ||||||
|  | 
 | ||||||
|  |     DOLPHIN_DEED(DolphinDeedPluginStart); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_run(app->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     bt_set_status_changed_callback(app->bt, NULL, NULL); | ||||||
|  |     bt_set_profile(app->bt, BtProfileSerial); | ||||||
|  | 
 | ||||||
|  |     hid_free(app); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								applications/plugins/hid_app/hid.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,60 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal_bt.h> | ||||||
|  | #include <furi_hal_bt_hid.h> | ||||||
|  | #include <furi_hal_usb.h> | ||||||
|  | #include <furi_hal_usb_hid.h> | ||||||
|  | 
 | ||||||
|  | #include <bt/bt_service/bt.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <notification/notification.h> | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | #include <gui/modules/popup.h> | ||||||
|  | #include "views/hid_keynote.h" | ||||||
|  | #include "views/hid_keyboard.h" | ||||||
|  | #include "views/hid_media.h" | ||||||
|  | #include "views/hid_mouse.h" | ||||||
|  | #include "views/hid_tiktok.h" | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     HidTransportUsb, | ||||||
|  |     HidTransportBle, | ||||||
|  | } HidTransport; | ||||||
|  | 
 | ||||||
|  | typedef struct Hid Hid; | ||||||
|  | 
 | ||||||
|  | struct Hid { | ||||||
|  |     Bt* bt; | ||||||
|  |     Gui* gui; | ||||||
|  |     NotificationApp* notifications; | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  |     Submenu* device_type_submenu; | ||||||
|  |     DialogEx* dialog; | ||||||
|  |     HidKeynote* hid_keynote; | ||||||
|  |     HidKeyboard* hid_keyboard; | ||||||
|  |     HidMedia* hid_media; | ||||||
|  |     HidMouse* hid_mouse; | ||||||
|  |     HidTikTok* hid_tiktok; | ||||||
|  | 
 | ||||||
|  |     HidTransport transport; | ||||||
|  |     uint32_t view_id; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void hid_hal_keyboard_press(Hid* instance, uint16_t event); | ||||||
|  | void hid_hal_keyboard_release(Hid* instance, uint16_t event); | ||||||
|  | void hid_hal_keyboard_release_all(Hid* instance); | ||||||
|  | 
 | ||||||
|  | void hid_hal_consumer_key_press(Hid* instance, uint16_t event); | ||||||
|  | void hid_hal_consumer_key_release(Hid* instance, uint16_t event); | ||||||
|  | void hid_hal_consumer_key_release_all(Hid* instance); | ||||||
|  | 
 | ||||||
|  | void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy); | ||||||
|  | void hid_hal_mouse_scroll(Hid* instance, int8_t delta); | ||||||
|  | void hid_hal_mouse_press(Hid* instance, uint16_t event); | ||||||
|  | void hid_hal_mouse_release(Hid* instance, uint16_t event); | ||||||
|  | void hid_hal_mouse_release_all(Hid* instance); | ||||||
| Before Width: | Height: | Size: 151 B After Width: | Height: | Size: 151 B | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/hid_app/hid_usb_10px.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 969 B | 
							
								
								
									
										9
									
								
								applications/plugins/hid_app/views.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,9 @@ | |||||||
|  | typedef enum { | ||||||
|  |     HidViewSubmenu, | ||||||
|  |     HidViewKeynote, | ||||||
|  |     HidViewKeyboard, | ||||||
|  |     HidViewMedia, | ||||||
|  |     HidViewMouse, | ||||||
|  |     BtHidViewTikTok, | ||||||
|  |     HidViewExitConfirm, | ||||||
|  | } HidView; | ||||||
| @ -1,14 +1,15 @@ | |||||||
| #include "bt_hid_keyboard.h" | #include "hid_keyboard.h" | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal_bt_hid.h> |  | ||||||
| #include <furi_hal_usb_hid.h> |  | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| #include <gui/icon_i.h> | #include <gui/icon_i.h> | ||||||
|  | #include "../hid.h" | ||||||
|  | #include "hid_icons.h" | ||||||
| 
 | 
 | ||||||
| #include "bt_hid_icons.h" | #define TAG "HidKeyboard" | ||||||
| 
 | 
 | ||||||
| struct BtHidKeyboard { | struct HidKeyboard { | ||||||
|     View* view; |     View* view; | ||||||
|  |     Hid* hid; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -24,7 +25,7 @@ typedef struct { | |||||||
|     bool back_pressed; |     bool back_pressed; | ||||||
|     bool connected; |     bool connected; | ||||||
|     char key_string[5]; |     char key_string[5]; | ||||||
| } BtHidKeyboardModel; | } HidKeyboardModel; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint8_t width; |     uint8_t width; | ||||||
| @ -32,13 +33,12 @@ typedef struct { | |||||||
|     const Icon* icon; |     const Icon* icon; | ||||||
|     char* shift_key; |     char* shift_key; | ||||||
|     uint8_t value; |     uint8_t value; | ||||||
| } BtHidKeyboardKey; | } HidKeyboardKey; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     int8_t x; |     int8_t x; | ||||||
|     int8_t y; |     int8_t y; | ||||||
| } BtHidKeyboardPoint; | } HidKeyboardPoint; | ||||||
| 
 |  | ||||||
| // 4 BY 12
 | // 4 BY 12
 | ||||||
| #define MARGIN_TOP 0 | #define MARGIN_TOP 0 | ||||||
| #define MARGIN_LEFT 4 | #define MARGIN_LEFT 4 | ||||||
| @ -49,7 +49,7 @@ typedef struct { | |||||||
| #define COLUMN_COUNT 12 | #define COLUMN_COUNT 12 | ||||||
| 
 | 
 | ||||||
| // 0 width items are not drawn, but there value is used
 | // 0 width items are not drawn, but there value is used
 | ||||||
| const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { | const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { | ||||||
|     { |     { | ||||||
|         {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, |         {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, | ||||||
|         {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, |         {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, | ||||||
| @ -112,7 +112,7 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, |         {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, | ||||||
|         {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA}, |         {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYBOARD_COMMA}, | ||||||
|         {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, |         {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, | ||||||
|         {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, |         {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, | ||||||
|         {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, |         {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, | ||||||
| @ -140,19 +140,19 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { | |||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void bt_hid_keyboard_to_upper(char* str) { | static void hid_keyboard_to_upper(char* str) { | ||||||
|     while(*str) { |     while(*str) { | ||||||
|         *str = toupper((unsigned char)*str); |         *str = toupper((unsigned char)*str); | ||||||
|         str++; |         str++; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_keyboard_draw_key( | static void hid_keyboard_draw_key( | ||||||
|     Canvas* canvas, |     Canvas* canvas, | ||||||
|     BtHidKeyboardModel* model, |     HidKeyboardModel* model, | ||||||
|     uint8_t x, |     uint8_t x, | ||||||
|     uint8_t y, |     uint8_t y, | ||||||
|     BtHidKeyboardKey key, |     HidKeyboardKey key, | ||||||
|     bool selected) { |     bool selected) { | ||||||
|     if(!key.width) return; |     if(!key.width) return; | ||||||
| 
 | 
 | ||||||
| @ -190,7 +190,7 @@ static void bt_hid_keyboard_draw_key( | |||||||
|         if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || |         if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || | ||||||
|            (model->alt && key.value == HID_KEYBOARD_L_ALT) || |            (model->alt && key.value == HID_KEYBOARD_L_ALT) || | ||||||
|            (model->gui && key.value == HID_KEYBOARD_L_GUI)) { |            (model->gui && key.value == HID_KEYBOARD_L_GUI)) { | ||||||
|             bt_hid_keyboard_to_upper(model->key_string); |             hid_keyboard_to_upper(model->key_string); | ||||||
|         } |         } | ||||||
|         canvas_draw_str_aligned( |         canvas_draw_str_aligned( | ||||||
|             canvas, |             canvas, | ||||||
| @ -202,9 +202,9 @@ static void bt_hid_keyboard_draw_key( | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { | static void hid_keyboard_draw_callback(Canvas* canvas, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHidKeyboardModel* model = context; |     HidKeyboardModel* model = context; | ||||||
| 
 | 
 | ||||||
|     // Header
 |     // Header
 | ||||||
|     if(!model->connected) { |     if(!model->connected) { | ||||||
| @ -225,17 +225,17 @@ static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { | |||||||
|     // Start shifting the all keys up if on the next row (Scrolling)
 |     // Start shifting the all keys up if on the next row (Scrolling)
 | ||||||
|     uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0; |     uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0; | ||||||
|     for(uint8_t y = initY; y < ROW_COUNT; y++) { |     for(uint8_t y = initY; y < ROW_COUNT; y++) { | ||||||
|         const BtHidKeyboardKey* keyboardKeyRow = bt_hid_keyboard_keyset[y]; |         const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y]; | ||||||
|         uint8_t x = 0; |         uint8_t x = 0; | ||||||
|         for(uint8_t i = 0; i < COLUMN_COUNT; i++) { |         for(uint8_t i = 0; i < COLUMN_COUNT; i++) { | ||||||
|             BtHidKeyboardKey key = keyboardKeyRow[i]; |             HidKeyboardKey key = keyboardKeyRow[i]; | ||||||
|             // Select when the button is hovered
 |             // Select when the button is hovered
 | ||||||
|             // Select if the button is hovered within its width
 |             // Select if the button is hovered within its width
 | ||||||
|             // Select if back is clicked and its the backspace key
 |             // Select if back is clicked and its the backspace key
 | ||||||
|             // Deselect when the button clicked or not hovered
 |             // Deselect when the button clicked or not hovered
 | ||||||
|             bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; |             bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; | ||||||
|             bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; |             bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; | ||||||
|             bt_hid_keyboard_draw_key( |             hid_keyboard_draw_key( | ||||||
|                 canvas, |                 canvas, | ||||||
|                 model, |                 model, | ||||||
|                 x, |                 x, | ||||||
| @ -247,8 +247,8 @@ static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) { | static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) { | ||||||
|     BtHidKeyboardKey key = bt_hid_keyboard_keyset[model->y][model->x]; |     HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x]; | ||||||
|     // Use upper case if shift is toggled
 |     // Use upper case if shift is toggled
 | ||||||
|     bool useUppercase = model->shift; |     bool useUppercase = model->shift; | ||||||
|     // Check if the key has an upper case version
 |     // Check if the key has an upper case version
 | ||||||
| @ -259,34 +259,34 @@ static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) { | |||||||
|         return key.value; |         return key.value; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_keyboard_get_select_key(BtHidKeyboardModel* model, BtHidKeyboardPoint delta) { | static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) { | ||||||
|     // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map
 |     // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map
 | ||||||
|     do { |     do { | ||||||
|         if(((int8_t)model->y) + delta.y < 0) |         if(((int8_t)model->y) + delta.y < 0) | ||||||
|             model->y = ROW_COUNT - 1; |             model->y = ROW_COUNT - 1; | ||||||
|         else |         else | ||||||
|             model->y = (model->y + delta.y) % ROW_COUNT; |             model->y = (model->y + delta.y) % ROW_COUNT; | ||||||
|     } while(delta.y != 0 && bt_hid_keyboard_keyset[model->y][model->x].value == 0); |     } while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0); | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         if(((int8_t)model->x) + delta.x < 0) |         if(((int8_t)model->x) + delta.x < 0) | ||||||
|             model->x = COLUMN_COUNT - 1; |             model->x = COLUMN_COUNT - 1; | ||||||
|         else |         else | ||||||
|             model->x = (model->x + delta.x) % COLUMN_COUNT; |             model->x = (model->x + delta.x) % COLUMN_COUNT; | ||||||
|     } while(delta.x != 0 && bt_hid_keyboard_keyset[model->y][model->x].width == |     } while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width == | ||||||
|                                 0); // Skip zero width keys, pretend they are one key
 |                                 0); // Skip zero width keys, pretend they are one key
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* event) { | static void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_keyboard->view, |         hid_keyboard->view, | ||||||
|         BtHidKeyboardModel * model, |         HidKeyboardModel * model, | ||||||
|         { |         { | ||||||
|             if(event->key == InputKeyOk) { |             if(event->key == InputKeyOk) { | ||||||
|                 if(event->type == InputTypePress) { |                 if(event->type == InputTypePress) { | ||||||
|                     model->ok_pressed = true; |                     model->ok_pressed = true; | ||||||
|                 } else if(event->type == InputTypeLong || event->type == InputTypeShort) { |                 } else if(event->type == InputTypeLong || event->type == InputTypeShort) { | ||||||
|                     model->last_key_code = bt_hid_keyboard_get_selected_key(model); |                     model->last_key_code = hid_keyboard_get_selected_key(model); | ||||||
| 
 | 
 | ||||||
|                     // Toggle the modifier key when clicked, and click the key
 |                     // Toggle the modifier key when clicked, and click the key
 | ||||||
|                     if(model->last_key_code == HID_KEYBOARD_L_SHIFT) { |                     if(model->last_key_code == HID_KEYBOARD_L_SHIFT) { | ||||||
| @ -314,10 +314,12 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* | |||||||
|                         else |                         else | ||||||
|                             model->modifier_code &= ~KEY_MOD_LEFT_GUI; |                             model->modifier_code &= ~KEY_MOD_LEFT_GUI; | ||||||
|                     } |                     } | ||||||
|                     furi_hal_bt_hid_kb_press(model->modifier_code | model->last_key_code); |                     hid_hal_keyboard_press( | ||||||
|  |                         hid_keyboard->hid, model->modifier_code | model->last_key_code); | ||||||
|                 } else if(event->type == InputTypeRelease) { |                 } else if(event->type == InputTypeRelease) { | ||||||
|                     // Release happens after short and long presses
 |                     // Release happens after short and long presses
 | ||||||
|                     furi_hal_bt_hid_kb_release(model->modifier_code | model->last_key_code); |                     hid_hal_keyboard_release( | ||||||
|  |                         hid_keyboard->hid, model->modifier_code | model->last_key_code); | ||||||
|                     model->ok_pressed = false; |                     model->ok_pressed = false; | ||||||
|                 } |                 } | ||||||
|             } else if(event->key == InputKeyBack) { |             } else if(event->key == InputKeyBack) { | ||||||
| @ -325,66 +327,67 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* | |||||||
|                 if(event->type == InputTypePress) { |                 if(event->type == InputTypePress) { | ||||||
|                     model->back_pressed = true; |                     model->back_pressed = true; | ||||||
|                 } else if(event->type == InputTypeShort) { |                 } else if(event->type == InputTypeShort) { | ||||||
|                     furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE); |                     hid_hal_keyboard_press(hid_keyboard->hid, HID_KEYBOARD_DELETE); | ||||||
|                     furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); |                     hid_hal_keyboard_release(hid_keyboard->hid, HID_KEYBOARD_DELETE); | ||||||
|                 } else if(event->type == InputTypeRelease) { |                 } else if(event->type == InputTypeRelease) { | ||||||
|                     model->back_pressed = false; |                     model->back_pressed = false; | ||||||
|                 } |                 } | ||||||
|             } else if(event->type == InputTypePress || event->type == InputTypeRepeat) { |             } else if(event->type == InputTypePress || event->type == InputTypeRepeat) { | ||||||
|                 // Cycle the selected keys
 |                 // Cycle the selected keys
 | ||||||
|                 if(event->key == InputKeyUp) { |                 if(event->key == InputKeyUp) { | ||||||
|                     bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = -1}); |                     hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = -1}); | ||||||
|                 } else if(event->key == InputKeyDown) { |                 } else if(event->key == InputKeyDown) { | ||||||
|                     bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = 1}); |                     hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = 1}); | ||||||
|                 } else if(event->key == InputKeyLeft) { |                 } else if(event->key == InputKeyLeft) { | ||||||
|                     bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = -1, .y = 0}); |                     hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = -1, .y = 0}); | ||||||
|                 } else if(event->key == InputKeyRight) { |                 } else if(event->key == InputKeyRight) { | ||||||
|                     bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 1, .y = 0}); |                     hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 1, .y = 0}); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         true); |         true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool bt_hid_keyboard_input_callback(InputEvent* event, void* context) { | static bool hid_keyboard_input_callback(InputEvent* event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHidKeyboard* bt_hid_keyboard = context; |     HidKeyboard* hid_keyboard = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == InputTypeLong && event->key == InputKeyBack) { |     if(event->type == InputTypeLong && event->key == InputKeyBack) { | ||||||
|         furi_hal_bt_hid_kb_release_all(); |         hid_hal_keyboard_release_all(hid_keyboard->hid); | ||||||
|     } else { |     } else { | ||||||
|         bt_hid_keyboard_process(bt_hid_keyboard, event); |         hid_keyboard_process(hid_keyboard, event); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BtHidKeyboard* bt_hid_keyboard_alloc() { | HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) { | ||||||
|     BtHidKeyboard* bt_hid_keyboard = malloc(sizeof(BtHidKeyboard)); |     HidKeyboard* hid_keyboard = malloc(sizeof(HidKeyboard)); | ||||||
|     bt_hid_keyboard->view = view_alloc(); |     hid_keyboard->view = view_alloc(); | ||||||
|     view_set_context(bt_hid_keyboard->view, bt_hid_keyboard); |     hid_keyboard->hid = bt_hid; | ||||||
|     view_allocate_model(bt_hid_keyboard->view, ViewModelTypeLocking, sizeof(BtHidKeyboardModel)); |     view_set_context(hid_keyboard->view, hid_keyboard); | ||||||
|     view_set_draw_callback(bt_hid_keyboard->view, bt_hid_keyboard_draw_callback); |     view_allocate_model(hid_keyboard->view, ViewModelTypeLocking, sizeof(HidKeyboardModel)); | ||||||
|     view_set_input_callback(bt_hid_keyboard->view, bt_hid_keyboard_input_callback); |     view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback); | ||||||
|  |     view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback); | ||||||
| 
 | 
 | ||||||
|     return bt_hid_keyboard; |     return hid_keyboard; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard) { | void hid_keyboard_free(HidKeyboard* hid_keyboard) { | ||||||
|     furi_assert(bt_hid_keyboard); |     furi_assert(hid_keyboard); | ||||||
|     view_free(bt_hid_keyboard->view); |     view_free(hid_keyboard->view); | ||||||
|     free(bt_hid_keyboard); |     free(hid_keyboard); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard) { | View* hid_keyboard_get_view(HidKeyboard* hid_keyboard) { | ||||||
|     furi_assert(bt_hid_keyboard); |     furi_assert(hid_keyboard); | ||||||
|     return bt_hid_keyboard->view; |     return hid_keyboard->view; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected) { | void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected) { | ||||||
|     furi_assert(bt_hid_keyboard); |     furi_assert(hid_keyboard); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_keyboard->view, BtHidKeyboardModel * model, { model->connected = connected; }, true); |         hid_keyboard->view, HidKeyboardModel * model, { model->connected = connected; }, true); | ||||||
| } | } | ||||||
							
								
								
									
										14
									
								
								applications/plugins/hid_app/views/hid_keyboard.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,14 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | typedef struct Hid Hid; | ||||||
|  | typedef struct HidKeyboard HidKeyboard; | ||||||
|  | 
 | ||||||
|  | HidKeyboard* hid_keyboard_alloc(Hid* bt_hid); | ||||||
|  | 
 | ||||||
|  | void hid_keyboard_free(HidKeyboard* hid_keyboard); | ||||||
|  | 
 | ||||||
|  | View* hid_keyboard_get_view(HidKeyboard* hid_keyboard); | ||||||
|  | 
 | ||||||
|  | void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected); | ||||||
| @ -1,13 +1,14 @@ | |||||||
| #include "bt_hid_keynote.h" | #include "hid_keynote.h" | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal_bt_hid.h> |  | ||||||
| #include <furi_hal_usb_hid.h> |  | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
|  | #include "../hid.h" | ||||||
| 
 | 
 | ||||||
| #include "bt_hid_icons.h" | #include "hid_icons.h" | ||||||
| 
 | 
 | ||||||
| struct BtHidKeynote { | #define TAG "HidKeynote" | ||||||
|  | 
 | ||||||
|  | struct HidKeynote { | ||||||
|     View* view; |     View* view; | ||||||
|  |     Hid* hid; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -18,9 +19,9 @@ typedef struct { | |||||||
|     bool ok_pressed; |     bool ok_pressed; | ||||||
|     bool back_pressed; |     bool back_pressed; | ||||||
|     bool connected; |     bool connected; | ||||||
| } BtHidKeynoteModel; | } HidKeynoteModel; | ||||||
| 
 | 
 | ||||||
| static void bt_hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { | static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { | ||||||
|     canvas_draw_triangle(canvas, x, y, 5, 3, dir); |     canvas_draw_triangle(canvas, x, y, 5, 3, dir); | ||||||
|     if(dir == CanvasDirectionBottomToTop) { |     if(dir == CanvasDirectionBottomToTop) { | ||||||
|         canvas_draw_line(canvas, x, y + 6, x, y - 1); |         canvas_draw_line(canvas, x, y + 6, x, y - 1); | ||||||
| @ -33,9 +34,9 @@ static void bt_hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, Canv | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { | static void hid_keynote_draw_callback(Canvas* canvas, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHidKeynoteModel* model = context; |     HidKeynoteModel* model = context; | ||||||
| 
 | 
 | ||||||
|     // Header
 |     // Header
 | ||||||
|     if(model->connected) { |     if(model->connected) { | ||||||
| @ -56,7 +57,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { | |||||||
|         elements_slightly_rounded_box(canvas, 24, 26, 13, 13); |         elements_slightly_rounded_box(canvas, 24, 26, 13, 13); | ||||||
|         canvas_set_color(canvas, ColorWhite); |         canvas_set_color(canvas, ColorWhite); | ||||||
|     } |     } | ||||||
|     bt_hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); |     hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     // Down
 |     // Down
 | ||||||
| @ -65,7 +66,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { | |||||||
|         elements_slightly_rounded_box(canvas, 24, 47, 13, 13); |         elements_slightly_rounded_box(canvas, 24, 47, 13, 13); | ||||||
|         canvas_set_color(canvas, ColorWhite); |         canvas_set_color(canvas, ColorWhite); | ||||||
|     } |     } | ||||||
|     bt_hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); |     hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     // Left
 |     // Left
 | ||||||
| @ -74,7 +75,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { | |||||||
|         elements_slightly_rounded_box(canvas, 3, 47, 13, 13); |         elements_slightly_rounded_box(canvas, 3, 47, 13, 13); | ||||||
|         canvas_set_color(canvas, ColorWhite); |         canvas_set_color(canvas, ColorWhite); | ||||||
|     } |     } | ||||||
|     bt_hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); |     hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     // Right
 |     // Right
 | ||||||
| @ -83,7 +84,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { | |||||||
|         elements_slightly_rounded_box(canvas, 45, 47, 13, 13); |         elements_slightly_rounded_box(canvas, 45, 47, 13, 13); | ||||||
|         canvas_set_color(canvas, ColorWhite); |         canvas_set_color(canvas, ColorWhite); | ||||||
|     } |     } | ||||||
|     bt_hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); |     hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     // Ok
 |     // Ok
 | ||||||
| @ -106,100 +107,101 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { | |||||||
|     elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); |     elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) { | static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_keynote->view, |         hid_keynote->view, | ||||||
|         BtHidKeynoteModel * model, |         HidKeynoteModel * model, | ||||||
|         { |         { | ||||||
|             if(event->type == InputTypePress) { |             if(event->type == InputTypePress) { | ||||||
|                 if(event->key == InputKeyUp) { |                 if(event->key == InputKeyUp) { | ||||||
|                     model->up_pressed = true; |                     model->up_pressed = true; | ||||||
|                     furi_hal_bt_hid_kb_press(HID_KEYBOARD_UP_ARROW); |                     hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); | ||||||
|                 } else if(event->key == InputKeyDown) { |                 } else if(event->key == InputKeyDown) { | ||||||
|                     model->down_pressed = true; |                     model->down_pressed = true; | ||||||
|                     furi_hal_bt_hid_kb_press(HID_KEYBOARD_DOWN_ARROW); |                     hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); | ||||||
|                 } else if(event->key == InputKeyLeft) { |                 } else if(event->key == InputKeyLeft) { | ||||||
|                     model->left_pressed = true; |                     model->left_pressed = true; | ||||||
|                     furi_hal_bt_hid_kb_press(HID_KEYBOARD_LEFT_ARROW); |                     hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); | ||||||
|                 } else if(event->key == InputKeyRight) { |                 } else if(event->key == InputKeyRight) { | ||||||
|                     model->right_pressed = true; |                     model->right_pressed = true; | ||||||
|                     furi_hal_bt_hid_kb_press(HID_KEYBOARD_RIGHT_ARROW); |                     hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); | ||||||
|                 } else if(event->key == InputKeyOk) { |                 } else if(event->key == InputKeyOk) { | ||||||
|                     model->ok_pressed = true; |                     model->ok_pressed = true; | ||||||
|                     furi_hal_bt_hid_kb_press(HID_KEYBOARD_SPACEBAR); |                     hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); | ||||||
|                 } else if(event->key == InputKeyBack) { |                 } else if(event->key == InputKeyBack) { | ||||||
|                     model->back_pressed = true; |                     model->back_pressed = true; | ||||||
|                 } |                 } | ||||||
|             } else if(event->type == InputTypeRelease) { |             } else if(event->type == InputTypeRelease) { | ||||||
|                 if(event->key == InputKeyUp) { |                 if(event->key == InputKeyUp) { | ||||||
|                     model->up_pressed = false; |                     model->up_pressed = false; | ||||||
|                     furi_hal_bt_hid_kb_release(HID_KEYBOARD_UP_ARROW); |                     hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); | ||||||
|                 } else if(event->key == InputKeyDown) { |                 } else if(event->key == InputKeyDown) { | ||||||
|                     model->down_pressed = false; |                     model->down_pressed = false; | ||||||
|                     furi_hal_bt_hid_kb_release(HID_KEYBOARD_DOWN_ARROW); |                     hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); | ||||||
|                 } else if(event->key == InputKeyLeft) { |                 } else if(event->key == InputKeyLeft) { | ||||||
|                     model->left_pressed = false; |                     model->left_pressed = false; | ||||||
|                     furi_hal_bt_hid_kb_release(HID_KEYBOARD_LEFT_ARROW); |                     hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); | ||||||
|                 } else if(event->key == InputKeyRight) { |                 } else if(event->key == InputKeyRight) { | ||||||
|                     model->right_pressed = false; |                     model->right_pressed = false; | ||||||
|                     furi_hal_bt_hid_kb_release(HID_KEYBOARD_RIGHT_ARROW); |                     hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); | ||||||
|                 } else if(event->key == InputKeyOk) { |                 } else if(event->key == InputKeyOk) { | ||||||
|                     model->ok_pressed = false; |                     model->ok_pressed = false; | ||||||
|                     furi_hal_bt_hid_kb_release(HID_KEYBOARD_SPACEBAR); |                     hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); | ||||||
|                 } else if(event->key == InputKeyBack) { |                 } else if(event->key == InputKeyBack) { | ||||||
|                     model->back_pressed = false; |                     model->back_pressed = false; | ||||||
|                 } |                 } | ||||||
|             } else if(event->type == InputTypeShort) { |             } else if(event->type == InputTypeShort) { | ||||||
|                 if(event->key == InputKeyBack) { |                 if(event->key == InputKeyBack) { | ||||||
|                     furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE); |                     hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DELETE); | ||||||
|                     furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); |                     hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DELETE); | ||||||
|                     furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_AC_BACK); |                     hid_hal_consumer_key_press(hid_keynote->hid, HID_CONSUMER_AC_BACK); | ||||||
|                     furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_AC_BACK); |                     hid_hal_consumer_key_release(hid_keynote->hid, HID_CONSUMER_AC_BACK); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         true); |         true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool bt_hid_keynote_input_callback(InputEvent* event, void* context) { | static bool hid_keynote_input_callback(InputEvent* event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHidKeynote* bt_hid_keynote = context; |     HidKeynote* hid_keynote = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == InputTypeLong && event->key == InputKeyBack) { |     if(event->type == InputTypeLong && event->key == InputKeyBack) { | ||||||
|         furi_hal_bt_hid_kb_release_all(); |         hid_hal_keyboard_release_all(hid_keynote->hid); | ||||||
|     } else { |     } else { | ||||||
|         bt_hid_keynote_process(bt_hid_keynote, event); |         hid_keynote_process(hid_keynote, event); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BtHidKeynote* bt_hid_keynote_alloc() { | HidKeynote* hid_keynote_alloc(Hid* hid) { | ||||||
|     BtHidKeynote* bt_hid_keynote = malloc(sizeof(BtHidKeynote)); |     HidKeynote* hid_keynote = malloc(sizeof(HidKeynote)); | ||||||
|     bt_hid_keynote->view = view_alloc(); |     hid_keynote->view = view_alloc(); | ||||||
|     view_set_context(bt_hid_keynote->view, bt_hid_keynote); |     hid_keynote->hid = hid; | ||||||
|     view_allocate_model(bt_hid_keynote->view, ViewModelTypeLocking, sizeof(BtHidKeynoteModel)); |     view_set_context(hid_keynote->view, hid_keynote); | ||||||
|     view_set_draw_callback(bt_hid_keynote->view, bt_hid_keynote_draw_callback); |     view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel)); | ||||||
|     view_set_input_callback(bt_hid_keynote->view, bt_hid_keynote_input_callback); |     view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); | ||||||
|  |     view_set_input_callback(hid_keynote->view, hid_keynote_input_callback); | ||||||
| 
 | 
 | ||||||
|     return bt_hid_keynote; |     return hid_keynote; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote) { | void hid_keynote_free(HidKeynote* hid_keynote) { | ||||||
|     furi_assert(bt_hid_keynote); |     furi_assert(hid_keynote); | ||||||
|     view_free(bt_hid_keynote->view); |     view_free(hid_keynote->view); | ||||||
|     free(bt_hid_keynote); |     free(hid_keynote); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote) { | View* hid_keynote_get_view(HidKeynote* hid_keynote) { | ||||||
|     furi_assert(bt_hid_keynote); |     furi_assert(hid_keynote); | ||||||
|     return bt_hid_keynote->view; |     return hid_keynote->view; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected) { | void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) { | ||||||
|     furi_assert(bt_hid_keynote); |     furi_assert(hid_keynote); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_keynote->view, BtHidKeynoteModel * model, { model->connected = connected; }, true); |         hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true); | ||||||
| } | } | ||||||
							
								
								
									
										14
									
								
								applications/plugins/hid_app/views/hid_keynote.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,14 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | typedef struct Hid Hid; | ||||||
|  | typedef struct HidKeynote HidKeynote; | ||||||
|  | 
 | ||||||
|  | HidKeynote* hid_keynote_alloc(Hid* bt_hid); | ||||||
|  | 
 | ||||||
|  | void hid_keynote_free(HidKeynote* hid_keynote); | ||||||
|  | 
 | ||||||
|  | View* hid_keynote_get_view(HidKeynote* hid_keynote); | ||||||
|  | 
 | ||||||
|  | void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected); | ||||||
| @ -1,13 +1,17 @@ | |||||||
| #include "bt_hid_media.h" | #include "hid_media.h" | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal_bt_hid.h> | #include <furi_hal_bt_hid.h> | ||||||
| #include <furi_hal_usb_hid.h> | #include <furi_hal_usb_hid.h> | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
|  | #include "../hid.h" | ||||||
| 
 | 
 | ||||||
| #include "bt_hid_icons.h" | #include "hid_icons.h" | ||||||
| 
 | 
 | ||||||
| struct BtHidMedia { | #define TAG "HidMedia" | ||||||
|  | 
 | ||||||
|  | struct HidMedia { | ||||||
|     View* view; |     View* view; | ||||||
|  |     Hid* hid; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -17,9 +21,9 @@ typedef struct { | |||||||
|     bool down_pressed; |     bool down_pressed; | ||||||
|     bool ok_pressed; |     bool ok_pressed; | ||||||
|     bool connected; |     bool connected; | ||||||
| } BtHidMediaModel; | } HidMediaModel; | ||||||
| 
 | 
 | ||||||
| static void bt_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { | static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { | ||||||
|     canvas_draw_triangle(canvas, x, y, 5, 3, dir); |     canvas_draw_triangle(canvas, x, y, 5, 3, dir); | ||||||
|     if(dir == CanvasDirectionBottomToTop) { |     if(dir == CanvasDirectionBottomToTop) { | ||||||
|         canvas_draw_dot(canvas, x, y - 1); |         canvas_draw_dot(canvas, x, y - 1); | ||||||
| @ -32,9 +36,9 @@ static void bt_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, Canvas | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { | static void hid_media_draw_callback(Canvas* canvas, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHidMediaModel* model = context; |     HidMediaModel* model = context; | ||||||
| 
 | 
 | ||||||
|     // Header
 |     // Header
 | ||||||
|     if(model->connected) { |     if(model->connected) { | ||||||
| @ -76,8 +80,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { | |||||||
|         canvas_set_bitmap_mode(canvas, 0); |         canvas_set_bitmap_mode(canvas, 0); | ||||||
|         canvas_set_color(canvas, ColorWhite); |         canvas_set_color(canvas, ColorWhite); | ||||||
|     } |     } | ||||||
|     bt_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); |     hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); | ||||||
|     bt_hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); |     hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     // Right
 |     // Right
 | ||||||
| @ -87,8 +91,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { | |||||||
|         canvas_set_bitmap_mode(canvas, 0); |         canvas_set_bitmap_mode(canvas, 0); | ||||||
|         canvas_set_color(canvas, ColorWhite); |         canvas_set_color(canvas, ColorWhite); | ||||||
|     } |     } | ||||||
|     bt_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); |     hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); | ||||||
|     bt_hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); |     hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     // Ok
 |     // Ok
 | ||||||
| @ -96,7 +100,7 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { | |||||||
|         canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13); |         canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13); | ||||||
|         canvas_set_color(canvas, ColorWhite); |         canvas_set_color(canvas, ColorWhite); | ||||||
|     } |     } | ||||||
|     bt_hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); |     hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); | ||||||
|     canvas_draw_line(canvas, 100, 29, 100, 33); |     canvas_draw_line(canvas, 100, 29, 100, 33); | ||||||
|     canvas_draw_line(canvas, 102, 29, 102, 33); |     canvas_draw_line(canvas, 102, 29, 102, 33); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| @ -107,100 +111,101 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { | |||||||
|     elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); |     elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* event) { | static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_media->view, |         hid_media->view, | ||||||
|         BtHidMediaModel * model, |         HidMediaModel * model, | ||||||
|         { |         { | ||||||
|             if(event->key == InputKeyUp) { |             if(event->key == InputKeyUp) { | ||||||
|                 model->up_pressed = true; |                 model->up_pressed = true; | ||||||
|                 furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); |                 hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); | ||||||
|             } else if(event->key == InputKeyDown) { |             } else if(event->key == InputKeyDown) { | ||||||
|                 model->down_pressed = true; |                 model->down_pressed = true; | ||||||
|                 furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); |                 hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); | ||||||
|             } else if(event->key == InputKeyLeft) { |             } else if(event->key == InputKeyLeft) { | ||||||
|                 model->left_pressed = true; |                 model->left_pressed = true; | ||||||
|                 furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_PREVIOUS_TRACK); |                 hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); | ||||||
|             } else if(event->key == InputKeyRight) { |             } else if(event->key == InputKeyRight) { | ||||||
|                 model->right_pressed = true; |                 model->right_pressed = true; | ||||||
|                 furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_NEXT_TRACK); |                 hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); | ||||||
|             } else if(event->key == InputKeyOk) { |             } else if(event->key == InputKeyOk) { | ||||||
|                 model->ok_pressed = true; |                 model->ok_pressed = true; | ||||||
|                 furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_PLAY_PAUSE); |                 hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         true); |         true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_media_process_release(BtHidMedia* bt_hid_media, InputEvent* event) { | static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_media->view, |         hid_media->view, | ||||||
|         BtHidMediaModel * model, |         HidMediaModel * model, | ||||||
|         { |         { | ||||||
|             if(event->key == InputKeyUp) { |             if(event->key == InputKeyUp) { | ||||||
|                 model->up_pressed = false; |                 model->up_pressed = false; | ||||||
|                 furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); |                 hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); | ||||||
|             } else if(event->key == InputKeyDown) { |             } else if(event->key == InputKeyDown) { | ||||||
|                 model->down_pressed = false; |                 model->down_pressed = false; | ||||||
|                 furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); |                 hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); | ||||||
|             } else if(event->key == InputKeyLeft) { |             } else if(event->key == InputKeyLeft) { | ||||||
|                 model->left_pressed = false; |                 model->left_pressed = false; | ||||||
|                 furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_PREVIOUS_TRACK); |                 hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); | ||||||
|             } else if(event->key == InputKeyRight) { |             } else if(event->key == InputKeyRight) { | ||||||
|                 model->right_pressed = false; |                 model->right_pressed = false; | ||||||
|                 furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_NEXT_TRACK); |                 hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); | ||||||
|             } else if(event->key == InputKeyOk) { |             } else if(event->key == InputKeyOk) { | ||||||
|                 model->ok_pressed = false; |                 model->ok_pressed = false; | ||||||
|                 furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_PLAY_PAUSE); |                 hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         true); |         true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool bt_hid_media_input_callback(InputEvent* event, void* context) { | static bool hid_media_input_callback(InputEvent* event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHidMedia* bt_hid_media = context; |     HidMedia* hid_media = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == InputTypePress) { |     if(event->type == InputTypePress) { | ||||||
|         bt_hid_media_process_press(bt_hid_media, event); |         hid_media_process_press(hid_media, event); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } else if(event->type == InputTypeRelease) { |     } else if(event->type == InputTypeRelease) { | ||||||
|         bt_hid_media_process_release(bt_hid_media, event); |         hid_media_process_release(hid_media, event); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } else if(event->type == InputTypeShort) { |     } else if(event->type == InputTypeShort) { | ||||||
|         if(event->key == InputKeyBack) { |         if(event->key == InputKeyBack) { | ||||||
|             furi_hal_bt_hid_consumer_key_release_all(); |             hid_hal_consumer_key_release_all(hid_media->hid); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BtHidMedia* bt_hid_media_alloc() { | HidMedia* hid_media_alloc(Hid* hid) { | ||||||
|     BtHidMedia* bt_hid_media = malloc(sizeof(BtHidMedia)); |     HidMedia* hid_media = malloc(sizeof(HidMedia)); | ||||||
|     bt_hid_media->view = view_alloc(); |     hid_media->view = view_alloc(); | ||||||
|     view_set_context(bt_hid_media->view, bt_hid_media); |     hid_media->hid = hid; | ||||||
|     view_allocate_model(bt_hid_media->view, ViewModelTypeLocking, sizeof(BtHidMediaModel)); |     view_set_context(hid_media->view, hid_media); | ||||||
|     view_set_draw_callback(bt_hid_media->view, bt_hid_media_draw_callback); |     view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel)); | ||||||
|     view_set_input_callback(bt_hid_media->view, bt_hid_media_input_callback); |     view_set_draw_callback(hid_media->view, hid_media_draw_callback); | ||||||
|  |     view_set_input_callback(hid_media->view, hid_media_input_callback); | ||||||
| 
 | 
 | ||||||
|     return bt_hid_media; |     return hid_media; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_hid_media_free(BtHidMedia* bt_hid_media) { | void hid_media_free(HidMedia* hid_media) { | ||||||
|     furi_assert(bt_hid_media); |     furi_assert(hid_media); | ||||||
|     view_free(bt_hid_media->view); |     view_free(hid_media->view); | ||||||
|     free(bt_hid_media); |     free(hid_media); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| View* bt_hid_media_get_view(BtHidMedia* bt_hid_media) { | View* hid_media_get_view(HidMedia* hid_media) { | ||||||
|     furi_assert(bt_hid_media); |     furi_assert(hid_media); | ||||||
|     return bt_hid_media->view; |     return hid_media->view; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected) { | void hid_media_set_connected_status(HidMedia* hid_media, bool connected) { | ||||||
|     furi_assert(bt_hid_media); |     furi_assert(hid_media); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_media->view, BtHidMediaModel * model, { model->connected = connected; }, true); |         hid_media->view, HidMediaModel * model, { model->connected = connected; }, true); | ||||||
| } | } | ||||||
							
								
								
									
										13
									
								
								applications/plugins/hid_app/views/hid_media.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | typedef struct HidMedia HidMedia; | ||||||
|  | 
 | ||||||
|  | HidMedia* hid_media_alloc(); | ||||||
|  | 
 | ||||||
|  | void hid_media_free(HidMedia* hid_media); | ||||||
|  | 
 | ||||||
|  | View* hid_media_get_view(HidMedia* hid_media); | ||||||
|  | 
 | ||||||
|  | void hid_media_set_connected_status(HidMedia* hid_media, bool connected); | ||||||
| @ -1,16 +1,15 @@ | |||||||
| #include "bt_hid_mouse.h" | #include "hid_mouse.h" | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal_bt_hid.h> |  | ||||||
| #include <furi_hal_usb_hid.h> |  | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
|  | #include "../hid.h" | ||||||
| 
 | 
 | ||||||
| #include "bt_hid_icons.h" | #include "hid_icons.h" | ||||||
| 
 | 
 | ||||||
| struct BtHidMouse { | #define TAG "HidMouse" | ||||||
|  | 
 | ||||||
|  | struct HidMouse { | ||||||
|     View* view; |     View* view; | ||||||
|  |     Hid* hid; | ||||||
| }; | }; | ||||||
| #define MOUSE_MOVE_SHORT 5 |  | ||||||
| #define MOUSE_MOVE_LONG 20 |  | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     bool left_pressed; |     bool left_pressed; | ||||||
| @ -21,11 +20,11 @@ typedef struct { | |||||||
|     bool left_mouse_held; |     bool left_mouse_held; | ||||||
|     bool right_mouse_pressed; |     bool right_mouse_pressed; | ||||||
|     bool connected; |     bool connected; | ||||||
| } BtHidMouseModel; | } HidMouseModel; | ||||||
| 
 | 
 | ||||||
| static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { | static void hid_mouse_draw_callback(Canvas* canvas, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHidMouseModel* model = context; |     HidMouseModel* model = context; | ||||||
| 
 | 
 | ||||||
|     // Header
 |     // Header
 | ||||||
|     if(model->connected) { |     if(model->connected) { | ||||||
| @ -103,15 +102,15 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { | static void hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_mouse->view, |         hid_mouse->view, | ||||||
|         BtHidMouseModel * model, |         HidMouseModel * model, | ||||||
|         { |         { | ||||||
|             if(event->key == InputKeyBack) { |             if(event->key == InputKeyBack) { | ||||||
|                 if(event->type == InputTypeShort) { |                 if(event->type == InputTypeShort) { | ||||||
|                     furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_RIGHT); |                     hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); | ||||||
|                     furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_RIGHT); |                     hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); | ||||||
|                 } else if(event->type == InputTypePress) { |                 } else if(event->type == InputTypePress) { | ||||||
|                     model->right_mouse_pressed = true; |                     model->right_mouse_pressed = true; | ||||||
|                 } else if(event->type == InputTypeRelease) { |                 } else if(event->type == InputTypeRelease) { | ||||||
| @ -120,11 +119,12 @@ static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { | |||||||
|             } else if(event->key == InputKeyOk) { |             } else if(event->key == InputKeyOk) { | ||||||
|                 if(event->type == InputTypeShort) { |                 if(event->type == InputTypeShort) { | ||||||
|                     // Just release if it was being held before
 |                     // Just release if it was being held before
 | ||||||
|                     if(!model->left_mouse_held) furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); |                     if(!model->left_mouse_held) | ||||||
|                     furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); |                         hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); | ||||||
|  |                     hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_LEFT); | ||||||
|                     model->left_mouse_held = false; |                     model->left_mouse_held = false; | ||||||
|                 } else if(event->type == InputTypeLong) { |                 } else if(event->type == InputTypeLong) { | ||||||
|                     furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); |                     hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); | ||||||
|                     model->left_mouse_held = true; |                     model->left_mouse_held = true; | ||||||
|                     model->left_mouse_pressed = true; |                     model->left_mouse_pressed = true; | ||||||
|                 } else if(event->type == InputTypePress) { |                 } else if(event->type == InputTypePress) { | ||||||
| @ -133,40 +133,39 @@ static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { | |||||||
|                     // Only release if it wasn't a long press
 |                     // Only release if it wasn't a long press
 | ||||||
|                     if(!model->left_mouse_held) model->left_mouse_pressed = false; |                     if(!model->left_mouse_held) model->left_mouse_pressed = false; | ||||||
|                 } |                 } | ||||||
| 
 |  | ||||||
|             } else if(event->key == InputKeyRight) { |             } else if(event->key == InputKeyRight) { | ||||||
|                 if(event->type == InputTypePress) { |                 if(event->type == InputTypePress) { | ||||||
|                     model->right_pressed = true; |                     model->right_pressed = true; | ||||||
|                     furi_hal_bt_hid_mouse_move(MOUSE_MOVE_SHORT, 0); |                     hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_SHORT, 0); | ||||||
|                 } else if(event->type == InputTypeRepeat) { |                 } else if(event->type == InputTypeRepeat) { | ||||||
|                     furi_hal_bt_hid_mouse_move(MOUSE_MOVE_LONG, 0); |                     hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_LONG, 0); | ||||||
|                 } else if(event->type == InputTypeRelease) { |                 } else if(event->type == InputTypeRelease) { | ||||||
|                     model->right_pressed = false; |                     model->right_pressed = false; | ||||||
|                 } |                 } | ||||||
|             } else if(event->key == InputKeyLeft) { |             } else if(event->key == InputKeyLeft) { | ||||||
|                 if(event->type == InputTypePress) { |                 if(event->type == InputTypePress) { | ||||||
|                     model->left_pressed = true; |                     model->left_pressed = true; | ||||||
|                     furi_hal_bt_hid_mouse_move(-MOUSE_MOVE_SHORT, 0); |                     hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_SHORT, 0); | ||||||
|                 } else if(event->type == InputTypeRepeat) { |                 } else if(event->type == InputTypeRepeat) { | ||||||
|                     furi_hal_bt_hid_mouse_move(-MOUSE_MOVE_LONG, 0); |                     hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_LONG, 0); | ||||||
|                 } else if(event->type == InputTypeRelease) { |                 } else if(event->type == InputTypeRelease) { | ||||||
|                     model->left_pressed = false; |                     model->left_pressed = false; | ||||||
|                 } |                 } | ||||||
|             } else if(event->key == InputKeyDown) { |             } else if(event->key == InputKeyDown) { | ||||||
|                 if(event->type == InputTypePress) { |                 if(event->type == InputTypePress) { | ||||||
|                     model->down_pressed = true; |                     model->down_pressed = true; | ||||||
|                     furi_hal_bt_hid_mouse_move(0, MOUSE_MOVE_SHORT); |                     hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_SHORT); | ||||||
|                 } else if(event->type == InputTypeRepeat) { |                 } else if(event->type == InputTypeRepeat) { | ||||||
|                     furi_hal_bt_hid_mouse_move(0, MOUSE_MOVE_LONG); |                     hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_LONG); | ||||||
|                 } else if(event->type == InputTypeRelease) { |                 } else if(event->type == InputTypeRelease) { | ||||||
|                     model->down_pressed = false; |                     model->down_pressed = false; | ||||||
|                 } |                 } | ||||||
|             } else if(event->key == InputKeyUp) { |             } else if(event->key == InputKeyUp) { | ||||||
|                 if(event->type == InputTypePress) { |                 if(event->type == InputTypePress) { | ||||||
|                     model->up_pressed = true; |                     model->up_pressed = true; | ||||||
|                     furi_hal_bt_hid_mouse_move(0, -MOUSE_MOVE_SHORT); |                     hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_SHORT); | ||||||
|                 } else if(event->type == InputTypeRepeat) { |                 } else if(event->type == InputTypeRepeat) { | ||||||
|                     furi_hal_bt_hid_mouse_move(0, -MOUSE_MOVE_LONG); |                     hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_LONG); | ||||||
|                 } else if(event->type == InputTypeRelease) { |                 } else if(event->type == InputTypeRelease) { | ||||||
|                     model->up_pressed = false; |                     model->up_pressed = false; | ||||||
|                 } |                 } | ||||||
| @ -175,45 +174,46 @@ static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { | |||||||
|         true); |         true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool bt_hid_mouse_input_callback(InputEvent* event, void* context) { | static bool hid_mouse_input_callback(InputEvent* event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHidMouse* bt_hid_mouse = context; |     HidMouse* hid_mouse = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == InputTypeLong && event->key == InputKeyBack) { |     if(event->type == InputTypeLong && event->key == InputKeyBack) { | ||||||
|         furi_hal_bt_hid_mouse_release_all(); |         hid_hal_mouse_release_all(hid_mouse->hid); | ||||||
|     } else { |     } else { | ||||||
|         bt_hid_mouse_process(bt_hid_mouse, event); |         hid_mouse_process(hid_mouse, event); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BtHidMouse* bt_hid_mouse_alloc() { | HidMouse* hid_mouse_alloc(Hid* hid) { | ||||||
|     BtHidMouse* bt_hid_mouse = malloc(sizeof(BtHidMouse)); |     HidMouse* hid_mouse = malloc(sizeof(HidMouse)); | ||||||
|     bt_hid_mouse->view = view_alloc(); |     hid_mouse->view = view_alloc(); | ||||||
|     view_set_context(bt_hid_mouse->view, bt_hid_mouse); |     hid_mouse->hid = hid; | ||||||
|     view_allocate_model(bt_hid_mouse->view, ViewModelTypeLocking, sizeof(BtHidMouseModel)); |     view_set_context(hid_mouse->view, hid_mouse); | ||||||
|     view_set_draw_callback(bt_hid_mouse->view, bt_hid_mouse_draw_callback); |     view_allocate_model(hid_mouse->view, ViewModelTypeLocking, sizeof(HidMouseModel)); | ||||||
|     view_set_input_callback(bt_hid_mouse->view, bt_hid_mouse_input_callback); |     view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback); | ||||||
|  |     view_set_input_callback(hid_mouse->view, hid_mouse_input_callback); | ||||||
| 
 | 
 | ||||||
|     return bt_hid_mouse; |     return hid_mouse; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse) { | void hid_mouse_free(HidMouse* hid_mouse) { | ||||||
|     furi_assert(bt_hid_mouse); |     furi_assert(hid_mouse); | ||||||
|     view_free(bt_hid_mouse->view); |     view_free(hid_mouse->view); | ||||||
|     free(bt_hid_mouse); |     free(hid_mouse); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse) { | View* hid_mouse_get_view(HidMouse* hid_mouse) { | ||||||
|     furi_assert(bt_hid_mouse); |     furi_assert(hid_mouse); | ||||||
|     return bt_hid_mouse->view; |     return hid_mouse->view; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected) { | void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected) { | ||||||
|     furi_assert(bt_hid_mouse); |     furi_assert(hid_mouse); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_mouse->view, BtHidMouseModel * model, { model->connected = connected; }, true); |         hid_mouse->view, HidMouseModel * model, { model->connected = connected; }, true); | ||||||
| } | } | ||||||
							
								
								
									
										17
									
								
								applications/plugins/hid_app/views/hid_mouse.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,17 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | #define MOUSE_MOVE_SHORT 5 | ||||||
|  | #define MOUSE_MOVE_LONG 20 | ||||||
|  | 
 | ||||||
|  | typedef struct Hid Hid; | ||||||
|  | typedef struct HidMouse HidMouse; | ||||||
|  | 
 | ||||||
|  | HidMouse* hid_mouse_alloc(Hid* bt_hid); | ||||||
|  | 
 | ||||||
|  | void hid_mouse_free(HidMouse* hid_mouse); | ||||||
|  | 
 | ||||||
|  | View* hid_mouse_get_view(HidMouse* hid_mouse); | ||||||
|  | 
 | ||||||
|  | void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected); | ||||||
| @ -1,13 +1,14 @@ | |||||||
| #include "bt_hid_tiktok.h" | #include "hid_tiktok.h" | ||||||
| #include <furi.h> | #include "../hid.h" | ||||||
| #include <furi_hal_bt_hid.h> |  | ||||||
| #include <furi_hal_usb_hid.h> |  | ||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| 
 | 
 | ||||||
| #include "bt_hid_icons.h" | #include "hid_icons.h" | ||||||
| 
 | 
 | ||||||
| struct BtHidTikTok { | #define TAG "HidTikTok" | ||||||
|  | 
 | ||||||
|  | struct HidTikTok { | ||||||
|     View* view; |     View* view; | ||||||
|  |     Hid* hid; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -18,11 +19,11 @@ typedef struct { | |||||||
|     bool ok_pressed; |     bool ok_pressed; | ||||||
|     bool connected; |     bool connected; | ||||||
|     bool is_cursor_set; |     bool is_cursor_set; | ||||||
| } BtHidTikTokModel; | } HidTikTokModel; | ||||||
| 
 | 
 | ||||||
| static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) { | static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHidTikTokModel* model = context; |     HidTikTokModel* model = context; | ||||||
| 
 | 
 | ||||||
|     // Header
 |     // Header
 | ||||||
|     if(model->connected) { |     if(model->connected) { | ||||||
| @ -89,102 +90,104 @@ static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) { | |||||||
|     elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); |     elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_tiktok_reset_cursor() { | static void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) { | ||||||
|     // Set cursor to the phone's left up corner
 |     // Set cursor to the phone's left up corner
 | ||||||
|     // Delays to guarantee one packet per connection interval
 |     // Delays to guarantee one packet per connection interval
 | ||||||
|     for(size_t i = 0; i < 8; i++) { |     for(size_t i = 0; i < 8; i++) { | ||||||
|         furi_hal_bt_hid_mouse_move(-127, -127); |         hid_hal_mouse_move(hid_tiktok->hid, -127, -127); | ||||||
|         furi_delay_ms(50); |         furi_delay_ms(50); | ||||||
|     } |     } | ||||||
|     // Move cursor from the corner
 |     // Move cursor from the corner
 | ||||||
|     furi_hal_bt_hid_mouse_move(20, 120); |     hid_hal_mouse_move(hid_tiktok->hid, 20, 120); | ||||||
|     furi_delay_ms(50); |     furi_delay_ms(50); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_tiktok_process_press(BtHidTikTokModel* model, InputEvent* event) { | static void | ||||||
|  |     hid_tiktok_process_press(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) { | ||||||
|     if(event->key == InputKeyUp) { |     if(event->key == InputKeyUp) { | ||||||
|         model->up_pressed = true; |         model->up_pressed = true; | ||||||
|     } else if(event->key == InputKeyDown) { |     } else if(event->key == InputKeyDown) { | ||||||
|         model->down_pressed = true; |         model->down_pressed = true; | ||||||
|     } else if(event->key == InputKeyLeft) { |     } else if(event->key == InputKeyLeft) { | ||||||
|         model->left_pressed = true; |         model->left_pressed = true; | ||||||
|         furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); |         hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); | ||||||
|     } else if(event->key == InputKeyRight) { |     } else if(event->key == InputKeyRight) { | ||||||
|         model->right_pressed = true; |         model->right_pressed = true; | ||||||
|         furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); |         hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); | ||||||
|     } else if(event->key == InputKeyOk) { |     } else if(event->key == InputKeyOk) { | ||||||
|         model->ok_pressed = true; |         model->ok_pressed = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_tiktok_process_release(BtHidTikTokModel* model, InputEvent* event) { | static void | ||||||
|  |     hid_tiktok_process_release(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) { | ||||||
|     if(event->key == InputKeyUp) { |     if(event->key == InputKeyUp) { | ||||||
|         model->up_pressed = false; |         model->up_pressed = false; | ||||||
|     } else if(event->key == InputKeyDown) { |     } else if(event->key == InputKeyDown) { | ||||||
|         model->down_pressed = false; |         model->down_pressed = false; | ||||||
|     } else if(event->key == InputKeyLeft) { |     } else if(event->key == InputKeyLeft) { | ||||||
|         model->left_pressed = false; |         model->left_pressed = false; | ||||||
|         furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); |         hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); | ||||||
|     } else if(event->key == InputKeyRight) { |     } else if(event->key == InputKeyRight) { | ||||||
|         model->right_pressed = false; |         model->right_pressed = false; | ||||||
|         furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); |         hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); | ||||||
|     } else if(event->key == InputKeyOk) { |     } else if(event->key == InputKeyOk) { | ||||||
|         model->ok_pressed = false; |         model->ok_pressed = false; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) { | static bool hid_tiktok_input_callback(InputEvent* event, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHidTikTok* bt_hid_tiktok = context; |     HidTikTok* hid_tiktok = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_tiktok->view, |         hid_tiktok->view, | ||||||
|         BtHidTikTokModel * model, |         HidTikTokModel * model, | ||||||
|         { |         { | ||||||
|             if(event->type == InputTypePress) { |             if(event->type == InputTypePress) { | ||||||
|                 bt_hid_tiktok_process_press(model, event); |                 hid_tiktok_process_press(hid_tiktok, model, event); | ||||||
|                 if(model->connected && !model->is_cursor_set) { |                 if(model->connected && !model->is_cursor_set) { | ||||||
|                     bt_hid_tiktok_reset_cursor(); |                     hid_tiktok_reset_cursor(hid_tiktok); | ||||||
|                     model->is_cursor_set = true; |                     model->is_cursor_set = true; | ||||||
|                 } |                 } | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } else if(event->type == InputTypeRelease) { |             } else if(event->type == InputTypeRelease) { | ||||||
|                 bt_hid_tiktok_process_release(model, event); |                 hid_tiktok_process_release(hid_tiktok, model, event); | ||||||
|                 consumed = true; |                 consumed = true; | ||||||
|             } else if(event->type == InputTypeShort) { |             } else if(event->type == InputTypeShort) { | ||||||
|                 if(event->key == InputKeyOk) { |                 if(event->key == InputKeyOk) { | ||||||
|                     furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); |                     hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); | ||||||
|                     furi_delay_ms(50); |                     furi_delay_ms(50); | ||||||
|                     furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); |                     hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); | ||||||
|                     furi_delay_ms(50); |                     furi_delay_ms(50); | ||||||
|                     furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); |                     hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); | ||||||
|                     furi_delay_ms(50); |                     furi_delay_ms(50); | ||||||
|                     furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); |                     hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); | ||||||
|                     consumed = true; |                     consumed = true; | ||||||
|                 } else if(event->key == InputKeyUp) { |                 } else if(event->key == InputKeyUp) { | ||||||
|                     // Emulate up swipe
 |                     // Emulate up swipe
 | ||||||
|                     furi_hal_bt_hid_mouse_scroll(-6); |                     hid_hal_mouse_scroll(hid_tiktok->hid, -6); | ||||||
|                     furi_hal_bt_hid_mouse_scroll(-12); |                     hid_hal_mouse_scroll(hid_tiktok->hid, -12); | ||||||
|                     furi_hal_bt_hid_mouse_scroll(-19); |                     hid_hal_mouse_scroll(hid_tiktok->hid, -19); | ||||||
|                     furi_hal_bt_hid_mouse_scroll(-12); |                     hid_hal_mouse_scroll(hid_tiktok->hid, -12); | ||||||
|                     furi_hal_bt_hid_mouse_scroll(-6); |                     hid_hal_mouse_scroll(hid_tiktok->hid, -6); | ||||||
|                     consumed = true; |                     consumed = true; | ||||||
|                 } else if(event->key == InputKeyDown) { |                 } else if(event->key == InputKeyDown) { | ||||||
|                     // Emulate down swipe
 |                     // Emulate down swipe
 | ||||||
|                     furi_hal_bt_hid_mouse_scroll(6); |                     hid_hal_mouse_scroll(hid_tiktok->hid, 6); | ||||||
|                     furi_hal_bt_hid_mouse_scroll(12); |                     hid_hal_mouse_scroll(hid_tiktok->hid, 12); | ||||||
|                     furi_hal_bt_hid_mouse_scroll(19); |                     hid_hal_mouse_scroll(hid_tiktok->hid, 19); | ||||||
|                     furi_hal_bt_hid_mouse_scroll(12); |                     hid_hal_mouse_scroll(hid_tiktok->hid, 12); | ||||||
|                     furi_hal_bt_hid_mouse_scroll(6); |                     hid_hal_mouse_scroll(hid_tiktok->hid, 6); | ||||||
|                     consumed = true; |                     consumed = true; | ||||||
|                 } else if(event->key == InputKeyBack) { |                 } else if(event->key == InputKeyBack) { | ||||||
|                     furi_hal_bt_hid_consumer_key_release_all(); |                     hid_hal_consumer_key_release_all(hid_tiktok->hid); | ||||||
|                     consumed = true; |                     consumed = true; | ||||||
|                 } |                 } | ||||||
|             } else if(event->type == InputTypeLong) { |             } else if(event->type == InputTypeLong) { | ||||||
|                 if(event->key == InputKeyBack) { |                 if(event->key == InputKeyBack) { | ||||||
|                     furi_hal_bt_hid_consumer_key_release_all(); |                     hid_hal_consumer_key_release_all(hid_tiktok->hid); | ||||||
|                     model->is_cursor_set = false; |                     model->is_cursor_set = false; | ||||||
|                     consumed = false; |                     consumed = false; | ||||||
|                 } |                 } | ||||||
| @ -195,33 +198,34 @@ static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) { | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BtHidTikTok* bt_hid_tiktok_alloc() { | HidTikTok* hid_tiktok_alloc(Hid* bt_hid) { | ||||||
|     BtHidTikTok* bt_hid_tiktok = malloc(sizeof(BtHidTikTok)); |     HidTikTok* hid_tiktok = malloc(sizeof(HidTikTok)); | ||||||
|     bt_hid_tiktok->view = view_alloc(); |     hid_tiktok->hid = bt_hid; | ||||||
|     view_set_context(bt_hid_tiktok->view, bt_hid_tiktok); |     hid_tiktok->view = view_alloc(); | ||||||
|     view_allocate_model(bt_hid_tiktok->view, ViewModelTypeLocking, sizeof(BtHidTikTokModel)); |     view_set_context(hid_tiktok->view, hid_tiktok); | ||||||
|     view_set_draw_callback(bt_hid_tiktok->view, bt_hid_tiktok_draw_callback); |     view_allocate_model(hid_tiktok->view, ViewModelTypeLocking, sizeof(HidTikTokModel)); | ||||||
|     view_set_input_callback(bt_hid_tiktok->view, bt_hid_tiktok_input_callback); |     view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback); | ||||||
|  |     view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback); | ||||||
| 
 | 
 | ||||||
|     return bt_hid_tiktok; |     return hid_tiktok; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok) { | void hid_tiktok_free(HidTikTok* hid_tiktok) { | ||||||
|     furi_assert(bt_hid_tiktok); |     furi_assert(hid_tiktok); | ||||||
|     view_free(bt_hid_tiktok->view); |     view_free(hid_tiktok->view); | ||||||
|     free(bt_hid_tiktok); |     free(hid_tiktok); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok) { | View* hid_tiktok_get_view(HidTikTok* hid_tiktok) { | ||||||
|     furi_assert(bt_hid_tiktok); |     furi_assert(hid_tiktok); | ||||||
|     return bt_hid_tiktok->view; |     return hid_tiktok->view; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected) { | void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected) { | ||||||
|     furi_assert(bt_hid_tiktok); |     furi_assert(hid_tiktok); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_tiktok->view, |         hid_tiktok->view, | ||||||
|         BtHidTikTokModel * model, |         HidTikTokModel * model, | ||||||
|         { |         { | ||||||
|             model->connected = connected; |             model->connected = connected; | ||||||
|             model->is_cursor_set = false; |             model->is_cursor_set = false; | ||||||
							
								
								
									
										14
									
								
								applications/plugins/hid_app/views/hid_tiktok.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,14 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
| @ -64,6 +64,7 @@ class FlipperApplication: | |||||||
|     fap_author: str = "" |     fap_author: str = "" | ||||||
|     fap_weburl: str = "" |     fap_weburl: str = "" | ||||||
|     fap_icon_assets: Optional[str] = None |     fap_icon_assets: Optional[str] = None | ||||||
|  |     fap_icon_assets_symbol: Optional[str] = None | ||||||
|     fap_extbuild: List[ExternallyBuiltFile] = field(default_factory=list) |     fap_extbuild: List[ExternallyBuiltFile] = field(default_factory=list) | ||||||
|     fap_private_libs: List[Library] = field(default_factory=list) |     fap_private_libs: List[Library] = field(default_factory=list) | ||||||
|     # Internally used by fbt |     # Internally used by fbt | ||||||
|  | |||||||
| @ -60,7 +60,7 @@ def BuildAppElf(env, app): | |||||||
|         fap_icons = app_env.CompileIcons( |         fap_icons = app_env.CompileIcons( | ||||||
|             app_env.Dir(app_work_dir), |             app_env.Dir(app_work_dir), | ||||||
|             app._appdir.Dir(app.fap_icon_assets), |             app._appdir.Dir(app.fap_icon_assets), | ||||||
|             icon_bundle_name=f"{app.appid}_icons", |             icon_bundle_name=f"{app.fap_icon_assets_symbol if app.fap_icon_assets_symbol else app.appid }_icons", | ||||||
|         ) |         ) | ||||||
|         app_env.Alias("_fap_icons", fap_icons) |         app_env.Alias("_fap_icons", fap_icons) | ||||||
| 
 | 
 | ||||||
|  | |||||||
 Smooklu
						Smooklu