|
|
@@ -0,0 +1,686 @@
|
|
|
+#include "usb_ccb_start.h"
|
|
|
+#include <furi.h>
|
|
|
+#include <furi_hal_usb_hid.h>
|
|
|
+#include <gui/elements.h>
|
|
|
+
|
|
|
+struct UsbCcbStart {
|
|
|
+ View* view;
|
|
|
+};
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ bool left_pressed;
|
|
|
+ bool up_pressed;
|
|
|
+ bool right_pressed;
|
|
|
+ bool down_pressed;
|
|
|
+ bool ok_pressed;
|
|
|
+ bool back_pressed;
|
|
|
+ bool connected;
|
|
|
+} UsbCcbStartModel;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ uint16_t value;
|
|
|
+ const char* name;
|
|
|
+} HID_CONSUMER;
|
|
|
+
|
|
|
+// All of the strings are prefixed with "HID_CONSUMER_", which has been removed to save some space.
|
|
|
+HID_CONSUMER hidConsumerArray[] = {
|
|
|
+ {0x00, "UNASSIGNED"}, // Generic Consumer Control Device
|
|
|
+ {0x01, "CONTROL"},
|
|
|
+ {0x02, "NUMERIC_KEY_PAD"},
|
|
|
+ {0x03, "PROGRAMMABLE_BUTTONS"},
|
|
|
+ {0x04, "MICROPHONE"},
|
|
|
+ {0x05, "HEADPHONE"},
|
|
|
+ {0x06, "GRAPHIC_EQUALIZER"},
|
|
|
+ {0x20, "PLUS_10"}, // Numeric Key Pad
|
|
|
+ {0x21, "PLUS_100"},
|
|
|
+ {0x22, "AM_PM"},
|
|
|
+ {0x30, "POWER"}, // General Controls
|
|
|
+ {0x31, "RESET"},
|
|
|
+ {0x32, "SLEEP"},
|
|
|
+ {0x33, "SLEEP_AFTER"},
|
|
|
+ {0x34, "SLEEP_MODE"},
|
|
|
+ {0x35, "ILLUMINATION"},
|
|
|
+ {0x36, "FUNCTION_BUTTONS"},
|
|
|
+ {0x40, "MENU"}, // Menu Controls
|
|
|
+ {0x41, "MENU_PICK"},
|
|
|
+ {0x42, "MENU_UP"},
|
|
|
+ {0x43, "MENU_DOWN"},
|
|
|
+ {0x44, "MENU_LEFT"},
|
|
|
+ {0x45, "MENU_RIGHT"},
|
|
|
+ {0x46, "MENU_ESCAPE"},
|
|
|
+ {0x47, "MENU_VALUE_INCREASE"},
|
|
|
+ {0x48, "MENU_VALUE_DECREASE"},
|
|
|
+ {0x60, "DATA_ON_SCREEN"}, // Display Controls
|
|
|
+ {0x61, "CLOSED_CAPTION"},
|
|
|
+ {0x62, "CLOSED_CAPTION_SELECT"},
|
|
|
+ {0x63, "VCR_TV"},
|
|
|
+ {0x64, "BROADCAST_MODE"},
|
|
|
+ {0x65, "SNAPSHOT"},
|
|
|
+ {0x66, "STILL"},
|
|
|
+ {0x80, "SELECTION"}, // Selection Controls
|
|
|
+ {0x81, "ASSIGN_SELECTION"},
|
|
|
+ {0x82, "MODE_STEP"},
|
|
|
+ {0x83, "RECALL_LAST"},
|
|
|
+ {0x84, "ENTER_CHANNEL"},
|
|
|
+ {0x85, "ORDER_MOVIE"},
|
|
|
+ {0x86, "CHANNEL"},
|
|
|
+ {0x87, "MEDIA_SELECTION"},
|
|
|
+ {0x88, "MEDIA_SELECT_COMPUTER"},
|
|
|
+ {0x89, "MEDIA_SELECT_TV"},
|
|
|
+ {0x8A, "MEDIA_SELECT_WWW"},
|
|
|
+ {0x8B, "MEDIA_SELECT_DVD"},
|
|
|
+ {0x8C, "MEDIA_SELECT_TELEPHONE"},
|
|
|
+ {0x8D, "MEDIA_SELECT_PROGRAM_GUIDE"},
|
|
|
+ {0x8E, "MEDIA_SELECT_VIDEO_PHONE"},
|
|
|
+ {0x8F, "MEDIA_SELECT_GAMES"},
|
|
|
+ {0x90, "MEDIA_SELECT_MESSAGES"},
|
|
|
+ {0x91, "MEDIA_SELECT_CD"},
|
|
|
+ {0x92, "MEDIA_SELECT_VCR"},
|
|
|
+ {0x93, "MEDIA_SELECT_TUNER"},
|
|
|
+ {0x94, "QUIT"},
|
|
|
+ {0x95, "HELP"},
|
|
|
+ {0x96, "MEDIA_SELECT_TAPE"},
|
|
|
+ {0x97, "MEDIA_SELECT_CABLE"},
|
|
|
+ {0x98, "MEDIA_SELECT_SATELLITE"},
|
|
|
+ {0x99, "MEDIA_SELECT_SECURITY"},
|
|
|
+ {0x9A, "MEDIA_SELECT_HOME"},
|
|
|
+ {0x9B, "MEDIA_SELECT_CALL"},
|
|
|
+ {0x9C, "CHANNEL_INCREMENT"},
|
|
|
+ {0x9D, "CHANNEL_DECREMENT"},
|
|
|
+ {0x9E, "MEDIA_SELECT_SAP"},
|
|
|
+ {0xA0, "VCR_PLUS"},
|
|
|
+ {0xA1, "ONCE"},
|
|
|
+ {0xA2, "DAILY"},
|
|
|
+ {0xA3, "WEEKLY"},
|
|
|
+ {0xA4, "MONTHLY"},
|
|
|
+ {0xB0, "PLAY"}, // Transport Controls
|
|
|
+ {0xB1, "PAUSE"},
|
|
|
+ {0xB2, "RECORD"},
|
|
|
+ {0xB3, "FAST_FORWARD"},
|
|
|
+ {0xB4, "REWIND"},
|
|
|
+ {0xB5, "SCAN_NEXT_TRACK"},
|
|
|
+ {0xB6, "SCAN_PREVIOUS_TRACK"},
|
|
|
+ {0xB7, "STOP"},
|
|
|
+ {0xB8, "EJECT"},
|
|
|
+ {0xB9, "RANDOM_PLAY"},
|
|
|
+ {0xBA, "SELECT_DISC"},
|
|
|
+ {0xBB, "ENTER_DISC"},
|
|
|
+ {0xBC, "REPEAT"},
|
|
|
+ {0xBD, "TRACKING"},
|
|
|
+ {0xBE, "TRACK_NORMAL"},
|
|
|
+ {0xBF, "SLOW_TRACKING"},
|
|
|
+ {0xC0, "FRAME_FORWARD"},
|
|
|
+ {0xC1, "FRAME_BACK"},
|
|
|
+ {0xC2, "MARK"}, // Search Controls
|
|
|
+ {0xC3, "CLEAR_MARK"},
|
|
|
+ {0xC4, "REPEAT_FROM_MARK"},
|
|
|
+ {0xC5, "RETURN_TO_MARK"},
|
|
|
+ {0xC6, "SEARCH_MARK_FORWARD"},
|
|
|
+ {0xC7, "SEARCH_MARK_BACKWARDS"},
|
|
|
+ {0xC8, "COUNTER_RESET"},
|
|
|
+ {0xC9, "SHOW_COUNTER"},
|
|
|
+ {0xCA, "TRACKING_INCREMENT"},
|
|
|
+ {0xCB, "TRACKING_DECREMENT"},
|
|
|
+ {0xCC, "STOP_EJECT"},
|
|
|
+ {0xCD, "PLAY_PAUSE"},
|
|
|
+ {0xCE, "PLAY_SKIP"},
|
|
|
+ {0xE0, "VOLUME"}, // Audio Controls
|
|
|
+ {0xE1, "BALANCE"},
|
|
|
+ {0xE2, "MUTE"},
|
|
|
+ {0xE3, "BASS"},
|
|
|
+ {0xE4, "TREBLE"},
|
|
|
+ {0xE5, "BASS_BOOST"},
|
|
|
+ {0xE6, "SURROUND_MODE"},
|
|
|
+ {0xE7, "LOUDNESS"},
|
|
|
+ {0xE8, "MPX"},
|
|
|
+ {0xE9, "VOLUME_INCREMENT"},
|
|
|
+ {0xEA, "VOLUME_DECREMENT"},
|
|
|
+ {0xF0, "SPEED_SELECT"}, // Speed Controls
|
|
|
+ {0xF1, "PLAYBACK_SPEED"},
|
|
|
+ {0xF2, "STANDARD_PLAY"},
|
|
|
+ {0xF3, "LONG_PLAY"},
|
|
|
+ {0xF4, "EXTENDED_PLAY"},
|
|
|
+ {0xF5, "SLOW"},
|
|
|
+ {0x100, "FAN_ENABLE"}, // Home and Security Controls
|
|
|
+ {0x101, "FAN_SPEED"},
|
|
|
+ {0x102, "LIGHT_ENABLE"},
|
|
|
+ {0x103, "LIGHT_ILLUMINATION_LEVEL"},
|
|
|
+ {0x104, "CLIMATE_CONTROL_ENABLE"},
|
|
|
+ {0x105, "ROOM_TEMPERATURE"},
|
|
|
+ {0x106, "SECURITY_ENABLE"},
|
|
|
+ {0x107, "FIRE_ALARM"},
|
|
|
+ {0x108, "POLICE_ALARM"},
|
|
|
+ {0x109, "PROXIMITY"},
|
|
|
+ {0x10A, "MOTION"},
|
|
|
+ {0x10B, "DURESS_ALARM"},
|
|
|
+ {0x10C, "HOLDUP_ALARM"},
|
|
|
+ {0x10D, "MEDICAL_ALARM"},
|
|
|
+ {0x150, "BALANCE_RIGHT"}, // Speaker Channels
|
|
|
+ {0x151, "BALANCE_LEFT"},
|
|
|
+ {0x152, "BASS_INCREMENT"},
|
|
|
+ {0x153, "BASS_DECREMENT"},
|
|
|
+ {0x154, "TREBLE_INCREMENT"},
|
|
|
+ {0x155, "TREBLE_DECREMENT"},
|
|
|
+ {0x160, "SPEAKER_SYSTEM"},
|
|
|
+ {0x161, "CHANNEL_LEFT"},
|
|
|
+ {0x162, "CHANNEL_RIGHT"},
|
|
|
+ {0x163, "CHANNEL_CENTER"},
|
|
|
+ {0x164, "CHANNEL_FRONT"},
|
|
|
+ {0x165, "CHANNEL_CENTER_FRONT"},
|
|
|
+ {0x166, "CHANNEL_SIDE"},
|
|
|
+ {0x167, "CHANNEL_SURROUND"},
|
|
|
+ {0x168, "CHANNEL_LOW_FREQUENCY_ENHANCEMENT"},
|
|
|
+ {0x169, "CHANNEL_TOP"},
|
|
|
+ {0x16A, "CHANNEL_UNKNOWN"},
|
|
|
+ {0x170, "SUB_CHANNEL"}, // PC Theatre
|
|
|
+ {0x171, "SUB_CHANNEL_INCREMENT"},
|
|
|
+ {0x172, "SUB_CHANNEL_DECREMENT"},
|
|
|
+ {0x173, "ALTERNATE_AUDIO_INCREMENT"},
|
|
|
+ {0x174, "ALTERNATE_AUDIO_DECREMENT"},
|
|
|
+ {0x180, "APPLICATION_LAUNCH_BUTTONS"}, // Application Launch Buttons
|
|
|
+ {0x181, "AL_LAUNCH_BUTTON_CONFIGURATION_TOOL"},
|
|
|
+ {0x182, "AL_PROGRAMMABLE_BUTTON_CONFIGURATION"},
|
|
|
+ {0x183, "AL_CONSUMER_CONTROL_CONFIGURATION"},
|
|
|
+ {0x184, "AL_WORD_PROCESSOR"},
|
|
|
+ {0x185, "AL_TEXT_EDITOR"},
|
|
|
+ {0x186, "AL_SPREADSHEET"},
|
|
|
+ {0x187, "AL_GRAPHICS_EDITOR"},
|
|
|
+ {0x188, "AL_PRESENTATION_APP"},
|
|
|
+ {0x189, "AL_DATABASE_APP"},
|
|
|
+ {0x18A, "AL_EMAIL_READER"},
|
|
|
+ {0x18B, "AL_NEWSREADER"},
|
|
|
+ {0x18C, "AL_VOICEMAIL"},
|
|
|
+ {0x18D, "AL_CONTACTS_ADDRESS_BOOK"},
|
|
|
+ {0x18E, "AL_CALENDAR_SCHEDULE"},
|
|
|
+ {0x18F, "AL_TASK_PROJECT_MANAGER"},
|
|
|
+ {0x190, "AL_LOG_JOURNAL_TIMECARD"},
|
|
|
+ {0x191, "AL_CHECKBOOK_FINANCE"},
|
|
|
+ {0x192, "AL_CALCULATOR"},
|
|
|
+ {0x193, "AL_A_V_CAPTURE_PLAYBACK"},
|
|
|
+ {0x194, "AL_LOCAL_MACHINE_BROWSER"},
|
|
|
+ {0x195, "AL_LAN_WAN_BROWSER"},
|
|
|
+ {0x196, "AL_INTERNET_BROWSER"},
|
|
|
+ {0x197, "AL_REMOTE_NETWORKING_ISP_CONNECT"},
|
|
|
+ {0x198, "AL_NETWORK_CONFERENCE"},
|
|
|
+ {0x199, "AL_NETWORK_CHAT"},
|
|
|
+ {0x19A, "AL_TELEPHONY_DIALER"},
|
|
|
+ {0x19B, "AL_LOGON"},
|
|
|
+ {0x19C, "AL_LOGOFF"},
|
|
|
+ {0x19D, "AL_LOGON_LOGOFF"},
|
|
|
+ {0x19E, "AL_TERMINAL_LOCK_SCREENSAVER"},
|
|
|
+ {0x19F, "AL_CONTROL_PANEL"},
|
|
|
+ {0x1A0, "AL_COMMAND_LINE_PROCESSOR_RUN"},
|
|
|
+ {0x1A1, "AL_PROCESS_TASK_MANAGER"},
|
|
|
+ {0x1A2, "AL_SELECT_TASK_APPLICATION"},
|
|
|
+ {0x1A3, "AL_NEXT_TASK_APPLICATION"},
|
|
|
+ {0x1A4, "AL_PREVIOUS_TASK_APPLICATION"},
|
|
|
+ {0x1A5, "AL_PREEMPTIVE_HALT_TASK_APPLICATION"},
|
|
|
+ {0x1A6, "AL_INTEGRATED_HELP_CENTER"},
|
|
|
+ {0x1A7, "AL_DOCUMENTS"},
|
|
|
+ {0x1A8, "AL_THESAURUS"},
|
|
|
+ {0x1A9, "AL_DICTIONARY"},
|
|
|
+ {0x1AA, "AL_DESKTOP"},
|
|
|
+ {0x1AB, "AL_SPELL_CHECK"},
|
|
|
+ {0x1AC, "AL_GRAMMAR_CHECK"},
|
|
|
+ {0x1AD, "AL_WIRELESS_STATUS"},
|
|
|
+ {0x1AE, "AL_KEYBOARD_LAYOUT"},
|
|
|
+ {0x1AF, "AL_VIRUS_PROTECTION"},
|
|
|
+ {0x1B0, "AL_ENCRYPTION"},
|
|
|
+ {0x1B1, "AL_SCREEN_SAVER"},
|
|
|
+ {0x1B2, "AL_ALARMS"},
|
|
|
+ {0x1B3, "AL_CLOCK"},
|
|
|
+ {0x1B4, "AL_FILE_BROWSER"},
|
|
|
+ {0x1B5, "AL_POWER_STATUS"},
|
|
|
+ {0x1B6, "AL_IMAGE_BROWSER"},
|
|
|
+ {0x1B7, "AL_AUDIO_BROWSER"},
|
|
|
+ {0x1B8, "AL_MOVIE_BROWSER"},
|
|
|
+ {0x1B9, "AL_DIGITAL_RIGHTS_MANAGER"},
|
|
|
+ {0x1BA, "AL_DIGITAL_WALLET"},
|
|
|
+ {0x1BC, "AL_INSTANT_MESSAGING"},
|
|
|
+ {0x1BD, "AL_OEM_FEATURES_TIPS_TUTORIAL_BROWSER"},
|
|
|
+ {0x1BE, "AL_OEM_HELP"},
|
|
|
+ {0x1BF, "AL_ONLINE_COMMUNITY"},
|
|
|
+ {0x1C0, "AL_ENTERTAINMENT_CONTENT_BROWSER"},
|
|
|
+ {0x1C1, "AL_ONLINE_SHOPPING_BROWSER"},
|
|
|
+ {0x1C2, "AL_SMARTCARD_INFORMATION_HELP"},
|
|
|
+ {0x1C3, "AL_MARKET_MONITOR_FINANCE_BROWSER"},
|
|
|
+ {0x1C4, "AL_CUSTOMIZED_CORPORATE_NEWS_BROWSER"},
|
|
|
+ {0x1C5, "AL_ONLINE_ACTIVITY_BROWSER"},
|
|
|
+ {0x1C6, "AL_RESEARCH_SEARCH_BROWSER"},
|
|
|
+ {0x1C7, "AL_AUDIO_PLAYER"},
|
|
|
+ {0x200, "GENERIC_GUI_APPLICATION_CONTROLS"}, // Generic GUI Application Controls
|
|
|
+ {0x201, "AC_NEW"},
|
|
|
+ {0x202, "AC_OPEN"},
|
|
|
+ {0x203, "AC_CLOSE"},
|
|
|
+ {0x204, "AC_EXIT"},
|
|
|
+ {0x205, "AC_MAXIMIZE"},
|
|
|
+ {0x206, "AC_MINIMIZE"},
|
|
|
+ {0x207, "AC_SAVE"},
|
|
|
+ {0x208, "AC_PRINT"},
|
|
|
+ {0x209, "AC_PROPERTIES"},
|
|
|
+ {0x21A, "AC_UNDO"},
|
|
|
+ {0x21B, "AC_COPY"},
|
|
|
+ {0x21C, "AC_CUT"},
|
|
|
+ {0x21D, "AC_PASTE"},
|
|
|
+ {0x21E, "AC_SELECT_ALL"},
|
|
|
+ {0x21F, "AC_FIND"},
|
|
|
+ {0x220, "AC_FIND_AND_REPLACE"},
|
|
|
+ {0x221, "AC_SEARCH"},
|
|
|
+ {0x222, "AC_GO_TO"},
|
|
|
+ {0x223, "AC_HOME"},
|
|
|
+ {0x224, "AC_BACK"},
|
|
|
+ {0x225, "AC_FORWARD"},
|
|
|
+ {0x226, "AC_STOP"},
|
|
|
+ {0x227, "AC_REFRESH"},
|
|
|
+ {0x228, "AC_PREVIOUS_LINK"},
|
|
|
+ {0x229, "AC_NEXT_LINK"},
|
|
|
+ {0x22A, "AC_BOOKMARKS"},
|
|
|
+ {0x22B, "AC_HISTORY"},
|
|
|
+ {0x22C, "AC_SUBSCRIPTIONS"},
|
|
|
+ {0x22D, "AC_ZOOM_IN"},
|
|
|
+ {0x22E, "AC_ZOOM_OUT"},
|
|
|
+ {0x22F, "AC_ZOOM"},
|
|
|
+ {0x230, "AC_FULL_SCREEN_VIEW"},
|
|
|
+ {0x231, "AC_NORMAL_VIEW"},
|
|
|
+ {0x232, "AC_VIEW_TOGGLE"},
|
|
|
+ {0x233, "AC_SCROLL_UP"},
|
|
|
+ {0x234, "AC_SCROLL_DOWN"},
|
|
|
+ {0x235, "AC_SCROLL"},
|
|
|
+ {0x236, "AC_PAN_LEFT"},
|
|
|
+ {0x237, "AC_PAN_RIGHT"},
|
|
|
+ {0x238, "AC_PAN"},
|
|
|
+ {0x239, "AC_NEW_WINDOW"},
|
|
|
+ {0x23A, "AC_TILE_HORIZONTALLY"},
|
|
|
+ {0x23B, "AC_TILE_VERTICALLY"},
|
|
|
+ {0x23C, "AC_FORMAT"},
|
|
|
+ {0x23D, "AC_EDIT"},
|
|
|
+ {0x23E, "AC_BOLD"},
|
|
|
+ {0x23F, "AC_ITALICS"},
|
|
|
+ {0x240, "AC_UNDERLINE"},
|
|
|
+ {0x241, "AC_STRIKETHROUGH"},
|
|
|
+ {0x242, "AC_SUBSCRIPT"},
|
|
|
+ {0x243, "AC_SUPERSCRIPT"},
|
|
|
+ {0x244, "AC_ALL_CAPS"},
|
|
|
+ {0x245, "AC_ROTATE"},
|
|
|
+ {0x246, "AC_RESIZE"},
|
|
|
+ {0x247, "AC_FLIP_HORIZONTAL"},
|
|
|
+ {0x248, "AC_FLIP_VERTICAL"},
|
|
|
+ {0x249, "AC_MIRROR_HORIZONTAL"},
|
|
|
+ {0x24A, "AC_MIRROR_VERTICAL"},
|
|
|
+ {0x24B, "AC_FONT_SELECT"},
|
|
|
+ {0x24C, "AC_FONT_COLOR"},
|
|
|
+ {0x24D, "AC_FONT_SIZE"},
|
|
|
+ {0x24E, "AC_JUSTIFY_LEFT"},
|
|
|
+ {0x24F, "AC_JUSTIFY_CENTER_H"},
|
|
|
+ {0x250, "AC_JUSTIFY_RIGHT"},
|
|
|
+ {0x251, "AC_JUSTIFY_BLOCK_H"},
|
|
|
+ {0x252, "AC_JUSTIFY_TOP"},
|
|
|
+ {0x253, "AC_JUSTIFY_CENTER_V"},
|
|
|
+ {0x254, "AC_JUSTIFY_BOTTOM"},
|
|
|
+ {0x255, "AC_JUSTIFY_BLOCK_V"},
|
|
|
+ {0x256, "AC_INDENT_DECREASE"},
|
|
|
+ {0x257, "AC_INDENT_INCREASE"},
|
|
|
+ {0x258, "AC_NUMBERED_LIST"},
|
|
|
+ {0x259, "AC_RESTART_NUMBERING"},
|
|
|
+ {0x25A, "AC_BULLETED_LIST"},
|
|
|
+ {0x25B, "AC_PROMOTE"},
|
|
|
+ {0x25C, "AC_DEMOTE"},
|
|
|
+ {0x25D, "AC_YES"},
|
|
|
+ {0x25E, "AC_NO"},
|
|
|
+ {0x25F, "AC_CANCEL"},
|
|
|
+ {0x260, "AC_CATALOG"},
|
|
|
+ {0x261, "AC_BUY_CHECKOUT"},
|
|
|
+ {0x262, "AC_ADD_TO_CART"},
|
|
|
+ {0x263, "AC_EXPAND"},
|
|
|
+ {0x264, "AC_EXPAND_ALL"},
|
|
|
+ {0x265, "AC_COLLAPSE"},
|
|
|
+ {0x266, "AC_COLLAPSE_ALL"},
|
|
|
+ {0x267, "AC_PRINT_PREVIEW"},
|
|
|
+ {0x268, "AC_PASTE_SPECIAL"},
|
|
|
+ {0x269, "AC_INSERT_MODE"},
|
|
|
+ {0x26A, "AC_DELETE"},
|
|
|
+ {0x26B, "AC_LOCK"},
|
|
|
+ {0x26C, "AC_UNLOCK"},
|
|
|
+ {0x26D, "AC_PROTECT"},
|
|
|
+ {0x26E, "AC_UNPROTECT"},
|
|
|
+ {0x26F, "AC_ATTACH_COMMENT"},
|
|
|
+ {0x270, "AC_DELETE_COMMENT"},
|
|
|
+ {0x271, "AC_VIEW_COMMENT"},
|
|
|
+ {0x272, "AC_SELECT_WORD"},
|
|
|
+ {0x273, "AC_SELECT_SENTENCE"},
|
|
|
+ {0x274, "AC_SELECT_PARAGRAPH"},
|
|
|
+ {0x275, "AC_SELECT_COLUMN"},
|
|
|
+ {0x276, "AC_SELECT_ROW"},
|
|
|
+ {0x277, "AC_SELECT_TABLE"},
|
|
|
+ {0x278, "AC_SELECT_OBJECT"},
|
|
|
+ {0x279, "AC_REDO_REPEAT"},
|
|
|
+ {0x27A, "AC_SORT"},
|
|
|
+ {0x27B, "AC_SORT_ASCENDING"},
|
|
|
+ {0x27C, "AC_SORT_DESCENDING"},
|
|
|
+ {0x27D, "AC_FILTER"},
|
|
|
+ {0x27E, "AC_SET_CLOCK"},
|
|
|
+ {0x27F, "AC_VIEW_CLOCK"},
|
|
|
+ {0x280, "AC_SELECT_TIME_ZONE"},
|
|
|
+ {0x281, "AC_EDIT_TIME_ZONES"},
|
|
|
+ {0x282, "AC_SET_ALARM"},
|
|
|
+ {0x283, "AC_CLEAR_ALARM"},
|
|
|
+ {0x284, "AC_SNOOZE_ALARM"},
|
|
|
+ {0x285, "AC_RESET_ALARM"},
|
|
|
+ {0x286, "AC_SYNCHRONIZE"},
|
|
|
+ {0x287, "AC_SEND_RECEIVE"},
|
|
|
+ {0x288, "AC_SEND_TO"},
|
|
|
+ {0x289, "AC_REPLY"},
|
|
|
+ {0x28A, "AC_REPLY_ALL"},
|
|
|
+ {0x28B, "AC_FORWARD_MSG"},
|
|
|
+ {0x28C, "AC_SEND"},
|
|
|
+ {0x28D, "AC_ATTACH_FILE"},
|
|
|
+ {0x28E, "AC_UPLOAD"},
|
|
|
+ {0x28F, "AC_DOWNLOAD_SAVE_TARGET_AS"},
|
|
|
+ {0x290, "AC_SET_BORDERS"},
|
|
|
+ {0x291, "AC_INSERT_ROW"},
|
|
|
+ {0x292, "AC_INSERT_COLUMN"},
|
|
|
+ {0x293, "AC_INSERT_FILE"},
|
|
|
+ {0x294, "AC_INSERT_PICTURE"},
|
|
|
+ {0x295, "AC_INSERT_OBJECT"},
|
|
|
+ {0x296, "AC_INSERT_SYMBOL"},
|
|
|
+ {0x297, "AC_SAVE_AND_CLOSE"},
|
|
|
+ {0x298, "AC_RENAME"},
|
|
|
+ {0x299, "AC_MERGE"},
|
|
|
+ {0x29A, "AC_SPLIT"},
|
|
|
+ {0x29B, "AC_DISRIBUTE_HORIZONTALLY"},
|
|
|
+ {0x29C, "AC_DISTRIBUTE_VERTICALLY"}
|
|
|
+};
|
|
|
+
|
|
|
+const int hidConsumerArraySize = sizeof(hidConsumerArray) / sizeof(hidConsumerArray[0]);
|
|
|
+
|
|
|
+void strrev(char* arr, int start, int end) {
|
|
|
+ char temp;
|
|
|
+
|
|
|
+ if (start >= end)
|
|
|
+ return;
|
|
|
+
|
|
|
+ temp = *(arr + start);
|
|
|
+ *(arr + start) = *(arr + end);
|
|
|
+ *(arr + end) = temp;
|
|
|
+
|
|
|
+ start++;
|
|
|
+ end--;
|
|
|
+ strrev(arr, start, end);
|
|
|
+}
|
|
|
+
|
|
|
+char *itoa(int number, char *arr, int base){
|
|
|
+ int i = 0, r, negative = 0;
|
|
|
+
|
|
|
+ if (number == 0)
|
|
|
+ {
|
|
|
+ arr[i] = '0';
|
|
|
+ arr[i + 1] = '\0';
|
|
|
+ return arr;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (number < 0 && base == 10)
|
|
|
+ {
|
|
|
+ number *= -1;
|
|
|
+ negative = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (number != 0)
|
|
|
+ {
|
|
|
+ r = number % base;
|
|
|
+ arr[i] = (r > 9) ? (r - 10) + 'a' : r + '0';
|
|
|
+ i++;
|
|
|
+ number /= base;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (negative)
|
|
|
+ {
|
|
|
+ arr[i] = '-';
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+
|
|
|
+ strrev(arr, 0, i - 1);
|
|
|
+
|
|
|
+ arr[i] = '\0';
|
|
|
+
|
|
|
+ return arr;
|
|
|
+}
|
|
|
+
|
|
|
+// Function to convert a single hex digit to its character representation
|
|
|
+char hexDigitToChar(uint8_t digit) {
|
|
|
+ if (digit < 10) {
|
|
|
+ return '0' + digit;
|
|
|
+ } else {
|
|
|
+ return 'A' + (digit - 10);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Function to convert a uint16_t value to its hex string representation
|
|
|
+void uint16ToHexString(uint16_t value, char* hexString) {
|
|
|
+ hexString[0] = '0';
|
|
|
+ hexString[1] = 'x';
|
|
|
+ int startIndex = 2;
|
|
|
+ for (int i = 3; i >= 0; --i) {
|
|
|
+ uint8_t digit = (value >> (i * 4)) & 0xF;
|
|
|
+ if (digit != 0 || startIndex != 2) {
|
|
|
+ hexString[startIndex++] = hexDigitToChar(digit);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (startIndex == 2) { // If there are no non-zero digits, display at least "0x0"
|
|
|
+ hexString[startIndex++] = '0';
|
|
|
+ }
|
|
|
+ hexString[startIndex] = '\0';
|
|
|
+}
|
|
|
+
|
|
|
+bool is_running = false;
|
|
|
+uint32_t autofire_delay = 1000;
|
|
|
+char autofire_delay_str[12];
|
|
|
+char hexString[7];
|
|
|
+
|
|
|
+// Start at the Application Launch Buttons by default, since that's the most interesting
|
|
|
+int i = 153;
|
|
|
+int currentSubsetIndex = 13;
|
|
|
+
|
|
|
+// Array to store indexes of different CCB subsets
|
|
|
+// The idea would be to try using these different subsets against different types of devices
|
|
|
+int hidConsumerSubsets[] = {
|
|
|
+ 0, // Generic Consumer Control Device
|
|
|
+ 7, // Numeric Key Pad
|
|
|
+ 10, // General Controls
|
|
|
+ 17, // Menu Controls
|
|
|
+ 26, // Display Controls
|
|
|
+ 33, // Selection Controls
|
|
|
+ 69, // Transport Controls (nice)
|
|
|
+ 87, // Search Controls
|
|
|
+ 100, // Audio Controls
|
|
|
+ 111, // Speed Controls
|
|
|
+ 117, // Home and Security Controls
|
|
|
+ 131, // Speaker Channels
|
|
|
+ 148, // PC Theatre
|
|
|
+ 153, // Application Launch Buttons
|
|
|
+ 224 // Generic GUI Application Controls
|
|
|
+};
|
|
|
+
|
|
|
+const int hidConsumerSubsetsSize = sizeof(hidConsumerSubsets) / sizeof(hidConsumerSubsets[0]);
|
|
|
+
|
|
|
+const char* getConsumerSubsetName(int i) {
|
|
|
+ if (i >= 0 && i < 7) {
|
|
|
+ return "Generic Consumer Control";
|
|
|
+ } else if (i >= 7 && i < 10) {
|
|
|
+ return "Numeric Key Pad";
|
|
|
+ } else if (i >= 10 && i < 17) {
|
|
|
+ return "General Controls";
|
|
|
+ } else if (i >= 17 && i < 26) {
|
|
|
+ return "Menu Controls";
|
|
|
+ } else if (i >= 26 && i < 33) {
|
|
|
+ return "Display Controls";
|
|
|
+ } else if (i >= 33 && i < 69) {
|
|
|
+ return "Selection Controls";
|
|
|
+ } else if (i >= 69 && i < 87) {
|
|
|
+ return "Transport Controls";
|
|
|
+ } else if (i >= 87 && i < 100) {
|
|
|
+ return "Search Controls";
|
|
|
+ } else if (i >= 100 && i < 111) {
|
|
|
+ return "Audio Controls";
|
|
|
+ } else if (i >= 111 && i < 117) {
|
|
|
+ return "Speed Controls";
|
|
|
+ } else if (i >= 117 && i < 131) {
|
|
|
+ return "Home and Security Controls";
|
|
|
+ } else if (i >= 131 && i < 148) {
|
|
|
+ return "Speaker Channels";
|
|
|
+ } else if (i >= 148 && i < 153) {
|
|
|
+ return "PC Theatre";
|
|
|
+ } else if (i >= 153 && i < 224) {
|
|
|
+ return "Application Launch Buttons";
|
|
|
+ } else if (i >= 224) {
|
|
|
+ return "GUI Application Controls";
|
|
|
+ }
|
|
|
+ // Won't ever happen though
|
|
|
+ return "Invalid Index";
|
|
|
+}
|
|
|
+
|
|
|
+// This is the main program loop
|
|
|
+static void usb_ccb_start_draw_callback(Canvas* canvas, void* context) {
|
|
|
+ furi_assert(context);
|
|
|
+ itoa(autofire_delay, autofire_delay_str, 10);
|
|
|
+ uint16ToHexString(hidConsumerArray[i].value, hexString);
|
|
|
+
|
|
|
+ canvas_clear(canvas);
|
|
|
+
|
|
|
+ canvas_set_font(canvas, FontPrimary);
|
|
|
+ canvas_draw_str(canvas, 0, 10, is_running ? "Running" : "Not running");
|
|
|
+
|
|
|
+ canvas_set_font(canvas, FontSecondary);
|
|
|
+ canvas_draw_str(canvas, 0, 24, "Delay [ms]: ");
|
|
|
+ canvas_draw_str(canvas, 50, 24, autofire_delay_str);
|
|
|
+
|
|
|
+ canvas_draw_str(canvas, 0, 38, "Current key subset:");
|
|
|
+ canvas_draw_str(canvas, 0, 46, getConsumerSubsetName(i));
|
|
|
+
|
|
|
+ canvas_draw_str(canvas, 0, 56, is_running ? "Sent: HID_CONSUMER_" : "Next: HID_CONSUMER_");
|
|
|
+ canvas_draw_str(canvas, 24, 56, hexString);
|
|
|
+ canvas_draw_str(canvas, 0, 64, hidConsumerArray[i].name);
|
|
|
+
|
|
|
+ if(is_running) {
|
|
|
+ uint16_t consumer_key = hidConsumerArray[i].value;
|
|
|
+ // Sending the consumer control button
|
|
|
+ furi_delay_us(autofire_delay * 500);
|
|
|
+ furi_hal_hid_consumer_key_press(consumer_key);
|
|
|
+ furi_delay_us(2000); // Hold the key pressed for a short amount of time
|
|
|
+
|
|
|
+ // Stop sending the consumer control button
|
|
|
+ furi_hal_hid_consumer_key_release(consumer_key);
|
|
|
+ furi_delay_us(autofire_delay * 500);
|
|
|
+
|
|
|
+ // Cycle onto next consumer control button
|
|
|
+ i += 1;
|
|
|
+
|
|
|
+ // Stop once we've cycled all consumer control buttons
|
|
|
+ if(i == hidConsumerArraySize){
|
|
|
+ i = 0;
|
|
|
+ is_running = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// This function is the controller
|
|
|
+static void usb_ccb_start_process(UsbCcbStart* usb_ccb_start, InputEvent* event) {
|
|
|
+ with_view_model(
|
|
|
+ usb_ccb_start->view,
|
|
|
+ UsbCcbStartModel * model,
|
|
|
+ {
|
|
|
+ if (event->type == InputTypeLong) {
|
|
|
+ if (event->key == InputKeyRight) {
|
|
|
+ model->right_pressed = true;
|
|
|
+ currentSubsetIndex = (currentSubsetIndex + 1) % hidConsumerSubsetsSize;
|
|
|
+ i = hidConsumerSubsets[currentSubsetIndex];
|
|
|
+ } else if (event->key == InputKeyLeft) {
|
|
|
+ model->left_pressed = true;
|
|
|
+ currentSubsetIndex = (currentSubsetIndex - 1 + hidConsumerSubsetsSize) % hidConsumerSubsetsSize;
|
|
|
+ i = hidConsumerSubsets[currentSubsetIndex];
|
|
|
+ }
|
|
|
+ } else if(event->type == InputTypePress) {
|
|
|
+ if(event->key == InputKeyRight) {
|
|
|
+ model->right_pressed = true;
|
|
|
+ i = (i + 1) % hidConsumerArraySize;
|
|
|
+ } else if(event->key == InputKeyLeft) {
|
|
|
+ model->left_pressed = true;
|
|
|
+ i = (i - 1 + hidConsumerArraySize) % hidConsumerArraySize;
|
|
|
+ } else if(event->key == InputKeyDown) {
|
|
|
+ model->down_pressed = true;
|
|
|
+ if(autofire_delay > 0) {
|
|
|
+ autofire_delay -= 100;
|
|
|
+ }
|
|
|
+ } else if(event->key == InputKeyUp) {
|
|
|
+ model->up_pressed = true;
|
|
|
+ autofire_delay += 100;
|
|
|
+ } else if(event->key == InputKeyOk) {
|
|
|
+ model->ok_pressed = true;
|
|
|
+ is_running = !is_running;
|
|
|
+ } else if(event->key == InputKeyBack) {
|
|
|
+ model->back_pressed = true;
|
|
|
+ }
|
|
|
+ } else if(event->type == InputTypeRelease) {
|
|
|
+ if(event->key == InputKeyUp) {
|
|
|
+ model->up_pressed = false;
|
|
|
+ } else if(event->key == InputKeyDown) {
|
|
|
+ model->down_pressed = false;
|
|
|
+ } else if(event->key == InputKeyLeft) {
|
|
|
+ model->left_pressed = false;
|
|
|
+ } else if(event->key == InputKeyRight) {
|
|
|
+ model->right_pressed = false;
|
|
|
+ } else if(event->key == InputKeyOk) {
|
|
|
+ model->ok_pressed = false;
|
|
|
+ } else if(event->key == InputKeyBack) {
|
|
|
+ model->back_pressed = false;
|
|
|
+ }
|
|
|
+ } else if(event->type == InputTypeShort) {
|
|
|
+ if(event->key == InputKeyBack) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+ true);
|
|
|
+}
|
|
|
+
|
|
|
+static bool usb_ccb_start_input_callback(InputEvent* event, void* context) {
|
|
|
+ furi_assert(context);
|
|
|
+ UsbCcbStart* usb_ccb_start = context;
|
|
|
+ bool consumed = false;
|
|
|
+
|
|
|
+ if(event->type == InputTypeLong && event->key == InputKeyBack) {
|
|
|
+ furi_hal_hid_kb_release_all();
|
|
|
+ } else {
|
|
|
+ usb_ccb_start_process(usb_ccb_start, event);
|
|
|
+ consumed = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return consumed;
|
|
|
+}
|
|
|
+
|
|
|
+UsbCcbStart* usb_ccb_start_alloc() {
|
|
|
+ UsbCcbStart* usb_ccb_start = malloc(sizeof(UsbCcbStart));
|
|
|
+ usb_ccb_start->view = view_alloc();
|
|
|
+ view_set_context(usb_ccb_start->view, usb_ccb_start);
|
|
|
+ view_allocate_model(usb_ccb_start->view, ViewModelTypeLocking, sizeof(UsbCcbStartModel));
|
|
|
+ view_set_draw_callback(usb_ccb_start->view, usb_ccb_start_draw_callback);
|
|
|
+ view_set_input_callback(usb_ccb_start->view, usb_ccb_start_input_callback);
|
|
|
+
|
|
|
+ return usb_ccb_start;
|
|
|
+}
|
|
|
+
|
|
|
+void usb_ccb_start_free(UsbCcbStart* usb_ccb_start) {
|
|
|
+ furi_assert(usb_ccb_start);
|
|
|
+ view_free(usb_ccb_start->view);
|
|
|
+ free(usb_ccb_start);
|
|
|
+ i = 0;
|
|
|
+}
|
|
|
+
|
|
|
+View* usb_ccb_start_get_view(UsbCcbStart* usb_ccb_start) {
|
|
|
+ furi_assert(usb_ccb_start);
|
|
|
+ return usb_ccb_start->view;
|
|
|
+}
|
|
|
+
|
|
|
+void usb_ccb_start_set_connected_status(UsbCcbStart* usb_ccb_start, bool connected) {
|
|
|
+ furi_assert(usb_ccb_start);
|
|
|
+ with_view_model(usb_ccb_start->view, UsbCcbStartModel * model, { model->connected = connected; }, true);
|
|
|
+}
|