Merge branch 'release-candidate' into release

This commit is contained in:
Aleksandr Kutuzov 2021-10-21 21:19:16 +03:00
commit ce74a35066
307 changed files with 16352 additions and 4553 deletions

View File

@ -11,7 +11,7 @@ on:
env:
TARGETS: f6 f7
DEFAULT_TARGET: f6
DEFAULT_TARGET: f7
jobs:
build:

9
.gitmodules vendored
View File

@ -7,3 +7,12 @@
[submodule "lib/littlefs"]
path = lib/littlefs
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
View 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
View 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:.

View File

@ -162,6 +162,7 @@ Finally, you will have **`firmware/.obj/f6/full.dfu`** file that can be distribu
- core - core libraries: home for furi
- debug - debug helpers, plugins and tools
- docker - docker image sources (used for automated firmware build)
- documentation - documentation generation system configs and input files
- firmware - firmware for flipper
* targets - targets' hal and implementation
- lib - different libraries and drivers that apps and firmware uses

49
applications/applications.c Executable file → Normal file
View File

@ -2,6 +2,7 @@
#include <assets_icons.h>
// Services
extern int32_t rpc_srv(void* p);
extern int32_t bt_srv(void* p);
extern int32_t cli_srv(void* p);
extern int32_t dialogs_srv(void* p);
@ -19,8 +20,8 @@ extern int32_t desktop_srv(void* p);
extern int32_t accessor_app(void* p);
extern int32_t archive_app(void* p);
extern int32_t blink_test_app(void* p);
extern int32_t flipper_test_app(void* p);
extern int32_t gpio_test_app(void* p);
extern int32_t delay_test_app(void* p);
extern int32_t gpio_app(void* p);
extern int32_t ibutton_app(void* p);
extern int32_t irda_app(void* p);
extern int32_t irda_monitor_app(void* p);
@ -33,6 +34,9 @@ extern int32_t storage_test_app(void* p);
extern int32_t subghz_app(void* p);
extern int32_t vibro_test_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
extern int32_t music_player_app(void* p);
@ -47,6 +51,7 @@ extern void nfc_cli_init();
extern void storage_cli_init();
extern void subghz_cli_init();
extern void power_cli_init();
extern void unit_tests_cli_init();
// Settings
extern int32_t notification_settings_app(void* p);
@ -58,6 +63,10 @@ extern int32_t power_settings_app(void* p);
const FlipperApplication FLIPPER_SERVICES[] = {
/* Services */
#ifdef SRV_RPC
{.app = rpc_srv, .name = "RPC", .stack_size = 1024 * 4, .icon = NULL},
#endif
#ifdef SRV_BT
{.app = bt_srv, .name = "BT", .stack_size = 1024, .icon = NULL},
#endif
@ -116,24 +125,24 @@ const FlipperApplication FLIPPER_APPS[] = {
{.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14},
#endif
#ifdef APP_NFC
{.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14},
#endif
#ifdef APP_LF_RFID
{.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_125khz_14},
#endif
#ifdef APP_NFC
{.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14},
#endif
#ifdef APP_IRDA
{.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14},
#endif
#ifdef APP_IBUTTON
{.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14},
#ifdef APP_GPIO
{.app = gpio_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14},
#endif
#ifdef APP_GPIO_TEST
{.app = gpio_test_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14},
#ifdef APP_IBUTTON
{.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14},
#endif
};
@ -177,6 +186,10 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = {
#ifdef SRV_STORAGE
storage_cli_init,
#endif
#ifdef APP_UNIT_TESTS
unit_tests_cli_init,
#endif
};
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},
#endif
#ifdef APP_UNIT_TESTS
{.app = flipper_test_app, .name = "Unit Tests", .stack_size = 1024, .icon = &A_Plugins_14},
#ifdef APP_USB_TEST
{.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
#ifdef APP_IRDA_MONITOR
@ -229,6 +250,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
#ifdef SRV_BT
{.app = bt_debug_app, .name = "Bluetooth Debug", .stack_size = 1024, .icon = NULL},
#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);

View File

@ -2,8 +2,8 @@ APP_DIR = $(PROJECT_ROOT)/applications
LIB_DIR = $(PROJECT_ROOT)/lib
CFLAGS += -I$(APP_DIR)
C_SOURCES += $(shell find $(APP_DIR) -name *.c)
CPP_SOURCES += $(shell find $(APP_DIR) -name *.cpp)
C_SOURCES += $(shell find $(APP_DIR) -name "*.c")
CPP_SOURCES += $(shell find $(APP_DIR) -name "*.cpp")
APP_RELEASE ?= 1
@ -19,12 +19,13 @@ SRV_LOADER = 1
SRV_NOTIFICATION = 1
SRV_POWER = 1
SRV_POWER_OBSERVER = 1
SRV_RPC = 1
SRV_STORAGE = 1
# Apps
SRV_DESKTOP = 1
APP_ARCHIVE = 1
APP_GPIO_TEST = 1
APP_GPIO = 1
APP_IBUTTON = 1
APP_IRDA = 1
APP_LF_RFID = 1
@ -41,8 +42,10 @@ APP_BLINK = 1
APP_IRDA_MONITOR = 1
APP_KEYPAD_TEST = 1
APP_SD_TEST = 1
APP_UNIT_TESTS = 0
APP_VIBRO_DEMO = 1
APP_USB_TEST = 1
APP_USB_MOUSE = 1
APP_BAD_USB = 1
endif
@ -58,7 +61,7 @@ SRV_GUI = 1
endif
APP_UNIT_TESTS ?= 0
APP_UNIT_TESTS ?= 0
ifeq ($(APP_UNIT_TESTS), 1)
CFLAGS += -DAPP_UNIT_TESTS
endif
@ -121,6 +124,27 @@ SRV_GUI = 1
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
ifeq ($(APP_KEYPAD_TEST), 1)
CFLAGS += -DAPP_KEYPAD_TEST
@ -135,9 +159,9 @@ SRV_GUI = 1
endif
APP_GPIO_TEST ?= 0
ifeq ($(APP_GPIO_TEST), 1)
CFLAGS += -DAPP_GPIO_TEST
APP_GPIO ?= 0
ifeq ($(APP_GPIO), 1)
CFLAGS += -DAPP_GPIO
SRV_GUI = 1
endif
@ -197,6 +221,10 @@ SRV_GUI = 1
SRV_CLI = 1
endif
SRV_RPC ?= 0
ifeq ($(SRV_RPC), 1)
CFLAGS += -DSRV_RPC
endif
SRV_LOADER ?= 0
ifeq ($(SRV_LOADER), 1)

View File

@ -10,7 +10,9 @@ void archive_update_offset(ArchiveBrowserView* browser) {
if(array_size > 3 && model->idx >= array_size - 1) {
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->last_offset = !model->last_offset;
} 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) {
model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0);
}
return true;
});
}
@ -75,6 +76,31 @@ void archive_file_array_rm_selected(ArchiveBrowserView* 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) {
with_view_model(
browser->view, (ArchiveBrowserViewModel * model) {
@ -94,6 +120,18 @@ ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) {
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 tab_id;
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) {
furi_assert(browser);
furi_assert(path);

View File

@ -52,9 +52,11 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target);
size_t archive_file_array_size(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);
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);
uint8_t archive_get_depth(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_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_enter_dir(ArchiveBrowserView* browser, string_t name);

View File

@ -34,7 +34,7 @@ uint16_t archive_favorites_count(void* context) {
bool archive_favorites_read(void* context) {
furi_assert(context);
ArchiveBrowserView* archive_view = context;
ArchiveBrowserView* browser = context;
FileWorker* file_worker = file_worker_alloc(true);
string_t buffer;
@ -52,7 +52,7 @@ bool archive_favorites_read(void* context) {
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);
}
}
@ -63,17 +63,15 @@ bool archive_favorites_read(void* context) {
}
bool archive_favorites_delete(const char* format, ...) {
string_t buffer;
string_t filename;
va_list args;
va_start(args, format);
uint8_t len = vsnprintf(NULL, 0, format, args);
char filename[len + 1];
vsnprintf(filename, len + 1, format, args);
string_init_vprintf(filename, format, args);
va_end(args);
FileWorker* file_worker = file_worker_alloc(true);
string_t 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);
if(result) {
@ -85,13 +83,14 @@ bool archive_favorites_delete(const char* format, ...) {
break;
}
if(string_search_str(buffer, filename)) {
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\r\n", string_get_cstr(buffer));
if(string_search(buffer, filename)) {
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
}
}
}
string_clear(buffer);
string_clear(filename);
file_worker_close(file_worker);
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, ...) {
string_t buffer;
string_t filename;
va_list args;
va_start(args, format);
uint8_t len = vsnprintf(NULL, 0, format, args);
char filename[len + 1];
vsnprintf(filename, len + 1, format, args);
string_init_vprintf(filename, format, args);
va_end(args);
FileWorker* file_worker = file_worker_alloc(true);
string_t buffer;
string_init(buffer);
FileWorker* file_worker = file_worker_alloc(true);
bool found = false;
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)) {
break;
}
if(!string_search_str(buffer, filename)) {
if(!string_search(buffer, filename)) {
found = true;
break;
}
@ -133,6 +131,7 @@ bool archive_is_favorite(const char* format, ...) {
}
string_clear(buffer);
string_clear(filename);
file_worker_close(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_FAV_TEMP_PATH,
"%s\r\n",
"%s\n",
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) {
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);
}

View File

@ -9,4 +9,5 @@ bool archive_favorites_read(void* context);
bool archive_favorites_delete(const char* format, ...);
bool archive_is_favorite(const char* format, ...);
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);

View File

@ -138,11 +138,10 @@ bool archive_read_dir(void* context, const char* path) {
void archive_file_append(const char* path, const char* format, ...) {
furi_assert(path);
string_t string;
va_list args;
va_start(args, format);
uint8_t len = vsnprintf(NULL, 0, format, args);
char cstr_buff[len + 1];
vsnprintf(cstr_buff, len + 1, format, args);
string_init_vprintf(string, format, args);
va_end(args);
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");
}
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");
}
@ -159,26 +158,28 @@ void archive_file_append(const char* path, const char* format, ...) {
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(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;
FileWorker* file_worker = file_worker_alloc(true);
string_t full_path;
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));
bool res = file_worker_remove(file_worker, string_get_cstr(filename));
file_worker_free(file_worker);
if(archive_is_favorite(string_get_cstr(full_path))) {
archive_favorites_delete(string_get_cstr(full_path));
if(archive_is_favorite("%s", string_get_cstr(filename))) {
archive_favorites_delete("%s", string_get_cstr(filename));
}
if(res) {
archive_file_array_rm_selected(browser);
}
string_clear(full_path);
}
string_clear(filename);
}

View File

@ -54,4 +54,4 @@ bool archive_get_filenames(void* context, const char* path);
bool archive_dir_empty(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_delete_file(void* context, string_t path, string_t name);
void archive_delete_file(void* context, const char* format, ...);

View File

@ -76,25 +76,32 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
if(favorites) {
archive_favorites_delete(name);
archive_file_array_rm_selected(browser);
archive_show_file_menu(browser, false);
} else if(known_app) {
if(archive_is_favorite("%s/%s", path, name)) {
archive_favorites_delete("%s/%s", path, name);
} 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;
break;
case ArchiveBrowserEventFileMenuRename:
if(known_app && !favorites) {
case ArchiveBrowserEventFileMenuAction:
if(favorites) {
browser->callback(ArchiveBrowserEventEnterFavMove, browser->context);
} else if(known_app) {
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename);
}
consumed = true;
break;
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);
consumed = true;
break;
@ -102,6 +109,30 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
archive_enter_dir(browser, selected->name);
consumed = true;
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:
if(archive_get_depth(browser)) {

View File

@ -55,7 +55,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
string_set_str(menu[1], "Unpin");
} else if(model->tab_idx == ArchiveTabFavorites) {
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++) {
@ -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);
}
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_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_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT);
canvas_draw_dot(canvas, 1, 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);
canvas_draw_dot(canvas, 1 + x_offset, 15 + idx * FRAME_HEIGHT);
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) + 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) {
string_t str_buff;
char cstr_buff[MAX_NAME_LEN];
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));
strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1);
archive_trim_file_path(cstr_buff, is_known_app(file->type));
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) {
archive_draw_frame(canvas, i, scrollbar);
archive_draw_frame(canvas, i, scrollbar, model->move_fav);
} else {
canvas_set_color(canvas, ColorBlack);
}
canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]);
canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
canvas_draw_icon(
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);
}
@ -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, 108, 12, 126, 12);
canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7);
canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7);
if(model->move_fav) {
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_draw_dot(canvas, 50, 0);
@ -174,9 +189,11 @@ bool archive_view_input(InputEvent* event, void* context) {
ArchiveBrowserView* browser = context;
bool in_menu;
bool move_fav_mode;
with_view_model(
browser->view, (ArchiveBrowserViewModel * model) {
in_menu = model->menu;
move_fav_mode = model->move_fav;
return false;
});
@ -210,11 +227,17 @@ bool archive_view_input(InputEvent* event, void* context) {
} else {
if(event->type == InputTypeShort) {
if(event->key == InputKeyLeft || event->key == InputKeyRight) {
if(move_fav_mode) return false;
archive_switch_tab(browser, event->key);
} 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) {
with_view_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->key == InputKeyUp) {
model->idx = ((model->idx - 1) + num_elements) % num_elements;
if(move_fav_mode) {
browser->callback(ArchiveBrowserEventFavMoveUp, browser->context);
}
} else if(event->key == InputKeyDown) {
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(favorites) {
browser->callback(ArchiveBrowserEventFileMenuRun, browser->context);
if(move_fav_mode) {
browser->callback(ArchiveBrowserEventSaveFavMove, browser->context);
} else {
browser->callback(ArchiveBrowserEventFileMenuRun, browser->context);
}
} else if(folder) {
browser->callback(ArchiveBrowserEventEnterDir, browser->context);
} else {
browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context);
}
} 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);
}
}

