Bluetooth Remote Additions (#1330)
* Update the HID Keycodes to pull from the library * Composite BLE Report Map, add consumer & mouse HID * Add Mouse & keyboard bt remote, fixed media remote * BT Keyboard remove long press shift * Fix usb hid modifier keys * Fixed misaligned bad usb keys * Fix keyboard app keys * Partial fix for bt app and linux * Update to work across platforms * Fix for report ids * BtHidApp: move variable from bss to model, cleanup naming. * FuriHal: add const to immutable data declaration Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									c72b678510
								
							
						
					
					
						commit
						6b3625f46b
					
				| @ -57,48 +57,48 @@ static const DuckyKey ducky_keys[] = { | |||||||
|     {"GUI", KEY_MOD_LEFT_GUI}, |     {"GUI", KEY_MOD_LEFT_GUI}, | ||||||
|     {"WINDOWS", KEY_MOD_LEFT_GUI}, |     {"WINDOWS", KEY_MOD_LEFT_GUI}, | ||||||
| 
 | 
 | ||||||
|     {"DOWNARROW", KEY_DOWN_ARROW}, |     {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, | ||||||
|     {"DOWN", KEY_DOWN_ARROW}, |     {"DOWN", HID_KEYBOARD_DOWN_ARROW}, | ||||||
|     {"LEFTARROW", KEY_LEFT_ARROW}, |     {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, | ||||||
|     {"LEFT", KEY_LEFT_ARROW}, |     {"LEFT", HID_KEYBOARD_LEFT_ARROW}, | ||||||
|     {"RIGHTARROW", KEY_RIGHT_ARROW}, |     {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW}, | ||||||
|     {"RIGHT", KEY_RIGHT_ARROW}, |     {"RIGHT", HID_KEYBOARD_RIGHT_ARROW}, | ||||||
|     {"UPARROW", KEY_UP_ARROW}, |     {"UPARROW", HID_KEYBOARD_UP_ARROW}, | ||||||
|     {"UP", KEY_UP_ARROW}, |     {"UP", HID_KEYBOARD_UP_ARROW}, | ||||||
| 
 | 
 | ||||||
|     {"ENTER", KEY_ENTER}, |     {"ENTER", HID_KEYBOARD_RETURN}, | ||||||
|     {"BREAK", KEY_PAUSE}, |     {"BREAK", HID_KEYBOARD_PAUSE}, | ||||||
|     {"PAUSE", KEY_PAUSE}, |     {"PAUSE", HID_KEYBOARD_PAUSE}, | ||||||
|     {"CAPSLOCK", KEY_CAPS_LOCK}, |     {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, | ||||||
|     {"DELETE", KEY_DELETE}, |     {"DELETE", HID_KEYBOARD_DELETE}, | ||||||
|     {"BACKSPACE", KEY_BACKSPACE}, |     {"BACKSPACE", HID_KEYPAD_BACKSPACE}, | ||||||
|     {"END", KEY_END}, |     {"END", HID_KEYBOARD_END}, | ||||||
|     {"ESC", KEY_ESC}, |     {"ESC", HID_KEYBOARD_ESCAPE}, | ||||||
|     {"ESCAPE", KEY_ESC}, |     {"ESCAPE", HID_KEYBOARD_ESCAPE}, | ||||||
|     {"HOME", KEY_HOME}, |     {"HOME", HID_KEYBOARD_HOME}, | ||||||
|     {"INSERT", KEY_INSERT}, |     {"INSERT", HID_KEYBOARD_INSERT}, | ||||||
|     {"NUMLOCK", KEY_NUM_LOCK}, |     {"NUMLOCK", HID_KEYPAD_NUMLOCK}, | ||||||
|     {"PAGEUP", KEY_PAGE_UP}, |     {"PAGEUP", HID_KEYBOARD_PAGE_UP}, | ||||||
|     {"PAGEDOWN", KEY_PAGE_DOWN}, |     {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, | ||||||
|     {"PRINTSCREEN", KEY_PRINT}, |     {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, | ||||||
|     {"SCROLLOCK", KEY_SCROLL_LOCK}, |     {"SCROLLOCK", HID_KEYBOARD_SCROLL_LOCK}, | ||||||
|     {"SPACE", KEY_SPACE}, |     {"SPACE", HID_KEYBOARD_SPACEBAR}, | ||||||
|     {"TAB", KEY_TAB}, |     {"TAB", HID_KEYBOARD_TAB}, | ||||||
|     {"MENU", KEY_APPLICATION}, |     {"MENU", HID_KEYBOARD_APPLICATION}, | ||||||
|     {"APP", KEY_APPLICATION}, |     {"APP", HID_KEYBOARD_APPLICATION}, | ||||||
| 
 | 
 | ||||||
|     {"F1", KEY_F1}, |     {"F1", HID_KEYBOARD_F1}, | ||||||
|     {"F2", KEY_F2}, |     {"F2", HID_KEYBOARD_F2}, | ||||||
|     {"F3", KEY_F3}, |     {"F3", HID_KEYBOARD_F3}, | ||||||
|     {"F4", KEY_F4}, |     {"F4", HID_KEYBOARD_F4}, | ||||||
|     {"F5", KEY_F5}, |     {"F5", HID_KEYBOARD_F5}, | ||||||
|     {"F6", KEY_F6}, |     {"F6", HID_KEYBOARD_F6}, | ||||||
|     {"F7", KEY_F7}, |     {"F7", HID_KEYBOARD_F7}, | ||||||
|     {"F8", KEY_F8}, |     {"F8", HID_KEYBOARD_F8}, | ||||||
|     {"F9", KEY_F9}, |     {"F9", HID_KEYBOARD_F9}, | ||||||
|     {"F10", KEY_F10}, |     {"F10", HID_KEYBOARD_F10}, | ||||||
|     {"F11", KEY_F11}, |     {"F11", HID_KEYBOARD_F11}, | ||||||
|     {"F12", KEY_F12}, |     {"F12", HID_KEYBOARD_F12}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const char ducky_cmd_comment[] = {"REM"}; | static const char ducky_cmd_comment[] = {"REM"}; | ||||||
| @ -114,16 +114,16 @@ static const char ducky_cmd_altstr_1[] = {"ALTSTRING "}; | |||||||
| static const char ducky_cmd_altstr_2[] = {"ALTCODE "}; | static const char ducky_cmd_altstr_2[] = {"ALTCODE "}; | ||||||
| 
 | 
 | ||||||
| static const uint8_t numpad_keys[10] = { | static const uint8_t numpad_keys[10] = { | ||||||
|     KEYPAD_0, |     HID_KEYPAD_0, | ||||||
|     KEYPAD_1, |     HID_KEYPAD_1, | ||||||
|     KEYPAD_2, |     HID_KEYPAD_2, | ||||||
|     KEYPAD_3, |     HID_KEYPAD_3, | ||||||
|     KEYPAD_4, |     HID_KEYPAD_4, | ||||||
|     KEYPAD_5, |     HID_KEYPAD_5, | ||||||
|     KEYPAD_6, |     HID_KEYPAD_6, | ||||||
|     KEYPAD_7, |     HID_KEYPAD_7, | ||||||
|     KEYPAD_8, |     HID_KEYPAD_8, | ||||||
|     KEYPAD_9, |     HID_KEYPAD_9, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static bool ducky_get_number(const char* param, uint32_t* val) { | static bool ducky_get_number(const char* param, uint32_t* val) { | ||||||
| @ -149,8 +149,8 @@ static bool ducky_is_line_end(const char chr) { | |||||||
| 
 | 
 | ||||||
| static void ducky_numlock_on() { | static void ducky_numlock_on() { | ||||||
|     if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { |     if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { | ||||||
|         furi_hal_hid_kb_press(KEY_NUM_LOCK); |         furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); | ||||||
|         furi_hal_hid_kb_release(KEY_NUM_LOCK); |         furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -170,7 +170,7 @@ static bool ducky_altchar(const char* charcode) { | |||||||
| 
 | 
 | ||||||
|     FURI_LOG_I(WORKER_TAG, "char %s", charcode); |     FURI_LOG_I(WORKER_TAG, "char %s", charcode); | ||||||
| 
 | 
 | ||||||
|     furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT); |     furi_hal_hid_kb_press(HID_KEYBOARD_L_ALT); | ||||||
| 
 | 
 | ||||||
|     while(!ducky_is_line_end(charcode[i])) { |     while(!ducky_is_line_end(charcode[i])) { | ||||||
|         state = ducky_numpad_press(charcode[i]); |         state = ducky_numpad_press(charcode[i]); | ||||||
| @ -178,7 +178,7 @@ static bool ducky_altchar(const char* charcode) { | |||||||
|         i++; |         i++; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT); |     furi_hal_hid_kb_release(HID_KEYBOARD_L_ALT); | ||||||
|     return state; |     return state; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -206,7 +206,7 @@ static bool ducky_string(const char* param) { | |||||||
|     uint32_t i = 0; |     uint32_t i = 0; | ||||||
|     while(param[i] != '\0') { |     while(param[i] != '\0') { | ||||||
|         uint16_t keycode = HID_ASCII_TO_KEY(param[i]); |         uint16_t keycode = HID_ASCII_TO_KEY(param[i]); | ||||||
|         if(keycode != KEY_NONE) { |         if(keycode != HID_KEYBOARD_NONE) { | ||||||
|             furi_hal_hid_kb_press(keycode); |             furi_hal_hid_kb_press(keycode); | ||||||
|             furi_hal_hid_kb_release(keycode); |             furi_hal_hid_kb_release(keycode); | ||||||
|         } |         } | ||||||
| @ -294,7 +294,7 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) { | |||||||
|     } else { |     } else { | ||||||
|         // Special keys + modifiers
 |         // Special keys + modifiers
 | ||||||
|         uint16_t key = ducky_get_keycode(line_tmp, false); |         uint16_t key = ducky_get_keycode(line_tmp, false); | ||||||
|         if(key == KEY_NONE) return SCRIPT_STATE_ERROR; |         if(key == HID_KEYBOARD_NONE) return SCRIPT_STATE_ERROR; | ||||||
|         if((key & 0xFF00) != 0) { |         if((key & 0xFF00) != 0) { | ||||||
|             // It's a modifier key
 |             // It's a modifier key
 | ||||||
|             line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; |             line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; | ||||||
|  | |||||||
| @ -6,7 +6,9 @@ | |||||||
| 
 | 
 | ||||||
| enum BtDebugSubmenuIndex { | enum BtDebugSubmenuIndex { | ||||||
|     BtHidSubmenuIndexKeynote, |     BtHidSubmenuIndexKeynote, | ||||||
|  |     BtHidSubmenuIndexKeyboard, | ||||||
|     BtHidSubmenuIndexMedia, |     BtHidSubmenuIndexMedia, | ||||||
|  |     BtHidSubmenuIndexMouse, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void bt_hid_submenu_callback(void* context, uint32_t index) { | void bt_hid_submenu_callback(void* context, uint32_t index) { | ||||||
| @ -15,9 +17,15 @@ void bt_hid_submenu_callback(void* context, uint32_t index) { | |||||||
|     if(index == BtHidSubmenuIndexKeynote) { |     if(index == BtHidSubmenuIndexKeynote) { | ||||||
|         app->view_id = BtHidViewKeynote; |         app->view_id = BtHidViewKeynote; | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, 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) { |     } else if(index == BtHidSubmenuIndexMedia) { | ||||||
|         app->view_id = BtHidViewMedia; |         app->view_id = BtHidViewMedia; | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, 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); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -25,10 +33,11 @@ void bt_hid_dialog_callback(DialogExResult result, void* context) { | |||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     BtHid* app = context; |     BtHid* app = context; | ||||||
|     if(result == DialogExResultLeft) { |     if(result == DialogExResultLeft) { | ||||||
|         // TODO switch to Submenu after Media is done
 |  | ||||||
|         view_dispatcher_stop(app->view_dispatcher); |         view_dispatcher_stop(app->view_dispatcher); | ||||||
|     } else if(result == DialogExResultRight) { |     } else if(result == DialogExResultRight) { | ||||||
|         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote); |         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); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -52,7 +61,9 @@ void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { | |||||||
|         notification_internal_message(bt_hid->notifications, &sequence_reset_blue); |         notification_internal_message(bt_hid->notifications, &sequence_reset_blue); | ||||||
|     } |     } | ||||||
|     bt_hid_keynote_set_connected_status(bt_hid->bt_hid_keynote, connected); |     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_media_set_connected_status(bt_hid->bt_hid_media, connected); | ||||||
|  |     bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BtHid* bt_hid_app_alloc() { | BtHid* bt_hid_app_alloc() { | ||||||
| @ -76,8 +87,11 @@ BtHid* bt_hid_app_alloc() { | |||||||
|     app->submenu = submenu_alloc(); |     app->submenu = submenu_alloc(); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         app->submenu, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app); |         app->submenu, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         app->submenu, "Media player", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); |         app->submenu, "Media player", BtHidSubmenuIndexMedia, 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_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, BtHidViewSubmenu, submenu_get_view(app->submenu)); |         app->view_dispatcher, BtHidViewSubmenu, submenu_get_view(app->submenu)); | ||||||
| @ -88,6 +102,7 @@ BtHid* bt_hid_app_alloc() { | |||||||
|     dialog_ex_set_context(app->dialog, app); |     dialog_ex_set_context(app->dialog, app); | ||||||
|     dialog_ex_set_left_button_text(app->dialog, "Exit"); |     dialog_ex_set_left_button_text(app->dialog, "Exit"); | ||||||
|     dialog_ex_set_right_button_text(app->dialog, "Stay"); |     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); |     dialog_ex_set_header(app->dialog, "Close current app?", 16, 12, AlignLeft, AlignTop); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, BtHidViewExitConfirm, dialog_ex_get_view(app->dialog)); |         app->view_dispatcher, BtHidViewExitConfirm, dialog_ex_get_view(app->dialog)); | ||||||
| @ -99,12 +114,25 @@ BtHid* bt_hid_app_alloc() { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, BtHidViewKeynote, bt_hid_keynote_get_view(app->bt_hid_keynote)); |         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
 |     // Media view
 | ||||||
|     app->bt_hid_media = bt_hid_media_alloc(); |     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_set_previous_callback(bt_hid_media_get_view(app->bt_hid_media), bt_hid_exit_confirm_view); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media)); |         app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media)); | ||||||
| 
 | 
 | ||||||
|  |     // 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
 |     // TODO switch to menu after Media is done
 | ||||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote); |     view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote); | ||||||
| 
 | 
 | ||||||
| @ -124,8 +152,12 @@ void bt_hid_app_free(BtHid* app) { | |||||||
|     dialog_ex_free(app->dialog); |     dialog_ex_free(app->dialog); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeynote); |     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeynote); | ||||||
|     bt_hid_keynote_free(app->bt_hid_keynote); |     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); |     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMedia); | ||||||
|     bt_hid_media_free(app->bt_hid_media); |     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_free(app->view_dispatcher); |     view_dispatcher_free(app->view_dispatcher); | ||||||
| 
 | 
 | ||||||
|     // Close records
 |     // Close records
 | ||||||
|  | |||||||
| @ -10,7 +10,9 @@ | |||||||
| #include <gui/modules/submenu.h> | #include <gui/modules/submenu.h> | ||||||
| #include <gui/modules/dialog_ex.h> | #include <gui/modules/dialog_ex.h> | ||||||
| #include "views/bt_hid_keynote.h" | #include "views/bt_hid_keynote.h" | ||||||
|  | #include "views/bt_hid_keyboard.h" | ||||||
| #include "views/bt_hid_media.h" | #include "views/bt_hid_media.h" | ||||||
|  | #include "views/bt_hid_mouse.h" | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     Bt* bt; |     Bt* bt; | ||||||
| @ -20,13 +22,17 @@ typedef struct { | |||||||
|     Submenu* submenu; |     Submenu* submenu; | ||||||
|     DialogEx* dialog; |     DialogEx* dialog; | ||||||
|     BtHidKeynote* bt_hid_keynote; |     BtHidKeynote* bt_hid_keynote; | ||||||
|  |     BtHidKeyboard* bt_hid_keyboard; | ||||||
|     BtHidMedia* bt_hid_media; |     BtHidMedia* bt_hid_media; | ||||||
|  |     BtHidMouse* bt_hid_mouse; | ||||||
|     uint32_t view_id; |     uint32_t view_id; | ||||||
| } BtHid; | } BtHid; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     BtHidViewSubmenu, |     BtHidViewSubmenu, | ||||||
|     BtHidViewKeynote, |     BtHidViewKeynote, | ||||||
|  |     BtHidViewKeyboard, | ||||||
|     BtHidViewMedia, |     BtHidViewMedia, | ||||||
|  |     BtHidViewMouse, | ||||||
|     BtHidViewExitConfirm, |     BtHidViewExitConfirm, | ||||||
| } BtHidView; | } BtHidView; | ||||||
|  | |||||||
							
								
								
									
										384
									
								
								applications/bt/bt_hid_app/views/bt_hid_keyboard.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								applications/bt/bt_hid_app/views/bt_hid_keyboard.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,384 @@ | |||||||
|  | #include "bt_hid_keyboard.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal_bt_hid.h> | ||||||
|  | #include <furi_hal_usb_hid.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | #include <gui/icon_i.h> | ||||||
|  | 
 | ||||||
|  | struct BtHidKeyboard { | ||||||
|  |     View* view; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool shift; | ||||||
|  |     bool alt; | ||||||
|  |     bool ctrl; | ||||||
|  |     bool gui; | ||||||
|  |     uint8_t x; | ||||||
|  |     uint8_t y; | ||||||
|  |     uint8_t last_key_code; | ||||||
|  |     uint16_t modifier_code; | ||||||
|  |     bool ok_pressed; | ||||||
|  |     bool back_pressed; | ||||||
|  |     bool connected; | ||||||
|  |     char key_string[5]; | ||||||
|  | } BtHidKeyboardModel; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t width; | ||||||
|  |     char* key; | ||||||
|  |     const Icon* icon; | ||||||
|  |     char* shift_key; | ||||||
|  |     uint8_t value; | ||||||
|  | } BtHidKeyboardKey; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     int8_t x; | ||||||
|  |     int8_t y; | ||||||
|  | } BtHidKeyboardPoint; | ||||||
|  | 
 | ||||||
|  | // 4 BY 12
 | ||||||
|  | #define MARGIN_TOP 0 | ||||||
|  | #define MARGIN_LEFT 4 | ||||||
|  | #define KEY_WIDTH 9 | ||||||
|  | #define KEY_HEIGHT 12 | ||||||
|  | #define KEY_PADDING 1 | ||||||
|  | #define ROW_COUNT 6 | ||||||
|  | #define COLUMN_COUNT 12 | ||||||
|  | 
 | ||||||
|  | // 0 width items are not drawn, but there value is used
 | ||||||
|  | const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { | ||||||
|  |     { | ||||||
|  |         {.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 = "3", .shift_key = "#", .value = HID_KEYBOARD_3}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "4", .shift_key = "$", .value = HID_KEYBOARD_4}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "5", .shift_key = "%", .value = HID_KEYBOARD_5}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "6", .shift_key = "^", .value = HID_KEYBOARD_6}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "7", .shift_key = "&", .value = HID_KEYBOARD_7}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "8", .shift_key = "*", .value = HID_KEYBOARD_8}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "9", .shift_key = "(", .value = HID_KEYBOARD_9}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "0", .shift_key = ")", .value = HID_KEYBOARD_0}, | ||||||
|  |         {.width = 2, .icon = &I_Pin_arrow_left_9x7, .value = HID_KEYBOARD_DELETE}, | ||||||
|  |         {.width = 0, .value = HID_KEYBOARD_DELETE}, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         {.width = 1, .icon = NULL, .key = "q", .shift_key = "Q", .value = HID_KEYBOARD_Q}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "w", .shift_key = "W", .value = HID_KEYBOARD_W}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "e", .shift_key = "E", .value = HID_KEYBOARD_E}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "r", .shift_key = "R", .value = HID_KEYBOARD_R}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "t", .shift_key = "T", .value = HID_KEYBOARD_T}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "y", .shift_key = "Y", .value = HID_KEYBOARD_Y}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "u", .shift_key = "U", .value = HID_KEYBOARD_U}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "i", .shift_key = "I", .value = HID_KEYBOARD_I}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "o", .shift_key = "O", .value = HID_KEYBOARD_O}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "p", .shift_key = "P", .value = HID_KEYBOARD_P}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "[", .shift_key = "{", .value = HID_KEYBOARD_OPEN_BRACKET}, | ||||||
|  |         {.width = 1, | ||||||
|  |          .icon = NULL, | ||||||
|  |          .key = "]", | ||||||
|  |          .shift_key = "}", | ||||||
|  |          .value = HID_KEYBOARD_CLOSE_BRACKET}, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         {.width = 1, .icon = NULL, .key = "a", .shift_key = "A", .value = HID_KEYBOARD_A}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "s", .shift_key = "S", .value = HID_KEYBOARD_S}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "d", .shift_key = "D", .value = HID_KEYBOARD_D}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "f", .shift_key = "F", .value = HID_KEYBOARD_F}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "g", .shift_key = "G", .value = HID_KEYBOARD_G}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "h", .shift_key = "H", .value = HID_KEYBOARD_H}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "j", .shift_key = "J", .value = HID_KEYBOARD_J}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "k", .shift_key = "K", .value = HID_KEYBOARD_K}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "l", .shift_key = "L", .value = HID_KEYBOARD_L}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = ";", .shift_key = ":", .value = HID_KEYBOARD_SEMICOLON}, | ||||||
|  |         {.width = 2, .icon = &I_Pin_arrow_right_9x7, .value = HID_KEYBOARD_RETURN}, | ||||||
|  |         {.width = 0, .value = HID_KEYBOARD_RETURN}, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         {.width = 1, .icon = NULL, .key = "z", .shift_key = "Z", .value = HID_KEYBOARD_Z}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "x", .shift_key = "X", .value = HID_KEYBOARD_X}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "c", .shift_key = "C", .value = HID_KEYBOARD_C}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "v", .shift_key = "V", .value = HID_KEYBOARD_V}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "b", .shift_key = "B", .value = HID_KEYBOARD_B}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "n", .shift_key = "N", .value = HID_KEYBOARD_N}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "m", .shift_key = "M", .value = HID_KEYBOARD_M}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "/", .shift_key = "?", .value = HID_KEYBOARD_SLASH}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "\\", .shift_key = "|", .value = HID_KEYBOARD_BACKSLASH}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "`", .shift_key = "~", .value = HID_KEYBOARD_GRAVE_ACCENT}, | ||||||
|  |         {.width = 1, .icon = &I_ButtonUp_7x4, .value = HID_KEYBOARD_UP_ARROW}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS}, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         {.width = 1, .icon = &I_Pin_arrow_up7x9, .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_DOT}, | ||||||
|  |         {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, | ||||||
|  |         {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, | ||||||
|  |         {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, | ||||||
|  |         {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "'", .shift_key = "\"", .value = HID_KEYBOARD_APOSTROPHE}, | ||||||
|  |         {.width = 1, .icon = NULL, .key = "=", .shift_key = "+", .value = HID_KEYBOARD_EQUAL_SIGN}, | ||||||
|  |         {.width = 1, .icon = &I_ButtonLeft_4x7, .value = HID_KEYBOARD_LEFT_ARROW}, | ||||||
|  |         {.width = 1, .icon = &I_ButtonDown_7x4, .value = HID_KEYBOARD_DOWN_ARROW}, | ||||||
|  |         {.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW}, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         {.width = 3, .icon = NULL, .key = "Ctrl", .value = HID_KEYBOARD_L_CTRL}, | ||||||
|  |         {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL}, | ||||||
|  |         {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL}, | ||||||
|  |         {.width = 3, .icon = NULL, .key = "Alt", .value = HID_KEYBOARD_L_ALT}, | ||||||
|  |         {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT}, | ||||||
|  |         {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT}, | ||||||
|  |         {.width = 3, .icon = NULL, .key = "Cmd", .value = HID_KEYBOARD_L_GUI}, | ||||||
|  |         {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI}, | ||||||
|  |         {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI}, | ||||||
|  |         {.width = 3, .icon = NULL, .key = "Tab", .value = HID_KEYBOARD_TAB}, | ||||||
|  |         {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, | ||||||
|  |         {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void bt_hid_keyboard_to_upper(char* str) { | ||||||
|  |     while(*str) { | ||||||
|  |         *str = toupper((unsigned char)*str); | ||||||
|  |         str++; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void bt_hid_keyboard_draw_key( | ||||||
|  |     Canvas* canvas, | ||||||
|  |     BtHidKeyboardModel* model, | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     BtHidKeyboardKey key, | ||||||
|  |     bool selected) { | ||||||
|  |     if(!key.width) return; | ||||||
|  | 
 | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     uint8_t keyWidth = KEY_WIDTH * key.width + KEY_PADDING * (key.width - 1); | ||||||
|  |     if(selected) { | ||||||
|  |         // Draw a filled box
 | ||||||
|  |         elements_slightly_rounded_box( | ||||||
|  |             canvas, | ||||||
|  |             MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING), | ||||||
|  |             MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING), | ||||||
|  |             keyWidth, | ||||||
|  |             KEY_HEIGHT); | ||||||
|  |         canvas_set_color(canvas, ColorWhite); | ||||||
|  |     } else { | ||||||
|  |         // Draw a framed box
 | ||||||
|  |         elements_slightly_rounded_frame( | ||||||
|  |             canvas, | ||||||
|  |             MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING), | ||||||
|  |             MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING), | ||||||
|  |             keyWidth, | ||||||
|  |             KEY_HEIGHT); | ||||||
|  |     } | ||||||
|  |     if(key.icon != NULL) { | ||||||
|  |         // Draw the icon centered on the button
 | ||||||
|  |         canvas_draw_icon( | ||||||
|  |             canvas, | ||||||
|  |             MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key.icon->width / 2, | ||||||
|  |             MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2 - key.icon->height / 2, | ||||||
|  |             key.icon); | ||||||
|  |     } else { | ||||||
|  |         // If shift is toggled use the shift key when available
 | ||||||
|  |         strcpy(model->key_string, (model->shift && key.shift_key != 0) ? key.shift_key : key.key); | ||||||
|  |         // Upper case if ctrl or alt was toggled true
 | ||||||
|  |         if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || | ||||||
|  |            (model->alt && key.value == HID_KEYBOARD_L_ALT) || | ||||||
|  |            (model->gui && key.value == HID_KEYBOARD_L_GUI)) { | ||||||
|  |             bt_hid_keyboard_to_upper(model->key_string); | ||||||
|  |         } | ||||||
|  |         canvas_draw_str_aligned( | ||||||
|  |             canvas, | ||||||
|  |             MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1, | ||||||
|  |             MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2, | ||||||
|  |             AlignCenter, | ||||||
|  |             AlignCenter, | ||||||
|  |             model->key_string); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     BtHidKeyboardModel* model = context; | ||||||
|  | 
 | ||||||
|  |     // Header
 | ||||||
|  |     if(!model->connected) { | ||||||
|  |         canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); | ||||||
|  |         canvas_set_font(canvas, FontPrimary); | ||||||
|  |         elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keyboard"); | ||||||
|  |         elements_multiline_text_aligned( | ||||||
|  |             canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection..."); | ||||||
|  |         return; // Dont render the keyboard if we are not yet connected
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontKeyboard); | ||||||
|  |     // Start shifting the all keys up if on the next row (Scrolling)
 | ||||||
|  |     uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0; | ||||||
|  |     for(uint8_t y = initY; y < ROW_COUNT; y++) { | ||||||
|  |         const BtHidKeyboardKey* keyboardKeyRow = bt_hid_keyboard_keyset[y]; | ||||||
|  |         uint8_t x = 0; | ||||||
|  |         for(uint8_t i = 0; i < COLUMN_COUNT; i++) { | ||||||
|  |             BtHidKeyboardKey key = keyboardKeyRow[i]; | ||||||
|  |             // Select when the button is hovered
 | ||||||
|  |             // Select if the button is hovered within its width
 | ||||||
|  |             // Select if back is clicked and its the backspace key
 | ||||||
|  |             // Deselect when the button clicked or not hovered
 | ||||||
|  |             bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; | ||||||
|  |             bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; | ||||||
|  |             bt_hid_keyboard_draw_key( | ||||||
|  |                 canvas, | ||||||
|  |                 model, | ||||||
|  |                 x, | ||||||
|  |                 y - initY, | ||||||
|  |                 key, | ||||||
|  |                 (!model->ok_pressed && keySelected) || backSelected); | ||||||
|  |             x += key.width; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) { | ||||||
|  |     BtHidKeyboardKey key = bt_hid_keyboard_keyset[model->y][model->x]; | ||||||
|  |     // Use upper case if shift is toggled
 | ||||||
|  |     bool useUppercase = model->shift; | ||||||
|  |     // Check if the key has an upper case version
 | ||||||
|  |     bool hasUppercase = key.shift_key != 0; | ||||||
|  |     if(useUppercase && hasUppercase) | ||||||
|  |         return key.value; | ||||||
|  |     else | ||||||
|  |         return key.value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void bt_hid_keyboard_get_select_key(BtHidKeyboardModel* model, BtHidKeyboardPoint delta) { | ||||||
|  |     // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map
 | ||||||
|  |     do { | ||||||
|  |         if(((int8_t)model->y) + delta.y < 0) | ||||||
|  |             model->y = ROW_COUNT - 1; | ||||||
|  |         else | ||||||
|  |             model->y = (model->y + delta.y) % ROW_COUNT; | ||||||
|  |     } while(delta.y != 0 && bt_hid_keyboard_keyset[model->y][model->x].value == 0); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(((int8_t)model->x) + delta.x < 0) | ||||||
|  |             model->x = COLUMN_COUNT - 1; | ||||||
|  |         else | ||||||
|  |             model->x = (model->x + delta.x) % COLUMN_COUNT; | ||||||
|  |     } while(delta.x != 0 && bt_hid_keyboard_keyset[model->y][model->x].width == | ||||||
|  |                                 0); // Skip zero width keys, pretend they are one key
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* event) { | ||||||
|  |     with_view_model( | ||||||
|  |         bt_hid_keyboard->view, (BtHidKeyboardModel * model) { | ||||||
|  |             if(event->key == InputKeyOk) { | ||||||
|  |                 if(event->type == InputTypePress) { | ||||||
|  |                     model->ok_pressed = true; | ||||||
|  |                 } else if(event->type == InputTypeLong || event->type == InputTypeShort) { | ||||||
|  |                     model->last_key_code = bt_hid_keyboard_get_selected_key(model); | ||||||
|  | 
 | ||||||
|  |                     // Toggle the modifier key when clicked, and click the key
 | ||||||
|  |                     if(model->last_key_code == HID_KEYBOARD_L_SHIFT) { | ||||||
|  |                         model->shift = !model->shift; | ||||||
|  |                         if(model->shift) | ||||||
|  |                             model->modifier_code |= KEY_MOD_LEFT_SHIFT; | ||||||
|  |                         else | ||||||
|  |                             model->modifier_code &= ~KEY_MOD_LEFT_SHIFT; | ||||||
|  |                     } else if(model->last_key_code == HID_KEYBOARD_L_ALT) { | ||||||
|  |                         model->alt = !model->alt; | ||||||
|  |                         if(model->alt) | ||||||
|  |                             model->modifier_code |= KEY_MOD_LEFT_ALT; | ||||||
|  |                         else | ||||||
|  |                             model->modifier_code &= ~KEY_MOD_LEFT_ALT; | ||||||
|  |                     } else if(model->last_key_code == HID_KEYBOARD_L_CTRL) { | ||||||
|  |                         model->ctrl = !model->ctrl; | ||||||
|  |                         if(model->ctrl) | ||||||
|  |                             model->modifier_code |= KEY_MOD_LEFT_CTRL; | ||||||
|  |                         else | ||||||
|  |                             model->modifier_code &= ~KEY_MOD_LEFT_CTRL; | ||||||
|  |                     } else if(model->last_key_code == HID_KEYBOARD_L_GUI) { | ||||||
|  |                         model->gui = !model->gui; | ||||||
|  |                         if(model->gui) | ||||||
|  |                             model->modifier_code |= KEY_MOD_LEFT_GUI; | ||||||
|  |                         else | ||||||
|  |                             model->modifier_code &= ~KEY_MOD_LEFT_GUI; | ||||||
|  |                     } | ||||||
|  |                     furi_hal_bt_hid_kb_press(model->modifier_code | model->last_key_code); | ||||||
|  |                 } else if(event->type == InputTypeRelease) { | ||||||
|  |                     // Release happens after short and long presses
 | ||||||
|  |                     furi_hal_bt_hid_kb_release(model->modifier_code | model->last_key_code); | ||||||
|  |                     model->ok_pressed = false; | ||||||
|  |                 } | ||||||
|  |             } else if(event->key == InputKeyBack) { | ||||||
|  |                 // If back is pressed for a short time, backspace
 | ||||||
|  |                 if(event->type == InputTypePress) { | ||||||
|  |                     model->back_pressed = true; | ||||||
|  |                 } else if(event->type == InputTypeShort) { | ||||||
|  |                     furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE); | ||||||
|  |                     furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); | ||||||
|  |                 } else if(event->type == InputTypeRelease) { | ||||||
|  |                     model->back_pressed = false; | ||||||
|  |                 } | ||||||
|  |             } else if(event->type == InputTypePress || event->type == InputTypeRepeat) { | ||||||
|  |                 // Cycle the selected keys
 | ||||||
|  |                 if(event->key == InputKeyUp) { | ||||||
|  |                     bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = -1}); | ||||||
|  |                 } else if(event->key == InputKeyDown) { | ||||||
|  |                     bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = 1}); | ||||||
|  |                 } else if(event->key == InputKeyLeft) { | ||||||
|  |                     bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = -1, .y = 0}); | ||||||
|  |                 } else if(event->key == InputKeyRight) { | ||||||
|  |                     bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 1, .y = 0}); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool bt_hid_keyboard_input_callback(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     BtHidKeyboard* bt_hid_keyboard = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == InputTypeLong && event->key == InputKeyBack) { | ||||||
|  |         furi_hal_bt_hid_kb_release_all(); | ||||||
|  |     } else { | ||||||
|  |         bt_hid_keyboard_process(bt_hid_keyboard, event); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BtHidKeyboard* bt_hid_keyboard_alloc() { | ||||||
|  |     BtHidKeyboard* bt_hid_keyboard = malloc(sizeof(BtHidKeyboard)); | ||||||
|  |     bt_hid_keyboard->view = view_alloc(); | ||||||
|  |     view_set_context(bt_hid_keyboard->view, bt_hid_keyboard); | ||||||
|  |     view_allocate_model(bt_hid_keyboard->view, ViewModelTypeLocking, sizeof(BtHidKeyboardModel)); | ||||||
|  |     view_set_draw_callback(bt_hid_keyboard->view, bt_hid_keyboard_draw_callback); | ||||||
|  |     view_set_input_callback(bt_hid_keyboard->view, bt_hid_keyboard_input_callback); | ||||||
|  | 
 | ||||||
|  |     return bt_hid_keyboard; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard) { | ||||||
|  |     furi_assert(bt_hid_keyboard); | ||||||
|  |     view_free(bt_hid_keyboard->view); | ||||||
|  |     free(bt_hid_keyboard); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard) { | ||||||
|  |     furi_assert(bt_hid_keyboard); | ||||||
|  |     return bt_hid_keyboard->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected) { | ||||||
|  |     furi_assert(bt_hid_keyboard); | ||||||
|  |     with_view_model( | ||||||
|  |         bt_hid_keyboard->view, (BtHidKeyboardModel * model) { | ||||||
|  |             model->connected = connected; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								applications/bt/bt_hid_app/views/bt_hid_keyboard.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/bt/bt_hid_app/views/bt_hid_keyboard.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #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); | ||||||
| @ -14,6 +14,7 @@ typedef struct { | |||||||
|     bool right_pressed; |     bool right_pressed; | ||||||
|     bool down_pressed; |     bool down_pressed; | ||||||
|     bool ok_pressed; |     bool ok_pressed; | ||||||
|  |     bool back_pressed; | ||||||
|     bool connected; |     bool connected; | ||||||
| } BtHidKeynoteModel; | } BtHidKeynoteModel; | ||||||
| 
 | 
 | ||||||
| @ -35,106 +36,119 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { | |||||||
|     BtHidKeynoteModel* model = context; |     BtHidKeynoteModel* model = context; | ||||||
| 
 | 
 | ||||||
|     // Header
 |     // Header
 | ||||||
|  |     if(model->connected) { | ||||||
|  |         canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); | ||||||
|  |     } else { | ||||||
|  |         canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); | ||||||
|  |     } | ||||||
|     canvas_set_font(canvas, FontPrimary); |     canvas_set_font(canvas, FontPrimary); | ||||||
|     elements_multiline_text_aligned(canvas, 9, 3, AlignLeft, AlignTop, "Keynote"); |     elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote"); | ||||||
|     canvas_set_font(canvas, FontSecondary); |     canvas_set_font(canvas, FontSecondary); | ||||||
| 
 | 
 | ||||||
|     // Connected status
 |  | ||||||
|     if(model->connected) { |  | ||||||
|         canvas_draw_icon(canvas, 18, 18, &I_Ble_connected_38x34); |  | ||||||
|         elements_multiline_text_aligned(canvas, 9, 60, AlignLeft, AlignBottom, "Connected"); |  | ||||||
|     } else { |  | ||||||
|         canvas_draw_icon(canvas, 18, 18, &I_Ble_disconnected_24x34); |  | ||||||
|         elements_multiline_text_aligned(canvas, 3, 60, AlignLeft, AlignBottom, "Disconnected"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Up
 |     // Up
 | ||||||
|     canvas_draw_icon(canvas, 86, 4, &I_Button_18x18); |     canvas_draw_icon(canvas, 21, 24, &I_Button_18x18); | ||||||
|     if(model->up_pressed) { |     if(model->up_pressed) { | ||||||
|         elements_slightly_rounded_box(canvas, 89, 6, 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, 95, 10, CanvasDirectionBottomToTop); |     bt_hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     // Down
 |     // Down
 | ||||||
|     canvas_draw_icon(canvas, 86, 25, &I_Button_18x18); |     canvas_draw_icon(canvas, 21, 45, &I_Button_18x18); | ||||||
|     if(model->down_pressed) { |     if(model->down_pressed) { | ||||||
|         elements_slightly_rounded_box(canvas, 89, 27, 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, 95, 35, CanvasDirectionTopToBottom); |     bt_hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     // Left
 |     // Left
 | ||||||
|     canvas_draw_icon(canvas, 65, 25, &I_Button_18x18); |     canvas_draw_icon(canvas, 0, 45, &I_Button_18x18); | ||||||
|     if(model->left_pressed) { |     if(model->left_pressed) { | ||||||
|         elements_slightly_rounded_box(canvas, 68, 27, 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, 72, 33, CanvasDirectionRightToLeft); |     bt_hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     // Right
 |     // Right
 | ||||||
|     canvas_draw_icon(canvas, 107, 25, &I_Button_18x18); |     canvas_draw_icon(canvas, 42, 45, &I_Button_18x18); | ||||||
|     if(model->right_pressed) { |     if(model->right_pressed) { | ||||||
|         elements_slightly_rounded_box(canvas, 110, 27, 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, 118, 33, CanvasDirectionLeftToRight); |     bt_hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     // Ok
 |     // Ok
 | ||||||
|     canvas_draw_icon(canvas, 63, 45, &I_Space_65x18); |     canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); | ||||||
|     if(model->ok_pressed) { |     if(model->ok_pressed) { | ||||||
|  |         elements_slightly_rounded_box(canvas, 66, 27, 60, 13); | ||||||
|  |         canvas_set_color(canvas, ColorWhite); | ||||||
|  |     } | ||||||
|  |     canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); | ||||||
|  |     elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space"); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | 
 | ||||||
|  |     // Back
 | ||||||
|  |     canvas_draw_icon(canvas, 63, 45, &I_Space_65x18); | ||||||
|  |     if(model->back_pressed) { | ||||||
|         elements_slightly_rounded_box(canvas, 66, 47, 60, 13); |         elements_slightly_rounded_box(canvas, 66, 47, 60, 13); | ||||||
|         canvas_set_color(canvas, ColorWhite); |         canvas_set_color(canvas, ColorWhite); | ||||||
|     } |     } | ||||||
|     canvas_draw_icon(canvas, 74, 49, &I_Ok_btn_9x9); |     canvas_draw_icon(canvas, 110, 49, &I_Ok_btn_9x9); | ||||||
|     elements_multiline_text_aligned(canvas, 91, 56, AlignLeft, AlignBottom, "Space"); |     elements_multiline_text_aligned(canvas, 76, 56, AlignLeft, AlignBottom, "Back"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void bt_hid_keynote_process_press(BtHidKeynote* bt_hid_keynote, InputEvent* event) { | static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         bt_hid_keynote->view, (BtHidKeynoteModel * model) { |         bt_hid_keynote->view, (BtHidKeynoteModel * model) { | ||||||
|  |             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(KEY_UP_ARROW); |                     furi_hal_bt_hid_kb_press(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(KEY_DOWN_ARROW); |                     furi_hal_bt_hid_kb_press(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(KEY_LEFT_ARROW); |                     furi_hal_bt_hid_kb_press(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(KEY_RIGHT_ARROW); |                     furi_hal_bt_hid_kb_press(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(KEY_SPACE); |                     furi_hal_bt_hid_kb_press(HID_KEYBOARD_SPACEBAR); | ||||||
|  |                 } else if(event->key == InputKeyBack) { | ||||||
|  |                     model->back_pressed = true; | ||||||
|                 } |                 } | ||||||
|             return true; |             } else if(event->type == InputTypeRelease) { | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void bt_hid_keynote_process_release(BtHidKeynote* bt_hid_keynote, InputEvent* event) { |  | ||||||
|     with_view_model( |  | ||||||
|         bt_hid_keynote->view, (BtHidKeynoteModel * model) { |  | ||||||
|                 if(event->key == InputKeyUp) { |                 if(event->key == InputKeyUp) { | ||||||
|                     model->up_pressed = false; |                     model->up_pressed = false; | ||||||
|                 furi_hal_bt_hid_kb_release(KEY_UP_ARROW); |                     furi_hal_bt_hid_kb_release(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(KEY_DOWN_ARROW); |                     furi_hal_bt_hid_kb_release(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(KEY_LEFT_ARROW); |                     furi_hal_bt_hid_kb_release(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(KEY_RIGHT_ARROW); |                     furi_hal_bt_hid_kb_release(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(KEY_SPACE); |                     furi_hal_bt_hid_kb_release(HID_KEYBOARD_SPACEBAR); | ||||||
|  |                 } else if(event->key == InputKeyBack) { | ||||||
|  |                     model->back_pressed = false; | ||||||
|  |                 } | ||||||
|  |             } else if(event->type == InputTypeShort) { | ||||||
|  |                 if(event->key == InputKeyBack) { | ||||||
|  |                     furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE); | ||||||
|  |                     furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); | ||||||
|  |                     furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_AC_BACK); | ||||||
|  |                     furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_AC_BACK); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| @ -145,16 +159,11 @@ static bool bt_hid_keynote_input_callback(InputEvent* event, void* context) { | |||||||
|     BtHidKeynote* bt_hid_keynote = context; |     BtHidKeynote* bt_hid_keynote = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == InputTypePress) { |     if(event->type == InputTypeLong && event->key == InputKeyBack) { | ||||||
|         bt_hid_keynote_process_press(bt_hid_keynote, event); |         furi_hal_bt_hid_kb_release_all(); | ||||||
|  |     } else { | ||||||
|  |         bt_hid_keynote_process(bt_hid_keynote, event); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } else if(event->type == InputTypeRelease) { |  | ||||||
|         bt_hid_keynote_process_release(bt_hid_keynote, event); |  | ||||||
|         consumed = true; |  | ||||||
|     } else if(event->type == InputTypeShort) { |  | ||||||
|         if(event->key == InputKeyBack) { |  | ||||||
|             furi_hal_hid_kb_release_all(); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
|  | |||||||
| @ -35,18 +35,14 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { | |||||||
|     BtHidMediaModel* model = context; |     BtHidMediaModel* model = context; | ||||||
| 
 | 
 | ||||||
|     // Header
 |     // Header
 | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
|     elements_multiline_text_aligned(canvas, 9, 3, AlignLeft, AlignTop, "Media player"); |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
| 
 |  | ||||||
|     // Connected status
 |  | ||||||
|     if(model->connected) { |     if(model->connected) { | ||||||
|         canvas_draw_icon(canvas, 23, 17, &I_Ble_connected_38x34); |         canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); | ||||||
|         elements_multiline_text_aligned(canvas, 35, 61, AlignCenter, AlignBottom, "Connected"); |  | ||||||
|     } else { |     } else { | ||||||
|         canvas_draw_icon(canvas, 23, 17, &I_Ble_disconnected_24x34); |         canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); | ||||||
|         elements_multiline_text_aligned(canvas, 35, 61, AlignCenter, AlignBottom, "Disconnected"); |  | ||||||
|     } |     } | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  |     elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Media"); | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
| 
 | 
 | ||||||
|     // Keypad circles
 |     // Keypad circles
 | ||||||
|     canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47); |     canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47); | ||||||
| @ -100,19 +96,19 @@ static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* eve | |||||||
|         bt_hid_media->view, (BtHidMediaModel * model) { |         bt_hid_media->view, (BtHidMediaModel * model) { | ||||||
|             if(event->key == InputKeyUp) { |             if(event->key == InputKeyUp) { | ||||||
|                 model->up_pressed = true; |                 model->up_pressed = true; | ||||||
|                 furi_hal_bt_hid_media_press(FuriHalBtHidMediaVolumeUp); |                 furi_hal_bt_hid_consumer_key_press(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_media_press(FuriHalBtHidMediaVolumeDown); |                 furi_hal_bt_hid_consumer_key_press(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_media_press(FuriHalBtHidMediaScanPrevious); |                 furi_hal_bt_hid_consumer_key_press(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_media_press(FuriHalBtHidMediaScanNext); |                 furi_hal_bt_hid_consumer_key_press(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_media_press(FuriHalBtHidMediaPlayPause); |                 furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_PLAY_PAUSE); | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| @ -123,19 +119,19 @@ static void bt_hid_media_process_release(BtHidMedia* bt_hid_media, InputEvent* e | |||||||
|         bt_hid_media->view, (BtHidMediaModel * model) { |         bt_hid_media->view, (BtHidMediaModel * model) { | ||||||
|             if(event->key == InputKeyUp) { |             if(event->key == InputKeyUp) { | ||||||
|                 model->up_pressed = false; |                 model->up_pressed = false; | ||||||
|                 furi_hal_bt_hid_media_release(FuriHalBtHidMediaVolumeUp); |                 furi_hal_bt_hid_consumer_key_release(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_media_release(FuriHalBtHidMediaVolumeDown); |                 furi_hal_bt_hid_consumer_key_release(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_media_release(FuriHalBtHidMediaScanPrevious); |                 furi_hal_bt_hid_consumer_key_release(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_media_release(FuriHalBtHidMediaScanNext); |                 furi_hal_bt_hid_consumer_key_release(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_media_release(FuriHalBtHidMediaPlayPause); |                 furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_PLAY_PAUSE); | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| @ -154,7 +150,7 @@ static bool bt_hid_media_input_callback(InputEvent* event, void* context) { | |||||||
|         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_media_release_all(); |             furi_hal_bt_hid_consumer_key_release_all(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										207
									
								
								applications/bt/bt_hid_app/views/bt_hid_mouse.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								applications/bt/bt_hid_app/views/bt_hid_mouse.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,207 @@ | |||||||
|  | #include "bt_hid_mouse.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal_bt_hid.h> | ||||||
|  | #include <furi_hal_usb_hid.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | struct BtHidMouse { | ||||||
|  |     View* view; | ||||||
|  | }; | ||||||
|  | #define MOUSE_MOVE_SHORT 5 | ||||||
|  | #define MOUSE_MOVE_LONG 20 | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool left_pressed; | ||||||
|  |     bool up_pressed; | ||||||
|  |     bool right_pressed; | ||||||
|  |     bool down_pressed; | ||||||
|  |     bool left_mouse_pressed; | ||||||
|  |     bool left_mouse_held; | ||||||
|  |     bool right_mouse_pressed; | ||||||
|  |     bool connected; | ||||||
|  | } BtHidMouseModel; | ||||||
|  | 
 | ||||||
|  | static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     BtHidMouseModel* model = context; | ||||||
|  | 
 | ||||||
|  |     // Header
 | ||||||
|  |     if(model->connected) { | ||||||
|  |         canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); | ||||||
|  |     } else { | ||||||
|  |         canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); | ||||||
|  |     } | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  |     elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse"); | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  | 
 | ||||||
|  |     if(model->left_mouse_held == true) { | ||||||
|  |         elements_multiline_text_aligned(canvas, 0, 60, AlignLeft, AlignBottom, "Selecting..."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Keypad circles
 | ||||||
|  |     canvas_draw_icon(canvas, 64, 8, &I_Circles_47x47); | ||||||
|  | 
 | ||||||
|  |     // Up
 | ||||||
|  |     if(model->up_pressed) { | ||||||
|  |         canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13); | ||||||
|  |         canvas_set_color(canvas, ColorWhite); | ||||||
|  |     } | ||||||
|  |     canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up7x9); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | 
 | ||||||
|  |     // Down
 | ||||||
|  |     if(model->down_pressed) { | ||||||
|  |         canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13); | ||||||
|  |         canvas_set_color(canvas, ColorWhite); | ||||||
|  |     } | ||||||
|  |     canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | 
 | ||||||
|  |     // Left
 | ||||||
|  |     if(model->left_pressed) { | ||||||
|  |         canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13); | ||||||
|  |         canvas_set_color(canvas, ColorWhite); | ||||||
|  |     } | ||||||
|  |     canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | 
 | ||||||
|  |     // Right
 | ||||||
|  |     if(model->right_pressed) { | ||||||
|  |         canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13); | ||||||
|  |         canvas_set_color(canvas, ColorWhite); | ||||||
|  |     } | ||||||
|  |     canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | 
 | ||||||
|  |     // Ok
 | ||||||
|  |     if(model->left_mouse_pressed) { | ||||||
|  |         canvas_draw_icon(canvas, 81, 25, &I_Pressed_Button_13x13); | ||||||
|  |         canvas_set_color(canvas, ColorWhite); | ||||||
|  |     } | ||||||
|  |     canvas_draw_icon(canvas, 83, 27, &I_Ok_btn_9x9); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | 
 | ||||||
|  |     // Back
 | ||||||
|  |     if(model->right_mouse_pressed) { | ||||||
|  |         canvas_draw_icon(canvas, 108, 48, &I_Pressed_Button_13x13); | ||||||
|  |         canvas_set_color(canvas, ColorWhite); | ||||||
|  |     } | ||||||
|  |     canvas_draw_icon(canvas, 110, 50, &I_Ok_btn_9x9); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { | ||||||
|  |     with_view_model( | ||||||
|  |         bt_hid_mouse->view, (BtHidMouseModel * model) { | ||||||
|  |             if(event->key == InputKeyBack) { | ||||||
|  |                 if(event->type == InputTypeShort) { | ||||||
|  |                     furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_RIGHT); | ||||||
|  |                     furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_RIGHT); | ||||||
|  |                 } else if(event->type == InputTypePress) { | ||||||
|  |                     model->right_mouse_pressed = true; | ||||||
|  |                 } else if(event->type == InputTypeRelease) { | ||||||
|  |                     model->right_mouse_pressed = false; | ||||||
|  |                 } | ||||||
|  |             } else if(event->key == InputKeyOk) { | ||||||
|  |                 if(event->type == InputTypeShort) { | ||||||
|  |                     // Just release if it was being held before
 | ||||||
|  |                     if(!model->left_mouse_held) furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); | ||||||
|  |                     furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); | ||||||
|  |                     model->left_mouse_held = false; | ||||||
|  |                 } else if(event->type == InputTypeLong) { | ||||||
|  |                     furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); | ||||||
|  |                     model->left_mouse_held = true; | ||||||
|  |                     model->left_mouse_pressed = true; | ||||||
|  |                 } else if(event->type == InputTypePress) { | ||||||
|  |                     model->left_mouse_pressed = true; | ||||||
|  |                 } else if(event->type == InputTypeRelease) { | ||||||
|  |                     // Only release if it wasn't a long press
 | ||||||
|  |                     if(!model->left_mouse_held) model->left_mouse_pressed = false; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } else if(event->key == InputKeyRight) { | ||||||
|  |                 if(event->type == InputTypePress) { | ||||||
|  |                     model->right_pressed = true; | ||||||
|  |                     furi_hal_bt_hid_mouse_move(MOUSE_MOVE_SHORT, 0); | ||||||
|  |                 } else if(event->type == InputTypeRepeat) { | ||||||
|  |                     furi_hal_bt_hid_mouse_move(MOUSE_MOVE_LONG, 0); | ||||||
|  |                 } else if(event->type == InputTypeRelease) { | ||||||
|  |                     model->right_pressed = false; | ||||||
|  |                 } | ||||||
|  |             } else if(event->key == InputKeyLeft) { | ||||||
|  |                 if(event->type == InputTypePress) { | ||||||
|  |                     model->left_pressed = true; | ||||||
|  |                     furi_hal_bt_hid_mouse_move(-MOUSE_MOVE_SHORT, 0); | ||||||
|  |                 } else if(event->type == InputTypeRepeat) { | ||||||
|  |                     furi_hal_bt_hid_mouse_move(-MOUSE_MOVE_LONG, 0); | ||||||
|  |                 } else if(event->type == InputTypeRelease) { | ||||||
|  |                     model->left_pressed = false; | ||||||
|  |                 } | ||||||
|  |             } else if(event->key == InputKeyDown) { | ||||||
|  |                 if(event->type == InputTypePress) { | ||||||
|  |                     model->down_pressed = true; | ||||||
|  |                     furi_hal_bt_hid_mouse_move(0, MOUSE_MOVE_SHORT); | ||||||
|  |                 } else if(event->type == InputTypeRepeat) { | ||||||
|  |                     furi_hal_bt_hid_mouse_move(0, MOUSE_MOVE_LONG); | ||||||
|  |                 } else if(event->type == InputTypeRelease) { | ||||||
|  |                     model->down_pressed = false; | ||||||
|  |                 } | ||||||
|  |             } else if(event->key == InputKeyUp) { | ||||||
|  |                 if(event->type == InputTypePress) { | ||||||
|  |                     model->up_pressed = true; | ||||||
|  |                     furi_hal_bt_hid_mouse_move(0, -MOUSE_MOVE_SHORT); | ||||||
|  |                 } else if(event->type == InputTypeRepeat) { | ||||||
|  |                     furi_hal_bt_hid_mouse_move(0, -MOUSE_MOVE_LONG); | ||||||
|  |                 } else if(event->type == InputTypeRelease) { | ||||||
|  |                     model->up_pressed = false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool bt_hid_mouse_input_callback(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     BtHidMouse* bt_hid_mouse = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == InputTypeLong && event->key == InputKeyBack) { | ||||||
|  |         furi_hal_bt_hid_mouse_release_all(); | ||||||
|  |     } else { | ||||||
|  |         bt_hid_mouse_process(bt_hid_mouse, event); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BtHidMouse* bt_hid_mouse_alloc() { | ||||||
|  |     BtHidMouse* bt_hid_mouse = malloc(sizeof(BtHidMouse)); | ||||||
|  |     bt_hid_mouse->view = view_alloc(); | ||||||
|  |     view_set_context(bt_hid_mouse->view, bt_hid_mouse); | ||||||
|  |     view_allocate_model(bt_hid_mouse->view, ViewModelTypeLocking, sizeof(BtHidMouseModel)); | ||||||
|  |     view_set_draw_callback(bt_hid_mouse->view, bt_hid_mouse_draw_callback); | ||||||
|  |     view_set_input_callback(bt_hid_mouse->view, bt_hid_mouse_input_callback); | ||||||
|  | 
 | ||||||
|  |     return bt_hid_mouse; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse) { | ||||||
|  |     furi_assert(bt_hid_mouse); | ||||||
|  |     view_free(bt_hid_mouse->view); | ||||||
|  |     free(bt_hid_mouse); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse) { | ||||||
|  |     furi_assert(bt_hid_mouse); | ||||||
|  |     return bt_hid_mouse->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected) { | ||||||
|  |     furi_assert(bt_hid_mouse); | ||||||
|  |     with_view_model( | ||||||
|  |         bt_hid_mouse->view, (BtHidMouseModel * model) { | ||||||
|  |             model->connected = connected; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								applications/bt/bt_hid_app/views/bt_hid_mouse.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/bt/bt_hid_app/views/bt_hid_mouse.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #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); | ||||||
							
								
								
									
										
											BIN
										
									
								
								assets/icons/BLE/BLE_HID/Ble_connected_15x15.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/BLE/BLE_HID/Ble_connected_15x15.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 177 B | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/BLE/BLE_HID/Ble_disconnected_15x15.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/BLE/BLE_HID/Ble_disconnected_15x15.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 178 B | 
| @ -9,10 +9,9 @@ | |||||||
| typedef struct { | typedef struct { | ||||||
|     uint16_t svc_handle; |     uint16_t svc_handle; | ||||||
|     uint16_t protocol_mode_char_handle; |     uint16_t protocol_mode_char_handle; | ||||||
|     uint16_t report_char_handle; |     uint16_t report_char_handle[HID_SVC_REPORT_COUNT]; | ||||||
|     uint16_t report_ref_desc_handle; |     uint16_t report_ref_desc_handle[HID_SVC_REPORT_COUNT]; | ||||||
|     uint16_t report_map_char_handle; |     uint16_t report_map_char_handle; | ||||||
|     uint16_t keyboard_boot_char_handle; |  | ||||||
|     uint16_t info_char_handle; |     uint16_t info_char_handle; | ||||||
|     uint16_t ctrl_point_char_handle; |     uint16_t ctrl_point_char_handle; | ||||||
| } HIDSvc; | } HIDSvc; | ||||||
| @ -47,12 +46,22 @@ void hid_svc_start() { | |||||||
|     SVCCTL_RegisterSvcHandler(hid_svc_event_handler); |     SVCCTL_RegisterSvcHandler(hid_svc_event_handler); | ||||||
|     // Add service
 |     // Add service
 | ||||||
|     svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; |     svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; | ||||||
|     status = |     /**
 | ||||||
|         aci_gatt_add_service(UUID_TYPE_16, &svc_uuid, PRIMARY_SERVICE, 30, &hid_svc->svc_handle); |      *  Add Human Interface Device Service | ||||||
|  |      */ | ||||||
|  |     status = aci_gatt_add_service( | ||||||
|  |         UUID_TYPE_16, | ||||||
|  |         &svc_uuid, | ||||||
|  |         PRIMARY_SERVICE, | ||||||
|  |         2 + /* protocol mode */ | ||||||
|  |             (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + | ||||||
|  |             (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + | ||||||
|  |             2, /* Service + Report Map + HID Information + HID Control Point */ | ||||||
|  |         &hid_svc->svc_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(TAG, "Failed to add HID service: %d", status); |         FURI_LOG_E(TAG, "Failed to add HID service: %d", status); | ||||||
|     } |     } | ||||||
|     // Add Protocol mode characterstics
 |     // Add Protocol mode characteristics
 | ||||||
|     char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID; |     char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID; | ||||||
|     status = aci_gatt_add_char( |     status = aci_gatt_add_char( | ||||||
|         hid_svc->svc_handle, |         hid_svc->svc_handle, | ||||||
| @ -75,7 +84,11 @@ void hid_svc_start() { | |||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status); |         FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status); | ||||||
|     } |     } | ||||||
|     // Add Report characterstics
 | 
 | ||||||
|  | #if(HID_SVC_REPORT_COUNT != 0) | ||||||
|  |     for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { | ||||||
|  |         if(i < HID_SVC_INPUT_REPORT_COUNT) { | ||||||
|  |             uint8_t buf[2] = {i + 1, 1}; // 1 input
 | ||||||
|             char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; |             char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; | ||||||
|             status = aci_gatt_add_char( |             status = aci_gatt_add_char( | ||||||
|                 hid_svc->svc_handle, |                 hid_svc->svc_handle, | ||||||
| @ -87,30 +100,104 @@ void hid_svc_start() { | |||||||
|                 GATT_DONT_NOTIFY_EVENTS, |                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                 10, |                 10, | ||||||
|                 CHAR_VALUE_LEN_VARIABLE, |                 CHAR_VALUE_LEN_VARIABLE, | ||||||
|         &hid_svc->report_char_handle); |                 &(hid_svc->report_char_handle[i])); | ||||||
|             if(status) { |             if(status) { | ||||||
|                 FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); |                 FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); | ||||||
|             } |             } | ||||||
|     // Add Report descriptor
 | 
 | ||||||
|     uint8_t desc_val[] = {0x00, 0x01}; |  | ||||||
|             desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; |             desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; | ||||||
|             status = aci_gatt_add_char_desc( |             status = aci_gatt_add_char_desc( | ||||||
|                 hid_svc->svc_handle, |                 hid_svc->svc_handle, | ||||||
|         hid_svc->report_char_handle, |                 hid_svc->report_char_handle[i], | ||||||
|                 UUID_TYPE_16, |                 UUID_TYPE_16, | ||||||
|                 &desc_uuid, |                 &desc_uuid, | ||||||
|                 HID_SVC_REPORT_REF_LEN, |                 HID_SVC_REPORT_REF_LEN, | ||||||
|                 HID_SVC_REPORT_REF_LEN, |                 HID_SVC_REPORT_REF_LEN, | ||||||
|         desc_val, |                 buf, | ||||||
|                 ATTR_PERMISSION_NONE, |                 ATTR_PERMISSION_NONE, | ||||||
|         ATTR_ACCESS_READ_ONLY, |                 ATTR_ACCESS_READ_WRITE, | ||||||
|                 GATT_DONT_NOTIFY_EVENTS, |                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                 MIN_ENCRY_KEY_SIZE, |                 MIN_ENCRY_KEY_SIZE, | ||||||
|                 CHAR_VALUE_LEN_CONSTANT, |                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|         &hid_svc->report_ref_desc_handle); |                 &(hid_svc->report_ref_desc_handle[i])); | ||||||
|             if(status) { |             if(status) { | ||||||
|                 FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); |                 FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); | ||||||
|             } |             } | ||||||
|  |         } else if((i - HID_SVC_INPUT_REPORT_COUNT) < HID_SVC_OUTPUT_REPORT_COUNT) { | ||||||
|  |             uint8_t buf[2] = {i + 1, 2}; // 2 output
 | ||||||
|  |             char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; | ||||||
|  |             status = aci_gatt_add_char( | ||||||
|  |                 hid_svc->svc_handle, | ||||||
|  |                 UUID_TYPE_16, | ||||||
|  |                 &char_uuid, | ||||||
|  |                 HID_SVC_REPORT_MAX_LEN, | ||||||
|  |                 CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||||
|  |                 ATTR_PERMISSION_NONE, | ||||||
|  |                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|  |                 10, | ||||||
|  |                 CHAR_VALUE_LEN_VARIABLE, | ||||||
|  |                 &(hid_svc->report_char_handle[i])); | ||||||
|  |             if(status) { | ||||||
|  |                 FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; | ||||||
|  |             status = aci_gatt_add_char_desc( | ||||||
|  |                 hid_svc->svc_handle, | ||||||
|  |                 hid_svc->report_char_handle[i], | ||||||
|  |                 UUID_TYPE_16, | ||||||
|  |                 &desc_uuid, | ||||||
|  |                 HID_SVC_REPORT_REF_LEN, | ||||||
|  |                 HID_SVC_REPORT_REF_LEN, | ||||||
|  |                 buf, | ||||||
|  |                 ATTR_PERMISSION_NONE, | ||||||
|  |                 ATTR_ACCESS_READ_WRITE, | ||||||
|  |                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|  |                 MIN_ENCRY_KEY_SIZE, | ||||||
|  |                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|  |                 &(hid_svc->report_ref_desc_handle[i])); | ||||||
|  |             if(status) { | ||||||
|  |                 FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             uint8_t buf[2] = {i + 1, 3}; // 3 feature
 | ||||||
|  |             char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; | ||||||
|  |             status = aci_gatt_add_char( | ||||||
|  |                 hid_svc->svc_handle, | ||||||
|  |                 UUID_TYPE_16, | ||||||
|  |                 &char_uuid, | ||||||
|  |                 HID_SVC_REPORT_MAX_LEN, | ||||||
|  |                 CHAR_PROP_READ | CHAR_PROP_NOTIFY, | ||||||
|  |                 ATTR_PERMISSION_NONE, | ||||||
|  |                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|  |                 10, | ||||||
|  |                 CHAR_VALUE_LEN_VARIABLE, | ||||||
|  |                 &(hid_svc->report_char_handle[i])); | ||||||
|  |             if(status) { | ||||||
|  |                 FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; | ||||||
|  |             status = aci_gatt_add_char_desc( | ||||||
|  |                 hid_svc->svc_handle, | ||||||
|  |                 hid_svc->report_char_handle[i], | ||||||
|  |                 UUID_TYPE_16, | ||||||
|  |                 &desc_uuid, | ||||||
|  |                 HID_SVC_REPORT_REF_LEN, | ||||||
|  |                 HID_SVC_REPORT_REF_LEN, | ||||||
|  |                 buf, | ||||||
|  |                 ATTR_PERMISSION_NONE, | ||||||
|  |                 ATTR_ACCESS_READ_WRITE, | ||||||
|  |                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|  |                 MIN_ENCRY_KEY_SIZE, | ||||||
|  |                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|  |                 &(hid_svc->report_ref_desc_handle[i])); | ||||||
|  |             if(status) { | ||||||
|  |                 FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|     // Add Report Map characteristic
 |     // Add Report Map characteristic
 | ||||||
|     char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; |     char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; | ||||||
|     status = aci_gatt_add_char( |     status = aci_gatt_add_char( | ||||||
| @ -127,22 +214,7 @@ void hid_svc_start() { | |||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); |         FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); | ||||||
|     } |     } | ||||||
|     // Add Boot Keyboard characteristic
 | 
 | ||||||
|     char_uuid.Char_UUID_16 = BOOT_KEYBOARD_INPUT_REPORT_CHAR_UUID; |  | ||||||
|     status = aci_gatt_add_char( |  | ||||||
|         hid_svc->svc_handle, |  | ||||||
|         UUID_TYPE_16, |  | ||||||
|         &char_uuid, |  | ||||||
|         HID_SVC_BOOT_KEYBOARD_INPUT_REPORT_MAX_LEN, |  | ||||||
|         CHAR_PROP_READ | CHAR_PROP_NOTIFY, |  | ||||||
|         ATTR_PERMISSION_NONE, |  | ||||||
|         GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, |  | ||||||
|         10, |  | ||||||
|         CHAR_VALUE_LEN_VARIABLE, |  | ||||||
|         &hid_svc->keyboard_boot_char_handle); |  | ||||||
|     if(status) { |  | ||||||
|         FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); |  | ||||||
|     } |  | ||||||
|     // Add Information characteristic
 |     // Add Information characteristic
 | ||||||
|     char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID; |     char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID; | ||||||
|     status = aci_gatt_add_char( |     status = aci_gatt_add_char( | ||||||
| @ -177,27 +249,27 @@ void hid_svc_start() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool hid_svc_update_report_map(uint8_t* data, uint16_t len) { | bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { | ||||||
|     furi_assert(data); |     furi_assert(data); | ||||||
|     furi_assert(hid_svc); |     furi_assert(hid_svc); | ||||||
| 
 | 
 | ||||||
|     tBleStatus status = aci_gatt_update_char_value( |     tBleStatus status = aci_gatt_update_char_value( | ||||||
|         hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data); |         hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(TAG, "Failed updating report map characteristic"); |         FURI_LOG_E(TAG, "Failed updating report map characteristic: %d", status); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool hid_svc_update_input_report(uint8_t* data, uint16_t len) { | bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { | ||||||
|     furi_assert(data); |     furi_assert(data); | ||||||
|     furi_assert(hid_svc); |     furi_assert(hid_svc); | ||||||
| 
 | 
 | ||||||
|     tBleStatus status = |     tBleStatus status = aci_gatt_update_char_value( | ||||||
|         aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->report_char_handle, 0, len, data); |         hid_svc->svc_handle, hid_svc->report_char_handle[input_report_num], 0, len, data); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(TAG, "Failed updating report characteristic"); |         FURI_LOG_E(TAG, "Failed updating report characteristic: %d", status); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
| @ -210,7 +282,7 @@ bool hid_svc_update_info(uint8_t* data, uint16_t len) { | |||||||
|     tBleStatus status = |     tBleStatus status = | ||||||
|         aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data); |         aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(TAG, "Failed updating info characteristic"); |         FURI_LOG_E(TAG, "Failed updating info characteristic: %d", status); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
| @ -228,18 +300,18 @@ void hid_svc_stop() { | |||||||
|         if(status) { |         if(status) { | ||||||
|             FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status); |             FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status); | ||||||
|         } |         } | ||||||
|         status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle); | #if(HID_SVC_INPUT_REPORT_COUNT != 0) | ||||||
|  |         for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { | ||||||
|  |             status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle[i]); | ||||||
|             if(status) { |             if(status) { | ||||||
|                 FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status); |                 FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status); | ||||||
|             } |             } | ||||||
|  |         } | ||||||
|  | #endif | ||||||
|         status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle); |         status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle); | ||||||
|         if(status) { |         if(status) { | ||||||
|             FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status); |             FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status); | ||||||
|         } |         } | ||||||
|         status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->keyboard_boot_char_handle); |  | ||||||
|         if(status) { |  | ||||||
|             FURI_LOG_E(TAG, "Failed to delete Keyboard Boot characteristic: %d", status); |  | ||||||
|         } |  | ||||||
|         status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle); |         status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle); | ||||||
|         if(status) { |         if(status) { | ||||||
|             FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status); |             FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status); | ||||||
|  | |||||||
| @ -3,21 +3,26 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
| #define HID_SVC_REPORT_MAP_MAX_LEN (120) | #define HID_SVC_REPORT_MAP_MAX_LEN (255) | ||||||
| #define HID_SVC_REPORT_MAX_LEN (9) | #define HID_SVC_REPORT_MAX_LEN (255) | ||||||
| #define HID_SVC_BOOT_KEYBOARD_INPUT_REPORT_MAX_LEN (8) |  | ||||||
| #define HID_SVC_REPORT_REF_LEN (2) | #define HID_SVC_REPORT_REF_LEN (2) | ||||||
| #define HID_SVC_INFO_LEN (4) | #define HID_SVC_INFO_LEN (4) | ||||||
| #define HID_SVC_CONTROL_POINT_LEN (1) | #define HID_SVC_CONTROL_POINT_LEN (1) | ||||||
| 
 | 
 | ||||||
|  | #define HID_SVC_INPUT_REPORT_COUNT (3) | ||||||
|  | #define HID_SVC_OUTPUT_REPORT_COUNT (0) | ||||||
|  | #define HID_SVC_FEATURE_REPORT_COUNT (0) | ||||||
|  | #define HID_SVC_REPORT_COUNT \ | ||||||
|  |     (HID_SVC_INPUT_REPORT_COUNT + HID_SVC_OUTPUT_REPORT_COUNT + HID_SVC_FEATURE_REPORT_COUNT) | ||||||
|  | 
 | ||||||
| void hid_svc_start(); | void hid_svc_start(); | ||||||
| 
 | 
 | ||||||
| void hid_svc_stop(); | void hid_svc_stop(); | ||||||
| 
 | 
 | ||||||
| bool hid_svc_is_started(); | bool hid_svc_is_started(); | ||||||
| 
 | 
 | ||||||
| bool hid_svc_update_report_map(uint8_t* data, uint16_t len); | bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); | ||||||
| 
 | 
 | ||||||
| bool hid_svc_update_input_report(uint8_t* data, uint16_t len); | bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); | ||||||
| 
 | 
 | ||||||
| bool hid_svc_update_info(uint8_t* data, uint16_t len); | bool hid_svc_update_info(uint8_t* data, uint16_t len); | ||||||
|  | |||||||
| @ -218,8 +218,8 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, | |||||||
|         } else if(profile == FuriHalBtProfileHidKeyboard) { |         } else if(profile == FuriHalBtProfileHidKeyboard) { | ||||||
|             // Change MAC address for HID profile
 |             // Change MAC address for HID profile
 | ||||||
|             config->mac_address[2]++; |             config->mac_address[2]++; | ||||||
|             // Change name Flipper -> Keynote
 |             // Change name Flipper -> Control
 | ||||||
|             const char* clicker_str = "Keynote"; |             const char* clicker_str = "Control"; | ||||||
|             memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str)); |             memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str)); | ||||||
|         } |         } | ||||||
|         if(!gap_init(config, event_cb, context)) { |         if(!gap_init(config, event_cb, context)) { | ||||||
|  | |||||||
| @ -1,4 +1,6 @@ | |||||||
| #include "furi_hal_bt_hid.h" | #include "furi_hal_bt_hid.h" | ||||||
|  | #include "furi_hal_usb_hid.h" | ||||||
|  | #include "usb_hid.h" | ||||||
| #include "dev_info_service.h" | #include "dev_info_service.h" | ||||||
| #include "battery_service.h" | #include "battery_service.h" | ||||||
| #include "hid_service.h" | #include "hid_service.h" | ||||||
| @ -10,128 +12,118 @@ | |||||||
| #define FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK (0x01) | #define FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK (0x01) | ||||||
| #define FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK (0x02) | #define FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK (0x02) | ||||||
| 
 | 
 | ||||||
| #define FURI_HAL_BT_HID_KB_KEYS_MAX (6) | #define FURI_HAL_BT_HID_KB_MAX_KEYS 6 | ||||||
|  | #define FURI_HAL_BT_HID_CONSUMER_MAX_KEYS 1 | ||||||
| 
 | 
 | ||||||
| typedef struct { | // Report ids cant be 0
 | ||||||
|     // uint8_t report_id;
 | enum HidReportId { | ||||||
|     uint8_t mods; |     ReportIdKeyboard = 1, | ||||||
|     uint8_t reserved; |     ReportIdMouse = 2, | ||||||
|     uint8_t key[FURI_HAL_BT_HID_KB_KEYS_MAX]; |     ReportIdConsumer = 3, | ||||||
| } FuriHalBtHidKbReport; | }; | ||||||
| 
 | // Report numbers corresponded to the report id with an offset of 1
 | ||||||
| typedef struct { | enum HidInputNumber { | ||||||
|     uint8_t report_id; |     ReportNumberKeyboard = 0, | ||||||
|     uint8_t key; |     ReportNumberMouse = 1, | ||||||
| } FuriHalBtHidMediaReport; |     ReportNumberConsumer = 2, | ||||||
| 
 |  | ||||||
| // TODO make composite HID device
 |  | ||||||
| static uint8_t furi_hal_bt_hid_report_map_data[] = { |  | ||||||
|     0x05, |  | ||||||
|     0x01, // Usage Page (Generic Desktop)
 |  | ||||||
|     0x09, |  | ||||||
|     0x06, // Usage (Keyboard)
 |  | ||||||
|     0xA1, |  | ||||||
|     0x01, // Collection (Application)
 |  | ||||||
|     // 0x85, 0x01,       // Report ID (1)
 |  | ||||||
|     0x05, |  | ||||||
|     0x07, // Usage Page (Key Codes)
 |  | ||||||
|     0x19, |  | ||||||
|     0xe0, // Usage Minimum (224)
 |  | ||||||
|     0x29, |  | ||||||
|     0xe7, // Usage Maximum (231)
 |  | ||||||
|     0x15, |  | ||||||
|     0x00, // Logical Minimum (0)
 |  | ||||||
|     0x25, |  | ||||||
|     0x01, // Logical Maximum (1)
 |  | ||||||
|     0x75, |  | ||||||
|     0x01, // Report Size (1)
 |  | ||||||
|     0x95, |  | ||||||
|     0x08, // Report Count (8)
 |  | ||||||
|     0x81, |  | ||||||
|     0x02, // Input (Data, Variable, Absolute)
 |  | ||||||
| 
 |  | ||||||
|     0x95, |  | ||||||
|     0x01, // Report Count (1)
 |  | ||||||
|     0x75, |  | ||||||
|     0x08, // Report Size (8)
 |  | ||||||
|     0x81, |  | ||||||
|     0x01, // Input (Constant) reserved byte(1)
 |  | ||||||
| 
 |  | ||||||
|     0x95, |  | ||||||
|     0x05, // Report Count (5)
 |  | ||||||
|     0x75, |  | ||||||
|     0x01, // Report Size (1)
 |  | ||||||
|     0x05, |  | ||||||
|     0x08, // Usage Page (Page# for LEDs)
 |  | ||||||
|     0x19, |  | ||||||
|     0x01, // Usage Minimum (1)
 |  | ||||||
|     0x29, |  | ||||||
|     0x05, // Usage Maximum (5)
 |  | ||||||
|     0x91, |  | ||||||
|     0x02, // Output (Data, Variable, Absolute), Led report
 |  | ||||||
|     0x95, |  | ||||||
|     0x01, // Report Count (1)
 |  | ||||||
|     0x75, |  | ||||||
|     0x03, // Report Size (3)
 |  | ||||||
|     0x91, |  | ||||||
|     0x01, // Output (Data, Variable, Absolute), Led report padding
 |  | ||||||
| 
 |  | ||||||
|     0x95, |  | ||||||
|     0x06, // Report Count (6)
 |  | ||||||
|     0x75, |  | ||||||
|     0x08, // Report Size (8)
 |  | ||||||
|     0x15, |  | ||||||
|     0x00, // Logical Minimum (0)
 |  | ||||||
|     0x25, |  | ||||||
|     0x65, // Logical Maximum (101)
 |  | ||||||
|     0x05, |  | ||||||
|     0x07, // Usage Page (Key codes)
 |  | ||||||
|     0x19, |  | ||||||
|     0x00, // Usage Minimum (0)
 |  | ||||||
|     0x29, |  | ||||||
|     0x65, // Usage Maximum (101)
 |  | ||||||
|     0x81, |  | ||||||
|     0x00, // Input (Data, Array) Key array(6 bytes)
 |  | ||||||
| 
 |  | ||||||
|     0x09, |  | ||||||
|     0x05, // Usage (Vendor Defined)
 |  | ||||||
|     0x15, |  | ||||||
|     0x00, // Logical Minimum (0)
 |  | ||||||
|     0x26, |  | ||||||
|     0xFF, |  | ||||||
|     0x00, // Logical Maximum (255)
 |  | ||||||
|     0x75, |  | ||||||
|     0x08, // Report Size (8 bit)
 |  | ||||||
|     0x95, |  | ||||||
|     0x02, // Report Count (2)
 |  | ||||||
|     0xB1, |  | ||||||
|     0x02, // Feature (Data, Variable, Absolute)
 |  | ||||||
| 
 |  | ||||||
|     0xC0, // End Collection (Application)
 |  | ||||||
| 
 |  | ||||||
|     // 0x05, 0x0C,        // Usage Page (Consumer)
 |  | ||||||
|     // 0x09, 0x01,        // Usage (Consumer Control)
 |  | ||||||
|     // 0xA1, 0x01,        // Collection (Application)
 |  | ||||||
|     // 0x85, 0x02,        //   Report ID (2)
 |  | ||||||
|     // 0x05, 0x0C,        //   Usage Page (Consumer)
 |  | ||||||
|     // 0x15, 0x00,        //   Logical Minimum (0)
 |  | ||||||
|     // 0x25, 0x01,        //   Logical Maximum (1)
 |  | ||||||
|     // 0x75, 0x01,        //   Report Size (1)
 |  | ||||||
|     // 0x95, 0x07,        //   Report Count (7)
 |  | ||||||
|     // 0x09, 0xB5,        //   Usage (Scan Next Track)
 |  | ||||||
|     // 0x09, 0xB6,        //   Usage (Scan Previous Track)
 |  | ||||||
|     // 0x09, 0xB7,        //   Usage (Stop)
 |  | ||||||
|     // 0x09, 0xB8,        //   Usage (Eject)
 |  | ||||||
|     // 0x09, 0xCD,        //   Usage (Play/Pause)
 |  | ||||||
|     // 0x09, 0xE2,        //   Usage (Mute)
 |  | ||||||
|     // 0x09, 0xE9,        //   Usage (Volume Increment)
 |  | ||||||
|     // 0x09, 0xEA,        //   Usage (Volume Decrement)
 |  | ||||||
|     // 0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
 |  | ||||||
|     // 0xC0,              // End Collection
 |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t mods; | ||||||
|  |     uint8_t reserved; | ||||||
|  |     uint8_t key[FURI_HAL_BT_HID_KB_MAX_KEYS]; | ||||||
|  | } __attribute__((__packed__)) FuriHalBtHidKbReport; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t btn; | ||||||
|  |     int8_t x; | ||||||
|  |     int8_t y; | ||||||
|  |     int8_t wheel; | ||||||
|  | } __attribute__((__packed__)) FuriHalBtHidMouseReport; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint16_t key[FURI_HAL_BT_HID_CONSUMER_MAX_KEYS]; | ||||||
|  | } __attribute__((__packed__)) FuriHalBtHidConsumerReport; | ||||||
|  | 
 | ||||||
|  | // keyboard+mouse+consumer hid report
 | ||||||
|  | static const uint8_t furi_hal_bt_hid_report_map_data[] = { | ||||||
|  |     // Keyboard Report
 | ||||||
|  |     HID_USAGE_PAGE(HID_PAGE_DESKTOP), | ||||||
|  |     HID_USAGE(HID_DESKTOP_KEYBOARD), | ||||||
|  |     HID_COLLECTION(HID_APPLICATION_COLLECTION), | ||||||
|  |     HID_REPORT_ID(ReportIdKeyboard), | ||||||
|  |     HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), | ||||||
|  |     HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL), | ||||||
|  |     HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI), | ||||||
|  |     HID_LOGICAL_MINIMUM(0), | ||||||
|  |     HID_LOGICAL_MAXIMUM(1), | ||||||
|  |     HID_REPORT_SIZE(1), | ||||||
|  |     HID_REPORT_COUNT(8), | ||||||
|  |     HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), | ||||||
|  |     HID_REPORT_COUNT(1), | ||||||
|  |     HID_REPORT_SIZE(8), | ||||||
|  |     HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), | ||||||
|  |     HID_USAGE_PAGE(HID_PAGE_LED), | ||||||
|  |     HID_REPORT_COUNT(8), | ||||||
|  |     HID_REPORT_SIZE(1), | ||||||
|  |     HID_USAGE_MINIMUM(1), | ||||||
|  |     HID_USAGE_MAXIMUM(8), | ||||||
|  |     HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), | ||||||
|  |     HID_REPORT_COUNT(FURI_HAL_BT_HID_KB_MAX_KEYS), | ||||||
|  |     HID_REPORT_SIZE(8), | ||||||
|  |     HID_LOGICAL_MINIMUM(0), | ||||||
|  |     HID_LOGICAL_MAXIMUM(101), | ||||||
|  |     HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), | ||||||
|  |     HID_USAGE_MINIMUM(0), | ||||||
|  |     HID_USAGE_MAXIMUM(101), | ||||||
|  |     HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), | ||||||
|  |     HID_END_COLLECTION, | ||||||
|  |     // Mouse Report
 | ||||||
|  |     HID_USAGE_PAGE(HID_PAGE_DESKTOP), | ||||||
|  |     HID_USAGE(HID_DESKTOP_MOUSE), | ||||||
|  |     HID_COLLECTION(HID_APPLICATION_COLLECTION), | ||||||
|  |     HID_USAGE(HID_DESKTOP_POINTER), | ||||||
|  |     HID_COLLECTION(HID_PHYSICAL_COLLECTION), | ||||||
|  |     HID_REPORT_ID(ReportIdMouse), | ||||||
|  |     HID_USAGE_PAGE(HID_PAGE_BUTTON), | ||||||
|  |     HID_USAGE_MINIMUM(1), | ||||||
|  |     HID_USAGE_MAXIMUM(3), | ||||||
|  |     HID_LOGICAL_MINIMUM(0), | ||||||
|  |     HID_LOGICAL_MAXIMUM(1), | ||||||
|  |     HID_REPORT_COUNT(3), | ||||||
|  |     HID_REPORT_SIZE(1), | ||||||
|  |     HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), | ||||||
|  |     HID_REPORT_SIZE(1), | ||||||
|  |     HID_REPORT_COUNT(5), | ||||||
|  |     HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), | ||||||
|  |     HID_USAGE_PAGE(HID_PAGE_DESKTOP), | ||||||
|  |     HID_USAGE(HID_DESKTOP_X), | ||||||
|  |     HID_USAGE(HID_DESKTOP_Y), | ||||||
|  |     HID_USAGE(HID_DESKTOP_WHEEL), | ||||||
|  |     HID_LOGICAL_MINIMUM(-127), | ||||||
|  |     HID_LOGICAL_MAXIMUM(127), | ||||||
|  |     HID_REPORT_SIZE(8), | ||||||
|  |     HID_REPORT_COUNT(3), | ||||||
|  |     HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), | ||||||
|  |     HID_END_COLLECTION, | ||||||
|  |     HID_END_COLLECTION, | ||||||
|  |     // Consumer Report
 | ||||||
|  |     HID_USAGE_PAGE(HID_PAGE_CONSUMER), | ||||||
|  |     HID_USAGE(HID_CONSUMER_CONTROL), | ||||||
|  |     HID_COLLECTION(HID_APPLICATION_COLLECTION), | ||||||
|  |     HID_REPORT_ID(ReportIdConsumer), | ||||||
|  |     HID_LOGICAL_MINIMUM(0), | ||||||
|  |     HID_RI_LOGICAL_MAXIMUM(16, 0x3FF), | ||||||
|  |     HID_USAGE_MINIMUM(0), | ||||||
|  |     HID_RI_USAGE_MAXIMUM(16, 0x3FF), | ||||||
|  |     HID_REPORT_COUNT(FURI_HAL_BT_HID_CONSUMER_MAX_KEYS), | ||||||
|  |     HID_REPORT_SIZE(16), | ||||||
|  |     HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), | ||||||
|  |     HID_END_COLLECTION, | ||||||
|  | }; | ||||||
| FuriHalBtHidKbReport* kb_report = NULL; | FuriHalBtHidKbReport* kb_report = NULL; | ||||||
| FuriHalBtHidMediaReport* media_report = NULL; | FuriHalBtHidMouseReport* mouse_report = NULL; | ||||||
|  | FuriHalBtHidConsumerReport* consumer_report = NULL; | ||||||
| 
 | 
 | ||||||
| void furi_hal_bt_hid_start() { | void furi_hal_bt_hid_start() { | ||||||
|     // Start device info
 |     // Start device info
 | ||||||
| @ -148,7 +140,8 @@ void furi_hal_bt_hid_start() { | |||||||
|     } |     } | ||||||
|     // Configure HID Keyboard
 |     // Configure HID Keyboard
 | ||||||
|     kb_report = malloc(sizeof(FuriHalBtHidKbReport)); |     kb_report = malloc(sizeof(FuriHalBtHidKbReport)); | ||||||
|     media_report = malloc(sizeof(FuriHalBtHidMediaReport)); |     mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); | ||||||
|  |     consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport)); | ||||||
|     // Configure Report Map characteristic
 |     // Configure Report Map characteristic
 | ||||||
|     hid_svc_update_report_map( |     hid_svc_update_report_map( | ||||||
|         furi_hal_bt_hid_report_map_data, sizeof(furi_hal_bt_hid_report_map_data)); |         furi_hal_bt_hid_report_map_data, sizeof(furi_hal_bt_hid_report_map_data)); | ||||||
| @ -165,6 +158,8 @@ void furi_hal_bt_hid_start() { | |||||||
| 
 | 
 | ||||||
| void furi_hal_bt_hid_stop() { | void furi_hal_bt_hid_stop() { | ||||||
|     furi_assert(kb_report); |     furi_assert(kb_report); | ||||||
|  |     furi_assert(mouse_report); | ||||||
|  |     furi_assert(consumer_report); | ||||||
|     // Stop all services
 |     // Stop all services
 | ||||||
|     if(dev_info_svc_is_started()) { |     if(dev_info_svc_is_started()) { | ||||||
|         dev_info_svc_stop(); |         dev_info_svc_stop(); | ||||||
| @ -176,61 +171,119 @@ void furi_hal_bt_hid_stop() { | |||||||
|         hid_svc_stop(); |         hid_svc_stop(); | ||||||
|     } |     } | ||||||
|     free(kb_report); |     free(kb_report); | ||||||
|     free(media_report); |     free(mouse_report); | ||||||
|     media_report = NULL; |     free(consumer_report); | ||||||
|     kb_report = NULL; |     kb_report = NULL; | ||||||
|  |     mouse_report = NULL; | ||||||
|  |     consumer_report = NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_hid_kb_press(uint16_t button) { | bool furi_hal_bt_hid_kb_press(uint16_t button) { | ||||||
|     furi_assert(kb_report); |     furi_assert(kb_report); | ||||||
|     // kb_report->report_id = 0x01;
 |     for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { | ||||||
|     for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_KEYS_MAX; i++) { |  | ||||||
|         if(kb_report->key[i] == 0) { |         if(kb_report->key[i] == 0) { | ||||||
|             kb_report->key[i] = button & 0xFF; |             kb_report->key[i] = button & 0xFF; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     kb_report->mods |= (button >> 8); |     kb_report->mods |= (button >> 8); | ||||||
|     return hid_svc_update_input_report((uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport)); |     return hid_svc_update_input_report( | ||||||
|  |         ReportNumberKeyboard, (uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_hid_kb_release(uint16_t button) { | bool furi_hal_bt_hid_kb_release(uint16_t button) { | ||||||
|     furi_assert(kb_report); |     furi_assert(kb_report); | ||||||
|     // kb_report->report_id = 0x01;
 |     for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { | ||||||
|     for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_KEYS_MAX; i++) { |  | ||||||
|         if(kb_report->key[i] == (button & 0xFF)) { |         if(kb_report->key[i] == (button & 0xFF)) { | ||||||
|             kb_report->key[i] = 0; |             kb_report->key[i] = 0; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     kb_report->mods &= ~(button >> 8); |     kb_report->mods &= ~(button >> 8); | ||||||
|     return hid_svc_update_input_report((uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport)); |     return hid_svc_update_input_report( | ||||||
|  |         ReportNumberKeyboard, (uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_hid_kb_release_all() { | bool furi_hal_bt_hid_kb_release_all() { | ||||||
|     furi_assert(kb_report); |     furi_assert(kb_report); | ||||||
|     // kb_report->report_id = 0x01;
 |     for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { | ||||||
|     memset(kb_report, 0, sizeof(FuriHalBtHidKbReport)); |         kb_report->key[i] = 0; | ||||||
|     return hid_svc_update_input_report((uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport)); |     } | ||||||
|  |     kb_report->mods = 0; | ||||||
|  |     return hid_svc_update_input_report( | ||||||
|  |         ReportNumberKeyboard, (uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_hid_media_press(uint8_t button) { | bool furi_hal_bt_hid_consumer_key_press(uint16_t button) { | ||||||
|     furi_assert(media_report); |     furi_assert(consumer_report); | ||||||
|     media_report->report_id = 0x02; |     for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { | ||||||
|     media_report->key |= (0x01 << button); |         if(consumer_report->key[i] == 0) { | ||||||
|     return hid_svc_update_input_report((uint8_t*)media_report, sizeof(FuriHalBtHidMediaReport)); |             consumer_report->key[i] = button; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return hid_svc_update_input_report( | ||||||
|  |         ReportNumberConsumer, (uint8_t*)consumer_report, sizeof(FuriHalBtHidConsumerReport)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_hid_media_release(uint8_t button) { | bool furi_hal_bt_hid_consumer_key_release(uint16_t button) { | ||||||
|     furi_assert(media_report); |     furi_assert(consumer_report); | ||||||
|     media_report->report_id = 0x02; |     for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { | ||||||
|     media_report->key &= ~(0x01 << button); |         if(consumer_report->key[i] == button) { | ||||||
|     return hid_svc_update_input_report((uint8_t*)media_report, sizeof(FuriHalBtHidMediaReport)); |             consumer_report->key[i] = 0; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return hid_svc_update_input_report( | ||||||
|  |         ReportNumberConsumer, (uint8_t*)consumer_report, sizeof(FuriHalBtHidConsumerReport)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_hid_media_release_all() { | bool furi_hal_bt_hid_consumer_key_release_all() { | ||||||
|     furi_assert(media_report); |     furi_assert(consumer_report); | ||||||
|     media_report->report_id = 0x02; |     for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { | ||||||
|     media_report->key = 0x00; |         consumer_report->key[i] = 0; | ||||||
|     return hid_svc_update_input_report((uint8_t*)media_report, sizeof(FuriHalBtHidMediaReport)); |     } | ||||||
|  |     return hid_svc_update_input_report( | ||||||
|  |         ReportNumberConsumer, (uint8_t*)consumer_report, sizeof(FuriHalBtHidConsumerReport)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_bt_hid_mouse_move(int8_t dx, int8_t dy) { | ||||||
|  |     furi_assert(mouse_report); | ||||||
|  |     mouse_report->x = dx; | ||||||
|  |     mouse_report->y = dy; | ||||||
|  |     bool state = hid_svc_update_input_report( | ||||||
|  |         ReportNumberMouse, (uint8_t*)mouse_report, sizeof(FuriHalBtHidMouseReport)); | ||||||
|  |     mouse_report->x = 0; | ||||||
|  |     mouse_report->y = 0; | ||||||
|  |     return state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_bt_hid_mouse_press(uint8_t button) { | ||||||
|  |     furi_assert(mouse_report); | ||||||
|  |     mouse_report->btn |= button; | ||||||
|  |     return hid_svc_update_input_report( | ||||||
|  |         ReportNumberMouse, (uint8_t*)mouse_report, sizeof(FuriHalBtHidMouseReport)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_bt_hid_mouse_release(uint8_t button) { | ||||||
|  |     furi_assert(mouse_report); | ||||||
|  |     mouse_report->btn &= ~button; | ||||||
|  |     return hid_svc_update_input_report( | ||||||
|  |         ReportNumberMouse, (uint8_t*)mouse_report, sizeof(FuriHalBtHidMouseReport)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_bt_hid_mouse_release_all() { | ||||||
|  |     furi_assert(mouse_report); | ||||||
|  |     mouse_report->btn = 0; | ||||||
|  |     return hid_svc_update_input_report( | ||||||
|  |         ReportNumberMouse, (uint8_t*)mouse_report, sizeof(FuriHalBtHidMouseReport)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool furi_hal_bt_hid_mouse_scroll(int8_t delta) { | ||||||
|  |     furi_assert(mouse_report); | ||||||
|  |     mouse_report->wheel = delta; | ||||||
|  |     bool state = hid_svc_update_input_report( | ||||||
|  |         ReportNumberMouse, (uint8_t*)mouse_report, sizeof(FuriHalBtHidMouseReport)); | ||||||
|  |     mouse_report->wheel = 0; | ||||||
|  |     return state; | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,11 +6,6 @@ | |||||||
| 
 | 
 | ||||||
| #include "usb.h" | #include "usb.h" | ||||||
| #include "usb_hid.h" | #include "usb_hid.h" | ||||||
| #include "hid_usage_desktop.h" |  | ||||||
| #include "hid_usage_button.h" |  | ||||||
| #include "hid_usage_keyboard.h" |  | ||||||
| #include "hid_usage_consumer.h" |  | ||||||
| #include "hid_usage_led.h" |  | ||||||
| 
 | 
 | ||||||
| #define HID_EP_IN 0x81 | #define HID_EP_IN 0x81 | ||||||
| #define HID_EP_OUT 0x01 | #define HID_EP_OUT 0x01 | ||||||
|  | |||||||
| @ -3,17 +3,6 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
| enum FuriHalBtHidMediKeys { |  | ||||||
|     FuriHalBtHidMediaScanNext, |  | ||||||
|     FuriHalBtHidMediaScanPrevious, |  | ||||||
|     FuriHalBtHidMediaStop, |  | ||||||
|     FuriHalBtHidMediaEject, |  | ||||||
|     FuriHalBtHidMediaPlayPause, |  | ||||||
|     FuriHalBtHidMediaMute, |  | ||||||
|     FuriHalBtHidMediaVolumeUp, |  | ||||||
|     FuriHalBtHidMediaVolumeDown, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /** Start Hid Keyboard Profile
 | /** Start Hid Keyboard Profile
 | ||||||
|  */ |  */ | ||||||
| void furi_hal_bt_hid_start(); | void furi_hal_bt_hid_start(); | ||||||
| @ -44,20 +33,51 @@ bool furi_hal_bt_hid_kb_release(uint16_t button); | |||||||
|  */ |  */ | ||||||
| bool furi_hal_bt_hid_kb_release_all(); | bool furi_hal_bt_hid_kb_release_all(); | ||||||
| 
 | 
 | ||||||
| /** Release all media buttons
 | /** Set mouse movement and send HID report
 | ||||||
|  * |  * | ||||||
|  * @return          true on success |  * @param      dx  x coordinate delta | ||||||
|  |  * @param      dy  y coordinate delta | ||||||
|  */ |  */ | ||||||
| bool furi_hal_bt_hid_media_press(uint8_t button); | bool furi_hal_bt_hid_mouse_move(int8_t dx, int8_t dy); | ||||||
| 
 | 
 | ||||||
| /** Release all media buttons
 | /** Set mouse button to pressed state and send HID report
 | ||||||
|  * |  * | ||||||
|  * @return          true on success |  * @param      button  key code | ||||||
|  */ |  */ | ||||||
| bool furi_hal_bt_hid_media_release(uint8_t button); | bool furi_hal_bt_hid_mouse_press(uint8_t button); | ||||||
| 
 | 
 | ||||||
| /** Release all media buttons
 | /** Set mouse button to released state and send HID report
 | ||||||
|  * |  * | ||||||
|  * @return          true on success |  * @param      button  key code | ||||||
|  */ |  */ | ||||||
| bool furi_hal_bt_hid_media_release_all(); | bool furi_hal_bt_hid_mouse_release(uint8_t button); | ||||||
|  | 
 | ||||||
|  | /** Set mouse button to released state and send HID report
 | ||||||
|  |  * | ||||||
|  |  * @param      button  key code | ||||||
|  |  */ | ||||||
|  | bool furi_hal_bt_hid_mouse_release_all(); | ||||||
|  | 
 | ||||||
|  | /** Set mouse wheel position and send HID report
 | ||||||
|  |  * | ||||||
|  |  * @param      delta  number of scroll steps | ||||||
|  |  */ | ||||||
|  | bool furi_hal_bt_hid_mouse_scroll(int8_t delta); | ||||||
|  | 
 | ||||||
|  | /** Set the following consumer key to pressed state and send HID report
 | ||||||
|  |  * | ||||||
|  |  * @param      button  key code | ||||||
|  |  */ | ||||||
|  | bool furi_hal_bt_hid_consumer_key_press(uint16_t button); | ||||||
|  | 
 | ||||||
|  | /** Set the following consumer key to released state and send HID report
 | ||||||
|  |  * | ||||||
|  |  * @param      button  key code | ||||||
|  |  */ | ||||||
|  | bool furi_hal_bt_hid_consumer_key_release(uint16_t button); | ||||||
|  | 
 | ||||||
|  | /** Set consumer key to released state and send HID report
 | ||||||
|  |  * | ||||||
|  |  * @param      button  key code | ||||||
|  |  */ | ||||||
|  | bool furi_hal_bt_hid_consumer_key_release_all(); | ||||||
|  | |||||||
| @ -1,110 +1,13 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  | #include "hid_usage_desktop.h" | ||||||
|  | #include "hid_usage_button.h" | ||||||
|  | #include "hid_usage_keyboard.h" | ||||||
|  | #include "hid_usage_consumer.h" | ||||||
|  | #include "hid_usage_led.h" | ||||||
| 
 | 
 | ||||||
| /** HID keyboard key codes */ | #define HID_KEYBOARD_NONE 0x00 | ||||||
| enum HidKeyboardKeys { | // Remapping the colon key which is shift + ; to comma
 | ||||||
|     KEY_NONE = 0x00, | #define HID_KEYBOARD_COMMA HID_KEYBOARD_COLON | ||||||
|     KEY_ERROR_ROLLOVER = 0x01, |  | ||||||
|     KEY_POST_FAIL = 0x02, |  | ||||||
|     KEY_ERROR_UNDEFINED = 0x03, |  | ||||||
|     KEY_A = 0x04, |  | ||||||
|     KEY_B = 0x05, |  | ||||||
|     KEY_C = 0x06, |  | ||||||
|     KEY_D = 0x07, |  | ||||||
|     KEY_E = 0x08, |  | ||||||
|     KEY_F = 0x09, |  | ||||||
|     KEY_G = 0x0A, |  | ||||||
|     KEY_H = 0x0B, |  | ||||||
|     KEY_I = 0x0C, |  | ||||||
|     KEY_J = 0x0D, |  | ||||||
|     KEY_K = 0x0E, |  | ||||||
|     KEY_L = 0x0F, |  | ||||||
|     KEY_M = 0x10, |  | ||||||
|     KEY_N = 0x11, |  | ||||||
|     KEY_O = 0x12, |  | ||||||
|     KEY_P = 0x13, |  | ||||||
|     KEY_Q = 0x14, |  | ||||||
|     KEY_R = 0x15, |  | ||||||
|     KEY_S = 0x16, |  | ||||||
|     KEY_T = 0x17, |  | ||||||
|     KEY_U = 0x18, |  | ||||||
|     KEY_V = 0x19, |  | ||||||
|     KEY_W = 0x1A, |  | ||||||
|     KEY_X = 0x1B, |  | ||||||
|     KEY_Y = 0x1C, |  | ||||||
|     KEY_Z = 0x1D, |  | ||||||
|     KEY_1 = 0x1E, |  | ||||||
|     KEY_2 = 0x1F, |  | ||||||
|     KEY_3 = 0x20, |  | ||||||
|     KEY_4 = 0x21, |  | ||||||
|     KEY_5 = 0x22, |  | ||||||
|     KEY_6 = 0x23, |  | ||||||
|     KEY_7 = 0x24, |  | ||||||
|     KEY_8 = 0x25, |  | ||||||
|     KEY_9 = 0x26, |  | ||||||
|     KEY_0 = 0x27, |  | ||||||
|     KEY_ENTER = 0x28, |  | ||||||
|     KEY_ESC = 0x29, |  | ||||||
|     KEY_BACKSPACE = 0x2A, |  | ||||||
|     KEY_TAB = 0x2B, |  | ||||||
|     KEY_SPACE = 0x2C, |  | ||||||
|     KEY_MINUS = 0x2D, |  | ||||||
|     KEY_EQUAL = 0x2E, |  | ||||||
|     KEY_LEFT_BRACE = 0x2F, |  | ||||||
|     KEY_RIGHT_BRACE = 0x30, |  | ||||||
|     KEY_BACKSLASH = 0x31, |  | ||||||
|     KEY_NON_US_NUM = 0x32, |  | ||||||
|     KEY_SEMICOLON = 0x33, |  | ||||||
|     KEY_QUOTE = 0x34, |  | ||||||
|     KEY_TILDE = 0x35, |  | ||||||
|     KEY_COMMA = 0x36, |  | ||||||
|     KEY_PERIOD = 0x37, |  | ||||||
|     KEY_SLASH = 0x38, |  | ||||||
|     KEY_CAPS_LOCK = 0x39, |  | ||||||
|     KEY_F1 = 0x3A, |  | ||||||
|     KEY_F2 = 0x3B, |  | ||||||
|     KEY_F3 = 0x3C, |  | ||||||
|     KEY_F4 = 0x3D, |  | ||||||
|     KEY_F5 = 0x3E, |  | ||||||
|     KEY_F6 = 0x3F, |  | ||||||
|     KEY_F7 = 0x40, |  | ||||||
|     KEY_F8 = 0x41, |  | ||||||
|     KEY_F9 = 0x42, |  | ||||||
|     KEY_F10 = 0x43, |  | ||||||
|     KEY_F11 = 0x44, |  | ||||||
|     KEY_F12 = 0x45, |  | ||||||
|     KEY_PRINT = 0x46, |  | ||||||
|     KEY_SCROLL_LOCK = 0x47, |  | ||||||
|     KEY_PAUSE = 0x48, |  | ||||||
|     KEY_INSERT = 0x49, |  | ||||||
|     KEY_HOME = 0x4A, |  | ||||||
|     KEY_PAGE_UP = 0x4B, |  | ||||||
|     KEY_DELETE = 0x4C, |  | ||||||
|     KEY_END = 0x4D, |  | ||||||
|     KEY_PAGE_DOWN = 0x4E, |  | ||||||
|     KEY_RIGHT_ARROW = 0x4F, |  | ||||||
|     KEY_LEFT_ARROW = 0x50, |  | ||||||
|     KEY_DOWN_ARROW = 0x51, |  | ||||||
|     KEY_UP_ARROW = 0x52, |  | ||||||
|     KEY_NUM_LOCK = 0x53, |  | ||||||
|     KEYPAD_DIVIDE = 0x54, |  | ||||||
|     KEYPAD_MULTIPLY = 0x55, |  | ||||||
|     KEYPAD_SUBTRACT = 0x56, |  | ||||||
|     KEYPAD_ADD = 0x57, |  | ||||||
|     KEYPAD_ENTER = 0x58, |  | ||||||
|     KEYPAD_1 = 0x59, |  | ||||||
|     KEYPAD_2 = 0x5A, |  | ||||||
|     KEYPAD_3 = 0x5B, |  | ||||||
|     KEYPAD_4 = 0x5C, |  | ||||||
|     KEYPAD_5 = 0x5D, |  | ||||||
|     KEYPAD_6 = 0x5E, |  | ||||||
|     KEYPAD_7 = 0x5F, |  | ||||||
|     KEYPAD_8 = 0x60, |  | ||||||
|     KEYPAD_9 = 0x61, |  | ||||||
|     KEYPAD_0 = 0x62, |  | ||||||
|     KEYPAD_DOT = 0x63, |  | ||||||
|     KEY_NON_US = 0x64, |  | ||||||
|     KEY_APPLICATION = 0x65, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| /** HID keyboard modifier keys */ | /** HID keyboard modifier keys */ | ||||||
| enum HidKeyboardMods { | enum HidKeyboardMods { | ||||||
| @ -120,134 +23,134 @@ enum HidKeyboardMods { | |||||||
| 
 | 
 | ||||||
| /** ASCII to keycode conversion table */ | /** ASCII to keycode conversion table */ | ||||||
| static const uint16_t hid_asciimap[] = { | static const uint16_t hid_asciimap[] = { | ||||||
|     KEY_NONE, // NUL
 |     HID_KEYBOARD_NONE, // NUL
 | ||||||
|     KEY_NONE, // SOH
 |     HID_KEYBOARD_NONE, // SOH
 | ||||||
|     KEY_NONE, // STX
 |     HID_KEYBOARD_NONE, // STX
 | ||||||
|     KEY_NONE, // ETX
 |     HID_KEYBOARD_NONE, // ETX
 | ||||||
|     KEY_NONE, // EOT
 |     HID_KEYBOARD_NONE, // EOT
 | ||||||
|     KEY_NONE, // ENQ
 |     HID_KEYBOARD_NONE, // ENQ
 | ||||||
|     KEY_NONE, // ACK
 |     HID_KEYBOARD_NONE, // ACK
 | ||||||
|     KEY_NONE, // BEL
 |     HID_KEYBOARD_NONE, // BEL
 | ||||||
|     KEY_BACKSPACE, // BS   Backspace
 |     HID_KEYBOARD_DELETE, // BS   Backspace
 | ||||||
|     KEY_TAB, // TAB  Tab
 |     HID_KEYBOARD_TAB, // TAB  Tab
 | ||||||
|     KEY_ENTER, // LF   Enter
 |     HID_KEYBOARD_RETURN, // LF   Enter
 | ||||||
|     KEY_NONE, // VT
 |     HID_KEYBOARD_NONE, // VT
 | ||||||
|     KEY_NONE, // FF
 |     HID_KEYBOARD_NONE, // FF
 | ||||||
|     KEY_NONE, // CR
 |     HID_KEYBOARD_NONE, // CR
 | ||||||
|     KEY_NONE, // SO
 |     HID_KEYBOARD_NONE, // SO
 | ||||||
|     KEY_NONE, // SI
 |     HID_KEYBOARD_NONE, // SI
 | ||||||
|     KEY_NONE, // DEL
 |     HID_KEYBOARD_NONE, // DEL
 | ||||||
|     KEY_NONE, // DC1
 |     HID_KEYBOARD_NONE, // DC1
 | ||||||
|     KEY_NONE, // DC2
 |     HID_KEYBOARD_NONE, // DC2
 | ||||||
|     KEY_NONE, // DC3
 |     HID_KEYBOARD_NONE, // DC3
 | ||||||
|     KEY_NONE, // DC4
 |     HID_KEYBOARD_NONE, // DC4
 | ||||||
|     KEY_NONE, // NAK
 |     HID_KEYBOARD_NONE, // NAK
 | ||||||
|     KEY_NONE, // SYN
 |     HID_KEYBOARD_NONE, // SYN
 | ||||||
|     KEY_NONE, // ETB
 |     HID_KEYBOARD_NONE, // ETB
 | ||||||
|     KEY_NONE, // CAN
 |     HID_KEYBOARD_NONE, // CAN
 | ||||||
|     KEY_NONE, // EM
 |     HID_KEYBOARD_NONE, // EM
 | ||||||
|     KEY_NONE, // SUB
 |     HID_KEYBOARD_NONE, // SUB
 | ||||||
|     KEY_NONE, // ESC
 |     HID_KEYBOARD_NONE, // ESC
 | ||||||
|     KEY_NONE, // FS
 |     HID_KEYBOARD_NONE, // FS
 | ||||||
|     KEY_NONE, // GS
 |     HID_KEYBOARD_NONE, // GS
 | ||||||
|     KEY_NONE, // RS
 |     HID_KEYBOARD_NONE, // RS
 | ||||||
|     KEY_NONE, // US
 |     HID_KEYBOARD_NONE, // US
 | ||||||
|     KEY_SPACE, // ' ' Space
 |     HID_KEYBOARD_SPACEBAR, // ' ' Space
 | ||||||
|     KEY_1 | KEY_MOD_LEFT_SHIFT, // !
 |     HID_KEYBOARD_1 | KEY_MOD_LEFT_SHIFT, // !
 | ||||||
|     KEY_QUOTE | KEY_MOD_LEFT_SHIFT, // "
 |     HID_KEYBOARD_APOSTROPHE | KEY_MOD_LEFT_SHIFT, // "
 | ||||||
|     KEY_3 | KEY_MOD_LEFT_SHIFT, // #
 |     HID_KEYBOARD_3 | KEY_MOD_LEFT_SHIFT, // #
 | ||||||
|     KEY_4 | KEY_MOD_LEFT_SHIFT, // $
 |     HID_KEYBOARD_4 | KEY_MOD_LEFT_SHIFT, // $
 | ||||||
|     KEY_5 | KEY_MOD_LEFT_SHIFT, // %
 |     HID_KEYBOARD_5 | KEY_MOD_LEFT_SHIFT, // %
 | ||||||
|     KEY_7 | KEY_MOD_LEFT_SHIFT, // &
 |     HID_KEYBOARD_7 | KEY_MOD_LEFT_SHIFT, // &
 | ||||||
|     KEY_QUOTE, // '
 |     HID_KEYBOARD_APOSTROPHE, // '
 | ||||||
|     KEY_9 | KEY_MOD_LEFT_SHIFT, // (
 |     HID_KEYBOARD_9 | KEY_MOD_LEFT_SHIFT, // (
 | ||||||
|     KEY_0 | KEY_MOD_LEFT_SHIFT, // )
 |     HID_KEYBOARD_0 | KEY_MOD_LEFT_SHIFT, // )
 | ||||||
|     KEY_8 | KEY_MOD_LEFT_SHIFT, // *
 |     HID_KEYBOARD_8 | KEY_MOD_LEFT_SHIFT, // *
 | ||||||
|     KEY_EQUAL | KEY_MOD_LEFT_SHIFT, // +
 |     HID_KEYBOARD_EQUAL_SIGN | KEY_MOD_LEFT_SHIFT, // +
 | ||||||
|     KEY_COMMA, // ,
 |     HID_KEYBOARD_COMMA, // ,
 | ||||||
|     KEY_MINUS, // -
 |     HID_KEYBOARD_MINUS, // -
 | ||||||
|     KEY_PERIOD, // .
 |     HID_KEYBOARD_DOT, // .
 | ||||||
|     KEY_SLASH, // /
 |     HID_KEYBOARD_SLASH, // /
 | ||||||
|     KEY_0, // 0
 |     HID_KEYBOARD_0, // 0
 | ||||||
|     KEY_1, // 1
 |     HID_KEYBOARD_1, // 1
 | ||||||
|     KEY_2, // 2
 |     HID_KEYBOARD_2, // 2
 | ||||||
|     KEY_3, // 3
 |     HID_KEYBOARD_3, // 3
 | ||||||
|     KEY_4, // 4
 |     HID_KEYBOARD_4, // 4
 | ||||||
|     KEY_5, // 5
 |     HID_KEYBOARD_5, // 5
 | ||||||
|     KEY_6, // 6
 |     HID_KEYBOARD_6, // 6
 | ||||||
|     KEY_7, // 7
 |     HID_KEYBOARD_7, // 7
 | ||||||
|     KEY_8, // 8
 |     HID_KEYBOARD_8, // 8
 | ||||||
|     KEY_9, // 9
 |     HID_KEYBOARD_9, // 9
 | ||||||
|     KEY_SEMICOLON | KEY_MOD_LEFT_SHIFT, // :
 |     HID_KEYBOARD_SEMICOLON | KEY_MOD_LEFT_SHIFT, // :
 | ||||||
|     KEY_SEMICOLON, // ;
 |     HID_KEYBOARD_SEMICOLON, // ;
 | ||||||
|     KEY_COMMA | KEY_MOD_LEFT_SHIFT, // <
 |     HID_KEYBOARD_COMMA | KEY_MOD_LEFT_SHIFT, // <
 | ||||||
|     KEY_EQUAL, // =
 |     HID_KEYBOARD_EQUAL_SIGN, // =
 | ||||||
|     KEY_PERIOD | KEY_MOD_LEFT_SHIFT, // >
 |     HID_KEYBOARD_DOT | KEY_MOD_LEFT_SHIFT, // >
 | ||||||
|     KEY_SLASH | KEY_MOD_LEFT_SHIFT, // ?
 |     HID_KEYBOARD_SLASH | KEY_MOD_LEFT_SHIFT, // ?
 | ||||||
|     KEY_2 | KEY_MOD_LEFT_SHIFT, // @
 |     HID_KEYBOARD_2 | KEY_MOD_LEFT_SHIFT, // @
 | ||||||
|     KEY_A | KEY_MOD_LEFT_SHIFT, // A
 |     HID_KEYBOARD_A | KEY_MOD_LEFT_SHIFT, // A
 | ||||||
|     KEY_B | KEY_MOD_LEFT_SHIFT, // B
 |     HID_KEYBOARD_B | KEY_MOD_LEFT_SHIFT, // B
 | ||||||
|     KEY_C | KEY_MOD_LEFT_SHIFT, // C
 |     HID_KEYBOARD_C | KEY_MOD_LEFT_SHIFT, // C
 | ||||||
|     KEY_D | KEY_MOD_LEFT_SHIFT, // D
 |     HID_KEYBOARD_D | KEY_MOD_LEFT_SHIFT, // D
 | ||||||
|     KEY_E | KEY_MOD_LEFT_SHIFT, // E
 |     HID_KEYBOARD_E | KEY_MOD_LEFT_SHIFT, // E
 | ||||||
|     KEY_F | KEY_MOD_LEFT_SHIFT, // F
 |     HID_KEYBOARD_F | KEY_MOD_LEFT_SHIFT, // F
 | ||||||
|     KEY_G | KEY_MOD_LEFT_SHIFT, // G
 |     HID_KEYBOARD_G | KEY_MOD_LEFT_SHIFT, // G
 | ||||||
|     KEY_H | KEY_MOD_LEFT_SHIFT, // H
 |     HID_KEYBOARD_H | KEY_MOD_LEFT_SHIFT, // H
 | ||||||
|     KEY_I | KEY_MOD_LEFT_SHIFT, // I
 |     HID_KEYBOARD_I | KEY_MOD_LEFT_SHIFT, // I
 | ||||||
|     KEY_J | KEY_MOD_LEFT_SHIFT, // J
 |     HID_KEYBOARD_J | KEY_MOD_LEFT_SHIFT, // J
 | ||||||
|     KEY_K | KEY_MOD_LEFT_SHIFT, // K
 |     HID_KEYBOARD_K | KEY_MOD_LEFT_SHIFT, // K
 | ||||||
|     KEY_L | KEY_MOD_LEFT_SHIFT, // L
 |     HID_KEYBOARD_L | KEY_MOD_LEFT_SHIFT, // L
 | ||||||
|     KEY_M | KEY_MOD_LEFT_SHIFT, // M
 |     HID_KEYBOARD_M | KEY_MOD_LEFT_SHIFT, // M
 | ||||||
|     KEY_N | KEY_MOD_LEFT_SHIFT, // N
 |     HID_KEYBOARD_N | KEY_MOD_LEFT_SHIFT, // N
 | ||||||
|     KEY_O | KEY_MOD_LEFT_SHIFT, // O
 |     HID_KEYBOARD_O | KEY_MOD_LEFT_SHIFT, // O
 | ||||||
|     KEY_P | KEY_MOD_LEFT_SHIFT, // P
 |     HID_KEYBOARD_P | KEY_MOD_LEFT_SHIFT, // P
 | ||||||
|     KEY_Q | KEY_MOD_LEFT_SHIFT, // Q
 |     HID_KEYBOARD_Q | KEY_MOD_LEFT_SHIFT, // Q
 | ||||||
|     KEY_R | KEY_MOD_LEFT_SHIFT, // R
 |     HID_KEYBOARD_R | KEY_MOD_LEFT_SHIFT, // R
 | ||||||
|     KEY_S | KEY_MOD_LEFT_SHIFT, // S
 |     HID_KEYBOARD_S | KEY_MOD_LEFT_SHIFT, // S
 | ||||||
|     KEY_T | KEY_MOD_LEFT_SHIFT, // T
 |     HID_KEYBOARD_T | KEY_MOD_LEFT_SHIFT, // T
 | ||||||
|     KEY_U | KEY_MOD_LEFT_SHIFT, // U
 |     HID_KEYBOARD_U | KEY_MOD_LEFT_SHIFT, // U
 | ||||||
|     KEY_V | KEY_MOD_LEFT_SHIFT, // V
 |     HID_KEYBOARD_V | KEY_MOD_LEFT_SHIFT, // V
 | ||||||
|     KEY_W | KEY_MOD_LEFT_SHIFT, // W
 |     HID_KEYBOARD_W | KEY_MOD_LEFT_SHIFT, // W
 | ||||||
|     KEY_X | KEY_MOD_LEFT_SHIFT, // X
 |     HID_KEYBOARD_X | KEY_MOD_LEFT_SHIFT, // X
 | ||||||
|     KEY_Y | KEY_MOD_LEFT_SHIFT, // Y
 |     HID_KEYBOARD_Y | KEY_MOD_LEFT_SHIFT, // Y
 | ||||||
|     KEY_Z | KEY_MOD_LEFT_SHIFT, // Z
 |     HID_KEYBOARD_Z | KEY_MOD_LEFT_SHIFT, // Z
 | ||||||
|     KEY_LEFT_BRACE, // [
 |     HID_KEYBOARD_OPEN_BRACKET, // [
 | ||||||
|     KEY_BACKSLASH, // bslash
 |     HID_KEYBOARD_BACKSLASH, // bslash
 | ||||||
|     KEY_RIGHT_BRACE, // ]
 |     HID_KEYBOARD_CLOSE_BRACKET, // ]
 | ||||||
|     KEY_6 | KEY_MOD_LEFT_SHIFT, // ^
 |     HID_KEYBOARD_6 | KEY_MOD_LEFT_SHIFT, // ^
 | ||||||
|     KEY_MINUS | KEY_MOD_LEFT_SHIFT, // _
 |     HID_KEYBOARD_MINUS | KEY_MOD_LEFT_SHIFT, // _
 | ||||||
|     KEY_TILDE, // `
 |     HID_KEYBOARD_GRAVE_ACCENT, // `
 | ||||||
|     KEY_A, // a
 |     HID_KEYBOARD_A, // a
 | ||||||
|     KEY_B, // b
 |     HID_KEYBOARD_B, // b
 | ||||||
|     KEY_C, // c
 |     HID_KEYBOARD_C, // c
 | ||||||
|     KEY_D, // d
 |     HID_KEYBOARD_D, // d
 | ||||||
|     KEY_E, // e
 |     HID_KEYBOARD_E, // e
 | ||||||
|     KEY_F, // f
 |     HID_KEYBOARD_F, // f
 | ||||||
|     KEY_G, // g
 |     HID_KEYBOARD_G, // g
 | ||||||
|     KEY_H, // h
 |     HID_KEYBOARD_H, // h
 | ||||||
|     KEY_I, // i
 |     HID_KEYBOARD_I, // i
 | ||||||
|     KEY_J, // j
 |     HID_KEYBOARD_J, // j
 | ||||||
|     KEY_K, // k
 |     HID_KEYBOARD_K, // k
 | ||||||
|     KEY_L, // l
 |     HID_KEYBOARD_L, // l
 | ||||||
|     KEY_M, // m
 |     HID_KEYBOARD_M, // m
 | ||||||
|     KEY_N, // n
 |     HID_KEYBOARD_N, // n
 | ||||||
|     KEY_O, // o
 |     HID_KEYBOARD_O, // o
 | ||||||
|     KEY_P, // p
 |     HID_KEYBOARD_P, // p
 | ||||||
|     KEY_Q, // q
 |     HID_KEYBOARD_Q, // q
 | ||||||
|     KEY_R, // r
 |     HID_KEYBOARD_R, // r
 | ||||||
|     KEY_S, // s
 |     HID_KEYBOARD_S, // s
 | ||||||
|     KEY_T, // t
 |     HID_KEYBOARD_T, // t
 | ||||||
|     KEY_U, // u
 |     HID_KEYBOARD_U, // u
 | ||||||
|     KEY_V, // v
 |     HID_KEYBOARD_V, // v
 | ||||||
|     KEY_W, // w
 |     HID_KEYBOARD_W, // w
 | ||||||
|     KEY_X, // x
 |     HID_KEYBOARD_X, // x
 | ||||||
|     KEY_Y, // y
 |     HID_KEYBOARD_Y, // y
 | ||||||
|     KEY_Z, // z
 |     HID_KEYBOARD_Z, // z
 | ||||||
|     KEY_LEFT_BRACE | KEY_MOD_LEFT_SHIFT, // {
 |     HID_KEYBOARD_OPEN_BRACKET | KEY_MOD_LEFT_SHIFT, // {
 | ||||||
|     KEY_BACKSLASH | KEY_MOD_LEFT_SHIFT, // |
 |     HID_KEYBOARD_BACKSLASH | KEY_MOD_LEFT_SHIFT, // |
 | ||||||
|     KEY_RIGHT_BRACE | KEY_MOD_LEFT_SHIFT, // }
 |     HID_KEYBOARD_CLOSE_BRACKET | KEY_MOD_LEFT_SHIFT, // }
 | ||||||
|     KEY_TILDE | KEY_MOD_LEFT_SHIFT, // ~
 |     HID_KEYBOARD_GRAVE_ACCENT | KEY_MOD_LEFT_SHIFT, // ~
 | ||||||
|     KEY_NONE, // DEL
 |     HID_KEYBOARD_NONE, // DEL
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -260,7 +163,7 @@ typedef struct { | |||||||
| typedef void (*HidStateCallback)(bool state, void* context); | typedef void (*HidStateCallback)(bool state, void* context); | ||||||
| 
 | 
 | ||||||
| /** ASCII to keycode conversion macro */ | /** ASCII to keycode conversion macro */ | ||||||
| #define HID_ASCII_TO_KEY(x) (((uint8_t)x < 128) ? (hid_asciimap[(uint8_t)x]) : KEY_NONE) | #define HID_ASCII_TO_KEY(x) (((uint8_t)x < 128) ? (hid_asciimap[(uint8_t)x]) : HID_KEYBOARD_NONE) | ||||||
| 
 | 
 | ||||||
| /** HID keyboard leds */ | /** HID keyboard leds */ | ||||||
| enum HidKeyboardLeds { | enum HidKeyboardLeds { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Michael Marcucci
						Michael Marcucci