Merge branch 'release-candidate' into release
This commit is contained in:
		
						commit
						ce74a35066
					
				
							
								
								
									
										2
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -11,7 +11,7 @@ on: | |||||||
| 
 | 
 | ||||||
| env: | env: | ||||||
|   TARGETS: f6 f7 |   TARGETS: f6 f7 | ||||||
|   DEFAULT_TARGET: f6 |   DEFAULT_TARGET: f7 | ||||||
| 
 | 
 | ||||||
| jobs: | jobs: | ||||||
|   build: |   build: | ||||||
|  | |||||||
							
								
								
									
										9
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @ -7,3 +7,12 @@ | |||||||
| [submodule "lib/littlefs"] | [submodule "lib/littlefs"] | ||||||
| 	path = lib/littlefs | 	path = lib/littlefs | ||||||
| 	url = https://github.com/littlefs-project/littlefs.git | 	url = https://github.com/littlefs-project/littlefs.git | ||||||
|  | [submodule "lib/nanopb"] | ||||||
|  | 	path = lib/nanopb | ||||||
|  | 	url = https://github.com/nanopb/nanopb.git | ||||||
|  | [submodule "assets/protobuf"] | ||||||
|  | 	path = assets/protobuf | ||||||
|  | 	url = https://github.com/flipperdevices/flipperzero-protobuf.git | ||||||
|  | [submodule "lib/libusb_stm32"] | ||||||
|  | 	path = lib/libusb_stm32 | ||||||
|  | 	url = https://github.com/flipperdevices/libusb_stm32.git | ||||||
|  | |||||||
							
								
								
									
										128
									
								
								CODE_OF_CONDUCT.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								CODE_OF_CONDUCT.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,128 @@ | |||||||
|  | # Contributor Covenant Code of Conduct | ||||||
|  | 
 | ||||||
|  | ## Our Pledge | ||||||
|  | 
 | ||||||
|  | We as members, contributors, and leaders pledge to make participation in our | ||||||
|  | community a harassment-free experience for everyone, regardless of age, body | ||||||
|  | size, visible or invisible disability, ethnicity, sex characteristics, gender | ||||||
|  | identity and expression, level of experience, education, socio-economic status, | ||||||
|  | nationality, personal appearance, race, religion, or sexual identity | ||||||
|  | and orientation. | ||||||
|  | 
 | ||||||
|  | We pledge to act and interact in ways that contribute to an open, welcoming, | ||||||
|  | diverse, inclusive, and healthy community. | ||||||
|  | 
 | ||||||
|  | ## Our Standards | ||||||
|  | 
 | ||||||
|  | Examples of behavior that contributes to a positive environment for our | ||||||
|  | community include: | ||||||
|  | 
 | ||||||
|  | * Demonstrating empathy and kindness toward other people | ||||||
|  | * Being respectful of differing opinions, viewpoints, and experiences | ||||||
|  | * Giving and gracefully accepting constructive feedback | ||||||
|  | * Accepting responsibility and apologizing to those affected by our mistakes, | ||||||
|  |   and learning from the experience | ||||||
|  | * Focusing on what is best not just for us as individuals, but for the | ||||||
|  |   overall community | ||||||
|  | 
 | ||||||
|  | Examples of unacceptable behavior include: | ||||||
|  | 
 | ||||||
|  | * The use of sexualized language or imagery, and sexual attention or | ||||||
|  |   advances of any kind | ||||||
|  | * Trolling, insulting or derogatory comments, and personal or political attacks | ||||||
|  | * Public or private harassment | ||||||
|  | * Publishing others' private information, such as a physical or email | ||||||
|  |   address, without their explicit permission | ||||||
|  | * Other conduct which could reasonably be considered inappropriate in a | ||||||
|  |   professional setting | ||||||
|  | 
 | ||||||
|  | ## Enforcement Responsibilities | ||||||
|  | 
 | ||||||
|  | Community leaders are responsible for clarifying and enforcing our standards of | ||||||
|  | acceptable behavior and will take appropriate and fair corrective action in | ||||||
|  | response to any behavior that they deem inappropriate, threatening, offensive, | ||||||
|  | or harmful. | ||||||
|  | 
 | ||||||
|  | Community leaders have the right and responsibility to remove, edit, or reject | ||||||
|  | comments, commits, code, wiki edits, issues, and other contributions that are | ||||||
|  | not aligned to this Code of Conduct, and will communicate reasons for moderation | ||||||
|  | decisions when appropriate. | ||||||
|  | 
 | ||||||
|  | ## Scope | ||||||
|  | 
 | ||||||
|  | This Code of Conduct applies within all community spaces, and also applies when | ||||||
|  | an individual is officially representing the community in public spaces. | ||||||
|  | Examples of representing our community include using an official e-mail address, | ||||||
|  | posting via an official social media account, or acting as an appointed | ||||||
|  | representative at an online or offline event. | ||||||
|  | 
 | ||||||
|  | ## Enforcement | ||||||
|  | 
 | ||||||
|  | Instances of abusive, harassing, or otherwise unacceptable behavior may be | ||||||
|  | reported to the community leaders responsible for enforcement at | ||||||
|  | hello@flipperdevices.com. | ||||||
|  | All complaints will be reviewed and investigated promptly and fairly. | ||||||
|  | 
 | ||||||
|  | All community leaders are obligated to respect the privacy and security of the | ||||||
|  | reporter of any incident. | ||||||
|  | 
 | ||||||
|  | ## Enforcement Guidelines | ||||||
|  | 
 | ||||||
|  | Community leaders will follow these Community Impact Guidelines in determining | ||||||
|  | the consequences for any action they deem in violation of this Code of Conduct: | ||||||
|  | 
 | ||||||
|  | ### 1. Correction | ||||||
|  | 
 | ||||||
|  | **Community Impact**: Use of inappropriate language or other behavior deemed | ||||||
|  | unprofessional or unwelcome in the community. | ||||||
|  | 
 | ||||||
|  | **Consequence**: A private, written warning from community leaders, providing | ||||||
|  | clarity around the nature of the violation and an explanation of why the | ||||||
|  | behavior was inappropriate. A public apology may be requested. | ||||||
|  | 
 | ||||||
|  | ### 2. Warning | ||||||
|  | 
 | ||||||
|  | **Community Impact**: A violation through a single incident or series | ||||||
|  | of actions. | ||||||
|  | 
 | ||||||
|  | **Consequence**: A warning with consequences for continued behavior. No | ||||||
|  | interaction with the people involved, including unsolicited interaction with | ||||||
|  | those enforcing the Code of Conduct, for a specified period of time. This | ||||||
|  | includes avoiding interactions in community spaces as well as external channels | ||||||
|  | like social media. Violating these terms may lead to a temporary or | ||||||
|  | permanent ban. | ||||||
|  | 
 | ||||||
|  | ### 3. Temporary Ban | ||||||
|  | 
 | ||||||
|  | **Community Impact**: A serious violation of community standards, including | ||||||
|  | sustained inappropriate behavior. | ||||||
|  | 
 | ||||||
|  | **Consequence**: A temporary ban from any sort of interaction or public | ||||||
|  | communication with the community for a specified period of time. No public or | ||||||
|  | private interaction with the people involved, including unsolicited interaction | ||||||
|  | with those enforcing the Code of Conduct, is allowed during this period. | ||||||
|  | Violating these terms may lead to a permanent ban. | ||||||
|  | 
 | ||||||
|  | ### 4. Permanent Ban | ||||||
|  | 
 | ||||||
|  | **Community Impact**: Demonstrating a pattern of violation of community | ||||||
|  | standards, including sustained inappropriate behavior,  harassment of an | ||||||
|  | individual, or aggression toward or disparagement of classes of individuals. | ||||||
|  | 
 | ||||||
|  | **Consequence**: A permanent ban from any sort of public interaction within | ||||||
|  | the community. | ||||||
|  | 
 | ||||||
|  | ## Attribution | ||||||
|  | 
 | ||||||
|  | This Code of Conduct is adapted from the [Contributor Covenant][homepage], | ||||||
|  | version 2.0, available at | ||||||
|  | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. | ||||||
|  | 
 | ||||||
|  | Community Impact Guidelines were inspired by [Mozilla's code of conduct | ||||||
|  | enforcement ladder](https://github.com/mozilla/diversity). | ||||||
|  | 
 | ||||||
|  | [homepage]: https://www.contributor-covenant.org | ||||||
|  | 
 | ||||||
|  | For answers to common questions about this code of conduct, see the FAQ at | ||||||
|  | https://www.contributor-covenant.org/faq. Translations are available at | ||||||
|  | https://www.contributor-covenant.org/translations. | ||||||
							
								
								
									
										71
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | |||||||
|  | # Welcome to FlipperZero contributing guide <!-- omit in toc --> | ||||||
|  | 
 | ||||||
|  | Thank you for investing your time in contributing to our project!  | ||||||
|  | 
 | ||||||
|  | Read our [Code of Coduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable. | ||||||
|  | 
 | ||||||
|  | In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR. | ||||||
|  | 
 | ||||||
|  | ## New contributor guide | ||||||
|  | 
 | ||||||
|  | See the [ReadMe](ReadMe.md) to get an overview of the project. Here are some helpful resources to get you comfortable with open source contribution: | ||||||
|  | 
 | ||||||
|  | - [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) | ||||||
|  | - [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git) | ||||||
|  | - [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) | ||||||
|  | - [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests) | ||||||
|  | 
 | ||||||
|  | ## Getting started | ||||||
|  | 
 | ||||||
|  | Before writing code and creating PR make sure that it aligns with our mission and guidlines: | ||||||
|  | 
 | ||||||
|  | - All our devices are intended for research and education. | ||||||
|  | - PR that contains code intended to commit crimes is not going to be accepted. | ||||||
|  | - Your PR must contain code compatiable with project [LICENSE](LICENSE). | ||||||
|  | - PR will only be merged if it pass CI/CD. | ||||||
|  | - PR will only be merged if it pass review by code owner. | ||||||
|  | 
 | ||||||
|  | Feel free to ask questions in issues if you're not sure. | ||||||
|  | 
 | ||||||
|  | ### Issues | ||||||
|  | 
 | ||||||
|  | #### Create a new issue | ||||||
|  | 
 | ||||||
|  | If you found a problem, [search if an issue already exists](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments). If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/flipperdevices/flipperzero-firmware/issues/new/choose).  | ||||||
|  | 
 | ||||||
|  | #### Solve an issue | ||||||
|  | 
 | ||||||
|  | Scan through our [existing issues](https://github.com/flipperdevices/flipperzero-firmware/issues) to find one that interests you. | ||||||
|  | 
 | ||||||
|  | ### Make Changes | ||||||
|  | 
 | ||||||
|  | 1. Fork the repository. | ||||||
|  | - Using GitHub Desktop: | ||||||
|  |   - [Getting started with GitHub Desktop](https://docs.github.com/en/desktop/installing-and-configuring-github-desktop/getting-started-with-github-desktop) will guide you through setting up Desktop. | ||||||
|  |   - Once Desktop is set up, you can use it to [fork the repo](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/cloning-and-forking-repositories-from-github-desktop)! | ||||||
|  | 
 | ||||||
|  | - Using the command line: | ||||||
|  |   - [Fork the repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository) so that you can make your changes without affecting the original project until you're ready to merge them. | ||||||
|  | 
 | ||||||
|  | 2. Install build requirements | ||||||
|  | 
 | ||||||
|  | 3. Create a working branch and start with your changes! | ||||||
|  | 
 | ||||||
|  | ### Commit your update | ||||||
|  | 
 | ||||||
|  | Commit the changes once you are happy with them. Make sure that code compilation is not broken and passes tests. Check syntax and formatting. | ||||||
|  | 
 | ||||||
|  | ### Pull Request | ||||||
|  | 
 | ||||||
|  | When you're done making the changes, open a pull request, often referred to as a PR.  | ||||||
|  | - Fill out the "Ready for review" template so we can review your PR. This template helps reviewers understand your changes and the purpose of your pull request.  | ||||||
|  | - Don't forget to [link PR to issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one. | ||||||
|  | - Enable the checkbox to [allow maintainer edits](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so the branch can be updated for a merge. | ||||||
|  | Once you submit your PR, a Docs team member will review your proposal. We may ask questions or request for additional information. | ||||||
|  | - We may ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch. | ||||||
|  | - As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations). | ||||||
|  | - If you run into any merge issues, checkout this [git tutorial](https://lab.github.com/githubtraining/managing-merge-conflicts) to help you resolve merge conflicts and other issues. | ||||||
|  | 
 | ||||||
|  | ### Your PR is merged! | ||||||
|  | 
 | ||||||
|  | Congratulations :tada::tada: The FlipperDevices team thanks you :sparkles:. | ||||||
| @ -162,6 +162,7 @@ Finally, you will have **`firmware/.obj/f6/full.dfu`** file that can be distribu | |||||||
| - core - core libraries: home for furi | - core - core libraries: home for furi | ||||||
| - debug - debug helpers, plugins and tools | - debug - debug helpers, plugins and tools | ||||||
| - docker - docker image sources (used for automated firmware build) | - docker - docker image sources (used for automated firmware build) | ||||||
|  | - documentation - documentation generation system configs and input files | ||||||
| - firmware - firmware for flipper | - firmware - firmware for flipper | ||||||
|   * targets - targets' hal and implementation |   * targets - targets' hal and implementation | ||||||
| - lib - different libraries and drivers that apps and firmware uses | - lib - different libraries and drivers that apps and firmware uses | ||||||
|  | |||||||
							
								
								
									
										49
									
								
								applications/applications.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										49
									
								
								applications/applications.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -2,6 +2,7 @@ | |||||||
| #include <assets_icons.h> | #include <assets_icons.h> | ||||||
| 
 | 
 | ||||||
| // Services
 | // Services
 | ||||||
|  | extern int32_t rpc_srv(void* p); | ||||||
| extern int32_t bt_srv(void* p); | extern int32_t bt_srv(void* p); | ||||||
| extern int32_t cli_srv(void* p); | extern int32_t cli_srv(void* p); | ||||||
| extern int32_t dialogs_srv(void* p); | extern int32_t dialogs_srv(void* p); | ||||||
| @ -19,8 +20,8 @@ extern int32_t desktop_srv(void* p); | |||||||
| extern int32_t accessor_app(void* p); | extern int32_t accessor_app(void* p); | ||||||
| extern int32_t archive_app(void* p); | extern int32_t archive_app(void* p); | ||||||
| extern int32_t blink_test_app(void* p); | extern int32_t blink_test_app(void* p); | ||||||
| extern int32_t flipper_test_app(void* p); | extern int32_t delay_test_app(void* p); | ||||||
| extern int32_t gpio_test_app(void* p); | extern int32_t gpio_app(void* p); | ||||||
| extern int32_t ibutton_app(void* p); | extern int32_t ibutton_app(void* p); | ||||||
| extern int32_t irda_app(void* p); | extern int32_t irda_app(void* p); | ||||||
| extern int32_t irda_monitor_app(void* p); | extern int32_t irda_monitor_app(void* p); | ||||||
| @ -33,6 +34,9 @@ extern int32_t storage_test_app(void* p); | |||||||
| extern int32_t subghz_app(void* p); | extern int32_t subghz_app(void* p); | ||||||
| extern int32_t vibro_test_app(void* p); | extern int32_t vibro_test_app(void* p); | ||||||
| extern int32_t bt_debug_app(void* p); | extern int32_t bt_debug_app(void* p); | ||||||
|  | extern int32_t usb_test_app(void* p); | ||||||
|  | extern int32_t usb_mouse_app(void* p); | ||||||
|  | extern int32_t bad_usb_app(void* p); | ||||||
| 
 | 
 | ||||||
| // Plugins
 | // Plugins
 | ||||||
| extern int32_t music_player_app(void* p); | extern int32_t music_player_app(void* p); | ||||||
| @ -47,6 +51,7 @@ extern void nfc_cli_init(); | |||||||
| extern void storage_cli_init(); | extern void storage_cli_init(); | ||||||
| extern void subghz_cli_init(); | extern void subghz_cli_init(); | ||||||
| extern void power_cli_init(); | extern void power_cli_init(); | ||||||
|  | extern void unit_tests_cli_init(); | ||||||
| 
 | 
 | ||||||
| // Settings
 | // Settings
 | ||||||
| extern int32_t notification_settings_app(void* p); | extern int32_t notification_settings_app(void* p); | ||||||
| @ -58,6 +63,10 @@ extern int32_t power_settings_app(void* p); | |||||||
| 
 | 
 | ||||||
| const FlipperApplication FLIPPER_SERVICES[] = { | const FlipperApplication FLIPPER_SERVICES[] = { | ||||||
| /* Services */ | /* Services */ | ||||||
|  | #ifdef SRV_RPC | ||||||
|  |     {.app = rpc_srv, .name = "RPC", .stack_size = 1024 * 4, .icon = NULL}, | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef SRV_BT | #ifdef SRV_BT | ||||||
|     {.app = bt_srv, .name = "BT", .stack_size = 1024, .icon = NULL}, |     {.app = bt_srv, .name = "BT", .stack_size = 1024, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
| @ -116,24 +125,24 @@ const FlipperApplication FLIPPER_APPS[] = { | |||||||
|     {.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14}, |     {.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_NFC |  | ||||||
|     {.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_LF_RFID | #ifdef APP_LF_RFID | ||||||
|     {.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_125khz_14}, |     {.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_125khz_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef APP_NFC | ||||||
|  |     {.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14}, | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef APP_IRDA | #ifdef APP_IRDA | ||||||
|     {.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14}, |     {.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_IBUTTON | #ifdef APP_GPIO | ||||||
|     {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14}, |     {.app = gpio_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_GPIO_TEST | #ifdef APP_IBUTTON | ||||||
|     {.app = gpio_test_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14}, |     {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| @ -177,6 +186,10 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { | |||||||
| #ifdef SRV_STORAGE | #ifdef SRV_STORAGE | ||||||
|     storage_cli_init, |     storage_cli_init, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef APP_UNIT_TESTS | ||||||
|  |     unit_tests_cli_init, | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const size_t FLIPPER_ON_SYSTEM_START_COUNT = | const size_t FLIPPER_ON_SYSTEM_START_COUNT = | ||||||
| @ -210,8 +223,16 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { | |||||||
|     {.app = accessor_app, .name = "Accessor", .stack_size = 4096, .icon = &A_Plugins_14}, |     {.app = accessor_app, .name = "Accessor", .stack_size = 4096, .icon = &A_Plugins_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_UNIT_TESTS | #ifdef APP_USB_TEST | ||||||
|     {.app = flipper_test_app, .name = "Unit Tests", .stack_size = 1024, .icon = &A_Plugins_14}, |     {.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = &A_Plugins_14}, | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef APP_USB_MOUSE | ||||||
|  |     {.app = usb_mouse_app, .name = "USB Mouse demo", .stack_size = 1024, .icon = &A_Plugins_14}, | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef APP_BAD_USB | ||||||
|  |     {.app = bad_usb_app, .name = "Bad USB test", .stack_size = 2048, .icon = &A_Plugins_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_IRDA_MONITOR | #ifdef APP_IRDA_MONITOR | ||||||
| @ -229,6 +250,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { | |||||||
| #ifdef SRV_BT | #ifdef SRV_BT | ||||||
|     {.app = bt_debug_app, .name = "Bluetooth Debug", .stack_size = 1024, .icon = NULL}, |     {.app = bt_debug_app, .name = "Bluetooth Debug", .stack_size = 1024, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef APP_UNIT_TESTS | ||||||
|  |     {.app = delay_test_app, .name = "Delay Test App", .stack_size = 1024, .icon = &A_Plugins_14}, | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const size_t FLIPPER_DEBUG_APPS_COUNT = sizeof(FLIPPER_DEBUG_APPS) / sizeof(FlipperApplication); | const size_t FLIPPER_DEBUG_APPS_COUNT = sizeof(FLIPPER_DEBUG_APPS) / sizeof(FlipperApplication); | ||||||
|  | |||||||
| @ -2,8 +2,8 @@ APP_DIR		= $(PROJECT_ROOT)/applications | |||||||
| LIB_DIR		= $(PROJECT_ROOT)/lib | LIB_DIR		= $(PROJECT_ROOT)/lib | ||||||
| 
 | 
 | ||||||
| CFLAGS		+= -I$(APP_DIR) | CFLAGS		+= -I$(APP_DIR) | ||||||
| C_SOURCES	+= $(shell find $(APP_DIR) -name *.c) | C_SOURCES	+= $(shell find $(APP_DIR) -name "*.c") | ||||||
| CPP_SOURCES	+= $(shell find $(APP_DIR) -name *.cpp) | CPP_SOURCES	+= $(shell find $(APP_DIR) -name "*.cpp") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| APP_RELEASE ?= 1 | APP_RELEASE ?= 1 | ||||||
| @ -19,12 +19,13 @@ SRV_LOADER	= 1 | |||||||
| SRV_NOTIFICATION = 1 | SRV_NOTIFICATION = 1 | ||||||
| SRV_POWER	= 1 | SRV_POWER	= 1 | ||||||
| SRV_POWER_OBSERVER = 1 | SRV_POWER_OBSERVER = 1 | ||||||
|  | SRV_RPC = 1 | ||||||
| SRV_STORAGE	= 1 | SRV_STORAGE	= 1 | ||||||
| 
 | 
 | ||||||
| # Apps
 | # Apps
 | ||||||
| SRV_DESKTOP	= 1 | SRV_DESKTOP	= 1 | ||||||
| APP_ARCHIVE	= 1 | APP_ARCHIVE	= 1 | ||||||
| APP_GPIO_TEST = 1 | APP_GPIO = 1 | ||||||
| APP_IBUTTON	= 1 | APP_IBUTTON	= 1 | ||||||
| APP_IRDA	= 1 | APP_IRDA	= 1 | ||||||
| APP_LF_RFID	= 1 | APP_LF_RFID	= 1 | ||||||
| @ -41,8 +42,10 @@ APP_BLINK	= 1 | |||||||
| APP_IRDA_MONITOR = 1 | APP_IRDA_MONITOR = 1 | ||||||
| APP_KEYPAD_TEST = 1 | APP_KEYPAD_TEST = 1 | ||||||
| APP_SD_TEST	= 1 | APP_SD_TEST	= 1 | ||||||
| APP_UNIT_TESTS = 0 |  | ||||||
| APP_VIBRO_DEMO = 1 | APP_VIBRO_DEMO = 1 | ||||||
|  | APP_USB_TEST = 1 | ||||||
|  | APP_USB_MOUSE = 1 | ||||||
|  | APP_BAD_USB = 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -58,7 +61,7 @@ SRV_GUI		= 1 | |||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| APP_UNIT_TESTS	?= 0 | APP_UNIT_TESTS ?= 0 | ||||||
| ifeq ($(APP_UNIT_TESTS), 1) | ifeq ($(APP_UNIT_TESTS), 1) | ||||||
| CFLAGS		+= -DAPP_UNIT_TESTS | CFLAGS		+= -DAPP_UNIT_TESTS | ||||||
| endif | endif | ||||||
| @ -121,6 +124,27 @@ SRV_GUI		= 1 | |||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | APP_USB_TEST ?= 0 | ||||||
|  | ifeq ($(APP_USB_TEST), 1) | ||||||
|  | CFLAGS		+= -DAPP_USB_TEST | ||||||
|  | SRV_INPUT = 1 | ||||||
|  | SRV_GUI = 1 | ||||||
|  | endif | ||||||
|  | 
 | ||||||
|  | APP_USB_MOUSE ?= 0 | ||||||
|  | ifeq ($(APP_USB_MOUSE), 1) | ||||||
|  | CFLAGS		+= -DAPP_USB_MOUSE | ||||||
|  | SRV_INPUT = 1 | ||||||
|  | SRV_GUI = 1 | ||||||
|  | endif | ||||||
|  | 
 | ||||||
|  | APP_BAD_USB ?= 0 | ||||||
|  | ifeq ($(APP_BAD_USB), 1) | ||||||
|  | CFLAGS		+= -DAPP_BAD_USB | ||||||
|  | SRV_INPUT = 1 | ||||||
|  | SRV_GUI = 1 | ||||||
|  | endif  | ||||||
|  | 
 | ||||||
| APP_KEYPAD_TEST ?= 0 | APP_KEYPAD_TEST ?= 0 | ||||||
| ifeq ($(APP_KEYPAD_TEST), 1) | ifeq ($(APP_KEYPAD_TEST), 1) | ||||||
| CFLAGS		+= -DAPP_KEYPAD_TEST | CFLAGS		+= -DAPP_KEYPAD_TEST | ||||||
| @ -135,9 +159,9 @@ SRV_GUI		= 1 | |||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| APP_GPIO_TEST ?= 0 | APP_GPIO ?= 0 | ||||||
| ifeq ($(APP_GPIO_TEST), 1) | ifeq ($(APP_GPIO), 1) | ||||||
| CFLAGS		+= -DAPP_GPIO_TEST | CFLAGS		+= -DAPP_GPIO | ||||||
| SRV_GUI		= 1 | SRV_GUI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| @ -197,6 +221,10 @@ SRV_GUI		= 1 | |||||||
| SRV_CLI		= 1 | SRV_CLI		= 1 | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | SRV_RPC ?= 0 | ||||||
|  | ifeq ($(SRV_RPC), 1) | ||||||
|  | CFLAGS		+= -DSRV_RPC | ||||||
|  | endif | ||||||
| 
 | 
 | ||||||
| SRV_LOADER ?= 0 | SRV_LOADER ?= 0 | ||||||
| ifeq ($(SRV_LOADER), 1) | ifeq ($(SRV_LOADER), 1) | ||||||
|  | |||||||
| @ -10,7 +10,9 @@ void archive_update_offset(ArchiveBrowserView* browser) { | |||||||
| 
 | 
 | ||||||
|             if(array_size > 3 && model->idx >= array_size - 1) { |             if(array_size > 3 && model->idx >= array_size - 1) { | ||||||
|                 model->list_offset = model->idx - 3; |                 model->list_offset = model->idx - 3; | ||||||
|             } else if(model->last_offset && model->last_offset != model->list_offset) { |             } else if( | ||||||
|  |                 model->last_offset && model->last_offset != model->list_offset && | ||||||
|  |                 model->tab_idx == model->last_tab) { | ||||||
|                 model->list_offset = model->last_offset; |                 model->list_offset = model->last_offset; | ||||||
|                 model->last_offset = !model->last_offset; |                 model->last_offset = !model->last_offset; | ||||||
|             } else if(model->list_offset < model->idx - bounds) { |             } else if(model->list_offset < model->idx - bounds) { | ||||||
| @ -18,7 +20,6 @@ void archive_update_offset(ArchiveBrowserView* browser) { | |||||||
|             } else if(model->list_offset > model->idx - bounds) { |             } else if(model->list_offset > model->idx - bounds) { | ||||||
|                 model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0); |                 model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0); | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| @ -75,6 +76,31 @@ void archive_file_array_rm_selected(ArchiveBrowserView* browser) { | |||||||
|     archive_update_offset(browser); |     archive_update_offset(browser); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void archive_file_array_swap(ArchiveBrowserView* browser, int8_t d) { | ||||||
|  |     with_view_model( | ||||||
|  |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|  |             ArchiveFile_t temp; | ||||||
|  |             size_t array_size = files_array_size(model->files) - 1; | ||||||
|  |             uint8_t swap_idx = CLAMP(model->idx + d, array_size, 0); | ||||||
|  | 
 | ||||||
|  |             if(model->idx == 0 && d < 0) { | ||||||
|  |                 ArchiveFile_t_init(&temp); | ||||||
|  |                 files_array_pop_at(&temp, model->files, array_size); | ||||||
|  |                 files_array_push_at(model->files, model->idx, temp); | ||||||
|  |                 ArchiveFile_t_clear(&temp); | ||||||
|  |             } else if(model->idx == array_size && d > 0) { | ||||||
|  |                 ArchiveFile_t_init(&temp); | ||||||
|  |                 files_array_pop_at(&temp, model->files, model->last_idx); | ||||||
|  |                 files_array_push_at(model->files, array_size, temp); | ||||||
|  |                 ArchiveFile_t_clear(&temp); | ||||||
|  |             } else { | ||||||
|  |                 files_array_swap_at(model->files, model->idx, swap_idx); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void archive_file_array_rm_all(ArchiveBrowserView* browser) { | void archive_file_array_rm_all(ArchiveBrowserView* browser) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
| @ -94,6 +120,18 @@ ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) { | |||||||
|     return selected; |     return selected; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx) { | ||||||
|  |     ArchiveFile_t* selected; | ||||||
|  |     idx = CLAMP(idx, archive_file_array_size(browser), 0); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|  |             selected = files_array_size(model->files) ? files_array_get(model->files, idx) : NULL; | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  |     return selected; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) { | ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) { | ||||||
|     ArchiveTabEnum tab_id; |     ArchiveTabEnum tab_id; | ||||||
|     with_view_model( |     with_view_model( | ||||||
| @ -178,6 +216,14 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { | |||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { | ||||||
|  |     with_view_model( | ||||||
|  |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|  |             model->move_fav = active; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void archive_switch_dir(ArchiveBrowserView* browser, const char* path) { | void archive_switch_dir(ArchiveBrowserView* browser, const char* path) { | ||||||
|     furi_assert(browser); |     furi_assert(browser); | ||||||
|     furi_assert(path); |     furi_assert(path); | ||||||
|  | |||||||
| @ -52,9 +52,11 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target); | |||||||
| 
 | 
 | ||||||
| size_t archive_file_array_size(ArchiveBrowserView* browser); | size_t archive_file_array_size(ArchiveBrowserView* browser); | ||||||
| void archive_file_array_rm_selected(ArchiveBrowserView* browser); | void archive_file_array_rm_selected(ArchiveBrowserView* browser); | ||||||
|  | void archive_file_array_swap(ArchiveBrowserView* browser, int8_t d); | ||||||
| void archive_file_array_rm_all(ArchiveBrowserView* browser); | void archive_file_array_rm_all(ArchiveBrowserView* browser); | ||||||
| 
 | 
 | ||||||
| ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser); | ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser); | ||||||
|  | ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx); | ||||||
| ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser); | ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser); | ||||||
| uint8_t archive_get_depth(ArchiveBrowserView* browser); | uint8_t archive_get_depth(ArchiveBrowserView* browser); | ||||||
| const char* archive_get_path(ArchiveBrowserView* browser); | const char* archive_get_path(ArchiveBrowserView* browser); | ||||||
| @ -62,6 +64,7 @@ const char* archive_get_name(ArchiveBrowserView* browser); | |||||||
| 
 | 
 | ||||||
| void archive_add_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name); | void archive_add_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name); | ||||||
| void archive_show_file_menu(ArchiveBrowserView* browser, bool show); | void archive_show_file_menu(ArchiveBrowserView* browser, bool show); | ||||||
|  | void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active); | ||||||
| 
 | 
 | ||||||
| void archive_switch_tab(ArchiveBrowserView* browser, InputKey key); | void archive_switch_tab(ArchiveBrowserView* browser, InputKey key); | ||||||
| void archive_enter_dir(ArchiveBrowserView* browser, string_t name); | void archive_enter_dir(ArchiveBrowserView* browser, string_t name); | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ uint16_t archive_favorites_count(void* context) { | |||||||
| bool archive_favorites_read(void* context) { | bool archive_favorites_read(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 | 
 | ||||||
|     ArchiveBrowserView* archive_view = context; |     ArchiveBrowserView* browser = context; | ||||||
|     FileWorker* file_worker = file_worker_alloc(true); |     FileWorker* file_worker = file_worker_alloc(true); | ||||||
| 
 | 
 | ||||||
|     string_t buffer; |     string_t buffer; | ||||||
| @ -52,7 +52,7 @@ bool archive_favorites_read(void* context) { | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             archive_add_item(archive_view, &file_info, string_get_cstr(buffer)); |             archive_add_item(browser, &file_info, string_get_cstr(buffer)); | ||||||
|             string_clean(buffer); |             string_clean(buffer); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -63,17 +63,15 @@ bool archive_favorites_read(void* context) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool archive_favorites_delete(const char* format, ...) { | bool archive_favorites_delete(const char* format, ...) { | ||||||
|  |     string_t buffer; | ||||||
|  |     string_t filename; | ||||||
|     va_list args; |     va_list args; | ||||||
|     va_start(args, format); |     va_start(args, format); | ||||||
|     uint8_t len = vsnprintf(NULL, 0, format, args); |     string_init_vprintf(filename, format, args); | ||||||
|     char filename[len + 1]; |  | ||||||
|     vsnprintf(filename, len + 1, format, args); |  | ||||||
|     va_end(args); |     va_end(args); | ||||||
| 
 | 
 | ||||||
|     FileWorker* file_worker = file_worker_alloc(true); |  | ||||||
| 
 |  | ||||||
|     string_t buffer; |  | ||||||
|     string_init(buffer); |     string_init(buffer); | ||||||
|  |     FileWorker* file_worker = file_worker_alloc(true); | ||||||
| 
 | 
 | ||||||
|     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); |     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); | ||||||
|     if(result) { |     if(result) { | ||||||
| @ -85,13 +83,14 @@ bool archive_favorites_delete(const char* format, ...) { | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if(string_search_str(buffer, filename)) { |             if(string_search(buffer, filename)) { | ||||||
|                 archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\r\n", string_get_cstr(buffer)); |                 archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     string_clear(buffer); |     string_clear(buffer); | ||||||
|  |     string_clear(filename); | ||||||
| 
 | 
 | ||||||
|     file_worker_close(file_worker); |     file_worker_close(file_worker); | ||||||
|     file_worker_remove(file_worker, ARCHIVE_FAV_PATH); |     file_worker_remove(file_worker, ARCHIVE_FAV_PATH); | ||||||
| @ -103,16 +102,15 @@ bool archive_favorites_delete(const char* format, ...) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool archive_is_favorite(const char* format, ...) { | bool archive_is_favorite(const char* format, ...) { | ||||||
|  |     string_t buffer; | ||||||
|  |     string_t filename; | ||||||
|     va_list args; |     va_list args; | ||||||
|     va_start(args, format); |     va_start(args, format); | ||||||
|     uint8_t len = vsnprintf(NULL, 0, format, args); |     string_init_vprintf(filename, format, args); | ||||||
|     char filename[len + 1]; |  | ||||||
|     vsnprintf(filename, len + 1, format, args); |  | ||||||
|     va_end(args); |     va_end(args); | ||||||
| 
 | 
 | ||||||
|     FileWorker* file_worker = file_worker_alloc(true); |  | ||||||
|     string_t buffer; |  | ||||||
|     string_init(buffer); |     string_init(buffer); | ||||||
|  |     FileWorker* file_worker = file_worker_alloc(true); | ||||||
| 
 | 
 | ||||||
|     bool found = false; |     bool found = false; | ||||||
|     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); |     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); | ||||||
| @ -125,7 +123,7 @@ bool archive_is_favorite(const char* format, ...) { | |||||||
|             if(!string_size(buffer)) { |             if(!string_size(buffer)) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             if(!string_search_str(buffer, filename)) { |             if(!string_search(buffer, filename)) { | ||||||
|                 found = true; |                 found = true; | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| @ -133,6 +131,7 @@ bool archive_is_favorite(const char* format, ...) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     string_clear(buffer); |     string_clear(buffer); | ||||||
|  |     string_clear(filename); | ||||||
|     file_worker_close(file_worker); |     file_worker_close(file_worker); | ||||||
|     file_worker_free(file_worker); |     file_worker_free(file_worker); | ||||||
| 
 | 
 | ||||||
| @ -166,7 +165,7 @@ bool archive_favorites_rename(const char* file_path, const char* src, const char | |||||||
| 
 | 
 | ||||||
|             archive_file_append( |             archive_file_append( | ||||||
|                 ARCHIVE_FAV_TEMP_PATH, |                 ARCHIVE_FAV_TEMP_PATH, | ||||||
|                 "%s\r\n", |                 "%s\n", | ||||||
|                 string_search(buffer, path) ? string_get_cstr(buffer) : dst); |                 string_search(buffer, path) ? string_get_cstr(buffer) : dst); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -186,5 +185,22 @@ bool archive_favorites_rename(const char* file_path, const char* src, const char | |||||||
| void archive_add_to_favorites(const char* file_path) { | void archive_add_to_favorites(const char* file_path) { | ||||||
|     furi_assert(file_path); |     furi_assert(file_path); | ||||||
| 
 | 
 | ||||||
|     archive_file_append(ARCHIVE_FAV_PATH, "%s\r\n", file_path); |     archive_file_append(ARCHIVE_FAV_PATH, "%s\n", file_path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_favorites_save(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     ArchiveBrowserView* browser = context; | ||||||
|  |     FileWorker* file_worker = file_worker_alloc(true); | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < archive_file_array_size(browser); i++) { | ||||||
|  |         ArchiveFile_t* item = archive_get_file_at(browser, i); | ||||||
|  |         archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(item->name)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     file_worker_remove(file_worker, ARCHIVE_FAV_PATH); | ||||||
|  |     file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); | ||||||
|  | 
 | ||||||
|  |     file_worker_free(file_worker); | ||||||
| } | } | ||||||
| @ -10,3 +10,4 @@ bool archive_favorites_delete(const char* format, ...); | |||||||
| bool archive_is_favorite(const char* format, ...); | bool archive_is_favorite(const char* format, ...); | ||||||
| bool archive_favorites_rename(const char* file_path, const char* src, const char* dst); | bool archive_favorites_rename(const char* file_path, const char* src, const char* dst); | ||||||
| void archive_add_to_favorites(const char* file_path); | void archive_add_to_favorites(const char* file_path); | ||||||
|  | void archive_favorites_save(void* context); | ||||||
|  | |||||||
| @ -138,11 +138,10 @@ bool archive_read_dir(void* context, const char* path) { | |||||||
| void archive_file_append(const char* path, const char* format, ...) { | void archive_file_append(const char* path, const char* format, ...) { | ||||||
|     furi_assert(path); |     furi_assert(path); | ||||||
| 
 | 
 | ||||||
|  |     string_t string; | ||||||
|     va_list args; |     va_list args; | ||||||
|     va_start(args, format); |     va_start(args, format); | ||||||
|     uint8_t len = vsnprintf(NULL, 0, format, args); |     string_init_vprintf(string, format, args); | ||||||
|     char cstr_buff[len + 1]; |  | ||||||
|     vsnprintf(cstr_buff, len + 1, format, args); |  | ||||||
|     va_end(args); |     va_end(args); | ||||||
| 
 | 
 | ||||||
|     FileWorker* file_worker = file_worker_alloc(false); |     FileWorker* file_worker = file_worker_alloc(false); | ||||||
| @ -151,7 +150,7 @@ void archive_file_append(const char* path, const char* format, ...) { | |||||||
|         FURI_LOG_E("Archive", "Append open error"); |         FURI_LOG_E("Archive", "Append open error"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(!file_worker_write(file_worker, cstr_buff, strlen(cstr_buff))) { |     if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) { | ||||||
|         FURI_LOG_E("Archive", "Append write error"); |         FURI_LOG_E("Archive", "Append write error"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -159,26 +158,28 @@ void archive_file_append(const char* path, const char* format, ...) { | |||||||
|     file_worker_free(file_worker); |     file_worker_free(file_worker); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void archive_delete_file(void* context, string_t path, string_t name) { | void archive_delete_file(void* context, const char* format, ...) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     furi_assert(path); | 
 | ||||||
|     furi_assert(name); |     string_t filename; | ||||||
|  |     va_list args; | ||||||
|  |     va_start(args, format); | ||||||
|  |     string_init_vprintf(filename, format, args); | ||||||
|  |     va_end(args); | ||||||
|  | 
 | ||||||
|     ArchiveBrowserView* browser = context; |     ArchiveBrowserView* browser = context; | ||||||
|     FileWorker* file_worker = file_worker_alloc(true); |     FileWorker* file_worker = file_worker_alloc(true); | ||||||
| 
 | 
 | ||||||
|     string_t full_path; |     bool res = file_worker_remove(file_worker, string_get_cstr(filename)); | ||||||
|     string_init_printf(full_path, "%s/%s", string_get_cstr(path), string_get_cstr(name)); |  | ||||||
| 
 |  | ||||||
|     bool res = file_worker_remove(file_worker, string_get_cstr(full_path)); |  | ||||||
|     file_worker_free(file_worker); |     file_worker_free(file_worker); | ||||||
| 
 | 
 | ||||||
|     if(archive_is_favorite(string_get_cstr(full_path))) { |     if(archive_is_favorite("%s", string_get_cstr(filename))) { | ||||||
|         archive_favorites_delete(string_get_cstr(full_path)); |         archive_favorites_delete("%s", string_get_cstr(filename)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(res) { |     if(res) { | ||||||
|         archive_file_array_rm_selected(browser); |         archive_file_array_rm_selected(browser); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     string_clear(full_path); |     string_clear(filename); | ||||||
| } | } | ||||||
| @ -54,4 +54,4 @@ bool archive_get_filenames(void* context, const char* path); | |||||||
| bool archive_dir_empty(void* context, const char* path); | bool archive_dir_empty(void* context, const char* path); | ||||||
| bool archive_read_dir(void* context, const char* path); | bool archive_read_dir(void* context, const char* path); | ||||||
| void archive_file_append(const char* path, const char* format, ...); | void archive_file_append(const char* path, const char* format, ...); | ||||||
| void archive_delete_file(void* context, string_t path, string_t name); | void archive_delete_file(void* context, const char* format, ...); | ||||||
| @ -76,25 +76,32 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | |||||||
|             if(favorites) { |             if(favorites) { | ||||||
|                 archive_favorites_delete(name); |                 archive_favorites_delete(name); | ||||||
|                 archive_file_array_rm_selected(browser); |                 archive_file_array_rm_selected(browser); | ||||||
|  |                 archive_show_file_menu(browser, false); | ||||||
|             } else if(known_app) { |             } else if(known_app) { | ||||||
|                 if(archive_is_favorite("%s/%s", path, name)) { |                 if(archive_is_favorite("%s/%s", path, name)) { | ||||||
|                     archive_favorites_delete("%s/%s", path, name); |                     archive_favorites_delete("%s/%s", path, name); | ||||||
|                 } else { |                 } else { | ||||||
|                     archive_file_append(ARCHIVE_FAV_PATH, "%s/%s\r\n", path, name); |                     archive_file_append(ARCHIVE_FAV_PATH, "%s/%s\n", path, name); | ||||||
|                 } |                 } | ||||||
|  |                 archive_show_file_menu(browser, false); | ||||||
|             } |             } | ||||||
|             archive_show_file_menu(browser, false); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case ArchiveBrowserEventFileMenuRename: |         case ArchiveBrowserEventFileMenuAction: | ||||||
|             if(known_app && !favorites) { |             if(favorites) { | ||||||
|  |                 browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); | ||||||
|  |             } else if(known_app) { | ||||||
|                 scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); |                 scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case ArchiveBrowserEventFileMenuDelete: |         case ArchiveBrowserEventFileMenuDelete: | ||||||
|             archive_delete_file(browser, browser->path, selected->name); |             if(favorites) { | ||||||
|  |                 archive_delete_file(browser, "%s", name); | ||||||
|  |             } else { | ||||||
|  |                 archive_delete_file(browser, "%s/%s", path, name); | ||||||
|  |             } | ||||||
|             archive_show_file_menu(browser, false); |             archive_show_file_menu(browser, false); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| @ -102,6 +109,30 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | |||||||
|             archive_enter_dir(browser, selected->name); |             archive_enter_dir(browser, selected->name); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  |         case ArchiveBrowserEventFavMoveUp: | ||||||
|  |             archive_file_array_swap(browser, 1); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case ArchiveBrowserEventFavMoveDown: | ||||||
|  |             archive_file_array_swap(browser, -1); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case ArchiveBrowserEventEnterFavMove: | ||||||
|  |             strlcpy(archive->text_store, archive_get_name(browser), MAX_NAME_LEN); | ||||||
|  |             archive_show_file_menu(browser, false); | ||||||
|  |             archive_favorites_move_mode(archive->browser, true); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case ArchiveBrowserEventExitFavMove: | ||||||
|  |             archive_update_focus(browser, archive->text_store); | ||||||
|  |             archive_favorites_move_mode(archive->browser, false); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case ArchiveBrowserEventSaveFavMove: | ||||||
|  |             archive_favorites_move_mode(archive->browser, false); | ||||||
|  |             archive_favorites_save(archive->browser); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
| 
 | 
 | ||||||
|         case ArchiveBrowserEventExit: |         case ArchiveBrowserEventExit: | ||||||
|             if(archive_get_depth(browser)) { |             if(archive_get_depth(browser)) { | ||||||
|  | |||||||
| @ -55,7 +55,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { | |||||||
|         string_set_str(menu[1], "Unpin"); |         string_set_str(menu[1], "Unpin"); | ||||||
|     } else if(model->tab_idx == ArchiveTabFavorites) { |     } else if(model->tab_idx == ArchiveTabFavorites) { | ||||||
|         string_set_str(menu[1], "Unpin"); |         string_set_str(menu[1], "Unpin"); | ||||||
|         string_set_str(menu[2], "---"); |         string_set_str(menu[2], "Move"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for(size_t i = 0; i < MENU_ITEMS; i++) { |     for(size_t i = 0; i < MENU_ITEMS; i++) { | ||||||
| @ -66,16 +66,23 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { | |||||||
|     canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7); |     canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { | static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) { | ||||||
|  |     uint8_t x_offset = moving ? MOVE_OFFSET : 0; | ||||||
|  | 
 | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
|     canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); |     canvas_draw_box( | ||||||
|  |         canvas, | ||||||
|  |         0 + x_offset, | ||||||
|  |         15 + idx * FRAME_HEIGHT, | ||||||
|  |         (scrollbar ? 122 : 127) - x_offset, | ||||||
|  |         FRAME_HEIGHT); | ||||||
| 
 | 
 | ||||||
|     canvas_set_color(canvas, ColorWhite); |     canvas_set_color(canvas, ColorWhite); | ||||||
|     canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT); |     canvas_draw_dot(canvas, 0 + x_offset, 15 + idx * FRAME_HEIGHT); | ||||||
|     canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT); |     canvas_draw_dot(canvas, 1 + x_offset, 15 + idx * FRAME_HEIGHT); | ||||||
|     canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1); |     canvas_draw_dot(canvas, 0 + x_offset, (15 + idx * FRAME_HEIGHT) + 1); | ||||||
| 
 | 
 | ||||||
|     canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11); |     canvas_draw_dot(canvas, 0 + x_offset, (15 + idx * FRAME_HEIGHT) + 11); | ||||||
|     canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT); |     canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT); | ||||||
|     canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); |     canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); | ||||||
| } | } | ||||||
| @ -89,23 +96,26 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { | |||||||
|     for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { |     for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { | ||||||
|         string_t str_buff; |         string_t str_buff; | ||||||
|         char cstr_buff[MAX_NAME_LEN]; |         char cstr_buff[MAX_NAME_LEN]; | ||||||
| 
 |  | ||||||
|         size_t idx = CLAMP(i + model->list_offset, array_size, 0); |         size_t idx = CLAMP(i + model->list_offset, array_size, 0); | ||||||
|  |         uint8_t x_offset = (model->move_fav && model->idx == idx) ? MOVE_OFFSET : 0; | ||||||
|  | 
 | ||||||
|         ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); |         ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); | ||||||
| 
 | 
 | ||||||
|         strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1); |         strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1); | ||||||
|         archive_trim_file_path(cstr_buff, is_known_app(file->type)); |         archive_trim_file_path(cstr_buff, is_known_app(file->type)); | ||||||
|         string_init_set_str(str_buff, cstr_buff); |         string_init_set_str(str_buff, cstr_buff); | ||||||
|         elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); |         elements_string_fit_width( | ||||||
|  |             canvas, str_buff, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset); | ||||||
| 
 | 
 | ||||||
|         if(model->idx == idx) { |         if(model->idx == idx) { | ||||||
|             archive_draw_frame(canvas, i, scrollbar); |             archive_draw_frame(canvas, i, scrollbar, model->move_fav); | ||||||
|         } else { |         } else { | ||||||
|             canvas_set_color(canvas, ColorBlack); |             canvas_set_color(canvas, ColorBlack); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]); |         canvas_draw_icon( | ||||||
|         canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); |             canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]); | ||||||
|  |         canvas_draw_str(canvas, 15 + x_offset, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); | ||||||
|         string_clear(str_buff); |         string_clear(str_buff); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -139,8 +149,13 @@ static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* m | |||||||
|     canvas_draw_line(canvas, 107, 1, 107, 11); |     canvas_draw_line(canvas, 107, 1, 107, 11); | ||||||
|     canvas_draw_line(canvas, 108, 12, 126, 12); |     canvas_draw_line(canvas, 108, 12, 126, 12); | ||||||
| 
 | 
 | ||||||
|     canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7); |     if(model->move_fav) { | ||||||
|     canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7); |         canvas_draw_icon(canvas, 111, 4, &I_ButtonUp_7x4); | ||||||
|  |         canvas_draw_icon(canvas, 118, 4, &I_ButtonDown_7x4); | ||||||
|  |     } else { | ||||||
|  |         canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7); | ||||||
|  |         canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     canvas_set_color(canvas, ColorWhite); |     canvas_set_color(canvas, ColorWhite); | ||||||
|     canvas_draw_dot(canvas, 50, 0); |     canvas_draw_dot(canvas, 50, 0); | ||||||
| @ -174,9 +189,11 @@ bool archive_view_input(InputEvent* event, void* context) { | |||||||
|     ArchiveBrowserView* browser = context; |     ArchiveBrowserView* browser = context; | ||||||
| 
 | 
 | ||||||
|     bool in_menu; |     bool in_menu; | ||||||
|  |     bool move_fav_mode; | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             in_menu = model->menu; |             in_menu = model->menu; | ||||||
|  |             move_fav_mode = model->move_fav; | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
| @ -210,11 +227,17 @@ bool archive_view_input(InputEvent* event, void* context) { | |||||||
|     } else { |     } else { | ||||||
|         if(event->type == InputTypeShort) { |         if(event->type == InputTypeShort) { | ||||||
|             if(event->key == InputKeyLeft || event->key == InputKeyRight) { |             if(event->key == InputKeyLeft || event->key == InputKeyRight) { | ||||||
|  |                 if(move_fav_mode) return false; | ||||||
|                 archive_switch_tab(browser, event->key); |                 archive_switch_tab(browser, event->key); | ||||||
|             } else if(event->key == InputKeyBack) { |             } else if(event->key == InputKeyBack) { | ||||||
|                 browser->callback(ArchiveBrowserEventExit, browser->context); |                 if(move_fav_mode) { | ||||||
|  |                     browser->callback(ArchiveBrowserEventExitFavMove, browser->context); | ||||||
|  |                 } else { | ||||||
|  |                     browser->callback(ArchiveBrowserEventExit, browser->context); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|         if(event->key == InputKeyUp || event->key == InputKeyDown) { |         if(event->key == InputKeyUp || event->key == InputKeyDown) { | ||||||
|             with_view_model( |             with_view_model( | ||||||
|                 browser->view, (ArchiveBrowserViewModel * model) { |                 browser->view, (ArchiveBrowserViewModel * model) { | ||||||
| @ -222,8 +245,15 @@ bool archive_view_input(InputEvent* event, void* context) { | |||||||
|                     if((event->type == InputTypeShort || event->type == InputTypeRepeat)) { |                     if((event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||||
|                         if(event->key == InputKeyUp) { |                         if(event->key == InputKeyUp) { | ||||||
|                             model->idx = ((model->idx - 1) + num_elements) % num_elements; |                             model->idx = ((model->idx - 1) + num_elements) % num_elements; | ||||||
|  |                             if(move_fav_mode) { | ||||||
|  |                                 browser->callback(ArchiveBrowserEventFavMoveUp, browser->context); | ||||||
|  |                             } | ||||||
|                         } else if(event->key == InputKeyDown) { |                         } else if(event->key == InputKeyDown) { | ||||||
|                             model->idx = (model->idx + 1) % num_elements; |                             model->idx = (model->idx + 1) % num_elements; | ||||||
|  |                             if(move_fav_mode) { | ||||||
|  |                                 browser->callback( | ||||||
|  |                                     ArchiveBrowserEventFavMoveDown, browser->context); | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
| @ -241,14 +271,20 @@ bool archive_view_input(InputEvent* event, void* context) { | |||||||
| 
 | 
 | ||||||
|                 if(event->type == InputTypeShort) { |                 if(event->type == InputTypeShort) { | ||||||
|                     if(favorites) { |                     if(favorites) { | ||||||
|                         browser->callback(ArchiveBrowserEventFileMenuRun, browser->context); |                         if(move_fav_mode) { | ||||||
|  |                             browser->callback(ArchiveBrowserEventSaveFavMove, browser->context); | ||||||
|  |                         } else { | ||||||
|  |                             browser->callback(ArchiveBrowserEventFileMenuRun, browser->context); | ||||||
|  |                         } | ||||||
|                     } else if(folder) { |                     } else if(folder) { | ||||||
|                         browser->callback(ArchiveBrowserEventEnterDir, browser->context); |                         browser->callback(ArchiveBrowserEventEnterDir, browser->context); | ||||||
|                     } else { |                     } else { | ||||||
|                         browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); |                         browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); | ||||||
|                     } |                     } | ||||||
|                 } else if(event->type == InputTypeLong) { |                 } else if(event->type == InputTypeLong) { | ||||||
|                     if(folder || favorites) { |                     if(move_fav_mode) { | ||||||
|  |                         browser->callback(ArchiveBrowserEventSaveFavMove, browser->context); | ||||||
|  |                     } else if(folder || favorites) { | ||||||
|                         browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); |                         browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  | |||||||
| @ -14,14 +14,15 @@ | |||||||
| #define FRAME_HEIGHT 12 | #define FRAME_HEIGHT 12 | ||||||
| #define MENU_ITEMS 4 | #define MENU_ITEMS 4 | ||||||
| #define MAX_DEPTH 32 | #define MAX_DEPTH 32 | ||||||
|  | #define MOVE_OFFSET 5 | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     ArchiveTabFavorites, |     ArchiveTabFavorites, | ||||||
|     ArchiveTabLFRFID, |  | ||||||
|     ArchiveTabSubGhz, |     ArchiveTabSubGhz, | ||||||
|  |     ArchiveTabLFRFID, | ||||||
|     ArchiveTabNFC, |     ArchiveTabNFC, | ||||||
|     ArchiveTabIButton, |  | ||||||
|     ArchiveTabIrda, |     ArchiveTabIrda, | ||||||
|  |     ArchiveTabIButton, | ||||||
|     ArchiveTabBrowser, |     ArchiveTabBrowser, | ||||||
|     ArchiveTabTotal, |     ArchiveTabTotal, | ||||||
| } ArchiveTabEnum; | } ArchiveTabEnum; | ||||||
| @ -31,16 +32,21 @@ typedef enum { | |||||||
|     ArchiveBrowserEventFileMenuClose, |     ArchiveBrowserEventFileMenuClose, | ||||||
|     ArchiveBrowserEventFileMenuRun, |     ArchiveBrowserEventFileMenuRun, | ||||||
|     ArchiveBrowserEventFileMenuPin, |     ArchiveBrowserEventFileMenuPin, | ||||||
|     ArchiveBrowserEventFileMenuRename, |     ArchiveBrowserEventFileMenuAction, | ||||||
|     ArchiveBrowserEventFileMenuDelete, |     ArchiveBrowserEventFileMenuDelete, | ||||||
|     ArchiveBrowserEventEnterDir, |     ArchiveBrowserEventEnterDir, | ||||||
|  |     ArchiveBrowserEventFavMoveUp, | ||||||
|  |     ArchiveBrowserEventFavMoveDown, | ||||||
|  |     ArchiveBrowserEventEnterFavMove, | ||||||
|  |     ArchiveBrowserEventExitFavMove, | ||||||
|  |     ArchiveBrowserEventSaveFavMove, | ||||||
|     ArchiveBrowserEventExit, |     ArchiveBrowserEventExit, | ||||||
| } ArchiveBrowserEvent; | } ArchiveBrowserEvent; | ||||||
| 
 | 
 | ||||||
| static const uint8_t file_menu_actions[MENU_ITEMS] = { | static const uint8_t file_menu_actions[MENU_ITEMS] = { | ||||||
|     [0] = ArchiveBrowserEventFileMenuRun, |     [0] = ArchiveBrowserEventFileMenuRun, | ||||||
|     [1] = ArchiveBrowserEventFileMenuPin, |     [1] = ArchiveBrowserEventFileMenuPin, | ||||||
|     [2] = ArchiveBrowserEventFileMenuRename, |     [2] = ArchiveBrowserEventFileMenuAction, | ||||||
|     [3] = ArchiveBrowserEventFileMenuDelete, |     [3] = ArchiveBrowserEventFileMenuDelete, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -68,6 +74,7 @@ typedef struct { | |||||||
|     files_array_t files; |     files_array_t files; | ||||||
| 
 | 
 | ||||||
|     uint8_t menu_idx; |     uint8_t menu_idx; | ||||||
|  |     bool move_fav; | ||||||
|     bool menu; |     bool menu; | ||||||
| 
 | 
 | ||||||
|     uint16_t idx; |     uint16_t idx; | ||||||
|  | |||||||
| @ -33,7 +33,10 @@ static void bt_battery_level_changed_callback(const void* _event, void* context) | |||||||
|     Bt* bt = context; |     Bt* bt = context; | ||||||
|     const PowerEvent* event = _event; |     const PowerEvent* event = _event; | ||||||
|     if(event->type == PowerEventTypeBatteryLevelChanged) { |     if(event->type == PowerEventTypeBatteryLevelChanged) { | ||||||
|         bt_update_battery_level(bt, event->data.battery_level); |         BtMessage message = { | ||||||
|  |             .type = BtMessageTypeUpdateBatteryLevel, | ||||||
|  |             .data.battery_level = event->data.battery_level}; | ||||||
|  |         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -61,9 +64,84 @@ Bt* bt_alloc() { | |||||||
|     PubSub* power_pubsub = power_get_pubsub(bt->power); |     PubSub* power_pubsub = power_get_pubsub(bt->power); | ||||||
|     subscribe_pubsub(power_pubsub, bt_battery_level_changed_callback, bt); |     subscribe_pubsub(power_pubsub, bt_battery_level_changed_callback, bt); | ||||||
| 
 | 
 | ||||||
|  |     // RPC
 | ||||||
|  |     bt->rpc = furi_record_open("rpc"); | ||||||
|  |     bt->rpc_sem = osSemaphoreNew(1, 0, NULL); | ||||||
|  | 
 | ||||||
|     return bt; |     return bt; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Called from GAP thread from Serial service
 | ||||||
|  | static void bt_on_data_received_callback(uint8_t* data, uint16_t size, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Bt* bt = context; | ||||||
|  | 
 | ||||||
|  |     size_t bytes_processed = rpc_feed_bytes(bt->rpc_session, data, size, 1000); | ||||||
|  |     if(bytes_processed != size) { | ||||||
|  |         FURI_LOG_E(BT_SERVICE_TAG, "Only %d of %d bytes processed by RPC", bytes_processed, size); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Called from GAP thread from Serial service
 | ||||||
|  | static void bt_on_data_sent_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Bt* bt = context; | ||||||
|  | 
 | ||||||
|  |     osSemaphoreRelease(bt->rpc_sem); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Called from RPC thread
 | ||||||
|  | static void bt_rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Bt* bt = context; | ||||||
|  | 
 | ||||||
|  |     size_t bytes_sent = 0; | ||||||
|  |     while(bytes_sent < bytes_len) { | ||||||
|  |         size_t bytes_remain = bytes_len - bytes_sent; | ||||||
|  |         if(bytes_remain > FURI_HAL_BT_PACKET_SIZE_MAX) { | ||||||
|  |             furi_hal_bt_tx(&bytes[bytes_sent], FURI_HAL_BT_PACKET_SIZE_MAX); | ||||||
|  |             bytes_sent += FURI_HAL_BT_PACKET_SIZE_MAX; | ||||||
|  |         } else { | ||||||
|  |             furi_hal_bt_tx(&bytes[bytes_sent], bytes_remain); | ||||||
|  |             bytes_sent += bytes_remain; | ||||||
|  |         } | ||||||
|  |         osSemaphoreAcquire(bt->rpc_sem, osWaitForever); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Called from GAP thread
 | ||||||
|  | static void bt_on_gap_event_callback(BleEvent event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Bt* bt = context; | ||||||
|  | 
 | ||||||
|  |     if(event.type == BleEventTypeConnected) { | ||||||
|  |         FURI_LOG_I(BT_SERVICE_TAG, "Open RPC connection"); | ||||||
|  |         bt->rpc_session = rpc_open_session(bt->rpc); | ||||||
|  |         rpc_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback, bt); | ||||||
|  |         furi_hal_bt_set_data_event_callbacks( | ||||||
|  |             bt_on_data_received_callback, bt_on_data_sent_callback, bt); | ||||||
|  |         // Update battery level
 | ||||||
|  |         PowerInfo info; | ||||||
|  |         power_get_info(bt->power, &info); | ||||||
|  |         BtMessage message = { | ||||||
|  |             .type = BtMessageTypeUpdateBatteryLevel, .data.battery_level = info.charge}; | ||||||
|  |         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  |     } else if(event.type == BleEventTypeDisconnected) { | ||||||
|  |         FURI_LOG_I(BT_SERVICE_TAG, "Close RPC connection"); | ||||||
|  |         if(bt->rpc_session) { | ||||||
|  |             rpc_close_session(bt->rpc_session); | ||||||
|  |             bt->rpc_session = NULL; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == BleEventTypeStartAdvertising || event.type == BleEventTypeStopAdvertising) { | ||||||
|  |         BtMessage message = {.type = BtMessageTypeUpdateStatusbar}; | ||||||
|  |         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  |     } else if(event.type == BleEventTypePinCodeShow) { | ||||||
|  |         BtMessage message = { | ||||||
|  |             .type = BtMessageTypePinCodeShow, .data.pin_code = event.data.pin_code}; | ||||||
|  |         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int32_t bt_srv() { | int32_t bt_srv() { | ||||||
|     Bt* bt = bt_alloc(); |     Bt* bt = bt_alloc(); | ||||||
|     furi_record_create("bt", bt); |     furi_record_create("bt", bt); | ||||||
| @ -72,11 +150,10 @@ int32_t bt_srv() { | |||||||
|         FURI_LOG_E(BT_SERVICE_TAG, "Core2 startup failed"); |         FURI_LOG_E(BT_SERVICE_TAG, "Core2 startup failed"); | ||||||
|     } else { |     } else { | ||||||
|         view_port_enabled_set(bt->statusbar_view_port, true); |         view_port_enabled_set(bt->statusbar_view_port, true); | ||||||
|         if(furi_hal_bt_init_app()) { |         if(furi_hal_bt_init_app(bt_on_gap_event_callback, bt)) { | ||||||
|             FURI_LOG_I(BT_SERVICE_TAG, "BLE stack started"); |             FURI_LOG_I(BT_SERVICE_TAG, "BLE stack started"); | ||||||
|             if(bt->bt_settings.enabled) { |             if(bt->bt_settings.enabled) { | ||||||
|                 furi_hal_bt_start_advertising(); |                 furi_hal_bt_start_advertising(); | ||||||
|                 FURI_LOG_I(BT_SERVICE_TAG, "Start advertising"); |  | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             FURI_LOG_E(BT_SERVICE_TAG, "BT App start failed"); |             FURI_LOG_E(BT_SERVICE_TAG, "BT App start failed"); | ||||||
|  | |||||||
| @ -9,12 +9,6 @@ extern "C" { | |||||||
| 
 | 
 | ||||||
| typedef struct Bt Bt; | typedef struct Bt Bt; | ||||||
| 
 | 
 | ||||||
| void bt_update_statusbar(Bt* bt); |  | ||||||
| 
 |  | ||||||
| void bt_update_battery_level(Bt* bt, uint8_t battery_level); |  | ||||||
| 
 |  | ||||||
| bool bt_pin_code_show(Bt* bt, uint32_t pin_code); |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -1,21 +0,0 @@ | |||||||
| #include "bt.h" |  | ||||||
| #include "bt_i.h" |  | ||||||
| 
 |  | ||||||
| void bt_update_statusbar(Bt* bt) { |  | ||||||
|     furi_assert(bt); |  | ||||||
|     BtMessage message = {.type = BtMessageTypeUpdateStatusbar}; |  | ||||||
|     furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void bt_update_battery_level(Bt* bt, uint8_t battery_level) { |  | ||||||
|     furi_assert(bt); |  | ||||||
|     BtMessage message = { |  | ||||||
|         .type = BtMessageTypeUpdateBatteryLevel, .data.battery_level = battery_level}; |  | ||||||
|     furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool bt_pin_code_show(Bt* bt, uint32_t pin_code) { |  | ||||||
|     furi_assert(bt); |  | ||||||
|     BtMessage message = {.type = BtMessageTypePinCodeShow, .data.pin_code = pin_code}; |  | ||||||
|     return osMessageQueuePut(bt->message_queue, &message, 0, 0) == osOK; |  | ||||||
| } |  | ||||||
| @ -11,6 +11,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <dialogs/dialogs.h> | #include <dialogs/dialogs.h> | ||||||
| #include <power/power_service/power.h> | #include <power/power_service/power.h> | ||||||
|  | #include <applications/rpc/rpc.h> | ||||||
| 
 | 
 | ||||||
| #include "../bt_settings.h" | #include "../bt_settings.h" | ||||||
| 
 | 
 | ||||||
| @ -38,4 +39,7 @@ struct Bt { | |||||||
|     DialogsApp* dialogs; |     DialogsApp* dialogs; | ||||||
|     DialogMessage* dialog_message; |     DialogMessage* dialog_message; | ||||||
|     Power* power; |     Power* power; | ||||||
|  |     Rpc* rpc; | ||||||
|  |     RpcSession* rpc_session; | ||||||
|  |     osSemaphoreId_t rpc_sem; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,11 +1,16 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file cli.h | ||||||
|  |  * Cli API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <m-string.h> | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include <m-string.h> |  | ||||||
| 
 |  | ||||||
| typedef enum { | typedef enum { | ||||||
|     CliSymbolAsciiSOH = 0x01, |     CliSymbolAsciiSOH = 0x01, | ||||||
|     CliSymbolAsciiETX = 0x03, |     CliSymbolAsciiETX = 0x03, | ||||||
| @ -21,30 +26,29 @@ typedef enum { | |||||||
| } CliSymbols; | } CliSymbols; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     CliCommandFlagDefault = 0, /** Default, loader lock is used */ |     CliCommandFlagDefault = 0, /**< Default, loader lock is used */ | ||||||
|     CliCommandFlagParallelSafe = |     CliCommandFlagParallelSafe = | ||||||
|         (1 << 0), /** Safe to run in parallel with other apps, loader lock is not used */ |         (1 << 0), /**< Safe to run in parallel with other apps, loader lock is not used */ | ||||||
|     CliCommandFlagInsomniaSafe = (1 << 1), /** Safe to run with insomnia mode on */ |     CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */ | ||||||
| } CliCommandFlag; | } CliCommandFlag; | ||||||
| 
 | 
 | ||||||
| /* Cli type
 | /** Cli type anonymous structure */ | ||||||
|  * Anonymous structure. Use cli_i.h if you need to go deeper. |  | ||||||
|  */ |  | ||||||
| typedef struct Cli Cli; | typedef struct Cli Cli; | ||||||
| 
 | 
 | ||||||
| /* Cli callback function pointer.
 | /** Cli callback function pointer. Implement this interface and use
 | ||||||
|  * Implement this interface and use add_cli_command |  * add_cli_command | ||||||
|  * @param args - string with what was passed after command |  * @param      args     string with what was passed after command | ||||||
|  * @param context - pointer to whatever you gave us on cli_add_command |  * @param      context  pointer to whatever you gave us on cli_add_command | ||||||
|  */ |  */ | ||||||
| typedef void (*CliCallback)(Cli* cli, string_t args, void* context); | typedef void (*CliCallback)(Cli* cli, string_t args, void* context); | ||||||
| 
 | 
 | ||||||
| /* Add cli command
 | /** Add cli command Registers you command callback
 | ||||||
|  * Registers you command callback |  * | ||||||
|  * @param cli - pointer to cli instance |  * @param      cli       pointer to cli instance | ||||||
|  * @param name - command name |  * @param      name      command name | ||||||
|  * @param callback - callback function |  * @param      flags     CliCommandFlag | ||||||
|  * @param context - pointer to whatever we need to pass to callback |  * @param      callback  callback function | ||||||
|  |  * @param      context   pointer to whatever we need to pass to callback | ||||||
|  */ |  */ | ||||||
| void cli_add_command( | void cli_add_command( | ||||||
|     Cli* cli, |     Cli* cli, | ||||||
| @ -53,51 +57,56 @@ void cli_add_command( | |||||||
|     CliCallback callback, |     CliCallback callback, | ||||||
|     void* context); |     void* context); | ||||||
| 
 | 
 | ||||||
| /* Print unified cmd usage tip
 | /** Print unified cmd usage tip
 | ||||||
|  * @param cmd - cmd name |  * | ||||||
|  * @param usage - usage tip |  * @param      cmd    cmd name | ||||||
|  * @param arg - arg passed by user |  * @param      usage  usage tip | ||||||
|  |  * @param      arg    arg passed by user | ||||||
|  */ |  */ | ||||||
| 
 |  | ||||||
| void cli_print_usage(const char* cmd, const char* usage, const char* arg); | void cli_print_usage(const char* cmd, const char* usage, const char* arg); | ||||||
| 
 | 
 | ||||||
| /* Delete cli command
 | /** Delete cli command
 | ||||||
|  * @param cli - pointer to cli instance |  * | ||||||
|  * @param name - command name |  * @param      cli   pointer to cli instance | ||||||
|  |  * @param      name  command name | ||||||
|  */ |  */ | ||||||
| void cli_delete_command(Cli* cli, const char* name); | void cli_delete_command(Cli* cli, const char* name); | ||||||
| 
 | 
 | ||||||
| /* Read from terminal
 | /** Read from terminal Do it only from inside of cli call.
 | ||||||
|  * Do it only from inside of cli call. |  * | ||||||
|  * @param cli - Cli instance |  * @param      cli     Cli instance | ||||||
|  * @param buffer - pointer to buffer |  * @param      buffer  pointer to buffer | ||||||
|  * @param size - size of buffer in bytes |  * @param      size    size of buffer in bytes | ||||||
|  * @return bytes written |  * | ||||||
|  |  * @return     bytes written | ||||||
|  */ |  */ | ||||||
| size_t cli_read(Cli* cli, uint8_t* buffer, size_t size); | size_t cli_read(Cli* cli, uint8_t* buffer, size_t size); | ||||||
| 
 | 
 | ||||||
| /* Not blocking check for interrupt command received
 | /** Not blocking check for interrupt command received
 | ||||||
|  * @param cli - Cli instance |  * | ||||||
|  |  * @param      cli   Cli instance | ||||||
|  |  * | ||||||
|  |  * @return     true if received | ||||||
|  */ |  */ | ||||||
| bool cli_cmd_interrupt_received(Cli* cli); | bool cli_cmd_interrupt_received(Cli* cli); | ||||||
| 
 | 
 | ||||||
| /* Write to terminal
 | /** Write to terminal Do it only from inside of cli call.
 | ||||||
|  * Do it only from inside of cli call. |  * | ||||||
|  * @param cli - Cli instance |  * @param      cli     Cli instance | ||||||
|  * @param buffer - pointer to buffer |  * @param      buffer  pointer to buffer | ||||||
|  * @param size - size of buffer in bytes |  * @param      size    size of buffer in bytes | ||||||
|  * @return bytes written |  | ||||||
|  */ |  */ | ||||||
| void cli_write(Cli* cli, const uint8_t* buffer, size_t size); | void cli_write(Cli* cli, const uint8_t* buffer, size_t size); | ||||||
| 
 | 
 | ||||||
| /* Read character
 | /** Read character
 | ||||||
|  * @param cli - Cli instance |  * | ||||||
|  * @return char |  * @param      cli   Cli instance | ||||||
|  |  * | ||||||
|  |  * @return     char | ||||||
|  */ |  */ | ||||||
| char cli_getc(Cli* cli); | char cli_getc(Cli* cli); | ||||||
| 
 | 
 | ||||||
| /* New line 
 | /** New line Send new ine sequence
 | ||||||
|  * Send new ine sequence |  | ||||||
|  */ |  */ | ||||||
| void cli_nl(); | void cli_nl(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,14 +7,47 @@ | |||||||
| #include <notification/notification-messages.h> | #include <notification/notification-messages.h> | ||||||
| #include <shci.h> | #include <shci.h> | ||||||
| 
 | 
 | ||||||
| #define ENCLAVE_SIGNATURE_KEY_SLOT 1 | #define ENCLAVE_SIGNATURE_KEY_SLOTS 10 | ||||||
| #define ENCLAVE_SIGNATURE_SIZE 16 | #define ENCLAVE_SIGNATURE_SIZE 16 | ||||||
| static const uint8_t enclave_signature_iv[16] = | 
 | ||||||
|     {0x32, 0xe6, 0xa7, 0x85, 0x20, 0xae, 0x0b, 0xf0, 0x00, 0xb6, 0x30, 0x9b, 0xd5, 0x42, 0x9e, 0xa6}; | static const uint8_t enclave_signature_iv[ENCLAVE_SIGNATURE_KEY_SLOTS][16] = { | ||||||
| static const uint8_t enclave_signature_input[ENCLAVE_SIGNATURE_SIZE] = |     {0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a}, | ||||||
|     {0xdc, 0x76, 0x15, 0x1e, 0x69, 0xe8, 0xdc, 0xd3, 0x4a, 0x71, 0x0b, 0x42, 0x71, 0xe0, 0xa9, 0x78}; |     {0x38, 0xe6, 0x6a, 0x90, 0x5e, 0x5b, 0x8a, 0xa6, 0x70, 0x30, 0x04, 0x72, 0xc2, 0x42, 0xea, 0xaf}, | ||||||
| static const uint8_t enclave_signature_expected[ENCLAVE_SIGNATURE_SIZE] = |     {0x73, 0xd5, 0x8e, 0xfb, 0x0f, 0x4b, 0xa9, 0x79, 0x0f, 0xde, 0x0e, 0x53, 0x44, 0x7d, 0xaa, 0xfd}, | ||||||
|     {0x1b, 0xb3, 0xcf, 0x16, 0xc, 0x27, 0xf7, 0xf2, 0xf0, 0x7e, 0x5f, 0xbe, 0xfe, 0x89, 0x52, 0xe1}; |     {0x3c, 0x9a, 0xf4, 0x43, 0x2b, 0xfe, 0xea, 0xae, 0x8c, 0xc6, 0xd1, 0x60, 0xd2, 0x96, 0x64, 0xa9}, | ||||||
|  |     {0x10, 0xac, 0x7b, 0x63, 0x03, 0x7f, 0x43, 0x18, 0xec, 0x9d, 0x9c, 0xc4, 0x01, 0xdc, 0x35, 0xa7}, | ||||||
|  |     {0x26, 0x21, 0x64, 0xe6, 0xd0, 0xf2, 0x47, 0x49, 0xdc, 0x36, 0xcd, 0x68, 0x0c, 0x91, 0x03, 0x44}, | ||||||
|  |     {0x7a, 0xbd, 0xce, 0x9c, 0x24, 0x7a, 0x2a, 0xb1, 0x3c, 0x4f, 0x5a, 0x7d, 0x80, 0x3e, 0xfc, 0x0d}, | ||||||
|  |     {0xcd, 0xdd, 0xd3, 0x02, 0x85, 0x65, 0x43, 0x83, 0xf9, 0xac, 0x75, 0x2f, 0x21, 0xef, 0x28, 0x6b}, | ||||||
|  |     {0xab, 0x73, 0x70, 0xe8, 0xe2, 0x56, 0x0f, 0x58, 0xab, 0x29, 0xa5, 0xb1, 0x13, 0x47, 0x5e, 0xe8}, | ||||||
|  |     {0x4f, 0x3c, 0x43, 0x77, 0xde, 0xed, 0x79, 0xa1, 0x8d, 0x4c, 0x1f, 0xfd, 0xdb, 0x96, 0x87, 0x2e}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const uint8_t enclave_signature_input[ENCLAVE_SIGNATURE_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = { | ||||||
|  |     {0x9f, 0x5c, 0xb1, 0x43, 0x17, 0x53, 0x18, 0x8c, 0x66, 0x3d, 0x39, 0x45, 0x90, 0x13, 0xa9, 0xde}, | ||||||
|  |     {0xc5, 0x98, 0xe9, 0x17, 0xb8, 0x97, 0x9e, 0x03, 0x33, 0x14, 0x13, 0x8f, 0xce, 0x74, 0x0d, 0x54}, | ||||||
|  |     {0x34, 0xba, 0x99, 0x59, 0x9f, 0x70, 0x67, 0xe9, 0x09, 0xee, 0x64, 0x0e, 0xb3, 0xba, 0xfb, 0x75}, | ||||||
|  |     {0xdc, 0xfa, 0x6c, 0x9a, 0x6f, 0x0a, 0x3e, 0xdc, 0x42, 0xf6, 0xae, 0x0d, 0x3c, 0xf7, 0x83, 0xaf}, | ||||||
|  |     {0xea, 0x2d, 0xe3, 0x1f, 0x02, 0x99, 0x1a, 0x7e, 0x6d, 0x93, 0x4c, 0xb5, 0x42, 0xf0, 0x7a, 0x9b}, | ||||||
|  |     {0x53, 0x5e, 0x04, 0xa2, 0x49, 0xa0, 0x73, 0x49, 0x56, 0xb0, 0x88, 0x8c, 0x12, 0xa0, 0xe4, 0x18}, | ||||||
|  |     {0x7d, 0xa7, 0xc5, 0x21, 0x7f, 0x12, 0x95, 0xdd, 0x4d, 0x77, 0x01, 0xfa, 0x71, 0x88, 0x2b, 0x7f}, | ||||||
|  |     {0xdc, 0x9b, 0xc5, 0xa7, 0x6b, 0x84, 0x5c, 0x37, 0x7c, 0xec, 0x05, 0xa1, 0x9f, 0x91, 0x17, 0x3b}, | ||||||
|  |     {0xea, 0xcf, 0xd9, 0x9b, 0x86, 0xcd, 0x2b, 0x43, 0x54, 0x45, 0x82, 0xc6, 0xfe, 0x73, 0x1a, 0x1a}, | ||||||
|  |     {0x77, 0xb8, 0x1b, 0x90, 0xb4, 0xb7, 0x32, 0x76, 0x8f, 0x8a, 0x57, 0x06, 0xc7, 0xdd, 0x08, 0x90}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const uint8_t enclave_signature_expected[ENCLAVE_SIGNATURE_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = { | ||||||
|  |     {0xe9, 0x9a, 0xce, 0xe9, 0x4d, 0xe1, 0x7f, 0x55, 0xcb, 0x8a, 0xbf, 0xf2, 0x4d, 0x98, 0x27, 0x67}, | ||||||
|  |     {0x34, 0x27, 0xa7, 0xea, 0xa8, 0x98, 0x66, 0x9b, 0xed, 0x43, 0xd3, 0x93, 0xb5, 0xa2, 0x87, 0x8e}, | ||||||
|  |     {0x6c, 0xf3, 0x01, 0x78, 0x53, 0x1b, 0x11, 0x32, 0xf0, 0x27, 0x2f, 0xe3, 0x7d, 0xa6, 0xe2, 0xfd}, | ||||||
|  |     {0xdf, 0x7f, 0x37, 0x65, 0x2f, 0xdb, 0x7c, 0xcf, 0x5b, 0xb6, 0xe4, 0x9c, 0x63, 0xc5, 0x0f, 0xe0}, | ||||||
|  |     {0x9b, 0x5c, 0xee, 0x44, 0x0e, 0xd1, 0xcb, 0x5f, 0x28, 0x9f, 0x12, 0x17, 0x59, 0x64, 0x40, 0xbb}, | ||||||
|  |     {0x94, 0xc2, 0x09, 0x98, 0x62, 0xa7, 0x2b, 0x93, 0xed, 0x36, 0x1f, 0x10, 0xbc, 0x26, 0xbd, 0x41}, | ||||||
|  |     {0x4d, 0xb2, 0x2b, 0xc5, 0x96, 0x47, 0x61, 0xf4, 0x16, 0xe0, 0x81, 0xc3, 0x8e, 0xb9, 0x9c, 0x9b}, | ||||||
|  |     {0xc3, 0x6b, 0x83, 0x55, 0x90, 0x38, 0x0f, 0xea, 0xd1, 0x65, 0xbf, 0x32, 0x4f, 0x8e, 0x62, 0x5b}, | ||||||
|  |     {0x8d, 0x5e, 0x27, 0xbc, 0x14, 0x4f, 0x08, 0xa8, 0x2b, 0x14, 0x89, 0x5e, 0xdf, 0x77, 0x04, 0x31}, | ||||||
|  |     {0xc9, 0xf7, 0x03, 0xf1, 0x6c, 0x65, 0xad, 0x49, 0x74, 0xbe, 0x00, 0x54, 0xfd, 0xa6, 0x9c, 0x32}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| /* 
 | /* 
 | ||||||
|  * Device Info Command |  * Device Info Command | ||||||
| @ -99,15 +132,23 @@ void cli_command_device_info(Cli* cli, string_t args, void* context) { | |||||||
| 
 | 
 | ||||||
|         // Signature verification
 |         // Signature verification
 | ||||||
|         uint8_t buffer[ENCLAVE_SIGNATURE_SIZE]; |         uint8_t buffer[ENCLAVE_SIGNATURE_SIZE]; | ||||||
|         bool enclave_valid = false; |         size_t enclave_valid_keys = 0; | ||||||
|         if(furi_hal_crypto_store_load_key(ENCLAVE_SIGNATURE_KEY_SLOT, enclave_signature_iv)) { |         for(size_t key_slot = 0; key_slot < ENCLAVE_SIGNATURE_KEY_SLOTS; key_slot++) { | ||||||
|             if(furi_hal_crypto_encrypt(enclave_signature_input, buffer, ENCLAVE_SIGNATURE_SIZE)) { |             if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) { | ||||||
|                 enclave_valid = |                 if(furi_hal_crypto_encrypt( | ||||||
|                     memcmp(buffer, enclave_signature_expected, ENCLAVE_SIGNATURE_SIZE) == 0; |                        enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) { | ||||||
|  |                     enclave_valid_keys += memcmp( | ||||||
|  |                                               buffer, | ||||||
|  |                                               enclave_signature_expected[key_slot], | ||||||
|  |                                               ENCLAVE_SIGNATURE_SIZE) == 0; | ||||||
|  |                 } | ||||||
|  |                 furi_hal_crypto_store_unload_key(key_slot + 1); | ||||||
|             } |             } | ||||||
|             furi_hal_crypto_store_unload_key(ENCLAVE_SIGNATURE_KEY_SLOT); |  | ||||||
|         } |         } | ||||||
|         printf("enclave_valid       : %s\r\n", enclave_valid ? "true" : "false"); |         printf("enclave_valid_keys  : %d\r\n", enclave_valid_keys); | ||||||
|  |         printf( | ||||||
|  |             "enclave_valid       : %s\r\n", | ||||||
|  |             (enclave_valid_keys == ENCLAVE_SIGNATURE_KEY_SLOTS) ? "true" : "false"); | ||||||
|     } else { |     } else { | ||||||
|         printf("radio_alive         : false\r\n"); |         printf("radio_alive         : false\r\n"); | ||||||
|     } |     } | ||||||
|  | |||||||
							
								
								
									
										364
									
								
								applications/debug_tools/bad_usb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										364
									
								
								applications/debug_tools/bad_usb.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,364 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <furi-hal.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <input/input.h> | ||||||
|  | #include <lib/toolbox/args.h> | ||||||
|  | #include <furi-hal-usb-hid.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     EventTypeInput, | ||||||
|  |     EventTypeWorkerState, | ||||||
|  | } EventType; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     WorkerStateDone, | ||||||
|  |     WorkerStateNoFile, | ||||||
|  |     WorkerStateScriptError, | ||||||
|  |     WorkerStateDisconnected, | ||||||
|  | } WorkerState; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     AppStateWait, | ||||||
|  |     AppStateRunning, | ||||||
|  |     AppStateError, | ||||||
|  |     AppStateExit, | ||||||
|  | } AppState; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     WorkerCmdStart = (1 << 0), | ||||||
|  |     WorkerCmdStop = (1 << 1), | ||||||
|  | } WorkerCommandFlags; | ||||||
|  | 
 | ||||||
|  | // Event message from worker
 | ||||||
|  | typedef struct { | ||||||
|  |     WorkerState state; | ||||||
|  |     uint16_t line; | ||||||
|  | } BadUsbWorkerState; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     union { | ||||||
|  |         InputEvent input; | ||||||
|  |         BadUsbWorkerState worker; | ||||||
|  |     }; | ||||||
|  |     EventType type; | ||||||
|  | } BadUsbEvent; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint32_t defdelay; | ||||||
|  |     char msg_text[32]; | ||||||
|  |     osThreadAttr_t thread_attr; | ||||||
|  |     osThreadId_t thread; | ||||||
|  |     osMessageQueueId_t event_queue; | ||||||
|  | } BadUsbParams; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     char* name; | ||||||
|  |     uint16_t keycode; | ||||||
|  | } DuckyKey; | ||||||
|  | 
 | ||||||
|  | static const DuckyKey ducky_keys[] = { | ||||||
|  |     {"CTRL", KEY_MOD_LEFT_CTRL}, | ||||||
|  |     {"CONTROL", KEY_MOD_LEFT_CTRL}, | ||||||
|  |     {"SHIFT", KEY_MOD_LEFT_SHIFT}, | ||||||
|  |     {"ALT", KEY_MOD_LEFT_ALT}, | ||||||
|  |     {"GUI", KEY_MOD_LEFT_GUI}, | ||||||
|  |     {"WINDOWS", KEY_MOD_LEFT_GUI}, | ||||||
|  | 
 | ||||||
|  |     {"DOWNARROW", KEY_DOWN_ARROW}, | ||||||
|  |     {"DOWN", KEY_DOWN_ARROW}, | ||||||
|  |     {"LEFTARROW", KEY_LEFT_ARROW}, | ||||||
|  |     {"LEFT", KEY_LEFT_ARROW}, | ||||||
|  |     {"RIGHTARROW", KEY_RIGHT_ARROW}, | ||||||
|  |     {"RIGHT", KEY_RIGHT_ARROW}, | ||||||
|  |     {"UPARROW", KEY_UP_ARROW}, | ||||||
|  |     {"UP", KEY_UP_ARROW}, | ||||||
|  | 
 | ||||||
|  |     {"ENTER", KEY_ENTER}, | ||||||
|  |     {"BREAK", KEY_PAUSE}, | ||||||
|  |     {"PAUSE", KEY_PAUSE}, | ||||||
|  |     {"CAPSLOCK", KEY_CAPS_LOCK}, | ||||||
|  |     {"DELETE", KEY_DELETE}, | ||||||
|  |     {"BACKSPACE", KEY_BACKSPACE}, | ||||||
|  |     {"END", KEY_END}, | ||||||
|  |     {"ESC", KEY_ESC}, | ||||||
|  |     {"ESCAPE", KEY_ESC}, | ||||||
|  |     {"HOME", KEY_HOME}, | ||||||
|  |     {"INSERT", KEY_INSERT}, | ||||||
|  |     {"NUMLOCK", KEY_NUM_LOCK}, | ||||||
|  |     {"PAGEUP", KEY_PAGE_UP}, | ||||||
|  |     {"PAGEDOWN", KEY_PAGE_DOWN}, | ||||||
|  |     {"PRINTSCREEN", KEY_PRINT}, | ||||||
|  |     {"SCROLLOCK", KEY_SCROLL_LOCK}, | ||||||
|  |     {"SPACE", KEY_SPACE}, | ||||||
|  |     {"TAB", KEY_TAB}, | ||||||
|  |     {"MENU", KEY_APPLICATION}, | ||||||
|  |     {"APP", KEY_APPLICATION}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const char ducky_cmd_comment[] = {"REM"}; | ||||||
|  | static const char ducky_cmd_delay[] = {"DELAY"}; | ||||||
|  | static const char ducky_cmd_string[] = {"STRING"}; | ||||||
|  | static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY"}; | ||||||
|  | static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY"}; | ||||||
|  | 
 | ||||||
|  | static bool ducky_get_delay_val(char* param, uint32_t* val) { | ||||||
|  |     uint32_t delay_val = 0; | ||||||
|  |     if(sscanf(param, "%lu", &delay_val) == 1) { | ||||||
|  |         *val = delay_val; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool ducky_string(char* param) { | ||||||
|  |     uint32_t i = 0; | ||||||
|  |     while(param[i] != '\0') { | ||||||
|  |         furi_hal_hid_kb_press(HID_ASCII_TO_KEY(param[i])); | ||||||
|  |         furi_hal_hid_kb_release(HID_ASCII_TO_KEY(param[i])); | ||||||
|  |         i++; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint16_t ducky_get_keycode(char* param, bool accept_chars) { | ||||||
|  |     for(uint8_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { | ||||||
|  |         if(strncmp(param, ducky_keys[i].name, strlen(ducky_keys[i].name)) == 0) | ||||||
|  |             return ducky_keys[i].keycode; | ||||||
|  |     } | ||||||
|  |     if((accept_chars) && (strlen(param) > 0)) { | ||||||
|  |         return (HID_ASCII_TO_KEY(param[0]) & 0xFF); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool ducky_parse_line(string_t line, BadUsbParams* app) { | ||||||
|  |     //uint32_t line_len = string_size(line);
 | ||||||
|  |     char* line_t = (char*)string_get_cstr(line); | ||||||
|  |     bool state = false; | ||||||
|  | 
 | ||||||
|  |     // General commands
 | ||||||
|  |     if(strncmp(line_t, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) { | ||||||
|  |         // REM - comment line
 | ||||||
|  |         return true; | ||||||
|  |     } else if(strncmp(line_t, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) { | ||||||
|  |         // DELAY
 | ||||||
|  |         line_t = &line_t[args_get_first_word_length(line) + 1]; | ||||||
|  |         uint32_t delay_val = 0; | ||||||
|  |         state = ducky_get_delay_val(line_t, &delay_val); | ||||||
|  |         if((state) && (delay_val > 0)) { | ||||||
|  |             // Using ThreadFlagsWait as delay function allows exiting task on WorkerCmdStop command
 | ||||||
|  |             if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny | osFlagsNoClear, delay_val) == | ||||||
|  |                WorkerCmdStop) | ||||||
|  |                 return true; | ||||||
|  |         } | ||||||
|  |         return state; | ||||||
|  |     } else if( | ||||||
|  |         (strncmp(line_t, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) || | ||||||
|  |         (strncmp(line_t, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) { | ||||||
|  |         // DEFAULT_DELAY
 | ||||||
|  |         line_t = &line_t[args_get_first_word_length(line) + 1]; | ||||||
|  |         return ducky_get_delay_val(line_t, &app->defdelay); | ||||||
|  |     } else if(strncmp(line_t, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { | ||||||
|  |         // STRING
 | ||||||
|  |         if(app->defdelay > 0) { | ||||||
|  |             if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny | osFlagsNoClear, app->defdelay) == | ||||||
|  |                WorkerCmdStop) | ||||||
|  |                 return true; | ||||||
|  |         } | ||||||
|  |         line_t = &line_t[args_get_first_word_length(line) + 1]; | ||||||
|  |         return ducky_string(line_t); | ||||||
|  |     } else { | ||||||
|  |         // Special keys + modifiers
 | ||||||
|  |         uint16_t key = ducky_get_keycode(line_t, false); | ||||||
|  |         if(key == KEY_NONE) return false; | ||||||
|  |         if((key & 0xFF00) != 0) { | ||||||
|  |             // It's a modifier key
 | ||||||
|  |             line_t = &line_t[args_get_first_word_length(line) + 1]; | ||||||
|  |             key |= ducky_get_keycode(line_t, true); | ||||||
|  |         } | ||||||
|  |         if(app->defdelay > 0) { | ||||||
|  |             if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny | osFlagsNoClear, app->defdelay) == | ||||||
|  |                WorkerCmdStop) | ||||||
|  |                 return true; | ||||||
|  |         } | ||||||
|  |         furi_hal_hid_kb_press(key); | ||||||
|  |         furi_hal_hid_kb_release(key); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void badusb_worker(void* context) { | ||||||
|  |     BadUsbParams* app = context; | ||||||
|  |     FURI_LOG_I("BadUSB worker", "Init"); | ||||||
|  |     File* script_file = storage_file_alloc(furi_record_open("storage")); | ||||||
|  |     BadUsbEvent evt; | ||||||
|  |     string_t line; | ||||||
|  |     uint32_t line_cnt = 0; | ||||||
|  |     string_init(line); | ||||||
|  |     if(storage_file_open(script_file, "/ext/badusb.txt", FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||||
|  |         char buffer[16]; | ||||||
|  |         uint16_t ret; | ||||||
|  |         uint32_t flags = | ||||||
|  |             osThreadFlagsWait(WorkerCmdStart | WorkerCmdStop, osFlagsWaitAny, osWaitForever); | ||||||
|  |         if(flags & WorkerCmdStart) { | ||||||
|  |             FURI_LOG_I("BadUSB worker", "Start"); | ||||||
|  |             do { | ||||||
|  |                 ret = storage_file_read(script_file, buffer, 16); | ||||||
|  |                 for(uint16_t i = 0; i < ret; i++) { | ||||||
|  |                     if(buffer[i] == '\n' && string_size(line) > 0) { | ||||||
|  |                         line_cnt++; | ||||||
|  |                         if(ducky_parse_line(line, app) == false) { | ||||||
|  |                             ret = 0; | ||||||
|  |                             FURI_LOG_E("BadUSB worker", "Unknown command at line %lu", line_cnt); | ||||||
|  |                             evt.type = EventTypeWorkerState; | ||||||
|  |                             evt.worker.state = WorkerStateScriptError; | ||||||
|  |                             evt.worker.line = line_cnt; | ||||||
|  |                             osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever); | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                         flags = osThreadFlagsGet(); | ||||||
|  |                         if(flags == WorkerCmdStop) { | ||||||
|  |                             ret = 0; | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                         string_clean(line); | ||||||
|  |                     } else { | ||||||
|  |                         string_push_back(line, buffer[i]); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } while(ret > 0); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         FURI_LOG_E("BadUSB worker", "Script file open error"); | ||||||
|  |         evt.type = EventTypeWorkerState; | ||||||
|  |         evt.worker.state = WorkerStateNoFile; | ||||||
|  |         osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever); | ||||||
|  |     } | ||||||
|  |     string_clean(line); | ||||||
|  |     string_clear(line); | ||||||
|  | 
 | ||||||
|  |     furi_hal_hid_kb_release_all(); | ||||||
|  |     storage_file_close(script_file); | ||||||
|  |     storage_file_free(script_file); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_I("BadUSB worker", "End"); | ||||||
|  |     evt.type = EventTypeWorkerState; | ||||||
|  |     evt.worker.state = WorkerStateDone; | ||||||
|  |     osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever); | ||||||
|  | 
 | ||||||
|  |     osThreadExit(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void bad_usb_render_callback(Canvas* canvas, void* ctx) { | ||||||
|  |     BadUsbParams* app = (BadUsbParams*)ctx; | ||||||
|  | 
 | ||||||
|  |     canvas_clear(canvas); | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  |     canvas_draw_str(canvas, 0, 10, "Bad USB test"); | ||||||
|  | 
 | ||||||
|  |     if(strlen(app->msg_text) > 0) { | ||||||
|  |         canvas_set_font(canvas, FontSecondary); | ||||||
|  |         canvas_draw_str(canvas, 0, 62, app->msg_text); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void bad_usb_input_callback(InputEvent* input_event, void* ctx) { | ||||||
|  |     osMessageQueueId_t event_queue = ctx; | ||||||
|  | 
 | ||||||
|  |     BadUsbEvent event; | ||||||
|  |     event.type = EventTypeInput; | ||||||
|  |     event.input = *input_event; | ||||||
|  |     osMessageQueuePut(event_queue, &event, 0, osWaitForever); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t bad_usb_app(void* p) { | ||||||
|  |     BadUsbParams* app = furi_alloc(sizeof(BadUsbParams)); | ||||||
|  |     app->event_queue = osMessageQueueNew(8, sizeof(BadUsbEvent), NULL); | ||||||
|  |     furi_check(app->event_queue); | ||||||
|  |     ViewPort* view_port = view_port_alloc(); | ||||||
|  | 
 | ||||||
|  |     UsbMode usb_mode_prev = furi_hal_usb_get_config(); | ||||||
|  |     furi_hal_usb_set_config(UsbModeHid); | ||||||
|  | 
 | ||||||
|  |     view_port_draw_callback_set(view_port, bad_usb_render_callback, app); | ||||||
|  |     view_port_input_callback_set(view_port, bad_usb_input_callback, app->event_queue); | ||||||
|  | 
 | ||||||
|  |     // Open GUI and register view_port
 | ||||||
|  |     Gui* gui = furi_record_open("gui"); | ||||||
|  |     gui_add_view_port(gui, view_port, GuiLayerFullscreen); | ||||||
|  | 
 | ||||||
|  |     app->thread = NULL; | ||||||
|  |     app->thread_attr.name = "bad_usb_worker"; | ||||||
|  |     app->thread_attr.stack_size = 2048; | ||||||
|  |     app->thread = osThreadNew(badusb_worker, app, &app->thread_attr); | ||||||
|  |     bool worker_running = true; | ||||||
|  |     AppState app_state = AppStateWait; | ||||||
|  |     snprintf(app->msg_text, sizeof(app->msg_text), "Press [OK] to start"); | ||||||
|  |     view_port_update(view_port); | ||||||
|  | 
 | ||||||
|  |     BadUsbEvent event; | ||||||
|  |     while(1) { | ||||||
|  |         osStatus_t event_status = osMessageQueueGet(app->event_queue, &event, NULL, osWaitForever); | ||||||
|  | 
 | ||||||
|  |         if(event_status == osOK) { | ||||||
|  |             if(event.type == EventTypeInput) { | ||||||
|  |                 if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) { | ||||||
|  |                     if(worker_running) { | ||||||
|  |                         osThreadFlagsSet(app->thread, WorkerCmdStop); | ||||||
|  |                         app_state = AppStateExit; | ||||||
|  |                     } else | ||||||
|  |                         break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(event.input.type == InputTypeShort && event.input.key == InputKeyOk) { | ||||||
|  |                     if(worker_running) { | ||||||
|  |                         app_state = AppStateRunning; | ||||||
|  |                         osThreadFlagsSet(app->thread, WorkerCmdStart); | ||||||
|  |                         snprintf(app->msg_text, sizeof(app->msg_text), "Running..."); | ||||||
|  |                         view_port_update(view_port); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else if(event.type == EventTypeWorkerState) { | ||||||
|  |                 FURI_LOG_I("BadUSB app", "ev: %d", event.worker.state); | ||||||
|  |                 if(event.worker.state == WorkerStateDone) { | ||||||
|  |                     worker_running = false; | ||||||
|  |                     if(app_state == AppStateExit) | ||||||
|  |                         break; | ||||||
|  |                     else if(app_state == AppStateRunning) { | ||||||
|  |                         //done
 | ||||||
|  |                         app->thread = osThreadNew(badusb_worker, app, &app->thread_attr); | ||||||
|  |                         worker_running = true; | ||||||
|  |                         app_state = AppStateWait; | ||||||
|  |                         snprintf(app->msg_text, sizeof(app->msg_text), "Press [OK] to start"); | ||||||
|  |                         view_port_update(view_port); | ||||||
|  |                     } | ||||||
|  |                 } else if(event.worker.state == WorkerStateNoFile) { | ||||||
|  |                     app_state = AppStateError; | ||||||
|  |                     snprintf(app->msg_text, sizeof(app->msg_text), "File not found!"); | ||||||
|  |                     view_port_update(view_port); | ||||||
|  |                 } else if(event.worker.state == WorkerStateScriptError) { | ||||||
|  |                     app_state = AppStateError; | ||||||
|  |                     snprintf( | ||||||
|  |                         app->msg_text, | ||||||
|  |                         sizeof(app->msg_text), | ||||||
|  |                         "Error at line %u", | ||||||
|  |                         event.worker.line); | ||||||
|  |                     view_port_update(view_port); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     furi_hal_usb_set_config(usb_mode_prev); | ||||||
|  | 
 | ||||||
|  |     // remove & free all stuff created by app
 | ||||||
|  |     gui_remove_view_port(gui, view_port); | ||||||
|  |     view_port_free(view_port); | ||||||
|  | 
 | ||||||
|  |     osMessageQueueDelete(app->event_queue); | ||||||
|  |     free(app); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										121
									
								
								applications/debug_tools/usb_mouse.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								applications/debug_tools/usb_mouse.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <furi-hal.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <input/input.h> | ||||||
|  | 
 | ||||||
|  | #define MOUSE_MOVE_SHORT 5 | ||||||
|  | #define MOUSE_MOVE_LONG 20 | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     EventTypeInput, | ||||||
|  | } EventType; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     union { | ||||||
|  |         InputEvent input; | ||||||
|  |     }; | ||||||
|  |     EventType type; | ||||||
|  | } UsbMouseEvent; | ||||||
|  | 
 | ||||||
|  | static void usb_mouse_render_callback(Canvas* canvas, void* ctx) { | ||||||
|  |     canvas_clear(canvas); | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  |     canvas_draw_str(canvas, 0, 10, "USB Mouse demo"); | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  |     canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void usb_mouse_input_callback(InputEvent* input_event, void* ctx) { | ||||||
|  |     osMessageQueueId_t event_queue = ctx; | ||||||
|  | 
 | ||||||
|  |     UsbMouseEvent event; | ||||||
|  |     event.type = EventTypeInput; | ||||||
|  |     event.input = *input_event; | ||||||
|  |     osMessageQueuePut(event_queue, &event, 0, osWaitForever); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t usb_mouse_app(void* p) { | ||||||
|  |     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(UsbMouseEvent), NULL); | ||||||
|  |     furi_check(event_queue); | ||||||
|  |     ViewPort* view_port = view_port_alloc(); | ||||||
|  | 
 | ||||||
|  |     UsbMode usb_mode_prev = furi_hal_usb_get_config(); | ||||||
|  |     furi_hal_usb_set_config(UsbModeHid); | ||||||
|  | 
 | ||||||
|  |     view_port_draw_callback_set(view_port, usb_mouse_render_callback, NULL); | ||||||
|  |     view_port_input_callback_set(view_port, usb_mouse_input_callback, event_queue); | ||||||
|  | 
 | ||||||
|  |     // Open GUI and register view_port
 | ||||||
|  |     Gui* gui = furi_record_open("gui"); | ||||||
|  |     gui_add_view_port(gui, view_port, GuiLayerFullscreen); | ||||||
|  | 
 | ||||||
|  |     UsbMouseEvent event; | ||||||
|  |     while(1) { | ||||||
|  |         osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, osWaitForever); | ||||||
|  | 
 | ||||||
|  |         if(event_status == osOK) { | ||||||
|  |             if(event.type == EventTypeInput) { | ||||||
|  |                 if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) { | ||||||
|  |                     furi_hal_hid_mouse_press(HID_MOUSE_BTN_RIGHT); | ||||||
|  |                     furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(event.input.key == InputKeyOk) { | ||||||
|  |                     if(event.input.type == InputTypePress) { | ||||||
|  |                         furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); | ||||||
|  |                     } else if(event.input.type == InputTypeRelease) { | ||||||
|  |                         furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(event.input.key == InputKeyRight) { | ||||||
|  |                     if(event.input.type == InputTypePress) { | ||||||
|  |                         furi_hal_hid_mouse_move(MOUSE_MOVE_SHORT, 0); | ||||||
|  |                     } else if(event.input.type == InputTypeRepeat) { | ||||||
|  |                         furi_hal_hid_mouse_move(MOUSE_MOVE_LONG, 0); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(event.input.key == InputKeyLeft) { | ||||||
|  |                     if(event.input.type == InputTypePress) { | ||||||
|  |                         furi_hal_hid_mouse_move(-MOUSE_MOVE_SHORT, 0); | ||||||
|  |                     } else if(event.input.type == InputTypeRepeat) { | ||||||
|  |                         furi_hal_hid_mouse_move(-MOUSE_MOVE_LONG, 0); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(event.input.key == InputKeyDown) { | ||||||
|  |                     if(event.input.type == InputTypePress) { | ||||||
|  |                         furi_hal_hid_mouse_move(0, MOUSE_MOVE_SHORT); | ||||||
|  |                     } else if(event.input.type == InputTypeRepeat) { | ||||||
|  |                         furi_hal_hid_mouse_move(0, MOUSE_MOVE_LONG); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(event.input.key == InputKeyUp) { | ||||||
|  |                     if(event.input.type == InputTypePress) { | ||||||
|  |                         furi_hal_hid_mouse_move(0, -MOUSE_MOVE_SHORT); | ||||||
|  |                     } else if(event.input.type == InputTypeRepeat) { | ||||||
|  |                         furi_hal_hid_mouse_move(0, -MOUSE_MOVE_LONG); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         view_port_update(view_port); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_usb_set_config(usb_mode_prev); | ||||||
|  | 
 | ||||||
|  |     // remove & free all stuff created by app
 | ||||||
|  |     gui_remove_view_port(gui, view_port); | ||||||
|  |     view_port_free(view_port); | ||||||
|  |     osMessageQueueDelete(event_queue); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										103
									
								
								applications/debug_tools/usb_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								applications/debug_tools/usb_test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <furi-hal.h> | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <cmsis_os.h> | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     Gui* gui; | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  |     Submenu* submenu; | ||||||
|  | } UsbTestApp; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     UsbTestSubmenuIndexEnable, | ||||||
|  |     UsbTestSubmenuIndexDisable, | ||||||
|  |     UsbTestSubmenuIndexVcpSingle, | ||||||
|  |     UsbTestSubmenuIndexVcpDual, | ||||||
|  |     UsbTestSubmenuIndexHid, | ||||||
|  |     UsbTestSubmenuIndexHidU2F, | ||||||
|  | } SubmenuIndex; | ||||||
|  | 
 | ||||||
|  | void usb_test_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     //UsbTestApp* app = context;
 | ||||||
|  |     if(index == UsbTestSubmenuIndexEnable) { | ||||||
|  |         furi_hal_usb_enable(); | ||||||
|  |     } else if(index == UsbTestSubmenuIndexDisable) { | ||||||
|  |         furi_hal_usb_disable(); | ||||||
|  |     } else if(index == UsbTestSubmenuIndexVcpSingle) { | ||||||
|  |         furi_hal_usb_set_config(UsbModeVcpSingle); | ||||||
|  |     } else if(index == UsbTestSubmenuIndexVcpDual) { | ||||||
|  |         furi_hal_usb_set_config(UsbModeVcpDual); | ||||||
|  |     } else if(index == UsbTestSubmenuIndexHid) { | ||||||
|  |         furi_hal_usb_set_config(UsbModeHid); | ||||||
|  |     } else if(index == UsbTestSubmenuIndexHidU2F) { | ||||||
|  |         //furi_hal_usb_set_config(UsbModeU2F);
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t usb_test_exit(void* context) { | ||||||
|  |     return VIEW_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | UsbTestApp* usb_test_app_alloc() { | ||||||
|  |     UsbTestApp* app = furi_alloc(sizeof(UsbTestApp)); | ||||||
|  | 
 | ||||||
|  |     // Gui
 | ||||||
|  |     app->gui = furi_record_open("gui"); | ||||||
|  | 
 | ||||||
|  |     // View dispatcher
 | ||||||
|  |     app->view_dispatcher = view_dispatcher_alloc(); | ||||||
|  |     view_dispatcher_enable_queue(app->view_dispatcher); | ||||||
|  |     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||||
|  | 
 | ||||||
|  |     // Views
 | ||||||
|  |     app->submenu = submenu_alloc(); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->submenu, "Enable", UsbTestSubmenuIndexEnable, usb_test_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->submenu, "Disable", UsbTestSubmenuIndexDisable, usb_test_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->submenu, "Single VCP", UsbTestSubmenuIndexVcpSingle, usb_test_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->submenu, "Dual VCP", UsbTestSubmenuIndexVcpDual, usb_test_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->submenu, "HID KB+Mouse", UsbTestSubmenuIndexHid, usb_test_submenu_callback, app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         app->submenu, "TODO: HID U2F", UsbTestSubmenuIndexHidU2F, usb_test_submenu_callback, app); | ||||||
|  |     view_set_previous_callback(submenu_get_view(app->submenu), usb_test_exit); | ||||||
|  |     view_dispatcher_add_view(app->view_dispatcher, 0, submenu_get_view(app->submenu)); | ||||||
|  | 
 | ||||||
|  |     // Switch to menu
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, 0); | ||||||
|  | 
 | ||||||
|  |     return app; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void usb_test_app_free(UsbTestApp* app) { | ||||||
|  |     furi_assert(app); | ||||||
|  | 
 | ||||||
|  |     // Free views
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, 0); | ||||||
|  |     submenu_free(app->submenu); | ||||||
|  |     view_dispatcher_free(app->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     // Close gui record
 | ||||||
|  |     furi_record_close("gui"); | ||||||
|  |     app->gui = NULL; | ||||||
|  | 
 | ||||||
|  |     // Free rest
 | ||||||
|  |     free(app); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t usb_test_app(void* p) { | ||||||
|  |     UsbTestApp* app = usb_test_app_alloc(); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_run(app->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     usb_test_app_free(app); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
| @ -40,7 +40,7 @@ Desktop* desktop_alloc() { | |||||||
|     desktop->locked_view = desktop_locked_alloc(); |     desktop->locked_view = desktop_locked_alloc(); | ||||||
|     desktop->debug_view = desktop_debug_alloc(); |     desktop->debug_view = desktop_debug_alloc(); | ||||||
|     desktop->first_start_view = desktop_first_start_alloc(); |     desktop->first_start_view = desktop_first_start_alloc(); | ||||||
|     desktop->hw_mismatch_view = desktop_hw_mismatch_alloc(); |     desktop->hw_mismatch_popup = popup_alloc(); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view)); |         desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view)); | ||||||
| @ -61,7 +61,7 @@ Desktop* desktop_alloc() { | |||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         desktop->view_dispatcher, |         desktop->view_dispatcher, | ||||||
|         DesktopViewHwMismatch, |         DesktopViewHwMismatch, | ||||||
|         desktop_hw_mismatch_get_view(desktop->hw_mismatch_view)); |         popup_get_view(desktop->hw_mismatch_popup)); | ||||||
| 
 | 
 | ||||||
|     // Lock icon
 |     // Lock icon
 | ||||||
|     desktop->lock_viewport = view_port_alloc(); |     desktop->lock_viewport = view_port_alloc(); | ||||||
| @ -91,7 +91,7 @@ void desktop_free(Desktop* desktop) { | |||||||
|     desktop_locked_free(desktop->locked_view); |     desktop_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); |     desktop_first_start_free(desktop->first_start_view); | ||||||
|     desktop_hw_mismatch_free(desktop->hw_mismatch_view); |     popup_free(desktop->hw_mismatch_popup); | ||||||
| 
 | 
 | ||||||
|     furi_record_close("gui"); |     furi_record_close("gui"); | ||||||
|     desktop->gui = NULL; |     desktop->gui = NULL; | ||||||
|  | |||||||
| @ -7,24 +7,21 @@ | |||||||
| 
 | 
 | ||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/modules/popup.h> | ||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| #include <assets_icons.h> | #include <assets_icons.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| 
 | 
 | ||||||
| #include "views/desktop_main.h" | #include "views/desktop_main.h" | ||||||
| #include "views/desktop_first_start.h" | #include "views/desktop_first_start.h" | ||||||
| #include "views/desktop_hw_mismatch.h" |  | ||||||
| #include "views/desktop_lock_menu.h" | #include "views/desktop_lock_menu.h" | ||||||
| #include "views/desktop_locked.h" | #include "views/desktop_locked.h" | ||||||
| #include "views/desktop_debug.h" | #include "views/desktop_debug.h" | ||||||
| 
 | 
 | ||||||
| #include "scenes/desktop_scene.h" | #include "scenes/desktop_scene.h" | ||||||
| 
 | #include "helpers/desktop_animation.h" | ||||||
| #include "desktop/desktop_settings/desktop_settings.h" | #include "desktop/desktop_settings/desktop_settings.h" | ||||||
| 
 | 
 | ||||||
| #define HINT_TIMEOUT_L 2 |  | ||||||
| #define HINT_TIMEOUT_H 11 |  | ||||||
| 
 |  | ||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopViewMain, |     DesktopViewMain, | ||||||
|     DesktopViewLockMenu, |     DesktopViewLockMenu, | ||||||
| @ -44,7 +41,7 @@ struct Desktop { | |||||||
|     SceneManager* scene_manager; |     SceneManager* scene_manager; | ||||||
| 
 | 
 | ||||||
|     DesktopFirstStartView* first_start_view; |     DesktopFirstStartView* first_start_view; | ||||||
|     DesktopHwMismatchView* hw_mismatch_view; |     Popup* hw_mismatch_popup; | ||||||
|     DesktopMainView* main_view; |     DesktopMainView* main_view; | ||||||
|     DesktopLockMenuView* lock_menu; |     DesktopLockMenuView* lock_menu; | ||||||
|     DesktopLockedView* locked_view; |     DesktopLockedView* locked_view; | ||||||
|  | |||||||
							
								
								
									
										26
									
								
								applications/desktop/helpers/desktop_animation.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								applications/desktop/helpers/desktop_animation.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | #include "desktop_animation.h" | ||||||
|  | 
 | ||||||
|  | static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64}; | ||||||
|  | 
 | ||||||
|  | const Icon* desktop_get_icon() { | ||||||
|  |     uint8_t new = 0; | ||||||
|  | 
 | ||||||
|  | #if 0 | ||||||
|  |     // checking dolphin state here to choose appropriate animation
 | ||||||
|  | 
 | ||||||
|  |     Dolphin* dolphin = furi_record_open("dolphin"); | ||||||
|  |     DolphinStats stats = dolphin_stats(dolphin); | ||||||
|  |     float timediff = fabs(difftime(stats.timestamp, dolphin_state_timestamp())); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_I("desktop-animation", "background change"); | ||||||
|  |     FURI_LOG_I("desktop-animation", "icounter: %d", stats.icounter); | ||||||
|  |     FURI_LOG_I("desktop-animation", "butthurt: %d", stats.butthurt); | ||||||
|  |     FURI_LOG_I("desktop-animation", "time since deeed: %.0f", timediff); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     if((random() % 100) > 50) { // temp rnd selection
 | ||||||
|  |         new = random() % COUNT_OF(idle_scenes); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return idle_scenes[new]; | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								applications/desktop/helpers/desktop_animation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								applications/desktop/helpers/desktop_animation.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <math.h> | ||||||
|  | #include <assets_icons.h> | ||||||
|  | #include "dolphin/dolphin.h" | ||||||
|  | #include "dolphin/helpers/dolphin_state.h" | ||||||
|  | #include "time.h" | ||||||
|  | 
 | ||||||
|  | const Icon* desktop_get_icon(); | ||||||
| @ -26,7 +26,7 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { | |||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case DesktopDebugEventExit: |         case DesktopDebugEventExit: | ||||||
|             scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); |             scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); | ||||||
|             dolphin_save(dolphin); |             dolphin_flush(dolphin); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
| @ -43,7 +43,7 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { | |||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case DesktopDebugEventSaveState: |         case DesktopDebugEventSaveState: | ||||||
|             dolphin_save(dolphin); |             dolphin_flush(dolphin); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,16 +1,26 @@ | |||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "../views/desktop_hw_mismatch.h" | #include <furi-hal-version.h> | ||||||
| 
 | 
 | ||||||
| void desktop_scene_hw_mismatch_callback(DesktopHwMismatchEvent event, void* context) { | #define HW_MISMATCH_BACK_EVENT (0UL) | ||||||
|  | 
 | ||||||
|  | void desktop_scene_hw_mismatch_callback(void* context) { | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
|     view_dispatcher_send_custom_event(desktop->view_dispatcher, event); |     view_dispatcher_send_custom_event(desktop->view_dispatcher, HW_MISMATCH_BACK_EVENT); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_scene_hw_mismatch_on_enter(void* context) { | void desktop_scene_hw_mismatch_on_enter(void* context) { | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
| 
 |     Popup* popup = desktop->hw_mismatch_popup; | ||||||
|     desktop_hw_mismatch_set_callback( |     char buffer[256]; // strange but smaller buffer not making it
 | ||||||
|         desktop->hw_mismatch_view, desktop_scene_hw_mismatch_callback, desktop); |     snprintf( | ||||||
|  |         buffer, | ||||||
|  |         sizeof(buffer), | ||||||
|  |         "HW target: F%d\nFW target: " TARGET, | ||||||
|  |         furi_hal_version_get_hw_target()); | ||||||
|  |     popup_set_context(popup, desktop); | ||||||
|  |     popup_set_header(popup, "!!!! HW Mismatch !!!!", 60, 14, AlignCenter, AlignCenter); | ||||||
|  |     popup_set_text(popup, buffer, 60, 37, AlignCenter, AlignCenter); | ||||||
|  |     popup_set_callback(popup, desktop_scene_hw_mismatch_callback); | ||||||
|     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewHwMismatch); |     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewHwMismatch); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -20,7 +30,7 @@ bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event) | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case DesktopHwMismatchEventExit: |         case HW_MISMATCH_BACK_EVENT: | ||||||
|             scene_manager_previous_scene(desktop->scene_manager); |             scene_manager_previous_scene(desktop->scene_manager); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| @ -33,5 +43,10 @@ bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_scene_hw_mismatch_on_exit(void* context) { | void desktop_scene_hw_mismatch_on_exit(void* context) { | ||||||
|     // Desktop* desktop = (Desktop*)context;
 |     Desktop* desktop = (Desktop*)context; | ||||||
|  |     Popup* popup = desktop->hw_mismatch_popup; | ||||||
|  |     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||||
|  |     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||||
|  |     popup_set_callback(popup, NULL); | ||||||
|  |     popup_set_context(popup, NULL); | ||||||
| } | } | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ void desktop_scene_locked_on_enter(void* context) { | |||||||
|     desktop_locked_set_callback(locked_view, desktop_scene_locked_callback, desktop); |     desktop_locked_set_callback(locked_view, desktop_scene_locked_callback, desktop); | ||||||
|     desktop_locked_reset_door_pos(locked_view); |     desktop_locked_reset_door_pos(locked_view); | ||||||
|     desktop_locked_update_hint_timeout(locked_view); |     desktop_locked_update_hint_timeout(locked_view); | ||||||
|  |     desktop_locked_set_dolphin_animation(locked_view); | ||||||
| 
 | 
 | ||||||
|     view_port_enabled_set(desktop->lock_viewport, true); |     view_port_enabled_set(desktop->lock_viewport, true); | ||||||
|     osTimerStart(locked_view->timer, 63); |     osTimerStart(locked_view->timer, 63); | ||||||
| @ -45,7 +46,6 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
| void desktop_scene_locked_on_exit(void* context) { | void desktop_scene_locked_on_exit(void* context) { | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
|     DesktopLockedView* locked_view = desktop->locked_view; |  | ||||||
|     desktop_locked_reset_counter(desktop->locked_view); |     desktop_locked_reset_counter(desktop->locked_view); | ||||||
|     osTimerStop(locked_view->timer); |     osTimerStop(desktop->locked_view->timer); | ||||||
| } | } | ||||||
|  | |||||||
| @ -39,6 +39,7 @@ void desktop_scene_main_on_enter(void* context) { | |||||||
|         desktop_main_unlocked(desktop->main_view); |         desktop_main_unlocked(desktop->main_view); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     desktop_main_switch_dolphin_animation(desktop->main_view); | ||||||
|     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewMain); |     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewMain); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -59,7 +60,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | |||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case DesktopMainEventOpenDebug: |         case DesktopMainEventOpenDebug: | ||||||
|             scene_manager_next_scene(desktop->scene_manager, DesktopViewDebug); |             scene_manager_next_scene(desktop->scene_manager, DesktopSceneDebug); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
| @ -67,6 +68,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | |||||||
|             desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE); |             desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  | 
 | ||||||
|         case DesktopMainEventOpenFavorite: |         case DesktopMainEventOpenFavorite: | ||||||
|             desktop_settings_load(&desktop->settings); |             desktop_settings_load(&desktop->settings); | ||||||
|             desktop_switch_to_app(desktop, &FLIPPER_APPS[desktop->settings.favorite]); |             desktop_switch_to_app(desktop, &FLIPPER_APPS[desktop->settings.favorite]); | ||||||
|  | |||||||
| @ -2,8 +2,8 @@ | |||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "desktop_debug.h" | #include "desktop_debug.h" | ||||||
| 
 | 
 | ||||||
| #include "applications/dolphin/helpers/dolphin_state.h" | #include "dolphin/helpers/dolphin_state.h" | ||||||
| #include "applications/dolphin/dolphin.h" | #include "dolphin/dolphin.h" | ||||||
| 
 | 
 | ||||||
| void desktop_debug_set_callback( | void desktop_debug_set_callback( | ||||||
|     DesktopDebugView* debug_view, |     DesktopDebugView* debug_view, | ||||||
| @ -21,7 +21,7 @@ void desktop_debug_render(Canvas* canvas, void* model) { | |||||||
|     const Version* ver; |     const Version* ver; | ||||||
|     char buffer[64]; |     char buffer[64]; | ||||||
| 
 | 
 | ||||||
|     static const char* headers[] = {"FW Version info:", "Boot Version info:", "Desktop info:"}; |     static const char* headers[] = {"FW Version info:", "Boot Version info:", "Dolphin info:"}; | ||||||
| 
 | 
 | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
|     canvas_set_font(canvas, FontPrimary); |     canvas_set_font(canvas, FontPrimary); | ||||||
| @ -56,7 +56,7 @@ void desktop_debug_render(Canvas* canvas, void* model) { | |||||||
|             "%s [%s]", |             "%s [%s]", | ||||||
|             version_get_version(ver), |             version_get_version(ver), | ||||||
|             version_get_builddate(ver)); |             version_get_builddate(ver)); | ||||||
|         canvas_draw_str(canvas, 5, 33, buffer); |         canvas_draw_str(canvas, 5, 32, buffer); | ||||||
| 
 | 
 | ||||||
|         snprintf( |         snprintf( | ||||||
|             buffer, |             buffer, | ||||||
| @ -68,16 +68,22 @@ void desktop_debug_render(Canvas* canvas, void* model) { | |||||||
| 
 | 
 | ||||||
|         snprintf( |         snprintf( | ||||||
|             buffer, sizeof(buffer), "[%s] %s", version_get_target(ver), version_get_gitbranch(ver)); |             buffer, sizeof(buffer), "[%s] %s", version_get_target(ver), version_get_gitbranch(ver)); | ||||||
|         canvas_draw_str(canvas, 5, 53, buffer); |         canvas_draw_str(canvas, 5, 54, buffer); | ||||||
| 
 | 
 | ||||||
|     } else { |     } else { | ||||||
|         char buffer[64]; |         char buffer[64]; | ||||||
|  |         uint32_t current_lvl = dolphin_state_get_level(m->icounter); | ||||||
|  |         uint32_t remaining = dolphin_state_xp_to_levelup(m->icounter, current_lvl, true); | ||||||
| 
 | 
 | ||||||
|         canvas_set_font(canvas, FontSecondary); |         canvas_set_font(canvas, FontSecondary); | ||||||
|         snprintf(buffer, 64, "Icounter: %ld", m->icounter); |         snprintf(buffer, 64, "Icounter: %ld  Butthurt %ld", m->icounter, m->butthurt); | ||||||
|         canvas_draw_str(canvas, 5, 30, buffer); |         canvas_draw_str(canvas, 5, 23, buffer); | ||||||
|         snprintf(buffer, 64, "Butthurt: %ld", m->butthurt); | 
 | ||||||
|         canvas_draw_str(canvas, 5, 40, buffer); |         snprintf(buffer, 64, "Level: %ld  To level up: %ld", current_lvl, remaining); | ||||||
|  |         canvas_draw_str(canvas, 5, 33, buffer); | ||||||
|  | 
 | ||||||
|  |         snprintf(buffer, 64, "%s", asctime(localtime((const time_t*)&m->timestamp))); | ||||||
|  |         canvas_draw_str(canvas, 5, 43, buffer); | ||||||
|         canvas_draw_str(canvas, 0, 53, "[< >] icounter value   [ok] save"); |         canvas_draw_str(canvas, 0, 53, "[< >] icounter value   [ok] save"); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -146,11 +152,12 @@ void desktop_debug_free(DesktopDebugView* debug_view) { | |||||||
| 
 | 
 | ||||||
| void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) { | void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) { | ||||||
|     Dolphin* dolphin = furi_record_open("dolphin"); |     Dolphin* dolphin = furi_record_open("dolphin"); | ||||||
|     DolphinDeedWeight stats = dolphin_stats(dolphin); |     DolphinStats stats = dolphin_stats(dolphin); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         debug_view->view, (DesktopDebugViewModel * model) { |         debug_view->view, (DesktopDebugViewModel * model) { | ||||||
|             model->icounter = stats.icounter; |             model->icounter = stats.icounter; | ||||||
|             model->butthurt = stats.butthurt; |             model->butthurt = stats.butthurt; | ||||||
|  |             model->timestamp = stats.timestamp; | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
|  | #include <time.h> | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopDebugEventDeed, |     DesktopDebugEventDeed, | ||||||
| @ -35,6 +36,7 @@ struct DesktopDebugView { | |||||||
| typedef struct { | typedef struct { | ||||||
|     uint32_t icounter; |     uint32_t icounter; | ||||||
|     uint32_t butthurt; |     uint32_t butthurt; | ||||||
|  |     uint64_t timestamp; | ||||||
|     DesktopViewStatsScreens screen; |     DesktopViewStatsScreens screen; | ||||||
| } DesktopDebugViewModel; | } DesktopDebugViewModel; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,66 +0,0 @@ | |||||||
| #include <furi.h> |  | ||||||
| #include "../desktop_i.h" |  | ||||||
| #include <furi-hal.h> |  | ||||||
| #include <furi-hal-version.h> |  | ||||||
| 
 |  | ||||||
| #include "desktop_hw_mismatch.h" |  | ||||||
| 
 |  | ||||||
| void desktop_hw_mismatch_set_callback( |  | ||||||
|     DesktopHwMismatchView* main_view, |  | ||||||
|     DesktopHwMismatchViewCallback callback, |  | ||||||
|     void* context) { |  | ||||||
|     furi_assert(main_view); |  | ||||||
|     furi_assert(callback); |  | ||||||
|     main_view->callback = callback; |  | ||||||
|     main_view->context = context; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_hw_mismatch_render(Canvas* canvas, void* model) { |  | ||||||
|     canvas_clear(canvas); |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
|     canvas_draw_str(canvas, 2, 15, "!!!! HW Mismatch !!!!"); |  | ||||||
| 
 |  | ||||||
|     char buffer[64]; |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
|     snprintf(buffer, 64, "HW target: F%d", furi_hal_version_get_hw_target()); |  | ||||||
|     canvas_draw_str(canvas, 5, 27, buffer); |  | ||||||
|     canvas_draw_str(canvas, 5, 38, "FW target: " TARGET); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| View* desktop_hw_mismatch_get_view(DesktopHwMismatchView* hw_mismatch_view) { |  | ||||||
|     furi_assert(hw_mismatch_view); |  | ||||||
|     return hw_mismatch_view->view; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool desktop_hw_mismatch_input(InputEvent* event, void* context) { |  | ||||||
|     furi_assert(event); |  | ||||||
|     furi_assert(context); |  | ||||||
| 
 |  | ||||||
|     DesktopHwMismatchView* hw_mismatch_view = context; |  | ||||||
| 
 |  | ||||||
|     if(event->type == InputTypeShort) { |  | ||||||
|         hw_mismatch_view->callback(DesktopHwMismatchEventExit, hw_mismatch_view->context); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| DesktopHwMismatchView* desktop_hw_mismatch_alloc() { |  | ||||||
|     DesktopHwMismatchView* hw_mismatch_view = furi_alloc(sizeof(DesktopHwMismatchView)); |  | ||||||
|     hw_mismatch_view->view = view_alloc(); |  | ||||||
|     view_allocate_model( |  | ||||||
|         hw_mismatch_view->view, ViewModelTypeLocking, sizeof(DesktopHwMismatchViewModel)); |  | ||||||
|     view_set_context(hw_mismatch_view->view, hw_mismatch_view); |  | ||||||
|     view_set_draw_callback(hw_mismatch_view->view, (ViewDrawCallback)desktop_hw_mismatch_render); |  | ||||||
|     view_set_input_callback(hw_mismatch_view->view, desktop_hw_mismatch_input); |  | ||||||
| 
 |  | ||||||
|     return hw_mismatch_view; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void desktop_hw_mismatch_free(DesktopHwMismatchView* hw_mismatch_view) { |  | ||||||
|     furi_assert(hw_mismatch_view); |  | ||||||
| 
 |  | ||||||
|     view_free(hw_mismatch_view->view); |  | ||||||
|     free(hw_mismatch_view); |  | ||||||
| } |  | ||||||
| @ -1,38 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <gui/gui_i.h> |  | ||||||
| #include <gui/view.h> |  | ||||||
| #include <gui/canvas.h> |  | ||||||
| #include <gui/elements.h> |  | ||||||
| #include <furi.h> |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     DesktopHwMismatchEventExit, |  | ||||||
| } DesktopHwMismatchEvent; |  | ||||||
| 
 |  | ||||||
| typedef struct DesktopHwMismatchView DesktopHwMismatchView; |  | ||||||
| 
 |  | ||||||
| typedef void (*DesktopHwMismatchViewCallback)(DesktopHwMismatchEvent event, void* context); |  | ||||||
| 
 |  | ||||||
| struct DesktopHwMismatchView { |  | ||||||
|     View* view; |  | ||||||
|     DesktopHwMismatchViewCallback callback; |  | ||||||
|     void* context; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     IconAnimation* animation; |  | ||||||
|     uint8_t scene_num; |  | ||||||
|     uint8_t hint_timeout; |  | ||||||
|     bool locked; |  | ||||||
| } DesktopHwMismatchViewModel; |  | ||||||
| 
 |  | ||||||
| void desktop_hw_mismatch_set_callback( |  | ||||||
|     DesktopHwMismatchView* hw_mismatch_view, |  | ||||||
|     DesktopHwMismatchViewCallback callback, |  | ||||||
|     void* context); |  | ||||||
| 
 |  | ||||||
| View* desktop_hw_mismatch_get_view(DesktopHwMismatchView* hw_mismatch_view); |  | ||||||
| 
 |  | ||||||
| DesktopHwMismatchView* desktop_hw_mismatch_alloc(); |  | ||||||
| void desktop_hw_mismatch_free(DesktopHwMismatchView* hw_mismatch_view); |  | ||||||
| @ -29,7 +29,7 @@ static void lock_menu_callback(void* context, uint8_t index) { | |||||||
|     default: // wip message
 |     default: // wip message
 | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             lock_menu->view, (DesktopLockMenuViewModel * model) { |             lock_menu->view, (DesktopLockMenuViewModel * model) { | ||||||
|                 model->hint_timeout = HINT_TIMEOUT_L; |                 model->hint_timeout = HINT_TIMEOUT; | ||||||
|                 return true; |                 return true; | ||||||
|             }); |             }); | ||||||
|         break; |         break; | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ | |||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
|  | #define HINT_TIMEOUT 2 | ||||||
|  | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopLockMenuEventLock, |     DesktopLockMenuEventLock, | ||||||
|     DesktopLockMenuEventUnlock, |     DesktopLockMenuEventUnlock, | ||||||
| @ -25,7 +27,6 @@ struct DesktopLockMenuView { | |||||||
| typedef struct { | typedef struct { | ||||||
|     uint8_t idx; |     uint8_t idx; | ||||||
|     uint8_t hint_timeout; |     uint8_t hint_timeout; | ||||||
|     bool locked; |  | ||||||
| } DesktopLockMenuViewModel; | } DesktopLockMenuViewModel; | ||||||
| 
 | 
 | ||||||
| void desktop_lock_menu_set_callback( | void desktop_lock_menu_set_callback( | ||||||
|  | |||||||
| @ -2,8 +2,6 @@ | |||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "desktop_locked.h" | #include "desktop_locked.h" | ||||||
| 
 | 
 | ||||||
| static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64}; |  | ||||||
| 
 |  | ||||||
| void desktop_locked_set_callback( | void desktop_locked_set_callback( | ||||||
|     DesktopLockedView* locked_view, |     DesktopLockedView* locked_view, | ||||||
|     DesktopLockedViewCallback callback, |     DesktopLockedViewCallback callback, | ||||||
| @ -20,13 +18,12 @@ void locked_view_timer_callback(void* context) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // temporary locked screen animation managment
 | // temporary locked screen animation managment
 | ||||||
| static void | void desktop_locked_set_dolphin_animation(DesktopLockedView* locked_view) { | ||||||
|     desktop_scene_handler_set_scene(DesktopLockedView* locked_view, const Icon* icon_data) { |  | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         locked_view->view, (DesktopLockedViewModel * model) { |         locked_view->view, (DesktopLockedViewModel * model) { | ||||||
|             if(model->animation) icon_animation_free(model->animation); |             if(model->animation) icon_animation_free(model->animation); | ||||||
|             model->animation = icon_animation_alloc(icon_data); |             model->animation = icon_animation_alloc(desktop_get_icon()); | ||||||
|             icon_animation_start(model->animation); |             view_tie_icon_animation(locked_view->view, model->animation); | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| @ -34,7 +31,7 @@ static void | |||||||
| void desktop_locked_update_hint_timeout(DesktopLockedView* locked_view) { | void desktop_locked_update_hint_timeout(DesktopLockedView* locked_view) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         locked_view->view, (DesktopLockedViewModel * model) { |         locked_view->view, (DesktopLockedViewModel * model) { | ||||||
|             model->hint_timeout = HINT_TIMEOUT_H; |             model->hint_expire_at = osKernelGetTickCount() + osKernelGetTickFreq(); | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| @ -43,8 +40,8 @@ void desktop_locked_reset_door_pos(DesktopLockedView* locked_view) { | |||||||
|     with_view_model( |     with_view_model( | ||||||
|         locked_view->view, (DesktopLockedViewModel * model) { |         locked_view->view, (DesktopLockedViewModel * model) { | ||||||
|             model->animation_seq_end = false; |             model->animation_seq_end = false; | ||||||
|             model->door_left_x = -57; |             model->door_left_x = DOOR_L_POS; | ||||||
|             model->door_right_x = 115; |             model->door_right_x = DOOR_R_POS; | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| @ -58,9 +55,12 @@ void desktop_locked_manage_redraw(DesktopLockedView* locked_view) { | |||||||
|             animation_seq_end = model->animation_seq_end; |             animation_seq_end = model->animation_seq_end; | ||||||
| 
 | 
 | ||||||
|             if(!model->animation_seq_end) { |             if(!model->animation_seq_end) { | ||||||
|                 model->door_left_x = CLAMP(model->door_left_x + 5, 0, -57); |                 model->door_left_x = CLAMP(model->door_left_x + 5, DOOR_L_POS_MAX, DOOR_L_POS); | ||||||
|                 model->door_right_x = CLAMP(model->door_right_x - 5, 115, 60); |                 model->door_right_x = CLAMP(model->door_right_x - 5, DOOR_R_POS, DOOR_R_POS_MIN); | ||||||
|  |             } else { | ||||||
|  |                 model->hint_expire_at = !model->hint_expire_at; | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
| @ -75,14 +75,14 @@ void desktop_locked_reset_counter(DesktopLockedView* locked_view) { | |||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         locked_view->view, (DesktopLockedViewModel * model) { |         locked_view->view, (DesktopLockedViewModel * model) { | ||||||
|             model->hint_timeout = 0; |             model->hint_expire_at = 0; | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_locked_render(Canvas* canvas, void* model) { | void desktop_locked_render(Canvas* canvas, void* model) { | ||||||
|     DesktopLockedViewModel* m = model; |     DesktopLockedViewModel* m = model; | ||||||
| 
 |     uint32_t now = osKernelGetTickCount(); | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
| @ -95,12 +95,11 @@ void desktop_locked_render(Canvas* canvas, void* model) { | |||||||
|         canvas_draw_icon_animation(canvas, 0, -3, m->animation); |         canvas_draw_icon_animation(canvas, 0, -3, m->animation); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(m->hint_timeout) { |     if(now < m->hint_expire_at) { | ||||||
|         m->hint_timeout--; |  | ||||||
| 
 |  | ||||||
|         if(!m->animation_seq_end) { |         if(!m->animation_seq_end) { | ||||||
|             canvas_set_font(canvas, FontPrimary); |             canvas_set_font(canvas, FontPrimary); | ||||||
|             elements_multiline_text_framed(canvas, 42, 30, "Locked"); |             elements_multiline_text_framed(canvas, 42, 30, "Locked"); | ||||||
|  | 
 | ||||||
|         } else { |         } else { | ||||||
|             canvas_set_font(canvas, FontSecondary); |             canvas_set_font(canvas, FontSecondary); | ||||||
|             canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49); |             canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49); | ||||||
| @ -120,15 +119,10 @@ bool desktop_locked_input(InputEvent* event, void* context) { | |||||||
| 
 | 
 | ||||||
|     DesktopLockedView* locked_view = context; |     DesktopLockedView* locked_view = context; | ||||||
|     if(event->type == InputTypeShort) { |     if(event->type == InputTypeShort) { | ||||||
|         with_view_model( |         desktop_locked_update_hint_timeout(locked_view); | ||||||
|             locked_view->view, (DesktopLockedViewModel * model) { |  | ||||||
|                 model->hint_timeout = HINT_TIMEOUT_L; |  | ||||||
|                 return true; |  | ||||||
|             }); |  | ||||||
| 
 | 
 | ||||||
|         if(event->key == InputKeyBack) { |         if(event->key == InputKeyBack) { | ||||||
|             uint32_t press_time = HAL_GetTick(); |             uint32_t press_time = osKernelGetTickCount(); | ||||||
| 
 |  | ||||||
|             // check if pressed sequentially
 |             // check if pressed sequentially
 | ||||||
|             if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) { |             if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) { | ||||||
|                 locked_view->lock_lastpress = press_time; |                 locked_view->lock_lastpress = press_time; | ||||||
| @ -148,6 +142,26 @@ bool desktop_locked_input(InputEvent* event, void* context) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void desktop_locked_enter(void* context) { | ||||||
|  |     DesktopLockedView* locked_view = context; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         locked_view->view, (DesktopLockedViewModel * model) { | ||||||
|  |             if(model->animation) icon_animation_start(model->animation); | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_locked_exit(void* context) { | ||||||
|  |     DesktopLockedView* locked_view = context; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         locked_view->view, (DesktopLockedViewModel * model) { | ||||||
|  |             if(model->animation) icon_animation_stop(model->animation); | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| DesktopLockedView* desktop_locked_alloc() { | DesktopLockedView* desktop_locked_alloc() { | ||||||
|     DesktopLockedView* locked_view = furi_alloc(sizeof(DesktopLockedView)); |     DesktopLockedView* locked_view = furi_alloc(sizeof(DesktopLockedView)); | ||||||
|     locked_view->view = view_alloc(); |     locked_view->view = view_alloc(); | ||||||
| @ -158,8 +172,9 @@ DesktopLockedView* desktop_locked_alloc() { | |||||||
|     view_set_context(locked_view->view, locked_view); |     view_set_context(locked_view->view, locked_view); | ||||||
|     view_set_draw_callback(locked_view->view, (ViewDrawCallback)desktop_locked_render); |     view_set_draw_callback(locked_view->view, (ViewDrawCallback)desktop_locked_render); | ||||||
|     view_set_input_callback(locked_view->view, desktop_locked_input); |     view_set_input_callback(locked_view->view, desktop_locked_input); | ||||||
|  |     view_set_enter_callback(locked_view->view, desktop_locked_enter); | ||||||
|  |     view_set_exit_callback(locked_view->view, desktop_locked_exit); | ||||||
| 
 | 
 | ||||||
|     desktop_scene_handler_set_scene(locked_view, idle_scenes[random() % COUNT_OF(idle_scenes)]); |  | ||||||
|     return locked_view; |     return locked_view; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,9 +6,14 @@ | |||||||
| #include <gui/elements.h> | #include <gui/elements.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| #define UNLOCK_RST_TIMEOUT 200 | #define UNLOCK_RST_TIMEOUT 300 | ||||||
| #define UNLOCK_CNT 2 // 3 actually
 | #define UNLOCK_CNT 2 // 3 actually
 | ||||||
| 
 | 
 | ||||||
|  | #define DOOR_L_POS -57 | ||||||
|  | #define DOOR_L_POS_MAX 0 | ||||||
|  | #define DOOR_R_POS 115 | ||||||
|  | #define DOOR_R_POS_MIN 60 | ||||||
|  | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopLockedEventUnlock, |     DesktopLockedEventUnlock, | ||||||
|     DesktopLockedEventUpdate, |     DesktopLockedEventUpdate, | ||||||
| @ -30,10 +35,11 @@ struct DesktopLockedView { | |||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     IconAnimation* animation; |     IconAnimation* animation; | ||||||
|  |     uint32_t hint_expire_at; | ||||||
|  | 
 | ||||||
|     uint8_t scene_num; |     uint8_t scene_num; | ||||||
|     int8_t door_left_x; |     int8_t door_left_x; | ||||||
|     int8_t door_right_x; |     int8_t door_right_x; | ||||||
|     uint8_t hint_timeout; |  | ||||||
|     bool animation_seq_end; |     bool animation_seq_end; | ||||||
| 
 | 
 | ||||||
| } DesktopLockedViewModel; | } DesktopLockedViewModel; | ||||||
| @ -43,6 +49,7 @@ void desktop_locked_set_callback( | |||||||
|     DesktopLockedViewCallback callback, |     DesktopLockedViewCallback callback, | ||||||
|     void* context); |     void* context); | ||||||
| 
 | 
 | ||||||
|  | void desktop_locked_set_dolphin_animation(DesktopLockedView* locked_view); | ||||||
| void desktop_locked_update_hint_timeout(DesktopLockedView* locked_view); | void desktop_locked_update_hint_timeout(DesktopLockedView* locked_view); | ||||||
| void desktop_locked_reset_counter(DesktopLockedView* locked_view); | void desktop_locked_reset_counter(DesktopLockedView* locked_view); | ||||||
| void desktop_locked_reset_door_pos(DesktopLockedView* locked_view); | void desktop_locked_reset_door_pos(DesktopLockedView* locked_view); | ||||||
|  | |||||||
| @ -2,8 +2,6 @@ | |||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
| #include "desktop_main.h" | #include "desktop_main.h" | ||||||
| 
 | 
 | ||||||
| static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64}; |  | ||||||
| 
 |  | ||||||
| void desktop_main_set_callback( | void desktop_main_set_callback( | ||||||
|     DesktopMainView* main_view, |     DesktopMainView* main_view, | ||||||
|     DesktopMainViewCallback callback, |     DesktopMainViewCallback callback, | ||||||
| @ -17,30 +15,17 @@ void desktop_main_set_callback( | |||||||
| void desktop_main_reset_hint(DesktopMainView* main_view) { | void desktop_main_reset_hint(DesktopMainView* main_view) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         main_view->view, (DesktopMainViewModel * model) { |         main_view->view, (DesktopMainViewModel * model) { | ||||||
|             model->hint_timeout = 0; |             model->hint_expire_at = 0; | ||||||
|             return true; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| // temporary main screen animation managment
 |  | ||||||
| void desktop_scene_handler_set_scene(DesktopMainView* main_view, const Icon* icon_data) { |  | ||||||
|     with_view_model( |  | ||||||
|         main_view->view, (DesktopMainViewModel * model) { |  | ||||||
|             if(model->animation) icon_animation_free(model->animation); |  | ||||||
|             model->animation = icon_animation_alloc(icon_data); |  | ||||||
|             icon_animation_start(model->animation); |  | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_scene_handler_switch_scene(DesktopMainView* main_view) { | void desktop_main_switch_dolphin_animation(DesktopMainView* main_view) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         main_view->view, (DesktopMainViewModel * model) { |         main_view->view, (DesktopMainViewModel * model) { | ||||||
|             if(icon_animation_is_last_frame(model->animation)) { |             if(model->animation) icon_animation_free(model->animation); | ||||||
|                 if(model->animation) icon_animation_free(model->animation); |             model->animation = icon_animation_alloc(desktop_get_icon()); | ||||||
|                 model->animation = icon_animation_alloc(idle_scenes[model->scene_num]); |             view_tie_icon_animation(main_view->view, model->animation); | ||||||
|                 icon_animation_start(model->animation); |  | ||||||
|                 model->scene_num = random() % COUNT_OF(idle_scenes); |  | ||||||
|             } |  | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| @ -48,13 +33,13 @@ void desktop_scene_handler_switch_scene(DesktopMainView* main_view) { | |||||||
| void desktop_main_render(Canvas* canvas, void* model) { | void desktop_main_render(Canvas* canvas, void* model) { | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
|     DesktopMainViewModel* m = model; |     DesktopMainViewModel* m = model; | ||||||
|  |     uint32_t now = osKernelGetTickCount(); | ||||||
| 
 | 
 | ||||||
|     if(m->animation) { |     if(m->animation) { | ||||||
|         canvas_draw_icon_animation(canvas, 0, -3, m->animation); |         canvas_draw_icon_animation(canvas, 0, -3, m->animation); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(m->unlocked && m->hint_timeout) { |     if(now < m->hint_expire_at) { | ||||||
|         m->hint_timeout = CLAMP(m->hint_timeout - 1, 2, 0); |  | ||||||
|         canvas_set_font(canvas, FontPrimary); |         canvas_set_font(canvas, FontPrimary); | ||||||
|         elements_multiline_text_framed(canvas, 42, 30, "Unlocked"); |         elements_multiline_text_framed(canvas, 42, 30, "Unlocked"); | ||||||
|     } |     } | ||||||
| @ -87,6 +72,25 @@ bool desktop_main_input(InputEvent* event, void* context) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void desktop_main_enter(void* context) { | ||||||
|  |     DesktopMainView* main_view = context; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (DesktopMainViewModel * model) { | ||||||
|  |             if(model->animation) icon_animation_start(model->animation); | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_main_exit(void* context) { | ||||||
|  |     DesktopMainView* main_view = context; | ||||||
|  |     with_view_model( | ||||||
|  |         main_view->view, (DesktopMainViewModel * model) { | ||||||
|  |             if(model->animation) icon_animation_stop(model->animation); | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| DesktopMainView* desktop_main_alloc() { | DesktopMainView* desktop_main_alloc() { | ||||||
|     DesktopMainView* main_view = furi_alloc(sizeof(DesktopMainView)); |     DesktopMainView* main_view = furi_alloc(sizeof(DesktopMainView)); | ||||||
|     main_view->view = view_alloc(); |     main_view->view = view_alloc(); | ||||||
| @ -94,8 +98,8 @@ DesktopMainView* desktop_main_alloc() { | |||||||
|     view_set_context(main_view->view, main_view); |     view_set_context(main_view->view, main_view); | ||||||
|     view_set_draw_callback(main_view->view, (ViewDrawCallback)desktop_main_render); |     view_set_draw_callback(main_view->view, (ViewDrawCallback)desktop_main_render); | ||||||
|     view_set_input_callback(main_view->view, desktop_main_input); |     view_set_input_callback(main_view->view, desktop_main_input); | ||||||
| 
 |     view_set_enter_callback(main_view->view, desktop_main_enter); | ||||||
|     desktop_scene_handler_set_scene(main_view, idle_scenes[random() % COUNT_OF(idle_scenes)]); |     view_set_exit_callback(main_view->view, desktop_main_exit); | ||||||
| 
 | 
 | ||||||
|     return main_view; |     return main_view; | ||||||
| } | } | ||||||
| @ -109,8 +113,7 @@ void desktop_main_free(DesktopMainView* main_view) { | |||||||
| void desktop_main_unlocked(DesktopMainView* main_view) { | void desktop_main_unlocked(DesktopMainView* main_view) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         main_view->view, (DesktopMainViewModel * model) { |         main_view->view, (DesktopMainViewModel * model) { | ||||||
|             model->unlocked = true; |             model->hint_expire_at = osKernelGetTickCount() + osKernelGetTickFreq(); | ||||||
|             model->hint_timeout = 2; |  | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -28,8 +28,7 @@ struct DesktopMainView { | |||||||
| typedef struct { | typedef struct { | ||||||
|     IconAnimation* animation; |     IconAnimation* animation; | ||||||
|     uint8_t scene_num; |     uint8_t scene_num; | ||||||
|     uint8_t hint_timeout; |     uint32_t hint_expire_at; | ||||||
|     bool unlocked; |  | ||||||
| } DesktopMainViewModel; | } DesktopMainViewModel; | ||||||
| 
 | 
 | ||||||
| void desktop_main_set_callback( | void desktop_main_set_callback( | ||||||
| @ -40,4 +39,7 @@ void desktop_main_set_callback( | |||||||
| View* desktop_main_get_view(DesktopMainView* main_view); | View* desktop_main_get_view(DesktopMainView* main_view); | ||||||
| 
 | 
 | ||||||
| DesktopMainView* desktop_main_alloc(); | DesktopMainView* desktop_main_alloc(); | ||||||
|  | 
 | ||||||
| void desktop_main_free(DesktopMainView* main_view); | void desktop_main_free(DesktopMainView* main_view); | ||||||
|  | 
 | ||||||
|  | void desktop_main_switch_dolphin_animation(DesktopMainView* main_view); | ||||||
|  | |||||||
| @ -60,3 +60,14 @@ DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage | |||||||
| 
 | 
 | ||||||
|     return return_data.dialog_value; |     return return_data.dialog_value; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /****************** Storage error ******************/ | ||||||
|  | 
 | ||||||
|  | void dialog_message_show_storage_error(DialogsApp* context, const char* error_text) { | ||||||
|  |     DialogMessage* message = dialog_message_alloc(); | ||||||
|  |     dialog_message_set_text(message, error_text, 88, 32, AlignCenter, AlignCenter); | ||||||
|  |     dialog_message_set_icon(message, &I_SDQuestion_35x43, 5, 6); | ||||||
|  |     dialog_message_set_buttons(message, "Back", NULL, NULL); | ||||||
|  |     dialog_message_show(context, message); | ||||||
|  |     dialog_message_free(message); | ||||||
|  | } | ||||||
| @ -123,6 +123,13 @@ void dialog_message_set_buttons( | |||||||
|  */ |  */ | ||||||
| DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* message); | DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* message); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Show SD error message (with question sign) | ||||||
|  |  * @param context  | ||||||
|  |  * @param error_text  | ||||||
|  |  */ | ||||||
|  | void dialog_message_show_storage_error(DialogsApp* context, const char* error_text); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| @ -1,34 +1,39 @@ | |||||||
| #include "dolphin_i.h" | #include "dolphin_i.h" | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | #define DOLPHIN_TIMEGATE 86400 // one day
 | ||||||
| bool dolphin_load(Dolphin* dolphin) { | #define DOLPHIN_LOCK_EVENT_FLAG (0x1) | ||||||
|     furi_assert(dolphin); |  | ||||||
|     return dolphin_state_load(dolphin->state); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void dolphin_save(Dolphin* dolphin) { |  | ||||||
|     furi_assert(dolphin); |  | ||||||
|     DolphinEvent event; |  | ||||||
|     event.type = DolphinEventTypeSave; |  | ||||||
|     furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { | void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { | ||||||
|     furi_assert(dolphin); |     furi_assert(dolphin); | ||||||
|     DolphinEvent event; |     DolphinEvent event; | ||||||
|     event.type = DolphinEventTypeDeed; |     event.type = DolphinEventTypeDeed; | ||||||
|     event.deed = deed; |     event.deed = deed; | ||||||
|     furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK); |     dolphin_event_send_async(dolphin, &event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| DolphinDeedWeight dolphin_stats(Dolphin* dolphin) { | DolphinStats dolphin_stats(Dolphin* dolphin) { | ||||||
|     DolphinDeedWeight stats; |     furi_assert(dolphin); | ||||||
|     stats.butthurt = dolphin_state_get_butthurt(dolphin->state); | 
 | ||||||
|     stats.icounter = dolphin_state_get_icounter(dolphin->state); |     DolphinStats stats; | ||||||
|  |     DolphinEvent event; | ||||||
|  | 
 | ||||||
|  |     event.type = DolphinEventTypeStats; | ||||||
|  |     event.stats = &stats; | ||||||
|  | 
 | ||||||
|  |     dolphin_event_send_wait(dolphin, &event); | ||||||
| 
 | 
 | ||||||
|     return stats; |     return stats; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void dolphin_flush(Dolphin* dolphin) { | ||||||
|  |     furi_assert(dolphin); | ||||||
|  | 
 | ||||||
|  |     DolphinEvent event; | ||||||
|  |     event.type = DolphinEventTypeFlush; | ||||||
|  | 
 | ||||||
|  |     dolphin_event_send_wait(dolphin, &event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Dolphin* dolphin_alloc() { | Dolphin* dolphin_alloc() { | ||||||
|     Dolphin* dolphin = furi_alloc(sizeof(Dolphin)); |     Dolphin* dolphin = furi_alloc(sizeof(Dolphin)); | ||||||
| 
 | 
 | ||||||
| @ -47,27 +52,72 @@ void dolphin_free(Dolphin* dolphin) { | |||||||
|     free(dolphin); |     free(dolphin); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event) { | ||||||
|  |     furi_assert(dolphin); | ||||||
|  |     furi_assert(event); | ||||||
|  |     event->flag = NULL; | ||||||
|  |     furi_check(osMessageQueuePut(dolphin->event_queue, event, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event) { | ||||||
|  |     furi_assert(dolphin); | ||||||
|  |     furi_assert(event); | ||||||
|  |     event->flag = osEventFlagsNew(NULL); | ||||||
|  |     furi_check(event->flag); | ||||||
|  |     furi_check(osMessageQueuePut(dolphin->event_queue, event, 0, osWaitForever) == osOK); | ||||||
|  |     furi_check( | ||||||
|  |         osEventFlagsWait(event->flag, DOLPHIN_LOCK_EVENT_FLAG, osFlagsWaitAny, osWaitForever) == | ||||||
|  |         DOLPHIN_LOCK_EVENT_FLAG); | ||||||
|  |     furi_check(osEventFlagsDelete(event->flag) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dolphin_event_release(Dolphin* dolphin, DolphinEvent* event) { | ||||||
|  |     if(event->flag) { | ||||||
|  |         osEventFlagsSet(event->flag, DOLPHIN_LOCK_EVENT_FLAG); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dolphin_check_butthurt(DolphinState* state) { | ||||||
|  |     furi_assert(state); | ||||||
|  |     float diff_time = difftime(dolphin_state_get_timestamp(state), dolphin_state_timestamp()); | ||||||
|  | 
 | ||||||
|  | #if 0 | ||||||
|  |     FURI_LOG_I("dolphin-state", "Butthurt check, time since deed %.0f", fabs(diff_time)); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     if((fabs(diff_time)) > DOLPHIN_TIMEGATE) { | ||||||
|  |         // increase butthurt
 | ||||||
|  |         FURI_LOG_I("dolphin-state", "Increasing butthurt"); | ||||||
|  |         dolphin_state_butthurted(state); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int32_t dolphin_srv(void* p) { | int32_t dolphin_srv(void* p) { | ||||||
|     Dolphin* dolphin = dolphin_alloc(); |     Dolphin* dolphin = dolphin_alloc(); | ||||||
|     furi_record_create("dolphin", dolphin); |     furi_record_create("dolphin", dolphin); | ||||||
| 
 | 
 | ||||||
|  |     dolphin_state_load(dolphin->state); | ||||||
|  | 
 | ||||||
|     DolphinEvent event; |     DolphinEvent event; | ||||||
|     while(1) { |     while(1) { | ||||||
|         furi_check(osMessageQueueGet(dolphin->event_queue, &event, NULL, osWaitForever) == osOK); |         if(osMessageQueueGet(dolphin->event_queue, &event, NULL, 60000) == osOK) { | ||||||
|         switch(event.type) { |             if(event.type == DolphinEventTypeDeed) { | ||||||
|         case DolphinEventTypeDeed: |                 dolphin_state_on_deed(dolphin->state, event.deed); | ||||||
|             dolphin_state_on_deed(dolphin->state, event.deed); |             } else if(event.type == DolphinEventTypeStats) { | ||||||
|             break; |                 event.stats->icounter = dolphin_state_get_icounter(dolphin->state); | ||||||
| 
 |                 event.stats->butthurt = dolphin_state_get_butthurt(dolphin->state); | ||||||
|         case DolphinEventTypeSave: |                 event.stats->timestamp = dolphin_state_get_timestamp(dolphin->state); | ||||||
|  |             } else if(event.type == DolphinEventTypeFlush) { | ||||||
|  |                 dolphin_state_save(dolphin->state); | ||||||
|  |             } | ||||||
|  |             dolphin_event_release(dolphin, &event); | ||||||
|  |         } else { | ||||||
|  |             dolphin_check_butthurt(dolphin->state); | ||||||
|             dolphin_state_save(dolphin->state); |             dolphin_state_save(dolphin->state); | ||||||
|             break; |  | ||||||
| 
 |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     dolphin_free(dolphin); |     dolphin_free(dolphin); | ||||||
|  | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,27 +4,24 @@ | |||||||
| 
 | 
 | ||||||
| typedef struct Dolphin Dolphin; | typedef struct Dolphin Dolphin; | ||||||
| 
 | 
 | ||||||
| /* Load Dolphin state
 | typedef struct { | ||||||
|  * Thread safe |     uint32_t icounter; | ||||||
|  */ |     uint32_t butthurt; | ||||||
|  |     uint64_t timestamp; | ||||||
|  | } DolphinStats; | ||||||
| 
 | 
 | ||||||
| bool dolphin_load(Dolphin* dolphin); | /** Deed complete notification. Call it on deed completion.
 | ||||||
| 
 |  | ||||||
| /* Deed complete notification. Call it on deed completion.
 |  | ||||||
|  * See dolphin_deed.h for available deeds. In futures it will become part of assets. |  * See dolphin_deed.h for available deeds. In futures it will become part of assets. | ||||||
|  * Thread safe |  * Thread safe, async | ||||||
|  */ |  */ | ||||||
| 
 |  | ||||||
| void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); | void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); | ||||||
| 
 | 
 | ||||||
| /* Save Dolphin state (write to permanent memory)
 | /** Retrieve dolphin stats
 | ||||||
|  * Thread safe |  * Thread safe, blocking | ||||||
|  */ |  */ | ||||||
|  | DolphinStats dolphin_stats(Dolphin* dolphin); | ||||||
| 
 | 
 | ||||||
| void dolphin_save(Dolphin* dolphin); | /** Flush dolphin queue and save state
 | ||||||
| 
 |  * Thread safe, blocking | ||||||
| /* Retrieve dolphin's icounter and butthurt values
 |  | ||||||
|  * Thread safe |  | ||||||
|  */ |  */ | ||||||
| 
 | void dolphin_flush(Dolphin* dolphin); | ||||||
| DolphinDeedWeight dolphin_stats(Dolphin* dolphin); |  | ||||||
| @ -8,14 +8,16 @@ | |||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DolphinEventTypeDeed, |     DolphinEventTypeDeed, | ||||||
|     DolphinEventTypeSave, |     DolphinEventTypeStats, | ||||||
|     DolphinEventTypeTick, |     DolphinEventTypeFlush, | ||||||
| } DolphinEventType; | } DolphinEventType; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     DolphinEventType type; |     DolphinEventType type; | ||||||
|  |     osEventFlagsId_t flag; | ||||||
|     union { |     union { | ||||||
|         DolphinDeed deed; |         DolphinDeed deed; | ||||||
|  |         DolphinStats* stats; | ||||||
|     }; |     }; | ||||||
| } DolphinEvent; | } DolphinEvent; | ||||||
| 
 | 
 | ||||||
| @ -29,3 +31,9 @@ struct Dolphin { | |||||||
| Dolphin* dolphin_alloc(); | Dolphin* dolphin_alloc(); | ||||||
| 
 | 
 | ||||||
| void dolphin_free(Dolphin* dolphin); | void dolphin_free(Dolphin* dolphin); | ||||||
|  | 
 | ||||||
|  | void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event); | ||||||
|  | 
 | ||||||
|  | void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event); | ||||||
|  | 
 | ||||||
|  | void dolphin_event_release(Dolphin* dolphin, DolphinEvent* event); | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ typedef struct { | |||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
|     uint32_t icounter; |     uint32_t icounter; | ||||||
|     uint32_t butthurt; |     uint32_t butthurt; | ||||||
|  |     uint64_t timestamp; | ||||||
| } DolphinStoreData; | } DolphinStoreData; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -35,6 +36,7 @@ typedef struct { | |||||||
| struct DolphinState { | struct DolphinState { | ||||||
|     Storage* fs_api; |     Storage* fs_api; | ||||||
|     DolphinStoreData data; |     DolphinStoreData data; | ||||||
|  |     bool dirty; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| DolphinState* dolphin_state_alloc() { | DolphinState* dolphin_state_alloc() { | ||||||
| @ -49,8 +51,12 @@ void dolphin_state_free(DolphinState* dolphin_state) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool dolphin_state_save(DolphinState* dolphin_state) { | bool dolphin_state_save(DolphinState* dolphin_state) { | ||||||
|  |     if(!dolphin_state->dirty) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_I("dolphin-state", "State is dirty, saving to \"%s\"", DOLPHIN_STORE_KEY); | ||||||
|     DolphinStore store; |     DolphinStore store; | ||||||
|     FURI_LOG_I("dolphin-state", "Saving state to \"%s\"", DOLPHIN_STORE_KEY); |  | ||||||
|     // Calculate checksum
 |     // Calculate checksum
 | ||||||
|     uint8_t* source = (uint8_t*)&dolphin_state->data; |     uint8_t* source = (uint8_t*)&dolphin_state->data; | ||||||
|     uint8_t checksum = 0; |     uint8_t checksum = 0; | ||||||
| @ -88,7 +94,10 @@ bool dolphin_state_save(DolphinState* dolphin_state) { | |||||||
|     storage_file_close(file); |     storage_file_close(file); | ||||||
|     storage_file_free(file); |     storage_file_free(file); | ||||||
| 
 | 
 | ||||||
|  |     dolphin_state->dirty = !save_result; | ||||||
|  | 
 | ||||||
|     FURI_LOG_I("dolphin-state", "Saved"); |     FURI_LOG_I("dolphin-state", "Saved"); | ||||||
|  | 
 | ||||||
|     return save_result; |     return save_result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -99,8 +108,12 @@ bool dolphin_state_load(DolphinState* dolphin_state) { | |||||||
| 
 | 
 | ||||||
|     File* file = storage_file_alloc(dolphin_state->fs_api); |     File* file = storage_file_alloc(dolphin_state->fs_api); | ||||||
|     bool load_result = storage_file_open(file, DOLPHIN_STORE_KEY, FSAM_READ, FSOM_OPEN_EXISTING); |     bool load_result = storage_file_open(file, DOLPHIN_STORE_KEY, FSAM_READ, FSOM_OPEN_EXISTING); | ||||||
| 
 |     if(!load_result) { | ||||||
|     if(load_result) { |         FURI_LOG_E( | ||||||
|  |             "dolphin-state", | ||||||
|  |             "Load failed. Storage returned: %s", | ||||||
|  |             storage_file_get_error_desc(file)); | ||||||
|  |     } else { | ||||||
|         uint16_t bytes_count = storage_file_read(file, &store, sizeof(DolphinStore)); |         uint16_t bytes_count = storage_file_read(file, &store, sizeof(DolphinStore)); | ||||||
| 
 | 
 | ||||||
|         if(bytes_count != sizeof(DolphinStore)) { |         if(bytes_count != sizeof(DolphinStore)) { | ||||||
| @ -109,12 +122,8 @@ bool dolphin_state_load(DolphinState* dolphin_state) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(!load_result) { |     if(!load_result) { | ||||||
|         FURI_LOG_E( |         FURI_LOG_E("dolphin-state", "DolphinStore size mismatch"); | ||||||
|             "dolphin-state", |  | ||||||
|             "Load failed. Storage returned: %s", |  | ||||||
|             storage_file_get_error_desc(file)); |  | ||||||
|     } else { |     } else { | ||||||
|         FURI_LOG_I("dolphin-state", "State loaded, verifying header"); |  | ||||||
|         if(store.header.magic == DOLPHIN_STORE_HEADER_MAGIC && |         if(store.header.magic == DOLPHIN_STORE_HEADER_MAGIC && | ||||||
|            store.header.version == DOLPHIN_STORE_HEADER_VERSION) { |            store.header.version == DOLPHIN_STORE_HEADER_VERSION) { | ||||||
|             FURI_LOG_I( |             FURI_LOG_I( | ||||||
| @ -142,7 +151,7 @@ bool dolphin_state_load(DolphinState* dolphin_state) { | |||||||
|         } else { |         } else { | ||||||
|             FURI_LOG_E( |             FURI_LOG_E( | ||||||
|                 "dolphin-state", |                 "dolphin-state", | ||||||
|                 "Magic(%d != %d) and Version(%d != %d) mismatch", |                 "Magic(%d != %d) or Version(%d != %d) mismatch", | ||||||
|                 store.header.magic, |                 store.header.magic, | ||||||
|                 DOLPHIN_STORE_HEADER_MAGIC, |                 DOLPHIN_STORE_HEADER_MAGIC, | ||||||
|                 store.header.version, |                 store.header.version, | ||||||
| @ -153,6 +162,9 @@ bool dolphin_state_load(DolphinState* dolphin_state) { | |||||||
| 
 | 
 | ||||||
|     storage_file_close(file); |     storage_file_close(file); | ||||||
|     storage_file_free(file); |     storage_file_free(file); | ||||||
|  | 
 | ||||||
|  |     dolphin_state->dirty = !load_result; | ||||||
|  | 
 | ||||||
|     return load_result; |     return load_result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -160,13 +172,43 @@ void dolphin_state_clear(DolphinState* dolphin_state) { | |||||||
|     memset(&dolphin_state->data, 0, sizeof(DolphinStoreData)); |     memset(&dolphin_state->data, 0, sizeof(DolphinStoreData)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | uint64_t dolphin_state_timestamp() { | ||||||
|  |     RTC_TimeTypeDef time; | ||||||
|  |     RTC_DateTypeDef date; | ||||||
|  |     struct tm current; | ||||||
|  | 
 | ||||||
|  |     HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN); | ||||||
|  |     HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN); | ||||||
|  | 
 | ||||||
|  |     current.tm_year = date.Year + 100; | ||||||
|  |     current.tm_mday = date.Date; | ||||||
|  |     current.tm_mon = date.Month - 1; | ||||||
|  | 
 | ||||||
|  |     current.tm_hour = time.Hours; | ||||||
|  |     current.tm_min = time.Minutes; | ||||||
|  |     current.tm_sec = time.Seconds; | ||||||
|  | 
 | ||||||
|  |     return mktime(¤t); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { | void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { | ||||||
|     const DolphinDeedWeight* deed_weight = dolphin_deed_weight(deed); |     const DolphinDeedWeight* deed_weight = dolphin_deed_weight(deed); | ||||||
|     int32_t icounter = dolphin_state->data.icounter + deed_weight->icounter; |     int32_t icounter = dolphin_state->data.icounter + deed_weight->icounter; | ||||||
|  |     int32_t butthurt = dolphin_state->data.butthurt; | ||||||
| 
 | 
 | ||||||
|     if(icounter >= 0) { |     if(icounter >= 0) { | ||||||
|         dolphin_state->data.icounter = icounter; |         dolphin_state->data.icounter = icounter; | ||||||
|  |         dolphin_state->data.butthurt = MAX(butthurt - deed_weight->icounter, 0); | ||||||
|  |         dolphin_state->data.timestamp = dolphin_state_timestamp(); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     dolphin_state->dirty = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dolphin_state_butthurted(DolphinState* dolphin_state) { | ||||||
|  |     dolphin_state->data.butthurt++; | ||||||
|  |     dolphin_state->data.timestamp = dolphin_state_timestamp(); | ||||||
|  |     dolphin_state->dirty = true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state) { | uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state) { | ||||||
| @ -177,13 +219,14 @@ uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state) { | |||||||
|     return dolphin_state->data.butthurt; |     return dolphin_state->data.butthurt; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t dolphin_state_get_level(DolphinState* dolphin_state) { | uint64_t dolphin_state_get_timestamp(DolphinState* dolphin_state) { | ||||||
|     return 0.5f + |     return dolphin_state->data.timestamp; | ||||||
|            sqrtf(1.0f + 8.0f * ((float)dolphin_state->data.icounter / DOLPHIN_LVL_THRESHOLD)) / |  | ||||||
|                2.0f; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t dolphin_state_xp_to_levelup(DolphinState* dolphin_state, uint32_t level, bool remaining) { | uint32_t dolphin_state_get_level(uint32_t icounter) { | ||||||
|     return (DOLPHIN_LVL_THRESHOLD * level * (level + 1) / 2) - |     return 0.5f + sqrtf(1.0f + 8.0f * ((float)icounter / DOLPHIN_LVL_THRESHOLD)) / 2.0f; | ||||||
|            (remaining ? dolphin_state->data.icounter : 0); | } | ||||||
|  | 
 | ||||||
|  | uint32_t dolphin_state_xp_to_levelup(uint32_t icounter, uint32_t level, bool remaining) { | ||||||
|  |     return (DOLPHIN_LVL_THRESHOLD * level * (level + 1) / 2) - (remaining ? icounter : 0); | ||||||
| } | } | ||||||
| @ -3,6 +3,8 @@ | |||||||
| #include "dolphin_deed.h" | #include "dolphin_deed.h" | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  | #include <rtc.h> | ||||||
|  | #include <time.h> | ||||||
| 
 | 
 | ||||||
| typedef struct DolphinState DolphinState; | typedef struct DolphinState DolphinState; | ||||||
| 
 | 
 | ||||||
| @ -16,12 +18,18 @@ bool dolphin_state_load(DolphinState* dolphin_state); | |||||||
| 
 | 
 | ||||||
| void dolphin_state_clear(DolphinState* dolphin_state); | void dolphin_state_clear(DolphinState* dolphin_state); | ||||||
| 
 | 
 | ||||||
|  | uint64_t dolphin_state_timestamp(); | ||||||
|  | 
 | ||||||
| void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed); | void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed); | ||||||
| 
 | 
 | ||||||
|  | void dolphin_state_butthurted(DolphinState* dolphin_state); | ||||||
|  | 
 | ||||||
| uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state); | uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state); | ||||||
| 
 | 
 | ||||||
| uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state); | uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state); | ||||||
| 
 | 
 | ||||||
| uint32_t dolphin_state_get_level(DolphinState* dolphin_state); | uint64_t dolphin_state_get_timestamp(DolphinState* dolphin_state); | ||||||
| 
 | 
 | ||||||
| uint32_t dolphin_state_xp_to_levelup(DolphinState* dolphin_state, uint32_t level, bool remaining); | uint32_t dolphin_state_get_level(uint32_t icounter); | ||||||
|  | 
 | ||||||
|  | uint32_t dolphin_state_xp_to_levelup(uint32_t icounter, uint32_t level, bool remaining); | ||||||
| @ -1,148 +0,0 @@ | |||||||
| #include <furi.h> |  | ||||||
| #include <furi-hal.h> |  | ||||||
| 
 |  | ||||||
| #include <gui/gui.h> |  | ||||||
| #include <notification/notification-messages.h> |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     const char* name; |  | ||||||
|     const GpioPin* pin; |  | ||||||
| } GpioItem; |  | ||||||
| 
 |  | ||||||
| static const GpioItem GPIO_PINS[] = { |  | ||||||
|     {"1.2: PA7", &gpio_ext_pa7}, |  | ||||||
|     {"1.3: PA6", &gpio_ext_pa6}, |  | ||||||
|     {"1.4: PA4", &gpio_ext_pa4}, |  | ||||||
|     {"1.5: PB3", &gpio_ext_pb3}, |  | ||||||
|     {"1.6: PB2", &gpio_ext_pb2}, |  | ||||||
|     {"1.7: PC3", &gpio_ext_pc3}, |  | ||||||
|     {"2.7: PC1", &gpio_ext_pc1}, |  | ||||||
|     {"2.8: PC0", &gpio_ext_pc0}, |  | ||||||
|     {"*.*: ALL", NULL}, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static const size_t GPIO_PINS_COUNT = sizeof(GPIO_PINS) / sizeof(GPIO_PINS[0]); |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     osMessageQueueId_t input_queue; |  | ||||||
|     uint8_t gpio_index; |  | ||||||
|     ViewPort* view_port; |  | ||||||
|     Gui* gui; |  | ||||||
|     NotificationApp* notification; |  | ||||||
| } GpioTest; |  | ||||||
| 
 |  | ||||||
| static void gpio_test_render_callback(Canvas* canvas, void* ctx) { |  | ||||||
|     GpioTest* gpio_test = ctx; |  | ||||||
| 
 |  | ||||||
|     canvas_clear(canvas); |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
|     canvas_draw_str(canvas, 2, 10, "GPIO Control"); |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
|     canvas_draw_str(canvas, 2, 25, GPIO_PINS[gpio_test->gpio_index].name); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void gpio_test_input_callback(InputEvent* input_event, void* ctx) { |  | ||||||
|     GpioTest* gpio_test = ctx; |  | ||||||
| 
 |  | ||||||
|     osMessageQueuePut(gpio_test->input_queue, input_event, 0, 0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void gpio_test_configure_pins(GpioMode mode) { |  | ||||||
|     for(size_t i = 0; i < GPIO_PINS_COUNT; i++) { |  | ||||||
|         if(!GPIO_PINS[i].pin) continue; |  | ||||||
|         hal_gpio_write(GPIO_PINS[i].pin, false); |  | ||||||
|         hal_gpio_init(GPIO_PINS[i].pin, mode, GpioPullNo, GpioSpeedVeryHigh); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void gpio_test_set_pin(uint8_t index, bool level) { |  | ||||||
|     if(GPIO_PINS[index].pin) { |  | ||||||
|         hal_gpio_write(GPIO_PINS[index].pin, level); |  | ||||||
|     } else { |  | ||||||
|         for(size_t i = 0; i < GPIO_PINS_COUNT; i++) { |  | ||||||
|             if(!GPIO_PINS[i].pin) continue; |  | ||||||
|             hal_gpio_write(GPIO_PINS[i].pin, level); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| GpioTest* gpio_test_alloc() { |  | ||||||
|     GpioTest* instance = furi_alloc(sizeof(GpioTest)); |  | ||||||
| 
 |  | ||||||
|     gpio_test_configure_pins(GpioModeOutputPushPull); |  | ||||||
| 
 |  | ||||||
|     instance->input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL); |  | ||||||
|     furi_check(instance->input_queue); |  | ||||||
| 
 |  | ||||||
|     instance->view_port = view_port_alloc(); |  | ||||||
|     view_port_draw_callback_set(instance->view_port, gpio_test_render_callback, instance); |  | ||||||
|     view_port_input_callback_set(instance->view_port, gpio_test_input_callback, instance); |  | ||||||
| 
 |  | ||||||
|     instance->gui = furi_record_open("gui"); |  | ||||||
|     gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); |  | ||||||
| 
 |  | ||||||
|     instance->notification = furi_record_open("notification"); |  | ||||||
| 
 |  | ||||||
|     return instance; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void gpio_test_free(GpioTest* instance) { |  | ||||||
|     furi_assert(instance); |  | ||||||
| 
 |  | ||||||
|     furi_record_close("notification"); |  | ||||||
| 
 |  | ||||||
|     view_port_enabled_set(instance->view_port, false); |  | ||||||
|     gui_remove_view_port(instance->gui, instance->view_port); |  | ||||||
|     furi_record_close("gui"); |  | ||||||
| 
 |  | ||||||
|     view_port_free(instance->view_port); |  | ||||||
| 
 |  | ||||||
|     osMessageQueueDelete(instance->input_queue); |  | ||||||
| 
 |  | ||||||
|     gpio_test_configure_pins(GpioModeAnalog); |  | ||||||
| 
 |  | ||||||
|     free(instance); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int32_t gpio_test_app(void* p) { |  | ||||||
|     GpioTest* gpio_test = gpio_test_alloc(); |  | ||||||
| 
 |  | ||||||
|     InputEvent event; |  | ||||||
|     while(osMessageQueueGet(gpio_test->input_queue, &event, NULL, osWaitForever) == osOK) { |  | ||||||
|         if(event.type == InputTypeShort) { |  | ||||||
|             if(event.key == InputKeyBack) { |  | ||||||
|                 notification_message(gpio_test->notification, &sequence_reset_green); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if(event.key == InputKeyRight) { |  | ||||||
|                 if(gpio_test->gpio_index < (GPIO_PINS_COUNT - 1)) { |  | ||||||
|                     gpio_test->gpio_index++; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if(event.key == InputKeyLeft) { |  | ||||||
|                 if(gpio_test->gpio_index > 0) { |  | ||||||
|                     gpio_test->gpio_index--; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             if(event.key == InputKeyOk) { |  | ||||||
|                 if(event.type == InputTypePress) { |  | ||||||
|                     gpio_test_set_pin(gpio_test->gpio_index, true); |  | ||||||
|                     notification_message(gpio_test->notification, &sequence_set_green_255); |  | ||||||
|                 } else if(event.type == InputTypeRelease) { |  | ||||||
|                     gpio_test_set_pin(gpio_test->gpio_index, false); |  | ||||||
|                     notification_message(gpio_test->notification, &sequence_reset_green); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         view_port_update(gpio_test->view_port); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     gpio_test_free(gpio_test); |  | ||||||
| 
 |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
							
								
								
									
										76
									
								
								applications/gpio/gpio_app.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										76
									
								
								applications/gpio/gpio_app.c
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,76 @@ | |||||||
|  | #include "gpio_app_i.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi-hal.h> | ||||||
|  | 
 | ||||||
|  | static bool gpio_app_custom_event_callback(void* context, uint32_t event) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     GpioApp* app = context; | ||||||
|  |     return scene_manager_handle_custom_event(app->scene_manager, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool gpio_app_back_event_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     GpioApp* app = context; | ||||||
|  |     return scene_manager_handle_back_event(app->scene_manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GpioApp* gpio_app_alloc() { | ||||||
|  |     GpioApp* app = furi_alloc(sizeof(GpioApp)); | ||||||
|  | 
 | ||||||
|  |     app->gui = furi_record_open("gui"); | ||||||
|  |     app->notifications = furi_record_open("notification"); | ||||||
|  | 
 | ||||||
|  |     app->view_dispatcher = view_dispatcher_alloc(); | ||||||
|  |     app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app); | ||||||
|  |     view_dispatcher_enable_queue(app->view_dispatcher); | ||||||
|  |     view_dispatcher_set_event_callback_context(app->view_dispatcher, app); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_set_custom_event_callback( | ||||||
|  |         app->view_dispatcher, gpio_app_custom_event_callback); | ||||||
|  |     view_dispatcher_set_navigation_event_callback( | ||||||
|  |         app->view_dispatcher, gpio_app_back_event_callback); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||||
|  | 
 | ||||||
|  |     app->var_item_list = variable_item_list_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, | ||||||
|  |         GpioAppViewVarItemList, | ||||||
|  |         variable_item_list_get_view(app->var_item_list)); | ||||||
|  |     app->gpio_test = gpio_test_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test)); | ||||||
|  | 
 | ||||||
|  |     scene_manager_next_scene(app->scene_manager, GpioSceneStart); | ||||||
|  | 
 | ||||||
|  |     return app; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gpio_app_free(GpioApp* app) { | ||||||
|  |     furi_assert(app); | ||||||
|  | 
 | ||||||
|  |     // Views
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList); | ||||||
|  |     variable_item_list_free(app->var_item_list); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest); | ||||||
|  |     gpio_test_free(app->gpio_test); | ||||||
|  |     // View dispatcher
 | ||||||
|  |     view_dispatcher_free(app->view_dispatcher); | ||||||
|  |     scene_manager_free(app->scene_manager); | ||||||
|  |     // Close records
 | ||||||
|  |     furi_record_close("gui"); | ||||||
|  |     furi_record_close("notification"); | ||||||
|  | 
 | ||||||
|  |     free(app); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t gpio_app(void* p) { | ||||||
|  |     GpioApp* gpio_app = gpio_app_alloc(); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_run(gpio_app->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     gpio_app_free(gpio_app); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
| @ -1,10 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| void MX_USB_Device_Init(); | typedef struct GpioApp GpioApp; | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
							
								
								
									
										28
									
								
								applications/gpio/gpio_app_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								applications/gpio/gpio_app_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "gpio_app.h" | ||||||
|  | #include "gpio_item.h" | ||||||
|  | #include "scenes/gpio_scene.h" | ||||||
|  | 
 | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | #include <notification/notification-messages.h> | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/variable-item-list.h> | ||||||
|  | #include "views/gpio_test.h" | ||||||
|  | 
 | ||||||
|  | struct GpioApp { | ||||||
|  |     Gui* gui; | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  |     SceneManager* scene_manager; | ||||||
|  |     NotificationApp* notifications; | ||||||
|  | 
 | ||||||
|  |     VariableItemList* var_item_list; | ||||||
|  |     GpioTest* gpio_test; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     GpioAppViewVarItemList, | ||||||
|  |     GpioAppViewGpioTest, | ||||||
|  | } GpioAppView; | ||||||
							
								
								
									
										51
									
								
								applications/gpio/gpio_item.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								applications/gpio/gpio_item.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | #include "gpio_item.h" | ||||||
|  | 
 | ||||||
|  | #include <furi-hal-resources.h> | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     const char* name; | ||||||
|  |     const GpioPin* pin; | ||||||
|  | } GpioItem; | ||||||
|  | 
 | ||||||
|  | static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { | ||||||
|  |     {"1.2: PA7", &gpio_ext_pa7}, | ||||||
|  |     {"1.3: PA6", &gpio_ext_pa6}, | ||||||
|  |     {"1.4: PA4", &gpio_ext_pa4}, | ||||||
|  |     {"1.5: PB3", &gpio_ext_pb3}, | ||||||
|  |     {"1.6: PB2", &gpio_ext_pb2}, | ||||||
|  |     {"1.7: PC3", &gpio_ext_pc3}, | ||||||
|  |     {"2.7: PC1", &gpio_ext_pc1}, | ||||||
|  |     {"2.8: PC0", &gpio_ext_pc0}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void gpio_item_configure_pin(uint8_t index, GpioMode mode) { | ||||||
|  |     furi_assert(index < GPIO_ITEM_COUNT); | ||||||
|  |     hal_gpio_write(gpio_item[index].pin, false); | ||||||
|  |     hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gpio_item_configure_all_pins(GpioMode mode) { | ||||||
|  |     for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { | ||||||
|  |         gpio_item_configure_pin(i, mode); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gpio_item_set_pin(uint8_t index, bool level) { | ||||||
|  |     furi_assert(index < GPIO_ITEM_COUNT); | ||||||
|  |     hal_gpio_write(gpio_item[index].pin, level); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gpio_item_set_all_pins(bool level) { | ||||||
|  |     for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { | ||||||
|  |         gpio_item_set_pin(i, level); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char* gpio_item_get_pin_name(uint8_t index) { | ||||||
|  |     furi_assert(index < GPIO_ITEM_COUNT + 1); | ||||||
|  |     if(index == GPIO_ITEM_COUNT) { | ||||||
|  |         return "ALL"; | ||||||
|  |     } else { | ||||||
|  |         return gpio_item[index].name; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								applications/gpio/gpio_item.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								applications/gpio/gpio_item.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi-hal-gpio.h> | ||||||
|  | 
 | ||||||
|  | #define GPIO_ITEM_COUNT 8 | ||||||
|  | 
 | ||||||
|  | void gpio_item_configure_pin(uint8_t index, GpioMode mode); | ||||||
|  | 
 | ||||||
|  | void gpio_item_configure_all_pins(GpioMode mode); | ||||||
|  | 
 | ||||||
|  | void gpio_item_set_pin(uint8_t index, bool level); | ||||||
|  | 
 | ||||||
|  | void gpio_item_set_all_pins(bool level); | ||||||
|  | 
 | ||||||
|  | const char* gpio_item_get_pin_name(uint8_t index); | ||||||
							
								
								
									
										30
									
								
								applications/gpio/scenes/gpio_scene.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								applications/gpio/scenes/gpio_scene.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | #include "gpio_scene.h" | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, | ||||||
|  | void (*const gpio_scene_on_enter_handlers[])(void*) = { | ||||||
|  | #include "gpio_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, | ||||||
|  | bool (*const gpio_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { | ||||||
|  | #include "gpio_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, | ||||||
|  | void (*const gpio_scene_on_exit_handlers[])(void* context) = { | ||||||
|  | #include "gpio_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Initialize scene handlers configuration structure
 | ||||||
|  | const SceneManagerHandlers gpio_scene_handlers = { | ||||||
|  |     .on_enter_handlers = gpio_scene_on_enter_handlers, | ||||||
|  |     .on_event_handlers = gpio_scene_on_event_handlers, | ||||||
|  |     .on_exit_handlers = gpio_scene_on_exit_handlers, | ||||||
|  |     .scene_num = GpioSceneNum, | ||||||
|  | }; | ||||||
							
								
								
									
										29
									
								
								applications/gpio/scenes/gpio_scene.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								applications/gpio/scenes/gpio_scene.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | // Generate scene id and total number
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) GpioScene##id, | ||||||
|  | typedef enum { | ||||||
|  | #include "gpio_scene_config.h" | ||||||
|  |     GpioSceneNum, | ||||||
|  | } GpioScene; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | extern const SceneManagerHandlers gpio_scene_handlers; | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); | ||||||
|  | #include "gpio_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) \ | ||||||
|  |     bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); | ||||||
|  | #include "gpio_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); | ||||||
|  | #include "gpio_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
							
								
								
									
										2
									
								
								applications/gpio/scenes/gpio_scene_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								applications/gpio/scenes/gpio_scene_config.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | ADD_SCENE(gpio, start, Start) | ||||||
|  | ADD_SCENE(gpio, test, Test) | ||||||
							
								
								
									
										92
									
								
								applications/gpio/scenes/gpio_scene_start.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										92
									
								
								applications/gpio/scenes/gpio_scene_start.c
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,92 @@ | |||||||
|  | #include "../gpio_app_i.h" | ||||||
|  | #include "furi-hal-power.h" | ||||||
|  | 
 | ||||||
|  | #define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL) | ||||||
|  | #define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL) | ||||||
|  | #define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL) | ||||||
|  | 
 | ||||||
|  | enum GpioItem { | ||||||
|  |     GpioItemOtg, | ||||||
|  |     GpioItemTest, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum GpioOtg { | ||||||
|  |     GpioOtgOff, | ||||||
|  |     GpioOtgOn, | ||||||
|  |     GpioOtgSettingsNum, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const char* const gpio_otg_text[GpioOtgSettingsNum] = { | ||||||
|  |     "Off", | ||||||
|  |     "On", | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t index) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     GpioApp* app = context; | ||||||
|  |     if(index == GpioItemTest) { | ||||||
|  |         view_dispatcher_send_custom_event( | ||||||
|  |             app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_TEST); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gpio_scene_start_var_list_change_callback(VariableItem* item) { | ||||||
|  |     GpioApp* app = variable_item_get_context(item); | ||||||
|  |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|  | 
 | ||||||
|  |     variable_item_set_current_value_text(item, gpio_otg_text[index]); | ||||||
|  |     if(index == GpioOtgOff) { | ||||||
|  |         view_dispatcher_send_custom_event( | ||||||
|  |             app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF); | ||||||
|  |     } else if(index == GpioOtgOn) { | ||||||
|  |         view_dispatcher_send_custom_event( | ||||||
|  |             app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gpio_scene_start_on_enter(void* context) { | ||||||
|  |     GpioApp* app = context; | ||||||
|  |     VariableItemList* var_item_list = app->var_item_list; | ||||||
|  | 
 | ||||||
|  |     VariableItem* item; | ||||||
|  |     variable_item_list_set_enter_callback( | ||||||
|  |         var_item_list, gpio_scene_start_var_list_enter_callback, app); | ||||||
|  |     item = variable_item_list_add( | ||||||
|  |         var_item_list, | ||||||
|  |         "5V on GPIO", | ||||||
|  |         GpioOtgSettingsNum, | ||||||
|  |         gpio_scene_start_var_list_change_callback, | ||||||
|  |         app); | ||||||
|  |     if(furi_hal_power_is_otg_enabled()) { | ||||||
|  |         variable_item_set_current_value_index(item, GpioOtgOn); | ||||||
|  |         variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOn]); | ||||||
|  |     } else { | ||||||
|  |         variable_item_set_current_value_index(item, GpioOtgOff); | ||||||
|  |         variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]); | ||||||
|  |     } | ||||||
|  |     variable_item_list_add(var_item_list, "GPIO tester", 0, NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewVarItemList); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     GpioApp* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON) { | ||||||
|  |             furi_hal_power_enable_otg(); | ||||||
|  |         } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF) { | ||||||
|  |             furi_hal_power_disable_otg(); | ||||||
|  |         } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_TEST) { | ||||||
|  |             scene_manager_next_scene(app->scene_manager, GpioSceneTest); | ||||||
|  |         } | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gpio_scene_start_on_exit(void* context) { | ||||||
|  |     GpioApp* app = context; | ||||||
|  |     variable_item_list_clean(app->var_item_list); | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								applications/gpio/scenes/gpio_scene_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								applications/gpio/scenes/gpio_scene_test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | #include "../gpio_app_i.h" | ||||||
|  | 
 | ||||||
|  | void gpio_scene_test_ok_callback(InputType type, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     GpioApp* app = context; | ||||||
|  | 
 | ||||||
|  |     if(type == InputTypePress) { | ||||||
|  |         notification_message(app->notifications, &sequence_set_green_255); | ||||||
|  |     } else if(type == InputTypeRelease) { | ||||||
|  |         notification_message(app->notifications, &sequence_reset_green); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gpio_scene_test_on_enter(void* context) { | ||||||
|  |     GpioApp* app = context; | ||||||
|  |     gpio_item_configure_all_pins(GpioModeOutputPushPull); | ||||||
|  |     gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app); | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool gpio_scene_test_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gpio_scene_test_on_exit(void* context) { | ||||||
|  |     gpio_item_configure_all_pins(GpioModeAnalog); | ||||||
|  | } | ||||||
							
								
								
									
										130
									
								
								applications/gpio/views/gpio_test.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										130
									
								
								applications/gpio/views/gpio_test.c
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,130 @@ | |||||||
|  | #include "gpio_test.h" | ||||||
|  | #include "../gpio_item.h" | ||||||
|  | 
 | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | struct GpioTest { | ||||||
|  |     View* view; | ||||||
|  |     GpioTestOkCallback callback; | ||||||
|  |     void* context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t pin_idx; | ||||||
|  | } GpioTestModel; | ||||||
|  | 
 | ||||||
|  | static bool gpio_test_process_left(GpioTest* gpio_test); | ||||||
|  | static bool gpio_test_process_right(GpioTest* gpio_test); | ||||||
|  | static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event); | ||||||
|  | 
 | ||||||
|  | static void gpio_test_draw_callback(Canvas* canvas, void* _model) { | ||||||
|  |     GpioTestModel* model = _model; | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  |     elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Gpio Output mode test"); | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  |     elements_multiline_text_aligned( | ||||||
|  |         canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin"); | ||||||
|  |     elements_multiline_text_aligned( | ||||||
|  |         canvas, 64, 32, AlignCenter, AlignTop, gpio_item_get_pin_name(model->pin_idx)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool gpio_test_input_callback(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     GpioTest* gpio_test = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == InputTypeShort) { | ||||||
|  |         if(event->key == InputKeyRight) { | ||||||
|  |             consumed = gpio_test_process_right(gpio_test); | ||||||
|  |         } else if(event->key == InputKeyLeft) { | ||||||
|  |             consumed = gpio_test_process_left(gpio_test); | ||||||
|  |         } | ||||||
|  |     } else if(event->key == InputKeyOk) { | ||||||
|  |         consumed = gpio_test_process_ok(gpio_test, event); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool gpio_test_process_left(GpioTest* gpio_test) { | ||||||
|  |     with_view_model( | ||||||
|  |         gpio_test->view, (GpioTestModel * model) { | ||||||
|  |             if(model->pin_idx) { | ||||||
|  |                 model->pin_idx--; | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool gpio_test_process_right(GpioTest* gpio_test) { | ||||||
|  |     with_view_model( | ||||||
|  |         gpio_test->view, (GpioTestModel * model) { | ||||||
|  |             if(model->pin_idx < GPIO_ITEM_COUNT) { | ||||||
|  |                 model->pin_idx++; | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         gpio_test->view, (GpioTestModel * model) { | ||||||
|  |             if(event->type == InputTypePress) { | ||||||
|  |                 if(model->pin_idx < GPIO_ITEM_COUNT) { | ||||||
|  |                     gpio_item_set_pin(model->pin_idx, true); | ||||||
|  |                 } else { | ||||||
|  |                     gpio_item_set_all_pins(true); | ||||||
|  |                 } | ||||||
|  |                 consumed = true; | ||||||
|  |             } else if(event->type == InputTypeRelease) { | ||||||
|  |                 if(model->pin_idx < GPIO_ITEM_COUNT) { | ||||||
|  |                     gpio_item_set_pin(model->pin_idx, false); | ||||||
|  |                 } else { | ||||||
|  |                     gpio_item_set_all_pins(false); | ||||||
|  |                 } | ||||||
|  |                 consumed = true; | ||||||
|  |             } | ||||||
|  |             gpio_test->callback(event->type, gpio_test->context); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GpioTest* gpio_test_alloc() { | ||||||
|  |     GpioTest* gpio_test = furi_alloc(sizeof(GpioTest)); | ||||||
|  | 
 | ||||||
|  |     gpio_test->view = view_alloc(); | ||||||
|  |     view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel)); | ||||||
|  |     view_set_context(gpio_test->view, gpio_test); | ||||||
|  |     view_set_draw_callback(gpio_test->view, gpio_test_draw_callback); | ||||||
|  |     view_set_input_callback(gpio_test->view, gpio_test_input_callback); | ||||||
|  | 
 | ||||||
|  |     return gpio_test; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gpio_test_free(GpioTest* gpio_test) { | ||||||
|  |     furi_assert(gpio_test); | ||||||
|  |     view_free(gpio_test->view); | ||||||
|  |     free(gpio_test); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* gpio_test_get_view(GpioTest* gpio_test) { | ||||||
|  |     furi_assert(gpio_test); | ||||||
|  |     return gpio_test->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context) { | ||||||
|  |     furi_assert(gpio_test); | ||||||
|  |     furi_assert(callback); | ||||||
|  |     with_view_model( | ||||||
|  |         gpio_test->view, (GpioTestModel * model) { | ||||||
|  |             gpio_test->callback = callback; | ||||||
|  |             gpio_test->context = context; | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								applications/gpio/views/gpio_test.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								applications/gpio/views/gpio_test.h
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | typedef struct GpioTest GpioTest; | ||||||
|  | typedef void (*GpioTestOkCallback)(InputType type, void* context); | ||||||
|  | 
 | ||||||
|  | GpioTest* gpio_test_alloc(); | ||||||
|  | 
 | ||||||
|  | void gpio_test_free(GpioTest* gpio_test); | ||||||
|  | 
 | ||||||
|  | View* gpio_test_get_view(GpioTest* gpio_test); | ||||||
|  | 
 | ||||||
|  | void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context); | ||||||
| @ -110,11 +110,13 @@ void canvas_set_font(Canvas* canvas, Font font) { | |||||||
|     furi_assert(canvas); |     furi_assert(canvas); | ||||||
|     u8g2_SetFontMode(&canvas->fb, 1); |     u8g2_SetFontMode(&canvas->fb, 1); | ||||||
|     if(font == FontPrimary) { |     if(font == FontPrimary) { | ||||||
|         u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tf); |         u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr); | ||||||
|     } else if(font == FontSecondary) { |     } else if(font == FontSecondary) { | ||||||
|         u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr); |         u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr); | ||||||
|     } else if(font == FontKeyboard) { |     } else if(font == FontKeyboard) { | ||||||
|         u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mf); |         u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr); | ||||||
|  |     } else if(font == FontBigNumbers) { | ||||||
|  |         u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn); | ||||||
|     } else { |     } else { | ||||||
|         furi_crash(NULL); |         furi_crash(NULL); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,3 +1,8 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file canvas.h | ||||||
|  |  * GUI: Canvas API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| @ -8,13 +13,16 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /** Color enumeration */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     ColorWhite = 0x00, |     ColorWhite = 0x00, | ||||||
|     ColorBlack = 0x01, |     ColorBlack = 0x01, | ||||||
| } Color; | } Color; | ||||||
| 
 | 
 | ||||||
| typedef enum { FontPrimary, FontSecondary, FontKeyboard } Font; | /** Fonts enumeration */ | ||||||
|  | typedef enum { FontPrimary, FontSecondary, FontKeyboard, FontBigNumbers } Font; | ||||||
| 
 | 
 | ||||||
|  | /** Alignment enumeration */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     AlignLeft, |     AlignLeft, | ||||||
|     AlignRight, |     AlignRight, | ||||||
| @ -23,59 +31,85 @@ typedef enum { | |||||||
|     AlignCenter, |     AlignCenter, | ||||||
| } Align; | } Align; | ||||||
| 
 | 
 | ||||||
|  | /** Canvas Orientation */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     CanvasOrientationHorizontal, |     CanvasOrientationHorizontal, | ||||||
|     CanvasOrientationVertical, |     CanvasOrientationVertical, | ||||||
| } CanvasOrientation; | } CanvasOrientation; | ||||||
| 
 | 
 | ||||||
|  | /** Canvas anonymouse structure */ | ||||||
| typedef struct Canvas Canvas; | typedef struct Canvas Canvas; | ||||||
| 
 | 
 | ||||||
| /*
 | /** Get Canvas width
 | ||||||
|  * Canvas width |  * | ||||||
|  * @return width in pixels. |  * @param      canvas  Canvas instance | ||||||
|  |  * | ||||||
|  |  * @return     width in pixels. | ||||||
|  */ |  */ | ||||||
| uint8_t canvas_width(Canvas* canvas); | uint8_t canvas_width(Canvas* canvas); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Get Canvas height
 | ||||||
|  * Canvas height |  * | ||||||
|  * @return height in pixels. |  * @param      canvas  Canvas instance | ||||||
|  |  * | ||||||
|  |  * @return     height in pixels. | ||||||
|  */ |  */ | ||||||
| uint8_t canvas_height(Canvas* canvas); | uint8_t canvas_height(Canvas* canvas); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Get current font height
 | ||||||
|  * Get current font height |  * | ||||||
|  * @return height in pixels. |  * @param      canvas  Canvas instance | ||||||
|  |  * | ||||||
|  |  * @return     height in pixels. | ||||||
|  */ |  */ | ||||||
| uint8_t canvas_current_font_height(Canvas* canvas); | uint8_t canvas_current_font_height(Canvas* canvas); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Clear canvas
 | ||||||
|  * Clear canvas, clear rendering buffer |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  */ |  */ | ||||||
| void canvas_clear(Canvas* canvas); | void canvas_clear(Canvas* canvas); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Set drawing color
 | ||||||
|  * Set drawing color |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      color   Color | ||||||
|  */ |  */ | ||||||
| void canvas_set_color(Canvas* canvas, Color color); | void canvas_set_color(Canvas* canvas, Color color); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Invert drawing color
 | ||||||
|  * Invert drawing color |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  */ |  */ | ||||||
| void canvas_invert_color(Canvas* canvas); | void canvas_invert_color(Canvas* canvas); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Set drawing font
 | ||||||
|  * Set drawing font |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      font    Font | ||||||
|  */ |  */ | ||||||
| void canvas_set_font(Canvas* canvas, Font font); | void canvas_set_font(Canvas* canvas, Font font); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw string at position of baseline defined by x, y.
 | ||||||
|  * Draw string at position of baseline defined by x, y. |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x       anchor point x coordinate | ||||||
|  |  * @param      y       anchor point y coordinate | ||||||
|  |  * @param      str     C-string | ||||||
|  */ |  */ | ||||||
| void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str); | void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw aligned string defined by x, y.
 | ||||||
|  * Draw aligned string defined by x, y. |  * | ||||||
|  * Align calculated from position of baseline, string width and ascent (height of the glyphs above the baseline) |  * Align calculated from position of baseline, string width and ascent (height | ||||||
|  |  * of the glyphs above the baseline) | ||||||
|  |  * | ||||||
|  |  * @param      canvas      Canvas instance | ||||||
|  |  * @param      x           anchor point x coordinate | ||||||
|  |  * @param      y           anchor point y coordinate | ||||||
|  |  * @param      horizontal  horizontal alignment | ||||||
|  |  * @param      vertical    vertical alignment | ||||||
|  |  * @param      str         C-string | ||||||
|  */ |  */ | ||||||
| void canvas_draw_str_aligned( | void canvas_draw_str_aligned( | ||||||
|     Canvas* canvas, |     Canvas* canvas, | ||||||
| @ -85,22 +119,30 @@ void canvas_draw_str_aligned( | |||||||
|     Align vertical, |     Align vertical, | ||||||
|     const char* str); |     const char* str); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Get string width
 | ||||||
|  * Get string width |  * | ||||||
|  * @return width in pixels. |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      str     C-string | ||||||
|  |  * | ||||||
|  |  * @return     width in pixels. | ||||||
|  */ |  */ | ||||||
| uint16_t canvas_string_width(Canvas* canvas, const char* str); | uint16_t canvas_string_width(Canvas* canvas, const char* str); | ||||||
| 
 | 
 | ||||||
| /** Get glyph width
 | /** Get glyph width
 | ||||||
|  * @return width in pixels |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param[in]  symbol  character | ||||||
|  |  * | ||||||
|  |  * @return     width in pixels | ||||||
|  */ |  */ | ||||||
| uint8_t canvas_glyph_width(Canvas* canvas, char symbol); | uint8_t canvas_glyph_width(Canvas* canvas, char symbol); | ||||||
| 
 | 
 | ||||||
| /** Draw animation at position defined by x,y.
 | /** Draw animation at position defined by x,y.
 | ||||||
|  * @param canvas - canvas instance |  * | ||||||
|  * @param x - x coordinate |  * @param      canvas          Canvas instance | ||||||
|  * @param y - y coordinate |  * @param      x               x coordinate | ||||||
|  * @param icon_animation - data pointer to IconAnimation |  * @param      y               y coordinate | ||||||
|  |  * @param      icon_animation  IconAnimation instance | ||||||
|  */ |  */ | ||||||
| void canvas_draw_icon_animation( | void canvas_draw_icon_animation( | ||||||
|     Canvas* canvas, |     Canvas* canvas, | ||||||
| @ -109,15 +151,22 @@ void canvas_draw_icon_animation( | |||||||
|     IconAnimation* icon_animation); |     IconAnimation* icon_animation); | ||||||
| 
 | 
 | ||||||
| /** Draw icon at position defined by x,y.
 | /** Draw icon at position defined by x,y.
 | ||||||
|  * @param canvas - canvas instance |  * | ||||||
|  * @param x - x coordinate |  * @param      canvas  Canvas instance | ||||||
|  * @param y - y coordinate |  * @param      x       x coordinate | ||||||
|  * @param icon - data pointer to Icon |  * @param      y       y coordinate | ||||||
|  |  * @param      icon    Icon instance | ||||||
|  */ |  */ | ||||||
| void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon); | void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw XBM bitmap
 | ||||||
|  * Draw xbm icon of width, height at position defined by x,y. |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x       x coordinate | ||||||
|  |  * @param      y       y coordinate | ||||||
|  |  * @param      w       bitmap width | ||||||
|  |  * @param      h       bitmap height | ||||||
|  |  * @param      bitmap  pointer to XBM bitmap data | ||||||
|  */ |  */ | ||||||
| void canvas_draw_xbm( | void canvas_draw_xbm( | ||||||
|     Canvas* canvas, |     Canvas* canvas, | ||||||
| @ -127,48 +176,86 @@ void canvas_draw_xbm( | |||||||
|     uint8_t h, |     uint8_t h, | ||||||
|     const uint8_t* bitmap); |     const uint8_t* bitmap); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw dot at x,y
 | ||||||
|  * Draw dot at x,y |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x       x coordinate | ||||||
|  |  * @param      y       y coordinate | ||||||
|  */ |  */ | ||||||
| void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y); | void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw box of width, height at x,y
 | ||||||
|  * Draw box of width, height at x,y |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x       x coordinate | ||||||
|  |  * @param      y       y coordinate | ||||||
|  |  * @param      width   box width | ||||||
|  |  * @param      height  box height | ||||||
|  */ |  */ | ||||||
| void canvas_draw_box(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); | void canvas_draw_box(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw frame of width, height at x,y
 | ||||||
|  * Draw frame of width, height at x,y |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x       x coordinate | ||||||
|  |  * @param      y       y coordinate | ||||||
|  |  * @param      width   frame width | ||||||
|  |  * @param      height  frame height | ||||||
|  */ |  */ | ||||||
| void canvas_draw_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); | void canvas_draw_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw line from x1,y1 to x2,y2
 | ||||||
|  * Draw line from x1,y1 to x2,y2 |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x1      x1 coordinate | ||||||
|  |  * @param      y1      y1 coordinate | ||||||
|  |  * @param      x2      x2 coordinate | ||||||
|  |  * @param      y2      y2 coordinate | ||||||
|  */ |  */ | ||||||
| void canvas_draw_line(Canvas* canvas, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2); | void canvas_draw_line(Canvas* canvas, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw circle at x,y with radius r
 | ||||||
|  * Draw circle at x,y with radius r |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x       x coordinate | ||||||
|  |  * @param      y       y coordinate | ||||||
|  |  * @param      r       radius | ||||||
|  */ |  */ | ||||||
| void canvas_draw_circle(Canvas* canvas, uint8_t x, uint8_t y, uint8_t r); | void canvas_draw_circle(Canvas* canvas, uint8_t x, uint8_t y, uint8_t r); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw disc at x,y with radius r
 | ||||||
|  * Draw disc at x,y with radius r |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x       x coordinate | ||||||
|  |  * @param      y       y coordinate | ||||||
|  |  * @param      r       radius | ||||||
|  */ |  */ | ||||||
| void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t r); | void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t r); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw glyph
 | ||||||
|  * Draw glyph |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x       x coordinate | ||||||
|  |  * @param      y       y coordinate | ||||||
|  |  * @param      ch      character | ||||||
|  */ |  */ | ||||||
| void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch); | void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Set transparency mode
 | ||||||
|  * Set transparency mode |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      alpha   transparency mode | ||||||
|  */ |  */ | ||||||
| void canvas_set_bitmap_mode(Canvas* canvas, bool alpha); | void canvas_set_bitmap_mode(Canvas* canvas, bool alpha); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw rounded-corner frame of width, height at x,y, with round value raduis
 | ||||||
|  * Draw rounded-corner frame of width, height at x,y, with round value raduis |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x       x coordinate | ||||||
|  |  * @param      y       y coordinate | ||||||
|  |  * @param      width   frame width | ||||||
|  |  * @param      height  frame height | ||||||
|  |  * @param      radius  frame corner radius | ||||||
|  */ |  */ | ||||||
| void canvas_draw_rframe( | void canvas_draw_rframe( | ||||||
|     Canvas* canvas, |     Canvas* canvas, | ||||||
| @ -178,8 +265,14 @@ void canvas_draw_rframe( | |||||||
|     uint8_t height, |     uint8_t height, | ||||||
|     uint8_t radius); |     uint8_t radius); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw rounded-corner box of width, height at x,y, with round value raduis
 | ||||||
|  * Draw rounded-corner box of width, height at x,y, with round value raduis |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * @param      x       x coordinate | ||||||
|  |  * @param      y       y coordinate | ||||||
|  |  * @param      width   box width | ||||||
|  |  * @param      height  box height | ||||||
|  |  * @param      radius  box corner radius | ||||||
|  */ |  */ | ||||||
| void canvas_draw_rbox( | void canvas_draw_rbox( | ||||||
|     Canvas* canvas, |     Canvas* canvas, | ||||||
|  | |||||||
| @ -1,8 +1,15 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file canvas_i.h | ||||||
|  |  * GUI: internal Canvas API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "canvas.h" | #include "canvas.h" | ||||||
| #include <u8g2.h> | #include <u8g2.h> | ||||||
| 
 | 
 | ||||||
|  | /** Canvas structure
 | ||||||
|  |  */ | ||||||
| struct Canvas { | struct Canvas { | ||||||
|     u8g2_t fb; |     u8g2_t fb; | ||||||
|     CanvasOrientation orientation; |     CanvasOrientation orientation; | ||||||
| @ -12,40 +19,53 @@ struct Canvas { | |||||||
|     uint8_t height; |     uint8_t height; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /*
 | /** Allocate memory and initialize canvas
 | ||||||
|  * Allocate memory and initialize canvas |  * | ||||||
|  |  * @return     Canvas instance | ||||||
|  */ |  */ | ||||||
| Canvas* canvas_init(); | Canvas* canvas_init(); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Free canvas memory
 | ||||||
|  * Free canvas memory |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  */ |  */ | ||||||
| void canvas_free(Canvas* canvas); | void canvas_free(Canvas* canvas); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Reset canvas drawing tools configuration
 | ||||||
|  * Reset canvas drawing tools configuration |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  */ |  */ | ||||||
| void canvas_reset(Canvas* canvas); | void canvas_reset(Canvas* canvas); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Commit canvas. Send buffer to display
 | ||||||
|  * Commit canvas. Send buffer to display |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  */ |  */ | ||||||
| void canvas_commit(Canvas* canvas); | void canvas_commit(Canvas* canvas); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Get canvas buffer.
 | ||||||
|  * Get canvas buffer. |  * | ||||||
|  * @return pointer to buffer |  * @param      canvas  Canvas instance | ||||||
|  |  * | ||||||
|  |  * @return     pointer to buffer | ||||||
|  */ |  */ | ||||||
| uint8_t* canvas_get_buffer(Canvas* canvas); | uint8_t* canvas_get_buffer(Canvas* canvas); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Get canvas buffer size.
 | ||||||
|  * Get canvas buffer size. |  * | ||||||
|  * @return size of canvas in bytes |  * @param      canvas  Canvas instance | ||||||
|  |  * | ||||||
|  |  * @return     size of canvas in bytes | ||||||
|  */ |  */ | ||||||
| size_t canvas_get_buffer_size(Canvas* canvas); | size_t canvas_get_buffer_size(Canvas* canvas); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Set drawing region relative to real screen buffer
 | ||||||
|  * Set drawing region relative to real screen buffer |  * | ||||||
|  |  * @param      canvas    Canvas instance | ||||||
|  |  * @param      offset_x  x coordinate offset | ||||||
|  |  * @param      offset_y  y coordinate offset | ||||||
|  |  * @param      width     width | ||||||
|  |  * @param      height    height | ||||||
|  */ |  */ | ||||||
| void canvas_frame_set( | void canvas_frame_set( | ||||||
|     Canvas* canvas, |     Canvas* canvas, | ||||||
| @ -54,12 +74,17 @@ void canvas_frame_set( | |||||||
|     uint8_t width, |     uint8_t width, | ||||||
|     uint8_t height); |     uint8_t height); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Set canvas orientation
 | ||||||
|  * Set canvas orientation |  * | ||||||
|  |  * @param      canvas       Canvas instance | ||||||
|  |  * @param      orientation  CanvasOrientation | ||||||
|  */ |  */ | ||||||
| void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation); | void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Get canvas orientation
 | ||||||
|  * Get canvas orientation |  * | ||||||
|  |  * @param      canvas  Canvas instance | ||||||
|  |  * | ||||||
|  |  * @return     CanvasOrientation | ||||||
|  */ |  */ | ||||||
| CanvasOrientation canvas_get_orientation(const Canvas* canvas); | CanvasOrientation canvas_get_orientation(const Canvas* canvas); | ||||||
|  | |||||||
| @ -278,7 +278,7 @@ void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* t | |||||||
|         canvas_draw_str(canvas, x, y, string_get_cstr(str)); |         canvas_draw_str(canvas, x, y, string_get_cstr(str)); | ||||||
|         start = end + 1; |         start = end + 1; | ||||||
|         y += font_height; |         y += font_height; | ||||||
|     } while(end); |     } while(end && y < 64); | ||||||
|     string_clear(str); |     string_clear(str); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,3 +1,11 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file elements.h | ||||||
|  |  * GUI: Elements API | ||||||
|  |  *  | ||||||
|  |  * Canvas helpers and UI building blocks. | ||||||
|  |  *  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  | |||||||
| @ -335,7 +335,7 @@ void gui_remove_view_port(Gui* gui, ViewPort* view_port) { | |||||||
|     gui_unlock(gui); |     gui_unlock(gui); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void gui_send_view_port_front(Gui* gui, ViewPort* view_port) { | void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port) { | ||||||
|     furi_assert(gui); |     furi_assert(gui); | ||||||
|     furi_assert(view_port); |     furi_assert(view_port); | ||||||
| 
 | 
 | ||||||
| @ -361,7 +361,7 @@ void gui_send_view_port_front(Gui* gui, ViewPort* view_port) { | |||||||
|     gui_unlock(gui); |     gui_unlock(gui); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void gui_send_view_port_back(Gui* gui, ViewPort* view_port) { | void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) { | ||||||
|     furi_assert(gui); |     furi_assert(gui); | ||||||
|     furi_assert(view_port); |     furi_assert(view_port); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,3 +1,8 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file gui.h | ||||||
|  |  * GUI: main API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "view_port.h" | #include "view_port.h" | ||||||
| @ -7,60 +12,74 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* Gui layers */ | /** Gui layers */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     GuiLayerNone, /* Special layer for internal use only */ |     GuiLayerNone, /**< Special layer for internal use only */ | ||||||
| 
 | 
 | ||||||
|     GuiLayerStatusBarLeft, /* Status bar left-side layer, auto-layout */ |     GuiLayerStatusBarLeft, /**< Status bar left-side layer, auto-layout */ | ||||||
|     GuiLayerStatusBarRight, /* Status bar right-side layer, auto-layout */ |     GuiLayerStatusBarRight, /**< Status bar right-side layer, auto-layout */ | ||||||
|     GuiLayerMain, /* Main layer, status bar is shown */ |     GuiLayerMain, /**< Main layer, status bar is shown */ | ||||||
|     GuiLayerFullscreen, /* Fullscreen layer */ |     GuiLayerFullscreen, /**< Fullscreen layer */ | ||||||
| 
 | 
 | ||||||
|     GuiLayerMAX /* Don't use or move, special value */ |     GuiLayerMAX /**< Don't use or move, special value */ | ||||||
| } GuiLayer; | } GuiLayer; | ||||||
| 
 | 
 | ||||||
| /* Gui frame buffer callback */ | /** Gui Canvas Commit Callback */ | ||||||
| typedef void (*GuiCanvasCommitCallback)(uint8_t* data, size_t size, void* context); | typedef void (*GuiCanvasCommitCallback)(uint8_t* data, size_t size, void* context); | ||||||
| 
 | 
 | ||||||
| typedef struct Gui Gui; | typedef struct Gui Gui; | ||||||
| 
 | 
 | ||||||
| /*
 | /** Add view_port to view_port tree
 | ||||||
|  * Add view_port to view_port tree |  * | ||||||
|  * @remarks thread safe |  * @remark     thread safe | ||||||
|  |  * | ||||||
|  |  * @param      gui        Gui instance | ||||||
|  |  * @param      view_port  ViewPort instance | ||||||
|  |  * @param[in]  layer      GuiLayer where to place view_port | ||||||
|  */ |  */ | ||||||
| void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer); | void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Remove view_port from rendering tree
 | ||||||
|  * Remove view_port from rendering tree |  * | ||||||
|  * @remarks thread safe |  * @remark     thread safe | ||||||
|  |  * | ||||||
|  |  * @param      gui        Gui instance | ||||||
|  |  * @param      view_port  ViewPort instance | ||||||
|  */ |  */ | ||||||
| void gui_remove_view_port(Gui* gui, ViewPort* view_port); | void gui_remove_view_port(Gui* gui, ViewPort* view_port); | ||||||
| 
 | 
 | ||||||
| /* Send ViewPort to the front
 | /** Send ViewPort to the front
 | ||||||
|  |  * | ||||||
|  * Places selected ViewPort to the top of the drawing stack |  * Places selected ViewPort to the top of the drawing stack | ||||||
|  * @param gui - Gui instance |  * | ||||||
|  * @param view_port - ViewPort instance |  * @param      gui        Gui instance | ||||||
|  |  * @param      view_port  ViewPort instance | ||||||
|  */ |  */ | ||||||
| void gui_send_view_port_front(Gui* gui, ViewPort* view_port); | void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port); | ||||||
| 
 | 
 | ||||||
| /* Send ViewPort to the back
 | /** Send ViewPort to the back
 | ||||||
|  |  * | ||||||
|  * Places selected ViewPort to the bottom of the drawing stack |  * Places selected ViewPort to the bottom of the drawing stack | ||||||
|  * @param gui - Gui instance |  * | ||||||
|  * @param view_port - ViewPort instance |  * @param      gui        Gui instance | ||||||
|  |  * @param      view_port  ViewPort instance | ||||||
|  */ |  */ | ||||||
| void gui_send_view_port_back(Gui* gui, ViewPort* view_port); | void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port); | ||||||
| 
 | 
 | ||||||
| /* Set gui canvas commit callback
 | /** Set gui canvas commit callback
 | ||||||
|  * This callback will be called upon Canvas commit |  * | ||||||
|  * Callback dispatched from GUI thread and is time critical |  * This callback will be called upon Canvas commit Callback dispatched from GUI | ||||||
|  * @param gui - Gui instance |  * thread and is time critical | ||||||
|  * @param callback - GuiCanvasCommitCallback |  * | ||||||
|  |  * @param      gui       Gui instance | ||||||
|  |  * @param      callback  GuiCanvasCommitCallback | ||||||
|  */ |  */ | ||||||
| void gui_set_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback); | void gui_set_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback); | ||||||
| 
 | 
 | ||||||
| /* Set gui canvas commit callback context
 | /** Set gui canvas commit callback context
 | ||||||
|  * @param gui - Gui instance |  * | ||||||
|  * @param context - pointer to context |  * @param      gui      Gui instance | ||||||
|  |  * @param      context  pointer to context | ||||||
|  */ |  */ | ||||||
| void gui_set_framebuffer_callback_context(Gui* gui, void* context); | void gui_set_framebuffer_callback_context(Gui* gui, void* context); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,3 +1,8 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file gui_i.h | ||||||
|  |  * GUI: main API internals | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "gui.h" | #include "gui.h" | ||||||
| @ -31,6 +36,7 @@ | |||||||
| 
 | 
 | ||||||
| ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST); | ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST); | ||||||
| 
 | 
 | ||||||
|  | /** Gui structure */ | ||||||
| struct Gui { | struct Gui { | ||||||
|     // Thread and lock
 |     // Thread and lock
 | ||||||
|     osThreadId_t thread; |     osThreadId_t thread; | ||||||
| @ -54,8 +60,9 @@ struct Gui { | |||||||
| 
 | 
 | ||||||
| ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); | ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); | ||||||
| 
 | 
 | ||||||
| /* Update GUI, request redraw
 | /** Update GUI, request redraw
 | ||||||
|  * @param gui, Gui instance |  * | ||||||
|  |  * @param      gui   Gui instance | ||||||
|  */ |  */ | ||||||
| void gui_update(Gui* gui); | void gui_update(Gui* gui); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,3 +1,8 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file icon.h | ||||||
|  |  * GUI: Icon API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| @ -8,10 +13,28 @@ extern "C" { | |||||||
| 
 | 
 | ||||||
| typedef struct Icon Icon; | typedef struct Icon Icon; | ||||||
| 
 | 
 | ||||||
|  | /** Get icon width 
 | ||||||
|  |  * | ||||||
|  |  * @param[in]  instance  pointer to Icon data | ||||||
|  |  * | ||||||
|  |  * @return     width in pixels | ||||||
|  |  */ | ||||||
| uint8_t icon_get_width(const Icon* instance); | uint8_t icon_get_width(const Icon* instance); | ||||||
| 
 | 
 | ||||||
|  | /** Get icon height
 | ||||||
|  |  * | ||||||
|  |  * @param[in]  instance  pointer to Icon data | ||||||
|  |  * | ||||||
|  |  * @return     height in pixels | ||||||
|  |  */ | ||||||
| uint8_t icon_get_height(const Icon* instance); | uint8_t icon_get_height(const Icon* instance); | ||||||
| 
 | 
 | ||||||
|  | /** Get Icon XBM bitmap data
 | ||||||
|  |  * | ||||||
|  |  * @param[in]  instance  pointer to Icon data | ||||||
|  |  * | ||||||
|  |  * @return     pointer to XBM bitmap data | ||||||
|  |  */ | ||||||
| const uint8_t* icon_get_data(const Icon* instance); | const uint8_t* icon_get_data(const Icon* instance); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | |||||||
| @ -2,31 +2,32 @@ | |||||||
| #include "icon_i.h" | #include "icon_i.h" | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | #include <timers.h> | ||||||
| 
 | 
 | ||||||
| IconAnimation* icon_animation_alloc(const Icon* icon) { | IconAnimation* icon_animation_alloc(const Icon* icon) { | ||||||
|     furi_assert(icon); |     furi_assert(icon); | ||||||
|     IconAnimation* instance = furi_alloc(sizeof(IconAnimation)); |     IconAnimation* instance = furi_alloc(sizeof(IconAnimation)); | ||||||
|     instance->icon = icon; |     instance->icon = icon; | ||||||
|  |     instance->timer = osTimerNew(icon_animation_timer_callback, osTimerPeriodic, instance, NULL); | ||||||
|     return instance; |     return instance; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void icon_animation_free(IconAnimation* instance) { | void icon_animation_free(IconAnimation* instance) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|  |     furi_check(osTimerDelete(instance->timer) == osOK); | ||||||
|     free(instance); |     free(instance); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const uint8_t* icon_animation_get_data(IconAnimation* instance) { | void icon_animation_set_update_callback( | ||||||
|  |     IconAnimation* instance, | ||||||
|  |     IconAnimationCallback callback, | ||||||
|  |     void* context) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     if(instance->tick) { |     instance->callback = callback; | ||||||
|         uint32_t now = osKernelGetTickCount(); |     instance->callback_context = context; | ||||||
|         if(now < instance->tick) { | } | ||||||
|             instance->tick = now; | 
 | ||||||
|             icon_animation_next_frame(instance); | const uint8_t* icon_animation_get_data(IconAnimation* instance) { | ||||||
|         } else if(now - instance->tick > osKernelGetTickFreq() / instance->icon->frame_rate) { |  | ||||||
|             instance->tick = now; |  | ||||||
|             icon_animation_next_frame(instance); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return instance->icon->frames[instance->frame]; |     return instance->icon->frames[instance->frame]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -35,6 +36,19 @@ void icon_animation_next_frame(IconAnimation* instance) { | |||||||
|     instance->frame = (instance->frame + 1) % instance->icon->frame_count; |     instance->frame = (instance->frame + 1) % instance->icon->frame_count; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void icon_animation_timer_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     IconAnimation* instance = context; | ||||||
|  | 
 | ||||||
|  |     if(!instance->animating) return; | ||||||
|  | 
 | ||||||
|  |     icon_animation_next_frame(instance); | ||||||
|  |     if(instance->callback) { | ||||||
|  |         instance->callback(instance, instance->callback_context); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| uint8_t icon_animation_get_width(IconAnimation* instance) { | uint8_t icon_animation_get_width(IconAnimation* instance) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     return instance->icon->width; |     return instance->icon->width; | ||||||
| @ -45,30 +59,24 @@ uint8_t icon_animation_get_height(IconAnimation* instance) { | |||||||
|     return instance->icon->height; |     return instance->icon->height; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool icon_animation_is_animated(IconAnimation* instance) { |  | ||||||
|     furi_assert(instance); |  | ||||||
|     return instance->icon->frame_count > 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool icon_animation_is_animating(IconAnimation* instance) { |  | ||||||
|     furi_assert(instance); |  | ||||||
|     return instance->tick > 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void icon_animation_start(IconAnimation* instance) { | void icon_animation_start(IconAnimation* instance) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     instance->tick = osKernelGetTickCount(); |     if(!instance->animating) { | ||||||
|  |         instance->animating = true; | ||||||
|  |         furi_check( | ||||||
|  |             xTimerChangePeriod( | ||||||
|  |                 instance->timer, (osKernelGetTickFreq() / instance->icon->frame_rate), 0) == | ||||||
|  |             pdPASS); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void icon_animation_stop(IconAnimation* instance) { | void icon_animation_stop(IconAnimation* instance) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     instance->tick = 0; |     if(instance->animating) { | ||||||
|     instance->frame = 0; |         instance->animating = false; | ||||||
| } |         furi_check(xTimerStop(instance->timer, 0) == pdPASS); | ||||||
| 
 |         instance->frame = 0; | ||||||
| uint8_t icon_animation_get_current_frame(IconAnimation* instance) { |     } | ||||||
|     furi_assert(instance); |  | ||||||
|     return instance->frame; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool icon_animation_is_last_frame(IconAnimation* instance) { | bool icon_animation_is_last_frame(IconAnimation* instance) { | ||||||
|  | |||||||
| @ -1,64 +1,87 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file icon_animation.h | ||||||
|  |  * GUI: IconAnimation API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  | #include <assets_icons.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include <assets_icons.h> | /** Icon Animation */ | ||||||
| 
 |  | ||||||
| typedef struct IconAnimation IconAnimation; | typedef struct IconAnimation IconAnimation; | ||||||
| 
 | 
 | ||||||
| /*
 | /** Icon Animation Callback. Used for update notification */ | ||||||
|  * Allocate icon animation instance with const icon data. | typedef void (*IconAnimationCallback)(IconAnimation* instance, void* context); | ||||||
|  | 
 | ||||||
|  | /** Allocate icon animation instance with const icon data.
 | ||||||
|  |  *  | ||||||
|  * always returns Icon or stops system if not enough memory |  * always returns Icon or stops system if not enough memory | ||||||
|  |  * | ||||||
|  |  * @param[in]  icon  pointer to Icon data | ||||||
|  |  * | ||||||
|  |  * @return     IconAnimation instance | ||||||
|  */ |  */ | ||||||
| IconAnimation* icon_animation_alloc(const Icon* icon); | IconAnimation* icon_animation_alloc(const Icon* icon); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Release icon animation instance
 | ||||||
|  * Release icon animation instance |  * | ||||||
|  |  * @param      instance  IconAnimation instance | ||||||
|  */ |  */ | ||||||
| void icon_animation_free(IconAnimation* instance); | void icon_animation_free(IconAnimation* instance); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Set IconAnimation update callback
 | ||||||
|  * Get icon animation width |  * | ||||||
|  |  * Normally you do not need to use this function, use view_tie_icon_animation | ||||||
|  |  * instead. | ||||||
|  |  * | ||||||
|  |  * @param      instance  IconAnimation instance | ||||||
|  |  * @param[in]  callback  IconAnimationCallback | ||||||
|  |  * @param      context   callback context | ||||||
|  |  */ | ||||||
|  | void icon_animation_set_update_callback( | ||||||
|  |     IconAnimation* instance, | ||||||
|  |     IconAnimationCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | /** Get icon animation width
 | ||||||
|  |  * | ||||||
|  |  * @param      instance  IconAnimation instance | ||||||
|  |  * | ||||||
|  |  * @return     width in pixels | ||||||
|  */ |  */ | ||||||
| uint8_t icon_animation_get_width(IconAnimation* instance); | uint8_t icon_animation_get_width(IconAnimation* instance); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Get icon animation height
 | ||||||
|  * Get icon animation height |  * | ||||||
|  |  * @param      instance  IconAnimation instance | ||||||
|  |  * | ||||||
|  |  * @return     height in pixels | ||||||
|  */ |  */ | ||||||
| uint8_t icon_animation_get_height(IconAnimation* instance); | uint8_t icon_animation_get_height(IconAnimation* instance); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Start icon animation
 | ||||||
|  * Check if icon is animated |  * | ||||||
|  */ |  * @param      instance  IconAnimation instance | ||||||
| bool icon_animation_is_animated(IconAnimation* instance); |  | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
|  * Check if icon animation is active |  | ||||||
|  */ |  | ||||||
| bool icon_animation_is_animating(IconAnimation* instance); |  | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
|  * Start icon animation |  | ||||||
|  */ |  */ | ||||||
| void icon_animation_start(IconAnimation* instance); | void icon_animation_start(IconAnimation* instance); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Stop icon animation
 | ||||||
|  * Stop icon animation |  * | ||||||
|  |  * @param      instance  IconAnimation instance | ||||||
|  */ |  */ | ||||||
| void icon_animation_stop(IconAnimation* instance); | void icon_animation_stop(IconAnimation* instance); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Returns true if current frame is a last one
 | ||||||
|  * Get current frame |  * | ||||||
|  */ |  * @param      instance  IconAnimation instance | ||||||
| uint8_t icon_animation_get_current_frame(IconAnimation* instance); |  * | ||||||
| 
 |  * @return     true if last frame | ||||||
| /*
 |  | ||||||
|  * Returns true if current frame is a last one |  | ||||||
|  */ |  */ | ||||||
| bool icon_animation_is_last_frame(IconAnimation* instance); | bool icon_animation_is_last_frame(IconAnimation* instance); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,21 +1,39 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file icon_animation_i.h | ||||||
|  |  * GUI: internal IconAnimation API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "icon_animation.h" | #include "icon_animation.h" | ||||||
| 
 | 
 | ||||||
| #include <stdint.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| struct IconAnimation { | struct IconAnimation { | ||||||
|     const Icon* icon; |     const Icon* icon; | ||||||
|     uint8_t frame; |     uint8_t frame; | ||||||
|     uint32_t tick; |     bool animating; | ||||||
|  |     osTimerId_t timer; | ||||||
|  |     IconAnimationCallback callback; | ||||||
|  |     void* callback_context; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /*
 | /** Get pointer to current frame data
 | ||||||
|  * Get pointer to current frame data |  * | ||||||
|  |  * @param      instance  IconAnimation instance | ||||||
|  |  * | ||||||
|  |  * @return     pointer to current frame XBM bitmap data | ||||||
|  */ |  */ | ||||||
| const uint8_t* icon_animation_get_data(IconAnimation* instance); | const uint8_t* icon_animation_get_data(IconAnimation* instance); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Advance to next frame
 | ||||||
|  * Advance to next frame |  * | ||||||
|  |  * @param      instance  IconAnimation instance | ||||||
|  */ |  */ | ||||||
| void icon_animation_next_frame(IconAnimation* instance); | void icon_animation_next_frame(IconAnimation* instance); | ||||||
|  | 
 | ||||||
|  | /** IconAnimation timer callback
 | ||||||
|  |  * | ||||||
|  |  * @param      context  pointer to IconAnimation | ||||||
|  |  */ | ||||||
|  | void icon_animation_timer_callback(void* context); | ||||||
|  | |||||||
| @ -1,3 +1,8 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file icon_i.h | ||||||
|  |  * GUI: internal Icon API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #include "icon.h" | #include "icon.h" | ||||||
| 
 | 
 | ||||||
| struct Icon { | struct Icon { | ||||||
|  | |||||||
| @ -1,4 +1,10 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file button_menu.h | ||||||
|  |  * GUI: ButtonMenu view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| @ -6,40 +12,48 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* ButtonMenu anonymous structure */ | /** ButtonMenu anonymous structure */ | ||||||
| typedef struct ButtonMenu ButtonMenu; | typedef struct ButtonMenu ButtonMenu; | ||||||
|  | 
 | ||||||
|  | /** ButtonMenuItem anonymous structure */ | ||||||
| typedef struct ButtonMenuItem ButtonMenuItem; | typedef struct ButtonMenuItem ButtonMenuItem; | ||||||
| 
 | 
 | ||||||
| /* Callback for any button menu actions */ | /** Callback for any button menu actions */ | ||||||
| typedef void (*ButtonMenuItemCallback)(void* context, int32_t index, InputType type); | typedef void (*ButtonMenuItemCallback)(void* context, int32_t index, InputType type); | ||||||
| 
 | 
 | ||||||
| /* Type of button. Difference in drawing buttons. */ | /** Type of button. Difference in drawing buttons. */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     ButtonMenuItemTypeCommon, |     ButtonMenuItemTypeCommon, | ||||||
|     ButtonMenuItemTypeControl, |     ButtonMenuItemTypeControl, | ||||||
| } ButtonMenuItemType; | } ButtonMenuItemType; | ||||||
| 
 | 
 | ||||||
| /**
 | /** Get button menu view
 | ||||||
|  * @brief Get button menu view |  * | ||||||
|  * @param button_menu - ButtonMenu instance |  * @param      button_menu  ButtonMenu instance | ||||||
|  * @return View instance that can be used for embedding |  * | ||||||
|  |  * @return     View instance that can be used for embedding | ||||||
|  */ |  */ | ||||||
| View* button_menu_get_view(ButtonMenu* button_menu); | View* button_menu_get_view(ButtonMenu* button_menu); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Clean button menu
 | ||||||
|  * @brief Clean button menu |  * | ||||||
|  * @param button_menu - ButtonMenu instance |  * @param      button_menu  ButtonMenu instance | ||||||
|  */ |  */ | ||||||
| void button_menu_clean(ButtonMenu* button_menu); | void button_menu_clean(ButtonMenu* button_menu); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Add item to button menu instance
 | ||||||
|  * @brief Add item to button menu instance |  * | ||||||
|  * @param button_menu   - ButtonMenu instance |  * @param      button_menu       ButtonMenu instance | ||||||
|  * @param label         - text inside new button |  * @param      label             text inside new button | ||||||
|  * @param index         - value to distinct between buttons inside ButtonMenuItemCallback |  * @param      index             value to distinct between buttons inside | ||||||
|  * @param type          - type of button to create. Differ by button drawing. |  *                               ButtonMenuItemCallback | ||||||
|  *                      Control buttons have no frames, and have more squared borders. |  * @param      callback          The callback | ||||||
|  * @return              pointer to just-created item |  * @param      type              type of button to create. Differ by button | ||||||
|  |  *                               drawing. Control buttons have no frames, and | ||||||
|  |  *                               have more squared borders. | ||||||
|  |  * @param      callback_context  The callback context | ||||||
|  |  * | ||||||
|  |  * @return     pointer to just-created item | ||||||
|  */ |  */ | ||||||
| ButtonMenuItem* button_menu_add_item( | ButtonMenuItem* button_menu_add_item( | ||||||
|     ButtonMenu* button_menu, |     ButtonMenu* button_menu, | ||||||
| @ -49,29 +63,29 @@ ButtonMenuItem* button_menu_add_item( | |||||||
|     ButtonMenuItemType type, |     ButtonMenuItemType type, | ||||||
|     void* callback_context); |     void* callback_context); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Allocate and initialize new instance of ButtonMenu model
 | ||||||
|  * @brief Allocate and initialize new instance of ButtonMenu model |  * | ||||||
|  * @return          just-created ButtonMenu model |  * @return     just-created ButtonMenu model | ||||||
|  */ |  */ | ||||||
| ButtonMenu* button_menu_alloc(void); | ButtonMenu* button_menu_alloc(void); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Free ButtonMenu element
 | ||||||
|  * @brief Free ButtonMenu element |  * | ||||||
|  * @param button_menu - ButtonMenu instance |  * @param      button_menu  ButtonMenu instance | ||||||
|  */ |  */ | ||||||
| void button_menu_free(ButtonMenu* button_menu); | void button_menu_free(ButtonMenu* button_menu); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Set ButtonMenu header on top of canvas
 | ||||||
|  * @brief Set ButtonMenu header on top of canvas |  * | ||||||
|  * @param button_menu   - ButtonMenu instance |  * @param      button_menu  ButtonMenu instance | ||||||
|  * @param header        - header on the top of button menu |  * @param      header       header on the top of button menu | ||||||
|  */ |  */ | ||||||
| void button_menu_set_header(ButtonMenu* button_menu, const char* header); | void button_menu_set_header(ButtonMenu* button_menu, const char* header); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Set selected item
 | ||||||
|  * @brief Set selected item |  * | ||||||
|  * @param button_menu   - ButtonMenu instance |  * @param      button_menu  ButtonMenu instance | ||||||
|  * @param index         - index of ButtonMenu to be selected |  * @param      index        index of ButtonMenu to be selected | ||||||
|  */ |  */ | ||||||
| void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index); | void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,10 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file button_panel.h | ||||||
|  |  * GUI: ButtonPanel view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| @ -10,37 +16,39 @@ typedef struct ButtonPanel ButtonPanel; | |||||||
| 
 | 
 | ||||||
| /** Callback type to call for handling selecting button_panel items */ | /** Callback type to call for handling selecting button_panel items */ | ||||||
| typedef void (*ButtonItemCallback)(void* context, uint32_t index); | typedef void (*ButtonItemCallback)(void* context, uint32_t index); | ||||||
|  | 
 | ||||||
| /** Callback type for additional drawings above main button_panel screen */ | /** Callback type for additional drawings above main button_panel screen */ | ||||||
| typedef void (*ButtonPanelDrawCallback)(Canvas* canvas, void* _model); | typedef void (*ButtonPanelDrawCallback)(Canvas* canvas, void* _model); | ||||||
|  | 
 | ||||||
| /** Callback type to intercept input events of button_panel */ | /** Callback type to intercept input events of button_panel */ | ||||||
| typedef bool (*ButtonPanelInputCallback)(InputEvent* event, void* context); | typedef bool (*ButtonPanelInputCallback)(InputEvent* event, void* context); | ||||||
| 
 | 
 | ||||||
| /** Allocate new button_panel module.
 | /** Allocate new button_panel module.
 | ||||||
|  * |  * | ||||||
|  * @return  just-created module |  * @return     ButtonPanel instance | ||||||
|  */ |  */ | ||||||
| ButtonPanel* button_panel_alloc(void); | ButtonPanel* button_panel_alloc(void); | ||||||
| 
 | 
 | ||||||
| /** Free button_panel module.
 | /** Free button_panel module.
 | ||||||
|  * |  * | ||||||
|  * @param   button_panel - module to free |  * @param      button_panel  ButtonPanel instance | ||||||
|  */ |  */ | ||||||
| void button_panel_free(ButtonPanel* button_panel); | void button_panel_free(ButtonPanel* button_panel); | ||||||
| 
 | 
 | ||||||
| /** Free items from button_panel module. Preallocated matrix stays unchanged.
 | /** Free items from button_panel module. Preallocated matrix stays unchanged.
 | ||||||
|  * |  * | ||||||
|  * @param   button_panel - module to clean |  * @param      button_panel  ButtonPanel instance | ||||||
|  */ |  */ | ||||||
| void button_panel_clean(ButtonPanel* button_panel); | void button_panel_clean(ButtonPanel* button_panel); | ||||||
| 
 | 
 | ||||||
| /** Reserve space for adding items.
 | /** Reserve space for adding items.
 | ||||||
|  * |  * | ||||||
|  * One does not simply use button_panel_add_item() without this function. |  * One does not simply use button_panel_add_item() without this function. It | ||||||
|  * It should be allocated space for it first. |  * should be allocated space for it first. | ||||||
|  * |  * | ||||||
|  * @param   button_panel - module to modify |  * @param      button_panel  ButtonPanel instance | ||||||
|  * @param   reserve_x - number of columns in button_panel |  * @param      reserve_x     number of columns in button_panel | ||||||
|  * @param   reserve_y - number of rows in button_panel |  * @param      reserve_y     number of rows in button_panel | ||||||
|  */ |  */ | ||||||
| void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y); | void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y); | ||||||
| 
 | 
 | ||||||
| @ -48,20 +56,20 @@ void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t re | |||||||
|  * |  * | ||||||
|  * Have to set element in bounds of allocated size by X and by Y. |  * Have to set element in bounds of allocated size by X and by Y. | ||||||
|  * |  * | ||||||
|  * @param   button_panel - module |  * @param      button_panel        ButtonPanel instance | ||||||
|  * @param   index - value to pass to callback |  * @param      index               value to pass to callback | ||||||
|  * @param   matrix_place_x - coordinates by x-axis on virtual grid, it |  * @param      matrix_place_x      coordinates by x-axis on virtual grid, it | ||||||
|  *                           is only used for naviagation |  *                                 is only used for naviagation | ||||||
|  * @param   matrix_place_y - coordinates by y-axis on virtual grid, it |  * @param      matrix_place_y      coordinates by y-axis on virtual grid, it | ||||||
|  *                           is only used for naviagation |  *                                 is only used for naviagation | ||||||
|  * @param   x - x-coordinate to draw icon on |  * @param      x                   x-coordinate to draw icon on | ||||||
|  * @param   y - y-coordinate to draw icon on |  * @param      y                   y-coordinate to draw icon on | ||||||
|  * @param   icon_name - name of the icon to draw |  * @param      icon_name           name of the icon to draw | ||||||
|  * @param   icon_name_selected - name of the icon to draw when current |  * @param      icon_name_selected  name of the icon to draw when current | ||||||
|  *                               element is selected |  *                                 element is selected | ||||||
|  * @param   callback - function to call when specific element is selected |  * @param      callback            function to call when specific element is | ||||||
|  *                     (pressed Ok on selected item) |  *                                 selected (pressed Ok on selected item) | ||||||
|  * @param   callback_context - context to pass to callback |  * @param      callback_context    context to pass to callback | ||||||
|  */ |  */ | ||||||
| void button_panel_add_item( | void button_panel_add_item( | ||||||
|     ButtonPanel* button_panel, |     ButtonPanel* button_panel, | ||||||
| @ -77,17 +85,19 @@ void button_panel_add_item( | |||||||
| 
 | 
 | ||||||
| /** Get button_panel view.
 | /** Get button_panel view.
 | ||||||
|  * |  * | ||||||
|  * @param   button_panel - module to get view from |  * @param      button_panel  ButtonPanel instance | ||||||
|  * @return  acquired view |  * | ||||||
|  |  * @return     acquired view | ||||||
|  */ |  */ | ||||||
| View* button_panel_get_view(ButtonPanel* button_panel); | View* button_panel_get_view(ButtonPanel* button_panel); | ||||||
| 
 | 
 | ||||||
| /** Add label to button_panel module.
 | /** Add label to button_panel module.
 | ||||||
|  * |  * | ||||||
|  * @param   x - x-coordinate to place label |  * @param      button_panel  ButtonPanel instance | ||||||
|  * @param   y - y-coordinate to place label |  * @param      x             x-coordinate to place label | ||||||
|  * @param   font - font to write label with |  * @param      y             y-coordinate to place label | ||||||
|  * @param   label_str - string label to write |  * @param      font          font to write label with | ||||||
|  |  * @param      label_str     string label to write | ||||||
|  */ |  */ | ||||||
| void button_panel_add_label( | void button_panel_add_label( | ||||||
|     ButtonPanel* button_panel, |     ButtonPanel* button_panel, | ||||||
| @ -101,9 +111,9 @@ void button_panel_add_label( | |||||||
|  * |  * | ||||||
|  * Used to add popup drawings after main draw callback is done. |  * Used to add popup drawings after main draw callback is done. | ||||||
|  * |  * | ||||||
|  * @param   button_panel - module to modify |  * @param      button_panel  ButtonPanel instance | ||||||
|  * @param   callback - callback function to set for draw event |  * @param      callback      callback function to set for draw event | ||||||
|  * @param   context - context to pass to callback |  * @param      context       context to pass to callback | ||||||
|  */ |  */ | ||||||
| void button_panel_set_popup_draw_callback( | void button_panel_set_popup_draw_callback( | ||||||
|     ButtonPanel* button_panel, |     ButtonPanel* button_panel, | ||||||
| @ -112,12 +122,12 @@ void button_panel_set_popup_draw_callback( | |||||||
| 
 | 
 | ||||||
| /** Set popup input callback for button_panel module.
 | /** Set popup input callback for button_panel module.
 | ||||||
|  * |  * | ||||||
|  * Used to add popup input callback. It will intercept all input |  * Used to add popup input callback. It will intercept all input events for | ||||||
|  * events for current view. |  * current view. | ||||||
|  * |  * | ||||||
|  * @param   button_panel - module to modify |  * @param      button_panel  ButtonPanel instance | ||||||
|  * @param   callback - function to overwrite main input callbacks |  * @param      callback      function to overwrite main input callbacks | ||||||
|  * @param   context - context to pass to callback |  * @param      context       context to pass to callback | ||||||
|  */ |  */ | ||||||
| void button_panel_set_popup_input_callback( | void button_panel_set_popup_input_callback( | ||||||
|     ButtonPanel* button_panel, |     ButtonPanel* button_panel, | ||||||
|  | |||||||
| @ -1,59 +1,53 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file byte_input.h | ||||||
|  |  * GUI: ByteInput keyboard view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /**
 | /** Byte input anonymous structure  */ | ||||||
|  * @brief Byte input anonymous structure  |  | ||||||
|  *  |  | ||||||
|  */ |  | ||||||
| typedef struct ByteInput ByteInput; | typedef struct ByteInput ByteInput; | ||||||
| 
 | 
 | ||||||
| /**
 | /** callback that is executed on save button press */ | ||||||
|  * @brief callback that is executed on save button press |  | ||||||
|  *  |  | ||||||
|  */ |  | ||||||
| typedef void (*ByteInputCallback)(void* context); | typedef void (*ByteInputCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| /**
 | /** callback that is executed when byte buffer is changed */ | ||||||
|  * @brief callback that is executed when byte buffer is changed |  | ||||||
|  *  |  | ||||||
|  */ |  | ||||||
| typedef void (*ByteChangedCallback)(void* context); | typedef void (*ByteChangedCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| /** 
 | /** Allocate and initialize byte input. This byte input is used to enter bytes.
 | ||||||
|  * @brief Allocate and initialize byte input. This byte input is used to enter bytes. |  | ||||||
|  * |  * | ||||||
|  * @return ByteInput instance pointer |  * @return     ByteInput instance pointer | ||||||
|  */ |  */ | ||||||
| ByteInput* byte_input_alloc(); | ByteInput* byte_input_alloc(); | ||||||
| 
 | 
 | ||||||
| /** 
 | /** Deinitialize and free byte input
 | ||||||
|  * @brief Deinitialize and free byte input |  | ||||||
|  * |  * | ||||||
|  * @param byte_input Byte input instance |  * @param      byte_input  Byte input instance | ||||||
|  */ |  */ | ||||||
| void byte_input_free(ByteInput* byte_input); | void byte_input_free(ByteInput* byte_input); | ||||||
| 
 | 
 | ||||||
| /** 
 | /** Get byte input view
 | ||||||
|  * @brief Get byte input view |  | ||||||
|  * |  * | ||||||
|  * @param byte_input byte input instance |  * @param      byte_input  byte input instance | ||||||
|  * @return View instance that can be used for embedding |  * | ||||||
|  |  * @return     View instance that can be used for embedding | ||||||
|  */ |  */ | ||||||
| View* byte_input_get_view(ByteInput* byte_input); | View* byte_input_get_view(ByteInput* byte_input); | ||||||
| 
 | 
 | ||||||
| /** 
 | /** Set byte input result callback
 | ||||||
|  * @brief Set byte input result callback |  | ||||||
|  * |  * | ||||||
|  * @param byte_input byte input instance |  * @param      byte_input        byte input instance | ||||||
|  * @param input_callback input callback fn |  * @param      input_callback    input callback fn | ||||||
|  * @param changed_callback changed callback fn |  * @param      changed_callback  changed callback fn | ||||||
|  * @param callback_context callback context |  * @param      callback_context  callback context | ||||||
|  * @param bytes buffer to use |  * @param      bytes             buffer to use | ||||||
|  * @param bytes_count buffer length |  * @param      bytes_count       buffer length | ||||||
|  */ |  */ | ||||||
| void byte_input_set_result_callback( | void byte_input_set_result_callback( | ||||||
|     ByteInput* byte_input, |     ByteInput* byte_input, | ||||||
| @ -63,11 +57,10 @@ void byte_input_set_result_callback( | |||||||
|     uint8_t* bytes, |     uint8_t* bytes, | ||||||
|     uint8_t bytes_count); |     uint8_t bytes_count); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Set byte input header text
 | ||||||
|  * @brief Set byte input header text |  | ||||||
|  * |  * | ||||||
|  * @param byte_input byte input instance |  * @param      byte_input  byte input instance | ||||||
|  * @param text text to be shown |  * @param      text        text to be shown | ||||||
|  */ |  */ | ||||||
| void byte_input_set_header_text(ByteInput* byte_input, const char* text); | void byte_input_set_header_text(ByteInput* byte_input, const char* text); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,74 +1,92 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file dialog.h | ||||||
|  |  * GUI: Dialog view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* Dialog anonymous structure */ | /** Dialog anonymous structure */ | ||||||
| typedef struct Dialog Dialog; | typedef struct Dialog Dialog; | ||||||
| 
 | 
 | ||||||
| /* Dialog result */ | /** Dialog result */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     DialogResultLeft, |     DialogResultLeft, | ||||||
|     DialogResultRight, |     DialogResultRight, | ||||||
|     DialogResultBack, |     DialogResultBack, | ||||||
| } DialogResult; | } DialogResult; | ||||||
| 
 | 
 | ||||||
| /* Dialog result callback type
 | /** Dialog result callback type
 | ||||||
|  * @warning comes from GUI thread |  * @warning    comes from GUI thread | ||||||
|  */ |  */ | ||||||
| typedef void (*DialogResultCallback)(DialogResult result, void* context); | typedef void (*DialogResultCallback)(DialogResult result, void* context); | ||||||
| 
 | 
 | ||||||
| /* Allocate and initialize dialog
 | /** Allocate and initialize dialog
 | ||||||
|  |  * | ||||||
|  * This dialog used to ask simple questions like Yes/ |  * This dialog used to ask simple questions like Yes/ | ||||||
|  |  * | ||||||
|  |  * @return     Dialog instance | ||||||
|  */ |  */ | ||||||
| Dialog* dialog_alloc(); | Dialog* dialog_alloc(); | ||||||
| 
 | 
 | ||||||
| /* Deinitialize and free dialog
 | /** Deinitialize and free dialog
 | ||||||
|  * @param dialog - Dialog instance |  * | ||||||
|  |  * @param      dialog  Dialog instance | ||||||
|  */ |  */ | ||||||
| void dialog_free(Dialog* dialog); | void dialog_free(Dialog* dialog); | ||||||
| 
 | 
 | ||||||
| /* Get dialog view
 | /** Get dialog view
 | ||||||
|  * @param dialog - Dialog instance |  * | ||||||
|  * @return View instance that can be used for embedding |  * @param      dialog  Dialog instance | ||||||
|  |  * | ||||||
|  |  * @return     View instance that can be used for embedding | ||||||
|  */ |  */ | ||||||
| View* dialog_get_view(Dialog* dialog); | View* dialog_get_view(Dialog* dialog); | ||||||
| 
 | 
 | ||||||
| /* Set dialog result callback
 | /** Set dialog result callback
 | ||||||
|  * @param dialog - Dialog instance |  * | ||||||
|  * @param callback - result callback function |  * @param      dialog    Dialog instance | ||||||
|  |  * @param      callback  result callback function | ||||||
|  */ |  */ | ||||||
| void dialog_set_result_callback(Dialog* dialog, DialogResultCallback callback); | void dialog_set_result_callback(Dialog* dialog, DialogResultCallback callback); | ||||||
| 
 | 
 | ||||||
| /* Set dialog context
 | /** Set dialog context
 | ||||||
|  * @param dialog - Dialog instance |  * | ||||||
|  * @param context - context pointer, will be passed to result callback |  * @param      dialog   Dialog instance | ||||||
|  |  * @param      context  context pointer, will be passed to result callback | ||||||
|  */ |  */ | ||||||
| void dialog_set_context(Dialog* dialog, void* context); | void dialog_set_context(Dialog* dialog, void* context); | ||||||
| 
 | 
 | ||||||
| /* Set dialog header text
 | /** Set dialog header text
 | ||||||
|  * @param dialog - Dialog instance |  * | ||||||
|  * @param text - text to be shown |  * @param      dialog  Dialog instance | ||||||
|  |  * @param      text    text to be shown | ||||||
|  */ |  */ | ||||||
| void dialog_set_header_text(Dialog* dialog, const char* text); | void dialog_set_header_text(Dialog* dialog, const char* text); | ||||||
| 
 | 
 | ||||||
| /* Set dialog text
 | /** Set dialog text
 | ||||||
|  * @param dialog - Dialog instance |  * | ||||||
|  * @param text - text to be shown |  * @param      dialog  Dialog instance | ||||||
|  |  * @param      text    text to be shown | ||||||
|  */ |  */ | ||||||
| void dialog_set_text(Dialog* dialog, const char* text); | void dialog_set_text(Dialog* dialog, const char* text); | ||||||
| 
 | 
 | ||||||
| /* Set left button text
 | /** Set left button text
 | ||||||
|  * @param dialog - Dialog instance |  * | ||||||
|  * @param text - text to be shown |  * @param      dialog  Dialog instance | ||||||
|  |  * @param      text    text to be shown | ||||||
|  */ |  */ | ||||||
| void dialog_set_left_button_text(Dialog* dialog, const char* text); | void dialog_set_left_button_text(Dialog* dialog, const char* text); | ||||||
| 
 | 
 | ||||||
| /* Set right button text
 | /** Set right button text
 | ||||||
|  * @param dialog - Dialog instance |  * | ||||||
|  * @param text - text to be shown |  * @param      dialog  Dialog instance | ||||||
|  |  * @param      text    text to be shown | ||||||
|  */ |  */ | ||||||
| void dialog_set_right_button_text(Dialog* dialog, const char* text); | void dialog_set_right_button_text(Dialog* dialog, const char* text); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,10 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file dialog_ex.h | ||||||
|  |  * GUI: DialogEx view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| @ -21,40 +27,51 @@ typedef enum { | |||||||
| typedef void (*DialogExResultCallback)(DialogExResult result, void* context); | typedef void (*DialogExResultCallback)(DialogExResult result, void* context); | ||||||
| 
 | 
 | ||||||
| /** Allocate and initialize dialog
 | /** Allocate and initialize dialog
 | ||||||
|  |  * | ||||||
|  * This dialog used to ask simple questions |  * This dialog used to ask simple questions | ||||||
|  * @return DialogEx instance |  * | ||||||
|  |  * @return     DialogEx instance | ||||||
|  */ |  */ | ||||||
| DialogEx* dialog_ex_alloc(); | DialogEx* dialog_ex_alloc(); | ||||||
| 
 | 
 | ||||||
| /** Deinitialize and free dialog
 | /** Deinitialize and free dialog
 | ||||||
|  * @param dialog - DialogEx instance |  * | ||||||
|  |  * @param      dialog_ex  DialogEx instance | ||||||
|  */ |  */ | ||||||
| void dialog_ex_free(DialogEx* dialog_ex); | void dialog_ex_free(DialogEx* dialog_ex); | ||||||
| 
 | 
 | ||||||
| /** Get dialog view
 | /** Get dialog view
 | ||||||
|  * @param dialog - DialogEx instance |  * | ||||||
|  * @return View instance that can be used for embedding |  * @param      dialog_ex  DialogEx instance | ||||||
|  |  * | ||||||
|  |  * @return     View instance that can be used for embedding | ||||||
|  */ |  */ | ||||||
| View* dialog_ex_get_view(DialogEx* dialog_ex); | View* dialog_ex_get_view(DialogEx* dialog_ex); | ||||||
| 
 | 
 | ||||||
| /** Set dialog result callback
 | /** Set dialog result callback
 | ||||||
|  * @param dialog_ex - DialogEx instance |  * | ||||||
|  * @param callback - result callback function |  * @param      dialog_ex  DialogEx instance | ||||||
|  |  * @param      callback   result callback function | ||||||
|  */ |  */ | ||||||
| void dialog_ex_set_result_callback(DialogEx* dialog_ex, DialogExResultCallback callback); | void dialog_ex_set_result_callback(DialogEx* dialog_ex, DialogExResultCallback callback); | ||||||
| 
 | 
 | ||||||
| /** Set dialog context
 | /** Set dialog context
 | ||||||
|  * @param dialog_ex - DialogEx instance |  * | ||||||
|  * @param context - context pointer, will be passed to result callback |  * @param      dialog_ex  DialogEx instance | ||||||
|  |  * @param      context    context pointer, will be passed to result callback | ||||||
|  */ |  */ | ||||||
| void dialog_ex_set_context(DialogEx* dialog_ex, void* context); | void dialog_ex_set_context(DialogEx* dialog_ex, void* context); | ||||||
| 
 | 
 | ||||||
| /** Set dialog header text
 | /** Set dialog header text
 | ||||||
|  |  * | ||||||
|  * If text is null, dialog header will not be rendered |  * If text is null, dialog header will not be rendered | ||||||
|  * @param dialog - DialogEx instance |  * | ||||||
|  * @param text - text to be shown, can be multiline |  * @param      dialog_ex   DialogEx instance | ||||||
|  * @param x, y - text position |  * @param      text        text to be shown, can be multiline | ||||||
|  * @param horizontal, vertical - text aligment |  * @param      x           x position | ||||||
|  |  * @param      y           y position | ||||||
|  |  * @param      horizontal  horizontal text aligment | ||||||
|  |  * @param      vertical    vertical text aligment | ||||||
|  */ |  */ | ||||||
| void dialog_ex_set_header( | void dialog_ex_set_header( | ||||||
|     DialogEx* dialog_ex, |     DialogEx* dialog_ex, | ||||||
| @ -65,11 +82,15 @@ void dialog_ex_set_header( | |||||||
|     Align vertical); |     Align vertical); | ||||||
| 
 | 
 | ||||||
| /** Set dialog text
 | /** Set dialog text
 | ||||||
|  |  * | ||||||
|  * If text is null, dialog text will not be rendered |  * If text is null, dialog text will not be rendered | ||||||
|  * @param dialog - DialogEx instance |  * | ||||||
|  * @param text - text to be shown, can be multiline |  * @param      dialog_ex   DialogEx instance | ||||||
|  * @param x, y - text position |  * @param      text        text to be shown, can be multiline | ||||||
|  * @param horizontal, vertical - text aligment |  * @param      x           x position | ||||||
|  |  * @param      y           y position | ||||||
|  |  * @param      horizontal  horizontal text aligment | ||||||
|  |  * @param      vertical    vertical text aligment | ||||||
|  */ |  */ | ||||||
| void dialog_ex_set_text( | void dialog_ex_set_text( | ||||||
|     DialogEx* dialog_ex, |     DialogEx* dialog_ex, | ||||||
| @ -80,36 +101,47 @@ void dialog_ex_set_text( | |||||||
|     Align vertical); |     Align vertical); | ||||||
| 
 | 
 | ||||||
| /** Set dialog icon
 | /** Set dialog icon
 | ||||||
|  |  * | ||||||
|  * If x or y is negative, dialog icon will not be rendered |  * If x or y is negative, dialog icon will not be rendered | ||||||
|  * @param dialog - DialogEx instance |  * | ||||||
|  * @param x, y - icon position |  * @param      dialog_ex  DialogEx instance | ||||||
|  * @param name - icon to be shown |  * @param      x          x position | ||||||
|  |  * @param      y          y position | ||||||
|  |  * @param      icon       The icon | ||||||
|  |  * @param      name  icon to be shown | ||||||
|  */ |  */ | ||||||
| void dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* icon); | void dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* icon); | ||||||
| 
 | 
 | ||||||
| /** Set left button text
 | /** Set left button text
 | ||||||
|  |  * | ||||||
|  * If text is null, left button will not be rendered and processed |  * If text is null, left button will not be rendered and processed | ||||||
|  * @param dialog - DialogEx instance |  * | ||||||
|  * @param text - text to be shown |  * @param      dialog_ex  DialogEx instance | ||||||
|  |  * @param      text       text to be shown | ||||||
|  */ |  */ | ||||||
| void dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text); | void dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text); | ||||||
| 
 | 
 | ||||||
| /** Set center button text
 | /** Set center button text
 | ||||||
|  |  * | ||||||
|  * If text is null, center button will not be rendered and processed |  * If text is null, center button will not be rendered and processed | ||||||
|  * @param dialog - DialogEx instance |  * | ||||||
|  * @param text - text to be shown |  * @param      dialog_ex  DialogEx instance | ||||||
|  |  * @param      text       text to be shown | ||||||
|  */ |  */ | ||||||
| void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text); | void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text); | ||||||
| 
 | 
 | ||||||
| /** Set right button text
 | /** Set right button text
 | ||||||
|  |  * | ||||||
|  * If text is null, right button will not be rendered and processed |  * If text is null, right button will not be rendered and processed | ||||||
|  * @param dialog - DialogEx instance |  * | ||||||
|  * @param text - text to be shown |  * @param      dialog_ex  DialogEx instance | ||||||
|  |  * @param      text       text to be shown | ||||||
|  */ |  */ | ||||||
| void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text); | void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text); | ||||||
| 
 | 
 | ||||||
| /** Clean dialog
 | /** Clean dialog
 | ||||||
|  * @param dialog_ex DialogEx instance |  * | ||||||
|  |  * @param      dialog_ex  DialogEx instance | ||||||
|  */ |  */ | ||||||
| void dialog_ex_clean(DialogEx* dialog_ex); | void dialog_ex_clean(DialogEx* dialog_ex); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,26 +1,38 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file empty_screen.h | ||||||
|  |  * GUI: EmptyScreen view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* Empty screen anonymous structure */ | /** Empty screen anonymous structure */ | ||||||
| typedef struct EmptyScreen EmptyScreen; | typedef struct EmptyScreen EmptyScreen; | ||||||
| 
 | 
 | ||||||
| /* Allocate and initialize empty screen
 | /** Allocate and initialize empty screen
 | ||||||
|  |  * | ||||||
|  * This empty screen used to ask simple questions like Yes/ |  * This empty screen used to ask simple questions like Yes/ | ||||||
|  |  * | ||||||
|  |  * @return     EmptyScreen instance | ||||||
|  */ |  */ | ||||||
| EmptyScreen* empty_screen_alloc(); | EmptyScreen* empty_screen_alloc(); | ||||||
| 
 | 
 | ||||||
| /* Deinitialize and free empty screen
 | /** Deinitialize and free empty screen
 | ||||||
|  * @param empty_screen - Empty screen instance |  * | ||||||
|  |  * @param      empty_screen  Empty screen instance | ||||||
|  */ |  */ | ||||||
| void empty_screen_free(EmptyScreen* empty_screen); | void empty_screen_free(EmptyScreen* empty_screen); | ||||||
| 
 | 
 | ||||||
| /* Get empty screen view
 | /** Get empty screen view
 | ||||||
|  * @param empty_screen - Empty screen instance |  * | ||||||
|  * @return View instance that can be used for embedding |  * @param      empty_screen  Empty screen instance | ||||||
|  |  * | ||||||
|  |  * @return     View instance that can be used for embedding | ||||||
|  */ |  */ | ||||||
| View* empty_screen_get_view(EmptyScreen* empty_screen); | View* empty_screen_get_view(EmptyScreen* empty_screen); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,10 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file file_select.h | ||||||
|  |  * GUI: FileSelect view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | |||||||
							
								
								
									
										54
									
								
								applications/gui/modules/menu.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										54
									
								
								applications/gui/modules/menu.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -20,7 +20,7 @@ ARRAY_DEF(MenuItemArray, MenuItem, M_POD_OPLIST); | |||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     MenuItemArray_t items; |     MenuItemArray_t items; | ||||||
|     uint8_t position; |     size_t position; | ||||||
| } MenuModel; | } MenuModel; | ||||||
| 
 | 
 | ||||||
| static void menu_process_up(Menu* menu); | static void menu_process_up(Menu* menu); | ||||||
| @ -32,7 +32,7 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { | |||||||
| 
 | 
 | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
| 
 | 
 | ||||||
|     uint8_t position = model->position; |     size_t position = model->position; | ||||||
|     size_t items_count = MenuItemArray_size(model->items); |     size_t items_count = MenuItemArray_size(model->items); | ||||||
|     if(items_count) { |     if(items_count) { | ||||||
|         MenuItem* item; |         MenuItem* item; | ||||||
| @ -43,7 +43,6 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { | |||||||
|         item = MenuItemArray_get(model->items, shift_position); |         item = MenuItemArray_get(model->items, shift_position); | ||||||
|         if(item->icon) { |         if(item->icon) { | ||||||
|             canvas_draw_icon_animation(canvas, 4, 3, item->icon); |             canvas_draw_icon_animation(canvas, 4, 3, item->icon); | ||||||
|             icon_animation_stop(item->icon); |  | ||||||
|         } |         } | ||||||
|         canvas_draw_str(canvas, 22, 14, item->label); |         canvas_draw_str(canvas, 22, 14, item->label); | ||||||
|         // Second line main
 |         // Second line main
 | ||||||
| @ -52,7 +51,6 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { | |||||||
|         item = MenuItemArray_get(model->items, shift_position); |         item = MenuItemArray_get(model->items, shift_position); | ||||||
|         if(item->icon) { |         if(item->icon) { | ||||||
|             canvas_draw_icon_animation(canvas, 4, 25, item->icon); |             canvas_draw_icon_animation(canvas, 4, 25, item->icon); | ||||||
|             icon_animation_start(item->icon); |  | ||||||
|         } |         } | ||||||
|         canvas_draw_str(canvas, 22, 36, item->label); |         canvas_draw_str(canvas, 22, 36, item->label); | ||||||
|         // Third line
 |         // Third line
 | ||||||
| @ -61,7 +59,6 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { | |||||||
|         item = MenuItemArray_get(model->items, shift_position); |         item = MenuItemArray_get(model->items, shift_position); | ||||||
|         if(item->icon) { |         if(item->icon) { | ||||||
|             canvas_draw_icon_animation(canvas, 4, 47, item->icon); |             canvas_draw_icon_animation(canvas, 4, 47, item->icon); | ||||||
|             icon_animation_stop(item->icon); |  | ||||||
|         } |         } | ||||||
|         canvas_draw_str(canvas, 22, 58, item->label); |         canvas_draw_str(canvas, 22, 58, item->label); | ||||||
|         // Frame and scrollbar
 |         // Frame and scrollbar
 | ||||||
| @ -93,6 +90,30 @@ static bool menu_input_callback(InputEvent* event, void* context) { | |||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void menu_enter(void* context) { | ||||||
|  |     Menu* menu = context; | ||||||
|  |     with_view_model( | ||||||
|  |         menu->view, (MenuModel * model) { | ||||||
|  |             MenuItem* item = MenuItemArray_get(model->items, model->position); | ||||||
|  |             if(item && item->icon) { | ||||||
|  |                 icon_animation_start(item->icon); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void menu_exit(void* context) { | ||||||
|  |     Menu* menu = context; | ||||||
|  |     with_view_model( | ||||||
|  |         menu->view, (MenuModel * model) { | ||||||
|  |             MenuItem* item = MenuItemArray_get(model->items, model->position); | ||||||
|  |             if(item && item->icon) { | ||||||
|  |                 icon_animation_stop(item->icon); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Menu* menu_alloc() { | Menu* menu_alloc() { | ||||||
|     Menu* menu = furi_alloc(sizeof(Menu)); |     Menu* menu = furi_alloc(sizeof(Menu)); | ||||||
|     menu->view = view_alloc(menu->view); |     menu->view = view_alloc(menu->view); | ||||||
| @ -100,6 +121,8 @@ Menu* menu_alloc() { | |||||||
|     view_allocate_model(menu->view, ViewModelTypeLocking, sizeof(MenuModel)); |     view_allocate_model(menu->view, ViewModelTypeLocking, sizeof(MenuModel)); | ||||||
|     view_set_draw_callback(menu->view, menu_draw_callback); |     view_set_draw_callback(menu->view, menu_draw_callback); | ||||||
|     view_set_input_callback(menu->view, menu_input_callback); |     view_set_input_callback(menu->view, menu_input_callback); | ||||||
|  |     view_set_enter_callback(menu->view, menu_enter); | ||||||
|  |     view_set_exit_callback(menu->view, menu_exit); | ||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         menu->view, (MenuModel * model) { |         menu->view, (MenuModel * model) { | ||||||
| @ -143,6 +166,7 @@ void menu_add_item( | |||||||
|             item = MenuItemArray_push_new(model->items); |             item = MenuItemArray_push_new(model->items); | ||||||
|             item->label = label; |             item->label = label; | ||||||
|             item->icon = icon; |             item->icon = icon; | ||||||
|  |             view_tie_icon_animation(menu->view, item->icon); | ||||||
|             item->index = index; |             item->index = index; | ||||||
|             item->callback = callback; |             item->callback = callback; | ||||||
|             item->callback_context = context; |             item->callback_context = context; | ||||||
| @ -175,11 +199,21 @@ void menu_set_selected_item(Menu* menu, uint32_t index) { | |||||||
| static void menu_process_up(Menu* menu) { | static void menu_process_up(Menu* menu) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         menu->view, (MenuModel * model) { |         menu->view, (MenuModel * model) { | ||||||
|  |             MenuItem* item = MenuItemArray_get(model->items, model->position); | ||||||
|  |             if(item && item->icon) { | ||||||
|  |                 icon_animation_stop(item->icon); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if(model->position > 0) { |             if(model->position > 0) { | ||||||
|                 model->position--; |                 model->position--; | ||||||
|             } else { |             } else { | ||||||
|                 model->position = MenuItemArray_size(model->items) - 1; |                 model->position = MenuItemArray_size(model->items) - 1; | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  |             item = MenuItemArray_get(model->items, model->position); | ||||||
|  |             if(item && item->icon) { | ||||||
|  |                 icon_animation_start(item->icon); | ||||||
|  |             } | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| @ -187,11 +221,21 @@ static void menu_process_up(Menu* menu) { | |||||||
| static void menu_process_down(Menu* menu) { | static void menu_process_down(Menu* menu) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         menu->view, (MenuModel * model) { |         menu->view, (MenuModel * model) { | ||||||
|  |             MenuItem* item = MenuItemArray_get(model->items, model->position); | ||||||
|  |             if(item && item->icon) { | ||||||
|  |                 icon_animation_stop(item->icon); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if(model->position < MenuItemArray_size(model->items) - 1) { |             if(model->position < MenuItemArray_size(model->items) - 1) { | ||||||
|                 model->position++; |                 model->position++; | ||||||
|             } else { |             } else { | ||||||
|                 model->position = 0; |                 model->position = 0; | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  |             item = MenuItemArray_get(model->items, model->position); | ||||||
|  |             if(item && item->icon) { | ||||||
|  |                 icon_animation_start(item->icon); | ||||||
|  |             } | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,10 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file menu.h | ||||||
|  |  * GUI: Menu view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| @ -7,31 +13,38 @@ extern "C" { | |||||||
| 
 | 
 | ||||||
| /** Menu anonymous structure */ | /** Menu anonymous structure */ | ||||||
| typedef struct Menu Menu; | typedef struct Menu Menu; | ||||||
|  | 
 | ||||||
|  | /** Menu Item Callback */ | ||||||
| typedef void (*MenuItemCallback)(void* context, uint32_t index); | typedef void (*MenuItemCallback)(void* context, uint32_t index); | ||||||
| 
 | 
 | ||||||
| /** Menu allocation and initialization
 | /** Menu allocation and initialization
 | ||||||
|  * @return Menu instance |  * | ||||||
|  |  * @return     Menu instance | ||||||
|  */ |  */ | ||||||
| Menu* menu_alloc(); | Menu* menu_alloc(); | ||||||
| 
 | 
 | ||||||
| /** Free menu
 | /** Free menu
 | ||||||
|  * @param menu - Menu instance |  * | ||||||
|  |  * @param      menu  Menu instance | ||||||
|  */ |  */ | ||||||
| void menu_free(Menu* menu); | void menu_free(Menu* menu); | ||||||
| 
 | 
 | ||||||
| /** Get Menu view
 | /** Get Menu view
 | ||||||
|  * @param menu - Menu instance |  * | ||||||
|  * @return View instance |  * @param      menu  Menu instance | ||||||
|  |  * | ||||||
|  |  * @return     View instance | ||||||
|  */ |  */ | ||||||
| View* menu_get_view(Menu* menu); | View* menu_get_view(Menu* menu); | ||||||
| 
 | 
 | ||||||
| /** Add item to menu
 | /** Add item to menu
 | ||||||
|  * @param menu - Menu instance |  * | ||||||
|  * @param label - menu item string label |  * @param      menu      Menu instance | ||||||
|  * @param icon - IconAnimation instance |  * @param      label     menu item string label | ||||||
|  * @param index - menu item index |  * @param      icon      IconAnimation instance | ||||||
|  * @param callback - MenuItemCallback instance |  * @param      index     menu item index | ||||||
|  * @param context - pointer to context |  * @param      callback  MenuItemCallback instance | ||||||
|  |  * @param      context   pointer to context | ||||||
|  */ |  */ | ||||||
| void menu_add_item( | void menu_add_item( | ||||||
|     Menu* menu, |     Menu* menu, | ||||||
| @ -42,14 +55,16 @@ void menu_add_item( | |||||||
|     void* context); |     void* context); | ||||||
| 
 | 
 | ||||||
| /** Clean menu
 | /** Clean menu
 | ||||||
|  * Note: this function does not free menu instance |  * @note       this function does not free menu instance | ||||||
|  * @param menu - Menu instance |  * | ||||||
|  |  * @param      menu  Menu instance | ||||||
|  */ |  */ | ||||||
| void menu_clean(Menu* menu); | void menu_clean(Menu* menu); | ||||||
| 
 | 
 | ||||||
| /** Set current menu item
 | /** Set current menu item
 | ||||||
|  * @param submenu |  * | ||||||
|  * @param index |  * @param      menu   Menu instance | ||||||
|  |  * @param      index  The index | ||||||
|  */ |  */ | ||||||
| void menu_set_selected_item(Menu* menu, uint32_t index); | void menu_set_selected_item(Menu* menu, uint32_t index); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,52 +1,70 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file popup.h | ||||||
|  |  * GUI: Popup view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* Popup anonymous structure */ | /** Popup anonymous structure */ | ||||||
| typedef struct Popup Popup; | typedef struct Popup Popup; | ||||||
| 
 | 
 | ||||||
| /* Popup result callback type
 | /** Popup result callback type
 | ||||||
|  * @warning comes from GUI thread |  * @warning    comes from GUI thread | ||||||
|  */ |  */ | ||||||
| typedef void (*PopupCallback)(void* context); | typedef void (*PopupCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| /* Allocate and initialize popup
 | /** Allocate and initialize popup
 | ||||||
|  |  * | ||||||
|  * This popup used to ask simple questions like Yes/ |  * This popup used to ask simple questions like Yes/ | ||||||
|  |  * | ||||||
|  |  * @return     Popup instance | ||||||
|  */ |  */ | ||||||
| Popup* popup_alloc(); | Popup* popup_alloc(); | ||||||
| 
 | 
 | ||||||
| /* Deinitialize and free popup
 | /** Deinitialize and free popup
 | ||||||
|  * @param popup - Popup instance |  * | ||||||
|  |  * @param      popup  Popup instance | ||||||
|  */ |  */ | ||||||
| void popup_free(Popup* popup); | void popup_free(Popup* popup); | ||||||
| 
 | 
 | ||||||
| /* Get popup view
 | /** Get popup view
 | ||||||
|  * @param popup - Popup instance |  * | ||||||
|  * @return View instance that can be used for embedding |  * @param      popup  Popup instance | ||||||
|  |  * | ||||||
|  |  * @return     View instance that can be used for embedding | ||||||
|  */ |  */ | ||||||
| View* popup_get_view(Popup* popup); | View* popup_get_view(Popup* popup); | ||||||
| 
 | 
 | ||||||
| /* Set popup header text
 | /** Set popup header text
 | ||||||
|  * @param popup - Popup instance |  * | ||||||
|  * @param text - text to be shown |  * @param      popup     Popup instance | ||||||
|  |  * @param      callback  PopupCallback | ||||||
|  */ |  */ | ||||||
| void popup_set_callback(Popup* popup, PopupCallback callback); | void popup_set_callback(Popup* popup, PopupCallback callback); | ||||||
| 
 | 
 | ||||||
| /* Set popup context
 | /** Set popup context
 | ||||||
|  * @param popup - Popup instance |  * | ||||||
|  * @param context - context pointer, will be passed to result callback |  * @param      popup    Popup instance | ||||||
|  |  * @param      context  context pointer, will be passed to result callback | ||||||
|  */ |  */ | ||||||
| void popup_set_context(Popup* popup, void* context); | void popup_set_context(Popup* popup, void* context); | ||||||
| 
 | 
 | ||||||
| /* Set popup header text
 | /** Set popup header text
 | ||||||
|  |  * | ||||||
|  * If text is null, popup header will not be rendered |  * If text is null, popup header will not be rendered | ||||||
|  * @param popup - Popup instance |  * | ||||||
|  * @param text - text to be shown, can be multiline |  * @param      popup       Popup instance | ||||||
|  * @param x, y - text position |  * @param      text        text to be shown, can be multiline | ||||||
|  * @param horizontal, vertical - text aligment |  * @param      x           x position | ||||||
|  |  * @param      y           y position | ||||||
|  |  * @param      horizontal  horizontal alignment | ||||||
|  |  * @param      vertical    vertical aligment | ||||||
|  */ |  */ | ||||||
| void popup_set_header( | void popup_set_header( | ||||||
|     Popup* popup, |     Popup* popup, | ||||||
| @ -56,12 +74,16 @@ void popup_set_header( | |||||||
|     Align horizontal, |     Align horizontal, | ||||||
|     Align vertical); |     Align vertical); | ||||||
| 
 | 
 | ||||||
| /* Set popup text
 | /** Set popup text
 | ||||||
|  |  * | ||||||
|  * If text is null, popup text will not be rendered |  * If text is null, popup text will not be rendered | ||||||
|  * @param popup - Popup instance |  * | ||||||
|  * @param text - text to be shown, can be multiline |  * @param      popup       Popup instance | ||||||
|  * @param x, y - text position |  * @param      text        text to be shown, can be multiline | ||||||
|  * @param horizontal, vertical - text aligment |  * @param      x           x position | ||||||
|  |  * @param      y           y position | ||||||
|  |  * @param      horizontal  horizontal alignment | ||||||
|  |  * @param      vertical    vertical aligment | ||||||
|  */ |  */ | ||||||
| void popup_set_text( | void popup_set_text( | ||||||
|     Popup* popup, |     Popup* popup, | ||||||
| @ -71,27 +93,33 @@ void popup_set_text( | |||||||
|     Align horizontal, |     Align horizontal, | ||||||
|     Align vertical); |     Align vertical); | ||||||
| 
 | 
 | ||||||
| /* Set popup icon
 | /** Set popup icon
 | ||||||
|  |  * | ||||||
|  * If icon position is negative, popup icon will not be rendered |  * If icon position is negative, popup icon will not be rendered | ||||||
|  * @param popup - Popup instance |  * | ||||||
|  * @param x, y - icon position |  * @param      popup  Popup instance | ||||||
|  * @param name - icon to be shown |  * @param      x      x position | ||||||
|  |  * @param      y      y position | ||||||
|  |  * @param      icon   pointer to Icon data | ||||||
|  */ |  */ | ||||||
| void popup_set_icon(Popup* popup, uint8_t x, uint8_t y, const Icon* icon); | void popup_set_icon(Popup* popup, uint8_t x, uint8_t y, const Icon* icon); | ||||||
| 
 | 
 | ||||||
| /* Set popup timeout
 | /** Set popup timeout
 | ||||||
|  * @param popup - Popup instance |  * | ||||||
|  * @param timeout_in_ms - popup timeout value in milliseconds |  * @param      popup          Popup instance | ||||||
|  |  * @param      timeout_in_ms  popup timeout value in milliseconds | ||||||
|  */ |  */ | ||||||
| void popup_set_timeout(Popup* popup, uint32_t timeout_in_ms); | void popup_set_timeout(Popup* popup, uint32_t timeout_in_ms); | ||||||
| 
 | 
 | ||||||
| /* Enable popup timeout
 | /** Enable popup timeout
 | ||||||
|  * @param popup - Popup instance |  * | ||||||
|  |  * @param      popup  Popup instance | ||||||
|  */ |  */ | ||||||
| void popup_enable_timeout(Popup* popup); | void popup_enable_timeout(Popup* popup); | ||||||
| 
 | 
 | ||||||
| /* Disable popup timeout
 | /** Disable popup timeout
 | ||||||
|  * @param popup - Popup instance |  * | ||||||
|  |  * @param      popup  Popup instance | ||||||
|  */ |  */ | ||||||
| void popup_disable_timeout(Popup* popup); | void popup_disable_timeout(Popup* popup); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,40 +1,50 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file submenu.h | ||||||
|  |  * GUI: SubMenu view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* Submenu anonymous structure */ | /** Submenu anonymous structure */ | ||||||
| typedef struct Submenu Submenu; | typedef struct Submenu Submenu; | ||||||
| typedef void (*SubmenuItemCallback)(void* context, uint32_t index); | typedef void (*SubmenuItemCallback)(void* context, uint32_t index); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Allocate and initialize submenu 
 | ||||||
|  * @brief Allocate and initialize submenu |  *  | ||||||
|  * This submenu is used to select one option |  * This submenu is used to select one option | ||||||
|  |  * | ||||||
|  |  * @return     Submenu instance | ||||||
|  */ |  */ | ||||||
| Submenu* submenu_alloc(); | Submenu* submenu_alloc(); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Deinitialize and free submenu
 | ||||||
|  * @brief Deinitialize and free submenu |  * | ||||||
|  * @param submenu - Submenu instance |  * @param      submenu  Submenu instance | ||||||
|  */ |  */ | ||||||
| void submenu_free(Submenu* submenu); | void submenu_free(Submenu* submenu); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Get submenu view
 | ||||||
|  * @brief Get submenu view |  * | ||||||
|  * @param submenu - Submenu instance |  * @param      submenu  Submenu instance | ||||||
|  * @return View instance that can be used for embedding |  * | ||||||
|  |  * @return     View instance that can be used for embedding | ||||||
|  */ |  */ | ||||||
| View* submenu_get_view(Submenu* submenu); | View* submenu_get_view(Submenu* submenu); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Add item to submenu
 | ||||||
|  * @brief Add item to submenu |  * | ||||||
|  * @param submenu - Submenu instance |  * @param      submenu           Submenu instance | ||||||
|  * @param label - menu item label |  * @param      label             menu item label | ||||||
|  * @param index - menu item index, used for callback, may be the same with other items |  * @param      index             menu item index, used for callback, may be | ||||||
|  * @param callback - menu item callback |  *                               the same with other items | ||||||
|  * @param callback_context - menu item callback context |  * @param      callback          menu item callback | ||||||
|  |  * @param      callback_context  menu item callback context | ||||||
|  */ |  */ | ||||||
| void submenu_add_item( | void submenu_add_item( | ||||||
|     Submenu* submenu, |     Submenu* submenu, | ||||||
| @ -43,23 +53,23 @@ void submenu_add_item( | |||||||
|     SubmenuItemCallback callback, |     SubmenuItemCallback callback, | ||||||
|     void* callback_context); |     void* callback_context); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Remove all items from submenu
 | ||||||
|  * @brief Remove all items from submenu |  * | ||||||
|  * @param submenu - Submenu instance |  * @param      submenu  Submenu instance | ||||||
|  */ |  */ | ||||||
| void submenu_clean(Submenu* submenu); | void submenu_clean(Submenu* submenu); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Set submenu item selector
 | ||||||
|  * @brief Set submenu item selector |  * | ||||||
|  * @param submenu |  * @param      submenu  Submenu instance | ||||||
|  * @param index |  * @param      index    The index | ||||||
|  */ |  */ | ||||||
| void submenu_set_selected_item(Submenu* submenu, uint32_t index); | void submenu_set_selected_item(Submenu* submenu, uint32_t index); | ||||||
| 
 | 
 | ||||||
| /**
 | /** Set optional header for submenu
 | ||||||
|  * @brief Set optional header for submenu |  * | ||||||
|  * @param submenu   - submenu entity |  * @param      submenu  Submenu instance | ||||||
|  * @param header    - header to set |  * @param      header   header to set | ||||||
|  */ |  */ | ||||||
| void submenu_set_header(Submenu* submenu, const char* header); | void submenu_set_header(Submenu* submenu, const char* header); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,11 +1,17 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file text_box.h | ||||||
|  |  * GUI: TextBox view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* TextBox anonymous structure */ | /** TextBox anonymous structure */ | ||||||
| typedef struct TextBox TextBox; | typedef struct TextBox TextBox; | ||||||
| typedef void (*TextBoxExitCallback)(void* context); | typedef void (*TextBoxExitCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| @ -15,46 +21,56 @@ typedef enum { | |||||||
| } TextBoxFont; | } TextBoxFont; | ||||||
| 
 | 
 | ||||||
| /** Allocate and initialize text_box
 | /** Allocate and initialize text_box
 | ||||||
|  |  * | ||||||
|  |  * @return     TextBox instance | ||||||
|  */ |  */ | ||||||
| TextBox* text_box_alloc(); | TextBox* text_box_alloc(); | ||||||
| 
 | 
 | ||||||
| /** Deinitialize and free text_box
 | /** Deinitialize and free text_box
 | ||||||
|  * @param text_box text_box instance |  * | ||||||
|  |  * @param      text_box  text_box instance | ||||||
|  */ |  */ | ||||||
| void text_box_free(TextBox* text_box); | void text_box_free(TextBox* text_box); | ||||||
| 
 | 
 | ||||||
| /** Get text_box view
 | /** Get text_box view
 | ||||||
|  * @param text_box TextBox instance |  * | ||||||
|  * @return View instance that can be used for embedding |  * @param      text_box  TextBox instance | ||||||
|  |  * | ||||||
|  |  * @return     View instance that can be used for embedding | ||||||
|  */ |  */ | ||||||
| View* text_box_get_view(TextBox* text_box); | View* text_box_get_view(TextBox* text_box); | ||||||
| 
 | 
 | ||||||
| /** Clean text_box
 | /** Clean text_box
 | ||||||
|  * @param text_box TextBox instance |  * | ||||||
|  |  * @param      text_box  TextBox instance | ||||||
|  */ |  */ | ||||||
| void text_box_clean(TextBox* text_box); | void text_box_clean(TextBox* text_box); | ||||||
| 
 | 
 | ||||||
| /** Set text for text_box
 | /** Set text for text_box
 | ||||||
|  * @param text_box TextBox instance |  * | ||||||
|  * @param text text to set |  * @param      text_box  TextBox instance | ||||||
|  |  * @param      text      text to set | ||||||
|  */ |  */ | ||||||
| void text_box_set_text(TextBox* text_box, const char* text); | void text_box_set_text(TextBox* text_box, const char* text); | ||||||
| 
 | 
 | ||||||
| /** Set TextBox font
 | /** Set TextBox font
 | ||||||
|  * @param text_box TextBox instance |  * | ||||||
|  * @param font TextBoxFont instance |  * @param      text_box  TextBox instance | ||||||
|  |  * @param      font      TextBoxFont instance | ||||||
|  */ |  */ | ||||||
| void text_box_set_font(TextBox* text_box, TextBoxFont font); | void text_box_set_font(TextBox* text_box, TextBoxFont font); | ||||||
| 
 | 
 | ||||||
| /** Set text_box context
 | /** Set text_box context
 | ||||||
|  * @param text_box TextBox instance |  * | ||||||
|  * @param context context pointer |  * @param      text_box  TextBox instance | ||||||
|  |  * @param      context   context pointer | ||||||
|  */ |  */ | ||||||
| void text_box_set_context(TextBox* text_box, void* context); | void text_box_set_context(TextBox* text_box, void* context); | ||||||
| 
 | 
 | ||||||
| /** Set exit callback
 | /** Set exit callback
 | ||||||
|  * @param text_box TextBox instance |  * | ||||||
|  * @param callback TextBoxExitCallback callback pointer |  * @param      text_box  TextBox instance | ||||||
|  |  * @param      callback  TextBoxExitCallback callback pointer | ||||||
|  */ |  */ | ||||||
| void text_box_set_exit_callback(TextBox* text_box, TextBoxExitCallback callback); | void text_box_set_exit_callback(TextBox* text_box, TextBoxExitCallback callback); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,44 +1,59 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file text_input.h | ||||||
|  |  * GUI: TextInput keybord view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* Text input anonymous structure */ | /** Text input anonymous structure */ | ||||||
| typedef struct TextInput TextInput; | typedef struct TextInput TextInput; | ||||||
| typedef void (*TextInputCallback)(void* context); | typedef void (*TextInputCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| /** Allocate and initialize text input 
 | /** Allocate and initialize text input 
 | ||||||
|  |  *  | ||||||
|  * This text input is used to enter string |  * This text input is used to enter string | ||||||
|  * @return TextInput instance |  * | ||||||
|  |  * @return     TextInput instance | ||||||
|  */ |  */ | ||||||
| TextInput* text_input_alloc(); | TextInput* text_input_alloc(); | ||||||
| 
 | 
 | ||||||
| /** Deinitialize and free text input
 | /** Deinitialize and free text input
 | ||||||
|  * @param text_input - TextInput instance |  * | ||||||
|  |  * @param      text_input  TextInput instance | ||||||
|  */ |  */ | ||||||
| void text_input_free(TextInput* text_input); | void text_input_free(TextInput* text_input); | ||||||
| 
 | 
 | ||||||
| /** Clean text input view
 | /** Clean text input view Note: this function does not free memory
 | ||||||
|  * Note: this function does not free memory |  * | ||||||
|  * @param text_input - Text input instance |  * @param      text_input  Text input instance | ||||||
|  */ |  */ | ||||||
| void text_input_clean(TextInput* text_input); | void text_input_clean(TextInput* text_input); | ||||||
| 
 | 
 | ||||||
| /** Get text input view
 | /** Get text input view
 | ||||||
|  * @param text_input - TextInput instance |  * | ||||||
|  * @return View instance that can be used for embedding |  * @param      text_input  TextInput instance | ||||||
|  |  * | ||||||
|  |  * @return     View instance that can be used for embedding | ||||||
|  */ |  */ | ||||||
| View* text_input_get_view(TextInput* text_input); | View* text_input_get_view(TextInput* text_input); | ||||||
| 
 | 
 | ||||||
| /** Set text input result callback
 | /** Set text input result callback
 | ||||||
|  * @param text_input - TextInput instance |  * | ||||||
|  * @param callback - callback fn |  * @param      text_input          TextInput instance | ||||||
|  * @param callback_context - callback context |  * @param      callback            callback fn | ||||||
|  * @param text_buffer - pointer to YOUR text buffer, that we going to modify |  * @param      callback_context    callback context | ||||||
|  * @param text_buffer_size - YOUR text buffer size in bytes. Max string length will be text_buffer_size - 1. |  * @param      text_buffer         pointer to YOUR text buffer, that we going | ||||||
|  * @param clear_default_text - clear text from text_buffer on first OK event |  *                                 to modify | ||||||
|  |  * @param      text_buffer_size    YOUR text buffer size in bytes. Max string | ||||||
|  |  *                                 length will be text_buffer_size-1. | ||||||
|  |  * @param      clear_default_text  clear text from text_buffer on first OK | ||||||
|  |  *                                 event | ||||||
|  */ |  */ | ||||||
| void text_input_set_result_callback( | void text_input_set_result_callback( | ||||||
|     TextInput* text_input, |     TextInput* text_input, | ||||||
| @ -49,8 +64,9 @@ void text_input_set_result_callback( | |||||||
|     bool clear_default_text); |     bool clear_default_text); | ||||||
| 
 | 
 | ||||||
| /** Set text input header text
 | /** Set text input header text
 | ||||||
|  * @param text_input - TextInput instance |  * | ||||||
|  * @param text - text to be shown |  * @param      text_input  TextInput instance | ||||||
|  |  * @param      text        text to be shown | ||||||
|  */ |  */ | ||||||
| void text_input_set_header_text(TextInput* text_input, const char* text); | void text_input_set_header_text(TextInput* text_input, const char* text); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										29
									
								
								applications/gui/modules/variable-item-list.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										29
									
								
								applications/gui/modules/variable-item-list.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -18,6 +18,8 @@ ARRAY_DEF(VariableItemArray, VariableItem, M_POD_OPLIST); | |||||||
| 
 | 
 | ||||||
| struct VariableItemList { | struct VariableItemList { | ||||||
|     View* view; |     View* view; | ||||||
|  |     VariableItemListEnterCallback callback; | ||||||
|  |     void* context; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -30,6 +32,7 @@ static void variable_item_list_process_up(VariableItemList* variable_item_list); | |||||||
| static void variable_item_list_process_down(VariableItemList* variable_item_list); | static void variable_item_list_process_down(VariableItemList* variable_item_list); | ||||||
| static void variable_item_list_process_left(VariableItemList* variable_item_list); | static void variable_item_list_process_left(VariableItemList* variable_item_list); | ||||||
| static void variable_item_list_process_right(VariableItemList* variable_item_list); | static void variable_item_list_process_right(VariableItemList* variable_item_list); | ||||||
|  | static void variable_item_list_process_ok(VariableItemList* variable_item_list); | ||||||
| 
 | 
 | ||||||
| static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { | static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { | ||||||
|     VariableItemListModel* model = _model; |     VariableItemListModel* model = _model; | ||||||
| @ -104,6 +107,9 @@ static bool variable_item_list_input_callback(InputEvent* event, void* context) | |||||||
|             consumed = true; |             consumed = true; | ||||||
|             variable_item_list_process_right(variable_item_list); |             variable_item_list_process_right(variable_item_list); | ||||||
|             break; |             break; | ||||||
|  |         case InputKeyOk: | ||||||
|  |             variable_item_list_process_ok(variable_item_list); | ||||||
|  |             break; | ||||||
|         default: |         default: | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @ -198,6 +204,16 @@ void variable_item_list_process_right(VariableItemList* variable_item_list) { | |||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void variable_item_list_process_ok(VariableItemList* variable_item_list) { | ||||||
|  |     with_view_model( | ||||||
|  |         variable_item_list->view, (VariableItemListModel * model) { | ||||||
|  |             if(variable_item_list->callback) { | ||||||
|  |                 variable_item_list->callback(variable_item_list->context, model->position); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| VariableItemList* variable_item_list_alloc() { | VariableItemList* variable_item_list_alloc() { | ||||||
|     VariableItemList* variable_item_list = furi_alloc(sizeof(VariableItemList)); |     VariableItemList* variable_item_list = furi_alloc(sizeof(VariableItemList)); | ||||||
|     variable_item_list->view = view_alloc(); |     variable_item_list->view = view_alloc(); | ||||||
| @ -280,6 +296,19 @@ VariableItem* variable_item_list_add( | |||||||
|     return item; |     return item; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void variable_item_list_set_enter_callback( | ||||||
|  |     VariableItemList* variable_item_list, | ||||||
|  |     VariableItemListEnterCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(callback); | ||||||
|  |     with_view_model( | ||||||
|  |         variable_item_list->view, (VariableItemListModel * model) { | ||||||
|  |             variable_item_list->callback = callback; | ||||||
|  |             variable_item_list->context = context; | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) { | void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) { | ||||||
|     item->current_value_index = current_value_index; |     item->current_value_index = current_value_index; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										70
									
								
								applications/gui/modules/variable-item-list.h
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										70
									
								
								applications/gui/modules/variable-item-list.h
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -1,4 +1,10 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file variable-item-list.h | ||||||
|  |  * GUI: VariableItemList view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| @ -8,28 +14,43 @@ extern "C" { | |||||||
| typedef struct VariableItemList VariableItemList; | typedef struct VariableItemList VariableItemList; | ||||||
| typedef struct VariableItem VariableItem; | typedef struct VariableItem VariableItem; | ||||||
| typedef void (*VariableItemChangeCallback)(VariableItem* item); | typedef void (*VariableItemChangeCallback)(VariableItem* item); | ||||||
|  | typedef void (*VariableItemListEnterCallback)(void* context, uint32_t index); | ||||||
| 
 | 
 | ||||||
| /** Allocate and initialize VariableItemList
 | /** Allocate and initialize VariableItemList
 | ||||||
|  * @return VariableItemList*  |  * | ||||||
|  |  * @return     VariableItemList* | ||||||
|  */ |  */ | ||||||
| VariableItemList* variable_item_list_alloc(); | VariableItemList* variable_item_list_alloc(); | ||||||
| 
 | 
 | ||||||
| /** Deinitialize and free VariableItemList
 | /** Deinitialize and free VariableItemList
 | ||||||
|  * @param variable_item_list VariableItemList instance |  * | ||||||
|  |  * @param      variable_item_list  VariableItemList instance | ||||||
|  */ |  */ | ||||||
| void variable_item_list_free(VariableItemList* variable_item_list); | void variable_item_list_free(VariableItemList* variable_item_list); | ||||||
| 
 | 
 | ||||||
|  | /** Clear all elements from list
 | ||||||
|  |  * | ||||||
|  |  * @param      variable_item_list  VariableItemList instance | ||||||
|  |  */ | ||||||
| void variable_item_list_clean(VariableItemList* variable_item_list); | void variable_item_list_clean(VariableItemList* variable_item_list); | ||||||
| 
 | 
 | ||||||
|  | /** Get VariableItemList View instance
 | ||||||
|  |  * | ||||||
|  |  * @param      variable_item_list  VariableItemList instance | ||||||
|  |  * | ||||||
|  |  * @return     View instance | ||||||
|  |  */ | ||||||
| View* variable_item_list_get_view(VariableItemList* variable_item_list); | View* variable_item_list_get_view(VariableItemList* variable_item_list); | ||||||
| 
 | 
 | ||||||
| /** Add item to VariableItemList
 | /** Add item to VariableItemList
 | ||||||
|  * @param variable_item_list VariableItemList instance |  * | ||||||
|  * @param label item name |  * @param      variable_item_list  VariableItemList instance | ||||||
|  * @param values_count item values count |  * @param      label               item name | ||||||
|  * @param change_callback called on value change in gui |  * @param      values_count        item values count | ||||||
|  * @param context item context |  * @param      change_callback     called on value change in gui | ||||||
|  * @return VariableItem* item instance |  * @param      context             item context | ||||||
|  |  * | ||||||
|  |  * @return     VariableItem* item instance | ||||||
|  */ |  */ | ||||||
| VariableItem* variable_item_list_add( | VariableItem* variable_item_list_add( | ||||||
|     VariableItemList* variable_item_list, |     VariableItemList* variable_item_list, | ||||||
| @ -38,27 +59,44 @@ VariableItem* variable_item_list_add( | |||||||
|     VariableItemChangeCallback change_callback, |     VariableItemChangeCallback change_callback, | ||||||
|     void* context); |     void* context); | ||||||
| 
 | 
 | ||||||
|  | /** Set enter callback
 | ||||||
|  |  * | ||||||
|  |  * @param      variable_item_list  VariableItemList instance | ||||||
|  |  * @param      callback            VariableItemListEnterCallback instance | ||||||
|  |  * @param      context             pointer to context | ||||||
|  |  */ | ||||||
|  | void variable_item_list_set_enter_callback( | ||||||
|  |     VariableItemList* variable_item_list, | ||||||
|  |     VariableItemListEnterCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
| /** Set item current selected index
 | /** Set item current selected index
 | ||||||
|  * @param item VariableItem* instance |  * | ||||||
|  * @param current_value_index  |  * @param      item                 VariableItem* instance | ||||||
|  |  * @param      current_value_index  The current value index | ||||||
|  */ |  */ | ||||||
| void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index); | void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index); | ||||||
| 
 | 
 | ||||||
| /** Set item current selected text
 | /** Set item current selected text
 | ||||||
|  * @param item VariableItem* instance |  * | ||||||
|  * @param current_value_text  |  * @param      item                VariableItem* instance | ||||||
|  |  * @param      current_value_text  The current value text | ||||||
|  */ |  */ | ||||||
| void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text); | void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text); | ||||||
| 
 | 
 | ||||||
| /** Get item current selected index
 | /** Get item current selected index
 | ||||||
|  * @param item VariableItem* instance |  * | ||||||
|  * @return uint8_t current selected index |  * @param      item  VariableItem* instance | ||||||
|  |  * | ||||||
|  |  * @return     uint8_t current selected index | ||||||
|  */ |  */ | ||||||
| uint8_t variable_item_get_current_value_index(VariableItem* item); | uint8_t variable_item_get_current_value_index(VariableItem* item); | ||||||
| 
 | 
 | ||||||
| /** Get item context
 | /** Get item context
 | ||||||
|  * @param item VariableItem* instance |  * | ||||||
|  * @return void* item context |  * @param      item  VariableItem* instance | ||||||
|  |  * | ||||||
|  |  * @return     void* item context | ||||||
|  */ |  */ | ||||||
| void* variable_item_get_context(VariableItem* item); | void* variable_item_get_context(VariableItem* item); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,38 +1,51 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file widget.h | ||||||
|  |  * GUI: Widget view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include "widget_elements/widget_element_i.h" | #include "widget_elements/widget_element_i.h" | ||||||
| 
 | 
 | ||||||
| typedef struct Widget Widget; | typedef struct Widget Widget; | ||||||
| typedef struct WidgetElement WidgetElement; | typedef struct WidgetElement WidgetElement; | ||||||
| 
 | 
 | ||||||
| /** Allocate Widget that holds Widget Elements
 | /** Allocate Widget that holds Widget Elements
 | ||||||
|  * @return Widget instance |  * | ||||||
|  |  * @return     Widget instance | ||||||
|  */ |  */ | ||||||
| Widget* widget_alloc(); | Widget* widget_alloc(); | ||||||
| 
 | 
 | ||||||
| /** Free Widget
 | /** Free Widget
 | ||||||
|  * @note this function free allocated Widget Elements |  * @note       this function free allocated Widget Elements | ||||||
|  * @param widget Widget instance |  * | ||||||
|  |  * @param      widget  Widget instance | ||||||
|  */ |  */ | ||||||
| void widget_free(Widget* widget); | void widget_free(Widget* widget); | ||||||
| 
 | 
 | ||||||
| /** Clear Widget
 | /** Clear Widget
 | ||||||
|  * @param widget Widget instance |  * | ||||||
|  |  * @param      widget  Widget instance | ||||||
|  */ |  */ | ||||||
| void widget_clear(Widget* widget); | void widget_clear(Widget* widget); | ||||||
| 
 | 
 | ||||||
| /** Get Widget view
 | /** Get Widget view
 | ||||||
|  * @param widget Widget instance |  * | ||||||
|  * @return View instance |  * @param      widget  Widget instance | ||||||
|  |  * | ||||||
|  |  * @return     View instance | ||||||
|  */ |  */ | ||||||
| View* widget_get_view(Widget* widget); | View* widget_get_view(Widget* widget); | ||||||
| 
 | 
 | ||||||
| /** Add Multi String Element
 | /** Add Multi String Element
 | ||||||
|  * @param widget Widget instance |  * | ||||||
|  * @param x - x coordinate |  * @param      widget      Widget instance | ||||||
|  * @param y - y coordinate |  * @param      x           x coordinate | ||||||
|  * @param horizontal - Align instance |  * @param      y           y coordinate | ||||||
|  * @param vertical - Align instance |  * @param      horizontal  Align instance | ||||||
|  * @param font Font instance |  * @param      vertical    Align instance | ||||||
|  |  * @param      font        Font instance | ||||||
|  |  * @param[in]  text        The text | ||||||
|  */ |  */ | ||||||
| void widget_add_string_multiline_element( | void widget_add_string_multiline_element( | ||||||
|     Widget* widget, |     Widget* widget, | ||||||
| @ -44,12 +57,14 @@ void widget_add_string_multiline_element( | |||||||
|     const char* text); |     const char* text); | ||||||
| 
 | 
 | ||||||
| /** Add String Element
 | /** Add String Element
 | ||||||
|  * @param widget Widget instance |  * | ||||||
|  * @param x - x coordinate |  * @param      widget      Widget instance | ||||||
|  * @param y - y coordinate |  * @param      x           x coordinate | ||||||
|  * @param horizontal - Align instance |  * @param      y           y coordinate | ||||||
|  * @param vertical - Align instance |  * @param      horizontal  Align instance | ||||||
|  * @param font Font instance |  * @param      vertical    Align instance | ||||||
|  |  * @param      font        Font instance | ||||||
|  |  * @param[in]  text        The text | ||||||
|  */ |  */ | ||||||
| void widget_add_string_element( | void widget_add_string_element( | ||||||
|     Widget* widget, |     Widget* widget, | ||||||
| @ -61,11 +76,12 @@ void widget_add_string_element( | |||||||
|     const char* text); |     const char* text); | ||||||
| 
 | 
 | ||||||
| /** Add Button Element
 | /** Add Button Element
 | ||||||
|  * @param widget Widget instance |  * | ||||||
|  * @param button_type GuiButtonType instance |  * @param      widget       Widget instance | ||||||
|  * @param text text on allocated button |  * @param      button_type  GuiButtonType instance | ||||||
|  * @param callback ButtonCallback instance |  * @param      text         text on allocated button | ||||||
|  * @param context pointer to context |  * @param      callback     ButtonCallback instance | ||||||
|  |  * @param      context      pointer to context | ||||||
|  */ |  */ | ||||||
| void widget_add_button_element( | void widget_add_button_element( | ||||||
|     Widget* widget, |     Widget* widget, | ||||||
| @ -75,20 +91,22 @@ void widget_add_button_element( | |||||||
|     void* context); |     void* context); | ||||||
| 
 | 
 | ||||||
| /** Add Icon Element
 | /** Add Icon Element
 | ||||||
|  * @param widget Widget instance |  * | ||||||
|  * @param x top left x coordinate |  * @param      widget  Widget instance | ||||||
|  * @param y top left y coordinate |  * @param      x       top left x coordinate | ||||||
|  * @param icon Icon instance |  * @param      y       top left y coordinate | ||||||
|  |  * @param      icon    Icon instance | ||||||
|  */ |  */ | ||||||
| void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon); | void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon); | ||||||
| 
 | 
 | ||||||
| /** Add Frame Element
 | /** Add Frame Element
 | ||||||
|  * @param widget Widget instance |  * | ||||||
|  * @param x top left x coordinate |  * @param      widget  Widget instance | ||||||
|  * @param y top left y coordinate |  * @param      x       top left x coordinate | ||||||
|  * @param width frame width |  * @param      y       top left y coordinate | ||||||
|  * @param height frame height |  * @param      width   frame width | ||||||
|  * @param radius frame radius |  * @param      height  frame height | ||||||
|  |  * @param      radius  frame radius | ||||||
|  */ |  */ | ||||||
| void widget_add_frame_element( | void widget_add_frame_element( | ||||||
|     Widget* widget, |     Widget* widget, | ||||||
|  | |||||||
| @ -1,3 +1,8 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file widget_element_i.h | ||||||
|  |  * GUI: internal Widget Element API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| @ -29,7 +34,7 @@ struct WidgetElement { | |||||||
|     Widget* parent; |     Widget* parent; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* Create multi string element */ | /** Create multi string element */ | ||||||
| WidgetElement* widget_element_string_multiline_create( | WidgetElement* widget_element_string_multiline_create( | ||||||
|     uint8_t x, |     uint8_t x, | ||||||
|     uint8_t y, |     uint8_t y, | ||||||
| @ -38,7 +43,7 @@ WidgetElement* widget_element_string_multiline_create( | |||||||
|     Font font, |     Font font, | ||||||
|     const char* text); |     const char* text); | ||||||
| 
 | 
 | ||||||
| /* Create string element */ | /** Create string element */ | ||||||
| WidgetElement* widget_element_string_create( | WidgetElement* widget_element_string_create( | ||||||
|     uint8_t x, |     uint8_t x, | ||||||
|     uint8_t y, |     uint8_t y, | ||||||
| @ -47,17 +52,17 @@ WidgetElement* widget_element_string_create( | |||||||
|     Font font, |     Font font, | ||||||
|     const char* text); |     const char* text); | ||||||
| 
 | 
 | ||||||
| /* Create button element */ | /** Create button element */ | ||||||
| WidgetElement* widget_element_button_create( | WidgetElement* widget_element_button_create( | ||||||
|     GuiButtonType button_type, |     GuiButtonType button_type, | ||||||
|     const char* text, |     const char* text, | ||||||
|     ButtonCallback callback, |     ButtonCallback callback, | ||||||
|     void* context); |     void* context); | ||||||
| 
 | 
 | ||||||
| /* Create icon element */ | /** Create icon element */ | ||||||
| WidgetElement* widget_element_icon_create(uint8_t x, uint8_t y, const Icon* icon); | WidgetElement* widget_element_icon_create(uint8_t x, uint8_t y, const Icon* icon); | ||||||
| 
 | 
 | ||||||
| /* Create frame element */ | /** Create frame element */ | ||||||
| WidgetElement* widget_element_frame_create( | WidgetElement* widget_element_frame_create( | ||||||
|     uint8_t x, |     uint8_t x, | ||||||
|     uint8_t y, |     uint8_t y, | ||||||
|  | |||||||
| @ -1,14 +1,18 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file scene_manager.h | ||||||
|  |  * GUI: SceneManager API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include <stdint.h> | /** Scene Manager events type */ | ||||||
| #include <stdbool.h> |  | ||||||
| 
 |  | ||||||
| /** Scene Manager events type
 |  | ||||||
|  */ |  | ||||||
| typedef enum { | typedef enum { | ||||||
|     SceneManagerEventTypeCustom, |     SceneManagerEventTypeCustom, | ||||||
|     SceneManagerEventTypeBack, |     SceneManagerEventTypeBack, | ||||||
| @ -44,86 +48,110 @@ typedef struct { | |||||||
| typedef struct SceneManager SceneManager; | typedef struct SceneManager SceneManager; | ||||||
| 
 | 
 | ||||||
| /** Set Scene state
 | /** Set Scene state
 | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  * @param scene_id Scene ID |  * @param      scene_manager  SceneManager instance | ||||||
|  * @param state Scene new state |  * @param      scene_id       Scene ID | ||||||
|  |  * @param      state          Scene new state | ||||||
|  */ |  */ | ||||||
| void scene_manager_set_scene_state(SceneManager* scene_manager, uint32_t scene_id, uint32_t state); | void scene_manager_set_scene_state(SceneManager* scene_manager, uint32_t scene_id, uint32_t state); | ||||||
| 
 | 
 | ||||||
| /** Get Scene state
 | /** Get Scene state
 | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  * @param scene_id Scene ID |  * @param      scene_manager  SceneManager instance | ||||||
|  * @return Scene state |  * @param      scene_id       Scene ID | ||||||
|  |  * | ||||||
|  |  * @return     Scene state | ||||||
|  */ |  */ | ||||||
| uint32_t scene_manager_get_scene_state(SceneManager* scene_manager, uint32_t scene_id); | uint32_t scene_manager_get_scene_state(SceneManager* scene_manager, uint32_t scene_id); | ||||||
| 
 | 
 | ||||||
| /** Scene Manager allocation and configuration
 | /** Scene Manager allocation and configuration
 | ||||||
|  |  * | ||||||
|  * Scene Manager allocates all scenes internally |  * Scene Manager allocates all scenes internally | ||||||
|  * @param app_scene_handlers SceneManagerHandlers instance |  * | ||||||
|  * @param context context to be set on Scene handlers calls |  * @param      app_scene_handlers  SceneManagerHandlers instance | ||||||
|  * @return SceneManager instance |  * @param      context             context to be set on Scene handlers calls | ||||||
|  |  * | ||||||
|  |  * @return     SceneManager instance | ||||||
|  */ |  */ | ||||||
| SceneManager* scene_manager_alloc(const SceneManagerHandlers* app_scene_handlers, void* context); | SceneManager* scene_manager_alloc(const SceneManagerHandlers* app_scene_handlers, void* context); | ||||||
| 
 | 
 | ||||||
| /** Free Scene Manager with allocated Scenes
 | /** Free Scene Manager with allocated Scenes
 | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  |  * @param      scene_manager  SceneManager instance | ||||||
|  */ |  */ | ||||||
| void scene_manager_free(SceneManager* scene_manager); | void scene_manager_free(SceneManager* scene_manager); | ||||||
| 
 | 
 | ||||||
| /** Custom event handler
 | /** Custom event handler
 | ||||||
|  |  * | ||||||
|  * Calls Scene event handler with Custom event parameter |  * Calls Scene event handler with Custom event parameter | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  * @param custom_event Custom event code |  * @param      scene_manager  SceneManager instance | ||||||
|  * @return true if event was consumed, false otherwise |  * @param      custom_event   Custom event code | ||||||
|  |  * | ||||||
|  |  * @return     true if event was consumed, false otherwise | ||||||
|  */ |  */ | ||||||
| bool scene_manager_handle_custom_event(SceneManager* scene_manager, uint32_t custom_event); | bool scene_manager_handle_custom_event(SceneManager* scene_manager, uint32_t custom_event); | ||||||
| 
 | 
 | ||||||
| /** Back event handler
 | /** Back event handler
 | ||||||
|  |  * | ||||||
|  * Calls Scene event handler with Back event parameter |  * Calls Scene event handler with Back event parameter | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  * @return true if event was consumed, false otherwise |  * @param      scene_manager  SceneManager instance | ||||||
|  |  * | ||||||
|  |  * @return     true if event was consumed, false otherwise | ||||||
|  */ |  */ | ||||||
| bool scene_manager_handle_back_event(SceneManager* scene_manager); | bool scene_manager_handle_back_event(SceneManager* scene_manager); | ||||||
| 
 | 
 | ||||||
| /** Tick event handler
 | /** Tick event handler
 | ||||||
|  |  * | ||||||
|  * Calls Scene event handler with Tick event parameter |  * Calls Scene event handler with Tick event parameter | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  * @return true if event was consumed, false otherwise |  * @param      scene_manager  SceneManager instance | ||||||
|  |  * @return     true if event was consumed, false otherwise | ||||||
|  */ |  */ | ||||||
| void scene_manager_handle_tick_event(SceneManager* scene_manager); | void scene_manager_handle_tick_event(SceneManager* scene_manager); | ||||||
| 
 | 
 | ||||||
| /** Add and run next Scene
 | /** Add and run next Scene
 | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  * @param next_scene_id next Scene ID |  * @param      scene_manager  SceneManager instance | ||||||
|  |  * @param      next_scene_id  next Scene ID | ||||||
|  */ |  */ | ||||||
| void scene_manager_next_scene(SceneManager* scene_manager, uint32_t next_scene_id); | void scene_manager_next_scene(SceneManager* scene_manager, uint32_t next_scene_id); | ||||||
| 
 | 
 | ||||||
| /** Run previous Scene
 | /** Run previous Scene
 | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  * @return true if previous scene was found, false otherwise |  * @param      scene_manager  SceneManager instance | ||||||
|  |  * | ||||||
|  |  * @return     true if previous scene was found, false otherwise | ||||||
|  */ |  */ | ||||||
| bool scene_manager_previous_scene(SceneManager* scene_manager); | bool scene_manager_previous_scene(SceneManager* scene_manager); | ||||||
| 
 | 
 | ||||||
| /** Search previous Scene
 | /** Search previous Scene
 | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  * @param scene_id Scene ID |  * @param      scene_manager  SceneManager instance | ||||||
|  * @return true if previous scene was found, false otherwise |  * @param      scene_id       Scene ID | ||||||
|  |  * | ||||||
|  |  * @return     true if previous scene was found, false otherwise | ||||||
|  */ |  */ | ||||||
| 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); | ||||||
| 
 | 
 | ||||||
| /** Search and switch to previous Scene
 | /** Search and switch to previous Scene
 | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  * @param scene_id Scene ID |  * @param      scene_manager  SceneManager instance | ||||||
|  * @return true if previous scene was found, false otherwise |  * @param      scene_id       Scene ID | ||||||
|  |  * | ||||||
|  |  * @return     true if previous scene was found, false otherwise | ||||||
|  */ |  */ | ||||||
| bool scene_manager_search_and_switch_to_previous_scene( | bool scene_manager_search_and_switch_to_previous_scene( | ||||||
|     SceneManager* scene_manager, |     SceneManager* scene_manager, | ||||||
|     uint32_t scene_id); |     uint32_t scene_id); | ||||||
| 
 | 
 | ||||||
| /** Clear Scene stack and switch to another Scene
 | /** Clear Scene stack and switch to another Scene
 | ||||||
|  * @param scene_manager SceneManager instance |  * | ||||||
|  * @param scene_id Scene ID |  * @param      scene_manager  SceneManager instance | ||||||
|  * @return true if previous scene was found, false otherwise |  * @param      scene_id       Scene ID | ||||||
|  |  * | ||||||
|  |  * @return     true if previous scene was found, false otherwise | ||||||
|  */ |  */ | ||||||
| bool scene_manager_search_and_switch_to_another_scene( | bool scene_manager_search_and_switch_to_another_scene( | ||||||
|     SceneManager* scene_manager, |     SceneManager* scene_manager, | ||||||
|  | |||||||
| @ -1,3 +1,8 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file scene_manager_i.h | ||||||
|  |  * GUI: internal SceneManager API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "scene_manager.h" | #include "scene_manager.h" | ||||||
|  | |||||||
| @ -12,6 +12,11 @@ void view_free(View* view) { | |||||||
|     free(view); |     free(view); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void view_tie_icon_animation(View* view, IconAnimation* icon_animation) { | ||||||
|  |     furi_assert(view); | ||||||
|  |     icon_animation_set_update_callback(icon_animation, view_icon_animation_callback, view); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void view_set_draw_callback(View* view, ViewDrawCallback callback) { | void view_set_draw_callback(View* view, ViewDrawCallback callback) { | ||||||
|     furi_assert(view); |     furi_assert(view); | ||||||
|     furi_assert(view->draw_callback == NULL); |     furi_assert(view->draw_callback == NULL); | ||||||
| @ -120,6 +125,14 @@ void view_commit_model(View* view, bool update) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void view_icon_animation_callback(IconAnimation* instance, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     View* view = context; | ||||||
|  |     if(view->update_callback) { | ||||||
|  |         view->update_callback(view, view->update_callback_context); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void view_unlock_model(View* view) { | void view_unlock_model(View* view) { | ||||||
|     furi_assert(view); |     furi_assert(view); | ||||||
|     if(view->model_type == ViewModelTypeLocking) { |     if(view->model_type == ViewModelTypeLocking) { | ||||||
|  | |||||||
| @ -1,6 +1,13 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file view.h | ||||||
|  |  * GUI: View API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <input/input.h> | #include <input/input.h> | ||||||
|  | 
 | ||||||
|  | #include "icon_animation.h" | ||||||
| #include "canvas.h" | #include "canvas.h" | ||||||
| 
 | 
 | ||||||
| #include <stddef.h> | #include <stddef.h> | ||||||
| @ -10,9 +17,10 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* Hides drawing view_port */ | /** Hides drawing view_port */ | ||||||
| #define VIEW_NONE 0xFFFFFFFF | #define VIEW_NONE 0xFFFFFFFF | ||||||
| /* Ignore navigation event */ | 
 | ||||||
|  | /** Ignore navigation event */ | ||||||
| #define VIEW_IGNORE 0xFFFFFFFE | #define VIEW_IGNORE 0xFFFFFFFE | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -20,158 +28,181 @@ typedef enum { | |||||||
|     ViewOrientationVertical, |     ViewOrientationVertical, | ||||||
| } ViewOrientation; | } ViewOrientation; | ||||||
| 
 | 
 | ||||||
| /* View, anonymous type */ | /** View, anonymous type */ | ||||||
| typedef struct View View; | typedef struct View View; | ||||||
| 
 | 
 | ||||||
| /* View Draw callback
 | /** View Draw callback
 | ||||||
|  * @param canvas, pointer to canvas |  * @param      canvas,      pointer to canvas | ||||||
|  * @param view_model, pointer to context |  * @param      view_model,  pointer to context | ||||||
|  * @warning called from GUI thread |  * @warning    called from GUI thread | ||||||
|  */ |  */ | ||||||
| typedef void (*ViewDrawCallback)(Canvas* canvas, void* model); | typedef void (*ViewDrawCallback)(Canvas* canvas, void* model); | ||||||
| 
 | 
 | ||||||
| /* View Input callback
 | /** View Input callback
 | ||||||
|  * @param event, pointer to input event data |  * @param      event,    pointer to input event data | ||||||
|  * @param context, pointer to context |  * @param      context,  pointer to context | ||||||
|  * @return true if event handled, false if event ignored |  * @return     true if event handled, false if event ignored | ||||||
|  * @warning called from GUI thread |  * @warning    called from GUI thread | ||||||
|  */ |  */ | ||||||
| typedef bool (*ViewInputCallback)(InputEvent* event, void* context); | typedef bool (*ViewInputCallback)(InputEvent* event, void* context); | ||||||
| 
 | 
 | ||||||
| /* View Custom callback
 | /** View Custom callback
 | ||||||
|  * @param event, number of custom event |  * @param      event,    number of custom event | ||||||
|  * @param context, pointer to context |  * @param      context,  pointer to context | ||||||
|  * @return true if event handled, false if event ignored |  * @return     true if event handled, false if event ignored | ||||||
|  */ |  */ | ||||||
| typedef bool (*ViewCustomCallback)(uint32_t event, void* context); | typedef bool (*ViewCustomCallback)(uint32_t event, void* context); | ||||||
| 
 | 
 | ||||||
| /* View navigation callback
 | /** View navigation callback
 | ||||||
|  * @param context, pointer to context |  * @param      context,  pointer to context | ||||||
|  * @return next view id |  * @return     next view id | ||||||
|  * @warning called from GUI thread |  * @warning    called from GUI thread | ||||||
|  */ |  */ | ||||||
| typedef uint32_t (*ViewNavigationCallback)(void* context); | typedef uint32_t (*ViewNavigationCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| /* View callback
 | /** View callback
 | ||||||
|  * @param context, pointer to context |  * @param      context,  pointer to context | ||||||
|  * @warning called from GUI thread |  * @warning    called from GUI thread | ||||||
|  */ |  */ | ||||||
| typedef void (*ViewCallback)(void* context); | typedef void (*ViewCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| /* View Update Callback
 | /** View Update Callback Called upon model change, need to be propagated to GUI
 | ||||||
|  * Called upon model change, need to be propagated to GUI throw ViewPort update |  * throw ViewPort update | ||||||
|  * @param view, pointer to view |  * @param      view,     pointer to view | ||||||
|  * @param context, pointer to context |  * @param      context,  pointer to context | ||||||
|  * @warning called from GUI thread |  * @warning    called from GUI thread | ||||||
|  */ |  */ | ||||||
| typedef void (*ViewUpdateCallback)(View* view, void* context); | typedef void (*ViewUpdateCallback)(View* view, void* context); | ||||||
| 
 | 
 | ||||||
| /* View model types */ | /** View model types */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     /* Model is not allocated */ |     /** Model is not allocated */ | ||||||
|     ViewModelTypeNone, |     ViewModelTypeNone, | ||||||
|     /* Model consist of atomic types and/or partial update is not critical for rendering.
 |     /** Model consist of atomic types and/or partial update is not critical for rendering.
 | ||||||
|      * Lock free. |      * Lock free. | ||||||
|      */ |      */ | ||||||
|     ViewModelTypeLockFree, |     ViewModelTypeLockFree, | ||||||
|     /* Model access is guarded with mutex.
 |     /** Model access is guarded with mutex.
 | ||||||
|      * Locking gui thread. |      * Locking gui thread. | ||||||
|      */ |      */ | ||||||
|     ViewModelTypeLocking, |     ViewModelTypeLocking, | ||||||
| } ViewModelType; | } ViewModelType; | ||||||
| 
 | 
 | ||||||
| /* Allocate and init View
 | /** Allocate and init View
 | ||||||
|  * @return pointer to View |  * @return View instance | ||||||
|  */ |  */ | ||||||
| View* view_alloc(); | View* view_alloc(); | ||||||
| 
 | 
 | ||||||
| /* Free View
 | /** Free View
 | ||||||
|  * @param pointer to View |  * | ||||||
|  |  * @param      view  instance | ||||||
|  */ |  */ | ||||||
| void view_free(View* view); | void view_free(View* view); | ||||||
| 
 | 
 | ||||||
| /* Set View Draw callback
 | /** Tie IconAnimation with View
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param callback, draw callback |  * @param      view            View instance | ||||||
|  |  * @param      icon_animation  IconAnimation instance | ||||||
|  |  */ | ||||||
|  | void view_tie_icon_animation(View* view, IconAnimation* icon_animation); | ||||||
|  | 
 | ||||||
|  | /** Set View Draw callback
 | ||||||
|  |  * | ||||||
|  |  * @param      view      View instance | ||||||
|  |  * @param      callback  draw callback | ||||||
|  */ |  */ | ||||||
| void view_set_draw_callback(View* view, ViewDrawCallback callback); | void view_set_draw_callback(View* view, ViewDrawCallback callback); | ||||||
| 
 | 
 | ||||||
| /* Set View Input callback
 | /** Set View Input callback
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param callback, input callback |  * @param      view      View instance | ||||||
|  |  * @param      callback  input callback | ||||||
|  */ |  */ | ||||||
| void view_set_input_callback(View* view, ViewInputCallback callback); | void view_set_input_callback(View* view, ViewInputCallback callback); | ||||||
| 
 | 
 | ||||||
| /* Set View Custom callback
 | /** Set View Custom callback
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param callback, input callback |  * @param      view      View instance | ||||||
|  |  * @param      callback  input callback | ||||||
|  */ |  */ | ||||||
| void view_set_custom_callback(View* view, ViewCustomCallback callback); | void view_set_custom_callback(View* view, ViewCustomCallback callback); | ||||||
| 
 | 
 | ||||||
| /* Set Navigation Previous callback
 | /** Set Navigation Previous callback
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param callback, input callback |  * @param      view      View instance | ||||||
|  |  * @param      callback  input callback | ||||||
|  */ |  */ | ||||||
| void view_set_previous_callback(View* view, ViewNavigationCallback callback); | void view_set_previous_callback(View* view, ViewNavigationCallback callback); | ||||||
| 
 | 
 | ||||||
| /* Set Enter callback
 | /** Set Enter callback
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param callback, callback |  * @param      view      View instance | ||||||
|  |  * @param      callback  callback | ||||||
|  */ |  */ | ||||||
| void view_set_enter_callback(View* view, ViewCallback callback); | void view_set_enter_callback(View* view, ViewCallback callback); | ||||||
| 
 | 
 | ||||||
| /* Set Exit callback
 | /** Set Exit callback
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param callback, callback |  * @param      view      View instance | ||||||
|  |  * @param      callback  callback | ||||||
|  */ |  */ | ||||||
| void view_set_exit_callback(View* view, ViewCallback callback); | void view_set_exit_callback(View* view, ViewCallback callback); | ||||||
| 
 | 
 | ||||||
| /* Set Update callback
 | /** Set Update callback
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param callback, callback |  * @param      view      View instance | ||||||
|  |  * @param      callback  callback | ||||||
|  */ |  */ | ||||||
| void view_set_update_callback(View* view, ViewUpdateCallback callback); | void view_set_update_callback(View* view, ViewUpdateCallback callback); | ||||||
| 
 | 
 | ||||||
| /* Set View Draw callback
 | /** Set View Draw callback
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param context, context for callbacks |  * @param      view     View instance | ||||||
|  |  * @param      context  context for callbacks | ||||||
|  */ |  */ | ||||||
| void view_set_update_callback_context(View* view, void* context); | void view_set_update_callback_context(View* view, void* context); | ||||||
| 
 | 
 | ||||||
| /* Set View Draw callback
 | /** Set View Draw callback
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param context, context for callbacks |  * @param      view     View instance | ||||||
|  |  * @param      context  context for callbacks | ||||||
|  */ |  */ | ||||||
| void view_set_context(View* view, void* context); | void view_set_context(View* view, void* context); | ||||||
| 
 | 
 | ||||||
| /* Set View Orientation
 | /** Set View Orientation
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param orientation, either vertical or horizontal |  * @param      view         View instance | ||||||
|  |  * @param      orientation  either vertical or horizontal | ||||||
|  */ |  */ | ||||||
| void view_set_orientation(View* view, ViewOrientation orientation); | void view_set_orientation(View* view, ViewOrientation orientation); | ||||||
| 
 | 
 | ||||||
| /* Allocate view model.
 | /** Allocate view model.
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param type, View Model Type |  * @param      view  View instance | ||||||
|  * @param size, size |  * @param      type  View Model Type | ||||||
|  |  * @param      size  size | ||||||
|  */ |  */ | ||||||
| void view_allocate_model(View* view, ViewModelType type, size_t size); | void view_allocate_model(View* view, ViewModelType type, size_t size); | ||||||
| 
 | 
 | ||||||
| /* Free view model data memory.
 | /** Free view model data memory.
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  |  * @param      view  View instance | ||||||
|  */ |  */ | ||||||
| void view_free_model(View* view); | void view_free_model(View* view); | ||||||
| 
 | 
 | ||||||
| /* Get view model data
 | /** Get view model data
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @return pointer to model data |  * @param      view  View instance | ||||||
|  * @warning Don't forget to commit model changes |  * | ||||||
|  |  * @return     pointer to model data | ||||||
|  |  * @warning    Don't forget to commit model changes | ||||||
|  */ |  */ | ||||||
| void* view_get_model(View* view); | void* view_get_model(View* view); | ||||||
| 
 | 
 | ||||||
| /* Commit view model
 | /** Commit view model
 | ||||||
|  * @param view, pointer to View |  * | ||||||
|  * @param update, true if you want to emit view update, false otherwise |  * @param      view    View instance | ||||||
|  |  * @param      update  true if you want to emit view update, false otherwise | ||||||
|  */ |  */ | ||||||
| void view_commit_model(View* view, bool update); | void view_commit_model(View* view, bool update); | ||||||
| 
 | 
 | ||||||
| @ -187,11 +218,13 @@ void view_commit_model(View* view, bool update); | |||||||
|         view_commit_model(view, update);                    \ |         view_commit_model(view, update);                    \ | ||||||
|     } |     } | ||||||
| #else | #else | ||||||
| /* 
 | /** With clause for view model
 | ||||||
|  * With clause for view model |  * | ||||||
|  * @param view, View instance pointer |  * @param      view           View instance pointer | ||||||
|  * @param function_body a (){} lambda declaration, executed within you parent function context |  * @param      function_body  a (){} lambda declaration, executed within you | ||||||
|  * @return true if you want to emit view update, false otherwise |  *                            parent function context | ||||||
|  |  * | ||||||
|  |  * @return     true if you want to emit view update, false otherwise | ||||||
|  */ |  */ | ||||||
| #define with_view_model(view, function_body)                      \ | #define with_view_model(view, function_body)                      \ | ||||||
|     {                                                             \ |     {                                                             \ | ||||||
|  | |||||||
| @ -177,6 +177,18 @@ void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t vi | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void view_dispatcher_send_to_front(ViewDispatcher* view_dispatcher) { | ||||||
|  |     furi_assert(view_dispatcher); | ||||||
|  |     furi_assert(view_dispatcher->gui); | ||||||
|  |     gui_view_port_send_to_front(view_dispatcher->gui, view_dispatcher->view_port); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void view_dispatcher_send_to_back(ViewDispatcher* view_dispatcher) { | ||||||
|  |     furi_assert(view_dispatcher); | ||||||
|  |     furi_assert(view_dispatcher->gui); | ||||||
|  |     gui_view_port_send_to_front(view_dispatcher->gui, view_dispatcher->view_port); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void view_dispatcher_attach_to_gui( | void view_dispatcher_attach_to_gui( | ||||||
|     ViewDispatcher* view_dispatcher, |     ViewDispatcher* view_dispatcher, | ||||||
|     Gui* gui, |     Gui* gui, | ||||||
|  | |||||||
| @ -1,3 +1,8 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file view_dispatcher.h | ||||||
|  |  * GUI: ViewDispatcher API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "view.h" | #include "view.h" | ||||||
| @ -8,8 +13,7 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /** ViewDispatcher view_port placement
 | /** ViewDispatcher view_port placement */ | ||||||
|  */ |  | ||||||
| typedef enum { | typedef enum { | ||||||
|     ViewDispatcherTypeNone, /**< Special layer for internal use only */ |     ViewDispatcherTypeNone, /**< Special layer for internal use only */ | ||||||
|     ViewDispatcherTypeWindow, /**< Main view_port layer, status bar is shown */ |     ViewDispatcherTypeWindow, /**< Main view_port layer, status bar is shown */ | ||||||
| @ -18,61 +22,70 @@ typedef enum { | |||||||
| 
 | 
 | ||||||
| typedef struct ViewDispatcher ViewDispatcher; | typedef struct ViewDispatcher ViewDispatcher; | ||||||
| 
 | 
 | ||||||
| /** Prototype for custom event callback
 | /** Prototype for custom event callback */ | ||||||
|  */ |  | ||||||
| typedef bool (*ViewDispatcherCustomEventCallback)(void* context, uint32_t event); | typedef bool (*ViewDispatcherCustomEventCallback)(void* context, uint32_t event); | ||||||
| 
 | 
 | ||||||
| /** Prototype for navigation event callback
 | /** Prototype for navigation event callback */ | ||||||
|  */ |  | ||||||
| typedef bool (*ViewDispatcherNavigationEventCallback)(void* context); | typedef bool (*ViewDispatcherNavigationEventCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| /** Prototype for tick event callback
 | /** Prototype for tick event callback */ | ||||||
|  */ |  | ||||||
| typedef void (*ViewDispatcherTickEventCallback)(void* context); | typedef void (*ViewDispatcherTickEventCallback)(void* context); | ||||||
| 
 | 
 | ||||||
| /** Allocate ViewDispatcher instance
 | /** Allocate ViewDispatcher instance
 | ||||||
|  * @return pointer to ViewDispatcher instance |  * | ||||||
|  |  * @return     pointer to ViewDispatcher instance | ||||||
|  */ |  */ | ||||||
| ViewDispatcher* view_dispatcher_alloc(); | ViewDispatcher* view_dispatcher_alloc(); | ||||||
| 
 | 
 | ||||||
| /** Free ViewDispatcher instance
 | /** Free ViewDispatcher instance
 | ||||||
|  * @param view_dispatcher pointer to ViewDispatcher |  * | ||||||
|  |  * @param      view_dispatcher  pointer to ViewDispatcher | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_free(ViewDispatcher* view_dispatcher); | void view_dispatcher_free(ViewDispatcher* view_dispatcher); | ||||||
| 
 | 
 | ||||||
| /** Enable queue support
 | /** Enable queue support
 | ||||||
|  * If queue enabled all input and custom events will be dispatched throw internal queue |  * | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * If queue enabled all input and custom events will be dispatched throw | ||||||
|  |  * internal queue | ||||||
|  |  * | ||||||
|  |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher); | void view_dispatcher_enable_queue(ViewDispatcher* view_dispatcher); | ||||||
| 
 | 
 | ||||||
| /** Send custom event
 | /** Send custom event
 | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * | ||||||
|  |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  |  * @param[in]  event            The event | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t event); | void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t event); | ||||||
| 
 | 
 | ||||||
| /** Set custom event handler
 | /** Set custom event handler
 | ||||||
|  |  * | ||||||
|  * Called on Custom Event, if it is not consumed by view |  * Called on Custom Event, if it is not consumed by view | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * | ||||||
|  * @param callback ViewDispatcherCustomEventCallback instance |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  |  * @param      callback         ViewDispatcherCustomEventCallback instance | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_set_custom_event_callback( | void view_dispatcher_set_custom_event_callback( | ||||||
|     ViewDispatcher* view_dispatcher, |     ViewDispatcher* view_dispatcher, | ||||||
|     ViewDispatcherCustomEventCallback callback); |     ViewDispatcherCustomEventCallback callback); | ||||||
| 
 | 
 | ||||||
| /** Set navigation event handler
 | /** Set navigation event handler
 | ||||||
|  |  * | ||||||
|  * Called on Input Short Back Event, if it is not consumed by view |  * Called on Input Short Back Event, if it is not consumed by view | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * | ||||||
|  * @param callback ViewDispatcherNavigationEventCallback instance |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  |  * @param      callback         ViewDispatcherNavigationEventCallback instance | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_set_navigation_event_callback( | void view_dispatcher_set_navigation_event_callback( | ||||||
|     ViewDispatcher* view_dispatcher, |     ViewDispatcher* view_dispatcher, | ||||||
|     ViewDispatcherNavigationEventCallback callback); |     ViewDispatcherNavigationEventCallback callback); | ||||||
| 
 | 
 | ||||||
| /** Set tick event handler
 | /** Set tick event handler
 | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * | ||||||
|  * @param callback ViewDispatcherTickEventCallback |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  * @param tick_period callback call period |  * @param      callback         ViewDispatcherTickEventCallback | ||||||
|  |  * @param      tick_period      callback call period | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_set_tick_event_callback( | void view_dispatcher_set_tick_event_callback( | ||||||
|     ViewDispatcher* view_dispatcher, |     ViewDispatcher* view_dispatcher, | ||||||
| @ -80,46 +93,69 @@ void view_dispatcher_set_tick_event_callback( | |||||||
|     uint32_t tick_period); |     uint32_t tick_period); | ||||||
| 
 | 
 | ||||||
| /** Set event callback context
 | /** Set event callback context
 | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * | ||||||
|  * @param context pointer to context |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  |  * @param      context          pointer to context | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context); | void view_dispatcher_set_event_callback_context(ViewDispatcher* view_dispatcher, void* context); | ||||||
| 
 | 
 | ||||||
| /** Run ViewDispatcher
 | /** Run ViewDispatcher
 | ||||||
|  |  * | ||||||
|  * Use only after queue enabled |  * Use only after queue enabled | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * | ||||||
|  |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_run(ViewDispatcher* view_dispatcher); | void view_dispatcher_run(ViewDispatcher* view_dispatcher); | ||||||
| 
 | 
 | ||||||
| /** Stop ViewDispatcher
 | /** Stop ViewDispatcher
 | ||||||
|  |  * | ||||||
|  * Use only after queue enabled |  * Use only after queue enabled | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * | ||||||
|  |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_stop(ViewDispatcher* view_dispatcher); | void view_dispatcher_stop(ViewDispatcher* view_dispatcher); | ||||||
| 
 | 
 | ||||||
| /** Add view to ViewDispatcher
 | /** Add view to ViewDispatcher
 | ||||||
|  * @param view_dispatcher, ViewDispatcher instance |  * | ||||||
|  * @param view_id View id to register |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  * @param view View instance |  * @param      view_id          View id to register | ||||||
|  |  * @param      view             View instance | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view); | void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view); | ||||||
| 
 | 
 | ||||||
| /** Remove view from ViewDispatcher
 | /** Remove view from ViewDispatcher
 | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * | ||||||
|  * @param view_id View id to remove |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  |  * @param      view_id          View id to remove | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_id); | void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_id); | ||||||
| 
 | 
 | ||||||
| /** Switch to View
 | /** Switch to View
 | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * | ||||||
|  * @param view_id View id to register |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  * @warning switching may be delayed till input events complementarity reached |  * @param      view_id          View id to register | ||||||
|  |  * @warning    switching may be delayed till input events complementarity | ||||||
|  |  *             reached | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id); | void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id); | ||||||
| 
 | 
 | ||||||
|  | /** Send ViewPort of this ViewDispatcher instance to front
 | ||||||
|  |  * | ||||||
|  |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  |  */ | ||||||
|  | void view_dispatcher_send_to_front(ViewDispatcher* view_dispatcher); | ||||||
|  | 
 | ||||||
|  | /** Send ViewPort of this ViewDispatcher instance to back
 | ||||||
|  |  * | ||||||
|  |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  |  */ | ||||||
|  | void view_dispatcher_send_to_back(ViewDispatcher* view_dispatcher); | ||||||
|  | 
 | ||||||
| /** Attach ViewDispatcher to GUI
 | /** Attach ViewDispatcher to GUI
 | ||||||
|  * @param view_dispatcher ViewDispatcher instance |  * | ||||||
|  * @param gui GUI instance to attach to |  * @param      view_dispatcher  ViewDispatcher instance | ||||||
|  |  * @param      gui              GUI instance to attach to | ||||||
|  |  * @param[in]  type             The type | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_attach_to_gui( | void view_dispatcher_attach_to_gui( | ||||||
|     ViewDispatcher* view_dispatcher, |     ViewDispatcher* view_dispatcher, | ||||||
|  | |||||||
| @ -1,3 +1,8 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file view_dispatcher_i.h | ||||||
|  |  * GUI: ViewDispatcher API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| @ -41,23 +46,23 @@ typedef struct { | |||||||
|     }; |     }; | ||||||
| } ViewDispatcherMessage; | } ViewDispatcherMessage; | ||||||
| 
 | 
 | ||||||
| /* ViewPort Draw Callback */ | /** ViewPort Draw Callback */ | ||||||
| void view_dispatcher_draw_callback(Canvas* canvas, void* context); | void view_dispatcher_draw_callback(Canvas* canvas, void* context); | ||||||
| 
 | 
 | ||||||
| /* ViewPort Input Callback */ | /** ViewPort Input Callback */ | ||||||
| void view_dispatcher_input_callback(InputEvent* event, void* context); | void view_dispatcher_input_callback(InputEvent* event, void* context); | ||||||
| 
 | 
 | ||||||
| /* Input handler */ | /** Input handler */ | ||||||
| void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event); | void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event); | ||||||
| 
 | 
 | ||||||
| /* Tick handler */ | /** Tick handler */ | ||||||
| void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher); | void view_dispatcher_handle_tick_event(ViewDispatcher* view_dispatcher); | ||||||
| 
 | 
 | ||||||
| /* Custom event handler */ | /** Custom event handler */ | ||||||
| void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32_t event); | void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32_t event); | ||||||
| 
 | 
 | ||||||
| /* Set current view, dispatches view enter and exit */ | /** Set current view, dispatches view enter and exit */ | ||||||
| void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* view); | void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* view); | ||||||
| 
 | 
 | ||||||
| /* ViewDispatcher update event */ | /** ViewDispatcher update event */ | ||||||
| void view_dispatcher_update(View* view, void* context); | void view_dispatcher_update(View* view, void* context); | ||||||
|  | |||||||
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