View File

@ -14,14 +14,15 @@
#define FRAME_HEIGHT 12
#define MENU_ITEMS 4
#define MAX_DEPTH 32
#define MOVE_OFFSET 5
typedef enum {
ArchiveTabFavorites,
ArchiveTabLFRFID,
ArchiveTabSubGhz,
ArchiveTabLFRFID,
ArchiveTabNFC,
ArchiveTabIButton,
ArchiveTabIrda,
ArchiveTabIButton,
ArchiveTabBrowser,
ArchiveTabTotal,
} ArchiveTabEnum;
@ -31,16 +32,21 @@ typedef enum {
ArchiveBrowserEventFileMenuClose,
ArchiveBrowserEventFileMenuRun,
ArchiveBrowserEventFileMenuPin,
ArchiveBrowserEventFileMenuRename,
ArchiveBrowserEventFileMenuAction,
ArchiveBrowserEventFileMenuDelete,
ArchiveBrowserEventEnterDir,
ArchiveBrowserEventFavMoveUp,
ArchiveBrowserEventFavMoveDown,
ArchiveBrowserEventEnterFavMove,
ArchiveBrowserEventExitFavMove,
ArchiveBrowserEventSaveFavMove,
ArchiveBrowserEventExit,
} ArchiveBrowserEvent;
static const uint8_t file_menu_actions[MENU_ITEMS] = {
[0] = ArchiveBrowserEventFileMenuRun,
[1] = ArchiveBrowserEventFileMenuPin,
[2] = ArchiveBrowserEventFileMenuRename,
[2] = ArchiveBrowserEventFileMenuAction,
[3] = ArchiveBrowserEventFileMenuDelete,
};
@ -68,6 +74,7 @@ typedef struct {
files_array_t files;
uint8_t menu_idx;
bool move_fav;
bool menu;
uint16_t idx;

View File

@ -33,7 +33,10 @@ static void bt_battery_level_changed_callback(const void* _event, void* context)
Bt* bt = context;
const PowerEvent* event = _event;
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);
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;
}
// 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() {
Bt* bt = bt_alloc();
furi_record_create("bt", bt);
@ -72,11 +150,10 @@ int32_t bt_srv() {
FURI_LOG_E(BT_SERVICE_TAG, "Core2 startup failed");
} else {
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");
if(bt->bt_settings.enabled) {
furi_hal_bt_start_advertising();
FURI_LOG_I(BT_SERVICE_TAG, "Start advertising");
}
} else {
FURI_LOG_E(BT_SERVICE_TAG, "BT App start failed");

View File

@ -9,12 +9,6 @@ extern "C" {
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
}
#endif

View File

@ -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;
}

View File

@ -11,6 +11,7 @@
#include <dialogs/dialogs.h>
#include <power/power_service/power.h>
#include <applications/rpc/rpc.h>
#include "../bt_settings.h"
@ -38,4 +39,7 @@ struct Bt {
DialogsApp* dialogs;
DialogMessage* dialog_message;
Power* power;
Rpc* rpc;
RpcSession* rpc_session;
osSemaphoreId_t rpc_sem;
};

View File

