Merge branch 'dev' into release-candidate
This commit is contained in:
		
						commit
						dc6da827fa
					
				
							
								
								
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							| @ -14,7 +14,7 @@ applications/gui/** @skotopes @DrZlo13 | ||||
| applications/gui-test/** @skotopes @DrZlo13 | ||||
| applications/ibutton/** @skotopes @DrZlo13 | ||||
| applications/input/** @skotopes @DrZlo13 | ||||
| applications/irda/** @skotopes @DrZlo13 @albkharisov | ||||
| applications/infrared/** @skotopes @DrZlo13 | ||||
| applications/lf-rfid/** @skotopes @DrZlo13 | ||||
| applications/menu/** @skotopes @DrZlo13 | ||||
| applications/music-player/** @skotopes @DrZlo13 | ||||
|  | ||||
| @ -58,7 +58,7 @@ Examples: | ||||
| 
 | ||||
| 	FuriHalUsb | ||||
| 	Gui | ||||
| 	SubghzKeystore | ||||
| 	SubGhzKeystore | ||||
| 
 | ||||
| 
 | ||||
| ### Functions are snake_case | ||||
| @ -73,8 +73,8 @@ This rule makes easier to locate types, functions and sources. | ||||
| 
 | ||||
| For example: | ||||
| 
 | ||||
| We have abstraction that we call `Subghz Keystore`, so there will be: | ||||
| file `subghz_keystore.h` we have type `SubghzKeystore` and function `subghz_keystore_read`. | ||||
| We have abstraction that we call `SubGhz Keystore`, so there will be: | ||||
| file `subghz_keystore.h` we have type `SubGhzKeystore` and function `subghz_keystore_read`. | ||||
| 
 | ||||
| ### File names | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										7
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								Makefile
									
									
									
									
									
								
							| @ -13,7 +13,7 @@ PROJECT_SOURCE_DIRECTORIES := \ | ||||
| 	$(PROJECT_ROOT)/lib/cyfral \
 | ||||
| 	$(PROJECT_ROOT)/lib/drivers \
 | ||||
| 	$(PROJECT_ROOT)/lib/flipper_file \
 | ||||
| 	$(PROJECT_ROOT)/lib/irda \
 | ||||
| 	$(PROJECT_ROOT)/lib/infrared \
 | ||||
| 	$(PROJECT_ROOT)/lib/nfc_protocols \
 | ||||
| 	$(PROJECT_ROOT)/lib/ST25RFAL002 \
 | ||||
| 	$(PROJECT_ROOT)/lib/onewire \
 | ||||
| @ -121,3 +121,8 @@ lint: | ||||
| format: | ||||
| 	@echo "Reformating sources code" | ||||
| 	@$(PROJECT_ROOT)/scripts/lint.py format $(PROJECT_SOURCE_DIRECTORIES) | ||||
| 
 | ||||
| .PHONY: guruguru | ||||
| guruguru: | ||||
| 	@echo "ぐるぐる回る" | ||||
| 	@$(PROJECT_ROOT)/scripts/guruguru.py $(PROJECT_ROOT) | ||||
|  | ||||
| @ -15,8 +15,8 @@ | ||||
| - `gui`                 - GUI service and API | ||||
| - `ibutton`             - iButton application, onewire keys and more | ||||
| - `input`               - Input service | ||||
| - `irda`                - Irda application, controls your IR devices | ||||
| - `irda_monitor`        - Irda debug tool | ||||
| - `infrared`            - Infrared application, controls your IR devices | ||||
| - `infrared_monitor`    - Infrared debug tool | ||||
| - `lfrfid`              - LF RFID application | ||||
| - `lfrfid_debug`        - LF RFID debug tool | ||||
| - `loader`              - Application loader service | ||||
| @ -30,7 +30,7 @@ | ||||
| - `snake_game`          - Snake game application | ||||
| - `storage`             - Storage service, internal + sdcard | ||||
| - `storage_settings`    - Storage settings app | ||||
| - `subghz`              - Subghz application, 433 fobs and etc | ||||
| - `subghz`              - SubGhz application, 433 fobs and etc | ||||
| - `system`              - System settings, tools and API | ||||
| - `tests`               - Unit tests and etc | ||||
| - `u2f`                 - U2F Application | ||||
|  | ||||
| @ -27,8 +27,8 @@ extern int32_t delay_test_app(void* p); | ||||
| extern int32_t display_test_app(void* p); | ||||
| extern int32_t gpio_app(void* p); | ||||
| extern int32_t ibutton_app(void* p); | ||||
| extern int32_t irda_app(void* p); | ||||
| extern int32_t irda_monitor_app(void* p); | ||||
| extern int32_t infrared_app(void* p); | ||||
| extern int32_t infrared_monitor_app(void* p); | ||||
| extern int32_t keypad_test_app(void* p); | ||||
| extern int32_t lfrfid_app(void* p); | ||||
| extern int32_t lfrfid_debug_app(void* p); | ||||
| @ -51,7 +51,7 @@ extern int32_t snake_game_app(void* p); | ||||
| extern void bt_on_system_start(); | ||||
| extern void crypto_on_system_start(); | ||||
| extern void ibutton_on_system_start(); | ||||
| extern void irda_on_system_start(); | ||||
| extern void infrared_on_system_start(); | ||||
| extern void lfrfid_on_system_start(); | ||||
| extern void nfc_on_system_start(); | ||||
| extern void storage_on_system_start(); | ||||
| @ -136,8 +136,8 @@ const FlipperApplication FLIPPER_APPS[] = { | ||||
|     {.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_IRDA | ||||
|     {.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14}, | ||||
| #ifdef APP_INFRARED | ||||
|     {.app = infrared_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_GPIO | ||||
| @ -164,8 +164,8 @@ const size_t FLIPPER_APPS_COUNT = sizeof(FLIPPER_APPS) / sizeof(FlipperApplicati | ||||
| const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { | ||||
|     crypto_on_system_start, | ||||
| 
 | ||||
| #ifdef APP_IRDA | ||||
|     irda_on_system_start, | ||||
| #ifdef APP_INFRARED | ||||
|     infrared_on_system_start, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_NFC | ||||
| @ -251,8 +251,8 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { | ||||
|     {.app = uart_echo_app, .name = "Uart Echo", .stack_size = 2048, .icon = NULL}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_IRDA_MONITOR | ||||
|     {.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = NULL}, | ||||
| #ifdef APP_INFRARED_MONITOR | ||||
|     {.app = infrared_monitor_app, .name = "Infrared Monitor", .stack_size = 1024, .icon = NULL}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_SCENED | ||||
|  | ||||
| @ -26,7 +26,7 @@ SRV_DESKTOP	= 1 | ||||
| APP_ARCHIVE	= 1 | ||||
| APP_GPIO = 1 | ||||
| APP_IBUTTON	= 1 | ||||
| APP_IRDA	= 1 | ||||
| APP_INFRARED	= 1 | ||||
| APP_LF_RFID	= 1 | ||||
| APP_NFC		= 1 | ||||
| APP_SUBGHZ	= 1 | ||||
| @ -40,7 +40,7 @@ APP_SNAKE_GAME = 1 | ||||
| # Debug
 | ||||
| APP_ACCESSOR = 1 | ||||
| APP_BLINK = 1 | ||||
| APP_IRDA_MONITOR = 1 | ||||
| APP_INFRARED_MONITOR = 1 | ||||
| APP_KEYPAD_TEST = 1 | ||||
| APP_SD_TEST	= 1 | ||||
| APP_VIBRO_TEST = 1 | ||||
| @ -59,9 +59,9 @@ endif | ||||
| # Prefix with APP_*
 | ||||
| 
 | ||||
| 
 | ||||
| APP_IRDA_MONITOR	?= 0 | ||||
| ifeq ($(APP_IRDA_MONITOR), 1) | ||||
| CFLAGS		+= -DAPP_IRDA_MONITOR | ||||
| APP_INFRARED_MONITOR	?= 0 | ||||
| ifeq ($(APP_INFRARED_MONITOR), 1) | ||||
| CFLAGS		+= -DAPP_INFRARED_MONITOR | ||||
| SRV_GUI		= 1 | ||||
| endif | ||||
| 
 | ||||
| @ -122,9 +122,9 @@ SRV_GUI		= 1 | ||||
| endif | ||||
| 
 | ||||
| 
 | ||||
| APP_IRDA ?= 0 | ||||
| ifeq ($(APP_IRDA), 1) | ||||
| CFLAGS		+= -DAPP_IRDA | ||||
| APP_INFRARED ?= 0 | ||||
| ifeq ($(APP_INFRARED), 1) | ||||
| CFLAGS		+= -DAPP_INFRARED | ||||
| SRV_GUI		= 1 | ||||
| endif | ||||
| 
 | ||||
|  | ||||
| @ -10,7 +10,7 @@ static const char* tab_default_paths[] = { | ||||
|     [ArchiveTabNFC] = "/any/nfc", | ||||
|     [ArchiveTabSubGhz] = "/any/subghz", | ||||
|     [ArchiveTabLFRFID] = "/any/lfrfid", | ||||
|     [ArchiveTabIrda] = "/any/irda", | ||||
|     [ArchiveTabInfrared] = "/any/infrared", | ||||
|     [ArchiveTabBadUsb] = "/any/badusb", | ||||
|     [ArchiveTabU2f] = "/app:u2f", | ||||
|     [ArchiveTabBrowser] = "/any", | ||||
| @ -21,7 +21,7 @@ static const char* known_ext[] = { | ||||
|     [ArchiveFileTypeNFC] = ".nfc", | ||||
|     [ArchiveFileTypeSubGhz] = ".sub", | ||||
|     [ArchiveFileTypeLFRFID] = ".rfid", | ||||
|     [ArchiveFileTypeIrda] = ".ir", | ||||
|     [ArchiveFileTypeInfrared] = ".ir", | ||||
|     [ArchiveFileTypeBadUsb] = ".txt", | ||||
|     [ArchiveFileTypeU2f] = "?", | ||||
|     [ArchiveFileTypeFolder] = "?", | ||||
| @ -34,7 +34,7 @@ static const ArchiveFileTypeEnum known_type[] = { | ||||
|     [ArchiveTabNFC] = ArchiveFileTypeNFC, | ||||
|     [ArchiveTabSubGhz] = ArchiveFileTypeSubGhz, | ||||
|     [ArchiveTabLFRFID] = ArchiveFileTypeLFRFID, | ||||
|     [ArchiveTabIrda] = ArchiveFileTypeIrda, | ||||
|     [ArchiveTabInfrared] = ArchiveFileTypeInfrared, | ||||
|     [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, | ||||
|     [ArchiveTabU2f] = ArchiveFileTypeU2f, | ||||
|     [ArchiveTabBrowser] = ArchiveFileTypeUnknown, | ||||
|  | ||||
| @ -8,7 +8,7 @@ typedef enum { | ||||
|     ArchiveFileTypeNFC, | ||||
|     ArchiveFileTypeSubGhz, | ||||
|     ArchiveFileTypeLFRFID, | ||||
|     ArchiveFileTypeIrda, | ||||
|     ArchiveFileTypeInfrared, | ||||
|     ArchiveFileTypeBadUsb, | ||||
|     ArchiveFileTypeU2f, | ||||
|     ArchiveFileTypeFolder, | ||||
|  | ||||
| @ -12,7 +12,7 @@ static const char* flipper_app_name[] = { | ||||
|     [ArchiveFileTypeNFC] = "NFC", | ||||
|     [ArchiveFileTypeSubGhz] = "Sub-GHz", | ||||
|     [ArchiveFileTypeLFRFID] = "125 kHz RFID", | ||||
|     [ArchiveFileTypeIrda] = "Infrared", | ||||
|     [ArchiveFileTypeInfrared] = "Infrared", | ||||
|     [ArchiveFileTypeBadUsb] = "Bad USB", | ||||
|     [ArchiveFileTypeU2f] = "U2F", | ||||
| }; | ||||
|  | ||||
| @ -9,7 +9,7 @@ static const char* ArchiveTabNames[] = { | ||||
|     [ArchiveTabNFC] = "NFC", | ||||
|     [ArchiveTabSubGhz] = "Sub-GHz", | ||||
|     [ArchiveTabLFRFID] = "RFID LF", | ||||
|     [ArchiveTabIrda] = "Infrared", | ||||
|     [ArchiveTabInfrared] = "Infrared", | ||||
|     [ArchiveTabBadUsb] = "Bad USB", | ||||
|     [ArchiveTabU2f] = "U2F", | ||||
|     [ArchiveTabBrowser] = "Browser"}; | ||||
| @ -19,7 +19,7 @@ static const Icon* ArchiveItemIcons[] = { | ||||
|     [ArchiveFileTypeNFC] = &I_Nfc_10px, | ||||
|     [ArchiveFileTypeSubGhz] = &I_sub1_10px, | ||||
|     [ArchiveFileTypeLFRFID] = &I_125_10px, | ||||
|     [ArchiveFileTypeIrda] = &I_ir_10px, | ||||
|     [ArchiveFileTypeInfrared] = &I_ir_10px, | ||||
|     [ArchiveFileTypeBadUsb] = &I_badusb_10px, | ||||
|     [ArchiveFileTypeU2f] = &I_u2f_10px, | ||||
|     [ArchiveFileTypeFolder] = &I_dir_10px, | ||||
|  | ||||
| @ -22,7 +22,7 @@ typedef enum { | ||||
|     ArchiveTabSubGhz, | ||||
|     ArchiveTabLFRFID, | ||||
|     ArchiveTabNFC, | ||||
|     ArchiveTabIrda, | ||||
|     ArchiveTabInfrared, | ||||
|     ArchiveTabIButton, | ||||
|     ArchiveTabBadUsb, | ||||
|     ArchiveTabU2f, | ||||
|  | ||||
| @ -12,12 +12,15 @@ | ||||
| #define WORKER_TAG TAG "Worker" | ||||
| #define FILE_BUFFER_LEN 16 | ||||
| 
 | ||||
| #define SCRIPT_STATE_ERROR (-1) | ||||
| #define SCRIPT_STATE_END (-2) | ||||
| #define SCRIPT_STATE_NEXT_LINE (-3) | ||||
| 
 | ||||
| typedef enum { | ||||
|     WorkerEvtReserved = (1 << 0), | ||||
|     WorkerEvtToggle = (1 << 1), | ||||
|     WorkerEvtEnd = (1 << 2), | ||||
|     WorkerEvtConnect = (1 << 3), | ||||
|     WorkerEvtDisconnect = (1 << 4), | ||||
|     WorkerEvtToggle = (1 << 0), | ||||
|     WorkerEvtEnd = (1 << 1), | ||||
|     WorkerEvtConnect = (1 << 2), | ||||
|     WorkerEvtDisconnect = (1 << 3), | ||||
| } WorkerEvtFlags; | ||||
| 
 | ||||
| struct BadUsbScript { | ||||
| @ -138,6 +141,10 @@ static uint32_t ducky_get_command_len(const char* line) { | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static bool ducky_is_line_end(const char chr) { | ||||
|     return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); | ||||
| } | ||||
| 
 | ||||
| static void ducky_numlock_on() { | ||||
|     if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { | ||||
|         furi_hal_hid_kb_press(KEY_NUM_LOCK); | ||||
| @ -163,7 +170,7 @@ static bool ducky_altchar(const char* charcode) { | ||||
| 
 | ||||
|     furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT); | ||||
| 
 | ||||
|     while((charcode[i] != ' ') && (charcode[i] != '\n') && (charcode[i] != '\0')) { | ||||
|     while(!ducky_is_line_end(charcode[i])) { | ||||
|         state = ducky_numpad_press(charcode[i]); | ||||
|         if(state == false) break; | ||||
|         i++; | ||||
| @ -196,8 +203,11 @@ static bool ducky_altstring(const char* param) { | ||||
| static bool ducky_string(const char* param) { | ||||
|     uint32_t i = 0; | ||||
|     while(param[i] != '\0') { | ||||
|         furi_hal_hid_kb_press(HID_ASCII_TO_KEY(param[i])); | ||||
|         furi_hal_hid_kb_release(HID_ASCII_TO_KEY(param[i])); | ||||
|         uint16_t keycode = HID_ASCII_TO_KEY(param[i]); | ||||
|         if(keycode != KEY_NONE) { | ||||
|             furi_hal_hid_kb_press(keycode); | ||||
|             furi_hal_hid_kb_release(keycode); | ||||
|         } | ||||
|         i++; | ||||
|     } | ||||
|     return true; | ||||
| @ -207,8 +217,7 @@ static uint16_t ducky_get_keycode(const char* param, bool accept_chars) { | ||||
|     for(uint8_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { | ||||
|         uint8_t key_cmd_len = strlen(ducky_keys[i].name); | ||||
|         if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && | ||||
|            ((param[key_cmd_len] == ' ') || (param[key_cmd_len] == '\n') || | ||||
|             (param[key_cmd_len] == '\0'))) { | ||||
|            (ducky_is_line_end(param[key_cmd_len]))) { | ||||
|             return ducky_keys[i].keycode; | ||||
|         } | ||||
|     } | ||||
| @ -228,7 +237,7 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) { | ||||
|             line_tmp = &line_tmp[i]; | ||||
|             break; // Skip spaces and tabs
 | ||||
|         } | ||||
|         if(i == line_len - 1) return 0; // Skip empty lines
 | ||||
|         if(i == line_len - 1) return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
 | ||||
|     } | ||||
| 
 | ||||
|     FURI_LOG_I(WORKER_TAG, "line:%s", line_tmp); | ||||
| @ -245,25 +254,25 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) { | ||||
|         if((state) && (delay_val > 0)) { | ||||
|             return (int32_t)delay_val; | ||||
|         } | ||||
|         return (-1); | ||||
|         return SCRIPT_STATE_ERROR; | ||||
|     } else if( | ||||
|         (strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) || | ||||
|         (strncmp(line_tmp, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) { | ||||
|         // DEFAULT_DELAY
 | ||||
|         line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; | ||||
|         state = ducky_get_number(line_tmp, &bad_usb->defdelay); | ||||
|         return (state) ? (0) : (-1); | ||||
|         return (state) ? (0) : SCRIPT_STATE_ERROR; | ||||
|     } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { | ||||
|         // STRING
 | ||||
|         line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; | ||||
|         state = ducky_string(line_tmp); | ||||
|         return (state) ? (0) : (-1); | ||||
|         return (state) ? (0) : SCRIPT_STATE_ERROR; | ||||
|     } else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) { | ||||
|         // ALTCHAR
 | ||||
|         line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; | ||||
|         ducky_numlock_on(); | ||||
|         state = ducky_altchar(line_tmp); | ||||
|         return (state) ? (0) : (-1); | ||||
|         return (state) ? (0) : SCRIPT_STATE_ERROR; | ||||
|     } else if( | ||||
|         (strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) || | ||||
|         (strncmp(line_tmp, ducky_cmd_altstr_2, strlen(ducky_cmd_altstr_2)) == 0)) { | ||||
| @ -271,16 +280,16 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) { | ||||
|         line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; | ||||
|         ducky_numlock_on(); | ||||
|         state = ducky_altstring(line_tmp); | ||||
|         return (state) ? (0) : (-1); | ||||
|         return (state) ? (0) : SCRIPT_STATE_ERROR; | ||||
|     } else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) { | ||||
|         // REPEAT
 | ||||
|         line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; | ||||
|         state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt); | ||||
|         return (state) ? (0) : (-1); | ||||
|         return (state) ? (0) : SCRIPT_STATE_ERROR; | ||||
|     } else { | ||||
|         // Special keys + modifiers
 | ||||
|         uint16_t key = ducky_get_keycode(line_tmp, false); | ||||
|         if(key == KEY_NONE) return (-1); | ||||
|         if(key == KEY_NONE) return SCRIPT_STATE_ERROR; | ||||
|         if((key & 0xFF00) != 0) { | ||||
|             // It's a modifier key
 | ||||
|             line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; | ||||
| @ -290,7 +299,7 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) { | ||||
|         furi_hal_hid_kb_release(key); | ||||
|         return (0); | ||||
|     } | ||||
|     return (-1); | ||||
|     return SCRIPT_STATE_ERROR; | ||||
| } | ||||
| 
 | ||||
| static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { | ||||
| @ -326,10 +335,12 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil | ||||
|     if(bad_usb->repeat_cnt > 0) { | ||||
|         bad_usb->repeat_cnt--; | ||||
|         delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev); | ||||
|         if(delay_val < 0) { | ||||
|         if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
 | ||||
|             return 0; | ||||
|         } else if(delay_val < 0) { // Script error
 | ||||
|             bad_usb->st.error_line = bad_usb->st.line_cur - 1; | ||||
|             FURI_LOG_E(WORKER_TAG, "Unknown command at line %lu", bad_usb->st.line_cur - 1); | ||||
|             return (-1); | ||||
|             return SCRIPT_STATE_ERROR; | ||||
|         } else { | ||||
|             return (delay_val + bad_usb->defdelay); | ||||
|         } | ||||
| @ -350,7 +361,7 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil | ||||
|             } | ||||
| 
 | ||||
|             bad_usb->buf_start = 0; | ||||
|             if(bad_usb->buf_len == 0) return (-2); | ||||
|             if(bad_usb->buf_len == 0) return SCRIPT_STATE_END; | ||||
|         } | ||||
|         for(uint8_t i = bad_usb->buf_start; i < (bad_usb->buf_start + bad_usb->buf_len); i++) { | ||||
|             if(bad_usb->file_buf[i] == '\n' && string_size(bad_usb->line) > 0) { | ||||
| @ -361,7 +372,7 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil | ||||
|                 if(delay_val < 0) { | ||||
|                     bad_usb->st.error_line = bad_usb->st.line_cur; | ||||
|                     FURI_LOG_E(WORKER_TAG, "Unknown command at line %lu", bad_usb->st.line_cur); | ||||
|                     return (-1); | ||||
|                     return SCRIPT_STATE_ERROR; | ||||
|                 } else { | ||||
|                     return (delay_val + bad_usb->defdelay); | ||||
|                 } | ||||
| @ -370,7 +381,7 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil | ||||
|             } | ||||
|         } | ||||
|         bad_usb->buf_len = 0; | ||||
|         if(bad_usb->file_end) return (-2); | ||||
|         if(bad_usb->file_end) return SCRIPT_STATE_END; | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| @ -479,11 +490,11 @@ static int32_t bad_usb_worker(void* context) { | ||||
|                 } | ||||
|                 bad_usb->st.state = BadUsbStateRunning; | ||||
|                 delay_val = ducky_script_execute_next(bad_usb, script_file); | ||||
|                 if(delay_val == -1) { // Script error
 | ||||
|                 if(delay_val == SCRIPT_STATE_ERROR) { // Script error
 | ||||
|                     delay_val = 0; | ||||
|                     worker_state = BadUsbStateScriptError; | ||||
|                     bad_usb->st.state = worker_state; | ||||
|                 } else if(delay_val == -2) { // End of script
 | ||||
|                 } else if(delay_val == SCRIPT_STATE_END) { // End of script
 | ||||
|                     delay_val = 0; | ||||
|                     worker_state = BadUsbStateIdle; | ||||
|                     bad_usb->st.state = BadUsbStateDone; | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| #include <task_control_block.h> | ||||
| #include <time.h> | ||||
| #include <notification/notification_messages.h> | ||||
| #include <loader/loader.h> | ||||
| 
 | ||||
| // Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'`
 | ||||
| #define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" | ||||
| @ -146,6 +147,20 @@ void cli_command_vibro(Cli* cli, string_t args, void* context) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void cli_command_debug(Cli* cli, string_t args, void* context) { | ||||
|     if(!string_cmp(args, "0")) { | ||||
|         furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); | ||||
|         loader_update_menu(); | ||||
|         printf("Debug disabled."); | ||||
|     } else if(!string_cmp(args, "1")) { | ||||
|         furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); | ||||
|         loader_update_menu(); | ||||
|         printf("Debug enabled."); | ||||
|     } else { | ||||
|         cli_print_usage("debug", "<1|0>", string_get_cstr(args)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void cli_command_led(Cli* cli, string_t args, void* context) { | ||||
|     // Get first word as light name
 | ||||
|     NotificationMessage notification_led_message; | ||||
| @ -348,6 +363,7 @@ void cli_commands_init(Cli* cli) { | ||||
| 
 | ||||
|     cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL); | ||||
|     cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL); | ||||
|     cli_add_command(cli, "debug", CliCommandFlagDefault, cli_command_debug, NULL); | ||||
|     cli_add_command(cli, "ps", CliCommandFlagParallelSafe, cli_command_ps, NULL); | ||||
|     cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL); | ||||
|     cli_add_command(cli, "free_blocks", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL); | ||||
|  | ||||
| @ -211,11 +211,10 @@ void gui_redraw(Gui* gui) { | ||||
|     } | ||||
| 
 | ||||
|     canvas_commit(gui->canvas); | ||||
|     if(gui->canvas_callback) { | ||||
|         gui->canvas_callback( | ||||
|             canvas_get_buffer(gui->canvas), | ||||
|             canvas_get_buffer_size(gui->canvas), | ||||
|             gui->canvas_callback_context); | ||||
|     for | ||||
|         M_EACH(p, gui->canvas_callback_pair, CanvasCallbackPairArray_t) { | ||||
|             p->callback( | ||||
|                 canvas_get_buffer(gui->canvas), canvas_get_buffer_size(gui->canvas), p->context); | ||||
|         } | ||||
|     gui_unlock(gui); | ||||
| } | ||||
| @ -396,16 +395,36 @@ void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) { | ||||
|     gui_unlock(gui); | ||||
| } | ||||
| 
 | ||||
| void gui_set_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) { | ||||
| void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) { | ||||
|     furi_assert(gui); | ||||
|     gui_lock(gui); | ||||
|     gui->canvas_callback = callback; | ||||
|     gui->canvas_callback_context = context; | ||||
|     gui_unlock(gui); | ||||
| 
 | ||||
|     if(callback != NULL) { | ||||
|     const CanvasCallbackPair p = {callback, context}; | ||||
| 
 | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 0); | ||||
|     CanvasCallbackPairArray_push_back(gui->canvas_callback_pair, p); | ||||
| 
 | ||||
|     gui_unlock(gui); | ||||
|     gui_update(gui); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) { | ||||
|     furi_assert(gui); | ||||
| 
 | ||||
|     const CanvasCallbackPair p = {callback, context}; | ||||
| 
 | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 1); | ||||
|     CanvasCallbackPairArray_remove_val(gui->canvas_callback_pair, p); | ||||
| 
 | ||||
|     gui_unlock(gui); | ||||
| } | ||||
| 
 | ||||
| size_t gui_get_framebuffer_size(Gui* gui) { | ||||
|     furi_assert(gui); | ||||
|     return canvas_get_buffer_size(gui->canvas); | ||||
| } | ||||
| 
 | ||||
| void gui_set_lockdown(Gui* gui, bool lockdown) { | ||||
| @ -429,9 +448,12 @@ Gui* gui_alloc() { | ||||
|     } | ||||
|     // Drawing canvas
 | ||||
|     gui->canvas = canvas_init(); | ||||
|     CanvasCallbackPairArray_init(gui->canvas_callback_pair); | ||||
| 
 | ||||
|     // Input
 | ||||
|     gui->input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL); | ||||
|     gui->input_events = furi_record_open("input_events"); | ||||
| 
 | ||||
|     furi_check(gui->input_events); | ||||
|     furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui); | ||||
| 
 | ||||
|  | ||||
| @ -68,7 +68,7 @@ void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port); | ||||
|  */ | ||||
| void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port); | ||||
| 
 | ||||
| /** Set gui canvas commit callback
 | ||||
| /** Add gui canvas commit callback
 | ||||
|  * | ||||
|  * This callback will be called upon Canvas commit Callback dispatched from GUI | ||||
|  * thread and is time critical | ||||
| @ -77,7 +77,22 @@ void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port); | ||||
|  * @param      callback  GuiCanvasCommitCallback | ||||
|  * @param      context   GuiCanvasCommitCallback context | ||||
|  */ | ||||
| void gui_set_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context); | ||||
| void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context); | ||||
| 
 | ||||
| /** Remove gui canvas commit callback
 | ||||
|  * | ||||
|  * @param      gui       Gui instance | ||||
|  * @param      callback  GuiCanvasCommitCallback | ||||
|  * @param      context   GuiCanvasCommitCallback context | ||||
|  */ | ||||
| void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context); | ||||
| 
 | ||||
| /** Get gui canvas frame buffer size
 | ||||
|  * * | ||||
|  * @param      gui       Gui instance | ||||
|  * @return     size_t    size of frame buffer in bytes | ||||
|  */ | ||||
| size_t gui_get_framebuffer_size(Gui* gui); | ||||
| 
 | ||||
| /** Set lockdown mode
 | ||||
|  * | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <m-array.h> | ||||
| #include <m-algo.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include "canvas.h" | ||||
| @ -42,6 +43,17 @@ | ||||
| 
 | ||||
| ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST); | ||||
| 
 | ||||
| typedef struct { | ||||
|     GuiCanvasCommitCallback callback; | ||||
|     void* context; | ||||
| } CanvasCallbackPair; | ||||
| 
 | ||||
| ARRAY_DEF(CanvasCallbackPairArray, CanvasCallbackPair, M_POD_OPLIST); | ||||
| 
 | ||||
| #define M_OPL_CanvasCallbackPairArray_t() ARRAY_OPLIST(CanvasCallbackPairArray, M_POD_OPLIST) | ||||
| 
 | ||||
| ALGO_DEF(CanvasCallbackPairArray, CanvasCallbackPairArray_t); | ||||
| 
 | ||||
| /** Gui structure */ | ||||
| struct Gui { | ||||
|     // Thread and lock
 | ||||
| @ -52,8 +64,7 @@ struct Gui { | ||||
|     bool lockdown; | ||||
|     ViewPortArray_t layers[GuiLayerMAX]; | ||||
|     Canvas* canvas; | ||||
|     GuiCanvasCommitCallback canvas_callback; | ||||
|     void* canvas_callback_context; | ||||
|     CanvasCallbackPairArray_t canvas_callback_pair; | ||||
| 
 | ||||
|     // Input
 | ||||
|     osMessageQueueId_t input_queue; | ||||
|  | ||||
| @ -108,11 +108,11 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     ButtonMenuItemArray_it_t it; | ||||
| 
 | ||||
|     if(active_screen > 0) { | ||||
|         canvas_draw_icon(canvas, 28, 1, &I_IrdaArrowUp_4x8); | ||||
|         canvas_draw_icon(canvas, 28, 1, &I_InfraredArrowUp_4x8); | ||||
|     } | ||||
| 
 | ||||
|     if(max_screen > active_screen) { | ||||
|         canvas_draw_icon(canvas, 28, 123, &I_IrdaArrowDown_4x8); | ||||
|         canvas_draw_icon(canvas, 28, 123, &I_InfraredArrowDown_4x8); | ||||
|     } | ||||
| 
 | ||||
|     if(model->header) { | ||||
|  | ||||
| @ -1,52 +1,52 @@ | ||||
| #include <furi_hal_delay.h> | ||||
| #include <irda.h> | ||||
| #include <infrared.h> | ||||
| #include <app_template.h> | ||||
| #include <cli/cli.h> | ||||
| #include <cmsis_os2.h> | ||||
| #include <irda_worker.h> | ||||
| #include <infrared_worker.h> | ||||
| #include <furi.h> | ||||
| #include <furi_hal_irda.h> | ||||
| #include <furi_hal_infrared.h> | ||||
| #include <sstream> | ||||
| #include <string> | ||||
| #include <m-string.h> | ||||
| #include <irda_transmit.h> | ||||
| #include <infrared_transmit.h> | ||||
| #include <sys/types.h> | ||||
| #include "../helpers/irda_parser.h" | ||||
| #include "../helpers/infrared_parser.h" | ||||
| 
 | ||||
| static void irda_cli_start_ir_rx(Cli* cli, string_t args); | ||||
| static void irda_cli_start_ir_tx(Cli* cli, string_t args); | ||||
| static void infrared_cli_start_ir_rx(Cli* cli, string_t args); | ||||
| static void infrared_cli_start_ir_tx(Cli* cli, string_t args); | ||||
| 
 | ||||
| static const struct { | ||||
|     const char* cmd; | ||||
|     void (*process_function)(Cli* cli, string_t args); | ||||
| } irda_cli_commands[] = { | ||||
|     {.cmd = "rx", .process_function = irda_cli_start_ir_rx}, | ||||
|     {.cmd = "tx", .process_function = irda_cli_start_ir_tx}, | ||||
| } infrared_cli_commands[] = { | ||||
|     {.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, | ||||
|     {.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, | ||||
| }; | ||||
| 
 | ||||
| static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { | ||||
| static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { | ||||
|     furi_assert(received_signal); | ||||
|     char buf[100]; | ||||
|     size_t buf_cnt; | ||||
|     Cli* cli = (Cli*)context; | ||||
| 
 | ||||
|     if(irda_worker_signal_is_decoded(received_signal)) { | ||||
|         const IrdaMessage* message = irda_worker_get_decoded_signal(received_signal); | ||||
|     if(infrared_worker_signal_is_decoded(received_signal)) { | ||||
|         const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); | ||||
|         buf_cnt = sniprintf( | ||||
|             buf, | ||||
|             sizeof(buf), | ||||
|             "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", | ||||
|             irda_get_protocol_name(message->protocol), | ||||
|             ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4), | ||||
|             infrared_get_protocol_name(message->protocol), | ||||
|             ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), | ||||
|             message->address, | ||||
|             ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||
|             ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), | ||||
|             message->command, | ||||
|             message->repeat ? " R" : ""); | ||||
|         cli_write(cli, (uint8_t*)buf, buf_cnt); | ||||
|     } else { | ||||
|         const uint32_t* timings; | ||||
|         size_t timings_cnt; | ||||
|         irda_worker_get_raw_signal(received_signal, &timings, &timings_cnt); | ||||
|         infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); | ||||
| 
 | ||||
|         buf_cnt = sniprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt); | ||||
|         cli_write(cli, (uint8_t*)buf, buf_cnt); | ||||
| @ -59,39 +59,39 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void irda_cli_start_ir_rx(Cli* cli, string_t args) { | ||||
|     IrdaWorker* worker = irda_worker_alloc(); | ||||
|     irda_worker_rx_start(worker); | ||||
|     irda_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); | ||||
| static void infrared_cli_start_ir_rx(Cli* cli, string_t args) { | ||||
|     InfraredWorker* worker = infrared_worker_alloc(); | ||||
|     infrared_worker_rx_start(worker); | ||||
|     infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); | ||||
| 
 | ||||
|     printf("Receiving IRDA...\r\nPress Ctrl+C to abort\r\n"); | ||||
|     printf("Receiving INFRARED...\r\nPress Ctrl+C to abort\r\n"); | ||||
|     while(!cli_cmd_interrupt_received(cli)) { | ||||
|         delay(50); | ||||
|     } | ||||
| 
 | ||||
|     irda_worker_rx_stop(worker); | ||||
|     irda_worker_free(worker); | ||||
|     infrared_worker_rx_stop(worker); | ||||
|     infrared_worker_free(worker); | ||||
| } | ||||
| 
 | ||||
| static void irda_cli_print_usage(void) { | ||||
| static void infrared_cli_print_usage(void) { | ||||
|     printf("Usage:\r\n"); | ||||
|     printf("\tir rx\r\n"); | ||||
|     printf("\tir tx <protocol> <address> <command>\r\n"); | ||||
|     printf("\t<command> and <address> are hex-formatted\r\n"); | ||||
|     printf("\tAvailable protocols:"); | ||||
|     for(int i = 0; irda_is_protocol_valid((IrdaProtocol)i); ++i) { | ||||
|         printf(" %s", irda_get_protocol_name((IrdaProtocol)i)); | ||||
|     for(int i = 0; infrared_is_protocol_valid((InfraredProtocol)i); ++i) { | ||||
|         printf(" %s", infrared_get_protocol_name((InfraredProtocol)i)); | ||||
|     } | ||||
|     printf("\r\n"); | ||||
|     printf("\tRaw format:\r\n"); | ||||
|     printf("\tir_tx RAW F:<frequency> DC:<duty_cycle> <sample0> <sample1>...\r\n"); | ||||
|     printf( | ||||
|         "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n", | ||||
|         IRDA_MIN_FREQUENCY, | ||||
|         IRDA_MAX_FREQUENCY); | ||||
|         INFRARED_MIN_FREQUENCY, | ||||
|         INFRARED_MAX_FREQUENCY); | ||||
| } | ||||
| 
 | ||||
| static bool parse_message(const char* str, IrdaMessage* message) { | ||||
| static bool parse_message(const char* str, InfraredMessage* message) { | ||||
|     char protocol_name[32]; | ||||
|     int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message->address, &message->command); | ||||
| 
 | ||||
| @ -99,10 +99,10 @@ static bool parse_message(const char* str, IrdaMessage* message) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     message->protocol = irda_get_protocol_by_name(protocol_name); | ||||
|     message->protocol = infrared_get_protocol_by_name(protocol_name); | ||||
|     message->repeat = false; | ||||
| 
 | ||||
|     return irda_parser_is_parsed_signal_valid(message); | ||||
|     return infrared_parser_is_parsed_signal_valid(message); | ||||
| } | ||||
| 
 | ||||
| static bool parse_signal_raw( | ||||
| @ -136,11 +136,11 @@ static bool parse_signal_raw( | ||||
|         ++*timings_cnt; | ||||
|     } | ||||
| 
 | ||||
|     return irda_parser_is_raw_signal_valid(*frequency, *duty_cycle, *timings_cnt); | ||||
|     return infrared_parser_is_raw_signal_valid(*frequency, *duty_cycle, *timings_cnt); | ||||
| } | ||||
| 
 | ||||
| static void irda_cli_start_ir_tx(Cli* cli, string_t args) { | ||||
|     IrdaMessage message; | ||||
| static void infrared_cli_start_ir_tx(Cli* cli, string_t args) { | ||||
|     InfraredMessage message; | ||||
|     const char* str = string_get_cstr(args); | ||||
|     uint32_t frequency; | ||||
|     float duty_cycle; | ||||
| @ -148,27 +148,27 @@ static void irda_cli_start_ir_tx(Cli* cli, string_t args) { | ||||
|     uint32_t* timings = (uint32_t*)malloc(sizeof(uint32_t) * timings_cnt); | ||||
| 
 | ||||
|     if(parse_message(str, &message)) { | ||||
|         irda_send(&message, 1); | ||||
|         infrared_send(&message, 1); | ||||
|     } else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) { | ||||
|         irda_send_raw_ext(timings, timings_cnt, true, frequency, duty_cycle); | ||||
|         infrared_send_raw_ext(timings, timings_cnt, true, frequency, duty_cycle); | ||||
|     } else { | ||||
|         printf("Wrong arguments.\r\n"); | ||||
|         irda_cli_print_usage(); | ||||
|         infrared_cli_print_usage(); | ||||
|     } | ||||
| 
 | ||||
|     free(timings); | ||||
| } | ||||
| 
 | ||||
| static void irda_cli_start_ir(Cli* cli, string_t args, void* context) { | ||||
|     if(furi_hal_irda_is_busy()) { | ||||
|         printf("IRDA is busy. Exit."); | ||||
| static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { | ||||
|     if(furi_hal_infrared_is_busy()) { | ||||
|         printf("INFRARED is busy. Exit."); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     size_t i = 0; | ||||
|     for(; i < COUNT_OF(irda_cli_commands); ++i) { | ||||
|         size_t size = strlen(irda_cli_commands[i].cmd); | ||||
|         bool cmd_found = !strncmp(string_get_cstr(args), irda_cli_commands[i].cmd, size); | ||||
|     for(; i < COUNT_OF(infrared_cli_commands); ++i) { | ||||
|         size_t size = strlen(infrared_cli_commands[i].cmd); | ||||
|         bool cmd_found = !strncmp(string_get_cstr(args), infrared_cli_commands[i].cmd, size); | ||||
|         if(cmd_found) { | ||||
|             if(string_size(args) == size) { | ||||
|                 break; | ||||
| @ -180,17 +180,17 @@ static void irda_cli_start_ir(Cli* cli, string_t args, void* context) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(i < COUNT_OF(irda_cli_commands)) { | ||||
|         irda_cli_commands[i].process_function(cli, args); | ||||
|     if(i < COUNT_OF(infrared_cli_commands)) { | ||||
|         infrared_cli_commands[i].process_function(cli, args); | ||||
|     } else { | ||||
|         irda_cli_print_usage(); | ||||
|         infrared_cli_print_usage(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| extern "C" void irda_on_system_start() { | ||||
| extern "C" void infrared_on_system_start() { | ||||
| #ifdef SRV_CLI | ||||
|     Cli* cli = (Cli*)furi_record_open("cli"); | ||||
|     cli_add_command(cli, "ir", CliCommandFlagDefault, irda_cli_start_ir, NULL); | ||||
|     cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); | ||||
|     furi_record_close("cli"); | ||||
| #endif | ||||
| } | ||||
| @ -1,19 +1,19 @@ | ||||
| 
 | ||||
| #include "../irda_app_signal.h" | ||||
| #include "irda.h" | ||||
| #include "irda/helpers/irda_parser.h" | ||||
| #include "irda_worker.h" | ||||
| #include "../infrared_app_signal.h" | ||||
| #include "infrared.h" | ||||
| #include "infrared/helpers/infrared_parser.h" | ||||
| #include "infrared_worker.h" | ||||
| #include "m-string.h" | ||||
| #include <flipper_format/flipper_format.h> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <furi_hal_irda.h> | ||||
| #include <furi_hal_infrared.h> | ||||
| 
 | ||||
| #define TAG "IrdaParser" | ||||
| #define TAG "InfraredParser" | ||||
| 
 | ||||
| bool irda_parser_save_signal( | ||||
| bool infrared_parser_save_signal( | ||||
|     FlipperFormat* ff, | ||||
|     const IrdaAppSignal& signal, | ||||
|     const InfraredAppSignal& signal, | ||||
|     const std::string& name) { | ||||
|     furi_assert(ff); | ||||
|     furi_assert(!name.empty()); | ||||
| @ -33,7 +33,7 @@ bool irda_parser_save_signal( | ||||
|                 break; | ||||
|         } else { | ||||
|             auto parsed_signal = signal.get_message(); | ||||
|             const char* protocol_name = irda_get_protocol_name(parsed_signal.protocol); | ||||
|             const char* protocol_name = infrared_get_protocol_name(parsed_signal.protocol); | ||||
|             if(!flipper_format_write_string_cstr(ff, "type", "parsed")) break; | ||||
|             if(!flipper_format_write_string_cstr(ff, "protocol", protocol_name)) break; | ||||
|             if(!flipper_format_write_hex(ff, "address", (uint8_t*)&parsed_signal.address, 4)) | ||||
| @ -47,7 +47,7 @@ bool irda_parser_save_signal( | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool irda_parser_read_signal(FlipperFormat* ff, IrdaAppSignal& signal, std::string& name) { | ||||
| bool infrared_parser_read_signal(FlipperFormat* ff, InfraredAppSignal& signal, std::string& name) { | ||||
|     furi_assert(ff); | ||||
| 
 | ||||
|     bool result = false; | ||||
| @ -75,12 +75,12 @@ bool irda_parser_read_signal(FlipperFormat* ff, IrdaAppSignal& signal, std::stri | ||||
|             } | ||||
|             free(timings); | ||||
|         } else if(!string_cmp_str(read_string, "parsed")) { | ||||
|             IrdaMessage parsed_signal; | ||||
|             InfraredMessage parsed_signal; | ||||
|             if(!flipper_format_read_string(ff, "protocol", read_string)) break; | ||||
|             parsed_signal.protocol = irda_get_protocol_by_name(string_get_cstr(read_string)); | ||||
|             parsed_signal.protocol = infrared_get_protocol_by_name(string_get_cstr(read_string)); | ||||
|             if(!flipper_format_read_hex(ff, "address", (uint8_t*)&parsed_signal.address, 4)) break; | ||||
|             if(!flipper_format_read_hex(ff, "command", (uint8_t*)&parsed_signal.command, 4)) break; | ||||
|             if(!irda_parser_is_parsed_signal_valid(&parsed_signal)) break; | ||||
|             if(!infrared_parser_is_parsed_signal_valid(&parsed_signal)) break; | ||||
|             signal.set_message(&parsed_signal); | ||||
|             result = true; | ||||
|         } else { | ||||
| @ -92,17 +92,17 @@ bool irda_parser_read_signal(FlipperFormat* ff, IrdaAppSignal& signal, std::stri | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool irda_parser_is_parsed_signal_valid(const IrdaMessage* signal) { | ||||
| bool infrared_parser_is_parsed_signal_valid(const InfraredMessage* signal) { | ||||
|     furi_assert(signal); | ||||
|     bool result = true; | ||||
| 
 | ||||
|     if(!irda_is_protocol_valid(signal->protocol)) { | ||||
|     if(!infrared_is_protocol_valid(signal->protocol)) { | ||||
|         FURI_LOG_E(TAG, "Unknown protocol"); | ||||
|         result = false; | ||||
|     } | ||||
| 
 | ||||
|     if(result) { | ||||
|         uint32_t address_length = irda_get_protocol_address_length(signal->protocol); | ||||
|         uint32_t address_length = infrared_get_protocol_address_length(signal->protocol); | ||||
|         uint32_t address_mask = (1LU << address_length) - 1; | ||||
|         if(signal->address != (signal->address & address_mask)) { | ||||
|             FURI_LOG_E( | ||||
| @ -115,7 +115,7 @@ bool irda_parser_is_parsed_signal_valid(const IrdaMessage* signal) { | ||||
|     } | ||||
| 
 | ||||
|     if(result) { | ||||
|         uint32_t command_length = irda_get_protocol_command_length(signal->protocol); | ||||
|         uint32_t command_length = infrared_get_protocol_command_length(signal->protocol); | ||||
|         uint32_t command_mask = (1LU << command_length) - 1; | ||||
|         if(signal->command != (signal->command & command_mask)) { | ||||
|             FURI_LOG_E( | ||||
| @ -130,15 +130,18 @@ bool irda_parser_is_parsed_signal_valid(const IrdaMessage* signal) { | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool irda_parser_is_raw_signal_valid(uint32_t frequency, float duty_cycle, uint32_t timings_cnt) { | ||||
| bool infrared_parser_is_raw_signal_valid( | ||||
|     uint32_t frequency, | ||||
|     float duty_cycle, | ||||
|     uint32_t timings_cnt) { | ||||
|     bool result = true; | ||||
| 
 | ||||
|     if((frequency > IRDA_MAX_FREQUENCY) || (frequency < IRDA_MIN_FREQUENCY)) { | ||||
|     if((frequency > INFRARED_MAX_FREQUENCY) || (frequency < INFRARED_MIN_FREQUENCY)) { | ||||
|         FURI_LOG_E( | ||||
|             TAG, | ||||
|             "Frequency is out of range (%lX - %lX): %lX", | ||||
|             IRDA_MIN_FREQUENCY, | ||||
|             IRDA_MAX_FREQUENCY, | ||||
|             INFRARED_MIN_FREQUENCY, | ||||
|             INFRARED_MAX_FREQUENCY, | ||||
|             frequency); | ||||
|         result = false; | ||||
|     } else if((duty_cycle <= 0) || (duty_cycle > 1)) { | ||||
							
								
								
									
										48
									
								
								applications/infrared/helpers/infrared_parser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								applications/infrared/helpers/infrared_parser.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| /**
 | ||||
|   * @file infrared_parser.h | ||||
|   * Infrared: Helper file for conversion Flipper File Format | ||||
|   *     to Infrared signal class, and backwards | ||||
|   */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "../infrared_app_signal.h" | ||||
| #include <flipper_format/flipper_format.h> | ||||
| #include <string> | ||||
| 
 | ||||
| /** Save Infrared signal into file
 | ||||
|  * | ||||
|  * @param ff - Flipper File Format instance | ||||
|  * @param signal - Infrared signal to save | ||||
|  * @param name - name for saved signal. Every | ||||
|  *      signal on disk has name. | ||||
|  */ | ||||
| bool infrared_parser_save_signal( | ||||
|     FlipperFormat* ff, | ||||
|     const InfraredAppSignal& signal, | ||||
|     const std::string& name); | ||||
| 
 | ||||
| /** Read Infrared signal from file
 | ||||
|  * | ||||
|  * @param ff - Flipper File Format instance | ||||
|  * @param signal - Infrared signal to read to | ||||
|  * @param name - name for saved signal. Every | ||||
|  *      signal in file has name. | ||||
|  */ | ||||
| bool infrared_parser_read_signal(FlipperFormat* ff, InfraredAppSignal& signal, std::string& name); | ||||
| 
 | ||||
| /** Validate parsed signal
 | ||||
|  * | ||||
|  * @signal - signal to validate | ||||
|  * @retval true if valid, false otherwise | ||||
|  */ | ||||
| bool infrared_parser_is_parsed_signal_valid(const InfraredMessage* signal); | ||||
| 
 | ||||
| /** Validate raw signal
 | ||||
|  * | ||||
|  * @signal - signal to validate | ||||
|  * @retval true if valid, false otherwise | ||||
|  */ | ||||
| bool infrared_parser_is_raw_signal_valid( | ||||
|     uint32_t frequency, | ||||
|     float duty_cycle, | ||||
|     uint32_t timings_cnt); | ||||
| @ -1,13 +1,13 @@ | ||||
| #include "irda_app.h" | ||||
| #include <irda_worker.h> | ||||
| #include "infrared_app.h" | ||||
| #include <infrared_worker.h> | ||||
| #include <furi.h> | ||||
| #include <gui/gui.h> | ||||
| #include <input/input.h> | ||||
| #include <stdio.h> | ||||
| #include <callback-connector.h> | ||||
| 
 | ||||
| int32_t IrdaApp::run(void* args) { | ||||
|     IrdaAppEvent event; | ||||
| int32_t InfraredApp::run(void* args) { | ||||
|     InfraredAppEvent event; | ||||
|     bool consumed; | ||||
|     bool exit = false; | ||||
| 
 | ||||
| @ -17,7 +17,7 @@ int32_t IrdaApp::run(void* args) { | ||||
|         remote_name.erase(remote_name.find_last_of('.')); | ||||
|         bool result = remote_manager.load(remote_name); | ||||
|         if(result) { | ||||
|             current_scene = IrdaApp::Scene::Remote; | ||||
|             current_scene = InfraredApp::Scene::Remote; | ||||
|         } else { | ||||
|             printf("Failed to load remote \'%s\'\r\n", remote_name.c_str()); | ||||
|             return -1; | ||||
| @ -29,12 +29,12 @@ int32_t IrdaApp::run(void* args) { | ||||
|     while(!exit) { | ||||
|         view_manager.receive_event(&event); | ||||
| 
 | ||||
|         if(event.type == IrdaAppEvent::Type::Exit) break; | ||||
|         if(event.type == InfraredAppEvent::Type::Exit) break; | ||||
| 
 | ||||
|         consumed = scenes[current_scene]->on_event(this, &event); | ||||
| 
 | ||||
|         if(!consumed) { | ||||
|             if(event.type == IrdaAppEvent::Type::Back) { | ||||
|             if(event.type == InfraredAppEvent::Type::Back) { | ||||
|                 exit = switch_to_previous_scene(); | ||||
|             } | ||||
|         } | ||||
| @ -45,36 +45,36 @@ int32_t IrdaApp::run(void* args) { | ||||
|     return 0; | ||||
| }; | ||||
| 
 | ||||
| IrdaApp::IrdaApp() { | ||||
|     furi_check(IrdaAppRemoteManager::max_button_name_length < get_text_store_size()); | ||||
| InfraredApp::InfraredApp() { | ||||
|     furi_check(InfraredAppRemoteManager::max_button_name_length < get_text_store_size()); | ||||
|     notification = static_cast<NotificationApp*>(furi_record_open("notification")); | ||||
|     irda_worker = irda_worker_alloc(); | ||||
|     infrared_worker = infrared_worker_alloc(); | ||||
| } | ||||
| 
 | ||||
| IrdaApp::~IrdaApp() { | ||||
|     irda_worker_free(irda_worker); | ||||
| InfraredApp::~InfraredApp() { | ||||
|     infrared_worker_free(infrared_worker); | ||||
|     furi_record_close("notification"); | ||||
|     for(auto& [key, scene] : scenes) delete scene; | ||||
| } | ||||
| 
 | ||||
| IrdaAppViewManager* IrdaApp::get_view_manager() { | ||||
| InfraredAppViewManager* InfraredApp::get_view_manager() { | ||||
|     return &view_manager; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::set_learn_new_remote(bool value) { | ||||
| void InfraredApp::set_learn_new_remote(bool value) { | ||||
|     learn_new_remote = value; | ||||
| } | ||||
| 
 | ||||
| bool IrdaApp::get_learn_new_remote() { | ||||
| bool InfraredApp::get_learn_new_remote() { | ||||
|     return learn_new_remote; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::switch_to_next_scene(Scene next_scene) { | ||||
| void InfraredApp::switch_to_next_scene(Scene next_scene) { | ||||
|     previous_scenes_list.push_front(current_scene); | ||||
|     switch_to_next_scene_without_saving(next_scene); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::switch_to_next_scene_without_saving(Scene next_scene) { | ||||
| void InfraredApp::switch_to_next_scene_without_saving(Scene next_scene) { | ||||
|     if(next_scene != Scene::Exit) { | ||||
|         scenes[current_scene]->on_exit(this); | ||||
|         current_scene = next_scene; | ||||
| @ -83,7 +83,8 @@ void IrdaApp::switch_to_next_scene_without_saving(Scene next_scene) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::search_and_switch_to_previous_scene(const std::initializer_list<Scene>& scenes_list) { | ||||
| void InfraredApp::search_and_switch_to_previous_scene( | ||||
|     const std::initializer_list<Scene>& scenes_list) { | ||||
|     Scene previous_scene = Scene::Start; | ||||
|     bool scene_found = false; | ||||
| 
 | ||||
| @ -101,8 +102,8 @@ void IrdaApp::search_and_switch_to_previous_scene(const std::initializer_list<Sc | ||||
|     } | ||||
| 
 | ||||
|     if(previous_scene == Scene::Exit) { | ||||
|         IrdaAppEvent event; | ||||
|         event.type = IrdaAppEvent::Type::Exit; | ||||
|         InfraredAppEvent event; | ||||
|         event.type = InfraredAppEvent::Type::Exit; | ||||
|         view_manager.send_event(&event); | ||||
|     } else { | ||||
|         scenes[current_scene]->on_exit(this); | ||||
| @ -112,7 +113,7 @@ void IrdaApp::search_and_switch_to_previous_scene(const std::initializer_list<Sc | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool IrdaApp::switch_to_previous_scene(uint8_t count) { | ||||
| bool InfraredApp::switch_to_previous_scene(uint8_t count) { | ||||
|     Scene previous_scene = Scene::Start; | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < count; i++) previous_scene = get_previous_scene(); | ||||
| @ -126,7 +127,7 @@ bool IrdaApp::switch_to_previous_scene(uint8_t count) { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| IrdaApp::Scene IrdaApp::get_previous_scene() { | ||||
| InfraredApp::Scene InfraredApp::get_previous_scene() { | ||||
|     Scene scene = Scene::Exit; | ||||
| 
 | ||||
|     if(!previous_scenes_list.empty()) { | ||||
| @ -137,11 +138,11 @@ IrdaApp::Scene IrdaApp::get_previous_scene() { | ||||
|     return scene; | ||||
| } | ||||
| 
 | ||||
| IrdaAppRemoteManager* IrdaApp::get_remote_manager() { | ||||
| InfraredAppRemoteManager* InfraredApp::get_remote_manager() { | ||||
|     return &remote_manager; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::set_text_store(uint8_t index, const char* text...) { | ||||
| void InfraredApp::set_text_store(uint8_t index, const char* text...) { | ||||
|     furi_check(index < text_store_max); | ||||
| 
 | ||||
|     va_list args; | ||||
| @ -152,77 +153,63 @@ void IrdaApp::set_text_store(uint8_t index, const char* text...) { | ||||
|     va_end(args); | ||||
| } | ||||
| 
 | ||||
| char* IrdaApp::get_text_store(uint8_t index) { | ||||
| char* InfraredApp::get_text_store(uint8_t index) { | ||||
|     furi_check(index < text_store_max); | ||||
| 
 | ||||
|     return text_store[index]; | ||||
| } | ||||
| 
 | ||||
| uint8_t IrdaApp::get_text_store_size() { | ||||
| uint8_t InfraredApp::get_text_store_size() { | ||||
|     return text_store_size; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::text_input_callback(void* context) { | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
|     event.type = IrdaAppEvent::Type::TextEditDone; | ||||
| void InfraredApp::text_input_callback(void* context) { | ||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
|     event.type = InfraredAppEvent::Type::TextEditDone; | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::popup_callback(void* context) { | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
|     event.type = IrdaAppEvent::Type::PopupTimer; | ||||
| void InfraredApp::popup_callback(void* context) { | ||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
|     event.type = InfraredAppEvent::Type::PopupTimer; | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::set_edit_element(IrdaApp::EditElement value) { | ||||
| void InfraredApp::set_edit_element(InfraredApp::EditElement value) { | ||||
|     element = value; | ||||
| } | ||||
| 
 | ||||
| IrdaApp::EditElement IrdaApp::get_edit_element(void) { | ||||
| InfraredApp::EditElement InfraredApp::get_edit_element(void) { | ||||
|     return element; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::set_edit_action(IrdaApp::EditAction value) { | ||||
| void InfraredApp::set_edit_action(InfraredApp::EditAction value) { | ||||
|     action = value; | ||||
| } | ||||
| 
 | ||||
| IrdaApp::EditAction IrdaApp::get_edit_action(void) { | ||||
| InfraredApp::EditAction InfraredApp::get_edit_action(void) { | ||||
|     return action; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::set_current_button(int value) { | ||||
| void InfraredApp::set_current_button(int value) { | ||||
|     current_button = value; | ||||
| } | ||||
| 
 | ||||
| int IrdaApp::get_current_button() { | ||||
| int InfraredApp::get_current_button() { | ||||
|     return current_button; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_success() { | ||||
| void InfraredApp::notify_success() { | ||||
|     notification_message(notification, &sequence_success); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_red_blink() { | ||||
| void InfraredApp::notify_red_blink() { | ||||
|     notification_message(notification, &sequence_blink_red_10); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_sent_just_learnt() { | ||||
|     static const NotificationSequence sequence = { | ||||
|         &message_green_0, | ||||
|         &message_vibro_on, | ||||
|         &message_delay_50, | ||||
|         &message_vibro_off, | ||||
|         &message_green_255, | ||||
|         &message_do_not_reset, | ||||
|         NULL, | ||||
|     }; | ||||
| 
 | ||||
|     notification_message_block(notification, &sequence); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_click() { | ||||
| void InfraredApp::notify_click() { | ||||
|     static const NotificationSequence sequence = { | ||||
|         &message_click, | ||||
|         &message_delay_1, | ||||
| @ -233,7 +220,7 @@ void IrdaApp::notify_click() { | ||||
|     notification_message_block(notification, &sequence); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_click_and_green_blink() { | ||||
| void InfraredApp::notify_click_and_green_blink() { | ||||
|     static const NotificationSequence sequence = { | ||||
|         &message_click, | ||||
|         &message_delay_1, | ||||
| @ -248,7 +235,7 @@ void IrdaApp::notify_click_and_green_blink() { | ||||
|     notification_message_block(notification, &sequence); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_blink_green() { | ||||
| void InfraredApp::notify_blink_green() { | ||||
|     static const NotificationSequence sequence = { | ||||
|         &message_green_255, | ||||
|         &message_delay_10, | ||||
| @ -260,27 +247,27 @@ void IrdaApp::notify_blink_green() { | ||||
|     notification_message(notification, &sequence); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_green_on() { | ||||
| void InfraredApp::notify_green_on() { | ||||
|     notification_message(notification, &sequence_set_only_green_255); | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::notify_green_off() { | ||||
| void InfraredApp::notify_green_off() { | ||||
|     notification_message(notification, &sequence_reset_green); | ||||
| } | ||||
| 
 | ||||
| IrdaWorker* IrdaApp::get_irda_worker() { | ||||
|     return irda_worker; | ||||
| InfraredWorker* InfraredApp::get_infrared_worker() { | ||||
|     return infrared_worker; | ||||
| } | ||||
| 
 | ||||
| const IrdaAppSignal& IrdaApp::get_received_signal() const { | ||||
| const InfraredAppSignal& InfraredApp::get_received_signal() const { | ||||
|     return received_signal; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::set_received_signal(const IrdaAppSignal& signal) { | ||||
| void InfraredApp::set_received_signal(const InfraredAppSignal& signal) { | ||||
|     received_signal = signal; | ||||
| } | ||||
| 
 | ||||
| void IrdaApp::signal_sent_callback(void* context) { | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
| void InfraredApp::signal_sent_callback(void* context) { | ||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||
|     app->notify_blink_green(); | ||||
| } | ||||
							
								
								
									
										322
									
								
								applications/infrared/infrared_app.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								applications/infrared/infrared_app.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,322 @@ | ||||
| /**
 | ||||
|   * @file infrared_app.h | ||||
|   * Infrared: Main infrared application class | ||||
|   */ | ||||
| #pragma once | ||||
| #include <map> | ||||
| #include <infrared.h> | ||||
| #include <furi.h> | ||||
| #include <forward_list> | ||||
| #include <stdint.h> | ||||
| #include <notification/notification_messages.h> | ||||
| #include <infrared_worker.h> | ||||
| 
 | ||||
| #include "scene/infrared_app_scene.h" | ||||
| #include "scene/infrared_app_scene.h" | ||||
| #include "infrared_app_view_manager.h" | ||||
| #include "infrared_app_remote_manager.h" | ||||
| #include "infrared_app_view_manager.h" | ||||
| 
 | ||||
| /** Main Infrared application class */ | ||||
| class InfraredApp { | ||||
| public: | ||||
|     /** Enum to save scene state: edit element */ | ||||
|     enum class EditElement : uint8_t { | ||||
|         Button, | ||||
|         Remote, | ||||
|     }; | ||||
|     /** Enum to save scene state: edit action */ | ||||
|     enum class EditAction : uint8_t { | ||||
|         Rename, | ||||
|         Delete, | ||||
|     }; | ||||
|     /** List of scenes for Infrared application */ | ||||
|     enum class Scene : uint8_t { | ||||
|         Exit, | ||||
|         Start, | ||||
|         Universal, | ||||
|         UniversalTV, | ||||
|         UniversalAudio, | ||||
|         UniversalAirConditioner, | ||||
|         Learn, | ||||
|         LearnSuccess, | ||||
|         LearnEnterName, | ||||
|         LearnDone, | ||||
|         AskBack, | ||||
|         Remote, | ||||
|         RemoteList, | ||||
|         Edit, | ||||
|         EditKeySelect, | ||||
|         EditRename, | ||||
|         EditDelete, | ||||
|         EditRenameDone, | ||||
|         EditDeleteDone, | ||||
|     }; | ||||
| 
 | ||||
|     /** Start application
 | ||||
|  * | ||||
|  * @param args - application arguments. | ||||
|  *      Allowed argument is path to remote file. | ||||
|  * @retval 0 on success, error code otherwise | ||||
|  */ | ||||
|     int32_t run(void* args); | ||||
| 
 | ||||
|     /** Switch to next scene. Put current scene number on stack.
 | ||||
|  * Doesn't save scene state. | ||||
|  * | ||||
|  * @param index - next scene index | ||||
|  */ | ||||
|     void switch_to_next_scene(Scene index); | ||||
| 
 | ||||
|     /** Switch to next scene, but don't put current scene on
 | ||||
|  * stack. Thus calling switch_to_previous_scene() doesn't return | ||||
|  * to current scene. | ||||
|  * | ||||
|  * @param index - next scene index | ||||
|  */ | ||||
|     void switch_to_next_scene_without_saving(Scene index); | ||||
| 
 | ||||
|     /** Switch to previous scene. Pop scenes from stack and switch to last one.
 | ||||
|  * | ||||
|  * @param count - how many scenes should be popped | ||||
|  * @retval false on failed, true on success | ||||
|  */ | ||||
|     bool switch_to_previous_scene(uint8_t count = 1); | ||||
| 
 | ||||
|     /** Get previous scene in scene stack
 | ||||
|  * | ||||
|  * @retval previous scene | ||||
|  */ | ||||
|     Scene get_previous_scene(); | ||||
| 
 | ||||
|     /** Get view manager instance
 | ||||
|  * | ||||
|  * @retval view manager instance | ||||
|  */ | ||||
|     InfraredAppViewManager* get_view_manager(); | ||||
| 
 | ||||
|     /** Set one of text stores
 | ||||
|  * | ||||
|  * @param index - index of text store | ||||
|  * @param text - text to set | ||||
|  */ | ||||
|     void set_text_store(uint8_t index, const char* text...); | ||||
| 
 | ||||
|     /** Get value in text store
 | ||||
|  * | ||||
|  * @param index - index of text store | ||||
|  * @retval value in text_store | ||||
|  */ | ||||
|     char* get_text_store(uint8_t index); | ||||
| 
 | ||||
|     /** Get text store size
 | ||||
|  * | ||||
|  * @retval size of text store | ||||
|  */ | ||||
|     uint8_t get_text_store_size(); | ||||
| 
 | ||||
|     /** Get remote manager instance
 | ||||
|  * | ||||
|  * @retval remote manager instance | ||||
|  */ | ||||
|     InfraredAppRemoteManager* get_remote_manager(); | ||||
| 
 | ||||
|     /** Get infrared worker instance
 | ||||
|  * | ||||
|  * @retval infrared worker instance | ||||
|  */ | ||||
|     InfraredWorker* get_infrared_worker(); | ||||
| 
 | ||||
|     /** Get signal, previously got on Learn scene
 | ||||
|  * | ||||
|  * @retval received signal | ||||
|  */ | ||||
|     const InfraredAppSignal& get_received_signal() const; | ||||
| 
 | ||||
|     /** Set received signal
 | ||||
|  * | ||||
|  * @param signal - signal | ||||
|  */ | ||||
|     void set_received_signal(const InfraredAppSignal& signal); | ||||
| 
 | ||||
|     /** Switch to previous scene in one of provided in list.
 | ||||
|  * Pop scene stack, and find first scene from list. | ||||
|  * | ||||
|  * @param scenes_list - list of scenes | ||||
|  */ | ||||
|     void search_and_switch_to_previous_scene(const std::initializer_list<Scene>& scenes_list); | ||||
| 
 | ||||
|     /** Set edit element value. It is used on edit scene to determine
 | ||||
|  * what should be deleted - remote or button. | ||||
|  * | ||||
|  * @param value - value to set | ||||
|  */ | ||||
|     void set_edit_element(EditElement value); | ||||
| 
 | ||||
|     /** Get edit element
 | ||||
|  * | ||||
|  * @retval edit element value | ||||
|  */ | ||||
|     EditElement get_edit_element(void); | ||||
| 
 | ||||
|     /** Set edit action value. It is used on edit scene to determine
 | ||||
|  * what action to perform - deletion or renaming. | ||||
|  * | ||||
|  * @param value - value to set | ||||
|  */ | ||||
|     void set_edit_action(EditAction value); | ||||
| 
 | ||||
|     /** Get edit action
 | ||||
|  * | ||||
|  * @retval edit action value | ||||
|  */ | ||||
|     EditAction get_edit_action(void); | ||||
| 
 | ||||
|     /** Get state of learning new signal.
 | ||||
|  * Adding new remote with 1 button from start scene and | ||||
|  * learning 1 additional button to remote have very similar | ||||
|  * flow, so they are joined. Difference in flow is handled | ||||
|  * by this boolean flag. | ||||
|  * | ||||
|  * @retval false if flow is in learning new remote, true if | ||||
|  *      adding signal to created remote | ||||
|  * | ||||
|  */ | ||||
|     bool get_learn_new_remote(); | ||||
| 
 | ||||
|     /** Set state of learning new signal.
 | ||||
|  * Adding new remote with 1 button from start scene and | ||||
|  * learning 1 additional button to remote have very similar | ||||
|  * flow, so they are joined. Difference in flow is handled | ||||
|  * by this boolean flag. | ||||
|  * | ||||
|  * @param value - false if flow is in learning new remote, true if | ||||
|  *      adding signal to created remote | ||||
|  */ | ||||
|     void set_learn_new_remote(bool value); | ||||
| 
 | ||||
|     /** Button is not assigned value
 | ||||
|  */ | ||||
|     enum : int { | ||||
|         ButtonNA = -1, | ||||
|     }; | ||||
| 
 | ||||
|     /** Get current button index
 | ||||
|  * | ||||
|  * @retval current button index | ||||
|  */ | ||||
|     int get_current_button(); | ||||
| 
 | ||||
|     /** Set current button index
 | ||||
|  * | ||||
|  * @param current button index | ||||
|  */ | ||||
|     void set_current_button(int value); | ||||
| 
 | ||||
|     /** Play success notification */ | ||||
|     void notify_success(); | ||||
|     /** Play red blink notification */ | ||||
|     void notify_red_blink(); | ||||
|     /** Light green */ | ||||
|     void notify_green_on(); | ||||
|     /** Disable green light */ | ||||
|     void notify_green_off(); | ||||
|     /** Play click sound */ | ||||
|     void notify_click(); | ||||
|     /** Play click and green notification */ | ||||
|     void notify_click_and_green_blink(); | ||||
|     /** Blink green light */ | ||||
|     void notify_blink_green(); | ||||
| 
 | ||||
|     /** Text input callback
 | ||||
|  * | ||||
|  * @param context - context to pass to callback | ||||
|  */ | ||||
|     static void text_input_callback(void* context); | ||||
| 
 | ||||
|     /** Popup callback
 | ||||
|  * | ||||
|  * @param context - context to pass to callback | ||||
|  */ | ||||
|     static void popup_callback(void* context); | ||||
| 
 | ||||
|     /** Signal sent callback
 | ||||
|  * | ||||
|  * @param context - context to pass to callback | ||||
|  */ | ||||
|     static void signal_sent_callback(void* context); | ||||
| 
 | ||||
|     /** Main class constructor, initializes all critical objects */ | ||||
|     InfraredApp(); | ||||
|     /** Main class destructor, deinitializes all critical objects */ | ||||
|     ~InfraredApp(); | ||||
| 
 | ||||
|     /** Path to Infrared directory */ | ||||
|     static constexpr const char* infrared_directory = "/any/infrared"; | ||||
|     /** Infrared files extension (remote files and universal databases) */ | ||||
|     static constexpr const char* infrared_extension = ".ir"; | ||||
|     /** Max Raw timings in signal */ | ||||
|     static constexpr const uint32_t max_raw_timings_in_signal = 512; | ||||
|     /** Max line length in Infrared file */ | ||||
|     static constexpr const uint32_t max_line_length = | ||||
|         (9 + 1) * InfraredApp::max_raw_timings_in_signal + 100; | ||||
| 
 | ||||
| private: | ||||
|     /** Text store size */ | ||||
|     static constexpr const uint8_t text_store_size = 128; | ||||
|     /** Amount of text stores */ | ||||
|     static constexpr const uint8_t text_store_max = 2; | ||||
|     /** Store text here, for some views, because they doesn't
 | ||||
|  * hold ownership of text */ | ||||
|     char text_store[text_store_max][text_store_size + 1]; | ||||
|     /**
 | ||||
|  * Flag to control adding new signal flow. | ||||
|  * Adding new remote with 1 button from start scene and | ||||
|  * learning 1 additional button to remote have very similar | ||||
|  * flow, so they are joined. Difference in flow is handled | ||||
|  * by this boolean flag. | ||||
|  */ | ||||
|     bool learn_new_remote; | ||||
|     /** Value to control edit scene */ | ||||
|     EditElement element; | ||||
|     /** Value to control edit scene */ | ||||
|     EditAction action; | ||||
|     /** Selected button index */ | ||||
|     uint32_t current_button; | ||||
| 
 | ||||
|     /** Notification instance */ | ||||
|     NotificationApp* notification; | ||||
|     /** View manager instance */ | ||||
|     InfraredAppViewManager view_manager; | ||||
|     /** Remote manager instance */ | ||||
|     InfraredAppRemoteManager remote_manager; | ||||
|     /** Infrared worker instance */ | ||||
|     InfraredWorker* infrared_worker; | ||||
|     /** Signal received on Learn scene */ | ||||
|     InfraredAppSignal received_signal; | ||||
| 
 | ||||
|     /** Stack of previous scenes */ | ||||
|     std::forward_list<Scene> previous_scenes_list; | ||||
|     /** Now acting scene */ | ||||
|     Scene current_scene = Scene::Start; | ||||
| 
 | ||||
|     /** Map of index/scene objects */ | ||||
|     std::map<Scene, InfraredAppScene*> scenes = { | ||||
|         {Scene::Start, new InfraredAppSceneStart()}, | ||||
|         {Scene::Universal, new InfraredAppSceneUniversal()}, | ||||
|         {Scene::UniversalTV, new InfraredAppSceneUniversalTV()}, | ||||
|         {Scene::Learn, new InfraredAppSceneLearn()}, | ||||
|         {Scene::LearnSuccess, new InfraredAppSceneLearnSuccess()}, | ||||
|         {Scene::LearnEnterName, new InfraredAppSceneLearnEnterName()}, | ||||
|         {Scene::LearnDone, new InfraredAppSceneLearnDone()}, | ||||
|         {Scene::AskBack, new InfraredAppSceneAskBack()}, | ||||
|         {Scene::Remote, new InfraredAppSceneRemote()}, | ||||
|         {Scene::RemoteList, new InfraredAppSceneRemoteList()}, | ||||
|         {Scene::Edit, new InfraredAppSceneEdit()}, | ||||
|         {Scene::EditKeySelect, new InfraredAppSceneEditKeySelect()}, | ||||
|         {Scene::EditRename, new InfraredAppSceneEditRename()}, | ||||
|         {Scene::EditDelete, new InfraredAppSceneEditDelete()}, | ||||
|         {Scene::EditRenameDone, new InfraredAppSceneEditRenameDone()}, | ||||
|         {Scene::EditDeleteDone, new InfraredAppSceneEditDeleteDone()}, | ||||
|     }; | ||||
| }; | ||||
| @ -1,18 +1,18 @@ | ||||
| 
 | ||||
| #include "helpers/irda_parser.h" | ||||
| #include "irda_app_brute_force.h" | ||||
| #include "irda_app_signal.h" | ||||
| #include "helpers/infrared_parser.h" | ||||
| #include "infrared_app_brute_force.h" | ||||
| #include "infrared_app_signal.h" | ||||
| #include <memory> | ||||
| #include <m-string.h> | ||||
| #include <furi.h> | ||||
| #include <file_worker_cpp.h> | ||||
| 
 | ||||
| void IrdaAppBruteForce::add_record(int index, const char* name) { | ||||
| void InfraredAppBruteForce::add_record(int index, const char* name) { | ||||
|     records[name].index = index; | ||||
|     records[name].amount = 0; | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppBruteForce::calculate_messages() { | ||||
| bool InfraredAppBruteForce::calculate_messages() { | ||||
|     bool result = false; | ||||
| 
 | ||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); | ||||
| @ -20,7 +20,7 @@ bool IrdaAppBruteForce::calculate_messages() { | ||||
|     result = flipper_format_file_open_existing(ff, universal_db_filename); | ||||
| 
 | ||||
|     if(result) { | ||||
|         IrdaAppSignal signal; | ||||
|         InfraredAppSignal signal; | ||||
| 
 | ||||
|         string_t signal_name; | ||||
|         string_init(signal_name); | ||||
| @ -38,7 +38,7 @@ bool IrdaAppBruteForce::calculate_messages() { | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppBruteForce::stop_bruteforce() { | ||||
| void InfraredAppBruteForce::stop_bruteforce() { | ||||
|     furi_assert((current_record.size())); | ||||
| 
 | ||||
|     if(current_record.size()) { | ||||
| @ -49,15 +49,15 @@ void IrdaAppBruteForce::stop_bruteforce() { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppBruteForce::send_next_bruteforce(void) { | ||||
| bool InfraredAppBruteForce::send_next_bruteforce(void) { | ||||
|     furi_assert(current_record.size()); | ||||
|     furi_assert(ff); | ||||
| 
 | ||||
|     IrdaAppSignal signal; | ||||
|     InfraredAppSignal signal; | ||||
|     std::string signal_name; | ||||
|     bool result = false; | ||||
|     do { | ||||
|         result = irda_parser_read_signal(ff, signal, signal_name); | ||||
|         result = infrared_parser_read_signal(ff, signal, signal_name); | ||||
|     } while(result && current_record.compare(signal_name)); | ||||
| 
 | ||||
|     if(result) { | ||||
| @ -66,7 +66,7 @@ bool IrdaAppBruteForce::send_next_bruteforce(void) { | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppBruteForce::start_bruteforce(int index, int& record_amount) { | ||||
| bool InfraredAppBruteForce::start_bruteforce(int index, int& record_amount) { | ||||
|     bool result = false; | ||||
|     record_amount = 0; | ||||
| 
 | ||||
							
								
								
									
										67
									
								
								applications/infrared/infrared_app_brute_force.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								applications/infrared/infrared_app_brute_force.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| /**
 | ||||
|   * @file infrared_app_brute_force.h | ||||
|   * Infrared: Brute Force class description | ||||
|   */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <unordered_map> | ||||
| #include <memory> | ||||
| #include <flipper_format/flipper_format.h> | ||||
| 
 | ||||
| /** Class handles brute force mechanic */ | ||||
| class InfraredAppBruteForce { | ||||
|     /** Universal database filename */ | ||||
|     const char* universal_db_filename; | ||||
| 
 | ||||
|     /** Current record name (POWER, MUTE, VOL+, etc).
 | ||||
|      * This is the name of signal to brute force. */ | ||||
|     std::string current_record; | ||||
| 
 | ||||
|     /** Flipper File Format instance */ | ||||
|     FlipperFormat* ff; | ||||
| 
 | ||||
|     /** Data about every record - index in button panel view
 | ||||
|      * and amount of signals, which is need for correct | ||||
|      * progress bar displaying. */ | ||||
|     typedef struct { | ||||
|         /** Index of record in button panel view model */ | ||||
|         int index; | ||||
|         /** Amount of signals of that type (POWER, MUTE, etc) */ | ||||
|         int amount; | ||||
|     } Record; | ||||
| 
 | ||||
|     /** Container to hold Record info.
 | ||||
|      * 'key' is record name, because we have to search by both, index and name, | ||||
|      * but index search has place once per button press, and should not be | ||||
|      * noticed, but name search should occur during entering universal menu, | ||||
|      * and will go through container for every record in file, that's why | ||||
|      * more critical to have faster search by record name. | ||||
|      */ | ||||
|     std::unordered_map<std::string, Record> records; | ||||
| 
 | ||||
| public: | ||||
|     /** Calculate messages. Walk through the file ('universal_db_name')
 | ||||
|      * and calculate amount of records of certain type. */ | ||||
|     bool calculate_messages(); | ||||
| 
 | ||||
|     /** Start brute force */ | ||||
|     bool start_bruteforce(int index, int& record_amount); | ||||
| 
 | ||||
|     /** Stop brute force */ | ||||
|     void stop_bruteforce(); | ||||
| 
 | ||||
|     /** Send next signal during brute force */ | ||||
|     bool send_next_bruteforce(); | ||||
| 
 | ||||
|     /** Add record to container of records */ | ||||
|     void add_record(int index, const char* name); | ||||
| 
 | ||||
|     /** Initialize class, set db file */ | ||||
|     InfraredAppBruteForce(const char* filename) | ||||
|         : universal_db_filename(filename) { | ||||
|     } | ||||
| 
 | ||||
|     /** Deinitialize class */ | ||||
|     ~InfraredAppBruteForce() { | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										47
									
								
								applications/infrared/infrared_app_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								applications/infrared/infrared_app_event.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| /**
 | ||||
|   * @file infrared_app_event.h | ||||
|   * Infrared: Scene events description | ||||
|   */ | ||||
| #pragma once | ||||
| #include <infrared.h> | ||||
| #include <gui/modules/dialog_ex.h> | ||||
| 
 | ||||
| /** Infrared events class */ | ||||
| class InfraredAppEvent { | ||||
| public: | ||||
|     /** Type of event enum */ | ||||
|     enum class Type : uint8_t { | ||||
|         /** Tick event come after no other events came in 100 ms */ | ||||
|         Tick, | ||||
|         /** Exit application event */ | ||||
|         Exit, | ||||
|         /** Back event */ | ||||
|         Back, | ||||
|         /** Menu selected event type. Provided with payload value. */ | ||||
|         MenuSelected, | ||||
|         /** Button press event. Need for continuous signal sending. */ | ||||
|         MenuSelectedPress, | ||||
|         /** Button release event. Need for continuous signal sending. */ | ||||
|         MenuSelectedRelease, | ||||
|         /** Events from DialogEx view module */ | ||||
|         DialogExSelected, | ||||
|         /** Infrared signal received event */ | ||||
|         InfraredMessageReceived, | ||||
|         /** Text edit done event */ | ||||
|         TextEditDone, | ||||
|         /** Popup timer finished event */ | ||||
|         PopupTimer, | ||||
|         /** Button panel pressed event */ | ||||
|         ButtonPanelPressed, | ||||
|     }; | ||||
| 
 | ||||
|     union { | ||||
|         /** Menu selected event type payload. Selected index. */ | ||||
|         int32_t menu_index; | ||||
|         /** DialogEx view module event type payload */ | ||||
|         DialogExResult dialog_ex_result; | ||||
|     } payload; | ||||
| 
 | ||||
|     /** Type of event */ | ||||
|     Type type; | ||||
| }; | ||||
| @ -1,25 +1,26 @@ | ||||
| #include <file_worker_cpp.h> | ||||
| #include <flipper_format/flipper_format.h> | ||||
| #include "irda_app_remote_manager.h" | ||||
| #include "irda/helpers/irda_parser.h" | ||||
| #include "irda/irda_app_signal.h" | ||||
| #include "infrared_app_remote_manager.h" | ||||
| #include "infrared/helpers/infrared_parser.h" | ||||
| #include "infrared/infrared_app_signal.h" | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| #include <irda.h> | ||||
| #include <infrared.h> | ||||
| #include <cstdio> | ||||
| #include <furi.h> | ||||
| #include <gui/modules/button_menu.h> | ||||
| #include <storage/storage.h> | ||||
| #include "irda_app.h" | ||||
| #include "infrared_app.h" | ||||
| 
 | ||||
| static const std::string default_remote_name = "remote"; | ||||
| 
 | ||||
| std::string IrdaAppRemoteManager::make_full_name(const std::string& remote_name) const { | ||||
|     return std::string("") + IrdaApp::irda_directory + "/" + remote_name + IrdaApp::irda_extension; | ||||
| std::string InfraredAppRemoteManager::make_full_name(const std::string& remote_name) const { | ||||
|     return std::string("") + InfraredApp::infrared_directory + "/" + remote_name + | ||||
|            InfraredApp::infrared_extension; | ||||
| } | ||||
| 
 | ||||
| std::string IrdaAppRemoteManager::find_vacant_remote_name(const std::string& name) { | ||||
| std::string InfraredAppRemoteManager::find_vacant_remote_name(const std::string& name) { | ||||
|     bool exist = true; | ||||
|     FileWorkerCpp file_worker; | ||||
| 
 | ||||
| @ -41,14 +42,14 @@ std::string IrdaAppRemoteManager::find_vacant_remote_name(const std::string& nam | ||||
|     return !exist ? name + std::to_string(i) : std::string(); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaAppSignal& signal) { | ||||
| bool InfraredAppRemoteManager::add_button(const char* button_name, const InfraredAppSignal& signal) { | ||||
|     remote->buttons.emplace_back(button_name, signal); | ||||
|     return store(); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::add_remote_with_button( | ||||
| bool InfraredAppRemoteManager::add_remote_with_button( | ||||
|     const char* button_name, | ||||
|     const IrdaAppSignal& signal) { | ||||
|     const InfraredAppSignal& signal) { | ||||
|     furi_check(button_name != nullptr); | ||||
| 
 | ||||
|     auto new_name = find_vacant_remote_name(default_remote_name); | ||||
| @ -56,11 +57,11 @@ bool IrdaAppRemoteManager::add_remote_with_button( | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     remote = std::make_unique<IrdaAppRemote>(new_name); | ||||
|     remote = std::make_unique<InfraredAppRemote>(new_name); | ||||
|     return add_button(button_name, signal); | ||||
| } | ||||
| 
 | ||||
| std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const { | ||||
| std::vector<std::string> InfraredAppRemoteManager::get_button_list(void) const { | ||||
|     std::vector<std::string> name_vector; | ||||
|     name_vector.reserve(remote->buttons.size()); | ||||
| 
 | ||||
| @ -72,7 +73,7 @@ std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const { | ||||
|     return name_vector; | ||||
| } | ||||
| 
 | ||||
| const IrdaAppSignal& IrdaAppRemoteManager::get_button_data(size_t index) const { | ||||
| const InfraredAppSignal& InfraredAppRemoteManager::get_button_data(size_t index) const { | ||||
|     furi_check(remote.get() != nullptr); | ||||
|     auto& buttons = remote->buttons; | ||||
|     furi_check(index < buttons.size()); | ||||
| @ -80,7 +81,7 @@ const IrdaAppSignal& IrdaAppRemoteManager::get_button_data(size_t index) const { | ||||
|     return buttons.at(index).signal; | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::delete_remote() { | ||||
| bool InfraredAppRemoteManager::delete_remote() { | ||||
|     bool result; | ||||
|     FileWorkerCpp file_worker; | ||||
|     result = file_worker.remove(make_full_name(remote->name).c_str()); | ||||
| @ -89,11 +90,11 @@ bool IrdaAppRemoteManager::delete_remote() { | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppRemoteManager::reset_remote() { | ||||
| void InfraredAppRemoteManager::reset_remote() { | ||||
|     remote.reset(); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::delete_button(uint32_t index) { | ||||
| bool InfraredAppRemoteManager::delete_button(uint32_t index) { | ||||
|     furi_check(remote.get() != nullptr); | ||||
|     auto& buttons = remote->buttons; | ||||
|     furi_check(index < buttons.size()); | ||||
| @ -102,30 +103,18 @@ bool IrdaAppRemoteManager::delete_button(uint32_t index) { | ||||
|     return store(); | ||||
| } | ||||
| 
 | ||||
| std::string IrdaAppRemoteManager::get_button_name(uint32_t index) { | ||||
| std::string InfraredAppRemoteManager::get_button_name(uint32_t index) { | ||||
|     furi_check(remote.get() != nullptr); | ||||
|     auto& buttons = remote->buttons; | ||||
|     furi_check(index < buttons.size()); | ||||
|     return buttons[index].name.c_str(); | ||||
| } | ||||
| 
 | ||||
| std::string IrdaAppRemoteManager::get_remote_name() { | ||||
| std::string InfraredAppRemoteManager::get_remote_name() { | ||||
|     return remote.get() ? remote->name : std::string(); | ||||
| } | ||||
| 
 | ||||
| int IrdaAppRemoteManager::find_remote_name(const std::vector<std::string>& strings) { | ||||
|     furi_assert(remote.get() != nullptr); | ||||
|     int i = 0; | ||||
|     for(const auto& str : strings) { | ||||
|         if(!str.compare(remote->name)) { | ||||
|             return i; | ||||
|         } | ||||
|         ++i; | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::rename_remote(const char* str) { | ||||
| bool InfraredAppRemoteManager::rename_remote(const char* str) { | ||||
|     furi_check(str != nullptr); | ||||
|     furi_check(remote.get() != nullptr); | ||||
| 
 | ||||
| @ -148,7 +137,7 @@ bool IrdaAppRemoteManager::rename_remote(const char* str) { | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::rename_button(uint32_t index, const char* str) { | ||||
| bool InfraredAppRemoteManager::rename_button(uint32_t index, const char* str) { | ||||
|     furi_check(remote.get() != nullptr); | ||||
|     auto& buttons = remote->buttons; | ||||
|     furi_check(index < buttons.size()); | ||||
| @ -157,16 +146,16 @@ bool IrdaAppRemoteManager::rename_button(uint32_t index, const char* str) { | ||||
|     return store(); | ||||
| } | ||||
| 
 | ||||
| size_t IrdaAppRemoteManager::get_number_of_buttons() { | ||||
| size_t InfraredAppRemoteManager::get_number_of_buttons() { | ||||
|     furi_check(remote.get() != nullptr); | ||||
|     return remote->buttons.size(); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::store(void) { | ||||
| bool InfraredAppRemoteManager::store(void) { | ||||
|     bool result = false; | ||||
|     FileWorkerCpp file_worker; | ||||
| 
 | ||||
|     if(!file_worker.mkdir(IrdaApp::irda_directory)) return false; | ||||
|     if(!file_worker.mkdir(InfraredApp::infrared_directory)) return false; | ||||
| 
 | ||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); | ||||
|     FlipperFormat* ff = flipper_format_file_alloc(storage); | ||||
| @ -178,7 +167,7 @@ bool IrdaAppRemoteManager::store(void) { | ||||
|     } | ||||
|     if(result) { | ||||
|         for(const auto& button : remote->buttons) { | ||||
|             result = irda_parser_save_signal(ff, button.signal, button.name.c_str()); | ||||
|             result = infrared_parser_save_signal(ff, button.signal, button.name.c_str()); | ||||
|             if(!result) { | ||||
|                 break; | ||||
|             } | ||||
| @ -190,7 +179,7 @@ bool IrdaAppRemoteManager::store(void) { | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppRemoteManager::load(const std::string& remote_name) { | ||||
| bool InfraredAppRemoteManager::load(const std::string& remote_name) { | ||||
|     bool result = false; | ||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); | ||||
|     FlipperFormat* ff = flipper_format_file_alloc(storage); | ||||
| @ -208,10 +197,10 @@ bool IrdaAppRemoteManager::load(const std::string& remote_name) { | ||||
|         string_clear(header); | ||||
|     } | ||||
|     if(result) { | ||||
|         remote = std::make_unique<IrdaAppRemote>(remote_name); | ||||
|         IrdaAppSignal signal; | ||||
|         remote = std::make_unique<InfraredAppRemote>(remote_name); | ||||
|         InfraredAppSignal signal; | ||||
|         std::string signal_name; | ||||
|         while(irda_parser_read_signal(ff, signal, signal_name)) { | ||||
|         while(infrared_parser_read_signal(ff, signal, signal_name)) { | ||||
|             remote->buttons.emplace_back(signal_name.c_str(), std::move(signal)); | ||||
|         } | ||||
|     } | ||||
							
								
								
									
										188
									
								
								applications/infrared/infrared_app_remote_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								applications/infrared/infrared_app_remote_manager.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,188 @@ | ||||
| /**
 | ||||
|   * @file infrared_app_remote_manager.h | ||||
|   * Infrared: Remote manager class. | ||||
|   * It holds remote, can load/save/rename remote, | ||||
|   * add/remove/rename buttons. | ||||
|   */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "infrared_app_signal.h" | ||||
| 
 | ||||
| #include <infrared_worker.h> | ||||
| #include <infrared.h> | ||||
| 
 | ||||
| #include <cstdint> | ||||
| #include <string> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| 
 | ||||
| /** Class to handle remote button */ | ||||
| class InfraredAppRemoteButton { | ||||
|     /** Allow field access */ | ||||
|     friend class InfraredAppRemoteManager; | ||||
|     /** Name of signal */ | ||||
|     std::string name; | ||||
|     /** Signal data */ | ||||
|     InfraredAppSignal signal; | ||||
| 
 | ||||
| public: | ||||
|     /** Initialize remote button
 | ||||
|      * | ||||
|      * @param name - button name | ||||
|      * @param signal - signal to copy for remote button | ||||
|      */ | ||||
|     InfraredAppRemoteButton(const char* name, const InfraredAppSignal& signal) | ||||
|         : name(name) | ||||
|         , signal(signal) { | ||||
|     } | ||||
| 
 | ||||
|     /** Initialize remote button
 | ||||
|      * | ||||
|      * @param name - button name | ||||
|      * @param signal - signal to move for remote button | ||||
|      */ | ||||
|     InfraredAppRemoteButton(const char* name, InfraredAppSignal&& signal) | ||||
|         : name(name) | ||||
|         , signal(std::move(signal)) { | ||||
|     } | ||||
| 
 | ||||
|     /** Deinitialize remote button */ | ||||
|     ~InfraredAppRemoteButton() { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /** Class to handle remote */ | ||||
| class InfraredAppRemote { | ||||
|     /** Allow field access */ | ||||
|     friend class InfraredAppRemoteManager; | ||||
|     /** Button container */ | ||||
|     std::vector<InfraredAppRemoteButton> buttons; | ||||
|     /** Name of remote */ | ||||
|     std::string name; | ||||
| 
 | ||||
| public: | ||||
|     /** Initialize new remote
 | ||||
|      * | ||||
|      * @param name - new remote name | ||||
|      */ | ||||
|     InfraredAppRemote(const std::string& name) | ||||
|         : name(name) { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /** Class to handle remote manager */ | ||||
| class InfraredAppRemoteManager { | ||||
|     /** Remote instance. There can be 1 remote loaded at a time. */ | ||||
|     std::unique_ptr<InfraredAppRemote> remote; | ||||
|     /** Make full name from remote name
 | ||||
|      * | ||||
|      * @param remote_name name of remote | ||||
|      * @retval full name of remote on disk | ||||
|      */ | ||||
|     std::string make_full_name(const std::string& remote_name) const; | ||||
| 
 | ||||
| public: | ||||
|     /** Restriction to button name length. Buttons larger are ignored. */ | ||||
|     static constexpr const uint32_t max_button_name_length = 22; | ||||
| 
 | ||||
|     /** Restriction to remote name length. Remotes larger are ignored. */ | ||||
|     static constexpr const uint32_t max_remote_name_length = 22; | ||||
| 
 | ||||
|     /** Construct button from signal, and create remote
 | ||||
|      * | ||||
|      * @param button_name - name of button to create | ||||
|      * @param signal - signal to create button from | ||||
|      * @retval true for success, false otherwise | ||||
|      * */ | ||||
|     bool add_remote_with_button(const char* button_name, const InfraredAppSignal& signal); | ||||
| 
 | ||||
|     /** Add button to current remote
 | ||||
|      * | ||||
|      * @param button_name - name of button to create | ||||
|      * @param signal - signal to create button from | ||||
|      * @retval true for success, false otherwise | ||||
|      * */ | ||||
|     bool add_button(const char* button_name, const InfraredAppSignal& signal); | ||||
| 
 | ||||
|     /** Rename button in current remote
 | ||||
|      * | ||||
|      * @param index - index of button to rename | ||||
|      * @param str - new button name | ||||
|      */ | ||||
|     bool rename_button(uint32_t index, const char* str); | ||||
| 
 | ||||
|     /** Rename current remote
 | ||||
|      * | ||||
|      * @param str - new remote name | ||||
|      */ | ||||
|     bool rename_remote(const char* str); | ||||
| 
 | ||||
|     /** Find vacant remote name. If suggested name is occupied,
 | ||||
|      * incremented digit(2,3,4,etc) added to name and check repeated. | ||||
|      * | ||||
|      * @param name - suggested remote name | ||||
|      * @retval garanteed free remote name, prefixed with suggested | ||||
|      */ | ||||
|     std::string find_vacant_remote_name(const std::string& name); | ||||
| 
 | ||||
|     /** Get button list
 | ||||
|      * | ||||
|      * @retval container of button names | ||||
|      */ | ||||
|     std::vector<std::string> get_button_list() const; | ||||
| 
 | ||||
|     /** Get button name by index
 | ||||
|      * | ||||
|      * @param index - index of button to get name from | ||||
|      * @retval button name | ||||
|      */ | ||||
|     std::string get_button_name(uint32_t index); | ||||
| 
 | ||||
|     /** Get remote name
 | ||||
|      * | ||||
|      * @retval remote name | ||||
|      */ | ||||
|     std::string get_remote_name(); | ||||
| 
 | ||||
|     /** Get number of buttons
 | ||||
|      * | ||||
|      * @retval number of buttons | ||||
|      */ | ||||
|     size_t get_number_of_buttons(); | ||||
| 
 | ||||
|     /** Get button's signal
 | ||||
|      * | ||||
|      * @param index - index of interested button | ||||
|      * @retval signal | ||||
|      */ | ||||
|     const InfraredAppSignal& get_button_data(size_t index) const; | ||||
| 
 | ||||
|     /** Delete button
 | ||||
|      * | ||||
|      * @param index - index of interested button | ||||
|      * @retval true if success, false otherwise | ||||
|      */ | ||||
|     bool delete_button(uint32_t index); | ||||
| 
 | ||||
|     /** Delete remote
 | ||||
|      * | ||||
|      * @retval true if success, false otherwise | ||||
|      */ | ||||
|     bool delete_remote(); | ||||
| 
 | ||||
|     /** Clean all loaded info in current remote */ | ||||
|     void reset_remote(); | ||||
| 
 | ||||
|     /** Store current remote data on disk
 | ||||
|      * | ||||
|      * @retval true if success, false otherwise | ||||
|      */ | ||||
|     bool store(); | ||||
| 
 | ||||
|     /** Load data from disk into current remote
 | ||||
|      * | ||||
|      * @param name - name of remote to load | ||||
|      * @retval true if success, false otherwise | ||||
|      */ | ||||
|     bool load(const std::string& name); | ||||
| }; | ||||
| @ -1,7 +1,7 @@ | ||||
| #include "irda_app_signal.h" | ||||
| #include <irda_transmit.h> | ||||
| #include "infrared_app_signal.h" | ||||
| #include <infrared_transmit.h> | ||||
| 
 | ||||
| void IrdaAppSignal::copy_raw_signal( | ||||
| void InfraredAppSignal::copy_raw_signal( | ||||
|     const uint32_t* timings, | ||||
|     size_t size, | ||||
|     uint32_t frequency, | ||||
| @ -18,7 +18,7 @@ void IrdaAppSignal::copy_raw_signal( | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignal::clear_timings() { | ||||
| void InfraredAppSignal::clear_timings() { | ||||
|     if(raw_signal) { | ||||
|         delete[] payload.raw.timings; | ||||
|         payload.raw.timings_cnt = 0; | ||||
| @ -26,7 +26,7 @@ void IrdaAppSignal::clear_timings() { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignal::IrdaAppSignal( | ||||
| InfraredAppSignal::InfraredAppSignal( | ||||
|     const uint32_t* timings, | ||||
|     size_t timings_cnt, | ||||
|     uint32_t frequency, | ||||
| @ -35,12 +35,12 @@ IrdaAppSignal::IrdaAppSignal( | ||||
|     copy_raw_signal(timings, timings_cnt, frequency, duty_cycle); | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignal::IrdaAppSignal(const IrdaMessage* irda_message) { | ||||
| InfraredAppSignal::InfraredAppSignal(const InfraredMessage* infrared_message) { | ||||
|     raw_signal = false; | ||||
|     payload.message = *irda_message; | ||||
|     payload.message = *infrared_message; | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignal& IrdaAppSignal::operator=(const IrdaAppSignal& other) { | ||||
| InfraredAppSignal& InfraredAppSignal::operator=(const InfraredAppSignal& other) { | ||||
|     clear_timings(); | ||||
|     raw_signal = other.raw_signal; | ||||
|     if(!raw_signal) { | ||||
| @ -56,7 +56,7 @@ IrdaAppSignal& IrdaAppSignal::operator=(const IrdaAppSignal& other) { | ||||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignal::IrdaAppSignal(const IrdaAppSignal& other) { | ||||
| InfraredAppSignal::InfraredAppSignal(const InfraredAppSignal& other) { | ||||
|     raw_signal = other.raw_signal; | ||||
|     if(!raw_signal) { | ||||
|         payload.message = other.payload.message; | ||||
| @ -69,7 +69,7 @@ IrdaAppSignal::IrdaAppSignal(const IrdaAppSignal& other) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| IrdaAppSignal::IrdaAppSignal(IrdaAppSignal&& other) { | ||||
| InfraredAppSignal::InfraredAppSignal(InfraredAppSignal&& other) { | ||||
|     raw_signal = other.raw_signal; | ||||
|     if(!raw_signal) { | ||||
|         payload.message = other.payload.message; | ||||
| @ -86,13 +86,13 @@ IrdaAppSignal::IrdaAppSignal(IrdaAppSignal&& other) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignal::set_message(const IrdaMessage* irda_message) { | ||||
| void InfraredAppSignal::set_message(const InfraredMessage* infrared_message) { | ||||
|     clear_timings(); | ||||
|     raw_signal = false; | ||||
|     payload.message = *irda_message; | ||||
|     payload.message = *infrared_message; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignal::set_raw_signal( | ||||
| void InfraredAppSignal::set_raw_signal( | ||||
|     uint32_t* timings, | ||||
|     size_t timings_cnt, | ||||
|     uint32_t frequency, | ||||
| @ -102,11 +102,11 @@ void IrdaAppSignal::set_raw_signal( | ||||
|     copy_raw_signal(timings, timings_cnt, frequency, duty_cycle); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSignal::transmit() const { | ||||
| void InfraredAppSignal::transmit() const { | ||||
|     if(!raw_signal) { | ||||
|         irda_send(&payload.message, 1); | ||||
|         infrared_send(&payload.message, 1); | ||||
|     } else { | ||||
|         irda_send_raw_ext( | ||||
|         infrared_send_raw_ext( | ||||
|             payload.raw.timings, | ||||
|             payload.raw.timings_cnt, | ||||
|             true, | ||||
							
								
								
									
										134
									
								
								applications/infrared/infrared_app_signal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								applications/infrared/infrared_app_signal.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | ||||
| /**
 | ||||
|   * @file infrared_app_signal.h | ||||
|   * Infrared: Signal class | ||||
|   */ | ||||
| #pragma once | ||||
| #include <infrared_worker.h> | ||||
| #include <stdint.h> | ||||
| #include <string> | ||||
| #include <infrared.h> | ||||
| 
 | ||||
| /** Infrared application signal class */ | ||||
| class InfraredAppSignal { | ||||
| public: | ||||
|     /** Raw signal structure */ | ||||
|     typedef struct { | ||||
|         /** Timings amount */ | ||||
|         size_t timings_cnt; | ||||
|         /** Samples of raw signal in ms */ | ||||
|         uint32_t* timings; | ||||
|         /** PWM Frequency of raw signal */ | ||||
|         uint32_t frequency; | ||||
|         /** PWM Duty cycle of raw signal */ | ||||
|         float duty_cycle; | ||||
|     } RawSignal; | ||||
| 
 | ||||
| private: | ||||
|     /** if true - signal is raw, if false - signal is parsed */ | ||||
|     bool raw_signal; | ||||
|     /** signal data, either raw or parsed */ | ||||
|     union { | ||||
|         /** signal data for parsed signal */ | ||||
|         InfraredMessage message; | ||||
|         /** raw signal data */ | ||||
|         RawSignal raw; | ||||
|     } payload; | ||||
| 
 | ||||
|     /** Copy raw signal into object
 | ||||
|      * | ||||
|      * @param timings - timings (samples) of raw signal | ||||
|      * @param size - number of timings | ||||
|      * @frequency - PWM frequency of raw signal | ||||
|      * @duty_cycle - PWM duty cycle | ||||
|      */ | ||||
|     void | ||||
|         copy_raw_signal(const uint32_t* timings, size_t size, uint32_t frequency, float duty_cycle); | ||||
|     /** Clear and free timings data */ | ||||
|     void clear_timings(); | ||||
| 
 | ||||
| public: | ||||
|     /** Construct Infrared signal class */ | ||||
|     InfraredAppSignal() { | ||||
|         raw_signal = false; | ||||
|         payload.message.protocol = InfraredProtocolUnknown; | ||||
|     } | ||||
| 
 | ||||
|     /** Destruct signal class and free all allocated data */ | ||||
|     ~InfraredAppSignal() { | ||||
|         clear_timings(); | ||||
|     } | ||||
| 
 | ||||
|     /** Construct object with raw signal
 | ||||
|      * | ||||
|      * @param timings - timings (samples) of raw signal | ||||
|      * @param size - number of timings | ||||
|      * @frequency - PWM frequency of raw signal | ||||
|      * @duty_cycle - PWM duty cycle | ||||
|      */ | ||||
|     InfraredAppSignal( | ||||
|         const uint32_t* timings, | ||||
|         size_t timings_cnt, | ||||
|         uint32_t frequency, | ||||
|         float duty_cycle); | ||||
| 
 | ||||
|     /** Construct object with parsed signal
 | ||||
|      * | ||||
|      * @param infrared_message - parsed_signal to construct from | ||||
|      */ | ||||
|     InfraredAppSignal(const InfraredMessage* infrared_message); | ||||
| 
 | ||||
|     /** Copy constructor */ | ||||
|     InfraredAppSignal(const InfraredAppSignal& other); | ||||
|     /** Move constructor */ | ||||
|     InfraredAppSignal(InfraredAppSignal&& other); | ||||
| 
 | ||||
|     /** Assignment operator */ | ||||
|     InfraredAppSignal& operator=(const InfraredAppSignal& signal); | ||||
| 
 | ||||
|     /** Set object to parsed signal
 | ||||
|      * | ||||
|      * @param infrared_message - parsed_signal to construct from | ||||
|      */ | ||||
|     void set_message(const InfraredMessage* infrared_message); | ||||
| 
 | ||||
|     /** Set object to raw signal
 | ||||
|      * | ||||
|      * @param timings - timings (samples) of raw signal | ||||
|      * @param size - number of timings | ||||
|      * @frequency - PWM frequency of raw signal | ||||
|      * @duty_cycle - PWM duty cycle | ||||
|      */ | ||||
|     void | ||||
|         set_raw_signal(uint32_t* timings, size_t timings_cnt, uint32_t frequency, float duty_cycle); | ||||
| 
 | ||||
|     /** Transmit held signal (???) */ | ||||
|     void transmit() const; | ||||
| 
 | ||||
|     /** Show is held signal raw
 | ||||
|      * | ||||
|      * @retval true if signal is raw, false if signal is parsed | ||||
|      */ | ||||
|     bool is_raw(void) const { | ||||
|         return raw_signal; | ||||
|     } | ||||
| 
 | ||||
|     /** Get parsed signal.
 | ||||
|      * User must check is_raw() signal before calling this function. | ||||
|      * | ||||
|      * @retval parsed signal pointer | ||||
|      */ | ||||
|     const InfraredMessage& get_message(void) const { | ||||
|         furi_assert(!raw_signal); | ||||
|         return payload.message; | ||||
|     } | ||||
| 
 | ||||
|     /** Get raw signal.
 | ||||
|      * User must check is_raw() signal before calling this function. | ||||
|      * | ||||
|      * @retval raw signal | ||||
|      */ | ||||
|     const RawSignal& get_raw_signal(void) const { | ||||
|         furi_assert(raw_signal); | ||||
|         return payload.raw; | ||||
|     } | ||||
| }; | ||||
| @ -6,16 +6,16 @@ | ||||
| #include <furi.h> | ||||
| #include <callback-connector.h> | ||||
| 
 | ||||
| #include "irda/irda_app_view_manager.h" | ||||
| #include "irda/view/irda_progress_view.h" | ||||
| #include "irda_app.h" | ||||
| #include "irda/irda_app_event.h" | ||||
| #include "infrared/infrared_app_view_manager.h" | ||||
| #include "infrared/view/infrared_progress_view.h" | ||||
| #include "infrared_app.h" | ||||
| #include "infrared/infrared_app_event.h" | ||||
| 
 | ||||
| IrdaAppViewManager::IrdaAppViewManager() { | ||||
|     event_queue = osMessageQueueNew(10, sizeof(IrdaAppEvent), NULL); | ||||
| InfraredAppViewManager::InfraredAppViewManager() { | ||||
|     event_queue = osMessageQueueNew(10, sizeof(InfraredAppEvent), NULL); | ||||
| 
 | ||||
|     view_dispatcher = view_dispatcher_alloc(); | ||||
|     auto callback = cbc::obtain_connector(this, &IrdaAppViewManager::previous_view_callback); | ||||
|     auto callback = cbc::obtain_connector(this, &InfraredAppViewManager::previous_view_callback); | ||||
| 
 | ||||
|     gui = static_cast<Gui*>(furi_record_open("gui")); | ||||
|     view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); | ||||
| @ -26,18 +26,18 @@ IrdaAppViewManager::IrdaAppViewManager() { | ||||
|     dialog_ex = dialog_ex_alloc(); | ||||
|     text_input = text_input_alloc(); | ||||
|     button_panel = button_panel_alloc(); | ||||
|     progress_view = irda_progress_view_alloc(); | ||||
|     progress_view = infrared_progress_view_alloc(); | ||||
|     loading_view = loading_alloc(); | ||||
|     universal_view_stack = view_stack_alloc(); | ||||
|     view_stack_add_view(universal_view_stack, button_panel_get_view(button_panel)); | ||||
|     view_set_orientation(view_stack_get_view(universal_view_stack), ViewOrientationVertical); | ||||
| 
 | ||||
|     add_view(ViewType::UniversalRemote, view_stack_get_view(universal_view_stack)); | ||||
|     add_view(ViewType::ButtonMenu, button_menu_get_view(button_menu)); | ||||
|     add_view(ViewType::Submenu, submenu_get_view(submenu)); | ||||
|     add_view(ViewType::Popup, popup_get_view(popup)); | ||||
|     add_view(ViewType::DialogEx, dialog_ex_get_view(dialog_ex)); | ||||
|     add_view(ViewType::TextInput, text_input_get_view(text_input)); | ||||
|     add_view(ViewId::UniversalRemote, view_stack_get_view(universal_view_stack)); | ||||
|     add_view(ViewId::ButtonMenu, button_menu_get_view(button_menu)); | ||||
|     add_view(ViewId::Submenu, submenu_get_view(submenu)); | ||||
|     add_view(ViewId::Popup, popup_get_view(popup)); | ||||
|     add_view(ViewId::DialogEx, dialog_ex_get_view(dialog_ex)); | ||||
|     add_view(ViewId::TextInput, text_input_get_view(text_input)); | ||||
| 
 | ||||
|     view_set_previous_callback(view_stack_get_view(universal_view_stack), callback); | ||||
|     view_set_previous_callback(button_menu_get_view(button_menu), callback); | ||||
| @ -47,19 +47,19 @@ IrdaAppViewManager::IrdaAppViewManager() { | ||||
|     view_set_previous_callback(text_input_get_view(text_input), callback); | ||||
| } | ||||
| 
 | ||||
| IrdaAppViewManager::~IrdaAppViewManager() { | ||||
| InfraredAppViewManager::~InfraredAppViewManager() { | ||||
|     view_dispatcher_remove_view( | ||||
|         view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::UniversalRemote)); | ||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::UniversalRemote)); | ||||
|     view_dispatcher_remove_view( | ||||
|         view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::ButtonMenu)); | ||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::ButtonMenu)); | ||||
|     view_dispatcher_remove_view( | ||||
|         view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::TextInput)); | ||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::TextInput)); | ||||
|     view_dispatcher_remove_view( | ||||
|         view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::DialogEx)); | ||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::DialogEx)); | ||||
|     view_dispatcher_remove_view( | ||||
|         view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::Submenu)); | ||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::Submenu)); | ||||
|     view_dispatcher_remove_view( | ||||
|         view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::Popup)); | ||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::Popup)); | ||||
| 
 | ||||
|     view_stack_remove_view(universal_view_stack, button_panel_get_view(button_panel)); | ||||
|     view_stack_free(universal_view_stack); | ||||
| @ -69,7 +69,7 @@ IrdaAppViewManager::~IrdaAppViewManager() { | ||||
|     button_menu_free(button_menu); | ||||
|     dialog_ex_free(dialog_ex); | ||||
|     text_input_free(text_input); | ||||
|     irda_progress_view_free(progress_view); | ||||
|     infrared_progress_view_free(progress_view); | ||||
|     loading_free(loading_view); | ||||
| 
 | ||||
|     view_dispatcher_free(view_dispatcher); | ||||
| @ -77,70 +77,70 @@ IrdaAppViewManager::~IrdaAppViewManager() { | ||||
|     osMessageQueueDelete(event_queue); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppViewManager::switch_to(ViewType type) { | ||||
| void InfraredAppViewManager::switch_to(ViewId type) { | ||||
|     view_dispatcher_switch_to_view(view_dispatcher, static_cast<uint32_t>(type)); | ||||
| } | ||||
| 
 | ||||
| TextInput* IrdaAppViewManager::get_text_input() { | ||||
| TextInput* InfraredAppViewManager::get_text_input() { | ||||
|     return text_input; | ||||
| } | ||||
| 
 | ||||
| DialogEx* IrdaAppViewManager::get_dialog_ex() { | ||||
| DialogEx* InfraredAppViewManager::get_dialog_ex() { | ||||
|     return dialog_ex; | ||||
| } | ||||
| 
 | ||||
| Submenu* IrdaAppViewManager::get_submenu() { | ||||
| Submenu* InfraredAppViewManager::get_submenu() { | ||||
|     return submenu; | ||||
| } | ||||
| 
 | ||||
| Popup* IrdaAppViewManager::get_popup() { | ||||
| Popup* InfraredAppViewManager::get_popup() { | ||||
|     return popup; | ||||
| } | ||||
| 
 | ||||
| ButtonMenu* IrdaAppViewManager::get_button_menu() { | ||||
| ButtonMenu* InfraredAppViewManager::get_button_menu() { | ||||
|     return button_menu; | ||||
| } | ||||
| 
 | ||||
| ButtonPanel* IrdaAppViewManager::get_button_panel() { | ||||
| ButtonPanel* InfraredAppViewManager::get_button_panel() { | ||||
|     return button_panel; | ||||
| } | ||||
| 
 | ||||
| IrdaProgressView* IrdaAppViewManager::get_progress() { | ||||
| InfraredProgressView* InfraredAppViewManager::get_progress() { | ||||
|     return progress_view; | ||||
| } | ||||
| 
 | ||||
| Loading* IrdaAppViewManager::get_loading() { | ||||
| Loading* InfraredAppViewManager::get_loading() { | ||||
|     return loading_view; | ||||
| } | ||||
| 
 | ||||
| ViewStack* IrdaAppViewManager::get_universal_view_stack() { | ||||
| ViewStack* InfraredAppViewManager::get_universal_view_stack() { | ||||
|     return universal_view_stack; | ||||
| } | ||||
| 
 | ||||
| osMessageQueueId_t IrdaAppViewManager::get_event_queue() { | ||||
| osMessageQueueId_t InfraredAppViewManager::get_event_queue() { | ||||
|     return event_queue; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppViewManager::clear_events() { | ||||
|     IrdaAppEvent event; | ||||
| void InfraredAppViewManager::clear_events() { | ||||
|     InfraredAppEvent event; | ||||
|     while(osMessageQueueGet(event_queue, &event, NULL, 0) == osOK) | ||||
|         ; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppViewManager::receive_event(IrdaAppEvent* event) { | ||||
| void InfraredAppViewManager::receive_event(InfraredAppEvent* event) { | ||||
|     if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) { | ||||
|         event->type = IrdaAppEvent::Type::Tick; | ||||
|         event->type = InfraredAppEvent::Type::Tick; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void IrdaAppViewManager::send_event(IrdaAppEvent* event) { | ||||
| void InfraredAppViewManager::send_event(InfraredAppEvent* event) { | ||||
|     uint32_t timeout = 0; | ||||
|     /* Rapid button hammering on signal send scenes causes queue overflow - ignore it,
 | ||||
|      * but try to keep button release event - it switches off IRDA DMA sending. */ | ||||
|     if(event->type == IrdaAppEvent::Type::MenuSelectedRelease) { | ||||
|      * but try to keep button release event - it switches off INFRARED DMA sending. */ | ||||
|     if(event->type == InfraredAppEvent::Type::MenuSelectedRelease) { | ||||
|         timeout = 200; | ||||
|     } | ||||
|     if((event->type == IrdaAppEvent::Type::DialogExSelected) && | ||||
|     if((event->type == InfraredAppEvent::Type::DialogExSelected) && | ||||
|        (event->payload.dialog_ex_result == DialogExReleaseCenter)) { | ||||
|         timeout = 200; | ||||
|     } | ||||
| @ -148,16 +148,16 @@ void IrdaAppViewManager::send_event(IrdaAppEvent* event) { | ||||
|     osMessageQueuePut(event_queue, event, 0, timeout); | ||||
| } | ||||
| 
 | ||||
| uint32_t IrdaAppViewManager::previous_view_callback(void* context) { | ||||
| uint32_t InfraredAppViewManager::previous_view_callback(void* context) { | ||||
|     if(event_queue != NULL) { | ||||
|         IrdaAppEvent event; | ||||
|         event.type = IrdaAppEvent::Type::Back; | ||||
|         InfraredAppEvent event; | ||||
|         event.type = InfraredAppEvent::Type::Back; | ||||
|         send_event(&event); | ||||
|     } | ||||
| 
 | ||||
|     return VIEW_IGNORE; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppViewManager::add_view(ViewType view_type, View* view) { | ||||
| void InfraredAppViewManager::add_view(ViewId view_type, View* view) { | ||||
|     view_dispatcher_add_view(view_dispatcher, static_cast<uint32_t>(view_type), view); | ||||
| } | ||||
							
								
								
									
										164
									
								
								applications/infrared/infrared_app_view_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								applications/infrared/infrared_app_view_manager.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,164 @@ | ||||
| /**
 | ||||
|   * @file infrared_app_view_manager.h | ||||
|   * Infrared: Scene events description | ||||
|   */ | ||||
| #pragma once | ||||
| #include <gui/modules/button_menu.h> | ||||
| #include <gui/modules/text_input.h> | ||||
| #include <gui/view_stack.h> | ||||
| #include <gui/modules/button_panel.h> | ||||
| #include <furi.h> | ||||
| #include <gui/view_dispatcher.h> | ||||
| #include <gui/modules/dialog_ex.h> | ||||
| #include <gui/modules/submenu.h> | ||||
| #include <gui/modules/popup.h> | ||||
| #include <gui/modules/loading.h> | ||||
| 
 | ||||
| #include "infrared_app_event.h" | ||||
| #include "view/infrared_progress_view.h" | ||||
| 
 | ||||
| /** Infrared View manager class */ | ||||
| class InfraredAppViewManager { | ||||
| public: | ||||
|     /** Infrared View Id enum, it is used
 | ||||
|      * to identify added views */ | ||||
|     enum class ViewId : uint8_t { | ||||
|         DialogEx, | ||||
|         TextInput, | ||||
|         Submenu, | ||||
|         ButtonMenu, | ||||
|         UniversalRemote, | ||||
|         Popup, | ||||
|     }; | ||||
| 
 | ||||
|     /** Class constructor */ | ||||
|     InfraredAppViewManager(); | ||||
|     /** Class destructor */ | ||||
|     ~InfraredAppViewManager(); | ||||
| 
 | ||||
|     /** Switch to another view
 | ||||
|      * | ||||
|      * @param id - view id to switch to | ||||
|      */ | ||||
|     void switch_to(ViewId id); | ||||
| 
 | ||||
|     /** Receive event from queue
 | ||||
|      * | ||||
|      * @param event - received event | ||||
|      */ | ||||
|     void receive_event(InfraredAppEvent* event); | ||||
| 
 | ||||
|     /** Send event to queue
 | ||||
|      * | ||||
|      * @param event - event to send | ||||
|      */ | ||||
|     void send_event(InfraredAppEvent* event); | ||||
| 
 | ||||
|     /** Clear events that already in queue
 | ||||
|      * | ||||
|      * @param event - event to send | ||||
|      */ | ||||
|     void clear_events(); | ||||
| 
 | ||||
|     /** Get dialog_ex view module
 | ||||
|      * | ||||
|      * @retval dialog_ex view module | ||||
|      */ | ||||
|     DialogEx* get_dialog_ex(); | ||||
| 
 | ||||
|     /** Get submenu view module
 | ||||
|      * | ||||
|      * @retval submenu view module | ||||
|      */ | ||||
|     Submenu* get_submenu(); | ||||
| 
 | ||||
|     /** Get popup view module
 | ||||
|      * | ||||
|      * @retval popup view module | ||||
|      */ | ||||
|     Popup* get_popup(); | ||||
| 
 | ||||
|     /** Get text_input view module
 | ||||
|      * | ||||
|      * @retval text_input view module | ||||
|      */ | ||||
|     TextInput* get_text_input(); | ||||
| 
 | ||||
|     /** Get button_menu view module
 | ||||
|      * | ||||
|      * @retval button_menu view module | ||||
|      */ | ||||
|     ButtonMenu* get_button_menu(); | ||||
| 
 | ||||
|     /** Get button_panel view module
 | ||||
|      * | ||||
|      * @retval button_panel view module | ||||
|      */ | ||||
|     ButtonPanel* get_button_panel(); | ||||
| 
 | ||||
|     /** Get view_stack view module used in universal remote
 | ||||
|      * | ||||
|      * @retval view_stack view module | ||||
|      */ | ||||
|     ViewStack* get_universal_view_stack(); | ||||
| 
 | ||||
|     /** Get progress view module
 | ||||
|      * | ||||
|      * @retval progress view module | ||||
|      */ | ||||
|     InfraredProgressView* get_progress(); | ||||
| 
 | ||||
|     /** Get loading view module
 | ||||
|      * | ||||
|      * @retval loading view module | ||||
|      */ | ||||
|     Loading* get_loading(); | ||||
| 
 | ||||
|     /** Get event queue
 | ||||
|      * | ||||
|      * @retval event queue | ||||
|      */ | ||||
|     osMessageQueueId_t get_event_queue(); | ||||
| 
 | ||||
|     /** Callback to handle back button
 | ||||
|      * | ||||
|      * @param context - context to pass to callback | ||||
|      * @retval always returns VIEW_IGNORE | ||||
|      */ | ||||
|     uint32_t previous_view_callback(void* context); | ||||
| 
 | ||||
| private: | ||||
|     /** View Dispatcher instance.
 | ||||
|      * It handles view switching */ | ||||
|     ViewDispatcher* view_dispatcher; | ||||
|     /** Gui instance */ | ||||
|     Gui* gui; | ||||
|     /** Text input view module instance */ | ||||
|     TextInput* text_input; | ||||
|     /** DialogEx view module instance */ | ||||
|     DialogEx* dialog_ex; | ||||
|     /** Submenu view module instance */ | ||||
|     Submenu* submenu; | ||||
|     /** Popup view module instance */ | ||||
|     Popup* popup; | ||||
|     /** ButtonMenu view module instance */ | ||||
|     ButtonMenu* button_menu; | ||||
|     /** ButtonPanel view module instance */ | ||||
|     ButtonPanel* button_panel; | ||||
|     /** ViewStack view module instance */ | ||||
|     ViewStack* universal_view_stack; | ||||
|     /** ProgressView view module instance */ | ||||
|     InfraredProgressView* progress_view; | ||||
|     /** Loading view module instance */ | ||||
|     Loading* loading_view; | ||||
| 
 | ||||
|     /** Queue to handle events, which are processed in scenes */ | ||||
|     osMessageQueueId_t event_queue; | ||||
| 
 | ||||
|     /** Add View to pull of views
 | ||||
|      * | ||||
|      * @param view_id - id to identify view | ||||
|      * @param view - view to add | ||||
|      */ | ||||
|     void add_view(ViewId view_id, View* view); | ||||
| }; | ||||
							
								
								
									
										9
									
								
								applications/infrared/infrared_runner.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								applications/infrared/infrared_runner.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| #include "infrared_app.h" | ||||
| 
 | ||||
| extern "C" int32_t infrared_app(void* p) { | ||||
|     InfraredApp* app = new InfraredApp(); | ||||
|     int32_t result = app->run(p); | ||||
|     delete app; | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
							
								
								
									
										305
									
								
								applications/infrared/scene/infrared_app_scene.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								applications/infrared/scene/infrared_app_scene.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,305 @@ | ||||
| /**
 | ||||
|  * @file infrared_app_scene.h | ||||
|  * Infrared: Application scenes | ||||
|  */ | ||||
| #pragma once | ||||
| #include "../infrared_app_event.h" | ||||
| #include <furi_hal_infrared.h> | ||||
| #include "infrared.h" | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include "../infrared_app_brute_force.h" | ||||
| 
 | ||||
| /** Anonymous class */ | ||||
| class InfraredApp; | ||||
| 
 | ||||
| /** Base Scene class */ | ||||
| class InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     virtual void on_enter(InfraredApp* app) = 0; | ||||
|     /** Events handler callback */ | ||||
|     virtual bool on_event(InfraredApp* app, InfraredAppEvent* event) = 0; | ||||
|     /** Called when exit scene */ | ||||
|     virtual void on_exit(InfraredApp* app) = 0; | ||||
|     /** Virtual destructor of base class */ | ||||
|     virtual ~InfraredAppScene(){}; | ||||
| 
 | ||||
| private: | ||||
| }; | ||||
| 
 | ||||
| /** Start scene
 | ||||
|  * Main Infrared application menu | ||||
|  */ | ||||
| class InfraredAppSceneStart : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     /** Save previously selected submenu index
 | ||||
|      * to highlight it when get back */ | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| 
 | ||||
| /** Universal menu scene
 | ||||
|  * Scene to select universal remote | ||||
|  */ | ||||
| class InfraredAppSceneUniversal : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     /** Save previously selected submenu index
 | ||||
|      * to highlight it when get back */ | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| 
 | ||||
| /** Learn new signal scene
 | ||||
|  * On this scene catching new IR signal performed. | ||||
|  */ | ||||
| class InfraredAppSceneLearn : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| /** New signal learn succeeded scene
 | ||||
|  */ | ||||
| class InfraredAppSceneLearnSuccess : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
|     bool button_pressed = false; | ||||
| }; | ||||
| 
 | ||||
| /** Scene to enter name for new button in remote
 | ||||
|  */ | ||||
| class InfraredAppSceneLearnEnterName : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| /** Scene where signal is learnt
 | ||||
|  */ | ||||
| class InfraredAppSceneLearnDone : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| /** Remote interface scene
 | ||||
|  * On this scene you can send IR signals from selected remote | ||||
|  */ | ||||
| class InfraredAppSceneRemote : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     /** container of button names in current remote. */ | ||||
|     std::vector<std::string> buttons_names; | ||||
|     /** Save previously selected index
 | ||||
|      * to highlight it when get back */ | ||||
|     uint32_t buttonmenu_item_selected = 0; | ||||
|     /** state flag to show button is pressed.
 | ||||
|      * As long as send-signal button pressed no other button | ||||
|      * events are handled. */ | ||||
|     bool button_pressed = false; | ||||
| }; | ||||
| 
 | ||||
| /** List of remotes scene
 | ||||
|  * Every remote is a file, located on internal/external storage. | ||||
|  * Every file has same format, and same extension. | ||||
|  * Files are parsed as you enter 'Remote scene' and showed | ||||
|  * as a buttons. | ||||
|  */ | ||||
| class InfraredAppSceneRemoteList : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     /** Save previously selected index
 | ||||
|      * to highlight it when get back */ | ||||
|     uint32_t submenu_item_selected = 0; | ||||
|     /** Remote names to show them in submenu */ | ||||
|     std::vector<std::string> remote_names; | ||||
| }; | ||||
| 
 | ||||
| class InfraredAppSceneAskBack : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class InfraredAppSceneEdit : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     /** Save previously selected index
 | ||||
|      * to highlight it when get back */ | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| 
 | ||||
| class InfraredAppSceneEditKeySelect : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     /** Button names to show them in submenu */ | ||||
|     std::vector<std::string> buttons_names; | ||||
| }; | ||||
| 
 | ||||
| class InfraredAppSceneEditRename : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class InfraredAppSceneEditDelete : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class InfraredAppSceneEditRenameDone : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class InfraredAppSceneEditDeleteDone : public InfraredAppScene { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class InfraredAppSceneUniversalCommon : public InfraredAppScene { | ||||
|     /** Brute force started flag */ | ||||
|     bool brute_force_started = false; | ||||
| 
 | ||||
| protected: | ||||
|     /** Events handler callback */ | ||||
|     bool on_event(InfraredApp* app, InfraredAppEvent* event) final; | ||||
|     /** Called when exit scene */ | ||||
|     void on_exit(InfraredApp* app) final; | ||||
| 
 | ||||
|     /** Show popup window
 | ||||
|      * | ||||
|      * @param app - application instance | ||||
|      */ | ||||
|     void show_popup(InfraredApp* app, int record_amount); | ||||
| 
 | ||||
|     /** Hide popup window
 | ||||
|      * | ||||
|      * @param app - application instance | ||||
|      */ | ||||
|     void hide_popup(InfraredApp* app); | ||||
| 
 | ||||
|     /** Propagate progress in popup window
 | ||||
|      * | ||||
|      * @param app - application instance | ||||
|      */ | ||||
|     bool progress_popup(InfraredApp* app); | ||||
| 
 | ||||
|     /** Item selected callback
 | ||||
|      * | ||||
|      * @param context - context | ||||
|      * @param index - selected item index | ||||
|      */ | ||||
|     static void infrared_app_item_callback(void* context, uint32_t index); | ||||
| 
 | ||||
|     /** Brute Force instance */ | ||||
|     InfraredAppBruteForce brute_force; | ||||
| 
 | ||||
|     /** Constructor */ | ||||
|     InfraredAppSceneUniversalCommon(const char* filename) | ||||
|         : brute_force(filename) { | ||||
|     } | ||||
| 
 | ||||
|     /** Destructor */ | ||||
|     ~InfraredAppSceneUniversalCommon() { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class InfraredAppSceneUniversalTV : public InfraredAppSceneUniversalCommon { | ||||
| public: | ||||
|     /** Called when enter scene */ | ||||
|     void on_enter(InfraredApp* app) final; | ||||
| 
 | ||||
|     /** Constructor
 | ||||
|      * Specifies path to brute force db library */ | ||||
|     InfraredAppSceneUniversalTV() | ||||
|         : InfraredAppSceneUniversalCommon("/ext/infrared/assets/tv.ir") { | ||||
|     } | ||||
| 
 | ||||
|     /** Destructor */ | ||||
|     ~InfraredAppSceneUniversalTV() { | ||||
|     } | ||||
| }; | ||||
| @ -1,21 +1,21 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "../infrared_app.h" | ||||
| #include "gui/modules/dialog_ex.h" | ||||
| #include "irda.h" | ||||
| #include "irda/scene/irda_app_scene.h" | ||||
| #include "infrared.h" | ||||
| #include "infrared/scene/infrared_app_scene.h" | ||||
| #include <string> | ||||
| 
 | ||||
| static void dialog_result_callback(DialogExResult result, void* context) { | ||||
|     auto app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
|     auto app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
| 
 | ||||
|     event.type = IrdaAppEvent::Type::DialogExSelected; | ||||
|     event.type = InfraredAppEvent::Type::DialogExSelected; | ||||
|     event.payload.dialog_ex_result = result; | ||||
| 
 | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneAskBack::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneAskBack::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||
| 
 | ||||
|     if(app->get_learn_new_remote()) { | ||||
| @ -33,21 +33,21 @@ void IrdaAppSceneAskBack::on_enter(IrdaApp* app) { | ||||
|     dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); | ||||
|     dialog_ex_set_context(dialog_ex, app); | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::DialogEx); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneAskBack::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneAskBack::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::DialogExSelected) { | ||||
|     if(event->type == InfraredAppEvent::Type::DialogExSelected) { | ||||
|         switch(event->payload.dialog_ex_result) { | ||||
|         case DialogExResultLeft: | ||||
|             consumed = true; | ||||
|             if(app->get_learn_new_remote()) { | ||||
|                 app->search_and_switch_to_previous_scene({IrdaApp::Scene::Start}); | ||||
|                 app->search_and_switch_to_previous_scene({InfraredApp::Scene::Start}); | ||||
|             } else { | ||||
|                 app->search_and_switch_to_previous_scene( | ||||
|                     {IrdaApp::Scene::Edit, IrdaApp::Scene::Remote}); | ||||
|                     {InfraredApp::Scene::Edit, InfraredApp::Scene::Remote}); | ||||
|             } | ||||
|             break; | ||||
|         case DialogExResultCenter: | ||||
| @ -62,12 +62,12 @@ bool IrdaAppSceneAskBack::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::Back) { | ||||
|     if(event->type == InfraredAppEvent::Type::Back) { | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneAskBack::on_exit(IrdaApp* app) { | ||||
| void InfraredAppSceneAskBack::on_exit(InfraredApp* app) { | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "../infrared_app.h" | ||||
| #include "gui/modules/submenu.h" | ||||
| 
 | ||||
| typedef enum { | ||||
| @ -10,17 +10,17 @@ typedef enum { | ||||
| } SubmenuIndex; | ||||
| 
 | ||||
| static void submenu_callback(void* context, uint32_t index) { | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
| 
 | ||||
|     event.type = IrdaAppEvent::Type::MenuSelected; | ||||
|     event.type = InfraredAppEvent::Type::MenuSelected; | ||||
|     event.payload.menu_index = index; | ||||
| 
 | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneEdit::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneEdit::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
| 
 | ||||
|     submenu_add_item(submenu, "Add key", SubmenuIndexAddKey, submenu_callback, app); | ||||
| @ -31,38 +31,38 @@ void IrdaAppSceneEdit::on_enter(IrdaApp* app) { | ||||
|     submenu_set_selected_item(submenu, submenu_item_selected); | ||||
|     submenu_item_selected = 0; | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneEdit::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneEdit::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||
|     if(event->type == InfraredAppEvent::Type::MenuSelected) { | ||||
|         submenu_item_selected = event->payload.menu_index; | ||||
|         switch(event->payload.menu_index) { | ||||
|         case SubmenuIndexAddKey: | ||||
|             app->set_learn_new_remote(false); | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::Learn); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::Learn); | ||||
|             break; | ||||
|         case SubmenuIndexRenameKey: | ||||
|             app->set_edit_action(IrdaApp::EditAction::Rename); | ||||
|             app->set_edit_element(IrdaApp::EditElement::Button); | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::EditKeySelect); | ||||
|             app->set_edit_action(InfraredApp::EditAction::Rename); | ||||
|             app->set_edit_element(InfraredApp::EditElement::Button); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::EditKeySelect); | ||||
|             break; | ||||
|         case SubmenuIndexDeleteKey: | ||||
|             app->set_edit_action(IrdaApp::EditAction::Delete); | ||||
|             app->set_edit_element(IrdaApp::EditElement::Button); | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::EditKeySelect); | ||||
|             app->set_edit_action(InfraredApp::EditAction::Delete); | ||||
|             app->set_edit_element(InfraredApp::EditElement::Button); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::EditKeySelect); | ||||
|             break; | ||||
|         case SubmenuIndexRenameRemote: | ||||
|             app->set_edit_action(IrdaApp::EditAction::Rename); | ||||
|             app->set_edit_element(IrdaApp::EditElement::Remote); | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::EditRename); | ||||
|             app->set_edit_action(InfraredApp::EditAction::Rename); | ||||
|             app->set_edit_element(InfraredApp::EditElement::Remote); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::EditRename); | ||||
|             break; | ||||
|         case SubmenuIndexDeleteRemote: | ||||
|             app->set_edit_action(IrdaApp::EditAction::Delete); | ||||
|             app->set_edit_element(IrdaApp::EditElement::Remote); | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::EditDelete); | ||||
|             app->set_edit_action(InfraredApp::EditAction::Delete); | ||||
|             app->set_edit_element(InfraredApp::EditElement::Remote); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::EditDelete); | ||||
|             break; | ||||
|         } | ||||
|         consumed = true; | ||||
| @ -71,8 +71,8 @@ bool IrdaAppSceneEdit::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneEdit::on_exit(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneEdit::on_exit(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
| 
 | ||||
|     submenu_reset(submenu); | ||||
| @ -1,25 +1,25 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "irda.h" | ||||
| #include "irda/scene/irda_app_scene.h" | ||||
| #include "../infrared_app.h" | ||||
| #include "infrared.h" | ||||
| #include "infrared/scene/infrared_app_scene.h" | ||||
| #include <string> | ||||
| 
 | ||||
| static void dialog_result_callback(DialogExResult result, void* context) { | ||||
|     auto app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
|     auto app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
| 
 | ||||
|     event.type = IrdaAppEvent::Type::DialogExSelected; | ||||
|     event.type = InfraredAppEvent::Type::DialogExSelected; | ||||
|     event.payload.dialog_ex_result = result; | ||||
| 
 | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneEditDelete::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||
| 
 | ||||
|     auto remote_manager = app->get_remote_manager(); | ||||
| 
 | ||||
|     if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||
|     if(app->get_edit_element() == InfraredApp::EditElement::Button) { | ||||
|         auto signal = remote_manager->get_button_data(app->get_current_button()); | ||||
|         dialog_ex_set_header(dialog_ex, "Delete button?", 64, 0, AlignCenter, AlignTop); | ||||
|         if(!signal.is_raw()) { | ||||
| @ -28,10 +28,10 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | ||||
|                 0, | ||||
|                 "%s\n%s\nA=0x%0*lX C=0x%0*lX", | ||||
|                 remote_manager->get_button_name(app->get_current_button()).c_str(), | ||||
|                 irda_get_protocol_name(message->protocol), | ||||
|                 ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4), | ||||
|                 infrared_get_protocol_name(message->protocol), | ||||
|                 ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), | ||||
|                 message->address, | ||||
|                 ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||
|                 ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), | ||||
|                 message->command); | ||||
|         } else { | ||||
|             app->set_text_store( | ||||
| @ -56,13 +56,13 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | ||||
|     dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); | ||||
|     dialog_ex_set_context(dialog_ex, app); | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::DialogEx); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneEditDelete::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneEditDelete::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::DialogExSelected) { | ||||
|     if(event->type == InfraredAppEvent::Type::DialogExSelected) { | ||||
|         switch(event->payload.dialog_ex_result) { | ||||
|         case DialogExResultLeft: | ||||
|             app->switch_to_previous_scene(); | ||||
| @ -73,18 +73,18 @@ bool IrdaAppSceneEditDelete::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|         case DialogExResultRight: { | ||||
|             auto remote_manager = app->get_remote_manager(); | ||||
|             bool result = false; | ||||
|             if(app->get_edit_element() == IrdaApp::EditElement::Remote) { | ||||
|             if(app->get_edit_element() == InfraredApp::EditElement::Remote) { | ||||
|                 result = remote_manager->delete_remote(); | ||||
|             } else { | ||||
|                 result = remote_manager->delete_button(app->get_current_button()); | ||||
|                 app->set_current_button(IrdaApp::ButtonNA); | ||||
|                 app->set_current_button(InfraredApp::ButtonNA); | ||||
|             } | ||||
| 
 | ||||
|             if(!result) { | ||||
|                 app->search_and_switch_to_previous_scene( | ||||
|                     {IrdaApp::Scene::RemoteList, IrdaApp::Scene::Start}); | ||||
|                     {InfraredApp::Scene::RemoteList, InfraredApp::Scene::Start}); | ||||
|             } else { | ||||
|                 app->switch_to_next_scene(IrdaApp::Scene::EditDeleteDone); | ||||
|                 app->switch_to_next_scene(InfraredApp::Scene::EditDeleteDone); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
| @ -96,5 +96,5 @@ bool IrdaAppSceneEditDelete::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneEditDelete::on_exit(IrdaApp* app) { | ||||
| void InfraredAppSceneEditDelete::on_exit(InfraredApp* app) { | ||||
| } | ||||
| @ -0,0 +1,38 @@ | ||||
| #include "../infrared_app.h" | ||||
| 
 | ||||
| void InfraredAppSceneEditDeleteDone::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
| 
 | ||||
|     popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); | ||||
|     popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); | ||||
| 
 | ||||
|     popup_set_callback(popup, InfraredApp::popup_callback); | ||||
|     popup_set_context(popup, app); | ||||
|     popup_set_timeout(popup, 1500); | ||||
|     popup_enable_timeout(popup); | ||||
| 
 | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Popup); | ||||
| } | ||||
| 
 | ||||
| bool InfraredAppSceneEditDeleteDone::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == InfraredAppEvent::Type::PopupTimer) { | ||||
|         if(app->get_edit_element() == InfraredApp::EditElement::Remote) { | ||||
|             app->search_and_switch_to_previous_scene( | ||||
|                 {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); | ||||
|         } else { | ||||
|             app->search_and_switch_to_previous_scene({InfraredApp::Scene::Remote}); | ||||
|         } | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void InfraredAppSceneEditDeleteDone::on_exit(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
|     popup_set_header(popup, nullptr, 0, 0, AlignLeft, AlignTop); | ||||
| } | ||||
| @ -0,0 +1,57 @@ | ||||
| #include "../infrared_app.h" | ||||
| #include "gui/modules/submenu.h" | ||||
| 
 | ||||
| static void submenu_callback(void* context, uint32_t index) { | ||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
| 
 | ||||
|     event.type = InfraredAppEvent::Type::MenuSelected; | ||||
|     event.payload.menu_index = index; | ||||
| 
 | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void InfraredAppSceneEditKeySelect::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
|     int item_number = 0; | ||||
| 
 | ||||
|     const char* header = | ||||
|         app->get_edit_action() == InfraredApp::EditAction::Rename ? "Rename key:" : "Delete key:"; | ||||
|     submenu_set_header(submenu, header); | ||||
| 
 | ||||
|     auto remote_manager = app->get_remote_manager(); | ||||
|     buttons_names = remote_manager->get_button_list(); | ||||
|     for(const auto& it : buttons_names) { | ||||
|         submenu_add_item(submenu, it.c_str(), item_number++, submenu_callback, app); | ||||
|     } | ||||
|     if((item_number > 0) && (app->get_current_button() != InfraredApp::ButtonNA)) { | ||||
|         submenu_set_selected_item(submenu, app->get_current_button()); | ||||
|         app->set_current_button(InfraredApp::ButtonNA); | ||||
|     } | ||||
| 
 | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); | ||||
| } | ||||
| 
 | ||||
| bool InfraredAppSceneEditKeySelect::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == InfraredAppEvent::Type::MenuSelected) { | ||||
|         app->set_current_button(event->payload.menu_index); | ||||
|         consumed = true; | ||||
|         if(app->get_edit_action() == InfraredApp::EditAction::Rename) { | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::EditRename); | ||||
|         } else { | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::EditDelete); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void InfraredAppSceneEditKeySelect::on_exit(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
| 
 | ||||
|     submenu_reset(submenu); | ||||
| } | ||||
| @ -1,16 +1,16 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "../infrared_app.h" | ||||
| 
 | ||||
| void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneEditRename::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     TextInput* text_input = view_manager->get_text_input(); | ||||
|     size_t enter_name_length = 0; | ||||
| 
 | ||||
|     auto remote_manager = app->get_remote_manager(); | ||||
|     if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||
|         furi_assert(app->get_current_button() != IrdaApp::ButtonNA); | ||||
|     if(app->get_edit_element() == InfraredApp::EditElement::Button) { | ||||
|         furi_assert(app->get_current_button() != InfraredApp::ButtonNA); | ||||
|         auto button_name = remote_manager->get_button_name(app->get_current_button()); | ||||
|         char* buffer_str = app->get_text_store(0); | ||||
|         size_t max_len = IrdaAppRemoteManager::max_button_name_length; | ||||
|         size_t max_len = InfraredAppRemoteManager::max_button_name_length; | ||||
|         strncpy(buffer_str, button_name.c_str(), max_len); | ||||
|         buffer_str[max_len + 1] = 0; | ||||
|         enter_name_length = max_len; | ||||
| @ -18,43 +18,43 @@ void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { | ||||
|     } else { | ||||
|         auto remote_name = remote_manager->get_remote_name(); | ||||
|         strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size()); | ||||
|         enter_name_length = IrdaAppRemoteManager::max_remote_name_length; | ||||
|         enter_name_length = InfraredAppRemoteManager::max_remote_name_length; | ||||
|         text_input_set_header_text(text_input, "Name the remote"); | ||||
| 
 | ||||
|         ValidatorIsFile* validator_is_file = | ||||
|             validator_is_file_alloc_init(app->irda_directory, app->irda_extension); | ||||
|             validator_is_file_alloc_init(app->infrared_directory, app->infrared_extension); | ||||
|         text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); | ||||
|     } | ||||
| 
 | ||||
|     text_input_set_result_callback( | ||||
|         text_input, | ||||
|         IrdaApp::text_input_callback, | ||||
|         InfraredApp::text_input_callback, | ||||
|         app, | ||||
|         app->get_text_store(0), | ||||
|         enter_name_length, | ||||
|         false); | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::TextInput); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneEditRename::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneEditRename::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::TextEditDone) { | ||||
|     if(event->type == InfraredAppEvent::Type::TextEditDone) { | ||||
|         auto remote_manager = app->get_remote_manager(); | ||||
|         bool result = false; | ||||
|         if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||
|         if(app->get_edit_element() == InfraredApp::EditElement::Button) { | ||||
|             result = | ||||
|                 remote_manager->rename_button(app->get_current_button(), app->get_text_store(0)); | ||||
|             app->set_current_button(IrdaApp::ButtonNA); | ||||
|             app->set_current_button(InfraredApp::ButtonNA); | ||||
|         } else { | ||||
|             result = remote_manager->rename_remote(app->get_text_store(0)); | ||||
|         } | ||||
|         if(!result) { | ||||
|             app->search_and_switch_to_previous_scene( | ||||
|                 {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); | ||||
|                 {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); | ||||
|         } else { | ||||
|             app->switch_to_next_scene_without_saving(IrdaApp::Scene::EditRenameDone); | ||||
|             app->switch_to_next_scene_without_saving(InfraredApp::Scene::EditRenameDone); | ||||
|         } | ||||
|         consumed = true; | ||||
|     } | ||||
| @ -62,7 +62,7 @@ bool IrdaAppSceneEditRename::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneEditRename::on_exit(IrdaApp* app) { | ||||
| void InfraredAppSceneEditRename::on_exit(InfraredApp* app) { | ||||
|     TextInput* text_input = app->get_view_manager()->get_text_input(); | ||||
| 
 | ||||
|     void* validator_context = text_input_get_validator_callback_context(text_input); | ||||
| @ -0,0 +1,31 @@ | ||||
| #include "../infrared_app.h" | ||||
| 
 | ||||
| void InfraredAppSceneEditRenameDone::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
| 
 | ||||
|     popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); | ||||
| 
 | ||||
|     popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); | ||||
| 
 | ||||
|     popup_set_callback(popup, InfraredApp::popup_callback); | ||||
|     popup_set_context(popup, app); | ||||
|     popup_set_timeout(popup, 1500); | ||||
|     popup_enable_timeout(popup); | ||||
| 
 | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Popup); | ||||
| } | ||||
| 
 | ||||
| bool InfraredAppSceneEditRenameDone::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == InfraredAppEvent::Type::PopupTimer) { | ||||
|         app->switch_to_next_scene(InfraredApp::Scene::Remote); | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void InfraredAppSceneEditRenameDone::on_exit(InfraredApp* app) { | ||||
| } | ||||
							
								
								
									
										75
									
								
								applications/infrared/scene/infrared_app_scene_learn.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								applications/infrared/scene/infrared_app_scene_learn.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| #include "../infrared_app.h" | ||||
| #include "../infrared_app_event.h" | ||||
| #include "infrared.h" | ||||
| #include <infrared_worker.h> | ||||
| 
 | ||||
| static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { | ||||
|     furi_assert(context); | ||||
|     furi_assert(received_signal); | ||||
| 
 | ||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||
| 
 | ||||
|     if(infrared_worker_signal_is_decoded(received_signal)) { | ||||
|         InfraredAppSignal signal(infrared_worker_get_decoded_signal(received_signal)); | ||||
|         app->set_received_signal(signal); | ||||
|     } else { | ||||
|         const uint32_t* timings; | ||||
|         size_t timings_cnt; | ||||
|         infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); | ||||
|         InfraredAppSignal signal( | ||||
|             timings, timings_cnt, INFRARED_COMMON_CARRIER_FREQUENCY, INFRARED_COMMON_DUTY_CYCLE); | ||||
|         app->set_received_signal(signal); | ||||
|     } | ||||
| 
 | ||||
|     infrared_worker_rx_set_received_signal_callback(app->get_infrared_worker(), NULL, NULL); | ||||
|     InfraredAppEvent event; | ||||
|     event.type = InfraredAppEvent::Type::InfraredMessageReceived; | ||||
|     auto view_manager = app->get_view_manager(); | ||||
|     view_manager->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void InfraredAppSceneLearn::on_enter(InfraredApp* app) { | ||||
|     auto view_manager = app->get_view_manager(); | ||||
|     auto popup = view_manager->get_popup(); | ||||
| 
 | ||||
|     auto worker = app->get_infrared_worker(); | ||||
|     infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, app); | ||||
|     infrared_worker_rx_start(worker); | ||||
| 
 | ||||
|     popup_set_icon(popup, 0, 32, &I_InfraredLearnShort_128x31); | ||||
|     popup_set_text( | ||||
|         popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); | ||||
|     popup_set_callback(popup, NULL); | ||||
| 
 | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Popup); | ||||
| } | ||||
| 
 | ||||
| bool InfraredAppSceneLearn::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     switch(event->type) { | ||||
|     case InfraredAppEvent::Type::Tick: | ||||
|         consumed = true; | ||||
|         app->notify_red_blink(); | ||||
|         break; | ||||
|     case InfraredAppEvent::Type::InfraredMessageReceived: | ||||
|         app->notify_success(); | ||||
|         app->switch_to_next_scene_without_saving(InfraredApp::Scene::LearnSuccess); | ||||
|         break; | ||||
|     case InfraredAppEvent::Type::Back: | ||||
|         consumed = true; | ||||
|         app->switch_to_previous_scene(); | ||||
|         break; | ||||
|     default: | ||||
|         furi_assert(0); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void InfraredAppSceneLearn::on_exit(InfraredApp* app) { | ||||
|     infrared_worker_rx_stop(app->get_infrared_worker()); | ||||
|     auto view_manager = app->get_view_manager(); | ||||
|     auto popup = view_manager->get_popup(); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter); | ||||
| } | ||||
| @ -1,8 +1,8 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "../infrared_app.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneLearnDone::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
| 
 | ||||
|     popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); | ||||
| @ -14,28 +14,28 @@ void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) { | ||||
|         popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); | ||||
|     } | ||||
| 
 | ||||
|     popup_set_callback(popup, IrdaApp::popup_callback); | ||||
|     popup_set_callback(popup, InfraredApp::popup_callback); | ||||
|     popup_set_context(popup, app); | ||||
|     popup_set_timeout(popup, 1500); | ||||
|     popup_enable_timeout(popup); | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Popup); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneLearnDone::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneLearnDone::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::PopupTimer) { | ||||
|         app->switch_to_next_scene(IrdaApp::Scene::Remote); | ||||
|     if(event->type == InfraredAppEvent::Type::PopupTimer) { | ||||
|         app->switch_to_next_scene(InfraredApp::Scene::Remote); | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneLearnDone::on_exit(IrdaApp* app) { | ||||
| void InfraredAppSceneLearnDone::on_exit(InfraredApp* app) { | ||||
|     app->set_learn_new_remote(false); | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
|     popup_set_header(popup, nullptr, 0, 0, AlignLeft, AlignTop); | ||||
| } | ||||
| @ -1,8 +1,8 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "../infrared_app.h" | ||||
| #include "gui/modules/text_input.h" | ||||
| 
 | ||||
| void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneLearnEnterName::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     TextInput* text_input = view_manager->get_text_input(); | ||||
| 
 | ||||
|     auto signal = app->get_received_signal(); | ||||
| @ -12,8 +12,8 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { | ||||
|         app->set_text_store( | ||||
|             0, | ||||
|             "%.4s_%0*lX", | ||||
|             irda_get_protocol_name(message->protocol), | ||||
|             ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||
|             infrared_get_protocol_name(message->protocol), | ||||
|             ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), | ||||
|             message->command); | ||||
|     } else { | ||||
|         auto raw_signal = signal.get_raw_signal(); | ||||
| @ -23,19 +23,19 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { | ||||
|     text_input_set_header_text(text_input, "Name the key"); | ||||
|     text_input_set_result_callback( | ||||
|         text_input, | ||||
|         IrdaApp::text_input_callback, | ||||
|         InfraredApp::text_input_callback, | ||||
|         app, | ||||
|         app->get_text_store(0), | ||||
|         IrdaAppRemoteManager::max_button_name_length, | ||||
|         InfraredAppRemoteManager::max_button_name_length, | ||||
|         true); | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::TextInput); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneLearnEnterName::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneLearnEnterName::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::TextEditDone) { | ||||
|     if(event->type == InfraredAppEvent::Type::TextEditDone) { | ||||
|         auto remote_manager = app->get_remote_manager(); | ||||
|         bool result = false; | ||||
|         if(app->get_learn_new_remote()) { | ||||
| @ -48,13 +48,13 @@ bool IrdaAppSceneLearnEnterName::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| 
 | ||||
|         if(!result) { | ||||
|             app->search_and_switch_to_previous_scene( | ||||
|                 {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); | ||||
|                 {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); | ||||
|         } else { | ||||
|             app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnDone); | ||||
|             app->switch_to_next_scene_without_saving(InfraredApp::Scene::LearnDone); | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneLearnEnterName::on_exit(IrdaApp* app) { | ||||
| void InfraredAppSceneLearnEnterName::on_exit(InfraredApp* app) { | ||||
| } | ||||
| @ -3,42 +3,44 @@ | ||||
| #include <memory> | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| #include "../irda_app.h" | ||||
| #include "irda.h" | ||||
| #include "../infrared_app.h" | ||||
| #include "infrared.h" | ||||
| 
 | ||||
| static void dialog_result_callback(DialogExResult result, void* context) { | ||||
|     auto app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
|     auto app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
| 
 | ||||
|     event.type = IrdaAppEvent::Type::DialogExSelected; | ||||
|     event.type = InfraredAppEvent::Type::DialogExSelected; | ||||
|     event.payload.dialog_ex_result = result; | ||||
| 
 | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneLearnSuccess::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||
| 
 | ||||
|     DOLPHIN_DEED(DolphinDeedIrLearnSuccess); | ||||
|     app->notify_green_on(); | ||||
| 
 | ||||
|     irda_worker_tx_set_get_signal_callback( | ||||
|         app->get_irda_worker(), irda_worker_tx_get_signal_steady_callback, app); | ||||
|     irda_worker_tx_set_signal_sent_callback( | ||||
|         app->get_irda_worker(), IrdaApp::signal_sent_callback, app); | ||||
|     infrared_worker_tx_set_get_signal_callback( | ||||
|         app->get_infrared_worker(), infrared_worker_tx_get_signal_steady_callback, app); | ||||
|     infrared_worker_tx_set_signal_sent_callback( | ||||
|         app->get_infrared_worker(), InfraredApp::signal_sent_callback, app); | ||||
| 
 | ||||
|     auto signal = app->get_received_signal(); | ||||
| 
 | ||||
|     if(!signal.is_raw()) { | ||||
|         auto message = &signal.get_message(); | ||||
|         uint8_t adr_digits = ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4); | ||||
|         uint8_t cmd_digits = ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4); | ||||
|         uint8_t adr_digits = | ||||
|             ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4); | ||||
|         uint8_t cmd_digits = | ||||
|             ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4); | ||||
|         uint8_t max_digits = MAX(adr_digits, cmd_digits); | ||||
|         max_digits = MIN(max_digits, 7); | ||||
|         size_t label_x_offset = 63 + (7 - max_digits) * 3; | ||||
| 
 | ||||
|         app->set_text_store(0, "%s", irda_get_protocol_name(message->protocol)); | ||||
|         app->set_text_store(0, "%s", infrared_get_protocol_name(message->protocol)); | ||||
|         app->set_text_store( | ||||
|             1, | ||||
|             "A: 0x%0*lX\nC: 0x%0*lX\n", | ||||
| @ -64,24 +66,24 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | ||||
|     dialog_ex_set_context(dialog_ex, app); | ||||
|     dialog_ex_enable_extended_events(dialog_ex); | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::DialogEx); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneLearnSuccess::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
|     if(event->type == IrdaAppEvent::Type::Tick) { | ||||
|     if(event->type == InfraredAppEvent::Type::Tick) { | ||||
|         /* Send event every tick to suppress any switching off green light */ | ||||
|         if(!button_pressed) { | ||||
|             app->notify_green_on(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::DialogExSelected) { | ||||
|     if(event->type == InfraredAppEvent::Type::DialogExSelected) { | ||||
|         switch(event->payload.dialog_ex_result) { | ||||
|         case DialogExResultLeft: | ||||
|             consumed = true; | ||||
|             if(!button_pressed) { | ||||
|                 app->switch_to_next_scene_without_saving(IrdaApp::Scene::Learn); | ||||
|                 app->switch_to_next_scene_without_saving(InfraredApp::Scene::Learn); | ||||
|             } | ||||
|             break; | ||||
|         case DialogExResultRight: { | ||||
| @ -89,7 +91,7 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|             FileWorkerCpp file_worker; | ||||
|             if(!button_pressed) { | ||||
|                 if(file_worker.check_errors()) { | ||||
|                     app->switch_to_next_scene(IrdaApp::Scene::LearnEnterName); | ||||
|                     app->switch_to_next_scene(InfraredApp::Scene::LearnEnterName); | ||||
|                 } else { | ||||
|                     app->switch_to_previous_scene(); | ||||
|                 } | ||||
| @ -103,21 +105,22 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| 
 | ||||
|                 auto signal = app->get_received_signal(); | ||||
|                 if(signal.is_raw()) { | ||||
|                     irda_worker_set_raw_signal( | ||||
|                         app->get_irda_worker(), | ||||
|                     infrared_worker_set_raw_signal( | ||||
|                         app->get_infrared_worker(), | ||||
|                         signal.get_raw_signal().timings, | ||||
|                         signal.get_raw_signal().timings_cnt); | ||||
|                 } else { | ||||
|                     irda_worker_set_decoded_signal(app->get_irda_worker(), &signal.get_message()); | ||||
|                     infrared_worker_set_decoded_signal( | ||||
|                         app->get_infrared_worker(), &signal.get_message()); | ||||
|                 } | ||||
| 
 | ||||
|                 irda_worker_tx_start(app->get_irda_worker()); | ||||
|                 infrared_worker_tx_start(app->get_infrared_worker()); | ||||
|             } | ||||
|             break; | ||||
|         case DialogExReleaseCenter: | ||||
|             if(button_pressed) { | ||||
|                 button_pressed = false; | ||||
|                 irda_worker_tx_stop(app->get_irda_worker()); | ||||
|                 infrared_worker_tx_stop(app->get_infrared_worker()); | ||||
|                 app->notify_green_off(); | ||||
|             } | ||||
|             break; | ||||
| @ -126,9 +129,9 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::Back) { | ||||
|     if(event->type == InfraredAppEvent::Type::Back) { | ||||
|         if(!button_pressed) { | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::AskBack); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::AskBack); | ||||
|         } | ||||
|         consumed = true; | ||||
|     } | ||||
| @ -136,11 +139,11 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneLearnSuccess::on_exit(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneLearnSuccess::on_exit(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||
|     dialog_ex_reset(dialog_ex); | ||||
|     app->notify_green_off(); | ||||
|     irda_worker_tx_set_get_signal_callback(app->get_irda_worker(), nullptr, nullptr); | ||||
|     irda_worker_tx_set_signal_sent_callback(app->get_irda_worker(), nullptr, nullptr); | ||||
|     infrared_worker_tx_set_get_signal_callback(app->get_infrared_worker(), nullptr, nullptr); | ||||
|     infrared_worker_tx_set_signal_sent_callback(app->get_infrared_worker(), nullptr, nullptr); | ||||
| } | ||||
| @ -1,9 +1,9 @@ | ||||
| #include <gui/modules/button_menu.h> | ||||
| #include <input/input.h> | ||||
| #include <irda_worker.h> | ||||
| #include <infrared_worker.h> | ||||
| #include <dolphin/dolphin.h> | ||||
| #include "../irda_app.h" | ||||
| #include "../irda_app_view_manager.h" | ||||
| #include "../infrared_app.h" | ||||
| #include "../infrared_app_view_manager.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     ButtonIndexPlus = -2, | ||||
| @ -12,15 +12,15 @@ typedef enum { | ||||
| } ButtonIndex; | ||||
| 
 | ||||
| static void button_menu_callback(void* context, int32_t index, InputType type) { | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
| 
 | ||||
|     if(type == InputTypePress) { | ||||
|         event.type = IrdaAppEvent::Type::MenuSelectedPress; | ||||
|         event.type = InfraredAppEvent::Type::MenuSelectedPress; | ||||
|     } else if(type == InputTypeRelease) { | ||||
|         event.type = IrdaAppEvent::Type::MenuSelectedRelease; | ||||
|         event.type = InfraredAppEvent::Type::MenuSelectedRelease; | ||||
|     } else if(type == InputTypeShort) { | ||||
|         event.type = IrdaAppEvent::Type::MenuSelected; | ||||
|         event.type = InfraredAppEvent::Type::MenuSelected; | ||||
|     } else { | ||||
|         furi_assert(0); | ||||
|     } | ||||
| @ -30,17 +30,17 @@ static void button_menu_callback(void* context, int32_t index, InputType type) { | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneRemote::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneRemote::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     ButtonMenu* button_menu = view_manager->get_button_menu(); | ||||
|     auto remote_manager = app->get_remote_manager(); | ||||
|     int i = 0; | ||||
|     button_pressed = false; | ||||
| 
 | ||||
|     irda_worker_tx_set_get_signal_callback( | ||||
|         app->get_irda_worker(), irda_worker_tx_get_signal_steady_callback, app); | ||||
|     irda_worker_tx_set_signal_sent_callback( | ||||
|         app->get_irda_worker(), IrdaApp::signal_sent_callback, app); | ||||
|     infrared_worker_tx_set_get_signal_callback( | ||||
|         app->get_infrared_worker(), infrared_worker_tx_get_signal_steady_callback, app); | ||||
|     infrared_worker_tx_set_signal_sent_callback( | ||||
|         app->get_infrared_worker(), InfraredApp::signal_sent_callback, app); | ||||
|     buttons_names = remote_manager->get_button_list(); | ||||
| 
 | ||||
|     i = 0; | ||||
| @ -60,32 +60,32 @@ void IrdaAppSceneRemote::on_enter(IrdaApp* app) { | ||||
|         button_menu_set_selected_item(button_menu, buttonmenu_item_selected); | ||||
|         buttonmenu_item_selected = ButtonIndexNA; | ||||
|     } | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::ButtonMenu); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::ButtonMenu); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneRemote::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = true; | ||||
| 
 | ||||
|     if((event->type == IrdaAppEvent::Type::MenuSelected) || | ||||
|        (event->type == IrdaAppEvent::Type::MenuSelectedPress) || | ||||
|        (event->type == IrdaAppEvent::Type::MenuSelectedRelease)) { | ||||
|     if((event->type == InfraredAppEvent::Type::MenuSelected) || | ||||
|        (event->type == InfraredAppEvent::Type::MenuSelectedPress) || | ||||
|        (event->type == InfraredAppEvent::Type::MenuSelectedRelease)) { | ||||
|         switch(event->payload.menu_index) { | ||||
|         case ButtonIndexPlus: | ||||
|             furi_assert(event->type == IrdaAppEvent::Type::MenuSelected); | ||||
|             furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); | ||||
|             app->notify_click(); | ||||
|             buttonmenu_item_selected = event->payload.menu_index; | ||||
|             app->set_learn_new_remote(false); | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::Learn); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::Learn); | ||||
|             break; | ||||
|         case ButtonIndexEdit: | ||||
|             furi_assert(event->type == IrdaAppEvent::Type::MenuSelected); | ||||
|             furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); | ||||
|             app->notify_click(); | ||||
|             buttonmenu_item_selected = event->payload.menu_index; | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::Edit); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::Edit); | ||||
|             break; | ||||
|         default: | ||||
|             furi_assert(event->type != IrdaAppEvent::Type::MenuSelected); | ||||
|             bool pressed = (event->type == IrdaAppEvent::Type::MenuSelectedPress); | ||||
|             furi_assert(event->type != InfraredAppEvent::Type::MenuSelected); | ||||
|             bool pressed = (event->type == InfraredAppEvent::Type::MenuSelectedPress); | ||||
| 
 | ||||
|             if(pressed && !button_pressed) { | ||||
|                 button_pressed = true; | ||||
| @ -94,28 +94,28 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|                 auto button_signal = | ||||
|                     app->get_remote_manager()->get_button_data(event->payload.menu_index); | ||||
|                 if(button_signal.is_raw()) { | ||||
|                     irda_worker_set_raw_signal( | ||||
|                         app->get_irda_worker(), | ||||
|                     infrared_worker_set_raw_signal( | ||||
|                         app->get_infrared_worker(), | ||||
|                         button_signal.get_raw_signal().timings, | ||||
|                         button_signal.get_raw_signal().timings_cnt); | ||||
|                 } else { | ||||
|                     irda_worker_set_decoded_signal( | ||||
|                         app->get_irda_worker(), &button_signal.get_message()); | ||||
|                     infrared_worker_set_decoded_signal( | ||||
|                         app->get_infrared_worker(), &button_signal.get_message()); | ||||
|                 } | ||||
| 
 | ||||
|                 DOLPHIN_DEED(DolphinDeedIrSend); | ||||
|                 irda_worker_tx_start(app->get_irda_worker()); | ||||
|                 infrared_worker_tx_start(app->get_infrared_worker()); | ||||
|             } else if(!pressed && button_pressed) { | ||||
|                 button_pressed = false; | ||||
|                 irda_worker_tx_stop(app->get_irda_worker()); | ||||
|                 infrared_worker_tx_stop(app->get_infrared_worker()); | ||||
|                 app->notify_green_off(); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|     } else if(event->type == IrdaAppEvent::Type::Back) { | ||||
|     } else if(event->type == InfraredAppEvent::Type::Back) { | ||||
|         if(!button_pressed) { | ||||
|             app->search_and_switch_to_previous_scene( | ||||
|                 {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); | ||||
|                 {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); | ||||
|         } | ||||
|     } else { | ||||
|         consumed = false; | ||||
| @ -124,10 +124,10 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneRemote::on_exit(IrdaApp* app) { | ||||
|     irda_worker_tx_set_get_signal_callback(app->get_irda_worker(), nullptr, nullptr); | ||||
|     irda_worker_tx_set_signal_sent_callback(app->get_irda_worker(), nullptr, nullptr); | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneRemote::on_exit(InfraredApp* app) { | ||||
|     infrared_worker_tx_set_get_signal_callback(app->get_infrared_worker(), nullptr, nullptr); | ||||
|     infrared_worker_tx_set_signal_sent_callback(app->get_infrared_worker(), nullptr, nullptr); | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     ButtonMenu* button_menu = view_manager->get_button_menu(); | ||||
| 
 | ||||
|     button_menu_reset(button_menu); | ||||
| @ -1,9 +1,9 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "irda/irda_app_event.h" | ||||
| #include "../infrared_app.h" | ||||
| #include "infrared/infrared_app_event.h" | ||||
| #include <text_store.h> | ||||
| #include <file_worker_cpp.h> | ||||
| 
 | ||||
| void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { | ||||
| void InfraredAppSceneRemoteList::on_enter(InfraredApp* app) { | ||||
|     furi_assert(app); | ||||
| 
 | ||||
|     FileWorkerCpp file_worker; | ||||
| @ -13,23 +13,24 @@ void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { | ||||
|     auto last_selected_remote = remote_manager->get_remote_name(); | ||||
|     const char* last_selected_remote_name = | ||||
|         last_selected_remote.size() ? last_selected_remote.c_str() : nullptr; | ||||
|     auto filename_ts = std::make_unique<TextStore>(IrdaAppRemoteManager::max_remote_name_length); | ||||
|     auto filename_ts = | ||||
|         std::make_unique<TextStore>(InfraredAppRemoteManager::max_remote_name_length); | ||||
| 
 | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     ButtonMenu* button_menu = view_manager->get_button_menu(); | ||||
|     button_menu_reset(button_menu); | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::ButtonMenu); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::ButtonMenu); | ||||
| 
 | ||||
|     file_select_result = file_worker.file_select( | ||||
|         IrdaApp::irda_directory, | ||||
|         IrdaApp::irda_extension, | ||||
|         InfraredApp::infrared_directory, | ||||
|         InfraredApp::infrared_extension, | ||||
|         filename_ts->text, | ||||
|         filename_ts->text_size, | ||||
|         last_selected_remote_name); | ||||
| 
 | ||||
|     if(file_select_result) { | ||||
|         if(remote_manager->load(std::string(filename_ts->text))) { | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::Remote); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::Remote); | ||||
|             result = true; | ||||
|         } | ||||
|     } | ||||
| @ -39,11 +40,11 @@ void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneRemoteList::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneRemoteList::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneRemoteList::on_exit(IrdaApp* app) { | ||||
| void InfraredAppSceneRemoteList::on_exit(InfraredApp* app) { | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "../infrared_app.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubmenuIndexUniversalLibrary, | ||||
| @ -7,17 +7,17 @@ typedef enum { | ||||
| } SubmenuIndex; | ||||
| 
 | ||||
| static void submenu_callback(void* context, uint32_t index) { | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
| 
 | ||||
|     event.type = IrdaAppEvent::Type::MenuSelected; | ||||
|     event.type = InfraredAppEvent::Type::MenuSelected; | ||||
|     event.payload.menu_index = index; | ||||
| 
 | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneStart::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneStart::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
| 
 | ||||
|     submenu_add_item( | ||||
| @ -28,24 +28,24 @@ void IrdaAppSceneStart::on_enter(IrdaApp* app) { | ||||
|     submenu_set_selected_item(submenu, submenu_item_selected); | ||||
|     submenu_item_selected = 0; | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneStart::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneStart::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||
|     if(event->type == InfraredAppEvent::Type::MenuSelected) { | ||||
|         submenu_item_selected = event->payload.menu_index; | ||||
|         switch(event->payload.menu_index) { | ||||
|         case SubmenuIndexUniversalLibrary: | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::Universal); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::Universal); | ||||
|             break; | ||||
|         case SubmenuIndexLearnNewRemote: | ||||
|             app->set_learn_new_remote(true); | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::Learn); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::Learn); | ||||
|             break; | ||||
|         case SubmenuIndexSavedRemotes: | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::RemoteList); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::RemoteList); | ||||
|             break; | ||||
|         default: | ||||
|             furi_assert(0); | ||||
| @ -57,8 +57,8 @@ bool IrdaAppSceneStart::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneStart::on_exit(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneStart::on_exit(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
| 
 | ||||
|     app->get_remote_manager()->reset_remote(); | ||||
| @ -1,4 +1,4 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "../infrared_app.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubmenuIndexUniversalTV, | ||||
| @ -7,40 +7,40 @@ typedef enum { | ||||
| } SubmenuIndex; | ||||
| 
 | ||||
| static void submenu_callback(void* context, uint32_t index) { | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
| 
 | ||||
|     event.type = IrdaAppEvent::Type::MenuSelected; | ||||
|     event.type = InfraredAppEvent::Type::MenuSelected; | ||||
|     event.payload.menu_index = index; | ||||
| 
 | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneUniversal::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneUniversal::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
| 
 | ||||
|     submenu_add_item(submenu, "TV's", SubmenuIndexUniversalTV, submenu_callback, app); | ||||
|     submenu_set_selected_item(submenu, submenu_item_selected); | ||||
|     submenu_item_selected = 0; | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneUniversal::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
| bool InfraredAppSceneUniversal::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||
|     if(event->type == InfraredAppEvent::Type::MenuSelected) { | ||||
|         submenu_item_selected = event->payload.menu_index; | ||||
|         switch(event->payload.menu_index) { | ||||
|         case SubmenuIndexUniversalTV: | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::UniversalTV); | ||||
|             app->switch_to_next_scene(InfraredApp::Scene::UniversalTV); | ||||
|             break; | ||||
|         case SubmenuIndexUniversalAudio: | ||||
|             //            app->switch_to_next_scene(IrdaApp::Scene::UniversalAudio);
 | ||||
|             //            app->switch_to_next_scene(InfraredApp::Scene::UniversalAudio);
 | ||||
|             break; | ||||
|         case SubmenuIndexUniversalAirConditioner: | ||||
|             //            app->switch_to_next_scene(IrdaApp::Scene::UniversalAirConditioner);
 | ||||
|             //            app->switch_to_next_scene(InfraredApp::Scene::UniversalAirConditioner);
 | ||||
|             break; | ||||
|         } | ||||
|         consumed = true; | ||||
| @ -49,8 +49,8 @@ bool IrdaAppSceneUniversal::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneUniversal::on_exit(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneUniversal::on_exit(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
| 
 | ||||
|     submenu_reset(submenu); | ||||
| @ -0,0 +1,101 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| #include <gui/modules/button_menu.h> | ||||
| #include <gui/modules/button_panel.h> | ||||
| #include <gui/view.h> | ||||
| #include <gui/view_stack.h> | ||||
| 
 | ||||
| #include "../infrared_app.h" | ||||
| #include "infrared/infrared_app_event.h" | ||||
| #include "infrared/infrared_app_view_manager.h" | ||||
| #include "infrared/scene/infrared_app_scene.h" | ||||
| #include "../view/infrared_progress_view.h" | ||||
| 
 | ||||
| void InfraredAppSceneUniversalCommon::infrared_app_item_callback(void* context, uint32_t index) { | ||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); | ||||
|     InfraredAppEvent event; | ||||
| 
 | ||||
|     event.type = InfraredAppEvent::Type::ButtonPanelPressed; | ||||
|     event.payload.menu_index = index; | ||||
| 
 | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| static void infrared_progress_back_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     auto app = static_cast<InfraredApp*>(context); | ||||
| 
 | ||||
|     InfraredAppEvent infrared_event = { | ||||
|         .type = InfraredAppEvent::Type::Back, | ||||
|     }; | ||||
|     app->get_view_manager()->clear_events(); | ||||
|     app->get_view_manager()->send_event(&infrared_event); | ||||
| } | ||||
| 
 | ||||
| void InfraredAppSceneUniversalCommon::hide_popup(InfraredApp* app) { | ||||
|     auto stack_view = app->get_view_manager()->get_universal_view_stack(); | ||||
|     auto progress_view = app->get_view_manager()->get_progress(); | ||||
|     view_stack_remove_view(stack_view, infrared_progress_view_get_view(progress_view)); | ||||
| } | ||||
| 
 | ||||
| void InfraredAppSceneUniversalCommon::show_popup(InfraredApp* app, int record_amount) { | ||||
|     auto stack_view = app->get_view_manager()->get_universal_view_stack(); | ||||
|     auto progress_view = app->get_view_manager()->get_progress(); | ||||
|     infrared_progress_view_set_progress_total(progress_view, record_amount); | ||||
|     infrared_progress_view_set_back_callback(progress_view, infrared_progress_back_callback, app); | ||||
|     view_stack_add_view(stack_view, infrared_progress_view_get_view(progress_view)); | ||||
| } | ||||
| 
 | ||||
| bool InfraredAppSceneUniversalCommon::progress_popup(InfraredApp* app) { | ||||
|     auto progress_view = app->get_view_manager()->get_progress(); | ||||
|     return infrared_progress_view_increase_progress(progress_view); | ||||
| } | ||||
| 
 | ||||
| bool InfraredAppSceneUniversalCommon::on_event(InfraredApp* app, InfraredAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(brute_force_started) { | ||||
|         if(event->type == InfraredAppEvent::Type::Tick) { | ||||
|             auto view_manager = app->get_view_manager(); | ||||
|             InfraredAppEvent tick_event = {.type = InfraredAppEvent::Type::Tick}; | ||||
|             view_manager->send_event(&tick_event); | ||||
|             bool result = brute_force.send_next_bruteforce(); | ||||
|             if(result) { | ||||
|                 result = progress_popup(app); | ||||
|             } | ||||
|             if(!result) { | ||||
|                 brute_force.stop_bruteforce(); | ||||
|                 brute_force_started = false; | ||||
|                 hide_popup(app); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } else if(event->type == InfraredAppEvent::Type::Back) { | ||||
|             brute_force_started = false; | ||||
|             brute_force.stop_bruteforce(); | ||||
|             hide_popup(app); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else { | ||||
|         if(event->type == InfraredAppEvent::Type::ButtonPanelPressed) { | ||||
|             int record_amount = 0; | ||||
|             if(brute_force.start_bruteforce(event->payload.menu_index, record_amount)) { | ||||
|                 DOLPHIN_DEED(DolphinDeedIrBruteForce); | ||||
|                 brute_force_started = true; | ||||
|                 show_popup(app, record_amount); | ||||
|             } else { | ||||
|                 app->switch_to_previous_scene(); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } else if(event->type == InfraredAppEvent::Type::Back) { | ||||
|             app->switch_to_previous_scene(); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void InfraredAppSceneUniversalCommon::on_exit(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     ButtonPanel* button_panel = view_manager->get_button_panel(); | ||||
|     button_panel_reset(button_panel); | ||||
| } | ||||
| @ -1,11 +1,11 @@ | ||||
| #include <stdint.h> | ||||
| #include <gui/modules/loading.h> | ||||
| #include <gui/view_stack.h> | ||||
| #include "irda/scene/irda_app_scene.h" | ||||
| #include "irda/irda_app.h" | ||||
| #include "infrared/scene/infrared_app_scene.h" | ||||
| #include "infrared/infrared_app.h" | ||||
| 
 | ||||
| void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
| void InfraredAppSceneUniversalTV::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     ButtonPanel* button_panel = view_manager->get_button_panel(); | ||||
|     button_panel_reserve(button_panel, 2, 3); | ||||
| 
 | ||||
| @ -19,7 +19,7 @@ void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) { | ||||
|         19, | ||||
|         &I_Power_25x27, | ||||
|         &I_Power_hvr_25x27, | ||||
|         irda_app_item_callback, | ||||
|         infrared_app_item_callback, | ||||
|         app); | ||||
|     brute_force.add_record(i, "POWER"); | ||||
|     ++i; | ||||
| @ -32,7 +32,7 @@ void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) { | ||||
|         19, | ||||
|         &I_Mute_25x27, | ||||
|         &I_Mute_hvr_25x27, | ||||
|         irda_app_item_callback, | ||||
|         infrared_app_item_callback, | ||||
|         app); | ||||
|     brute_force.add_record(i, "MUTE"); | ||||
|     ++i; | ||||
| @ -45,12 +45,21 @@ void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) { | ||||
|         66, | ||||
|         &I_Vol_up_25x27, | ||||
|         &I_Vol_up_hvr_25x27, | ||||
|         irda_app_item_callback, | ||||
|         infrared_app_item_callback, | ||||
|         app); | ||||
|     brute_force.add_record(i, "VOL+"); | ||||
|     ++i; | ||||
|     button_panel_add_item( | ||||
|         button_panel, i, 1, 1, 36, 66, &I_Up_25x27, &I_Up_hvr_25x27, irda_app_item_callback, app); | ||||
|         button_panel, | ||||
|         i, | ||||
|         1, | ||||
|         1, | ||||
|         36, | ||||
|         66, | ||||
|         &I_Up_25x27, | ||||
|         &I_Up_hvr_25x27, | ||||
|         infrared_app_item_callback, | ||||
|         app); | ||||
|     brute_force.add_record(i, "CH+"); | ||||
|     ++i; | ||||
|     button_panel_add_item( | ||||
| @ -62,7 +71,7 @@ void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) { | ||||
|         98, | ||||
|         &I_Vol_down_25x27, | ||||
|         &I_Vol_down_hvr_25x27, | ||||
|         irda_app_item_callback, | ||||
|         infrared_app_item_callback, | ||||
|         app); | ||||
|     brute_force.add_record(i, "VOL-"); | ||||
|     ++i; | ||||
| @ -75,7 +84,7 @@ void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) { | ||||
|         98, | ||||
|         &I_Down_25x27, | ||||
|         &I_Down_hvr_25x27, | ||||
|         irda_app_item_callback, | ||||
|         infrared_app_item_callback, | ||||
|         app); | ||||
|     brute_force.add_record(i, "CH-"); | ||||
| 
 | ||||
| @ -83,7 +92,7 @@ void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) { | ||||
|     button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol"); | ||||
|     button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch"); | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::UniversalRemote); | ||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::UniversalRemote); | ||||
| 
 | ||||
|     auto stack_view = app->get_view_manager()->get_universal_view_stack(); | ||||
|     auto loading_view = app->get_view_manager()->get_loading(); | ||||
| @ -7,26 +7,26 @@ | ||||
| #include "m-string.h" | ||||
| #include <gui/elements.h> | ||||
| #include <furi.h> | ||||
| #include "irda_progress_view.h" | ||||
| #include "infrared_progress_view.h" | ||||
| #include "gui/modules/button_panel.h" | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| struct IrdaProgressView { | ||||
| struct InfraredProgressView { | ||||
|     View* view; | ||||
|     IrdaProgressViewBackCallback back_callback; | ||||
|     InfraredProgressViewBackCallback back_callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     size_t progress; | ||||
|     size_t progress_total; | ||||
| } IrdaProgressViewModel; | ||||
| } InfraredProgressViewModel; | ||||
| 
 | ||||
| bool irda_progress_view_increase_progress(IrdaProgressView* progress) { | ||||
| bool infrared_progress_view_increase_progress(InfraredProgressView* progress) { | ||||
|     furi_assert(progress); | ||||
|     bool result = false; | ||||
| 
 | ||||
|     IrdaProgressViewModel* model = view_get_model(progress->view); | ||||
|     InfraredProgressViewModel* model = view_get_model(progress->view); | ||||
|     if(model->progress < model->progress_total) { | ||||
|         ++model->progress; | ||||
|         result = model->progress < model->progress_total; | ||||
| @ -36,8 +36,8 @@ bool irda_progress_view_increase_progress(IrdaProgressView* progress) { | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| static void irda_progress_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     IrdaProgressViewModel* model = (IrdaProgressViewModel*)_model; | ||||
| static void infrared_progress_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     InfraredProgressViewModel* model = (InfraredProgressViewModel*)_model; | ||||
| 
 | ||||
|     uint8_t x = 0; | ||||
|     uint8_t y = 36; | ||||
| @ -63,16 +63,18 @@ static void irda_progress_view_draw_callback(Canvas* canvas, void* _model) { | ||||
|     canvas_draw_str(canvas, x + 30, y + height - 6, "= stop"); | ||||
| } | ||||
| 
 | ||||
| void irda_progress_view_set_progress_total(IrdaProgressView* progress, uint16_t progress_total) { | ||||
| void infrared_progress_view_set_progress_total( | ||||
|     InfraredProgressView* progress, | ||||
|     uint16_t progress_total) { | ||||
|     furi_assert(progress); | ||||
|     IrdaProgressViewModel* model = view_get_model(progress->view); | ||||
|     InfraredProgressViewModel* model = view_get_model(progress->view); | ||||
|     model->progress = 0; | ||||
|     model->progress_total = progress_total; | ||||
|     view_commit_model(progress->view, false); | ||||
| } | ||||
| 
 | ||||
| bool irda_progress_view_input_callback(InputEvent* event, void* context) { | ||||
|     IrdaProgressView* instance = context; | ||||
| bool infrared_progress_view_input_callback(InputEvent* event, void* context) { | ||||
|     InfraredProgressView* instance = context; | ||||
| 
 | ||||
|     if((event->type == InputTypeShort) && (event->key == InputKeyBack)) { | ||||
|         if(instance->back_callback) { | ||||
| @ -83,36 +85,36 @@ bool irda_progress_view_input_callback(InputEvent* event, void* context) { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| IrdaProgressView* irda_progress_view_alloc(void) { | ||||
|     IrdaProgressView* instance = malloc(sizeof(IrdaProgressView)); | ||||
| InfraredProgressView* infrared_progress_view_alloc(void) { | ||||
|     InfraredProgressView* instance = malloc(sizeof(InfraredProgressView)); | ||||
|     instance->view = view_alloc(); | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(IrdaProgressViewModel)); | ||||
|     IrdaProgressViewModel* model = view_get_model(instance->view); | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(InfraredProgressViewModel)); | ||||
|     InfraredProgressViewModel* model = view_get_model(instance->view); | ||||
|     model->progress = 0; | ||||
|     model->progress_total = 0; | ||||
|     view_commit_model(instance->view, false); | ||||
|     view_set_draw_callback(instance->view, irda_progress_view_draw_callback); | ||||
|     view_set_input_callback(instance->view, irda_progress_view_input_callback); | ||||
|     view_set_draw_callback(instance->view, infrared_progress_view_draw_callback); | ||||
|     view_set_input_callback(instance->view, infrared_progress_view_input_callback); | ||||
|     view_set_context(instance->view, instance); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void irda_progress_view_free(IrdaProgressView* progress) { | ||||
| void infrared_progress_view_free(InfraredProgressView* progress) { | ||||
|     view_free(progress->view); | ||||
|     free(progress); | ||||
| } | ||||
| 
 | ||||
| void irda_progress_view_set_back_callback( | ||||
|     IrdaProgressView* instance, | ||||
|     IrdaProgressViewBackCallback callback, | ||||
| void infrared_progress_view_set_back_callback( | ||||
|     InfraredProgressView* instance, | ||||
|     InfraredProgressViewBackCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     instance->back_callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| View* irda_progress_view_get_view(IrdaProgressView* instance) { | ||||
| View* infrared_progress_view_get_view(InfraredProgressView* instance) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(instance->view); | ||||
|     return instance->view; | ||||
							
								
								
									
										68
									
								
								applications/infrared/view/infrared_progress_view.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								applications/infrared/view/infrared_progress_view.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| /**
 | ||||
|   * @file infrared_progress_view.h | ||||
|   * Infrared: Custom Infrared view module. | ||||
|   * It shows popup progress bar during brute force. | ||||
|   */ | ||||
| #pragma once | ||||
| #include <gui/view.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /** Anonumous instance */ | ||||
| typedef struct InfraredProgressView InfraredProgressView; | ||||
| 
 | ||||
| /** Callback for back button handling */ | ||||
| typedef void (*InfraredProgressViewBackCallback)(void*); | ||||
| 
 | ||||
| /** Allocate and initialize Infrared view
 | ||||
|  * | ||||
|  * @retval new allocated instance | ||||
|  */ | ||||
| InfraredProgressView* infrared_progress_view_alloc(); | ||||
| 
 | ||||
| /** Free previously allocated Progress view module instance
 | ||||
|  * | ||||
|  * @param instance to free | ||||
|  */ | ||||
| void infrared_progress_view_free(InfraredProgressView* instance); | ||||
| 
 | ||||
| /** Get progress view module view
 | ||||
|  * | ||||
|  * @param instance view module | ||||
|  * @retval view | ||||
|  */ | ||||
| View* infrared_progress_view_get_view(InfraredProgressView* instance); | ||||
| 
 | ||||
| /** Increase progress on progress view module
 | ||||
|  * | ||||
|  * @param instance view module | ||||
|  * @retval true - value is incremented and maximum is reached, | ||||
|  *      false - value is incremented and maximum is not reached | ||||
|  */ | ||||
| bool infrared_progress_view_increase_progress(InfraredProgressView* instance); | ||||
| 
 | ||||
| /** Set maximum progress value
 | ||||
|  * | ||||
|  * @param instance - view module | ||||
|  * @param progress_max - maximum value of progress | ||||
|  */ | ||||
| void infrared_progress_view_set_progress_total( | ||||
|     InfraredProgressView* instance, | ||||
|     uint16_t progress_max); | ||||
| 
 | ||||
| /** Set back button callback
 | ||||
|  * | ||||
|  * @param instance - view module | ||||
|  * @param callback - callback to call for back button | ||||
|  * @param context - context to pass to callback | ||||
|  */ | ||||
| void infrared_progress_view_set_back_callback( | ||||
|     InfraredProgressView* instance, | ||||
|     InfraredProgressViewBackCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										140
									
								
								applications/infrared_monitor/infrared_monitor.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								applications/infrared_monitor/infrared_monitor.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,140 @@ | ||||
| #include <gui/canvas.h> | ||||
| #include <input/input.h> | ||||
| #include <infrared.h> | ||||
| #include <infrared_worker.h> | ||||
| #include <stdio.h> | ||||
| #include <furi.h> | ||||
| #include <furi_hal_infrared.h> | ||||
| #include <furi_hal.h> | ||||
| #include <gui/view_port.h> | ||||
| #include <gui/gui.h> | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #define INFRARED_TIMINGS_SIZE 700 | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t timing_cnt; | ||||
|     struct { | ||||
|         uint8_t level; | ||||
|         uint32_t duration; | ||||
|     } timing[INFRARED_TIMINGS_SIZE]; | ||||
| } InfraredDelaysArray; | ||||
| 
 | ||||
| typedef struct { | ||||
|     char display_text[64]; | ||||
|     osMessageQueueId_t event_queue; | ||||
|     InfraredDelaysArray delays; | ||||
|     InfraredWorker* worker; | ||||
|     ViewPort* view_port; | ||||
| } InfraredMonitor; | ||||
| 
 | ||||
| void infrared_monitor_input_callback(InputEvent* input_event, void* ctx) { | ||||
|     furi_assert(ctx); | ||||
|     InfraredMonitor* infrared_monitor = (InfraredMonitor*)ctx; | ||||
| 
 | ||||
|     if((input_event->type == InputTypeShort) && (input_event->key == InputKeyBack)) { | ||||
|         osMessageQueuePut(infrared_monitor->event_queue, input_event, 0, 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void infrared_monitor_draw_callback(Canvas* canvas, void* ctx) { | ||||
|     furi_assert(canvas); | ||||
|     furi_assert(ctx); | ||||
|     InfraredMonitor* infrared_monitor = (InfraredMonitor*)ctx; | ||||
| 
 | ||||
|     canvas_clear(canvas); | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
|     elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, "INFRARED monitor\n"); | ||||
|     canvas_set_font(canvas, FontKeyboard); | ||||
|     if(strlen(infrared_monitor->display_text)) { | ||||
|         elements_multiline_text_aligned( | ||||
|             canvas, 64, 43, AlignCenter, AlignCenter, infrared_monitor->display_text); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { | ||||
|     furi_assert(context); | ||||
|     furi_assert(received_signal); | ||||
|     InfraredMonitor* infrared_monitor = context; | ||||
| 
 | ||||
|     if(infrared_worker_signal_is_decoded(received_signal)) { | ||||
|         const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); | ||||
|         snprintf( | ||||
|             infrared_monitor->display_text, | ||||
|             sizeof(infrared_monitor->display_text), | ||||
|             "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n", | ||||
|             infrared_get_protocol_name(message->protocol), | ||||
|             ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), | ||||
|             message->address, | ||||
|             ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), | ||||
|             message->command, | ||||
|             message->repeat ? " R" : ""); | ||||
|         view_port_update(infrared_monitor->view_port); | ||||
|         printf( | ||||
|             "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n", | ||||
|             infrared_get_protocol_name(message->protocol), | ||||
|             ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), | ||||
|             message->address, | ||||
|             ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), | ||||
|             message->command, | ||||
|             message->repeat ? " R" : ""); | ||||
|     } else { | ||||
|         const uint32_t* timings; | ||||
|         size_t timings_cnt; | ||||
|         infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); | ||||
|         snprintf( | ||||
|             infrared_monitor->display_text, | ||||
|             sizeof(infrared_monitor->display_text), | ||||
|             "RAW\n%d samples\n", | ||||
|             timings_cnt); | ||||
|         view_port_update(infrared_monitor->view_port); | ||||
|         printf("RAW, %d samples:\r\n", timings_cnt); | ||||
|         for(size_t i = 0; i < timings_cnt; ++i) { | ||||
|             printf("%lu ", timings[i]); | ||||
|         } | ||||
|         printf("\r\n"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int32_t infrared_monitor_app(void* p) { | ||||
|     (void)p; | ||||
| 
 | ||||
|     InfraredMonitor* infrared_monitor = malloc(sizeof(InfraredMonitor)); | ||||
|     infrared_monitor->display_text[0] = 0; | ||||
|     infrared_monitor->event_queue = osMessageQueueNew(1, sizeof(InputEvent), NULL); | ||||
|     infrared_monitor->view_port = view_port_alloc(); | ||||
|     Gui* gui = furi_record_open("gui"); | ||||
| 
 | ||||
|     view_port_draw_callback_set( | ||||
|         infrared_monitor->view_port, infrared_monitor_draw_callback, infrared_monitor); | ||||
|     view_port_input_callback_set( | ||||
|         infrared_monitor->view_port, infrared_monitor_input_callback, infrared_monitor); | ||||
| 
 | ||||
|     gui_add_view_port(gui, infrared_monitor->view_port, GuiLayerFullscreen); | ||||
| 
 | ||||
|     infrared_monitor->worker = infrared_worker_alloc(); | ||||
|     infrared_worker_rx_start(infrared_monitor->worker); | ||||
|     infrared_worker_rx_set_received_signal_callback( | ||||
|         infrared_monitor->worker, signal_received_callback, infrared_monitor); | ||||
|     infrared_worker_rx_enable_blink_on_receiving(infrared_monitor->worker, true); | ||||
| 
 | ||||
|     while(1) { | ||||
|         InputEvent event; | ||||
|         if(osOK == osMessageQueueGet(infrared_monitor->event_queue, &event, NULL, 50)) { | ||||
|             if((event.type == InputTypeShort) && (event.key == InputKeyBack)) { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     infrared_worker_rx_stop(infrared_monitor->worker); | ||||
|     infrared_worker_free(infrared_monitor->worker); | ||||
|     osMessageQueueDelete(infrared_monitor->event_queue); | ||||
|     view_port_enabled_set(infrared_monitor->view_port, false); | ||||
|     gui_remove_view_port(gui, infrared_monitor->view_port); | ||||
|     view_port_free(infrared_monitor->view_port); | ||||
|     furi_record_close("gui"); | ||||
|     free(infrared_monitor); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| @ -1,13 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "../irda_app_signal.h" | ||||
| #include <flipper_format/flipper_format.h> | ||||
| #include <string> | ||||
| 
 | ||||
| bool irda_parser_save_signal( | ||||
|     FlipperFormat* ff, | ||||
|     const IrdaAppSignal& signal, | ||||
|     const std::string& name); | ||||
| bool irda_parser_read_signal(FlipperFormat* ff, IrdaAppSignal& signal, std::string& name); | ||||
| bool irda_parser_is_parsed_signal_valid(const IrdaMessage* signal); | ||||
| bool irda_parser_is_raw_signal_valid(uint32_t frequency, float duty_cycle, uint32_t timings_cnt); | ||||
| @ -1,138 +0,0 @@ | ||||
| #pragma once | ||||
| #include <map> | ||||
| #include <irda.h> | ||||
| #include <furi.h> | ||||
| #include "scene/irda_app_scene.h" | ||||
| #include "scene/irda_app_scene.h" | ||||
| #include "irda_app_view_manager.h" | ||||
| #include "irda_app_remote_manager.h" | ||||
| #include <forward_list> | ||||
| #include <stdint.h> | ||||
| #include <notification/notification_messages.h> | ||||
| #include <irda_worker.h> | ||||
| #include "irda_app_view_manager.h" | ||||
| 
 | ||||
| class IrdaApp { | ||||
| public: | ||||
|     enum class EditElement : uint8_t { | ||||
|         Button, | ||||
|         Remote, | ||||
|     }; | ||||
|     enum class EditAction : uint8_t { | ||||
|         Rename, | ||||
|         Delete, | ||||
|     }; | ||||
|     enum class Scene : uint8_t { | ||||
|         Exit, | ||||
|         Start, | ||||
|         Universal, | ||||
|         UniversalTV, | ||||
|         UniversalAudio, | ||||
|         UniversalAirConditioner, | ||||
|         Learn, | ||||
|         LearnSuccess, | ||||
|         LearnEnterName, | ||||
|         LearnDone, | ||||
|         AskBack, | ||||
|         Remote, | ||||
|         RemoteList, | ||||
|         Edit, | ||||
|         EditKeySelect, | ||||
|         EditRename, | ||||
|         EditDelete, | ||||
|         EditRenameDone, | ||||
|         EditDeleteDone, | ||||
|     }; | ||||
| 
 | ||||
|     int32_t run(void* args); | ||||
|     void switch_to_next_scene(Scene index); | ||||
|     void switch_to_next_scene_without_saving(Scene index); | ||||
|     bool switch_to_previous_scene(uint8_t count = 1); | ||||
|     Scene get_previous_scene(); | ||||
|     IrdaAppViewManager* get_view_manager(); | ||||
|     void set_text_store(uint8_t index, const char* text...); | ||||
|     char* get_text_store(uint8_t index); | ||||
|     uint8_t get_text_store_size(); | ||||
|     IrdaAppRemoteManager* get_remote_manager(); | ||||
| 
 | ||||
|     IrdaWorker* get_irda_worker(); | ||||
|     const IrdaAppSignal& get_received_signal() const; | ||||
|     void set_received_signal(const IrdaAppSignal& signal); | ||||
| 
 | ||||
|     void search_and_switch_to_previous_scene(const std::initializer_list<Scene>& scenes_list); | ||||
| 
 | ||||
|     void set_edit_element(EditElement value); | ||||
|     EditElement get_edit_element(void); | ||||
| 
 | ||||
|     void set_edit_action(EditAction value); | ||||
|     EditAction get_edit_action(void); | ||||
| 
 | ||||
|     bool get_learn_new_remote(); | ||||
|     void set_learn_new_remote(bool value); | ||||
| 
 | ||||
|     enum : int { | ||||
|         ButtonNA = -1, | ||||
|     }; | ||||
|     int get_current_button(); | ||||
|     void set_current_button(int value); | ||||
| 
 | ||||
|     void notify_success(); | ||||
|     void notify_red_blink(); | ||||
|     void notify_sent_just_learnt(); | ||||
|     void notify_green_on(); | ||||
|     void notify_green_off(); | ||||
|     void notify_click(); | ||||
|     void notify_click_and_green_blink(); | ||||
|     void notify_blink_green(); | ||||
| 
 | ||||
|     static void text_input_callback(void* context); | ||||
|     static void popup_callback(void* context); | ||||
|     static void signal_sent_callback(void* context); | ||||
| 
 | ||||
|     IrdaApp(); | ||||
|     ~IrdaApp(); | ||||
| 
 | ||||
|     static constexpr const char* irda_directory = "/any/irda"; | ||||
|     static constexpr const char* irda_extension = ".ir"; | ||||
|     static constexpr const uint32_t max_raw_timings_in_signal = 512; | ||||
|     static constexpr const uint32_t max_line_length = | ||||
|         (9 + 1) * IrdaApp::max_raw_timings_in_signal + 100; | ||||
| 
 | ||||
| private: | ||||
|     static constexpr const uint8_t text_store_size = 128; | ||||
|     static constexpr const uint8_t text_store_max = 2; | ||||
|     char text_store[text_store_max][text_store_size + 1]; | ||||
|     bool learn_new_remote; | ||||
|     EditElement element; | ||||
|     EditAction action; | ||||
|     uint32_t current_button; | ||||
| 
 | ||||
|     NotificationApp* notification; | ||||
|     IrdaAppViewManager view_manager; | ||||
|     IrdaAppRemoteManager remote_manager; | ||||
|     IrdaWorker* irda_worker; | ||||
|     IrdaAppSignal received_signal; | ||||
| 
 | ||||
|     std::forward_list<Scene> previous_scenes_list; | ||||
|     Scene current_scene = Scene::Start; | ||||
| 
 | ||||
|     std::map<Scene, IrdaAppScene*> scenes = { | ||||
|         {Scene::Start, new IrdaAppSceneStart()}, | ||||
|         {Scene::Universal, new IrdaAppSceneUniversal()}, | ||||
|         {Scene::UniversalTV, new IrdaAppSceneUniversalTV()}, | ||||
|         //        {Scene::UniversalAudio, new IrdaAppSceneUniversalAudio()},
 | ||||
|         {Scene::Learn, new IrdaAppSceneLearn()}, | ||||
|         {Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()}, | ||||
|         {Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()}, | ||||
|         {Scene::LearnDone, new IrdaAppSceneLearnDone()}, | ||||
|         {Scene::AskBack, new IrdaAppSceneAskBack()}, | ||||
|         {Scene::Remote, new IrdaAppSceneRemote()}, | ||||
|         {Scene::RemoteList, new IrdaAppSceneRemoteList()}, | ||||
|         {Scene::Edit, new IrdaAppSceneEdit()}, | ||||
|         {Scene::EditKeySelect, new IrdaAppSceneEditKeySelect()}, | ||||
|         {Scene::EditRename, new IrdaAppSceneEditRename()}, | ||||
|         {Scene::EditDelete, new IrdaAppSceneEditDelete()}, | ||||
|         {Scene::EditRenameDone, new IrdaAppSceneEditRenameDone()}, | ||||
|         {Scene::EditDeleteDone, new IrdaAppSceneEditDeleteDone()}, | ||||
|     }; | ||||
| }; | ||||
| @ -1,36 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <unordered_map> | ||||
| #include <memory> | ||||
| #include <flipper_format/flipper_format.h> | ||||
| 
 | ||||
| class IrdaAppBruteForce { | ||||
|     const char* universal_db_filename; | ||||
|     std::string current_record; | ||||
|     FlipperFormat* ff; | ||||
| 
 | ||||
|     typedef struct { | ||||
|         int index; | ||||
|         int amount; | ||||
|     } Record; | ||||
| 
 | ||||
|     // 'key' is record name, because we have to search by both, index and name,
 | ||||
|     // but index search has place once per button press, and should not be
 | ||||
|     // noticed, but name search should occur during entering universal menu,
 | ||||
|     // and will go through container for every record in file, that's why
 | ||||
|     // more critical to have faster search by record name.
 | ||||
|     std::unordered_map<std::string, Record> records; | ||||
| 
 | ||||
| public: | ||||
|     bool calculate_messages(); | ||||
|     void stop_bruteforce(); | ||||
|     bool send_next_bruteforce(); | ||||
|     bool start_bruteforce(int index, int& record_amount); | ||||
|     void add_record(int index, const char* name); | ||||
| 
 | ||||
|     IrdaAppBruteForce(const char* filename) | ||||
|         : universal_db_filename(filename) { | ||||
|     } | ||||
|     ~IrdaAppBruteForce() { | ||||
|     } | ||||
| }; | ||||
| @ -1,28 +0,0 @@ | ||||
| #pragma once | ||||
| #include <irda.h> | ||||
| #include <gui/modules/dialog_ex.h> | ||||
| 
 | ||||
| class IrdaAppEvent { | ||||
| public: | ||||
|     enum class Type : uint8_t { | ||||
|         Tick, | ||||
|         Exit, | ||||
|         Back, | ||||
|         MenuSelected, | ||||
|         MenuSelectedPress, | ||||
|         MenuSelectedRelease, | ||||
|         DialogExSelected, | ||||
|         NextScene, | ||||
|         IrdaMessageReceived, | ||||
|         TextEditDone, | ||||
|         PopupTimer, | ||||
|         ButtonPanelPressed, | ||||
|     }; | ||||
| 
 | ||||
|     union { | ||||
|         int32_t menu_index; | ||||
|         DialogExResult dialog_ex_result; | ||||
|     } payload; | ||||
| 
 | ||||
|     Type type; | ||||
| }; | ||||
| @ -1,76 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "irda_app_signal.h" | ||||
| 
 | ||||
| #include <irda_worker.h> | ||||
| #include <irda.h> | ||||
| 
 | ||||
| #include <cstdint> | ||||
| #include <string> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| 
 | ||||
| class IrdaAppRemoteButton { | ||||
|     friend class IrdaAppRemoteManager; | ||||
|     std::string name; | ||||
|     IrdaAppSignal signal; | ||||
| 
 | ||||
| public: | ||||
|     IrdaAppRemoteButton(const char* name, const IrdaAppSignal& signal) | ||||
|         : name(name) | ||||
|         , signal(signal) { | ||||
|     } | ||||
| 
 | ||||
|     IrdaAppRemoteButton(const char* name, IrdaAppSignal&& signal) | ||||
|         : name(name) | ||||
|         , signal(std::move(signal)) { | ||||
|     } | ||||
|     ~IrdaAppRemoteButton() { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppRemote { | ||||
|     friend class IrdaAppRemoteManager; | ||||
|     std::vector<IrdaAppRemoteButton> buttons; | ||||
|     std::string name; | ||||
| 
 | ||||
| public: | ||||
|     IrdaAppRemote(const std::string& name) | ||||
|         : name(name) { | ||||
|     } | ||||
| 
 | ||||
|     IrdaAppRemote& operator=(std::string& new_name) noexcept { | ||||
|         name = new_name; | ||||
|         buttons.clear(); | ||||
|         return *this; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppRemoteManager { | ||||
|     std::unique_ptr<IrdaAppRemote> remote; | ||||
|     std::string make_full_name(const std::string& remote_name) const; | ||||
|     std::string make_remote_name(const std::string& full_name) const; | ||||
| 
 | ||||
| public: | ||||
|     static constexpr const uint32_t max_button_name_length = 22; | ||||
|     static constexpr const uint32_t max_remote_name_length = 22; | ||||
|     bool add_remote_with_button(const char* button_name, const IrdaAppSignal& signal); | ||||
|     bool add_button(const char* button_name, const IrdaAppSignal& signal); | ||||
| 
 | ||||
|     int find_remote_name(const std::vector<std::string>& strings); | ||||
|     bool rename_button(uint32_t index, const char* str); | ||||
|     bool rename_remote(const char* str); | ||||
|     std::string find_vacant_remote_name(const std::string& name); | ||||
| 
 | ||||
|     std::vector<std::string> get_button_list() const; | ||||
|     std::string get_button_name(uint32_t index); | ||||
|     std::string get_remote_name(); | ||||
|     size_t get_number_of_buttons(); | ||||
|     const IrdaAppSignal& get_button_data(size_t index) const; | ||||
|     bool delete_button(uint32_t index); | ||||
|     bool delete_remote(); | ||||
|     void reset_remote(); | ||||
| 
 | ||||
|     bool store(); | ||||
|     bool load(const std::string& name); | ||||
| }; | ||||
| @ -1,68 +0,0 @@ | ||||
| #pragma once | ||||
| #include <irda_worker.h> | ||||
| #include <stdint.h> | ||||
| #include <string> | ||||
| #include <irda.h> | ||||
| 
 | ||||
| class IrdaAppSignal { | ||||
| public: | ||||
|     typedef struct { | ||||
|         size_t timings_cnt; | ||||
|         uint32_t* timings; | ||||
|         uint32_t frequency; | ||||
|         float duty_cycle; | ||||
|     } RawSignal; | ||||
| 
 | ||||
| private: | ||||
|     bool raw_signal; | ||||
|     union { | ||||
|         IrdaMessage message; | ||||
|         RawSignal raw; | ||||
|     } payload; | ||||
| 
 | ||||
|     void | ||||
|         copy_raw_signal(const uint32_t* timings, size_t size, uint32_t frequency, float duty_cycle); | ||||
|     void clear_timings(); | ||||
| 
 | ||||
| public: | ||||
|     IrdaAppSignal() { | ||||
|         raw_signal = false; | ||||
|         payload.message.protocol = IrdaProtocolUnknown; | ||||
|     } | ||||
| 
 | ||||
|     ~IrdaAppSignal() { | ||||
|         clear_timings(); | ||||
|     } | ||||
| 
 | ||||
|     IrdaAppSignal( | ||||
|         const uint32_t* timings, | ||||
|         size_t timings_cnt, | ||||
|         uint32_t frequency, | ||||
|         float duty_cycle); | ||||
|     IrdaAppSignal(const IrdaMessage* irda_message); | ||||
| 
 | ||||
|     IrdaAppSignal(const IrdaAppSignal& other); | ||||
|     IrdaAppSignal(IrdaAppSignal&& other); | ||||
| 
 | ||||
|     IrdaAppSignal& operator=(const IrdaAppSignal& signal); | ||||
| 
 | ||||
|     void set_message(const IrdaMessage* irda_message); | ||||
|     void | ||||
|         set_raw_signal(uint32_t* timings, size_t timings_cnt, uint32_t frequency, float duty_cycle); | ||||
| 
 | ||||
|     void transmit() const; | ||||
| 
 | ||||
|     bool is_raw(void) const { | ||||
|         return raw_signal; | ||||
|     } | ||||
| 
 | ||||
|     const IrdaMessage& get_message(void) const { | ||||
|         furi_assert(!raw_signal); | ||||
|         return payload.message; | ||||
|     } | ||||
| 
 | ||||
|     const RawSignal& get_raw_signal(void) const { | ||||
|         furi_assert(raw_signal); | ||||
|         return payload.raw; | ||||
|     } | ||||
| }; | ||||
| @ -1,66 +0,0 @@ | ||||
| #pragma once | ||||
| #include <gui/modules/button_menu.h> | ||||
| #include <gui/modules/text_input.h> | ||||
| #include <gui/view_stack.h> | ||||
| #include <gui/modules/button_panel.h> | ||||
| #include <furi.h> | ||||
| #include <gui/view_dispatcher.h> | ||||
| #include <gui/modules/dialog_ex.h> | ||||
| #include <gui/modules/submenu.h> | ||||
| #include <gui/modules/popup.h> | ||||
| #include <gui/modules/loading.h> | ||||
| 
 | ||||
| #include "irda_app_event.h" | ||||
| #include "view/irda_progress_view.h" | ||||
| 
 | ||||
| class IrdaAppViewManager { | ||||
| public: | ||||
|     enum class ViewType : uint8_t { | ||||
|         DialogEx, | ||||
|         TextInput, | ||||
|         Submenu, | ||||
|         ButtonMenu, | ||||
|         UniversalRemote, | ||||
|         Popup, | ||||
|     }; | ||||
| 
 | ||||
|     IrdaAppViewManager(); | ||||
|     ~IrdaAppViewManager(); | ||||
| 
 | ||||
|     void switch_to(ViewType type); | ||||
| 
 | ||||
|     void receive_event(IrdaAppEvent* event); | ||||
|     void send_event(IrdaAppEvent* event); | ||||
|     void clear_events(); | ||||
| 
 | ||||
|     DialogEx* get_dialog_ex(); | ||||
|     Submenu* get_submenu(); | ||||
|     Popup* get_popup(); | ||||
|     TextInput* get_text_input(); | ||||
|     ButtonMenu* get_button_menu(); | ||||
|     ButtonPanel* get_button_panel(); | ||||
|     ViewStack* get_universal_view_stack(); | ||||
|     IrdaProgressView* get_progress(); | ||||
|     Loading* get_loading(); | ||||
| 
 | ||||
|     osMessageQueueId_t get_event_queue(); | ||||
| 
 | ||||
|     uint32_t previous_view_callback(void* context); | ||||
| 
 | ||||
| private: | ||||
|     ViewDispatcher* view_dispatcher; | ||||
|     Gui* gui; | ||||
|     TextInput* text_input; | ||||
|     DialogEx* dialog_ex; | ||||
|     Submenu* submenu; | ||||
|     Popup* popup; | ||||
|     ButtonMenu* button_menu; | ||||
|     ButtonPanel* button_panel; | ||||
|     ViewStack* universal_view_stack; | ||||
|     IrdaProgressView* progress_view; | ||||
|     Loading* loading_view; | ||||
| 
 | ||||
|     osMessageQueueId_t event_queue; | ||||
| 
 | ||||
|     void add_view(ViewType view_type, View* view); | ||||
| }; | ||||
| @ -1,9 +0,0 @@ | ||||
| #include "irda_app.h" | ||||
| 
 | ||||
| extern "C" int32_t irda_app(void* p) { | ||||
|     IrdaApp* app = new IrdaApp(); | ||||
|     int32_t result = app->run(p); | ||||
|     delete app; | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| @ -1,184 +0,0 @@ | ||||
| #pragma once | ||||
| #include "../irda_app_event.h" | ||||
| #include <furi_hal_irda.h> | ||||
| #include "irda.h" | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include "../irda_app_brute_force.h" | ||||
| 
 | ||||
| class IrdaApp; | ||||
| 
 | ||||
| class IrdaAppScene { | ||||
| public: | ||||
|     virtual void on_enter(IrdaApp* app) = 0; | ||||
|     virtual bool on_event(IrdaApp* app, IrdaAppEvent* event) = 0; | ||||
|     virtual void on_exit(IrdaApp* app) = 0; | ||||
|     virtual ~IrdaAppScene(){}; | ||||
| 
 | ||||
| private: | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneStart : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneUniversal : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneLearn : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneLearnSuccess : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
|     bool button_pressed = false; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneLearnEnterName : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneLearnDone : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneRemote : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     std::vector<std::string> buttons_names; | ||||
|     uint32_t buttonmenu_item_selected = 0; | ||||
|     bool button_pressed = false; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneRemoteList : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     uint32_t submenu_item_selected = 0; | ||||
|     std::vector<std::string> remote_names; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneAskBack : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneEdit : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     uint32_t submenu_item_selected = 0; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneEditKeySelect : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| 
 | ||||
| private: | ||||
|     std::vector<std::string> buttons_names; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneEditRename : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneEditDelete : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneEditRenameDone : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneEditDeleteDone : public IrdaAppScene { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneUniversalCommon : public IrdaAppScene { | ||||
|     bool brute_force_started = false; | ||||
| 
 | ||||
| protected: | ||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||
|     void on_exit(IrdaApp* app) final; | ||||
|     IrdaAppBruteForce brute_force; | ||||
|     void remove_popup(IrdaApp* app); | ||||
|     void show_popup(IrdaApp* app, int record_amount); | ||||
|     bool progress_popup(IrdaApp* app); | ||||
|     static void irda_app_item_callback(void* context, uint32_t index); | ||||
|     IrdaAppSceneUniversalCommon(const char* filename) | ||||
|         : brute_force(filename) { | ||||
|     } | ||||
|     ~IrdaAppSceneUniversalCommon() { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneUniversalTV : public IrdaAppSceneUniversalCommon { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     IrdaAppSceneUniversalTV() | ||||
|         : IrdaAppSceneUniversalCommon("/ext/irda/assets/tv.ir") { | ||||
|     } | ||||
|     ~IrdaAppSceneUniversalTV() { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class IrdaAppSceneUniversalAudio : public IrdaAppSceneUniversalCommon { | ||||
| public: | ||||
|     void on_enter(IrdaApp* app) final; | ||||
|     IrdaAppSceneUniversalAudio() | ||||
|         : IrdaAppSceneUniversalCommon("/ext/irda/assets/audio.ir") { | ||||
|     } | ||||
|     ~IrdaAppSceneUniversalAudio() { | ||||
|     } | ||||
| }; | ||||
| @ -1,38 +0,0 @@ | ||||
| #include "../irda_app.h" | ||||
| 
 | ||||
| void IrdaAppSceneEditDeleteDone::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
| 
 | ||||
|     popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); | ||||
|     popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); | ||||
| 
 | ||||
|     popup_set_callback(popup, IrdaApp::popup_callback); | ||||
|     popup_set_context(popup, app); | ||||
|     popup_set_timeout(popup, 1500); | ||||
|     popup_enable_timeout(popup); | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneEditDeleteDone::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::PopupTimer) { | ||||
|         if(app->get_edit_element() == IrdaApp::EditElement::Remote) { | ||||
|             app->search_and_switch_to_previous_scene( | ||||
|                 {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); | ||||
|         } else { | ||||
|             app->search_and_switch_to_previous_scene({IrdaApp::Scene::Remote}); | ||||
|         } | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneEditDeleteDone::on_exit(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
|     popup_set_header(popup, nullptr, 0, 0, AlignLeft, AlignTop); | ||||
| } | ||||
| @ -1,57 +0,0 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "gui/modules/submenu.h" | ||||
| 
 | ||||
| static void submenu_callback(void* context, uint32_t index) { | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
| 
 | ||||
|     event.type = IrdaAppEvent::Type::MenuSelected; | ||||
|     event.payload.menu_index = index; | ||||
| 
 | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneEditKeySelect::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
|     int item_number = 0; | ||||
| 
 | ||||
|     const char* header = app->get_edit_action() == IrdaApp::EditAction::Rename ? "Rename key:" : | ||||
|                                                                                  "Delete key:"; | ||||
|     submenu_set_header(submenu, header); | ||||
| 
 | ||||
|     auto remote_manager = app->get_remote_manager(); | ||||
|     buttons_names = remote_manager->get_button_list(); | ||||
|     for(const auto& it : buttons_names) { | ||||
|         submenu_add_item(submenu, it.c_str(), item_number++, submenu_callback, app); | ||||
|     } | ||||
|     if((item_number > 0) && (app->get_current_button() != IrdaApp::ButtonNA)) { | ||||
|         submenu_set_selected_item(submenu, app->get_current_button()); | ||||
|         app->set_current_button(IrdaApp::ButtonNA); | ||||
|     } | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneEditKeySelect::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||
|         app->set_current_button(event->payload.menu_index); | ||||
|         consumed = true; | ||||
|         if(app->get_edit_action() == IrdaApp::EditAction::Rename) { | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::EditRename); | ||||
|         } else { | ||||
|             app->switch_to_next_scene(IrdaApp::Scene::EditDelete); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneEditKeySelect::on_exit(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
| 
 | ||||
|     submenu_reset(submenu); | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| #include "../irda_app.h" | ||||
| 
 | ||||
| void IrdaAppSceneEditRenameDone::on_enter(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Popup* popup = view_manager->get_popup(); | ||||
| 
 | ||||
|     popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); | ||||
| 
 | ||||
|     popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); | ||||
| 
 | ||||
|     popup_set_callback(popup, IrdaApp::popup_callback); | ||||
|     popup_set_context(popup, app); | ||||
|     popup_set_timeout(popup, 1500); | ||||
|     popup_enable_timeout(popup); | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneEditRenameDone::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event->type == IrdaAppEvent::Type::PopupTimer) { | ||||
|         app->switch_to_next_scene(IrdaApp::Scene::Remote); | ||||
|         consumed = true; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneEditRenameDone::on_exit(IrdaApp* app) { | ||||
| } | ||||
| @ -1,75 +0,0 @@ | ||||
| #include "../irda_app.h" | ||||
| #include "../irda_app_event.h" | ||||
| #include "irda.h" | ||||
| #include <irda_worker.h> | ||||
| 
 | ||||
| static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { | ||||
|     furi_assert(context); | ||||
|     furi_assert(received_signal); | ||||
| 
 | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
| 
 | ||||
|     if(irda_worker_signal_is_decoded(received_signal)) { | ||||
|         IrdaAppSignal signal(irda_worker_get_decoded_signal(received_signal)); | ||||
|         app->set_received_signal(signal); | ||||
|     } else { | ||||
|         const uint32_t* timings; | ||||
|         size_t timings_cnt; | ||||
|         irda_worker_get_raw_signal(received_signal, &timings, &timings_cnt); | ||||
|         IrdaAppSignal signal( | ||||
|             timings, timings_cnt, IRDA_COMMON_CARRIER_FREQUENCY, IRDA_COMMON_DUTY_CYCLE); | ||||
|         app->set_received_signal(signal); | ||||
|     } | ||||
| 
 | ||||
|     irda_worker_rx_set_received_signal_callback(app->get_irda_worker(), NULL, NULL); | ||||
|     IrdaAppEvent event; | ||||
|     event.type = IrdaAppEvent::Type::IrdaMessageReceived; | ||||
|     auto view_manager = app->get_view_manager(); | ||||
|     view_manager->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneLearn::on_enter(IrdaApp* app) { | ||||
|     auto view_manager = app->get_view_manager(); | ||||
|     auto popup = view_manager->get_popup(); | ||||
| 
 | ||||
|     auto worker = app->get_irda_worker(); | ||||
|     irda_worker_rx_set_received_signal_callback(worker, signal_received_callback, app); | ||||
|     irda_worker_rx_start(worker); | ||||
| 
 | ||||
|     popup_set_icon(popup, 0, 32, &I_IrdaLearnShort_128x31); | ||||
|     popup_set_text( | ||||
|         popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); | ||||
|     popup_set_callback(popup, NULL); | ||||
| 
 | ||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     switch(event->type) { | ||||
|     case IrdaAppEvent::Type::Tick: | ||||
|         consumed = true; | ||||
|         app->notify_red_blink(); | ||||
|         break; | ||||
|     case IrdaAppEvent::Type::IrdaMessageReceived: | ||||
|         app->notify_success(); | ||||
|         app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); | ||||
|         break; | ||||
|     case IrdaAppEvent::Type::Back: | ||||
|         consumed = true; | ||||
|         app->switch_to_previous_scene(); | ||||
|         break; | ||||
|     default: | ||||
|         furi_assert(0); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneLearn::on_exit(IrdaApp* app) { | ||||
|     irda_worker_rx_stop(app->get_irda_worker()); | ||||
|     auto view_manager = app->get_view_manager(); | ||||
|     auto popup = view_manager->get_popup(); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter); | ||||
| } | ||||
| @ -1,101 +0,0 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| #include <gui/modules/button_menu.h> | ||||
| #include <gui/modules/button_panel.h> | ||||
| #include <gui/view.h> | ||||
| #include <gui/view_stack.h> | ||||
| 
 | ||||
| #include "../irda_app.h" | ||||
| #include "irda/irda_app_event.h" | ||||
| #include "irda/irda_app_view_manager.h" | ||||
| #include "irda/scene/irda_app_scene.h" | ||||
| #include "../view/irda_progress_view.h" | ||||
| 
 | ||||
| void IrdaAppSceneUniversalCommon::irda_app_item_callback(void* context, uint32_t index) { | ||||
|     IrdaApp* app = static_cast<IrdaApp*>(context); | ||||
|     IrdaAppEvent event; | ||||
| 
 | ||||
|     event.type = IrdaAppEvent::Type::ButtonPanelPressed; | ||||
|     event.payload.menu_index = index; | ||||
| 
 | ||||
|     app->get_view_manager()->send_event(&event); | ||||
| } | ||||
| 
 | ||||
| static void irda_progress_back_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     auto app = static_cast<IrdaApp*>(context); | ||||
| 
 | ||||
|     IrdaAppEvent irda_event = { | ||||
|         .type = IrdaAppEvent::Type::Back, | ||||
|     }; | ||||
|     app->get_view_manager()->clear_events(); | ||||
|     app->get_view_manager()->send_event(&irda_event); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneUniversalCommon::remove_popup(IrdaApp* app) { | ||||
|     auto stack_view = app->get_view_manager()->get_universal_view_stack(); | ||||
|     auto progress_view = app->get_view_manager()->get_progress(); | ||||
|     view_stack_remove_view(stack_view, irda_progress_view_get_view(progress_view)); | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneUniversalCommon::show_popup(IrdaApp* app, int record_amount) { | ||||
|     auto stack_view = app->get_view_manager()->get_universal_view_stack(); | ||||
|     auto progress_view = app->get_view_manager()->get_progress(); | ||||
|     irda_progress_view_set_progress_total(progress_view, record_amount); | ||||
|     irda_progress_view_set_back_callback(progress_view, irda_progress_back_callback, app); | ||||
|     view_stack_add_view(stack_view, irda_progress_view_get_view(progress_view)); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneUniversalCommon::progress_popup(IrdaApp* app) { | ||||
|     auto progress_view = app->get_view_manager()->get_progress(); | ||||
|     return irda_progress_view_increase_progress(progress_view); | ||||
| } | ||||
| 
 | ||||
| bool IrdaAppSceneUniversalCommon::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(brute_force_started) { | ||||
|         if(event->type == IrdaAppEvent::Type::Tick) { | ||||
|             auto view_manager = app->get_view_manager(); | ||||
|             IrdaAppEvent tick_event = {.type = IrdaAppEvent::Type::Tick}; | ||||
|             view_manager->send_event(&tick_event); | ||||
|             bool result = brute_force.send_next_bruteforce(); | ||||
|             if(result) { | ||||
|                 result = progress_popup(app); | ||||
|             } | ||||
|             if(!result) { | ||||
|                 brute_force.stop_bruteforce(); | ||||
|                 brute_force_started = false; | ||||
|                 remove_popup(app); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } else if(event->type == IrdaAppEvent::Type::Back) { | ||||
|             brute_force_started = false; | ||||
|             brute_force.stop_bruteforce(); | ||||
|             remove_popup(app); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else { | ||||
|         if(event->type == IrdaAppEvent::Type::ButtonPanelPressed) { | ||||
|             int record_amount = 0; | ||||
|             if(brute_force.start_bruteforce(event->payload.menu_index, record_amount)) { | ||||
|                 DOLPHIN_DEED(DolphinDeedIrBruteForce); | ||||
|                 brute_force_started = true; | ||||
|                 show_popup(app, record_amount); | ||||
|             } else { | ||||
|                 app->switch_to_previous_scene(); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } else if(event->type == IrdaAppEvent::Type::Back) { | ||||
|             app->switch_to_previous_scene(); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void IrdaAppSceneUniversalCommon::on_exit(IrdaApp* app) { | ||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||
|     ButtonPanel* button_panel = view_manager->get_button_panel(); | ||||
|     button_panel_reset(button_panel); | ||||
| } | ||||
| @ -1,25 +0,0 @@ | ||||
| #pragma once | ||||
| #include <gui/view.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| typedef struct IrdaProgressView IrdaProgressView; | ||||
| 
 | ||||
| typedef void (*IrdaProgressViewBackCallback)(void*); | ||||
| 
 | ||||
| IrdaProgressView* irda_progress_view_alloc(); | ||||
| void irda_progress_view_free(IrdaProgressView* progress); | ||||
| View* irda_progress_view_get_view(IrdaProgressView* progress); | ||||
| 
 | ||||
| bool irda_progress_view_increase_progress(IrdaProgressView* progress); | ||||
| void irda_progress_view_set_progress_total(IrdaProgressView* progress, uint16_t progress_max); | ||||
| void irda_progress_view_set_back_callback( | ||||
|     IrdaProgressView* instance, | ||||
|     IrdaProgressViewBackCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -1,139 +0,0 @@ | ||||
| #include <gui/canvas.h> | ||||
| #include <input/input.h> | ||||
| #include <irda.h> | ||||
| #include <irda_worker.h> | ||||
| #include <stdio.h> | ||||
| #include <furi.h> | ||||
| #include <furi_hal_irda.h> | ||||
| #include <furi_hal.h> | ||||
| #include <gui/view_port.h> | ||||
| #include <gui/gui.h> | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #define IRDA_TIMINGS_SIZE 700 | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t timing_cnt; | ||||
|     struct { | ||||
|         uint8_t level; | ||||
|         uint32_t duration; | ||||
|     } timing[IRDA_TIMINGS_SIZE]; | ||||
| } IrdaDelaysArray; | ||||
| 
 | ||||
| typedef struct { | ||||
|     char display_text[64]; | ||||
|     osMessageQueueId_t event_queue; | ||||
|     IrdaDelaysArray delays; | ||||
|     IrdaWorker* worker; | ||||
|     ViewPort* view_port; | ||||
| } IrdaMonitor; | ||||
| 
 | ||||
| void irda_monitor_input_callback(InputEvent* input_event, void* ctx) { | ||||
|     furi_assert(ctx); | ||||
|     IrdaMonitor* irda_monitor = (IrdaMonitor*)ctx; | ||||
| 
 | ||||
|     if((input_event->type == InputTypeShort) && (input_event->key == InputKeyBack)) { | ||||
|         osMessageQueuePut(irda_monitor->event_queue, input_event, 0, 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void irda_monitor_draw_callback(Canvas* canvas, void* ctx) { | ||||
|     furi_assert(canvas); | ||||
|     furi_assert(ctx); | ||||
|     IrdaMonitor* irda_monitor = (IrdaMonitor*)ctx; | ||||
| 
 | ||||
|     canvas_clear(canvas); | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
|     elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, "IRDA monitor\n"); | ||||
|     canvas_set_font(canvas, FontKeyboard); | ||||
|     if(strlen(irda_monitor->display_text)) { | ||||
|         elements_multiline_text_aligned( | ||||
|             canvas, 64, 43, AlignCenter, AlignCenter, irda_monitor->display_text); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { | ||||
|     furi_assert(context); | ||||
|     furi_assert(received_signal); | ||||
|     IrdaMonitor* irda_monitor = context; | ||||
| 
 | ||||
|     if(irda_worker_signal_is_decoded(received_signal)) { | ||||
|         const IrdaMessage* message = irda_worker_get_decoded_signal(received_signal); | ||||
|         snprintf( | ||||
|             irda_monitor->display_text, | ||||
|             sizeof(irda_monitor->display_text), | ||||
|             "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n", | ||||
|             irda_get_protocol_name(message->protocol), | ||||
|             ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4), | ||||
|             message->address, | ||||
|             ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||
|             message->command, | ||||
|             message->repeat ? " R" : ""); | ||||
|         view_port_update(irda_monitor->view_port); | ||||
|         printf( | ||||
|             "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n", | ||||
|             irda_get_protocol_name(message->protocol), | ||||
|             ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4), | ||||
|             message->address, | ||||
|             ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4), | ||||
|             message->command, | ||||
|             message->repeat ? " R" : ""); | ||||
|     } else { | ||||
|         const uint32_t* timings; | ||||
|         size_t timings_cnt; | ||||
|         irda_worker_get_raw_signal(received_signal, &timings, &timings_cnt); | ||||
|         snprintf( | ||||
|             irda_monitor->display_text, | ||||
|             sizeof(irda_monitor->display_text), | ||||
|             "RAW\n%d samples\n", | ||||
|             timings_cnt); | ||||
|         view_port_update(irda_monitor->view_port); | ||||
|         printf("RAW, %d samples:\r\n", timings_cnt); | ||||
|         for(size_t i = 0; i < timings_cnt; ++i) { | ||||
|             printf("%lu ", timings[i]); | ||||
|         } | ||||
|         printf("\r\n"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int32_t irda_monitor_app(void* p) { | ||||
|     (void)p; | ||||
| 
 | ||||
|     IrdaMonitor* irda_monitor = malloc(sizeof(IrdaMonitor)); | ||||
|     irda_monitor->display_text[0] = 0; | ||||
|     irda_monitor->event_queue = osMessageQueueNew(1, sizeof(InputEvent), NULL); | ||||
|     irda_monitor->view_port = view_port_alloc(); | ||||
|     Gui* gui = furi_record_open("gui"); | ||||
| 
 | ||||
|     view_port_draw_callback_set(irda_monitor->view_port, irda_monitor_draw_callback, irda_monitor); | ||||
|     view_port_input_callback_set( | ||||
|         irda_monitor->view_port, irda_monitor_input_callback, irda_monitor); | ||||
| 
 | ||||
|     gui_add_view_port(gui, irda_monitor->view_port, GuiLayerFullscreen); | ||||
| 
 | ||||
|     irda_monitor->worker = irda_worker_alloc(); | ||||
|     irda_worker_rx_start(irda_monitor->worker); | ||||
|     irda_worker_rx_set_received_signal_callback( | ||||
|         irda_monitor->worker, signal_received_callback, irda_monitor); | ||||
|     irda_worker_rx_enable_blink_on_receiving(irda_monitor->worker, true); | ||||
| 
 | ||||
|     while(1) { | ||||
|         InputEvent event; | ||||
|         if(osOK == osMessageQueueGet(irda_monitor->event_queue, &event, NULL, 50)) { | ||||
|             if((event.type == InputTypeShort) && (event.key == InputKeyBack)) { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     irda_worker_rx_stop(irda_monitor->worker); | ||||
|     irda_worker_free(irda_monitor->worker); | ||||
|     osMessageQueueDelete(irda_monitor->event_queue); | ||||
|     view_port_enabled_set(irda_monitor->view_port, false); | ||||
|     gui_remove_view_port(gui, irda_monitor->view_port); | ||||
|     view_port_free(irda_monitor->view_port); | ||||
|     furi_record_close("gui"); | ||||
|     free(irda_monitor); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| @ -11,6 +11,9 @@ static void nfc_cli_print_usage() { | ||||
|     printf("Cmd list:\r\n"); | ||||
|     printf("\tdetect\t - detect nfc device\r\n"); | ||||
|     printf("\temulate\t - emulate predefined nfca card\r\n"); | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         printf("\tfield\t - turn field on\r\n"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_cli_detect(Cli* cli, string_t args) { | ||||
| @ -76,6 +79,27 @@ void nfc_cli_emulate(Cli* cli, string_t args) { | ||||
|     furi_hal_nfc_deactivate(); | ||||
| } | ||||
| 
 | ||||
| void nfc_cli_field(Cli* cli, string_t args) { | ||||
|     // Check if nfc worker is not busy
 | ||||
|     if(furi_hal_nfc_is_busy()) { | ||||
|         printf("Nfc is busy\r\n"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     furi_hal_nfc_exit_sleep(); | ||||
|     furi_hal_nfc_field_on(); | ||||
| 
 | ||||
|     printf("Field is on. Don't leave device in this mode for too long.\r\n"); | ||||
|     printf("Press Ctrl+C to abort\r\n"); | ||||
| 
 | ||||
|     while(!cli_cmd_interrupt_received(cli)) { | ||||
|         osDelay(50); | ||||
|     } | ||||
| 
 | ||||
|     furi_hal_nfc_field_off(); | ||||
|     furi_hal_nfc_deactivate(); | ||||
| } | ||||
| 
 | ||||
| static void nfc_cli(Cli* cli, string_t args, void* context) { | ||||
|     string_t cmd; | ||||
|     string_init(cmd); | ||||
| @ -94,6 +118,13 @@ static void nfc_cli(Cli* cli, string_t args, void* context) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|             if(string_cmp_str(cmd, "field") == 0) { | ||||
|                 nfc_cli_field(cli, args); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         nfc_cli_print_usage(); | ||||
|     } while(false); | ||||
| 
 | ||||
|  | ||||
| @ -27,3 +27,5 @@ ADD_SCENE(nfc, read_emv_data, ReadEmvData) | ||||
| ADD_SCENE(nfc, read_emv_data_success, ReadEmvDataSuccess) | ||||
| ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) | ||||
| ADD_SCENE(nfc, restore_original, RestoreOriginal) | ||||
| ADD_SCENE(nfc, debug, Debug) | ||||
| ADD_SCENE(nfc, field, Field) | ||||
|  | ||||
							
								
								
									
										54
									
								
								applications/nfc/scenes/nfc_scene_debug.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								applications/nfc/scenes/nfc_scene_debug.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| enum SubmenuDebugIndex { | ||||
|     SubmenuDebugIndexField, | ||||
|     SubmenuDebugIndexApdu, | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_debug_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_debug_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     submenu_add_item( | ||||
|         submenu, "Field", SubmenuDebugIndexField, nfc_scene_debug_submenu_callback, nfc); | ||||
|     submenu_add_item( | ||||
|         submenu, "Apdu", SubmenuDebugIndexApdu, nfc_scene_debug_submenu_callback, nfc); | ||||
| 
 | ||||
|     submenu_set_selected_item( | ||||
|         submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDebug)); | ||||
| 
 | ||||
|     nfc_device_clear(nfc->dev); | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuDebugIndexField) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneDebug, SubmenuDebugIndexField); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneField); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuDebugIndexApdu) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneDebug, SubmenuDebugIndexApdu); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateApduSequence); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_debug_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
| @ -32,7 +32,5 @@ void nfc_scene_emulate_apdu_sequence_on_exit(void* context) { | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_reset(popup); | ||||
| } | ||||
|  | ||||
							
								
								
									
										34
									
								
								applications/nfc/scenes/nfc_scene_field.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								applications/nfc/scenes/nfc_scene_field.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_field_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     furi_hal_nfc_field_on(); | ||||
| 
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header( | ||||
|         popup, | ||||
|         "Field is on\nDon't leave device\nin this mode for too long.", | ||||
|         64, | ||||
|         11, | ||||
|         AlignCenter, | ||||
|         AlignTop); | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||
| 
 | ||||
|     notification_internal_message(nfc->notifications, &sequence_set_blue_255); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_field_on_event(void* context, SceneManagerEvent event) { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_field_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     notification_internal_message(nfc->notifications, &sequence_reset_blue); | ||||
| 
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_reset(popup); | ||||
| 
 | ||||
|     furi_hal_nfc_field_off(); | ||||
| } | ||||
| @ -17,7 +17,7 @@ void nfc_scene_run_emv_app_confirm_on_enter(void* context) { | ||||
|     dialog_ex_set_header(dialog_ex, "Run EMV app?", 64, 8, AlignCenter, AlignCenter); | ||||
|     dialog_ex_set_text( | ||||
|         dialog_ex, | ||||
|         "It will try to run card's app\nand detect unencrypred\ndata", | ||||
|         "It will try to run card's app\nand detect unencrypted\ndata", | ||||
|         64, | ||||
|         18, | ||||
|         AlignCenter, | ||||
|  | ||||
| @ -68,7 +68,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexDebug) { | ||||
|             scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateApduSequence); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -54,7 +54,9 @@ static void power_cli_command_print_usage() { | ||||
|     printf("\treboot2dfu\t - reboot to dfu bootloader\r\n"); | ||||
|     printf("\tdebug\t - show debug information\r\n"); | ||||
|     printf("\t5v <0 or 1>\t - enable or disable 5v ext\r\n"); | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|         printf("\t3v3 <0 or 1>\t - enable or disable 3v3 ext\r\n"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void power_cli(Cli* cli, string_t args, void* context) { | ||||
| @ -92,10 +94,12 @@ void power_cli(Cli* cli, string_t args, void* context) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||
|             if(string_cmp_str(cmd, "3v3") == 0) { | ||||
|                 power_cli_3v3(cli, args); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         power_cli_command_print_usage(); | ||||
|     } while(false); | ||||
|  | ||||
| @ -19,9 +19,12 @@ | ||||
| 
 | ||||
| #define TAG "RpcSrv" | ||||
| 
 | ||||
| #define RPC_EVENT_NEW_DATA (1 << 0) | ||||
| #define RPC_EVENT_DISCONNECT (1 << 1) | ||||
| #define RPC_EVENTS_ALL (RPC_EVENT_DISCONNECT | RPC_EVENT_NEW_DATA) | ||||
| typedef enum { | ||||
|     RpcEvtNewData = (1 << 0), | ||||
|     RpcEvtDisconnect = (1 << 1), | ||||
| } RpcEvtFlags; | ||||
| 
 | ||||
| #define RPC_ALL_EVENTS (RpcEvtNewData | RpcEvtDisconnect) | ||||
| 
 | ||||
| DICT_DEF2(RpcHandlerDict, pb_size_t, M_DEFAULT_OPLIST, RpcHandler, M_POD_OPLIST) | ||||
| 
 | ||||
| @ -51,44 +54,45 @@ static RpcSystemCallbacks rpc_systems[] = { | ||||
| }; | ||||
| 
 | ||||
| struct RpcSession { | ||||
|     Rpc* rpc; | ||||
| 
 | ||||
|     FuriThread* thread; | ||||
| 
 | ||||
|     RpcHandlerDict_t handlers; | ||||
|     StreamBufferHandle_t stream; | ||||
|     PB_Main* decoded_message; | ||||
|     bool terminate; | ||||
|     void** system_contexts; | ||||
|     bool decode_error; | ||||
| 
 | ||||
|     osMutexId_t callbacks_mutex; | ||||
|     RpcSendBytesCallback send_bytes_callback; | ||||
|     RpcBufferIsEmptyCallback buffer_is_empty_callback; | ||||
|     RpcSessionClosedCallback closed_callback; | ||||
|     RpcSessionTerminatedCallback terminated_callback; | ||||
|     void* context; | ||||
|     osMutexId_t callbacks_mutex; | ||||
|     Rpc* rpc; | ||||
|     bool terminate; | ||||
|     void** system_contexts; | ||||
|     bool decode_error; | ||||
| }; | ||||
| 
 | ||||
| struct Rpc { | ||||
|     bool busy; | ||||
|     osMutexId_t busy_mutex; | ||||
|     RpcSession session; | ||||
|     osEventFlagsId_t events; | ||||
|     StreamBufferHandle_t stream; | ||||
|     RpcHandlerDict_t handlers; | ||||
|     PB_Main* decoded_message; | ||||
| }; | ||||
| 
 | ||||
| static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg); | ||||
| 
 | ||||
| static void rpc_close_session_process(const PB_Main* msg_request, void* context) { | ||||
|     furi_assert(msg_request); | ||||
|     furi_assert(context); | ||||
| static void rpc_close_session_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
| 
 | ||||
|     Rpc* rpc = context; | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     rpc_send_and_release_empty(rpc, msg_request->command_id, PB_CommandStatus_OK); | ||||
|     osMutexAcquire(rpc->session.callbacks_mutex, osWaitForever); | ||||
|     if(rpc->session.closed_callback) { | ||||
|         rpc->session.closed_callback(rpc->session.context); | ||||
|     rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); | ||||
|     osMutexAcquire(session->callbacks_mutex, osWaitForever); | ||||
|     if(session->closed_callback) { | ||||
|         session->closed_callback(session->context); | ||||
|     } else { | ||||
|         FURI_LOG_W(TAG, "Session stop doesn't processed by transport layer"); | ||||
|         FURI_LOG_W(TAG, "Session stop isn't processed by transport layer"); | ||||
|     } | ||||
|     osMutexRelease(rpc->session.callbacks_mutex); | ||||
|     osMutexRelease(session->callbacks_mutex); | ||||
| } | ||||
| 
 | ||||
| static size_t rpc_sprintf_msg_file( | ||||
| @ -349,94 +353,8 @@ void rpc_print_message(const PB_Main* message) { | ||||
|     string_clear(str); | ||||
| } | ||||
| 
 | ||||
| static Rpc* rpc_alloc(void) { | ||||
|     Rpc* rpc = malloc(sizeof(Rpc)); | ||||
|     rpc->busy_mutex = osMutexNew(NULL); | ||||
|     rpc->busy = false; | ||||
|     rpc->events = osEventFlagsNew(NULL); | ||||
|     rpc->stream = xStreamBufferCreate(RPC_BUFFER_SIZE, 1); | ||||
| 
 | ||||
|     rpc->decoded_message = malloc(sizeof(PB_Main)); | ||||
|     rpc->decoded_message->cb_content.funcs.decode = content_callback; | ||||
|     rpc->decoded_message->cb_content.arg = rpc; | ||||
| 
 | ||||
|     RpcHandlerDict_init(rpc->handlers); | ||||
| 
 | ||||
|     return rpc; | ||||
| } | ||||
| 
 | ||||
| RpcSession* rpc_session_open(Rpc* rpc) { | ||||
|     furi_assert(rpc); | ||||
|     bool result = false; | ||||
|     furi_check(osMutexAcquire(rpc->busy_mutex, osWaitForever) == osOK); | ||||
|     if(rpc->busy) { | ||||
|         result = false; | ||||
|     } else { | ||||
|         rpc->busy = true; | ||||
|         result = true; | ||||
|     } | ||||
|     furi_check(osMutexRelease(rpc->busy_mutex) == osOK); | ||||
| 
 | ||||
|     if(result) { | ||||
|         RpcSession* session = &rpc->session; | ||||
|         session->callbacks_mutex = osMutexNew(NULL); | ||||
|         session->rpc = rpc; | ||||
|         session->terminate = false; | ||||
|         session->decode_error = false; | ||||
|         xStreamBufferReset(rpc->stream); | ||||
| 
 | ||||
|         session->system_contexts = malloc(COUNT_OF(rpc_systems) * sizeof(void*)); | ||||
|         for(int i = 0; i < COUNT_OF(rpc_systems); ++i) { | ||||
|             session->system_contexts[i] = rpc_systems[i].alloc(rpc); | ||||
|         } | ||||
| 
 | ||||
|         RpcHandler rpc_handler = { | ||||
|             .message_handler = rpc_close_session_process, | ||||
|             .decode_submessage = NULL, | ||||
|             .context = rpc, | ||||
|         }; | ||||
|         rpc_add_handler(rpc, PB_Main_stop_session_tag, &rpc_handler); | ||||
| 
 | ||||
|         FURI_LOG_D(TAG, "Session started"); | ||||
|     } | ||||
| 
 | ||||
|     return result ? &rpc->session : NULL; /* support 1 open session for now */ | ||||
| } | ||||
| 
 | ||||
| void rpc_session_close(RpcSession* session) { | ||||
|     furi_assert(session); | ||||
|     furi_assert(session->rpc); | ||||
|     furi_assert(session->rpc->busy); | ||||
| 
 | ||||
|     rpc_session_set_send_bytes_callback(session, NULL); | ||||
|     rpc_session_set_close_callback(session, NULL); | ||||
|     rpc_session_set_buffer_is_empty_callback(session, NULL); | ||||
|     osEventFlagsSet(session->rpc->events, RPC_EVENT_DISCONNECT); | ||||
| } | ||||
| 
 | ||||
| static void rpc_free_session(RpcSession* session) { | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     for(int i = 0; i < COUNT_OF(rpc_systems); ++i) { | ||||
|         if(rpc_systems[i].free) { | ||||
|             rpc_systems[i].free(session->system_contexts[i]); | ||||
|         } | ||||
|     } | ||||
|     free(session->system_contexts); | ||||
|     osMutexDelete(session->callbacks_mutex); | ||||
|     RpcHandlerDict_reset(session->rpc->handlers); | ||||
| 
 | ||||
|     session->context = NULL; | ||||
|     session->closed_callback = NULL; | ||||
|     session->send_bytes_callback = NULL; | ||||
|     session->buffer_is_empty_callback = NULL; | ||||
|     session->terminated_callback = NULL; | ||||
| } | ||||
| 
 | ||||
| void rpc_session_set_context(RpcSession* session, void* context) { | ||||
|     furi_assert(session); | ||||
|     furi_assert(session->rpc); | ||||
|     furi_assert(session->rpc->busy); | ||||
| 
 | ||||
|     osMutexAcquire(session->callbacks_mutex, osWaitForever); | ||||
|     session->context = context; | ||||
| @ -445,8 +363,6 @@ void rpc_session_set_context(RpcSession* session, void* context) { | ||||
| 
 | ||||
| void rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallback callback) { | ||||
|     furi_assert(session); | ||||
|     furi_assert(session->rpc); | ||||
|     furi_assert(session->rpc->busy); | ||||
| 
 | ||||
|     osMutexAcquire(session->callbacks_mutex, osWaitForever); | ||||
|     session->closed_callback = callback; | ||||
| @ -455,8 +371,6 @@ void rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallbac | ||||
| 
 | ||||
| void rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback) { | ||||
|     furi_assert(session); | ||||
|     furi_assert(session->rpc); | ||||
|     furi_assert(session->rpc->busy); | ||||
| 
 | ||||
|     osMutexAcquire(session->callbacks_mutex, osWaitForever); | ||||
|     session->send_bytes_callback = callback; | ||||
| @ -467,7 +381,6 @@ void rpc_session_set_buffer_is_empty_callback( | ||||
|     RpcSession* session, | ||||
|     RpcBufferIsEmptyCallback callback) { | ||||
|     furi_assert(session); | ||||
|     furi_assert(session->rpc->busy); | ||||
| 
 | ||||
|     osMutexAcquire(session->callbacks_mutex, osWaitForever); | ||||
|     session->buffer_is_empty_callback = callback; | ||||
| @ -478,8 +391,6 @@ void rpc_session_set_terminated_callback( | ||||
|     RpcSession* session, | ||||
|     RpcSessionTerminatedCallback callback) { | ||||
|     furi_assert(session); | ||||
|     furi_assert(session->rpc); | ||||
|     furi_assert(session->rpc->busy); | ||||
| 
 | ||||
|     osMutexAcquire(session->callbacks_mutex, osWaitForever); | ||||
|     session->terminated_callback = callback; | ||||
| @ -495,54 +406,54 @@ void rpc_session_set_terminated_callback( | ||||
| size_t | ||||
|     rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout) { | ||||
|     furi_assert(session); | ||||
|     Rpc* rpc = session->rpc; | ||||
|     furi_assert(rpc->busy); | ||||
|     size_t bytes_sent = xStreamBufferSend(session->stream, encoded_bytes, size, timeout); | ||||
| 
 | ||||
|     size_t bytes_sent = xStreamBufferSend(rpc->stream, encoded_bytes, size, timeout); | ||||
|     osEventFlagsSet(rpc->events, RPC_EVENT_NEW_DATA); | ||||
|     osThreadFlagsSet(furi_thread_get_thread_id(session->thread), RpcEvtNewData); | ||||
| 
 | ||||
|     return bytes_sent; | ||||
| } | ||||
| 
 | ||||
| size_t rpc_session_get_available_size(RpcSession* session) { | ||||
|     furi_assert(session); | ||||
|     Rpc* rpc = session->rpc; | ||||
|     return xStreamBufferSpacesAvailable(rpc->stream); | ||||
|     return xStreamBufferSpacesAvailable(session->stream); | ||||
| } | ||||
| 
 | ||||
| bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { | ||||
|     Rpc* rpc = istream->state; | ||||
|     RpcSession* session = istream->state; | ||||
|     furi_assert(session); | ||||
|     furi_assert(istream->bytes_left); | ||||
| 
 | ||||
|     uint32_t flags = 0; | ||||
|     size_t bytes_received = 0; | ||||
| 
 | ||||
|     furi_assert(istream->bytes_left); | ||||
| 
 | ||||
|     while(1) { | ||||
|         bytes_received += | ||||
|             xStreamBufferReceive(rpc->stream, buf + bytes_received, count - bytes_received, 0); | ||||
|         if(xStreamBufferIsEmpty(rpc->stream)) { | ||||
|             if(rpc->session.buffer_is_empty_callback) { | ||||
|                 rpc->session.buffer_is_empty_callback(rpc->session.context); | ||||
|             xStreamBufferReceive(session->stream, buf + bytes_received, count - bytes_received, 0); | ||||
|         if(xStreamBufferIsEmpty(session->stream)) { | ||||
|             if(session->buffer_is_empty_callback) { | ||||
|                 session->buffer_is_empty_callback(session->context); | ||||
|             } | ||||
|         } | ||||
|         if(rpc->session.decode_error) { | ||||
|         if(session->decode_error) { | ||||
|             /* never go out till RPC_EVENT_DISCONNECT come */ | ||||
|             bytes_received = 0; | ||||
|         } | ||||
|         if(count == bytes_received) { | ||||
|             break; | ||||
|         } else { | ||||
|             flags = osEventFlagsWait(rpc->events, RPC_EVENTS_ALL, 0, osWaitForever); | ||||
|             if(flags & RPC_EVENT_DISCONNECT) { | ||||
|                 if(xStreamBufferIsEmpty(rpc->stream)) { | ||||
|                     rpc->session.terminate = true; | ||||
|             flags = osThreadFlagsWait(RPC_ALL_EVENTS, osFlagsWaitAny, osWaitForever); | ||||
|             if(flags & RpcEvtDisconnect) { | ||||
|                 if(xStreamBufferIsEmpty(session->stream)) { | ||||
|                     session->terminate = true; | ||||
|                     istream->bytes_left = 0; | ||||
|                     bytes_received = 0; | ||||
|                     break; | ||||
|                 } else { | ||||
|                     /* Save disconnect flag and continue reading buffer */ | ||||
|                     osEventFlagsSet(rpc->events, RPC_EVENT_DISCONNECT); | ||||
|                     osThreadFlagsSet(furi_thread_get_thread_id(session->thread), RpcEvtDisconnect); | ||||
|                 } | ||||
|             } else if(flags & RpcEvtNewData) { | ||||
|                 // Just wake thread up
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -554,10 +465,211 @@ bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { | ||||
|     return (count == bytes_received); | ||||
| } | ||||
| 
 | ||||
| void rpc_send_and_release(Rpc* rpc, PB_Main* message) { | ||||
| static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg) { | ||||
|     furi_assert(stream); | ||||
|     RpcSession* session = stream->state; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     RpcHandler* handler = RpcHandlerDict_get(session->handlers, field->tag); | ||||
| 
 | ||||
|     if(handler && handler->decode_submessage) { | ||||
|         handler->decode_submessage(stream, field, arg); | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static int32_t rpc_session_worker(void* context) { | ||||
|     furi_assert(context); | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     Rpc* rpc = session->rpc; | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "Session started"); | ||||
| 
 | ||||
|     while(1) { | ||||
|         pb_istream_t istream = { | ||||
|             .callback = rpc_pb_stream_read, | ||||
|             .state = session, | ||||
|             .errmsg = NULL, | ||||
|             .bytes_left = RPC_MAX_MESSAGE_SIZE, /* max incoming message size */ | ||||
|         }; | ||||
| 
 | ||||
|         bool message_decode_failed = false; | ||||
| 
 | ||||
|         if(pb_decode_ex(&istream, &PB_Main_msg, session->decoded_message, PB_DECODE_DELIMITED)) { | ||||
| #if SRV_RPC_DEBUG | ||||
|             FURI_LOG_I(TAG, "INPUT:"); | ||||
|             rpc_print_message(session->decoded_message); | ||||
| #endif | ||||
|             RpcHandler* handler = | ||||
|                 RpcHandlerDict_get(session->handlers, session->decoded_message->which_content); | ||||
| 
 | ||||
|             if(handler && handler->message_handler) { | ||||
|                 furi_check(osMutexAcquire(rpc->busy_mutex, osWaitForever) == osOK); | ||||
|                 handler->message_handler(session->decoded_message, handler->context); | ||||
|                 furi_check(osMutexRelease(rpc->busy_mutex) == osOK); | ||||
|             } else if(session->decoded_message->which_content == 0) { | ||||
|                 /* Receiving zeroes means message is 0-length, which
 | ||||
|                  * is valid for proto3: all fields are filled with default values. | ||||
|                  * 0 - is default value for which_content field. | ||||
|                  * Mark it as decode error, because there is no content message | ||||
|                  * in Main message with tag 0. | ||||
|                  */ | ||||
|                 message_decode_failed = true; | ||||
|             } else if(!handler && !session->terminate) { | ||||
|                 FURI_LOG_E( | ||||
|                     TAG, | ||||
|                     "Message(%d) decoded, but not implemented", | ||||
|                     session->decoded_message->which_content); | ||||
|                 rpc_send_and_release_empty( | ||||
|                     session, | ||||
|                     session->decoded_message->command_id, | ||||
|                     PB_CommandStatus_ERROR_NOT_IMPLEMENTED); | ||||
|             } | ||||
|         } else { | ||||
|             message_decode_failed = true; | ||||
|         } | ||||
| 
 | ||||
|         if(message_decode_failed) { | ||||
|             xStreamBufferReset(session->stream); | ||||
|             if(!session->terminate) { | ||||
|                 /* Protobuf can't determine start and end of message.
 | ||||
|                  * Handle this by adding varint at beginning | ||||
|                  * of a message (PB_ENCODE_DELIMITED). But decoding fail | ||||
|                  * means we can't be sure next bytes are varint for next | ||||
|                  * message, so the only way to close session. | ||||
|                  * RPC itself can't make decision to close session. It has | ||||
|                  * to notify: | ||||
|                  * 1) down layer (transport) | ||||
|                  * 2) other side (companion app) | ||||
|                  * Who are responsible to handle RPC session lifecycle. | ||||
|                  * Companion receives 2 messages: ERROR_DECODE and session_closed. | ||||
|                  */ | ||||
|                 FURI_LOG_E(TAG, "Decode failed, error: \'%.128s\'", PB_GET_ERROR(&istream)); | ||||
|                 session->decode_error = true; | ||||
|                 rpc_send_and_release_empty(session, 0, PB_CommandStatus_ERROR_DECODE); | ||||
|                 osMutexAcquire(session->callbacks_mutex, osWaitForever); | ||||
|                 if(session->closed_callback) { | ||||
|                     session->closed_callback(session->context); | ||||
|                 } | ||||
|                 osMutexRelease(session->callbacks_mutex); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         pb_release(&PB_Main_msg, session->decoded_message); | ||||
| 
 | ||||
|         if(session->terminate) { | ||||
|             FURI_LOG_D(TAG, "Session terminated"); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void rpc_session_free_callback(FuriThreadState thread_state, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
| 
 | ||||
|     if(thread_state == FuriThreadStateStopped) { | ||||
|         for(int i = 0; i < COUNT_OF(rpc_systems); ++i) { | ||||
|             if(rpc_systems[i].free) { | ||||
|                 rpc_systems[i].free(session->system_contexts[i]); | ||||
|             } | ||||
|         } | ||||
|         free(session->system_contexts); | ||||
|         free(session->decoded_message); | ||||
|         RpcHandlerDict_clear(session->handlers); | ||||
|         vStreamBufferDelete(session->stream); | ||||
| 
 | ||||
|         osMutexAcquire(session->callbacks_mutex, osWaitForever); | ||||
|         if(session->terminated_callback) { | ||||
|             session->terminated_callback(session->context); | ||||
|         } | ||||
|         osMutexRelease(session->callbacks_mutex); | ||||
| 
 | ||||
|         osMutexDelete(session->callbacks_mutex); | ||||
|         furi_thread_free(session->thread); | ||||
|         free(session); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| RpcSession* rpc_session_open(Rpc* rpc) { | ||||
|     furi_assert(rpc); | ||||
| 
 | ||||
|     RpcSession* session = malloc(sizeof(RpcSession)); | ||||
|     session->callbacks_mutex = osMutexNew(NULL); | ||||
|     session->stream = xStreamBufferCreate(RPC_BUFFER_SIZE, 1); | ||||
|     session->rpc = rpc; | ||||
|     session->terminate = false; | ||||
|     session->decode_error = false; | ||||
|     RpcHandlerDict_init(session->handlers); | ||||
| 
 | ||||
|     session->decoded_message = malloc(sizeof(PB_Main)); | ||||
|     session->decoded_message->cb_content.funcs.decode = content_callback; | ||||
|     session->decoded_message->cb_content.arg = session; | ||||
| 
 | ||||
|     session->system_contexts = malloc(COUNT_OF(rpc_systems) * sizeof(void*)); | ||||
|     for(int i = 0; i < COUNT_OF(rpc_systems); ++i) { | ||||
|         session->system_contexts[i] = rpc_systems[i].alloc(session); | ||||
|     } | ||||
| 
 | ||||
|     RpcHandler rpc_handler = { | ||||
|         .message_handler = rpc_close_session_process, | ||||
|         .decode_submessage = NULL, | ||||
|         .context = session, | ||||
|     }; | ||||
|     rpc_add_handler(session, PB_Main_stop_session_tag, &rpc_handler); | ||||
| 
 | ||||
|     session->thread = furi_thread_alloc(); | ||||
|     furi_thread_set_name(session->thread, "RpcSessionWorker"); | ||||
|     furi_thread_set_stack_size(session->thread, 2048); | ||||
|     furi_thread_set_context(session->thread, session); | ||||
|     furi_thread_set_callback(session->thread, rpc_session_worker); | ||||
| 
 | ||||
|     furi_thread_set_state_context(session->thread, session); | ||||
|     furi_thread_set_state_callback(session->thread, rpc_session_free_callback); | ||||
| 
 | ||||
|     furi_thread_start(session->thread); | ||||
| 
 | ||||
|     return session; | ||||
| } | ||||
| 
 | ||||
| void rpc_session_close(RpcSession* session) { | ||||
|     furi_assert(session); | ||||
|     furi_assert(session->rpc); | ||||
| 
 | ||||
|     rpc_session_set_send_bytes_callback(session, NULL); | ||||
|     rpc_session_set_close_callback(session, NULL); | ||||
|     rpc_session_set_buffer_is_empty_callback(session, NULL); | ||||
|     osThreadFlagsSet(furi_thread_get_thread_id(session->thread), RpcEvtDisconnect); | ||||
| } | ||||
| 
 | ||||
| int32_t rpc_srv(void* p) { | ||||
|     Rpc* rpc = malloc(sizeof(Rpc)); | ||||
| 
 | ||||
|     rpc->busy_mutex = osMutexNew(NULL); | ||||
| 
 | ||||
|     Cli* cli = furi_record_open("cli"); | ||||
|     cli_add_command( | ||||
|         cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc); | ||||
| 
 | ||||
|     furi_record_create("rpc", rpc); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| void rpc_add_handler(RpcSession* session, pb_size_t message_tag, RpcHandler* handler) { | ||||
|     furi_assert(RpcHandlerDict_get(session->handlers, message_tag) == NULL); | ||||
| 
 | ||||
|     RpcHandlerDict_set_at(session->handlers, message_tag, *handler); | ||||
| } | ||||
| 
 | ||||
| void rpc_send(RpcSession* session, PB_Main* message) { | ||||
|     furi_assert(session); | ||||
|     furi_assert(message); | ||||
|     RpcSession* session = &rpc->session; | ||||
| 
 | ||||
|     pb_ostream_t ostream = PB_OSTREAM_SIZING; | ||||
| 
 | ||||
| #if SRV_RPC_DEBUG | ||||
| @ -584,127 +696,20 @@ void rpc_send_and_release(Rpc* rpc, PB_Main* message) { | ||||
|     osMutexRelease(session->callbacks_mutex); | ||||
| 
 | ||||
|     free(buffer); | ||||
| } | ||||
| 
 | ||||
| void rpc_send_and_release(RpcSession* session, PB_Main* message) { | ||||
|     rpc_send(session, message); | ||||
|     pb_release(&PB_Main_msg, message); | ||||
| } | ||||
| 
 | ||||
| static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg) { | ||||
|     furi_assert(stream); | ||||
|     Rpc* rpc = *arg; | ||||
| 
 | ||||
|     RpcHandler* handler = RpcHandlerDict_get(rpc->handlers, field->tag); | ||||
| 
 | ||||
|     if(handler && handler->decode_submessage) { | ||||
|         handler->decode_submessage(stream, field, arg); | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| int32_t rpc_srv(void* p) { | ||||
|     Rpc* rpc = rpc_alloc(); | ||||
|     furi_record_create("rpc", rpc); | ||||
| 
 | ||||
|     Cli* cli = furi_record_open("cli"); | ||||
| 
 | ||||
|     cli_add_command( | ||||
|         cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc); | ||||
| 
 | ||||
|     while(1) { | ||||
|         pb_istream_t istream = { | ||||
|             .callback = rpc_pb_stream_read, | ||||
|             .state = rpc, | ||||
|             .errmsg = NULL, | ||||
|             .bytes_left = RPC_MAX_MESSAGE_SIZE, /* max incoming message size */ | ||||
|         }; | ||||
| 
 | ||||
|         bool message_decode_failed = false; | ||||
| 
 | ||||
|         if(pb_decode_ex(&istream, &PB_Main_msg, rpc->decoded_message, PB_DECODE_DELIMITED)) { | ||||
| #if SRV_RPC_DEBUG | ||||
|             FURI_LOG_I(TAG, "INPUT:"); | ||||
|             rpc_print_message(rpc->decoded_message); | ||||
| #endif | ||||
|             RpcHandler* handler = | ||||
|                 RpcHandlerDict_get(rpc->handlers, rpc->decoded_message->which_content); | ||||
| 
 | ||||
|             if(handler && handler->message_handler) { | ||||
|                 handler->message_handler(rpc->decoded_message, handler->context); | ||||
|             } else if(rpc->decoded_message->which_content == 0) { | ||||
|                 /* Receiving zeroes means message is 0-length, which
 | ||||
|                  * is valid for proto3: all fields are filled with default values. | ||||
|                  * 0 - is default value for which_content field. | ||||
|                  * Mark it as decode error, because there is no content message | ||||
|                  * in Main message with tag 0. | ||||
|                  */ | ||||
|                 message_decode_failed = true; | ||||
|             } else if(!handler && !rpc->session.terminate) { | ||||
|                 FURI_LOG_E( | ||||
|                     TAG, | ||||
|                     "Message(%d) decoded, but not implemented", | ||||
|                     rpc->decoded_message->which_content); | ||||
|                 rpc_send_and_release_empty( | ||||
|                     rpc, rpc->decoded_message->command_id, PB_CommandStatus_ERROR_NOT_IMPLEMENTED); | ||||
|             } | ||||
|         } else { | ||||
|             message_decode_failed = true; | ||||
|         } | ||||
| 
 | ||||
|         if(message_decode_failed) { | ||||
|             xStreamBufferReset(rpc->stream); | ||||
|             if(!rpc->session.terminate) { | ||||
|                 /* Protobuf can't determine start and end of message.
 | ||||
|                  * Handle this by adding varint at beginning | ||||
|                  * of a message (PB_ENCODE_DELIMITED). But decoding fail | ||||
|                  * means we can't be sure next bytes are varint for next | ||||
|                  * message, so the only way to close session. | ||||
|                  * RPC itself can't make decision to close session. It has | ||||
|                  * to notify: | ||||
|                  * 1) down layer (transport) | ||||
|                  * 2) other side (companion app) | ||||
|                  * Who are responsible to handle RPC session lifecycle. | ||||
|                  * Companion receives 2 messages: ERROR_DECODE and session_closed. | ||||
|                  */ | ||||
|                 FURI_LOG_E(TAG, "Decode failed, error: \'%.128s\'", PB_GET_ERROR(&istream)); | ||||
|                 rpc->session.decode_error = true; | ||||
|                 rpc_send_and_release_empty(rpc, 0, PB_CommandStatus_ERROR_DECODE); | ||||
|                 osMutexAcquire(rpc->session.callbacks_mutex, osWaitForever); | ||||
|                 if(rpc->session.closed_callback) { | ||||
|                     rpc->session.closed_callback(rpc->session.context); | ||||
|                 } | ||||
|                 osMutexRelease(rpc->session.callbacks_mutex); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         pb_release(&PB_Main_msg, rpc->decoded_message); | ||||
| 
 | ||||
|         if(rpc->session.terminate) { | ||||
|             FURI_LOG_D(TAG, "Session terminated"); | ||||
|             osMutexAcquire(rpc->session.callbacks_mutex, osWaitForever); | ||||
|             if(rpc->session.terminated_callback) { | ||||
|                 rpc->session.terminated_callback(rpc->session.context); | ||||
|             } | ||||
|             osMutexRelease(rpc->session.callbacks_mutex); | ||||
|             osEventFlagsClear(rpc->events, RPC_EVENTS_ALL); | ||||
|             rpc_free_session(&rpc->session); | ||||
|             rpc->busy = false; | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| void rpc_add_handler(Rpc* rpc, pb_size_t message_tag, RpcHandler* handler) { | ||||
|     furi_assert(RpcHandlerDict_get(rpc->handlers, message_tag) == NULL); | ||||
| 
 | ||||
|     RpcHandlerDict_set_at(rpc->handlers, message_tag, *handler); | ||||
| } | ||||
| 
 | ||||
| void rpc_send_and_release_empty(Rpc* rpc, uint32_t command_id, PB_CommandStatus status) { | ||||
| void rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_CommandStatus status) { | ||||
|     PB_Main message = { | ||||
|         .command_id = command_id, | ||||
|         .command_status = status, | ||||
|         .has_next = false, | ||||
|         .which_content = PB_Main_empty_tag, | ||||
|     }; | ||||
|     rpc_send_and_release(rpc, &message); | ||||
|     rpc_send_and_release(session, &message); | ||||
|     pb_release(&PB_Main_msg, &message); | ||||
| } | ||||
|  | ||||
| @ -5,10 +5,11 @@ | ||||
| #include <loader/loader.h> | ||||
| 
 | ||||
| static void rpc_system_app_start_process(const PB_Main* request, void* context) { | ||||
|     Rpc* rpc = context; | ||||
|     furi_assert(rpc); | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_app_start_request_tag); | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START; | ||||
| 
 | ||||
|     Loader* loader = furi_record_open("loader"); | ||||
| @ -33,14 +34,14 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | ||||
| 
 | ||||
|     furi_record_close("loader"); | ||||
| 
 | ||||
|     rpc_send_and_release_empty(rpc, request->command_id, result); | ||||
|     rpc_send_and_release_empty(session, request->command_id, result); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { | ||||
|     Rpc* rpc = context; | ||||
|     furi_assert(rpc); | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_app_lock_status_request_tag); | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     Loader* loader = furi_record_open("loader"); | ||||
| 
 | ||||
| @ -55,24 +56,24 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con | ||||
| 
 | ||||
|     furi_record_close("loader"); | ||||
| 
 | ||||
|     rpc_send_and_release(rpc, &response); | ||||
|     rpc_send_and_release(session, &response); | ||||
|     pb_release(&PB_Main_msg, &response); | ||||
| } | ||||
| 
 | ||||
| void* rpc_system_app_alloc(Rpc* rpc) { | ||||
|     furi_assert(rpc); | ||||
| void* rpc_system_app_alloc(RpcSession* session) { | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     RpcHandler rpc_handler = { | ||||
|         .message_handler = NULL, | ||||
|         .decode_submessage = NULL, | ||||
|         .context = rpc, | ||||
|         .context = session, | ||||
|     }; | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_app_start_process; | ||||
|     rpc_add_handler(rpc, PB_Main_app_start_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_app_start_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_app_lock_status_process; | ||||
|     rpc_add_handler(rpc, PB_Main_app_lock_status_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| @ -31,7 +31,7 @@ void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { | ||||
| 
 | ||||
|     RpcSession* rpc_session = rpc_session_open(rpc); | ||||
|     if(rpc_session == NULL) { | ||||
|         printf("Another session is in progress\r\n"); | ||||
|         printf("Session start error\r\n"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -5,35 +5,61 @@ | ||||
| 
 | ||||
| #define TAG "RpcGui" | ||||
| 
 | ||||
| typedef enum { | ||||
|     RpcGuiWorkerFlagTransmit = (1 << 0), | ||||
|     RpcGuiWorkerFlagExit = (1 << 1), | ||||
| } RpcGuiWorkerFlag; | ||||
| 
 | ||||
| #define RpcGuiWorkerFlagAny (RpcGuiWorkerFlagTransmit | RpcGuiWorkerFlagExit) | ||||
| 
 | ||||
| typedef struct { | ||||
|     Rpc* rpc; | ||||
|     RpcSession* session; | ||||
|     Gui* gui; | ||||
| 
 | ||||
|     // Receive part
 | ||||
|     ViewPort* virtual_display_view_port; | ||||
|     uint8_t* virtual_display_buffer; | ||||
| 
 | ||||
|     // Transmit
 | ||||
|     PB_Main* transmit_frame; | ||||
|     FuriThread* transmit_thread; | ||||
| 
 | ||||
|     bool virtual_display_not_empty; | ||||
|     bool is_streaming; | ||||
| } RpcGuiSystem; | ||||
| 
 | ||||
| static void | ||||
|     rpc_system_gui_screen_stream_frame_callback(uint8_t* data, size_t size, void* context) { | ||||
|     furi_assert(data); | ||||
|     furi_assert(size == 1024); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     RpcGuiSystem* rpc_gui = context; | ||||
|     RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context; | ||||
|     uint8_t* buffer = rpc_gui->transmit_frame->content.gui_screen_frame.data->bytes; | ||||
| 
 | ||||
|     PB_Main* frame = malloc(sizeof(PB_Main)); | ||||
|     furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size); | ||||
| 
 | ||||
|     frame->which_content = PB_Main_gui_screen_frame_tag; | ||||
|     frame->command_status = PB_CommandStatus_OK; | ||||
|     frame->content.gui_screen_frame.data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(size)); | ||||
|     uint8_t* buffer = frame->content.gui_screen_frame.data->bytes; | ||||
|     uint16_t* frame_size_msg = &frame->content.gui_screen_frame.data->size; | ||||
|     *frame_size_msg = size; | ||||
|     memcpy(buffer, data, size); | ||||
| 
 | ||||
|     rpc_send_and_release(rpc_gui->rpc, frame); | ||||
|     osThreadFlagsSet( | ||||
|         furi_thread_get_thread_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagTransmit); | ||||
| } | ||||
| 
 | ||||
|     free(frame); | ||||
| static int32_t rpc_system_gui_screen_stream_frame_transmit_thread(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context; | ||||
| 
 | ||||
|     while(true) { | ||||
|         uint32_t flags = osThreadFlagsWait(RpcGuiWorkerFlagAny, osFlagsWaitAny, osWaitForever); | ||||
|         if(flags & RpcGuiWorkerFlagTransmit) { | ||||
|             rpc_send(rpc_gui->session, rpc_gui->transmit_frame); | ||||
|         } | ||||
|         if(flags & RpcGuiWorkerFlagExit) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_gui_start_screen_stream_process(const PB_Main* request, void* context) { | ||||
| @ -41,20 +67,59 @@ static void rpc_system_gui_start_screen_stream_process(const PB_Main* request, v | ||||
|     furi_assert(context); | ||||
|     RpcGuiSystem* rpc_gui = context; | ||||
| 
 | ||||
|     rpc_send_and_release_empty(rpc_gui->rpc, request->command_id, PB_CommandStatus_OK); | ||||
|     RpcSession* session = rpc_gui->session; | ||||
|     furi_assert(session); | ||||
|     furi_assert(!rpc_gui->is_streaming); | ||||
| 
 | ||||
|     gui_set_framebuffer_callback( | ||||
|     rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); | ||||
| 
 | ||||
|     rpc_gui->is_streaming = true; | ||||
|     size_t framebuffer_size = gui_get_framebuffer_size(rpc_gui->gui); | ||||
|     // Reusable Frame
 | ||||
|     rpc_gui->transmit_frame = malloc(sizeof(PB_Main)); | ||||
|     rpc_gui->transmit_frame->which_content = PB_Main_gui_screen_frame_tag; | ||||
|     rpc_gui->transmit_frame->command_status = PB_CommandStatus_OK; | ||||
|     rpc_gui->transmit_frame->content.gui_screen_frame.data = | ||||
|         malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(framebuffer_size)); | ||||
|     rpc_gui->transmit_frame->content.gui_screen_frame.data->size = framebuffer_size; | ||||
|     // Transmission thread for async TX
 | ||||
|     rpc_gui->transmit_thread = furi_thread_alloc(); | ||||
|     furi_thread_set_name(rpc_gui->transmit_thread, "GuiRpcWorker"); | ||||
|     furi_thread_set_callback( | ||||
|         rpc_gui->transmit_thread, rpc_system_gui_screen_stream_frame_transmit_thread); | ||||
|     furi_thread_set_context(rpc_gui->transmit_thread, rpc_gui); | ||||
|     furi_thread_set_stack_size(rpc_gui->transmit_thread, 1024); | ||||
|     furi_thread_start(rpc_gui->transmit_thread); | ||||
|     // GUI framebuffer callback
 | ||||
|     gui_add_framebuffer_callback( | ||||
|         rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_gui_stop_screen_stream_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     RpcGuiSystem* rpc_gui = context; | ||||
|     RpcSession* session = rpc_gui->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     gui_set_framebuffer_callback(rpc_gui->gui, NULL, NULL); | ||||
|     if(rpc_gui->is_streaming) { | ||||
|         rpc_gui->is_streaming = false; | ||||
|         // Remove GUI framebuffer callback
 | ||||
|         gui_remove_framebuffer_callback( | ||||
|             rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context); | ||||
|         // Stop and release worker thread
 | ||||
|         osThreadFlagsSet( | ||||
|             furi_thread_get_thread_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagExit); | ||||
|         furi_thread_join(rpc_gui->transmit_thread); | ||||
|         furi_thread_free(rpc_gui->transmit_thread); | ||||
|         // Release frame
 | ||||
|         pb_release(&PB_Main_msg, rpc_gui->transmit_frame); | ||||
|         free(rpc_gui->transmit_frame); | ||||
|         rpc_gui->transmit_frame = NULL; | ||||
|     } | ||||
| 
 | ||||
|     rpc_send_and_release_empty(rpc_gui->rpc, request->command_id, PB_CommandStatus_OK); | ||||
|     rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| @ -62,7 +127,10 @@ static void | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_gui_send_input_event_request_tag); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     RpcGuiSystem* rpc_gui = context; | ||||
|     RpcSession* session = rpc_gui->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     InputEvent event; | ||||
| 
 | ||||
| @ -117,7 +185,7 @@ static void | ||||
| 
 | ||||
|     if(invalid) { | ||||
|         rpc_send_and_release_empty( | ||||
|             rpc_gui->rpc, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); | ||||
|             session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
| @ -125,12 +193,13 @@ static void | ||||
|     furi_check(input_events); | ||||
|     furi_pubsub_publish(input_events, &event); | ||||
|     furi_record_close("input_events"); | ||||
|     rpc_send_and_release_empty(rpc_gui->rpc, request->command_id, PB_CommandStatus_OK); | ||||
|     rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_gui_virtual_display_render_callback(Canvas* canvas, void* context) { | ||||
|     furi_assert(canvas); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     RpcGuiSystem* rpc_gui = context; | ||||
| 
 | ||||
|     if(!rpc_gui->virtual_display_not_empty) { | ||||
| @ -146,13 +215,14 @@ static void rpc_system_gui_virtual_display_render_callback(Canvas* canvas, void* | ||||
| static void rpc_system_gui_start_virtual_display_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     RpcGuiSystem* rpc_gui = context; | ||||
|     RpcSession* session = rpc_gui->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     if(rpc_gui->virtual_display_view_port) { | ||||
|         rpc_send_and_release_empty( | ||||
|             rpc_gui->rpc, | ||||
|             request->command_id, | ||||
|             PB_CommandStatus_ERROR_VIRTUAL_DISPLAY_ALREADY_STARTED); | ||||
|             session, request->command_id, PB_CommandStatus_ERROR_VIRTUAL_DISPLAY_ALREADY_STARTED); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
| @ -178,17 +248,20 @@ static void rpc_system_gui_start_virtual_display_process(const PB_Main* request, | ||||
|         rpc_gui); | ||||
|     gui_add_view_port(rpc_gui->gui, rpc_gui->virtual_display_view_port, GuiLayerFullscreen); | ||||
| 
 | ||||
|     rpc_send_and_release_empty(rpc_gui->rpc, request->command_id, PB_CommandStatus_OK); | ||||
|     rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_gui_stop_virtual_display_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     RpcGuiSystem* rpc_gui = context; | ||||
|     RpcSession* session = rpc_gui->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     if(!rpc_gui->virtual_display_view_port) { | ||||
|         rpc_send_and_release_empty( | ||||
|             rpc_gui->rpc, request->command_id, PB_CommandStatus_ERROR_VIRTUAL_DISPLAY_NOT_STARTED); | ||||
|             session, request->command_id, PB_CommandStatus_ERROR_VIRTUAL_DISPLAY_NOT_STARTED); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
| @ -198,13 +271,16 @@ static void rpc_system_gui_stop_virtual_display_process(const PB_Main* request, | ||||
|     rpc_gui->virtual_display_view_port = NULL; | ||||
|     rpc_gui->virtual_display_not_empty = false; | ||||
| 
 | ||||
|     rpc_send_and_release_empty(rpc_gui->rpc, request->command_id, PB_CommandStatus_OK); | ||||
|     rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_gui_virtual_display_frame_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     RpcGuiSystem* rpc_gui = context; | ||||
|     RpcSession* session = rpc_gui->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     if(!rpc_gui->virtual_display_view_port) { | ||||
|         FURI_LOG_W(TAG, "Virtual display is not started, ignoring incoming frame packet"); | ||||
| @ -218,14 +294,16 @@ static void rpc_system_gui_virtual_display_frame_process(const PB_Main* request, | ||||
|         buffer_size); | ||||
|     rpc_gui->virtual_display_not_empty = true; | ||||
|     view_port_update(rpc_gui->virtual_display_view_port); | ||||
| 
 | ||||
|     (void)session; | ||||
| } | ||||
| 
 | ||||
| void* rpc_system_gui_alloc(Rpc* rpc) { | ||||
|     furi_assert(rpc); | ||||
| void* rpc_system_gui_alloc(RpcSession* session) { | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     RpcGuiSystem* rpc_gui = malloc(sizeof(RpcGuiSystem)); | ||||
|     rpc_gui->gui = furi_record_open("gui"); | ||||
|     rpc_gui->rpc = rpc; | ||||
|     rpc_gui->session = session; | ||||
| 
 | ||||
|     RpcHandler rpc_handler = { | ||||
|         .message_handler = NULL, | ||||
| @ -234,29 +312,29 @@ void* rpc_system_gui_alloc(Rpc* rpc) { | ||||
|     }; | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_gui_start_screen_stream_process; | ||||
|     rpc_add_handler(rpc, PB_Main_gui_start_screen_stream_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_gui_start_screen_stream_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_gui_stop_screen_stream_process; | ||||
|     rpc_add_handler(rpc, PB_Main_gui_stop_screen_stream_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_gui_stop_screen_stream_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_gui_send_input_event_request_process; | ||||
|     rpc_add_handler(rpc, PB_Main_gui_send_input_event_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_gui_send_input_event_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_gui_start_virtual_display_process; | ||||
|     rpc_add_handler(rpc, PB_Main_gui_start_virtual_display_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_gui_start_virtual_display_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_gui_stop_virtual_display_process; | ||||
|     rpc_add_handler(rpc, PB_Main_gui_stop_virtual_display_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_gui_stop_virtual_display_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_gui_virtual_display_frame_process; | ||||
|     rpc_add_handler(rpc, PB_Main_gui_screen_frame_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_gui_screen_frame_tag, &rpc_handler); | ||||
| 
 | ||||
|     return rpc_gui; | ||||
| } | ||||
| 
 | ||||
| void rpc_system_gui_free(void* ctx) { | ||||
|     furi_assert(ctx); | ||||
|     RpcGuiSystem* rpc_gui = ctx; | ||||
| void rpc_system_gui_free(void* context) { | ||||
|     furi_assert(context); | ||||
|     RpcGuiSystem* rpc_gui = context; | ||||
|     furi_assert(rpc_gui->gui); | ||||
| 
 | ||||
|     if(rpc_gui->virtual_display_view_port) { | ||||
| @ -267,7 +345,10 @@ void rpc_system_gui_free(void* ctx) { | ||||
|         rpc_gui->virtual_display_not_empty = false; | ||||
|     } | ||||
| 
 | ||||
|     gui_set_framebuffer_callback(rpc_gui->gui, NULL, NULL); | ||||
|     if(rpc_gui->is_streaming) { | ||||
|         gui_remove_framebuffer_callback( | ||||
|             rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context); | ||||
|     } | ||||
|     furi_record_close("gui"); | ||||
|     free(rpc_gui); | ||||
| } | ||||
|  | ||||
| @ -7,8 +7,8 @@ | ||||
| #include <flipper.pb.h> | ||||
| #include <cli/cli.h> | ||||
| 
 | ||||
| typedef void* (*RpcSystemAlloc)(Rpc*); | ||||
| typedef void (*RpcSystemFree)(void*); | ||||
| typedef void* (*RpcSystemAlloc)(RpcSession* session); | ||||
| typedef void (*RpcSystemFree)(void* context); | ||||
| typedef void (*PBMessageHandler)(const PB_Main* msg_request, void* context); | ||||
| 
 | ||||
| typedef struct { | ||||
| @ -17,15 +17,19 @@ typedef struct { | ||||
|     void* context; | ||||
| } RpcHandler; | ||||
| 
 | ||||
| void rpc_send_and_release(Rpc* rpc, PB_Main* main_message); | ||||
| void rpc_send_and_release_empty(Rpc* rpc, uint32_t command_id, PB_CommandStatus status); | ||||
| void rpc_add_handler(Rpc* rpc, pb_size_t message_tag, RpcHandler* handler); | ||||
| void rpc_send(RpcSession* session, PB_Main* main_message); | ||||
| 
 | ||||
| void* rpc_system_system_alloc(Rpc* rpc); | ||||
| void* rpc_system_storage_alloc(Rpc* rpc); | ||||
| void rpc_send_and_release(RpcSession* session, PB_Main* main_message); | ||||
| 
 | ||||
| void rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_CommandStatus status); | ||||
| 
 | ||||
| void rpc_add_handler(RpcSession* session, pb_size_t message_tag, RpcHandler* handler); | ||||
| 
 | ||||
| void* rpc_system_system_alloc(RpcSession* session); | ||||
| void* rpc_system_storage_alloc(RpcSession* session); | ||||
| void rpc_system_storage_free(void* ctx); | ||||
| void* rpc_system_app_alloc(Rpc* rpc); | ||||
| void* rpc_system_gui_alloc(Rpc* rpc); | ||||
| void* rpc_system_app_alloc(RpcSession* session); | ||||
| void* rpc_system_gui_alloc(RpcSession* session); | ||||
| void rpc_system_gui_free(void* ctx); | ||||
| 
 | ||||
| void rpc_print_message(const PB_Main* message); | ||||
|  | ||||
| @ -21,7 +21,7 @@ typedef enum { | ||||
| } RpcStorageState; | ||||
| 
 | ||||
| typedef struct { | ||||
|     Rpc* rpc; | ||||
|     RpcSession* session; | ||||
|     Storage* api; | ||||
|     File* file; | ||||
|     RpcStorageState state; | ||||
| @ -30,13 +30,16 @@ typedef struct { | ||||
| 
 | ||||
| void rpc_print_message(const PB_Main* message); | ||||
| 
 | ||||
| static void rpc_system_storage_reset_state(RpcStorageSystem* rpc_storage, bool send_error) { | ||||
| static void rpc_system_storage_reset_state( | ||||
|     RpcStorageSystem* rpc_storage, | ||||
|     RpcSession* session, | ||||
|     bool send_error) { | ||||
|     furi_assert(rpc_storage); | ||||
| 
 | ||||
|     if(rpc_storage->state != RpcStorageStateIdle) { | ||||
|         if(send_error) { | ||||
|             rpc_send_and_release_empty( | ||||
|                 rpc_storage->rpc, | ||||
|                 session, | ||||
|                 rpc_storage->current_command_id, | ||||
|                 PB_CommandStatus_ERROR_CONTINUOUS_COMMAND_INTERRUPTED); | ||||
|         } | ||||
| @ -102,7 +105,10 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex | ||||
|     furi_assert(request->which_content == PB_Main_storage_info_request_tag); | ||||
| 
 | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     rpc_system_storage_reset_state(rpc_storage, true); | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     rpc_system_storage_reset_state(rpc_storage, session, true); | ||||
| 
 | ||||
|     PB_Main* response = malloc(sizeof(PB_Main)); | ||||
|     response->command_id = request->command_id; | ||||
| @ -122,7 +128,7 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex | ||||
|         response->which_content = PB_Main_empty_tag; | ||||
|     } | ||||
| 
 | ||||
|     rpc_send_and_release(rpc_storage->rpc, response); | ||||
|     rpc_send_and_release(session, response); | ||||
|     free(response); | ||||
|     furi_record_close("storage"); | ||||
| } | ||||
| @ -133,7 +139,10 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex | ||||
|     furi_assert(request->which_content == PB_Main_storage_stat_request_tag); | ||||
| 
 | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     rpc_system_storage_reset_state(rpc_storage, true); | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     rpc_system_storage_reset_state(rpc_storage, session, true); | ||||
| 
 | ||||
|     PB_Main* response = malloc(sizeof(PB_Main)); | ||||
|     response->command_id = request->command_id; | ||||
| @ -156,13 +165,16 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex | ||||
|         response->content.storage_stat_response.file.size = fileinfo.size; | ||||
|     } | ||||
| 
 | ||||
|     rpc_send_and_release(rpc_storage->rpc, response); | ||||
|     rpc_send_and_release(session, response); | ||||
|     free(response); | ||||
|     furi_record_close("storage"); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_storage_list_root(const PB_Main* request, void* context) { | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     const char* hard_coded_dirs[] = {"any", "int", "ext"}; | ||||
| 
 | ||||
|     PB_Main response = { | ||||
| @ -183,7 +195,7 @@ static void rpc_system_storage_list_root(const PB_Main* request, void* context) | ||||
|         response.content.storage_list_response.file[i].name = str; | ||||
|     } | ||||
| 
 | ||||
|     rpc_send_and_release(rpc_storage->rpc, &response); | ||||
|     rpc_send_and_release(session, &response); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_storage_list_process(const PB_Main* request, void* context) { | ||||
| @ -192,7 +204,10 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex | ||||
|     furi_assert(request->which_content == PB_Main_storage_list_request_tag); | ||||
| 
 | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     rpc_system_storage_reset_state(rpc_storage, true); | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     rpc_system_storage_reset_state(rpc_storage, session, true); | ||||
| 
 | ||||
|     if(!strcmp(request->content.storage_list_request.path, "/")) { | ||||
|         rpc_system_storage_list_root(request, context); | ||||
| @ -226,7 +241,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex | ||||
|             if(i == COUNT_OF(list->file)) { | ||||
|                 list->file_count = i; | ||||
|                 response.has_next = true; | ||||
|                 rpc_send_and_release(rpc_storage->rpc, &response); | ||||
|                 rpc_send_and_release(session, &response); | ||||
|                 i = 0; | ||||
|             } | ||||
|             list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ? PB_Storage_File_FileType_DIR : | ||||
| @ -243,7 +258,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex | ||||
|     } | ||||
| 
 | ||||
|     response.has_next = false; | ||||
|     rpc_send_and_release(rpc_storage->rpc, &response); | ||||
|     rpc_send_and_release(session, &response); | ||||
| 
 | ||||
|     storage_dir_close(dir); | ||||
|     storage_file_free(dir); | ||||
| @ -253,10 +268,14 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex | ||||
| 
 | ||||
| static void rpc_system_storage_read_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(context); | ||||
|     furi_assert(request->which_content == PB_Main_storage_read_request_tag); | ||||
| 
 | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     rpc_system_storage_reset_state(rpc_storage, true); | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     rpc_system_storage_reset_state(rpc_storage, session, true); | ||||
| 
 | ||||
|     /* use same message memory to send reponse */ | ||||
|     PB_Main* response = malloc(sizeof(PB_Main)); | ||||
| @ -284,17 +303,17 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex | ||||
| 
 | ||||
|             if(result) { | ||||
|                 response->has_next = (size_left > 0); | ||||
|                 rpc_send_and_release(rpc_storage->rpc, response); | ||||
|                 rpc_send_and_release(session, response); | ||||
|             } | ||||
|         } while((size_left != 0) && result); | ||||
| 
 | ||||
|         if(!result) { | ||||
|             rpc_send_and_release_empty( | ||||
|                 rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file)); | ||||
|                 session, request->command_id, rpc_system_storage_get_file_error(file)); | ||||
|         } | ||||
|     } else { | ||||
|         rpc_send_and_release_empty( | ||||
|             rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file)); | ||||
|             session, request->command_id, rpc_system_storage_get_file_error(file)); | ||||
|     } | ||||
| 
 | ||||
|     free(response); | ||||
| @ -306,14 +325,18 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex | ||||
| 
 | ||||
| static void rpc_system_storage_write_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(context); | ||||
|     furi_assert(request->which_content == PB_Main_storage_write_request_tag); | ||||
| 
 | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     bool result = true; | ||||
| 
 | ||||
|     if((request->command_id != rpc_storage->current_command_id) && | ||||
|        (rpc_storage->state == RpcStorageStateWriting)) { | ||||
|         rpc_system_storage_reset_state(rpc_storage, true); | ||||
|         rpc_system_storage_reset_state(rpc_storage, session, true); | ||||
|     } | ||||
| 
 | ||||
|     if(rpc_storage->state != RpcStorageStateWriting) { | ||||
| @ -336,17 +359,15 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte | ||||
| 
 | ||||
|         if(result && !request->has_next) { | ||||
|             rpc_send_and_release_empty( | ||||
|                 rpc_storage->rpc, rpc_storage->current_command_id, PB_CommandStatus_OK); | ||||
|             rpc_system_storage_reset_state(rpc_storage, false); | ||||
|                 session, rpc_storage->current_command_id, PB_CommandStatus_OK); | ||||
|             rpc_system_storage_reset_state(rpc_storage, session, false); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(!result) { | ||||
|         rpc_send_and_release_empty( | ||||
|             rpc_storage->rpc, | ||||
|             rpc_storage->current_command_id, | ||||
|             rpc_system_storage_get_file_error(file)); | ||||
|         rpc_system_storage_reset_state(rpc_storage, false); | ||||
|             session, rpc_storage->current_command_id, rpc_system_storage_get_file_error(file)); | ||||
|         rpc_system_storage_reset_state(rpc_storage, session, false); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -373,8 +394,11 @@ static void rpc_system_storage_delete_process(const PB_Main* request, void* cont | ||||
|     furi_assert(request->which_content == PB_Main_storage_delete_request_tag); | ||||
|     furi_assert(context); | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     PB_CommandStatus status = PB_CommandStatus_ERROR; | ||||
|     rpc_system_storage_reset_state(rpc_storage, true); | ||||
|     rpc_system_storage_reset_state(rpc_storage, session, true); | ||||
| 
 | ||||
|     Storage* fs_api = furi_record_open("storage"); | ||||
| 
 | ||||
| @ -400,7 +424,7 @@ static void rpc_system_storage_delete_process(const PB_Main* request, void* cont | ||||
|     } | ||||
| 
 | ||||
|     furi_record_close("storage"); | ||||
|     rpc_send_and_release_empty(rpc_storage->rpc, request->command_id, status); | ||||
|     rpc_send_and_release_empty(session, request->command_id, status); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_storage_mkdir_process(const PB_Main* request, void* context) { | ||||
| @ -408,8 +432,11 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte | ||||
|     furi_assert(request->which_content == PB_Main_storage_mkdir_request_tag); | ||||
|     furi_assert(context); | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     PB_CommandStatus status; | ||||
|     rpc_system_storage_reset_state(rpc_storage, true); | ||||
|     rpc_system_storage_reset_state(rpc_storage, session, true); | ||||
| 
 | ||||
|     Storage* fs_api = furi_record_open("storage"); | ||||
|     char* path = request->content.storage_mkdir_request.path; | ||||
| @ -420,7 +447,7 @@ static void rpc_system_storage_mkdir_process(const PB_Main* request, void* conte | ||||
|         status = PB_CommandStatus_ERROR_INVALID_PARAMETERS; | ||||
|     } | ||||
|     furi_record_close("storage"); | ||||
|     rpc_send_and_release_empty(rpc_storage->rpc, request->command_id, status); | ||||
|     rpc_send_and_release_empty(session, request->command_id, status); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_storage_md5sum_process(const PB_Main* request, void* context) { | ||||
| @ -428,12 +455,15 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont | ||||
|     furi_assert(request->which_content == PB_Main_storage_md5sum_request_tag); | ||||
|     furi_assert(context); | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     rpc_system_storage_reset_state(rpc_storage, true); | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     rpc_system_storage_reset_state(rpc_storage, session, true); | ||||
| 
 | ||||
|     const char* filename = request->content.storage_md5sum_request.path; | ||||
|     if(!filename) { | ||||
|         rpc_send_and_release_empty( | ||||
|             rpc_storage->rpc, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); | ||||
|             session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
| @ -474,10 +504,10 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont | ||||
|         free(hash); | ||||
|         free(data); | ||||
|         storage_file_close(file); | ||||
|         rpc_send_and_release(rpc_storage->rpc, &response); | ||||
|         rpc_send_and_release(session, &response); | ||||
|     } else { | ||||
|         rpc_send_and_release_empty( | ||||
|             rpc_storage->rpc, request->command_id, rpc_system_storage_get_file_error(file)); | ||||
|             session, request->command_id, rpc_system_storage_get_file_error(file)); | ||||
|     } | ||||
| 
 | ||||
|     storage_file_free(file); | ||||
| @ -490,8 +520,11 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont | ||||
|     furi_assert(request->which_content == PB_Main_storage_rename_request_tag); | ||||
|     furi_assert(context); | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     PB_CommandStatus status; | ||||
|     rpc_system_storage_reset_state(rpc_storage, true); | ||||
|     rpc_system_storage_reset_state(rpc_storage, session, true); | ||||
| 
 | ||||
|     Storage* fs_api = furi_record_open("storage"); | ||||
| 
 | ||||
| @ -502,15 +535,15 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont | ||||
|     status = rpc_system_storage_get_error(error); | ||||
| 
 | ||||
|     furi_record_close("storage"); | ||||
|     rpc_send_and_release_empty(rpc_storage->rpc, request->command_id, status); | ||||
|     rpc_send_and_release_empty(session, request->command_id, status); | ||||
| } | ||||
| 
 | ||||
| void* rpc_system_storage_alloc(Rpc* rpc) { | ||||
|     furi_assert(rpc); | ||||
| void* rpc_system_storage_alloc(RpcSession* session) { | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     RpcStorageSystem* rpc_storage = malloc(sizeof(RpcStorageSystem)); | ||||
|     rpc_storage->api = furi_record_open("storage"); | ||||
|     rpc_storage->rpc = rpc; | ||||
|     rpc_storage->session = session; | ||||
|     rpc_storage->state = RpcStorageStateIdle; | ||||
| 
 | ||||
|     RpcHandler rpc_handler = { | ||||
| @ -520,37 +553,40 @@ void* rpc_system_storage_alloc(Rpc* rpc) { | ||||
|     }; | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_storage_info_process; | ||||
|     rpc_add_handler(rpc, PB_Main_storage_info_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_storage_info_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_storage_stat_process; | ||||
|     rpc_add_handler(rpc, PB_Main_storage_stat_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_storage_stat_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_storage_list_process; | ||||
|     rpc_add_handler(rpc, PB_Main_storage_list_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_storage_list_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_storage_read_process; | ||||
|     rpc_add_handler(rpc, PB_Main_storage_read_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_storage_read_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_storage_write_process; | ||||
|     rpc_add_handler(rpc, PB_Main_storage_write_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_storage_write_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_storage_delete_process; | ||||
|     rpc_add_handler(rpc, PB_Main_storage_delete_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_storage_delete_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_storage_mkdir_process; | ||||
|     rpc_add_handler(rpc, PB_Main_storage_mkdir_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_storage_mkdir_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_storage_md5sum_process; | ||||
|     rpc_add_handler(rpc, PB_Main_storage_md5sum_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_storage_md5sum_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_storage_rename_process; | ||||
|     rpc_add_handler(rpc, PB_Main_storage_rename_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_storage_rename_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     return rpc_storage; | ||||
| } | ||||
| 
 | ||||
| void rpc_system_storage_free(void* ctx) { | ||||
|     RpcStorageSystem* rpc_storage = ctx; | ||||
|     rpc_system_storage_reset_state(rpc_storage, false); | ||||
| void rpc_system_storage_free(void* context) { | ||||
|     RpcStorageSystem* rpc_storage = context; | ||||
|     RpcSession* session = rpc_storage->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     rpc_system_storage_reset_state(rpc_storage, session, false); | ||||
|     free(rpc_storage); | ||||
| } | ||||
|  | ||||
| @ -6,40 +6,42 @@ | ||||
| 
 | ||||
| #include "rpc_i.h" | ||||
| 
 | ||||
| static void rpc_system_system_ping_process(const PB_Main* msg_request, void* context) { | ||||
|     furi_assert(msg_request); | ||||
|     furi_assert(msg_request->which_content == PB_Main_system_ping_request_tag); | ||||
|     furi_assert(context); | ||||
|     Rpc* rpc = context; | ||||
| static void rpc_system_system_ping_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_system_ping_request_tag); | ||||
| 
 | ||||
|     if(msg_request->has_next) { | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     if(request->has_next) { | ||||
|         rpc_send_and_release_empty( | ||||
|             rpc, msg_request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); | ||||
|             session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     PB_Main msg_response = PB_Main_init_default; | ||||
|     msg_response.has_next = false; | ||||
|     msg_response.command_status = PB_CommandStatus_OK; | ||||
|     msg_response.command_id = msg_request->command_id; | ||||
|     msg_response.which_content = PB_Main_system_ping_response_tag; | ||||
|     PB_Main response = PB_Main_init_default; | ||||
|     response.has_next = false; | ||||
|     response.command_status = PB_CommandStatus_OK; | ||||
|     response.command_id = request->command_id; | ||||
|     response.which_content = PB_Main_system_ping_response_tag; | ||||
| 
 | ||||
|     const PB_System_PingRequest* request = &msg_request->content.system_ping_request; | ||||
|     PB_System_PingResponse* response = &msg_response.content.system_ping_response; | ||||
|     if(request->data && (request->data->size > 0)) { | ||||
|         response->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(request->data->size)); | ||||
|         memcpy(response->data->bytes, request->data->bytes, request->data->size); | ||||
|         response->data->size = request->data->size; | ||||
|     const PB_System_PingRequest* ping_request = &request->content.system_ping_request; | ||||
|     PB_System_PingResponse* ping_response = &response.content.system_ping_response; | ||||
|     if(ping_request->data && (ping_request->data->size > 0)) { | ||||
|         ping_response->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(ping_request->data->size)); | ||||
|         memcpy(ping_response->data->bytes, ping_request->data->bytes, ping_request->data->size); | ||||
|         ping_response->data->size = ping_request->data->size; | ||||
|     } | ||||
| 
 | ||||
|     rpc_send_and_release(rpc, &msg_response); | ||||
|     rpc_send_and_release(session, &response); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_system_reboot_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_system_reboot_request_tag); | ||||
|     furi_assert(context); | ||||
|     Rpc* rpc = context; | ||||
| 
 | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     const int mode = request->content.system_reboot_request.mode; | ||||
| 
 | ||||
| @ -49,12 +51,12 @@ static void rpc_system_system_reboot_process(const PB_Main* request, void* conte | ||||
|         power_reboot(PowerBootModeDfu); | ||||
|     } else { | ||||
|         rpc_send_and_release_empty( | ||||
|             rpc, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); | ||||
|             session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| typedef struct { | ||||
|     Rpc* rpc; | ||||
|     RpcSession* session; | ||||
|     PB_Main* response; | ||||
| } RpcSystemSystemDeviceInfoContext; | ||||
| 
 | ||||
| @ -65,7 +67,6 @@ static void rpc_system_system_device_info_callback( | ||||
|     void* context) { | ||||
|     furi_assert(key); | ||||
|     furi_assert(value); | ||||
|     furi_assert(context); | ||||
|     RpcSystemSystemDeviceInfoContext* ctx = context; | ||||
| 
 | ||||
|     char* str_key = strdup(key); | ||||
| @ -75,14 +76,15 @@ static void rpc_system_system_device_info_callback( | ||||
|     ctx->response->content.system_device_info_response.key = str_key; | ||||
|     ctx->response->content.system_device_info_response.value = str_value; | ||||
| 
 | ||||
|     rpc_send_and_release(ctx->rpc, ctx->response); | ||||
|     rpc_send_and_release(ctx->session, ctx->response); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_system_device_info_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_system_device_info_request_tag); | ||||
|     furi_assert(context); | ||||
|     Rpc* rpc = context; | ||||
| 
 | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     PB_Main* response = malloc(sizeof(PB_Main)); | ||||
|     response->command_id = request->command_id; | ||||
| @ -90,10 +92,9 @@ static void rpc_system_system_device_info_process(const PB_Main* request, void* | ||||
|     response->command_status = PB_CommandStatus_OK; | ||||
| 
 | ||||
|     RpcSystemSystemDeviceInfoContext device_info_context = { | ||||
|         .rpc = rpc, | ||||
|         .session = session, | ||||
|         .response = response, | ||||
|     }; | ||||
| 
 | ||||
|     furi_hal_info_get(rpc_system_system_device_info_callback, &device_info_context); | ||||
| 
 | ||||
|     free(response); | ||||
| @ -102,8 +103,9 @@ static void rpc_system_system_device_info_process(const PB_Main* request, void* | ||||
| static void rpc_system_system_get_datetime_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_system_get_datetime_request_tag); | ||||
|     furi_assert(context); | ||||
|     Rpc* rpc = context; | ||||
| 
 | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     FuriHalRtcDateTime datetime; | ||||
|     furi_hal_rtc_get_datetime(&datetime); | ||||
| @ -121,19 +123,20 @@ static void rpc_system_system_get_datetime_process(const PB_Main* request, void* | ||||
|     response->content.system_get_datetime_response.datetime.year = datetime.year; | ||||
|     response->content.system_get_datetime_response.datetime.weekday = datetime.weekday; | ||||
| 
 | ||||
|     rpc_send_and_release(rpc, response); | ||||
|     rpc_send_and_release(session, response); | ||||
|     free(response); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_system_set_datetime_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_system_set_datetime_request_tag); | ||||
|     furi_assert(context); | ||||
|     Rpc* rpc = context; | ||||
| 
 | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     if(!request->content.system_set_datetime_request.has_datetime) { | ||||
|         rpc_send_and_release_empty( | ||||
|             rpc, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); | ||||
|             session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
| @ -147,38 +150,43 @@ static void rpc_system_system_set_datetime_process(const PB_Main* request, void* | ||||
|     datetime.weekday = request->content.system_set_datetime_request.datetime.weekday; | ||||
|     furi_hal_rtc_set_datetime(&datetime); | ||||
| 
 | ||||
|     rpc_send_and_release_empty(rpc, request->command_id, PB_CommandStatus_OK); | ||||
|     rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_system_factory_reset_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_system_factory_reset_request_tag); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); | ||||
|     power_reboot(PowerBootModeNormal); | ||||
| 
 | ||||
|     (void)session; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|     rpc_system_system_play_audiovisual_alert_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_system_play_audiovisual_alert_request_tag); | ||||
|     furi_assert(context); | ||||
|     Rpc* rpc = context; | ||||
| 
 | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     NotificationApp* notification = furi_record_open("notification"); | ||||
|     notification_message(notification, &sequence_audiovisual_alert); | ||||
|     furi_record_close("notification"); | ||||
| 
 | ||||
|     rpc_send_and_release_empty(rpc, request->command_id, PB_CommandStatus_OK); | ||||
|     rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_system_protobuf_version_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
|     furi_assert(request->which_content == PB_Main_system_protobuf_version_request_tag); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     Rpc* rpc = context; | ||||
|     RpcSession* session = (RpcSession*)context; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     PB_Main* response = malloc(sizeof(PB_Main)); | ||||
|     response->command_id = request->command_id; | ||||
| @ -190,40 +198,40 @@ static void rpc_system_system_protobuf_version_process(const PB_Main* request, v | ||||
|     response->content.system_protobuf_version_response.major = PROTOBUF_MAJOR_VERSION; | ||||
|     response->content.system_protobuf_version_response.minor = PROTOBUF_MINOR_VERSION; | ||||
| 
 | ||||
|     rpc_send_and_release(rpc, response); | ||||
|     rpc_send_and_release(session, response); | ||||
|     free(response); | ||||
| } | ||||
| 
 | ||||
| void* rpc_system_system_alloc(Rpc* rpc) { | ||||
| void* rpc_system_system_alloc(RpcSession* session) { | ||||
|     RpcHandler rpc_handler = { | ||||
|         .message_handler = NULL, | ||||
|         .decode_submessage = NULL, | ||||
|         .context = rpc, | ||||
|         .context = session, | ||||
|     }; | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_system_ping_process; | ||||
|     rpc_add_handler(rpc, PB_Main_system_ping_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_system_ping_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_system_reboot_process; | ||||
|     rpc_add_handler(rpc, PB_Main_system_reboot_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_system_reboot_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_system_device_info_process; | ||||
|     rpc_add_handler(rpc, PB_Main_system_device_info_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_system_device_info_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_system_factory_reset_process; | ||||
|     rpc_add_handler(rpc, PB_Main_system_factory_reset_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_system_factory_reset_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_system_get_datetime_process; | ||||
|     rpc_add_handler(rpc, PB_Main_system_get_datetime_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_system_get_datetime_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_system_set_datetime_process; | ||||
|     rpc_add_handler(rpc, PB_Main_system_set_datetime_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_system_set_datetime_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_system_play_audiovisual_alert_process; | ||||
|     rpc_add_handler(rpc, PB_Main_system_play_audiovisual_alert_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_system_play_audiovisual_alert_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     rpc_handler.message_handler = rpc_system_system_protobuf_version_process; | ||||
|     rpc_add_handler(rpc, PB_Main_system_protobuf_version_request_tag, &rpc_handler); | ||||
|     rpc_add_handler(session, PB_Main_system_protobuf_version_request_tag, &rpc_handler); | ||||
| 
 | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| @ -23,12 +23,12 @@ static int32_t subghz_chat_worker_thread(void* context) { | ||||
|     SubGhzChatWorker* instance = context; | ||||
|     FURI_LOG_I(TAG, "Worker start"); | ||||
|     char c; | ||||
|     SubghzChatEvent event; | ||||
|     event.event = SubghzChatEventUserEntrance; | ||||
|     SubGhzChatEvent event; | ||||
|     event.event = SubGhzChatEventUserEntrance; | ||||
|     osMessageQueuePut(instance->event_queue, &event, 0, 0); | ||||
|     while(instance->worker_running) { | ||||
|         if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 1000) == 1) { | ||||
|             event.event = SubghzChatEventInputData; | ||||
|             event.event = SubGhzChatEventInputData; | ||||
|             event.c = c; | ||||
|             osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); | ||||
|         } | ||||
| @ -41,13 +41,13 @@ static int32_t subghz_chat_worker_thread(void* context) { | ||||
| static void subghz_chat_worker_update_rx_event_chat(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhzChatWorker* instance = context; | ||||
|     SubghzChatEvent event; | ||||
|     SubGhzChatEvent event; | ||||
|     if((millis() - instance->last_time_rx_data) > SUBGHZ_CHAT_WORKER_TIMEOUT_BETWEEN_MESSAGES) { | ||||
|         event.event = SubghzChatEventNewMessage; | ||||
|         event.event = SubGhzChatEventNewMessage; | ||||
|         osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); | ||||
|     } | ||||
|     instance->last_time_rx_data = millis(); | ||||
|     event.event = SubghzChatEventRXData; | ||||
|     event.event = SubGhzChatEventRXData; | ||||
|     osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); | ||||
| } | ||||
| 
 | ||||
| @ -55,12 +55,12 @@ SubGhzChatWorker* subghz_chat_worker_alloc() { | ||||
|     SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker)); | ||||
| 
 | ||||
|     instance->thread = furi_thread_alloc(); | ||||
|     furi_thread_set_name(instance->thread, "SubghzChat"); | ||||
|     furi_thread_set_name(instance->thread, "SubGhzChat"); | ||||
|     furi_thread_set_stack_size(instance->thread, 2048); | ||||
|     furi_thread_set_context(instance->thread, instance); | ||||
|     furi_thread_set_callback(instance->thread, subghz_chat_worker_thread); | ||||
|     instance->subghz_txrx = subghz_tx_rx_worker_alloc(); | ||||
|     instance->event_queue = osMessageQueueNew(80, sizeof(SubghzChatEvent), NULL); | ||||
|     instance->event_queue = osMessageQueueNew(80, sizeof(SubGhzChatEvent), NULL); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| @ -109,18 +109,18 @@ bool subghz_chat_worker_is_running(SubGhzChatWorker* instance) { | ||||
|     return instance->worker_running; | ||||
| } | ||||
| 
 | ||||
| SubghzChatEvent subghz_chat_worker_get_event_chat(SubGhzChatWorker* instance) { | ||||
| SubGhzChatEvent subghz_chat_worker_get_event_chat(SubGhzChatWorker* instance) { | ||||
|     furi_assert(instance); | ||||
|     SubghzChatEvent event; | ||||
|     SubGhzChatEvent event; | ||||
|     if(osMessageQueueGet(instance->event_queue, &event, NULL, osWaitForever) == osOK) { | ||||
|         return event; | ||||
|     } else { | ||||
|         event.event = SubghzChatEventNoEvent; | ||||
|         event.event = SubGhzChatEventNoEvent; | ||||
|         return event; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void subghz_chat_worker_put_event_chat(SubGhzChatWorker* instance, SubghzChatEvent* event) { | ||||
| void subghz_chat_worker_put_event_chat(SubGhzChatWorker* instance, SubGhzChatEvent* event) { | ||||
|     furi_assert(instance); | ||||
|     osMessageQueuePut(instance->event_queue, event, 0, osWaitForever); | ||||
| } | ||||
|  | ||||
| @ -4,26 +4,26 @@ | ||||
| typedef struct SubGhzChatWorker SubGhzChatWorker; | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubghzChatEventNoEvent, | ||||
|     SubghzChatEventUserEntrance, | ||||
|     SubghzChatEventUserExit, | ||||
|     SubghzChatEventInputData, | ||||
|     SubghzChatEventRXData, | ||||
|     SubghzChatEventNewMessage, | ||||
| } SubghzChatEventType; | ||||
|     SubGhzChatEventNoEvent, | ||||
|     SubGhzChatEventUserEntrance, | ||||
|     SubGhzChatEventUserExit, | ||||
|     SubGhzChatEventInputData, | ||||
|     SubGhzChatEventRXData, | ||||
|     SubGhzChatEventNewMessage, | ||||
| } SubGhzChatEventType; | ||||
| 
 | ||||
| typedef struct { | ||||
|     SubghzChatEventType event; | ||||
|     SubGhzChatEventType event; | ||||
|     char c; | ||||
| } SubghzChatEvent; | ||||
| } SubGhzChatEvent; | ||||
| 
 | ||||
| SubGhzChatWorker* subghz_chat_worker_alloc(); | ||||
| void subghz_chat_worker_free(SubGhzChatWorker* instance); | ||||
| bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency); | ||||
| void subghz_chat_worker_stop(SubGhzChatWorker* instance); | ||||
| bool subghz_chat_worker_is_running(SubGhzChatWorker* instance); | ||||
| SubghzChatEvent subghz_chat_worker_get_event_chat(SubGhzChatWorker* instance); | ||||
| void subghz_chat_worker_put_event_chat(SubGhzChatWorker* instance, SubghzChatEvent* event); | ||||
| SubGhzChatEvent subghz_chat_worker_get_event_chat(SubGhzChatWorker* instance); | ||||
| void subghz_chat_worker_put_event_chat(SubGhzChatWorker* instance, SubGhzChatEvent* event); | ||||
| size_t subghz_chat_worker_available(SubGhzChatWorker* instance); | ||||
| size_t subghz_chat_worker_read(SubGhzChatWorker* instance, uint8_t* data, size_t size); | ||||
| bool subghz_chat_worker_write(SubGhzChatWorker* instance, uint8_t* data, size_t size); | ||||
|  | ||||
| @ -1,46 +1,46 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubghzCustomEventManagerNoSet = 0, | ||||
|     SubghzCustomEventManagerSet, | ||||
|     SubghzCustomEventManagerSetRAW, | ||||
|     SubGhzCustomEventManagerNoSet = 0, | ||||
|     SubGhzCustomEventManagerSet, | ||||
|     SubGhzCustomEventManagerSetRAW, | ||||
| 
 | ||||
|     SubghzCustomEventSceneDeleteSuccess = 100, | ||||
|     SubghzCustomEventSceneDelete, | ||||
|     SubghzCustomEventSceneDeleteRAW, | ||||
|     SubghzCustomEventSceneDeleteRAWBack, | ||||
|     SubGhzCustomEventSceneDeleteSuccess = 100, | ||||
|     SubGhzCustomEventSceneDelete, | ||||
|     SubGhzCustomEventSceneDeleteRAW, | ||||
|     SubGhzCustomEventSceneDeleteRAWBack, | ||||
| 
 | ||||
|     SubghzCustomEventSceneReceiverInfoTxStart, | ||||
|     SubghzCustomEventSceneReceiverInfoTxStop, | ||||
|     SubghzCustomEventSceneReceiverInfoSave, | ||||
|     SubghzCustomEventSceneSaveName, | ||||
|     SubghzCustomEventSceneSaveSuccess, | ||||
|     SubghzCustomEventSceneShowErrorBack, | ||||
|     SubghzCustomEventSceneShowErrorOk, | ||||
|     SubghzCustomEventSceneShowErrorSub, | ||||
|     SubghzCustomEventSceneShowOnlyRX, | ||||
|     SubGhzCustomEventSceneReceiverInfoTxStart, | ||||
|     SubGhzCustomEventSceneReceiverInfoTxStop, | ||||
|     SubGhzCustomEventSceneReceiverInfoSave, | ||||
|     SubGhzCustomEventSceneSaveName, | ||||
|     SubGhzCustomEventSceneSaveSuccess, | ||||
|     SubGhzCustomEventSceneShowErrorBack, | ||||
|     SubGhzCustomEventSceneShowErrorOk, | ||||
|     SubGhzCustomEventSceneShowErrorSub, | ||||
|     SubGhzCustomEventSceneShowOnlyRX, | ||||
| 
 | ||||
|     SubghzCustomEventSceneExit, | ||||
|     SubghzCustomEventSceneStay, | ||||
|     SubGhzCustomEventSceneExit, | ||||
|     SubGhzCustomEventSceneStay, | ||||
| 
 | ||||
|     SubghzCustomEventViewReceverOK, | ||||
|     SubghzCustomEventViewReceverConfig, | ||||
|     SubghzCustomEventViewReceverBack, | ||||
|     SubGhzCustomEventViewReceverOK, | ||||
|     SubGhzCustomEventViewReceverConfig, | ||||
|     SubGhzCustomEventViewReceverBack, | ||||
| 
 | ||||
|     SubghzCustomEventViewReadRAWBack, | ||||
|     SubghzCustomEventViewReadRAWIDLE, | ||||
|     SubghzCustomEventViewReadRAWREC, | ||||
|     SubghzCustomEventViewReadRAWConfig, | ||||
|     SubghzCustomEventViewReadRAWErase, | ||||
|     SubghzCustomEventViewReadRAWSendStart, | ||||
|     SubghzCustomEventViewReadRAWSendStop, | ||||
|     SubghzCustomEventViewReadRAWSave, | ||||
|     SubghzCustomEventViewReadRAWVibro, | ||||
|     SubghzCustomEventViewReadRAWTXRXStop, | ||||
|     SubghzCustomEventViewReadRAWMore, | ||||
|     SubGhzCustomEventViewReadRAWBack, | ||||
|     SubGhzCustomEventViewReadRAWIDLE, | ||||
|     SubGhzCustomEventViewReadRAWREC, | ||||
|     SubGhzCustomEventViewReadRAWConfig, | ||||
|     SubGhzCustomEventViewReadRAWErase, | ||||
|     SubGhzCustomEventViewReadRAWSendStart, | ||||
|     SubGhzCustomEventViewReadRAWSendStop, | ||||
|     SubGhzCustomEventViewReadRAWSave, | ||||
|     SubGhzCustomEventViewReadRAWVibro, | ||||
|     SubGhzCustomEventViewReadRAWTXRXStop, | ||||
|     SubGhzCustomEventViewReadRAWMore, | ||||
| 
 | ||||
|     SubghzCustomEventViewTransmitterBack, | ||||
|     SubghzCustomEventViewTransmitterSendStart, | ||||
|     SubghzCustomEventViewTransmitterSendStop, | ||||
|     SubghzCustomEventViewTransmitterError, | ||||
| } SubghzCustomEvent; | ||||
|     SubGhzCustomEventViewTransmitterBack, | ||||
|     SubGhzCustomEventViewTransmitterSendStart, | ||||
|     SubGhzCustomEventViewTransmitterSendStop, | ||||
|     SubGhzCustomEventViewTransmitterError, | ||||
| } SubGhzCustomEvent; | ||||
|  | ||||
| @ -145,7 +145,7 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc() { | ||||
|     SubGhzFrequencyAnalyzerWorker* instance = malloc(sizeof(SubGhzFrequencyAnalyzerWorker)); | ||||
| 
 | ||||
|     instance->thread = furi_thread_alloc(); | ||||
|     furi_thread_set_name(instance->thread, "SubghzFAWorker"); | ||||
|     furi_thread_set_name(instance->thread, "SubGhzFAWorker"); | ||||
|     furi_thread_set_stack_size(instance->thread, 2048); | ||||
|     furi_thread_set_context(instance->thread, instance); | ||||
|     furi_thread_set_callback(instance->thread, subghz_frequency_analyzer_worker_thread); | ||||
|  | ||||
| @ -5,7 +5,7 @@ void subghz_scene_delete_callback(GuiButtonType result, InputType type, void* co | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { | ||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneDelete); | ||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneDelete); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -31,8 +31,7 @@ void subghz_scene_delete_on_enter(void* context) { | ||||
|         AlignTop, | ||||
|         FontSecondary, | ||||
|         string_get_cstr(modulation_str)); | ||||
| 
 | ||||
|     subghz->txrx->protocol_result->to_string(subghz->txrx->protocol_result, text); | ||||
|     subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, text); | ||||
|     widget_add_string_multiline_element( | ||||
|         subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, string_get_cstr(text)); | ||||
| 
 | ||||
| @ -43,13 +42,13 @@ void subghz_scene_delete_on_enter(void* context) { | ||||
|     widget_add_button_element( | ||||
|         subghz->widget, GuiButtonTypeRight, "Delete", subghz_scene_delete_callback, subghz); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubghzCustomEventSceneDelete) { | ||||
|         if(event.event == SubGhzCustomEventSceneDelete) { | ||||
|             strcpy(subghz->file_name_tmp, subghz->file_name); | ||||
|             if(subghz_delete_file(subghz)) { | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); | ||||
|  | ||||
| @ -6,10 +6,10 @@ void subghz_scene_delete_raw_callback(GuiButtonType result, InputType type, void | ||||
|     SubGhz* subghz = context; | ||||
|     if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { | ||||
|         view_dispatcher_send_custom_event( | ||||
|             subghz->view_dispatcher, SubghzCustomEventSceneDeleteRAW); | ||||
|             subghz->view_dispatcher, SubGhzCustomEventSceneDeleteRAW); | ||||
|     } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { | ||||
|         view_dispatcher_send_custom_event( | ||||
|             subghz->view_dispatcher, SubghzCustomEventSceneDeleteRAWBack); | ||||
|             subghz->view_dispatcher, SubGhzCustomEventSceneDeleteRAWBack); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -49,13 +49,13 @@ void subghz_scene_delete_raw_on_enter(void* context) { | ||||
|     widget_add_button_element( | ||||
|         subghz->widget, GuiButtonTypeLeft, "Back", subghz_scene_delete_raw_callback, subghz); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubghzCustomEventSceneDeleteRAW) { | ||||
|         if(event.event == SubGhzCustomEventSceneDeleteRAW) { | ||||
|             strcpy(subghz->file_name_tmp, subghz->file_name); | ||||
|             if(subghz_delete_file(subghz)) { | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); | ||||
| @ -64,7 +64,7 @@ bool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|                     subghz->scene_manager, SubGhzSceneStart); | ||||
|             } | ||||
|             return true; | ||||
|         } else if(event.event == SubghzCustomEventSceneDeleteRAWBack) { | ||||
|         } else if(event.event == SubGhzCustomEventSceneDeleteRAWBack) { | ||||
|             return scene_manager_previous_scene(subghz->scene_manager); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
| void subghz_scene_delete_success_popup_callback(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_send_custom_event( | ||||
|         subghz->view_dispatcher, SubghzCustomEventSceneDeleteSuccess); | ||||
|         subghz->view_dispatcher, SubGhzCustomEventSceneDeleteSuccess); | ||||
| } | ||||
| 
 | ||||
| void subghz_scene_delete_success_on_enter(void* context) { | ||||
| @ -18,14 +18,14 @@ void subghz_scene_delete_success_on_enter(void* context) { | ||||
|     popup_set_context(popup, subghz); | ||||
|     popup_set_callback(popup, subghz_scene_delete_success_popup_callback); | ||||
|     popup_enable_timeout(popup); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubghzCustomEventSceneDeleteSuccess) { | ||||
|         if(event.event == SubGhzCustomEventSceneDeleteSuccess) { | ||||
|             if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                    subghz->scene_manager, SubGhzSceneSaved)) { | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| #include "../views/subghz_frequency_analyzer.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void subghz_scene_frequency_analyzer_callback(SubghzCustomEvent event, void* context) { | ||||
| void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, event); | ||||
| @ -13,7 +13,7 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) { | ||||
|     DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); | ||||
|     subghz_frequency_analyzer_set_callback( | ||||
|         subghz->subghz_frequency_analyzer, subghz_scene_frequency_analyzer_callback, subghz); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewFrequencyAnalyzer); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdFrequencyAnalyzer); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) { | ||||
|  | ||||
| @ -30,7 +30,7 @@ void subghz_scene_more_raw_on_enter(void* context) { | ||||
|     submenu_set_selected_item( | ||||
|         subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneMoreRAW)); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) { | ||||
| @ -39,7 +39,7 @@ bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexDelete) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet); | ||||
|                 subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); | ||||
|             scene_manager_set_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW); | ||||
|  | ||||
| @ -6,9 +6,9 @@ void subghz_scene_need_saving_callback(GuiButtonType result, InputType type, voi | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { | ||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneStay); | ||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneStay); | ||||
|     } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { | ||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneExit); | ||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -31,7 +31,7 @@ void subghz_scene_need_saving_on_enter(void* context) { | ||||
|     widget_add_button_element( | ||||
|         subghz->widget, GuiButtonTypeLeft, "Exit", subghz_scene_need_saving_callback, subghz); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { | ||||
| @ -41,11 +41,11 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { | ||||
|         scene_manager_previous_scene(subghz->scene_manager); | ||||
|         return true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubghzCustomEventSceneStay) { | ||||
|         if(event.event == SubGhzCustomEventSceneStay) { | ||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; | ||||
|             scene_manager_previous_scene(subghz->scene_manager); | ||||
|             return true; | ||||
|         } else if(event.event == SubghzCustomEventSceneExit) { | ||||
|         } else if(event.event == SubGhzCustomEventSceneExit) { | ||||
|             if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) { | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|                 scene_manager_search_and_switch_to_previous_scene( | ||||
|  | ||||
| @ -1,35 +1,35 @@ | ||||
| #include "../subghz_i.h" | ||||
| #include "../views/subghz_read_raw.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| #include <lib/subghz/protocols/subghz_protocol_raw.h> | ||||
| #include <lib/subghz/subghz_parser.h> | ||||
| #include <lib/subghz/protocols/raw.h> | ||||
| #include <lib/toolbox/path.h> | ||||
| 
 | ||||
| #define RAW_FILE_NAME "Raw_signal_" | ||||
| #define TAG "SubGhzSceneReadRAW" | ||||
| 
 | ||||
| bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { | ||||
|     bool ret = false; | ||||
|     //set the path to read the file
 | ||||
|     if(strcmp( | ||||
|            subghz_protocol_raw_get_last_file_name( | ||||
|                (SubGhzProtocolRAW*)subghz->txrx->protocol_result), | ||||
|            "")) { | ||||
|     string_t temp_str; | ||||
|         string_init_printf( | ||||
|             temp_str, | ||||
|             "%s", | ||||
|             subghz_protocol_raw_get_last_file_name( | ||||
|                 (SubGhzProtocolRAW*)subghz->txrx->protocol_result)); | ||||
|     string_init(temp_str); | ||||
|     do { | ||||
|         if(!flipper_format_rewind(subghz->txrx->fff_data)) { | ||||
|             FURI_LOG_E(TAG, "Rewind error"); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         if(!flipper_format_read_string(subghz->txrx->fff_data, "File_name", temp_str)) { | ||||
|             FURI_LOG_E(TAG, "Missing File_name"); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         path_extract_filename_no_ext(string_get_cstr(temp_str), temp_str); | ||||
|         strcpy(subghz->file_name, string_get_cstr(temp_str)); | ||||
|         string_printf( | ||||
|             temp_str, "%s/%s%s", SUBGHZ_APP_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION); | ||||
| 
 | ||||
|         subghz_protocol_raw_set_last_file_name( | ||||
|             (SubGhzProtocolRAW*)subghz->txrx->protocol_result, string_get_cstr(temp_str)); | ||||
|         string_clear(temp_str); | ||||
|         ret = true; | ||||
|     } | ||||
|     } while(false); | ||||
| 
 | ||||
|     string_clear(temp_str); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| @ -51,7 +51,7 @@ static void subghz_scene_read_raw_update_statusbar(void* context) { | ||||
|     string_clear(modulation_str); | ||||
| } | ||||
| 
 | ||||
| void subghz_scene_read_raw_callback(SubghzCustomEvent event, void* context) { | ||||
| void subghz_scene_read_raw_callback(SubGhzCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, event); | ||||
| @ -61,7 +61,7 @@ void subghz_scene_read_raw_callback_end_tx(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_send_custom_event( | ||||
|         subghz->view_dispatcher, SubghzCustomEventViewReadRAWSendStop); | ||||
|         subghz->view_dispatcher, SubGhzCustomEventViewReadRAWSendStop); | ||||
| } | ||||
| 
 | ||||
| void subghz_scene_read_raw_on_enter(void* context) { | ||||
| @ -69,46 +69,43 @@ void subghz_scene_read_raw_on_enter(void* context) { | ||||
| 
 | ||||
|     switch(subghz->txrx->rx_key_state) { | ||||
|     case SubGhzRxKeyStateBack: | ||||
|         subghz_read_raw_set_status(subghz->subghz_read_raw, SubghzReadRAWStatusIDLE, ""); | ||||
|         subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, ""); | ||||
|         break; | ||||
|     case SubGhzRxKeyStateRAWLoad: | ||||
|         subghz_read_raw_set_status( | ||||
|             subghz->subghz_read_raw, SubghzReadRAWStatusLoadKeyTX, subghz->file_name); | ||||
|             subghz->subghz_read_raw, SubGhzReadRAWStatusLoadKeyTX, subghz->file_name); | ||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|         break; | ||||
|     case SubGhzRxKeyStateRAWSave: | ||||
|         subghz_read_raw_set_status( | ||||
|             subghz->subghz_read_raw, SubghzReadRAWStatusSaveKey, subghz->file_name); | ||||
|             subghz->subghz_read_raw, SubGhzReadRAWStatusSaveKey, subghz->file_name); | ||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|         break; | ||||
|     default: | ||||
|         subghz_read_raw_set_status(subghz->subghz_read_raw, SubghzReadRAWStatusStart, ""); | ||||
|         subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusStart, ""); | ||||
|         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     subghz_scene_read_raw_update_statusbar(subghz); | ||||
| 
 | ||||
|     //set callback view raw
 | ||||
|     subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz); | ||||
| 
 | ||||
|     subghz->txrx->protocol_result = subghz_parser_get_by_name(subghz->txrx->parser, "RAW"); | ||||
|     furi_assert(subghz->txrx->protocol_result); | ||||
|     subghz->txrx->decoder_result = | ||||
|         subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "RAW"); | ||||
|     furi_assert(subghz->txrx->decoder_result); | ||||
| 
 | ||||
|     subghz_worker_set_pair_callback( | ||||
|         subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_parser_raw_parse); | ||||
| 
 | ||||
|     subghz_protocol_raw_file_encoder_worker_set_callback_end( | ||||
|         (SubGhzProtocolRAW*)subghz->txrx->protocol_result, | ||||
|         subghz_scene_read_raw_callback_end_tx, | ||||
|         subghz); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReadRAW); | ||||
|     //set filter RAW feed
 | ||||
|     subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_RAW); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         switch(event.event) { | ||||
|         case SubghzCustomEventViewReadRAWBack: | ||||
|         case SubGhzCustomEventViewReadRAWBack: | ||||
|             //Stop TX
 | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|                 subghz_tx_stop(subghz); | ||||
| @ -121,7 +118,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             }; | ||||
|             //Stop save file
 | ||||
|             subghz_protocol_raw_save_to_file_stop( | ||||
|                 (SubGhzProtocolRAW*)subghz->txrx->protocol_result); | ||||
|                 (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             //needed save?
 | ||||
|             if((subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) || | ||||
| @ -144,7 +141,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             return true; | ||||
|             break; | ||||
| 
 | ||||
|         case SubghzCustomEventViewReadRAWTXRXStop: | ||||
|         case SubGhzCustomEventViewReadRAWTXRXStop: | ||||
|             //Stop TX
 | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|                 subghz_tx_stop(subghz); | ||||
| @ -159,27 +156,27 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             return true; | ||||
|             break; | ||||
| 
 | ||||
|         case SubghzCustomEventViewReadRAWConfig: | ||||
|         case SubGhzCustomEventViewReadRAWConfig: | ||||
|             scene_manager_set_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet); | ||||
|                 subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); | ||||
|             return true; | ||||
|             break; | ||||
| 
 | ||||
|         case SubghzCustomEventViewReadRAWErase: | ||||
|         case SubGhzCustomEventViewReadRAWErase: | ||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; | ||||
|             return true; | ||||
|             break; | ||||
| 
 | ||||
|         case SubghzCustomEventViewReadRAWVibro: | ||||
|         case SubGhzCustomEventViewReadRAWVibro: | ||||
|             notification_message(subghz->notifications, &sequence_single_vibro); | ||||
|             return true; | ||||
|             break; | ||||
| 
 | ||||
|         case SubghzCustomEventViewReadRAWMore: | ||||
|         case SubGhzCustomEventViewReadRAWMore: | ||||
|             if(subghz_scene_read_raw_update_filename(subghz)) { | ||||
|                 scene_manager_set_scene_state( | ||||
|                     subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet); | ||||
|                     subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); | ||||
|                 return true; | ||||
| @ -188,7 +185,8 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             } | ||||
|             break; | ||||
| 
 | ||||
|         case SubghzCustomEventViewReadRAWSendStart: | ||||
|         case SubGhzCustomEventViewReadRAWSendStart: | ||||
| 
 | ||||
|             if(subghz_scene_read_raw_update_filename(subghz)) { | ||||
|                 //start send
 | ||||
|                 subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
| @ -197,10 +195,17 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|                 } | ||||
|                 if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || | ||||
|                    (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { | ||||
|                     if(!subghz_tx_start(subghz)) { | ||||
|                     //ToDo FIX
 | ||||
| 
 | ||||
|                     if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { | ||||
|                         scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); | ||||
|                     } else { | ||||
|                         DOLPHIN_DEED(DolphinDeedSubGhzSend); | ||||
|                         // set callback end tx
 | ||||
|                         subghz_protocol_raw_file_encoder_worker_set_callback_end( | ||||
|                             (SubGhzProtocolEncoderRAW*)subghz->txrx->transmitter->protocol_instance, | ||||
|                             subghz_scene_read_raw_callback_end_tx, | ||||
|                             subghz); | ||||
|                         subghz->state_notifications = SubGhzNotificationStateTX; | ||||
|                     } | ||||
|                 } | ||||
| @ -208,7 +213,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             return true; | ||||
|             break; | ||||
| 
 | ||||
|         case SubghzCustomEventViewReadRAWSendStop: | ||||
|         case SubGhzCustomEventViewReadRAWSendStop: | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|                 subghz_tx_stop(subghz); | ||||
| @ -218,13 +223,14 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             return true; | ||||
|             break; | ||||
| 
 | ||||
|         case SubghzCustomEventViewReadRAWIDLE: | ||||
|         case SubGhzCustomEventViewReadRAWIDLE: | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
|                 subghz_rx_end(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|             }; | ||||
|             subghz_protocol_raw_save_to_file_stop( | ||||
|                 (SubGhzProtocolRAW*)subghz->txrx->protocol_result); | ||||
|                 (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); | ||||
|             subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, RAW_FILE_NAME); | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
| 
 | ||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; | ||||
| @ -232,16 +238,16 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             return true; | ||||
|             break; | ||||
| 
 | ||||
|         case SubghzCustomEventViewReadRAWREC: | ||||
|         case SubGhzCustomEventViewReadRAWREC: | ||||
|             if(subghz->txrx->rx_key_state != SubGhzRxKeyStateIDLE) { | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); | ||||
|             } else { | ||||
|                 subghz_get_preset_name(subghz, subghz->error_str); | ||||
|                 //subghz_get_preset_name(subghz, subghz->error_str);
 | ||||
|                 if(subghz_protocol_raw_save_to_file_init( | ||||
|                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result, | ||||
|                        (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, | ||||
|                        RAW_FILE_NAME, | ||||
|                        subghz->txrx->frequency, | ||||
|                        string_get_cstr(subghz->error_str))) { | ||||
|                        subghz->txrx->preset)) { | ||||
|                     DOLPHIN_DEED(DolphinDeedSubGhzRawRec); | ||||
|                     if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || | ||||
|                        (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { | ||||
| @ -258,10 +264,10 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             return true; | ||||
|             break; | ||||
| 
 | ||||
|         case SubghzCustomEventViewReadRAWSave: | ||||
|         case SubGhzCustomEventViewReadRAWSave: | ||||
|             if(subghz_scene_read_raw_update_filename(subghz)) { | ||||
|                 scene_manager_set_scene_state( | ||||
|                     subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSetRAW); | ||||
|                     subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW); | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||
|             } | ||||
| @ -278,7 +284,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             subghz_read_raw_update_sample_write( | ||||
|                 subghz->subghz_read_raw, | ||||
|                 subghz_protocol_raw_get_sample_write( | ||||
|                     (SubGhzProtocolRAW*)subghz->txrx->protocol_result)); | ||||
|                     (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result)); | ||||
|             subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi()); | ||||
|             break; | ||||
|         case SubGhzNotificationStateTX: | ||||
| @ -302,7 +308,6 @@ void subghz_scene_read_raw_on_exit(void* context) { | ||||
|     }; | ||||
|     subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
| 
 | ||||
|     //Сallback restoration
 | ||||
|     subghz_worker_set_pair_callback( | ||||
|         subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_parser_parse); | ||||
|     //filter restoration
 | ||||
|     subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| #include "../subghz_i.h" | ||||
| #include "../views/subghz_receiver.h" | ||||
| #include "../views/receiver.h" | ||||
| 
 | ||||
| static void subghz_scene_receiver_update_statusbar(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| @ -14,7 +14,7 @@ static void subghz_scene_receiver_update_statusbar(void* context) { | ||||
| 
 | ||||
|         subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); | ||||
| 
 | ||||
|         subghz_receiver_add_data_statusbar( | ||||
|         subghz_view_receiver_add_data_statusbar( | ||||
|             subghz->subghz_receiver, | ||||
|             string_get_cstr(frequency_str), | ||||
|             string_get_cstr(modulation_str), | ||||
| @ -23,36 +23,41 @@ static void subghz_scene_receiver_update_statusbar(void* context) { | ||||
|         string_clear(frequency_str); | ||||
|         string_clear(modulation_str); | ||||
|     } else { | ||||
|         subghz_receiver_add_data_statusbar( | ||||
|         subghz_view_receiver_add_data_statusbar( | ||||
|             subghz->subghz_receiver, string_get_cstr(history_stat_str), "", ""); | ||||
|         subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|     } | ||||
|     string_clear(history_stat_str); | ||||
| } | ||||
| 
 | ||||
| void subghz_scene_receiver_callback(SubghzCustomEvent event, void* context) { | ||||
| void subghz_scene_receiver_callback(SubGhzCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, event); | ||||
| } | ||||
| 
 | ||||
| void subghz_scene_add_to_history_callback(SubGhzProtocolCommon* parser, void* context) { | ||||
| static void subghz_scene_add_to_history_callback( | ||||
|     SubGhzReceiver* receiver, | ||||
|     SubGhzProtocolDecoderBase* decoder_base, | ||||
|     void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     string_t str_buff; | ||||
|     string_init(str_buff); | ||||
| 
 | ||||
|     if(subghz_history_add_to_history( | ||||
|            subghz->txrx->history, parser, subghz->txrx->frequency, subghz->txrx->preset)) { | ||||
|         subghz_parser_reset(subghz->txrx->parser); | ||||
|            subghz->txrx->history, decoder_base, subghz->txrx->frequency, subghz->txrx->preset)) { | ||||
|         subghz_receiver_reset(receiver); | ||||
|         string_reset(str_buff); | ||||
| 
 | ||||
|         subghz_history_get_text_item_menu( | ||||
|             subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); | ||||
|         subghz_receiver_add_item_to_menu( | ||||
|         subghz_view_receiver_add_item_to_menu( | ||||
|             subghz->subghz_receiver, | ||||
|             string_get_cstr(str_buff), | ||||
|             subghz_history_get_type_protocol( | ||||
|                 subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1)); | ||||
| 
 | ||||
|         subghz_scene_receiver_update_statusbar(subghz); | ||||
|     } | ||||
|     string_clear(str_buff); | ||||
| @ -70,11 +75,11 @@ void subghz_scene_receiver_on_enter(void* context) { | ||||
|     } | ||||
| 
 | ||||
|     //Load history to receiver
 | ||||
|     subghz_receiver_exit(subghz->subghz_receiver); | ||||
|     subghz_view_receiver_exit(subghz->subghz_receiver); | ||||
|     for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) { | ||||
|         string_reset(str_buff); | ||||
|         subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i); | ||||
|         subghz_receiver_add_item_to_menu( | ||||
|         subghz_view_receiver_add_item_to_menu( | ||||
|             subghz->subghz_receiver, | ||||
|             string_get_cstr(str_buff), | ||||
|             subghz_history_get_type_protocol(subghz->txrx->history, i)); | ||||
| @ -82,8 +87,10 @@ void subghz_scene_receiver_on_enter(void* context) { | ||||
|     } | ||||
|     string_clear(str_buff); | ||||
|     subghz_scene_receiver_update_statusbar(subghz); | ||||
|     subghz_receiver_set_callback(subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); | ||||
|     subghz_parser_enable_dump(subghz->txrx->parser, subghz_scene_add_to_history_callback, subghz); | ||||
|     subghz_view_receiver_set_callback( | ||||
|         subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); | ||||
|     subghz_receiver_set_rx_callback( | ||||
|         subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); | ||||
| 
 | ||||
|     subghz->state_notifications = SubGhzNotificationStateRX; | ||||
|     if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
| @ -94,9 +101,9 @@ void subghz_scene_receiver_on_enter(void* context) { | ||||
|         subghz_begin(subghz, subghz->txrx->preset); | ||||
|         subghz_rx(subghz, subghz->txrx->frequency); | ||||
|     } | ||||
|     subghz_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); | ||||
|     subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReceiver); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | ||||
| @ -104,7 +111,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         switch(event.event) { | ||||
|         case SubghzCustomEventViewReceverBack: | ||||
|         case SubGhzCustomEventViewReceverBack: | ||||
| 
 | ||||
|             // Stop CC1101 Rx
 | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
| @ -116,7 +123,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | ||||
|             subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92]; | ||||
|             subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; | ||||
|             subghz->txrx->idx_menu_chosen = 0; | ||||
|             subghz_parser_enable_dump(subghz->txrx->parser, NULL, subghz); | ||||
|             subghz_receiver_set_rx_callback(subghz->txrx->receiver, NULL, subghz); | ||||
| 
 | ||||
|             if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; | ||||
| @ -127,14 +134,16 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | ||||
|             } | ||||
|             return true; | ||||
|             break; | ||||
|         case SubghzCustomEventViewReceverOK: | ||||
|             subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver); | ||||
|         case SubGhzCustomEventViewReceverOK: | ||||
|             subghz->txrx->idx_menu_chosen = | ||||
|                 subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); | ||||
|             return true; | ||||
|             break; | ||||
|         case SubghzCustomEventViewReceverConfig: | ||||
|         case SubGhzCustomEventViewReceverConfig: | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver); | ||||
|             subghz->txrx->idx_menu_chosen = | ||||
|                 subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); | ||||
|             return true; | ||||
|             break; | ||||
|  | ||||
| @ -127,7 +127,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { | ||||
|     variable_item_set_current_value_text(item, subghz_frequencies_text[value_index]); | ||||
| 
 | ||||
|     if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||
|        SubghzCustomEventManagerSet) { | ||||
|        SubGhzCustomEventManagerSet) { | ||||
|         item = variable_item_list_add( | ||||
|             subghz->variable_item_list, | ||||
|             "Hopping:", | ||||
| @ -151,7 +151,7 @@ void subghz_scene_receiver_config_on_enter(void* context) { | ||||
|     variable_item_set_current_value_index(item, value_index); | ||||
|     variable_item_set_current_value_text(item, preset_text[value_index]); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewVariableItemList); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent event) { | ||||
| @ -163,5 +163,5 @@ void subghz_scene_receiver_config_on_exit(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     variable_item_list_reset(subghz->variable_item_list); | ||||
|     scene_manager_set_scene_state( | ||||
|         subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet); | ||||
|         subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); | ||||
| } | ||||
|  | ||||
| @ -8,25 +8,24 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v | ||||
| 
 | ||||
|     if((result == GuiButtonTypeCenter) && (type == InputTypePress)) { | ||||
|         view_dispatcher_send_custom_event( | ||||
|             subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoTxStart); | ||||
|             subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxStart); | ||||
|     } else if((result == GuiButtonTypeCenter) && (type == InputTypeRelease)) { | ||||
|         view_dispatcher_send_custom_event( | ||||
|             subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoTxStop); | ||||
|             subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxStop); | ||||
|     } else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { | ||||
|         view_dispatcher_send_custom_event( | ||||
|             subghz->view_dispatcher, SubghzCustomEventSceneReceiverInfoSave); | ||||
|             subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoSave); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool subghz_scene_receiver_info_update_parser(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     subghz->txrx->protocol_result = subghz_parser_get_by_name( | ||||
|         subghz->txrx->parser, | ||||
|         subghz_history_get_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); | ||||
| 
 | ||||
|     if(subghz->txrx->protocol_result->to_load_protocol != NULL) { | ||||
|         subghz->txrx->protocol_result->to_load_protocol( | ||||
|             subghz->txrx->protocol_result, | ||||
|     subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( | ||||
|         subghz->txrx->receiver, | ||||
|         subghz_history_get_protocol_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); | ||||
|     if(subghz->txrx->decoder_result) { | ||||
|         subghz_protocol_decoder_base_deserialize( | ||||
|             subghz->txrx->decoder_result, | ||||
|             subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); | ||||
|         subghz->txrx->frequency = | ||||
|             subghz_history_get_frequency(subghz->txrx->history, subghz->txrx->idx_menu_chosen); | ||||
| @ -68,8 +67,7 @@ void subghz_scene_receiver_info_on_enter(void* context) { | ||||
|             AlignTop, | ||||
|             FontSecondary, | ||||
|             string_get_cstr(modulation_str)); | ||||
| 
 | ||||
|         subghz->txrx->protocol_result->to_string(subghz->txrx->protocol_result, text); | ||||
|         subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, text); | ||||
|         widget_add_string_multiline_element( | ||||
|             subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, string_get_cstr(text)); | ||||
| 
 | ||||
| @ -77,14 +75,19 @@ void subghz_scene_receiver_info_on_enter(void* context) { | ||||
|         string_clear(modulation_str); | ||||
|         string_clear(text); | ||||
| 
 | ||||
|         if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->to_save_file && | ||||
|            strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) { | ||||
|         if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == | ||||
|            SubGhzProtocolFlag_Save) { | ||||
|             widget_add_button_element( | ||||
|                 subghz->widget, | ||||
|                 GuiButtonTypeRight, | ||||
|                 "Save", | ||||
|                 subghz_scene_receiver_info_callback, | ||||
|                 subghz); | ||||
|         } | ||||
|         if(((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == | ||||
|             SubGhzProtocolFlag_Send) && | ||||
|            subghz->txrx->decoder_result->protocol->encoder->deserialize && | ||||
|            subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeStatic) { | ||||
|             widget_add_button_element( | ||||
|                 subghz->widget, | ||||
|                 GuiButtonTypeCenter, | ||||
| @ -92,20 +95,19 @@ void subghz_scene_receiver_info_on_enter(void* context) { | ||||
|                 subghz_scene_receiver_info_callback, | ||||
|                 subghz); | ||||
|         } | ||||
| 
 | ||||
|     } else { | ||||
|         widget_add_icon_element(subghz->widget, 32, 12, &I_DolphinFirstStart7_61x51); | ||||
|         widget_add_string_element( | ||||
|             subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse."); | ||||
|     } | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubghzCustomEventSceneReceiverInfoTxStart) { | ||||
|         if(event.event == SubGhzCustomEventSceneReceiverInfoTxStart) { | ||||
|             //CC1101 Stop RX -> Start TX
 | ||||
|             if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { | ||||
|                 subghz->txrx->hopper_state = SubGhzHopperStatePause; | ||||
| @ -118,14 +120,17 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) | ||||
|             } | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE || | ||||
|                subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { | ||||
|                 if(!subghz_tx_start(subghz)) { | ||||
|                 if(!subghz_tx_start( | ||||
|                        subghz, | ||||
|                        subghz_history_get_raw_data( | ||||
|                            subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { | ||||
|                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); | ||||
|                 } else { | ||||
|                     subghz->state_notifications = SubGhzNotificationStateTX; | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         } else if(event.event == SubghzCustomEventSceneReceiverInfoTxStop) { | ||||
|         } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { | ||||
|             //CC1101 Stop Tx -> Start RX
 | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
| @ -140,7 +145,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) | ||||
|             } | ||||
|             subghz->state_notifications = SubGhzNotificationStateRX; | ||||
|             return true; | ||||
|         } else if(event.event == SubghzCustomEventSceneReceiverInfoSave) { | ||||
|         } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { | ||||
|             //CC1101 Stop RX -> Save
 | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { | ||||
| @ -153,8 +158,9 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) | ||||
|             if(!subghz_scene_receiver_info_update_parser(subghz)) { | ||||
|                 return false; | ||||
|             } | ||||
|             if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->to_save_file && | ||||
|                strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) { | ||||
| 
 | ||||
|             if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == | ||||
|                SubGhzProtocolFlag_Save) { | ||||
|                 subghz_file_name_clear(subghz); | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||
|             } | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| #include "../subghz_i.h" | ||||
| #include <lib/toolbox/random_name.h> | ||||
| #include "../helpers/subghz_custom_event.h" | ||||
| #include <lib/subghz/protocols/subghz_protocol_raw.h> | ||||
| #include <lib/subghz/protocols/raw.h> | ||||
| #include <gui/modules/validators.h> | ||||
| 
 | ||||
| void subghz_scene_save_name_text_input_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneSaveName); | ||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveName); | ||||
| } | ||||
| 
 | ||||
| void subghz_scene_save_name_on_enter(void* context) { | ||||
| @ -24,10 +24,10 @@ void subghz_scene_save_name_on_enter(void* context) { | ||||
|     } else { | ||||
|         strcpy(subghz->file_name_tmp, subghz->file_name); | ||||
|         if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||
|            SubghzCustomEventManagerNoSet) { | ||||
|            SubGhzCustomEventManagerNoSet) { | ||||
|             subghz_get_next_name_file(subghz); | ||||
|             if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == | ||||
|                SubghzCustomEventManagerSetRAW) { | ||||
|                SubGhzCustomEventManagerSetRAW) { | ||||
|                 dev_name_empty = true; | ||||
|             } | ||||
|         } | ||||
| @ -46,7 +46,7 @@ void subghz_scene_save_name_on_enter(void* context) { | ||||
|         validator_is_file_alloc_init(SUBGHZ_APP_FOLDER, SUBGHZ_APP_EXTENSION); | ||||
|     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTextInput); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | ||||
| @ -56,22 +56,35 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | ||||
|         scene_manager_previous_scene(subghz->scene_manager); | ||||
|         return true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubghzCustomEventSceneSaveName) { | ||||
|         if(event.event == SubGhzCustomEventSceneSaveName) { | ||||
|             if(strcmp(subghz->file_name, "")) { | ||||
|                 if(strcmp(subghz->file_name_tmp, "")) { | ||||
|                     if(!subghz_rename_file(subghz)) { | ||||
|                         return false; | ||||
|                     } | ||||
|                 } else { | ||||
|                     subghz_save_protocol_to_file(subghz, subghz->file_name); | ||||
|                     if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType) != | ||||
|                        SubGhzCustomEventManagerNoSet) { | ||||
|                         subghz_save_protocol_to_file( | ||||
|                             subghz, subghz->txrx->fff_data, subghz->file_name); | ||||
|                         scene_manager_set_scene_state( | ||||
|                             subghz->scene_manager, | ||||
|                             SubGhzSceneSetType, | ||||
|                             SubGhzCustomEventManagerNoSet); | ||||
|                     } else { | ||||
|                         subghz_save_protocol_to_file( | ||||
|                             subghz, | ||||
|                             subghz_history_get_raw_data( | ||||
|                                 subghz->txrx->history, subghz->txrx->idx_menu_chosen), | ||||
|                             subghz->file_name); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||
|                    SubghzCustomEventManagerNoSet) { | ||||
|                     subghz_protocol_raw_set_last_file_name( | ||||
|                         (SubGhzProtocolRAW*)subghz->txrx->protocol_result, subghz->file_name); | ||||
|                    SubGhzCustomEventManagerNoSet) { | ||||
|                     subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, subghz->file_name); | ||||
|                     scene_manager_set_scene_state( | ||||
|                         subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet); | ||||
|                         subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); | ||||
|                 } else { | ||||
|                     subghz_file_name_clear(subghz); | ||||
|                 } | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
| 
 | ||||
| void subghz_scene_save_success_popup_callback(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneSaveSuccess); | ||||
|     view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveSuccess); | ||||
| } | ||||
| 
 | ||||
| void subghz_scene_save_success_on_enter(void* context) { | ||||
| @ -20,13 +20,13 @@ void subghz_scene_save_success_on_enter(void* context) { | ||||
|     popup_set_context(popup, subghz); | ||||
|     popup_set_callback(popup, subghz_scene_save_success_popup_callback); | ||||
|     popup_enable_timeout(popup); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); | ||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup); | ||||
| } | ||||
| 
 | ||||
| bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubghzCustomEventSceneSaveSuccess) { | ||||
|         if(event.event == SubGhzCustomEventSceneSaveSuccess) { | ||||
|             if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                    subghz->scene_manager, SubGhzSceneReceiver)) { | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWSave; | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov