Merge remote-tracking branch 'origin/dev' into release-candidate
This commit is contained in:
		
						commit
						c5fd214f80
					
				
							
								
								
									
										24
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -71,14 +71,6 @@ jobs: | |||||||
|         run: | |         run: | | ||||||
|           tar czpf artifacts/flipper-z-any-scripts-${{steps.names.outputs.suffix}}.tgz scripts |           tar czpf artifacts/flipper-z-any-scripts-${{steps.names.outputs.suffix}}.tgz scripts | ||||||
| 
 | 
 | ||||||
|       - name: 'Rebuild Assets' |  | ||||||
|         uses: ./.github/actions/docker |  | ||||||
|         with: |  | ||||||
|           run: | |  | ||||||
|             set -e |  | ||||||
|             make assets_rebuild assets_manifest |  | ||||||
|             git diff --quiet || ( echo "Assets recompilation required."; exit 255 ) |  | ||||||
| 
 |  | ||||||
|       - name: 'Build the firmware in docker' |       - name: 'Build the firmware in docker' | ||||||
|         uses: ./.github/actions/docker |         uses: ./.github/actions/docker | ||||||
|         with: |         with: | ||||||
| @ -86,7 +78,7 @@ jobs: | |||||||
|             set -e |             set -e | ||||||
|             for TARGET in ${TARGETS} |             for TARGET in ${TARGETS} | ||||||
|             do |             do | ||||||
|               make updater_package TARGET=${TARGET} ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} |               ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` --with-updater updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} | ||||||
|             done |             done | ||||||
| 
 | 
 | ||||||
|       - name: 'Move upload files' |       - name: 'Move upload files' | ||||||
| @ -97,7 +89,7 @@ jobs: | |||||||
|             set -e |             set -e | ||||||
|             for TARGET in ${TARGETS} |             for TARGET in ${TARGETS} | ||||||
|             do |             do | ||||||
|               mv dist/${TARGET}/* artifacts/ |               mv dist/${TARGET}-*/* artifacts/ | ||||||
|             done |             done | ||||||
| 
 | 
 | ||||||
|       - name: 'Bundle self-update package' |       - name: 'Bundle self-update package' | ||||||
| @ -124,7 +116,7 @@ jobs: | |||||||
|         uses: ./.github/actions/docker |         uses: ./.github/actions/docker | ||||||
|         with: |         with: | ||||||
|           run: | |           run: | | ||||||
|             make -C assets copro_bundle |             ./fbt copro_dist | ||||||
|             tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets core2_firmware |             tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets core2_firmware | ||||||
| 
 | 
 | ||||||
|       - name: 'Upload artifacts to update server' |       - name: 'Upload artifacts to update server' | ||||||
| @ -208,14 +200,6 @@ jobs: | |||||||
|           echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV |           echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV | ||||||
|           echo "DIST_SUFFIX=${SUFFIX}" >> $GITHUB_ENV |           echo "DIST_SUFFIX=${SUFFIX}" >> $GITHUB_ENV | ||||||
| 
 | 
 | ||||||
|       - name: 'Rebuild Assets' |  | ||||||
|         uses: ./.github/actions/docker |  | ||||||
|         with: |  | ||||||
|           run: | |  | ||||||
|             set -e |  | ||||||
|             make assets_rebuild assets_manifest |  | ||||||
|             git diff --quiet || ( echo "Assets recompilation required."; exit 255 ) |  | ||||||
| 
 |  | ||||||
|       - name: 'Build the firmware in docker' |       - name: 'Build the firmware in docker' | ||||||
|         uses: ./.github/actions/docker |         uses: ./.github/actions/docker | ||||||
|         with: |         with: | ||||||
| @ -223,5 +207,5 @@ jobs: | |||||||
|             set -e |             set -e | ||||||
|             for TARGET in ${TARGETS} |             for TARGET in ${TARGETS} | ||||||
|             do |             do | ||||||
|               make TARGET=${TARGET} DEBUG=0 COMPACT=1 |               ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` --with-updater updater_package DEBUG=0 COMPACT=1 | ||||||
|             done |             done | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								.github/workflows/lint_c.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/lint_c.yml
									
									
									
									
										vendored
									
									
								
							| @ -47,7 +47,7 @@ jobs: | |||||||
|         id: syntax_check |         id: syntax_check | ||||||
|         uses: ./.github/actions/docker |         uses: ./.github/actions/docker | ||||||
|         with: |         with: | ||||||
|           run: SET_GH_OUTPUT=1 make lint |           run: SET_GH_OUTPUT=1 ./fbt lint | ||||||
| 
 | 
 | ||||||
|       - name: Report code formatting errors |       - name: Report code formatting errors | ||||||
|         if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request |         if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -39,3 +39,11 @@ dist | |||||||
| 
 | 
 | ||||||
| # kde | # kde | ||||||
| .directory | .directory | ||||||
|  | 
 | ||||||
|  | # SCons | ||||||
|  | .sconsign.dblite | ||||||
|  | # SCons build dir | ||||||
|  | build/ | ||||||
|  | 
 | ||||||
|  | # Toolchain | ||||||
|  | toolchain*/ | ||||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @ -22,3 +22,6 @@ | |||||||
| [submodule "lib/microtar"] | [submodule "lib/microtar"] | ||||||
| 	path = lib/microtar | 	path = lib/microtar | ||||||
| 	url = https://github.com/amachronic/microtar.git | 	url = https://github.com/amachronic/microtar.git | ||||||
|  | [submodule "lib/scons"] | ||||||
|  | 	path = lib/scons | ||||||
|  | 	url = https://github.com/SCons/scons.git | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								Brewfile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Brewfile
									
									
									
									
									
								
							| @ -1,8 +1,6 @@ | |||||||
| cask "gcc-arm-embedded" | cask "gcc-arm-embedded" | ||||||
| brew "protobuf" | brew "protobuf" | ||||||
| brew "gdb" | brew "gdb" | ||||||
| brew "heatshrink" |  | ||||||
| brew "open-ocd" | brew "open-ocd" | ||||||
| brew "clang-format" | brew "clang-format" | ||||||
| brew "dfu-util" | brew "dfu-util" | ||||||
| brew "imagemagick" |  | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ Almost everything in flipper firmware is built around this concept. | |||||||
| # C coding style | # C coding style | ||||||
| 
 | 
 | ||||||
| - Tab is 4 spaces | - Tab is 4 spaces | ||||||
| - Use `make format` to reformat source code and check style guide before commit | - Use `fbt format` to reformat source code and check style guide before commit | ||||||
| 
 | 
 | ||||||
| ## Naming | ## Naming | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										176
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										176
									
								
								Makefile
									
									
									
									
									
								
							| @ -1,155 +1,21 @@ | |||||||
| PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))) | $(info +-------------------------------------------------+) | ||||||
| 
 | $(info |                                                 |) | ||||||
| include			$(PROJECT_ROOT)/make/git.mk | $(info |      Hello, this is Flipper team speaking!      |) | ||||||
| include			$(PROJECT_ROOT)/assets/copro.mk | $(info |                                                 |) | ||||||
| 
 | $(info |       We've migrated to new build system        |) | ||||||
| PROJECT_SOURCE_DIRECTORIES := \
 | $(info |          It's nice and based on scons           |) | ||||||
| 	$(PROJECT_ROOT)/applications \
 | $(info |                                                 |) | ||||||
| 	$(PROJECT_ROOT)/core \
 | $(info |      Crash course:                              |) | ||||||
| 	$(PROJECT_ROOT)/firmware/targets \
 | $(info |                                                 |) | ||||||
| 	$(PROJECT_ROOT)/lib/app-template \
 | $(info |        `./fbt`                                  |) | ||||||
| 	$(PROJECT_ROOT)/lib/app-scened-template \
 | $(info |        `./fbt flash`                            |) | ||||||
| 	$(PROJECT_ROOT)/lib/common-api \
 | $(info |        `./fbt debug`                            |) | ||||||
| 	$(PROJECT_ROOT)/lib/drivers \
 | $(info |                                                 |) | ||||||
| 	$(PROJECT_ROOT)/lib/flipper_file \
 | $(info |      More details in documentation/fbt.md       |) | ||||||
| 	$(PROJECT_ROOT)/lib/infrared \
 | $(info |                                                 |) | ||||||
| 	$(PROJECT_ROOT)/lib/nfc_protocols \
 | $(info |      Also Please leave your feedback here:      |) | ||||||
| 	$(PROJECT_ROOT)/lib/ST25RFAL002 \
 | $(info |           https://flipp.dev/4RDu                |) | ||||||
| 	$(PROJECT_ROOT)/lib/onewire \
 | $(info |                      or                         |) | ||||||
| 	$(PROJECT_ROOT)/lib/qrcode \
 | $(info |           https://flipp.dev/2XM8                |) | ||||||
| 	$(PROJECT_ROOT)/lib/subghz \
 | $(info |                                                 |) | ||||||
| 	$(PROJECT_ROOT)/lib/toolbox \
 | $(info +-------------------------------------------------+) | ||||||
| 	$(PROJECT_ROOT)/lib/u8g2 |  | ||||||
| 
 |  | ||||||
| NPROCS := 3 |  | ||||||
| OS := $(shell uname -s) |  | ||||||
| 
 |  | ||||||
| ifeq ($(OS), Linux) |  | ||||||
| NPROCS := $(shell grep -c ^processor /proc/cpuinfo) |  | ||||||
| else ifeq ($(OS), Darwin) |  | ||||||
| NPROCS := $(shell sysctl -n hw.ncpu) |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| include	$(PROJECT_ROOT)/make/defaults.mk |  | ||||||
| 
 |  | ||||||
| .PHONY: all |  | ||||||
| all: firmware_all |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware -s $(DIST_SUFFIX) |  | ||||||
| 
 |  | ||||||
| .PHONY: whole |  | ||||||
| whole: flash_radio firmware_flash |  | ||||||
| 
 |  | ||||||
| .PHONY: clean |  | ||||||
| clean: firmware_clean updater_clean |  | ||||||
| 	@rm -rf $(PROJECT_ROOT)/dist/$(TARGET) |  | ||||||
| 
 |  | ||||||
| .PHONY: flash |  | ||||||
| flash: firmware_flash |  | ||||||
| 
 |  | ||||||
| .PHONY: debug |  | ||||||
| debug: |  | ||||||
| 	@$(MAKE) -C firmware -j$(NPROCS) debug |  | ||||||
| 
 |  | ||||||
| .PHONY: debug_other |  | ||||||
| debug_other: |  | ||||||
| 	@$(MAKE) -C firmware -j$(NPROCS) debug_other |  | ||||||
| 
 |  | ||||||
| .PHONY: blackmagic |  | ||||||
| blackmagic: |  | ||||||
| 	@$(MAKE) -C firmware -j$(NPROCS) blackmagic |  | ||||||
| 
 |  | ||||||
| .PHONY: wipe |  | ||||||
| wipe: |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/flash.py wipe |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/ob.py set |  | ||||||
| 
 |  | ||||||
| .PHONY: firmware_all |  | ||||||
| firmware_all: |  | ||||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) all |  | ||||||
| 
 |  | ||||||
| .PHONY: firmware_clean |  | ||||||
| firmware_clean: |  | ||||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) clean |  | ||||||
| 
 |  | ||||||
| .PHONY: firmware_flash |  | ||||||
| firmware_flash: |  | ||||||
| ifeq ($(FORCE), 1) |  | ||||||
| 	@rm $(PROJECT_ROOT)/firmware/.obj/f*-firmware/flash || true |  | ||||||
| endif |  | ||||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash |  | ||||||
| 
 |  | ||||||
| .PHONY: updater |  | ||||||
| updater: |  | ||||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 all |  | ||||||
| 
 |  | ||||||
| .PHONY: updater_clean |  | ||||||
| updater_clean: |  | ||||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 clean |  | ||||||
| 
 |  | ||||||
| .PHONY: updater_debug |  | ||||||
| updater_debug: |  | ||||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 debug |  | ||||||
| 
 |  | ||||||
| .PHONY: updater_package_bin |  | ||||||
| updater_package_bin: firmware_all updater |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) --bundlever "$(VERSION_STRING)" |  | ||||||
| 
 |  | ||||||
| .PHONY: updater_package |  | ||||||
| updater_package: firmware_all updater assets_manifest |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/dist.py copy \
 |  | ||||||
| 	-t $(TARGET) -p firmware updater \
 |  | ||||||
| 	-s $(DIST_SUFFIX) -r $(PROJECT_ROOT)/assets/resources \
 |  | ||||||
| 	--bundlever "$(VERSION_STRING)" \
 |  | ||||||
| 	--radio $(COPRO_STACK_BIN_PATH) \
 |  | ||||||
| 	--radiotype $(COPRO_STACK_TYPE) \
 |  | ||||||
| 	$(COPRO_DISCLAIMER) \
 |  | ||||||
| 	--obdata $(PROJECT_ROOT)/scripts/$(COPRO_OB_DATA) |  | ||||||
| 
 |  | ||||||
| .PHONY: assets_manifest |  | ||||||
| assets_manifest: |  | ||||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/assets manifest |  | ||||||
| 
 |  | ||||||
| .PHONY: assets_rebuild |  | ||||||
| assets_rebuild: |  | ||||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/assets clean all |  | ||||||
| 
 |  | ||||||
| .PHONY: flash_radio |  | ||||||
| flash_radio: |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/flash.py core2radio $(COPRO_STACK_BIN_PATH) --addr=$(COPRO_STACK_ADDR) |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/ob.py set |  | ||||||
| 
 |  | ||||||
| .PHONY: flash_radio_fus |  | ||||||
| flash_radio_fus: |  | ||||||
| 	@echo |  | ||||||
| 	@echo "================   DON'T DO IT    ================" |  | ||||||
| 	@echo "= Flashing FUS is going to erase secure enclave  =" |  | ||||||
| 	@echo "= You will lose ability to use encrypted assets  =" |  | ||||||
| 	@echo "=       type 'find / -exec rm -rf {} \;'         =" |  | ||||||
| 	@echo "=     In case if you still want to continue      =" |  | ||||||
| 	@echo "================    JUST DON'T    ================" |  | ||||||
| 	@echo |  | ||||||
| 
 |  | ||||||
| .PHONY: flash_radio_fus_please_i_m_not_going_to_complain |  | ||||||
| flash_radio_fus_please_i_m_not_going_to_complain: |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_FIRMWARE_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_FIRMWARE_DIR)/stm32wb5x_FUS_fw.bin |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/ob.py set |  | ||||||
| 
 |  | ||||||
| .PHONY: lint |  | ||||||
| lint: |  | ||||||
| 	@echo "Checking source code formatting" |  | ||||||
| 	@$(PROJECT_ROOT)/scripts/lint.py check $(PROJECT_SOURCE_DIRECTORIES) |  | ||||||
| 
 |  | ||||||
| .PHONY: format |  | ||||||
| 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) |  | ||||||
| 
 |  | ||||||
| .PHONY: generate_compile_db |  | ||||||
| generate_compile_db: |  | ||||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) generate_compile_db |  | ||||||
							
								
								
									
										28
									
								
								ReadMe.md
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								ReadMe.md
									
									
									
									
									
								
							| @ -25,6 +25,12 @@ Flipper Zero's firmware consists of two components: | |||||||
| 
 | 
 | ||||||
| They both must be flashed in order described. | They both must be flashed in order described. | ||||||
| 
 | 
 | ||||||
|  | ## With offline update package | ||||||
|  | 
 | ||||||
|  | `./fbt --with-updater updater_package` | ||||||
|  | 
 | ||||||
|  | Copy the resulting directory to Flipper's SD card and navigate to `update.fuf` file in Archive app.  | ||||||
|  | 
 | ||||||
| ## With STLink | ## With STLink | ||||||
| 
 | 
 | ||||||
| ### Core1 Firmware | ### Core1 Firmware | ||||||
| @ -36,17 +42,7 @@ Prerequisites: | |||||||
| - [arm-gcc-none-eabi](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads) | - [arm-gcc-none-eabi](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads) | ||||||
| - openocd | - openocd | ||||||
| 
 | 
 | ||||||
| One liner: `make flash` | One liner: `./fbt firmware_flash` | ||||||
| 
 |  | ||||||
| ### Core2 flashing procedures |  | ||||||
| 
 |  | ||||||
| Prerequisites: |  | ||||||
| 
 |  | ||||||
| - Linux / macOS |  | ||||||
| - Terminal |  | ||||||
| - STM32_Programmer_CLI (v2.5.0) added to $PATH |  | ||||||
| 
 |  | ||||||
| One liner: `make flash_radio` |  | ||||||
| 
 | 
 | ||||||
| ## With USB DFU  | ## With USB DFU  | ||||||
| 
 | 
 | ||||||
| @ -56,7 +52,6 @@ One liner: `make flash_radio` | |||||||
|  - Press and hold `← Left` + `↩ Back` for reset  |  - Press and hold `← Left` + `↩ Back` for reset  | ||||||
|  - Release `↩ Back` and keep holding `← Left` until blue LED lights up |  - Release `↩ Back` and keep holding `← Left` until blue LED lights up | ||||||
|  - Release `← Left` |  - Release `← Left` | ||||||
| <!--  --> |  | ||||||
| 
 | 
 | ||||||
| 3. Run `dfu-util -D full.dfu -a 0` | 3. Run `dfu-util -D full.dfu -a 0` | ||||||
| 
 | 
 | ||||||
| @ -74,7 +69,7 @@ One liner: `make flash_radio` | |||||||
| ## Compile everything | ## Compile everything | ||||||
| 
 | 
 | ||||||
| ```sh | ```sh | ||||||
| docker-compose exec dev make | docker-compose exec dev ./fbt | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Check `dist/` for build outputs. | Check `dist/` for build outputs. | ||||||
| @ -85,6 +80,8 @@ If compilation fails, make sure all submodules are all initialized. Either clone | |||||||
| 
 | 
 | ||||||
| # Build on Linux/macOS | # Build on Linux/macOS | ||||||
| 
 | 
 | ||||||
|  | Check out `documentation/fbt.md` for details on building and flashing firmware.  | ||||||
|  | 
 | ||||||
| ## macOS Prerequisites | ## macOS Prerequisites | ||||||
| 
 | 
 | ||||||