@ -1,11 +1,16 @@
/**
* @file cli.h
* Cli API
*/
#pragma once
#include <m-string.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <m-string.h>
typedef enum {
CliSymbolAsciiSOH = 0x01,
CliSymbolAsciiETX = 0x03,
@ -21,30 +26,29 @@ typedef enum {
} CliSymbols;
typedef enum {
CliCommandFlagDefault = 0, /** Default, loader lock is used */
CliCommandFlagDefault = 0, /**< Default, loader lock is used */
CliCommandFlagParallelSafe =
(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 */
(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 */
} CliCommandFlag;
/* Cli type
* Anonymous structure. Use cli_i.h if you need to go deeper.
*/
/** Cli type anonymous structure */
typedef struct Cli Cli;
/* Cli callback function pointer.
* Implement this interface and use add_cli_command
* @param args - string with what was passed after command
* @param context - pointer to whatever you gave us on cli_add_command
/** Cli callback function pointer. Implement this interface and use
* add_cli_command
* @param args string with what was passed after command
* @param context pointer to whatever you gave us on cli_add_command
*/
typedef void (*CliCallback)(Cli* cli, string_t args, void* context);
/* Add cli command
* Registers you command callback
* @param cli - pointer to cli instance
* @param name - command name
* @param callback - callback function
* @param context - pointer to whatever we need to pass to callback
/** Add cli command Registers you command callback
*
* @param cli pointer to cli instance
* @param name command name
* @param flags CliCommandFlag
* @param callback callback function
* @param context pointer to whatever we need to pass to callback
*/
void cli_add_command(
Cli* cli,
@ -53,51 +57,56 @@ void cli_add_command(
CliCallback callback,
void* context);
/* Print unified cmd usage tip
* @param cmd - cmd name
* @param usage - usage tip
* @param arg - arg passed by user
/** Print unified cmd usage tip
*
* @param cmd cmd name
* @param usage usage tip
* @param arg arg passed by user
*/
void cli_print_usage(const char* cmd, const char* usage, const char* arg);
/* Delete cli command
* @param cli - pointer to cli instance
* @param name - command name
/** Delete cli command
*
* @param cli pointer to cli instance
* @param name command name
*/
void cli_delete_command(Cli* cli, const char* name);
/* Read from terminal
* Do it only from inside of cli call.
* @param cli - Cli instance
* @param buffer - pointer to buffer
* @param size - size of buffer in bytes
* @return bytes written
/** Read from terminal Do it only from inside of cli call.
*
* @param cli Cli instance
* @param buffer pointer to buffer
* @param size size of buffer in bytes
*
* @return bytes written
*/
size_t cli_read(Cli* cli, uint8_t* buffer, size_t size);
/* Not blocking check for interrupt command received
* @param cli - Cli instance
/** Not blocking check for interrupt command received
*
* @param cli Cli instance
*
* @return true if received
*/
bool cli_cmd_interrupt_received(Cli* cli);
/* Write to terminal
* Do it only from inside of cli call.
* @param cli - Cli instance
* @param buffer - pointer to buffer
* @param size - size of buffer in bytes
* @return bytes written
/** Write to terminal Do it only from inside of cli call.
*
* @param cli Cli instance
* @param buffer pointer to buffer
* @param size size of buffer in bytes
*/
void cli_write(Cli* cli, const uint8_t* buffer, size_t size);
/* Read character
* @param cli - Cli instance
* @return char
/** Read character
*
* @param cli Cli instance
*
* @return char
*/
char cli_getc(Cli* cli);
/* New line
* Send new ine sequence
/** New line Send new ine sequence
*/
void cli_nl();

View File

@ -7,14 +7,47 @@
#include <notification/notification-messages.h>
#include <shci.h>
#define ENCLAVE_SIGNATURE_KEY_SLOT 1
#define ENCLAVE_SIGNATURE_KEY_SLOTS 10
#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_input[ENCLAVE_SIGNATURE_SIZE] =
{0xdc, 0x76, 0x15, 0x1e, 0x69, 0xe8, 0xdc, 0xd3, 0x4a, 0x71, 0x0b, 0x42, 0x71, 0xe0, 0xa9, 0x78};
static const uint8_t enclave_signature_expected[ENCLAVE_SIGNATURE_SIZE] =
{0x1b, 0xb3, 0xcf, 0x16, 0xc, 0x27, 0xf7, 0xf2, 0xf0, 0x7e, 0x5f, 0xbe, 0xfe, 0x89, 0x52, 0xe1};
static const uint8_t enclave_signature_iv[ENCLAVE_SIGNATURE_KEY_SLOTS][16] = {
{0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a},
{0x38, 0xe6, 0x6a, 0x90, 0x5e, 0x5b, 0x8a, 0xa6, 0x70, 0x30, 0x04, 0x72, 0xc2, 0x42, 0xea, 0xaf},
{0x73, 0xd5, 0x8e, 0xfb, 0x0f, 0x4b, 0xa9, 0x79, 0x0f, 0xde, 0x0e, 0x53, 0x44, 0x7d, 0xaa, 0xfd},
{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
@ -99,15 +132,23 @@ void cli_command_device_info(Cli* cli, string_t args, void* context) {
// Signature verification
uint8_t buffer[ENCLAVE_SIGNATURE_SIZE];
bool enclave_valid = false;
if(furi_hal_crypto_store_load_key(ENCLAVE_SIGNATURE_KEY_SLOT, enclave_signature_iv)) {
if(furi_hal_crypto_encrypt(enclave_signature_input, buffer, ENCLAVE_SIGNATURE_SIZE)) {
enclave_valid =
memcmp(buffer, enclave_signature_expected, ENCLAVE_SIGNATURE_SIZE) == 0;
size_t enclave_valid_keys = 0;
for(size_t key_slot = 0; key_slot < ENCLAVE_SIGNATURE_KEY_SLOTS; key_slot++) {
if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) {
if(furi_hal_crypto_encrypt(
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 {
printf("radio_alive : false\r\n");
}

View 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;
}

View 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;
}

View 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;
}

View File

@ -40,7 +40,7 @@ Desktop* desktop_alloc() {
desktop->locked_view = desktop_locked_alloc();
desktop->debug_view = desktop_debug_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(
desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view));
@ -61,7 +61,7 @@ Desktop* desktop_alloc() {
view_dispatcher_add_view(
desktop->view_dispatcher,
DesktopViewHwMismatch,
desktop_hw_mismatch_get_view(desktop->hw_mismatch_view));
popup_get_view(desktop->hw_mismatch_popup));
// Lock icon
desktop->lock_viewport = view_port_alloc();
@ -91,7 +91,7 @@ void desktop_free(Desktop* desktop) {
desktop_locked_free(desktop->locked_view);
desktop_debug_free(desktop->debug_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");
desktop->gui = NULL;

View File

@ -7,24 +7,21 @@
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/popup.h>
#include <gui/scene_manager.h>
#include <assets_icons.h>
#include <storage/storage.h>
#include "views/desktop_main.h"
#include "views/desktop_first_start.h"
#include "views/desktop_hw_mismatch.h"
#include "views/desktop_lock_menu.h"
#include "views/desktop_locked.h"
#include "views/desktop_debug.h"
#include "scenes/desktop_scene.h"
#include "helpers/desktop_animation.h"
#include "desktop/desktop_settings/desktop_settings.h"
#define HINT_TIMEOUT_L 2
#define HINT_TIMEOUT_H 11
typedef enum {
DesktopViewMain,
DesktopViewLockMenu,
@ -44,7 +41,7 @@ struct Desktop {
SceneManager* scene_manager;
DesktopFirstStartView* first_start_view;
DesktopHwMismatchView* hw_mismatch_view;
Popup* hw_mismatch_popup;
DesktopMainView* main_view;
DesktopLockMenuView* lock_menu;
DesktopLockedView* locked_view;

View 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];
}

View 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();

View File

@ -26,7 +26,7 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) {
switch(event.event) {
case DesktopDebugEventExit:
scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
dolphin_save(dolphin);
dolphin_flush(dolphin);
consumed = true;
break;
@ -43,7 +43,7 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) {
break;
case DesktopDebugEventSaveState:
dolphin_save(dolphin);
dolphin_flush(dolphin);
consumed = true;
break;

View File

@ -1,16 +1,26 @@
#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;
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) {
Desktop* desktop = (Desktop*)context;
desktop_hw_mismatch_set_callback(
desktop->hw_mismatch_view, desktop_scene_hw_mismatch_callback, desktop);
Popup* popup = desktop->hw_mismatch_popup;
char buffer[256]; // strange but smaller buffer not making it
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);
}
@ -20,7 +30,7 @@ bool desktop_scene_hw_mismatch_on_event(void* context, SceneManagerEvent event)
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopHwMismatchEventExit:
case HW_MISMATCH_BACK_EVENT:
scene_manager_previous_scene(desktop->scene_manager);
consumed = true;
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) {
// 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);
}

View File

@ -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_reset_door_pos(locked_view);
desktop_locked_update_hint_timeout(locked_view);
desktop_locked_set_dolphin_animation(locked_view);
view_port_enabled_set(desktop->lock_viewport, true);
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) {
Desktop* desktop = (Desktop*)context;
DesktopLockedView* locked_view = desktop->locked_view;
desktop_locked_reset_counter(desktop->locked_view);
osTimerStop(locked_view->timer);
osTimerStop(desktop->locked_view->timer);
}

View File

@ -39,6 +39,7 @@ void desktop_scene_main_on_enter(void* context) {
desktop_main_unlocked(desktop->main_view);
}
desktop_main_switch_dolphin_animation(desktop->main_view);
view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewMain);
}
@ -59,7 +60,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
break;
case DesktopMainEventOpenDebug:
scene_manager_next_scene(desktop->scene_manager, DesktopViewDebug);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneDebug);
consumed = true;
break;
@ -67,6 +68,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE);
consumed = true;
break;
case DesktopMainEventOpenFavorite:
desktop_settings_load(&desktop->settings);
desktop_switch_to_app(desktop, &FLIPPER_APPS[desktop->settings.favorite]);

View File

@ -2,8 +2,8 @@
#include "../desktop_i.h"
#include "desktop_debug.h"
#include "applications/dolphin/helpers/dolphin_state.h"
#include "applications/dolphin/dolphin.h"
#include "dolphin/helpers/dolphin_state.h"
#include "dolphin/dolphin.h"
void desktop_debug_set_callback(
DesktopDebugView* debug_view,
@ -21,7 +21,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
const Version* ver;
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_font(canvas, FontPrimary);
@ -56,7 +56,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
"%s [%s]",
version_get_version(ver),
version_get_builddate(ver));
canvas_draw_str(canvas, 5, 33, buffer);
canvas_draw_str(canvas, 5, 32, buffer);
snprintf(
buffer,
@ -68,16 +68,22 @@ void desktop_debug_render(Canvas* canvas, void* model) {
snprintf(
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 {
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);
snprintf(buffer, 64, "Icounter: %ld", m->icounter);
canvas_draw_str(canvas, 5, 30, buffer);
snprintf(buffer, 64, "Butthurt: %ld", m->butthurt);
canvas_draw_str(canvas, 5, 40, buffer);
snprintf(buffer, 64, "Icounter: %ld Butthurt %ld", m->icounter, m->butthurt);
canvas_draw_str(canvas, 5, 23, 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");
}
}
@ -146,11 +152,12 @@ void desktop_debug_free(DesktopDebugView* debug_view) {
void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) {
Dolphin* dolphin = furi_record_open("dolphin");
DolphinDeedWeight stats = dolphin_stats(dolphin);
DolphinStats stats = dolphin_stats(dolphin);
with_view_model(
debug_view->view, (DesktopDebugViewModel * model) {
model->icounter = stats.icounter;
model->butthurt = stats.butthurt;
model->timestamp = stats.timestamp;
return true;
});

View File

@ -6,6 +6,7 @@
#include <gui/elements.h>
#include <furi.h>
#include <storage/storage.h>
#include <time.h>
typedef enum {
DesktopDebugEventDeed,
@ -35,6 +36,7 @@ struct DesktopDebugView {
typedef struct {
uint32_t icounter;
uint32_t butthurt;
uint64_t timestamp;
DesktopViewStatsScreens screen;
} DesktopDebugViewModel;

View File

@ -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);
}

View File

@ -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);

View File

@ -29,7 +29,7 @@ static void lock_menu_callback(void* context, uint8_t index) {
default: // wip message
with_view_model(
lock_menu->view, (DesktopLockMenuViewModel * model) {
model->hint_timeout = HINT_TIMEOUT_L;
model->hint_timeout = HINT_TIMEOUT;
return true;
});
break;

View File

@ -6,6 +6,8 @@
#include <gui/elements.h>
#include <furi.h>
#define HINT_TIMEOUT 2
typedef enum {
DesktopLockMenuEventLock,
DesktopLockMenuEventUnlock,
@ -25,7 +27,6 @@ struct DesktopLockMenuView {
typedef struct {
uint8_t idx;
uint8_t hint_timeout;
bool locked;
} DesktopLockMenuViewModel;
void desktop_lock_menu_set_callback(

View File

@ -2,8 +2,6 @@
#include "../desktop_i.h"
#include "desktop_locked.h"
static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64};
void desktop_locked_set_callback(
DesktopLockedView* locked_view,
DesktopLockedViewCallback callback,
@ -20,13 +18,12 @@ void locked_view_timer_callback(void* context) {
}
// temporary locked screen animation managment
static void
desktop_scene_handler_set_scene(DesktopLockedView* locked_view, const Icon* icon_data) {
void desktop_locked_set_dolphin_animation(DesktopLockedView* locked_view) {
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
if(model->animation) icon_animation_free(model->animation);
model->animation = icon_animation_alloc(icon_data);
icon_animation_start(model->animation);
model->animation = icon_animation_alloc(desktop_get_icon());
view_tie_icon_animation(locked_view->view, model->animation);
return true;
});
}
@ -34,7 +31,7 @@ static void
void desktop_locked_update_hint_timeout(DesktopLockedView* locked_view) {
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
model->hint_timeout = HINT_TIMEOUT_H;
model->hint_expire_at = osKernelGetTickCount() + osKernelGetTickFreq();
return true;
});
}
@ -43,8 +40,8 @@ void desktop_locked_reset_door_pos(DesktopLockedView* locked_view) {
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
model->animation_seq_end = false;
model->door_left_x = -57;
model->door_right_x = 115;
model->door_left_x = DOOR_L_POS;
model->door_right_x = DOOR_R_POS;
return true;
});
}
@ -58,9 +55,12 @@ void desktop_locked_manage_redraw(DesktopLockedView* locked_view) {
animation_seq_end = model->animation_seq_end;
if(!model->animation_seq_end) {
model->door_left_x = CLAMP(model->door_left_x + 5, 0, -57);
model->door_right_x = CLAMP(model->door_right_x - 5, 115, 60);
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, DOOR_R_POS, DOOR_R_POS_MIN);
} else {
model->hint_expire_at = !model->hint_expire_at;
}
return true;
});
@ -75,14 +75,14 @@ void desktop_locked_reset_counter(DesktopLockedView* locked_view) {
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
model->hint_timeout = 0;
model->hint_expire_at = 0;
return true;
});
}
void desktop_locked_render(Canvas* canvas, void* model) {
DesktopLockedViewModel* m = model;
uint32_t now = osKernelGetTickCount();
canvas_clear(canvas);
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);
}
if(m->hint_timeout) {
m->hint_timeout--;
if(now < m->hint_expire_at) {
if(!m->animation_seq_end) {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30, "Locked");
} else {
canvas_set_font(canvas, FontSecondary);
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;
if(event->type == InputTypeShort) {
with_view_model(
locked_view->view, (DesktopLockedViewModel * model) {
model->hint_timeout = HINT_TIMEOUT_L;
return true;
});
desktop_locked_update_hint_timeout(locked_view);
if(event->key == InputKeyBack) {
uint32_t press_time = HAL_GetTick();
uint32_t press_time = osKernelGetTickCount();
// check if pressed sequentially
if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) {
locked_view->lock_lastpress = press_time;
@ -148,6 +142,26 @@ bool desktop_locked_input(InputEvent* event, void* context) {
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* locked_view = furi_alloc(sizeof(DesktopLockedView));
locked_view->view = view_alloc();
@ -158,8 +172,9 @@ DesktopLockedView* desktop_locked_alloc() {
view_set_context(locked_view->view, locked_view);
view_set_draw_callback(locked_view->view, (ViewDrawCallback)desktop_locked_render);
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;
}

View File

@ -6,9 +6,14 @@
#include <gui/elements.h>
#include <furi.h>
#define UNLOCK_RST_TIMEOUT 200
#define UNLOCK_RST_TIMEOUT 300
#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 {
DesktopLockedEventUnlock,
DesktopLockedEventUpdate,
@ -30,10 +35,11 @@ struct DesktopLockedView {
typedef struct {
IconAnimation* animation;
uint32_t hint_expire_at;
uint8_t scene_num;
int8_t door_left_x;
int8_t door_right_x;
uint8_t hint_timeout;
bool animation_seq_end;
} DesktopLockedViewModel;
@ -43,6 +49,7 @@ void desktop_locked_set_callback(
DesktopLockedViewCallback callback,
void* context);
void desktop_locked_set_dolphin_animation(DesktopLockedView* locked_view);
void desktop_locked_update_hint_timeout(DesktopLockedView* locked_view);
void desktop_locked_reset_counter(DesktopLockedView* locked_view);
void desktop_locked_reset_door_pos(DesktopLockedView* locked_view);

View File

@ -2,8 +2,6 @@
#include "../desktop_i.h"
#include "desktop_main.h"
static const Icon* idle_scenes[] = {&A_Wink_128x64, &A_WatchingTV_128x64};
void desktop_main_set_callback(
DesktopMainView* main_view,
DesktopMainViewCallback callback,
@ -17,30 +15,17 @@ void desktop_main_set_callback(
void desktop_main_reset_hint(DesktopMainView* main_view) {
with_view_model(
main_view->view, (DesktopMainViewModel * model) {
model->hint_timeout = 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);
model->hint_expire_at = 0;
return true;
});
}
void desktop_scene_handler_switch_scene(DesktopMainView* main_view) {
void desktop_main_switch_dolphin_animation(DesktopMainView* main_view) {
with_view_model(
main_view->view, (DesktopMainViewModel * model) {
if(icon_animation_is_last_frame(model->animation)) {
if(model->animation) icon_animation_free(model->animation);
model->animation = icon_animation_alloc(idle_scenes[model->scene_num]);
icon_animation_start(model->animation);
model->scene_num = random() % COUNT_OF(idle_scenes);
}
if(model->animation) icon_animation_free(model->animation);
model->animation = icon_animation_alloc(desktop_get_icon());
view_tie_icon_animation(main_view->view, model->animation);
return true;
});
}
@ -48,13 +33,13 @@ void desktop_scene_handler_switch_scene(DesktopMainView* main_view) {
void desktop_main_render(Canvas* canvas, void* model) {
canvas_clear(canvas);
DesktopMainViewModel* m = model;
uint32_t now = osKernelGetTickCount();
if(m->animation) {
canvas_draw_icon_animation(canvas, 0, -3, m->animation);
}
if(m->unlocked && m->hint_timeout) {
m->hint_timeout = CLAMP(m->hint_timeout - 1, 2, 0);
if(now < m->hint_expire_at) {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_framed(canvas, 42, 30, "Unlocked");
}
@ -87,6 +72,25 @@ bool desktop_main_input(InputEvent* event, void* context) {
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* main_view = furi_alloc(sizeof(DesktopMainView));
main_view->view = view_alloc();
@ -94,8 +98,8 @@ DesktopMainView* desktop_main_alloc() {
view_set_context(main_view->view, main_view);
view_set_draw_callback(main_view->view, (ViewDrawCallback)desktop_main_render);
view_set_input_callback(main_view->view, desktop_main_input);
desktop_scene_handler_set_scene(main_view, idle_scenes[random() % COUNT_OF(idle_scenes)]);
view_set_enter_callback(main_view->view, desktop_main_enter);
view_set_exit_callback(main_view->view, desktop_main_exit);
return main_view;
}
@ -109,8 +113,7 @@ void desktop_main_free(DesktopMainView* main_view) {
void desktop_main_unlocked(DesktopMainView* main_view) {
with_view_model(
main_view->view, (DesktopMainViewModel * model) {
model->unlocked = true;
model->hint_timeout = 2;
model->hint_expire_at = osKernelGetTickCount() + osKernelGetTickFreq();
return true;
});
}

View File

@ -28,8 +28,7 @@ struct DesktopMainView {
typedef struct {
IconAnimation* animation;
uint8_t scene_num;
uint8_t hint_timeout;
bool unlocked;
uint32_t hint_expire_at;
} DesktopMainViewModel;
void desktop_main_set_callback(
@ -40,4 +39,7 @@ void desktop_main_set_callback(
View* desktop_main_get_view(DesktopMainView* main_view);
DesktopMainView* desktop_main_alloc();
void desktop_main_free(DesktopMainView* main_view);
void desktop_main_switch_dolphin_animation(DesktopMainView* main_view);

View File

@ -60,3 +60,14 @@ DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage
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);
}

View File

@ -123,6 +123,13 @@ void dialog_message_set_buttons(
*/
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
}
#endif

View File

@ -1,34 +1,39 @@
#include "dolphin_i.h"
#include <furi.h>
bool dolphin_load(Dolphin* dolphin) {
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);
}
#define DOLPHIN_TIMEGATE 86400 // one day
#define DOLPHIN_LOCK_EVENT_FLAG (0x1)
void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) {
furi_assert(dolphin);
DolphinEvent event;
event.type = DolphinEventTypeDeed;
event.deed = deed;
furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK);
dolphin_event_send_async(dolphin, &event);
}
DolphinDeedWeight dolphin_stats(Dolphin* dolphin) {
DolphinDeedWeight stats;
stats.butthurt = dolphin_state_get_butthurt(dolphin->state);
stats.icounter = dolphin_state_get_icounter(dolphin->state);
DolphinStats dolphin_stats(Dolphin* dolphin) {
furi_assert(dolphin);
DolphinStats stats;
DolphinEvent event;
event.type = DolphinEventTypeStats;
event.stats = &stats;
dolphin_event_send_wait(dolphin, &event);
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 = furi_alloc(sizeof(Dolphin));
@ -47,27 +52,72 @@ void dolphin_free(Dolphin* 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) {
Dolphin* dolphin = dolphin_alloc();
furi_record_create("dolphin", dolphin);
dolphin_state_load(dolphin->state);
DolphinEvent event;
while(1) {
furi_check(osMessageQueueGet(dolphin->event_queue, &event, NULL, osWaitForever) == osOK);
switch(event.type) {
case DolphinEventTypeDeed:
dolphin_state_on_deed(dolphin->state, event.deed);
break;
case DolphinEventTypeSave:
if(osMessageQueueGet(dolphin->event_queue, &event, NULL, 60000) == osOK) {
if(event.type == DolphinEventTypeDeed) {
dolphin_state_on_deed(dolphin->state, event.deed);
} else if(event.type == DolphinEventTypeStats) {
event.stats->icounter = dolphin_state_get_icounter(dolphin->state);
event.stats->butthurt = dolphin_state_get_butthurt(dolphin->state);
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);
break;
default:
break;
}
}
dolphin_free(dolphin);
return 0;
}

View File

@ -4,27 +4,24 @@
typedef struct Dolphin Dolphin;
/* Load Dolphin state
* Thread safe
*/
typedef struct {
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.
* Thread safe
* Thread safe, async
*/
void dolphin_deed(Dolphin* dolphin, DolphinDeed deed);
/* Save Dolphin state (write to permanent memory)
* Thread safe
/** Retrieve dolphin stats
* Thread safe, blocking
*/
DolphinStats dolphin_stats(Dolphin* dolphin);
void dolphin_save(Dolphin* dolphin);
/* Retrieve dolphin's icounter and butthurt values
* Thread safe
/** Flush dolphin queue and save state
* Thread safe, blocking
*/
DolphinDeedWeight dolphin_stats(Dolphin* dolphin);
void dolphin_flush(Dolphin* dolphin);

View File

@ -8,14 +8,16 @@
typedef enum {
DolphinEventTypeDeed,
DolphinEventTypeSave,
DolphinEventTypeTick,
DolphinEventTypeStats,
DolphinEventTypeFlush,
} DolphinEventType;
typedef struct {
DolphinEventType type;
osEventFlagsId_t flag;
union {
DolphinDeed deed;
DolphinStats* stats;
};
} DolphinEvent;
@ -29,3 +31,9 @@ struct Dolphin {
Dolphin* dolphin_alloc();
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);

View File

@ -25,6 +25,7 @@ typedef struct {
uint32_t flags;
uint32_t icounter;
uint32_t butthurt;
uint64_t timestamp;
} DolphinStoreData;
typedef struct {
@ -35,6 +36,7 @@ typedef struct {
struct DolphinState {
Storage* fs_api;
DolphinStoreData data;
bool dirty;
};
DolphinState* dolphin_state_alloc() {
@ -49,8 +51,12 @@ void dolphin_state_free(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;
FURI_LOG_I("dolphin-state", "Saving state to \"%s\"", DOLPHIN_STORE_KEY);
// Calculate checksum
uint8_t* source = (uint8_t*)&dolphin_state->data;
uint8_t checksum = 0;
@ -88,7 +94,10 @@ bool dolphin_state_save(DolphinState* dolphin_state) {
storage_file_close(file);
storage_file_free(file);
dolphin_state->dirty = !save_result;
FURI_LOG_I("dolphin-state", "Saved");
return save_result;
}
@ -99,8 +108,12 @@ bool dolphin_state_load(DolphinState* dolphin_state) {
File* file = storage_file_alloc(dolphin_state->fs_api);
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));
if(bytes_count != sizeof(DolphinStore)) {
@ -109,12 +122,8 @@ bool dolphin_state_load(DolphinState* dolphin_state) {
}
if(!load_result) {
FURI_LOG_E(
"dolphin-state",
"Load failed. Storage returned: %s",
storage_file_get_error_desc(file));
FURI_LOG_E("dolphin-state", "DolphinStore size mismatch");
} else {
FURI_LOG_I("dolphin-state", "State loaded, verifying header");
if(store.header.magic == DOLPHIN_STORE_HEADER_MAGIC &&
store.header.version == DOLPHIN_STORE_HEADER_VERSION) {
FURI_LOG_I(
@ -142,7 +151,7 @@ bool dolphin_state_load(DolphinState* dolphin_state) {
} else {
FURI_LOG_E(
"dolphin-state",
"Magic(%d != %d) and Version(%d != %d) mismatch",
"Magic(%d != %d) or Version(%d != %d) mismatch",
store.header.magic,
DOLPHIN_STORE_HEADER_MAGIC,
store.header.version,
@ -153,6 +162,9 @@ bool dolphin_state_load(DolphinState* dolphin_state) {
storage_file_close(file);
storage_file_free(file);
dolphin_state->dirty = !load_result;
return load_result;
}
@ -160,13 +172,43 @@ void dolphin_state_clear(DolphinState* dolphin_state) {
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(&current);
}
void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) {
const DolphinDeedWeight* deed_weight = dolphin_deed_weight(deed);
int32_t icounter = dolphin_state->data.icounter + deed_weight->icounter;
int32_t butthurt = dolphin_state->data.butthurt;
if(icounter >= 0) {
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) {
@ -177,13 +219,14 @@ uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state) {
return dolphin_state->data.butthurt;
}
uint32_t dolphin_state_get_level(DolphinState* dolphin_state) {
return 0.5f +
sqrtf(1.0f + 8.0f * ((float)dolphin_state->data.icounter / DOLPHIN_LVL_THRESHOLD)) /
2.0f;
uint64_t dolphin_state_get_timestamp(DolphinState* dolphin_state) {
return dolphin_state->data.timestamp;
}
uint32_t dolphin_state_xp_to_levelup(DolphinState* dolphin_state, uint32_t level, bool remaining) {
return (DOLPHIN_LVL_THRESHOLD * level * (level + 1) / 2) -
(remaining ? dolphin_state->data.icounter : 0);
uint32_t dolphin_state_get_level(uint32_t icounter) {
return 0.5f + sqrtf(1.0f + 8.0f * ((float)icounter / DOLPHIN_LVL_THRESHOLD)) / 2.0f;
}
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);
}

View File

@ -3,6 +3,8 @@
#include "dolphin_deed.h"
#include <stdbool.h>
#include <stdint.h>
#include <rtc.h>
#include <time.h>
typedef struct DolphinState DolphinState;
@ -16,12 +18,18 @@ bool dolphin_state_load(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_butthurted(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_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);

View File

@ -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
View 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;
}

View File

@ -1,11 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void MX_USB_Device_Init();
#ifdef __cplusplus
}
#endif
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct GpioApp GpioApp;
#ifdef __cplusplus
}
#endif

View 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;

View 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;
}
}

View 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);

View 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,
};

View 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

View File

@ -0,0 +1,2 @@
ADD_SCENE(gpio, start, Start)
ADD_SCENE(gpio, test, Test)

View 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);
}

View 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);
}

View 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;
});
}

View 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);

View File

@ -110,11 +110,13 @@ void canvas_set_font(Canvas* canvas, Font font) {
furi_assert(canvas);
u8g2_SetFontMode(&canvas->fb, 1);
if(font == FontPrimary) {
u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tf);
u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr);
} else if(font == FontSecondary) {
u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr);
} 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 {
furi_crash(NULL);
}

View File

@ -1,3 +1,8 @@
/**
* @file canvas.h
* GUI: Canvas API
*/
#pragma once
#include <stdint.h>
@ -8,13 +13,16 @@
extern "C" {
#endif
/** Color enumeration */
typedef enum {
ColorWhite = 0x00,
ColorBlack = 0x01,
} Color;
typedef enum { FontPrimary, FontSecondary, FontKeyboard } Font;
/** Fonts enumeration */
typedef enum { FontPrimary, FontSecondary, FontKeyboard, FontBigNumbers } Font;
/** Alignment enumeration */
typedef enum {
AlignLeft,
AlignRight,
@ -23,59 +31,85 @@ typedef enum {
AlignCenter,
} Align;
/** Canvas Orientation */
typedef enum {
CanvasOrientationHorizontal,
CanvasOrientationVertical,
} CanvasOrientation;
/** Canvas anonymouse structure */
typedef struct Canvas Canvas;
/*
* Canvas width
* @return width in pixels.
/** Get Canvas width
*
* @param canvas Canvas instance
*
* @return width in pixels.
*/
uint8_t canvas_width(Canvas* canvas);
/*
* Canvas height
* @return height in pixels.
/** Get Canvas height
*
* @param canvas Canvas instance
*
* @return height in pixels.
*/
uint8_t canvas_height(Canvas* canvas);
/*
* Get current font height
* @return height in pixels.
/** Get current font height
*
* @param canvas Canvas instance
*
* @return height in pixels.
*/
uint8_t canvas_current_font_height(Canvas* canvas);
/*
* Clear canvas, clear rendering buffer
/** Clear canvas
*
* @param canvas Canvas instance
*/
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);
/*
* Invert drawing color
/** Invert drawing color
*
* @param canvas Canvas instance
*/
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);
/*
* 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);
/*
* Draw aligned string defined by x, y.
* Align calculated from position of baseline, string width and ascent (height of the glyphs above the baseline)
/** Draw aligned string defined by x, y.
*
* 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(
Canvas* canvas,
@ -85,22 +119,30 @@ void canvas_draw_str_aligned(
Align vertical,
const char* str);
/*
* Get string width
* @return width in pixels.
/** Get string width
*
* @param canvas Canvas instance
* @param str C-string
*
* @return width in pixels.
*/
uint16_t canvas_string_width(Canvas* canvas, const char* str);
/** 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);
/** Draw animation at position defined by x,y.
* @param canvas - canvas instance
* @param x - x coordinate
* @param y - y coordinate
* @param icon_animation - data pointer to IconAnimation
*
* @param canvas Canvas instance
* @param x x coordinate
* @param y y coordinate
* @param icon_animation IconAnimation instance
*/
void canvas_draw_icon_animation(
Canvas* canvas,
@ -109,15 +151,22 @@ void canvas_draw_icon_animation(
IconAnimation* icon_animation);
/** Draw icon at position defined by x,y.
* @param canvas - canvas instance
* @param x - x coordinate
* @param y - y coordinate
* @param icon - data pointer to Icon
*
* @param canvas Canvas instance
* @param x x coordinate
* @param y y coordinate
* @param icon Icon instance
*/
void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon);
/*
* Draw xbm icon of width, height at position defined by x,y.
/** Draw XBM bitmap
*
* @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(
Canvas* canvas,
@ -127,48 +176,86 @@ void canvas_draw_xbm(
uint8_t h,
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);
/*
* 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);
/*
* 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);
/*
* 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);
/*
* 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);
/*
* 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);
/*
* 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);
/*
* Set transparency mode
/** Set transparency mode
*
* @param canvas Canvas instance
* @param alpha transparency mode
*/
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(
Canvas* canvas,
@ -178,8 +265,14 @@ void canvas_draw_rframe(
uint8_t height,
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(
Canvas* canvas,

View File

@ -1,8 +1,15 @@
/**
* @file canvas_i.h
* GUI: internal Canvas API
*/
#pragma once
#include "canvas.h"
#include <u8g2.h>
/** Canvas structure
*/
struct Canvas {
u8g2_t fb;
CanvasOrientation orientation;
@ -12,40 +19,53 @@ struct Canvas {
uint8_t height;
};
/*
* Allocate memory and initialize canvas
/** Allocate memory and initialize canvas
*
* @return Canvas instance
*/
Canvas* canvas_init();
/*
* Free canvas memory
/** Free canvas memory
*
* @param canvas Canvas instance
*/
void canvas_free(Canvas* canvas);
/*
* Reset canvas drawing tools configuration
/** Reset canvas drawing tools configuration
*
* @param canvas Canvas instance
*/
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);
/*
* Get canvas buffer.
* @return pointer to buffer
/** Get canvas buffer.
*
* @param canvas Canvas instance
*
* @return pointer to buffer
*/
uint8_t* canvas_get_buffer(Canvas* canvas);
/*
* Get canvas buffer size.
* @return size of canvas in bytes
/** Get canvas buffer size.
*
* @param canvas Canvas instance
*
* @return size of canvas in bytes
*/
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(
Canvas* canvas,
@ -54,12 +74,17 @@ void canvas_frame_set(
uint8_t width,
uint8_t height);
/*
* Set canvas orientation
/** Set canvas orientation
*
* @param canvas Canvas instance
* @param orientation CanvasOrientation
*/
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);

View File

@ -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));
start = end + 1;
y += font_height;
} while(end);
} while(end && y < 64);
string_clear(str);
}

View File

@ -1,3 +1,11 @@
/**
* @file elements.h
* GUI: Elements API
*
* Canvas helpers and UI building blocks.
*
*/
#pragma once
#include <stdint.h>

View File

@ -335,7 +335,7 @@ void gui_remove_view_port(Gui* gui, ViewPort* view_port) {
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(view_port);
@ -361,7 +361,7 @@ void gui_send_view_port_front(Gui* gui, ViewPort* view_port) {
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(view_port);

View File

@ -1,3 +1,8 @@
/**
* @file gui.h
* GUI: main API
*/
#pragma once
#include "view_port.h"
@ -7,60 +12,74 @@
extern "C" {
#endif
/* Gui layers */
/** Gui layers */
typedef enum {
GuiLayerNone, /* Special layer for internal use only */
GuiLayerNone, /**< Special layer for internal use only */
GuiLayerStatusBarLeft, /* Status bar left-side layer, auto-layout */
GuiLayerStatusBarRight, /* Status bar right-side layer, auto-layout */
GuiLayerMain, /* Main layer, status bar is shown */
GuiLayerFullscreen, /* Fullscreen layer */
GuiLayerStatusBarLeft, /**< Status bar left-side layer, auto-layout */
GuiLayerStatusBarRight, /**< Status bar right-side layer, auto-layout */
GuiLayerMain, /**< Main layer, status bar is shown */
GuiLayerFullscreen, /**< Fullscreen layer */
GuiLayerMAX /* Don't use or move, special value */
GuiLayerMAX /**< Don't use or move, special value */
} GuiLayer;
/* Gui frame buffer callback */
/** Gui Canvas Commit Callback */
typedef void (*GuiCanvasCommitCallback)(uint8_t* data, size_t size, void* context);
typedef struct Gui Gui;
/*
* Add view_port to view_port tree
* @remarks thread safe
/** Add view_port to view_port tree
*
* @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);
/*
* Remove view_port from rendering tree
* @remarks thread safe
/** Remove view_port from rendering tree
*
* @remark thread safe
*
* @param gui Gui instance
* @param view_port ViewPort instance
*/
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
* @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
* @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
* This callback will be called upon Canvas commit
* Callback dispatched from GUI thread and is time critical
* @param gui - Gui instance
* @param callback - GuiCanvasCommitCallback
/** Set gui canvas commit callback
*
* This callback will be called upon Canvas commit Callback dispatched from GUI
* thread and is time critical
*
* @param gui Gui instance
* @param callback GuiCanvasCommitCallback
*/
void gui_set_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback);
/* Set gui canvas commit callback context
* @param gui - Gui instance
* @param context - pointer to context
/** Set gui canvas commit callback context
*
* @param gui Gui instance
* @param context pointer to context
*/
void gui_set_framebuffer_callback_context(Gui* gui, void* context);

View File

@ -1,3 +1,8 @@
/**
* @file gui_i.h
* GUI: main API internals
*/
#pragma once
#include "gui.h"
@ -31,6 +36,7 @@
ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST);
/** Gui structure */
struct Gui {
// Thread and lock
osThreadId_t thread;
@ -54,8 +60,9 @@ struct Gui {
ViewPort* gui_view_port_find_enabled(ViewPortArray_t array);
/* Update GUI, request redraw
* @param gui, Gui instance
/** Update GUI, request redraw
*
* @param gui Gui instance
*/
void gui_update(Gui* gui);
@ -67,4 +74,4 @@ void gui_unlock(Gui* gui);
void gui_cli_screen_stream_callback(uint8_t* data, size_t size, void* context);
void gui_cli_screen_stream(Cli* cli, string_t args, void* context);
void gui_cli_screen_stream(Cli* cli, string_t args, void* context);

View File

@ -1,3 +1,8 @@
/**
* @file icon.h
* GUI: Icon API
*/
#pragma once
#include <stdint.h>
@ -8,10 +13,28 @@ extern "C" {
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);
/** Get icon height
*
* @param[in] instance pointer to Icon data
*
* @return height in pixels
*/
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);
#ifdef __cplusplus

View File

@ -2,31 +2,32 @@
#include "icon_i.h"
#include <furi.h>
#include <timers.h>
IconAnimation* icon_animation_alloc(const Icon* icon) {
furi_assert(icon);
IconAnimation* instance = furi_alloc(sizeof(IconAnimation));
instance->icon = icon;
instance->timer = osTimerNew(icon_animation_timer_callback, osTimerPeriodic, instance, NULL);
return instance;
}
void icon_animation_free(IconAnimation* instance) {
furi_assert(instance);
furi_check(osTimerDelete(instance->timer) == osOK);
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);
if(instance->tick) {
uint32_t now = osKernelGetTickCount();
if(now < instance->tick) {
instance->tick = now;
icon_animation_next_frame(instance);
} else if(now - instance->tick > osKernelGetTickFreq() / instance->icon->frame_rate) {
instance->tick = now;
icon_animation_next_frame(instance);
}
}
instance->callback = callback;
instance->callback_context = context;
}
const uint8_t* icon_animation_get_data(IconAnimation* instance) {
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;
}
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) {
furi_assert(instance);
return instance->icon->width;
@ -45,33 +59,27 @@ uint8_t icon_animation_get_height(IconAnimation* instance) {
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) {
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) {
furi_assert(instance);
instance->tick = 0;
instance->frame = 0;
}
uint8_t icon_animation_get_current_frame(IconAnimation* instance) {
furi_assert(instance);
return instance->frame;
if(instance->animating) {
instance->animating = false;
furi_check(xTimerStop(instance->timer, 0) == pdPASS);
instance->frame = 0;
}
}
bool icon_animation_is_last_frame(IconAnimation* instance) {
furi_assert(instance);
return instance->icon->frame_count - instance->frame <= 1;
}
}

View File

@ -1,64 +1,87 @@
/**
* @file icon_animation.h
* GUI: IconAnimation API
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <assets_icons.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <assets_icons.h>
/** Icon Animation */
typedef struct IconAnimation IconAnimation;
/*
* Allocate icon animation instance with const icon data.
/** Icon Animation Callback. Used for update notification */
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
*
* @param[in] icon pointer to Icon data
*
* @return IconAnimation instance
*/
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);
/*
* Get icon animation width
/** Set IconAnimation update callback
*
* 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);
/*
* Get icon animation height
/** Get icon animation height
*
* @param instance IconAnimation instance
*
* @return height in pixels
*/
uint8_t icon_animation_get_height(IconAnimation* instance);
/*
* Check if icon is animated
*/
bool icon_animation_is_animated(IconAnimation* instance);
/*
* Check if icon animation is active
*/
bool icon_animation_is_animating(IconAnimation* instance);
/*
* Start icon animation
/** Start icon animation
*
* @param instance IconAnimation instance
*/
void icon_animation_start(IconAnimation* instance);
/*
* Stop icon animation
/** Stop icon animation
*
* @param instance IconAnimation instance
*/
void icon_animation_stop(IconAnimation* instance);
/*
* Get current frame
*/
uint8_t icon_animation_get_current_frame(IconAnimation* instance);
/*
* Returns true if current frame is a last one
/** Returns true if current frame is a last one
*
* @param instance IconAnimation instance
*
* @return true if last frame
*/
bool icon_animation_is_last_frame(IconAnimation* instance);

View File

@ -1,21 +1,39 @@
/**
* @file icon_animation_i.h
* GUI: internal IconAnimation API
*/
#pragma once
#include "icon_animation.h"
#include <stdint.h>
#include <furi.h>
struct IconAnimation {
const Icon* icon;
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);
/*
* Advance to next frame
/** Advance to next frame
*
* @param instance IconAnimation instance
*/
void icon_animation_next_frame(IconAnimation* instance);
/** IconAnimation timer callback
*
* @param context pointer to IconAnimation
*/
void icon_animation_timer_callback(void* context);

View File

@ -1,3 +1,8 @@
/**
* @file icon_i.h
* GUI: internal Icon API
*/
#include "icon.h"
struct Icon {

View File

@ -1,4 +1,10 @@
/**
* @file button_menu.h
* GUI: ButtonMenu view module API
*/
#pragma once
#include <stdint.h>
#include <gui/view.h>
@ -6,40 +12,48 @@
extern "C" {
#endif
/* ButtonMenu anonymous structure */
/** ButtonMenu anonymous structure */
typedef struct ButtonMenu ButtonMenu;
/** ButtonMenuItem anonymous structure */
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);
/* Type of button. Difference in drawing buttons. */
/** Type of button. Difference in drawing buttons. */
typedef enum {
ButtonMenuItemTypeCommon,
ButtonMenuItemTypeControl,
} ButtonMenuItemType;
/**
* @brief Get button menu view
* @param button_menu - ButtonMenu instance
* @return View instance that can be used for embedding
/** Get button menu view
*
* @param button_menu ButtonMenu instance
*
* @return View instance that can be used for embedding
*/
View* button_menu_get_view(ButtonMenu* button_menu);
/**
* @brief Clean button menu
* @param button_menu - ButtonMenu instance
/** Clean button menu
*
* @param button_menu ButtonMenu instance
*/
void button_menu_clean(ButtonMenu* button_menu);
/**
* @brief Add item to button menu instance
* @param button_menu - ButtonMenu instance
* @param label - text inside new button
* @param index - value to distinct between buttons inside ButtonMenuItemCallback
* @param type - type of button to create. Differ by button drawing.
* Control buttons have no frames, and have more squared borders.
* @return pointer to just-created item
/** Add item to button menu instance
*
* @param button_menu ButtonMenu instance
* @param label text inside new button
* @param index value to distinct between buttons inside
* ButtonMenuItemCallback
* @param callback The callback
* @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(
ButtonMenu* button_menu,
@ -49,29 +63,29 @@ ButtonMenuItem* button_menu_add_item(
ButtonMenuItemType type,
void* callback_context);
/**
* @brief Allocate and initialize new instance of ButtonMenu model
* @return just-created ButtonMenu model
/** Allocate and initialize new instance of ButtonMenu model
*
* @return just-created ButtonMenu model
*/
ButtonMenu* button_menu_alloc(void);
/**
* @brief Free ButtonMenu element
* @param button_menu - ButtonMenu instance
/** Free ButtonMenu element
*
* @param button_menu ButtonMenu instance
*/
void button_menu_free(ButtonMenu* button_menu);
/**
* @brief Set ButtonMenu header on top of canvas
* @param button_menu - ButtonMenu instance
* @param header - header on the top of button menu
/** Set ButtonMenu header on top of canvas
*
* @param button_menu ButtonMenu instance
* @param header header on the top of button menu
*/
void button_menu_set_header(ButtonMenu* button_menu, const char* header);
/**
* @brief Set selected item
* @param button_menu - ButtonMenu instance
* @param index - index of ButtonMenu to be selected
/** Set selected item
*
* @param button_menu ButtonMenu instance
* @param index index of ButtonMenu to be selected
*/
void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index);

View File

@ -1,4 +1,10 @@
/**
* @file button_panel.h
* GUI: ButtonPanel view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
@ -10,37 +16,39 @@ typedef struct ButtonPanel ButtonPanel;
/** Callback type to call for handling selecting button_panel items */
typedef void (*ButtonItemCallback)(void* context, uint32_t index);
/** Callback type for additional drawings above main button_panel screen */
typedef void (*ButtonPanelDrawCallback)(Canvas* canvas, void* _model);
/** Callback type to intercept input events of button_panel */
typedef bool (*ButtonPanelInputCallback)(InputEvent* event, void* context);
/** Allocate new button_panel module.
*
* @return just-created module
* @return ButtonPanel instance
*/
ButtonPanel* button_panel_alloc(void);
/** Free button_panel module.
*
* @param button_panel - module to free
* @param button_panel ButtonPanel instance
*/
void button_panel_free(ButtonPanel* button_panel);
/** 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);
/** Reserve space for adding items.
*
* One does not simply use button_panel_add_item() without this function.
* It should be allocated space for it first.
* One does not simply use button_panel_add_item() without this function. It
* should be allocated space for it first.
*
* @param button_panel - module to modify
* @param reserve_x - number of columns in button_panel
* @param reserve_y - number of rows in button_panel
* @param button_panel ButtonPanel instance
* @param reserve_x number of columns 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);
@ -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.
*
* @param button_panel - module
* @param index - value to pass to callback
* @param matrix_place_x - coordinates by x-axis on virtual grid, it
* is only used for naviagation
* @param matrix_place_y - coordinates by y-axis on virtual grid, it
* is only used for naviagation
* @param x - x-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_selected - name of the icon to draw when current
* element is selected
* @param callback - function to call when specific element is selected
* (pressed Ok on selected item)
* @param callback_context - context to pass to callback
* @param button_panel ButtonPanel instance
* @param index value to pass to callback
* @param matrix_place_x coordinates by x-axis on virtual grid, it
* is only used for naviagation
* @param matrix_place_y coordinates by y-axis on virtual grid, it
* is only used for naviagation
* @param x x-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_selected name of the icon to draw when current
* element is selected
* @param callback function to call when specific element is
* selected (pressed Ok on selected item)
* @param callback_context context to pass to callback
*/
void button_panel_add_item(
ButtonPanel* button_panel,
@ -77,17 +85,19 @@ void button_panel_add_item(
/** Get button_panel view.
*
* @param button_panel - module to get view from
* @return acquired view
* @param button_panel ButtonPanel instance
*
* @return acquired view
*/
View* button_panel_get_view(ButtonPanel* button_panel);
/** Add label to button_panel module.
*
* @param x - x-coordinate to place label
* @param y - y-coordinate to place label
* @param font - font to write label with
* @param label_str - string label to write
* @param button_panel ButtonPanel instance
* @param x x-coordinate to place label
* @param y y-coordinate to place label
* @param font font to write label with
* @param label_str string label to write
*/
void button_panel_add_label(
ButtonPanel* button_panel,
@ -101,9 +111,9 @@ void button_panel_add_label(
*
* Used to add popup drawings after main draw callback is done.
*
* @param button_panel - module to modify
* @param callback - callback function to set for draw event
* @param context - context to pass to callback
* @param button_panel ButtonPanel instance
* @param callback callback function to set for draw event
* @param context context to pass to callback
*/
void button_panel_set_popup_draw_callback(
ButtonPanel* button_panel,
@ -112,12 +122,12 @@ void button_panel_set_popup_draw_callback(
/** Set popup input callback for button_panel module.
*
* Used to add popup input callback. It will intercept all input
* events for current view.
* Used to add popup input callback. It will intercept all input events for
* current view.
*
* @param button_panel - module to modify
* @param callback - function to overwrite main input callbacks
* @param context - context to pass to callback
* @param button_panel ButtonPanel instance
* @param callback function to overwrite main input callbacks
* @param context context to pass to callback
*/
void button_panel_set_popup_input_callback(
ButtonPanel* button_panel,

View File

@ -1,59 +1,53 @@
/**
* @file byte_input.h
* GUI: ByteInput keyboard view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Byte input anonymous structure
*
*/
/** Byte input anonymous structure */
typedef struct ByteInput ByteInput;
/**
* @brief callback that is executed on save button press
*
*/
/** callback that is executed on save button press */
typedef void (*ByteInputCallback)(void* context);
/**
* @brief callback that is executed when byte buffer is changed
*
*/
/** callback that is executed when byte buffer is changed */
typedef void (*ByteChangedCallback)(void* context);
/**
* @brief Allocate and initialize byte input. This byte input is used to enter bytes.
*
* @return ByteInput instance pointer
/** Allocate and initialize byte input. This byte input is used to enter bytes.
*
* @return ByteInput instance pointer
*/
ByteInput* byte_input_alloc();
/**
* @brief Deinitialize and free byte input
*
* @param byte_input Byte input instance
/** Deinitialize and free byte input
*
* @param byte_input Byte input instance
*/
void byte_input_free(ByteInput* byte_input);
/**
* @brief Get byte input view
*
* @param byte_input byte input instance
* @return View instance that can be used for embedding
/** Get byte input view
*
* @param byte_input byte input instance
*
* @return View instance that can be used for embedding
*/
View* byte_input_get_view(ByteInput* byte_input);
/**
* @brief Set byte input result callback
*
* @param byte_input byte input instance
* @param input_callback input callback fn
* @param changed_callback changed callback fn
* @param callback_context callback context
* @param bytes buffer to use
* @param bytes_count buffer length
/** Set byte input result callback
*
* @param byte_input byte input instance
* @param input_callback input callback fn
* @param changed_callback changed callback fn
* @param callback_context callback context
* @param bytes buffer to use
* @param bytes_count buffer length
*/
void byte_input_set_result_callback(
ByteInput* byte_input,
@ -63,11 +57,10 @@ void byte_input_set_result_callback(
uint8_t* bytes,
uint8_t bytes_count);
/**
* @brief Set byte input header text
*
* @param byte_input byte input instance
* @param text text to be shown
/** Set byte input header text
*
* @param byte_input byte input instance
* @param text text to be shown
*/
void byte_input_set_header_text(ByteInput* byte_input, const char* text);

View File

@ -1,77 +1,95 @@
/**
* @file dialog.h
* GUI: Dialog view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Dialog anonymous structure */
/** Dialog anonymous structure */
typedef struct Dialog Dialog;
/* Dialog result */
/** Dialog result */
typedef enum {
DialogResultLeft,
DialogResultRight,
DialogResultBack,
} DialogResult;
/* Dialog result callback type
* @warning comes from GUI thread
/** Dialog result callback type
* @warning comes from GUI thread
*/
typedef void (*DialogResultCallback)(DialogResult result, void* context);
/* Allocate and initialize dialog
/** Allocate and initialize dialog
*
* This dialog used to ask simple questions like Yes/
*
* @return Dialog instance
*/
Dialog* dialog_alloc();
/* Deinitialize and free dialog
* @param dialog - Dialog instance
/** Deinitialize and free dialog
*
* @param dialog Dialog instance
*/
void dialog_free(Dialog* dialog);
/* Get dialog view
* @param dialog - Dialog instance
* @return View instance that can be used for embedding
/** Get dialog view
*
* @param dialog Dialog instance
*
* @return View instance that can be used for embedding
*/
View* dialog_get_view(Dialog* dialog);
/* Set dialog result callback
* @param dialog - Dialog instance
* @param callback - result callback function
/** Set dialog result callback
*
* @param dialog Dialog instance
* @param callback result callback function
*/
void dialog_set_result_callback(Dialog* dialog, DialogResultCallback callback);
/* Set dialog context
* @param dialog - Dialog instance
* @param context - context pointer, will be passed to result callback
/** Set dialog context
*
* @param dialog Dialog instance
* @param context context pointer, will be passed to result callback
*/
void dialog_set_context(Dialog* dialog, void* context);
/* Set dialog header text
* @param dialog - Dialog instance
* @param text - text to be shown
/** Set dialog header text
*
* @param dialog Dialog instance
* @param text text to be shown
*/
void dialog_set_header_text(Dialog* dialog, const char* text);
/* Set dialog text
* @param dialog - Dialog instance
* @param text - text to be shown
/** Set dialog text
*
* @param dialog Dialog instance
* @param text text to be shown
*/
void dialog_set_text(Dialog* dialog, const char* text);
/* Set left button text
* @param dialog - Dialog instance
* @param text - text to be shown
/** Set left button text
*
* @param dialog Dialog instance
* @param text text to be shown
*/
void dialog_set_left_button_text(Dialog* dialog, const char* text);
/* Set right button text
* @param dialog - Dialog instance
* @param text - text to be shown
/** Set right button text
*
* @param dialog Dialog instance
* @param text text to be shown
*/
void dialog_set_right_button_text(Dialog* dialog, const char* text);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,4 +1,10 @@
/**
* @file dialog_ex.h
* GUI: DialogEx view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
@ -21,40 +27,51 @@ typedef enum {
typedef void (*DialogExResultCallback)(DialogExResult result, void* context);
/** Allocate and initialize dialog
*
* This dialog used to ask simple questions
* @return DialogEx instance
*
* @return DialogEx instance
*/
DialogEx* dialog_ex_alloc();
/** Deinitialize and free dialog
* @param dialog - DialogEx instance
*
* @param dialog_ex DialogEx instance
*/
void dialog_ex_free(DialogEx* dialog_ex);
/** 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);
/** 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);
/** 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);
/** Set dialog header text
*
* If text is null, dialog header will not be rendered
* @param dialog - DialogEx instance
* @param text - text to be shown, can be multiline
* @param x, y - text position
* @param horizontal, vertical - text aligment
*
* @param dialog_ex DialogEx instance
* @param text text to be shown, can be multiline
* @param x x position
* @param y y position
* @param horizontal horizontal text aligment
* @param vertical vertical text aligment
*/
void dialog_ex_set_header(
DialogEx* dialog_ex,
@ -65,11 +82,15 @@ void dialog_ex_set_header(
Align vertical);
/** Set dialog text
*
* If text is null, dialog text will not be rendered
* @param dialog - DialogEx instance
* @param text - text to be shown, can be multiline
* @param x, y - text position
* @param horizontal, vertical - text aligment
*
* @param dialog_ex DialogEx instance
* @param text text to be shown, can be multiline
* @param x x position
* @param y y position
* @param horizontal horizontal text aligment
* @param vertical vertical text aligment
*/
void dialog_ex_set_text(
DialogEx* dialog_ex,
@ -80,36 +101,47 @@ void dialog_ex_set_text(
Align vertical);
/** Set dialog icon
*
* If x or y is negative, dialog icon will not be rendered
* @param dialog - DialogEx instance
* @param x, y - icon position
* @param name - icon to be shown
*
* @param dialog_ex DialogEx instance
* @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);
/** Set left button text
*
* 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);
/** Set center button text
*
* 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);
/** Set right button text
*
* 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);
/** Clean dialog
* @param dialog_ex DialogEx instance
*
* @param dialog_ex DialogEx instance
*/
void dialog_ex_clean(DialogEx* dialog_ex);

View File

@ -1,26 +1,38 @@
/**
* @file empty_screen.h
* GUI: EmptyScreen view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Empty screen anonymous structure */
/** Empty screen anonymous structure */
typedef struct EmptyScreen EmptyScreen;
/* Allocate and initialize empty screen
/** Allocate and initialize empty screen
*
* This empty screen used to ask simple questions like Yes/
*
* @return EmptyScreen instance
*/
EmptyScreen* empty_screen_alloc();
/* Deinitialize and free empty screen
* @param empty_screen - Empty screen instance
/** Deinitialize and free empty screen
*
* @param empty_screen Empty screen instance
*/
void empty_screen_free(EmptyScreen* empty_screen);
/* Get empty screen view
* @param empty_screen - Empty screen instance
* @return View instance that can be used for embedding
/** Get empty screen view
*
* @param empty_screen Empty screen instance
*
* @return View instance that can be used for embedding
*/
View* empty_screen_get_view(EmptyScreen* empty_screen);

View File

@ -1,4 +1,10 @@
/**
* @file file_select.h
* GUI: FileSelect view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus

54
applications/gui/modules/menu.c Executable file → Normal file
View File

@ -20,7 +20,7 @@ ARRAY_DEF(MenuItemArray, MenuItem, M_POD_OPLIST);
typedef struct {
MenuItemArray_t items;
uint8_t position;
size_t position;
} MenuModel;
static void menu_process_up(Menu* menu);
@ -32,7 +32,7 @@ static void menu_draw_callback(Canvas* canvas, void* _model) {
canvas_clear(canvas);
uint8_t position = model->position;
size_t position = model->position;
size_t items_count = MenuItemArray_size(model->items);
if(items_count) {
MenuItem* item;
@ -43,7 +43,6 @@ static void menu_draw_callback(Canvas* canvas, void* _model) {
item = MenuItemArray_get(model->items, shift_position);
if(item->icon) {
canvas_draw_icon_animation(canvas, 4, 3, item->icon);
icon_animation_stop(item->icon);
}
canvas_draw_str(canvas, 22, 14, item->label);
// Second line main
@ -52,7 +51,6 @@ static void menu_draw_callback(Canvas* canvas, void* _model) {
item = MenuItemArray_get(model->items, shift_position);
if(item->icon) {
canvas_draw_icon_animation(canvas, 4, 25, item->icon);
icon_animation_start(item->icon);
}
canvas_draw_str(canvas, 22, 36, item->label);
// Third line
@ -61,7 +59,6 @@ static void menu_draw_callback(Canvas* canvas, void* _model) {
item = MenuItemArray_get(model->items, shift_position);
if(item->icon) {
canvas_draw_icon_animation(canvas, 4, 47, item->icon);
icon_animation_stop(item->icon);
}
canvas_draw_str(canvas, 22, 58, item->label);
// Frame and scrollbar
@ -93,6 +90,30 @@ static bool menu_input_callback(InputEvent* event, void* context) {
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 = furi_alloc(sizeof(Menu));
menu->view = view_alloc(menu->view);
@ -100,6 +121,8 @@ Menu* menu_alloc() {
view_allocate_model(menu->view, ViewModelTypeLocking, sizeof(MenuModel));
view_set_draw_callback(menu->view, menu_draw_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(
menu->view, (MenuModel * model) {
@ -143,6 +166,7 @@ void menu_add_item(
item = MenuItemArray_push_new(model->items);
item->label = label;
item->icon = icon;
view_tie_icon_animation(menu->view, item->icon);
item->index = index;
item->callback = callback;
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) {
with_view_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) {
model->position--;
} else {
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;
});
}
@ -187,11 +221,21 @@ static void menu_process_up(Menu* menu) {
static void menu_process_down(Menu* menu) {
with_view_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) {
model->position++;
} else {
model->position = 0;
}
item = MenuItemArray_get(model->items, model->position);
if(item && item->icon) {
icon_animation_start(item->icon);
}
return true;
});
}

View File

@ -1,4 +1,10 @@
/**
* @file menu.h
* GUI: Menu view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
@ -7,31 +13,38 @@ extern "C" {
/** Menu anonymous structure */
typedef struct Menu Menu;
/** Menu Item Callback */
typedef void (*MenuItemCallback)(void* context, uint32_t index);
/** Menu allocation and initialization
* @return Menu instance
*
* @return Menu instance
*/
Menu* menu_alloc();
/** Free menu
* @param menu - Menu instance
*
* @param menu Menu instance
*/
void menu_free(Menu* menu);
/** Get Menu view
* @param menu - Menu instance
* @return View instance
*
* @param menu Menu instance
*
* @return View instance
*/
View* menu_get_view(Menu* menu);
/** Add item to menu
* @param menu - Menu instance
* @param label - menu item string label
* @param icon - IconAnimation instance
* @param index - menu item index
* @param callback - MenuItemCallback instance
* @param context - pointer to context
*
* @param menu Menu instance
* @param label menu item string label
* @param icon IconAnimation instance
* @param index menu item index
* @param callback MenuItemCallback instance
* @param context pointer to context
*/
void menu_add_item(
Menu* menu,
@ -42,14 +55,16 @@ void menu_add_item(
void* context);
/** Clean menu
* Note: this function does not free menu instance
* @param menu - Menu instance
* @note this function does not free menu instance
*
* @param menu Menu instance
*/
void menu_clean(Menu* menu);
/** 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);

View File

@ -1,52 +1,70 @@
/**
* @file popup.h
* GUI: Popup view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Popup anonymous structure */
/** Popup anonymous structure */
typedef struct Popup Popup;
/* Popup result callback type
* @warning comes from GUI thread
/** Popup result callback type
* @warning comes from GUI thread
*/
typedef void (*PopupCallback)(void* context);
/* Allocate and initialize popup
/** Allocate and initialize popup
*
* This popup used to ask simple questions like Yes/
*
* @return Popup instance
*/
Popup* popup_alloc();
/* Deinitialize and free popup
* @param popup - Popup instance
/** Deinitialize and free popup
*
* @param popup Popup instance
*/
void popup_free(Popup* popup);
/* Get popup view
* @param popup - Popup instance
* @return View instance that can be used for embedding
/** Get popup view
*
* @param popup Popup instance
*
* @return View instance that can be used for embedding
*/
View* popup_get_view(Popup* popup);
/* Set popup header text
* @param popup - Popup instance
* @param text - text to be shown
/** Set popup header text
*
* @param popup Popup instance
* @param callback PopupCallback
*/
void popup_set_callback(Popup* popup, PopupCallback callback);
/* Set popup context
* @param popup - Popup instance
* @param context - context pointer, will be passed to result callback
/** Set popup context
*
* @param popup Popup instance
* @param context context pointer, will be passed to result callback
*/
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
* @param popup - Popup instance
* @param text - text to be shown, can be multiline
* @param x, y - text position
* @param horizontal, vertical - text aligment
*
* @param popup Popup instance
* @param text text to be shown, can be multiline
* @param x x position
* @param y y position
* @param horizontal horizontal alignment
* @param vertical vertical aligment
*/
void popup_set_header(
Popup* popup,
@ -56,12 +74,16 @@ void popup_set_header(
Align horizontal,
Align vertical);
/* Set popup text
/** Set popup text
*
* If text is null, popup text will not be rendered
* @param popup - Popup instance
* @param text - text to be shown, can be multiline
* @param x, y - text position
* @param horizontal, vertical - text aligment
*
* @param popup Popup instance
* @param text text to be shown, can be multiline
* @param x x position
* @param y y position
* @param horizontal horizontal alignment
* @param vertical vertical aligment
*/
void popup_set_text(
Popup* popup,
@ -71,30 +93,36 @@ void popup_set_text(
Align horizontal,
Align vertical);
/* Set popup icon
/** Set popup icon
*
* If icon position is negative, popup icon will not be rendered
* @param popup - Popup instance
* @param x, y - icon position
* @param name - icon to be shown
*
* @param popup Popup instance
* @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);
/* Set popup timeout
* @param popup - Popup instance
* @param timeout_in_ms - popup timeout value in milliseconds
/** Set popup timeout
*
* @param popup Popup instance
* @param timeout_in_ms popup timeout value in milliseconds
*/
void popup_set_timeout(Popup* popup, uint32_t timeout_in_ms);
/* Enable popup timeout
* @param popup - Popup instance
/** Enable popup timeout
*
* @param popup Popup instance
*/
void popup_enable_timeout(Popup* popup);
/* Disable popup timeout
* @param popup - Popup instance
/** Disable popup timeout
*
* @param popup Popup instance
*/
void popup_disable_timeout(Popup* popup);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,40 +1,50 @@
/**
* @file submenu.h
* GUI: SubMenu view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Submenu anonymous structure */
/** Submenu anonymous structure */
typedef struct Submenu Submenu;
typedef void (*SubmenuItemCallback)(void* context, uint32_t index);
/**
* @brief Allocate and initialize submenu
/** Allocate and initialize submenu
*
* This submenu is used to select one option
*
* @return Submenu instance
*/
Submenu* submenu_alloc();
/**
* @brief Deinitialize and free submenu
* @param submenu - Submenu instance
/** Deinitialize and free submenu
*
* @param submenu Submenu instance
*/
void submenu_free(Submenu* submenu);
/**
* @brief Get submenu view
* @param submenu - Submenu instance
* @return View instance that can be used for embedding
/** Get submenu view
*
* @param submenu Submenu instance
*
* @return View instance that can be used for embedding
*/
View* submenu_get_view(Submenu* submenu);
/**
* @brief Add item to submenu
* @param submenu - Submenu instance
* @param label - menu item label
* @param index - menu item index, used for callback, may be the same with other items
* @param callback - menu item callback
* @param callback_context - menu item callback context
/** Add item to submenu
*
* @param submenu Submenu instance
* @param label menu item label
* @param index menu item index, used for callback, may be
* the same with other items
* @param callback menu item callback
* @param callback_context menu item callback context
*/
void submenu_add_item(
Submenu* submenu,
@ -43,23 +53,23 @@ void submenu_add_item(
SubmenuItemCallback callback,
void* callback_context);
/**
* @brief Remove all items from submenu
* @param submenu - Submenu instance
/** Remove all items from submenu
*
* @param submenu Submenu instance
*/
void submenu_clean(Submenu* submenu);
/**
* @brief Set submenu item selector
* @param submenu
* @param index
/** Set submenu item selector
*
* @param submenu Submenu instance
* @param index The index
*/
void submenu_set_selected_item(Submenu* submenu, uint32_t index);
/**
* @brief Set optional header for submenu
* @param submenu - submenu entity
* @param header - header to set
/** Set optional header for submenu
*
* @param submenu Submenu instance
* @param header header to set
*/
void submenu_set_header(Submenu* submenu, const char* header);

View File

@ -1,11 +1,17 @@
/**
* @file text_box.h
* GUI: TextBox view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/* TextBox anonymous structure */
/** TextBox anonymous structure */
typedef struct TextBox TextBox;
typedef void (*TextBoxExitCallback)(void* context);
@ -15,46 +21,56 @@ typedef enum {
} TextBoxFont;
/** Allocate and initialize text_box
*
* @return TextBox instance
*/
TextBox* text_box_alloc();
/** Deinitialize and free text_box
* @param text_box text_box instance
*
* @param text_box text_box instance
*/
void text_box_free(TextBox* text_box);
/** 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);
/** Clean text_box
* @param text_box TextBox instance
*
* @param text_box TextBox instance
*/
void text_box_clean(TextBox* 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);
/** 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);
/** 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);
/** 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);

View File

@ -1,44 +1,59 @@
/**
* @file text_input.h
* GUI: TextInput keybord view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Text input anonymous structure */
/** Text input anonymous structure */
typedef struct TextInput TextInput;
typedef void (*TextInputCallback)(void* context);
/** Allocate and initialize text input
/** Allocate and initialize text input
*
* This text input is used to enter string
* @return TextInput instance
*
* @return TextInput instance
*/
TextInput* text_input_alloc();
/** Deinitialize and free text input
* @param text_input - TextInput instance
*
* @param text_input TextInput instance
*/
void text_input_free(TextInput* text_input);
/** Clean text input view
* Note: this function does not free memory
* @param text_input - Text input instance
/** Clean text input view Note: this function does not free memory
*
* @param text_input Text input instance
*/
void text_input_clean(TextInput* text_input);
/** 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);
/** Set text input result callback
* @param text_input - TextInput instance
* @param callback - callback fn
* @param callback_context - callback context
* @param text_buffer - pointer to YOUR text buffer, that we going 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
*
* @param text_input TextInput instance
* @param callback callback fn
* @param callback_context callback context
* @param text_buffer pointer to YOUR text buffer, that we going
* 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(
TextInput* text_input,
@ -49,8 +64,9 @@ void text_input_set_result_callback(
bool clear_default_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);

29
applications/gui/modules/variable-item-list.c Normal file → Executable file
View File

@ -18,6 +18,8 @@ ARRAY_DEF(VariableItemArray, VariableItem, M_POD_OPLIST);
struct VariableItemList {
View* view;
VariableItemListEnterCallback callback;
void* context;
};
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_left(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) {
VariableItemListModel* model = _model;
@ -104,6 +107,9 @@ static bool variable_item_list_input_callback(InputEvent* event, void* context)
consumed = true;
variable_item_list_process_right(variable_item_list);
break;
case InputKeyOk:
variable_item_list_process_ok(variable_item_list);
break;
default:
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 = furi_alloc(sizeof(VariableItemList));
variable_item_list->view = view_alloc();
@ -280,6 +296,19 @@ VariableItem* variable_item_list_add(
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) {
item->current_value_index = current_value_index;
}

72
applications/gui/modules/variable-item-list.h Normal file → Executable file
View File

@ -1,4 +1,10 @@
/**
* @file variable-item-list.h
* GUI: VariableItemList view module API
*/
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
@ -8,28 +14,43 @@ extern "C" {
typedef struct VariableItemList VariableItemList;
typedef struct VariableItem VariableItem;
typedef void (*VariableItemChangeCallback)(VariableItem* item);
typedef void (*VariableItemListEnterCallback)(void* context, uint32_t index);
/** Allocate and initialize VariableItemList
* @return VariableItemList*
*
* @return VariableItemList*
*/
VariableItemList* variable_item_list_alloc();
/** 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);
/** Clear all elements from list
*
* @param variable_item_list VariableItemList instance
*/
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);
/** Add item to VariableItemList
* @param variable_item_list VariableItemList instance
* @param label item name
* @param values_count item values count
* @param change_callback called on value change in gui
* @param context item context
* @return VariableItem* item instance
*
* @param variable_item_list VariableItemList instance
* @param label item name
* @param values_count item values count
* @param change_callback called on value change in gui
* @param context item context
*
* @return VariableItem* item instance
*/
VariableItem* variable_item_list_add(
VariableItemList* variable_item_list,
@ -38,30 +59,47 @@ VariableItem* variable_item_list_add(
VariableItemChangeCallback change_callback,
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
* @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);
/** 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);
/** 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);
/** 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);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,38 +1,51 @@
/**
* @file widget.h
* GUI: Widget view module API
*/
#pragma once
#include "widget_elements/widget_element_i.h"
typedef struct Widget Widget;
typedef struct WidgetElement WidgetElement;
/** Allocate Widget that holds Widget Elements
* @return Widget instance
*
* @return Widget instance
*/
Widget* widget_alloc();
/** Free Widget
* @note this function free allocated Widget Elements
* @param widget Widget instance
* @note this function free allocated Widget Elements
*
* @param widget Widget instance
*/
void widget_free(Widget* widget);
/** Clear Widget
* @param widget Widget instance
*
* @param widget Widget instance
*/
void widget_clear(Widget* widget);
/** Get Widget view
* @param widget Widget instance
* @return View instance
*
* @param widget Widget instance
*
* @return View instance
*/
View* widget_get_view(Widget* widget);
/** Add Multi String Element
* @param widget Widget instance
* @param x - x coordinate
* @param y - y coordinate
* @param horizontal - Align instance
* @param vertical - Align instance
* @param font Font instance
*
* @param widget Widget instance
* @param x x coordinate
* @param y y coordinate
* @param horizontal Align instance
* @param vertical Align instance
* @param font Font instance
* @param[in] text The text
*/
void widget_add_string_multiline_element(
Widget* widget,
@ -44,12 +57,14 @@ void widget_add_string_multiline_element(
const char* text);
/** Add String Element
* @param widget Widget instance
* @param x - x coordinate
* @param y - y coordinate
* @param horizontal - Align instance
* @param vertical - Align instance
* @param font Font instance
*
* @param widget Widget instance
* @param x x coordinate
* @param y y coordinate
* @param horizontal Align instance
* @param vertical Align instance
* @param font Font instance
* @param[in] text The text
*/
void widget_add_string_element(
Widget* widget,
@ -61,11 +76,12 @@ void widget_add_string_element(
const char* text);
/** Add Button Element
* @param widget Widget instance
* @param button_type GuiButtonType instance
* @param text text on allocated button
* @param callback ButtonCallback instance
* @param context pointer to context
*
* @param widget Widget instance
* @param button_type GuiButtonType instance
* @param text text on allocated button
* @param callback ButtonCallback instance
* @param context pointer to context
*/
void widget_add_button_element(
Widget* widget,
@ -75,20 +91,22 @@ void widget_add_button_element(
void* context);
/** Add Icon Element
* @param widget Widget instance
* @param x top left x coordinate
* @param y top left y coordinate
* @param icon Icon instance
*
* @param widget Widget instance
* @param x top left x coordinate
* @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);
/** Add Frame Element
* @param widget Widget instance
* @param x top left x coordinate
* @param y top left y coordinate
* @param width frame width
* @param height frame height
* @param radius frame radius
*
* @param widget Widget instance
* @param x top left x coordinate
* @param y top left y coordinate
* @param width frame width
* @param height frame height
* @param radius frame radius
*/
void widget_add_frame_element(
Widget* widget,

View File

@ -1,3 +1,8 @@
/**
* @file widget_element_i.h
* GUI: internal Widget Element API
*/
#pragma once
#include <furi.h>
#include <gui/view.h>
@ -29,7 +34,7 @@ struct WidgetElement {
Widget* parent;
};
/* Create multi string element */
/** Create multi string element */
WidgetElement* widget_element_string_multiline_create(
uint8_t x,
uint8_t y,
@ -38,7 +43,7 @@ WidgetElement* widget_element_string_multiline_create(
Font font,
const char* text);
/* Create string element */
/** Create string element */
WidgetElement* widget_element_string_create(
uint8_t x,
uint8_t y,
@ -47,20 +52,20 @@ WidgetElement* widget_element_string_create(
Font font,
const char* text);
/* Create button element */
/** Create button element */
WidgetElement* widget_element_button_create(
GuiButtonType button_type,
const char* text,
ButtonCallback callback,
void* context);
/* Create icon element */
/** Create icon element */
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(
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t height,
uint8_t radius);
uint8_t radius);

View File

@ -1,14 +1,18 @@
/**
* @file scene_manager.h
* GUI: SceneManager API
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
/** Scene Manager events type
*/
/** Scene Manager events type */
typedef enum {
SceneManagerEventTypeCustom,
SceneManagerEventTypeBack,
@ -44,86 +48,110 @@ typedef struct {
typedef struct SceneManager SceneManager;
/** Set Scene state
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
* @param state Scene new state
*
* @param scene_manager SceneManager instance
* @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);
/** Get Scene state
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
* @return Scene state
*
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
*
* @return Scene state
*/
uint32_t scene_manager_get_scene_state(SceneManager* scene_manager, uint32_t scene_id);
/** Scene Manager allocation and configuration
*
* Scene Manager allocates all scenes internally
* @param app_scene_handlers SceneManagerHandlers instance
* @param context context to be set on Scene handlers calls
* @return SceneManager instance
*
* @param app_scene_handlers SceneManagerHandlers 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);
/** Free Scene Manager with allocated Scenes
* @param scene_manager SceneManager instance
*
* @param scene_manager SceneManager instance
*/
void scene_manager_free(SceneManager* scene_manager);
/** Custom event handler
*
* Calls Scene event handler with Custom event parameter
* @param scene_manager SceneManager instance
* @param custom_event Custom event code
* @return true if event was consumed, false otherwise
*
* @param scene_manager SceneManager instance
* @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);
/** Back event handler
*
* 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);
/** Tick event handler
*
* 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);
/** 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);
/** 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);
/** Search previous Scene
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
* @return true if previous scene was found, false otherwise
*
* @param scene_manager SceneManager instance
* @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);
/** Search and switch to previous Scene
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
* @return true if previous scene was found, false otherwise
*
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
*
* @return true if previous scene was found, false otherwise
*/
bool scene_manager_search_and_switch_to_previous_scene(
SceneManager* scene_manager,
uint32_t scene_id);
/** Clear Scene stack and switch to another Scene
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
* @return true if previous scene was found, false otherwise
*
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
*
* @return true if previous scene was found, false otherwise
*/
bool scene_manager_search_and_switch_to_another_scene(
SceneManager* scene_manager,

View File

@ -1,3 +1,8 @@
/**
* @file scene_manager_i.h
* GUI: internal SceneManager API
*/
#pragma once
#include "scene_manager.h"

View File

@ -12,6 +12,11 @@ void view_free(View* 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) {
furi_assert(view);
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) {
furi_assert(view);
if(view->model_type == ViewModelTypeLocking) {

View File

@ -1,6 +1,13 @@
/**
* @file view.h
* GUI: View API
*/
#pragma once
#include <input/input.h>
#include "icon_animation.h"
#include "canvas.h"
#include <stddef.h>
@ -10,9 +17,10 @@
extern "C" {
#endif
/* Hides drawing view_port */
/** Hides drawing view_port */
#define VIEW_NONE 0xFFFFFFFF
/* Ignore navigation event */
/** Ignore navigation event */
#define VIEW_IGNORE 0xFFFFFFFE
typedef enum {
@ -20,158 +28,181 @@ typedef enum {
ViewOrientationVertical,
} ViewOrientation;
/* View, anonymous type */
/** View, anonymous type */
typedef struct View View;
/* View Draw callback
* @param canvas, pointer to canvas
* @param view_model, pointer to context
* @warning called from GUI thread
/** View Draw callback
* @param canvas, pointer to canvas
* @param view_model, pointer to context
* @warning called from GUI thread
*/
typedef void (*ViewDrawCallback)(Canvas* canvas, void* model);
/* View Input callback
* @param event, pointer to input event data
* @param context, pointer to context
* @return true if event handled, false if event ignored
* @warning called from GUI thread
/** View Input callback
* @param event, pointer to input event data
* @param context, pointer to context
* @return true if event handled, false if event ignored
* @warning called from GUI thread
*/
typedef bool (*ViewInputCallback)(InputEvent* event, void* context);
/* View Custom callback
* @param event, number of custom event
* @param context, pointer to context
* @return true if event handled, false if event ignored
/** View Custom callback
* @param event, number of custom event
* @param context, pointer to context
* @return true if event handled, false if event ignored
*/
typedef bool (*ViewCustomCallback)(uint32_t event, void* context);
/* View navigation callback
* @param context, pointer to context
* @return next view id
* @warning called from GUI thread
/** View navigation callback
* @param context, pointer to context
* @return next view id
* @warning called from GUI thread
*/
typedef uint32_t (*ViewNavigationCallback)(void* context);
/* View callback
* @param context, pointer to context
* @warning called from GUI thread
/** View callback
* @param context, pointer to context
* @warning called from GUI thread
*/
typedef void (*ViewCallback)(void* context);
/* View Update Callback
* Called upon model change, need to be propagated to GUI throw ViewPort update
* @param view, pointer to view
* @param context, pointer to context
* @warning called from GUI thread
/** View Update Callback Called upon model change, need to be propagated to GUI
* throw ViewPort update
* @param view, pointer to view
* @param context, pointer to context
* @warning called from GUI thread
*/
typedef void (*ViewUpdateCallback)(View* view, void* context);
/* View model types */
/** View model types */
typedef enum {
/* Model is not allocated */
/** Model is not allocated */
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.
*/
ViewModelTypeLockFree,
/* Model access is guarded with mutex.
/** Model access is guarded with mutex.
* Locking gui thread.
*/
ViewModelTypeLocking,
} ViewModelType;
/* Allocate and init View
* @return pointer to View
/** Allocate and init View
* @return View instance
*/
View* view_alloc();
/* Free View
* @param pointer to View
/** Free View
*
* @param view instance
*/
void view_free(View* view);
/* Set View Draw callback
* @param view, pointer to View
* @param callback, draw callback
/** Tie IconAnimation with View
*
* @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);
/* Set View Input callback
* @param view, pointer to View
* @param callback, input callback
/** Set View Input callback
*
* @param view View instance
* @param callback input callback
*/
void view_set_input_callback(View* view, ViewInputCallback callback);
/* Set View Custom callback
* @param view, pointer to View
* @param callback, input callback
/** Set View Custom callback
*
* @param view View instance
* @param callback input callback
*/
void view_set_custom_callback(View* view, ViewCustomCallback callback);
/* Set Navigation Previous callback
* @param view, pointer to View
* @param callback, input callback
/** Set Navigation Previous callback
*
* @param view View instance
* @param callback input callback
*/
void view_set_previous_callback(View* view, ViewNavigationCallback callback);
/* Set Enter callback
* @param view, pointer to View
* @param callback, callback
/** Set Enter callback
*
* @param view View instance
* @param callback callback
*/
void view_set_enter_callback(View* view, ViewCallback callback);
/* Set Exit callback
* @param view, pointer to View
* @param callback, callback
/** Set Exit callback
*
* @param view View instance
* @param callback callback
*/
void view_set_exit_callback(View* view, ViewCallback callback);
/* Set Update callback
* @param view, pointer to View
* @param callback, callback
/** Set Update callback
*
* @param view View instance
* @param callback callback
*/
void view_set_update_callback(View* view, ViewUpdateCallback callback);
/* Set View Draw callback
* @param view, pointer to View
* @param context, context for callbacks
/** Set View Draw callback
*
* @param view View instance
* @param context context for callbacks
*/
void view_set_update_callback_context(View* view, void* context);
/* Set View Draw callback
* @param view, pointer to View
* @param context, context for callbacks
/** Set View Draw callback
*
* @param view View instance
* @param context context for callbacks
*/
void view_set_context(View* view, void* context);
/* Set View Orientation
* @param view, pointer to View
* @param orientation, either vertical or horizontal
/** Set View Orientation
*
* @param view View instance
* @param orientation either vertical or horizontal
*/
void view_set_orientation(View* view, ViewOrientation orientation);
/* Allocate view model.
* @param view, pointer to View
* @param type, View Model Type
* @param size, size
/** Allocate view model.
*
* @param view View instance
* @param type View Model Type
* @param size size
*/
void view_allocate_model(View* view, ViewModelType type, size_t size);
/* Free view model data memory.
* @param view, pointer to View
/** Free view model data memory.
*
* @param view View instance
*/
void view_free_model(View* view);
/* Get view model data
* @param view, pointer to View
* @return pointer to model data
* @warning Don't forget to commit model changes
/** Get view model data
*
* @param view View instance
*
* @return pointer to model data
* @warning Don't forget to commit model changes
*/
void* view_get_model(View* view);
/* Commit view model
* @param view, pointer to View
* @param update, true if you want to emit view update, false otherwise
/** Commit view model
*
* @param view View instance
* @param update true if you want to emit view update, false otherwise
*/
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); \
}
#else
/*
* With clause for view model
* @param view, View instance pointer
* @param function_body a (){} lambda declaration, executed within you parent function context
* @return true if you want to emit view update, false otherwise
/** With clause for view model
*
* @param view View instance pointer
* @param function_body a (){} lambda declaration, executed within you
* parent function context
*
* @return true if you want to emit view update, false otherwise
*/
#define with_view_model(view, function_body) \
{ \

View File

@ -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(
ViewDispatcher* view_dispatcher,
Gui* gui,

View File

@ -1,3 +1,8 @@
/**
* @file view_dispatcher.h
* GUI: ViewDispatcher API
*/
#pragma once
#include "view.h"
@ -8,8 +13,7 @@
extern "C" {
#endif
/** ViewDispatcher view_port placement
*/
/** ViewDispatcher view_port placement */
typedef enum {
ViewDispatcherTypeNone, /**< Special layer for internal use only */
ViewDispatcherTypeWindow, /**< Main view_port layer, status bar is shown */
@ -18,61 +22,70 @@ typedef enum {
typedef struct ViewDispatcher ViewDispatcher;
/** Prototype for custom event callback
*/
/** Prototype for custom event callback */
typedef bool (*ViewDispatcherCustomEventCallback)(void* context, uint32_t event);
/** Prototype for navigation event callback
*/
/** Prototype for navigation event callback */
typedef bool (*ViewDispatcherNavigationEventCallback)(void* context);
/** Prototype for tick event callback
*/
/** Prototype for tick event callback */
typedef void (*ViewDispatcherTickEventCallback)(void* context);
/** Allocate ViewDispatcher instance
* @return pointer to ViewDispatcher instance
*
* @return pointer to ViewDispatcher instance
*/
ViewDispatcher* view_dispatcher_alloc();
/** Free ViewDispatcher instance
* @param view_dispatcher pointer to ViewDispatcher
*
* @param view_dispatcher pointer to ViewDispatcher
*/
void view_dispatcher_free(ViewDispatcher* view_dispatcher);
/** 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);
/** 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);
/** Set custom event handler
*
* 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(
ViewDispatcher* view_dispatcher,
ViewDispatcherCustomEventCallback callback);
/** Set navigation event handler
*
* 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(
ViewDispatcher* view_dispatcher,
ViewDispatcherNavigationEventCallback callback);
/** Set tick event handler
* @param view_dispatcher ViewDispatcher instance
* @param callback ViewDispatcherTickEventCallback
* @param tick_period callback call period
*
* @param view_dispatcher ViewDispatcher instance
* @param callback ViewDispatcherTickEventCallback
* @param tick_period callback call period
*/
void view_dispatcher_set_tick_event_callback(
ViewDispatcher* view_dispatcher,
@ -80,46 +93,69 @@ void view_dispatcher_set_tick_event_callback(
uint32_t tick_period);
/** 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);
/** Run ViewDispatcher
*
* Use only after queue enabled
* @param view_dispatcher ViewDispatcher instance
*
* @param view_dispatcher ViewDispatcher instance
*/
void view_dispatcher_run(ViewDispatcher* view_dispatcher);
/** Stop ViewDispatcher
*
* Use only after queue enabled
* @param view_dispatcher ViewDispatcher instance
*
* @param view_dispatcher ViewDispatcher instance
*/
void view_dispatcher_stop(ViewDispatcher* view_dispatcher);
/** Add view to ViewDispatcher
* @param view_dispatcher, ViewDispatcher instance
* @param view_id View id to register
* @param view View instance
*
* @param view_dispatcher ViewDispatcher 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);
/** 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);
/** Switch to View
* @param view_dispatcher ViewDispatcher instance
* @param view_id View id to register
* @warning switching may be delayed till input events complementarity reached
*
* @param view_dispatcher ViewDispatcher instance
* @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);
/** 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
* @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(
ViewDispatcher* view_dispatcher,

View File

@ -1,3 +1,8 @@
/**
* @file view_dispatcher_i.h
* GUI: ViewDispatcher API
*/
#pragma once
#include <furi.h>
@ -41,23 +46,23 @@ typedef struct {
};
} ViewDispatcherMessage;
/* ViewPort Draw Callback */
/** ViewPort Draw Callback */
void view_dispatcher_draw_callback(Canvas* canvas, void* context);
/* ViewPort Input Callback */
/** ViewPort Input Callback */
void view_dispatcher_input_callback(InputEvent* event, void* context);
/* Input handler */
/** Input handler */
void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event);
/* Tick handler */
/** Tick handler */
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);
/* 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);
/* ViewDispatcher update event */
/** ViewDispatcher update event */
void view_dispatcher_update(View* view, void* context);

Some files were not shown because too many files have changed in this diff Show More