| Make sure you have [brew](https://brew.sh) and install all the dependencies: | Make sure you have [brew](https://brew.sh) and install all the dependencies: | ||||||
| @ -127,7 +124,7 @@ heatshrink has to be compiled [from sources](https://github.com/atomicobject/hea | |||||||
| ## Compile everything | ## Compile everything | ||||||
| 
 | 
 | ||||||
| ```sh | ```sh | ||||||
| make | ./fbt | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Check `dist/` for build outputs. | Check `dist/` for build outputs. | ||||||
| @ -138,7 +135,7 @@ Use **`flipper-z-{target}-full-{suffix}.dfu`** to flash your device. | |||||||
| 
 | 
 | ||||||
| Connect your device via ST-Link and run: | Connect your device via ST-Link and run: | ||||||
| ```sh | ```sh | ||||||
| make whole | ./fbt firmware_flash | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| # Links | # Links | ||||||
| @ -158,7 +155,6 @@ make whole | |||||||
| - `documentation`   - Documentation generation system configs and input files | - `documentation`   - Documentation generation system configs and input files | ||||||
| - `firmware`        - Firmware source code | - `firmware`        - Firmware source code | ||||||
| - `lib`             - Our and 3rd party libraries, drivers and etc... | - `lib`             - Our and 3rd party libraries, drivers and etc... | ||||||
| - `make`            - Make helpers |  | ||||||
| - `scripts`         - Supplementary scripts and python libraries home | - `scripts`         - Supplementary scripts and python libraries home | ||||||
| 
 | 
 | ||||||
| Also pay attention to `ReadMe.md` files inside of those directories. | Also pay attention to `ReadMe.md` files inside of those directories. | ||||||
|  | |||||||
							
								
								
									
										150
									
								
								SConstruct
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								SConstruct
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,150 @@ | |||||||
|  | # | ||||||
|  | # Main Fipper Build System entry point | ||||||
|  | # | ||||||
|  | # This file is evaluated by scons (the build system) every time fbt is invoked. | ||||||
|  | # Scons constructs all referenced environments & their targets' dependency | ||||||
|  | # trees on startup. So, to keep startup time as low as possible, we're hiding | ||||||
|  | # construction of certain targets behind command-line options. | ||||||
|  | 
 | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | DefaultEnvironment(tools=[]) | ||||||
|  | # Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # This environment is created only for loading options & validating file/dir existance | ||||||
|  | fbt_variables = SConscript("site_scons/commandline.scons") | ||||||
|  | cmd_environment = Environment(tools=[], variables=fbt_variables) | ||||||
|  | Help(fbt_variables.GenerateHelpText(cmd_environment)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Building basic environment - tools, utility methods, cross-compilation | ||||||
|  | # settings, gcc flags for Cortex-M4, basic builders and more | ||||||
|  | coreenv = SConscript( | ||||||
|  |     "site_scons/environ.scons", | ||||||
|  |     exports={"VAR_ENV": cmd_environment}, | ||||||
|  | ) | ||||||
|  | SConscript("site_scons/cc.scons", exports={"ENV": coreenv}) | ||||||
|  | 
 | ||||||
|  | # Store root dir in environment for certain tools | ||||||
|  | coreenv["ROOT_DIR"] = Dir(".") | ||||||
|  | 
 | ||||||
|  | # Create a separate "dist" environment and add construction envs to it | ||||||
|  | distenv = coreenv.Clone( | ||||||
|  |     tools=["fbt_dist", "openocd"], | ||||||
|  |     GDBOPTS="-ex 'target extended-remote | ${OPENOCD} -c \"gdb_port pipe\" ${OPENOCD_OPTS}' " | ||||||
|  |     '-ex "set confirm off" ', | ||||||
|  |     ENV=os.environ, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | firmware_out = distenv.AddFwProject( | ||||||
|  |     base_env=coreenv, | ||||||
|  |     fw_type="firmware", | ||||||
|  |     fw_env_key="FW_ENV", | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # If enabled, initialize updater-related targets | ||||||
|  | if GetOption("fullenv"): | ||||||
|  |     updater_out = distenv.AddFwProject( | ||||||
|  |         base_env=coreenv, | ||||||
|  |         fw_type="updater", | ||||||
|  |         fw_env_key="UPD_ENV", | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     # Target for self-update package | ||||||
|  |     dist_arguments = [ | ||||||
|  |         "-r", | ||||||
|  |         '"${ROOT_DIR.abspath}/assets/resources"', | ||||||
|  |         "--bundlever", | ||||||
|  |         '"${UPDATE_VERSION_STRING}"', | ||||||
|  |         "--radio", | ||||||
|  |         '"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"', | ||||||
|  |         "--radiotype", | ||||||
|  |         "${COPRO_STACK_TYPE}", | ||||||
|  |         "${COPRO_DISCLAIMER}", | ||||||
|  |         "--obdata", | ||||||
|  |         '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"', | ||||||
|  |     ] | ||||||
|  |     if distenv["UPDATE_SPLASH"]: | ||||||
|  |         dist_arguments += [ | ||||||
|  |             "--splash", | ||||||
|  |             distenv.subst("assets/slideshow/$UPDATE_SPLASH"), | ||||||
|  |         ] | ||||||
|  |     selfupdate_dist = distenv.DistBuilder( | ||||||
|  |         "selfupdate.pseudo", | ||||||
|  |         (distenv["DIST_DEPENDS"], firmware_out["FW_RESOURCES"]), | ||||||
|  |         DIST_EXTRA=dist_arguments, | ||||||
|  |     ) | ||||||
|  |     distenv.Pseudo("selfupdate.pseudo") | ||||||
|  |     AlwaysBuild(selfupdate_dist) | ||||||
|  |     Alias("updater_package", selfupdate_dist) | ||||||
|  | 
 | ||||||
|  |     # Updater debug | ||||||
|  |     debug_updater_elf = distenv.AddDebugTarget(updater_out, False) | ||||||
|  |     Alias("updater_debug", debug_updater_elf) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Target for copying & renaming binaries to dist folder | ||||||
|  | basic_dist = distenv.DistBuilder("dist.pseudo", distenv["DIST_DEPENDS"]) | ||||||
|  | distenv.Pseudo("dist.pseudo") | ||||||
|  | AlwaysBuild(basic_dist) | ||||||
|  | Alias("fw_dist", basic_dist) | ||||||
|  | Default(basic_dist) | ||||||
|  | 
 | ||||||
|  | # Target for bundling core2 package for qFlipper | ||||||
|  | copro_dist = distenv.CoproBuilder( | ||||||
|  |     Dir("assets/core2_firmware"), | ||||||
|  |     [], | ||||||
|  | ) | ||||||
|  | AlwaysBuild(copro_dist) | ||||||
|  | Alias("copro_dist", copro_dist) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Debugging firmware | ||||||
|  | 
 | ||||||
|  | debug_fw_elf = distenv.AddDebugTarget(firmware_out) | ||||||
|  | Alias("debug", debug_fw_elf) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Debug alien elf | ||||||
|  | debug_other = distenv.GDBPy( | ||||||
|  |     "debugother.pseudo", | ||||||
|  |     None, | ||||||
|  |     GDBPYOPTS= | ||||||
|  |     # '-ex "source ${ROOT_DIR.abspath}/debug/FreeRTOS/FreeRTOS.py" ' | ||||||
|  |     '-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ' | ||||||
|  | ) | ||||||
|  | distenv.Pseudo("debugother.pseudo") | ||||||
|  | AlwaysBuild(debug_other) | ||||||
|  | Alias("debug_other", debug_other) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Just start OpenOCD | ||||||
|  | openocd = distenv.OOCDCommand("openocd.pseudo", []) | ||||||
|  | distenv.Pseudo("openocd.pseudo") | ||||||
|  | AlwaysBuild(openocd) | ||||||
|  | Alias("openocd", openocd) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Linter | ||||||
|  | lint_check = distenv.Command( | ||||||
|  |     "lint.check.pseudo", | ||||||
|  |     [], | ||||||
|  |     "${PYTHON3} scripts/lint.py check $LINT_SOURCES", | ||||||
|  |     LINT_SOURCES=firmware_out["LINT_SOURCES"], | ||||||
|  | ) | ||||||
|  | distenv.Pseudo("lint.check.pseudo") | ||||||
|  | AlwaysBuild(lint_check) | ||||||
|  | Alias("lint", lint_check) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | lint_format = distenv.Command( | ||||||
|  |     "lint.format.pseudo", | ||||||
|  |     [], | ||||||
|  |     "${PYTHON3} scripts/lint.py format $LINT_SOURCES", | ||||||
|  |     LINT_SOURCES=firmware_out["LINT_SOURCES"], | ||||||
|  | ) | ||||||
|  | distenv.Pseudo("lint.format.pseudo") | ||||||
|  | AlwaysBuild(lint_format) | ||||||
|  | Alias("format", lint_format) | ||||||
| @ -16,7 +16,6 @@ | |||||||
| - `ibutton`             - iButton application, onewire keys and more | - `ibutton`             - iButton application, onewire keys and more | ||||||
| - `input`               - Input service | - `input`               - Input service | ||||||
| - `infrared`            - Infrared application, controls your IR devices | - `infrared`            - Infrared application, controls your IR devices | ||||||
| - `infrared_monitor`    - Infrared debug tool |  | ||||||
| - `lfrfid`              - LF RFID application | - `lfrfid`              - LF RFID application | ||||||
| - `lfrfid_debug`        - LF RFID debug tool | - `lfrfid_debug`        - LF RFID debug tool | ||||||
| - `loader`              - Application loader service | - `loader`              - Application loader service | ||||||
| @ -36,6 +35,4 @@ | |||||||
| - `u2f`                 - U2F Application | - `u2f`                 - U2F Application | ||||||
| - `updater`             - Update service & application | - `updater`             - Update service & application | ||||||
| 
 | 
 | ||||||
| - `application.c`       - Firmware application list source |  | ||||||
| - `application.h`       - Firmware application list header | - `application.h`       - Firmware application list header | ||||||
| - `application.mk`      - Makefile helper |  | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #include <gui/modules/empty_screen.h> | #include <gui/modules/empty_screen.h> | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| #include <furi_hal_version.h> | #include <furi_hal_version.h> | ||||||
|  | #include <furi_hal_bt.h> | ||||||
| 
 | 
 | ||||||
| typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message); | typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message); | ||||||
| 
 | 
 | ||||||
| @ -57,7 +58,7 @@ static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* | |||||||
| static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* message) { | static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* message) { | ||||||
|     DialogMessageButton result; |     DialogMessageButton result; | ||||||
| 
 | 
 | ||||||
|     dialog_message_set_icon(message, &I_Certification1_103x23, 12, 12); |     dialog_message_set_icon(message, &I_Certification1_103x56, 13, 0); | ||||||
|     result = dialog_message_show(dialogs, message); |     result = dialog_message_show(dialogs, message); | ||||||
|     dialog_message_set_icon(message, NULL, 0, 0); |     dialog_message_set_icon(message, NULL, 0, 0); | ||||||
| 
 | 
 | ||||||
| @ -67,7 +68,7 @@ static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* mess | |||||||
| static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* message) { | static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* message) { | ||||||
|     DialogMessageButton result; |     DialogMessageButton result; | ||||||
| 
 | 
 | ||||||
|     dialog_message_set_icon(message, &I_Certification2_119x30, 4, 9); |     dialog_message_set_icon(message, &I_Certification2_98x33, 15, 10); | ||||||
|     result = dialog_message_show(dialogs, message); |     result = dialog_message_show(dialogs, message); | ||||||
|     dialog_message_set_icon(message, NULL, 0, 0); |     dialog_message_set_icon(message, NULL, 0, 0); | ||||||
| 
 | 
 | ||||||
| @ -111,18 +112,23 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* | |||||||
|     string_t buffer; |     string_t buffer; | ||||||
|     string_init(buffer); |     string_init(buffer); | ||||||
|     const Version* ver = furi_hal_version_get_firmware_version(); |     const Version* ver = furi_hal_version_get_firmware_version(); | ||||||
|  |     const BleGlueC2Info* c2_ver = NULL; | ||||||
|  | #ifdef SRV_BT | ||||||
|  |     c2_ver = ble_glue_get_c2_info(); | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|     if(!ver) { |     if(!ver) { | ||||||
|         string_cat_printf(buffer, "No info\n"); |         string_cat_printf(buffer, "No info\n"); | ||||||
|     } else { |     } else { | ||||||
|         string_cat_printf( |         string_cat_printf( | ||||||
|             buffer, |             buffer, | ||||||
|             "%s [%s]\n%s%s [%s]\n[%d] %s", |             "%s [%s]\n%s%s [%s] %s\n[%d] %s", | ||||||
|             version_get_version(ver), |             version_get_version(ver), | ||||||
|             version_get_builddate(ver), |             version_get_builddate(ver), | ||||||
|             version_get_dirty_flag(ver) ? "[!] " : "", |             version_get_dirty_flag(ver) ? "[!] " : "", | ||||||
|             version_get_githash(ver), |             version_get_githash(ver), | ||||||
|             version_get_gitbranchnum(ver), |             version_get_gitbranchnum(ver), | ||||||
|  |             c2_ver ? c2_ver->StackTypeString : "<none>", | ||||||
|             version_get_target(ver), |             version_get_target(ver), | ||||||
|             version_get_gitbranch(ver)); |             version_get_gitbranch(ver)); | ||||||
|     } |     } | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								applications/about/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/about/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | App( | ||||||
|  |     appid="about", | ||||||
|  |     name="About", | ||||||
|  |     apptype=FlipperAppType.SETTINGS, | ||||||
|  |     entry_point="about_settings_app", | ||||||
|  |     cdefines=["APP_ABOUT"], | ||||||
|  |     requires=[ | ||||||
|  |         "gui", | ||||||
|  |         "dialogs", | ||||||
|  |     ], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=1000, | ||||||
|  | ) | ||||||
							
								
								
									
										10
									
								
								applications/accessor/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								applications/accessor/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | App( | ||||||
|  |     appid="accessor", | ||||||
|  |     name="Accessor", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="accessor_app", | ||||||
|  |     cdefines=["APP_ACCESSOR"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=4 * 1024, | ||||||
|  |     order=40, | ||||||
|  | ) | ||||||
| @ -1,558 +0,0 @@ | |||||||
| #include "applications.h" |  | ||||||
| #include <assets_icons.h> |  | ||||||
| 
 |  | ||||||
| // Services
 |  | ||||||
| extern int32_t rpc_srv(void* p); |  | ||||||
| extern int32_t bt_srv(void* p); |  | ||||||
| extern int32_t cli_srv(void* p); |  | ||||||
| extern int32_t dialogs_srv(void* p); |  | ||||||
| extern int32_t dolphin_srv(void* p); |  | ||||||
| extern int32_t gui_srv(void* p); |  | ||||||
| extern int32_t input_srv(void* p); |  | ||||||
| extern int32_t loader_srv(void* p); |  | ||||||
| extern int32_t notification_srv(void* p); |  | ||||||
| extern int32_t power_srv(void* p); |  | ||||||
| extern int32_t storage_srv(void* p); |  | ||||||
| extern int32_t desktop_srv(void* p); |  | ||||||
| extern int32_t updater_srv(void* p); |  | ||||||
| 
 |  | ||||||
| // Apps
 |  | ||||||
| extern int32_t accessor_app(void* p); |  | ||||||
| extern int32_t archive_app(void* p); |  | ||||||
| extern int32_t bad_usb_app(void* p); |  | ||||||
| extern int32_t u2f_app(void* p); |  | ||||||
| extern int32_t uart_echo_app(void* p); |  | ||||||
| extern int32_t blink_test_app(void* p); |  | ||||||
| extern int32_t bt_debug_app(void* p); |  | ||||||
| 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 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); |  | ||||||
| extern int32_t nfc_app(void* p); |  | ||||||
| extern int32_t passport_app(void* p); |  | ||||||
| extern int32_t scened_app(void* p); |  | ||||||
| extern int32_t storage_test_app(void* p); |  | ||||||
| extern int32_t subghz_app(void* p); |  | ||||||
| extern int32_t usb_mouse_app(void* p); |  | ||||||
| extern int32_t usb_test_app(void* p); |  | ||||||
| extern int32_t vibro_test_app(void* p); |  | ||||||
| extern int32_t bt_hid_app(void* p); |  | ||||||
| extern int32_t battery_test_app(void* p); |  | ||||||
| extern int32_t text_box_test_app(void* p); |  | ||||||
| extern int32_t file_browser_app(void* p); |  | ||||||
| 
 |  | ||||||
| // Plugins
 |  | ||||||
| extern int32_t music_player_app(void* p); |  | ||||||
| extern int32_t snake_game_app(void* p); |  | ||||||
| 
 |  | ||||||
| // On system start hooks declaration
 |  | ||||||
| extern void bt_on_system_start(); |  | ||||||
| extern void crypto_on_system_start(); |  | ||||||
| extern void ibutton_on_system_start(); |  | ||||||
| extern void infrared_on_system_start(); |  | ||||||
| extern void lfrfid_on_system_start(); |  | ||||||
| extern void music_player_on_system_start(); |  | ||||||
| extern void nfc_on_system_start(); |  | ||||||
| extern void storage_on_system_start(); |  | ||||||
| extern void subghz_on_system_start(); |  | ||||||
| extern void power_on_system_start(); |  | ||||||
| extern void unit_tests_on_system_start(); |  | ||||||
| extern void updater_on_system_start(); |  | ||||||
| 
 |  | ||||||
| // Settings
 |  | ||||||
| extern int32_t notification_settings_app(void* p); |  | ||||||
| extern int32_t storage_settings_app(void* p); |  | ||||||
| extern int32_t bt_settings_app(void* p); |  | ||||||
| extern int32_t desktop_settings_app(void* p); |  | ||||||
| extern int32_t about_settings_app(void* p); |  | ||||||
| extern int32_t power_settings_app(void* p); |  | ||||||
| extern int32_t system_settings_app(void* p); |  | ||||||
| 
 |  | ||||||
| const FlipperApplication FLIPPER_SERVICES[] = { |  | ||||||
| /* Services */ |  | ||||||
| #ifdef SRV_RPC |  | ||||||
|     {.app = rpc_srv, |  | ||||||
|      .name = "RpcSrv", |  | ||||||
|      .stack_size = 1024 * 4, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_BT |  | ||||||
|     {.app = bt_srv, |  | ||||||
|      .name = "BtSrv", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_CLI |  | ||||||
|     {.app = cli_srv, |  | ||||||
|      .name = "CliSrv", |  | ||||||
|      .stack_size = 4096, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_DIALOGS |  | ||||||
|     {.app = dialogs_srv, |  | ||||||
|      .name = "DialogsSrv", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_DOLPHIN |  | ||||||
|     {.app = dolphin_srv, |  | ||||||
|      .name = "DolphinSrv", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_DESKTOP |  | ||||||
| #ifdef SRV_UPDATER |  | ||||||
| #error SRV_UPDATER and SRV_DESKTOP are mutually exclusive! |  | ||||||
| #endif |  | ||||||
|     {.app = desktop_srv, |  | ||||||
|      .name = "DesktopSrv", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_GUI |  | ||||||
|     {.app = gui_srv, |  | ||||||
|      .name = "GuiSrv", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_INPUT |  | ||||||
|     {.app = input_srv, |  | ||||||
|      .name = "InputSrv", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_LOADER |  | ||||||
|     {.app = loader_srv, |  | ||||||
|      .name = "LoaderSrv", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_NOTIFICATION |  | ||||||
|     {.app = notification_srv, |  | ||||||
|      .name = "NotificationSrv", |  | ||||||
|      .stack_size = 1536, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_POWER |  | ||||||
|     {.app = power_srv, |  | ||||||
|      .name = "PowerSrv", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_STORAGE |  | ||||||
|     {.app = storage_srv, |  | ||||||
|      .name = "StorageSrv", |  | ||||||
|      .stack_size = 3072, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_UPDATER |  | ||||||
| #ifdef SRV_DESKTOP |  | ||||||
| #error SRV_UPDATER and SRV_DESKTOP are mutually exclusive! |  | ||||||
| #endif |  | ||||||
|     {.app = updater_srv, |  | ||||||
|      .name = "UpdaterSrv", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const size_t FLIPPER_SERVICES_COUNT = COUNT_OF(FLIPPER_SERVICES); |  | ||||||
| 
 |  | ||||||
| const FlipperApplication FLIPPER_SYSTEM_APPS[] = { |  | ||||||
| #ifdef APP_UPDATER |  | ||||||
| #ifdef SRV_UPDATER |  | ||||||
| #error APP_UPDATER and SRV_UPDATER are mutually exclusive! |  | ||||||
| #endif |  | ||||||
|     {.app = updater_srv, |  | ||||||
|      .name = "UpdaterApp", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const size_t FLIPPER_SYSTEM_APPS_COUNT = COUNT_OF(FLIPPER_SYSTEM_APPS); |  | ||||||
| 
 |  | ||||||
| // Main menu APP
 |  | ||||||
| const FlipperApplication FLIPPER_APPS[] = { |  | ||||||
| 
 |  | ||||||
| #ifdef APP_SUBGHZ |  | ||||||
|     {.app = subghz_app, |  | ||||||
|      .name = "Sub-GHz", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = &A_Sub1ghz_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_LF_RFID |  | ||||||
|     {.app = lfrfid_app, |  | ||||||
|      .name = "125 kHz RFID", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = &A_125khz_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_NFC |  | ||||||
|     {.app = nfc_app, |  | ||||||
|      .name = "NFC", |  | ||||||
|      .stack_size = 4096, |  | ||||||
|      .icon = &A_NFC_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_INFRARED |  | ||||||
|     {.app = infrared_app, |  | ||||||
|      .name = "Infrared", |  | ||||||
|      .stack_size = 1024 * 3, |  | ||||||
|      .icon = &A_Infrared_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_GPIO |  | ||||||
|     {.app = gpio_app, |  | ||||||
|      .name = "GPIO", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = &A_GPIO_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_IBUTTON |  | ||||||
|     {.app = ibutton_app, |  | ||||||
|      .name = "iButton", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = &A_iButton_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_BAD_USB |  | ||||||
|     {.app = bad_usb_app, |  | ||||||
|      .name = "Bad USB", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = &A_BadUsb_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_U2F |  | ||||||
|     {.app = u2f_app, |  | ||||||
|      .name = "U2F", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = &A_U2F_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const size_t FLIPPER_APPS_COUNT = COUNT_OF(FLIPPER_APPS); |  | ||||||
| 
 |  | ||||||
| // On system start hooks
 |  | ||||||
| const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { |  | ||||||
|     crypto_on_system_start, |  | ||||||
| 
 |  | ||||||
| #ifdef APP_INFRARED |  | ||||||
|     infrared_on_system_start, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_MUSIC_PLAYER |  | ||||||
|     music_player_on_system_start, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_NFC |  | ||||||
|     nfc_on_system_start, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_SUBGHZ |  | ||||||
|     subghz_on_system_start, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_LF_RFID |  | ||||||
|     lfrfid_on_system_start, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_IBUTTON |  | ||||||
|     ibutton_on_system_start, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_BT |  | ||||||
|     bt_on_system_start, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_POWER |  | ||||||
|     power_on_system_start, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_STORAGE |  | ||||||
|     storage_on_system_start, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_UNIT_TESTS |  | ||||||
|     unit_tests_on_system_start, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_UPDATER |  | ||||||
|     updater_on_system_start, |  | ||||||
| #endif |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const size_t FLIPPER_ON_SYSTEM_START_COUNT = COUNT_OF(FLIPPER_ON_SYSTEM_START); |  | ||||||
| 
 |  | ||||||
| // Plugin menu
 |  | ||||||
| const FlipperApplication FLIPPER_PLUGINS[] = { |  | ||||||
| #ifdef APP_BLE_HID |  | ||||||
|     {.app = bt_hid_app, |  | ||||||
|      .name = "Bluetooth Remote", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_MUSIC_PLAYER |  | ||||||
|     {.app = music_player_app, |  | ||||||
|      .name = "Music Player", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = &A_Plugins_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_SNAKE_GAME |  | ||||||
|     {.app = snake_game_app, |  | ||||||
|      .name = "Snake Game", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = &A_Plugins_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const size_t FLIPPER_PLUGINS_COUNT = COUNT_OF(FLIPPER_PLUGINS); |  | ||||||
| 
 |  | ||||||
| // Plugin menu
 |  | ||||||
| const FlipperApplication FLIPPER_DEBUG_APPS[] = { |  | ||||||
| #ifdef APP_BLINK |  | ||||||
|     {.app = blink_test_app, |  | ||||||
|      .name = "Blink Test", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_VIBRO_TEST |  | ||||||
|     {.app = vibro_test_app, |  | ||||||
|      .name = "Vibro Test", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_KEYPAD_TEST |  | ||||||
|     {.app = keypad_test_app, |  | ||||||
|      .name = "Keypad Test", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_ACCESSOR |  | ||||||
|     {.app = accessor_app, |  | ||||||
|      .name = "Accessor", |  | ||||||
|      .stack_size = 4096, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_USB_TEST |  | ||||||
|     {.app = usb_test_app, |  | ||||||
|      .name = "USB Test", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_USB_MOUSE |  | ||||||
|     {.app = usb_mouse_app, |  | ||||||
|      .name = "USB Mouse Demo", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_UART_ECHO |  | ||||||
|     {.app = uart_echo_app, |  | ||||||
|      .name = "Uart Echo", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_INFRARED_MONITOR |  | ||||||
|     {.app = infrared_monitor_app, |  | ||||||
|      .name = "Infrared Monitor", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_LF_RFID |  | ||||||
|     {.app = lfrfid_debug_app, |  | ||||||
|      .name = "LF-RFID Debug", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_BT |  | ||||||
|     {.app = bt_debug_app, |  | ||||||
|      .name = "Bluetooth Debug", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_UNIT_TESTS |  | ||||||
|     {.app = delay_test_app, |  | ||||||
|      .name = "Delay Test", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_DISPLAY_TEST |  | ||||||
|     {.app = display_test_app, |  | ||||||
|      .name = "Display Test", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_FILE_BROWSER_TEST |  | ||||||
|     {.app = file_browser_app, |  | ||||||
|      .name = "File Browser test", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = &A_BadUsb_14, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_BATTERY_TEST |  | ||||||
|     {.app = battery_test_app, |  | ||||||
|      .name = "Battery Test", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_TEXT_BOX_TEST |  | ||||||
|     {.app = text_box_test_app, |  | ||||||
|      .name = "Text Box Test", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS); |  | ||||||
| 
 |  | ||||||
| #ifdef APP_ARCHIVE |  | ||||||
| const FlipperApplication FLIPPER_ARCHIVE = { |  | ||||||
|     .app = archive_app, |  | ||||||
|     .name = "Archive", |  | ||||||
|     .stack_size = 4096, |  | ||||||
|     .icon = &A_FileManager_14, |  | ||||||
|     .flags = FlipperApplicationFlagDefault}; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| // Settings menu
 |  | ||||||
| const FlipperApplication FLIPPER_SETTINGS_APPS[] = { |  | ||||||
| #ifdef SRV_BT |  | ||||||
|     {.app = bt_settings_app, |  | ||||||
|      .name = "Bluetooth", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_NOTIFICATION |  | ||||||
|     {.app = notification_settings_app, |  | ||||||
|      .name = "LCD and Notifications", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_STORAGE |  | ||||||
|     {.app = storage_settings_app, |  | ||||||
|      .name = "Storage", |  | ||||||
|      .stack_size = 2048, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_POWER |  | ||||||
|     {.app = power_settings_app, |  | ||||||
|      .name = "Power", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagInsomniaSafe}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_DESKTOP |  | ||||||
|     {.app = desktop_settings_app, |  | ||||||
|      .name = "Desktop", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_PASSPORT |  | ||||||
|     {.app = passport_app, |  | ||||||
|      .name = "Passport", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_GUI |  | ||||||
|     {.app = system_settings_app, |  | ||||||
|      .name = "System", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_ABOUT |  | ||||||
|     {.app = about_settings_app, |  | ||||||
|      .name = "About", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const size_t FLIPPER_SETTINGS_APPS_COUNT = COUNT_OF(FLIPPER_SETTINGS_APPS); |  | ||||||
| @ -1,362 +0,0 @@ | |||||||
| APP_DIR		= $(PROJECT_ROOT)/applications |  | ||||||
| LIB_DIR		= $(PROJECT_ROOT)/lib |  | ||||||
| 
 |  | ||||||
| CFLAGS		+= -I$(APP_DIR) |  | ||||||
| C_SOURCES	+= $(shell find $(APP_DIR) -name "*.c") |  | ||||||
| CPP_SOURCES	+= $(shell find $(APP_DIR) -name "*.cpp") |  | ||||||
| 
 |  | ||||||
| RAM_EXEC ?= 0 |  | ||||||
| ifeq ($(RAM_EXEC), 1) |  | ||||||
| APP_RELEASE	= 0 |  | ||||||
| SRV_GUI 	= 1 |  | ||||||
| SRV_INPUT 	= 1 |  | ||||||
| SRV_NOTIFICATION	= 1 |  | ||||||
| SRV_STORAGE	= 1 |  | ||||||
| SRV_UPDATER	= 1 |  | ||||||
| APP_UPDATER	= 0 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_RELEASE ?= 1 |  | ||||||
| ifeq ($(APP_RELEASE), 1) |  | ||||||
| # Services
 |  | ||||||
| SRV_BT		= 1 |  | ||||||
| SRV_CLI		= 1 |  | ||||||
| SRV_DIALOGS	= 1 |  | ||||||
| SRV_DOLPHIN	= 1 |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| SRV_INPUT	= 1 |  | ||||||
| SRV_LOADER	= 1 |  | ||||||
| SRV_NOTIFICATION = 1 |  | ||||||
| SRV_POWER	= 1 |  | ||||||
| SRV_RPC 	= 1 |  | ||||||
| SRV_STORAGE	= 1 |  | ||||||
| 
 |  | ||||||
| # Apps
 |  | ||||||
| SRV_DESKTOP	= 1 |  | ||||||
| APP_ARCHIVE	= 1 |  | ||||||
| APP_GPIO 	= 1 |  | ||||||
| APP_IBUTTON	= 1 |  | ||||||
| APP_INFRARED	= 1 |  | ||||||
| APP_LF_RFID	= 1 |  | ||||||
| APP_NFC		= 1 |  | ||||||
| APP_SUBGHZ	= 1 |  | ||||||
| APP_ABOUT	= 1 |  | ||||||
| APP_PASSPORT = 1 |  | ||||||
| APP_UPDATER = 1 |  | ||||||
| 
 |  | ||||||
| # Plugins
 |  | ||||||
| APP_MUSIC_PLAYER = 1 |  | ||||||
| APP_SNAKE_GAME = 1 |  | ||||||
| 
 |  | ||||||
| # Debug
 |  | ||||||
| APP_ACCESSOR = 1 |  | ||||||
| APP_BLINK = 1 |  | ||||||
| APP_INFRARED_MONITOR = 1 |  | ||||||
| APP_KEYPAD_TEST = 1 |  | ||||||
| APP_SD_TEST	= 1 |  | ||||||
| APP_VIBRO_TEST = 1 |  | ||||||
| APP_USB_TEST = 1 |  | ||||||
| APP_DISPLAY_TEST = 1 |  | ||||||
| APP_BLE_HID = 1 |  | ||||||
| APP_USB_MOUSE = 1 |  | ||||||
| APP_BAD_USB = 1 |  | ||||||
| APP_U2F = 1 |  | ||||||
| APP_UART_ECHO = 1 |  | ||||||
| APP_FILE_BROWSER_TEST = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Applications
 |  | ||||||
| # that will be shown in menu
 |  | ||||||
| # Prefix with APP_*
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_INFRARED_MONITOR	?= 0 |  | ||||||
| ifeq ($(APP_INFRARED_MONITOR), 1) |  | ||||||
| CFLAGS		+= -DAPP_INFRARED_MONITOR |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_UNIT_TESTS ?= 0 |  | ||||||
| ifeq ($(APP_UNIT_TESTS), 1) |  | ||||||
| CFLAGS		+= -DAPP_UNIT_TESTS |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_ARCHIVE ?= 0 |  | ||||||
| ifeq ($(APP_ARCHIVE), 1) |  | ||||||
| CFLAGS		+= -DAPP_ARCHIVE |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_BLINK ?= 0 |  | ||||||
| ifeq ($(APP_BLINK), 1) |  | ||||||
| CFLAGS		+= -DAPP_BLINK |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_SUBGHZ ?= 0 |  | ||||||
| ifeq ($(APP_SUBGHZ), 1) |  | ||||||
| CFLAGS		+= -DAPP_SUBGHZ |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| SRV_CLI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_ABOUT ?= 0 |  | ||||||
| ifeq ($(APP_ABOUT), 1) |  | ||||||
| CFLAGS		+= -DAPP_ABOUT |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_PASSPORT ?= 0 |  | ||||||
| ifeq ($(APP_PASSPORT), 1) |  | ||||||
| CFLAGS		+= -DAPP_PASSPORT |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_LF_RFID ?= 0 |  | ||||||
| ifeq ($(APP_LF_RFID), 1) |  | ||||||
| CFLAGS		+= -DAPP_LF_RFID |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_NFC ?= 0 |  | ||||||
| ifeq ($(APP_NFC), 1) |  | ||||||
| CFLAGS		+= -DAPP_NFC |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_INFRARED ?= 0 |  | ||||||
| ifeq ($(APP_INFRARED), 1) |  | ||||||
| CFLAGS		+= -DAPP_INFRARED |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_VIBRO_TEST ?= 0 |  | ||||||
| ifeq ($(APP_VIBRO_TEST), 1) |  | ||||||
| CFLAGS		+= -DAPP_VIBRO_TEST |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_USB_TEST ?= 0 |  | ||||||
| ifeq ($(APP_USB_TEST), 1) |  | ||||||
| CFLAGS		+= -DAPP_USB_TEST |  | ||||||
| SRV_GUI = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_UART_ECHO ?= 0 |  | ||||||
| ifeq ($(APP_UART_ECHO), 1) |  | ||||||
| CFLAGS		+= -DAPP_UART_ECHO |  | ||||||
| SRV_GUI = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_DISPLAY_TEST ?= 0 |  | ||||||
| ifeq ($(APP_DISPLAY_TEST), 1) |  | ||||||
| CFLAGS		+= -DAPP_DISPLAY_TEST |  | ||||||
| SRV_GUI = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_TEXT_BOX_TEST ?= 0 |  | ||||||
| ifeq ($(APP_TEXT_BOX_TEST), 1) |  | ||||||
| CFLAGS		+= -DAPP_TEXT_BOX_TEST |  | ||||||
| SRV_GUI = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_BATTERY_TEST ?= 0 |  | ||||||
| ifeq ($(APP_BATTERY_TEST), 1) |  | ||||||
| CFLAGS		+= -DAPP_BATTERY_TEST |  | ||||||
| SRV_GUI = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_USB_MOUSE ?= 0 |  | ||||||
| ifeq ($(APP_USB_MOUSE), 1) |  | ||||||
| CFLAGS		+= -DAPP_USB_MOUSE |  | ||||||
| SRV_GUI = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_BAD_USB ?= 0 |  | ||||||
| ifeq ($(APP_BAD_USB), 1) |  | ||||||
| CFLAGS		+= -DAPP_BAD_USB |  | ||||||
| SRV_GUI = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_U2F ?= 0 |  | ||||||
| ifeq ($(APP_U2F), 1) |  | ||||||
| CFLAGS		+= -DAPP_U2F |  | ||||||
| SRV_GUI = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_BLE_HID ?=0 |  | ||||||
| ifeq ($(APP_BLE_HID), 1) |  | ||||||
| CFLAGS		+= -DAPP_BLE_HID |  | ||||||
| SRV_GUI = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_KEYPAD_TEST ?= 0 |  | ||||||
| ifeq ($(APP_KEYPAD_TEST), 1) |  | ||||||
| CFLAGS		+= -DAPP_KEYPAD_TEST |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_FILE_BROWSER_TEST ?= 0 |  | ||||||
| ifeq ($(APP_FILE_BROWSER_TEST), 1) |  | ||||||
| CFLAGS		+= -DAPP_FILE_BROWSER_TEST |  | ||||||
| SRV_GUI = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_ACCESSOR ?= 0 |  | ||||||
| ifeq ($(APP_ACCESSOR), 1) |  | ||||||
| CFLAGS		+= -DAPP_ACCESSOR |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_GPIO ?= 0 |  | ||||||
| ifeq ($(APP_GPIO), 1) |  | ||||||
| CFLAGS		+= -DAPP_GPIO |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_MUSIC_PLAYER ?= 0 |  | ||||||
| ifeq ($(APP_MUSIC_PLAYER), 1) |  | ||||||
| CFLAGS		+= -DAPP_MUSIC_PLAYER |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_SNAKE_GAME ?= 0 |  | ||||||
| ifeq ($(APP_SNAKE_GAME), 1) |  | ||||||
| CFLAGS		+= -DAPP_SNAKE_GAME |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_IBUTTON ?= 0 |  | ||||||
| ifeq ($(APP_IBUTTON), 1) |  | ||||||
| CFLAGS		+= -DAPP_IBUTTON |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| APP_UPDATER ?= 0 |  | ||||||
| ifeq ($(APP_UPDATER), 1) |  | ||||||
| CFLAGS		+= -DAPP_UPDATER |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| SRV_STORAGE = 1 |  | ||||||
| SRV_NOTIFICATION = 1 |  | ||||||
| SRV_INPUT   = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| # Services
 |  | ||||||
| # that will start with OS
 |  | ||||||
| # Prefix with SRV_*
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_BT ?= 0 |  | ||||||
| ifeq ($(SRV_BT), 1) |  | ||||||
| CFLAGS		+= -DSRV_BT |  | ||||||
| SRV_CLI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_DESKTOP ?= 0 |  | ||||||
| ifeq ($(SRV_DESKTOP), 1) |  | ||||||
| CFLAGS		+= -DSRV_DESKTOP |  | ||||||
| SRV_DOLPHIN	= 1 |  | ||||||
| SRV_STORAGE	= 1 |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_UPDATER ?= 0 |  | ||||||
| ifeq ($(SRV_UPDATER), 1) |  | ||||||
| CFLAGS		+= -DSRV_UPDATER |  | ||||||
| SRV_STORAGE	= 1 |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_DOLPHIN ?= 0 |  | ||||||
| ifeq ($(SRV_DOLPHIN), 1) |  | ||||||
| CFLAGS		+= -DSRV_DOLPHIN |  | ||||||
| SRV_DOLPHIN_STATE_DEBUG ?= 0 |  | ||||||
| ifeq ($(SRV_DOLPHIN_STATE_DEBUG), 1) |  | ||||||
| CFLAGS		+= -DSRV_DOLPHIN_STATE_DEBUG |  | ||||||
| endif |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_POWER ?= 0 |  | ||||||
| ifeq ($(SRV_POWER), 1) |  | ||||||
| CFLAGS		+= -DSRV_POWER |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| SRV_CLI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| SRV_RPC ?= 0 |  | ||||||
| ifeq ($(SRV_RPC), 1) |  | ||||||
| CFLAGS		+= -DSRV_RPC |  | ||||||
| ifeq ($(SRV_RPC_DEBUG), 1) |  | ||||||
| CFLAGS		+= -DSRV_RPC_DEBUG |  | ||||||
| endif |  | ||||||
| SRV_CLI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| SRV_LOADER ?= 0 |  | ||||||
| ifeq ($(SRV_LOADER), 1) |  | ||||||
| CFLAGS		+= -DSRV_LOADER |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| # Loader autostart hook
 |  | ||||||
| LOADER_AUTOSTART ?= "" |  | ||||||
| ifneq ($(strip $(LOADER_AUTOSTART)), "") |  | ||||||
| CFLAGS		+= -DLOADER_AUTOSTART="\"$(LOADER_AUTOSTART)\"" |  | ||||||
| endif |  | ||||||
| # Loader autostart hook END
 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_DIALOGS ?= 0 |  | ||||||
| ifeq ($(SRV_DIALOGS), 1) |  | ||||||
| CFLAGS		+= -DSRV_DIALOGS |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_GUI	?= 0 |  | ||||||
| ifeq ($(SRV_GUI), 1) |  | ||||||
| CFLAGS		+= -DSRV_GUI |  | ||||||
| SRV_INPUT	= 1 |  | ||||||
| SRV_NOTIFICATION = 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_INPUT	?= 0 |  | ||||||
| ifeq ($(SRV_INPUT), 1) |  | ||||||
| CFLAGS		+= -DSRV_INPUT |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_CLI ?= 0 |  | ||||||
| ifeq ($(SRV_CLI), 1) |  | ||||||
| CFLAGS		+= -DSRV_CLI |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_NOTIFICATION ?= 0 |  | ||||||
| ifeq ($(SRV_NOTIFICATION), 1) |  | ||||||
| CFLAGS		+= -DSRV_NOTIFICATION |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_STORAGE ?= 0 |  | ||||||
| ifeq ($(SRV_STORAGE), 1) |  | ||||||
| CFLAGS		+= -DSRV_STORAGE |  | ||||||
| endif |  | ||||||
							
								
								
									
										11
									
								
								applications/archive/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								applications/archive/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | App( | ||||||
|  |     appid="archive", | ||||||
|  |     name="Archive", | ||||||
|  |     apptype=FlipperAppType.ARCHIVE, | ||||||
|  |     entry_point="archive_app", | ||||||
|  |     cdefines=["APP_ARCHIVE"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=4 * 1024, | ||||||
|  |     icon="A_FileManager_14", | ||||||
|  |     order=0, | ||||||
|  | ) | ||||||
							
								
								
									
										14
									
								
								applications/bad_usb/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								applications/bad_usb/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | App( | ||||||
|  |     appid="bad_usb", | ||||||
|  |     name="Bad USB", | ||||||
|  |     apptype=FlipperAppType.APP, | ||||||
|  |     entry_point="bad_usb_app", | ||||||
|  |     cdefines=["APP_BAD_USB"], | ||||||
|  |     requires=[ | ||||||
|  |         "gui", | ||||||
|  |         "dialogs", | ||||||
|  |     ], | ||||||
|  |     stack_size=2 * 1024, | ||||||
|  |     icon="A_BadUsb_14", | ||||||
|  |     order=70, | ||||||
|  | ) | ||||||
| @ -440,9 +440,9 @@ static void bad_usb_hid_state_callback(bool state, void* context) { | |||||||
|     BadUsbScript* bad_usb = context; |     BadUsbScript* bad_usb = context; | ||||||
| 
 | 
 | ||||||
|     if(state == true) |     if(state == true) | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(bad_usb->thread), WorkerEvtConnect); |         furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect); | ||||||
|     else |     else | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(bad_usb->thread), WorkerEvtDisconnect); |         furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int32_t bad_usb_worker(void* context) { | static int32_t bad_usb_worker(void* context) { | ||||||
| @ -483,8 +483,8 @@ static int32_t bad_usb_worker(void* context) { | |||||||
|             bad_usb->st.state = worker_state; |             bad_usb->st.state = worker_state; | ||||||
| 
 | 
 | ||||||
|         } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
 |         } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
 | ||||||
|             uint32_t flags = |             uint32_t flags = furi_thread_flags_wait( | ||||||
|                 osThreadFlagsWait(WorkerEvtEnd | WorkerEvtConnect, osFlagsWaitAny, osWaitForever); |                 WorkerEvtEnd | WorkerEvtConnect, osFlagsWaitAny, osWaitForever); | ||||||
|             furi_check((flags & osFlagsError) == 0); |             furi_check((flags & osFlagsError) == 0); | ||||||
|             if(flags & WorkerEvtEnd) { |             if(flags & WorkerEvtEnd) { | ||||||
|                 break; |                 break; | ||||||
| @ -494,7 +494,7 @@ static int32_t bad_usb_worker(void* context) { | |||||||
|             bad_usb->st.state = worker_state; |             bad_usb->st.state = worker_state; | ||||||
| 
 | 
 | ||||||
|         } else if(worker_state == BadUsbStateIdle) { // State: ready to start
 |         } else if(worker_state == BadUsbStateIdle) { // State: ready to start
 | ||||||
|             uint32_t flags = osThreadFlagsWait( |             uint32_t flags = furi_thread_flags_wait( | ||||||
|                 WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, |                 WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, | ||||||
|                 osFlagsWaitAny, |                 osFlagsWaitAny, | ||||||
|                 osWaitForever); |                 osWaitForever); | ||||||
| @ -518,7 +518,7 @@ static int32_t bad_usb_worker(void* context) { | |||||||
| 
 | 
 | ||||||
|         } else if(worker_state == BadUsbStateRunning) { // State: running
 |         } else if(worker_state == BadUsbStateRunning) { // State: running
 | ||||||
|             uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); |             uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); | ||||||
|             uint32_t flags = osThreadFlagsWait( |             uint32_t flags = furi_thread_flags_wait( | ||||||
|                 WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, osFlagsWaitAny, delay_cur); |                 WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, osFlagsWaitAny, delay_cur); | ||||||
|             delay_val -= delay_cur; |             delay_val -= delay_cur; | ||||||
|             if(!(flags & osFlagsError)) { |             if(!(flags & osFlagsError)) { | ||||||
| @ -561,7 +561,7 @@ static int32_t bad_usb_worker(void* context) { | |||||||
|         } else if( |         } else if( | ||||||
|             (worker_state == BadUsbStateFileError) || |             (worker_state == BadUsbStateFileError) || | ||||||
|             (worker_state == BadUsbStateScriptError)) { // State: error
 |             (worker_state == BadUsbStateScriptError)) { // State: error
 | ||||||
|             uint32_t flags = osThreadFlagsWait( |             uint32_t flags = furi_thread_flags_wait( | ||||||
|                 WorkerEvtEnd, osFlagsWaitAny, osWaitForever); // Waiting for exit command
 |                 WorkerEvtEnd, osFlagsWaitAny, osWaitForever); // Waiting for exit command
 | ||||||
|             furi_check((flags & osFlagsError) == 0); |             furi_check((flags & osFlagsError) == 0); | ||||||
|             if(flags & WorkerEvtEnd) { |             if(flags & WorkerEvtEnd) { | ||||||
| @ -605,7 +605,7 @@ BadUsbScript* bad_usb_script_open(string_t file_path) { | |||||||
| 
 | 
 | ||||||
| void bad_usb_script_close(BadUsbScript* bad_usb) { | void bad_usb_script_close(BadUsbScript* bad_usb) { | ||||||
|     furi_assert(bad_usb); |     furi_assert(bad_usb); | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(bad_usb->thread), WorkerEvtEnd); |     furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtEnd); | ||||||
|     furi_thread_join(bad_usb->thread); |     furi_thread_join(bad_usb->thread); | ||||||
|     furi_thread_free(bad_usb->thread); |     furi_thread_free(bad_usb->thread); | ||||||
|     string_clear(bad_usb->file_path); |     string_clear(bad_usb->file_path); | ||||||
| @ -614,7 +614,7 @@ void bad_usb_script_close(BadUsbScript* bad_usb) { | |||||||
| 
 | 
 | ||||||
| void bad_usb_script_toggle(BadUsbScript* bad_usb) { | void bad_usb_script_toggle(BadUsbScript* bad_usb) { | ||||||
|     furi_assert(bad_usb); |     furi_assert(bad_usb); | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(bad_usb->thread), WorkerEvtToggle); |     furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) { | BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) { | ||||||
|  | |||||||
							
								
								
									
										66
									
								
								applications/bt/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								applications/bt/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | |||||||
|  | App( | ||||||
|  |     appid="bt", | ||||||
|  |     name="BtSrv", | ||||||
|  |     apptype=FlipperAppType.SERVICE, | ||||||
|  |     entry_point="bt_srv", | ||||||
|  |     cdefines=["SRV_BT"], | ||||||
|  |     requires=[ | ||||||
|  |         "cli", | ||||||
|  |         "dialogs", | ||||||
|  |     ], | ||||||
|  |     provides=[ | ||||||
|  |         "bt_start", | ||||||
|  |         "bt_settings", | ||||||
|  |         "bt_debug", | ||||||
|  |     ], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=20, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="bt_start", | ||||||
|  |     apptype=FlipperAppType.STARTUP, | ||||||
|  |     entry_point="bt_on_system_start", | ||||||
|  |     order=70, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="bt_settings", | ||||||
|  |     name="Bluetooth", | ||||||
|  |     apptype=FlipperAppType.SETTINGS, | ||||||
|  |     entry_point="bt_settings_app", | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     requires=[ | ||||||
|  |         "bt", | ||||||
|  |         "gui", | ||||||
|  |     ], | ||||||
|  |     order=10, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="bt_debug", | ||||||
|  |     name="Bluetooth Debug", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="bt_debug_app", | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     requires=[ | ||||||
|  |         "bt", | ||||||
|  |         "gui", | ||||||
|  |         "dialogs", | ||||||
|  |     ], | ||||||
|  |     order=110, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="bt_hid", | ||||||
|  |     name="Bluetooth Remote", | ||||||
|  |     apptype=FlipperAppType.PLUGIN, | ||||||
|  |     entry_point="bt_hid_app", | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     cdefines=["APP_BLE_HID"], | ||||||
|  |     requires=[ | ||||||
|  |         "bt", | ||||||
|  |         "gui", | ||||||
|  |     ], | ||||||
|  |     order=10, | ||||||
|  | ) | ||||||
							
								
								
									
										9
									
								
								applications/cli/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								applications/cli/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | App( | ||||||
|  |     appid="cli", | ||||||
|  |     name="CliSrv", | ||||||
|  |     apptype=FlipperAppType.SERVICE, | ||||||
|  |     entry_point="cli_srv", | ||||||
|  |     cdefines=["SRV_CLI"], | ||||||
|  |     stack_size=4 * 1024, | ||||||
|  |     order=30, | ||||||
|  | ) | ||||||
| @ -255,19 +255,19 @@ void cli_command_ps(Cli* cli, string_t args, void* context) { | |||||||
|     UNUSED(context); |     UNUSED(context); | ||||||
| 
 | 
 | ||||||
|     const uint8_t threads_num_max = 32; |     const uint8_t threads_num_max = 32; | ||||||
|     osThreadId_t threads_id[threads_num_max]; |     FuriThreadId threads_ids[threads_num_max]; | ||||||
|     uint8_t thread_num = osThreadEnumerate(threads_id, threads_num_max); |     uint8_t thread_num = furi_thread_enumerate(threads_ids, threads_num_max); | ||||||
|     printf( |     printf( | ||||||
|         "%-20s %-14s %-8s %-8s %s\r\n", "Name", "Stack start", "Heap", "Stack", "Stack min free"); |         "%-20s %-14s %-8s %-8s %s\r\n", "Name", "Stack start", "Heap", "Stack", "Stack min free"); | ||||||
|     for(uint8_t i = 0; i < thread_num; i++) { |     for(uint8_t i = 0; i < thread_num; i++) { | ||||||
|         TaskControlBlock* tcb = (TaskControlBlock*)threads_id[i]; |         TaskControlBlock* tcb = (TaskControlBlock*)threads_ids[i]; | ||||||
|         printf( |         printf( | ||||||
|             "%-20s 0x%-12lx %-8d %-8ld %-8ld\r\n", |             "%-20s 0x%-12lx %-8d %-8ld %-8ld\r\n", | ||||||
|             osThreadGetName(threads_id[i]), |             furi_thread_get_name(threads_ids[i]), | ||||||
|             (uint32_t)tcb->pxStack, |             (uint32_t)tcb->pxStack, | ||||||
|             memmgr_heap_get_thread_memory(threads_id[i]), |             memmgr_heap_get_thread_memory(threads_ids[i]), | ||||||
|             (uint32_t)(tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t), |             (uint32_t)(tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t), | ||||||
|             osThreadGetStackSpace(threads_id[i])); |             furi_thread_get_stack_space(threads_ids[i])); | ||||||
|     } |     } | ||||||
|     printf("\r\nTotal: %d", thread_num); |     printf("\r\nTotal: %d", thread_num); | ||||||
| } | } | ||||||
|  | |||||||
| @ -79,7 +79,7 @@ static void cli_vcp_init() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void cli_vcp_deinit() { | static void cli_vcp_deinit() { | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStop); |     furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStop); | ||||||
|     furi_thread_join(vcp->thread); |     furi_thread_join(vcp->thread); | ||||||
|     furi_thread_free(vcp->thread); |     furi_thread_free(vcp->thread); | ||||||
|     vcp->thread = NULL; |     vcp->thread = NULL; | ||||||
| @ -102,7 +102,8 @@ static int32_t vcp_worker(void* context) { | |||||||
|     vcp->running = true; |     vcp->running = true; | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); |         uint32_t flags = | ||||||
|  |             furi_thread_flags_wait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); | ||||||
|         furi_assert((flags & osFlagsError) == 0); |         furi_assert((flags & osFlagsError) == 0); | ||||||
| 
 | 
 | ||||||
|         // VCP session opened
 |         // VCP session opened
 | ||||||
| @ -232,7 +233,7 @@ static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) { | |||||||
|         FURI_LOG_D(TAG, "rx %u ", batch_size); |         FURI_LOG_D(TAG, "rx %u ", batch_size); | ||||||
| #endif | #endif | ||||||
|         if(len == 0) break; |         if(len == 0) break; | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamRx); |         furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamRx); | ||||||
|         size -= len; |         size -= len; | ||||||
|         buffer += len; |         buffer += len; | ||||||
|         rx_cnt += len; |         rx_cnt += len; | ||||||
| @ -261,7 +262,7 @@ static void cli_vcp_tx(const uint8_t* buffer, size_t size) { | |||||||
|         if(batch_size > USB_CDC_PKT_LEN) batch_size = USB_CDC_PKT_LEN; |         if(batch_size > USB_CDC_PKT_LEN) batch_size = USB_CDC_PKT_LEN; | ||||||
| 
 | 
 | ||||||
|         xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever); |         xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever); | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamTx); |         furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamTx); | ||||||
| #ifdef CLI_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|         FURI_LOG_D(TAG, "tx %u", batch_size); |         FURI_LOG_D(TAG, "tx %u", batch_size); | ||||||
| #endif | #endif | ||||||
| @ -283,7 +284,7 @@ static void cli_vcp_tx_stdout(void* _cookie, const char* data, size_t size) { | |||||||
| static void vcp_state_callback(void* context, uint8_t state) { | static void vcp_state_callback(void* context, uint8_t state) { | ||||||
|     UNUSED(context); |     UNUSED(context); | ||||||
|     if(state == 0) { |     if(state == 0) { | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect); |         furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtDisconnect); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -293,21 +294,21 @@ static void vcp_on_cdc_control_line(void* context, uint8_t state) { | |||||||
|     bool dtr = state & (1 << 0); |     bool dtr = state & (1 << 0); | ||||||
| 
 | 
 | ||||||
|     if(dtr == true) { |     if(dtr == true) { | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtConnect); |         furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtConnect); | ||||||
|     } else { |     } else { | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect); |         furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtDisconnect); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void vcp_on_cdc_rx(void* context) { | static void vcp_on_cdc_rx(void* context) { | ||||||
|     UNUSED(context); |     UNUSED(context); | ||||||
|     uint32_t ret = osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtRx); |     uint32_t ret = furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtRx); | ||||||
|     furi_check((ret & osFlagsError) == 0); |     furi_check((ret & osFlagsError) == 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void vcp_on_cdc_tx_complete(void* context) { | static void vcp_on_cdc_tx_complete(void* context) { | ||||||
|     UNUSED(context); |     UNUSED(context); | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtTx); |     furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtTx); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool cli_vcp_is_connected(void) { | static bool cli_vcp_is_connected(void) { | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								applications/crypto/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								applications/crypto/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | App( | ||||||
|  |     appid="crypto_start", | ||||||
|  |     apptype=FlipperAppType.STARTUP, | ||||||
|  |     entry_point="crypto_on_system_start", | ||||||
|  |     order=10, | ||||||
|  | ) | ||||||
							
								
								
									
										115
									
								
								applications/debug_tools/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								applications/debug_tools/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,115 @@ | |||||||
|  | App( | ||||||
|  |     appid="debug_apps", | ||||||
|  |     name="Basic debug apps bundle", | ||||||
|  |     apptype=FlipperAppType.METAPACKAGE, | ||||||
|  |     provides=[ | ||||||
|  |         "blink_test", | ||||||
|  |         "vibro_test", | ||||||
|  |         "keypad_test", | ||||||
|  |         "usb_test", | ||||||
|  |         "usb_mouse", | ||||||
|  |         "uart_echo", | ||||||
|  |         "display_test", | ||||||
|  |         "text_box_test", | ||||||
|  |         "file_browser_test", | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="blink_test", | ||||||
|  |     name="Blink Test", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="blink_test_app", | ||||||
|  |     cdefines=["APP_BLINK"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=10, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="vibro_test", | ||||||
|  |     name="Vibro Test", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="vibro_test_app", | ||||||
|  |     cdefines=["APP_VIBRO_TEST"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=20, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="keypad_test", | ||||||
|  |     name="Keypad Test", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="keypad_test_app", | ||||||
|  |     cdefines=["APP_KEYPAD_TEST"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=30, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="usb_test", | ||||||
|  |     name="USB Test", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="usb_test_app", | ||||||
|  |     cdefines=["APP_USB_TEST"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=50, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="usb_mouse", | ||||||
|  |     name="USB Mouse Demo", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="usb_mouse_app", | ||||||
|  |     cdefines=["APP_USB_MOUSE"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=60, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="uart_echo", | ||||||
|  |     name="UART Echo", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="uart_echo_app", | ||||||
|  |     cdefines=["APP_UART_ECHO"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=2 * 1024, | ||||||
|  |     order=70, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="display_test", | ||||||
|  |     name="Display Test", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="display_test_app", | ||||||
|  |     cdefines=["APP_DISPLAY_TEST"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=120, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="text_box_test", | ||||||
|  |     name="Text Box Test", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="text_box_test_app", | ||||||
|  |     cdefines=["APP_TEXT_BOX_TEST"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=140, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="file_browser_test", | ||||||
|  |     name="File Browser Test", | ||||||
|  |     apptype=FlipperAppType.DEBUG, | ||||||
|  |     entry_point="file_browser_app", | ||||||
|  |     cdefines=["APP_FILE_BROWSER_TEST"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=2 * 1024, | ||||||
|  |     order=150, | ||||||
|  | ) | ||||||
| @ -97,7 +97,7 @@ static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { | |||||||
| 
 | 
 | ||||||
|     if(ev == UartIrqEventRXNE) { |     if(ev == UartIrqEventRXNE) { | ||||||
|         xStreamBufferSendFromISR(app->rx_stream, &data, 1, &xHigherPriorityTaskWoken); |         xStreamBufferSendFromISR(app->rx_stream, &data, 1, &xHigherPriorityTaskWoken); | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(app->worker_thread), WorkerEventRx); |         furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventRx); | ||||||
|         portYIELD_FROM_ISR(xHigherPriorityTaskWoken); |         portYIELD_FROM_ISR(xHigherPriorityTaskWoken); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -149,7 +149,8 @@ static int32_t uart_echo_worker(void* context) { | |||||||
|     UartEchoApp* app = context; |     UartEchoApp* app = context; | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         uint32_t events = osThreadFlagsWait(WORKER_EVENTS_MASK, osFlagsWaitAny, osWaitForever); |         uint32_t events = | ||||||
|  |             furi_thread_flags_wait(WORKER_EVENTS_MASK, osFlagsWaitAny, osWaitForever); | ||||||
|         furi_check((events & osFlagsError) == 0); |         furi_check((events & osFlagsError) == 0); | ||||||
| 
 | 
 | ||||||
|         if(events & WorkerEventStop) break; |         if(events & WorkerEventStop) break; | ||||||
| @ -234,7 +235,7 @@ static UartEchoApp* uart_echo_app_alloc() { | |||||||
| static void uart_echo_app_free(UartEchoApp* app) { | static void uart_echo_app_free(UartEchoApp* app) { | ||||||
|     furi_assert(app); |     furi_assert(app); | ||||||
| 
 | 
 | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(app->worker_thread), WorkerEventStop); |     furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop); | ||||||
|     furi_thread_join(app->worker_thread); |     furi_thread_join(app->worker_thread); | ||||||
|     furi_thread_free(app->worker_thread); |     furi_thread_free(app->worker_thread); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								applications/desktop/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								applications/desktop/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | App( | ||||||
|  |     appid="desktop", | ||||||
|  |     name="DesktopSrv", | ||||||
|  |     apptype=FlipperAppType.SERVICE, | ||||||
|  |     entry_point="desktop_srv", | ||||||
|  |     cdefines=["SRV_DESKTOP"], | ||||||
|  |     requires=[ | ||||||
|  |         "gui", | ||||||
|  |         "dolphin", | ||||||
|  |         "storage", | ||||||
|  |         "input", | ||||||
|  |     ], | ||||||
|  |     provides=["desktop_settings"], | ||||||
|  |     conflicts=["updater"], | ||||||
|  |     stack_size=2 * 1024, | ||||||
|  |     order=60, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="desktop_settings", | ||||||
|  |     name="Desktop", | ||||||
|  |     apptype=FlipperAppType.SETTINGS, | ||||||
|  |     entry_point="desktop_settings_app", | ||||||
|  |     requires=[ | ||||||
|  |         "desktop", | ||||||
|  |         "gui", | ||||||
|  |     ], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=50, | ||||||
|  | ) | ||||||
| @ -158,11 +158,11 @@ Desktop* desktop_alloc() { | |||||||
| 
 | 
 | ||||||
|     desktop->lock_menu = desktop_lock_menu_alloc(); |     desktop->lock_menu = desktop_lock_menu_alloc(); | ||||||
|     desktop->debug_view = desktop_debug_alloc(); |     desktop->debug_view = desktop_debug_alloc(); | ||||||
|     desktop->first_start_view = desktop_first_start_alloc(); |  | ||||||
|     desktop->hw_mismatch_popup = popup_alloc(); |     desktop->hw_mismatch_popup = popup_alloc(); | ||||||
|     desktop->locked_view = desktop_view_locked_alloc(); |     desktop->locked_view = desktop_view_locked_alloc(); | ||||||
|     desktop->pin_input_view = desktop_view_pin_input_alloc(); |     desktop->pin_input_view = desktop_view_pin_input_alloc(); | ||||||
|     desktop->pin_timeout_view = desktop_view_pin_timeout_alloc(); |     desktop->pin_timeout_view = desktop_view_pin_timeout_alloc(); | ||||||
|  |     desktop->slideshow_view = desktop_view_slideshow_alloc(); | ||||||
| 
 | 
 | ||||||
|     desktop->main_view_stack = view_stack_alloc(); |     desktop->main_view_stack = view_stack_alloc(); | ||||||
|     desktop->main_view = desktop_main_alloc(); |     desktop->main_view = desktop_main_alloc(); | ||||||
| @ -193,10 +193,6 @@ Desktop* desktop_alloc() { | |||||||
|         desktop_lock_menu_get_view(desktop->lock_menu)); |         desktop_lock_menu_get_view(desktop->lock_menu)); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         desktop->view_dispatcher, DesktopViewIdDebug, desktop_debug_get_view(desktop->debug_view)); |         desktop->view_dispatcher, DesktopViewIdDebug, desktop_debug_get_view(desktop->debug_view)); | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         desktop->view_dispatcher, |  | ||||||
|         DesktopViewIdFirstStart, |  | ||||||
|         desktop_first_start_get_view(desktop->first_start_view)); |  | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         desktop->view_dispatcher, |         desktop->view_dispatcher, | ||||||
|         DesktopViewIdHwMismatch, |         DesktopViewIdHwMismatch, | ||||||
| @ -209,6 +205,10 @@ Desktop* desktop_alloc() { | |||||||
|         desktop->view_dispatcher, |         desktop->view_dispatcher, | ||||||
|         DesktopViewIdPinInput, |         DesktopViewIdPinInput, | ||||||
|         desktop_view_pin_input_get_view(desktop->pin_input_view)); |         desktop_view_pin_input_get_view(desktop->pin_input_view)); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         desktop->view_dispatcher, | ||||||
|  |         DesktopViewIdSlideshow, | ||||||
|  |         desktop_view_slideshow_get_view(desktop->slideshow_view)); | ||||||
| 
 | 
 | ||||||
|     // Lock icon
 |     // Lock icon
 | ||||||
|     desktop->lock_viewport = view_port_alloc(); |     desktop->lock_viewport = view_port_alloc(); | ||||||
| @ -258,7 +258,6 @@ void desktop_free(Desktop* desktop) { | |||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLocked); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLocked); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdDebug); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdDebug); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdFirstStart); |  | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdHwMismatch); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdHwMismatch); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinInput); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinInput); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinTimeout); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinTimeout); | ||||||
| @ -274,7 +273,6 @@ void desktop_free(Desktop* desktop) { | |||||||
|     desktop_lock_menu_free(desktop->lock_menu); |     desktop_lock_menu_free(desktop->lock_menu); | ||||||
|     desktop_view_locked_free(desktop->locked_view); |     desktop_view_locked_free(desktop->locked_view); | ||||||
|     desktop_debug_free(desktop->debug_view); |     desktop_debug_free(desktop->debug_view); | ||||||
|     desktop_first_start_free(desktop->first_start_view); |  | ||||||
|     popup_free(desktop->hw_mismatch_popup); |     popup_free(desktop->hw_mismatch_popup); | ||||||
|     desktop_view_pin_timeout_free(desktop->pin_timeout_view); |     desktop_view_pin_timeout_free(desktop->pin_timeout_view); | ||||||
| 
 | 
 | ||||||
| @ -290,9 +288,9 @@ void desktop_free(Desktop* desktop) { | |||||||
|     free(desktop); |     free(desktop); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool desktop_is_first_start() { | static bool desktop_check_file_flag(const char* flag_path) { | ||||||
|     Storage* storage = furi_record_open("storage"); |     Storage* storage = furi_record_open("storage"); | ||||||
|     bool exists = storage_common_stat(storage, "/int/first_start", NULL) == FSE_OK; |     bool exists = storage_common_stat(storage, flag_path, NULL) == FSE_OK; | ||||||
|     furi_record_close("storage"); |     furi_record_close("storage"); | ||||||
| 
 | 
 | ||||||
|     return exists; |     return exists; | ||||||
| @ -320,8 +318,8 @@ int32_t desktop_srv(void* p) { | |||||||
|         desktop_lock(desktop); |         desktop_lock(desktop); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(desktop_is_first_start()) { |     if(desktop_check_file_flag("/int/slideshow")) { | ||||||
|         scene_manager_next_scene(desktop->scene_manager, DesktopSceneFirstStart); |         scene_manager_next_scene(desktop->scene_manager, DesktopSceneSlideshow); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(!furi_hal_version_do_i_belong_here()) { |     if(!furi_hal_version_do_i_belong_here()) { | ||||||
|  | |||||||
| @ -6,9 +6,9 @@ | |||||||
| #include "views/desktop_view_pin_input.h" | #include "views/desktop_view_pin_input.h" | ||||||
| #include "views/desktop_view_locked.h" | #include "views/desktop_view_locked.h" | ||||||
| #include "views/desktop_view_main.h" | #include "views/desktop_view_main.h" | ||||||
| #include "views/desktop_view_first_start.h" |  | ||||||
| #include "views/desktop_view_lock_menu.h" | #include "views/desktop_view_lock_menu.h" | ||||||
| #include "views/desktop_view_debug.h" | #include "views/desktop_view_debug.h" | ||||||
|  | #include "views/desktop_view_slideshow.h" | ||||||
| #include "desktop/desktop_settings/desktop_settings.h" | #include "desktop/desktop_settings/desktop_settings.h" | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| @ -28,10 +28,10 @@ typedef enum { | |||||||
|     DesktopViewIdLockMenu, |     DesktopViewIdLockMenu, | ||||||
|     DesktopViewIdLocked, |     DesktopViewIdLocked, | ||||||
|     DesktopViewIdDebug, |     DesktopViewIdDebug, | ||||||
|     DesktopViewIdFirstStart, |  | ||||||
|     DesktopViewIdHwMismatch, |     DesktopViewIdHwMismatch, | ||||||
|     DesktopViewIdPinInput, |     DesktopViewIdPinInput, | ||||||
|     DesktopViewIdPinTimeout, |     DesktopViewIdPinTimeout, | ||||||
|  |     DesktopViewIdSlideshow, | ||||||
|     DesktopViewIdTotal, |     DesktopViewIdTotal, | ||||||
| } DesktopViewId; | } DesktopViewId; | ||||||
| 
 | 
 | ||||||
| @ -43,13 +43,13 @@ struct Desktop { | |||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|     SceneManager* scene_manager; |     SceneManager* scene_manager; | ||||||
| 
 | 
 | ||||||
|     DesktopFirstStartView* first_start_view; |  | ||||||
|     Popup* hw_mismatch_popup; |     Popup* hw_mismatch_popup; | ||||||
|     DesktopLockMenuView* lock_menu; |     DesktopLockMenuView* lock_menu; | ||||||
|     DesktopDebugView* debug_view; |     DesktopDebugView* debug_view; | ||||||
|     DesktopViewLocked* locked_view; |     DesktopViewLocked* locked_view; | ||||||
|     DesktopMainView* main_view; |     DesktopMainView* main_view; | ||||||
|     DesktopViewPinTimeout* pin_timeout_view; |     DesktopViewPinTimeout* pin_timeout_view; | ||||||
|  |     DesktopSlideshowView* slideshow_view; | ||||||
| 
 | 
 | ||||||
|     ViewStack* main_view_stack; |     ViewStack* main_view_stack; | ||||||
|     ViewStack* locked_view_stack; |     ViewStack* locked_view_stack; | ||||||
|  | |||||||
							
								
								
									
										115
									
								
								applications/desktop/helpers/slideshow.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								applications/desktop/helpers/slideshow.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,115 @@ | |||||||
|  | #include "slideshow.h" | ||||||
|  | 
 | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <gui/icon.h> | ||||||
|  | #include <gui/icon_i.h> | ||||||
|  | #include <furi/dangerous_defines.h> | ||||||
|  | 
 | ||||||
|  | #define SLIDESHOW_MAGIC 0x72676468 | ||||||
|  | #define SLIDESHOW_MAX_SUPPORTED_VERSION 1 | ||||||
|  | 
 | ||||||
|  | struct Slideshow { | ||||||
|  |     Icon icon; | ||||||
|  |     uint32_t current_frame; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #pragma pack(push, 1) | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint32_t magic; | ||||||
|  |     uint8_t version; | ||||||
|  |     uint8_t width; | ||||||
|  |     uint8_t height; | ||||||
|  |     uint8_t frame_count; | ||||||
|  | } SlideshowFileHeader; | ||||||
|  | _Static_assert(sizeof(SlideshowFileHeader) == 8, "Incorrect SlideshowFileHeader size"); | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint16_t size; | ||||||
|  | } SlideshowFrameHeader; | ||||||
|  | _Static_assert(sizeof(SlideshowFrameHeader) == 2, "Incorrect SlideshowFrameHeader size"); | ||||||
|  | 
 | ||||||
|  | #pragma pack(pop) | ||||||
|  | 
 | ||||||
|  | Slideshow* slideshow_alloc() { | ||||||
|  |     Slideshow* ret = malloc(sizeof(Slideshow)); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void slideshow_free(Slideshow* slideshow) { | ||||||
|  |     Icon* icon = &slideshow->icon; | ||||||
|  |     if(icon) { | ||||||
|  |         for(int frame_idx = 0; frame_idx < icon->frame_count; ++frame_idx) { | ||||||
|  |             uint8_t* frame_data = (uint8_t*)icon->frames[frame_idx]; | ||||||
|  |             free(frame_data); | ||||||
|  |         } | ||||||
|  |         free((uint8_t**)icon->frames); | ||||||
|  |     } | ||||||
|  |     free(slideshow); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool slideshow_load(Slideshow* slideshow, const char* fspath) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     File* slideshow_file = storage_file_alloc(storage); | ||||||
|  |     bool load_success = false; | ||||||
|  |     do { | ||||||
|  |         if(!storage_file_open(slideshow_file, fspath, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         SlideshowFileHeader header; | ||||||
|  |         if((storage_file_read(slideshow_file, &header, sizeof(header)) != sizeof(header)) || | ||||||
|  |            (header.magic != SLIDESHOW_MAGIC) || | ||||||
|  |            (header.version > SLIDESHOW_MAX_SUPPORTED_VERSION)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         Icon* icon = &slideshow->icon; | ||||||
|  |         FURI_CONST_ASSIGN(icon->frame_count, header.frame_count); | ||||||
|  |         FURI_CONST_ASSIGN(icon->width, header.width); | ||||||
|  |         FURI_CONST_ASSIGN(icon->height, header.height); | ||||||
|  |         icon->frames = malloc(header.frame_count * sizeof(uint8_t*)); | ||||||
|  |         for(int frame_idx = 0; frame_idx < header.frame_count; ++frame_idx) { | ||||||
|  |             SlideshowFrameHeader frame_header; | ||||||
|  |             if(storage_file_read(slideshow_file, &frame_header, sizeof(frame_header)) != | ||||||
|  |                sizeof(frame_header)) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             FURI_CONST_ASSIGN_PTR(icon->frames[frame_idx], malloc(frame_header.size)); | ||||||
|  |             uint8_t* frame_data = (uint8_t*)icon->frames[frame_idx]; | ||||||
|  |             if(storage_file_read(slideshow_file, frame_data, frame_header.size) != | ||||||
|  |                frame_header.size) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             load_success = (frame_idx + 1) == header.frame_count; | ||||||
|  |         } | ||||||
|  |     } while(false); | ||||||
|  |     storage_file_free(slideshow_file); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  |     return load_success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool slideshow_advance(Slideshow* slideshow) { | ||||||
|  |     uint8_t next_frame = slideshow->current_frame + 1; | ||||||
|  |     if(next_frame < slideshow->icon.frame_count) { | ||||||
|  |         slideshow->current_frame = next_frame; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void slideshow_goback(Slideshow* slideshow) { | ||||||
|  |     if(slideshow->current_frame > 0) { | ||||||
|  |         slideshow->current_frame--; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y) { | ||||||
|  |     furi_assert(slideshow->current_frame < slideshow->icon.frame_count); | ||||||
|  |     canvas_draw_bitmap( | ||||||
|  |         canvas, | ||||||
|  |         x, | ||||||
|  |         y, | ||||||
|  |         slideshow->icon.width, | ||||||
|  |         slideshow->icon.height, | ||||||
|  |         slideshow->icon.frames[slideshow->current_frame]); | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								applications/desktop/helpers/slideshow.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/desktop/helpers/slideshow.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/canvas.h> | ||||||
|  | 
 | ||||||
|  | typedef struct Slideshow Slideshow; | ||||||
|  | 
 | ||||||
|  | Slideshow* slideshow_alloc(); | ||||||
|  | 
 | ||||||
|  | void slideshow_free(Slideshow* slideshow); | ||||||
|  | bool slideshow_load(Slideshow* slideshow, const char* fspath); | ||||||
|  | void slideshow_goback(Slideshow* slideshow); | ||||||
|  | bool slideshow_advance(Slideshow* slideshow); | ||||||
|  | void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y); | ||||||
| @ -1,9 +1,9 @@ | |||||||
| ADD_SCENE(desktop, main, Main) | ADD_SCENE(desktop, main, Main) | ||||||
| ADD_SCENE(desktop, lock_menu, LockMenu) | ADD_SCENE(desktop, lock_menu, LockMenu) | ||||||
| ADD_SCENE(desktop, debug, Debug) | ADD_SCENE(desktop, debug, Debug) | ||||||
| ADD_SCENE(desktop, first_start, FirstStart) |  | ||||||
| ADD_SCENE(desktop, hw_mismatch, HwMismatch) | ADD_SCENE(desktop, hw_mismatch, HwMismatch) | ||||||
| ADD_SCENE(desktop, fault, Fault) | ADD_SCENE(desktop, fault, Fault) | ||||||
| ADD_SCENE(desktop, locked, Locked) | ADD_SCENE(desktop, locked, Locked) | ||||||
| ADD_SCENE(desktop, pin_input, PinInput) | ADD_SCENE(desktop, pin_input, PinInput) | ||||||
| ADD_SCENE(desktop, pin_timeout, PinTimeout) | ADD_SCENE(desktop, pin_timeout, PinTimeout) | ||||||
|  | ADD_SCENE(desktop, slideshow, Slideshow) | ||||||
|  | |||||||
| @ -1,54 +0,0 @@ | |||||||
| #include <power/power_service/power.h> |  | ||||||
| #include <storage/storage.h> |  | ||||||
| 
 |  | ||||||
| #include "../desktop_i.h" |  | ||||||
| #include "../views/desktop_view_first_start.h" |  | ||||||
| #include "../views/desktop_events.h" |  | ||||||
| 
 |  | ||||||
| void desktop_scene_first_start_callback(DesktopEvent event, void* context) { |  | ||||||
|     Desktop* desktop = (Desktop*)context; |  | ||||||
|     view_dispatcher_send_custom_event(desktop->view_dispatcher, event); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_scene_first_start_on_enter(void* context) { |  | ||||||
|     Desktop* desktop = (Desktop*)context; |  | ||||||
|     DesktopFirstStartView* first_start_view = desktop->first_start_view; |  | ||||||
| 
 |  | ||||||
|     desktop_first_start_set_callback( |  | ||||||
|         first_start_view, desktop_scene_first_start_callback, desktop); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdFirstStart); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool desktop_scene_first_start_on_event(void* context, SceneManagerEvent event) { |  | ||||||
|     Desktop* desktop = (Desktop*)context; |  | ||||||
|     bool consumed = false; |  | ||||||
|     Storage* storage = NULL; |  | ||||||
|     Power* power = NULL; |  | ||||||
| 
 |  | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |  | ||||||
|         switch(event.event) { |  | ||||||
|         case DesktopFirstStartCompleted: |  | ||||||
|             storage = furi_record_open("storage"); |  | ||||||
|             storage_common_remove(storage, "/int/first_start"); |  | ||||||
|             furi_record_close("storage"); |  | ||||||
|             scene_manager_previous_scene(desktop->scene_manager); |  | ||||||
|             consumed = true; |  | ||||||
|             break; |  | ||||||
|         case DesktopFirstStartPoweroff: |  | ||||||
|             power = furi_record_open("power"); |  | ||||||
|             power_off(power); |  | ||||||
|             furi_record_close("power"); |  | ||||||
|             consumed = true; |  | ||||||
|             break; |  | ||||||
| 
 |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_scene_first_start_on_exit(void* context) { |  | ||||||
|     UNUSED(context); |  | ||||||
| } |  | ||||||
							
								
								
									
										45
									
								
								applications/desktop/scenes/desktop_scene_slideshow.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								applications/desktop/scenes/desktop_scene_slideshow.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | #include <storage/storage.h> | ||||||
|  | 
 | ||||||
|  | #include "../desktop_i.h" | ||||||
|  | #include "../views/desktop_view_slideshow.h" | ||||||
|  | #include "../views/desktop_events.h" | ||||||
|  | 
 | ||||||
|  | void desktop_scene_slideshow_callback(DesktopEvent event, void* context) { | ||||||
|  |     Desktop* desktop = (Desktop*)context; | ||||||
|  |     view_dispatcher_send_custom_event(desktop->view_dispatcher, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_scene_slideshow_on_enter(void* context) { | ||||||
|  |     Desktop* desktop = (Desktop*)context; | ||||||
|  |     DesktopSlideshowView* slideshow_view = desktop->slideshow_view; | ||||||
|  | 
 | ||||||
|  |     desktop_view_slideshow_set_callback(slideshow_view, desktop_scene_slideshow_callback, desktop); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdSlideshow); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Desktop* desktop = (Desktop*)context; | ||||||
|  |     bool consumed = false; | ||||||
|  |     Storage* storage = NULL; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         switch(event.event) { | ||||||
|  |         case DesktopSlideshowCompleted: | ||||||
|  |             storage = furi_record_open("storage"); | ||||||
|  |             storage_common_remove(storage, "/int/slideshow"); | ||||||
|  |             furi_record_close("storage"); | ||||||
|  |             scene_manager_previous_scene(desktop->scene_manager); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_scene_slideshow_on_exit(void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  | } | ||||||
| @ -26,9 +26,6 @@ typedef enum { | |||||||
|     DesktopDebugEventSaveState, |     DesktopDebugEventSaveState, | ||||||
|     DesktopDebugEventExit, |     DesktopDebugEventExit, | ||||||
| 
 | 
 | ||||||
|     DesktopFirstStartCompleted, |  | ||||||
|     DesktopFirstStartPoweroff, |  | ||||||
| 
 |  | ||||||
|     DesktopLockMenuEventLock, |     DesktopLockMenuEventLock, | ||||||
|     DesktopLockMenuEventPinLock, |     DesktopLockMenuEventPinLock, | ||||||
|     DesktopLockMenuEventExit, |     DesktopLockMenuEventExit, | ||||||
| @ -37,6 +34,8 @@ typedef enum { | |||||||
|     DesktopAnimationEventNewIdleAnimation, |     DesktopAnimationEventNewIdleAnimation, | ||||||
|     DesktopAnimationEventInteractAnimation, |     DesktopAnimationEventInteractAnimation, | ||||||
| 
 | 
 | ||||||
|  |     DesktopSlideshowCompleted, | ||||||
|  | 
 | ||||||
|     // Global events
 |     // Global events
 | ||||||
|     DesktopGlobalBeforeAppStarted, |     DesktopGlobalBeforeAppStarted, | ||||||
|     DesktopGlobalAfterAppFinished, |     DesktopGlobalAfterAppFinished, | ||||||
|  | |||||||
| @ -46,7 +46,10 @@ void desktop_debug_render(Canvas* canvas, void* model) { | |||||||
|         canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer); |         canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer); | ||||||
| 
 | 
 | ||||||
|         ver = furi_hal_version_get_firmware_version(); |         ver = furi_hal_version_get_firmware_version(); | ||||||
| 
 |         const BleGlueC2Info* c2_ver = NULL; | ||||||
|  | #ifdef SRV_BT | ||||||
|  |         c2_ver = ble_glue_get_c2_info(); | ||||||
|  | #endif | ||||||
|         if(!ver) { |         if(!ver) { | ||||||
|             canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, "No info"); |             canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, "No info"); | ||||||
|             return; |             return; | ||||||
| @ -63,10 +66,11 @@ void desktop_debug_render(Canvas* canvas, void* model) { | |||||||
|         snprintf( |         snprintf( | ||||||
|             buffer, |             buffer, | ||||||
|             sizeof(buffer), |             sizeof(buffer), | ||||||
|             "%s%s [%s]", |             "%s%s [%s] %s", | ||||||
|             version_get_dirty_flag(ver) ? "[!] " : "", |             version_get_dirty_flag(ver) ? "[!] " : "", | ||||||
|             version_get_githash(ver), |             version_get_githash(ver), | ||||||
|             version_get_gitbranchnum(ver)); |             version_get_gitbranchnum(ver), | ||||||
|  |             c2_ver ? c2_ver->StackTypeString : "<none>"); | ||||||
|         canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer); |         canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer); | ||||||
| 
 | 
 | ||||||
|         snprintf( |         snprintf( | ||||||
|  | |||||||
| @ -1,166 +0,0 @@ | |||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| #include <gui/elements.h> |  | ||||||
| 
 |  | ||||||
| #include "../desktop_i.h" |  | ||||||
| #include "desktop_view_first_start.h" |  | ||||||
| 
 |  | ||||||
| #define DESKTOP_FIRST_START_POWEROFF_SHORT 5000 |  | ||||||
| #define DESKTOP_FIRST_START_POWEROFF_LONG (60 * 60 * 1000) |  | ||||||
| 
 |  | ||||||
| struct DesktopFirstStartView { |  | ||||||
|     View* view; |  | ||||||
|     DesktopFirstStartViewCallback callback; |  | ||||||
|     void* context; |  | ||||||
|     osTimerId_t timer; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     uint8_t page; |  | ||||||
| } DesktopFirstStartViewModel; |  | ||||||
| 
 |  | ||||||
| static void desktop_first_start_draw(Canvas* canvas, void* model) { |  | ||||||
|     DesktopFirstStartViewModel* m = model; |  | ||||||
| 
 |  | ||||||
|     canvas_clear(canvas); |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
|     uint8_t width = canvas_width(canvas); |  | ||||||
|     uint8_t height = canvas_height(canvas); |  | ||||||
|     const char* my_name = furi_hal_version_get_name_ptr(); |  | ||||||
|     if(m->page == 0) { |  | ||||||
|         canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart0_70x53); |  | ||||||
|         elements_multiline_text_framed( |  | ||||||
|             canvas, 75, 16 + STATUS_BAR_Y_SHIFT, "Hey m8,\npress > to\ncontinue"); |  | ||||||
|     } else if(m->page == 1) { |  | ||||||
|         canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart1_59x53); |  | ||||||
|         elements_multiline_text_framed( |  | ||||||
|             canvas, 64, 16 + STATUS_BAR_Y_SHIFT, "First Of All,\n...      >"); |  | ||||||
|     } else if(m->page == 2) { |  | ||||||
|         canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart2_59x51); |  | ||||||
|         elements_multiline_text_framed( |  | ||||||
|             canvas, 64, 16 + STATUS_BAR_Y_SHIFT, "Thank you\nfor your\nsupport! >"); |  | ||||||
|     } else if(m->page == 3) { |  | ||||||
|         canvas_draw_icon(canvas, width - 57, height - 45, &I_DolphinFirstStart3_57x48); |  | ||||||
|         elements_multiline_text_framed( |  | ||||||
|             canvas, 0, 16 + STATUS_BAR_Y_SHIFT, "Kickstarter\ncampaign\nwas INSANE! >"); |  | ||||||
|     } else if(m->page == 4) { |  | ||||||
|         canvas_draw_icon(canvas, width - 67, height - 51, &I_DolphinFirstStart4_67x53); |  | ||||||
|         elements_multiline_text_framed( |  | ||||||
|             canvas, 0, 13 + STATUS_BAR_Y_SHIFT, "Now\nallow me\nto introduce\nmyself >"); |  | ||||||
|     } else if(m->page == 5) { |  | ||||||
|         char buf[64]; |  | ||||||
|         snprintf( |  | ||||||
|             buf, |  | ||||||
|             64, |  | ||||||
|             "%s %s%s", |  | ||||||
|             "I am", |  | ||||||
|             my_name ? my_name : "Unknown", |  | ||||||
|             ",\ncyberdolphin\nliving in your\npocket >"); |  | ||||||
|         canvas_draw_icon(canvas, 0, height - 49, &I_DolphinFirstStart5_54x49); |  | ||||||
|         elements_multiline_text_framed(canvas, 60, 13 + STATUS_BAR_Y_SHIFT, buf); |  | ||||||
|     } else if(m->page == 6) { |  | ||||||
|         canvas_draw_icon(canvas, 0, height - 51, &I_DolphinFirstStart6_58x54); |  | ||||||
|         elements_multiline_text_framed( |  | ||||||
|             canvas, |  | ||||||
|             63, |  | ||||||
|             13 + STATUS_BAR_Y_SHIFT, |  | ||||||
|             "I can grow\nsmart'n'cool\nif you use me\noften >"); |  | ||||||
|     } else if(m->page == 7) { |  | ||||||
|         canvas_draw_icon(canvas, width - 61, height - 51, &I_DolphinFirstStart7_61x51); |  | ||||||
|         elements_multiline_text_framed( |  | ||||||
|             canvas, 0, 13 + STATUS_BAR_Y_SHIFT, "As long as\nyou read, write\nand emulate >"); |  | ||||||
|     } else if(m->page == 8) { |  | ||||||
|         canvas_draw_icon(canvas, width - 56, height - 51, &I_DolphinFirstStart8_56x51); |  | ||||||
|         elements_multiline_text_framed( |  | ||||||
|             canvas, |  | ||||||
|             0, |  | ||||||
|             13 + STATUS_BAR_Y_SHIFT, |  | ||||||
|             "You can check\nmy level and\nmood in the\nPassport menu"); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool desktop_first_start_input(InputEvent* event, void* context) { |  | ||||||
|     furi_assert(event); |  | ||||||
|     DesktopFirstStartView* instance = context; |  | ||||||
| 
 |  | ||||||
|     if(event->type == InputTypeShort) { |  | ||||||
|         DesktopFirstStartViewModel* model = view_get_model(instance->view); |  | ||||||
|         if(event->key == InputKeyLeft) { |  | ||||||
|             if(model->page > 0) model->page--; |  | ||||||
|         } else if(event->key == InputKeyRight) { |  | ||||||
|             uint32_t page = ++model->page; |  | ||||||
|             if(page > 8) { |  | ||||||
|                 instance->callback(DesktopFirstStartCompleted, instance->context); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         view_commit_model(instance->view, true); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(event->key == InputKeyOk) { |  | ||||||
|         if(event->type == InputTypePress) { |  | ||||||
|             osTimerStart(instance->timer, DESKTOP_FIRST_START_POWEROFF_SHORT); |  | ||||||
|         } else if(event->type == InputTypeRelease) { |  | ||||||
|             osTimerStop(instance->timer); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void desktop_first_start_timer_callback(void* context) { |  | ||||||
|     DesktopFirstStartView* instance = context; |  | ||||||
|     instance->callback(DesktopFirstStartPoweroff, instance->context); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void desktop_first_start_enter(void* context) { |  | ||||||
|     DesktopFirstStartView* instance = context; |  | ||||||
| 
 |  | ||||||
|     furi_assert(instance->timer == NULL); |  | ||||||
|     instance->timer = osTimerNew(desktop_first_start_timer_callback, osTimerOnce, instance, NULL); |  | ||||||
| 
 |  | ||||||
|     osTimerStart(instance->timer, DESKTOP_FIRST_START_POWEROFF_LONG); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void desktop_first_start_exit(void* context) { |  | ||||||
|     DesktopFirstStartView* instance = context; |  | ||||||
| 
 |  | ||||||
|     osTimerStop(instance->timer); |  | ||||||
|     osTimerDelete(instance->timer); |  | ||||||
|     instance->timer = NULL; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| DesktopFirstStartView* desktop_first_start_alloc() { |  | ||||||
|     DesktopFirstStartView* instance = malloc(sizeof(DesktopFirstStartView)); |  | ||||||
|     instance->view = view_alloc(); |  | ||||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DesktopFirstStartViewModel)); |  | ||||||
|     view_set_context(instance->view, instance); |  | ||||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)desktop_first_start_draw); |  | ||||||
|     view_set_input_callback(instance->view, desktop_first_start_input); |  | ||||||
|     view_set_enter_callback(instance->view, desktop_first_start_enter); |  | ||||||
|     view_set_exit_callback(instance->view, desktop_first_start_exit); |  | ||||||
| 
 |  | ||||||
|     return instance; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_first_start_free(DesktopFirstStartView* instance) { |  | ||||||
|     furi_assert(instance); |  | ||||||
| 
 |  | ||||||
|     view_free(instance->view); |  | ||||||
|     free(instance); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| View* desktop_first_start_get_view(DesktopFirstStartView* instance) { |  | ||||||
|     furi_assert(instance); |  | ||||||
|     return instance->view; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_first_start_set_callback( |  | ||||||
|     DesktopFirstStartView* instance, |  | ||||||
|     DesktopFirstStartViewCallback callback, |  | ||||||
|     void* context) { |  | ||||||
|     furi_assert(instance); |  | ||||||
|     furi_assert(callback); |  | ||||||
|     instance->callback = callback; |  | ||||||
|     instance->context = context; |  | ||||||
| } |  | ||||||
| @ -1,20 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <gui/view.h> |  | ||||||
| 
 |  | ||||||
| #include "desktop_events.h" |  | ||||||
| 
 |  | ||||||
| typedef struct DesktopFirstStartView DesktopFirstStartView; |  | ||||||
| 
 |  | ||||||
| typedef void (*DesktopFirstStartViewCallback)(DesktopEvent event, void* context); |  | ||||||
| 
 |  | ||||||
| DesktopFirstStartView* desktop_first_start_alloc(); |  | ||||||
| 
 |  | ||||||
| void desktop_first_start_free(DesktopFirstStartView* main_view); |  | ||||||
| 
 |  | ||||||
| View* desktop_first_start_get_view(DesktopFirstStartView* main_view); |  | ||||||
| 
 |  | ||||||
| void desktop_first_start_set_callback( |  | ||||||
|     DesktopFirstStartView* main_view, |  | ||||||
|     DesktopFirstStartViewCallback callback, |  | ||||||
|     void* context); |  | ||||||
							
								
								
									
										108
									
								
								applications/desktop/views/desktop_view_slideshow.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								applications/desktop/views/desktop_view_slideshow.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | #include "../desktop_i.h" | ||||||
|  | #include "desktop_view_slideshow.h" | ||||||
|  | #include "../helpers/slideshow.h" | ||||||
|  | 
 | ||||||
|  | struct DesktopSlideshowView { | ||||||
|  |     View* view; | ||||||
|  |     DesktopSlideshowViewCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t page; | ||||||
|  |     Slideshow* slideshow; | ||||||
|  | } DesktopSlideshowViewModel; | ||||||
|  | 
 | ||||||
|  | static void desktop_view_slideshow_draw(Canvas* canvas, void* model) { | ||||||
|  |     DesktopSlideshowViewModel* m = model; | ||||||
|  | 
 | ||||||
|  |     canvas_clear(canvas); | ||||||
|  |     slideshow_draw(m->slideshow, canvas, 0, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool desktop_view_slideshow_input(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(event); | ||||||
|  |     DesktopSlideshowView* instance = context; | ||||||
|  | 
 | ||||||
|  |     if(event->type == InputTypeShort) { | ||||||
|  |         DesktopSlideshowViewModel* model = view_get_model(instance->view); | ||||||
|  |         bool end_slideshow = false; | ||||||
|  |         switch(event->key) { | ||||||
|  |         case InputKeyLeft: | ||||||
|  |             slideshow_goback(model->slideshow); | ||||||
|  |             break; | ||||||
|  |         case InputKeyRight: | ||||||
|  |         case InputKeyOk: | ||||||
|  |             end_slideshow = !slideshow_advance(model->slideshow); | ||||||
|  |             break; | ||||||
|  |         case InputKeyBack: | ||||||
|  |             end_slideshow = true; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(end_slideshow) { | ||||||
|  |             instance->callback(DesktopSlideshowCompleted, instance->context); | ||||||
|  |         } | ||||||
|  |         view_commit_model(instance->view, true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void desktop_view_slideshow_enter(void* context) { | ||||||
|  |     DesktopSlideshowView* instance = context; | ||||||
|  | 
 | ||||||
|  |     DesktopSlideshowViewModel* model = view_get_model(instance->view); | ||||||
|  |     model->slideshow = slideshow_alloc(); | ||||||
|  |     if(!slideshow_load(model->slideshow, "/int/slideshow")) { | ||||||
|  |         instance->callback(DesktopSlideshowCompleted, instance->context); | ||||||
|  |     } | ||||||
|  |     view_commit_model(instance->view, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void desktop_view_slideshow_exit(void* context) { | ||||||
|  |     DesktopSlideshowView* instance = context; | ||||||
|  | 
 | ||||||
|  |     DesktopSlideshowViewModel* model = view_get_model(instance->view); | ||||||
|  |     slideshow_free(model->slideshow); | ||||||
|  |     view_commit_model(instance->view, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DesktopSlideshowView* desktop_view_slideshow_alloc() { | ||||||
|  |     DesktopSlideshowView* instance = malloc(sizeof(DesktopSlideshowView)); | ||||||
|  |     instance->view = view_alloc(); | ||||||
|  |     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DesktopSlideshowViewModel)); | ||||||
|  |     view_set_context(instance->view, instance); | ||||||
|  |     view_set_draw_callback(instance->view, (ViewDrawCallback)desktop_view_slideshow_draw); | ||||||
|  |     view_set_input_callback(instance->view, desktop_view_slideshow_input); | ||||||
|  |     view_set_enter_callback(instance->view, desktop_view_slideshow_enter); | ||||||
|  |     view_set_exit_callback(instance->view, desktop_view_slideshow_exit); | ||||||
|  | 
 | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_view_slideshow_free(DesktopSlideshowView* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  | 
 | ||||||
|  |     view_free(instance->view); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* desktop_view_slideshow_get_view(DesktopSlideshowView* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return instance->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_view_slideshow_set_callback( | ||||||
|  |     DesktopSlideshowView* instance, | ||||||
|  |     DesktopSlideshowViewCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     furi_assert(callback); | ||||||
|  |     instance->callback = callback; | ||||||
|  |     instance->context = context; | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								applications/desktop/views/desktop_view_slideshow.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								applications/desktop/views/desktop_view_slideshow.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | #include "desktop_events.h" | ||||||
|  | 
 | ||||||
|  | typedef struct DesktopSlideshowView DesktopSlideshowView; | ||||||
|  | 
 | ||||||
|  | typedef void (*DesktopSlideshowViewCallback)(DesktopEvent event, void* context); | ||||||
|  | 
 | ||||||
|  | DesktopSlideshowView* desktop_view_slideshow_alloc(); | ||||||
|  | 
 | ||||||
|  | void desktop_view_slideshow_free(DesktopSlideshowView* main_view); | ||||||
|  | 
 | ||||||
|  | View* desktop_view_slideshow_get_view(DesktopSlideshowView* main_view); | ||||||
|  | 
 | ||||||
|  | void desktop_view_slideshow_set_callback( | ||||||
|  |     DesktopSlideshowView* main_view, | ||||||
|  |     DesktopSlideshowViewCallback callback, | ||||||
|  |     void* context); | ||||||
							
								
								
									
										10
									
								
								applications/dialogs/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								applications/dialogs/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | App( | ||||||
|  |     appid="dialogs", | ||||||
|  |     name="DialogsSrv", | ||||||
|  |     apptype=FlipperAppType.SERVICE, | ||||||
|  |     entry_point="dialogs_srv", | ||||||
|  |     cdefines=["SRV_DIALOGS"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=40, | ||||||
|  | ) | ||||||
							
								
								
									
										23
									
								
								applications/dolphin/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								applications/dolphin/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | App( | ||||||
|  |     appid="dolphin", | ||||||
|  |     name="DolphinSrv", | ||||||
|  |     apptype=FlipperAppType.SERVICE, | ||||||
|  |     entry_point="dolphin_srv", | ||||||
|  |     cdefines=["SRV_DOLPHIN"], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=50, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="passport", | ||||||
|  |     name="Passport", | ||||||
|  |     apptype=FlipperAppType.SETTINGS, | ||||||
|  |     entry_point="passport_app", | ||||||
|  |     cdefines=["APP_PASSPORT"], | ||||||
|  |     requires=[ | ||||||
|  |         "gui", | ||||||
|  |         "dolphin", | ||||||
|  |     ], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     order=60, | ||||||
|  | ) | ||||||
							
								
								
									
										57
									
								
								applications/extapps.scons
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								applications/extapps.scons
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | |||||||
|  | Import("ENV") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | from fbt.appmanifest import FlipperAppType | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | appenv = ENV.Clone(tools=["fbt_extapps"]) | ||||||
|  | 
 | ||||||
|  | appenv.Replace( | ||||||
|  |     LINKER_SCRIPT="application-ext", | ||||||
|  |     STRIPFLAGS=[ | ||||||
|  |         "--strip-debug", | ||||||
|  |         "--strip-unneeded", | ||||||
|  |         "-d", | ||||||
|  |         "-g", | ||||||
|  |         "-S", | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | appenv.AppendUnique( | ||||||
|  |     CCFLAGS=[ | ||||||
|  |         "-Os", | ||||||
|  |         "-ggdb3", | ||||||
|  |         "-mword-relocations", | ||||||
|  |         "-mlong-calls", | ||||||
|  |         "-fno-common", | ||||||
|  |         "-nostdlib", | ||||||
|  |         "-fvisibility=hidden", | ||||||
|  |     ], | ||||||
|  |     LINKFLAGS=[ | ||||||
|  |         "-r", | ||||||
|  |         "-s", | ||||||
|  |         # "-Bsymbolic", | ||||||
|  |         "-nostartfiles", | ||||||
|  |         "-mlong-calls", | ||||||
|  |         "-fno-common", | ||||||
|  |         "-nostdlib", | ||||||
|  |         "-Wl,--gc-sections", | ||||||
|  |         "-Wl,--no-export-dynamic", | ||||||
|  |         "-fvisibility=hidden", | ||||||
|  |         "-Wl,-e${APP_ENTRY}", | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | extapps = [] | ||||||
|  | for apptype in (FlipperAppType.PLUGIN, FlipperAppType.EXTERNAL): | ||||||
|  |     for app in appenv["APPBUILD"].get_apps_of_type(apptype): | ||||||
|  |         extapps.append(appenv.BuildAppElf(app)) | ||||||
|  | 
 | ||||||
|  | # Ugly access to global option | ||||||
|  | if extra_app_list := GetOption("extra_ext_apps"): | ||||||
|  |     for extra_app in extra_app_list.split(","): | ||||||
|  |         extapps.append(appenv.BuildAppElf(appenv["APPMGR"].get(extra_app))) | ||||||
|  | 
 | ||||||
|  | Alias(appenv["FIRMWARE_BUILD_CFG"] + "_extapps", extapps) | ||||||
|  | Return("extapps") | ||||||
							
								
								
									
										11
									
								
								applications/gpio/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								applications/gpio/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | App( | ||||||
|  |     appid="gpio", | ||||||
|  |     name="GPIO", | ||||||
|  |     apptype=FlipperAppType.APP, | ||||||
|  |     entry_point="gpio_app", | ||||||
|  |     cdefines=["APP_GPIO"], | ||||||
|  |     requires=["gui"], | ||||||
|  |     stack_size=1 * 1024, | ||||||
|  |     icon="A_GPIO_14", | ||||||
|  |     order=50, | ||||||
|  | ) | ||||||
| @ -78,7 +78,7 @@ static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { | |||||||
| 
 | 
 | ||||||
|     if(ev == UartIrqEventRXNE) { |     if(ev == UartIrqEventRXNE) { | ||||||
|         xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken); |         xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken); | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtRxDone); |         furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtRxDone); | ||||||
|         portYIELD_FROM_ISR(xHigherPriorityTaskWoken); |         portYIELD_FROM_ISR(xHigherPriorityTaskWoken); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -181,12 +181,13 @@ static int32_t usb_uart_worker(void* context) { | |||||||
|         usb_uart_update_ctrl_lines(usb_uart); |         usb_uart_update_ctrl_lines(usb_uart); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtCdcRx); |     furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtCdcRx); | ||||||
| 
 | 
 | ||||||
|     furi_thread_start(usb_uart->tx_thread); |     furi_thread_start(usb_uart->tx_thread); | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         uint32_t events = osThreadFlagsWait(WORKER_ALL_RX_EVENTS, osFlagsWaitAny, osWaitForever); |         uint32_t events = | ||||||
|  |             furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, osFlagsWaitAny, osWaitForever); | ||||||
|         furi_check((events & osFlagsError) == 0); |         furi_check((events & osFlagsError) == 0); | ||||||
|         if(events & WorkerEvtStop) break; |         if(events & WorkerEvtStop) break; | ||||||
|         if(events & WorkerEvtRxDone) { |         if(events & WorkerEvtRxDone) { | ||||||
| @ -205,7 +206,7 @@ static int32_t usb_uart_worker(void* context) { | |||||||
|         } |         } | ||||||
|         if(events & WorkerEvtCfgChange) { |         if(events & WorkerEvtCfgChange) { | ||||||
|             if(usb_uart->cfg.vcp_ch != usb_uart->cfg_new.vcp_ch) { |             if(usb_uart->cfg.vcp_ch != usb_uart->cfg_new.vcp_ch) { | ||||||
|                 osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtTxStop); |                 furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop); | ||||||
|                 furi_thread_join(usb_uart->tx_thread); |                 furi_thread_join(usb_uart->tx_thread); | ||||||
| 
 | 
 | ||||||
|                 usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); |                 usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); | ||||||
| @ -217,7 +218,7 @@ static int32_t usb_uart_worker(void* context) { | |||||||
|                 events |= WorkerEvtLineCfgSet; |                 events |= WorkerEvtLineCfgSet; | ||||||
|             } |             } | ||||||
|             if(usb_uart->cfg.uart_ch != usb_uart->cfg_new.uart_ch) { |             if(usb_uart->cfg.uart_ch != usb_uart->cfg_new.uart_ch) { | ||||||
|                 osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtTxStop); |                 furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop); | ||||||
|                 furi_thread_join(usb_uart->tx_thread); |                 furi_thread_join(usb_uart->tx_thread); | ||||||
| 
 | 
 | ||||||
|                 usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); |                 usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); | ||||||
| @ -266,7 +267,7 @@ static int32_t usb_uart_worker(void* context) { | |||||||
|         furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); |         furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtTxStop); |     furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop); | ||||||
|     furi_thread_join(usb_uart->tx_thread); |     furi_thread_join(usb_uart->tx_thread); | ||||||
|     furi_thread_free(usb_uart->tx_thread); |     furi_thread_free(usb_uart->tx_thread); | ||||||
| 
 | 
 | ||||||
| @ -288,7 +289,8 @@ static int32_t usb_uart_tx_thread(void* context) { | |||||||
| 
 | 
 | ||||||
|     uint8_t data[USB_CDC_PKT_LEN]; |     uint8_t data[USB_CDC_PKT_LEN]; | ||||||
|     while(1) { |     while(1) { | ||||||
|         uint32_t events = osThreadFlagsWait(WORKER_ALL_TX_EVENTS, osFlagsWaitAny, osWaitForever); |         uint32_t events = | ||||||
|  |             furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, osFlagsWaitAny, osWaitForever); | ||||||
|         furi_check((events & osFlagsError) == 0); |         furi_check((events & osFlagsError) == 0); | ||||||
|         if(events & WorkerEvtTxStop) break; |         if(events & WorkerEvtTxStop) break; | ||||||
|         if(events & WorkerEvtCdcRx) { |         if(events & WorkerEvtCdcRx) { | ||||||
| @ -314,7 +316,7 @@ static void vcp_on_cdc_tx_complete(void* context) { | |||||||
| 
 | 
 | ||||||
| static void vcp_on_cdc_rx(void* context) { | static void vcp_on_cdc_rx(void* context) { | ||||||
|     UsbUartBridge* usb_uart = (UsbUartBridge*)context; |     UsbUartBridge* usb_uart = (UsbUartBridge*)context; | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtCdcRx); |     furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtCdcRx); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void vcp_state_callback(void* context, uint8_t state) { | static void vcp_state_callback(void* context, uint8_t state) { | ||||||
| @ -325,13 +327,13 @@ static void vcp_state_callback(void* context, uint8_t state) { | |||||||
| static void vcp_on_cdc_control_line(void* context, uint8_t state) { | static void vcp_on_cdc_control_line(void* context, uint8_t state) { | ||||||
|     UNUSED(state); |     UNUSED(state); | ||||||
|     UsbUartBridge* usb_uart = (UsbUartBridge*)context; |     UsbUartBridge* usb_uart = (UsbUartBridge*)context; | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtCtrlLineSet); |     furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCtrlLineSet); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) { | static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) { | ||||||
|     UNUSED(config); |     UNUSED(config); | ||||||
|     UsbUartBridge* usb_uart = (UsbUartBridge*)context; |     UsbUartBridge* usb_uart = (UsbUartBridge*)context; | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtLineCfgSet); |     furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtLineCfgSet); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) { | UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) { | ||||||
| @ -351,7 +353,7 @@ UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) { | |||||||
| 
 | 
 | ||||||
| void usb_uart_disable(UsbUartBridge* usb_uart) { | void usb_uart_disable(UsbUartBridge* usb_uart) { | ||||||
|     furi_assert(usb_uart); |     furi_assert(usb_uart); | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtStop); |     furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtStop); | ||||||
|     furi_thread_join(usb_uart->thread); |     furi_thread_join(usb_uart->thread); | ||||||
|     furi_thread_free(usb_uart->thread); |     furi_thread_free(usb_uart->thread); | ||||||
|     free(usb_uart); |     free(usb_uart); | ||||||
| @ -361,7 +363,7 @@ void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { | |||||||
|     furi_assert(usb_uart); |     furi_assert(usb_uart); | ||||||
|     furi_assert(cfg); |     furi_assert(cfg); | ||||||
|     memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); |     memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtCfgChange); |     furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { | void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								applications/gui/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								applications/gui/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | App( | ||||||
|  |     appid="gui", | ||||||
|  |     name="GuiSrv", | ||||||
|  |     apptype=FlipperAppType.SERVICE, | ||||||
|  |     entry_point="gui_srv", | ||||||
|  |     cdefines=["SRV_GUI"], | ||||||
|  |     requires=[ | ||||||
|  |         "input", | ||||||
|  |         "notification", | ||||||
|  |     ], | ||||||
|  |     stack_size=2 * 1024, | ||||||
|  |     order=70, | ||||||
|  | ) | ||||||
| @ -19,7 +19,7 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { | |||||||
| 
 | 
 | ||||||
| void gui_update(Gui* gui) { | void gui_update(Gui* gui) { | ||||||
|     furi_assert(gui); |     furi_assert(gui); | ||||||
|     osThreadFlagsSet(gui->thread, GUI_THREAD_FLAG_DRAW); |     furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void gui_input_events_callback(const void* value, void* ctx) { | void gui_input_events_callback(const void* value, void* ctx) { | ||||||
| @ -29,7 +29,7 @@ void gui_input_events_callback(const void* value, void* ctx) { | |||||||
|     Gui* gui = ctx; |     Gui* gui = ctx; | ||||||
| 
 | 
 | ||||||
|     osMessageQueuePut(gui->input_queue, value, 0, osWaitForever); |     osMessageQueuePut(gui->input_queue, value, 0, osWaitForever); | ||||||
|     osThreadFlagsSet(gui->thread, GUI_THREAD_FLAG_INPUT); |     furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_INPUT); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Only Fullscreen supports vertical display for now
 | // Only Fullscreen supports vertical display for now
 | ||||||
| @ -471,7 +471,7 @@ void gui_set_lockdown(Gui* gui, bool lockdown) { | |||||||
| Gui* gui_alloc() { | Gui* gui_alloc() { | ||||||
|     Gui* gui = malloc(sizeof(Gui)); |     Gui* gui = malloc(sizeof(Gui)); | ||||||
|     // Thread ID
 |     // Thread ID
 | ||||||
|     gui->thread = osThreadGetId(); |     gui->thread_id = furi_thread_get_current_id(); | ||||||
|     // Allocate mutex
 |     // Allocate mutex
 | ||||||
|     gui->mutex = osMutexNew(NULL); |     gui->mutex = osMutexNew(NULL); | ||||||
|     furi_check(gui->mutex); |     furi_check(gui->mutex); | ||||||
| @ -500,7 +500,8 @@ int32_t gui_srv(void* p) { | |||||||
|     furi_record_create("gui", gui); |     furi_record_create("gui", gui); | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         uint32_t flags = osThreadFlagsWait(GUI_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); |         uint32_t flags = | ||||||
|  |             furi_thread_flags_wait(GUI_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); | ||||||
|         // Process and dispatch input
 |         // Process and dispatch input
 | ||||||
|         if(flags & GUI_THREAD_FLAG_INPUT) { |         if(flags & GUI_THREAD_FLAG_INPUT) { | ||||||
|             // Process till queue become empty
 |             // Process till queue become empty
 | ||||||
| @ -512,7 +513,7 @@ int32_t gui_srv(void* p) { | |||||||
|         // Process and dispatch draw call
 |         // Process and dispatch draw call
 | ||||||
|         if(flags & GUI_THREAD_FLAG_DRAW) { |         if(flags & GUI_THREAD_FLAG_DRAW) { | ||||||
|             // Clear flags that arrived on input step
 |             // Clear flags that arrived on input step
 | ||||||
|             osThreadFlagsClear(GUI_THREAD_FLAG_DRAW); |             furi_thread_flags_clear(GUI_THREAD_FLAG_DRAW); | ||||||
|             gui_redraw(gui); |             gui_redraw(gui); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -57,7 +57,7 @@ ALGO_DEF(CanvasCallbackPairArray, CanvasCallbackPairArray_t); | |||||||
| /** Gui structure */ | /** Gui structure */ | ||||||
| struct Gui { | struct Gui { | ||||||
|     // Thread and lock
 |     // Thread and lock
 | ||||||
|     osThreadId_t thread; |     FuriThreadId thread_id; | ||||||
|     osMutexId_t mutex; |     osMutexId_t mutex; | ||||||
| 
 | 
 | ||||||
|     // Layers and Canvas
 |     // Layers and Canvas
 | ||||||
|  | |||||||
| @ -259,10 +259,10 @@ static int32_t browser_worker(void* context) { | |||||||
|     string_t filename; |     string_t filename; | ||||||
|     string_init(filename); |     string_init(filename); | ||||||
| 
 | 
 | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtConfigChange); |     furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         uint32_t flags = osThreadFlagsWait(WORKER_FLAGS_ALL, osFlagsWaitAny, osWaitForever); |         uint32_t flags = furi_thread_flags_wait(WORKER_FLAGS_ALL, osFlagsWaitAny, osWaitForever); | ||||||
|         furi_assert((flags & osFlagsError) == 0); |         furi_assert((flags & osFlagsError) == 0); | ||||||
| 
 | 
 | ||||||
|         if(flags & WorkerEvtConfigChange) { |         if(flags & WorkerEvtConfigChange) { | ||||||
| @ -272,7 +272,7 @@ static int32_t browser_worker(void* context) { | |||||||
|             } |             } | ||||||
|             idx_last_array_reset(browser->idx_last); |             idx_last_array_reset(browser->idx_last); | ||||||
| 
 | 
 | ||||||
|             osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderEnter); |             furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(flags & WorkerEvtFolderEnter) { |         if(flags & WorkerEvtFolderEnter) { | ||||||
| @ -369,7 +369,7 @@ BrowserWorker* file_browser_worker_alloc(string_t path, const char* filter_ext, | |||||||
| void file_browser_worker_free(BrowserWorker* browser) { | void file_browser_worker_free(BrowserWorker* browser) { | ||||||
|     furi_assert(browser); |     furi_assert(browser); | ||||||
| 
 | 
 | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtStop); |     furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtStop); | ||||||
|     furi_thread_join(browser->thread); |     furi_thread_join(browser->thread); | ||||||
|     furi_thread_free(browser->thread); |     furi_thread_free(browser->thread); | ||||||
| 
 | 
 | ||||||
| @ -423,30 +423,30 @@ void file_browser_worker_set_config( | |||||||
|     string_set(browser->path_next, path); |     string_set(browser->path_next, path); | ||||||
|     string_set_str(browser->filter_extension, filter_ext); |     string_set_str(browser->filter_extension, filter_ext); | ||||||
|     browser->skip_assets = skip_assets; |     browser->skip_assets = skip_assets; | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtConfigChange); |     furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void file_browser_worker_folder_enter(BrowserWorker* browser, string_t path, int32_t item_idx) { | void file_browser_worker_folder_enter(BrowserWorker* browser, string_t path, int32_t item_idx) { | ||||||
|     furi_assert(browser); |     furi_assert(browser); | ||||||
|     string_set(browser->path_next, path); |     string_set(browser->path_next, path); | ||||||
|     browser->item_sel_idx = item_idx; |     browser->item_sel_idx = item_idx; | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderEnter); |     furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void file_browser_worker_folder_exit(BrowserWorker* browser) { | void file_browser_worker_folder_exit(BrowserWorker* browser) { | ||||||
|     furi_assert(browser); |     furi_assert(browser); | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderExit); |     furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderExit); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx) { | void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx) { | ||||||
|     furi_assert(browser); |     furi_assert(browser); | ||||||
|     browser->item_sel_idx = item_idx; |     browser->item_sel_idx = item_idx; | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderRefresh); |     furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderRefresh); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count) { | void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count) { | ||||||
|     furi_assert(browser); |     furi_assert(browser); | ||||||
|     browser->load_offset = offset; |     browser->load_offset = offset; | ||||||
|     browser->load_count = count; |     browser->load_count = count; | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtLoad); |     furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtLoad); | ||||||
| } | } | ||||||
|  | |||||||
| @ -20,10 +20,10 @@ typedef struct { | |||||||
| static void loading_draw_callback(Canvas* canvas, void* _model) { | static void loading_draw_callback(Canvas* canvas, void* _model) { | ||||||
|     LoadingModel* model = (LoadingModel*)_model; |     LoadingModel* model = (LoadingModel*)_model; | ||||||
| 
 | 
 | ||||||
|     uint8_t x = 7; |  | ||||||
|     uint8_t y = 40; |  | ||||||
|     uint8_t width = 49; |     uint8_t width = 49; | ||||||
|     uint8_t height = 47; |     uint8_t height = 47; | ||||||
|  |     uint8_t x = (canvas_width(canvas) - width) / 2; | ||||||
|  |     uint8_t y = (canvas_height(canvas) - height) / 2; | ||||||
| 
 | 
 | ||||||
|     elements_bold_rounded_frame(canvas, x, y, width, height); |     elements_bold_rounded_frame(canvas, x, y, width, height); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -165,6 +165,25 @@ bool scene_manager_search_and_switch_to_previous_scene( | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool scene_manager_search_and_switch_to_previous_scene_one_of( | ||||||
|  |     SceneManager* scene_manager, | ||||||
|  |     const uint32_t* scene_ids, | ||||||
|  |     size_t scene_ids_size) { | ||||||
|  |     furi_assert(scene_manager); | ||||||
|  |     furi_assert(scene_ids); | ||||||
|  |     bool scene_found = false; | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < scene_ids_size; ++i) { | ||||||
|  |         const uint32_t scene_id = scene_ids[i]; | ||||||
|  |         if(scene_manager_has_previous_scene(scene_manager, scene_id)) { | ||||||
|  |             scene_manager_search_and_switch_to_previous_scene(scene_manager, scene_id); | ||||||
|  |             scene_found = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return scene_found; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id) { | bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id) { | ||||||
|     furi_assert(scene_manager); |     furi_assert(scene_manager); | ||||||
|     bool scene_found = false; |     bool scene_found = false; | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <stddef.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
| @ -146,6 +147,19 @@ bool scene_manager_search_and_switch_to_previous_scene( | |||||||
|     SceneManager* scene_manager, |     SceneManager* scene_manager, | ||||||
|     uint32_t scene_id); |     uint32_t scene_id); | ||||||
| 
 | 
 | ||||||
|  | /** Search and switch to previous Scene, multiple choice
 | ||||||
|  |  * | ||||||
|  |  * @param      scene_manager    SceneManager instance | ||||||
|  |  * @param      scene_ids        Array of scene IDs | ||||||
|  |  * @param      scene_ids_size   Array of scene IDs size | ||||||
|  |  * | ||||||
|  |  * @return     true if one of previous scenes was found, false otherwise | ||||||
|  |  */ | ||||||
|  | bool scene_manager_search_and_switch_to_previous_scene_one_of( | ||||||
|  |     SceneManager* scene_manager, | ||||||
|  |     const uint32_t* scene_ids, | ||||||
|  |     size_t scene_ids_size); | ||||||
|  | 
 | ||||||
| /** Clear Scene stack and switch to another Scene
 | /** Clear Scene stack and switch to another Scene
 | ||||||
|  * |  * | ||||||
|  * @param      scene_manager  SceneManager instance |  * @param      scene_manager  SceneManager instance | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								applications/ibutton/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								applications/ibutton/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | App( | ||||||
|  |     appid="ibutton", | ||||||
|  |     name="iButton", | ||||||
|  |     apptype=FlipperAppType.APP, | ||||||
|  |     entry_point="ibutton_app", | ||||||
|  |     cdefines=["APP_IBUTTON"], | ||||||
|  |     requires=[ | ||||||
|  |         "gui", | ||||||
|  |         "dialogs", | ||||||
|  |     ], | ||||||
|  |     provides=["ibutton_start"], | ||||||
|  |     icon="A_iButton_14", | ||||||
|  |     stack_size=2 * 1024, | ||||||
|  |     order=60, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="ibutton_start", | ||||||
|  |     apptype=FlipperAppType.STARTUP, | ||||||
|  |     entry_point="ibutton_on_system_start", | ||||||
|  |     requires=["ibutton"], | ||||||
|  |     order=60, | ||||||
|  | ) | ||||||
							
								
								
									
										23
									
								
								applications/infrared/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								applications/infrared/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | App( | ||||||
|  |     appid="infrared", | ||||||
|  |     name="Infrared", | ||||||
|  |     apptype=FlipperAppType.APP, | ||||||
|  |     entry_point="infrared_app", | ||||||
|  |     cdefines=["APP_INFRARED"], | ||||||
|  |     requires=[ | ||||||
|  |         "gui", | ||||||
|  |         "dialogs", | ||||||
|  |     ], | ||||||
|  |     provides=["infrared_start"], | ||||||
|  |     icon="A_Infrared_14", | ||||||
|  |     stack_size=3 * 1024, | ||||||
|  |     order=40, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | App( | ||||||
|  |     appid="infrared_start", | ||||||
|  |     apptype=FlipperAppType.STARTUP, | ||||||
|  |     entry_point="infrared_on_system_start", | ||||||
|  |     requires=["infrared"], | ||||||
|  |     order=20, | ||||||
|  | ) | ||||||
| @ -1,157 +0,0 @@ | |||||||
| 
 |  | ||||||
| #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_infrared.h> |  | ||||||
| 
 |  | ||||||
| #define TAG "InfraredParser" |  | ||||||
| 
 |  | ||||||
| bool infrared_parser_save_signal( |  | ||||||
|     FlipperFormat* ff, |  | ||||||
|     const InfraredAppSignal& signal, |  | ||||||
|     const std::string& name) { |  | ||||||
|     furi_assert(ff); |  | ||||||
|     furi_assert(!name.empty()); |  | ||||||
| 
 |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         if(!flipper_format_write_comment_cstr(ff, "")) break; |  | ||||||
|         if(!flipper_format_write_string_cstr(ff, "name", name.c_str())) break; |  | ||||||
|         if(signal.is_raw()) { |  | ||||||
|             furi_assert(signal.get_raw_signal().timings_cnt <= MAX_TIMINGS_AMOUNT); |  | ||||||
|             auto raw_signal = signal.get_raw_signal(); |  | ||||||
|             if(!flipper_format_write_string_cstr(ff, "type", "raw")) break; |  | ||||||
|             if(!flipper_format_write_uint32(ff, "frequency", &raw_signal.frequency, 1)) break; |  | ||||||
|             if(!flipper_format_write_float(ff, "duty_cycle", &raw_signal.duty_cycle, 1)) break; |  | ||||||
|             if(!flipper_format_write_uint32(ff, "data", raw_signal.timings, raw_signal.timings_cnt)) |  | ||||||
|                 break; |  | ||||||
|         } else { |  | ||||||
|             auto parsed_signal = signal.get_message(); |  | ||||||
|             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)) |  | ||||||
|                 break; |  | ||||||
|             if(!flipper_format_write_hex(ff, "command", (uint8_t*)&parsed_signal.command, 4)) |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
|         result = true; |  | ||||||
|     } while(0); |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool infrared_parser_read_signal(FlipperFormat* ff, InfraredAppSignal& signal, std::string& name) { |  | ||||||
|     furi_assert(ff); |  | ||||||
| 
 |  | ||||||
|     bool result = false; |  | ||||||
|     string_t read_string; |  | ||||||
|     string_init(read_string); |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         if(!flipper_format_read_string(ff, "name", read_string)) break; |  | ||||||
|         name = string_get_cstr(read_string); |  | ||||||
|         if(!flipper_format_read_string(ff, "type", read_string)) break; |  | ||||||
|         if(!string_cmp_str(read_string, "raw")) { |  | ||||||
|             uint32_t* timings = nullptr; |  | ||||||
|             uint32_t timings_cnt = 0; |  | ||||||
|             uint32_t frequency = 0; |  | ||||||
|             float duty_cycle = 0; |  | ||||||
| 
 |  | ||||||
|             if(!flipper_format_read_uint32(ff, "frequency", &frequency, 1)) break; |  | ||||||
|             if(!flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1)) break; |  | ||||||
|             if(!flipper_format_get_value_count(ff, "data", &timings_cnt)) break; |  | ||||||
|             if(timings_cnt > MAX_TIMINGS_AMOUNT) break; |  | ||||||
|             timings = (uint32_t*)malloc(sizeof(uint32_t) * timings_cnt); |  | ||||||
|             if(flipper_format_read_uint32(ff, "data", timings, timings_cnt)) { |  | ||||||
|                 signal.set_raw_signal(timings, timings_cnt, frequency, duty_cycle); |  | ||||||
|                 result = true; |  | ||||||
|             } |  | ||||||
|             free(timings); |  | ||||||
|         } else if(!string_cmp_str(read_string, "parsed")) { |  | ||||||
|             InfraredMessage parsed_signal; |  | ||||||
|             if(!flipper_format_read_string(ff, "protocol", read_string)) break; |  | ||||||
|             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(!infrared_parser_is_parsed_signal_valid(&parsed_signal)) break; |  | ||||||
|             signal.set_message(&parsed_signal); |  | ||||||
|             result = true; |  | ||||||
|         } else { |  | ||||||
|             FURI_LOG_E(TAG, "Unknown type of signal (allowed - raw/parsed) "); |  | ||||||
|         } |  | ||||||
|     } while(0); |  | ||||||
| 
 |  | ||||||
|     string_clear(read_string); |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool infrared_parser_is_parsed_signal_valid(const InfraredMessage* signal) { |  | ||||||
|     furi_assert(signal); |  | ||||||
|     bool result = true; |  | ||||||
| 
 |  | ||||||
|     if(!infrared_is_protocol_valid(signal->protocol)) { |  | ||||||
|         FURI_LOG_E(TAG, "Unknown protocol"); |  | ||||||
|         result = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(result) { |  | ||||||
|         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( |  | ||||||
|                 TAG, |  | ||||||
|                 "Address is out of range (mask 0x%08lX): 0x%lX\r\n", |  | ||||||
|                 address_mask, |  | ||||||
|                 signal->address); |  | ||||||
|             result = false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(result) { |  | ||||||
|         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( |  | ||||||
|                 TAG, |  | ||||||
|                 "Command is out of range (mask 0x%08lX): 0x%lX\r\n", |  | ||||||
|                 command_mask, |  | ||||||
|                 signal->command); |  | ||||||
|             result = false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool infrared_parser_is_raw_signal_valid( |  | ||||||
|     uint32_t frequency, |  | ||||||
|     float duty_cycle, |  | ||||||
|     uint32_t timings_cnt) { |  | ||||||
|     bool result = true; |  | ||||||
| 
 |  | ||||||
|     if((frequency > INFRARED_MAX_FREQUENCY) || (frequency < INFRARED_MIN_FREQUENCY)) { |  | ||||||
|         FURI_LOG_E( |  | ||||||
|             TAG, |  | ||||||
|             "Frequency is out of range (%lX - %lX): %lX", |  | ||||||
|             INFRARED_MIN_FREQUENCY, |  | ||||||
|             INFRARED_MAX_FREQUENCY, |  | ||||||
|             frequency); |  | ||||||
|         result = false; |  | ||||||
|     } else if((duty_cycle <= 0) || (duty_cycle > 1)) { |  | ||||||
|         FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)duty_cycle); |  | ||||||
|         result = false; |  | ||||||
|     } else if((timings_cnt <= 0) || (timings_cnt > MAX_TIMINGS_AMOUNT)) { |  | ||||||
|         FURI_LOG_E( |  | ||||||
|             TAG, "Timings amount is out of range (0 - %lX): %lX", MAX_TIMINGS_AMOUNT, timings_cnt); |  | ||||||
|         result = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| @ -1,48 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @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); |  | ||||||
							
								
								
									
										399
									
								
								applications/infrared/infrared.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								applications/infrared/infrared.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,399 @@ | |||||||
|  | #include "infrared_i.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | static const NotificationSequence* infrared_notification_sequences[] = { | ||||||
|  |     &sequence_success, | ||||||
|  |     &sequence_set_only_green_255, | ||||||
|  |     &sequence_reset_green, | ||||||
|  |     &sequence_blink_cyan_10, | ||||||
|  |     &sequence_blink_magenta_10, | ||||||
|  |     &sequence_solid_yellow, | ||||||
|  |     &sequence_reset_rgb}; | ||||||
|  | 
 | ||||||
|  | static void infrared_make_app_folder(Infrared* infrared) { | ||||||
|  |     if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) { | ||||||
|  |         dialog_message_show_storage_error(infrared->dialogs, "Cannot create\napp folder"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool infrared_custom_event_callback(void* context, uint32_t event) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     return scene_manager_handle_custom_event(infrared->scene_manager, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool infrared_back_event_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     return scene_manager_handle_back_event(infrared->scene_manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void infrared_tick_event_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     scene_manager_handle_tick_event(infrared->scene_manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void infrared_find_vacant_remote_name(string_t name, const char* path) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  | 
 | ||||||
|  |     string_t base_path; | ||||||
|  |     string_init_set_str(base_path, path); | ||||||
|  | 
 | ||||||
|  |     if(string_end_with_str_p(base_path, INFRARED_APP_EXTENSION)) { | ||||||
|  |         size_t filename_start = string_search_rchar(base_path, '/'); | ||||||
|  |         string_left(base_path, filename_start); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_printf(base_path, "%s/%s%s", path, string_get_cstr(name), INFRARED_APP_EXTENSION); | ||||||
|  | 
 | ||||||
|  |     FS_Error status = storage_common_stat(storage, string_get_cstr(base_path), NULL); | ||||||
|  | 
 | ||||||
|  |     if(status == FSE_OK) { | ||||||
|  |         /* If the suggested name is occupied, try another one (name2, name3, etc) */ | ||||||
|  |         size_t dot = string_search_rchar(base_path, '.'); | ||||||
|  |         string_left(base_path, dot); | ||||||
|  | 
 | ||||||
|  |         string_t path_temp; | ||||||
|  |         string_init(path_temp); | ||||||
|  | 
 | ||||||
|  |         uint32_t i = 1; | ||||||
|  |         do { | ||||||
|  |             string_printf( | ||||||
|  |                 path_temp, "%s%u%s", string_get_cstr(base_path), ++i, INFRARED_APP_EXTENSION); | ||||||
|  |             status = storage_common_stat(storage, string_get_cstr(path_temp), NULL); | ||||||
|  |         } while(status == FSE_OK); | ||||||
|  | 
 | ||||||
|  |         string_clear(path_temp); | ||||||
|  | 
 | ||||||
|  |         if(status == FSE_NOT_EXIST) { | ||||||
|  |             string_cat_printf(name, "%u", i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(base_path); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static Infrared* infrared_alloc() { | ||||||
|  |     Infrared* infrared = malloc(sizeof(Infrared)); | ||||||
|  | 
 | ||||||
|  |     string_init(infrared->file_path); | ||||||
|  | 
 | ||||||
|  |     InfraredAppState* app_state = &infrared->app_state; | ||||||
|  |     app_state->is_learning_new_remote = false; | ||||||
|  |     app_state->is_debug_enabled = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); | ||||||
|  |     app_state->edit_target = InfraredEditTargetNone; | ||||||
|  |     app_state->edit_mode = InfraredEditModeNone; | ||||||
|  |     app_state->current_button_index = InfraredButtonIndexNone; | ||||||
|  | 
 | ||||||
|  |     infrared->scene_manager = scene_manager_alloc(&infrared_scene_handlers, infrared); | ||||||
|  |     infrared->view_dispatcher = view_dispatcher_alloc(); | ||||||
|  | 
 | ||||||
|  |     infrared->gui = furi_record_open("gui"); | ||||||
|  | 
 | ||||||
|  |     ViewDispatcher* view_dispatcher = infrared->view_dispatcher; | ||||||
|  |     view_dispatcher_attach_to_gui(view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen); | ||||||
|  |     view_dispatcher_enable_queue(view_dispatcher); | ||||||
|  |     view_dispatcher_set_event_callback_context(view_dispatcher, infrared); | ||||||
|  |     view_dispatcher_set_custom_event_callback(view_dispatcher, infrared_custom_event_callback); | ||||||
|  |     view_dispatcher_set_navigation_event_callback(view_dispatcher, infrared_back_event_callback); | ||||||
|  |     view_dispatcher_set_tick_event_callback(view_dispatcher, infrared_tick_event_callback, 100); | ||||||
|  | 
 | ||||||
|  |     infrared->storage = furi_record_open("storage"); | ||||||
|  |     infrared->dialogs = furi_record_open("dialogs"); | ||||||
|  |     infrared->notifications = furi_record_open("notification"); | ||||||
|  | 
 | ||||||
|  |     infrared->worker = infrared_worker_alloc(); | ||||||
|  |     infrared->remote = infrared_remote_alloc(); | ||||||
|  |     infrared->received_signal = infrared_signal_alloc(); | ||||||
|  |     infrared->brute_force = infrared_brute_force_alloc(); | ||||||
|  | 
 | ||||||
|  |     infrared->submenu = submenu_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         view_dispatcher, InfraredViewSubmenu, submenu_get_view(infrared->submenu)); | ||||||
|  | 
 | ||||||
|  |     infrared->text_input = text_input_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input)); | ||||||
|  | 
 | ||||||
|  |     infrared->dialog_ex = dialog_ex_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex)); | ||||||
|  | 
 | ||||||
|  |     infrared->button_menu = button_menu_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         view_dispatcher, InfraredViewButtonMenu, button_menu_get_view(infrared->button_menu)); | ||||||
|  | 
 | ||||||
|  |     infrared->popup = popup_alloc(); | ||||||
|  |     view_dispatcher_add_view(view_dispatcher, InfraredViewPopup, popup_get_view(infrared->popup)); | ||||||
|  | 
 | ||||||
|  |     infrared->view_stack = view_stack_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack)); | ||||||
|  | 
 | ||||||
|  |     if(app_state->is_debug_enabled) { | ||||||
|  |         infrared->debug_view = infrared_debug_view_alloc(); | ||||||
|  |         view_dispatcher_add_view( | ||||||
|  |             view_dispatcher, | ||||||
|  |             InfraredViewDebugView, | ||||||
|  |             infrared_debug_view_get_view(infrared->debug_view)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     infrared->button_panel = button_panel_alloc(); | ||||||
|  |     infrared->loading = loading_alloc(); | ||||||
|  |     infrared->progress = infrared_progress_view_alloc(); | ||||||
|  | 
 | ||||||
|  |     return infrared; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void infrared_free(Infrared* infrared) { | ||||||
|  |     furi_assert(infrared); | ||||||
|  |     ViewDispatcher* view_dispatcher = infrared->view_dispatcher; | ||||||
|  |     InfraredAppState* app_state = &infrared->app_state; | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_remove_view(view_dispatcher, InfraredViewSubmenu); | ||||||
|  |     submenu_free(infrared->submenu); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput); | ||||||
|  |     text_input_free(infrared->text_input); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx); | ||||||
|  |     dialog_ex_free(infrared->dialog_ex); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_remove_view(view_dispatcher, InfraredViewButtonMenu); | ||||||
|  |     button_menu_free(infrared->button_menu); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_remove_view(view_dispatcher, InfraredViewPopup); | ||||||
|  |     popup_free(infrared->popup); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_remove_view(view_dispatcher, InfraredViewStack); | ||||||
|  |     view_stack_free(infrared->view_stack); | ||||||
|  | 
 | ||||||
|  |     if(app_state->is_debug_enabled) { | ||||||
|  |         view_dispatcher_remove_view(view_dispatcher, InfraredViewDebugView); | ||||||
|  |         infrared_debug_view_free(infrared->debug_view); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     button_panel_free(infrared->button_panel); | ||||||
|  |     loading_free(infrared->loading); | ||||||
|  |     infrared_progress_view_free(infrared->progress); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_free(view_dispatcher); | ||||||
|  |     scene_manager_free(infrared->scene_manager); | ||||||
|  | 
 | ||||||
|  |     infrared_brute_force_free(infrared->brute_force); | ||||||
|  |     infrared_signal_free(infrared->received_signal); | ||||||
|  |     infrared_remote_free(infrared->remote); | ||||||
|  |     infrared_worker_free(infrared->worker); | ||||||
|  | 
 | ||||||
|  |     furi_record_close("gui"); | ||||||
|  |     infrared->gui = NULL; | ||||||
|  | 
 | ||||||
|  |     furi_record_close("notification"); | ||||||
|  |     infrared->notifications = NULL; | ||||||
|  | 
 | ||||||
|  |     furi_record_close("dialogs"); | ||||||
|  |     infrared->dialogs = NULL; | ||||||
|  | 
 | ||||||
|  |     furi_record_close("gui"); | ||||||
|  |     infrared->gui = NULL; | ||||||
|  | 
 | ||||||
|  |     string_clear(infrared->file_path); | ||||||
|  | 
 | ||||||
|  |     free(infrared); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_add_remote_with_button( | ||||||
|  |     Infrared* infrared, | ||||||
|  |     const char* button_name, | ||||||
|  |     InfraredSignal* signal) { | ||||||
|  |     InfraredRemote* remote = infrared->remote; | ||||||
|  | 
 | ||||||
|  |     string_t new_name, new_path; | ||||||
|  |     string_init_set_str(new_name, INFRARED_DEFAULT_REMOTE_NAME); | ||||||
|  |     string_init_set_str(new_path, INFRARED_APP_FOLDER); | ||||||
|  | 
 | ||||||
|  |     infrared_find_vacant_remote_name(new_name, string_get_cstr(new_path)); | ||||||
|  |     string_cat_printf(new_path, "/%s%s", string_get_cstr(new_name), INFRARED_APP_EXTENSION); | ||||||
|  | 
 | ||||||
|  |     infrared_remote_reset(remote); | ||||||
|  |     infrared_remote_set_name(remote, string_get_cstr(new_name)); | ||||||
|  |     infrared_remote_set_path(remote, string_get_cstr(new_path)); | ||||||
|  | 
 | ||||||
|  |     string_clear(new_name); | ||||||
|  |     string_clear(new_path); | ||||||
|  |     return infrared_remote_add_button(remote, button_name, signal); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_rename_current_remote(Infrared* infrared, const char* name) { | ||||||
|  |     InfraredRemote* remote = infrared->remote; | ||||||
|  |     const char* remote_path = infrared_remote_get_path(remote); | ||||||
|  | 
 | ||||||
|  |     if(!strcmp(infrared_remote_get_name(remote), name)) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_t new_name; | ||||||
|  |     string_init_set_str(new_name, name); | ||||||
|  | 
 | ||||||
|  |     infrared_find_vacant_remote_name(new_name, remote_path); | ||||||
|  | 
 | ||||||
|  |     string_t new_path; | ||||||
|  |     string_init_set(new_path, infrared_remote_get_path(remote)); | ||||||
|  |     if(string_end_with_str_p(new_path, INFRARED_APP_EXTENSION)) { | ||||||
|  |         size_t filename_start = string_search_rchar(new_path, '/'); | ||||||
|  |         string_left(new_path, filename_start); | ||||||
|  |     } | ||||||
|  |     string_cat_printf(new_path, "/%s%s", string_get_cstr(new_name), INFRARED_APP_EXTENSION); | ||||||
|  | 
 | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  | 
 | ||||||
|  |     FS_Error status = storage_common_rename( | ||||||
|  |         storage, infrared_remote_get_path(remote), string_get_cstr(new_path)); | ||||||
|  |     infrared_remote_set_name(remote, string_get_cstr(new_name)); | ||||||
|  | 
 | ||||||
|  |     string_clear(new_name); | ||||||
|  |     string_clear(new_path); | ||||||
|  | 
 | ||||||
|  |     furi_record_close("storage"); | ||||||
|  |     return (status == FSE_OK || status == FSE_EXIST); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { | ||||||
|  |     if(infrared_signal_is_raw(signal)) { | ||||||
|  |         InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); | ||||||
|  |         infrared_worker_set_raw_signal(infrared->worker, raw->timings, raw->timings_size); | ||||||
|  |     } else { | ||||||
|  |         InfraredMessage* message = infrared_signal_get_message(signal); | ||||||
|  |         infrared_worker_set_decoded_signal(infrared->worker, message); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     DOLPHIN_DEED(DolphinDeedIrSend); | ||||||
|  |     infrared_worker_tx_start(infrared->worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) { | ||||||
|  |     furi_assert(button_index < infrared_remote_get_button_count(infrared->remote)); | ||||||
|  | 
 | ||||||
|  |     InfraredRemoteButton* button = infrared_remote_get_button(infrared->remote, button_index); | ||||||
|  |     InfraredSignal* signal = infrared_remote_button_get_signal(button); | ||||||
|  | 
 | ||||||
|  |     infrared_tx_start_signal(infrared, signal); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_tx_start_received(Infrared* infrared) { | ||||||
|  |     infrared_tx_start_signal(infrared, infrared->received_signal); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_tx_stop(Infrared* infrared) { | ||||||
|  |     infrared_worker_tx_stop(infrared->worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) { | ||||||
|  |     va_list args; | ||||||
|  |     va_start(args, text); | ||||||
|  | 
 | ||||||
|  |     vsnprintf(infrared->text_store[bank], INFRARED_TEXT_STORE_SIZE, text, args); | ||||||
|  | 
 | ||||||
|  |     va_end(args); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_text_store_clear(Infrared* infrared, uint32_t bank) { | ||||||
|  |     memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_play_notification_message(Infrared* infrared, uint32_t message) { | ||||||
|  |     furi_assert(message < sizeof(infrared_notification_sequences) / sizeof(NotificationSequence*)); | ||||||
|  |     notification_message(infrared->notifications, infrared_notification_sequences[message]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_show_loading_popup(Infrared* infrared, bool show) { | ||||||
|  |     TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); | ||||||
|  |     ViewStack* view_stack = infrared->view_stack; | ||||||
|  |     Loading* loading = infrared->loading; | ||||||
|  | 
 | ||||||
|  |     if(show) { | ||||||
|  |         // Raise timer priority so that animations can play
 | ||||||
|  |         vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); | ||||||
|  |         view_stack_add_view(view_stack, loading_get_view(loading)); | ||||||
|  |     } else { | ||||||
|  |         view_stack_remove_view(view_stack, loading_get_view(loading)); | ||||||
|  |         // Restore default timer priority
 | ||||||
|  |         vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_signal_sent_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkSend); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Infrared* infrared = context; | ||||||
|  | 
 | ||||||
|  |     if(infrared_worker_signal_is_decoded(received_signal)) { | ||||||
|  |         infrared_signal_set_message( | ||||||
|  |             infrared->received_signal, infrared_worker_get_decoded_signal(received_signal)); | ||||||
|  |     } else { | ||||||
|  |         const uint32_t* timings; | ||||||
|  |         size_t timings_size; | ||||||
|  |         infrared_worker_get_raw_signal(received_signal, &timings, &timings_size); | ||||||
|  |         infrared_signal_set_raw_signal( | ||||||
|  |             infrared->received_signal, | ||||||
|  |             timings, | ||||||
|  |             timings_size, | ||||||
|  |             INFRARED_COMMON_CARRIER_FREQUENCY, | ||||||
|  |             INFRARED_COMMON_DUTY_CYCLE); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event( | ||||||
|  |         infrared->view_dispatcher, InfraredCustomEventTypeSignalReceived); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_text_input_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     view_dispatcher_send_custom_event( | ||||||
|  |         infrared->view_dispatcher, InfraredCustomEventTypeTextEditDone); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_popup_closed_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     view_dispatcher_send_custom_event( | ||||||
|  |         infrared->view_dispatcher, InfraredCustomEventTypePopupClosed); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t infrared_app(void* p) { | ||||||
|  |     Infrared* infrared = infrared_alloc(); | ||||||
|  | 
 | ||||||
|  |     infrared_make_app_folder(infrared); | ||||||
|  | 
 | ||||||
|  |     bool is_remote_loaded = false; | ||||||
|  | 
 | ||||||
|  |     if(p) { | ||||||
|  |         string_set_str(infrared->file_path, (const char*)p); | ||||||
|  |         is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path); | ||||||
|  |         if(!is_remote_loaded) { | ||||||
|  |             dialog_message_show_storage_error( | ||||||
|  |                 infrared->dialogs, "Failed to load\nselected remote"); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(is_remote_loaded) { | ||||||
|  |         scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); | ||||||
|  |     } else { | ||||||
|  |         scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_run(infrared->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     infrared_free(infrared); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								applications/infrared/infrared.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								applications/infrared/infrared.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | typedef struct Infrared Infrared; | ||||||
| @ -1,250 +0,0 @@ | |||||||
| #include "infrared_app.h" |  | ||||||
| #include "m-string.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 InfraredApp::run(void* args) { |  | ||||||
|     InfraredAppEvent event; |  | ||||||
|     bool consumed; |  | ||||||
|     bool exit = false; |  | ||||||
| 
 |  | ||||||
|     if(args) { |  | ||||||
|         string_t path; |  | ||||||
|         string_init_set_str(path, (char*)args); |  | ||||||
|         if(string_end_with_str_p(path, InfraredApp::infrared_extension)) { |  | ||||||
|             bool result = remote_manager.load(path); |  | ||||||
|             if(result) { |  | ||||||
|                 current_scene = InfraredApp::Scene::Remote; |  | ||||||
|             } else { |  | ||||||
|                 printf("Failed to load remote \'%s\'\r\n", string_get_cstr(path)); |  | ||||||
|                 return -1; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         string_clear(path); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     scenes[current_scene]->on_enter(this); |  | ||||||
| 
 |  | ||||||
|     while(!exit) { |  | ||||||
|         view_manager.receive_event(&event); |  | ||||||
| 
 |  | ||||||
|         if(event.type == InfraredAppEvent::Type::Exit) break; |  | ||||||
| 
 |  | ||||||
|         consumed = scenes[current_scene]->on_event(this, &event); |  | ||||||
| 
 |  | ||||||
|         if(!consumed) { |  | ||||||
|             if(event.type == InfraredAppEvent::Type::Back) { |  | ||||||
|                 exit = switch_to_previous_scene(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     scenes[current_scene]->on_exit(this); |  | ||||||
| 
 |  | ||||||
|     return 0; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| InfraredApp::InfraredApp() { |  | ||||||
|     furi_check(InfraredAppRemoteManager::max_button_name_length < get_text_store_size()); |  | ||||||
|     string_init_set_str(file_path, InfraredApp::infrared_directory); |  | ||||||
|     notification = static_cast<NotificationApp*>(furi_record_open("notification")); |  | ||||||
|     dialogs = static_cast<DialogsApp*>(furi_record_open("dialogs")); |  | ||||||
|     infrared_worker = infrared_worker_alloc(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredApp::~InfraredApp() { |  | ||||||
|     infrared_worker_free(infrared_worker); |  | ||||||
|     furi_record_close("notification"); |  | ||||||
|     furi_record_close("dialogs"); |  | ||||||
|     string_clear(file_path); |  | ||||||
|     for(auto& [key, scene] : scenes) delete scene; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredAppViewManager* InfraredApp::get_view_manager() { |  | ||||||
|     return &view_manager; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::set_learn_new_remote(bool value) { |  | ||||||
|     learn_new_remote = value; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredApp::get_learn_new_remote() { |  | ||||||
|     return learn_new_remote; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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 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; |  | ||||||
|         scenes[current_scene]->on_enter(this); |  | ||||||
|         view_manager.clear_events(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::search_and_switch_to_previous_scene( |  | ||||||
|     const std::initializer_list<Scene>& scenes_list) { |  | ||||||
|     Scene previous_scene = Scene::Start; |  | ||||||
|     bool scene_found = false; |  | ||||||
| 
 |  | ||||||
|     while(!scene_found) { |  | ||||||
|         previous_scene = get_previous_scene(); |  | ||||||
| 
 |  | ||||||
|         if(previous_scene == Scene::Exit) break; |  | ||||||
| 
 |  | ||||||
|         for(Scene element : scenes_list) { |  | ||||||
|             if(previous_scene == element) { |  | ||||||
|                 scene_found = true; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(previous_scene == Scene::Exit) { |  | ||||||
|         InfraredAppEvent event; |  | ||||||
|         event.type = InfraredAppEvent::Type::Exit; |  | ||||||
|         view_manager.send_event(&event); |  | ||||||
|     } else { |  | ||||||
|         scenes[current_scene]->on_exit(this); |  | ||||||
|         current_scene = previous_scene; |  | ||||||
|         scenes[current_scene]->on_enter(this); |  | ||||||
|         view_manager.clear_events(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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(); |  | ||||||
| 
 |  | ||||||
|     if(previous_scene == Scene::Exit) return true; |  | ||||||
| 
 |  | ||||||
|     scenes[current_scene]->on_exit(this); |  | ||||||
|     current_scene = previous_scene; |  | ||||||
|     scenes[current_scene]->on_enter(this); |  | ||||||
|     view_manager.clear_events(); |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredApp::Scene InfraredApp::get_previous_scene() { |  | ||||||
|     Scene scene = Scene::Exit; |  | ||||||
| 
 |  | ||||||
|     if(!previous_scenes_list.empty()) { |  | ||||||
|         scene = previous_scenes_list.front(); |  | ||||||
|         previous_scenes_list.pop_front(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return scene; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredAppRemoteManager* InfraredApp::get_remote_manager() { |  | ||||||
|     return &remote_manager; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::set_text_store(uint8_t index, const char* text...) { |  | ||||||
|     furi_check(index < text_store_max); |  | ||||||
| 
 |  | ||||||
|     va_list args; |  | ||||||
|     va_start(args, text); |  | ||||||
| 
 |  | ||||||
|     vsnprintf(text_store[index], text_store_size, text, args); |  | ||||||
| 
 |  | ||||||
|     va_end(args); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| char* InfraredApp::get_text_store(uint8_t index) { |  | ||||||
|     furi_check(index < text_store_max); |  | ||||||
| 
 |  | ||||||
|     return text_store[index]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint8_t InfraredApp::get_text_store_size() { |  | ||||||
|     return text_store_size; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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 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 InfraredApp::set_edit_element(InfraredApp::EditElement value) { |  | ||||||
|     element = value; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredApp::EditElement InfraredApp::get_edit_element(void) { |  | ||||||
|     return element; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::set_edit_action(InfraredApp::EditAction value) { |  | ||||||
|     action = value; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredApp::EditAction InfraredApp::get_edit_action(void) { |  | ||||||
|     return action; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::set_current_button(int value) { |  | ||||||
|     current_button = value; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int InfraredApp::get_current_button() { |  | ||||||
|     return current_button; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::notify_success() { |  | ||||||
|     notification_message(notification, &sequence_success); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::notify_blink_read() { |  | ||||||
|     notification_message(notification, &sequence_blink_cyan_10); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::notify_blink_send() { |  | ||||||
|     notification_message(notification, &sequence_blink_magenta_10); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| DialogsApp* InfraredApp::get_dialogs() { |  | ||||||
|     return dialogs; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::notify_green_on() { |  | ||||||
|     notification_message(notification, &sequence_set_only_green_255); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::notify_green_off() { |  | ||||||
|     notification_message(notification, &sequence_reset_green); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredWorker* InfraredApp::get_infrared_worker() { |  | ||||||
|     return infrared_worker; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const InfraredAppSignal& InfraredApp::get_received_signal() const { |  | ||||||
|     return received_signal; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::set_received_signal(const InfraredAppSignal& signal) { |  | ||||||
|     received_signal = signal; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredApp::signal_sent_callback(void* context) { |  | ||||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); |  | ||||||
|     app->notify_blink_send(); |  | ||||||
| } |  | ||||||
| @ -1,326 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @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 <dialogs/dialogs.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_blink_read(); |  | ||||||
|     /** Light green */ |  | ||||||
|     void notify_green_on(); |  | ||||||
|     /** Disable green light */ |  | ||||||
|     void notify_green_off(); |  | ||||||
|     /** Blink on send */ |  | ||||||
|     void notify_blink_send(); |  | ||||||
| 
 |  | ||||||
|     /** Get Dialogs instance */ |  | ||||||
|     DialogsApp* get_dialogs(); |  | ||||||
| 
 |  | ||||||
|     /** 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(); |  | ||||||
| 
 |  | ||||||
|     string_t file_path; |  | ||||||
| 
 |  | ||||||
|     /** 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; |  | ||||||
|     /** Dialogs instance */ |  | ||||||
|     DialogsApp* dialogs; |  | ||||||
|     /** 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,93 +0,0 @@ | |||||||
| 
 |  | ||||||
| #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> |  | ||||||
| 
 |  | ||||||
| void InfraredAppBruteForce::add_record(int index, const char* name) { |  | ||||||
|     records[name].index = index; |  | ||||||
|     records[name].amount = 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppBruteForce::calculate_messages() { |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); |  | ||||||
|     FlipperFormat* ff = flipper_format_file_alloc(storage); |  | ||||||
|     result = flipper_format_file_open_existing(ff, universal_db_filename); |  | ||||||
| 
 |  | ||||||
|     if(result) { |  | ||||||
|         InfraredAppSignal signal; |  | ||||||
| 
 |  | ||||||
|         string_t signal_name; |  | ||||||
|         string_init(signal_name); |  | ||||||
|         while(flipper_format_read_string(ff, "name", signal_name)) { |  | ||||||
|             auto element = records.find(string_get_cstr(signal_name)); |  | ||||||
|             if(element != records.cend()) { |  | ||||||
|                 ++element->second.amount; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         string_clear(signal_name); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     flipper_format_free(ff); |  | ||||||
|     furi_record_close("storage"); |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppBruteForce::stop_bruteforce() { |  | ||||||
|     furi_assert((current_record.size())); |  | ||||||
| 
 |  | ||||||
|     if(current_record.size()) { |  | ||||||
|         furi_assert(ff); |  | ||||||
|         current_record.clear(); |  | ||||||
|         flipper_format_free(ff); |  | ||||||
|         furi_record_close("storage"); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppBruteForce::send_next_bruteforce(void) { |  | ||||||
|     furi_assert(current_record.size()); |  | ||||||
|     furi_assert(ff); |  | ||||||
| 
 |  | ||||||
|     InfraredAppSignal signal; |  | ||||||
|     std::string signal_name; |  | ||||||
|     bool result = false; |  | ||||||
|     do { |  | ||||||
|         result = infrared_parser_read_signal(ff, signal, signal_name); |  | ||||||
|     } while(result && current_record.compare(signal_name)); |  | ||||||
| 
 |  | ||||||
|     if(result) { |  | ||||||
|         signal.transmit(); |  | ||||||
|     } |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppBruteForce::start_bruteforce(int index, int& record_amount) { |  | ||||||
|     bool result = false; |  | ||||||
|     record_amount = 0; |  | ||||||
| 
 |  | ||||||
|     for(const auto& it : records) { |  | ||||||
|         if(it.second.index == index) { |  | ||||||
|             record_amount = it.second.amount; |  | ||||||
|             if(record_amount) { |  | ||||||
|                 current_record = it.first; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(record_amount) { |  | ||||||
|         Storage* storage = static_cast<Storage*>(furi_record_open("storage")); |  | ||||||
|         ff = flipper_format_file_alloc(storage); |  | ||||||
|         result = flipper_format_file_open_existing(ff, universal_db_filename); |  | ||||||
|         if(!result) { |  | ||||||
|             flipper_format_free(ff); |  | ||||||
|             furi_record_close("storage"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| @ -1,67 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @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() { |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| @ -1,48 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @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 { |  | ||||||
|         int32_t dummy; |  | ||||||
|         /** 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,266 +0,0 @@ | |||||||
| #include "m-string.h" |  | ||||||
| #include "storage/filesystem_api_defines.h" |  | ||||||
| #include <flipper_format/flipper_format.h> |  | ||||||
| #include "infrared_app_remote_manager.h" |  | ||||||
| #include "infrared/helpers/infrared_parser.h" |  | ||||||
| #include "infrared/infrared_app_signal.h" |  | ||||||
| 
 |  | ||||||
| #include <utility> |  | ||||||
| 
 |  | ||||||
| #include <infrared.h> |  | ||||||
| #include <cstdio> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <gui/modules/button_menu.h> |  | ||||||
| #include <storage/storage.h> |  | ||||||
| #include "infrared_app.h" |  | ||||||
| #include <toolbox/path.h> |  | ||||||
| 
 |  | ||||||
| static const char* default_remote_name = "remote"; |  | ||||||
| 
 |  | ||||||
| void InfraredAppRemoteManager::find_vacant_remote_name(string_t name, string_t path) { |  | ||||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); |  | ||||||
| 
 |  | ||||||
|     string_t base_path; |  | ||||||
|     string_init_set(base_path, path); |  | ||||||
| 
 |  | ||||||
|     if(string_end_with_str_p(base_path, InfraredApp::infrared_extension)) { |  | ||||||
|         size_t filename_start = string_search_rchar(base_path, '/'); |  | ||||||
|         string_left(base_path, filename_start); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_printf( |  | ||||||
|         base_path, |  | ||||||
|         "%s/%s%s", |  | ||||||
|         string_get_cstr(path), |  | ||||||
|         string_get_cstr(name), |  | ||||||
|         InfraredApp::infrared_extension); |  | ||||||
| 
 |  | ||||||
|     FS_Error error = storage_common_stat(storage, string_get_cstr(base_path), NULL); |  | ||||||
| 
 |  | ||||||
|     if(error == FSE_OK) { |  | ||||||
|         /* if suggested name is occupied, try another one (name2, name3, etc) */ |  | ||||||
|         size_t dot = string_search_rchar(base_path, '.'); |  | ||||||
|         string_left(base_path, dot); |  | ||||||
| 
 |  | ||||||
|         string_t path_temp; |  | ||||||
|         string_init(path_temp); |  | ||||||
| 
 |  | ||||||
|         uint32_t i = 1; |  | ||||||
|         do { |  | ||||||
|             string_printf( |  | ||||||
|                 path_temp, |  | ||||||
|                 "%s%u%s", |  | ||||||
|                 string_get_cstr(base_path), |  | ||||||
|                 ++i, |  | ||||||
|                 InfraredApp::infrared_extension); |  | ||||||
|             error = storage_common_stat(storage, string_get_cstr(path_temp), NULL); |  | ||||||
|         } while(error == FSE_OK); |  | ||||||
| 
 |  | ||||||
|         string_clear(path_temp); |  | ||||||
| 
 |  | ||||||
|         if(error == FSE_NOT_EXIST) { |  | ||||||
|             string_cat_printf(name, "%u", i); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_clear(base_path); |  | ||||||
|     furi_record_close("storage"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppRemoteManager::add_button(const char* button_name, const InfraredAppSignal& signal) { |  | ||||||
|     remote->buttons.emplace_back(button_name, signal); |  | ||||||
|     return store(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppRemoteManager::add_remote_with_button( |  | ||||||
|     const char* button_name, |  | ||||||
|     const InfraredAppSignal& signal) { |  | ||||||
|     furi_check(button_name != nullptr); |  | ||||||
| 
 |  | ||||||
|     string_t new_name; |  | ||||||
|     string_init_set_str(new_name, default_remote_name); |  | ||||||
| 
 |  | ||||||
|     string_t new_path; |  | ||||||
|     string_init_set_str(new_path, InfraredApp::infrared_directory); |  | ||||||
| 
 |  | ||||||
|     find_vacant_remote_name(new_name, new_path); |  | ||||||
| 
 |  | ||||||
|     string_cat_printf( |  | ||||||
|         new_path, "/%s%s", string_get_cstr(new_name), InfraredApp::infrared_extension); |  | ||||||
| 
 |  | ||||||
|     remote = std::make_unique<InfraredAppRemote>(new_path); |  | ||||||
|     remote->name = std::string(string_get_cstr(new_name)); |  | ||||||
| 
 |  | ||||||
|     string_clear(new_path); |  | ||||||
|     string_clear(new_name); |  | ||||||
| 
 |  | ||||||
|     return add_button(button_name, signal); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::vector<std::string> InfraredAppRemoteManager::get_button_list(void) const { |  | ||||||
|     std::vector<std::string> name_vector; |  | ||||||
|     name_vector.reserve(remote->buttons.size()); |  | ||||||
| 
 |  | ||||||
|     for(const auto& it : remote->buttons) { |  | ||||||
|         name_vector.emplace_back(it.name); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // copy elision
 |  | ||||||
|     return name_vector; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const InfraredAppSignal& InfraredAppRemoteManager::get_button_data(size_t index) const { |  | ||||||
|     furi_check(remote.get() != nullptr); |  | ||||||
|     auto& buttons = remote->buttons; |  | ||||||
|     furi_check(index < buttons.size()); |  | ||||||
| 
 |  | ||||||
|     return buttons.at(index).signal; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppRemoteManager::delete_remote() { |  | ||||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); |  | ||||||
| 
 |  | ||||||
|     FS_Error error = storage_common_remove(storage, string_get_cstr(remote->path)); |  | ||||||
|     reset_remote(); |  | ||||||
| 
 |  | ||||||
|     furi_record_close("storage"); |  | ||||||
|     return (error == FSE_OK || error == FSE_NOT_EXIST); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppRemoteManager::reset_remote() { |  | ||||||
|     remote.reset(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppRemoteManager::delete_button(uint32_t index) { |  | ||||||
|     furi_check(remote.get() != nullptr); |  | ||||||
|     auto& buttons = remote->buttons; |  | ||||||
|     furi_check(index < buttons.size()); |  | ||||||
| 
 |  | ||||||
|     buttons.erase(buttons.begin() + index); |  | ||||||
|     return store(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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 InfraredAppRemoteManager::get_remote_name() { |  | ||||||
|     return remote.get() ? remote->name : std::string(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppRemoteManager::rename_remote(const char* str) { |  | ||||||
|     furi_check(str != nullptr); |  | ||||||
|     furi_check(remote.get() != nullptr); |  | ||||||
|     furi_check(!string_empty_p(remote->path)); |  | ||||||
| 
 |  | ||||||
|     if(!remote->name.compare(str)) { |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_t new_name; |  | ||||||
|     string_init_set_str(new_name, str); |  | ||||||
|     find_vacant_remote_name(new_name, remote->path); |  | ||||||
| 
 |  | ||||||
|     string_t new_path; |  | ||||||
|     string_init_set(new_path, remote->path); |  | ||||||
|     if(string_end_with_str_p(new_path, InfraredApp::infrared_extension)) { |  | ||||||
|         size_t filename_start = string_search_rchar(new_path, '/'); |  | ||||||
|         string_left(new_path, filename_start); |  | ||||||
|     } |  | ||||||
|     string_cat_printf( |  | ||||||
|         new_path, "/%s%s", string_get_cstr(new_name), InfraredApp::infrared_extension); |  | ||||||
| 
 |  | ||||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); |  | ||||||
| 
 |  | ||||||
|     FS_Error error = |  | ||||||
|         storage_common_rename(storage, string_get_cstr(remote->path), string_get_cstr(new_path)); |  | ||||||
|     remote->name = std::string(string_get_cstr(new_name)); |  | ||||||
| 
 |  | ||||||
|     string_clear(new_name); |  | ||||||
|     string_clear(new_path); |  | ||||||
| 
 |  | ||||||
|     furi_record_close("storage"); |  | ||||||
|     return (error == FSE_OK || error == FSE_EXIST); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppRemoteManager::rename_button(uint32_t index, const char* str) { |  | ||||||
|     furi_check(remote.get() != nullptr); |  | ||||||
|     auto& buttons = remote->buttons; |  | ||||||
|     furi_check(index < buttons.size()); |  | ||||||
| 
 |  | ||||||
|     buttons[index].name = str; |  | ||||||
|     return store(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| size_t InfraredAppRemoteManager::get_number_of_buttons() { |  | ||||||
|     furi_check(remote.get() != nullptr); |  | ||||||
|     return remote->buttons.size(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppRemoteManager::store(void) { |  | ||||||
|     bool result = false; |  | ||||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); |  | ||||||
| 
 |  | ||||||
|     if(!storage_simply_mkdir(storage, InfraredApp::infrared_directory)) return false; |  | ||||||
| 
 |  | ||||||
|     FlipperFormat* ff = flipper_format_file_alloc(storage); |  | ||||||
| 
 |  | ||||||
|     FURI_LOG_I("RemoteManager", "store file: \'%s\'", string_get_cstr(remote->path)); |  | ||||||
|     result = flipper_format_file_open_always(ff, string_get_cstr(remote->path)); |  | ||||||
|     if(result) { |  | ||||||
|         result = flipper_format_write_header_cstr(ff, "IR signals file", 1); |  | ||||||
|     } |  | ||||||
|     if(result) { |  | ||||||
|         for(const auto& button : remote->buttons) { |  | ||||||
|             result = infrared_parser_save_signal(ff, button.signal, button.name.c_str()); |  | ||||||
|             if(!result) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     flipper_format_free(ff); |  | ||||||
|     furi_record_close("storage"); |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppRemoteManager::load(string_t path) { |  | ||||||
|     bool result = false; |  | ||||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); |  | ||||||
|     FlipperFormat* ff = flipper_format_file_alloc(storage); |  | ||||||
| 
 |  | ||||||
|     FURI_LOG_I("RemoteManager", "load file: \'%s\'", string_get_cstr(path)); |  | ||||||
|     result = flipper_format_file_open_existing(ff, string_get_cstr(path)); |  | ||||||
|     if(result) { |  | ||||||
|         string_t header; |  | ||||||
|         string_init(header); |  | ||||||
|         uint32_t version; |  | ||||||
|         result = flipper_format_read_header(ff, header, &version); |  | ||||||
|         if(result) { |  | ||||||
|             result = !string_cmp_str(header, "IR signals file") && (version == 1); |  | ||||||
|         } |  | ||||||
|         string_clear(header); |  | ||||||
|     } |  | ||||||
|     if(result) { |  | ||||||
|         string_t new_name; |  | ||||||
|         string_init(new_name); |  | ||||||
| 
 |  | ||||||
|         remote = std::make_unique<InfraredAppRemote>(path); |  | ||||||
|         path_extract_filename(path, new_name, true); |  | ||||||
|         remote->name = std::string(string_get_cstr(new_name)); |  | ||||||
| 
 |  | ||||||
|         string_clear(new_name); |  | ||||||
|         InfraredAppSignal signal; |  | ||||||
|         std::string signal_name; |  | ||||||
|         while(infrared_parser_read_signal(ff, signal, signal_name)) { |  | ||||||
|             remote->buttons.emplace_back(signal_name.c_str(), std::move(signal)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     flipper_format_free(ff); |  | ||||||
|     furi_record_close("storage"); |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| @ -1,189 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @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 "m-string.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; |  | ||||||
|     /** Path to remote file */ |  | ||||||
|     string_t path; |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     /** Initialize new remote
 |  | ||||||
|      *  |  | ||||||
|      * @param path - remote file path |  | ||||||
|      */ |  | ||||||
|     InfraredAppRemote(string_t file_path) { |  | ||||||
|         string_init_set(path, file_path); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ~InfraredAppRemote() { |  | ||||||
|         string_clear(path); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /** Class to handle remote manager */ |  | ||||||
| class InfraredAppRemoteManager { |  | ||||||
|     /** Remote instance. There can be 1 remote loaded at a time. */ |  | ||||||
|     std::unique_ptr<InfraredAppRemote> remote; |  | ||||||
| 
 |  | ||||||
| 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 |  | ||||||
|      * @param path - remote file path |  | ||||||
|      */ |  | ||||||
|     void find_vacant_remote_name(string_t name, string_t path); |  | ||||||
| 
 |  | ||||||
|     /** 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 path - path to remote file |  | ||||||
|      * @retval true if success, false otherwise |  | ||||||
|      */ |  | ||||||
|     bool load(string_t path); |  | ||||||
| }; |  | ||||||
| @ -1,116 +0,0 @@ | |||||||
| #include "infrared_app_signal.h" |  | ||||||
| #include <infrared_transmit.h> |  | ||||||
| 
 |  | ||||||
| void InfraredAppSignal::copy_raw_signal( |  | ||||||
|     const uint32_t* timings, |  | ||||||
|     size_t size, |  | ||||||
|     uint32_t frequency, |  | ||||||
|     float duty_cycle) { |  | ||||||
|     furi_assert(size); |  | ||||||
|     furi_assert(timings); |  | ||||||
| 
 |  | ||||||
|     payload.raw.frequency = frequency; |  | ||||||
|     payload.raw.duty_cycle = duty_cycle; |  | ||||||
|     payload.raw.timings_cnt = size; |  | ||||||
|     if(size) { |  | ||||||
|         payload.raw.timings = new uint32_t[size]; |  | ||||||
|         memcpy(payload.raw.timings, timings, size * sizeof(uint32_t)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppSignal::clear_timings() { |  | ||||||
|     if(raw_signal) { |  | ||||||
|         delete[] payload.raw.timings; |  | ||||||
|         payload.raw.timings_cnt = 0; |  | ||||||
|         payload.raw.timings = nullptr; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredAppSignal::InfraredAppSignal( |  | ||||||
|     const uint32_t* timings, |  | ||||||
|     size_t timings_cnt, |  | ||||||
|     uint32_t frequency, |  | ||||||
|     float duty_cycle) { |  | ||||||
|     raw_signal = true; |  | ||||||
|     copy_raw_signal(timings, timings_cnt, frequency, duty_cycle); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredAppSignal::InfraredAppSignal(const InfraredMessage* infrared_message) { |  | ||||||
|     raw_signal = false; |  | ||||||
|     payload.message = *infrared_message; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredAppSignal& InfraredAppSignal::operator=(const InfraredAppSignal& other) { |  | ||||||
|     clear_timings(); |  | ||||||
|     raw_signal = other.raw_signal; |  | ||||||
|     if(!raw_signal) { |  | ||||||
|         payload.message = other.payload.message; |  | ||||||
|     } else { |  | ||||||
|         copy_raw_signal( |  | ||||||
|             other.payload.raw.timings, |  | ||||||
|             other.payload.raw.timings_cnt, |  | ||||||
|             other.payload.raw.frequency, |  | ||||||
|             other.payload.raw.duty_cycle); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return *this; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredAppSignal::InfraredAppSignal(const InfraredAppSignal& other) { |  | ||||||
|     raw_signal = other.raw_signal; |  | ||||||
|     if(!raw_signal) { |  | ||||||
|         payload.message = other.payload.message; |  | ||||||
|     } else { |  | ||||||
|         copy_raw_signal( |  | ||||||
|             other.payload.raw.timings, |  | ||||||
|             other.payload.raw.timings_cnt, |  | ||||||
|             other.payload.raw.frequency, |  | ||||||
|             other.payload.raw.duty_cycle); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredAppSignal::InfraredAppSignal(InfraredAppSignal&& other) { |  | ||||||
|     raw_signal = other.raw_signal; |  | ||||||
|     if(!raw_signal) { |  | ||||||
|         payload.message = other.payload.message; |  | ||||||
|     } else { |  | ||||||
|         furi_assert(other.payload.raw.timings_cnt > 0); |  | ||||||
| 
 |  | ||||||
|         payload.raw.timings = other.payload.raw.timings; |  | ||||||
|         payload.raw.timings_cnt = other.payload.raw.timings_cnt; |  | ||||||
|         payload.raw.frequency = other.payload.raw.frequency; |  | ||||||
|         payload.raw.duty_cycle = other.payload.raw.duty_cycle; |  | ||||||
|         other.payload.raw.timings = nullptr; |  | ||||||
|         other.payload.raw.timings_cnt = 0; |  | ||||||
|         other.raw_signal = false; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppSignal::set_message(const InfraredMessage* infrared_message) { |  | ||||||
|     clear_timings(); |  | ||||||
|     raw_signal = false; |  | ||||||
|     payload.message = *infrared_message; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppSignal::set_raw_signal( |  | ||||||
|     uint32_t* timings, |  | ||||||
|     size_t timings_cnt, |  | ||||||
|     uint32_t frequency, |  | ||||||
|     float duty_cycle) { |  | ||||||
|     clear_timings(); |  | ||||||
|     raw_signal = true; |  | ||||||
|     copy_raw_signal(timings, timings_cnt, frequency, duty_cycle); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppSignal::transmit() const { |  | ||||||
|     if(!raw_signal) { |  | ||||||
|         infrared_send(&payload.message, 1); |  | ||||||
|     } else { |  | ||||||
|         infrared_send_raw_ext( |  | ||||||
|             payload.raw.timings, |  | ||||||
|             payload.raw.timings_cnt, |  | ||||||
|             true, |  | ||||||
|             payload.raw.frequency, |  | ||||||
|             payload.raw.duty_cycle); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,134 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @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; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| @ -1,163 +0,0 @@ | |||||||
| #include <gui/modules/button_menu.h> |  | ||||||
| #include <gui/view_stack.h> |  | ||||||
| #include <gui/modules/loading.h> |  | ||||||
| #include <gui/modules/button_panel.h> |  | ||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <callback-connector.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" |  | ||||||
| 
 |  | ||||||
| InfraredAppViewManager::InfraredAppViewManager() { |  | ||||||
|     event_queue = osMessageQueueNew(10, sizeof(InfraredAppEvent), NULL); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher = view_dispatcher_alloc(); |  | ||||||
|     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); |  | ||||||
| 
 |  | ||||||
|     button_menu = button_menu_alloc(); |  | ||||||
|     submenu = submenu_alloc(); |  | ||||||
|     popup = popup_alloc(); |  | ||||||
|     dialog_ex = dialog_ex_alloc(); |  | ||||||
|     text_input = text_input_alloc(); |  | ||||||
|     button_panel = button_panel_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(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); |  | ||||||
|     view_set_previous_callback(submenu_get_view(submenu), callback); |  | ||||||
|     view_set_previous_callback(popup_get_view(popup), callback); |  | ||||||
|     view_set_previous_callback(dialog_ex_get_view(dialog_ex), callback); |  | ||||||
|     view_set_previous_callback(text_input_get_view(text_input), callback); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredAppViewManager::~InfraredAppViewManager() { |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::UniversalRemote)); |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::ButtonMenu)); |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::TextInput)); |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::DialogEx)); |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::Submenu)); |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         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); |  | ||||||
|     button_panel_free(button_panel); |  | ||||||
|     submenu_free(submenu); |  | ||||||
|     popup_free(popup); |  | ||||||
|     button_menu_free(button_menu); |  | ||||||
|     dialog_ex_free(dialog_ex); |  | ||||||
|     text_input_free(text_input); |  | ||||||
|     infrared_progress_view_free(progress_view); |  | ||||||
|     loading_free(loading_view); |  | ||||||
| 
 |  | ||||||
|     view_dispatcher_free(view_dispatcher); |  | ||||||
|     furi_record_close("gui"); |  | ||||||
|     osMessageQueueDelete(event_queue); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppViewManager::switch_to(ViewId type) { |  | ||||||
|     view_dispatcher_switch_to_view(view_dispatcher, static_cast<uint32_t>(type)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| TextInput* InfraredAppViewManager::get_text_input() { |  | ||||||
|     return text_input; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| DialogEx* InfraredAppViewManager::get_dialog_ex() { |  | ||||||
|     return dialog_ex; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Submenu* InfraredAppViewManager::get_submenu() { |  | ||||||
|     return submenu; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Popup* InfraredAppViewManager::get_popup() { |  | ||||||
|     return popup; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ButtonMenu* InfraredAppViewManager::get_button_menu() { |  | ||||||
|     return button_menu; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ButtonPanel* InfraredAppViewManager::get_button_panel() { |  | ||||||
|     return button_panel; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| InfraredProgressView* InfraredAppViewManager::get_progress() { |  | ||||||
|     return progress_view; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Loading* InfraredAppViewManager::get_loading() { |  | ||||||
|     return loading_view; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ViewStack* InfraredAppViewManager::get_universal_view_stack() { |  | ||||||
|     return universal_view_stack; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| osMessageQueueId_t InfraredAppViewManager::get_event_queue() { |  | ||||||
|     return event_queue; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppViewManager::clear_events() { |  | ||||||
|     InfraredAppEvent event; |  | ||||||
|     while(osMessageQueueGet(event_queue, &event, NULL, 0) == osOK) |  | ||||||
|         ; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppViewManager::receive_event(InfraredAppEvent* event) { |  | ||||||
|     if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) { |  | ||||||
|         event->type = InfraredAppEvent::Type::Tick; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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 INFRARED DMA sending. */ |  | ||||||
|     if(event->type == InfraredAppEvent::Type::MenuSelectedRelease) { |  | ||||||
|         timeout = 200; |  | ||||||
|     } |  | ||||||
|     if((event->type == InfraredAppEvent::Type::DialogExSelected) && |  | ||||||
|        (event->payload.dialog_ex_result == DialogExReleaseCenter)) { |  | ||||||
|         timeout = 200; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     osMessageQueuePut(event_queue, event, 0, timeout); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint32_t InfraredAppViewManager::previous_view_callback(void*) { |  | ||||||
|     if(event_queue != NULL) { |  | ||||||
|         InfraredAppEvent event; |  | ||||||
|         event.type = InfraredAppEvent::Type::Back; |  | ||||||
|         send_event(&event); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return VIEW_IGNORE; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppViewManager::add_view(ViewId view_type, View* view) { |  | ||||||
|     view_dispatcher_add_view(view_dispatcher, static_cast<uint32_t>(view_type), view); |  | ||||||
| } |  | ||||||
| @ -1,164 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @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); |  | ||||||
| }; |  | ||||||
							
								
								
									
										154
									
								
								applications/infrared/infrared_brute_force.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								applications/infrared/infrared_brute_force.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,154 @@ | |||||||
|  | #include "infrared_brute_force.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <m-dict.h> | ||||||
|  | #include <m-string.h> | ||||||
|  | #include <flipper_format/flipper_format.h> | ||||||
|  | 
 | ||||||
|  | #include "infrared_signal.h" | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint32_t index; | ||||||
|  |     uint32_t count; | ||||||
|  | } InfraredBruteForceRecord; | ||||||
|  | 
 | ||||||
|  | DICT_DEF2( | ||||||
|  |     InfraredBruteForceRecordDict, | ||||||
|  |     string_t, | ||||||
|  |     STRING_OPLIST, | ||||||
|  |     InfraredBruteForceRecord, | ||||||
|  |     M_POD_OPLIST); | ||||||
|  | 
 | ||||||
|  | struct InfraredBruteForce { | ||||||
|  |     FlipperFormat* ff; | ||||||
|  |     const char* db_filename; | ||||||
|  |     string_t current_record_name; | ||||||
|  |     InfraredBruteForceRecordDict_t records; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | InfraredBruteForce* infrared_brute_force_alloc() { | ||||||
|  |     InfraredBruteForce* brute_force = malloc(sizeof(InfraredBruteForce)); | ||||||
|  |     brute_force->ff = NULL; | ||||||
|  |     brute_force->db_filename = NULL; | ||||||
|  |     string_init(brute_force->current_record_name); | ||||||
|  |     InfraredBruteForceRecordDict_init(brute_force->records); | ||||||
|  |     return brute_force; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_brute_force_free(InfraredBruteForce* brute_force) { | ||||||
|  |     furi_assert(!brute_force->ff); | ||||||
|  |     InfraredBruteForceRecordDict_clear(brute_force->records); | ||||||
|  |     string_clear(brute_force->current_record_name); | ||||||
|  |     free(brute_force); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename) { | ||||||
|  |     brute_force->db_filename = db_filename; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { | ||||||
|  |     furi_assert(brute_force->db_filename); | ||||||
|  |     bool success = false; | ||||||
|  | 
 | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     FlipperFormat* ff = flipper_format_file_alloc(storage); | ||||||
|  | 
 | ||||||
|  |     success = flipper_format_file_open_existing(ff, brute_force->db_filename); | ||||||
|  |     if(success) { | ||||||
|  |         string_t signal_name; | ||||||
|  |         string_init(signal_name); | ||||||
|  |         while(flipper_format_read_string(ff, "name", signal_name)) { | ||||||
|  |             InfraredBruteForceRecord* record = | ||||||
|  |                 InfraredBruteForceRecordDict_get(brute_force->records, signal_name); | ||||||
|  |             if(record) { | ||||||
|  |                 ++(record->count); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         string_clear(signal_name); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     flipper_format_free(ff); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_brute_force_start( | ||||||
|  |     InfraredBruteForce* brute_force, | ||||||
|  |     uint32_t index, | ||||||
|  |     uint32_t* record_count) { | ||||||
|  |     bool success = false; | ||||||
|  |     *record_count = 0; | ||||||
|  | 
 | ||||||
|  |     InfraredBruteForceRecordDict_it_t it; | ||||||
|  |     for(InfraredBruteForceRecordDict_it(it, brute_force->records); | ||||||
|  |         !InfraredBruteForceRecordDict_end_p(it); | ||||||
|  |         InfraredBruteForceRecordDict_next(it)) { | ||||||
|  |         const InfraredBruteForceRecordDict_itref_t* record = InfraredBruteForceRecordDict_cref(it); | ||||||
|  |         if(record->value.index == index) { | ||||||
|  |             *record_count = record->value.count; | ||||||
|  |             if(*record_count) { | ||||||
|  |                 string_set(brute_force->current_record_name, record->key); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(*record_count) { | ||||||
|  |         Storage* storage = furi_record_open("storage"); | ||||||
|  |         brute_force->ff = flipper_format_file_alloc(storage); | ||||||
|  |         success = flipper_format_file_open_existing(brute_force->ff, brute_force->db_filename); | ||||||
|  |         if(!success) { | ||||||
|  |             flipper_format_free(brute_force->ff); | ||||||
|  |             brute_force->ff = NULL; | ||||||
|  |             furi_record_close("storage"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_brute_force_is_started(InfraredBruteForce* brute_force) { | ||||||
|  |     return brute_force->ff; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_brute_force_stop(InfraredBruteForce* brute_force) { | ||||||
|  |     furi_assert(string_size(brute_force->current_record_name)); | ||||||
|  |     furi_assert(brute_force->ff); | ||||||
|  | 
 | ||||||
|  |     string_reset(brute_force->current_record_name); | ||||||
|  |     flipper_format_free(brute_force->ff); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  |     brute_force->ff = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) { | ||||||
|  |     furi_assert(string_size(brute_force->current_record_name)); | ||||||
|  |     furi_assert(brute_force->ff); | ||||||
|  |     bool success = false; | ||||||
|  | 
 | ||||||
|  |     string_t signal_name; | ||||||
|  |     string_init(signal_name); | ||||||
|  |     InfraredSignal* signal = infrared_signal_alloc(); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         success = infrared_signal_read(signal, brute_force->ff, signal_name); | ||||||
|  |     } while(success && !string_equal_p(brute_force->current_record_name, signal_name)); | ||||||
|  | 
 | ||||||
|  |     if(success) { | ||||||
|  |         infrared_signal_transmit(signal); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     infrared_signal_free(signal); | ||||||
|  |     string_clear(signal_name); | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_brute_force_add_record( | ||||||
|  |     InfraredBruteForce* brute_force, | ||||||
|  |     uint32_t index, | ||||||
|  |     const char* name) { | ||||||
|  |     InfraredBruteForceRecord value = {.index = index, .count = 0}; | ||||||
|  |     string_t key; | ||||||
|  |     string_init_set_str(key, name); | ||||||
|  |     InfraredBruteForceRecordDict_set_at(brute_force->records, key, value); | ||||||
|  |     string_clear(key); | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								applications/infrared/infrared_brute_force.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								applications/infrared/infrared_brute_force.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | typedef struct InfraredBruteForce InfraredBruteForce; | ||||||
|  | 
 | ||||||
|  | InfraredBruteForce* infrared_brute_force_alloc(); | ||||||
|  | void infrared_brute_force_free(InfraredBruteForce* brute_force); | ||||||
|  | void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename); | ||||||
|  | bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); | ||||||
|  | bool infrared_brute_force_start( | ||||||
|  |     InfraredBruteForce* brute_force, | ||||||
|  |     uint32_t index, | ||||||
|  |     uint32_t* record_count); | ||||||
|  | bool infrared_brute_force_is_started(InfraredBruteForce* brute_force); | ||||||
|  | void infrared_brute_force_stop(InfraredBruteForce* brute_force); | ||||||
|  | bool infrared_brute_force_send_next(InfraredBruteForce* brute_force); | ||||||
|  | void infrared_brute_force_add_record( | ||||||
|  |     InfraredBruteForce* brute_force, | ||||||
|  |     uint32_t index, | ||||||
|  |     const char* name); | ||||||
| @ -1,16 +1,12 @@ | |||||||
| #include <furi_hal_delay.h> |  | ||||||
| #include <infrared.h> |  | ||||||
| #include <cli/cli.h> |  | ||||||
| #include <cmsis_os2.h> |  | ||||||
| #include <infrared_worker.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal_infrared.h> |  | ||||||
| #include <sstream> |  | ||||||
| #include <string> |  | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| #include <infrared_transmit.h> | #include <cli/cli.h> | ||||||
| #include <sys/types.h> | #include <infrared.h> | ||||||
| #include "../helpers/infrared_parser.h" | #include <infrared_worker.h> | ||||||
|  | #include <furi_hal_infrared.h> | ||||||
|  | 
 | ||||||
|  | #include "infrared_signal.h" | ||||||
|  | 
 | ||||||
|  | #define INFRARED_CLI_BUF_SIZE 10 | ||||||
| 
 | 
 | ||||||
| static void infrared_cli_start_ir_rx(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 void infrared_cli_start_ir_tx(Cli* cli, string_t args); | ||||||
| @ -92,79 +88,83 @@ static void infrared_cli_print_usage(void) { | |||||||
|         INFRARED_MAX_FREQUENCY); |         INFRARED_MAX_FREQUENCY); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool parse_message(const char* str, InfraredMessage* message) { | static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { | ||||||
|     char protocol_name[32]; |     char protocol_name[32]; | ||||||
|     int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message->address, &message->command); |     InfraredMessage message; | ||||||
|  |     int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message.address, &message.command); | ||||||
| 
 | 
 | ||||||
|     if(parsed != 3) { |     if(parsed != 3) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     message->protocol = infrared_get_protocol_by_name(protocol_name); |     message.repeat = false; | ||||||
|     message->repeat = false; |     infrared_signal_set_message(signal, &message); | ||||||
| 
 |     return infrared_signal_is_valid(signal); | ||||||
|     return infrared_parser_is_parsed_signal_valid(message); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool parse_signal_raw( | static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) { | ||||||
|     const char* str, |     char frequency_str[INFRARED_CLI_BUF_SIZE]; | ||||||
|     uint32_t* timings, |     char duty_cycle_str[INFRARED_CLI_BUF_SIZE]; | ||||||
|     uint32_t* timings_cnt, |  | ||||||
|     float* duty_cycle, |  | ||||||
|     uint32_t* frequency) { |  | ||||||
|     char frequency_str[10]; |  | ||||||
|     char duty_cycle_str[10]; |  | ||||||
|     int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); |     int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); | ||||||
|     if(parsed != 2) return false; |  | ||||||
| 
 | 
 | ||||||
|     *frequency = atoi(frequency_str); |     if(parsed != 2) { | ||||||
|     *duty_cycle = (float)atoi(duty_cycle_str) / 100; |         return false; | ||||||
|     str += strlen(frequency_str) + strlen(duty_cycle_str) + 10; |  | ||||||
| 
 |  | ||||||
|     uint32_t timings_cnt_max = *timings_cnt; |  | ||||||
|     *timings_cnt = 0; |  | ||||||
| 
 |  | ||||||
|     while(1) { |  | ||||||
|         char timing_str[10]; |  | ||||||
|         for(; *str == ' '; ++str) |  | ||||||
|             ; |  | ||||||
|         if(1 != sscanf(str, "%9s", timing_str)) break; |  | ||||||
|         str += strlen(timing_str); |  | ||||||
|         uint32_t timing = atoi(timing_str); |  | ||||||
|         if(timing <= 0) break; |  | ||||||
|         if(*timings_cnt >= timings_cnt_max) break; |  | ||||||
|         timings[*timings_cnt] = timing; |  | ||||||
|         ++*timings_cnt; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return infrared_parser_is_raw_signal_valid(*frequency, *duty_cycle, *timings_cnt); |     uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT); | ||||||
|  |     uint32_t frequency = atoi(frequency_str); | ||||||
|  |     float duty_cycle = (float)atoi(duty_cycle_str) / 100; | ||||||
|  | 
 | ||||||
|  |     str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE; | ||||||
|  | 
 | ||||||
|  |     size_t timings_size = 0; | ||||||
|  |     while(1) { | ||||||
|  |         while(*str == ' ') { | ||||||
|  |             ++str; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         char timing_str[INFRARED_CLI_BUF_SIZE]; | ||||||
|  |         if(sscanf(str, "%9s", timing_str) != 1) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         str += strlen(timing_str); | ||||||
|  |         uint32_t timing = atoi(timing_str); | ||||||
|  | 
 | ||||||
|  |         if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         timings[timings_size] = timing; | ||||||
|  |         ++timings_size; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); | ||||||
|  |     free(timings); | ||||||
|  | 
 | ||||||
|  |     return infrared_signal_is_valid(signal); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void infrared_cli_start_ir_tx(Cli* cli, string_t args) { | static void infrared_cli_start_ir_tx(Cli* cli, string_t args) { | ||||||
|     UNUSED(cli); |     UNUSED(cli); | ||||||
|     InfraredMessage message; |  | ||||||
|     const char* str = string_get_cstr(args); |     const char* str = string_get_cstr(args); | ||||||
|     uint32_t frequency; |     InfraredSignal* signal = infrared_signal_alloc(); | ||||||
|     float duty_cycle; |  | ||||||
|     uint32_t timings_cnt = MAX_TIMINGS_AMOUNT; |  | ||||||
|     uint32_t* timings = (uint32_t*)malloc(sizeof(uint32_t) * timings_cnt); |  | ||||||
| 
 | 
 | ||||||
|     if(parse_message(str, &message)) { |     bool success = infrared_cli_parse_message(str, signal) || infrared_cli_parse_raw(str, signal); | ||||||
|         infrared_send(&message, 1); |     if(success) { | ||||||
|     } else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) { |         infrared_signal_transmit(signal); | ||||||
|         infrared_send_raw_ext(timings, timings_cnt, true, frequency, duty_cycle); |  | ||||||
|     } else { |     } else { | ||||||
|         printf("Wrong arguments.\r\n"); |         printf("Wrong arguments.\r\n"); | ||||||
|         infrared_cli_print_usage(); |         infrared_cli_print_usage(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     free(timings); |     infrared_signal_free(signal); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { | static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { | ||||||
|     UNUSED(context); |     UNUSED(context); | ||||||
|     if(furi_hal_infrared_is_busy()) { |     if(furi_hal_infrared_is_busy()) { | ||||||
|         printf("INFRARED is busy. Exit."); |         printf("INFRARED is busy. Exiting."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -189,8 +189,7 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { | |||||||
|         infrared_cli_print_usage(); |         infrared_cli_print_usage(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | void infrared_on_system_start() { | ||||||
| extern "C" void infrared_on_system_start() { |  | ||||||
| #ifdef SRV_CLI | #ifdef SRV_CLI | ||||||
|     Cli* cli = (Cli*)furi_record_open("cli"); |     Cli* cli = (Cli*)furi_record_open("cli"); | ||||||
|     cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); |     cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); | ||||||
							
								
								
									
										51
									
								
								applications/infrared/infrared_custom_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								applications/infrared/infrared_custom_event.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | 
 | ||||||
|  | enum InfraredCustomEventType { | ||||||
|  |     // Reserve first 100 events for button types and indexes, starting from 0
 | ||||||
|  |     InfraredCustomEventTypeReserved = 100, | ||||||
|  |     InfraredCustomEventTypeMenuSelected, | ||||||
|  |     InfraredCustomEventTypeTransmitStarted, | ||||||
|  |     InfraredCustomEventTypeTransmitStopped, | ||||||
|  |     InfraredCustomEventTypeSignalReceived, | ||||||
|  |     InfraredCustomEventTypeTextEditDone, | ||||||
|  |     InfraredCustomEventTypePopupClosed, | ||||||
|  |     InfraredCustomEventTypeButtonSelected, | ||||||
|  |     InfraredCustomEventTypeBackPressed, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #pragma pack(push, 1) | ||||||
|  | typedef union { | ||||||
|  |     uint32_t packed_value; | ||||||
|  |     struct { | ||||||
|  |         uint16_t type; | ||||||
|  |         int16_t value; | ||||||
|  |     } content; | ||||||
|  | } InfraredCustomEvent; | ||||||
|  | #pragma pack(pop) | ||||||
|  | 
 | ||||||
|  | static inline uint32_t infrared_custom_event_pack(uint16_t type, int16_t value) { | ||||||
|  |     InfraredCustomEvent event = {.content = {.type = type, .value = value}}; | ||||||
|  |     return event.packed_value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void | ||||||
|  |     infrared_custom_event_unpack(uint32_t packed_value, uint16_t* type, int16_t* value) { | ||||||
|  |     InfraredCustomEvent event = {.packed_value = packed_value}; | ||||||
|  |     if(type) *type = event.content.type; | ||||||
|  |     if(value) *value = event.content.value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline uint16_t infrared_custom_event_get_type(uint32_t packed_value) { | ||||||
|  |     uint16_t type; | ||||||
|  |     infrared_custom_event_unpack(packed_value, &type, NULL); | ||||||
|  |     return type; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline int16_t infrared_custom_event_get_value(uint32_t packed_value) { | ||||||
|  |     int16_t value; | ||||||
|  |     infrared_custom_event_unpack(packed_value, NULL, &value); | ||||||
|  |     return value; | ||||||
|  | } | ||||||
							
								
								
									
										134
									
								
								applications/infrared/infrared_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								applications/infrared/infrared_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include <gui/view_stack.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/popup.h> | ||||||
|  | #include <gui/modules/loading.h> | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | #include <gui/modules/text_input.h> | ||||||
|  | #include <gui/modules/button_menu.h> | ||||||
|  | #include <gui/modules/button_panel.h> | ||||||
|  | 
 | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <dialogs/dialogs.h> | ||||||
|  | 
 | ||||||
|  | #include <notification/notification_messages.h> | ||||||
|  | 
 | ||||||
|  | #include <infrared_worker.h> | ||||||
|  | 
 | ||||||
|  | #include "infrared.h" | ||||||
|  | #include "infrared_remote.h" | ||||||
|  | #include "infrared_brute_force.h" | ||||||
|  | #include "infrared_custom_event.h" | ||||||
|  | 
 | ||||||
|  | #include "scenes/infrared_scene.h" | ||||||
|  | #include "views/infrared_progress_view.h" | ||||||
|  | #include "views/infrared_debug_view.h" | ||||||
|  | 
 | ||||||
|  | #define INFRARED_FILE_NAME_SIZE 100 | ||||||
|  | #define INFRARED_TEXT_STORE_NUM 2 | ||||||
|  | #define INFRARED_TEXT_STORE_SIZE 128 | ||||||
|  | 
 | ||||||
|  | #define INFRARED_MAX_BUTTON_NAME_LENGTH 22 | ||||||
|  | #define INFRARED_MAX_REMOTE_NAME_LENGTH 22 | ||||||
|  | 
 | ||||||
|  | #define INFRARED_APP_FOLDER "/any/infrared" | ||||||
|  | #define INFRARED_APP_EXTENSION ".ir" | ||||||
|  | 
 | ||||||
|  | #define INFRARED_DEFAULT_REMOTE_NAME "Remote" | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     InfraredButtonIndexNone = -1, | ||||||
|  | } InfraredButtonIndex; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     InfraredEditTargetNone, | ||||||
|  |     InfraredEditTargetRemote, | ||||||
|  |     InfraredEditTargetButton, | ||||||
|  | } InfraredEditTarget; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     InfraredEditModeNone, | ||||||
|  |     InfraredEditModeRename, | ||||||
|  |     InfraredEditModeDelete, | ||||||
|  | } InfraredEditMode; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool is_learning_new_remote; | ||||||
|  |     bool is_debug_enabled; | ||||||
|  |     InfraredEditTarget edit_target : 8; | ||||||
|  |     InfraredEditMode edit_mode : 8; | ||||||
|  |     int32_t current_button_index; | ||||||
|  | } InfraredAppState; | ||||||
|  | 
 | ||||||
|  | struct Infrared { | ||||||
|  |     SceneManager* scene_manager; | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  | 
 | ||||||
|  |     Gui* gui; | ||||||
|  |     Storage* storage; | ||||||
|  |     DialogsApp* dialogs; | ||||||
|  |     NotificationApp* notifications; | ||||||
|  |     InfraredWorker* worker; | ||||||
|  |     InfraredRemote* remote; | ||||||
|  |     InfraredSignal* received_signal; | ||||||
|  |     InfraredBruteForce* brute_force; | ||||||
|  | 
 | ||||||
|  |     Submenu* submenu; | ||||||
|  |     TextInput* text_input; | ||||||
|  |     DialogEx* dialog_ex; | ||||||
|  |     ButtonMenu* button_menu; | ||||||
|  |     Popup* popup; | ||||||
|  | 
 | ||||||
|  |     ViewStack* view_stack; | ||||||
|  |     InfraredDebugView* debug_view; | ||||||
|  | 
 | ||||||
|  |     ButtonPanel* button_panel; | ||||||
|  |     Loading* loading; | ||||||
|  |     InfraredProgressView* progress; | ||||||
|  | 
 | ||||||
|  |     string_t file_path; | ||||||
|  |     char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; | ||||||
|  |     InfraredAppState app_state; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     InfraredViewSubmenu, | ||||||
|  |     InfraredViewTextInput, | ||||||
|  |     InfraredViewDialogEx, | ||||||
|  |     InfraredViewButtonMenu, | ||||||
|  |     InfraredViewPopup, | ||||||
|  |     InfraredViewStack, | ||||||
|  |     InfraredViewDebugView, | ||||||
|  | } InfraredView; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     InfraredNotificationMessageSuccess, | ||||||
|  |     InfraredNotificationMessageGreenOn, | ||||||
|  |     InfraredNotificationMessageGreenOff, | ||||||
|  |     InfraredNotificationMessageBlinkRead, | ||||||
|  |     InfraredNotificationMessageBlinkSend, | ||||||
|  |     InfraredNotificationMessageYellowOn, | ||||||
|  |     InfraredNotificationMessageYellowOff, | ||||||
|  | } InfraredNotificationMessage; | ||||||
|  | 
 | ||||||
|  | bool infrared_add_remote_with_button(Infrared* infrared, const char* name, InfraredSignal* signal); | ||||||
|  | bool infrared_rename_current_remote(Infrared* infrared, const char* name); | ||||||
|  | void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal); | ||||||
|  | void infrared_tx_start_button_index(Infrared* infrared, size_t button_index); | ||||||
|  | void infrared_tx_start_received(Infrared* infrared); | ||||||
|  | void infrared_tx_stop(Infrared* infrared); | ||||||
|  | void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...); | ||||||
|  | void infrared_text_store_clear(Infrared* infrared, uint32_t bank); | ||||||
|  | void infrared_play_notification_message(Infrared* infrared, uint32_t message); | ||||||
|  | void infrared_show_loading_popup(Infrared* infrared, bool show); | ||||||
|  | 
 | ||||||
|  | void infrared_signal_sent_callback(void* context); | ||||||
|  | void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal); | ||||||
|  | void infrared_text_input_callback(void* context); | ||||||
|  | void infrared_popup_closed_callback(void* context); | ||||||
							
								
								
									
										176
									
								
								applications/infrared/infrared_remote.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								applications/infrared/infrared_remote.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | |||||||
|  | #include "infrared_remote.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <m-string.h> | ||||||
|  | #include <m-array.h> | ||||||
|  | #include <toolbox/path.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <furi/common_defines.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "InfraredRemote" | ||||||
|  | 
 | ||||||
|  | ARRAY_DEF(InfraredButtonArray, InfraredRemoteButton*, M_PTR_OPLIST); | ||||||
|  | 
 | ||||||
|  | struct InfraredRemote { | ||||||
|  |     InfraredButtonArray_t buttons; | ||||||
|  |     string_t name; | ||||||
|  |     string_t path; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void infrared_remote_clear_buttons(InfraredRemote* remote) { | ||||||
|  |     InfraredButtonArray_it_t it; | ||||||
|  |     for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it); | ||||||
|  |         InfraredButtonArray_next(it)) { | ||||||
|  |         infrared_remote_button_free(*InfraredButtonArray_cref(it)); | ||||||
|  |     } | ||||||
|  |     InfraredButtonArray_reset(remote->buttons); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | InfraredRemote* infrared_remote_alloc() { | ||||||
|  |     InfraredRemote* remote = malloc(sizeof(InfraredRemote)); | ||||||
|  |     InfraredButtonArray_init(remote->buttons); | ||||||
|  |     string_init(remote->name); | ||||||
|  |     string_init(remote->path); | ||||||
|  |     return remote; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_remote_free(InfraredRemote* remote) { | ||||||
|  |     infrared_remote_clear_buttons(remote); | ||||||
|  |     InfraredButtonArray_clear(remote->buttons); | ||||||
|  |     string_clear(remote->path); | ||||||
|  |     string_clear(remote->name); | ||||||
|  |     free(remote); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_remote_reset(InfraredRemote* remote) { | ||||||
|  |     infrared_remote_clear_buttons(remote); | ||||||
|  |     string_reset(remote->name); | ||||||
|  |     string_reset(remote->path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_remote_set_name(InfraredRemote* remote, const char* name) { | ||||||
|  |     string_set_str(remote->name, name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* infrared_remote_get_name(InfraredRemote* remote) { | ||||||
|  |     return string_get_cstr(remote->name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_remote_set_path(InfraredRemote* remote, const char* path) { | ||||||
|  |     string_set_str(remote->path, path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* infrared_remote_get_path(InfraredRemote* remote) { | ||||||
|  |     return string_get_cstr(remote->path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t infrared_remote_get_button_count(InfraredRemote* remote) { | ||||||
|  |     return InfraredButtonArray_size(remote->buttons); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index) { | ||||||
|  |     furi_assert(index < InfraredButtonArray_size(remote->buttons)); | ||||||
|  |     return *InfraredButtonArray_get(remote->buttons, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) { | ||||||
|  |     InfraredRemoteButton* button = infrared_remote_button_alloc(); | ||||||
|  |     infrared_remote_button_set_name(button, name); | ||||||
|  |     infrared_remote_button_set_signal(button, signal); | ||||||
|  |     InfraredButtonArray_push_back(remote->buttons, button); | ||||||
|  |     return infrared_remote_store(remote); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index) { | ||||||
|  |     furi_assert(index < InfraredButtonArray_size(remote->buttons)); | ||||||
|  |     InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, index); | ||||||
|  |     infrared_remote_button_set_name(button, new_name); | ||||||
|  |     return infrared_remote_store(remote); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) { | ||||||
|  |     furi_assert(index < InfraredButtonArray_size(remote->buttons)); | ||||||
|  |     InfraredRemoteButton* button; | ||||||
|  |     InfraredButtonArray_pop_at(&button, remote->buttons, index); | ||||||
|  |     infrared_remote_button_free(button); | ||||||
|  |     return infrared_remote_store(remote); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_remote_store(InfraredRemote* remote) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     FlipperFormat* ff = flipper_format_file_alloc(storage); | ||||||
|  |     const char* path = string_get_cstr(remote->path); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_I(TAG, "store file: \'%s\'", path); | ||||||
|  | 
 | ||||||
|  |     bool success = flipper_format_file_open_always(ff, path) && | ||||||
|  |                    flipper_format_write_header_cstr(ff, "IR signals file", 1); | ||||||
|  |     if(success) { | ||||||
|  |         InfraredButtonArray_it_t it; | ||||||
|  |         for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it); | ||||||
|  |             InfraredButtonArray_next(it)) { | ||||||
|  |             InfraredRemoteButton* button = *InfraredButtonArray_cref(it); | ||||||
|  |             success = infrared_signal_save( | ||||||
|  |                 infrared_remote_button_get_signal(button), | ||||||
|  |                 ff, | ||||||
|  |                 infrared_remote_button_get_name(button)); | ||||||
|  |             if(!success) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     flipper_format_free(ff); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_remote_load(InfraredRemote* remote, string_t path) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     FlipperFormat* ff = flipper_format_file_alloc(storage); | ||||||
|  | 
 | ||||||
|  |     string_t buf; | ||||||
|  |     string_init(buf); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_I(TAG, "load file: \'%s\'", string_get_cstr(path)); | ||||||
|  |     bool success = flipper_format_file_open_existing(ff, string_get_cstr(path)); | ||||||
|  | 
 | ||||||
|  |     if(success) { | ||||||
|  |         uint32_t version; | ||||||
|  |         success = flipper_format_read_header(ff, buf, &version) && | ||||||
|  |                   !string_cmp_str(buf, "IR signals file") && (version == 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(success) { | ||||||
|  |         path_extract_filename(path, buf, true); | ||||||
|  |         infrared_remote_clear_buttons(remote); | ||||||
|  |         infrared_remote_set_name(remote, string_get_cstr(buf)); | ||||||
|  |         infrared_remote_set_path(remote, string_get_cstr(path)); | ||||||
|  | 
 | ||||||
|  |         for(bool can_read = true; can_read;) { | ||||||
|  |             InfraredRemoteButton* button = infrared_remote_button_alloc(); | ||||||
|  |             can_read = infrared_signal_read(infrared_remote_button_get_signal(button), ff, buf); | ||||||
|  |             if(can_read) { | ||||||
|  |                 infrared_remote_button_set_name(button, string_get_cstr(buf)); | ||||||
|  |                 InfraredButtonArray_push_back(remote->buttons, button); | ||||||
|  |             } else { | ||||||
|  |                 infrared_remote_button_free(button); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     string_clear(buf); | ||||||
|  |     flipper_format_free(ff); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_remote_remove(InfraredRemote* remote) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  | 
 | ||||||
|  |     FS_Error status = storage_common_remove(storage, string_get_cstr(remote->path)); | ||||||
|  |     infrared_remote_reset(remote); | ||||||
|  | 
 | ||||||
|  |     furi_record_close("storage"); | ||||||
|  |     return (status == FSE_OK || status == FSE_NOT_EXIST); | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								applications/infrared/infrared_remote.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								applications/infrared/infrared_remote.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #include "infrared_remote_button.h" | ||||||
|  | 
 | ||||||
|  | typedef struct InfraredRemote InfraredRemote; | ||||||
|  | 
 | ||||||
|  | InfraredRemote* infrared_remote_alloc(); | ||||||
|  | void infrared_remote_free(InfraredRemote* remote); | ||||||
|  | void infrared_remote_reset(InfraredRemote* remote); | ||||||
|  | 
 | ||||||
|  | void infrared_remote_set_name(InfraredRemote* remote, const char* name); | ||||||
|  | const char* infrared_remote_get_name(InfraredRemote* remote); | ||||||
|  | 
 | ||||||
|  | void infrared_remote_set_path(InfraredRemote* remote, const char* path); | ||||||
|  | const char* infrared_remote_get_path(InfraredRemote* remote); | ||||||
|  | 
 | ||||||
|  | size_t infrared_remote_get_button_count(InfraredRemote* remote); | ||||||
|  | InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index); | ||||||
|  | 
 | ||||||
|  | bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal); | ||||||
|  | bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index); | ||||||
|  | bool infrared_remote_delete_button(InfraredRemote* remote, size_t index); | ||||||
|  | 
 | ||||||
|  | bool infrared_remote_store(InfraredRemote* remote); | ||||||
|  | bool infrared_remote_load(InfraredRemote* remote, string_t path); | ||||||
|  | bool infrared_remote_remove(InfraredRemote* remote); | ||||||
							
								
								
									
										38
									
								
								applications/infrared/infrared_remote_button.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								applications/infrared/infrared_remote_button.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | #include "infrared_remote_button.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <m-string.h> | ||||||
|  | 
 | ||||||
|  | struct InfraredRemoteButton { | ||||||
|  |     string_t name; | ||||||
|  |     InfraredSignal* signal; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | InfraredRemoteButton* infrared_remote_button_alloc() { | ||||||
|  |     InfraredRemoteButton* button = malloc(sizeof(InfraredRemoteButton)); | ||||||
|  |     string_init(button->name); | ||||||
|  |     button->signal = infrared_signal_alloc(); | ||||||
|  |     return button; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_remote_button_free(InfraredRemoteButton* button) { | ||||||
|  |     string_clear(button->name); | ||||||
|  |     infrared_signal_free(button->signal); | ||||||
|  |     free(button); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name) { | ||||||
|  |     string_set_str(button->name, name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* infrared_remote_button_get_name(InfraredRemoteButton* button) { | ||||||
|  |     return string_get_cstr(button->name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal) { | ||||||
|  |     infrared_signal_set_signal(button->signal, signal); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button) { | ||||||
|  |     return button->signal; | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								applications/infrared/infrared_remote_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								applications/infrared/infrared_remote_button.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "infrared_signal.h" | ||||||
|  | 
 | ||||||
|  | typedef struct InfraredRemoteButton InfraredRemoteButton; | ||||||
|  | 
 | ||||||
|  | InfraredRemoteButton* infrared_remote_button_alloc(); | ||||||
|  | void infrared_remote_button_free(InfraredRemoteButton* button); | ||||||
|  | 
 | ||||||
|  | void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name); | ||||||
|  | const char* infrared_remote_button_get_name(InfraredRemoteButton* button); | ||||||
|  | 
 | ||||||
|  | void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal); | ||||||
|  | InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button); | ||||||
| @ -1,9 +0,0 @@ | |||||||
| #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; |  | ||||||
| } |  | ||||||
							
								
								
									
										264
									
								
								applications/infrared/infrared_signal.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								applications/infrared/infrared_signal.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,264 @@ | |||||||
|  | #include "infrared_signal.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <furi/check.h> | ||||||
|  | #include <infrared_worker.h> | ||||||
|  | #include <infrared_transmit.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "InfraredSignal" | ||||||
|  | 
 | ||||||
|  | struct InfraredSignal { | ||||||
|  |     bool is_raw; | ||||||
|  |     union { | ||||||
|  |         InfraredMessage message; | ||||||
|  |         InfraredRawSignal raw; | ||||||
|  |     } payload; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void infrared_signal_clear_timings(InfraredSignal* signal) { | ||||||
|  |     if(signal->is_raw) { | ||||||
|  |         free(signal->payload.raw.timings); | ||||||
|  |         signal->payload.raw.timings_size = 0; | ||||||
|  |         signal->payload.raw.timings = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool infrared_signal_is_message_valid(InfraredMessage* message) { | ||||||
|  |     if(!infrared_is_protocol_valid(message->protocol)) { | ||||||
|  |         FURI_LOG_E(TAG, "Unknown protocol"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     uint32_t address_length = infrared_get_protocol_address_length(message->protocol); | ||||||
|  |     uint32_t address_mask = (1UL << address_length) - 1; | ||||||
|  | 
 | ||||||
|  |     if(message->address != (message->address & address_mask)) { | ||||||
|  |         FURI_LOG_E( | ||||||
|  |             TAG, | ||||||
|  |             "Address is out of range (mask 0x%08lX): 0x%lX\r\n", | ||||||
|  |             address_mask, | ||||||
|  |             message->address); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     uint32_t command_length = infrared_get_protocol_command_length(message->protocol); | ||||||
|  |     uint32_t command_mask = (1UL << command_length) - 1; | ||||||
|  | 
 | ||||||
|  |     if(message->command != (message->command & command_mask)) { | ||||||
|  |         FURI_LOG_E( | ||||||
|  |             TAG, | ||||||
|  |             "Command is out of range (mask 0x%08lX): 0x%lX\r\n", | ||||||
|  |             command_mask, | ||||||
|  |             message->command); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) { | ||||||
|  |     if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) { | ||||||
|  |         FURI_LOG_E( | ||||||
|  |             TAG, | ||||||
|  |             "Frequency is out of range (%lX - %lX): %lX", | ||||||
|  |             INFRARED_MIN_FREQUENCY, | ||||||
|  |             INFRARED_MAX_FREQUENCY, | ||||||
|  |             raw->frequency); | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) { | ||||||
|  |         FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle); | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) { | ||||||
|  |         FURI_LOG_E( | ||||||
|  |             TAG, | ||||||
|  |             "Timings amount is out of range (0 - %lX): %lX", | ||||||
|  |             MAX_TIMINGS_AMOUNT, | ||||||
|  |             raw->timings_size); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) { | ||||||
|  |     const char* protocol_name = infrared_get_protocol_name(message->protocol); | ||||||
|  |     return flipper_format_write_string_cstr(ff, "type", "parsed") && | ||||||
|  |            flipper_format_write_string_cstr(ff, "protocol", protocol_name) && | ||||||
|  |            flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) && | ||||||
|  |            flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) { | ||||||
|  |     furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); | ||||||
|  |     return flipper_format_write_string_cstr(ff, "type", "raw") && | ||||||
|  |            flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) && | ||||||
|  |            flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) && | ||||||
|  |            flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { | ||||||
|  |     string_t buf; | ||||||
|  |     string_init(buf); | ||||||
|  |     bool success = false; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!flipper_format_read_string(ff, "protocol", buf)) break; | ||||||
|  | 
 | ||||||
|  |         InfraredMessage message; | ||||||
|  |         message.protocol = infrared_get_protocol_by_name(string_get_cstr(buf)); | ||||||
|  | 
 | ||||||
|  |         success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) && | ||||||
|  |                   flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) && | ||||||
|  |                   infrared_signal_is_message_valid(&message); | ||||||
|  | 
 | ||||||
|  |         if(!success) break; | ||||||
|  | 
 | ||||||
|  |         infrared_signal_set_message(signal, &message); | ||||||
|  |     } while(0); | ||||||
|  | 
 | ||||||
|  |     string_clear(buf); | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { | ||||||
|  |     uint32_t timings_size, frequency; | ||||||
|  |     float duty_cycle; | ||||||
|  | 
 | ||||||
|  |     bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) && | ||||||
|  |                    flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) && | ||||||
|  |                    flipper_format_get_value_count(ff, "data", &timings_size); | ||||||
|  | 
 | ||||||
|  |     if(!success || timings_size > MAX_TIMINGS_AMOUNT) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); | ||||||
|  |     success = flipper_format_read_uint32(ff, "data", timings, timings_size); | ||||||
|  | 
 | ||||||
|  |     if(success) { | ||||||
|  |         infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     free(timings); | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | InfraredSignal* infrared_signal_alloc() { | ||||||
|  |     InfraredSignal* signal = malloc(sizeof(InfraredSignal)); | ||||||
|  | 
 | ||||||
|  |     signal->is_raw = false; | ||||||
|  |     signal->payload.message.protocol = InfraredProtocolUnknown; | ||||||
|  | 
 | ||||||
|  |     return signal; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_signal_free(InfraredSignal* signal) { | ||||||
|  |     infrared_signal_clear_timings(signal); | ||||||
|  |     free(signal); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_signal_is_raw(InfraredSignal* signal) { | ||||||
|  |     return signal->is_raw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_signal_is_valid(InfraredSignal* signal) { | ||||||
|  |     return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) : | ||||||
|  |                             infrared_signal_is_message_valid(&signal->payload.message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) { | ||||||
|  |     if(other->is_raw) { | ||||||
|  |         const InfraredRawSignal* raw = &other->payload.raw; | ||||||
|  |         infrared_signal_set_raw_signal( | ||||||
|  |             signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle); | ||||||
|  |     } else { | ||||||
|  |         const InfraredMessage* message = &other->payload.message; | ||||||
|  |         infrared_signal_set_message(signal, message); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_signal_set_raw_signal( | ||||||
|  |     InfraredSignal* signal, | ||||||
|  |     const uint32_t* timings, | ||||||
|  |     size_t timings_size, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     float duty_cycle) { | ||||||
|  |     infrared_signal_clear_timings(signal); | ||||||
|  | 
 | ||||||
|  |     signal->is_raw = true; | ||||||
|  | 
 | ||||||
|  |     signal->payload.raw.timings_size = timings_size; | ||||||
|  |     signal->payload.raw.frequency = frequency; | ||||||
|  |     signal->payload.raw.duty_cycle = duty_cycle; | ||||||
|  | 
 | ||||||
|  |     signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t)); | ||||||
|  |     memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) { | ||||||
|  |     furi_assert(signal->is_raw); | ||||||
|  |     return &signal->payload.raw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) { | ||||||
|  |     infrared_signal_clear_timings(signal); | ||||||
|  | 
 | ||||||
|  |     signal->is_raw = false; | ||||||
|  |     signal->payload.message = *message; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) { | ||||||
|  |     furi_assert(!signal->is_raw); | ||||||
|  |     return &signal->payload.message; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) { | ||||||
|  |     if(!flipper_format_write_comment_cstr(ff, "") || | ||||||
|  |        !flipper_format_write_string_cstr(ff, "name", name)) { | ||||||
|  |         return false; | ||||||
|  |     } else if(signal->is_raw) { | ||||||
|  |         return infrared_signal_save_raw(&signal->payload.raw, ff); | ||||||
|  |     } else { | ||||||
|  |         return infrared_signal_save_message(&signal->payload.message, ff); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name) { | ||||||
|  |     string_t buf; | ||||||
|  |     string_init(buf); | ||||||
|  |     bool success = false; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!flipper_format_read_string(ff, "name", buf)) break; | ||||||
|  |         string_set(name, buf); | ||||||
|  |         if(!flipper_format_read_string(ff, "type", buf)) break; | ||||||
|  |         if(!string_cmp_str(buf, "raw")) { | ||||||
|  |             success = infrared_signal_read_raw(signal, ff); | ||||||
|  |         } else if(!string_cmp_str(buf, "parsed")) { | ||||||
|  |             success = infrared_signal_read_message(signal, ff); | ||||||
|  |         } else { | ||||||
|  |             FURI_LOG_E(TAG, "Unknown type of signal (allowed - raw/parsed) "); | ||||||
|  |         } | ||||||
|  |     } while(0); | ||||||
|  | 
 | ||||||
|  |     string_clear(buf); | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_signal_transmit(InfraredSignal* signal) { | ||||||
|  |     if(signal->is_raw) { | ||||||
|  |         InfraredRawSignal* raw_signal = &signal->payload.raw; | ||||||
|  |         infrared_send_raw_ext( | ||||||
|  |             raw_signal->timings, | ||||||
|  |             raw_signal->timings_size, | ||||||
|  |             true, | ||||||
|  |             raw_signal->frequency, | ||||||
|  |             raw_signal->duty_cycle); | ||||||
|  |     } else { | ||||||
|  |         InfraredMessage* message = &signal->payload.message; | ||||||
|  |         infrared_send(message, 1); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								applications/infrared/infrared_signal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								applications/infrared/infrared_signal.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #include <infrared.h> | ||||||
|  | #include <flipper_format/flipper_format.h> | ||||||
|  | 
 | ||||||
|  | typedef struct InfraredSignal InfraredSignal; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     size_t timings_size; | ||||||
|  |     uint32_t* timings; | ||||||
|  |     uint32_t frequency; | ||||||
|  |     float duty_cycle; | ||||||
|  | } InfraredRawSignal; | ||||||
|  | 
 | ||||||
|  | InfraredSignal* infrared_signal_alloc(); | ||||||
|  | void infrared_signal_free(InfraredSignal* signal); | ||||||
|  | 
 | ||||||
|  | bool infrared_signal_is_raw(InfraredSignal* signal); | ||||||
|  | bool infrared_signal_is_valid(InfraredSignal* signal); | ||||||
|  | 
 | ||||||
|  | void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other); | ||||||
|  | 
 | ||||||
|  | void infrared_signal_set_raw_signal( | ||||||
|  |     InfraredSignal* signal, | ||||||
|  |     const uint32_t* timings, | ||||||
|  |     size_t timings_size, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     float duty_cycle); | ||||||
|  | InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal); | ||||||
|  | 
 | ||||||
|  | void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message); | ||||||
|  | InfraredMessage* infrared_signal_get_message(InfraredSignal* signal); | ||||||
|  | 
 | ||||||
|  | bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name); | ||||||
|  | bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name); | ||||||
|  | 
 | ||||||
|  | void infrared_signal_transmit(InfraredSignal* signal); | ||||||
| @ -1,305 +0,0 @@ | |||||||
| /**
 |  | ||||||
|  * @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,73 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| #include "gui/modules/dialog_ex.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<InfraredApp*>(context); |  | ||||||
|     InfraredAppEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = InfraredAppEvent::Type::DialogExSelected; |  | ||||||
|     event.payload.dialog_ex_result = result; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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()) { |  | ||||||
|         dialog_ex_set_header(dialog_ex, "Exit to Infrared menu?", 64, 0, AlignCenter, AlignTop); |  | ||||||
|     } else { |  | ||||||
|         dialog_ex_set_header(dialog_ex, "Exit to remote menu?", 64, 0, AlignCenter, AlignTop); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     dialog_ex_set_text( |  | ||||||
|         dialog_ex, "All unsaved data\nwill be lost", 64, 31, AlignCenter, AlignCenter); |  | ||||||
|     dialog_ex_set_icon(dialog_ex, 0, 0, NULL); |  | ||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Exit"); |  | ||||||
|     dialog_ex_set_center_button_text(dialog_ex, nullptr); |  | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "Stay"); |  | ||||||
|     dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); |  | ||||||
|     dialog_ex_set_context(dialog_ex, app); |  | ||||||
| 
 |  | ||||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppSceneAskBack::on_event(InfraredApp* app, InfraredAppEvent* event) { |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     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({InfraredApp::Scene::Start}); |  | ||||||
|             } else { |  | ||||||
|                 app->search_and_switch_to_previous_scene( |  | ||||||
|                     {InfraredApp::Scene::Edit, InfraredApp::Scene::Remote}); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         case DialogExResultCenter: |  | ||||||
|             furi_assert(0); |  | ||||||
|             break; |  | ||||||
|         case DialogExResultRight: |  | ||||||
|             app->switch_to_previous_scene(); |  | ||||||
|             consumed = true; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(event->type == InfraredAppEvent::Type::Back) { |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppSceneAskBack::on_exit(InfraredApp*) { |  | ||||||
| } |  | ||||||
| @ -1,79 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| #include "gui/modules/submenu.h" |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     SubmenuIndexAddKey, |  | ||||||
|     SubmenuIndexRenameKey, |  | ||||||
|     SubmenuIndexDeleteKey, |  | ||||||
|     SubmenuIndexRenameRemote, |  | ||||||
|     SubmenuIndexDeleteRemote, |  | ||||||
| } SubmenuIndex; |  | ||||||
| 
 |  | ||||||
| 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 InfraredAppSceneEdit::on_enter(InfraredApp* app) { |  | ||||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); |  | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |  | ||||||
| 
 |  | ||||||
|     submenu_add_item(submenu, "Add Button", SubmenuIndexAddKey, submenu_callback, app); |  | ||||||
|     submenu_add_item(submenu, "Rename Button", SubmenuIndexRenameKey, submenu_callback, app); |  | ||||||
|     submenu_add_item(submenu, "Delete Button", SubmenuIndexDeleteKey, submenu_callback, app); |  | ||||||
|     submenu_add_item(submenu, "Rename Remote", SubmenuIndexRenameRemote, submenu_callback, app); |  | ||||||
|     submenu_add_item(submenu, "Delete Remote", SubmenuIndexDeleteRemote, submenu_callback, app); |  | ||||||
|     submenu_set_selected_item(submenu, submenu_item_selected); |  | ||||||
|     submenu_item_selected = 0; |  | ||||||
| 
 |  | ||||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppSceneEdit::on_event(InfraredApp* app, InfraredAppEvent* event) { |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     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(InfraredApp::Scene::Learn); |  | ||||||
|             break; |  | ||||||
|         case SubmenuIndexRenameKey: |  | ||||||
|             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(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(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(InfraredApp::EditAction::Delete); |  | ||||||
|             app->set_edit_element(InfraredApp::EditElement::Remote); |  | ||||||
|             app->switch_to_next_scene(InfraredApp::Scene::EditDelete); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppSceneEdit::on_exit(InfraredApp* app) { |  | ||||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); |  | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |  | ||||||
| 
 |  | ||||||
|     submenu_reset(submenu); |  | ||||||
| } |  | ||||||
| @ -1,100 +0,0 @@ | |||||||
| #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<InfraredApp*>(context); |  | ||||||
|     InfraredAppEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = InfraredAppEvent::Type::DialogExSelected; |  | ||||||
|     event.payload.dialog_ex_result = result; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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() == 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()) { |  | ||||||
|             auto message = &signal.get_message(); |  | ||||||
|             app->set_text_store( |  | ||||||
|                 0, |  | ||||||
|                 "%s\n%s\nA=0x%0*lX C=0x%0*lX", |  | ||||||
|                 remote_manager->get_button_name(app->get_current_button()).c_str(), |  | ||||||
|                 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); |  | ||||||
|         } else { |  | ||||||
|             app->set_text_store( |  | ||||||
|                 0, |  | ||||||
|                 "%s\nRAW\n%ld samples", |  | ||||||
|                 remote_manager->get_button_name(app->get_current_button()).c_str(), |  | ||||||
|                 signal.get_raw_signal().timings_cnt); |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 0, AlignCenter, AlignTop); |  | ||||||
|         app->set_text_store( |  | ||||||
|             0, |  | ||||||
|             "%s\n with %lu buttons", |  | ||||||
|             remote_manager->get_remote_name().c_str(), |  | ||||||
|             remote_manager->get_number_of_buttons()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 31, AlignCenter, AlignCenter); |  | ||||||
|     dialog_ex_set_icon(dialog_ex, 0, 0, NULL); |  | ||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Cancel"); |  | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "Delete"); |  | ||||||
|     dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); |  | ||||||
|     dialog_ex_set_context(dialog_ex, app); |  | ||||||
| 
 |  | ||||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppSceneEditDelete::on_event(InfraredApp* app, InfraredAppEvent* event) { |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event->type == InfraredAppEvent::Type::DialogExSelected) { |  | ||||||
|         switch(event->payload.dialog_ex_result) { |  | ||||||
|         case DialogExResultLeft: |  | ||||||
|             app->switch_to_previous_scene(); |  | ||||||
|             break; |  | ||||||
|         case DialogExResultCenter: |  | ||||||
|             furi_assert(0); |  | ||||||
|             break; |  | ||||||
|         case DialogExResultRight: { |  | ||||||
|             auto remote_manager = app->get_remote_manager(); |  | ||||||
|             bool result = false; |  | ||||||
|             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(InfraredApp::ButtonNA); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if(!result) { |  | ||||||
|                 app->search_and_switch_to_previous_scene( |  | ||||||
|                     {InfraredApp::Scene::RemoteList, InfraredApp::Scene::Start}); |  | ||||||
|             } else { |  | ||||||
|                 app->switch_to_next_scene(InfraredApp::Scene::EditDeleteDone); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppSceneEditDelete::on_exit(InfraredApp*) { |  | ||||||
| } |  | ||||||
| @ -1,38 +0,0 @@ | |||||||
| #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); |  | ||||||
| } |  | ||||||
| @ -1,58 +0,0 @@ | |||||||
| #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 Button:" : |  | ||||||
|                              "Delete Button:"; |  | ||||||
|     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,83 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| #include "m-string.h" |  | ||||||
| #include "toolbox/path.h" |  | ||||||
| 
 |  | ||||||
| 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() == 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 = 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; |  | ||||||
|         text_input_set_header_text(text_input, "Name the button"); |  | ||||||
|     } 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 = InfraredAppRemoteManager::max_remote_name_length; |  | ||||||
|         text_input_set_header_text(text_input, "Name the remote"); |  | ||||||
| 
 |  | ||||||
|         string_t folder_path; |  | ||||||
|         string_init(folder_path); |  | ||||||
| 
 |  | ||||||
|         if(string_end_with_str_p(app->file_path, InfraredApp::infrared_extension)) { |  | ||||||
|             path_extract_dirname(string_get_cstr(app->file_path), folder_path); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( |  | ||||||
|             string_get_cstr(folder_path), app->infrared_extension, remote_name.c_str()); |  | ||||||
|         text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); |  | ||||||
| 
 |  | ||||||
|         string_clear(folder_path); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     text_input_set_result_callback( |  | ||||||
|         text_input, |  | ||||||
|         InfraredApp::text_input_callback, |  | ||||||
|         app, |  | ||||||
|         app->get_text_store(0), |  | ||||||
|         enter_name_length, |  | ||||||
|         false); |  | ||||||
| 
 |  | ||||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::TextInput); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppSceneEditRename::on_event(InfraredApp* app, InfraredAppEvent* event) { |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event->type == InfraredAppEvent::Type::TextEditDone) { |  | ||||||
|         auto remote_manager = app->get_remote_manager(); |  | ||||||
|         bool result = false; |  | ||||||
|         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(InfraredApp::ButtonNA); |  | ||||||
|         } else { |  | ||||||
|             result = remote_manager->rename_remote(app->get_text_store(0)); |  | ||||||
|         } |  | ||||||
|         if(!result) { |  | ||||||
|             app->search_and_switch_to_previous_scene( |  | ||||||
|                 {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); |  | ||||||
|         } else { |  | ||||||
|             app->switch_to_next_scene_without_saving(InfraredApp::Scene::EditRenameDone); |  | ||||||
|         } |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
|     text_input_set_validator(text_input, NULL, NULL); |  | ||||||
| 
 |  | ||||||
|     if(validator_context != NULL) validator_is_file_free((ValidatorIsFile*)validator_context); |  | ||||||
| } |  | ||||||
| @ -1,30 +0,0 @@ | |||||||
| #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*) { |  | ||||||
| } |  | ||||||
| @ -1,76 +0,0 @@ | |||||||
| #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_header(popup, NULL, 0, 0, AlignCenter, AlignCenter); |  | ||||||
|     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_blink_read(); |  | ||||||
|         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,41 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
|     DOLPHIN_DEED(DolphinDeedIrSave); |  | ||||||
| 
 |  | ||||||
|     if(app->get_learn_new_remote()) { |  | ||||||
|         popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop); |  | ||||||
|     } else { |  | ||||||
|         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 InfraredAppSceneLearnDone::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 InfraredAppSceneLearnDone::on_exit(InfraredApp* app) { |  | ||||||
|     app->set_learn_new_remote(false); |  | ||||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); |  | ||||||
|     Popup* popup = view_manager->get_popup(); |  | ||||||
|     popup_set_header(popup, nullptr, 0, 0, AlignLeft, AlignTop); |  | ||||||
| } |  | ||||||
| @ -1,60 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| #include "gui/modules/text_input.h" |  | ||||||
| 
 |  | ||||||
| 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(); |  | ||||||
| 
 |  | ||||||
|     if(!signal.is_raw()) { |  | ||||||
|         auto message = &signal.get_message(); |  | ||||||
|         app->set_text_store( |  | ||||||
|             0, |  | ||||||
|             "%.4s_%0*lX", |  | ||||||
|             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(); |  | ||||||
|         app->set_text_store(0, "RAW_%d", raw_signal.timings_cnt); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     text_input_set_header_text(text_input, "Name the button"); |  | ||||||
|     text_input_set_result_callback( |  | ||||||
|         text_input, |  | ||||||
|         InfraredApp::text_input_callback, |  | ||||||
|         app, |  | ||||||
|         app->get_text_store(0), |  | ||||||
|         InfraredAppRemoteManager::max_button_name_length, |  | ||||||
|         true); |  | ||||||
| 
 |  | ||||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::TextInput); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppSceneLearnEnterName::on_event(InfraredApp* app, InfraredAppEvent* event) { |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event->type == InfraredAppEvent::Type::TextEditDone) { |  | ||||||
|         auto remote_manager = app->get_remote_manager(); |  | ||||||
|         bool result = false; |  | ||||||
|         if(app->get_learn_new_remote()) { |  | ||||||
|             result = remote_manager->add_remote_with_button( |  | ||||||
|                 app->get_text_store(0), app->get_received_signal()); |  | ||||||
|         } else { |  | ||||||
|             result = |  | ||||||
|                 remote_manager->add_button(app->get_text_store(0), app->get_received_signal()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(!result) { |  | ||||||
|             app->search_and_switch_to_previous_scene( |  | ||||||
|                 {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); |  | ||||||
|         } else { |  | ||||||
|             app->switch_to_next_scene_without_saving(InfraredApp::Scene::LearnDone); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppSceneLearnEnterName::on_exit(InfraredApp*) { |  | ||||||
| } |  | ||||||
| @ -1,142 +0,0 @@ | |||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| #include <memory> |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| #include "../infrared_app.h" |  | ||||||
| #include "infrared.h" |  | ||||||
| 
 |  | ||||||
| static void dialog_result_callback(DialogExResult result, void* context) { |  | ||||||
|     auto app = static_cast<InfraredApp*>(context); |  | ||||||
|     InfraredAppEvent event; |  | ||||||
| 
 |  | ||||||
|     event.type = InfraredAppEvent::Type::DialogExSelected; |  | ||||||
|     event.payload.dialog_ex_result = result; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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(); |  | ||||||
| 
 |  | ||||||
|     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(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", infrared_get_protocol_name(message->protocol)); |  | ||||||
|         app->set_text_store( |  | ||||||
|             1, |  | ||||||
|             "A: 0x%0*lX\nC: 0x%0*lX\n", |  | ||||||
|             adr_digits, |  | ||||||
|             message->address, |  | ||||||
|             cmd_digits, |  | ||||||
|             message->command); |  | ||||||
| 
 |  | ||||||
|         dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 7, AlignCenter, AlignCenter); |  | ||||||
|         dialog_ex_set_text( |  | ||||||
|             dialog_ex, app->get_text_store(1), label_x_offset, 34, AlignLeft, AlignCenter); |  | ||||||
|     } else { |  | ||||||
|         dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter); |  | ||||||
|         app->set_text_store(0, "%d samples", signal.get_raw_signal().timings_cnt); |  | ||||||
|         dialog_ex_set_text(dialog_ex, app->get_text_store(0), 75, 23, AlignLeft, AlignTop); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); |  | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "Save"); |  | ||||||
|     dialog_ex_set_center_button_text(dialog_ex, "Send"); |  | ||||||
|     dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63); |  | ||||||
|     dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); |  | ||||||
|     dialog_ex_set_context(dialog_ex, app); |  | ||||||
|     dialog_ex_enable_extended_events(dialog_ex); |  | ||||||
| 
 |  | ||||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppSceneLearnSuccess::on_event(InfraredApp* app, InfraredAppEvent* event) { |  | ||||||
|     bool consumed = false; |  | ||||||
|     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 == InfraredAppEvent::Type::DialogExSelected) { |  | ||||||
|         switch(event->payload.dialog_ex_result) { |  | ||||||
|         case DialogExResultLeft: |  | ||||||
|             consumed = true; |  | ||||||
|             if(!button_pressed) { |  | ||||||
|                 app->switch_to_next_scene_without_saving(InfraredApp::Scene::Learn); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         case DialogExResultRight: { |  | ||||||
|             consumed = true; |  | ||||||
|             if(!button_pressed) { |  | ||||||
|                 app->switch_to_next_scene(InfraredApp::Scene::LearnEnterName); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         case DialogExPressCenter: |  | ||||||
|             if(!button_pressed) { |  | ||||||
|                 button_pressed = true; |  | ||||||
| 
 |  | ||||||
|                 auto signal = app->get_received_signal(); |  | ||||||
|                 if(signal.is_raw()) { |  | ||||||
|                     infrared_worker_set_raw_signal( |  | ||||||
|                         app->get_infrared_worker(), |  | ||||||
|                         signal.get_raw_signal().timings, |  | ||||||
|                         signal.get_raw_signal().timings_cnt); |  | ||||||
|                 } else { |  | ||||||
|                     infrared_worker_set_decoded_signal( |  | ||||||
|                         app->get_infrared_worker(), &signal.get_message()); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 infrared_worker_tx_start(app->get_infrared_worker()); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         case DialogExReleaseCenter: |  | ||||||
|             if(button_pressed) { |  | ||||||
|                 button_pressed = false; |  | ||||||
|                 infrared_worker_tx_stop(app->get_infrared_worker()); |  | ||||||
|                 app->notify_green_off(); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(event->type == InfraredAppEvent::Type::Back) { |  | ||||||
|         if(!button_pressed) { |  | ||||||
|             app->switch_to_next_scene(InfraredApp::Scene::AskBack); |  | ||||||
|         } |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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(); |  | ||||||
|     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,131 +0,0 @@ | |||||||
| #include <gui/modules/button_menu.h> |  | ||||||
| #include <input/input.h> |  | ||||||
| #include <infrared_worker.h> |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| #include "../infrared_app.h" |  | ||||||
| #include "../infrared_app_view_manager.h" |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     ButtonIndexPlus = -2, |  | ||||||
|     ButtonIndexEdit = -1, |  | ||||||
|     ButtonIndexNA = 0, |  | ||||||
| } ButtonIndex; |  | ||||||
| 
 |  | ||||||
| static void button_menu_callback(void* context, int32_t index, InputType type) { |  | ||||||
|     InfraredApp* app = static_cast<InfraredApp*>(context); |  | ||||||
|     InfraredAppEvent event; |  | ||||||
| 
 |  | ||||||
|     if(type == InputTypePress) { |  | ||||||
|         event.type = InfraredAppEvent::Type::MenuSelectedPress; |  | ||||||
|     } else if(type == InputTypeRelease) { |  | ||||||
|         event.type = InfraredAppEvent::Type::MenuSelectedRelease; |  | ||||||
|     } else if(type == InputTypeShort) { |  | ||||||
|         event.type = InfraredAppEvent::Type::MenuSelected; |  | ||||||
|     } else { |  | ||||||
|         furi_assert(0); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     event.payload.menu_index = index; |  | ||||||
| 
 |  | ||||||
|     app->get_view_manager()->send_event(&event); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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; |  | ||||||
| 
 |  | ||||||
|     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; |  | ||||||
|     for(auto& name : buttons_names) { |  | ||||||
|         button_menu_add_item( |  | ||||||
|             button_menu, name.c_str(), i++, button_menu_callback, ButtonMenuItemTypeCommon, app); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     button_menu_add_item( |  | ||||||
|         button_menu, "+", ButtonIndexPlus, button_menu_callback, ButtonMenuItemTypeControl, app); |  | ||||||
|     button_menu_add_item( |  | ||||||
|         button_menu, "Edit", ButtonIndexEdit, button_menu_callback, ButtonMenuItemTypeControl, app); |  | ||||||
| 
 |  | ||||||
|     app->set_text_store(0, "%s", remote_manager->get_remote_name().c_str()); |  | ||||||
|     button_menu_set_header(button_menu, app->get_text_store(0)); |  | ||||||
|     if(buttonmenu_item_selected != ButtonIndexNA) { |  | ||||||
|         button_menu_set_selected_item(button_menu, buttonmenu_item_selected); |  | ||||||
|         buttonmenu_item_selected = ButtonIndexNA; |  | ||||||
|     } |  | ||||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::ButtonMenu); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppSceneRemote::on_event(InfraredApp* app, InfraredAppEvent* event) { |  | ||||||
|     bool consumed = true; |  | ||||||
| 
 |  | ||||||
|     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 == InfraredAppEvent::Type::MenuSelected); |  | ||||||
|             buttonmenu_item_selected = event->payload.menu_index; |  | ||||||
|             app->set_learn_new_remote(false); |  | ||||||
|             app->switch_to_next_scene(InfraredApp::Scene::Learn); |  | ||||||
|             break; |  | ||||||
|         case ButtonIndexEdit: |  | ||||||
|             furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); |  | ||||||
|             buttonmenu_item_selected = event->payload.menu_index; |  | ||||||
|             app->switch_to_next_scene(InfraredApp::Scene::Edit); |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             furi_assert(event->type != InfraredAppEvent::Type::MenuSelected); |  | ||||||
|             bool pressed = (event->type == InfraredAppEvent::Type::MenuSelectedPress); |  | ||||||
| 
 |  | ||||||
|             if(pressed && !button_pressed) { |  | ||||||
|                 button_pressed = true; |  | ||||||
| 
 |  | ||||||
|                 auto button_signal = |  | ||||||
|                     app->get_remote_manager()->get_button_data(event->payload.menu_index); |  | ||||||
|                 if(button_signal.is_raw()) { |  | ||||||
|                     infrared_worker_set_raw_signal( |  | ||||||
|                         app->get_infrared_worker(), |  | ||||||
|                         button_signal.get_raw_signal().timings, |  | ||||||
|                         button_signal.get_raw_signal().timings_cnt); |  | ||||||
|                 } else { |  | ||||||
|                     infrared_worker_set_decoded_signal( |  | ||||||
|                         app->get_infrared_worker(), &button_signal.get_message()); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 DOLPHIN_DEED(DolphinDeedIrSend); |  | ||||||
|                 infrared_worker_tx_start(app->get_infrared_worker()); |  | ||||||
|             } else if(!pressed && button_pressed) { |  | ||||||
|                 button_pressed = false; |  | ||||||
|                 infrared_worker_tx_stop(app->get_infrared_worker()); |  | ||||||
|                 app->notify_green_off(); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } else if(event->type == InfraredAppEvent::Type::Back) { |  | ||||||
|         if(!button_pressed) { |  | ||||||
|             app->search_and_switch_to_previous_scene( |  | ||||||
|                 {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         consumed = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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,45 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| #include "assets_icons.h" |  | ||||||
| #include "infrared/infrared_app_event.h" |  | ||||||
| #include <text_store.h> |  | ||||||
| 
 |  | ||||||
| void InfraredAppSceneRemoteList::on_enter(InfraredApp* app) { |  | ||||||
|     furi_assert(app); |  | ||||||
| 
 |  | ||||||
|     bool result = false; |  | ||||||
|     bool file_select_result; |  | ||||||
|     auto remote_manager = app->get_remote_manager(); |  | ||||||
|     DialogsApp* dialogs = app->get_dialogs(); |  | ||||||
| 
 |  | ||||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); |  | ||||||
|     ButtonMenu* button_menu = view_manager->get_button_menu(); |  | ||||||
|     button_menu_reset(button_menu); |  | ||||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::ButtonMenu); |  | ||||||
| 
 |  | ||||||
|     file_select_result = dialog_file_browser_show( |  | ||||||
|         dialogs, |  | ||||||
|         app->file_path, |  | ||||||
|         app->file_path, |  | ||||||
|         InfraredApp::infrared_extension, |  | ||||||
|         true, |  | ||||||
|         &I_ir_10px, |  | ||||||
|         true); |  | ||||||
| 
 |  | ||||||
|     if(file_select_result) { |  | ||||||
|         if(remote_manager->load(app->file_path)) { |  | ||||||
|             app->switch_to_next_scene(InfraredApp::Scene::Remote); |  | ||||||
|             result = true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(!result) { |  | ||||||
|         app->switch_to_previous_scene(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppSceneRemoteList::on_event(InfraredApp*, InfraredAppEvent*) { |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppSceneRemoteList::on_exit(InfraredApp*) { |  | ||||||
| } |  | ||||||
| @ -1,68 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     SubmenuIndexUniversalLibrary, |  | ||||||
|     SubmenuIndexLearnNewRemote, |  | ||||||
|     SubmenuIndexSavedRemotes, |  | ||||||
| } SubmenuIndex; |  | ||||||
| 
 |  | ||||||
| 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 InfraredAppSceneStart::on_enter(InfraredApp* app) { |  | ||||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); |  | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |  | ||||||
| 
 |  | ||||||
|     submenu_add_item( |  | ||||||
|         submenu, "Universal Library", SubmenuIndexUniversalLibrary, submenu_callback, app); |  | ||||||
|     submenu_add_item( |  | ||||||
|         submenu, "Learn New Remote", SubmenuIndexLearnNewRemote, submenu_callback, app); |  | ||||||
|     submenu_add_item(submenu, "Saved Remotes", SubmenuIndexSavedRemotes, submenu_callback, app); |  | ||||||
|     submenu_set_selected_item(submenu, submenu_item_selected); |  | ||||||
| 
 |  | ||||||
|     string_set_str(app->file_path, InfraredApp::infrared_directory); |  | ||||||
|     submenu_item_selected = 0; |  | ||||||
| 
 |  | ||||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppSceneStart::on_event(InfraredApp* app, InfraredAppEvent* event) { |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     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(InfraredApp::Scene::Universal); |  | ||||||
|             break; |  | ||||||
|         case SubmenuIndexLearnNewRemote: |  | ||||||
|             app->set_learn_new_remote(true); |  | ||||||
|             app->switch_to_next_scene(InfraredApp::Scene::Learn); |  | ||||||
|             break; |  | ||||||
|         case SubmenuIndexSavedRemotes: |  | ||||||
|             app->switch_to_next_scene(InfraredApp::Scene::RemoteList); |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             furi_assert(0); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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(); |  | ||||||
|     submenu_reset(submenu); |  | ||||||
| } |  | ||||||
| @ -1,57 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     SubmenuIndexUniversalTV, |  | ||||||
|     SubmenuIndexUniversalAudio, |  | ||||||
|     SubmenuIndexUniversalAirConditioner, |  | ||||||
| } SubmenuIndex; |  | ||||||
| 
 |  | ||||||
| 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 InfraredAppSceneUniversal::on_enter(InfraredApp* app) { |  | ||||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); |  | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |  | ||||||
| 
 |  | ||||||
|     submenu_add_item(submenu, "TVs", SubmenuIndexUniversalTV, submenu_callback, app); |  | ||||||
|     submenu_set_selected_item(submenu, submenu_item_selected); |  | ||||||
|     submenu_item_selected = 0; |  | ||||||
| 
 |  | ||||||
|     view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool InfraredAppSceneUniversal::on_event(InfraredApp* app, InfraredAppEvent* event) { |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     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(InfraredApp::Scene::UniversalTV); |  | ||||||
|             break; |  | ||||||
|         case SubmenuIndexUniversalAudio: |  | ||||||
|             //            app->switch_to_next_scene(InfraredApp::Scene::UniversalAudio);
 |  | ||||||
|             break; |  | ||||||
|         case SubmenuIndexUniversalAirConditioner: |  | ||||||
|             //            app->switch_to_next_scene(InfraredApp::Scene::UniversalAirConditioner);
 |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void InfraredAppSceneUniversal::on_exit(InfraredApp* app) { |  | ||||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); |  | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |  | ||||||
| 
 |  | ||||||
|     submenu_reset(submenu); |  | ||||||
| } |  | ||||||
| @ -1,107 +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 "../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 = { |  | ||||||
|         .payload = {.dummy = 0}, |  | ||||||
|         .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(); |  | ||||||
|             app->notify_blink_send(); |  | ||||||
|             InfraredAppEvent tick_event = { |  | ||||||
|                 .payload = {.dummy = 0}, |  | ||||||
|                 .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); |  | ||||||
|                 app->notify_blink_send(); |  | ||||||
|             } 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,123 +0,0 @@ | |||||||
| #include <stdint.h> |  | ||||||
| #include <gui/modules/loading.h> |  | ||||||
| #include <gui/view_stack.h> |  | ||||||
| #include "infrared/scene/infrared_app_scene.h" |  | ||||||
| #include "infrared/infrared_app.h" |  | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
| 
 |  | ||||||
|     int i = 0; |  | ||||||
|     button_panel_add_item( |  | ||||||
|         button_panel, |  | ||||||
|         i, |  | ||||||
|         0, |  | ||||||
|         0, |  | ||||||
|         3, |  | ||||||
|         19, |  | ||||||
|         &I_Power_25x27, |  | ||||||
|         &I_Power_hvr_25x27, |  | ||||||
|         infrared_app_item_callback, |  | ||||||
|         app); |  | ||||||
|     brute_force.add_record(i, "POWER"); |  | ||||||
|     ++i; |  | ||||||
|     button_panel_add_item( |  | ||||||
|         button_panel, |  | ||||||
|         i, |  | ||||||
|         1, |  | ||||||
|         0, |  | ||||||
|         36, |  | ||||||
|         19, |  | ||||||
|         &I_Mute_25x27, |  | ||||||
|         &I_Mute_hvr_25x27, |  | ||||||
|         infrared_app_item_callback, |  | ||||||
|         app); |  | ||||||
|     brute_force.add_record(i, "MUTE"); |  | ||||||
|     ++i; |  | ||||||
|     button_panel_add_item( |  | ||||||
|         button_panel, |  | ||||||
|         i, |  | ||||||
|         0, |  | ||||||
|         1, |  | ||||||
|         3, |  | ||||||
|         66, |  | ||||||
|         &I_Vol_up_25x27, |  | ||||||
|         &I_Vol_up_hvr_25x27, |  | ||||||
|         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, |  | ||||||
|         infrared_app_item_callback, |  | ||||||
|         app); |  | ||||||
|     brute_force.add_record(i, "CH+"); |  | ||||||
|     ++i; |  | ||||||
|     button_panel_add_item( |  | ||||||
|         button_panel, |  | ||||||
|         i, |  | ||||||
|         0, |  | ||||||
|         2, |  | ||||||
|         3, |  | ||||||
|         98, |  | ||||||
|         &I_Vol_down_25x27, |  | ||||||
|         &I_Vol_down_hvr_25x27, |  | ||||||
|         infrared_app_item_callback, |  | ||||||
|         app); |  | ||||||
|     brute_force.add_record(i, "VOL-"); |  | ||||||
|     ++i; |  | ||||||
|     button_panel_add_item( |  | ||||||
|         button_panel, |  | ||||||
|         i, |  | ||||||
|         1, |  | ||||||
|         2, |  | ||||||
|         36, |  | ||||||
|         98, |  | ||||||
|         &I_Down_25x27, |  | ||||||
|         &I_Down_hvr_25x27, |  | ||||||
|         infrared_app_item_callback, |  | ||||||
|         app); |  | ||||||
|     brute_force.add_record(i, "CH-"); |  | ||||||
| 
 |  | ||||||
|     button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote"); |  | ||||||
|     button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol"); |  | ||||||
|     button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch"); |  | ||||||
| 
 |  | ||||||
|     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(); |  | ||||||
|     view_stack_add_view(stack_view, loading_get_view(loading_view)); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Problem: Update events are not handled in Loading View, because: |  | ||||||
|      * 1) Timer task has least prio |  | ||||||
|      * 2) Storage service uses drivers that capture whole CPU time |  | ||||||
|      *      to handle SD communication |  | ||||||
|      * |  | ||||||
|      * Ugly workaround, but it works for current situation: |  | ||||||
|      * raise timer task prio for DB scanning period. |  | ||||||
|      */ |  | ||||||
|     TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); |  | ||||||
|     TaskHandle_t storage_task = xTaskGetHandle("StorageSrv"); |  | ||||||
|     uint32_t timer_prio = uxTaskPriorityGet(timer_task); |  | ||||||
|     uint32_t storage_prio = uxTaskPriorityGet(storage_task); |  | ||||||
|     vTaskPrioritySet(timer_task, storage_prio + 1); |  | ||||||
|     bool result = brute_force.calculate_messages(); |  | ||||||
|     vTaskPrioritySet(timer_task, timer_prio); |  | ||||||
| 
 |  | ||||||
|     view_stack_remove_view(stack_view, loading_get_view(loading_view)); |  | ||||||
| 
 |  | ||||||
|     if(!result) { |  | ||||||
|         app->switch_to_previous_scene(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -0,0 +1,92 @@ | |||||||
|  | #include "../../infrared_i.h" | ||||||
|  | 
 | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | void infrared_scene_universal_common_item_callback(void* context, uint32_t index) { | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index); | ||||||
|  |     view_dispatcher_send_custom_event(infrared->view_dispatcher, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void infrared_scene_universal_common_progress_back_callback(void* context) { | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeBackPressed, -1); | ||||||
|  |     view_dispatcher_send_custom_event(infrared->view_dispatcher, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint32_t record_count) { | ||||||
|  |     ViewStack* view_stack = infrared->view_stack; | ||||||
|  |     InfraredProgressView* progress = infrared->progress; | ||||||
|  |     infrared_progress_view_set_progress_total(progress, record_count); | ||||||
|  |     infrared_progress_view_set_back_callback( | ||||||
|  |         progress, infrared_scene_universal_common_progress_back_callback, infrared); | ||||||
|  |     view_stack_add_view(view_stack, infrared_progress_view_get_view(progress)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void infrared_scene_universal_common_hide_popup(Infrared* infrared) { | ||||||
|  |     ViewStack* view_stack = infrared->view_stack; | ||||||
|  |     InfraredProgressView* progress = infrared->progress; | ||||||
|  |     view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_scene_universal_common_on_enter(void* context) { | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     SceneManager* scene_manager = infrared->scene_manager; | ||||||
|  |     InfraredBruteForce* brute_force = infrared->brute_force; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(infrared_brute_force_is_started(brute_force)) { | ||||||
|  |         if(event.type == SceneManagerEventTypeTick) { | ||||||
|  |             infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkSend); | ||||||
|  |             bool success = infrared_brute_force_send_next(brute_force); | ||||||
|  |             if(success) { | ||||||
|  |                 success = infrared_progress_view_increase_progress(infrared->progress); | ||||||
|  |             } | ||||||
|  |             if(!success) { | ||||||
|  |                 infrared_brute_force_stop(brute_force); | ||||||
|  |                 infrared_scene_universal_common_hide_popup(infrared); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |             if(infrared_custom_event_get_type(event.event) == InfraredCustomEventTypeBackPressed) { | ||||||
|  |                 infrared_brute_force_stop(brute_force); | ||||||
|  |                 infrared_scene_universal_common_hide_popup(infrared); | ||||||
|  |                 consumed = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         if(event.type == SceneManagerEventTypeBack) { | ||||||
|  |             scene_manager_previous_scene(scene_manager); | ||||||
|  |             consumed = true; | ||||||
|  |         } else if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |             if(infrared_custom_event_get_type(event.event) == | ||||||
|  |                InfraredCustomEventTypeButtonSelected) { | ||||||
|  |                 uint32_t record_count; | ||||||
|  |                 if(infrared_brute_force_start( | ||||||
|  |                        brute_force, infrared_custom_event_get_value(event.event), &record_count)) { | ||||||
|  |                     DOLPHIN_DEED(DolphinDeedIrBruteForce); | ||||||
|  |                     infrared_scene_universal_common_show_popup(infrared, record_count); | ||||||
|  |                     infrared_play_notification_message( | ||||||
|  |                         infrared, InfraredNotificationMessageBlinkSend); | ||||||
|  |                 } else { | ||||||
|  |                     scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); | ||||||
|  |                 } | ||||||
|  |                 consumed = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void infrared_scene_universal_common_on_exit(void* context) { | ||||||
|  |     Infrared* infrared = context; | ||||||
|  |     ButtonPanel* button_panel = infrared->button_panel; | ||||||
|  |     view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel)); | ||||||
|  |     button_panel_reset(button_panel); | ||||||
|  | } | ||||||
| @ -0,0 +1,8 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | void infrared_scene_universal_common_on_enter(void* context); | ||||||
|  | bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event); | ||||||
|  | void infrared_scene_universal_common_on_exit(void* context); | ||||||
|  | void infrared_scene_universal_common_item_callback(void* context, uint32_t index); | ||||||
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