diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 000000000..b7eee574a
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,160 @@
+name: 'Build'
+
+on:
+ push:
+ branches:
+ - dev
+ - "release*"
+ tags:
+ - '*'
+ pull_request:
+
+env:
+ TARGETS: f7 f18
+ DEFAULT_TARGET: f7
+ FBT_TOOLCHAIN_PATH: /home/runner/work
+
+jobs:
+ main:
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Decontaminate previous build leftovers'
+ run: |
+ if [ -d .git ]; then
+ git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
+ fi
+
+ - name: 'Checkout code'
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: 'Get commit details'
+ id: names
+ run: |
+ if [[ ${{ github.event_name }} == 'pull_request' ]]; then
+ TYPE="pull"
+ elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
+ TYPE="tag"
+ else
+ TYPE="other"
+ fi
+ python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE"
+ echo random_hash=$(openssl rand -base64 40 | shasum -a 256 | awk '{print $1}') >> $GITHUB_OUTPUT
+ echo "event_type=$TYPE" >> $GITHUB_OUTPUT
+
+ - name: 'Make artifacts directory'
+ run: |
+ rm -rf artifacts
+ mkdir artifacts
+
+ - name: 'Bundle scripts'
+ run: |
+ tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug
+
+ - name: 'Build the firmware'
+ run: |
+ set -e
+ for TARGET in ${TARGETS}; do
+ TARGET="$(echo "${TARGET}" | sed 's/f//')"; \
+ ./fbt TARGET_HW=$TARGET copro_dist updater_package \
+ ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
+ done
+
+ - name: 'Move upload files'
+ run: |
+ set -e
+ for TARGET in ${TARGETS}; do
+ mv dist/${TARGET}-*/* artifacts/
+ done
+
+ - name: "Check for uncommitted changes"
+ run: |
+ git diff --exit-code
+
+ - name: 'Bundle resources'
+ run: |
+ tar czpf "artifacts/flipper-z-any-resources-${SUFFIX}.tgz" -C assets resources
+
+ - name: 'Bundle core2 firmware'
+ run: |
+ cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz"
+
+ - name: 'Updater artifact'
+ uses: actions/upload-artifact@v3
+ with:
+ name: updater
+ path: |
+ artifacts/f7-*
+
+ - name: 'Firmware artifact'
+ uses: actions/upload-artifact@v3
+ with:
+ name: firmware
+ path: |
+ artifacts
+
+ # - name: 'Find Previous Comment'
+ # if: ${{ github.event.pull_request }}
+ # uses: peter-evans/find-comment@v1
+ # id: fc
+ # with:
+ # issue-number: ${{ github.event.pull_request.number }}
+ # comment-author: 'github-actions[bot]'
+ # body-includes: 'Compiled firmware for commit'
+
+ # - name: Artifact info
+ # id: artifact-info
+ # uses: dawidd6/action-download-artifact@v2
+ # with:
+ # dry_run: true
+
+ # - name: 'Create or update comment'
+ # if: ${{ github.event.pull_request}}
+ # uses: peter-evans/create-or-update-comment@v1
+ # with:
+ # comment-id: ${{ steps.fc.outputs.comment-id }}
+ # issue-number: ${{ github.event.pull_request.number }}
+ # body: |
+ # **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:**
+ # - [📦 Update package](${{steps.artifact-info.outputs.artifacts[0].archive_download_url}})
+ # edit-mode: replace
+
+ compact:
+ if: ${{ !startsWith(github.ref, 'refs/tags') }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Decontaminate previous build leftovers'
+ run: |
+ if [ -d .git ]
+ then
+ git submodule status \
+ || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
+ fi
+
+ - name: 'Checkout code'
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ submodules: true
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: 'Get commit details'
+ run: |
+ if [[ ${{ github.event_name }} == 'pull_request' ]]; then
+ TYPE="pull"
+ elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
+ TYPE="tag"
+ else
+ TYPE="other"
+ fi
+ python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE"
+
+ - name: 'Build the firmware'
+ run: |
+ set -e
+ for TARGET in ${TARGETS}; do
+ TARGET="$(echo "${TARGET}" | sed 's/f//')"; \
+ ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 updater_package
+ done
diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/check_submodules.yml
new file mode 100644
index 000000000..d1a1a64c3
--- /dev/null
+++ b/.github/workflows/check_submodules.yml
@@ -0,0 +1,47 @@
+name: 'Check submodules branch'
+
+on:
+ push:
+ branches:
+ - dev
+ - "release*"
+ tags:
+ - '*'
+ pull_request:
+
+jobs:
+ check_protobuf:
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Decontaminate previous build leftovers'
+ run: |
+ if [ -d .git ]; then
+ git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
+ fi
+
+ - name: 'Checkout code'
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: 'Check protobuf branch'
+ run: |
+ git submodule update --init
+ SUB_PATH="assets/protobuf";
+ SUB_BRANCH="dev";
+ SUB_COMMITS_MIN=40;
+ cd "$SUB_PATH";
+ SUBMODULE_HASH="$(git rev-parse HEAD)";
+ BRANCHES=$(git branch -r --contains "$SUBMODULE_HASH");
+ COMMITS_IN_BRANCH="$(git rev-list --count dev)";
+ if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then
+ echo "name=fails::error" >> $GITHUB_OUTPUT
+ echo "::error::Error: Too low commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)";
+ exit 1;
+ fi
+ if ! grep -q "/$SUB_BRANCH" <<< "$BRANCHES"; then
+ echo "name=fails::error" >> $GITHUB_OUTPUT
+ echo "::error::Error: Submodule $SUB_PATH is not on branch $SUB_BRANCH";
+ exit 1;
+ fi
diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml
new file mode 100644
index 000000000..232e3c689
--- /dev/null
+++ b/.github/workflows/lint_c.yml
@@ -0,0 +1,46 @@
+name: 'Lint C/C++ with clang-format'
+
+on:
+ push:
+ branches:
+ - dev
+ - "release*"
+ tags:
+ - '*'
+ pull_request:
+
+env:
+ TARGETS: f7
+ SET_GH_OUTPUT: 1
+
+jobs:
+ lint_c_cpp:
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Decontaminate previous build leftovers'
+ run: |
+ if [ -d .git ]; then
+ git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
+ fi
+
+ - name: 'Checkout code'
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: 'Check code formatting'
+ id: syntax_check
+ run: ./fbt lint
+
+ - name: Report code formatting errors
+ if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request
+ uses: peter-evans/create-or-update-comment@v1
+ with:
+ issue-number: ${{ github.event.pull_request.number }}
+ body: |
+ Please fix following code formatting errors:
+ ```
+ ${{ steps.syntax_check.outputs.errors }}
+ ```
+ You might want to run `./fbt format` for an auto-fix.
diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml
new file mode 100644
index 000000000..65a8b6150
--- /dev/null
+++ b/.github/workflows/pvs_studio.yml
@@ -0,0 +1,93 @@
+name: 'Static C/C++ analysis with PVS-Studio'
+
+on:
+ push:
+ branches:
+ - dev
+ - "release*"
+ tags:
+ - '*'
+ pull_request:
+
+env:
+ TARGETS: f7
+ DEFAULT_TARGET: f7
+ FBT_TOOLCHAIN_PATH: /runner/_work
+
+jobs:
+ analyse_c_cpp:
+ if: ${{ !github.event.pull_request.head.repo.fork }}
+ runs-on: [self-hosted, FlipperZeroShell]
+ steps:
+ - name: 'Decontaminate previous build leftovers'
+ run: |
+ if [ -d .git ]; then
+ git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)"
+ fi
+
+ - name: 'Checkout code'
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: 'Get commit details'
+ id: names
+ run: |
+ if [[ ${{ github.event_name }} == 'pull_request' ]]; then
+ TYPE="pull"
+ elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
+ TYPE="tag"
+ else
+ TYPE="other"
+ fi
+ python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE"
+
+ - name: 'Supply PVS credentials'
+ run: |
+ pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }}
+
+ - name: 'Convert PVS-Studio output to html and detect warnings'
+ id: pvs-warn
+ run: |
+ WARNINGS=0
+ ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1
+ echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT
+
+ - name: 'Upload report'
+ if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }}
+ uses: prewk/s3-cp-action@v2
+ with:
+ aws_s3_endpoint: "${{ secrets.PVS_AWS_ENDPOINT }}"
+ aws_access_key_id: "${{ secrets.PVS_AWS_ACCESS_KEY }}"
+ aws_secret_access_key: "${{ secrets.PVS_AWS_SECRET_KEY }}"
+ source: "./build/f7-firmware-DC/pvsreport"
+ dest: "s3://${{ secrets.PVS_AWS_BUCKET }}/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/"
+ flags: "--recursive --acl public-read"
+
+ - name: 'Find Previous Comment'
+ if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }}
+ uses: peter-evans/find-comment@v2
+ id: fc
+ with:
+ issue-number: ${{ github.event.pull_request.number }}
+ comment-author: 'github-actions[bot]'
+ body-includes: 'PVS-Studio report for commit'
+
+ - name: 'Create or update comment'
+ if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }}
+ uses: peter-evans/create-or-update-comment@v1
+ with:
+ comment-id: ${{ steps.fc.outputs.comment-id }}
+ issue-number: ${{ github.event.pull_request.number }}
+ body: |
+ **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:**
+ - [Report](https://pvs.flipp.dev/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html)
+ edit-mode: replace
+
+ - name: 'Raise exception'
+ if: ${{ steps.pvs-warn.outputs.warnings != 0 }}
+ run: |
+ echo "Please fix all PVS warnings before merge"
+ exit 1
+
diff --git a/.gitignore b/.gitignore
index 6a3b80c65..025246faa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
*.swp
+*.swo
*.gdb_history
@@ -67,8 +68,15 @@ PVS-Studio.log
# Automate files, etc
automate.py
deployments/
-assets/dolphin/custom/
-assets/resources/dolphin_custom/
fbt_options.py
commitnotes.md
lib/STM32CubeWB
+
+# Asset packs
+assets/dolphin/custom/*
+!assets/dolphin/custom/NSFW/
+!assets/dolphin/custom/WatchDogs/
+!assets/dolphin/custom/ReadMe.md
+assets/resources/dolphin_custom/*
+!assets/resources/dolphin_custom/NSFW/
+!assets/resources/dolphin_custom/WatchDogs/
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index b53ffc24c..b5791a91e 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -11,5 +11,8 @@
"augustocdias.tasks-shell-input"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
- "unwantedRecommendations": []
-}
\ No newline at end of file
+ "unwantedRecommendations": [
+ "twxs.cmake",
+ "ms-vscode.cmake-tools"
+ ]
+}
diff --git a/CODING_STYLE.md b/CODING_STYLE.md
index 6c7d6d792..c62009eff 100644
--- a/CODING_STYLE.md
+++ b/CODING_STYLE.md
@@ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept.
## Naming
-### Type names are CamelCase
+### Type names are PascalCase
Examples:
diff --git a/ReadMe.md b/ReadMe.md
index 58123126d..027fb2fce 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -4,7 +4,7 @@
-[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Docs](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme#Known-bugs) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme)
+[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Docs](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme/issues?q=is%3Aissue+is%3Aopen+label%3Arelease-pending) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme)
-----
This firmware is a complete overhaul of the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), it also features lots of awesome code-bits from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware).
@@ -42,7 +42,7 @@ We wrote a powerful yet easy-to-use application specifically for our Firmware, t
Anim Speed: Speed in which the animations play
Cycle Anims: Duration of how long animations are played before switching to next
Unlock Anims: Custom setting just for NSFW fallback animations. Figure it out ;)
-Battery style: Classic Firmware battery style toggle, just at a more convenient place
+Battery Icon: Classic Firmware battery style toggle, just at a more convenient place
XP Level: Changes your Flippers level
SubGhz Extend: Allows you to extend the subghz range beyond what FZ devs planned
SubGhz Bypass: Allows you to bypass the subghz region locks of the Flipper
@@ -113,9 +113,9 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
- NSFW Animations tied to the level system. Read more above
- Folder handling for empty ones (Now indicate they are empty)
-- Jamming Files
- Custom subghz presets
- Multiple NFC protocols
+- Multiple Sub-Ghz protocols | Merged from Unleashed, thanks @xMasterX
- Subghz and IR signal replication via gpio | Credits to @ankris812
- Honda Keys (CVE-2022-27254) & Ford blockers
@@ -127,6 +127,7 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
- All Assets
- Tons of apps
+- File browser
- Massive compiler re-do
- About 1k files to speed things up a lot
- Applications to now use the new Locale setting
@@ -147,14 +148,6 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
- Tons of unused code from FAPs and system calls
```
-----
-
-Known Bugs:
-
-```txt
-- Nothing rn. Hopefully that wont change
-```
-
----
Install:
diff --git a/applications/ReadMe.md b/applications/ReadMe.md
index 3bd2aeb06..efc9afd86 100644
--- a/applications/ReadMe.md
+++ b/applications/ReadMe.md
@@ -25,7 +25,7 @@ Applications for factory testing the Flipper.
Applications for main Flipper menu.
- `archive` - Archive and file manager
-- `bad_usb` - Bad USB application
+- `bad_kb` - Bad KB application
- `fap_loader` - External applications loader
- `gpio` - GPIO application: includes USART bridge and GPIO control
- `ibutton` - iButton application, onewire keys and more
diff --git a/applications/debug/accessor/application.fam b/applications/debug/accessor/application.fam
index 93fc9bc3f..6b8472711 100644
--- a/applications/debug/accessor/application.fam
+++ b/applications/debug/accessor/application.fam
@@ -2,6 +2,7 @@ App(
appid="accessor",
name="Accessor",
apptype=FlipperAppType.DEBUG,
+ targets=["f7"],
entry_point="accessor_app",
cdefines=["APP_ACCESSOR"],
requires=["gui"],
diff --git a/applications/debug/battery_test_app/application.fam b/applications/debug/battery_test_app/application.fam
index b388445cc..f97d10279 100644
--- a/applications/debug/battery_test_app/application.fam
+++ b/applications/debug/battery_test_app/application.fam
@@ -11,4 +11,5 @@ App(
stack_size=1 * 1024,
order=130,
fap_category="Debug",
+ fap_libs=["assets"],
)
diff --git a/applications/debug/battery_test_app/views/battery_info.c b/applications/debug/battery_test_app/views/battery_info.c
new file mode 100644
index 000000000..5353a2e2a
--- /dev/null
+++ b/applications/debug/battery_test_app/views/battery_info.c
@@ -0,0 +1,148 @@
+#include "battery_info.h"
+#include
+#include
+#include
+
+#define LOW_CHARGE_THRESHOLD 10
+#define HIGH_DRAIN_CURRENT_THRESHOLD 100
+
+struct BatteryInfo {
+ View* view;
+};
+
+static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) {
+ canvas_draw_frame(canvas, x - 7, y + 7, 30, 13);
+ canvas_draw_icon(canvas, x, y, icon);
+ canvas_set_color(canvas, ColorWhite);
+ canvas_draw_box(canvas, x - 4, y + 16, 24, 6);
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val);
+};
+
+static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {
+ char emote[20] = {};
+ char header[20] = {};
+ char value[20] = {};
+
+ int32_t drain_current = data->gauge_current * (-1000);
+ uint32_t charge_current = data->gauge_current * 1000;
+
+ // Draw battery
+ canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28);
+ if(charge_current > 0) {
+ canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14);
+ } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) {
+ canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14);
+ } else if(data->charge < LOW_CHARGE_THRESHOLD) {
+ canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14);
+ } else {
+ canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14);
+ }
+
+ // Draw bubble
+ elements_bubble(canvas, 53, 0, 71, 39);
+
+ // Set text
+ if(charge_current > 0) {
+ snprintf(emote, sizeof(emote), "%s", "Yummy!");
+ snprintf(header, sizeof(header), "%s", "Charging at");
+ snprintf(
+ value,
+ sizeof(value),
+ "%lu.%luV %lumA",
+ (uint32_t)(data->vbus_voltage),
+ (uint32_t)(data->vbus_voltage * 10) % 10,
+ charge_current);
+ } else if(drain_current > 0) {
+ snprintf(
+ emote,
+ sizeof(emote),
+ "%s",
+ drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!");
+ snprintf(header, sizeof(header), "%s", "Consumption is");
+ snprintf(
+ value,
+ sizeof(value),
+ "%ld %s",
+ drain_current,
+ drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA");
+ } else if(drain_current != 0) {
+ snprintf(header, 20, "...");
+ } else if(data->charging_voltage < 4.2) {
+ // Non-default battery charging limit, mention it
+ snprintf(emote, sizeof(emote), "Charged!");
+ snprintf(header, sizeof(header), "Limited to");
+ snprintf(
+ value,
+ sizeof(value),
+ "%lu.%luV",
+ (uint32_t)(data->charging_voltage),
+ (uint32_t)(data->charging_voltage * 10) % 10);
+ } else {
+ snprintf(header, sizeof(header), "Charged!");
+ }
+
+ canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote);
+ canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header);
+ canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value);
+};
+
+static void battery_info_draw_callback(Canvas* canvas, void* context) {
+ furi_assert(context);
+ BatteryInfoModel* model = context;
+
+ canvas_clear(canvas);
+ canvas_set_color(canvas, ColorBlack);
+ draw_battery(canvas, model, 0, 5);
+
+ char batt_level[10];
+ char temperature[10];
+ char voltage[10];
+ char health[10];
+
+ snprintf(batt_level, sizeof(batt_level), "%lu%%", (uint32_t)model->charge);
+ snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature);
+ snprintf(
+ voltage,
+ sizeof(voltage),
+ "%lu.%01lu V",
+ (uint32_t)model->gauge_voltage,
+ (uint32_t)(model->gauge_voltage * 10) % 10UL);
+ snprintf(health, sizeof(health), "%d%%", model->health);
+
+ draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level);
+ draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature);
+ draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage);
+ draw_stat(canvas, 104, 42, &I_Health_16x16, health);
+}
+
+BatteryInfo* battery_info_alloc() {
+ BatteryInfo* battery_info = malloc(sizeof(BatteryInfo));
+ battery_info->view = view_alloc();
+ view_set_context(battery_info->view, battery_info);
+ view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel));
+ view_set_draw_callback(battery_info->view, battery_info_draw_callback);
+
+ return battery_info;
+}
+
+void battery_info_free(BatteryInfo* battery_info) {
+ furi_assert(battery_info);
+ view_free(battery_info->view);
+ free(battery_info);
+}
+
+View* battery_info_get_view(BatteryInfo* battery_info) {
+ furi_assert(battery_info);
+ return battery_info->view;
+}
+
+void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) {
+ furi_assert(battery_info);
+ furi_assert(data);
+ with_view_model(
+ battery_info->view,
+ BatteryInfoModel * model,
+ { memcpy(model, data, sizeof(BatteryInfoModel)); },
+ true);
+}
diff --git a/applications/debug/battery_test_app/views/battery_info.h b/applications/debug/battery_test_app/views/battery_info.h
new file mode 100644
index 000000000..7bfacf69e
--- /dev/null
+++ b/applications/debug/battery_test_app/views/battery_info.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include
+
+typedef struct BatteryInfo BatteryInfo;
+
+typedef struct {
+ float vbus_voltage;
+ float gauge_voltage;
+ float gauge_current;
+ float gauge_temperature;
+ float charging_voltage;
+ uint8_t charge;
+ uint8_t health;
+} BatteryInfoModel;
+
+BatteryInfo* battery_info_alloc();
+
+void battery_info_free(BatteryInfo* battery_info);
+
+View* battery_info_get_view(BatteryInfo* battery_info);
+
+void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data);
diff --git a/applications/debug/bt_debug_app/bt_debug_app.c b/applications/debug/bt_debug_app/bt_debug_app.c
index 405051a4a..bf13f6570 100644
--- a/applications/debug/bt_debug_app/bt_debug_app.c
+++ b/applications/debug/bt_debug_app/bt_debug_app.c
@@ -31,9 +31,6 @@ uint32_t bt_debug_start_view(void* context) {
BtDebugApp* bt_debug_app_alloc() {
BtDebugApp* app = malloc(sizeof(BtDebugApp));
- // Load settings
- bt_settings_load(&app->settings);
-
// Gui
app->gui = furi_record_open(RECORD_GUI);
@@ -105,13 +102,15 @@ int32_t bt_debug_app(void* p) {
}
BtDebugApp* app = bt_debug_app_alloc();
+ // Was bt active?
+ const bool was_active = furi_hal_bt_is_active();
// Stop advertising
furi_hal_bt_stop_advertising();
view_dispatcher_run(app->view_dispatcher);
// Restart advertising
- if(app->settings.enabled) {
+ if(was_active) {
furi_hal_bt_start_advertising();
}
bt_debug_app_free(app);
diff --git a/applications/debug/bt_debug_app/bt_debug_app.h b/applications/debug/bt_debug_app/bt_debug_app.h
index cd59e4d00..0ad94d7dd 100644
--- a/applications/debug/bt_debug_app/bt_debug_app.h
+++ b/applications/debug/bt_debug_app/bt_debug_app.h
@@ -4,15 +4,14 @@
#include
#include
#include
+#include
+
#include
-#include
#include "views/bt_carrier_test.h"
#include "views/bt_packet_test.h"
-#include
typedef struct {
- BtSettings settings;
Gui* gui;
ViewDispatcher* view_dispatcher;
Submenu* submenu;
diff --git a/applications/debug/bt_debug_app/views/bt_carrier_test.c b/applications/debug/bt_debug_app/views/bt_carrier_test.c
index c09aa3fdf..8e2240495 100644
--- a/applications/debug/bt_debug_app/views/bt_carrier_test.c
+++ b/applications/debug/bt_debug_app/views/bt_carrier_test.c
@@ -1,7 +1,7 @@
#include "bt_carrier_test.h"
#include "bt_test.h"
#include "bt_test_types.h"
-#include "furi_hal_bt.h"
+#include
struct BtCarrierTest {
BtTest* bt_test;
diff --git a/applications/debug/bt_debug_app/views/bt_packet_test.c b/applications/debug/bt_debug_app/views/bt_packet_test.c
index 7cbc3c5c5..8a56a3003 100644
--- a/applications/debug/bt_debug_app/views/bt_packet_test.c
+++ b/applications/debug/bt_debug_app/views/bt_packet_test.c
@@ -1,7 +1,7 @@
#include "bt_packet_test.h"
#include "bt_test.h"
#include "bt_test_types.h"
-#include "furi_hal_bt.h"
+#include
struct BtPacketTest {
BtTest* bt_test;
diff --git a/applications/debug/display_test/application.fam b/applications/debug/display_test/application.fam
index 4b40322fb..e8a00d2ae 100644
--- a/applications/debug/display_test/application.fam
+++ b/applications/debug/display_test/application.fam
@@ -5,6 +5,7 @@ App(
entry_point="display_test_app",
cdefines=["APP_DISPLAY_TEST"],
requires=["gui"],
+ fap_libs=["misc"],
stack_size=1 * 1024,
order=120,
fap_category="Debug",
diff --git a/applications/debug/display_test/display_test.c b/applications/debug/display_test/display_test.c
index 5b46d2b41..8065a23a1 100644
--- a/applications/debug/display_test/display_test.c
+++ b/applications/debug/display_test/display_test.c
@@ -91,7 +91,6 @@ static void display_test_reload_config(DisplayTest* instance) {
instance->config_contrast,
instance->config_regulation_ratio,
instance->config_bias);
- gui_update(instance->gui);
}
static void display_config_set_bias(VariableItem* item) {
diff --git a/applications/debug/example_custom_font/application.fam b/applications/debug/example_custom_font/application.fam
new file mode 100644
index 000000000..06c0a7f61
--- /dev/null
+++ b/applications/debug/example_custom_font/application.fam
@@ -0,0 +1,9 @@
+App(
+ appid="example_custom_font",
+ name="Example: custom font",
+ apptype=FlipperAppType.DEBUG,
+ entry_point="example_custom_font_main",
+ requires=["gui"],
+ stack_size=1 * 1024,
+ fap_category="Debug",
+)
diff --git a/applications/debug/example_custom_font/example_custom_font.c b/applications/debug/example_custom_font/example_custom_font.c
new file mode 100644
index 000000000..15eeb5f02
--- /dev/null
+++ b/applications/debug/example_custom_font/example_custom_font.c
@@ -0,0 +1,98 @@
+#include
+#include
+
+#include
+#include
+
+//This arrays contains the font itself. You can use any u8g2 font you want
+
+/*
+Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1
+Copyright:
+Glyphs: 95/203
+BBX Build Mode: 0
+*/
+const uint8_t u8g2_font_tom_thumb_4x6_tr[725] =
+ "_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310"
+ "\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1"
+ "&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244"
+ "\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60"
+ "\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227"
+ "\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227"
+ "\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32"
+ "d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3"
+ "\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0"
+ "E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12"
+ "I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227"
+ "\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310"
+ "Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$"
+ "W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U"
+ "V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^"
+ "\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7"
+ "\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35"
+ "\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T"
+ "\1l\7\227\310\310\326\0m\7\223\310
#include "file_browser_app_i.h"
-#include "gui/modules/file_browser.h"
-#include
-#include
+#include
+
+#include
#include
#include
+#include
+#include
static bool file_browser_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
@@ -48,7 +49,7 @@ FileBrowserApp* file_browser_app_alloc(char* arg) {
app->file_path = furi_string_alloc();
app->file_browser = file_browser_alloc(app->file_path);
- file_browser_configure(app->file_browser, "*", NULL, true, false, &I_badusb_10px, true);
+ file_browser_configure(app->file_browser, "*", NULL, true, false, &I_badkb_10px, true);
view_dispatcher_add_view(
app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget));
diff --git a/applications/debug/file_browser_test/icons/badusb_10px.png b/applications/debug/file_browser_test/icons/badkb_10px.png
similarity index 100%
rename from applications/debug/file_browser_test/icons/badusb_10px.png
rename to applications/debug/file_browser_test/icons/badkb_10px.png
diff --git a/applications/debug/file_browser_test/scenes/file_browser_scene_start.c b/applications/debug/file_browser_test/scenes/file_browser_scene_start.c
index 9eb26944f..9211ff3bb 100644
--- a/applications/debug/file_browser_test/scenes/file_browser_scene_start.c
+++ b/applications/debug/file_browser_test/scenes/file_browser_scene_start.c
@@ -19,7 +19,7 @@ bool file_browser_scene_start_on_event(void* context, SceneManagerEvent event) {
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
- furi_string_set(app->file_path, ANY_PATH("badusb/demo_windows.txt"));
+ furi_string_set(app->file_path, ANY_PATH("badkb/demo_windows.txt"));
scene_manager_next_scene(app->scene_manager, FileBrowserSceneBrowser);
consumed = true;
} else if(event.type == SceneManagerEventTypeTick) {
diff --git a/applications/debug/lfrfid_debug/application.fam b/applications/debug/lfrfid_debug/application.fam
index 6844f9291..323f77818 100644
--- a/applications/debug/lfrfid_debug/application.fam
+++ b/applications/debug/lfrfid_debug/application.fam
@@ -2,6 +2,7 @@ App(
appid="lfrfid_debug",
name="LF-RFID Debug",
apptype=FlipperAppType.DEBUG,
+ targets=["f7"],
entry_point="lfrfid_debug_app",
requires=[
"gui",
diff --git a/applications/debug/locale_test/application.fam b/applications/debug/locale_test/application.fam
index 32d065299..e46eeff51 100644
--- a/applications/debug/locale_test/application.fam
+++ b/applications/debug/locale_test/application.fam
@@ -8,4 +8,4 @@ App(
stack_size=2 * 1024,
order=70,
fap_category="Debug",
-)
\ No newline at end of file
+)
diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c
index eae12e6ee..367ca7a4f 100644
--- a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c
+++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c
@@ -44,7 +44,11 @@ bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEv
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RpcDebugAppCustomEventInputErrorCode) {
- rpc_system_app_set_error_code(app->rpc, (uint32_t)atol(app->text_store));
+ char* end;
+ int error_code = strtol(app->text_store, &end, 10);
+ if(!*end) {
+ rpc_system_app_set_error_code(app->rpc, error_code);
+ }
scene_manager_previous_scene(app->scene_manager);
consumed = true;
}
diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c
index e9e7b35f6..54bdd5909 100644
--- a/applications/debug/unit_tests/nfc/nfc_test.c
+++ b/applications/debug/unit_tests/nfc/nfc_test.c
@@ -348,13 +348,37 @@ static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) {
memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2);
MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data;
- // Check the manufacturer block (should be uid[uid_len] + 0xFF[rest])
+ // Check the manufacturer block (should be uid[uid_len] + BCC (for 4byte only) + SAK + ATQA0 + ATQA1 + 0xFF[rest])
uint8_t manufacturer_block[16] = {0};
memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16);
mu_assert(
memcmp(manufacturer_block, uid, uid_len) == 0,
"manufacturer_block uid doesn't match the file\r\n");
- for(uint8_t i = uid_len; i < 16; i++) {
+
+ uint8_t position = 0;
+ if(uid_len == 4) {
+ position = uid_len;
+
+ uint8_t bcc = 0;
+
+ for(int i = 0; i < uid_len; i++) {
+ bcc ^= uid[i];
+ }
+
+ mu_assert(manufacturer_block[position] == bcc, "manufacturer_block bcc assert failed\r\n");
+ } else {
+ position = uid_len - 1;
+ }
+
+ mu_assert(manufacturer_block[position + 1] == sak, "manufacturer_block sak assert failed\r\n");
+
+ mu_assert(
+ manufacturer_block[position + 2] == atqa[0], "manufacturer_block atqa0 assert failed\r\n");
+
+ mu_assert(
+ manufacturer_block[position + 3] == atqa[1], "manufacturer_block atqa1 assert failed\r\n");
+
+ for(uint8_t i = position + 4; i < 16; i++) {
mu_assert(
manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n");
}
@@ -466,6 +490,10 @@ static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) {
nfc_device_free(nfc_keys);
}
+MU_TEST(mf_mini_file_test) {
+ mf_classic_generator_test(4, MfClassicTypeMini);
+}
+
MU_TEST(mf_classic_1k_4b_file_test) {
mf_classic_generator_test(4, MfClassicType1k);
}
@@ -486,6 +514,7 @@ MU_TEST_SUITE(nfc) {
nfc_test_alloc();
MU_RUN_TEST(nfca_file_test);
+ MU_RUN_TEST(mf_mini_file_test);
MU_RUN_TEST(mf_classic_1k_4b_file_test);
MU_RUN_TEST(mf_classic_4k_4b_file_test);
MU_RUN_TEST(mf_classic_1k_7b_file_test);
diff --git a/applications/examples/example_thermo/README.md b/applications/examples/example_thermo/README.md
new file mode 100644
index 000000000..fa00264dc
--- /dev/null
+++ b/applications/examples/example_thermo/README.md
@@ -0,0 +1,44 @@
+# 1-Wire Thermometer
+This example application demonstrates the use of the 1-Wire library with a DS18B20 thermometer.
+It also covers basic GUI, input handling, threads and localisation.
+
+## Electrical connections
+Before launching the application, connect the sensor to Flipper's external GPIO according to the table below:
+| DS18B20 | Flipper |
+| :-----: | :-----: |
+| VDD | 9 |
+| GND | 18 |
+| DQ | 17 |
+
+*NOTE 1*: GND is also available on pins 8 and 11.
+
+*NOTE 2*: For any other pin than 17, connect an external 4.7k pull-up resistor to pin 9.
+
+## Launching the application
+In order to launch this demo, follow the steps below:
+1. Make sure your Flipper has an SD card installed.
+2. Connect your Flipper to the computer via a USB cable.
+3. Run `./fbt launch_app APPSRC=example_thermo` in your terminal emulator of choice.
+
+## Changing the data pin
+It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below:
+
+```c
+/* Possible GPIO pin choices:
+ - gpio_ext_pc0
+ - gpio_ext_pc1
+ - gpio_ext_pc3
+ - gpio_ext_pb2
+ - gpio_ext_pb3
+ - gpio_ext_pa4
+ - gpio_ext_pa6
+ - gpio_ext_pa7
+ - ibutton_gpio
+*/
+
+#define THERMO_GPIO_PIN (ibutton_gpio)
+```
+Do not forget about the external pull-up resistor as these pins do not have one built-in.
+
+With the changes been made, recompile and launch the application again.
+The on-screen text should reflect it by asking to connect the thermometer to another pin.
diff --git a/applications/examples/example_thermo/application.fam b/applications/examples/example_thermo/application.fam
new file mode 100644
index 000000000..b4a05c7f9
--- /dev/null
+++ b/applications/examples/example_thermo/application.fam
@@ -0,0 +1,10 @@
+App(
+ appid="example_thermo",
+ name="Example: Thermometer",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="example_thermo_main",
+ requires=["gui"],
+ stack_size=1 * 1024,
+ fap_icon="example_thermo_10px.png",
+ fap_category="Examples",
+)
diff --git a/applications/examples/example_thermo/example_thermo.c b/applications/examples/example_thermo/example_thermo.c
new file mode 100644
index 000000000..b3bc7cd99
--- /dev/null
+++ b/applications/examples/example_thermo/example_thermo.c
@@ -0,0 +1,356 @@
+/*
+ * This file contains an example application that reads and displays
+ * the temperature from a DS18B20 1-wire thermometer.
+ *
+ * It also covers basic GUI, input handling, threads and localisation.
+ *
+ * References:
+ * [1] DS18B20 Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf
+ */
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include
+#include
+
+#define UPDATE_PERIOD_MS 1000UL
+#define TEXT_STORE_SIZE 64U
+
+#define DS18B20_CMD_CONVERT 0x44U
+#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU
+
+#define DS18B20_CFG_RESOLUTION_POS 5U
+#define DS18B20_CFG_RESOLUTION_MASK 0x03U
+#define DS18B20_DECIMAL_PART_MASK 0x0fU
+
+#define DS18B20_SIGN_MASK 0xf0U
+
+/* Possible GPIO pin choices:
+ - gpio_ext_pc0
+ - gpio_ext_pc1
+ - gpio_ext_pc3
+ - gpio_ext_pb2
+ - gpio_ext_pb3
+ - gpio_ext_pa4
+ - gpio_ext_pa6
+ - gpio_ext_pa7
+ - ibutton_gpio
+*/
+
+#define THERMO_GPIO_PIN (ibutton_gpio)
+
+/* Flags which the reader thread responds to */
+typedef enum {
+ ReaderThreadFlagExit = 1,
+} ReaderThreadFlag;
+
+typedef union {
+ struct {
+ uint8_t temp_lsb; /* Least significant byte of the temperature */
+ uint8_t temp_msb; /* Most significant byte of the temperature */
+ uint8_t user_alarm_high; /* User register 1 (Temp high alarm) */
+ uint8_t user_alarm_low; /* User register 2 (Temp low alarm) */
+ uint8_t config; /* Configuration register */
+ uint8_t reserved[3]; /* Not used */
+ uint8_t crc; /* CRC checksum for error detection */
+ } fields;
+ uint8_t bytes[9];
+} DS18B20Scratchpad;
+
+/* Application context structure */
+typedef struct {
+ Gui* gui;
+ ViewPort* view_port;
+ FuriThread* reader_thread;
+ FuriMessageQueue* event_queue;
+ OneWireHost* onewire;
+ float temp_celsius;
+ bool has_device;
+} ExampleThermoContext;
+
+/*************** 1-Wire Communication and Processing *****************/
+
+/* Commands the thermometer to begin measuring the temperature. */
+static void example_thermo_request_temperature(ExampleThermoContext* context) {
+ OneWireHost* onewire = context->onewire;
+
+ /* All 1-wire transactions must happen in a critical section, i.e
+ not interrupted by other threads. */
+ FURI_CRITICAL_ENTER();
+
+ bool success = false;
+ do {
+ /* Each communication with a 1-wire device starts by a reset.
+ The functon will return true if a device responded with a presence pulse. */
+ if(!onewire_host_reset(onewire)) break;
+ /* After the reset, a ROM operation must follow.
+ If there is only one device connected, the "Skip ROM" command is most appropriate
+ (it can also be used to address all of the connected devices in some cases).*/
+ onewire_host_skip(onewire);
+ /* After the ROM operation, a device-specific command is issued.
+ In this case, it's a request to start measuring the temperature. */
+ onewire_host_write(onewire, DS18B20_CMD_CONVERT);
+
+ success = true;
+ } while(false);
+
+ context->has_device = success;
+
+ FURI_CRITICAL_EXIT();
+}
+
+/* Reads the measured temperature from the thermometer. */
+static void example_thermo_read_temperature(ExampleThermoContext* context) {
+ /* If there was no device detected, don't try to read the temperature */
+ if(!context->has_device) {
+ return;
+ }
+
+ OneWireHost* onewire = context->onewire;
+
+ /* All 1-wire transactions must happen in a critical section, i.e
+ not interrupted by other threads. */
+ FURI_CRITICAL_ENTER();
+
+ bool success = false;
+
+ do {
+ DS18B20Scratchpad buf;
+
+ /* Attempt reading the temperature 10 times before giving up */
+ size_t attempts_left = 10;
+ do {
+ /* Each communication with a 1-wire device starts by a reset.
+ The functon will return true if a device responded with a presence pulse. */
+ if(!onewire_host_reset(onewire)) continue;
+
+ /* After the reset, a ROM operation must follow.
+ If there is only one device connected, the "Skip ROM" command is most appropriate
+ (it can also be used to address all of the connected devices in some cases).*/
+ onewire_host_skip(onewire);
+
+ /* After the ROM operation, a device-specific command is issued.
+ This time, it will be the "Read Scratchpad" command which will
+ prepare the device's internal buffer memory for reading. */
+ onewire_host_write(onewire, DS18B20_CMD_READ_SCRATCHPAD);
+
+ /* The actual reading happens here. A total of 9 bytes is read. */
+ onewire_host_read_bytes(onewire, buf.bytes, sizeof(buf.bytes));
+
+ /* Calculate the checksum and compare it with one provided by the device. */
+ const uint8_t crc = maxim_crc8(buf.bytes, sizeof(buf.bytes) - 1, MAXIM_CRC8_INIT);
+
+ /* Checksums match, exit the loop */
+ if(crc == buf.fields.crc) break;
+
+ } while(--attempts_left);
+
+ if(attempts_left == 0) break;
+
+ /* Get the measurement resolution from the configuration register. (See [1] page 9) */
+ const uint8_t resolution_mode = (buf.fields.config >> DS18B20_CFG_RESOLUTION_POS) &
+ DS18B20_CFG_RESOLUTION_MASK;
+
+ /* Generate a mask for undefined bits in the decimal part. (See [1] page 6) */
+ const uint8_t decimal_mask =
+ (DS18B20_DECIMAL_PART_MASK << (DS18B20_CFG_RESOLUTION_MASK - resolution_mode)) &
+ DS18B20_DECIMAL_PART_MASK;
+
+ /* Get the integer and decimal part of the temperature (See [1] page 6) */
+ const uint8_t integer_part = (buf.fields.temp_msb << 4U) | (buf.fields.temp_lsb >> 4U);
+ const uint8_t decimal_part = buf.fields.temp_lsb & decimal_mask;
+
+ /* Calculate the sign of the temperature (See [1] page 6) */
+ const bool is_negative = (buf.fields.temp_msb & DS18B20_SIGN_MASK) != 0;
+
+ /* Combine the integer and decimal part together */
+ const float temp_celsius_abs = integer_part + decimal_part / 16.f;
+
+ /* Set the appropriate sign */
+ context->temp_celsius = is_negative ? -temp_celsius_abs : temp_celsius_abs;
+
+ success = true;
+ } while(false);
+
+ context->has_device = success;
+
+ FURI_CRITICAL_EXIT();
+}
+
+/* Periodically requests measurements and reads temperature. This function runs in a separare thread. */
+static int32_t example_thermo_reader_thread_callback(void* ctx) {
+ ExampleThermoContext* context = ctx;
+
+ for(;;) {
+ /* Tell the termometer to start measuring the temperature. The process may take up to 750ms. */
+ example_thermo_request_temperature(context);
+
+ /* Wait for the measurement to finish. At the same time wait for an exit signal. */
+ const uint32_t flags =
+ furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS);
+
+ /* If an exit signal was received, return from this thread. */
+ if(flags != (unsigned)FuriFlagErrorTimeout) break;
+
+ /* The measurement is now ready, read it from the termometer. */
+ example_thermo_read_temperature(context);
+ }
+
+ return 0;
+}
+
+/*************** GUI, Input and Main Loop *****************/
+
+/* Draw the GUI of the application. The screen is completely redrawn during each call. */
+static void example_thermo_draw_callback(Canvas* canvas, void* ctx) {
+ ExampleThermoContext* context = ctx;
+ char text_store[TEXT_STORE_SIZE];
+ const size_t middle_x = canvas_width(canvas) / 2U;
+
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(canvas, middle_x, 12, AlignCenter, AlignBottom, "Thermometer Demo");
+ canvas_draw_line(canvas, 0, 16, 128, 16);
+
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(
+ canvas, middle_x, 30, AlignCenter, AlignBottom, "Connnect thermometer");
+
+ snprintf(
+ text_store,
+ TEXT_STORE_SIZE,
+ "to GPIO pin %ld",
+ furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN));
+ canvas_draw_str_aligned(canvas, middle_x, 42, AlignCenter, AlignBottom, text_store);
+
+ canvas_set_font(canvas, FontKeyboard);
+
+ if(context->has_device) {
+ float temp;
+ char temp_units;
+
+ /* The applicaton is locale-aware.
+ Change Settings->System->Units to check it out. */
+ switch(locale_get_measurement_unit()) {
+ case LocaleMeasurementUnitsMetric:
+ temp = context->temp_celsius;
+ temp_units = 'C';
+ break;
+ case LocaleMeasurementUnitsImperial:
+ temp = locale_celsius_to_fahrenheit(context->temp_celsius);
+ temp_units = 'F';
+ break;
+ default:
+ furi_crash("Illegal measurement units");
+ }
+ /* If a reading is available, display it */
+ snprintf(text_store, TEXT_STORE_SIZE, "Temperature: %+.1f%c", (double)temp, temp_units);
+ } else {
+ /* Or show a message that no data is available */
+ strncpy(text_store, "-- No data --", TEXT_STORE_SIZE);
+ }
+
+ canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store);
+}
+
+/* This function is called from the GUI thread. All it does is put the event
+ into the application's queue so it can be processed later. */
+static void example_thermo_input_callback(InputEvent* event, void* ctx) {
+ ExampleThermoContext* context = ctx;
+ furi_message_queue_put(context->event_queue, event, FuriWaitForever);
+}
+
+/* Starts the reader thread and handles the input */
+static void example_thermo_run(ExampleThermoContext* context) {
+ /* Configure the hardware in host mode */
+ onewire_host_start(context->onewire);
+
+ /* Start the reader thread. It will talk to the thermometer in the background. */
+ furi_thread_start(context->reader_thread);
+
+ /* An endless loop which handles the input*/
+ for(bool is_running = true; is_running;) {
+ InputEvent event;
+ /* Wait for an input event. Input events come from the GUI thread via a callback. */
+ const FuriStatus status =
+ furi_message_queue_get(context->event_queue, &event, FuriWaitForever);
+
+ /* This application is only interested in short button presses. */
+ if((status != FuriStatusOk) || (event.type != InputTypeShort)) {
+ continue;
+ }
+
+ /* When the user presses the "Back" button, break the loop and exit the application. */
+ if(event.key == InputKeyBack) {
+ is_running = false;
+ }
+ }
+
+ /* Signal the reader thread to cease operation and exit */
+ furi_thread_flags_set(furi_thread_get_id(context->reader_thread), ReaderThreadFlagExit);
+
+ /* Wait for the reader thread to finish */
+ furi_thread_join(context->reader_thread);
+
+ /* Reset the hardware */
+ onewire_host_stop(context->onewire);
+}
+
+/******************** Initialisation & startup *****************************/
+
+/* Allocate the memory and initialise the variables */
+static ExampleThermoContext* example_thermo_context_alloc() {
+ ExampleThermoContext* context = malloc(sizeof(ExampleThermoContext));
+
+ context->view_port = view_port_alloc();
+ view_port_draw_callback_set(context->view_port, example_thermo_draw_callback, context);
+ view_port_input_callback_set(context->view_port, example_thermo_input_callback, context);
+
+ context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+
+ context->reader_thread = furi_thread_alloc();
+ furi_thread_set_stack_size(context->reader_thread, 1024U);
+ furi_thread_set_context(context->reader_thread, context);
+ furi_thread_set_callback(context->reader_thread, example_thermo_reader_thread_callback);
+
+ context->gui = furi_record_open(RECORD_GUI);
+ gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen);
+
+ context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN);
+
+ return context;
+}
+
+/* Release the unused resources and deallocate memory */
+static void example_thermo_context_free(ExampleThermoContext* context) {
+ view_port_enabled_set(context->view_port, false);
+ gui_remove_view_port(context->gui, context->view_port);
+
+ onewire_host_free(context->onewire);
+ furi_thread_free(context->reader_thread);
+ furi_message_queue_free(context->event_queue);
+ view_port_free(context->view_port);
+
+ furi_record_close(RECORD_GUI);
+}
+
+/* The application's entry point. Execution starts from here. */
+int32_t example_thermo_main(void* p) {
+ UNUSED(p);
+
+ /* Allocate all of the necessary structures */
+ ExampleThermoContext* context = example_thermo_context_alloc();
+
+ /* Start the applicaton's main loop. It won't return until the application was requested to exit. */
+ example_thermo_run(context);
+
+ /* Release all unneeded resources */
+ example_thermo_context_free(context);
+
+ return 0;
+}
diff --git a/applications/examples/example_thermo/example_thermo_10px.png b/applications/examples/example_thermo/example_thermo_10px.png
new file mode 100644
index 000000000..3d527f306
Binary files /dev/null and b/applications/examples/example_thermo/example_thermo_10px.png differ
diff --git a/applications/main/application.fam b/applications/main/application.fam
index 376af8c42..eefb801b3 100644
--- a/applications/main/application.fam
+++ b/applications/main/application.fam
@@ -9,7 +9,7 @@ App(
"lfrfid",
"nfc",
"subghz",
- "bad_usb",
+ "bad_kb",
"u2f",
"fap_loader",
"sub_playlist",
@@ -25,13 +25,13 @@ App(
apptype=FlipperAppType.METAPACKAGE,
provides=[
"gpio",
- #"ibutton",
+ # "ibutton",
"infrared",
"lfrfid",
"nfc",
"subghz",
- #"bad_usb",
- #"u2f",
+ # "bad_kb",
+ # "u2f",
"fap_loader",
"archive",
],
diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c
index f579a2beb..78b010a78 100644
--- a/applications/main/archive/helpers/archive_browser.c
+++ b/applications/main/archive/helpers/archive_browser.c
@@ -1,10 +1,11 @@
-#include
#include "archive_files.h"
#include "archive_apps.h"
#include "archive_browser.h"
+#include "../views/archive_browser_view.h"
+
#include
#include
-#include "gui/modules/file_browser_worker.h"
+#include
#include
#include
@@ -55,9 +56,14 @@ static void archive_list_load_cb(void* context, uint32_t list_load_offset) {
false);
}
-static void
- archive_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last) {
+static void archive_list_item_cb(
+ void* context,
+ FuriString* item_path,
+ uint32_t idx,
+ bool is_folder,
+ bool is_last) {
furi_assert(context);
+ UNUSED(idx);
ArchiveBrowserView* browser = (ArchiveBrowserView*)context;
if(!is_last) {
@@ -67,7 +73,9 @@ static void
browser->view,
ArchiveBrowserViewModel * model,
{
- files_array_sort(model->files);
+ if(model->item_cnt <= BROWSER_SORT_THRESHOLD) {
+ files_array_sort(model->files);
+ }
model->list_loading = false;
},
true);
@@ -456,10 +464,14 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
browser->last_tab_switch_dir = key;
- if(key == InputKeyLeft) {
- tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
- } else {
- tab = (tab + 1) % ArchiveTabTotal;
+ for(int i = 0; i < 2; i++) {
+ if(key == InputKeyLeft) {
+ tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
+ } else {
+ tab = (tab + 1) % ArchiveTabTotal;
+ }
+ if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue;
+ break;
}
browser->is_root = true;
diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h
index 5b13e98da..36da022da 100644
--- a/applications/main/archive/helpers/archive_browser.h
+++ b/applications/main/archive/helpers/archive_browser.h
@@ -14,9 +14,10 @@ static const char* tab_default_paths[] = {
[ArchiveTabSubGhz] = ANY_PATH("subghz"),
[ArchiveTabLFRFID] = ANY_PATH("lfrfid"),
[ArchiveTabInfrared] = ANY_PATH("infrared"),
- [ArchiveTabBadUsb] = ANY_PATH("badusb"),
+ [ArchiveTabBadKb] = ANY_PATH("badkb"),
[ArchiveTabU2f] = "/app:u2f",
[ArchiveTabApplications] = ANY_PATH("apps"),
+ [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX,
[ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX,
};
@@ -26,7 +27,7 @@ static const char* known_ext[] = {
[ArchiveFileTypeSubGhz] = ".sub",
[ArchiveFileTypeLFRFID] = ".rfid",
[ArchiveFileTypeInfrared] = ".ir",
- [ArchiveFileTypeBadUsb] = ".txt",
+ [ArchiveFileTypeBadKb] = ".txt",
[ArchiveFileTypeU2f] = "?",
[ArchiveFileTypeApplication] = ".fap",
[ArchiveFileTypeUpdateManifest] = ".fuf",
@@ -41,9 +42,10 @@ static const ArchiveFileTypeEnum known_type[] = {
[ArchiveTabSubGhz] = ArchiveFileTypeSubGhz,
[ArchiveTabLFRFID] = ArchiveFileTypeLFRFID,
[ArchiveTabInfrared] = ArchiveFileTypeInfrared,
- [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb,
+ [ArchiveTabBadKb] = ArchiveFileTypeBadKb,
[ArchiveTabU2f] = ArchiveFileTypeU2f,
[ArchiveTabApplications] = ArchiveFileTypeApplication,
+ [ArchiveTabInternal] = ArchiveFileTypeUnknown,
[ArchiveTabBrowser] = ArchiveFileTypeUnknown,
};
diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c
index 5c06c1bda..83eb2a845 100644
--- a/applications/main/archive/helpers/archive_files.c
+++ b/applications/main/archive/helpers/archive_files.c
@@ -16,11 +16,11 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder
for(size_t i = 0; i < COUNT_OF(known_ext); i++) {
if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue;
if(furi_string_search(file->path, known_ext[i], 0) != FURI_STRING_FAILURE) {
- if(i == ArchiveFileTypeBadUsb) {
- if(furi_string_search(
- file->path, archive_get_default_path(ArchiveTabBadUsb)) == 0) {
+ if(i == ArchiveFileTypeBadKb) {
+ if(furi_string_search(file->path, archive_get_default_path(ArchiveTabBadKb)) ==
+ 0) {
file->type = i;
- return; // *.txt file is a BadUSB script only if it is in BadUSB folder
+ return; // *.txt file is a BadKB script only if it is in BadKB folder
}
} else {
file->type = i;
diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h
index db624f5b5..863a8e7da 100644
--- a/applications/main/archive/helpers/archive_files.h
+++ b/applications/main/archive/helpers/archive_files.h
@@ -3,9 +3,9 @@
#include
#include
#include
-#include
#include
#include "toolbox/path.h"
+#include "xtreme/settings.h"
#define FAP_MANIFEST_MAX_ICON_SIZE 32
@@ -15,7 +15,7 @@ typedef enum {
ArchiveFileTypeSubGhz,
ArchiveFileTypeLFRFID,
ArchiveFileTypeInfrared,
- ArchiveFileTypeBadUsb,
+ ArchiveFileTypeBadKb,
ArchiveFileTypeU2f,
ArchiveFileTypeApplication,
ArchiveFileTypeUpdateManifest,
@@ -84,11 +84,16 @@ static void ArchiveFile_t_clear(ArchiveFile_t* obj) {
}
static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) {
- if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) {
- return -1;
+ if(!XTREME_SETTINGS()->sort_ignore_dirs) {
+ if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) {
+ return -1;
+ }
+ if(a->type != ArchiveFileTypeFolder && b->type == ArchiveFileTypeFolder) {
+ return 1;
+ }
}
- return furi_string_cmp(a->path, b->path);
+ return furi_string_cmpi(a->path, b->path);
}
#define M_OPL_ArchiveFile_t() \
diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c
index f88efb0c4..0696647ea 100644
--- a/applications/main/archive/scenes/archive_scene_browser.c
+++ b/applications/main/archive/scenes/archive_scene_browser.c
@@ -17,7 +17,7 @@ static const char* flipper_app_name[] = {
[ArchiveFileTypeSubGhz] = "Sub-GHz",
[ArchiveFileTypeLFRFID] = "125 kHz RFID",
[ArchiveFileTypeInfrared] = "Infrared",
- [ArchiveFileTypeBadUsb] = "Bad USB",
+ [ArchiveFileTypeBadKb] = "Bad KB",
[ArchiveFileTypeU2f] = "U2F",
[ArchiveFileTypeApplication] = "Applications",
[ArchiveFileTypeUpdateManifest] = "UpdaterApp",
diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c
index dce753fde..59c20ce6b 100644
--- a/applications/main/archive/views/archive_browser_view.c
+++ b/applications/main/archive/views/archive_browser_view.c
@@ -16,9 +16,10 @@ static const char* ArchiveTabNames[] = {
[ArchiveTabSubGhz] = "Sub-GHz",
[ArchiveTabLFRFID] = "RFID LF",
[ArchiveTabInfrared] = "Infrared",
- [ArchiveTabBadUsb] = "Bad USB",
+ [ArchiveTabBadKb] = "Bad KB",
[ArchiveTabU2f] = "U2F",
[ArchiveTabApplications] = "Apps",
+ [ArchiveTabInternal] = "Internal",
[ArchiveTabBrowser] = "Browser",
};
@@ -28,7 +29,7 @@ static const Icon* ArchiveItemIcons[] = {
[ArchiveFileTypeSubGhz] = &I_sub1_10px,
[ArchiveFileTypeLFRFID] = &I_125_10px,
[ArchiveFileTypeInfrared] = &I_ir_10px,
- [ArchiveFileTypeBadUsb] = &I_badusb_10px,
+ [ArchiveFileTypeBadKb] = &I_badkb_10px,
[ArchiveFileTypeU2f] = &I_u2f_10px,
[ArchiveFileTypeApplication] = &I_Apps_10px,
[ArchiveFileTypeUpdateManifest] = &I_update_10px,
@@ -109,7 +110,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
menu_array_push_raw(model->context_menu),
item_pin,
ArchiveBrowserEventFileMenuPin);
- if(selected->type <= ArchiveFileTypeBadUsb) {
+ if(selected->type <= ArchiveFileTypeBadKb) {
archive_menu_add_item(
menu_array_push_raw(model->context_menu),
item_show,
@@ -129,7 +130,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
menu_array_push_raw(model->context_menu),
item_info,
ArchiveBrowserEventFileMenuInfo);
- if(selected->type <= ArchiveFileTypeBadUsb) {
+ if(selected->type <= ArchiveFileTypeBadKb) {
archive_menu_add_item(
menu_array_push_raw(model->context_menu),
item_show,
@@ -157,7 +158,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) {
menu_array_push_raw(model->context_menu),
item_info,
ArchiveBrowserEventFileMenuInfo);
- if(selected->type <= ArchiveFileTypeBadUsb) {
+ if(selected->type <= ArchiveFileTypeBadKb) {
archive_menu_add_item(
menu_array_push_raw(model->context_menu),
item_show,
@@ -397,15 +398,20 @@ static bool archive_view_input(InputEvent* event, void* context) {
bool in_menu;
bool move_fav_mode;
+ bool is_loading;
with_view_model(
browser->view,
ArchiveBrowserViewModel * model,
{
in_menu = model->menu;
move_fav_mode = model->move_fav;
+ is_loading = model->folder_loading || model->list_loading;
},
false);
+ if(is_loading) {
+ return false;
+ }
if(in_menu) {
if(event->type != InputTypeShort) {
return true; // RETURN
@@ -481,7 +487,7 @@ static bool archive_view_input(InputEvent* event, void* context) {
model->scroll_counter = 0;
}
},
- true);
+ false);
archive_update_offset(browser);
}
@@ -588,4 +594,4 @@ void browser_free(ArchiveBrowserView* browser) {
view_free(browser->view);
free(browser);
-}
\ No newline at end of file
+}
diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h
index dfe18d13b..1ce35cb53 100644
--- a/applications/main/archive/views/archive_browser_view.h
+++ b/applications/main/archive/views/archive_browser_view.h
@@ -1,10 +1,13 @@
#pragma once
+#include "../helpers/archive_files.h"
+#include "../helpers/archive_favorites.h"
+
#include
#include
#include
#include
-#include
+#include
#include
#include "../helpers/archive_files.h"
#include "../helpers/archive_menu.h"
@@ -25,9 +28,10 @@ typedef enum {
ArchiveTabNFC,
ArchiveTabInfrared,
ArchiveTabIButton,
- ArchiveTabBadUsb,
+ ArchiveTabBadKb,
ArchiveTabU2f,
ArchiveTabApplications,
+ ArchiveTabInternal,
ArchiveTabBrowser,
ArchiveTabTotal,
} ArchiveTabEnum;
diff --git a/applications/main/bad_usb/application.fam b/applications/main/bad_kb/application.fam
similarity index 55%
rename from applications/main/bad_usb/application.fam
rename to applications/main/bad_kb/application.fam
index 2442dd3aa..09531da81 100644
--- a/applications/main/bad_usb/application.fam
+++ b/applications/main/bad_kb/application.fam
@@ -1,15 +1,15 @@
App(
- appid="bad_usb",
- name="Bad USB",
+ appid="bad_kb",
+ name="Bad KB",
apptype=FlipperAppType.APP,
- entry_point="bad_usb_app",
- cdefines=["APP_BAD_USB"],
+ entry_point="bad_kb_app",
+ cdefines=["APP_BAD_KB"],
requires=[
"gui",
"dialogs",
],
stack_size=2 * 1024,
- icon="A_BadUsb_14",
+ icon="A_BadKb_14",
order=70,
fap_libs=["assets"],
)
diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c
new file mode 100644
index 000000000..dfce5acbf
--- /dev/null
+++ b/applications/main/bad_kb/bad_kb_app.c
@@ -0,0 +1,223 @@
+#include "bad_kb_app_i.h"
+#include "bad_kb_settings_filename.h"
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#define BAD_KB_SETTINGS_PATH BAD_KB_APP_BASE_FOLDER "/" BAD_KB_SETTINGS_FILE_NAME
+
+static bool bad_kb_app_custom_event_callback(void* context, uint32_t event) {
+ furi_assert(context);
+ BadKbApp* app = context;
+ return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+static bool bad_kb_app_back_event_callback(void* context) {
+ furi_assert(context);
+ BadKbApp* app = context;
+ return scene_manager_handle_back_event(app->scene_manager);
+}
+
+static void bad_kb_app_tick_event_callback(void* context) {
+ furi_assert(context);
+ BadKbApp* app = context;
+ scene_manager_handle_tick_event(app->scene_manager);
+}
+
+static void bad_kb_load_settings(BadKbApp* app) {
+ File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
+ if(storage_file_open(settings_file, BAD_KB_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
+ char chr;
+ while((storage_file_read(settings_file, &chr, 1) == 1) &&
+ !storage_file_eof(settings_file) && !isspace(chr)) {
+ furi_string_push_back(app->keyboard_layout, chr);
+ }
+ }
+ storage_file_close(settings_file);
+ storage_file_free(settings_file);
+}
+
+static void bad_kb_save_settings(BadKbApp* app) {
+ File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
+ if(storage_file_open(settings_file, BAD_KB_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
+ storage_file_write(
+ settings_file,
+ furi_string_get_cstr(app->keyboard_layout),
+ furi_string_size(app->keyboard_layout));
+ storage_file_write(settings_file, "\n", 1);
+ }
+ storage_file_close(settings_file);
+ storage_file_free(settings_file);
+}
+
+void bad_kb_set_name(BadKbApp* app, const char* fmt, ...) {
+ furi_assert(app);
+
+ va_list args;
+ va_start(args, fmt);
+
+ vsnprintf(app->name, BAD_KB_ADV_NAME_MAX_LEN, fmt, args);
+
+ va_end(args);
+}
+
+BadKbApp* bad_kb_app_alloc(char* arg) {
+ BadKbApp* app = malloc(sizeof(BadKbApp));
+
+ app->bad_kb_script = NULL;
+
+ app->file_path = furi_string_alloc();
+ app->keyboard_layout = furi_string_alloc();
+ if(arg && strlen(arg)) {
+ furi_string_set(app->file_path, arg);
+ }
+
+ bad_kb_load_settings(app);
+
+ app->gui = furi_record_open(RECORD_GUI);
+ app->notifications = furi_record_open(RECORD_NOTIFICATION);
+ app->dialogs = furi_record_open(RECORD_DIALOGS);
+
+ app->view_dispatcher = view_dispatcher_alloc();
+ view_dispatcher_enable_queue(app->view_dispatcher);
+
+ app->scene_manager = scene_manager_alloc(&bad_kb_scene_handlers, app);
+
+ view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
+ view_dispatcher_set_tick_event_callback(
+ app->view_dispatcher, bad_kb_app_tick_event_callback, 500);
+ view_dispatcher_set_custom_event_callback(
+ app->view_dispatcher, bad_kb_app_custom_event_callback);
+ view_dispatcher_set_navigation_event_callback(
+ app->view_dispatcher, bad_kb_app_back_event_callback);
+
+ Bt* bt = furi_record_open(RECORD_BT);
+ app->bt = bt;
+ app->is_bt = XTREME_SETTINGS()->bad_bt;
+ const char* adv_name = bt_get_profile_adv_name(bt);
+ memcpy(app->name, adv_name, BAD_KB_ADV_NAME_MAX_LEN);
+ memcpy(app->bt_old_config.name, adv_name, BAD_KB_ADV_NAME_MAX_LEN);
+
+ const uint8_t* mac_addr = bt_get_profile_mac_address(bt);
+ memcpy(app->mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN);
+ memcpy(app->bt_old_config.mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN);
+
+ // Custom Widget
+ app->widget = widget_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, BadKbAppViewError, widget_get_view(app->widget));
+
+ app->var_item_list_bt = variable_item_list_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ BadKbAppViewConfigBt,
+ variable_item_list_get_view(app->var_item_list_bt));
+ app->var_item_list_usb = variable_item_list_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ BadKbAppViewConfigUsb,
+ variable_item_list_get_view(app->var_item_list_usb));
+
+ app->bad_kb_view = bad_kb_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, BadKbAppViewWork, bad_kb_get_view(app->bad_kb_view));
+
+ app->text_input = text_input_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, BadKbAppViewConfigName, text_input_get_view(app->text_input));
+
+ app->byte_input = byte_input_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, BadKbAppViewConfigMac, byte_input_get_view(app->byte_input));
+
+ view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+ if(furi_hal_usb_is_locked()) {
+ app->error = BadKbAppErrorCloseRpc;
+ scene_manager_next_scene(app->scene_manager, BadKbSceneError);
+ } else {
+ if(!furi_string_empty(app->file_path)) {
+ app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL);
+ bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout);
+ scene_manager_next_scene(app->scene_manager, BadKbSceneWork);
+ } else {
+ furi_string_set(app->file_path, BAD_KB_APP_BASE_FOLDER);
+ scene_manager_next_scene(app->scene_manager, BadKbSceneFileSelect);
+ }
+ }
+
+ return app;
+}
+
+void bad_kb_app_free(BadKbApp* app) {
+ furi_assert(app);
+
+ if(app->bad_kb_script) {
+ bad_kb_script_close(app->bad_kb_script);
+ app->bad_kb_script = NULL;
+ }
+
+ // Views
+ view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewWork);
+ bad_kb_free(app->bad_kb_view);
+
+ // Custom Widget
+ view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewError);
+ widget_free(app->widget);
+
+ // Variable item list
+ view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigBt);
+ variable_item_list_free(app->var_item_list_bt);
+ view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigUsb);
+ variable_item_list_free(app->var_item_list_usb);
+
+ // Text Input
+ view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigName);
+ text_input_free(app->text_input);
+
+ // Byte Input
+ view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigMac);
+ byte_input_free(app->byte_input);
+
+ // View dispatcher
+ view_dispatcher_free(app->view_dispatcher);
+ scene_manager_free(app->scene_manager);
+
+ // restores bt config
+ // BtProfile have already been switched to the previous one
+ // so we directly modify the right profile
+ bad_kb_connection_deinit(app->bt);
+ if(strcmp(app->bt_old_config.name, app->name) != 0) {
+ furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->bt_old_config.name);
+ }
+ if(memcmp(app->bt_old_config.mac, app->mac, BAD_KB_MAC_ADDRESS_LEN) != 0) {
+ furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->bt_old_config.mac);
+ }
+
+ // Close records
+ furi_record_close(RECORD_GUI);
+ furi_record_close(RECORD_NOTIFICATION);
+ furi_record_close(RECORD_DIALOGS);
+ furi_record_close(RECORD_BT);
+
+ bad_kb_save_settings(app);
+
+ furi_string_free(app->file_path);
+ furi_string_free(app->keyboard_layout);
+
+ free(app);
+}
+
+int32_t bad_kb_app(void* p) {
+ BadKbApp* bad_kb_app = bad_kb_app_alloc((char*)p);
+
+ view_dispatcher_run(bad_kb_app->view_dispatcher);
+
+ bad_kb_app_free(bad_kb_app);
+ return 0;
+}
diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h
new file mode 100644
index 000000000..e75a94651
--- /dev/null
+++ b/applications/main/bad_kb/bad_kb_app.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BadKbApp BadKbApp;
+
+void bad_kb_set_name(BadKbApp* app, const char* fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/applications/main/bad_kb/bad_kb_app_i.h b/applications/main/bad_kb/bad_kb_app_i.h
new file mode 100644
index 000000000..913830e72
--- /dev/null
+++ b/applications/main/bad_kb/bad_kb_app_i.h
@@ -0,0 +1,80 @@
+#pragma once
+
+#include "bad_kb_app.h"
+#include "scenes/bad_kb_scene.h"
+#include "bad_kb_script.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "views/bad_kb_view.h"
+
+#define BAD_KB_APP_BASE_FOLDER ANY_PATH("badkb")
+#define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/layouts"
+#define BAD_KB_APP_SCRIPT_EXTENSION ".txt"
+#define BAD_KB_APP_LAYOUT_EXTENSION ".kl"
+
+#define BAD_KB_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro
+#define BAD_KB_ADV_NAME_MAX_LEN 18
+
+typedef enum {
+ BadKbAppErrorNoFiles,
+ BadKbAppErrorCloseRpc,
+} BadKbAppError;
+
+typedef enum BadKbCustomEvent {
+ BadKbAppCustomEventTextEditResult,
+ BadKbAppCustomEventByteInputDone,
+ BadKbCustomEventErrorBack
+} BadKbCustomEvent;
+
+typedef struct {
+ uint8_t mac[BAD_KB_MAC_ADDRESS_LEN];
+ char name[BAD_KB_ADV_NAME_MAX_LEN + 1];
+
+ // number of bt keys before starting the app (all keys added in
+ // the bt keys file then will be removed)
+ uint16_t n_keys;
+} BadKbBtConfig;
+
+struct BadKbApp {
+ Gui* gui;
+ ViewDispatcher* view_dispatcher;
+ SceneManager* scene_manager;
+ NotificationApp* notifications;
+ DialogsApp* dialogs;
+ Widget* widget;
+ VariableItemList* var_item_list_bt;
+ VariableItemList* var_item_list_usb;
+
+ Bt* bt;
+ TextInput* text_input;
+ ByteInput* byte_input;
+ uint8_t mac[BAD_KB_MAC_ADDRESS_LEN];
+ char name[BAD_KB_ADV_NAME_MAX_LEN + 1];
+ BadKbBtConfig bt_old_config;
+
+ BadKbAppError error;
+ FuriString* file_path;
+ FuriString* keyboard_layout;
+ BadKb* bad_kb_view;
+ BadKbScript* bad_kb_script;
+
+ bool is_bt;
+};
+
+typedef enum {
+ BadKbAppViewError,
+ BadKbAppViewWork,
+ BadKbAppViewConfigBt,
+ BadKbAppViewConfigUsb,
+ BadKbAppViewConfigMac,
+ BadKbAppViewConfigName
+} BadKbAppView;
diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c
new file mode 100644
index 000000000..e2fd464ff
--- /dev/null
+++ b/applications/main/bad_kb/bad_kb_script.c
@@ -0,0 +1,976 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "bad_kb_script.h"
+#include
+
+#include
+
+#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys")
+
+#define TAG "BadKB"
+#define WORKER_TAG TAG "Worker"
+#define FILE_BUFFER_LEN 16
+
+#define SCRIPT_STATE_ERROR (-1)
+#define SCRIPT_STATE_END (-2)
+#define SCRIPT_STATE_NEXT_LINE (-3)
+
+#define BADKB_ASCII_TO_KEY(script, x) \
+ (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
+
+typedef enum {
+ WorkerEvtToggle = (1 << 0),
+ WorkerEvtEnd = (1 << 1),
+ WorkerEvtConnect = (1 << 2),
+ WorkerEvtDisconnect = (1 << 3),
+} WorkerEvtFlags;
+
+typedef enum {
+ LevelRssi122_100,
+ LevelRssi99_80,
+ LevelRssi79_60,
+ LevelRssi59_40,
+ LevelRssi39_0,
+ LevelRssiNum,
+ LevelRssiError = 0xFF,
+} LevelRssiRange;
+
+typedef enum {
+ BadKbConnectionModeNone,
+ BadKbConnectionModeUsb,
+ BadKbConnectionModeBt,
+} BadKbConnectionMode;
+
+/**
+ * Delays for waiting between HID key press and key release
+*/
+const uint8_t bt_hid_delays[LevelRssiNum] = {
+ 30, // LevelRssi122_100
+ 25, // LevelRssi99_80
+ 20, // LevelRssi79_60
+ 17, // LevelRssi59_40
+ 14, // LevelRssi39_0
+};
+
+struct BadKbScript {
+ FuriHalUsbHidConfig hid_cfg;
+ BadKbState st;
+ FuriString* file_path;
+ FuriString* keyboard_layout;
+ uint32_t defdelay;
+ uint16_t layout[128];
+ FuriThread* thread;
+ uint8_t file_buf[FILE_BUFFER_LEN + 1];
+ uint8_t buf_start;
+ uint8_t buf_len;
+ bool file_end;
+ FuriString* line;
+
+ FuriString* line_prev;
+ uint32_t repeat_cnt;
+
+ Bt* bt;
+};
+
+typedef struct {
+ char* name;
+ uint16_t keycode;
+} DuckyKey;
+
+static const DuckyKey ducky_keys[] = {
+ {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT},
+ {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT},
+ {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
+ {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
+ {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT},
+ {"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL},
+
+ {"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", HID_KEYBOARD_DOWN_ARROW},
+ {"DOWN", HID_KEYBOARD_DOWN_ARROW},
+ {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW},
+ {"LEFT", HID_KEYBOARD_LEFT_ARROW},
+ {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW},
+ {"RIGHT", HID_KEYBOARD_RIGHT_ARROW},
+ {"UPARROW", HID_KEYBOARD_UP_ARROW},
+ {"UP", HID_KEYBOARD_UP_ARROW},
+
+ {"ENTER", HID_KEYBOARD_RETURN},
+ {"BREAK", HID_KEYBOARD_PAUSE},
+ {"PAUSE", HID_KEYBOARD_PAUSE},
+ {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
+ {"DELETE", HID_KEYBOARD_DELETE},
+ {"BACKSPACE", HID_KEYPAD_BACKSPACE},
+ {"END", HID_KEYBOARD_END},
+ {"ESC", HID_KEYBOARD_ESCAPE},
+ {"ESCAPE", HID_KEYBOARD_ESCAPE},
+ {"HOME", HID_KEYBOARD_HOME},
+ {"INSERT", HID_KEYBOARD_INSERT},
+ {"NUMLOCK", HID_KEYPAD_NUMLOCK},
+ {"PAGEUP", HID_KEYBOARD_PAGE_UP},
+ {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
+ {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
+ {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
+ {"SPACE", HID_KEYBOARD_SPACEBAR},
+ {"TAB", HID_KEYBOARD_TAB},
+ {"MENU", HID_KEYBOARD_APPLICATION},
+ {"APP", HID_KEYBOARD_APPLICATION},
+
+ {"F1", HID_KEYBOARD_F1},
+ {"F2", HID_KEYBOARD_F2},
+ {"F3", HID_KEYBOARD_F3},
+ {"F4", HID_KEYBOARD_F4},
+ {"F5", HID_KEYBOARD_F5},
+ {"F6", HID_KEYBOARD_F6},
+ {"F7", HID_KEYBOARD_F7},
+ {"F8", HID_KEYBOARD_F8},
+ {"F9", HID_KEYBOARD_F9},
+ {"F10", HID_KEYBOARD_F10},
+ {"F11", HID_KEYBOARD_F11},
+ {"F12", HID_KEYBOARD_F12},
+};
+
+static const char ducky_cmd_comment[] = {"REM"};
+static const char ducky_cmd_id[] = {"ID"};
+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 const char ducky_cmd_repeat[] = {"REPEAT "};
+static const char ducky_cmd_sysrq[] = {"SYSRQ "};
+
+static const char ducky_cmd_altchar[] = {"ALTCHAR "};
+static const char ducky_cmd_altstr_1[] = {"ALTSTRING "};
+static const char ducky_cmd_altstr_2[] = {"ALTCODE "};
+
+static const char ducky_cmd_lang[] = {"DUCKY_LANG"};
+
+static const uint8_t numpad_keys[10] = {
+ HID_KEYPAD_0,
+ HID_KEYPAD_1,
+ HID_KEYPAD_2,
+ HID_KEYPAD_3,
+ HID_KEYPAD_4,
+ HID_KEYPAD_5,
+ HID_KEYPAD_6,
+ HID_KEYPAD_7,
+ HID_KEYPAD_8,
+ HID_KEYPAD_9,
+};
+
+BadKbConnectionMode connection_mode = BadKbConnectionModeNone;
+FuriHalUsbInterface* usb_mode_prev = NULL;
+GapPairing bt_mode_prev = GapPairingNone;
+bool bt_connected = false;
+bool usb_connected = false;
+uint8_t bt_timeout = 0;
+
+static LevelRssiRange bt_remote_rssi_range(Bt* bt) {
+ BtRssi rssi_data = {0};
+
+ if(!bt_remote_rssi(bt, &rssi_data)) return LevelRssiError;
+
+ if(rssi_data.rssi <= 39)
+ return LevelRssi39_0;
+ else if(rssi_data.rssi <= 59)
+ return LevelRssi59_40;
+ else if(rssi_data.rssi <= 79)
+ return LevelRssi79_60;
+ else if(rssi_data.rssi <= 99)
+ return LevelRssi99_80;
+ else if(rssi_data.rssi <= 122)
+ return LevelRssi122_100;
+
+ return LevelRssiError;
+}
+
+static inline void update_bt_timeout(Bt* bt) {
+ LevelRssiRange r = bt_remote_rssi_range(bt);
+ if(r < LevelRssiNum) {
+ bt_timeout = bt_hid_delays[r];
+ FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout);
+ }
+}
+
+static bool ducky_get_number(const char* param, uint32_t* val) {
+ uint32_t value = 0;
+ if(sscanf(param, "%lu", &value) == 1) {
+ *val = value;
+ return true;
+ }
+ return false;
+}
+
+static uint32_t ducky_get_command_len(const char* line) {
+ uint32_t len = strlen(line);
+ for(uint32_t i = 0; i < len; i++) {
+ if(line[i] == ' ') return i;
+ }
+ return 0;
+}
+
+static bool ducky_is_line_end(const char chr) {
+ return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n'));
+}
+
+static void ducky_numlock_on(BadKbScript* bad_kb) {
+ if(bad_kb->bt) {
+ if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) { // FIXME
+ furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK);
+ furi_delay_ms(bt_timeout);
+ furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK);
+ }
+ } else {
+ if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) {
+ furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK);
+ furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK);
+ }
+ }
+}
+
+static bool ducky_numpad_press(BadKbScript* bad_kb, const char num) {
+ if((num < '0') || (num > '9')) return false;
+
+ uint16_t key = numpad_keys[num - '0'];
+ if(bad_kb->bt) {
+ furi_hal_bt_hid_kb_press(key);
+ furi_delay_ms(bt_timeout);
+ furi_hal_bt_hid_kb_release(key);
+ } else {
+ furi_hal_hid_kb_press(key);
+ furi_hal_hid_kb_release(key);
+ }
+
+ return true;
+}
+
+static bool ducky_altchar(BadKbScript* bad_kb, const char* charcode) {
+ uint8_t i = 0;
+ bool state = false;
+
+ FURI_LOG_I(WORKER_TAG, "char %s", charcode);
+
+ if(bad_kb->bt) {
+ furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT);
+ } else {
+ furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT);
+ }
+
+ while(!ducky_is_line_end(charcode[i])) {
+ state = ducky_numpad_press(bad_kb, charcode[i]);
+ if(state == false) break;
+ i++;
+ }
+
+ if(bad_kb->bt) {
+ furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT);
+ } else {
+ furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT);
+ }
+ return state;
+}
+
+static bool ducky_altstring(BadKbScript* bad_kb, const char* param) {
+ uint32_t i = 0;
+ bool state = false;
+
+ while(param[i] != '\0') {
+ if((param[i] < ' ') || (param[i] > '~')) {
+ i++;
+ continue; // Skip non-printable chars
+ }
+
+ char temp_str[4];
+ snprintf(temp_str, 4, "%u", param[i]);
+
+ state = ducky_altchar(bad_kb, temp_str);
+ if(state == false) break;
+ i++;
+ }
+ return state;
+}
+
+static bool ducky_string(BadKbScript* bad_kb, const char* param) {
+ uint32_t i = 0;
+ while(param[i] != '\0') {
+ uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, param[i]);
+ if(keycode != HID_KEYBOARD_NONE) {
+ if(bad_kb->bt) {
+ furi_hal_bt_hid_kb_press(keycode);
+ furi_delay_ms(bt_timeout);
+ furi_hal_bt_hid_kb_release(keycode);
+ } else {
+ furi_hal_hid_kb_press(keycode);
+ furi_hal_hid_kb_release(keycode);
+ }
+ }
+ i++;
+ }
+ return true;
+}
+
+static uint16_t ducky_get_keycode(BadKbScript* bad_kb, const char* param, bool accept_chars) {
+ for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
+ size_t key_cmd_len = strlen(ducky_keys[i].name);
+ if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
+ (ducky_is_line_end(param[key_cmd_len]))) {
+ return ducky_keys[i].keycode;
+ }
+ }
+ if((accept_chars) && (strlen(param) > 0)) {
+ return (BADKB_ASCII_TO_KEY(bad_kb, param[0]) & 0xFF);
+ }
+ return 0;
+}
+
+static int32_t
+ ducky_parse_line(BadKbScript* bad_kb, FuriString* line, char* error, size_t error_len) {
+ uint32_t line_len = furi_string_size(line);
+ const char* line_tmp = furi_string_get_cstr(line);
+ bool state = false;
+
+ if(line_len == 0) {
+ return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
+ }
+
+ FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
+
+ // General commands
+ if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) {
+ // REM - comment line
+ return (0);
+ } else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
+ // ID - executed in ducky_script_preload
+ return (0);
+ } else if(strncmp(line_tmp, ducky_cmd_lang, strlen(ducky_cmd_lang)) == 0) {
+ // DUCKY_LANG - ignore command to retain compatibility with existing scripts
+ return (0);
+ } else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) {
+ // DELAY
+ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
+ uint32_t delay_val = 0;
+ state = ducky_get_number(line_tmp, &delay_val);
+ if((state) && (delay_val > 0)) {
+ return (int32_t)delay_val;
+ }
+ if(error != NULL) {
+ snprintf(error, error_len, "Invalid number %s", line_tmp);
+ }
+ return SCRIPT_STATE_ERROR;
+ } else if(
+ (strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
+ (strncmp(line_tmp, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) {
+ // DEFAULT_DELAY
+ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
+ state = ducky_get_number(line_tmp, &bad_kb->defdelay);
+ if(!state && error != NULL) {
+ snprintf(error, error_len, "Invalid number %s", line_tmp);
+ }
+ return (state) ? (0) : SCRIPT_STATE_ERROR;
+ } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
+ // STRING
+ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
+ state = ducky_string(bad_kb, line_tmp);
+ if(!state && error != NULL) {
+ snprintf(error, error_len, "Invalid string %s", line_tmp);
+ }
+ return (state) ? (0) : SCRIPT_STATE_ERROR;
+ } else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
+ // ALTCHAR
+ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
+ ducky_numlock_on(bad_kb);
+ state = ducky_altchar(bad_kb, line_tmp);
+ if(!state && error != NULL) {
+ snprintf(error, error_len, "Invalid altchar %s", line_tmp);
+ }
+ return (state) ? (0) : SCRIPT_STATE_ERROR;
+ } else if(
+ (strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) ||
+ (strncmp(line_tmp, ducky_cmd_altstr_2, strlen(ducky_cmd_altstr_2)) == 0)) {
+ // ALTSTRING
+ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
+ ducky_numlock_on(bad_kb);
+ state = ducky_altstring(bad_kb, line_tmp);
+ if(!state && error != NULL) {
+ snprintf(error, error_len, "Invalid altstring %s", line_tmp);
+ }
+ return (state) ? (0) : SCRIPT_STATE_ERROR;
+ } else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
+ // REPEAT
+ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
+ state = ducky_get_number(line_tmp, &bad_kb->repeat_cnt);
+ if(!state && error != NULL) {
+ snprintf(error, error_len, "Invalid number %s", line_tmp);
+ }
+ return (state) ? (0) : SCRIPT_STATE_ERROR;
+ } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
+ // SYSRQ
+ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
+ uint16_t key = ducky_get_keycode(bad_kb, line_tmp, true);
+ if(bad_kb->bt) {
+ furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
+ furi_hal_bt_hid_kb_press(key);
+ furi_delay_ms(bt_timeout);
+ furi_hal_bt_hid_kb_release(key);
+ furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
+ } else {
+ furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
+ furi_hal_hid_kb_press(key);
+ furi_hal_hid_kb_release_all();
+ }
+ return (0);
+ } else {
+ // Special keys + modifiers
+ uint16_t key = ducky_get_keycode(bad_kb, line_tmp, false);
+ if(key == HID_KEYBOARD_NONE) {
+ if(error != NULL) {
+ snprintf(error, error_len, "No keycode defined for %s", line_tmp);
+ }
+ return SCRIPT_STATE_ERROR;
+ }
+ if((key & 0xFF00) != 0) {
+ // It's a modifier key
+ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
+ key |= ducky_get_keycode(bad_kb, line_tmp, true);
+ }
+ if(bad_kb->bt) {
+ furi_hal_bt_hid_kb_press(key);
+ furi_delay_ms(bt_timeout);
+ furi_hal_bt_hid_kb_release(key);
+ } else {
+ furi_hal_hid_kb_press(key);
+ furi_hal_hid_kb_release(key);
+ }
+ return (0);
+ }
+}
+
+static bool ducky_set_usb_id(BadKbScript* bad_kb, const char* line) {
+ if(sscanf(line, "%lX:%lX", &bad_kb->hid_cfg.vid, &bad_kb->hid_cfg.pid) == 2) {
+ bad_kb->hid_cfg.manuf[0] = '\0';
+ bad_kb->hid_cfg.product[0] = '\0';
+
+ uint8_t id_len = ducky_get_command_len(line);
+ if(!ducky_is_line_end(line[id_len + 1])) {
+ sscanf(
+ &line[id_len + 1],
+ "%31[^\r\n:]:%31[^\r\n]",
+ bad_kb->hid_cfg.manuf,
+ bad_kb->hid_cfg.product);
+ }
+ FURI_LOG_D(
+ WORKER_TAG,
+ "set id: %04lX:%04lX mfr:%s product:%s",
+ bad_kb->hid_cfg.vid,
+ bad_kb->hid_cfg.pid,
+ bad_kb->hid_cfg.manuf,
+ bad_kb->hid_cfg.product);
+ return true;
+ }
+ return false;
+}
+
+static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) {
+ uint8_t ret = 0;
+ uint32_t line_len = 0;
+
+ furi_string_reset(bad_kb->line);
+
+ do {
+ ret = storage_file_read(script_file, bad_kb->file_buf, FILE_BUFFER_LEN);
+ for(uint16_t i = 0; i < ret; i++) {
+ if(bad_kb->file_buf[i] == '\n' && line_len > 0) {
+ bad_kb->st.line_nb++;
+ line_len = 0;
+ } else {
+ if(bad_kb->st.line_nb == 0) { // Save first line
+ furi_string_push_back(bad_kb->line, bad_kb->file_buf[i]);
+ }
+ line_len++;
+ }
+ }
+ if(storage_file_eof(script_file)) {
+ if(line_len > 0) {
+ bad_kb->st.line_nb++;
+ break;
+ }
+ }
+ } while(ret > 0);
+
+ if(!bad_kb->bt) {
+ const char* line_tmp = furi_string_get_cstr(bad_kb->line);
+ bool id_set = false; // Looking for ID command at first line
+ if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
+ id_set = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]);
+ }
+
+ if(id_set) {
+ furi_check(furi_hal_usb_set_config(&usb_hid, &bad_kb->hid_cfg));
+ } else {
+ furi_check(furi_hal_usb_set_config(&usb_hid, NULL));
+ }
+ }
+
+ storage_file_seek(script_file, 0, true);
+ furi_string_reset(bad_kb->line);
+
+ return true;
+}
+
+static int32_t ducky_script_execute_next(BadKbScript* bad_kb, File* script_file) {
+ int32_t delay_val = 0;
+
+ if(bad_kb->repeat_cnt > 0) {
+ bad_kb->repeat_cnt--;
+ delay_val = ducky_parse_line(
+ bad_kb, bad_kb->line_prev, bad_kb->st.error, sizeof(bad_kb->st.error));
+ if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
+ return 0;
+ } else if(delay_val < 0) { // Script error
+ bad_kb->st.error_line = bad_kb->st.line_cur - 1;
+ FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_kb->st.line_cur - 1U);
+ return SCRIPT_STATE_ERROR;
+ } else {
+ return (delay_val + bad_kb->defdelay);
+ }
+ }
+
+ furi_string_set(bad_kb->line_prev, bad_kb->line);
+ furi_string_reset(bad_kb->line);
+
+ while(1) {
+ if(bad_kb->buf_len == 0) {
+ bad_kb->buf_len = storage_file_read(script_file, bad_kb->file_buf, FILE_BUFFER_LEN);
+ if(storage_file_eof(script_file)) {
+ if((bad_kb->buf_len < FILE_BUFFER_LEN) && (bad_kb->file_end == false)) {
+ bad_kb->file_buf[bad_kb->buf_len] = '\n';
+ bad_kb->buf_len++;
+ bad_kb->file_end = true;
+ }
+ }
+
+ bad_kb->buf_start = 0;
+ if(bad_kb->buf_len == 0) return SCRIPT_STATE_END;
+ }
+ for(uint8_t i = bad_kb->buf_start; i < (bad_kb->buf_start + bad_kb->buf_len); i++) {
+ if(bad_kb->file_buf[i] == '\n' && furi_string_size(bad_kb->line) > 0) {
+ bad_kb->st.line_cur++;
+ bad_kb->buf_len = bad_kb->buf_len + bad_kb->buf_start - (i + 1);
+ bad_kb->buf_start = i + 1;
+ furi_string_trim(bad_kb->line);
+ delay_val = ducky_parse_line(
+ bad_kb, bad_kb->line, bad_kb->st.error, sizeof(bad_kb->st.error));
+ if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
+ return 0;
+ } else if(delay_val < 0) {
+ bad_kb->st.error_line = bad_kb->st.line_cur;
+ if(delay_val == SCRIPT_STATE_NEXT_LINE) {
+ snprintf(
+ bad_kb->st.error, sizeof(bad_kb->st.error), "Forbidden empty line");
+ FURI_LOG_E(
+ WORKER_TAG, "Forbidden empty line at line %u", bad_kb->st.line_cur);
+ } else {
+ FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_kb->st.line_cur);
+ }
+ return SCRIPT_STATE_ERROR;
+ } else {
+ return (delay_val + bad_kb->defdelay);
+ }
+ } else {
+ furi_string_push_back(bad_kb->line, bad_kb->file_buf[i]);
+ }
+ }
+ bad_kb->buf_len = 0;
+ if(bad_kb->file_end) return SCRIPT_STATE_END;
+ }
+}
+
+static void bad_kb_bt_hid_state_callback(BtStatus status, void* context) {
+ furi_assert(context);
+ BadKbScript* bad_kb = (BadKbScript*)context;
+ bool state = (status == BtStatusConnected);
+
+ if(state == true) {
+ LevelRssiRange r = bt_remote_rssi_range(bad_kb->bt);
+ if(r != LevelRssiError) {
+ bt_timeout = bt_hid_delays[r];
+ }
+ bt_connected = true;
+ furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect);
+ } else {
+ bt_connected = false;
+ furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect);
+ }
+}
+
+static void bad_kb_usb_hid_state_callback(bool state, void* context) {
+ furi_assert(context);
+ BadKbScript* bad_kb = context;
+
+ if(state == true) {
+ usb_connected = true;
+ furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect);
+ } else {
+ usb_connected = false;
+ furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect);
+ }
+}
+
+void bad_kb_bt_init(Bt* bt) {
+ bt_timeout = bt_hid_delays[LevelRssi39_0];
+ bt_disconnect(bt);
+ furi_delay_ms(200);
+ bt_keys_storage_set_storage_path(bt, HID_BT_KEYS_STORAGE_PATH);
+ furi_assert(bt_set_profile(bt, BtProfileHidKeyboard));
+ bt_mode_prev = bt_get_profile_pairing_method(bt);
+ bt_set_profile_pairing_method(bt, GapPairingNone);
+ furi_hal_bt_start_advertising();
+ // disable peer key adding to bt SRAM storage
+ bt_disable_peer_key_update(bt);
+
+ connection_mode = BadKbConnectionModeBt;
+}
+
+void bad_kb_bt_deinit(Bt* bt) {
+ // release all keys
+ // bt_hid_hold_while_keyboard_buffer_full(6, 3000);
+
+ // stop ble
+ bt_disconnect(bt);
+
+ // Wait 2nd core to update nvm storage
+ furi_delay_ms(200);
+
+ bt_keys_storage_set_default_path(bt);
+
+ bt_set_profile_pairing_method(bt, bt_mode_prev);
+
+ // fails if ble radio stack isn't ready when switching profile
+ // if it happens, maybe we should increase the delay after bt_disconnect
+ bt_set_profile(bt, BtProfileSerial);
+
+ // starts saving peer keys (bounded devices)
+ bt_enable_peer_key_update(bt);
+
+ connection_mode = BadKbConnectionModeNone;
+}
+
+void bad_kb_usb_init() {
+ usb_mode_prev = furi_hal_usb_get_config();
+
+ connection_mode = BadKbConnectionModeUsb;
+}
+
+void bad_kb_usb_deinit() {
+ furi_hal_usb_set_config(usb_mode_prev, NULL);
+
+ connection_mode = BadKbConnectionModeNone;
+}
+
+void bad_kb_connection_init(Bt* bt) {
+ if(connection_mode != BadKbConnectionModeNone) return;
+
+ if(bt) {
+ bad_kb_bt_init(bt);
+ } else {
+ bad_kb_usb_init();
+ }
+}
+
+void bad_kb_connection_deinit(Bt* bt) {
+ if(connection_mode == BadKbConnectionModeNone) return;
+
+ if(connection_mode == BadKbConnectionModeBt) {
+ bad_kb_bt_deinit(bt);
+ } else {
+ bad_kb_usb_deinit();
+ }
+}
+
+static int32_t bad_kb_worker(void* context) {
+ BadKbScript* bad_kb = context;
+
+ BadKbWorkerState worker_state = BadKbStateInit;
+ int32_t delay_val = 0;
+
+ bad_kb_connection_init(bad_kb->bt);
+
+ if(bad_kb->bt) {
+ bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb);
+ } else {
+ furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, bad_kb);
+ }
+
+ FURI_LOG_I(WORKER_TAG, "Init");
+ File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
+ bad_kb->line = furi_string_alloc();
+ bad_kb->line_prev = furi_string_alloc();
+
+ while(1) {
+ if(worker_state == BadKbStateInit) { // State: initialization
+ if(storage_file_open(
+ script_file,
+ furi_string_get_cstr(bad_kb->file_path),
+ FSAM_READ,
+ FSOM_OPEN_EXISTING)) {
+ if((ducky_script_preload(bad_kb, script_file)) && (bad_kb->st.line_nb > 0)) {
+ if(bad_kb->bt) {
+ worker_state = BadKbStateNotConnected; // Ready to run
+ } else {
+ if(furi_hal_hid_is_connected()) {
+ worker_state = BadKbStateIdle; // Ready to run
+ } else {
+ worker_state = BadKbStateNotConnected; // Not connected
+ }
+ }
+ } else {
+ worker_state = BadKbStateScriptError; // Script preload error
+ }
+ } else {
+ FURI_LOG_E(WORKER_TAG, "File open error");
+ worker_state = BadKbStateFileError; // File open error
+ }
+ bad_kb->st.state = worker_state;
+
+ } else if(worker_state == BadKbStateNotConnected) { // State: Not connected
+ if((bad_kb->bt && bt_connected) || (!bad_kb->bt && usb_connected)) {
+ worker_state = BadKbStateIdle; // Ready to run
+ } else {
+ uint32_t flags = furi_thread_flags_wait(
+ WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
+ FuriFlagWaitAny,
+ FuriWaitForever);
+ furi_check((flags & FuriFlagError) == 0);
+ if(flags & WorkerEvtEnd) {
+ break;
+ } else if(flags & WorkerEvtConnect) {
+ worker_state = BadKbStateIdle; // Ready to run
+ } else if(flags & WorkerEvtToggle) {
+ worker_state = BadKbStateWillRun; // Will run when connected
+ }
+ }
+ bad_kb->st.state = worker_state;
+
+ } else if(worker_state == BadKbStateIdle) { // State: ready to start
+ uint32_t flags = furi_thread_flags_wait(
+ WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
+ FuriFlagWaitAny,
+ FuriWaitForever);
+ furi_check((flags & FuriFlagError) == 0);
+ if(flags & WorkerEvtEnd) {
+ break;
+ } else if(flags & WorkerEvtToggle) { // Start executing script
+ DOLPHIN_DEED(DolphinDeedBadKbPlayScript);
+ delay_val = 0;
+ bad_kb->buf_len = 0;
+ bad_kb->st.line_cur = 0;
+ bad_kb->defdelay = 0;
+ bad_kb->repeat_cnt = 0;
+ bad_kb->file_end = false;
+ storage_file_seek(script_file, 0, true);
+ bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout);
+ worker_state = BadKbStateRunning;
+ } else if(flags & WorkerEvtDisconnect) {
+ worker_state = BadKbStateNotConnected; // Disconnected
+ }
+ bad_kb->st.state = worker_state;
+
+ } else if(worker_state == BadKbStateWillRun) { // State: start on connection
+ uint32_t flags = furi_thread_flags_wait(
+ WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
+ FuriFlagWaitAny,
+ FuriWaitForever);
+ furi_check((flags & FuriFlagError) == 0);
+ if(flags & WorkerEvtEnd) {
+ break;
+ } else if(flags & WorkerEvtConnect) { // Start executing script
+ DOLPHIN_DEED(DolphinDeedBadKbPlayScript);
+ delay_val = 0;
+ bad_kb->buf_len = 0;
+ bad_kb->st.line_cur = 0;
+ bad_kb->defdelay = 0;
+ bad_kb->repeat_cnt = 0;
+ bad_kb->file_end = false;
+ storage_file_seek(script_file, 0, true);
+ // extra time for PC to recognize Flipper as keyboard
+ furi_thread_flags_wait(0, FuriFlagWaitAny, 1500);
+ if(bad_kb->bt) {
+ update_bt_timeout(bad_kb->bt);
+ }
+ bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout);
+ worker_state = BadKbStateRunning;
+ } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
+ worker_state = BadKbStateNotConnected;
+ }
+ bad_kb->st.state = worker_state;
+
+ } else if(worker_state == BadKbStateRunning) { // State: running
+ uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
+ uint32_t flags = furi_thread_flags_wait(
+ WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur);
+ delay_val -= delay_cur;
+ if(!(flags & FuriFlagError)) {
+ if(flags & WorkerEvtEnd) {
+ break;
+ } else if(flags & WorkerEvtToggle) {
+ worker_state = BadKbStateIdle; // Stop executing script
+ if(bad_kb->bt) {
+ furi_hal_bt_hid_kb_release_all();
+ } else {
+ furi_hal_hid_kb_release_all();
+ }
+ } else if(flags & WorkerEvtDisconnect) {
+ worker_state = BadKbStateNotConnected; // Disconnected
+ if(bad_kb->bt) {
+ furi_hal_bt_hid_kb_release_all();
+ } else {
+ furi_hal_hid_kb_release_all();
+ }
+ }
+ bad_kb->st.state = worker_state;
+ continue;
+ } else if(
+ (flags == (unsigned)FuriFlagErrorTimeout) ||
+ (flags == (unsigned)FuriFlagErrorResource)) {
+ if(delay_val > 0) {
+ bad_kb->st.delay_remain--;
+ continue;
+ }
+ bad_kb->st.state = BadKbStateRunning;
+ delay_val = ducky_script_execute_next(bad_kb, script_file);
+ if(delay_val == SCRIPT_STATE_ERROR) { // Script error
+ delay_val = 0;
+ worker_state = BadKbStateScriptError;
+ bad_kb->st.state = worker_state;
+ } else if(delay_val == SCRIPT_STATE_END) { // End of script
+ delay_val = 0;
+ worker_state = BadKbStateIdle;
+ bad_kb->st.state = BadKbStateDone;
+ if(bad_kb->bt) {
+ furi_hal_bt_hid_kb_release_all();
+ } else {
+ furi_hal_hid_kb_release_all();
+ }
+ continue;
+ } else if(delay_val > 1000) {
+ bad_kb->st.state = BadKbStateDelay; // Show long delays
+ bad_kb->st.delay_remain = delay_val / 1000;
+ }
+ } else {
+ furi_check((flags & FuriFlagError) == 0);
+ }
+
+ } else if(
+ (worker_state == BadKbStateFileError) ||
+ (worker_state == BadKbStateScriptError)) { // State: error
+ uint32_t flags = furi_thread_flags_wait(
+ WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command
+ furi_check((flags & FuriFlagError) == 0);
+ if(flags & WorkerEvtEnd) {
+ break;
+ }
+ }
+ if(bad_kb->bt) {
+ update_bt_timeout(bad_kb->bt);
+ }
+ }
+
+ if(bad_kb->bt) {
+ bt_set_status_changed_callback(bad_kb->bt, NULL, NULL);
+ } else {
+ furi_hal_hid_set_state_callback(NULL, NULL);
+ }
+
+ storage_file_close(script_file);
+ storage_file_free(script_file);
+ furi_string_free(bad_kb->line);
+ furi_string_free(bad_kb->line_prev);
+
+ FURI_LOG_I(WORKER_TAG, "End");
+
+ return 0;
+}
+
+static void bad_kb_script_set_default_keyboard_layout(BadKbScript* bad_kb) {
+ furi_assert(bad_kb);
+ furi_string_set_str(bad_kb->keyboard_layout, "");
+ memset(bad_kb->layout, HID_KEYBOARD_NONE, sizeof(bad_kb->layout));
+ memcpy(bad_kb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_kb->layout)));
+}
+
+BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt) {
+ furi_assert(file_path);
+
+ BadKbScript* bad_kb = malloc(sizeof(BadKbScript));
+ bad_kb->file_path = furi_string_alloc();
+ furi_string_set(bad_kb->file_path, file_path);
+ bad_kb->keyboard_layout = furi_string_alloc();
+ bad_kb_script_set_default_keyboard_layout(bad_kb);
+
+ bad_kb->st.state = BadKbStateInit;
+ bad_kb->st.error[0] = '\0';
+
+ bad_kb->bt = bt;
+
+ bad_kb->thread = furi_thread_alloc_ex("BadKbWorker", 2048, bad_kb_worker, bad_kb);
+ furi_thread_start(bad_kb->thread);
+ return bad_kb;
+} //-V773
+
+void bad_kb_script_close(BadKbScript* bad_kb) {
+ furi_assert(bad_kb);
+ furi_record_close(RECORD_STORAGE);
+ furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtEnd);
+ furi_thread_join(bad_kb->thread);
+ furi_thread_free(bad_kb->thread);
+ furi_string_free(bad_kb->file_path);
+ furi_string_free(bad_kb->keyboard_layout);
+ free(bad_kb);
+}
+
+void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_path) {
+ furi_assert(bad_kb);
+
+ if((bad_kb->st.state == BadKbStateRunning) || (bad_kb->st.state == BadKbStateDelay)) {
+ // do not update keyboard layout while a script is running
+ return;
+ }
+
+ File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
+ if(!furi_string_empty(layout_path)) {
+ furi_string_set(bad_kb->keyboard_layout, layout_path);
+ if(storage_file_open(
+ layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
+ uint16_t layout[128];
+ if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) {
+ memcpy(bad_kb->layout, layout, sizeof(layout));
+ }
+ }
+ storage_file_close(layout_file);
+ } else {
+ bad_kb_script_set_default_keyboard_layout(bad_kb);
+ }
+ storage_file_free(layout_file);
+}
+
+void bad_kb_script_toggle(BadKbScript* bad_kb) {
+ furi_assert(bad_kb);
+ furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtToggle);
+}
+
+BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb) {
+ furi_assert(bad_kb);
+ return &(bad_kb->st);
+}
diff --git a/applications/main/bad_kb/bad_kb_script.h b/applications/main/bad_kb/bad_kb_script.h
new file mode 100644
index 000000000..0ea701eb8
--- /dev/null
+++ b/applications/main/bad_kb/bad_kb_script.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+typedef struct BadKbScript BadKbScript;
+
+typedef enum {
+ BadKbStateInit,
+ BadKbStateNotConnected,
+ BadKbStateIdle,
+ BadKbStateWillRun,
+ BadKbStateRunning,
+ BadKbStateDelay,
+ BadKbStateDone,
+ BadKbStateScriptError,
+ BadKbStateFileError,
+} BadKbWorkerState;
+
+typedef struct {
+ BadKbWorkerState state;
+ uint16_t line_cur;
+ uint16_t line_nb;
+ uint32_t delay_remain;
+ uint16_t error_line;
+ char error[64];
+} BadKbState;
+
+void bad_kb_connection_init(Bt* bt);
+
+void bad_kb_connection_deinit(Bt* bt);
+
+BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt);
+
+void bad_kb_script_close(BadKbScript* bad_kb);
+
+void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_path);
+
+void bad_kb_script_start(BadKbScript* bad_kb);
+
+void bad_kb_script_stop(BadKbScript* bad_kb);
+
+void bad_kb_script_toggle(BadKbScript* bad_kb);
+
+BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/applications/main/bad_kb/bad_kb_settings_filename.h b/applications/main/bad_kb/bad_kb_settings_filename.h
new file mode 100644
index 000000000..3eb7d3c0a
--- /dev/null
+++ b/applications/main/bad_kb/bad_kb_settings_filename.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define BAD_KB_SETTINGS_FILE_NAME ".badkb.settings"
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene.c b/applications/main/bad_kb/scenes/bad_kb_scene.c
new file mode 100644
index 000000000..f90d23a77
--- /dev/null
+++ b/applications/main/bad_kb/scenes/bad_kb_scene.c
@@ -0,0 +1,30 @@
+#include "bad_kb_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const bad_kb_scene_on_enter_handlers[])(void*) = {
+#include "bad_kb_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 bad_kb_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "bad_kb_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 bad_kb_scene_on_exit_handlers[])(void* context) = {
+#include "bad_kb_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers bad_kb_scene_handlers = {
+ .on_enter_handlers = bad_kb_scene_on_enter_handlers,
+ .on_event_handlers = bad_kb_scene_on_event_handlers,
+ .on_exit_handlers = bad_kb_scene_on_exit_handlers,
+ .scene_num = BadKbSceneNum,
+};
diff --git a/applications/main/bad_usb/scenes/bad_usb_scene.h b/applications/main/bad_kb/scenes/bad_kb_scene.h
similarity index 68%
rename from applications/main/bad_usb/scenes/bad_usb_scene.h
rename to applications/main/bad_kb/scenes/bad_kb_scene.h
index 68a753210..82db02873 100644
--- a/applications/main/bad_usb/scenes/bad_usb_scene.h
+++ b/applications/main/bad_kb/scenes/bad_kb_scene.h
@@ -3,27 +3,27 @@
#include
// Generate scene id and total number
-#define ADD_SCENE(prefix, name, id) BadUsbScene##id,
+#define ADD_SCENE(prefix, name, id) BadKbScene##id,
typedef enum {
-#include "bad_usb_scene_config.h"
- BadUsbSceneNum,
-} BadUsbScene;
+#include "bad_kb_scene_config.h"
+ BadKbSceneNum,
+} BadKbScene;
#undef ADD_SCENE
-extern const SceneManagerHandlers bad_usb_scene_handlers;
+extern const SceneManagerHandlers bad_kb_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
-#include "bad_usb_scene_config.h"
+#include "bad_kb_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 "bad_usb_scene_config.h"
+#include "bad_kb_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 "bad_usb_scene_config.h"
+#include "bad_kb_scene_config.h"
#undef ADD_SCENE
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.h b/applications/main/bad_kb/scenes/bad_kb_scene_config.h
new file mode 100644
index 000000000..794468eba
--- /dev/null
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.h
@@ -0,0 +1,8 @@
+ADD_SCENE(bad_kb, file_select, FileSelect)
+ADD_SCENE(bad_kb, work, Work)
+ADD_SCENE(bad_kb, error, Error)
+ADD_SCENE(bad_kb, config_bt, ConfigBt)
+ADD_SCENE(bad_kb, config_usb, ConfigUsb)
+ADD_SCENE(bad_kb, config_layout, ConfigLayout)
+ADD_SCENE(bad_kb, config_name, ConfigName)
+ADD_SCENE(bad_kb, config_mac, ConfigMac)
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c
new file mode 100644
index 000000000..4412f0796
--- /dev/null
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c
@@ -0,0 +1,87 @@
+#include "../bad_kb_app_i.h"
+#include "furi_hal_power.h"
+#include "furi_hal_usb.h"
+#include
+
+enum VarItemListIndex {
+ VarItemListIndexConnection,
+ VarItemListIndexKeyboardLayout,
+ VarItemListIndexAdvertisementName,
+ VarItemListIndexMacAddress,
+};
+
+void bad_kb_scene_config_bt_connection_callback(VariableItem* item) {
+ BadKbApp* bad_kb = variable_item_get_context(item);
+ bad_kb->is_bt = variable_item_get_current_value_index(item);
+ XTREME_SETTINGS()->bad_bt = bad_kb->is_bt;
+ XTREME_SETTINGS_SAVE();
+ variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB");
+ view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection);
+}
+
+void bad_kb_scene_config_bt_var_item_list_callback(void* context, uint32_t index) {
+ BadKbApp* bad_kb = context;
+ view_dispatcher_send_custom_event(bad_kb->view_dispatcher, index);
+}
+
+void bad_kb_scene_config_bt_on_enter(void* context) {
+ BadKbApp* bad_kb = context;
+ VariableItemList* var_item_list = bad_kb->var_item_list_bt;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list, "Connection", 2, bad_kb_scene_config_bt_connection_callback, bad_kb);
+ variable_item_set_current_value_index(item, bad_kb->is_bt);
+ variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB");
+
+ item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_kb);
+
+ item = variable_item_list_add(var_item_list, "BT device name", 0, NULL, bad_kb);
+
+ item = variable_item_list_add(var_item_list, "BT MAC address", 0, NULL, bad_kb);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, bad_kb_scene_config_bt_var_item_list_callback, bad_kb);
+
+ view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigBt);
+}
+
+bool bad_kb_scene_config_bt_on_event(void* context, SceneManagerEvent event) {
+ BadKbApp* bad_kb = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(bad_kb->scene_manager, BadKbSceneConfigBt, event.event);
+ consumed = true;
+ if(event.event == VarItemListIndexKeyboardLayout) {
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout);
+ } else if(event.event == VarItemListIndexConnection) {
+ bad_kb_script_close(bad_kb->bad_kb_script);
+ bad_kb_connection_deinit(bad_kb->bt);
+ bad_kb->bad_kb_script =
+ bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL);
+ bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout);
+ scene_manager_previous_scene(bad_kb->scene_manager);
+ if(bad_kb->is_bt) {
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt);
+ } else {
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb);
+ }
+ } else if(event.event == VarItemListIndexAdvertisementName) {
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigName);
+ } else if(event.event == VarItemListIndexMacAddress) {
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigMac);
+ // } else {
+ // furi_crash("Unknown key type");
+ }
+ }
+
+ return consumed;
+}
+
+void bad_kb_scene_config_bt_on_exit(void* context) {
+ BadKbApp* bad_kb = context;
+ VariableItemList* var_item_list = bad_kb->var_item_list_bt;
+
+ variable_item_list_reset(var_item_list);
+}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c
new file mode 100644
index 000000000..3842c59fa
--- /dev/null
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c
@@ -0,0 +1,51 @@
+#include "../bad_kb_app_i.h"
+#include "furi_hal_power.h"
+#include "furi_hal_usb.h"
+#include
+
+#define KEYBOARD_FOLDER "/ext/badkb/layouts"
+
+static bool bad_kb_layout_select(BadKbApp* bad_kb) {
+ furi_assert(bad_kb);
+
+ FuriString* predefined_path;
+ predefined_path = furi_string_alloc();
+ if(!furi_string_empty(bad_kb->keyboard_layout)) {
+ furi_string_set(predefined_path, bad_kb->keyboard_layout);
+ } else {
+ furi_string_set(predefined_path, BAD_KB_APP_PATH_LAYOUT_FOLDER);
+ }
+
+ DialogsFileBrowserOptions browser_options;
+ dialog_file_browser_set_basic_options(
+ &browser_options, BAD_KB_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
+ browser_options.base_path = KEYBOARD_FOLDER;
+
+ // Input events and views are managed by file_browser
+ bool res = dialog_file_browser_show(
+ bad_kb->dialogs, bad_kb->keyboard_layout, predefined_path, &browser_options);
+
+ furi_string_free(predefined_path);
+ return res;
+}
+
+void bad_kb_scene_config_layout_on_enter(void* context) {
+ BadKbApp* bad_kb = context;
+
+ if(bad_kb_layout_select(bad_kb)) {
+ bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout);
+ }
+ scene_manager_previous_scene(bad_kb->scene_manager);
+}
+
+bool bad_kb_scene_config_layout_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ // BadKbApp* bad_kb = context;
+ return false;
+}
+
+void bad_kb_scene_config_layout_on_exit(void* context) {
+ UNUSED(context);
+ // BadKbApp* bad_kb = context;
+}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c
new file mode 100644
index 000000000..0dc4be10a
--- /dev/null
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c
@@ -0,0 +1,57 @@
+#include "../bad_kb_app_i.h"
+
+#define TAG "BadKbConfigMac"
+
+static uint8_t* reverse_mac_addr(uint8_t* mac) {
+ uint8_t tmp;
+ for(int i = 0; i < 3; i++) {
+ tmp = mac[i];
+ mac[i] = mac[5 - i];
+ mac[5 - i] = tmp;
+ }
+ return mac;
+}
+
+void bad_kb_scene_config_mac_byte_input_callback(void* context) {
+ BadKbApp* bad_kb = context;
+
+ view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone);
+}
+
+void bad_kb_scene_config_mac_on_enter(void* context) {
+ BadKbApp* bad_kb = context;
+
+ // Setup view
+ ByteInput* byte_input = bad_kb->byte_input;
+ byte_input_set_header_text(byte_input, "Enter new MAC address");
+ byte_input_set_result_callback(
+ byte_input,
+ bad_kb_scene_config_mac_byte_input_callback,
+ NULL,
+ bad_kb,
+ reverse_mac_addr(bad_kb->mac),
+ GAP_MAC_ADDR_SIZE);
+ view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigMac);
+}
+
+bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) {
+ BadKbApp* bad_kb = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == BadKbAppCustomEventByteInputDone) {
+ bt_set_profile_mac_address(bad_kb->bt, reverse_mac_addr(bad_kb->mac));
+ scene_manager_previous_scene(bad_kb->scene_manager);
+ consumed = true;
+ }
+ }
+ return consumed;
+}
+
+void bad_kb_scene_config_mac_on_exit(void* context) {
+ BadKbApp* bad_kb = context;
+
+ // Clear view
+ byte_input_set_result_callback(bad_kb->byte_input, NULL, NULL, NULL, NULL, 0);
+ byte_input_set_header_text(bad_kb->byte_input, "");
+}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c
new file mode 100644
index 000000000..82dd7a850
--- /dev/null
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c
@@ -0,0 +1,45 @@
+#include "../bad_kb_app_i.h"
+
+static void bad_kb_scene_config_name_text_input_callback(void* context) {
+ BadKbApp* bad_kb = context;
+
+ view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextEditResult);
+}
+
+void bad_kb_scene_config_name_on_enter(void* context) {
+ BadKbApp* bad_kb = context;
+ TextInput* text_input = bad_kb->text_input;
+
+ text_input_set_header_text(text_input, "Set BLE adv name");
+
+ text_input_set_result_callback(
+ text_input,
+ bad_kb_scene_config_name_text_input_callback,
+ bad_kb,
+ bad_kb->name,
+ BAD_KB_ADV_NAME_MAX_LEN,
+ true);
+
+ view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigName);
+}
+
+bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) {
+ BadKbApp* bad_kb = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ consumed = true;
+ if(event.event == BadKbAppCustomEventTextEditResult) {
+ bt_set_profile_adv_name(bad_kb->bt, bad_kb->name);
+ }
+ scene_manager_previous_scene(bad_kb->scene_manager);
+ }
+ return consumed;
+}
+
+void bad_kb_scene_config_name_on_exit(void* context) {
+ BadKbApp* bad_kb = context;
+ TextInput* text_input = bad_kb->text_input;
+
+ text_input_reset(text_input);
+}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c
new file mode 100644
index 000000000..232ef8796
--- /dev/null
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c
@@ -0,0 +1,77 @@
+#include "../bad_kb_app_i.h"
+#include "furi_hal_power.h"
+#include "furi_hal_usb.h"
+#include
+
+enum VarItemListIndex {
+ VarItemListIndexConnection,
+ VarItemListIndexKeyboardLayout,
+};
+
+void bad_kb_scene_config_usb_connection_callback(VariableItem* item) {
+ BadKbApp* bad_kb = variable_item_get_context(item);
+ bad_kb->is_bt = variable_item_get_current_value_index(item);
+ XTREME_SETTINGS()->bad_bt = bad_kb->is_bt;
+ XTREME_SETTINGS_SAVE();
+ variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB");
+ view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection);
+}
+
+void bad_kb_scene_config_usb_var_item_list_callback(void* context, uint32_t index) {
+ BadKbApp* bad_kb = context;
+ view_dispatcher_send_custom_event(bad_kb->view_dispatcher, index);
+}
+
+void bad_kb_scene_config_usb_on_enter(void* context) {
+ BadKbApp* bad_kb = context;
+ VariableItemList* var_item_list = bad_kb->var_item_list_usb;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list, "Connection", 2, bad_kb_scene_config_usb_connection_callback, bad_kb);
+ variable_item_set_current_value_index(item, bad_kb->is_bt);
+ variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB");
+
+ item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_kb);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, bad_kb_scene_config_usb_var_item_list_callback, bad_kb);
+
+ view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigUsb);
+}
+
+bool bad_kb_scene_config_usb_on_event(void* context, SceneManagerEvent event) {
+ BadKbApp* bad_kb = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsb, event.event);
+ consumed = true;
+ if(event.event == VarItemListIndexKeyboardLayout) {
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout);
+ } else if(event.event == VarItemListIndexConnection) {
+ bad_kb_script_close(bad_kb->bad_kb_script);
+ bad_kb_connection_deinit(bad_kb->bt);
+ bad_kb->bad_kb_script =
+ bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL);
+ bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout);
+ scene_manager_previous_scene(bad_kb->scene_manager);
+ if(bad_kb->is_bt) {
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt);
+ } else {
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb);
+ }
+ // } else {
+ // furi_crash("Unknown key type");
+ }
+ }
+
+ return consumed;
+}
+
+void bad_kb_scene_config_usb_on_exit(void* context) {
+ BadKbApp* bad_kb = context;
+ VariableItemList* var_item_list = bad_kb->var_item_list_usb;
+
+ variable_item_list_reset(var_item_list);
+}
diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_kb/scenes/bad_kb_scene_error.c
similarity index 64%
rename from applications/main/bad_usb/scenes/bad_usb_scene_error.c
rename to applications/main/bad_kb/scenes/bad_kb_scene_error.c
index 2c707bbf1..bfec05652 100644
--- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_error.c
@@ -1,24 +1,20 @@
-#include "../bad_usb_app_i.h"
-#include "../../../settings/xtreme_settings/xtreme_settings.h"
-
-typedef enum {
- BadUsbCustomEventErrorBack,
-} BadUsbCustomEvent;
+#include "../bad_kb_app_i.h"
+#include "xtreme/assets.h"
static void
- bad_usb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {
+ bad_kb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
- BadUsbApp* app = context;
+ BadKbApp* app = context;
if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
- view_dispatcher_send_custom_event(app->view_dispatcher, BadUsbCustomEventErrorBack);
+ view_dispatcher_send_custom_event(app->view_dispatcher, BadKbCustomEventErrorBack);
}
}
-void bad_usb_scene_error_on_enter(void* context) {
- BadUsbApp* app = context;
+void bad_kb_scene_error_on_enter(void* context) {
+ BadKbApp* app = context;
- if(app->error == BadUsbAppErrorNoFiles) {
+ if(app->error == BadKbAppErrorNoFiles) {
widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43);
widget_add_string_multiline_element(
app->widget,
@@ -29,10 +25,10 @@ void bad_usb_scene_error_on_enter(void* context) {
FontSecondary,
"No SD card or\napp data found.\nThis app will not\nwork without\nrequired files.");
widget_add_button_element(
- app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app);
- } else if(app->error == BadUsbAppErrorCloseRpc) {
+ app->widget, GuiButtonTypeLeft, "Back", bad_kb_scene_error_event_callback, app);
+ } else if(app->error == BadKbAppErrorCloseRpc) {
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
- if(XTREME_SETTINGS()->nsfw_mode) {
+ if(XTREME_ASSETS()->is_nsfw) {
widget_add_string_multiline_element(
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!");
widget_add_string_multiline_element(
@@ -57,15 +53,15 @@ void bad_usb_scene_error_on_enter(void* context) {
}
}
- view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewError);
+ view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewError);
}
-bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) {
- BadUsbApp* app = context;
+bool bad_kb_scene_error_on_event(void* context, SceneManagerEvent event) {
+ BadKbApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
- if(event.event == BadUsbCustomEventErrorBack) {
+ if(event.event == BadKbCustomEventErrorBack) {
view_dispatcher_stop(app->view_dispatcher);
consumed = true;
}
@@ -73,7 +69,7 @@ bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) {
return consumed;
}
-void bad_usb_scene_error_on_exit(void* context) {
- BadUsbApp* app = context;
+void bad_kb_scene_error_on_exit(void* context) {
+ BadKbApp* app = context;
widget_reset(app->widget);
}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c
new file mode 100644
index 000000000..bb2ddf6ca
--- /dev/null
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c
@@ -0,0 +1,53 @@
+#include "../bad_kb_app_i.h"
+#include
+#include
+#include
+
+static bool bad_kb_file_select(BadKbApp* bad_kb) {
+ furi_assert(bad_kb);
+
+ DialogsFileBrowserOptions browser_options;
+ dialog_file_browser_set_basic_options(
+ &browser_options, BAD_KB_APP_SCRIPT_EXTENSION, &I_badkb_10px);
+ browser_options.base_path = BAD_KB_APP_BASE_FOLDER;
+ browser_options.skip_assets = true;
+
+ // Input events and views are managed by file_browser
+ bool res = dialog_file_browser_show(
+ bad_kb->dialogs, bad_kb->file_path, bad_kb->file_path, &browser_options);
+
+ return res;
+}
+
+void bad_kb_scene_file_select_on_enter(void* context) {
+ BadKbApp* bad_kb = context;
+
+ furi_hal_usb_disable();
+ if(bad_kb->bad_kb_script) {
+ bad_kb_script_close(bad_kb->bad_kb_script);
+ bad_kb->bad_kb_script = NULL;
+ }
+
+ if(bad_kb_file_select(bad_kb)) {
+ bad_kb->bad_kb_script =
+ bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL);
+ bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout);
+
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneWork);
+ } else {
+ furi_hal_usb_enable();
+ view_dispatcher_stop(bad_kb->view_dispatcher);
+ }
+}
+
+bool bad_kb_scene_file_select_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ // BadKbApp* bad_kb = context;
+ return false;
+}
+
+void bad_kb_scene_file_select_on_exit(void* context) {
+ UNUSED(context);
+ // BadKbApp* bad_kb = context;
+}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_work.c b/applications/main/bad_kb/scenes/bad_kb_scene_work.c
new file mode 100644
index 000000000..165ee0289
--- /dev/null
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_work.c
@@ -0,0 +1,58 @@
+#include "../bad_kb_script.h"
+#include "../bad_kb_app_i.h"
+#include "../views/bad_kb_view.h"
+#include
+#include "toolbox/path.h"
+
+void bad_kb_scene_work_button_callback(InputKey key, void* context) {
+ furi_assert(context);
+ BadKbApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, key);
+}
+
+bool bad_kb_scene_work_on_event(void* context, SceneManagerEvent event) {
+ BadKbApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == InputKeyLeft) {
+ if(app->is_bt) {
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt);
+ } else {
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb);
+ }
+ consumed = true;
+ } else if(event.event == InputKeyOk) {
+ bad_kb_script_toggle(app->bad_kb_script);
+ consumed = true;
+ }
+ } else if(event.type == SceneManagerEventTypeTick) {
+ bad_kb_set_state(app->bad_kb_view, bad_kb_script_get_state(app->bad_kb_script));
+ }
+ return consumed;
+}
+
+void bad_kb_scene_work_on_enter(void* context) {
+ BadKbApp* app = context;
+
+ FuriString* file_name;
+ file_name = furi_string_alloc();
+ path_extract_filename(app->file_path, file_name, true);
+ bad_kb_set_file_name(app->bad_kb_view, furi_string_get_cstr(file_name));
+ furi_string_free(file_name);
+
+ FuriString* layout;
+ layout = furi_string_alloc();
+ path_extract_filename(app->keyboard_layout, layout, true);
+ bad_kb_set_layout(app->bad_kb_view, furi_string_get_cstr(layout));
+ furi_string_free(layout);
+
+ bad_kb_set_state(app->bad_kb_view, bad_kb_script_get_state(app->bad_kb_script));
+
+ bad_kb_set_button_callback(app->bad_kb_view, bad_kb_scene_work_button_callback, app);
+ view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewWork);
+}
+
+void bad_kb_scene_work_on_exit(void* context) {
+ UNUSED(context);
+}
diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_kb/views/bad_kb_view.c
similarity index 65%
rename from applications/main/bad_usb/views/bad_usb_view.c
rename to applications/main/bad_kb/views/bad_kb_view.c
index ad889cd1c..1ffe58e9c 100644
--- a/applications/main/bad_usb/views/bad_usb_view.c
+++ b/applications/main/bad_kb/views/bad_kb_view.c
@@ -1,34 +1,33 @@
-#include "bad_usb_view.h"
-#include "../bad_usb_script.h"
+#include "bad_kb_view.h"
+#include "../bad_kb_script.h"
#include
#include
#include
-#include "../../../settings/xtreme_settings/xtreme_settings.h"
+#include "xtreme/assets.h"
#define MAX_NAME_LEN 64
-struct BadUsb {
+struct BadKb {
View* view;
- BadUsbButtonCallback callback;
+ BadKbButtonCallback callback;
void* context;
};
typedef struct {
char file_name[MAX_NAME_LEN];
char layout[MAX_NAME_LEN];
- BadUsbState state;
+ BadKbState state;
uint8_t anim_frame;
-} BadUsbModel;
+} BadKbModel;
-static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
- BadUsbModel* model = _model;
+static void bad_kb_draw_callback(Canvas* canvas, void* _model) {
+ BadKbModel* model = _model;
FuriString* disp_str;
disp_str = furi_string_alloc_set(model->file_name);
elements_string_fit_width(canvas, disp_str, 128 - 2);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
- XtremeSettings* xtreme_settings = XTREME_SETTINGS();
if(strlen(model->layout) == 0) {
furi_string_set(disp_str, "(default)");
@@ -47,49 +46,49 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22);
- if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) ||
- (model->state.state == BadUsbStateNotConnected)) {
- if(xtreme_settings->nsfw_mode) {
+ if((model->state.state == BadKbStateIdle) || (model->state.state == BadKbStateDone) ||
+ (model->state.state == BadKbStateNotConnected)) {
+ if(XTREME_ASSETS()->is_nsfw) {
elements_button_center(canvas, "Cum");
} else {
elements_button_center(canvas, "Start");
}
- } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) {
+ } else if((model->state.state == BadKbStateRunning) || (model->state.state == BadKbStateDelay)) {
elements_button_center(canvas, "Stop");
- } else if(model->state.state == BadUsbStateWillRun) {
+ } else if(model->state.state == BadKbStateWillRun) {
elements_button_center(canvas, "Cancel");
}
- if((model->state.state == BadUsbStateNotConnected) ||
- (model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) {
+ if((model->state.state == BadKbStateNotConnected) || (model->state.state == BadKbStateIdle) ||
+ (model->state.state == BadKbStateDone)) {
elements_button_left(canvas, "Config");
}
- if(model->state.state == BadUsbStateNotConnected) {
+ if(model->state.state == BadKbStateNotConnected) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
- if(xtreme_settings->nsfw_mode) {
+ if(XTREME_ASSETS()->is_nsfw) {
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy");
} else {
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect to");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "a device");
}
- } else if(model->state.state == BadUsbStateWillRun) {
+ } else if(model->state.state == BadKbStateWillRun) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
- if(xtreme_settings->nsfw_mode) {
+ if(XTREME_ASSETS()->is_nsfw) {
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum");
} else {
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
}
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
- } else if(model->state.state == BadUsbStateFileError) {
+ } else if(model->state.state == BadKbStateFileError) {
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR");
- } else if(model->state.state == BadUsbStateScriptError) {
+ } else if(model->state.state == BadKbStateScriptError) {
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:");
@@ -99,12 +98,12 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error);
- } else if(model->state.state == BadUsbStateIdle) {
+ } else if(model->state.state == BadKbStateIdle) {
canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0");
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
- } else if(model->state.state == BadUsbStateRunning) {
+ } else if(model->state.state == BadKbStateRunning) {
if(model->anim_frame == 0) {
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
} else {
@@ -117,13 +116,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
- } else if(model->state.state == BadUsbStateDone) {
+ } else if(model->state.state == BadKbStateDone) {
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100");
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
- } else if(model->state.state == BadUsbStateDelay) {
+ } else if(model->state.state == BadKbStateDelay) {
if(model->anim_frame == 0) {
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
} else {
@@ -148,84 +147,78 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
furi_string_free(disp_str);
}
-static bool bad_usb_input_callback(InputEvent* event, void* context) {
+static bool bad_kb_input_callback(InputEvent* event, void* context) {
furi_assert(context);
- BadUsb* bad_usb = context;
+ BadKb* bad_kb = context;
bool consumed = false;
if(event->type == InputTypeShort) {
if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) {
consumed = true;
- furi_assert(bad_usb->callback);
- bad_usb->callback(event->key, bad_usb->context);
+ furi_assert(bad_kb->callback);
+ bad_kb->callback(event->key, bad_kb->context);
}
}
return consumed;
}
-BadUsb* bad_usb_alloc() {
- BadUsb* bad_usb = malloc(sizeof(BadUsb));
+BadKb* bad_kb_alloc() {
+ BadKb* bad_kb = malloc(sizeof(BadKb));
- bad_usb->view = view_alloc();
- view_allocate_model(bad_usb->view, ViewModelTypeLocking, sizeof(BadUsbModel));
- view_set_context(bad_usb->view, bad_usb);
- view_set_draw_callback(bad_usb->view, bad_usb_draw_callback);
- view_set_input_callback(bad_usb->view, bad_usb_input_callback);
+ bad_kb->view = view_alloc();
+ view_allocate_model(bad_kb->view, ViewModelTypeLocking, sizeof(BadKbModel));
+ view_set_context(bad_kb->view, bad_kb);
+ view_set_draw_callback(bad_kb->view, bad_kb_draw_callback);
+ view_set_input_callback(bad_kb->view, bad_kb_input_callback);
- return bad_usb;
+ return bad_kb;
}
-void bad_usb_free(BadUsb* bad_usb) {
- furi_assert(bad_usb);
- view_free(bad_usb->view);
- free(bad_usb);
+void bad_kb_free(BadKb* bad_kb) {
+ furi_assert(bad_kb);
+ view_free(bad_kb->view);
+ free(bad_kb);
}
-View* bad_usb_get_view(BadUsb* bad_usb) {
- furi_assert(bad_usb);
- return bad_usb->view;
+View* bad_kb_get_view(BadKb* bad_kb) {
+ furi_assert(bad_kb);
+ return bad_kb->view;
}
-void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context) {
- furi_assert(bad_usb);
+void bad_kb_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context) {
+ furi_assert(bad_kb);
furi_assert(callback);
with_view_model(
- bad_usb->view,
- BadUsbModel * model,
+ bad_kb->view,
+ BadKbModel * model,
{
UNUSED(model);
- bad_usb->callback = callback;
- bad_usb->context = context;
+ bad_kb->callback = callback;
+ bad_kb->context = context;
},
true);
}
-void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) {
+void bad_kb_set_file_name(BadKb* bad_kb, const char* name) {
furi_assert(name);
with_view_model(
- bad_usb->view,
- BadUsbModel * model,
- { strlcpy(model->file_name, name, MAX_NAME_LEN); },
- true);
+ bad_kb->view, BadKbModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true);
}
-void bad_usb_set_layout(BadUsb* bad_usb, const char* layout) {
+void bad_kb_set_layout(BadKb* bad_kb, const char* layout) {
furi_assert(layout);
with_view_model(
- bad_usb->view,
- BadUsbModel * model,
- { strlcpy(model->layout, layout, MAX_NAME_LEN); },
- true);
+ bad_kb->view, BadKbModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true);
}
-void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) {
+void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) {
furi_assert(st);
with_view_model(
- bad_usb->view,
- BadUsbModel * model,
+ bad_kb->view,
+ BadKbModel * model,
{
- memcpy(&(model->state), st, sizeof(BadUsbState));
+ memcpy(&(model->state), st, sizeof(BadKbState));
model->anim_frame ^= 1;
},
true);
diff --git a/applications/main/bad_kb/views/bad_kb_view.h b/applications/main/bad_kb/views/bad_kb_view.h
new file mode 100644
index 000000000..24fdf4792
--- /dev/null
+++ b/applications/main/bad_kb/views/bad_kb_view.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+#include "../bad_kb_script.h"
+
+typedef struct BadKb BadKb;
+typedef void (*BadKbButtonCallback)(InputKey key, void* context);
+
+BadKb* bad_kb_alloc();
+
+void bad_kb_free(BadKb* bad_kb);
+
+View* bad_kb_get_view(BadKb* bad_kb);
+
+void bad_kb_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context);
+
+void bad_kb_set_file_name(BadKb* bad_kb, const char* name);
+
+void bad_kb_set_layout(BadKb* bad_kb, const char* layout);
+
+void bad_kb_set_state(BadKb* bad_kb, BadKbState* st);
diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c
deleted file mode 100644
index bb29d3be5..000000000
--- a/applications/main/bad_usb/bad_usb_app.c
+++ /dev/null
@@ -1,160 +0,0 @@
-#include "bad_usb_app_i.h"
-#include "bad_usb_settings_filename.h"
-#include
-#include
-#include
-#include
-
-#define BAD_USB_SETTINGS_PATH BAD_USB_APP_BASE_FOLDER "/" BAD_USB_SETTINGS_FILE_NAME
-
-static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) {
- furi_assert(context);
- BadUsbApp* app = context;
- return scene_manager_handle_custom_event(app->scene_manager, event);
-}
-
-static bool bad_usb_app_back_event_callback(void* context) {
- furi_assert(context);
- BadUsbApp* app = context;
- return scene_manager_handle_back_event(app->scene_manager);
-}
-
-static void bad_usb_app_tick_event_callback(void* context) {
- furi_assert(context);
- BadUsbApp* app = context;
- scene_manager_handle_tick_event(app->scene_manager);
-}
-
-static void bad_usb_load_settings(BadUsbApp* app) {
- File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
- if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
- char chr;
- while((storage_file_read(settings_file, &chr, 1) == 1) &&
- !storage_file_eof(settings_file) && !isspace(chr)) {
- furi_string_push_back(app->keyboard_layout, chr);
- }
- }
- storage_file_close(settings_file);
- storage_file_free(settings_file);
-}
-
-static void bad_usb_save_settings(BadUsbApp* app) {
- File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
- if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
- storage_file_write(
- settings_file,
- furi_string_get_cstr(app->keyboard_layout),
- furi_string_size(app->keyboard_layout));
- storage_file_write(settings_file, "\n", 1);
- }
- storage_file_close(settings_file);
- storage_file_free(settings_file);
-}
-
-BadUsbApp* bad_usb_app_alloc(char* arg) {
- BadUsbApp* app = malloc(sizeof(BadUsbApp));
-
- app->bad_usb_script = NULL;
-
- app->file_path = furi_string_alloc();
- app->keyboard_layout = furi_string_alloc();
- if(arg && strlen(arg)) {
- furi_string_set(app->file_path, arg);
- }
-
- bad_usb_load_settings(app);
-
- app->gui = furi_record_open(RECORD_GUI);
- app->notifications = furi_record_open(RECORD_NOTIFICATION);
- app->dialogs = furi_record_open(RECORD_DIALOGS);
-
- app->view_dispatcher = view_dispatcher_alloc();
- view_dispatcher_enable_queue(app->view_dispatcher);
-
- app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app);
-
- view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
- view_dispatcher_set_tick_event_callback(
- app->view_dispatcher, bad_usb_app_tick_event_callback, 500);
- view_dispatcher_set_custom_event_callback(
- app->view_dispatcher, bad_usb_app_custom_event_callback);
- view_dispatcher_set_navigation_event_callback(
- app->view_dispatcher, bad_usb_app_back_event_callback);
-
- // Custom Widget
- app->widget = widget_alloc();
- view_dispatcher_add_view(
- app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget));
-
- app->submenu = submenu_alloc();
- view_dispatcher_add_view(
- app->view_dispatcher, BadUsbAppViewConfig, submenu_get_view(app->submenu));
-
- app->bad_usb_view = bad_usb_alloc();
- view_dispatcher_add_view(
- app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view));
-
- view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
-
- if(furi_hal_usb_is_locked()) {
- app->error = BadUsbAppErrorCloseRpc;
- scene_manager_next_scene(app->scene_manager, BadUsbSceneError);
- } else {
- if(!furi_string_empty(app->file_path)) {
- app->bad_usb_script = bad_usb_script_open(app->file_path);
- bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout);
- scene_manager_next_scene(app->scene_manager, BadUsbSceneWork);
- } else {
- furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER);
- scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect);
- }
- }
-
- return app;
-}
-
-void bad_usb_app_free(BadUsbApp* app) {
- furi_assert(app);
-
- if(app->bad_usb_script) {
- bad_usb_script_close(app->bad_usb_script);
- app->bad_usb_script = NULL;
- }
-
- // Views
- view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork);
- bad_usb_free(app->bad_usb_view);
-
- // Custom Widget
- view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError);
- widget_free(app->widget);
-
- // Submenu
- view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig);
- submenu_free(app->submenu);
-
- // View dispatcher
- view_dispatcher_free(app->view_dispatcher);
- scene_manager_free(app->scene_manager);
-
- // Close records
- furi_record_close(RECORD_GUI);
- furi_record_close(RECORD_NOTIFICATION);
- furi_record_close(RECORD_DIALOGS);
-
- bad_usb_save_settings(app);
-
- furi_string_free(app->file_path);
- furi_string_free(app->keyboard_layout);
-
- free(app);
-}
-
-int32_t bad_usb_app(void* p) {
- BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p);
-
- view_dispatcher_run(bad_usb_app->view_dispatcher);
-
- bad_usb_app_free(bad_usb_app);
- return 0;
-}
diff --git a/applications/main/bad_usb/bad_usb_app.h b/applications/main/bad_usb/bad_usb_app.h
deleted file mode 100644
index afadd87e9..000000000
--- a/applications/main/bad_usb/bad_usb_app.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct BadUsbApp BadUsbApp;
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h
deleted file mode 100644
index b3fbb1679..000000000
--- a/applications/main/bad_usb/bad_usb_app_i.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-#include "bad_usb_app.h"
-#include "scenes/bad_usb_scene.h"
-#include "bad_usb_script.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "views/bad_usb_view.h"
-
-#define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb")
-#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/layouts"
-#define BAD_USB_APP_SCRIPT_EXTENSION ".txt"
-#define BAD_USB_APP_LAYOUT_EXTENSION ".kl"
-
-typedef enum {
- BadUsbAppErrorNoFiles,
- BadUsbAppErrorCloseRpc,
-} BadUsbAppError;
-
-struct BadUsbApp {
- Gui* gui;
- ViewDispatcher* view_dispatcher;
- SceneManager* scene_manager;
- NotificationApp* notifications;
- DialogsApp* dialogs;
- Widget* widget;
- Submenu* submenu;
-
- BadUsbAppError error;
- FuriString* file_path;
- FuriString* keyboard_layout;
- BadUsb* bad_usb_view;
- BadUsbScript* bad_usb_script;
-};
-
-typedef enum {
- BadUsbAppViewError,
- BadUsbAppViewWork,
- BadUsbAppViewConfig,
-} BadUsbAppView;
\ No newline at end of file
diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c
deleted file mode 100644
index 1a73150a5..000000000
--- a/applications/main/bad_usb/bad_usb_script.c
+++ /dev/null
@@ -1,724 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "bad_usb_script.h"
-#include
-
-#define TAG "BadUSB"
-#define WORKER_TAG TAG "Worker"
-#define FILE_BUFFER_LEN 16
-
-#define SCRIPT_STATE_ERROR (-1)
-#define SCRIPT_STATE_END (-2)
-#define SCRIPT_STATE_NEXT_LINE (-3)
-
-#define BADUSB_ASCII_TO_KEY(script, x) \
- (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
-
-typedef enum {
- WorkerEvtToggle = (1 << 0),
- WorkerEvtEnd = (1 << 1),
- WorkerEvtConnect = (1 << 2),
- WorkerEvtDisconnect = (1 << 3),
-} WorkerEvtFlags;
-
-struct BadUsbScript {
- FuriHalUsbHidConfig hid_cfg;
- BadUsbState st;
- FuriString* file_path;
- uint32_t defdelay;
- uint16_t layout[128];
- FuriThread* thread;
- uint8_t file_buf[FILE_BUFFER_LEN + 1];
- uint8_t buf_start;
- uint8_t buf_len;
- bool file_end;
- FuriString* line;
-
- FuriString* line_prev;
- uint32_t repeat_cnt;
-};
-
-typedef struct {
- char* name;
- uint16_t keycode;
-} DuckyKey;
-
-static const DuckyKey ducky_keys[] = {
- {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT},
- {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT},
- {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
- {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
- {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT},
-
- {"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", HID_KEYBOARD_DOWN_ARROW},
- {"DOWN", HID_KEYBOARD_DOWN_ARROW},
- {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW},
- {"LEFT", HID_KEYBOARD_LEFT_ARROW},
- {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW},
- {"RIGHT", HID_KEYBOARD_RIGHT_ARROW},
- {"UPARROW", HID_KEYBOARD_UP_ARROW},
- {"UP", HID_KEYBOARD_UP_ARROW},
-
- {"ENTER", HID_KEYBOARD_RETURN},
- {"BREAK", HID_KEYBOARD_PAUSE},
- {"PAUSE", HID_KEYBOARD_PAUSE},
- {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
- {"DELETE", HID_KEYBOARD_DELETE_FORWARD},
- {"BACKSPACE", HID_KEYBOARD_DELETE},
- {"END", HID_KEYBOARD_END},
- {"ESC", HID_KEYBOARD_ESCAPE},
- {"ESCAPE", HID_KEYBOARD_ESCAPE},
- {"HOME", HID_KEYBOARD_HOME},
- {"INSERT", HID_KEYBOARD_INSERT},
- {"NUMLOCK", HID_KEYPAD_NUMLOCK},
- {"PAGEUP", HID_KEYBOARD_PAGE_UP},
- {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
- {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
- {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
- {"SPACE", HID_KEYBOARD_SPACEBAR},
- {"TAB", HID_KEYBOARD_TAB},
- {"MENU", HID_KEYBOARD_APPLICATION},
- {"APP", HID_KEYBOARD_APPLICATION},
-
- {"F1", HID_KEYBOARD_F1},
- {"F2", HID_KEYBOARD_F2},
- {"F3", HID_KEYBOARD_F3},
- {"F4", HID_KEYBOARD_F4},
- {"F5", HID_KEYBOARD_F5},
- {"F6", HID_KEYBOARD_F6},
- {"F7", HID_KEYBOARD_F7},
- {"F8", HID_KEYBOARD_F8},
- {"F9", HID_KEYBOARD_F9},
- {"F10", HID_KEYBOARD_F10},
- {"F11", HID_KEYBOARD_F11},
- {"F12", HID_KEYBOARD_F12},
-};
-
-static const char ducky_cmd_comment[] = {"REM"};
-static const char ducky_cmd_id[] = {"ID"};
-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 const char ducky_cmd_repeat[] = {"REPEAT "};
-static const char ducky_cmd_sysrq[] = {"SYSRQ "};
-
-static const char ducky_cmd_altchar[] = {"ALTCHAR "};
-static const char ducky_cmd_altstr_1[] = {"ALTSTRING "};
-static const char ducky_cmd_altstr_2[] = {"ALTCODE "};
-
-static const char ducky_cmd_lang[] = {"DUCKY_LANG"};
-
-static const uint8_t numpad_keys[10] = {
- HID_KEYPAD_0,
- HID_KEYPAD_1,
- HID_KEYPAD_2,
- HID_KEYPAD_3,
- HID_KEYPAD_4,
- HID_KEYPAD_5,
- HID_KEYPAD_6,
- HID_KEYPAD_7,
- HID_KEYPAD_8,
- HID_KEYPAD_9,
-};
-
-static bool ducky_get_number(const char* param, uint32_t* val) {
- uint32_t value = 0;
- if(sscanf(param, "%lu", &value) == 1) {
- *val = value;
- return true;
- }
- return false;
-}
-
-static uint32_t ducky_get_command_len(const char* line) {
- uint32_t len = strlen(line);
- for(uint32_t i = 0; i < len; i++) {
- if(line[i] == ' ') return i;
- }
- return 0;
-}
-
-static bool ducky_is_line_end(const char chr) {
- return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n'));
-}
-
-static void ducky_numlock_on() {
- if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) {
- furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK);
- furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK);
- }
-}
-
-static bool ducky_numpad_press(const char num) {
- if((num < '0') || (num > '9')) return false;
-
- uint16_t key = numpad_keys[num - '0'];
- furi_hal_hid_kb_press(key);
- furi_hal_hid_kb_release(key);
-
- return true;
-}
-
-static bool ducky_altchar(const char* charcode) {
- uint8_t i = 0;
- bool state = false;
-
- FURI_LOG_I(WORKER_TAG, "char %s", charcode);
-
- furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT);
-
- while(!ducky_is_line_end(charcode[i])) {
- state = ducky_numpad_press(charcode[i]);
- if(state == false) break;
- i++;
- }
-
- furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT);
- return state;
-}
-
-static bool ducky_altstring(const char* param) {
- uint32_t i = 0;
- bool state = false;
-
- while(param[i] != '\0') {
- if((param[i] < ' ') || (param[i] > '~')) {
- i++;
- continue; // Skip non-printable chars
- }
-
- char temp_str[4];
- snprintf(temp_str, 4, "%u", param[i]);
-
- state = ducky_altchar(temp_str);
- if(state == false) break;
- i++;
- }
- return state;
-}
-
-static bool ducky_string(BadUsbScript* bad_usb, const char* param) {
- uint32_t i = 0;
- while(param[i] != '\0') {
- uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]);
- if(keycode != HID_KEYBOARD_NONE) {
- furi_hal_hid_kb_press(keycode);
- furi_hal_hid_kb_release(keycode);
- }
- i++;
- }
- return true;
-}
-
-static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) {
- for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
- size_t key_cmd_len = strlen(ducky_keys[i].name);
- if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
- (ducky_is_line_end(param[key_cmd_len]))) {
- return ducky_keys[i].keycode;
- }
- }
- if((accept_chars) && (strlen(param) > 0)) {
- return (BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF);
- }
- return 0;
-}
-
-static int32_t
- ducky_parse_line(BadUsbScript* bad_usb, FuriString* line, char* error, size_t error_len) {
- uint32_t line_len = furi_string_size(line);
- const char* line_tmp = furi_string_get_cstr(line);
- bool state = false;
-
- if(line_len == 0) {
- return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
- }
-
- FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
-
- // General commands
- if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) {
- // REM - comment line
- return (0);
- } else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
- // ID - executed in ducky_script_preload
- return (0);
- } else if(strncmp(line_tmp, ducky_cmd_lang, strlen(ducky_cmd_lang)) == 0) {
- // DUCKY_LANG - ignore command to retain compatibility with existing scripts
- return (0);
- } else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) {
- // DELAY
- line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
- uint32_t delay_val = 0;
- state = ducky_get_number(line_tmp, &delay_val);
- if((state) && (delay_val > 0)) {
- return (int32_t)delay_val;
- }
- if(error != NULL) {
- snprintf(error, error_len, "Invalid number %s", line_tmp);
- }
- return SCRIPT_STATE_ERROR;
- } else if(
- (strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
- (strncmp(line_tmp, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) {
- // DEFAULT_DELAY
- line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
- state = ducky_get_number(line_tmp, &bad_usb->defdelay);
- if(!state && error != NULL) {
- snprintf(error, error_len, "Invalid number %s", line_tmp);
- }
- return (state) ? (0) : SCRIPT_STATE_ERROR;
- } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
- // STRING
- line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
- state = ducky_string(bad_usb, line_tmp);
- if(!state && error != NULL) {
- snprintf(error, error_len, "Invalid string %s", line_tmp);
- }
- return (state) ? (0) : SCRIPT_STATE_ERROR;
- } else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
- // ALTCHAR
- line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
- ducky_numlock_on();
- state = ducky_altchar(line_tmp);
- if(!state && error != NULL) {
- snprintf(error, error_len, "Invalid altchar %s", line_tmp);
- }
- return (state) ? (0) : SCRIPT_STATE_ERROR;
- } else if(
- (strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) ||
- (strncmp(line_tmp, ducky_cmd_altstr_2, strlen(ducky_cmd_altstr_2)) == 0)) {
- // ALTSTRING
- line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
- ducky_numlock_on();
- state = ducky_altstring(line_tmp);
- if(!state && error != NULL) {
- snprintf(error, error_len, "Invalid altstring %s", line_tmp);
- }
- return (state) ? (0) : SCRIPT_STATE_ERROR;
- } else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
- // REPEAT
- line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
- state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt);
- if(!state && error != NULL) {
- snprintf(error, error_len, "Invalid number %s", line_tmp);
- }
- return (state) ? (0) : SCRIPT_STATE_ERROR;
- } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
- // SYSRQ
- line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
- uint16_t key = ducky_get_keycode(bad_usb, line_tmp, true);
- furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
- furi_hal_hid_kb_press(key);
- furi_hal_hid_kb_release_all();
- return (0);
- } else {
- // Special keys + modifiers
- uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false);
- if(key == HID_KEYBOARD_NONE) {
- if(error != NULL) {
- snprintf(error, error_len, "No keycode defined for %s", line_tmp);
- }
- return SCRIPT_STATE_ERROR;
- }
- if((key & 0xFF00) != 0) {
- // It's a modifier key
- line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
- key |= ducky_get_keycode(bad_usb, line_tmp, true);
- }
- furi_hal_hid_kb_press(key);
- furi_hal_hid_kb_release(key);
- return (0);
- }
-}
-
-static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
- if(sscanf(line, "%lX:%lX", &bad_usb->hid_cfg.vid, &bad_usb->hid_cfg.pid) == 2) {
- bad_usb->hid_cfg.manuf[0] = '\0';
- bad_usb->hid_cfg.product[0] = '\0';
-
- uint8_t id_len = ducky_get_command_len(line);
- if(!ducky_is_line_end(line[id_len + 1])) {
- sscanf(
- &line[id_len + 1],
- "%31[^\r\n:]:%31[^\r\n]",
- bad_usb->hid_cfg.manuf,
- bad_usb->hid_cfg.product);
- }
- FURI_LOG_D(
- WORKER_TAG,
- "set id: %04lX:%04lX mfr:%s product:%s",
- bad_usb->hid_cfg.vid,
- bad_usb->hid_cfg.pid,
- bad_usb->hid_cfg.manuf,
- bad_usb->hid_cfg.product);
- return true;
- }
- return false;
-}
-
-static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) {
- uint8_t ret = 0;
- uint32_t line_len = 0;
-
- furi_string_reset(bad_usb->line);
-
- do {
- ret = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN);
- for(uint16_t i = 0; i < ret; i++) {
- if(bad_usb->file_buf[i] == '\n' && line_len > 0) {
- bad_usb->st.line_nb++;
- line_len = 0;
- } else {
- if(bad_usb->st.line_nb == 0) { // Save first line
- furi_string_push_back(bad_usb->line, bad_usb->file_buf[i]);
- }
- line_len++;
- }
- }
- if(storage_file_eof(script_file)) {
- if(line_len > 0) {
- bad_usb->st.line_nb++;
- break;
- }
- }
- } while(ret > 0);
-
- const char* line_tmp = furi_string_get_cstr(bad_usb->line);
- bool id_set = false; // Looking for ID command at first line
- if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
- id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]);
- }
-
- if(id_set) {
- furi_check(furi_hal_usb_set_config(&usb_hid, &bad_usb->hid_cfg));
- } else {
- furi_check(furi_hal_usb_set_config(&usb_hid, NULL));
- }
-
- storage_file_seek(script_file, 0, true);
- furi_string_reset(bad_usb->line);
-
- return true;
-}
-
-static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_file) {
- int32_t delay_val = 0;
-
- if(bad_usb->repeat_cnt > 0) {
- bad_usb->repeat_cnt--;
- delay_val = ducky_parse_line(
- bad_usb, bad_usb->line_prev, bad_usb->st.error, sizeof(bad_usb->st.error));
- if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
- return 0;
- } else if(delay_val < 0) { // Script error
- bad_usb->st.error_line = bad_usb->st.line_cur - 1;
- FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur - 1U);
- return SCRIPT_STATE_ERROR;
- } else {
- return (delay_val + bad_usb->defdelay);
- }
- }
-
- furi_string_set(bad_usb->line_prev, bad_usb->line);
- furi_string_reset(bad_usb->line);
-
- while(1) {
- if(bad_usb->buf_len == 0) {
- bad_usb->buf_len = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN);
- if(storage_file_eof(script_file)) {
- if((bad_usb->buf_len < FILE_BUFFER_LEN) && (bad_usb->file_end == false)) {
- bad_usb->file_buf[bad_usb->buf_len] = '\n';
- bad_usb->buf_len++;
- bad_usb->file_end = true;
- }
- }
-
- bad_usb->buf_start = 0;
- if(bad_usb->buf_len == 0) return SCRIPT_STATE_END;
- }
- for(uint8_t i = bad_usb->buf_start; i < (bad_usb->buf_start + bad_usb->buf_len); i++) {
- if(bad_usb->file_buf[i] == '\n' && furi_string_size(bad_usb->line) > 0) {
- bad_usb->st.line_cur++;
- bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
- bad_usb->buf_start = i + 1;
- furi_string_trim(bad_usb->line);
- delay_val = ducky_parse_line(
- bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error));
- if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
- return 0;
- } else if(delay_val < 0) {
- bad_usb->st.error_line = bad_usb->st.line_cur;
- FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
- return SCRIPT_STATE_ERROR;
- } else {
- return (delay_val + bad_usb->defdelay);
- }
- } else {
- furi_string_push_back(bad_usb->line, bad_usb->file_buf[i]);
- }
- }
- bad_usb->buf_len = 0;
- if(bad_usb->file_end) return SCRIPT_STATE_END;
- }
-
- return 0;
-}
-
-static void bad_usb_hid_state_callback(bool state, void* context) {
- furi_assert(context);
- BadUsbScript* bad_usb = context;
-
- if(state == true)
- furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect);
- else
- furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect);
-}
-
-static int32_t bad_usb_worker(void* context) {
- BadUsbScript* bad_usb = context;
-
- BadUsbWorkerState worker_state = BadUsbStateInit;
- int32_t delay_val = 0;
-
- FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
-
- FURI_LOG_I(WORKER_TAG, "Init");
- File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
- bad_usb->line = furi_string_alloc();
- bad_usb->line_prev = furi_string_alloc();
-
- furi_hal_hid_set_state_callback(bad_usb_hid_state_callback, bad_usb);
-
- while(1) {
- if(worker_state == BadUsbStateInit) { // State: initialization
- if(storage_file_open(
- script_file,
- furi_string_get_cstr(bad_usb->file_path),
- FSAM_READ,
- FSOM_OPEN_EXISTING)) {
- if((ducky_script_preload(bad_usb, script_file)) && (bad_usb->st.line_nb > 0)) {
- if(furi_hal_hid_is_connected()) {
- worker_state = BadUsbStateIdle; // Ready to run
- } else {
- worker_state = BadUsbStateNotConnected; // USB not connected
- }
- } else {
- worker_state = BadUsbStateScriptError; // Script preload error
- }
- } else {
- FURI_LOG_E(WORKER_TAG, "File open error");
- worker_state = BadUsbStateFileError; // File open error
- }
- bad_usb->st.state = worker_state;
-
- } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
- FuriFlagWaitAny,
- FuriWaitForever);
- furi_check((flags & FuriFlagError) == 0);
- if(flags & WorkerEvtEnd) {
- break;
- } else if(flags & WorkerEvtConnect) {
- worker_state = BadUsbStateIdle; // Ready to run
- } else if(flags & WorkerEvtToggle) {
- worker_state = BadUsbStateWillRun; // Will run when USB is connected
- }
- bad_usb->st.state = worker_state;
-
- } else if(worker_state == BadUsbStateIdle) { // State: ready to start
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
- FuriFlagWaitAny,
- FuriWaitForever);
- furi_check((flags & FuriFlagError) == 0);
- if(flags & WorkerEvtEnd) {
- break;
- } else if(flags & WorkerEvtToggle) { // Start executing script
- DOLPHIN_DEED(DolphinDeedBadUsbPlayScript);
- delay_val = 0;
- bad_usb->buf_len = 0;
- bad_usb->st.line_cur = 0;
- bad_usb->defdelay = 0;
- bad_usb->repeat_cnt = 0;
- bad_usb->file_end = false;
- storage_file_seek(script_file, 0, true);
- worker_state = BadUsbStateRunning;
- } else if(flags & WorkerEvtDisconnect) {
- worker_state = BadUsbStateNotConnected; // USB disconnected
- }
- bad_usb->st.state = worker_state;
-
- } else if(worker_state == BadUsbStateWillRun) { // State: start on connection
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
- FuriFlagWaitAny,
- FuriWaitForever);
- furi_check((flags & FuriFlagError) == 0);
- if(flags & WorkerEvtEnd) {
- break;
- } else if(flags & WorkerEvtConnect) { // Start executing script
- DOLPHIN_DEED(DolphinDeedBadUsbPlayScript);
- delay_val = 0;
- bad_usb->buf_len = 0;
- bad_usb->st.line_cur = 0;
- bad_usb->defdelay = 0;
- bad_usb->repeat_cnt = 0;
- bad_usb->file_end = false;
- storage_file_seek(script_file, 0, true);
- // extra time for PC to recognize Flipper as keyboard
- furi_thread_flags_wait(0, FuriFlagWaitAny, 1500);
- worker_state = BadUsbStateRunning;
- } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
- worker_state = BadUsbStateNotConnected;
- }
- bad_usb->st.state = worker_state;
-
- } else if(worker_state == BadUsbStateRunning) { // State: running
- uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur);
- delay_val -= delay_cur;
- if(!(flags & FuriFlagError)) {
- if(flags & WorkerEvtEnd) {
- break;
- } else if(flags & WorkerEvtToggle) {
- worker_state = BadUsbStateIdle; // Stop executing script
- furi_hal_hid_kb_release_all();
- } else if(flags & WorkerEvtDisconnect) {
- worker_state = BadUsbStateNotConnected; // USB disconnected
- furi_hal_hid_kb_release_all();
- }
- bad_usb->st.state = worker_state;
- continue;
- } else if(
- (flags == (unsigned)FuriFlagErrorTimeout) ||
- (flags == (unsigned)FuriFlagErrorResource)) {
- if(delay_val > 0) {
- bad_usb->st.delay_remain--;
- continue;
- }
- bad_usb->st.state = BadUsbStateRunning;
- delay_val = ducky_script_execute_next(bad_usb, script_file);
- if(delay_val == SCRIPT_STATE_ERROR) { // Script error
- delay_val = 0;
- worker_state = BadUsbStateScriptError;
- bad_usb->st.state = worker_state;
- } else if(delay_val == SCRIPT_STATE_END) { // End of script
- delay_val = 0;
- worker_state = BadUsbStateIdle;
- bad_usb->st.state = BadUsbStateDone;
- furi_hal_hid_kb_release_all();
- continue;
- } else if(delay_val > 1000) {
- bad_usb->st.state = BadUsbStateDelay; // Show long delays
- bad_usb->st.delay_remain = delay_val / 1000;
- }
- } else {
- furi_check((flags & FuriFlagError) == 0);
- }
-
- } else if(
- (worker_state == BadUsbStateFileError) ||
- (worker_state == BadUsbStateScriptError)) { // State: error
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command
- furi_check((flags & FuriFlagError) == 0);
- if(flags & WorkerEvtEnd) {
- break;
- }
- }
- }
-
- furi_hal_hid_set_state_callback(NULL, NULL);
-
- furi_hal_usb_set_config(usb_mode_prev, NULL);
-
- storage_file_close(script_file);
- storage_file_free(script_file);
- furi_string_free(bad_usb->line);
- furi_string_free(bad_usb->line_prev);
-
- FURI_LOG_I(WORKER_TAG, "End");
-
- return 0;
-}
-
-static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) {
- furi_assert(bad_usb);
- memset(bad_usb->layout, HID_KEYBOARD_NONE, sizeof(bad_usb->layout));
- memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout)));
-}
-
-BadUsbScript* bad_usb_script_open(FuriString* file_path) {
- furi_assert(file_path);
-
- BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript));
- bad_usb->file_path = furi_string_alloc();
- furi_string_set(bad_usb->file_path, file_path);
- bad_usb_script_set_default_keyboard_layout(bad_usb);
-
- bad_usb->st.state = BadUsbStateInit;
- bad_usb->st.error[0] = '\0';
-
- bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
- furi_thread_start(bad_usb->thread);
- return bad_usb;
-} //-V773
-
-void bad_usb_script_close(BadUsbScript* bad_usb) {
- furi_assert(bad_usb);
- furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtEnd);
- furi_thread_join(bad_usb->thread);
- furi_thread_free(bad_usb->thread);
- furi_string_free(bad_usb->file_path);
- free(bad_usb);
-}
-
-void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path) {
- furi_assert(bad_usb);
-
- if((bad_usb->st.state == BadUsbStateRunning) || (bad_usb->st.state == BadUsbStateDelay)) {
- // do not update keyboard layout while a script is running
- return;
- }
-
- File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
- if(!furi_string_empty(layout_path)) {
- if(storage_file_open(
- layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
- uint16_t layout[128];
- if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) {
- memcpy(bad_usb->layout, layout, sizeof(layout));
- }
- }
- storage_file_close(layout_file);
- } else {
- bad_usb_script_set_default_keyboard_layout(bad_usb);
- }
- storage_file_free(layout_file);
-}
-
-void bad_usb_script_toggle(BadUsbScript* bad_usb) {
- furi_assert(bad_usb);
- furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle);
-}
-
-BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) {
- furi_assert(bad_usb);
- return &(bad_usb->st);
-}
diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h
deleted file mode 100644
index 1e4d98fe7..000000000
--- a/applications/main/bad_usb/bad_usb_script.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include
-
-typedef struct BadUsbScript BadUsbScript;
-
-typedef enum {
- BadUsbStateInit,
- BadUsbStateNotConnected,
- BadUsbStateIdle,
- BadUsbStateWillRun,
- BadUsbStateRunning,
- BadUsbStateDelay,
- BadUsbStateDone,
- BadUsbStateScriptError,
- BadUsbStateFileError,
-} BadUsbWorkerState;
-
-typedef struct {
- BadUsbWorkerState state;
- uint16_t line_cur;
- uint16_t line_nb;
- uint32_t delay_remain;
- uint16_t error_line;
- char error[64];
-} BadUsbState;
-
-BadUsbScript* bad_usb_script_open(FuriString* file_path);
-
-void bad_usb_script_close(BadUsbScript* bad_usb);
-
-void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path);
-
-void bad_usb_script_start(BadUsbScript* bad_usb);
-
-void bad_usb_script_stop(BadUsbScript* bad_usb);
-
-void bad_usb_script_toggle(BadUsbScript* bad_usb);
-
-BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/applications/main/bad_usb/bad_usb_settings_filename.h b/applications/main/bad_usb/bad_usb_settings_filename.h
deleted file mode 100644
index 12ba8f31c..000000000
--- a/applications/main/bad_usb/bad_usb_settings_filename.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-#define BAD_USB_SETTINGS_FILE_NAME ".badusb.settings"
diff --git a/applications/main/bad_usb/scenes/bad_usb_scene.c b/applications/main/bad_usb/scenes/bad_usb_scene.c
deleted file mode 100644
index 03c7c4471..000000000
--- a/applications/main/bad_usb/scenes/bad_usb_scene.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "bad_usb_scene.h"
-
-// Generate scene on_enter handlers array
-#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
-void (*const bad_usb_scene_on_enter_handlers[])(void*) = {
-#include "bad_usb_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 bad_usb_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
-#include "bad_usb_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 bad_usb_scene_on_exit_handlers[])(void* context) = {
-#include "bad_usb_scene_config.h"
-};
-#undef ADD_SCENE
-
-// Initialize scene handlers configuration structure
-const SceneManagerHandlers bad_usb_scene_handlers = {
- .on_enter_handlers = bad_usb_scene_on_enter_handlers,
- .on_event_handlers = bad_usb_scene_on_event_handlers,
- .on_exit_handlers = bad_usb_scene_on_exit_handlers,
- .scene_num = BadUsbSceneNum,
-};
diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.c b/applications/main/bad_usb/scenes/bad_usb_scene_config.c
deleted file mode 100644
index 2a9f2f76c..000000000
--- a/applications/main/bad_usb/scenes/bad_usb_scene_config.c
+++ /dev/null
@@ -1,53 +0,0 @@
-#include "../bad_usb_app_i.h"
-#include "furi_hal_power.h"
-#include "furi_hal_usb.h"
-
-enum SubmenuIndex {
- SubmenuIndexKeyboardLayout,
-};
-
-void bad_usb_scene_config_submenu_callback(void* context, uint32_t index) {
- BadUsbApp* bad_usb = context;
- view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index);
-}
-
-void bad_usb_scene_config_on_enter(void* context) {
- BadUsbApp* bad_usb = context;
- Submenu* submenu = bad_usb->submenu;
-
- submenu_add_item(
- submenu,
- "Keyboard layout",
- SubmenuIndexKeyboardLayout,
- bad_usb_scene_config_submenu_callback,
- bad_usb);
-
- submenu_set_selected_item(
- submenu, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig));
-
- view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig);
-}
-
-bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) {
- BadUsbApp* bad_usb = context;
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeCustom) {
- scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event);
- consumed = true;
- if(event.event == SubmenuIndexKeyboardLayout) {
- scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout);
- } else {
- furi_crash("Unknown key type");
- }
- }
-
- return consumed;
-}
-
-void bad_usb_scene_config_on_exit(void* context) {
- BadUsbApp* bad_usb = context;
- Submenu* submenu = bad_usb->submenu;
-
- submenu_reset(submenu);
-}
diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.h b/applications/main/bad_usb/scenes/bad_usb_scene_config.h
deleted file mode 100644
index 423aedc51..000000000
--- a/applications/main/bad_usb/scenes/bad_usb_scene_config.h
+++ /dev/null
@@ -1,5 +0,0 @@
-ADD_SCENE(bad_usb, file_select, FileSelect)
-ADD_SCENE(bad_usb, work, Work)
-ADD_SCENE(bad_usb, error, Error)
-ADD_SCENE(bad_usb, config, Config)
-ADD_SCENE(bad_usb, config_layout, ConfigLayout)
diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c
deleted file mode 100644
index 44dcd55af..000000000
--- a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "../bad_usb_app_i.h"
-#include "furi_hal_power.h"
-#include "furi_hal_usb.h"
-#include
-
-static bool bad_usb_layout_select(BadUsbApp* bad_usb) {
- furi_assert(bad_usb);
-
- FuriString* predefined_path;
- predefined_path = furi_string_alloc();
- if(!furi_string_empty(bad_usb->keyboard_layout)) {
- furi_string_set(predefined_path, bad_usb->keyboard_layout);
- } else {
- furi_string_set(predefined_path, BAD_USB_APP_PATH_LAYOUT_FOLDER);
- }
-
- DialogsFileBrowserOptions browser_options;
- dialog_file_browser_set_basic_options(
- &browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
-
- // Input events and views are managed by file_browser
- bool res = dialog_file_browser_show(
- bad_usb->dialogs, bad_usb->keyboard_layout, predefined_path, &browser_options);
-
- furi_string_free(predefined_path);
- return res;
-}
-
-void bad_usb_scene_config_layout_on_enter(void* context) {
- BadUsbApp* bad_usb = context;
-
- if(bad_usb_layout_select(bad_usb)) {
- bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout);
- }
- scene_manager_previous_scene(bad_usb->scene_manager);
-}
-
-bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) {
- UNUSED(context);
- UNUSED(event);
- // BadUsbApp* bad_usb = context;
- return false;
-}
-
-void bad_usb_scene_config_layout_on_exit(void* context) {
- UNUSED(context);
- // BadUsbApp* bad_usb = context;
-}
diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c
deleted file mode 100644
index 21a2ce024..000000000
--- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#include "../bad_usb_app_i.h"
-#include "furi_hal_power.h"
-#include "furi_hal_usb.h"
-#include
-
-static bool bad_usb_file_select(BadUsbApp* bad_usb) {
- furi_assert(bad_usb);
-
- DialogsFileBrowserOptions browser_options;
- dialog_file_browser_set_basic_options(
- &browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px);
- browser_options.base_path = BAD_USB_APP_BASE_FOLDER;
- browser_options.skip_assets = true;
-
- // Input events and views are managed by file_browser
- bool res = dialog_file_browser_show(
- bad_usb->dialogs, bad_usb->file_path, bad_usb->file_path, &browser_options);
-
- return res;
-}
-
-void bad_usb_scene_file_select_on_enter(void* context) {
- BadUsbApp* bad_usb = context;
-
- furi_hal_usb_disable();
- if(bad_usb->bad_usb_script) {
- bad_usb_script_close(bad_usb->bad_usb_script);
- bad_usb->bad_usb_script = NULL;
- }
-
- if(bad_usb_file_select(bad_usb)) {
- bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path);
- bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout);
-
- scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork);
- } else {
- furi_hal_usb_enable();
- view_dispatcher_stop(bad_usb->view_dispatcher);
- }
-}
-
-bool bad_usb_scene_file_select_on_event(void* context, SceneManagerEvent event) {
- UNUSED(context);
- UNUSED(event);
- // BadUsbApp* bad_usb = context;
- return false;
-}
-
-void bad_usb_scene_file_select_on_exit(void* context) {
- UNUSED(context);
- // BadUsbApp* bad_usb = context;
-}
diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c
deleted file mode 100644
index 2971c01e9..000000000
--- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#include "../bad_usb_script.h"
-#include "../bad_usb_app_i.h"
-#include "../views/bad_usb_view.h"
-#include "furi_hal.h"
-#include "toolbox/path.h"
-
-void bad_usb_scene_work_button_callback(InputKey key, void* context) {
- furi_assert(context);
- BadUsbApp* app = context;
- view_dispatcher_send_custom_event(app->view_dispatcher, key);
-}
-
-bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) {
- BadUsbApp* app = context;
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeCustom) {
- if(event.event == InputKeyLeft) {
- scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig);
- consumed = true;
- } else if(event.event == InputKeyOk) {
- bad_usb_script_toggle(app->bad_usb_script);
- consumed = true;
- }
- } else if(event.type == SceneManagerEventTypeTick) {
- bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));
- }
- return consumed;
-}
-
-void bad_usb_scene_work_on_enter(void* context) {
- BadUsbApp* app = context;
-
- FuriString* file_name;
- file_name = furi_string_alloc();
- path_extract_filename(app->file_path, file_name, true);
- bad_usb_set_file_name(app->bad_usb_view, furi_string_get_cstr(file_name));
- furi_string_free(file_name);
-
- FuriString* layout;
- layout = furi_string_alloc();
- path_extract_filename(app->keyboard_layout, layout, true);
- bad_usb_set_layout(app->bad_usb_view, furi_string_get_cstr(layout));
- furi_string_free(layout);
-
- bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script));
-
- bad_usb_set_button_callback(app->bad_usb_view, bad_usb_scene_work_button_callback, app);
- view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork);
-}
-
-void bad_usb_scene_work_on_exit(void* context) {
- UNUSED(context);
-}
diff --git a/applications/main/bad_usb/views/bad_usb_view.h b/applications/main/bad_usb/views/bad_usb_view.h
deleted file mode 100644
index 8447fb055..000000000
--- a/applications/main/bad_usb/views/bad_usb_view.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include
-#include "../bad_usb_script.h"
-
-typedef struct BadUsb BadUsb;
-typedef void (*BadUsbButtonCallback)(InputKey key, void* context);
-
-BadUsb* bad_usb_alloc();
-
-void bad_usb_free(BadUsb* bad_usb);
-
-View* bad_usb_get_view(BadUsb* bad_usb);
-
-void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context);
-
-void bad_usb_set_file_name(BadUsb* bad_usb, const char* name);
-
-void bad_usb_set_layout(BadUsb* bad_usb, const char* layout);
-
-void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st);
diff --git a/applications/main/gpio/gpio_app.c b/applications/main/gpio/gpio_app.c
index 19e53f453..3fecc5427 100644
--- a/applications/main/gpio/gpio_app.c
+++ b/applications/main/gpio/gpio_app.c
@@ -25,6 +25,7 @@ GpioApp* gpio_app_alloc() {
GpioApp* app = malloc(sizeof(GpioApp));
app->gui = furi_record_open(RECORD_GUI);
+ app->gpio_items = gpio_items_alloc();
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app);
@@ -47,7 +48,7 @@ GpioApp* gpio_app_alloc() {
app->view_dispatcher,
GpioAppViewVarItemList,
variable_item_list_get_view(app->var_item_list));
- app->gpio_test = gpio_test_alloc();
+ app->gpio_test = gpio_test_alloc(app->gpio_items);
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));
@@ -105,6 +106,7 @@ void gpio_app_free(GpioApp* app) {
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
+ gpio_items_free(app->gpio_items);
free(app);
}
diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h
index ac6c77a8e..20140f07a 100644
--- a/applications/main/gpio/gpio_app_i.h
+++ b/applications/main/gpio/gpio_app_i.h
@@ -1,7 +1,7 @@
#pragma once
#include "gpio_app.h"
-#include "gpio_item.h"
+#include "gpio_items.h"
#include "scenes/gpio_scene.h"
#include "gpio_custom_event.h"
#include "usb_uart_bridge.h"
@@ -30,6 +30,7 @@ struct GpioApp {
VariableItem* var_item_flow;
GpioTest* gpio_test;
GpioUsbUart* gpio_usb_uart;
+ GPIOItems* gpio_items;
UsbUartBridge* usb_uart_bridge;
GpioI2CScanner* gpio_i2c_scanner;
GpioI2CSfp* gpio_i2c_sfp;
diff --git a/applications/main/gpio/gpio_item.c b/applications/main/gpio/gpio_item.c
deleted file mode 100644
index 2d0f5f676..000000000
--- a/applications/main/gpio/gpio_item.c
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "gpio_item.h"
-
-#include
-
-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);
- furi_hal_gpio_write(gpio_item[index].pin, false);
- furi_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);
- furi_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;
- }
-}
diff --git a/applications/main/gpio/gpio_item.h b/applications/main/gpio/gpio_item.h
deleted file mode 100644
index 5cb2b86c1..000000000
--- a/applications/main/gpio/gpio_item.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include
-
-#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);
diff --git a/applications/main/gpio/gpio_items.c b/applications/main/gpio/gpio_items.c
new file mode 100644
index 000000000..02f7d95b0
--- /dev/null
+++ b/applications/main/gpio/gpio_items.c
@@ -0,0 +1,69 @@
+#include "gpio_items.h"
+
+#include
+
+struct GPIOItems {
+ GpioPinRecord* pins;
+ size_t count;
+};
+
+GPIOItems* gpio_items_alloc() {
+ GPIOItems* items = malloc(sizeof(GPIOItems));
+
+ items->count = 0;
+ for(size_t i = 0; i < gpio_pins_count; i++) {
+ if(!gpio_pins[i].debug) {
+ items->count++;
+ }
+ }
+
+ items->pins = malloc(sizeof(GpioPinRecord) * items->count);
+ for(size_t i = 0; i < items->count; i++) {
+ if(!gpio_pins[i].debug) {
+ items->pins[i].pin = gpio_pins[i].pin;
+ items->pins[i].name = gpio_pins[i].name;
+ }
+ }
+ return items;
+}
+
+void gpio_items_free(GPIOItems* items) {
+ free(items->pins);
+ free(items);
+}
+
+uint8_t gpio_items_get_count(GPIOItems* items) {
+ return items->count;
+}
+
+void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode) {
+ furi_assert(index < items->count);
+ furi_hal_gpio_write(items->pins[index].pin, false);
+ furi_hal_gpio_init(items->pins[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh);
+}
+
+void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode) {
+ for(uint8_t i = 0; i < items->count; i++) {
+ gpio_items_configure_pin(items, i, mode);
+ }
+}
+
+void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level) {
+ furi_assert(index < items->count);
+ furi_hal_gpio_write(items->pins[index].pin, level);
+}
+
+void gpio_items_set_all_pins(GPIOItems* items, bool level) {
+ for(uint8_t i = 0; i < items->count; i++) {
+ gpio_items_set_pin(items, i, level);
+ }
+}
+
+const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index) {
+ furi_assert(index < items->count + 1);
+ if(index == items->count) {
+ return "ALL";
+ } else {
+ return items->pins[index].name;
+ }
+}
diff --git a/applications/main/gpio/gpio_items.h b/applications/main/gpio/gpio_items.h
new file mode 100644
index 000000000..68afbe693
--- /dev/null
+++ b/applications/main/gpio/gpio_items.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct GPIOItems GPIOItems;
+
+GPIOItems* gpio_items_alloc();
+
+void gpio_items_free(GPIOItems* items);
+
+uint8_t gpio_items_get_count(GPIOItems* items);
+
+void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode);
+
+void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode);
+
+void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level);
+
+void gpio_items_set_all_pins(GPIOItems* items, bool level);
+
+const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c
index 08b77238f..1c1a665c7 100644
--- a/applications/main/gpio/scenes/gpio_scene_start.c
+++ b/applications/main/gpio/scenes/gpio_scene_start.c
@@ -1,6 +1,10 @@
+#ifndef __GPIO_SCENE_START_H__
+#define __GPIO_SCENE_START_H__
+
#include "../gpio_app_i.h"
-#include "furi_hal_power.h"
-#include "furi_hal_usb.h"
+#include
+#include
+#include
enum GpioItem {
GpioItemUsbUart,
@@ -117,3 +121,5 @@ void gpio_scene_start_on_exit(void* context) {
GpioApp* app = context;
variable_item_list_reset(app->var_item_list);
}
+
+#endif // __GPIO_SCENE_START_H__
\ No newline at end of file
diff --git a/applications/main/gpio/scenes/gpio_scene_test.c b/applications/main/gpio/scenes/gpio_scene_test.c
index b015d8090..9940b95d4 100644
--- a/applications/main/gpio/scenes/gpio_scene_test.c
+++ b/applications/main/gpio/scenes/gpio_scene_test.c
@@ -12,8 +12,9 @@ void gpio_scene_test_ok_callback(InputType type, void* context) {
}
void gpio_scene_test_on_enter(void* context) {
+ furi_assert(context);
GpioApp* app = context;
- gpio_item_configure_all_pins(GpioModeOutputPushPull);
+ gpio_items_configure_all_pins(app->gpio_items, GpioModeOutputPushPull);
gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest);
}
@@ -25,6 +26,7 @@ bool gpio_scene_test_on_event(void* context, SceneManagerEvent event) {
}
void gpio_scene_test_on_exit(void* context) {
- UNUSED(context);
- gpio_item_configure_all_pins(GpioModeAnalog);
+ furi_assert(context);
+ GpioApp* app = context;
+ gpio_items_configure_all_pins(app->gpio_items, GpioModeAnalog);
}
diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c
index 776343fb0..e2ab66264 100644
--- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c
+++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c
@@ -1,6 +1,6 @@
#include "../usb_uart_bridge.h"
#include "../gpio_app_i.h"
-#include "furi_hal.h"
+#include
typedef enum {
UsbUartLineIndexVcp,
diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c
index 1a82dbdc2..9bc759dc8 100644
--- a/applications/main/gpio/usb_uart_bridge.c
+++ b/applications/main/gpio/usb_uart_bridge.c
@@ -1,10 +1,10 @@
#include "usb_uart_bridge.h"
-#include "furi_hal.h"
-#include
#include "usb_cdc.h"
-#include "cli/cli_vcp.h"
+#include
+#include
#include
-#include "cli/cli.h"
+#include
+#include
#define USB_CDC_PKT_LEN CDC_DATA_SZ
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
diff --git a/applications/main/gpio/views/gpio_i2c_scanner.c b/applications/main/gpio/views/gpio_i2c_scanner.c
index 0f00639d6..fc9ed78a7 100644
--- a/applications/main/gpio/views/gpio_i2c_scanner.c
+++ b/applications/main/gpio/views/gpio_i2c_scanner.c
@@ -1,6 +1,6 @@
#include
#include "gpio_i2c_scanner.h"
-#include "../gpio_item.h"
+#include "../gpio_items.h"
#include
diff --git a/applications/main/gpio/views/gpio_i2c_sfp.c b/applications/main/gpio/views/gpio_i2c_sfp.c
index 85492e783..c8e14ce7a 100644
--- a/applications/main/gpio/views/gpio_i2c_sfp.c
+++ b/applications/main/gpio/views/gpio_i2c_sfp.c
@@ -1,6 +1,6 @@
#include
#include "gpio_i2c_sfp.h"
-#include "../gpio_item.h"
+#include "../gpio_items.h"
#include
diff --git a/applications/main/gpio/views/gpio_test.c b/applications/main/gpio/views/gpio_test.c
index 69dc0f67b..c154a7275 100644
--- a/applications/main/gpio/views/gpio_test.c
+++ b/applications/main/gpio/views/gpio_test.c
@@ -1,5 +1,5 @@
#include "gpio_test.h"
-#include "../gpio_item.h"
+#include "../gpio_items.h"
#include
@@ -11,6 +11,7 @@ struct GpioTest {
typedef struct {
uint8_t pin_idx;
+ GPIOItems* gpio_items;
} GpioTestModel;
static bool gpio_test_process_left(GpioTest* gpio_test);
@@ -25,7 +26,12 @@ static void gpio_test_draw_callback(Canvas* canvas, void* _model) {
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));
+ canvas,
+ 64,
+ 32,
+ AlignCenter,
+ AlignTop,
+ gpio_items_get_pin_name(model->gpio_items, model->pin_idx));
}
static bool gpio_test_input_callback(InputEvent* event, void* context) {
@@ -64,7 +70,7 @@ static bool gpio_test_process_right(GpioTest* gpio_test) {
gpio_test->view,
GpioTestModel * model,
{
- if(model->pin_idx < GPIO_ITEM_COUNT) {
+ if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {
model->pin_idx++;
}
},
@@ -80,17 +86,17 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) {
GpioTestModel * model,
{
if(event->type == InputTypePress) {
- if(model->pin_idx < GPIO_ITEM_COUNT) {
- gpio_item_set_pin(model->pin_idx, true);
+ if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {
+ gpio_items_set_pin(model->gpio_items, model->pin_idx, true);
} else {
- gpio_item_set_all_pins(true);
+ gpio_items_set_all_pins(model->gpio_items, true);
}
consumed = true;
} else if(event->type == InputTypeRelease) {
- if(model->pin_idx < GPIO_ITEM_COUNT) {
- gpio_item_set_pin(model->pin_idx, false);
+ if(model->pin_idx < gpio_items_get_count(model->gpio_items)) {
+ gpio_items_set_pin(model->gpio_items, model->pin_idx, false);
} else {
- gpio_item_set_all_pins(false);
+ gpio_items_set_all_pins(model->gpio_items, false);
}
consumed = true;
}
@@ -101,11 +107,15 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) {
return consumed;
}
-GpioTest* gpio_test_alloc() {
+GpioTest* gpio_test_alloc(GPIOItems* gpio_items) {
GpioTest* gpio_test = malloc(sizeof(GpioTest));
gpio_test->view = view_alloc();
view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel));
+
+ with_view_model(
+ gpio_test->view, GpioTestModel * model, { model->gpio_items = gpio_items; }, false);
+
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);
diff --git a/applications/main/gpio/views/gpio_test.h b/applications/main/gpio/views/gpio_test.h
index 5cbd11e82..38fcbc5fb 100644
--- a/applications/main/gpio/views/gpio_test.h
+++ b/applications/main/gpio/views/gpio_test.h
@@ -1,11 +1,13 @@
#pragma once
+#include "../gpio_items.h"
+
#include
typedef struct GpioTest GpioTest;
typedef void (*GpioTestOkCallback)(InputType type, void* context);
-GpioTest* gpio_test_alloc();
+GpioTest* gpio_test_alloc(GPIOItems* gpio_items);
void gpio_test_free(GpioTest* gpio_test);
diff --git a/applications/main/gpio/views/gpio_usb_uart.c b/applications/main/gpio/views/gpio_usb_uart.c
index c7406d29b..837f2e3ec 100644
--- a/applications/main/gpio/views/gpio_usb_uart.c
+++ b/applications/main/gpio/views/gpio_usb_uart.c
@@ -1,6 +1,6 @@
#include "../usb_uart_bridge.h"
#include "../gpio_app_i.h"
-#include "furi_hal.h"
+#include
#include
struct GpioUsbUart {
diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam
index 77bb9a33c..06968bba4 100644
--- a/applications/main/ibutton/application.fam
+++ b/applications/main/ibutton/application.fam
@@ -2,6 +2,7 @@ App(
appid="ibutton",
name="iButton",
apptype=FlipperAppType.APP,
+ targets=["f7"],
entry_point="ibutton_app",
cdefines=["APP_IBUTTON"],
requires=[
diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c
index 5f12d49d7..e2b7e3837 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c
@@ -1,5 +1,5 @@
#include "../ibutton_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
static void ibutton_scene_delete_success_popup_callback(void* context) {
iButton* ibutton = context;
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c
index 718cb8bfc..1ccd2562b 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_read.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_read.c
@@ -1,6 +1,6 @@
#include "../ibutton_i.h"
#include
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
static void ibutton_scene_read_callback(void* context) {
iButton* ibutton = context;
diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c
index ee49b432c..03e88e047 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c
@@ -1,5 +1,5 @@
#include "../ibutton_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
static void ibutton_scene_save_success_popup_callback(void* context) {
iButton* ibutton = context;
diff --git a/applications/main/ibutton/scenes/ibutton_scene_write_success.c b/applications/main/ibutton/scenes/ibutton_scene_write_success.c
index e77f3f049..3f565e274 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_write_success.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_write_success.c
@@ -1,5 +1,5 @@
#include "../ibutton_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
static void ibutton_scene_write_success_popup_callback(void* context) {
iButton* ibutton = context;
diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam
index 9c5eaf392..e5483e9ff 100644
--- a/applications/main/infrared/application.fam
+++ b/applications/main/infrared/application.fam
@@ -3,6 +3,7 @@ App(
name="Infrared",
apptype=FlipperAppType.APP,
entry_point="infrared_app",
+ targets=["f7"],
cdefines=["APP_INFRARED"],
requires=[
"gui",
diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c
index 3d7a9d081..36224f418 100644
--- a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c
+++ b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c
@@ -1,5 +1,5 @@
#include "../infrared_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void infrared_scene_edit_rename_done_on_enter(void* context) {
Infrared* infrared = context;
diff --git a/applications/main/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c
index dce74db54..0d2522946 100644
--- a/applications/main/infrared/scenes/infrared_scene_learn_done.c
+++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c
@@ -1,5 +1,5 @@
#include "../infrared_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void infrared_scene_learn_done_on_enter(void* context) {
Infrared* infrared = context;
diff --git a/applications/main/infrared/scenes/infrared_scene_learn_success.c b/applications/main/infrared/scenes/infrared_scene_learn_success.c
index 3340a6342..bbc84ba3b 100644
--- a/applications/main/infrared/scenes/infrared_scene_learn_success.c
+++ b/applications/main/infrared/scenes/infrared_scene_learn_success.c
@@ -1,5 +1,5 @@
#include "../infrared_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
static void
infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) {
diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c
index 8044e8318..04f17416d 100644
--- a/applications/main/infrared/scenes/infrared_scene_rpc.c
+++ b/applications/main/infrared/scenes/infrared_scene_rpc.c
@@ -1,5 +1,5 @@
#include "../infrared_i.h"
-#include "gui/canvas.h"
+#include
typedef enum {
InfraredRpcStateIdle,
diff --git a/applications/main/infrared/views/infrared_debug_view.c b/applications/main/infrared/views/infrared_debug_view.c
index ab2c679c4..ec0896281 100644
--- a/applications/main/infrared/views/infrared_debug_view.c
+++ b/applications/main/infrared/views/infrared_debug_view.c
@@ -1,11 +1,11 @@
#include "infrared_debug_view.h"
-#include
-#include
-
#include
#include
+#include
+#include
+
#define INFRARED_DEBUG_TEXT_LENGTH 64
struct InfraredDebugView {
diff --git a/applications/main/infrared/views/infrared_progress_view.c b/applications/main/infrared/views/infrared_progress_view.c
index 3c50f89e4..432da7ff1 100644
--- a/applications/main/infrared/views/infrared_progress_view.c
+++ b/applications/main/infrared/views/infrared_progress_view.c
@@ -1,13 +1,15 @@
-#include
-#include "furi_hal_resources.h"
-#include "assets_icons.h"
-#include "gui/canvas.h"
-#include "gui/view.h"
-#include "input/input.h"
-#include
-#include
#include "infrared_progress_view.h"
-#include "gui/modules/button_panel.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
#include
struct InfraredProgressView {
diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam
index 150a6f3db..8fe1bac4d 100644
--- a/applications/main/lfrfid/application.fam
+++ b/applications/main/lfrfid/application.fam
@@ -2,6 +2,7 @@ App(
appid="lfrfid",
name="125 kHz RFID",
apptype=FlipperAppType.APP,
+ targets=["f7"],
entry_point="lfrfid_app",
cdefines=["APP_LF_RFID"],
requires=[
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
index 7d9d50765..888288a31 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
@@ -1,6 +1,6 @@
#include "../lfrfid_i.h"
#include "../helpers/rfid_writer.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
static void writer_initialize(T55xxTiming* t55xxtiming) {
t55xxtiming->wait_time = 400;
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c
index 873382b6c..991748515 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c
@@ -1,5 +1,5 @@
#include "../lfrfid_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void lfrfid_scene_delete_success_on_enter(void* context) {
LfRfid* app = context;
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c
index 9132f4a91..eff92dc37 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c
@@ -1,5 +1,5 @@
#include "../lfrfid_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void lfrfid_scene_emulate_on_enter(void* context) {
LfRfid* app = context;
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c
index 73e1dbbb9..3213297d7 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c
@@ -1,5 +1,5 @@
#include "../lfrfid_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
#define RAW_READ_TIME 5000
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c
index c11dd1109..181346e9d 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c
@@ -1,5 +1,5 @@
#include "../lfrfid_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void lfrfid_scene_rpc_on_enter(void* context) {
LfRfid* app = context;
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c
index 4003ee405..738e90bfd 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c
@@ -1,5 +1,5 @@
#include "../lfrfid_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void lfrfid_scene_save_success_on_enter(void* context) {
LfRfid* app = context;
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write.c b/applications/main/lfrfid/scenes/lfrfid_scene_write.c
index 9cae43c96..0f74ece45 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_write.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_write.c
@@ -1,5 +1,5 @@
#include "../lfrfid_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* context) {
LfRfid* app = context;
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c
index 48d046227..5eeb88616 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c
@@ -1,5 +1,5 @@
#include "../lfrfid_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void lfrfid_scene_write_success_on_enter(void* context) {
LfRfid* app = context;
diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam
index ce21bfd27..f09127045 100644
--- a/applications/main/nfc/application.fam
+++ b/applications/main/nfc/application.fam
@@ -2,6 +2,7 @@ App(
appid="nfc",
name="NFC",
apptype=FlipperAppType.APP,
+ targets=["f7"],
entry_point="nfc_app",
cdefines=["APP_NFC"],
requires=[
diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c
index 2ff224ec2..b6ea9cfb8 100644
--- a/applications/main/nfc/nfc.c
+++ b/applications/main/nfc/nfc.c
@@ -1,5 +1,5 @@
#include "nfc_i.h"
-#include "furi_hal_nfc.h"
+#include
#include
bool nfc_custom_event_callback(void* context, uint32_t event) {
diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c
index a732f5384..4994dd8d4 100644
--- a/applications/main/nfc/scenes/nfc_scene_delete_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c
@@ -1,5 +1,5 @@
#include "../nfc_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void nfc_scene_delete_success_popup_callback(void* context) {
Nfc* nfc = context;
diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c
index f423efc38..0e9f6b428 100644
--- a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c
+++ b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c
@@ -1,5 +1,5 @@
#include "../nfc_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (100)
diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c
index a583ac12d..7c016ceda 100644
--- a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c
+++ b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c
@@ -1,5 +1,5 @@
#include "../nfc_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
#define NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX (200)
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c
index 1d2e1784f..7f5cac406 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c
@@ -1,5 +1,5 @@
#include "../nfc_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
#define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL)
#define NFC_MF_CLASSIC_DATA_CHANGED (1UL)
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c
index 3c2c724bd..9544721b6 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c
@@ -1,6 +1,6 @@
#include "../nfc_i.h"
#include
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void nfc_scene_mf_classic_update_success_popup_callback(void* context) {
Nfc* nfc = context;
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c
index f16d2f733..e82dedeaf 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c
@@ -1,6 +1,6 @@
#include "../nfc_i.h"
#include
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void nfc_scene_mf_classic_write_success_popup_callback(void* context) {
Nfc* nfc = context;
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c
index d50ad5b84..77d831cf8 100644
--- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c
+++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c
@@ -1,6 +1,6 @@
#include "../nfc_i.h"
#include
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
#define NFC_SCENE_MF_ULTRALIGHT_EMULATE_LOG_SIZE_MAX (200)
diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original.c b/applications/main/nfc/scenes/nfc_scene_restore_original.c
index 3794b438d..409785e26 100644
--- a/applications/main/nfc/scenes/nfc_scene_restore_original.c
+++ b/applications/main/nfc/scenes/nfc_scene_restore_original.c
@@ -1,5 +1,5 @@
#include "../nfc_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void nfc_scene_restore_original_popup_callback(void* context) {
Nfc* nfc = context;
diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c
index e6b31fffa..6adacfda5 100644
--- a/applications/main/nfc/scenes/nfc_scene_rpc.c
+++ b/applications/main/nfc/scenes/nfc_scene_rpc.c
@@ -1,5 +1,5 @@
#include "../nfc_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void nfc_scene_rpc_on_enter(void* context) {
Nfc* nfc = context;
diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c
index a9359c434..c5d8a6872 100644
--- a/applications/main/nfc/scenes/nfc_scene_save_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_save_success.c
@@ -1,5 +1,5 @@
#include "../nfc_i.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void nfc_scene_save_success_popup_callback(void* context) {
Nfc* nfc = context;
diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam
index 7d31ecae5..f0dc66e89 100644
--- a/applications/main/subghz/application.fam
+++ b/applications/main/subghz/application.fam
@@ -2,6 +2,7 @@ App(
appid="subghz",
name="Sub-GHz",
apptype=FlipperAppType.APP,
+ targets=["f7"],
entry_point="subghz_app",
cdefines=["APP_SUBGHZ"],
requires=[
@@ -11,7 +12,7 @@ App(
],
provides=["subghz_start"],
icon="A_Sub1ghz_14",
- stack_size=2 * 1024,
+ stack_size=3 * 1024,
order=10,
)
diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h
index 0559ac67e..350e68ee6 100644
--- a/applications/main/subghz/helpers/subghz_custom_event.h
+++ b/applications/main/subghz/helpers/subghz_custom_event.h
@@ -50,7 +50,6 @@ typedef enum {
SubGhzCustomEventSceneAnalyzerLock,
SubGhzCustomEventSceneAnalyzerUnlock,
SubGhzCustomEventSceneSettingLock,
- SubGhzCustomEventSceneSettingError,
SubGhzCustomEventSceneExit,
SubGhzCustomEventSceneStay,
diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
index 7dc67c9f2..6452792a6 100644
--- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
+++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
@@ -36,13 +36,13 @@ struct SubGhzFrequencyAnalyzerWorker {
};
static void subghz_frequency_analyzer_worker_load_registers(const uint8_t data[][2]) {
- furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
+ furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
size_t i = 0;
while(data[i][0]) {
- cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i][0], data[i][1]);
+ cc1101_write_reg(furi_hal_subghz.spi_bus_handle, data[i][0], data[i][1]);
i++;
}
- furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
+ furi_hal_spi_release(furi_hal_subghz.spi_bus_handle);
}
// running average with adaptive coefficient
@@ -80,26 +80,26 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
//Start CC1101
furi_hal_subghz_reset();
- furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
- cc1101_flush_rx(&furi_hal_spi_bus_handle_subghz);
- cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz);
- cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW);
- cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_MDMCFG3,
+ furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
+ cc1101_flush_rx(furi_hal_subghz.spi_bus_handle);
+ cc1101_flush_tx(furi_hal_subghz.spi_bus_handle);
+ cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW);
+ cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_MDMCFG3,
0b01111111); // symbol rate
cc1101_write_reg(
- &furi_hal_spi_bus_handle_subghz,
+ furi_hal_subghz.spi_bus_handle,
CC1101_AGCCTRL2,
0b00000111); // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAGN_TARGET 42 dB
cc1101_write_reg(
- &furi_hal_spi_bus_handle_subghz,
+ furi_hal_subghz.spi_bus_handle,
CC1101_AGCCTRL1,
0b00001000); // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 1000 - Absolute carrier sense threshold disabled
cc1101_write_reg(
- &furi_hal_spi_bus_handle_subghz,
+ furi_hal_subghz.spi_bus_handle,
CC1101_AGCCTRL0,
0b00110000); // 00 - No hysteresis, medium asymmetric dead zone, medium gain ; 11 - 64 samples agc; 00 - Normal AGC, 00 - 4dB boundary
- furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
+ furi_hal_spi_release(furi_hal_subghz.spi_bus_handle);
furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
@@ -118,20 +118,23 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
// First stage: coarse scan
for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
if(furi_hal_subghz_is_frequency_valid(
- subghz_setting_get_frequency(instance->setting, i))) {
- furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
- cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);
+ subghz_setting_get_frequency(instance->setting, i)) &&
+ !((furi_hal_subghz.radio_type == SubGhzRadioExternal) &&
+ (subghz_setting_get_frequency(instance->setting, i) >= 311900000 &&
+ subghz_setting_get_frequency(instance->setting, i) <= 312200000))) {
+ furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
+ cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle);
frequency = cc1101_set_frequency(
- &furi_hal_spi_bus_handle_subghz,
+ furi_hal_subghz.spi_bus_handle,
subghz_setting_get_frequency(instance->setting, i));
- cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);
+ cc1101_calibrate(furi_hal_subghz.spi_bus_handle);
do {
- status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz);
+ status = cc1101_get_status(furi_hal_subghz.spi_bus_handle);
} while(status.STATE != CC1101StateIDLE);
- cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);
- furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
+ cc1101_switch_to_rx(furi_hal_subghz.spi_bus_handle);
+ furi_hal_spi_release(furi_hal_subghz.spi_bus_handle);
furi_delay_ms(2);
@@ -166,17 +169,17 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
i < frequency_rssi.frequency_coarse + 300000;
i += 20000) {
if(furi_hal_subghz_is_frequency_valid(i)) {
- furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
- cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);
- frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, i);
+ furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
+ cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle);
+ frequency = cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, i);
- cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);
+ cc1101_calibrate(furi_hal_subghz.spi_bus_handle);
do {
- status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz);
+ status = cc1101_get_status(furi_hal_subghz.spi_bus_handle);
} while(status.STATE != CC1101StateIDLE);
- cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);
- furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
+ cc1101_switch_to_rx(furi_hal_subghz.spi_bus_handle);
+ furi_hal_spi_release(furi_hal_subghz.spi_bus_handle);
furi_delay_ms(2);
diff --git a/applications/main/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h
index 1ad41a8b5..5acd534dc 100644
--- a/applications/main/subghz/scenes/subghz_scene_config.h
+++ b/applications/main/subghz/scenes/subghz_scene_config.h
@@ -26,6 +26,7 @@ ADD_SCENE(subghz, set_fix_bft, SetFixBft)
ADD_SCENE(subghz, set_cnt_bft, SetCntBft)
ADD_SCENE(subghz, set_seed_bft, SetSeedBft)
ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer)
+ADD_SCENE(subghz, ext_module_settings, ExtModuleSettings)
ADD_SCENE(subghz, read_raw, ReadRAW)
ADD_SCENE(subghz, more_raw, MoreRAW)
ADD_SCENE(subghz, decode_raw, DecodeRAW)
diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c
index 6194d0dba..5746efbed 100644
--- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c
+++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c
@@ -170,11 +170,6 @@ void subghz_scene_decode_raw_on_enter(void* context) {
subghz_receiver_set_rx_callback(
subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz);
- // make sure we're not in auto-detect mode, which is only meant for the Read app
- subghz_protocol_decoder_raw_set_auto_mode(
- subghz_receiver_search_decoder_base_by_name(
- subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME),
- false);
subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
if(subghz->decode_raw_state == SubGhzDecodeRawStateStart) {
diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c
index 4f00df70b..8e3ca5c78 100644
--- a/applications/main/subghz/scenes/subghz_scene_delete_success.c
+++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c
@@ -1,6 +1,6 @@
#include "../subghz_i.h"
#include "../helpers/subghz_custom_event.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void subghz_scene_delete_success_popup_callback(void* context) {
SubGhz* subghz = context;
@@ -32,7 +32,6 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
} else if(scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneSaved)) {
- //scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);
} else {
scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneStart);
diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c
new file mode 100644
index 000000000..7d7a505cb
--- /dev/null
+++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c
@@ -0,0 +1,92 @@
+#include "../subghz_i.h"
+#include "../helpers/subghz_custom_event.h"
+
+uint8_t value_index;
+uint8_t value_index2;
+
+#define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const))
+const char* const radio_modules_variables_text[] = {
+ "Internal",
+ "External",
+};
+
+#define DEBUG_P_COUNT 2
+const char* const debug_pin_text[DEBUG_P_COUNT] = {
+ "OFF",
+ "A7",
+};
+
+static void subghz_scene_ext_module_changed(VariableItem* item) {
+ SubGhz* subghz = variable_item_get_context(item);
+ value_index = variable_item_get_current_value_index(item);
+ UNUSED(subghz);
+
+ variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]);
+}
+static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) {
+ SubGhz* subghz = context;
+ view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
+}
+
+static void subghz_scene_receiver_config_set_debug_pin(VariableItem* item) {
+ SubGhz* subghz = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+
+ variable_item_set_current_value_text(item, debug_pin_text[index]);
+
+ subghz->txrx->debug_pin_state = index == 1;
+}
+
+void subghz_scene_ext_module_settings_on_enter(void* context) {
+ SubGhz* subghz = context;
+
+ VariableItemList* variable_item_list = subghz->variable_item_list;
+
+ value_index = furi_hal_subghz.radio_type;
+ VariableItem* item = variable_item_list_add(
+ variable_item_list, "Module", EXT_MODULES_COUNT, subghz_scene_ext_module_changed, subghz);
+
+ variable_item_list_set_enter_callback(
+ variable_item_list, subghz_ext_module_start_var_list_enter_callback, subghz);
+
+ variable_item_set_current_value_index(item, value_index);
+ variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]);
+
+ if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+ item = variable_item_list_add(
+ subghz->variable_item_list,
+ "Debug Pin:",
+ DEBUG_P_COUNT,
+ subghz_scene_receiver_config_set_debug_pin,
+ subghz);
+ value_index2 = subghz->txrx->debug_pin_state;
+ variable_item_set_current_value_index(item, value_index2);
+ variable_item_set_current_value_text(item, debug_pin_text[value_index2]);
+ }
+
+ view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
+}
+
+bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent event) {
+ SubGhz* subghz = context;
+ UNUSED(subghz);
+ UNUSED(event);
+
+ // Set selected radio module
+ furi_hal_subghz_set_radio_type(value_index);
+
+ // Check if module is present, if no -> show error
+ if(!furi_hal_subghz_check_radio()) {
+ value_index = 0;
+ furi_hal_subghz_set_radio_type(value_index);
+ furi_string_set(subghz->error_str, "Please connect\nexternal radio");
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
+ }
+
+ return false;
+}
+
+void subghz_scene_ext_module_settings_on_exit(void* context) {
+ SubGhz* subghz = context;
+ variable_item_list_reset(subghz->variable_item_list);
+}
diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c
index fea4b6aef..e396527cc 100644
--- a/applications/main/subghz/scenes/subghz_scene_read_raw.c
+++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c
@@ -119,9 +119,6 @@ void subghz_scene_read_raw_on_enter(void* context) {
subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME);
furi_assert(subghz->txrx->decoder_result);
- // make sure we're not in auto-detect mode, which is only meant for the Read app
- subghz_protocol_decoder_raw_set_auto_mode(subghz->txrx->decoder_result, false);
-
//set filter RAW feed
subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_RAW);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW);
@@ -422,10 +419,6 @@ void subghz_scene_read_raw_on_exit(void* context) {
subghz->state_notifications = SubGhzNotificationStateIDLE;
notification_message(subghz->notifications, &sequence_reset_rgb);
-//filter restoration
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- subghz_last_settings_set_detect_raw_values(subghz);
-#else
- subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
-#endif
+ //filter restoration
+ subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
}
\ No newline at end of file
diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c
index cab19f730..e1ea08497 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver.c
@@ -1,6 +1,7 @@
#include "../subghz_i.h"
#include "../views/receiver.h"
#include
+#include
#define TAG "SubGhzSceneReceiver"
@@ -53,7 +54,10 @@ static void subghz_scene_receiver_update_statusbar(void* context) {
} else {
subghz_get_frequency_modulation(subghz, frequency_str, NULL);
furi_string_printf(
- modulation_str, "Mod: %s", furi_string_get_cstr(subghz->txrx->preset->name));
+ modulation_str,
+ "%s Mod: %s",
+ furi_hal_subghz_get_radio_type() ? "Ext" : "Int",
+ furi_string_get_cstr(subghz->txrx->preset->name));
}
#else
subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
@@ -157,6 +161,11 @@ void subghz_scene_receiver_on_enter(void* context) {
}
subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen);
+ //to use a universal decoder, we are looking for a link to it
+ subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
+ subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME);
+ furi_assert(subghz->txrx->decoder_result);
+
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver);
}
@@ -173,7 +182,6 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
subghz_sleep(subghz);
}
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
- subghz_history_set_hopper_state(subghz->txrx->history, false);
subghz->txrx->idx_menu_chosen = 0;
subghz_receiver_set_rx_callback(subghz->txrx->receiver, NULL, subghz);
@@ -221,6 +229,13 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
subghz_hopper_update(subghz);
subghz_scene_receiver_update_statusbar(subghz);
}
+
+ //get RSSI
+ float rssi = furi_hal_subghz_get_rssi();
+ subghz_receiver_rssi(subghz->subghz_receiver, rssi);
+ subghz_protocol_decoder_bin_raw_data_input_rssi(
+ (SubGhzProtocolDecoderBinRAW*)subghz->txrx->decoder_result, rssi);
+
switch(subghz->state_notifications) {
case SubGhzNotificationStateRx:
notification_message(subghz->notifications, &sequence_blink_cyan_10);
diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c
index c23d93496..af4c6aca3 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c
@@ -1,16 +1,11 @@
#include "../subghz_i.h"
#include
-#include
-
-#define TAG "SubGhzSceneReceiverConfig"
-
enum SubGhzSettingIndex {
SubGhzSettingIndexFrequency,
SubGhzSettingIndexHopping,
SubGhzSettingIndexModulation,
- SubGhzSettingIndexDetectRaw,
- SubGhzSettingIndexRSSIThreshold,
+ SubGhzSettingIndexBinRAW,
SubGhzSettingIndexSound,
SubGhzSettingIndexLock,
SubGhzSettingIndexRAWThesholdRSSI,
@@ -45,35 +40,6 @@ const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = {
-40.0f,
};
-#define BANDWIDTH_COUNT 16
-const char* const bandwidth_labels[BANDWIDTH_COUNT] = {
- "58 kHz",
- "68 kHz",
- "81 kHz",
- "102 kHz",
- "116 kHz",
- "135 kHz",
- "162 kHz",
- "203 kHz",
- "232 kHz",
- "270 kHz",
- "325 kHz",
- "406 kHz",
- "464 kHz",
- "541 kHz",
- "650 kHz",
- "812 kHz",
-};
-
-// Bandwidths values are ordered from F (58kHz) to 0 (812kHz)
-#define BANDWIDTH_INDEX(value) ((uint8_t)15 - ((uint8_t)(value >> 4) & 0x0F))
-
-#define MANCHESTER_FLAG_COUNT 2
-const char* const manchester_flag_text[MANCHESTER_FLAG_COUNT] = {
- "OFF",
- "ON",
-};
-
#define HOPPING_COUNT 2
const char* const hopping_text[HOPPING_COUNT] = {
"OFF",
@@ -84,40 +50,6 @@ const uint32_t hopping_value[HOPPING_COUNT] = {
SubGhzHopperStateRunnig,
};
-#define DETECT_RAW_COUNT 2
-const char* const detect_raw_text[DETECT_RAW_COUNT] = {
- "OFF",
- "ON",
-};
-
-#ifndef SUBGHZ_SAVE_DETECT_RAW_SETTING
-const SubGhzProtocolFlag detect_raw_value[DETECT_RAW_COUNT] = {
- SubGhzProtocolFlag_Decodable,
- SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_RAW,
-};
-#endif
-
-#define RSSI_THRESHOLD_COUNT 7
-const char* const rssi_threshold_text[RSSI_THRESHOLD_COUNT] = {
- "-72db",
- "-67db",
- "-62db",
- "-57db",
- "-52db",
- "-47db",
- "-42db",
-};
-
-const int rssi_threshold_value[RSSI_THRESHOLD_COUNT] = {
- -72,
- -67,
- -62,
- -57,
- -52,
- -47,
- -42,
-};
-
#define SPEAKER_COUNT 2
const char* const speaker_text[SPEAKER_COUNT] = {
"OFF",
@@ -127,18 +59,15 @@ const uint32_t speaker_value[SPEAKER_COUNT] = {
SubGhzSpeakerStateShutdown,
SubGhzSpeakerStateEnable,
};
-
-// Allow advanced edit only on specific preset
-bool subghz_scene_receiver_config_can_edit_current_preset(SubGhz* subghz) {
- SubGhzRadioPreset* preset = subghz->txrx->preset;
-
- bool preset_name_allow_edit =
- !strcmp(furi_string_get_cstr(preset->name), ADVANCED_AM_PRESET_NAME) ||
- !strcmp(furi_string_get_cstr(preset->name), "CUSTOM");
-
- return preset && preset_name_allow_edit &&
- subghz_preset_custom_is_ook_modulation(preset->data, preset->data_size);
-}
+#define BIN_RAW_COUNT 2
+const char* const bin_raw_text[BIN_RAW_COUNT] = {
+ "OFF",
+ "ON",
+};
+const uint32_t bin_raw_value[BIN_RAW_COUNT] = {
+ SubGhzProtocolFlag_Decodable,
+ SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW,
+};
uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
furi_assert(context);
@@ -170,52 +99,6 @@ uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void*
return index;
}
-// Advanced settings of preset may change if preset was changed.
-// In that case - update values
-static void subghz_scene_receiver_config_update_advanced(SubGhz* subghz) {
- uint8_t value_index;
-
- if(subghz->variable_item_bandwidth) {
- value_index = BANDWIDTH_INDEX(subghz->txrx->raw_bandwidth);
- variable_item_set_current_value_index(subghz->variable_item_bandwidth, value_index);
- variable_item_set_current_value_text(
- subghz->variable_item_bandwidth, bandwidth_labels[value_index]);
- }
-
- if(subghz->variable_item_datarate) {
- variable_item_set_current_value_index(subghz->variable_item_datarate, 0);
-
- char datarate_str[16] = {0};
- subghz_preset_custom_printf_datarate(
- subghz->txrx->raw_datarate, datarate_str, sizeof(datarate_str));
- variable_item_set_current_value_text(subghz->variable_item_datarate, datarate_str);
- }
-
- if(subghz->variable_item_manchester) {
- value_index = subghz->txrx->raw_manchester_enabled ? 1 : 0;
-
- variable_item_set_current_value_index(subghz->variable_item_manchester, value_index);
- variable_item_set_current_value_text(
- subghz->variable_item_manchester, manchester_flag_text[value_index]);
- }
-}
-
-// Apply advanced configuration to advanced am preset
-static void subghz_scene_receiver_config_apply_advanced(SubGhz* subghz) {
- if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) {
- SubGhzRadioPreset* preset = subghz->txrx->preset;
-
- subghz_preset_custom_set_bandwidth(
- preset->data, preset->data_size, subghz->txrx->raw_bandwidth);
-
- subghz_preset_custom_set_machester_enable(
- preset->data, preset->data_size, subghz->txrx->raw_manchester_enabled);
-
- subghz_preset_custom_set_datarate(
- preset->data, preset->data_size, subghz->txrx->raw_datarate);
- }
-}
-
uint8_t subghz_scene_receiver_config_hopper_value_index(
const uint32_t value,
const uint32_t values[],
@@ -236,36 +119,6 @@ uint8_t subghz_scene_receiver_config_hopper_value_index(
}
}
-#ifndef SUBGHZ_SAVE_DETECT_RAW_SETTING
-uint8_t subghz_scene_receiver_config_detect_raw_value_index(
- const SubGhzProtocolFlag value,
- const SubGhzProtocolFlag values[],
- uint8_t values_count) {
- uint8_t index = 0;
- for(uint8_t i = 0; i < values_count; i++) {
- if(value == values[i]) {
- index = i;
- break;
- }
- }
- return index;
-}
-#endif
-
-uint8_t subghz_scene_receiver_config_rssi_threshold_value_index(
- const int value,
- const int values[],
- uint8_t values_count) {
- uint8_t index = 0;
- for(uint8_t i = 0; i < values_count; i++) {
- if(value == values[i]) {
- index = i;
- break;
- }
- }
- return index;
-}
-
static void subghz_scene_receiver_config_set_frequency(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
@@ -301,49 +154,12 @@ static void subghz_scene_receiver_config_set_preset(VariableItem* item) {
subghz->txrx->preset->frequency,
subghz_setting_get_preset_data(subghz->setting, index),
subghz_setting_get_preset_data_size(subghz->setting, index));
-
- subghz_scene_receiver_config_update_advanced(subghz);
-}
-
-static void subghz_scene_receiver_config_set_rssi_threshold(VariableItem* item) {
- SubGhz* subghz = variable_item_get_context(item);
- uint8_t index = variable_item_get_current_value_index(item);
-
- variable_item_set_current_value_text(item, rssi_threshold_text[index]);
- subghz_protocol_decoder_raw_set_rssi_threshold(
- subghz_receiver_search_decoder_base_by_name(
- subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME),
- rssi_threshold_value[index]);
-}
-
-static void subghz_scene_receiver_config_set_detect_raw(VariableItem* item) {
- SubGhz* subghz = variable_item_get_context(item);
- uint8_t index = variable_item_get_current_value_index(item);
-
- //if(subghz->txrx->hopper_state == 0) {
- variable_item_set_current_value_text(item, detect_raw_text[index]);
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- subghz->last_settings->detect_raw = index;
-
- subghz_last_settings_set_detect_raw_values(subghz);
-#else
- subghz_receiver_set_filter(subghz->txrx->receiver, detect_raw_value[index]);
-
- subghz_protocol_decoder_raw_set_auto_mode(
- subghz_receiver_search_decoder_base_by_name(
- subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME),
- (index == 1));
-#endif
- /*} else {
- variable_item_set_current_value_index(item, 0);
- }*/
}
static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
- //if(subghz_receiver_get_filter(subghz->txrx->receiver) == SubGhzProtocolFlag_Decodable) {
variable_item_set_current_value_text(item, hopping_text[index]);
if(hopping_value[index] == SubGhzHopperStateOFF) {
char text_buf[10] = {0};
@@ -374,10 +190,6 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item)
}
subghz->txrx->hopper_state = hopping_value[index];
- subghz_history_set_hopper_state(subghz->txrx->history, (index == 1));
- /*} else {
- variable_item_set_current_value_index(item, 0);
- }*/
}
static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
@@ -388,6 +200,15 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
subghz->txrx->speaker_state = speaker_value[index];
}
+static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) {
+ SubGhz* subghz = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+
+ variable_item_set_current_value_text(item, bin_raw_text[index]);
+ subghz->txrx->filter = bin_raw_value[index];
+ subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
+}
+
static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
@@ -396,107 +217,6 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it
subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index];
}
-static void subghz_scene_receiver_config_set_raw_ook_bandwidth(VariableItem* item) {
- SubGhz* subghz = variable_item_get_context(item);
- if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) {
- // update bandwidth value from selected index
- uint8_t index = variable_item_get_current_value_index(item);
- subghz->txrx->raw_bandwidth = subghz_preset_custom_bandwidth_values[index];
-
- subghz_scene_receiver_config_update_advanced(subghz);
- } else {
- furi_string_set(
- subghz->error_str, "Read-only\nsetting!\nUse '" ADVANCED_AM_PRESET_NAME "'\npreset.");
- view_dispatcher_send_custom_event(
- subghz->view_dispatcher, SubGhzCustomEventSceneSettingError);
- }
-}
-
-static void subghz_scene_receiver_config_set_manchester_flag(VariableItem* item) {
- SubGhz* subghz = variable_item_get_context(item);
- if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) {
- // update enable flag from view
- uint8_t index = variable_item_get_current_value_index(item);
- subghz->txrx->raw_manchester_enabled = index == 0 ? false : true;
-
- subghz_scene_receiver_config_update_advanced(subghz);
- } else {
- furi_string_set(
- subghz->error_str, "Read-only\nsetting!\nUse '" ADVANCED_AM_PRESET_NAME "'\npreset.");
- view_dispatcher_send_custom_event(
- subghz->view_dispatcher, SubGhzCustomEventSceneSettingError);
- }
-}
-
-static void subghz_scene_receiver_config_datarate_input_callback(void* context) {
- furi_assert(context);
- SubGhz* subghz = context;
-
- float value = atoff(subghz->datarate_input_str);
- if(value != 0 && value > 0) {
- subghz->txrx->raw_datarate = value;
- subghz_scene_receiver_config_update_advanced(subghz);
- }
-
- // show list view
- view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
-}
-
-static bool subghz_scene_receiver_config_datarate_input_validate(
- const char* text,
- FuriString* error,
- void* context) {
- UNUSED(context);
-
- float value = atoff(text);
- if(value == 0) {
- furi_string_printf(error, "Cannot parse\r\nvalue");
- } else if(value < 0) {
- furi_string_printf(error, "Value\r\nshould be\r\ngreater\r\nthan 0");
- } else {
- return true;
- }
-
- return false;
-}
-
-static void subghz_scene_receiver_config_show_datarate_input(SubGhz* subghz) {
- TextInput* text_input = subghz->text_input;
-
- snprintf(
- subghz->datarate_input_str,
- sizeof(subghz->datarate_input_str),
- "%.2f",
- (double)subghz->txrx->raw_datarate);
-
- text_input_set_header_text(text_input, "Datarate bauds (not kBauds)");
- text_input_set_result_callback(
- text_input,
- subghz_scene_receiver_config_datarate_input_callback,
- subghz,
- subghz->datarate_input_str,
- sizeof(subghz->datarate_input_str),
- false);
-
- text_input_set_validator(
- text_input, subghz_scene_receiver_config_datarate_input_validate, NULL);
- view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput);
-}
-
-static void subghz_scene_receiver_config_set_datarate(VariableItem* item) {
- SubGhz* subghz = variable_item_get_context(item);
- if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) {
- // reset value index in order to show '>' symbol always
- variable_item_set_current_value_index(item, 0);
- subghz_scene_receiver_config_show_datarate_input(subghz);
- } else {
- furi_string_set(
- subghz->error_str, "Read-only\nsetting!\nUse '" ADVANCED_AM_PRESET_NAME "'\npreset.");
- view_dispatcher_send_custom_event(
- subghz->view_dispatcher, SubGhzCustomEventSceneSettingError);
- }
-}
-
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
furi_assert(context);
SubGhz* subghz = context;
@@ -511,13 +231,6 @@ void subghz_scene_receiver_config_on_enter(void* context) {
VariableItem* item;
uint8_t value_index;
-#ifdef FURI_DEBUG
- FURI_LOG_D(
- TAG,
- "Last frequency: %ld, Preset: %ld",
- subghz->last_settings->frequency,
- subghz->last_settings->preset);
-#endif
item = variable_item_list_add(
subghz->variable_item_list,
"Frequency:",
@@ -563,52 +276,35 @@ void subghz_scene_receiver_config_on_enter(void* context) {
subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, hopping_text[value_index]);
+ }
- // Detect Raw
+ if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
+ SubGhzCustomEventManagerSet) {
item = variable_item_list_add(
subghz->variable_item_list,
- "Detect Raw:",
- DETECT_RAW_COUNT,
- subghz_scene_receiver_config_set_detect_raw,
+ "Bin RAW:",
+ BIN_RAW_COUNT,
+ subghz_scene_receiver_config_set_bin_raw,
subghz);
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- value_index = subghz->last_settings->detect_raw;
-#else
- value_index = subghz_scene_receiver_config_detect_raw_value_index(
- subghz_receiver_get_filter(subghz->txrx->receiver),
- detect_raw_value,
- DETECT_RAW_COUNT);
-#endif
+ value_index = value_index_uint32(subghz->txrx->filter, bin_raw_value, BIN_RAW_COUNT);
variable_item_set_current_value_index(item, value_index);
- variable_item_set_current_value_text(item, detect_raw_text[value_index]);
+ variable_item_set_current_value_text(item, bin_raw_text[value_index]);
+ }
- // RSSI
- item = variable_item_list_add(
- subghz->variable_item_list,
- "RSSI for Raw:",
- RSSI_THRESHOLD_COUNT,
- subghz_scene_receiver_config_set_rssi_threshold,
- subghz);
- value_index = subghz_scene_receiver_config_rssi_threshold_value_index(
- subghz_protocol_encoder_get_rssi_threshold(subghz_receiver_search_decoder_base_by_name(
- subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME)),
- rssi_threshold_value,
- RSSI_THRESHOLD_COUNT);
- variable_item_set_current_value_index(item, value_index);
- variable_item_set_current_value_text(item, rssi_threshold_text[value_index]);
+ // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :)
+ item = variable_item_list_add(
+ subghz->variable_item_list,
+ "Sound:",
+ SPEAKER_COUNT,
+ subghz_scene_receiver_config_set_speaker,
+ subghz);
+ value_index = value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT);
+ variable_item_set_current_value_index(item, value_index);
+ variable_item_set_current_value_text(item, speaker_text[value_index]);
+ if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
+ SubGhzCustomEventManagerSet) {
// Lock keyboard
- item = variable_item_list_add(
- subghz->variable_item_list,
- "Sound:",
- SPEAKER_COUNT,
- subghz_scene_receiver_config_set_speaker,
- subghz);
- value_index =
- value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT);
- variable_item_set_current_value_index(item, value_index);
- variable_item_set_current_value_text(item, speaker_text[value_index]);
-
variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL);
variable_item_list_set_enter_callback(
subghz->variable_item_list,
@@ -627,33 +323,6 @@ void subghz_scene_receiver_config_on_enter(void* context) {
subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]);
-
- // Advanced MODEM settings. RW only for ADVANCED_AM_PRESET_NAME
- // Bandwidth
- subghz->variable_item_bandwidth = variable_item_list_add(
- subghz->variable_item_list,
- "Bandwidth:",
- BANDWIDTH_COUNT,
- subghz_scene_receiver_config_set_raw_ook_bandwidth,
- subghz);
-
- // Data rate (editable via OK click)
- subghz->variable_item_datarate = variable_item_list_add(
- subghz->variable_item_list,
- "Data rate:",
- 2,
- subghz_scene_receiver_config_set_datarate,
- subghz);
-
- // Manchester codec flag
- subghz->variable_item_manchester = variable_item_list_add(
- subghz->variable_item_list,
- "Manch. Enc.:",
- MANCHESTER_FLAG_COUNT,
- subghz_scene_receiver_config_set_manchester_flag,
- subghz);
-
- subghz_scene_receiver_config_update_advanced(subghz);
}
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
}
@@ -667,11 +336,6 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even
subghz->lock = SubGhzLockOn;
scene_manager_previous_scene(subghz->scene_manager);
consumed = true;
- } else if(event.event == SubGhzCustomEventSceneSettingError) {
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneShowErrorSub, event.event);
- consumed = true;
}
}
return consumed;
@@ -679,16 +343,6 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even
void subghz_scene_receiver_config_on_exit(void* context) {
SubGhz* subghz = context;
-
- // reset UI variable list items (next scene may be not RAW config)
- subghz->variable_item_bandwidth = NULL;
- subghz->variable_item_datarate = NULL;
- subghz->variable_item_manchester = NULL;
- text_input_set_validator(subghz->text_input, NULL, NULL);
-
- // apply advanced preset variables (if applicable)
- subghz_scene_receiver_config_apply_advanced(subghz);
-
variable_item_list_set_selected_item(subghz->variable_item_list, 0);
variable_item_list_reset(subghz->variable_item_list);
subghz_last_settings_save(subghz->last_settings);
diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c
index e9c849e1e..0b265cca2 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c
@@ -92,8 +92,6 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) {
// Removed static check
if(((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) ==
SubGhzProtocolFlag_Send) &&
- // disable "Send" for auto-captured RAW signals for now. They can still be saved and sent by loading them.
- subghz->txrx->decoder_result->protocol->type != SubGhzProtocolTypeRAW &&
subghz->txrx->decoder_result->protocol->encoder->deserialize) {
widget_add_button_element(
subghz->widget,
@@ -138,6 +136,21 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
subghz_history_get_raw_data(
subghz->txrx->history, subghz->txrx->idx_menu_chosen))) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
+ if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
+ subghz_tx_stop(subghz);
+ }
+ if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) {
+ subghz_begin(
+ subghz,
+ subghz_setting_get_preset_data_by_name(
+ subghz->setting,
+ furi_string_get_cstr(subghz->txrx->preset->name)));
+ subghz_rx(subghz, subghz->txrx->preset->frequency);
+ }
+ if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
+ subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
+ }
+ subghz->state_notifications = SubGhzNotificationStateRx;
} else {
subghz->state_notifications = SubGhzNotificationStateTx;
}
diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c
index 2a61d5dc5..609b4a71d 100644
--- a/applications/main/subghz/scenes/subghz_scene_rpc.c
+++ b/applications/main/subghz/scenes/subghz_scene_rpc.c
@@ -1,7 +1,7 @@
#include "../subghz_i.h"
#include
#include
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
typedef enum {
SubGhzRpcStateIdle,
diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c
index bf8ed0185..48804fe54 100644
--- a/applications/main/subghz/scenes/subghz_scene_save_success.c
+++ b/applications/main/subghz/scenes/subghz_scene_save_success.c
@@ -1,6 +1,6 @@
#include "../subghz_i.h"
#include "../helpers/subghz_custom_event.h"
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
void subghz_scene_save_success_popup_callback(void* context) {
SubGhz* subghz = context;
diff --git a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c
index 2943c764a..113e7ae74 100644
--- a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c
+++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c
@@ -26,16 +26,8 @@ bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneShowErrorSub) {
- if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowErrorSub) ==
- SubGhzCustomEventSceneSettingError) {
- scene_manager_set_scene_state(
- subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet);
- scene_manager_search_and_switch_to_previous_scene(
- subghz->scene_manager, SubGhzSceneReceiverConfig);
- } else {
- scene_manager_search_and_switch_to_previous_scene(
- subghz->scene_manager, SubGhzSceneStart);
- }
+ scene_manager_search_and_switch_to_previous_scene(
+ subghz->scene_manager, SubGhzSceneStart);
return true;
}
}
diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c
index 62e30784f..69e6cbea7 100644
--- a/applications/main/subghz/scenes/subghz_scene_start.c
+++ b/applications/main/subghz/scenes/subghz_scene_start.c
@@ -10,27 +10,9 @@ enum SubmenuIndex {
SubmenuIndexAddManually,
SubmenuIndexFrequencyAnalyzer,
SubmenuIndexReadRAW,
+ SubmenuIndexExtSettings,
};
-void subghz_scene_start_remove_advanced_preset(SubGhz* subghz) {
- // delete operation is harmless
- subghz_setting_delete_custom_preset(subghz->setting, ADVANCED_AM_PRESET_NAME);
-}
-
-void subghz_scene_start_load_advanced_preset(SubGhz* subghz) {
- for(uint8_t i = 0; i < subghz_setting_get_preset_count(subghz->setting); i++) {
- if(!strcmp(subghz_setting_get_preset_name(subghz->setting, i), ADVANCED_AM_PRESET_NAME)) {
- return; // already exists
- }
- }
-
- // Load custom advanced AM preset with configurable CFGMDM settings
- FlipperFormat* advanced_am_preset = subghz_preset_custom_advanced_am_preset_alloc();
- subghz_setting_load_custom_preset(
- subghz->setting, ADVANCED_AM_PRESET_NAME, advanced_am_preset);
- flipper_format_free(advanced_am_preset);
-}
-
void subghz_scene_start_submenu_callback(void* context, uint32_t index) {
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
@@ -41,15 +23,6 @@ void subghz_scene_start_on_enter(void* context) {
if(subghz->state_notifications == SubGhzNotificationStateStarting) {
subghz->state_notifications = SubGhzNotificationStateIDLE;
}
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- subghz_last_settings_set_detect_raw_values(subghz);
-#else
- subghz_protocol_decoder_raw_set_auto_mode(
- subghz_receiver_search_decoder_base_by_name(
- subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME),
- false);
- subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
-#endif
submenu_add_item(
subghz->submenu, "Read", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz);
@@ -73,6 +46,12 @@ void subghz_scene_start_on_enter(void* context) {
SubmenuIndexFrequencyAnalyzer,
subghz_scene_start_submenu_callback,
subghz);
+ submenu_add_item(
+ subghz->submenu,
+ "Radio Settings",
+ SubmenuIndexExtSettings,
+ subghz_scene_start_submenu_callback,
+ subghz);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
submenu_add_item(
subghz->submenu, "Test", SubmenuIndexTest, subghz_scene_start_submenu_callback, subghz);
@@ -91,15 +70,23 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
view_dispatcher_stop(subghz->view_dispatcher);
return true;
} else if(event.type == SceneManagerEventTypeCustom) {
- if(event.event == SubmenuIndexReadRAW) {
- subghz_scene_start_load_advanced_preset(subghz);
+ if(event.event == SubmenuIndexExtSettings) {
+ scene_manager_set_scene_state(
+ subghz->scene_manager, SubGhzSceneStart, SubmenuIndexExtSettings);
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneExtModuleSettings);
+ return true;
+
+ } else if(!furi_hal_subghz_check_radio()) {
+ furi_string_set(subghz->error_str, "Please connect\nexternal radio");
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
+ return true;
+ } else if(event.event == SubmenuIndexReadRAW) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
return true;
} else if(event.event == SubmenuIndexRead) {
- subghz_scene_start_remove_advanced_preset(subghz);
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead);
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
@@ -120,7 +107,6 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer);
DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer);
return true;
-
} else if(event.event == SubmenuIndexTest) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest);
diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c
index c39c35679..78295f08c 100644
--- a/applications/main/subghz/subghz.c
+++ b/applications/main/subghz/subghz.c
@@ -208,27 +208,38 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
flipper_format_free(temp_fm_preset2);
- // # HND - FM presets
+ // Pagers
FlipperFormat* temp_fm_preset3 = flipper_format_string_alloc();
flipper_format_write_string_cstr(
temp_fm_preset3,
(const char*)"Custom_preset_data",
- (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00");
+ (const char*)"02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00");
flipper_format_rewind(temp_fm_preset3);
- subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_1", temp_fm_preset3);
+ subghz_setting_load_custom_preset(subghz->setting, (const char*)"Pagers", temp_fm_preset3);
flipper_format_free(temp_fm_preset3);
+ // # HND - FM presets
FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc();
flipper_format_write_string_cstr(
temp_fm_preset4,
(const char*)"Custom_preset_data",
- (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00");
+ (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00");
flipper_format_rewind(temp_fm_preset4);
- subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_2", temp_fm_preset4);
+ subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_1", temp_fm_preset4);
flipper_format_free(temp_fm_preset4);
+ FlipperFormat* temp_fm_preset5 = flipper_format_string_alloc();
+ flipper_format_write_string_cstr(
+ temp_fm_preset5,
+ (const char*)"Custom_preset_data",
+ (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00");
+ flipper_format_rewind(temp_fm_preset5);
+ subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_2", temp_fm_preset5);
+
+ flipper_format_free(temp_fm_preset5);
+
// custom presets loading - end
// Load last used values for Read, Read RAW, etc. or default
@@ -236,20 +247,11 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
subghz->last_settings = subghz_last_settings_alloc();
subghz_last_settings_load(subghz->last_settings, 0);
#if FURI_DEBUG
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- FURI_LOG_D(
- TAG,
- "last frequency: %ld, preset: %ld, detect_raw: %d",
- subghz->last_settings->frequency,
- subghz->last_settings->preset,
- subghz->last_settings->detect_raw);
-#else
FURI_LOG_D(
TAG,
"last frequency: %ld, preset: %ld",
subghz->last_settings->frequency,
subghz->last_settings->preset);
-#endif
#endif
subghz_setting_set_default_frequency(subghz->setting, subghz->last_settings->frequency);
}
@@ -268,6 +270,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
+ subghz->txrx->debug_pin_state = false;
if(!alloc_for_tx_only) {
subghz->txrx->history = subghz_history_alloc();
}
@@ -281,16 +284,15 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
subghz->txrx->environment = subghz_environment_alloc();
subghz_environment_set_came_atomo_rainbow_table_file_name(
subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo"));
+ subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
+ subghz->txrx->environment, EXT_PATH("subghz/assets/alutech_at_4n"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(
subghz->txrx->environment, (void*)&subghz_protocol_registry);
subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment);
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- subghz_last_settings_set_detect_raw_values(subghz);
-#else
- subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable);
-#endif
+ subghz->txrx->filter = SubGhzProtocolFlag_Decodable;
+ subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
subghz_worker_set_overrun_callback(
subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
@@ -314,6 +316,8 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) {
subghz->rpc_ctx = NULL;
}
+ subghz_speaker_off(subghz);
+
#if FURI_DEBUG
// Packet Test
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket);
diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c
index ed1648083..be7143643 100644
--- a/applications/main/subghz/subghz_cli.c
+++ b/applications/main/subghz/subghz_cli.c
@@ -42,8 +42,9 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) {
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
frequency = furi_hal_subghz_set_frequency_and_path(frequency);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
- furi_hal_gpio_write(&gpio_cc1101_g0, true);
+ furi_hal_gpio_init(
+ furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true);
furi_hal_power_suppress_charge_enter();
@@ -252,6 +253,8 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"));
subghz_environment_set_came_atomo_rainbow_table_file_name(
environment, EXT_PATH("subghz/assets/came_atomo"));
+ subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
+ environment, EXT_PATH("subghz/assets/alutech_at_4n"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
@@ -264,7 +267,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
furi_hal_subghz_reset();
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
frequency = furi_hal_subghz_set_frequency_and_path(frequency);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_power_suppress_charge_enter();
@@ -304,6 +307,81 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
free(instance);
}
+void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) {
+ UNUSED(context);
+ uint32_t frequency = 433920000;
+
+ if(furi_string_size(args)) {
+ int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency);
+ if(ret != 1) {
+ printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency);
+ cli_print_usage("subghz rx", "", furi_string_get_cstr(args));
+ return;
+ }
+ if(!furi_hal_subghz_is_frequency_valid(frequency)) {
+ printf(
+ "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n",
+ frequency);
+ return;
+ }
+ }
+
+ // Allocate context and buffers
+ SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));
+ instance->stream =
+ furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));
+ furi_check(instance->stream);
+
+ // Configure radio
+ furi_hal_subghz_reset();
+ furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok270Async);
+ frequency = furi_hal_subghz_set_frequency_and_path(frequency);
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
+
+ furi_hal_power_suppress_charge_enter();
+
+ // Prepare and start RX
+ furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance);
+
+ // Wait for packets to arrive
+ printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency);
+ LevelDuration level_duration;
+ size_t counter = 0;
+ while(!cli_cmd_interrupt_received(cli)) {
+ int ret = furi_stream_buffer_receive(
+ instance->stream, &level_duration, sizeof(LevelDuration), 10);
+ if(ret == 0) {
+ continue;
+ }
+ if(ret != sizeof(LevelDuration)) {
+ puts("stream corrupt");
+ break;
+ }
+ if(level_duration_is_reset(level_duration)) {
+ puts(". ");
+ } else {
+ bool level = level_duration_get_level(level_duration);
+ uint32_t duration = level_duration_get_duration(level_duration);
+ printf("%c%lu ", level ? '+' : '-', duration);
+ }
+ furi_thread_stdout_flush();
+ counter++;
+ if(counter > 255) {
+ puts("\r\n");
+ counter = 0;
+ }
+ }
+
+ // Shutdown radio
+ furi_hal_subghz_stop_async_rx();
+ furi_hal_subghz_sleep();
+
+ furi_hal_power_suppress_charge_exit();
+
+ // Cleanup
+ furi_stream_buffer_free(instance->stream);
+ free(instance);
+}
void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
UNUSED(context);
FuriString* file_name;
@@ -372,6 +450,8 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
}
subghz_environment_set_came_atomo_rainbow_table_file_name(
environment, EXT_PATH("subghz/assets/came_atomo"));
+ subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
+ environment, EXT_PATH("subghz/assets/alutech_at_4n"));
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment, EXT_PATH("subghz/assets/nice_flor_s"));
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
@@ -387,7 +467,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
}
printf(
- "Listening at %s.\r\n\r\nPress CTRL+C to stop\r\n\r\n",
+ "Listening at \033[0;33m%s\033[0m.\r\n\r\nPress CTRL+C to stop\r\n\r\n",
furi_string_get_cstr(file_name));
LevelDuration level_duration;
@@ -426,7 +506,8 @@ static void subghz_cli_command_print_usage() {
printf("\tchat \t - Chat with other Flippers\r\n");
printf(
"\ttx <3 byte Key: in hex> \t - Transmitting key\r\n");
- printf("\trx \t - Reception key\r\n");
+ printf("\trx \t - Receive\r\n");
+ printf("\trx_raw \t - Receive RAW\r\n");
printf("\tdecode_raw \t - Testing\r\n");
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
@@ -578,7 +659,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
- furi_string_printf(name, "%s: ", furi_hal_version_get_name_ptr());
+ furi_string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr());
furi_string_set(input, name);
printf("%s", furi_string_get_cstr(input));
fflush(stdout);
@@ -659,14 +740,18 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) {
notification_message(notification, &sequence_single_vibro);
break;
case SubGhzChatEventUserEntrance:
- furi_string_printf(sysmsg, "%s joined chat.\r\n", furi_hal_version_get_name_ptr());
+ furi_string_printf(
+ sysmsg,
+ "\033[0;34m%s joined chat.\033[0m\r\n",
+ furi_hal_version_get_name_ptr());
subghz_chat_worker_write(
subghz_chat,
(uint8_t*)furi_string_get_cstr(sysmsg),
strlen(furi_string_get_cstr(sysmsg)));
break;
case SubGhzChatEventUserExit:
- furi_string_printf(sysmsg, "%s left chat.\r\n", furi_hal_version_get_name_ptr());
+ furi_string_printf(
+ sysmsg, "\033[0;31m%s left chat.\033[0m\r\n", furi_hal_version_get_name_ptr());
subghz_chat_worker_write(
subghz_chat,
(uint8_t*)furi_string_get_cstr(sysmsg),
@@ -725,6 +810,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) {
break;
}
+ if(furi_string_cmp_str(cmd, "rx_raw") == 0) {
+ subghz_cli_command_rx_raw(cli, args, context);
+ break;
+ }
+
if(furi_string_cmp_str(cmd, "decode_raw") == 0) {
subghz_cli_command_decode_raw(cli, args, context);
break;
@@ -763,6 +853,7 @@ void subghz_on_system_start() {
Cli* cli = furi_record_open(RECORD_CLI);
cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL);
+
// psst RM... i know you dont care much about errors, but if you ever see this... incompatible pointer type :3
cli_add_command(cli, "chat", CliCommandFlagDefault, subghz_cli_command_chat, NULL);
@@ -770,4 +861,4 @@ void subghz_on_system_start() {
#else
UNUSED(subghz_cli_command);
#endif
-}
\ No newline at end of file
+}
diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c
index e8d3acfd7..184146698 100644
--- a/applications/main/subghz/subghz_history.c
+++ b/applications/main/subghz/subghz_history.c
@@ -1,33 +1,15 @@
#include "subghz_history.h"
-#include "subghz_history_private.h"
#include
-#include
-#include
-#include "flipper_format_stream_i.h"
-#include
-#define SUBGHZ_HISTORY_MAX 60
-
-/**
- * @brief Settings for temporary files
- *
- */
-#define SUBGHZ_HISTORY_TMP_DIR EXT_PATH("subghz/tmp_history")
-#define SUBGHZ_HISTORY_TMP_EXTENSION ".tmp"
-#define SUBGHZ_HISTORY_TMP_SIGNAL_MAX 700
-#define SUBGHZ_HISTORY_TMP_SIGNAL_MIN 100
-#define SUBGHZ_HISTORY_TMP_REMOVE_FILES true
-#define SUBGHZ_HISTORY_TMP_RAW_KEY "RAW_Data"
-#define MAX_LINE 500
-const size_t buffer_size = 32;
+#include
+#define SUBGHZ_HISTORY_MAX 55
+#define SUBGHZ_HISTORY_FREE_HEAP 20480
#define TAG "SubGhzHistory"
typedef struct {
FuriString* item_str;
FlipperFormat* flipper_string;
- FuriString* protocol_name;
- bool is_file;
uint8_t type;
SubGhzRadioPreset* preset;
} SubGhzHistoryItem;
@@ -45,146 +27,30 @@ struct SubGhzHistory {
uint16_t last_index_write;
uint8_t code_last_hash_data;
FuriString* tmp_string;
- bool write_tmp_files;
- bool is_hopper_running;
- Storage* storage;
SubGhzHistoryStruct* history;
};
-#ifdef FURI_DEBUG
-#define LOG_DELAY 0
-#endif
-
-FuriString* subghz_history_generate_temp_filename(uint32_t index) {
- FuriHalRtcDateTime datetime = {0};
- furi_hal_rtc_get_datetime(&datetime);
- return furi_string_alloc_printf("%03ld%s", index, SUBGHZ_HISTORY_TMP_EXTENSION);
-}
-
-bool subghz_history_is_tmp_dir_exists(SubGhzHistory* instance) {
- FileInfo file_info;
- FS_Error error = storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &file_info);
-
- if(error == FSE_OK) {
- if(file_info.flags & FSF_DIRECTORY) {
- return true;
- }
- }
-
- return false;
-}
-
-bool subghz_history_check_sdcard(SubGhzHistory* instance) {
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "check_sdcard");
- uint32_t start_time = furi_get_tick();
-#endif
-
- bool result = false;
- // Stage 0 - check SD Card
- FS_Error status = storage_sd_status(instance->storage);
- if(status == FSE_OK) {
- result = subghz_history_is_tmp_dir_exists(instance);
- if(!subghz_history_is_tmp_dir_exists(instance)) {
- result = storage_simply_mkdir(instance->storage, SUBGHZ_HISTORY_TMP_DIR);
- }
- } else {
- FURI_LOG_W(TAG, "SD storage not installed! Status: %d", status);
- }
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "Running time (check_sdcard): %ld ms", furi_get_tick() - start_time);
-#endif
-
- return result;
-}
-
-void subghz_history_clear_tmp_dir(SubGhzHistory* instance) {
- furi_assert(instance);
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "clear_tmp_dir");
-#endif
-
- if(!instance->write_tmp_files) {
- // Nothing to do here!
- return;
- }
- //uint32_t start_time = furi_get_tick();
-#ifdef SUBGHZ_HISTORY_TMP_REMOVE_FILES
- // Stage 0 - Dir exists?
- bool res = subghz_history_is_tmp_dir_exists(instance);
- if(res) {
- // Stage 1 - delete all content if exists
- FileInfo fileinfo;
- storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &fileinfo);
-
- res = fileinfo.flags & FSF_DIRECTORY ?
- storage_simply_remove_recursive(instance->storage, SUBGHZ_HISTORY_TMP_DIR) :
- (storage_common_remove(instance->storage, SUBGHZ_HISTORY_TMP_DIR) == FSE_OK);
- }
-
- // Stage 2 - create dir if necessary
- res = storage_simply_mkdir(instance->storage, SUBGHZ_HISTORY_TMP_DIR);
- if(!res) {
- FURI_LOG_E(TAG, "Cannot process temp dir!");
- }
-#endif
- /* uint32_t stop_time = furi_get_tick() - start_time;
- FURI_LOG_I(TAG, "Running time (clear_tmp_dir): %d ms", stop_time);*/
-}
-
SubGhzHistory* subghz_history_alloc(void) {
SubGhzHistory* instance = malloc(sizeof(SubGhzHistory));
instance->tmp_string = furi_string_alloc();
instance->history = malloc(sizeof(SubGhzHistoryStruct));
SubGhzHistoryItemArray_init(instance->history->data);
- instance->storage = furi_record_open(RECORD_STORAGE);
- instance->write_tmp_files = subghz_history_check_sdcard(instance);
-
- instance->is_hopper_running = false;
-
- if(!instance->write_tmp_files) {
- FURI_LOG_E(TAG, "Unstable work! Cannot use SD Card!");
- }
-
return instance;
}
-void subghz_history_item_free(void* current_item) {
- furi_assert(current_item);
- SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item;
- furi_string_free(item->item_str);
- furi_string_free(item->preset->name);
- furi_string_free(item->protocol_name);
-
- free(item->preset);
- item->type = 0;
- item->is_file = false;
-
- if(item->flipper_string != NULL) {
- flipper_format_free(item->flipper_string);
- }
-}
-
-void subghz_history_clean_item_array(SubGhzHistory* instance) {
- for
- M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) {
- subghz_history_item_free(item);
- }
-}
-
void subghz_history_free(SubGhzHistory* instance) {
furi_assert(instance);
furi_string_free(instance->tmp_string);
-
- subghz_history_clean_item_array(instance);
+ for
+ M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) {
+ furi_string_free(item->item_str);
+ furi_string_free(item->preset->name);
+ free(item->preset);
+ flipper_format_free(item->flipper_string);
+ item->type = 0;
+ }
SubGhzHistoryItemArray_clear(instance->history->data);
free(instance->history);
-
- // Delete all temporary file, on exit it's ok
- subghz_history_clear_tmp_dir(instance);
-
- furi_record_close(RECORD_STORAGE);
-
free(instance);
}
@@ -209,20 +75,19 @@ const char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) {
void subghz_history_reset(SubGhzHistory* instance) {
furi_assert(instance);
furi_string_reset(instance->tmp_string);
-
- subghz_history_clean_item_array(instance);
-
+ for
+ M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) {
+ furi_string_free(item->item_str);
+ furi_string_free(item->preset->name);
+ free(item->preset);
+ flipper_format_free(item->flipper_string);
+ item->type = 0;
+ }
SubGhzHistoryItemArray_reset(instance->history->data);
instance->last_index_write = 0;
instance->code_last_hash_data = 0;
}
-void subghz_history_set_hopper_state(SubGhzHistory* instance, bool hopper_state) {
- furi_assert(instance);
-
- instance->is_hopper_running = hopper_state;
-}
-
uint16_t subghz_history_get_item(SubGhzHistory* instance) {
furi_assert(instance);
return instance->last_index_write;
@@ -237,8 +102,12 @@ uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx)
const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance);
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
-
- return furi_string_get_cstr(item->protocol_name);
+ flipper_format_rewind(item->flipper_string);
+ if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) {
+ FURI_LOG_E(TAG, "Missing Protocol");
+ furi_string_reset(instance->tmp_string);
+ }
+ return furi_string_get_cstr(instance->tmp_string);
}
FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx) {
@@ -247,72 +116,27 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx
if(item->flipper_string) {
return item->flipper_string;
} else {
- bool result_ok = false;
- if(instance->write_tmp_files && item->is_file) {
- // We have files!
- FuriString* filename = subghz_history_generate_temp_filename(idx);
- FuriString* dir_path;
-
- dir_path = furi_string_alloc_printf(
- "%s/%s", SUBGHZ_HISTORY_TMP_DIR, furi_string_get_cstr(filename));
-
- if(storage_file_exists(instance->storage, furi_string_get_cstr(dir_path))) {
-#ifdef FURI_DEBUG
- FURI_LOG_D(TAG, "Exist: %s", furi_string_get_cstr(dir_path));
- furi_delay_ms(LOG_DELAY);
-#endif
- // Set to current anyway it has NULL value
- item->flipper_string = flipper_format_string_alloc();
- Stream* dst_stream = flipper_format_get_raw_stream(item->flipper_string);
- stream_clean(dst_stream);
-
- size_t size = stream_load_from_file(
- dst_stream, instance->storage, furi_string_get_cstr(dir_path));
- if(size > 0) {
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "Save ok!");
- furi_delay_ms(LOG_DELAY);
-#endif
- // We changed contents of file, so we no needed to load
- // content from disk for the next time
- item->is_file = false;
- result_ok = true;
- } else {
- FURI_LOG_E(TAG, "Stream copy failed!");
- flipper_format_free(item->flipper_string);
- }
- } else {
- FURI_LOG_E(TAG, "Can't convert filename to file");
- }
-
- furi_string_free(filename);
- furi_string_free(dir_path);
- } else {
-#ifdef FURI_DEBUG
- FURI_LOG_W(TAG, "Write TMP files failed!");
- furi_delay_ms(LOG_DELAY);
-#endif
- }
- return result_ok ? item->flipper_string : NULL;
+ return NULL;
}
}
-
bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) {
furi_assert(instance);
- if(instance->last_index_write == SUBGHZ_HISTORY_MAX) {
- if(output != NULL) furi_string_printf(output, "Memory is FULL");
+ if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) {
+ if(output != NULL) furi_string_printf(output, " Free heap LOW");
return true;
}
- if(output != NULL) {
- furi_string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX);
+ if(instance->last_index_write == SUBGHZ_HISTORY_MAX) {
+ if(output != NULL) furi_string_printf(output, " Memory is FULL");
+ return true;
}
+ if(output != NULL)
+ furi_string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX);
return false;
}
uint16_t subghz_history_get_last_index(SubGhzHistory* instance) {
return instance->last_index_write;
}
-
void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) {
SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
furi_string_set(output, item->item_str);
@@ -325,9 +149,8 @@ bool subghz_history_add_to_history(
furi_assert(instance);
furi_assert(context);
- if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) {
- return false;
- }
+ if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false;
+ if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false;
SubGhzProtocolDecoderBase* decoder_base = context;
if((instance->code_last_hash_data ==
@@ -339,6 +162,7 @@ bool subghz_history_add_to_history(
instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base);
instance->last_update_timestamp = furi_get_tick();
+
FuriString* text;
text = furi_string_alloc();
SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data);
@@ -351,11 +175,6 @@ bool subghz_history_add_to_history(
item->preset->data_size = preset->data_size;
item->item_str = furi_string_alloc();
- item->protocol_name = furi_string_alloc();
-
- bool tmp_file_for_raw = false;
-
- // At this point file mapped to memory otherwise file cannot decode
item->flipper_string = flipper_format_string_alloc();
subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset);
@@ -367,30 +186,8 @@ bool subghz_history_add_to_history(
if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
- } else {
- furi_string_printf(
- item->protocol_name, "%s", furi_string_get_cstr(instance->tmp_string));
}
- if(!strcmp(furi_string_get_cstr(instance->tmp_string), "RAW")) {
- // Check if hopper enabled we need to add little delay
- if(instance->is_hopper_running) {
- furi_delay_ms(40);
- }
- // Enable writing temp files to micro sd
- tmp_file_for_raw = true;
- // Write display name
- furi_string_printf(
- item->item_str,
- "RAW %03ld.%02ld",
- preset->frequency / 1000000 % 1000,
- preset->frequency / 10000 % 100);
- // Rewind
- if(!flipper_format_rewind(item->flipper_string)) {
- FURI_LOG_E(TAG, "Rewind error");
- }
-
- break;
- } else if(!strcmp(furi_string_get_cstr(instance->tmp_string), "KeeLoq")) {
+ if(!strcmp(furi_string_get_cstr(instance->tmp_string), "KeeLoq")) {
furi_string_set(instance->tmp_string, "KL ");
if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) {
FURI_LOG_E(TAG, "Missing Protocol");
@@ -411,484 +208,34 @@ bool subghz_history_add_to_history(
}
uint8_t key_data[sizeof(uint64_t)] = {0};
if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) {
- FURI_LOG_E(TAG, "Missing Key");
- break;
+ FURI_LOG_D(TAG, "No Key");
}
uint64_t data = 0;
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
data = (data << 8) | key_data[i];
}
- if(!(uint32_t)(data >> 32)) {
- furi_string_printf(
- item->item_str,
- "%s %lX",
- furi_string_get_cstr(instance->tmp_string),
- (uint32_t)(data & 0xFFFFFFFF));
+ if(data != 0) {
+ if(!(uint32_t)(data >> 32)) {
+ furi_string_printf(
+ item->item_str,
+ "%s %lX",
+ furi_string_get_cstr(instance->tmp_string),
+ (uint32_t)(data & 0xFFFFFFFF));
+ } else {
+ furi_string_printf(
+ item->item_str,
+ "%s %lX%08lX",
+ furi_string_get_cstr(instance->tmp_string),
+ (uint32_t)(data >> 32),
+ (uint32_t)(data & 0xFFFFFFFF));
+ }
} else {
- furi_string_printf(
- item->item_str,
- "%s %lX%08lX",
- furi_string_get_cstr(instance->tmp_string),
- (uint32_t)(data >> 32),
- (uint32_t)(data & 0xFFFFFFFF));
+ furi_string_printf(item->item_str, "%s", furi_string_get_cstr(instance->tmp_string));
}
+
} while(false);
- // If we can write to files
- if(instance->write_tmp_files && tmp_file_for_raw) {
- FuriString* filename = subghz_history_generate_temp_filename(instance->last_index_write);
- FuriString* dir_path;
- dir_path = furi_string_alloc();
-
- furi_string_cat_printf(
- dir_path, "%s/%s", SUBGHZ_HISTORY_TMP_DIR, furi_string_get_cstr(filename));
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "Save temp file: %s", furi_string_get_cstr(dir_path));
-#endif
- if(!subghz_history_tmp_write_file_split(instance, item, furi_string_get_cstr(dir_path))) {
- // Plan B!
- subghz_history_tmp_write_file_full(instance, item, dir_path);
- }
- if(item->is_file) {
- flipper_format_free(item->flipper_string);
- item->flipper_string = NULL;
- }
- furi_string_free(filename);
- furi_string_free(dir_path);
-
- } else {
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "Old fashion way");
-#endif
- }
-
furi_string_free(text);
-
instance->last_index_write++;
return true;
}
-
-static inline bool is_space_playground(char c) {
- return c == ' ' || c == '\t' || c == flipper_format_eolr;
-}
-
-bool subghz_history_stream_read_valid_key(Stream* stream, FuriString* key) {
- furi_string_reset(key);
- uint8_t buffer[buffer_size];
-
- bool found = false;
- bool error = false;
- bool accumulate = true;
- bool new_line = true;
-
- while(true) {
- size_t was_read = stream_read(stream, buffer, buffer_size);
- if(was_read == 0) break;
-
- for(size_t i = 0; i < was_read; i++) {
- uint8_t data = buffer[i];
- if(data == flipper_format_eoln) {
- // EOL found, clean data, start accumulating data and set the new_line flag
- furi_string_reset(key);
- accumulate = true;
- new_line = true;
- } else if(data == flipper_format_eolr) {
- // ignore
- } else if(data == flipper_format_comment && new_line) {
- // if there is a comment character and we are at the beginning of a new line
- // do not accumulate comment data and reset the new_line flag
- accumulate = false;
- new_line = false;
- } else if(data == flipper_format_delimiter) {
- if(new_line) {
- // we are on a "new line" and found the delimiter
- // this can only be if we have previously found some kind of key, so
- // clear the data, set the flag that we no longer want to accumulate data
- // and reset the new_line flag
- furi_string_reset(key);
- accumulate = false;
- new_line = false;
- } else {
- // parse the delimiter only if we are accumulating data
- if(accumulate) {
- // we found the delimiter, move the rw pointer to the delimiter location
- // and signal that we have found something
- if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {
- error = true;
- break;
- }
-
- found = true;
- break;
- }
- }
- } else {
- // just new symbol, reset the new_line flag
- new_line = false;
- if(accumulate) {
- // and accumulate data if we want
- furi_string_push_back(key, data);
- }
- }
- }
-
- if(found || error) break;
- }
-
- return found;
-}
-
-bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) {
- bool found = false;
- FuriString* read_key;
-
- read_key = furi_string_alloc();
-
- while(!stream_eof(stream)) {
- if(subghz_history_stream_read_valid_key(stream, read_key)) {
- if(furi_string_cmp_str(read_key, key) == 0) {
- if(!stream_seek(stream, 2, StreamOffsetFromCurrent)) {
- break;
- }
- found = true;
- break;
- } else if(strict_mode) {
- found = false;
- break;
- }
- }
- }
- furi_string_free(read_key);
-
- return found;
-}
-
-bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last) {
- enum { LeadingSpace, ReadValue, TrailingSpace } state = LeadingSpace;
- const size_t buffer_size = 32;
- uint8_t buffer[buffer_size];
- bool result = false;
- bool error = false;
-
- furi_string_reset(value);
-
- while(true) {
- size_t was_read = stream_read(stream, buffer, buffer_size);
-
- if(was_read == 0) {
- if(state != LeadingSpace && stream_eof(stream)) {
- result = true;
- *last = true;
- } else {
- error = true;
- }
- }
-
- for(uint16_t i = 0; i < was_read; i++) {
- const uint8_t data = buffer[i];
-
- if(state == LeadingSpace) {
- if(is_space_playground(data)) {
- continue;
- } else if(data == flipper_format_eoln) {
- stream_seek(stream, i - was_read, StreamOffsetFromCurrent);
- error = true;
- break;
- } else {
- state = ReadValue;
- furi_string_push_back(value, data);
- }
- } else if(state == ReadValue) {
- if(is_space_playground(data)) {
- state = TrailingSpace;
- } else if(data == flipper_format_eoln) {
- if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {
- error = true;
- } else {
- result = true;
- *last = true;
- }
- break;
- } else {
- furi_string_push_back(value, data);
- }
- } else if(state == TrailingSpace) {
- if(is_space_playground(data)) {
- continue;
- } else if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {
- error = true;
- } else {
- *last = (data == flipper_format_eoln);
- result = true;
- }
- break;
- }
- }
-
- if(error || result) break;
- }
-
- return result;
-}
-
-bool subghz_history_read_int32(Stream* stream, int32_t* _data, const uint16_t data_size) {
- bool result = false;
- result = true;
- FuriString* value;
- value = furi_string_alloc();
-
- for(size_t i = 0; i < data_size; i++) {
- bool last = false;
- result = subghz_history_stream_read_value(stream, value, &last);
- if(result) {
- int scan_values = 0;
-
- int32_t* data = _data;
- scan_values = sscanf(furi_string_get_cstr(value), "%" PRIi32, &data[i]);
-
- if(scan_values != 1) {
- result = false;
- break;
- }
- } else {
- break;
- }
-
- if(last && ((i + 1) != data_size)) {
- result = false;
- break;
- }
- }
-
- furi_string_free(value);
- return result;
-}
-
-uint32_t subghz_history_rand_range(uint32_t min, uint32_t max) {
- // size of range, inclusive
- const uint32_t length_of_range = max - min + 1;
-
- // add n so that we don't return a number below our range
- return (uint32_t)(rand() % length_of_range + min);
-}
-
-bool subghz_history_write_file_noise(
- Stream* file,
- bool is_negative_start,
- size_t current_position,
- bool empty_line) {
- size_t was_write = 0;
- if(empty_line) {
- was_write = stream_write_format(file, "%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY);
-
- if(was_write <= 0) {
- FURI_LOG_E(TAG, "Can't write key!");
- return false;
- }
- }
-
- int8_t first;
- int8_t second;
- if(is_negative_start) {
- first = -1;
- second = 1;
- } else {
- first = 1;
- second = -1;
- }
- while(current_position < MAX_LINE) {
- was_write = stream_write_format(
- file,
- "%ld %ld ",
- subghz_history_rand_range(
- SUBGHZ_HISTORY_TMP_SIGNAL_MIN, SUBGHZ_HISTORY_TMP_SIGNAL_MAX) *
- first,
- subghz_history_rand_range(
- SUBGHZ_HISTORY_TMP_SIGNAL_MIN, SUBGHZ_HISTORY_TMP_SIGNAL_MAX) *
- second);
-
- if(was_write <= 0) {
- FURI_LOG_E(TAG, "Can't write random values!");
- return false;
- }
-
- current_position += was_write;
- }
-
- // Step back to write \n instead of space
- size_t offset = stream_tell(file);
- if(stream_seek(file, offset - 1, StreamOffsetFromCurrent)) {
- FURI_LOG_E(TAG, "Step back failed!");
- return false;
- }
-
- return stream_write_char(file, flipper_format_eoln) > 0;
-}
-
-bool subghz_history_write_file_data(
- Stream* src,
- Stream* file,
- bool* is_negative_start,
- size_t* current_position) {
- size_t offset_file = 0;
- bool result = false;
- int32_t value = 0;
-
- do {
- if(!subghz_history_read_int32(src, &value, 1)) {
- result = true;
- break;
- }
- offset_file = stream_tell(file);
- stream_write_format(file, "%ld ", value);
- *current_position += stream_tell(file) - offset_file;
-
- if(*current_position > MAX_LINE) {
- if((is_negative_start && value > 0) || (!is_negative_start && value < 0)) {
- // Align values
- continue;
- }
-
- if(stream_write_format(file, "\n%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY) == 0) {
- FURI_LOG_E(TAG, "Can't write new line!");
- result = false;
- break;
- }
- *current_position = 0;
- }
- } while(true);
-
- *is_negative_start = value < 0;
-
- return result;
-}
-
-bool subghz_history_tmp_write_file_split(
- SubGhzHistory* instance,
- void* current_item,
- const char* dir_path) {
- furi_assert(instance);
- furi_assert(current_item);
- furi_assert(dir_path);
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "Save temp file splitted: %s", dir_path);
-#endif
- SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item;
-
- uint8_t buffer[buffer_size];
- Stream* src = flipper_format_get_raw_stream(item->flipper_string);
- stream_rewind(src);
-
- FlipperFormat* flipper_format_file = flipper_format_file_alloc(instance->storage);
- bool result = false;
- FuriString* temp_str = furi_string_alloc();
-
- do {
- if(storage_file_exists(instance->storage, dir_path) &&
- storage_common_remove(instance->storage, dir_path) != FSE_OK) {
- FURI_LOG_E(TAG, "Can't delete old file!");
- break;
- }
- path_extract_dirname(dir_path, temp_str);
- FS_Error fs_result =
- storage_common_mkdir(instance->storage, furi_string_get_cstr(temp_str));
- if(fs_result != FSE_OK && fs_result != FSE_EXIST) {
- FURI_LOG_E(TAG, "Can't create dir!");
- break;
- }
- result = flipper_format_file_open_always(flipper_format_file, dir_path);
- if(!result) {
- FURI_LOG_E(TAG, "Can't open file for write!");
- break;
- }
- Stream* file = flipper_format_get_raw_stream(flipper_format_file);
-
- if(!subghz_history_stream_seek_to_key(src, SUBGHZ_HISTORY_TMP_RAW_KEY, false)) {
- FURI_LOG_E(TAG, "Can't find key!");
- break;
- }
- bool is_negative_start = false;
- bool found = false;
-
- size_t offset_start;
- offset_start = stream_tell(src);
-
- // Check for negative value at the start and end to align file by correct values
- size_t was_read = stream_read(src, buffer, 1);
- if(was_read <= 0) {
- FURI_LOG_E(TAG, "Can't obtain first mark!");
- break;
- }
-
- is_negative_start = buffer[0] == '-';
-
- // Ready to write stream to file
- size_t current_position;
- stream_rewind(src);
- current_position = stream_copy(src, file, offset_start);
- if(current_position != offset_start) {
- FURI_LOG_E(TAG, "Invalid copy header data from one stream to another!");
- break;
- }
-
- found = true;
-
- current_position = 0;
- if(!subghz_history_write_file_noise(file, is_negative_start, current_position, false)) {
- FURI_LOG_E(TAG, "Add start noise failed!");
- break;
- }
-
- if(stream_write_format(file, "%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY) == 0) {
- FURI_LOG_E(TAG, "Can't write new line!");
- result = false;
- break;
- }
-
- if(!subghz_history_write_file_data(src, file, &is_negative_start, ¤t_position)) {
- FURI_LOG_E(TAG, "Split by lines failed!");
- break;
- }
-
- if(!subghz_history_write_file_noise(file, is_negative_start, current_position, false)) {
- FURI_LOG_E(TAG, "Add end noise failed!");
- break;
- }
-
- if(!subghz_history_write_file_noise(file, is_negative_start, 0, true)) {
- FURI_LOG_E(TAG, "Add end noise failed!");
- break;
- }
-
- result = found;
- } while(false);
- flipper_format_file_close(flipper_format_file);
- flipper_format_free(flipper_format_file);
- furi_string_free(temp_str);
-
- item->is_file = result;
-
- return result;
-}
-
-void subghz_history_tmp_write_file_full(
- SubGhzHistory* instance,
- void* current_item,
- FuriString* dir_path) {
- SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item;
-#ifdef FURI_DEBUG
- FURI_LOG_W(TAG, "Save temp file full: %s", furi_string_get_cstr(dir_path));
-#endif
- Stream* dst = flipper_format_get_raw_stream(item->flipper_string);
- stream_rewind(dst);
- if(stream_save_to_file(
- dst, instance->storage, furi_string_get_cstr(dir_path), FSOM_CREATE_ALWAYS) > 0) {
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "Save done!");
-#endif
- // This item contains fake data to load from SD
- item->is_file = true;
- } else {
- FURI_LOG_E(TAG, "Stream copy failed!");
- }
-}
\ No newline at end of file
diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h
index ee1ee1a4d..607dbeae2 100644
--- a/applications/main/subghz/subghz_history.h
+++ b/applications/main/subghz/subghz_history.h
@@ -110,10 +110,3 @@ bool subghz_history_add_to_history(
* @return SubGhzProtocolCommonLoad*
*/
FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx);
-
-/** Set hopper state for internal usage in history
- *
- * @param instance - SubGhzHistory instance
- * @param hopper_state - bool is hopper running?
- */
-void subghz_history_set_hopper_state(SubGhzHistory* instance, bool hopper_state);
diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c
index bac25759a..1fbe662ed 100644
--- a/applications/main/subghz/subghz_i.c
+++ b/applications/main/subghz/subghz_i.c
@@ -30,12 +30,6 @@ void subghz_preset_init(
subghz->txrx->preset->frequency = frequency;
subghz->txrx->preset->data = preset_data;
subghz->txrx->preset->data_size = preset_data_size;
-
- subghz->txrx->raw_bandwidth =
- subghz_preset_custom_get_bandwidth(preset_data, preset_data_size);
- subghz->txrx->raw_manchester_enabled =
- subghz_preset_custom_get_machester_enable(preset_data, preset_data_size);
- subghz->txrx->raw_datarate = subghz_preset_custom_get_datarate(preset_data, preset_data_size);
}
bool subghz_set_preset(SubGhz* subghz, const char* preset) {
@@ -75,7 +69,7 @@ void subghz_begin(SubGhz* subghz, uint8_t* preset_data) {
furi_hal_subghz_reset();
furi_hal_subghz_idle();
furi_hal_subghz_load_custom_preset(preset_data);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
}
@@ -90,7 +84,7 @@ uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) {
furi_hal_subghz_idle();
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_flush_rx();
subghz_speaker_on(subghz);
furi_hal_subghz_rx();
@@ -109,8 +103,9 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) {
furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep);
furi_hal_subghz_idle();
furi_hal_subghz_set_frequency_and_path(frequency);
- furi_hal_gpio_write(&gpio_cc1101_g0, false);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false);
+ furi_hal_gpio_init(
+ furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
subghz_speaker_on(subghz);
bool ret = furi_hal_subghz_tx();
subghz->txrx->txrx_state = SubGhzTxRxStateTx;
@@ -602,9 +597,15 @@ void subghz_hopper_update(SubGhz* subghz) {
}
void subghz_speaker_on(SubGhz* subghz) {
+ if(subghz->txrx->debug_pin_state) {
+ furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7);
+ }
+
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_acquire(30)) {
- furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
+ if(!subghz->txrx->debug_pin_state) {
+ furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
+ }
} else {
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
}
@@ -612,9 +613,14 @@ void subghz_speaker_on(SubGhz* subghz) {
}
void subghz_speaker_off(SubGhz* subghz) {
+ if(subghz->txrx->debug_pin_state) {
+ furi_hal_subghz_set_async_mirror_pin(NULL);
+ }
if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) {
if(furi_hal_speaker_is_mine()) {
- furi_hal_subghz_set_async_mirror_pin(NULL);
+ if(!subghz->txrx->debug_pin_state) {
+ furi_hal_subghz_set_async_mirror_pin(NULL);
+ }
furi_hal_speaker_release();
if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown)
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
@@ -623,17 +629,27 @@ void subghz_speaker_off(SubGhz* subghz) {
}
void subghz_speaker_mute(SubGhz* subghz) {
+ if(subghz->txrx->debug_pin_state) {
+ furi_hal_subghz_set_async_mirror_pin(NULL);
+ }
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
- furi_hal_subghz_set_async_mirror_pin(NULL);
+ if(!subghz->txrx->debug_pin_state) {
+ furi_hal_subghz_set_async_mirror_pin(NULL);
+ }
}
}
}
void subghz_speaker_unmute(SubGhz* subghz) {
+ if(subghz->txrx->debug_pin_state) {
+ furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7);
+ }
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
- furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
+ if(!subghz->txrx->debug_pin_state) {
+ furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
+ }
}
}
}
diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h
index a6c96cb69..393dd667d 100644
--- a/applications/main/subghz/subghz_i.h
+++ b/applications/main/subghz/subghz_i.h
@@ -3,7 +3,6 @@
#include "helpers/subghz_types.h"
#include "helpers/subghz_error_type.h"
#include
-#include
#include "subghz.h"
#include "views/receiver.h"
#include "views/transmitter.h"
@@ -63,6 +62,7 @@ struct SubGhzTxRx {
SubGhzEnvironment* environment;
SubGhzReceiver* receiver;
SubGhzTransmitter* transmitter;
+ SubGhzProtocolFlag filter;
SubGhzProtocolDecoderBase* decoder_result;
FlipperFormat* fff_data;
SecureData* secure_data;
@@ -77,15 +77,10 @@ struct SubGhzTxRx {
uint8_t hopper_idx_frequency;
SubGhzRxKeyState rx_key_state;
+ bool debug_pin_state;
+
float raw_threshold_rssi;
uint8_t raw_threshold_rssi_low_count;
-
- // one of the 16 possible bandwidth values
- uint8_t raw_bandwidth;
- // datarate in bauds
- float raw_datarate;
- // flag if manchester encoding/decoding enabled
- bool raw_manchester_enabled;
};
typedef struct SubGhzTxRx SubGhzTxRx;
@@ -114,13 +109,6 @@ struct SubGhz {
SubGhzViewTransmitter* subghz_transmitter;
VariableItemList* variable_item_list;
- // Advanced config items
- VariableItem* variable_item_bandwidth; // specific config list view item: bandwidth
- VariableItem* variable_item_datarate; // specific config list view item: data rate
- VariableItem* variable_item_manchester; // specific config list view item: manchester enc flag
- // Advanced config strings
- char datarate_input_str[16];
-
SubGhzFrequencyAnalyzer* subghz_frequency_analyzer;
SubGhzReadRAW* subghz_read_raw;
bool raw_send_only;
diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c
index 478e32347..8e7016df7 100644
--- a/applications/main/subghz/subghz_last_settings.c
+++ b/applications/main/subghz/subghz_last_settings.c
@@ -1,8 +1,5 @@
#include "subghz_last_settings.h"
#include "subghz_i.h"
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
-#include
-#endif
#define TAG "SubGhzLastSettings"
@@ -16,11 +13,6 @@
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER -93.0f
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
-#define SUBGHZ_LAST_SETTING_DEFAULT_READ_RAW 0
-#define SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW "DetectRaw"
-#endif
-
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY "Frequency"
//#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset"
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL "FeedbackLevel"
@@ -52,9 +44,6 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
//int32_t temp_preset = 0;
bool frequency_analyzer_feedback_level_was_read = false;
bool frequency_analyzer_trigger_was_read = false;
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- uint32_t temp_read_raw = 0;
-#endif
if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH &&
flipper_format_file_open_existing(fff_data_file, SUBGHZ_LAST_SETTINGS_PATH)) {
@@ -73,10 +62,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER,
(float*)&temp_frequency_analyzer_trigger,
1);
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- flipper_format_read_uint32(
- fff_data_file, SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW, (uint32_t*)&temp_read_raw, 1);
-#endif
+
} else {
FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH);
}
@@ -88,9 +74,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
instance->frequency_analyzer_feedback_level =
SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL;
instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER;
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- instance->detect_raw = SUBGHZ_LAST_SETTING_DEFAULT_READ_RAW;
-#endif
+
} else {
instance->frequency = temp_frequency;
instance->frequency_analyzer_feedback_level =
@@ -101,9 +85,6 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
instance->frequency_analyzer_trigger = frequency_analyzer_trigger_was_read ?
temp_frequency_analyzer_trigger :
SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER;
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- instance->detect_raw = temp_read_raw;
-#endif
/*if(temp_preset > (int32_t)preset_count - 1 || temp_preset < 0) {
FURI_LOG_W(TAG, "Last used preset no found");*/
@@ -164,12 +145,6 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
1)) {
break;
}
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- if(!flipper_format_insert_or_update_uint32(
- file, SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW, &instance->detect_raw, 1)) {
- break;
- }
-#endif
saved = true;
} while(0);
@@ -183,17 +158,3 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
return saved;
}
-
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
-void subghz_last_settings_set_detect_raw_values(void* context) {
- furi_assert(context);
- SubGhz* instance = (SubGhz*)context;
- bool is_detect_raw = instance->last_settings->detect_raw > 0;
- subghz_receiver_set_filter(
- instance->txrx->receiver, is_detect_raw ? DETECT_RAW_TRUE : DETECT_RAW_FALSE);
- subghz_protocol_decoder_raw_set_auto_mode(
- subghz_receiver_search_decoder_base_by_name(
- instance->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME),
- is_detect_raw);
-}
-#endif
\ No newline at end of file
diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h
index ef3674d69..f08d99c81 100644
--- a/applications/main/subghz/subghz_last_settings.h
+++ b/applications/main/subghz/subghz_last_settings.h
@@ -1,23 +1,12 @@
#pragma once
-// Enable saving detect raw setting state
-// #define SUBGHZ_SAVE_DETECT_RAW_SETTING 1
-
#include
#include
#include
#include
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
-#include
-#define DETECT_RAW_FALSE SubGhzProtocolFlag_Decodable
-#define DETECT_RAW_TRUE SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_RAW
-#endif
typedef struct {
uint32_t frequency;
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
- uint32_t detect_raw;
-#endif
int32_t preset;
uint32_t frequency_analyzer_feedback_level;
float frequency_analyzer_trigger;
@@ -30,6 +19,3 @@ void subghz_last_settings_free(SubGhzLastSettings* instance);
void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count);
bool subghz_last_settings_save(SubGhzLastSettings* instance);
-#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING
-void subghz_last_settings_set_detect_raw_values(void* context);
-#endif
\ No newline at end of file
diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c
index 0a219a48c..fa3569245 100644
--- a/applications/main/subghz/views/receiver.c
+++ b/applications/main/subghz/views/receiver.c
@@ -7,13 +7,15 @@
#include
#include
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
#define FRAME_HEIGHT 12
#define MAX_LEN_PX 111
#define MENU_ITEMS 4u
#define UNLOCK_CNT 3
+#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
+
typedef struct {
FuriString* item_str;
uint8_t type;
@@ -34,7 +36,7 @@ static const Icon* ReceiverItemIcons[] = {
[SubGhzProtocolTypeUnknown] = &I_Quest_7x8,
[SubGhzProtocolTypeStatic] = &I_Static_9x7,
[SubGhzProtocolTypeDynamic] = &I_Dynamic_9x7,
- [SubGhzProtocolTypeRAW] = &I_Raw_9x7,
+ [SubGhzProtocolTypeBinRAW] = &I_Raw_9x7,
};
typedef enum {
@@ -64,6 +66,7 @@ typedef struct {
uint16_t history_item;
SubGhzViewReceiverBarShow bar_show;
SubGhzViewReceiverMode mode;
+ uint8_t u_rssi;
} SubGhzViewReceiverModel;
void subghz_view_receiver_set_mode(
@@ -73,6 +76,21 @@ void subghz_view_receiver_set_mode(
subghz_receiver->view, SubGhzViewReceiverModel * model, { model->mode = mode; }, true);
}
+void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {
+ furi_assert(instance);
+ with_view_model(
+ instance->view,
+ SubGhzViewReceiverModel * model,
+ {
+ if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
+ model->u_rssi = 0;
+ } else {
+ model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN);
+ }
+ },
+ true);
+}
+
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) {
furi_assert(subghz_receiver);
subghz_receiver->lock_count = 0;
@@ -191,6 +209,16 @@ static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool s
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
}
+static void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
+ for(uint8_t i = 1; i < model->u_rssi; i++) {
+ if(i % 5) {
+ canvas_draw_dot(canvas, 46 + i, 50);
+ canvas_draw_dot(canvas, 47 + i, 51);
+ canvas_draw_dot(canvas, 46 + i, 52);
+ }
+ }
+}
+
void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
@@ -198,8 +226,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
if(model->mode == SubGhzViewReceiverModeLive) {
elements_button_left(canvas, "Config");
- canvas_draw_line(canvas, 46, 51, 125, 51);
+ //canvas_draw_line(canvas, 46, 51, 125, 51);
} else {
+ canvas_draw_line(canvas, 2, 52, 125, 52);
canvas_draw_str(canvas, 3, 62, furi_string_get_cstr(model->progress_str));
}
@@ -235,7 +264,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 63, 46, "Scanning...");
- canvas_draw_line(canvas, 46, 51, 125, 51);
+ //canvas_draw_line(canvas, 46, 51, 125, 51);
canvas_set_font(canvas, FontSecondary);
} else {
canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52);
@@ -245,6 +274,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
}
}
+ if(model->mode == SubGhzViewReceiverModeLive) {
+ subghz_view_rssi_draw(canvas, model);
+ }
switch(model->bar_show) {
case SubGhzViewReceiverBarShowLock:
canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);
@@ -252,7 +284,28 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
break;
case SubGhzViewReceiverBarShowToUnlockPress:
canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str));
+#ifdef SUBGHZ_EXT_PRESET_NAME
+ if(model->history_item == 0 && model->mode == SubGhzViewReceiverModeLive) {
+ canvas_draw_str(
+ canvas,
+ 44 + canvas_string_width(canvas, furi_string_get_cstr(model->frequency_str)) + 1,
+ 62,
+ "MHz");
+ const char* str = furi_string_get_cstr(model->preset_str);
+ const uint8_t vertical_offset = 7;
+ const uint8_t horizontal_offset = 3;
+ const uint8_t string_width = canvas_string_width(canvas, str);
+ canvas_draw_str(
+ canvas,
+ canvas_width(canvas) - (string_width + horizontal_offset),
+ vertical_offset,
+ str);
+ } else {
+ canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str));
+ }
+#else
canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str));
+#endif
canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str));
canvas_set_font(canvas, FontSecondary);
elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h
index 829277174..37eb473de 100644
--- a/applications/main/subghz/views/receiver.h
+++ b/applications/main/subghz/views/receiver.h
@@ -12,6 +12,8 @@ void subghz_view_receiver_set_mode(
SubGhzViewReceiver* subghz_receiver,
SubGhzViewReceiverMode mode);
+void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi);
+
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard);
void subghz_view_receiver_set_callback(
diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c
index ff971fd3b..ce2e34297 100644
--- a/applications/main/subghz/views/subghz_frequency_analyzer.c
+++ b/applications/main/subghz/views/subghz_frequency_analyzer.c
@@ -21,11 +21,12 @@
#define MAX_HISTORY 4
static const uint32_t subghz_frequency_list[] = {
- 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, 309000000,
- 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, 314350000, 314980000,
- 315000000, 318000000, 330000000, 345000000, 348000000, 387000000, 390000000, 418000000,
- 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, 434176948,
- 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, 868350000,
+ 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000,
+ 309000000, 310000000, 312000000, 312100000, 313000000, 313850000, 314000000,
+ 314350000, 314980000, 315000000, 318000000, 330000000, 345000000, 348000000,
+ 350000000, 387000000, 390000000, 418000000, 433075000, 433220000, 433420000,
+ 433657070, 433889000, 433920000, 434075000, 434176948, 434390000, 434420000,
+ 434775000, 438900000, 440175000, 464000000, 467750000, 779000000, 868350000,
868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000};
typedef enum {
@@ -165,6 +166,7 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel
// Title
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str(canvas, 0, 7, furi_hal_subghz_get_radio_type() ? "Ext" : "Int");
canvas_draw_str(canvas, 20, 7, "Frequency Analyzer");
// RSSI
@@ -644,4 +646,4 @@ SubGHzFrequencyAnalyzerFeedbackLevel subghz_frequency_analyzer_feedback_level(
float subghz_frequency_analyzer_get_trigger_level(SubGhzFrequencyAnalyzer* instance) {
furi_assert(instance);
return subghz_frequency_analyzer_worker_get_trigger_level(instance->worker);
-}
\ No newline at end of file
+}
diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c
index dcfc281d2..7ba2f4434 100644
--- a/applications/main/subghz/views/subghz_read_raw.c
+++ b/applications/main/subghz/views/subghz_read_raw.c
@@ -30,6 +30,7 @@ typedef struct {
SubGhzReadRAWStatus status;
bool raw_send_only;
float raw_threshold_rssi;
+ bool not_showing_samples;
} SubGhzReadRAWModel;
void subghz_read_raw_set_callback(
@@ -92,7 +93,10 @@ void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample)
with_view_model(
instance->view,
SubGhzReadRAWModel * model,
- { furi_string_printf(model->sample_write, "%zu spl.", sample); },
+ {
+ model->not_showing_samples = false;
+ furi_string_printf(model->sample_write, "%zu spl.", sample);
+ },
false);
}
@@ -280,8 +284,15 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) {
uint8_t graphics_mode = 1;
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
- canvas_draw_str(canvas, 5, 7, furi_string_get_cstr(model->frequency_str));
- canvas_draw_str(canvas, 40, 7, furi_string_get_cstr(model->preset_str));
+ canvas_draw_str(canvas, 0, 7, furi_string_get_cstr(model->frequency_str));
+ canvas_draw_str(canvas, 35, 7, furi_string_get_cstr(model->preset_str));
+
+ if(model->not_showing_samples) {
+ canvas_draw_str(canvas, 77, 7, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int");
+ } else {
+ canvas_draw_str(canvas, 70, 7, furi_hal_subghz_get_radio_type() ? "E" : "I");
+ }
+
canvas_draw_str_aligned(
canvas, 126, 0, AlignRight, AlignTop, furi_string_get_cstr(model->sample_write));
@@ -448,6 +459,7 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
model->status = SubGhzReadRAWStatusStart;
model->rssi_history_end = false;
model->ind_write = 0;
+ model->not_showing_samples = true;
furi_string_set(model->sample_write, "0 spl.");
furi_string_reset(model->file_name);
instance->callback(SubGhzCustomEventViewReadRAWErase, instance->context);
@@ -509,6 +521,7 @@ void subghz_read_raw_set_status(
model->status = SubGhzReadRAWStatusStart;
model->rssi_history_end = false;
model->ind_write = 0;
+ model->not_showing_samples = true;
furi_string_reset(model->file_name);
furi_string_set(model->sample_write, "0 spl.");
model->raw_threshold_rssi = raw_threshold_rssi;
@@ -530,6 +543,7 @@ void subghz_read_raw_set_status(
model->status = SubGhzReadRAWStatusLoadKeyIDLE;
model->rssi_history_end = false;
model->ind_write = 0;
+ model->not_showing_samples = true;
furi_string_set(model->file_name, file_name);
furi_string_set(model->sample_write, "RAW");
},
@@ -542,6 +556,7 @@ void subghz_read_raw_set_status(
{
model->status = SubGhzReadRAWStatusLoadKeyIDLE;
if(!model->ind_write) {
+ model->not_showing_samples = true;
furi_string_set(model->file_name, file_name);
furi_string_set(model->sample_write, "RAW");
} else {
diff --git a/applications/main/subghz/views/subghz_test_carrier.c b/applications/main/subghz/views/subghz_test_carrier.c
index e533a6aac..2cbde6e32 100644
--- a/applications/main/subghz/views/subghz_test_carrier.c
+++ b/applications/main/subghz/views/subghz_test_carrier.c
@@ -115,14 +115,19 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) {
furi_hal_subghz_set_path(model->path);
if(model->status == SubGhzTestCarrierModelStatusRx) {
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(
+ furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_rx();
} else {
furi_hal_gpio_init(
- &gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
- furi_hal_gpio_write(&gpio_cc1101_g0, true);
+ furi_hal_subghz.cc1101_g0_pin,
+ GpioModeOutputPushPull,
+ GpioPullNo,
+ GpioSpeedLow);
+ furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true);
if(!furi_hal_subghz_tx()) {
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(
+ furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
subghz_test_carrier->callback(
SubGhzTestCarrierEventOnlyRx, subghz_test_carrier->context);
}
@@ -140,7 +145,7 @@ void subghz_test_carrier_enter(void* context) {
furi_hal_subghz_reset();
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
with_view_model(
subghz_test_carrier->view,
diff --git a/applications/main/subghz/views/subghz_test_static.c b/applications/main/subghz/views/subghz_test_static.c
index 6abefda76..b9e5a8c9c 100644
--- a/applications/main/subghz/views/subghz_test_static.c
+++ b/applications/main/subghz/views/subghz_test_static.c
@@ -143,8 +143,9 @@ void subghz_test_static_enter(void* context) {
furi_hal_subghz_reset();
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
- furi_hal_gpio_write(&gpio_cc1101_g0, false);
+ furi_hal_gpio_init(
+ furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false);
instance->status_tx = SubGhzTestStaticStatusIDLE;
with_view_model(
diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c
index dc324a6a5..102639924 100644
--- a/applications/main/subghz/views/transmitter.c
+++ b/applications/main/subghz/views/transmitter.c
@@ -84,10 +84,15 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
- elements_multiline_text(canvas, 0, 7, furi_string_get_cstr(model->key_str));
+ elements_multiline_text_aligned(
+ canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str));
canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str));
- if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send");
+
+ if(model->show_button) {
+ canvas_draw_str(canvas, 58, 62, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int");
+ subghz_view_transmitter_button_right(canvas, "Send");
+ }
}
bool subghz_view_transmitter_input(InputEvent* event, void* context) {
diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c
index 162faf2f1..d87b13063 100644
--- a/applications/main/u2f/scenes/u2f_scene_error.c
+++ b/applications/main/u2f/scenes/u2f_scene_error.c
@@ -1,5 +1,5 @@
#include "../u2f_app_i.h"
-#include "../../../settings/xtreme_settings/xtreme_settings.h"
+#include "xtreme/assets.h"
static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
@@ -27,7 +27,7 @@ void u2f_scene_error_on_enter(void* context) {
app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app);
} else if(app->error == U2fAppErrorCloseRpc) {
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
- if(XTREME_SETTINGS()->nsfw_mode) {
+ if(XTREME_ASSETS()->is_nsfw) {
widget_add_string_multiline_element(
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!");
widget_add_string_multiline_element(
diff --git a/applications/main/u2f/scenes/u2f_scene_main.c b/applications/main/u2f/scenes/u2f_scene_main.c
index af7f1159b..251bc4d99 100644
--- a/applications/main/u2f/scenes/u2f_scene_main.c
+++ b/applications/main/u2f/scenes/u2f_scene_main.c
@@ -1,7 +1,7 @@
#include "../u2f_app_i.h"
#include "../views/u2f_view.h"
#include
-#include "furi_hal.h"
+#include
#include "../u2f.h"
#define U2F_REQUEST_TIMEOUT 500
diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c
index eecd32702..7bd2cf94f 100644
--- a/applications/main/u2f/views/u2f_view.c
+++ b/applications/main/u2f/views/u2f_view.c
@@ -1,7 +1,7 @@
#include "u2f_view.h"
#include
#include
-#include "../../../settings/xtreme_settings/xtreme_assets.h"
+#include "xtreme/assets.h"
struct U2fView {
View* view;
@@ -21,7 +21,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) {
if(model->display_msg == U2fMsgNotConnected) {
canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connect_me_62x31);
- if(XTREME_SETTINGS()->nsfw_mode) {
+ if(XTREME_ASSETS()->is_nsfw) {
canvas_draw_str_aligned(
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Plug me in d-daddy");
} else {
@@ -32,7 +32,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) {
canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31);
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!");
} else if(model->display_msg == U2fMsgRegister) {
- if(XTREME_SETTINGS()->nsfw_mode) {
+ if(XTREME_ASSETS()->is_nsfw) {
elements_button_center(canvas, "CUM");
canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31);
canvas_draw_str_aligned(
@@ -44,7 +44,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) {
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register");
}
} else if(model->display_msg == U2fMsgAuth) {
- if(XTREME_SETTINGS()->nsfw_mode) {
+ if(XTREME_ASSETS()->is_nsfw) {
elements_button_center(canvas, "CUM");
canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31);
canvas_draw_str_aligned(
@@ -57,7 +57,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) {
}
} else if(model->display_msg == U2fMsgSuccess) {
canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31);
- if(XTREME_SETTINGS()->nsfw_mode) {
+ if(XTREME_ASSETS()->is_nsfw) {
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Cum released~");
} else {
canvas_draw_str_aligned(
@@ -65,7 +65,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) {
}
} else if(model->display_msg == U2fMsgError) {
canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Error_62x31);
- if(XTREME_SETTINGS()->nsfw_mode) {
+ if(XTREME_ASSETS()->is_nsfw) {
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Unable to cum");
} else {
canvas_draw_str_aligned(
diff --git a/applications/main/unirfremix/unirfremix_app.c b/applications/main/unirfremix/unirfremix_app.c
index 2c0b68ae7..2b12a12b2 100644
--- a/applications/main/unirfremix/unirfremix_app.c
+++ b/applications/main/unirfremix/unirfremix_app.c
@@ -706,7 +706,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
void unirfremix_subghz_alloc(UniRFRemix* app) {
// load subghz presets
app->setting = subghz_setting_alloc();
- subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user"));
+ subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user.txt"));
// load mfcodes
app->environment = subghz_environment_alloc();
diff --git a/applications/plugins/application.fam b/applications/plugins/application.fam
index 3e5a654aa..3331888f2 100644
--- a/applications/plugins/application.fam
+++ b/applications/plugins/application.fam
@@ -3,7 +3,7 @@ App(
name="Basic applications for plug-in menu",
apptype=FlipperAppType.METAPACKAGE,
provides=[
- "music_player",
+ "music_player",
"music_beeper",
"snake_game",
"bt_hid",
diff --git a/applications/plugins/asteroids/application.fam b/applications/plugins/asteroids/application.fam
index 0a56122e7..f5ad2cdb9 100644
--- a/applications/plugins/asteroids/application.fam
+++ b/applications/plugins/asteroids/application.fam
@@ -5,7 +5,7 @@ App(
entry_point="asteroids_app_entry",
cdefines=["APP_PROTOVIEW"],
requires=["gui"],
- stack_size=8*1024,
+ stack_size=8 * 1024,
order=50,
fap_icon="appicon.png",
fap_category="Games",
diff --git a/applications/plugins/blackjack/application.fam b/applications/plugins/blackjack/application.fam
index 8230cd047..489ce2aeb 100644
--- a/applications/plugins/blackjack/application.fam
+++ b/applications/plugins/blackjack/application.fam
@@ -4,10 +4,10 @@ App(
apptype=FlipperAppType.EXTERNAL,
entry_point="blackjack_app",
cdefines=["APP_BLACKJACK"],
- requires=["gui","storage","canvas"],
+ requires=["gui", "storage", "canvas"],
stack_size=2 * 1024,
order=30,
fap_icon="blackjack_10px.png",
fap_category="Games",
- fap_icon_assets="assets"
-)
\ No newline at end of file
+ fap_icon_assets="assets",
+)
diff --git a/applications/plugins/brainfuck/application.fam b/applications/plugins/brainfuck/application.fam
new file mode 100644
index 000000000..6e2b6d1f9
--- /dev/null
+++ b/applications/plugins/brainfuck/application.fam
@@ -0,0 +1,15 @@
+App(
+ appid="Brainfuck",
+ name="Brainfuck",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="brainfuck_app",
+ requires=[
+ "storage",
+ "gui",
+ ],
+ stack_size=8 * 1024,
+ fap_icon="bfico.png",
+ fap_category="Misc",
+ fap_icon_assets="icons",
+ fap_icon_assets_symbol="brainfuck",
+)
diff --git a/applications/plugins/brainfuck/bfico.png b/applications/plugins/brainfuck/bfico.png
new file mode 100644
index 000000000..b25368fb5
Binary files /dev/null and b/applications/plugins/brainfuck/bfico.png differ
diff --git a/applications/plugins/brainfuck/brainfuck.c b/applications/plugins/brainfuck/brainfuck.c
new file mode 100644
index 000000000..4577de68b
--- /dev/null
+++ b/applications/plugins/brainfuck/brainfuck.c
@@ -0,0 +1,149 @@
+#include "brainfuck_i.h"
+
+/*
+ Due to the lack of documentation on the flipper i copied the picopass app,
+ ripped its insides out and used its hollow corpse to build this app inside of.
+
+ i dont know how this stuff works and after 6 hours of trying to learn it, i dont care
+*/
+
+bool brainfuck_custom_event_callback(void* context, uint32_t event) {
+ furi_assert(context);
+ BFApp* brainfuck = context;
+ return scene_manager_handle_custom_event(brainfuck->scene_manager, event);
+}
+
+bool brainfuck_back_event_callback(void* context) {
+ furi_assert(context);
+ BFApp* brainfuck = context;
+ return scene_manager_handle_back_event(brainfuck->scene_manager);
+}
+
+BFApp* brainfuck_alloc() {
+ BFApp* brainfuck = malloc(sizeof(BFApp));
+
+ brainfuck->dataSize = 0;
+ brainfuck->view_dispatcher = view_dispatcher_alloc();
+ brainfuck->scene_manager = scene_manager_alloc(&brainfuck_scene_handlers, brainfuck);
+ view_dispatcher_enable_queue(brainfuck->view_dispatcher);
+ view_dispatcher_set_event_callback_context(brainfuck->view_dispatcher, brainfuck);
+ view_dispatcher_set_custom_event_callback(
+ brainfuck->view_dispatcher, brainfuck_custom_event_callback);
+ view_dispatcher_set_navigation_event_callback(
+ brainfuck->view_dispatcher, brainfuck_back_event_callback);
+
+ // Open GUI record
+ brainfuck->gui = furi_record_open(RECORD_GUI);
+ view_dispatcher_attach_to_gui(
+ brainfuck->view_dispatcher, brainfuck->gui, ViewDispatcherTypeFullscreen);
+
+ // Open Notification record
+ brainfuck->notifications = furi_record_open(RECORD_NOTIFICATION);
+
+ // Submenu
+ brainfuck->submenu = submenu_alloc();
+ view_dispatcher_add_view(
+ brainfuck->view_dispatcher, brainfuckViewMenu, submenu_get_view(brainfuck->submenu));
+
+ // Popup
+ brainfuck->popup = popup_alloc();
+ view_dispatcher_add_view(
+ brainfuck->view_dispatcher, brainfuckViewPopup, popup_get_view(brainfuck->popup));
+
+ // Text Input
+ brainfuck->text_input = text_input_alloc();
+ view_dispatcher_add_view(
+ brainfuck->view_dispatcher,
+ brainfuckViewTextInput,
+ text_input_get_view(brainfuck->text_input));
+
+ // Textbox
+ brainfuck->text_box = text_box_alloc();
+ view_dispatcher_add_view(
+ brainfuck->view_dispatcher, brainfuckViewTextBox, text_box_get_view(brainfuck->text_box));
+ brainfuck->text_box_store = furi_string_alloc();
+
+ // Dev environment
+ brainfuck->BF_dev_env = bf_dev_env_alloc(brainfuck);
+ view_dispatcher_add_view(
+ brainfuck->view_dispatcher, brainfuckViewDev, bf_dev_env_get_view(brainfuck->BF_dev_env));
+
+ // File path
+ brainfuck->BF_file_path = furi_string_alloc();
+
+ return brainfuck;
+}
+
+void brainfuck_free(BFApp* brainfuck) {
+ furi_assert(brainfuck);
+
+ // Submenu
+ view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewMenu);
+ submenu_free(brainfuck->submenu);
+
+ // Popup
+ view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewPopup);
+ popup_free(brainfuck->popup);
+
+ // TextInput
+ view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewTextInput);
+ text_input_free(brainfuck->text_input);
+
+ // TextBox
+ view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewTextBox);
+ text_box_free(brainfuck->text_box);
+ furi_string_free(brainfuck->text_box_store);
+
+ //dev env
+ view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewDev);
+ bf_dev_env_free(brainfuck->BF_dev_env);
+
+ // View Dispatcher
+ view_dispatcher_free(brainfuck->view_dispatcher);
+
+ // Scene Manager
+ scene_manager_free(brainfuck->scene_manager);
+
+ // GUI
+ furi_record_close(RECORD_GUI);
+ brainfuck->gui = NULL;
+
+ // Notifications
+ furi_record_close(RECORD_NOTIFICATION);
+ brainfuck->notifications = NULL;
+
+ free(brainfuck);
+}
+
+void brainfuck_show_loading_popup(void* context, bool show) {
+ BFApp* brainfuck = context;
+ TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
+
+ if(show) {
+ // Raise timer priority so that animations can play
+ vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
+ view_dispatcher_switch_to_view(brainfuck->view_dispatcher, brainfuckViewLoading);
+ } else {
+ // Restore default timer priority
+ vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
+ }
+}
+
+int32_t brainfuck_app(void* p) {
+ UNUSED(p);
+ BFApp* brainfuck = brainfuck_alloc();
+ if(!brainfuck) {
+ return 0;
+ }
+
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ storage_simply_mkdir(storage, "/ext/brainfuck");
+
+ scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneStart);
+
+ view_dispatcher_run(brainfuck->view_dispatcher);
+
+ brainfuck_free(brainfuck);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/applications/plugins/brainfuck/brainfuck.h b/applications/plugins/brainfuck/brainfuck.h
new file mode 100644
index 000000000..2e58321a6
--- /dev/null
+++ b/applications/plugins/brainfuck/brainfuck.h
@@ -0,0 +1,3 @@
+#pragma once
+
+typedef struct BFApp BFApp;
\ No newline at end of file
diff --git a/applications/plugins/brainfuck/brainfuck_i.h b/applications/plugins/brainfuck/brainfuck_i.h
new file mode 100644
index 000000000..d3d27dcbd
--- /dev/null
+++ b/applications/plugins/brainfuck/brainfuck_i.h
@@ -0,0 +1,89 @@
+#pragma once
+
+typedef struct BFDevEnv BFDevEnv;
+typedef struct BFExecEnv BFExecEnv;
+typedef unsigned char byte;
+
+#include "brainfuck.h"
+#include "worker.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "scenes/brainfuck_scene.h"
+
+#include "views/bf_dev_env.h"
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#define BF_INST_BUFFER_SIZE 2048
+#define BF_OUTPUT_SIZE 512
+#define BF_STACK_INITIAL_SIZE 128
+#define BF_INPUT_BUFFER_SIZE 64
+#define BF_STACK_STEP_SIZE 32
+
+enum brainfuckCustomEvent {
+ // Reserve first 100 events for button types and indexes, starting from 0
+ brainfuckCustomEventReserved = 100,
+
+ brainfuckCustomEventViewExit,
+ brainfuckCustomEventWorkerExit,
+ brainfuckCustomEventByteInputDone,
+ brainfuckCustomEventTextInputDone,
+};
+
+typedef enum {
+ EventTypeTick,
+ EventTypeKey,
+} EventType;
+
+struct BFApp {
+ ViewDispatcher* view_dispatcher;
+ Gui* gui;
+ NotificationApp* notifications;
+ SceneManager* scene_manager;
+ Submenu* submenu;
+ Popup* popup;
+ TextInput* text_input;
+ TextBox* text_box;
+ FuriString* text_box_store;
+ FuriString* BF_file_path;
+ BFDevEnv* BF_dev_env;
+ int dataSize;
+ char dataBuffer[BF_INST_BUFFER_SIZE];
+ char inputBuffer[BF_INPUT_BUFFER_SIZE];
+};
+
+typedef enum {
+ brainfuckViewMenu,
+ brainfuckViewPopup,
+ brainfuckViewLoading,
+ brainfuckViewTextInput,
+ brainfuckViewTextBox,
+ brainfuckViewWidget,
+ brainfuckViewDev,
+ brainfuckViewExec,
+} brainfuckView;
diff --git a/applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png b/applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png
new file mode 100644
index 000000000..b9d5f87db
Binary files /dev/null and b/applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png differ
diff --git a/applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png b/applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png
new file mode 100644
index 000000000..c79cfb6c6
Binary files /dev/null and b/applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png differ
diff --git a/applications/plugins/brainfuck/icons/KeyBackspace_24x11.png b/applications/plugins/brainfuck/icons/KeyBackspace_24x11.png
new file mode 100644
index 000000000..00e66428d
Binary files /dev/null and b/applications/plugins/brainfuck/icons/KeyBackspace_24x11.png differ
diff --git a/applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png b/applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png
new file mode 100644
index 000000000..4c04a0856
Binary files /dev/null and b/applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png differ
diff --git a/applications/plugins/brainfuck/icons/KeyInput_30x11.png b/applications/plugins/brainfuck/icons/KeyInput_30x11.png
new file mode 100644
index 000000000..d23e24aaf
Binary files /dev/null and b/applications/plugins/brainfuck/icons/KeyInput_30x11.png differ
diff --git a/applications/plugins/brainfuck/icons/KeyRunSelected_24x11.png b/applications/plugins/brainfuck/icons/KeyRunSelected_24x11.png
new file mode 100644
index 000000000..3ff5ac5e6
Binary files /dev/null and b/applications/plugins/brainfuck/icons/KeyRunSelected_24x11.png differ
diff --git a/applications/plugins/brainfuck/icons/KeyRun_24x11.png b/applications/plugins/brainfuck/icons/KeyRun_24x11.png
new file mode 100644
index 000000000..cce46c972
Binary files /dev/null and b/applications/plugins/brainfuck/icons/KeyRun_24x11.png differ
diff --git a/applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png b/applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png
new file mode 100644
index 000000000..eeb3569d3
Binary files /dev/null and b/applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png differ
diff --git a/applications/plugins/brainfuck/icons/KeySave_24x11.png b/applications/plugins/brainfuck/icons/KeySave_24x11.png
new file mode 100644
index 000000000..e7dba987a
Binary files /dev/null and b/applications/plugins/brainfuck/icons/KeySave_24x11.png differ
diff --git a/applications/plugins/brainfuck/icons/bfico.png b/applications/plugins/brainfuck/icons/bfico.png
new file mode 100644
index 000000000..b25368fb5
Binary files /dev/null and b/applications/plugins/brainfuck/icons/bfico.png differ
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene.c b/applications/plugins/brainfuck/scenes/brainfuck_scene.c
new file mode 100644
index 000000000..90707c926
--- /dev/null
+++ b/applications/plugins/brainfuck/scenes/brainfuck_scene.c
@@ -0,0 +1,30 @@
+#include "brainfuck_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const brainfuck_on_enter_handlers[])(void*) = {
+#include "brainfuck_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 brainfuck_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "brainfuck_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 brainfuck_on_exit_handlers[])(void* context) = {
+#include "brainfuck_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers brainfuck_scene_handlers = {
+ .on_enter_handlers = brainfuck_on_enter_handlers,
+ .on_event_handlers = brainfuck_on_event_handlers,
+ .on_exit_handlers = brainfuck_on_exit_handlers,
+ .scene_num = brainfuckSceneNum,
+};
diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene.h b/applications/plugins/brainfuck/scenes/brainfuck_scene.h
similarity index 63%
rename from applications/settings/xtreme_settings/scenes/xtreme_settings_scene.h
rename to applications/plugins/brainfuck/scenes/brainfuck_scene.h
index 70abf4f77..f7131a56c 100644
--- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene.h
+++ b/applications/plugins/brainfuck/scenes/brainfuck_scene.h
@@ -3,27 +3,27 @@
#include
// Generate scene id and total number
-#define ADD_SCENE(prefix, name, id) XtremeSettingsAppScene##id,
+#define ADD_SCENE(prefix, name, id) brainfuckScene##id,
typedef enum {
-#include "xtreme_settings_scene_config.h"
- XtremeSettingsAppSceneNum,
-} XtremeSettingsAppScene;
+#include "brainfuck_scene_config.h"
+ brainfuckSceneNum,
+} brainfuckScene;
#undef ADD_SCENE
-extern const SceneManagerHandlers xtreme_settings_scene_handlers;
+extern const SceneManagerHandlers brainfuck_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
-#include "xtreme_settings_scene_config.h"
+#include "brainfuck_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 "xtreme_settings_scene_config.h"
+#include "brainfuck_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 "xtreme_settings_scene_config.h"
+#include "brainfuck_scene_config.h"
#undef ADD_SCENE
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_config.h b/applications/plugins/brainfuck/scenes/brainfuck_scene_config.h
new file mode 100644
index 000000000..0efc41641
--- /dev/null
+++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_config.h
@@ -0,0 +1,6 @@
+ADD_SCENE(brainfuck, start, Start)
+ADD_SCENE(brainfuck, file_select, FileSelect)
+ADD_SCENE(brainfuck, file_create, FileCreate)
+ADD_SCENE(brainfuck, dev_env, DevEnv)
+ADD_SCENE(brainfuck, exec_env, ExecEnv)
+ADD_SCENE(brainfuck, set_input, SetInput)
\ No newline at end of file
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c
new file mode 100644
index 000000000..475e9e573
--- /dev/null
+++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c
@@ -0,0 +1,16 @@
+#include "../brainfuck_i.h"
+
+void brainfuck_scene_dev_env_on_enter(void* context) {
+ BFApp* app = context;
+ view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewDev);
+}
+
+bool brainfuck_scene_dev_env_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
+
+void brainfuck_scene_dev_env_on_exit(void* context) {
+ UNUSED(context);
+}
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c
new file mode 100644
index 000000000..d344f7271
--- /dev/null
+++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c
@@ -0,0 +1,16 @@
+#include "../brainfuck_i.h"
+
+void brainfuck_scene_exec_env_on_enter(void* context) {
+ BFApp* app = context;
+ view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextBox);
+}
+
+bool brainfuck_scene_exec_env_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
+
+void brainfuck_scene_exec_env_on_exit(void* context) {
+ UNUSED(context);
+}
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c
new file mode 100644
index 000000000..9f8885977
--- /dev/null
+++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c
@@ -0,0 +1,50 @@
+#include "../brainfuck_i.h"
+
+void file_name_text_input_callback(void* context) {
+ BFApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, brainfuckCustomEventTextInputDone);
+}
+
+char tmpName[64] = {};
+byte empty[1] = {0x00};
+void brainfuck_scene_file_create_on_enter(void* context) {
+ BFApp* app = context;
+ TextInput* text_input = app->text_input;
+
+ text_input_set_header_text(text_input, "New script name");
+ text_input_set_result_callback(
+ text_input, file_name_text_input_callback, app, tmpName, 64, true);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextInput);
+}
+
+bool brainfuck_scene_file_create_on_event(void* context, SceneManagerEvent event) {
+ BFApp* app = context;
+ UNUSED(app);
+
+ bool consumed = false;
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == brainfuckCustomEventTextInputDone) {
+ furi_string_cat_printf(app->BF_file_path, "/ext/brainfuck/%s.b", tmpName);
+
+ //remove old file
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ storage_simply_remove(storage, furi_string_get_cstr(app->BF_file_path));
+
+ //save new file
+ Stream* stream = buffered_file_stream_alloc(storage);
+ buffered_file_stream_open(
+ stream, furi_string_get_cstr(app->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS);
+ stream_write(stream, (const uint8_t*)empty, 1);
+ buffered_file_stream_close(stream);
+
+ //scene_manager_next_scene(app->scene_manager, brainfuckSceneFileSelect);
+ scene_manager_next_scene(app->scene_manager, brainfuckSceneDevEnv);
+ }
+ }
+ return consumed;
+}
+
+void brainfuck_scene_file_create_on_exit(void* context) {
+ UNUSED(context);
+}
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c
new file mode 100644
index 000000000..33c06ee81
--- /dev/null
+++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c
@@ -0,0 +1,34 @@
+#include "../brainfuck_i.h"
+
+void brainfuck_scene_file_select_on_enter(void* context) {
+ BFApp* app = context;
+
+ DialogsApp* dialogs = furi_record_open("dialogs");
+ FuriString* path;
+ path = furi_string_alloc();
+ furi_string_set(path, "/ext/brainfuck");
+
+ DialogsFileBrowserOptions browser_options;
+ dialog_file_browser_set_basic_options(&browser_options, ".b", &I_bfico);
+ browser_options.base_path = "/ext/brainfuck";
+ browser_options.hide_ext = false;
+
+ bool selected = dialog_file_browser_show(dialogs, path, path, &browser_options);
+
+ if(selected) {
+ furi_string_set(app->BF_file_path, path);
+ scene_manager_next_scene(app->scene_manager, brainfuckSceneDevEnv);
+ } else {
+ scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneStart);
+ }
+}
+
+bool brainfuck_scene_file_select_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
+
+void brainfuck_scene_file_select_on_exit(void* context) {
+ UNUSED(context);
+}
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c
new file mode 100644
index 000000000..efb9237cb
--- /dev/null
+++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c
@@ -0,0 +1,35 @@
+#include "../brainfuck_i.h"
+
+void set_input_text_input_callback(void* context) {
+ BFApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, brainfuckCustomEventTextInputDone);
+}
+
+void brainfuck_scene_set_input_on_enter(void* context) {
+ BFApp* app = context;
+ TextInput* text_input = app->text_input;
+
+ text_input_set_header_text(text_input, "Edit input buffer");
+ text_input_set_result_callback(
+ text_input, set_input_text_input_callback, app, app->inputBuffer, 64, true);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextInput);
+}
+
+bool brainfuck_scene_set_input_on_event(void* context, SceneManagerEvent event) {
+ BFApp* app = context;
+
+ bool consumed = false;
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == brainfuckCustomEventTextInputDone) {
+ scene_manager_search_and_switch_to_previous_scene(
+ app->scene_manager, brainfuckSceneDevEnv);
+ }
+ }
+ return consumed;
+}
+
+void brainfuck_scene_set_input_on_exit(void* context) {
+ BFApp* app = context;
+ scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneDevEnv);
+}
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_start.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_start.c
new file mode 100644
index 000000000..8eaaf751a
--- /dev/null
+++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_start.c
@@ -0,0 +1,55 @@
+#include "../brainfuck_i.h"
+enum SubmenuIndex {
+ SubmenuIndexNew,
+ SubmenuIndexOpen,
+ SubmenuIndexAbout,
+};
+
+void brainfuck_scene_start_submenu_callback(void* context, uint32_t index) {
+ BFApp* brainfuck = context;
+ view_dispatcher_send_custom_event(brainfuck->view_dispatcher, index);
+}
+void brainfuck_scene_start_on_enter(void* context) {
+ BFApp* brainfuck = context;
+
+ Submenu* submenu = brainfuck->submenu;
+ submenu_add_item(
+ submenu, "New", SubmenuIndexNew, brainfuck_scene_start_submenu_callback, brainfuck);
+ submenu_add_item(
+ submenu, "Open", SubmenuIndexOpen, brainfuck_scene_start_submenu_callback, brainfuck);
+ submenu_add_item(
+ submenu, "About", SubmenuIndexAbout, brainfuck_scene_start_submenu_callback, brainfuck);
+
+ submenu_set_selected_item(
+ submenu, scene_manager_get_scene_state(brainfuck->scene_manager, brainfuckSceneStart));
+ view_dispatcher_switch_to_view(brainfuck->view_dispatcher, brainfuckViewMenu);
+}
+
+bool brainfuck_scene_start_on_event(void* context, SceneManagerEvent event) {
+ BFApp* brainfuck = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == SubmenuIndexNew) {
+ scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneFileCreate);
+ consumed = true;
+ } else if(event.event == SubmenuIndexOpen) {
+ scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneFileSelect);
+ consumed = true;
+ } else if(event.event == SubmenuIndexAbout) {
+ text_box_set_text(
+ brainfuck->text_box,
+ "FlipperBrainfuck\n\nAn F0 brainfuck intepretor\nBy github.com/Nymda");
+ scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneExecEnv);
+ consumed = true;
+ }
+ scene_manager_set_scene_state(brainfuck->scene_manager, brainfuckSceneStart, event.event);
+ }
+
+ return consumed;
+}
+
+void brainfuck_scene_start_on_exit(void* context) {
+ BFApp* brainfuck = context;
+ submenu_reset(brainfuck->submenu);
+}
diff --git a/applications/plugins/brainfuck/views/bf_dev_env.c b/applications/plugins/brainfuck/views/bf_dev_env.c
new file mode 100644
index 000000000..c5f194500
--- /dev/null
+++ b/applications/plugins/brainfuck/views/bf_dev_env.c
@@ -0,0 +1,419 @@
+#include "bf_dev_env.h"
+#include
+
+typedef struct BFDevEnv {
+ View* view;
+ DevEnvOkCallback callback;
+ void* context;
+ BFApp* appDev;
+} BFDevEnv;
+
+typedef struct {
+ uint32_t row;
+ uint32_t col;
+} BFDevEnvModel;
+
+typedef struct {
+ int up;
+ int down;
+ int left;
+ int right;
+} bMapping;
+
+static bool bf_dev_process_up(BFDevEnv* devEnv);
+static bool bf_dev_process_down(BFDevEnv* devEnv);
+static bool bf_dev_process_left(BFDevEnv* devEnv);
+static bool bf_dev_process_right(BFDevEnv* devEnv);
+static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event);
+
+BFApp* appDev;
+FuriThread* workerThread;
+
+char bfChars[9] = {'<', '>', '[', ']', '+', '-', '.', ',', 0x00};
+
+int selectedButton = 0;
+int saveNotifyCountdown = 0;
+int execCountdown = 0;
+
+char dspLine0[25] = {};
+char dspLine1[25] = {};
+char dspLine2[25] = {};
+
+static bMapping buttonMappings[12] = {
+ {8, 8, 7, 1}, //0
+ {8, 8, 0, 2}, //1
+ {9, 9, 1, 3}, //2
+ {9, 9, 2, 4}, //3
+ {10, 10, 3, 5}, //4
+ {10, 10, 4, 6}, //5
+ {11, 11, 5, 7}, //6
+ {11, 11, 6, 0}, //7
+
+ {0, 0, 11, 9}, //8
+ {3, 3, 8, 10}, //9
+ {5, 5, 9, 11}, //10
+ {6, 6, 10, 8} //11
+};
+
+#define BT_X 14
+#define BT_Y 14
+static void bf_dev_draw_button(Canvas* canvas, int x, int y, bool selected, const char* lbl) {
+ UNUSED(lbl);
+
+ if(selected) {
+ canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3);
+ canvas_invert_color(canvas);
+ canvas_set_font(canvas, FontBatteryPercent);
+ canvas_draw_str_aligned(
+ canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl);
+ canvas_invert_color(canvas);
+ } else {
+ canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3);
+ canvas_invert_color(canvas);
+ canvas_draw_rbox(canvas, x + 2, y - 1, BT_X - 2, BT_Y - 1, 3);
+ canvas_invert_color(canvas);
+ canvas_draw_rframe(canvas, x, y, BT_X, BT_Y, 3);
+ canvas_set_font(canvas, FontBatteryPercent);
+ canvas_draw_str_aligned(
+ canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl);
+ }
+}
+
+void bf_save_changes() {
+ //remove old file
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ storage_simply_remove(storage, furi_string_get_cstr(appDev->BF_file_path));
+
+ //save new file
+ Stream* stream = buffered_file_stream_alloc(storage);
+ buffered_file_stream_open(
+ stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS);
+ stream_write(stream, (const uint8_t*)appDev->dataBuffer, appDev->dataSize);
+ buffered_file_stream_close(stream);
+}
+
+static void bf_dev_draw_callback(Canvas* canvas, void* _model) {
+ UNUSED(_model);
+
+ if(saveNotifyCountdown > 0) {
+ canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "SAVED");
+ saveNotifyCountdown--;
+ return;
+ }
+
+ bf_dev_draw_button(canvas, 1, 36, (selectedButton == 0), "+"); //T 0
+ bf_dev_draw_button(canvas, 17, 36, (selectedButton == 1), "-"); //T 1
+ bf_dev_draw_button(canvas, 33, 36, (selectedButton == 2), "<"); //T 2
+ bf_dev_draw_button(canvas, 49, 36, (selectedButton == 3), ">"); //T 3
+ bf_dev_draw_button(canvas, 65, 36, (selectedButton == 4), "["); //B 0
+ bf_dev_draw_button(canvas, 81, 36, (selectedButton == 5), "]"); //B 1
+ bf_dev_draw_button(canvas, 97, 36, (selectedButton == 6), "."); //B 2
+ bf_dev_draw_button(canvas, 113, 36, (selectedButton == 7), ","); //B 3
+
+ //backspace, input, run, save
+ canvas_draw_icon(
+ canvas,
+ 1,
+ 52,
+ (selectedButton == 8) ? &I_KeyBackspaceSelected_24x11 : &I_KeyBackspace_24x11);
+ canvas_draw_icon(
+ canvas, 45, 52, (selectedButton == 9) ? &I_KeyInputSelected_30x11 : &I_KeyInput_30x11);
+ canvas_draw_icon(
+ canvas, 77, 52, (selectedButton == 10) ? &I_KeyRunSelected_24x11 : &I_KeyRun_24x11);
+ canvas_draw_icon(
+ canvas, 103, 52, (selectedButton == 11) ? &I_KeySaveSelected_24x11 : &I_KeySave_24x11);
+
+ if(saveNotifyCountdown > 0) {
+ canvas_draw_icon(canvas, 98, 54, &I_ButtonRightSmall_3x5);
+ saveNotifyCountdown--;
+ }
+
+ //textbox
+ //grossly overcomplicated. not fixing it.
+ canvas_draw_rframe(canvas, 1, 1, 126, 33, 2);
+ canvas_set_font(canvas, FontBatteryPercent);
+
+ int dbOffset = 0;
+ if(appDev->dataSize > 72) {
+ dbOffset = (appDev->dataSize - 72);
+ }
+
+ memset(dspLine0, 0x00, 25);
+ memset(dspLine1, 0x00, 25);
+ memset(dspLine2, 0x00, 25);
+
+ int tpM = 0;
+ int tp0 = 0;
+ int tp1 = 0;
+ int tp2 = 0;
+
+ for(int p = dbOffset; p < appDev->dataSize; p++) {
+ if(tpM < 24 * 1) {
+ dspLine0[tp0] = appDev->dataBuffer[p];
+ tp0++;
+ } else if(tpM < 24 * 2) {
+ dspLine1[tp1] = appDev->dataBuffer[p];
+ tp1++;
+ } else if(tpM < 24 * 3) {
+ dspLine2[tp2] = appDev->dataBuffer[p];
+ tp2++;
+ }
+ tpM++;
+ }
+
+ canvas_draw_str_aligned(canvas, 3, 8, AlignLeft, AlignCenter, dspLine0);
+ canvas_draw_str_aligned(canvas, 3, 17, AlignLeft, AlignCenter, dspLine1);
+ canvas_draw_str_aligned(canvas, 3, 26, AlignLeft, AlignCenter, dspLine2);
+}
+
+static bool bf_dev_input_callback(InputEvent* event, void* context) {
+ furi_assert(context);
+ BFDevEnv* devEnv = context;
+ bool consumed = false;
+
+ if(event->type == InputTypeShort) {
+ if(event->key == InputKeyRight) {
+ consumed = bf_dev_process_right(devEnv);
+ } else if(event->key == InputKeyLeft) {
+ consumed = bf_dev_process_left(devEnv);
+ } else if(event->key == InputKeyUp) {
+ consumed = bf_dev_process_up(devEnv);
+ } else if(event->key == InputKeyDown) {
+ consumed = bf_dev_process_down(devEnv);
+ }
+ } else if(event->key == InputKeyOk) {
+ consumed = bf_dev_process_ok(devEnv, event);
+ }
+
+ return consumed;
+}
+
+static bool bf_dev_process_up(BFDevEnv* devEnv) {
+ UNUSED(devEnv);
+ selectedButton = buttonMappings[selectedButton].up;
+ return true;
+}
+
+static bool bf_dev_process_down(BFDevEnv* devEnv) {
+ UNUSED(devEnv);
+ selectedButton = buttonMappings[selectedButton].down;
+ return true;
+}
+
+static bool bf_dev_process_left(BFDevEnv* devEnv) {
+ UNUSED(devEnv);
+ selectedButton = buttonMappings[selectedButton].left;
+ return true;
+}
+
+static bool bf_dev_process_right(BFDevEnv* devEnv) {
+ UNUSED(devEnv);
+ selectedButton = buttonMappings[selectedButton].right;
+ return true;
+}
+
+static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event) {
+ UNUSED(devEnv);
+ UNUSED(event);
+
+ if(event->type != InputTypePress) {
+ return false;
+ }
+
+ switch(selectedButton) {
+ case 0: {
+ if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
+ appDev->dataBuffer[appDev->dataSize] = '+';
+ appDev->dataSize++;
+ }
+ break;
+ }
+
+ case 1: {
+ if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
+ appDev->dataBuffer[appDev->dataSize] = '-';
+ appDev->dataSize++;
+ }
+ break;
+ }
+
+ case 2: {
+ if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
+ appDev->dataBuffer[appDev->dataSize] = '<';
+ appDev->dataSize++;
+ }
+ break;
+ }
+
+ case 3: {
+ if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
+ appDev->dataBuffer[appDev->dataSize] = '>';
+ appDev->dataSize++;
+ }
+ break;
+ }
+
+ case 4: {
+ if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
+ appDev->dataBuffer[appDev->dataSize] = '[';
+ appDev->dataSize++;
+ }
+ break;
+ }
+
+ case 5: {
+ if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
+ appDev->dataBuffer[appDev->dataSize] = ']';
+ appDev->dataSize++;
+ }
+ break;
+ }
+
+ case 6: {
+ if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
+ appDev->dataBuffer[appDev->dataSize] = '.';
+ appDev->dataSize++;
+ }
+ break;
+ }
+
+ case 7: {
+ if(appDev->dataSize < BF_INST_BUFFER_SIZE) {
+ appDev->dataBuffer[appDev->dataSize] = ',';
+ appDev->dataSize++;
+ }
+ break;
+ }
+
+ case 8: {
+ if(appDev->dataSize > 0) {
+ appDev->dataSize--;
+ appDev->dataBuffer[appDev->dataSize] = (uint32_t)0x00;
+ }
+ break;
+ }
+
+ case 9: {
+ scene_manager_next_scene(appDev->scene_manager, brainfuckSceneSetInput);
+ break;
+ }
+
+ case 10: {
+ if(getStatus() != 0) {
+ killThread();
+ furi_thread_join(workerThread);
+ }
+
+ bf_save_changes();
+
+ initWorker(appDev);
+ text_box_set_focus(appDev->text_box, TextBoxFocusEnd);
+ text_box_set_text(appDev->text_box, workerGetOutput());
+
+ workerThread = furi_thread_alloc_ex("Worker", 2048, (void*)beginWorker, NULL);
+ furi_thread_start(workerThread);
+
+ scene_manager_next_scene(appDev->scene_manager, brainfuckSceneExecEnv);
+ break;
+ }
+
+ case 11: {
+ bf_save_changes();
+ saveNotifyCountdown = 3;
+ break;
+ }
+ }
+
+ bool consumed = false;
+ return consumed;
+}
+
+static void bf_dev_enter_callback(void* context) {
+ furi_assert(context);
+ BFDevEnv* devEnv = context;
+
+ with_view_model(
+ devEnv->view,
+ BFDevEnvModel * model,
+ {
+ model->col = 0;
+ model->row = 0;
+ },
+ true);
+
+ appDev = devEnv->appDev;
+ selectedButton = 0;
+
+ //exit the running thread if required
+ if(getStatus() != 0) {
+ killThread();
+ furi_thread_join(workerThread);
+ }
+
+ //clear the bf instruction buffer
+ memset(appDev->dataBuffer, 0x00, BF_INST_BUFFER_SIZE * sizeof(char));
+
+ //open the file
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ Stream* stream = buffered_file_stream_alloc(storage);
+ buffered_file_stream_open(
+ stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_READ, FSOM_OPEN_EXISTING);
+
+ //read into the buffer
+ appDev->dataSize = stream_size(stream);
+ stream_read(stream, (uint8_t*)appDev->dataBuffer, appDev->dataSize);
+ buffered_file_stream_close(stream);
+
+ //replaces any invalid characters with an underscore. strips out newlines, comments, etc
+ for(int i = 0; i < appDev->dataSize; i++) {
+ if(!strchr(bfChars, appDev->dataBuffer[i])) {
+ appDev->dataBuffer[i] = '_';
+ }
+ }
+
+ //find the end of the file to begin editing
+ int tptr = 0;
+ while(appDev->dataBuffer[tptr] != 0x00) {
+ tptr++;
+ }
+ appDev->dataSize = tptr;
+}
+
+BFDevEnv* bf_dev_env_alloc(BFApp* appDev) {
+ BFDevEnv* devEnv = malloc(sizeof(BFDevEnv));
+
+ devEnv->view = view_alloc();
+ devEnv->appDev = appDev;
+ view_allocate_model(devEnv->view, ViewModelTypeLocking, sizeof(BFDevEnvModel));
+
+ with_view_model(
+ devEnv->view,
+ BFDevEnvModel * model,
+ {
+ model->col = 0;
+ model->row = 0;
+ },
+ true);
+
+ view_set_context(devEnv->view, devEnv);
+ view_set_draw_callback(devEnv->view, bf_dev_draw_callback);
+ view_set_input_callback(devEnv->view, bf_dev_input_callback);
+ view_set_enter_callback(devEnv->view, bf_dev_enter_callback);
+ return devEnv;
+}
+
+void bf_dev_env_free(BFDevEnv* devEnv) {
+ if(getStatus() != 0) {
+ killThread();
+ furi_thread_join(workerThread);
+ }
+
+ furi_assert(devEnv);
+ view_free(devEnv->view);
+ free(devEnv);
+}
+
+View* bf_dev_env_get_view(BFDevEnv* devEnv) {
+ furi_assert(devEnv);
+ return devEnv->view;
+}
diff --git a/applications/plugins/brainfuck/views/bf_dev_env.h b/applications/plugins/brainfuck/views/bf_dev_env.h
new file mode 100644
index 000000000..31059544b
--- /dev/null
+++ b/applications/plugins/brainfuck/views/bf_dev_env.h
@@ -0,0 +1,15 @@
+#pragma once
+#include "../brainfuck_i.h"
+#include
+
+typedef void (*DevEnvOkCallback)(InputType type, void* context);
+
+BFDevEnv* bf_dev_env_alloc(BFApp* application);
+
+void bf_dev_set_file_path(FuriString* path);
+
+void bf_dev_env_free(BFDevEnv* devEnv);
+
+View* bf_dev_env_get_view(BFDevEnv* devEnv);
+
+void bf_dev_env_set_ok(BFDevEnv* devEnv, DevEnvOkCallback callback, void* context);
diff --git a/applications/plugins/brainfuck/worker.c b/applications/plugins/brainfuck/worker.c
new file mode 100644
index 000000000..1b05ac3fd
--- /dev/null
+++ b/applications/plugins/brainfuck/worker.c
@@ -0,0 +1,276 @@
+#include "worker.h"
+
+bool killswitch = false;
+
+int status = 0; //0: idle, 1: running, 2: failure
+
+char* inst = 0;
+int instCount = 0;
+int instPtr = 0;
+int runOpCount = 0;
+
+char* wOutput = 0;
+int wOutputPtr = 0;
+
+char* wInput = 0;
+int wInputPtr = 0;
+
+uint8_t* bfStack = 0;
+int stackPtr = 0;
+int stackSize = BF_STACK_INITIAL_SIZE;
+int stackSizeReal = 0;
+
+BFApp* wrkrApp = 0;
+
+void killThread() {
+ killswitch = true;
+}
+
+bool validateInstPtr() {
+ if(instPtr > instCount || instPtr < 0) {
+ return false;
+ }
+ return true;
+}
+
+bool validateStackPtr() {
+ if(stackPtr > stackSize || stackPtr < 0) {
+ return false;
+ }
+ return true;
+}
+
+char* workerGetOutput() {
+ return wOutput;
+}
+
+int getStackSize() {
+ return stackSizeReal;
+}
+
+int getOpCount() {
+ return runOpCount;
+}
+
+int getStatus() {
+ return status;
+}
+
+void initWorker(BFApp* app) {
+ wrkrApp = app;
+
+ //rebuild output
+ if(wOutput) {
+ free(wOutput);
+ }
+ wOutput = (char*)malloc(BF_OUTPUT_SIZE);
+ wOutputPtr = 0;
+
+ //rebuild stack
+ if(bfStack) {
+ free(bfStack);
+ }
+ bfStack = (uint8_t*)malloc(BF_STACK_INITIAL_SIZE);
+ memset(bfStack, 0x00, BF_STACK_INITIAL_SIZE);
+ stackSize = BF_STACK_INITIAL_SIZE;
+ stackSizeReal = 0;
+ stackPtr = 0;
+
+ //set instructions
+ inst = wrkrApp->dataBuffer;
+ instCount = wrkrApp->dataSize;
+ instPtr = 0;
+ runOpCount = 0;
+
+ //set input
+ wInput = wrkrApp->inputBuffer;
+ wInputPtr = 0;
+
+ //set status
+ status = 0;
+}
+
+void rShift() {
+ runOpCount++;
+ stackPtr++;
+ if(!validateStackPtr()) {
+ status = 2;
+ return;
+ }
+
+ while(stackPtr > stackSize) {
+ stackSize += BF_STACK_STEP_SIZE;
+ void* tmp = realloc(bfStack, stackSize);
+
+ if(!tmp) {
+ status = 2;
+ return;
+ }
+
+ memset((tmp + stackSize) - BF_STACK_STEP_SIZE, 0x00, BF_STACK_STEP_SIZE);
+ bfStack = (uint8_t*)tmp;
+ };
+ if(stackPtr > stackSizeReal) {
+ stackSizeReal = stackPtr;
+ }
+}
+
+void lShift() {
+ runOpCount++;
+ stackPtr--;
+ if(!validateStackPtr()) {
+ status = 2;
+ return;
+ }
+}
+
+void inc() {
+ runOpCount++;
+ if(!validateStackPtr()) {
+ status = 2;
+ return;
+ }
+ bfStack[stackPtr]++;
+}
+
+void dec() {
+ runOpCount++;
+ if(!validateStackPtr()) {
+ status = 2;
+ return;
+ }
+ bfStack[stackPtr]--;
+}
+
+void print() {
+ runOpCount++;
+ wOutput[wOutputPtr] = bfStack[stackPtr];
+ wOutputPtr++;
+ if(wOutputPtr > (BF_OUTPUT_SIZE - 1)) {
+ wOutputPtr = 0;
+ }
+}
+
+void input() {
+ runOpCount++;
+
+ bfStack[stackPtr] = (uint8_t)wInput[wInputPtr];
+ if(wInput[wInputPtr] == 0x00 || wInputPtr >= 64) {
+ wInputPtr = 0;
+ } else {
+ wInputPtr++;
+ }
+}
+
+void loop() {
+ runOpCount++;
+ if(bfStack[stackPtr] == 0) {
+ int loopCount = 1;
+ while(loopCount > 0) {
+ instPtr++;
+ if(!validateInstPtr()) {
+ status = 2;
+ return;
+ }
+ if(inst[instPtr] == '[') {
+ loopCount++;
+ } else if(inst[instPtr] == ']') {
+ loopCount--;
+ }
+ }
+ }
+}
+
+void endLoop() {
+ runOpCount++;
+ if(bfStack[stackPtr] != 0) {
+ int loopCount = 1;
+ while(loopCount > 0) {
+ instPtr--;
+ if(!validateInstPtr()) {
+ status = 2;
+ return;
+ }
+ if(inst[instPtr] == ']') {
+ loopCount++;
+ } else if(inst[instPtr] == '[') {
+ loopCount--;
+ }
+ }
+ }
+}
+
+static const NotificationSequence led_on = {
+ &message_blue_255,
+ &message_do_not_reset,
+ NULL,
+};
+
+static const NotificationSequence led_off = {
+ &message_green_0,
+ NULL,
+};
+
+void beginWorker() {
+ status = 1;
+ while(inst[instPtr] != 0x00) {
+ if(runOpCount % 500 == 0) {
+ text_box_set_text(wrkrApp->text_box, workerGetOutput());
+ notification_message(wrkrApp->notifications, &led_on);
+ }
+
+ if(status == 2) {
+ status = 0;
+ break;
+ }
+ if(killswitch) {
+ status = 0;
+ killswitch = false;
+ break;
+ }
+ switch(inst[instPtr]) {
+ case '>':
+ rShift();
+ break;
+ case '<':
+ lShift();
+ break;
+
+ case '+':
+ inc();
+ break;
+
+ case '-':
+ dec();
+ break;
+
+ case '.':
+ print();
+ break;
+
+ case ',':
+ input();
+ break;
+
+ case '[':
+ loop();
+ break;
+
+ case ']':
+ endLoop();
+ break;
+
+ default:
+ break;
+ }
+ instPtr++;
+ if(!validateInstPtr()) {
+ status = 0;
+ break;
+ }
+ }
+
+ notification_message(wrkrApp->notifications, &led_off);
+ text_box_set_text(wrkrApp->text_box, workerGetOutput());
+ status = 0;
+}
\ No newline at end of file
diff --git a/applications/plugins/brainfuck/worker.h b/applications/plugins/brainfuck/worker.h
new file mode 100644
index 000000000..b12e364c3
--- /dev/null
+++ b/applications/plugins/brainfuck/worker.h
@@ -0,0 +1,9 @@
+#include "brainfuck_i.h"
+
+void initWorker(BFApp* application);
+char* workerGetOutput();
+int getStackSize();
+int getOpCount();
+int getStatus();
+void beginWorker();
+void killThread();
\ No newline at end of file
diff --git a/applications/plugins/cli_bridge/application.fam b/applications/plugins/cli_bridge/application.fam
index 60cd9648d..f5f294d9b 100644
--- a/applications/plugins/cli_bridge/application.fam
+++ b/applications/plugins/cli_bridge/application.fam
@@ -3,7 +3,7 @@ App(
name="CLI (subghz chat)",
apptype=FlipperAppType.EXTERNAL,
entry_point="cligui_main",
- requires=["gui","cli"],
+ requires=["gui", "cli"],
stack_size=8 * 1024,
fap_icon="cligui.png",
fap_category="Tools",
diff --git a/applications/plugins/clock_app/application.fam b/applications/plugins/clock_app/application.fam
index 644fbb65f..a6a2eff3e 100644
--- a/applications/plugins/clock_app/application.fam
+++ b/applications/plugins/clock_app/application.fam
@@ -7,4 +7,4 @@ App(
stack_size=2 * 1024,
fap_icon="clock.png",
fap_category="Tools",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/counter/application.fam b/applications/plugins/counter/application.fam
index bb2e58df1..5d164f20c 100644
--- a/applications/plugins/counter/application.fam
+++ b/applications/plugins/counter/application.fam
@@ -9,4 +9,4 @@ App(
fap_category="Misc",
fap_icon="icons/counter_icon.png",
fap_icon_assets="icons",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/dap_link/dap_link.c b/applications/plugins/dap_link/dap_link.c
index c46c68788..dd684810a 100644
--- a/applications/plugins/dap_link/dap_link.c
+++ b/applications/plugins/dap_link/dap_link.c
@@ -486,8 +486,7 @@ int32_t dap_link_app(void* p) {
if(furi_hal_usb_is_locked()) {
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessage* message = dialog_message_alloc();
- dialog_message_set_header(
- message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop);
+ dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop);
dialog_message_set_text(
message,
"Disconnect from\nPC or phone to\nuse this function.",
diff --git a/applications/plugins/dice/dice.c b/applications/plugins/dice/dice.c
index 61aa7b4f5..dc748b68f 100644
--- a/applications/plugins/dice/dice.c
+++ b/applications/plugins/dice/dice.c
@@ -467,7 +467,6 @@ int32_t dice_app(void* p) {
return 255;
}
-
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, dice_render_callback, plugin_state);
view_port_input_callback_set(view_port, dice_input_callback, plugin_state->event_queue);
diff --git a/applications/plugins/dolphinbackup/application.fam b/applications/plugins/dolphinbackup/application.fam
index 024c1b197..166f9a22b 100644
--- a/applications/plugins/dolphinbackup/application.fam
+++ b/applications/plugins/dolphinbackup/application.fam
@@ -9,4 +9,4 @@ App(
order=85,
fap_icon="bckupIcon.png",
fap_category="Tools",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/dolphinbackup/storage_DolphinBackup.c b/applications/plugins/dolphinbackup/storage_DolphinBackup.c
index 02baddc0f..7e7bd8f36 100644
--- a/applications/plugins/dolphinbackup/storage_DolphinBackup.c
+++ b/applications/plugins/dolphinbackup/storage_DolphinBackup.c
@@ -16,7 +16,7 @@ static const char* app_dirsDolphinBackup[] = {
"nfc",
"infrared",
"ibutton",
- "badusb",
+ "badkb",
".bt.settings",
".desktop.settings",
".dolphin.state",
diff --git a/applications/plugins/dolphinrestorer/application.fam b/applications/plugins/dolphinrestorer/application.fam
index 46699404a..afd263c45 100644
--- a/applications/plugins/dolphinrestorer/application.fam
+++ b/applications/plugins/dolphinrestorer/application.fam
@@ -4,9 +4,9 @@ App(
apptype=FlipperAppType.EXTERNAL,
entry_point="drestorer_app",
cdefines=["APP_DRESTORER"],
- requires=["gui","storage"],
- stack_size= 2 * 1024,
+ requires=["gui", "storage"],
+ stack_size=2 * 1024,
order=90,
fap_icon="restoreIcon.png",
fap_category="Tools",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/flashlight/application.fam b/applications/plugins/flashlight/application.fam
index 368e14833..6d70a036f 100644
--- a/applications/plugins/flashlight/application.fam
+++ b/applications/plugins/flashlight/application.fam
@@ -11,4 +11,4 @@ App(
order=20,
fap_icon="flash10px.png",
fap_category="GPIO",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/flipper_i2ctools/application.fam b/applications/plugins/flipper_i2ctools/application.fam
index d8d10dfce..f6522a86e 100644
--- a/applications/plugins/flipper_i2ctools/application.fam
+++ b/applications/plugins/flipper_i2ctools/application.fam
@@ -10,4 +10,4 @@ App(
fap_icon="i2ctools.png",
fap_category="GPIO",
fap_icon_assets="images",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/game_2048/application.fam b/applications/plugins/game_2048/application.fam
index c8940c881..f2456668e 100644
--- a/applications/plugins/game_2048/application.fam
+++ b/applications/plugins/game_2048/application.fam
@@ -10,6 +10,6 @@ App(
],
stack_size=1 * 1024,
order=90,
- fap_icon="game_2048.png",
- fap_category="Games"
-)
\ No newline at end of file
+ fap_icon="game_2048.png",
+ fap_category="Games",
+)
diff --git a/applications/plugins/geigercounter/application.fam b/applications/plugins/geigercounter/application.fam
new file mode 100644
index 000000000..71be5a161
--- /dev/null
+++ b/applications/plugins/geigercounter/application.fam
@@ -0,0 +1,13 @@
+App(
+ appid="Geiger_Coutner",
+ name="[GPIO] Geiger Counter",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="flipper_geiger_app",
+ cdefines=["APP_GEIGER"],
+ requires=[
+ "gui",
+ ],
+ stack_size=1 * 1024,
+ fap_icon="geiger.png",
+ fap_category="GPIO",
+)
diff --git a/applications/plugins/geigercounter/flipper_geiger.c b/applications/plugins/geigercounter/flipper_geiger.c
new file mode 100644
index 000000000..709db9a26
--- /dev/null
+++ b/applications/plugins/geigercounter/flipper_geiger.c
@@ -0,0 +1,227 @@
+// CC0 1.0 Universal (CC0 1.0)
+// Public Domain Dedication
+// https://github.com/nmrr
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define SCREEN_SIZE_X 128
+#define SCREEN_SIZE_Y 64
+
+// FOR J305 GEIGER TUBE
+#define CONVERSION_FACTOR 0.0081
+
+typedef enum {
+ EventTypeInput,
+ ClockEventTypeTick,
+ EventGPIO,
+} EventType;
+
+typedef struct {
+ EventType type;
+ InputEvent input;
+} EventApp;
+
+typedef struct {
+ uint32_t cps, cpm;
+ uint32_t line[SCREEN_SIZE_X / 2];
+ float coef;
+ uint8_t data;
+} mutexStruct;
+
+static void draw_callback(Canvas* canvas, void* ctx) {
+ UNUSED(ctx);
+
+ mutexStruct displayStruct;
+ mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block((ValueMutex*)ctx);
+ memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct));
+ release_mutex((ValueMutex*)ctx, geigerMutex);
+
+ char buffer[32];
+ if(displayStruct.data == 0)
+ snprintf(
+ buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm);
+ else if(displayStruct.data == 1)
+ snprintf(
+ buffer,
+ sizeof(buffer),
+ "%ld cps - %.2f uSv/h",
+ displayStruct.cps,
+ ((double)displayStruct.cpm * (double)CONVERSION_FACTOR));
+ else
+ snprintf(
+ buffer,
+ sizeof(buffer),
+ "%ld cps - %.2f mSv/y",
+ displayStruct.cps,
+ (((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76);
+
+ for(int i = 0; i < SCREEN_SIZE_X; i += 2) {
+ float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef);
+
+ canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y);
+ canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y);
+ }
+
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, buffer);
+}
+
+static void input_callback(InputEvent* input_event, void* ctx) {
+ furi_assert(ctx);
+ FuriMessageQueue* event_queue = ctx;
+ EventApp event = {.type = EventTypeInput, .input = *input_event};
+ furi_message_queue_put(event_queue, &event, FuriWaitForever);
+}
+
+static void clock_tick(void* ctx) {
+ furi_assert(ctx);
+
+ uint32_t randomNumber = furi_hal_random_get();
+ randomNumber &= 0xFFF;
+ if(randomNumber == 0) randomNumber = 1;
+
+ furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, randomNumber, 50);
+
+ FuriMessageQueue* queue = ctx;
+ EventApp event = {.type = ClockEventTypeTick};
+ furi_message_queue_put(queue, &event, 0);
+}
+
+static void gpiocallback(void* ctx) {
+ furi_assert(ctx);
+ FuriMessageQueue* queue = ctx;
+ EventApp event = {.type = EventGPIO};
+ furi_message_queue_put(queue, &event, 0);
+}
+
+int32_t flipper_geiger_app() {
+ EventApp event;
+ FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp));
+
+ furi_hal_gpio_init(&gpio_ext_pa7, GpioModeInterruptFall, GpioPullUp, GpioSpeedVeryHigh);
+ furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 5, 50);
+
+ mutexStruct mutexVal;
+ mutexVal.cps = 0;
+ mutexVal.cpm = 0;
+ for(int i = 0; i < SCREEN_SIZE_X / 2; i++) mutexVal.line[i] = 0;
+ mutexVal.coef = 1;
+ mutexVal.data = 0;
+
+ uint32_t counter = 0;
+
+ ValueMutex state_mutex;
+ init_mutex(&state_mutex, &mutexVal, sizeof(mutexVal));
+
+ ViewPort* view_port = view_port_alloc();
+ view_port_draw_callback_set(view_port, draw_callback, &state_mutex);
+ view_port_input_callback_set(view_port, input_callback, event_queue);
+
+ furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue);
+
+ Gui* gui = furi_record_open(RECORD_GUI);
+ gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+ FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, event_queue);
+ furi_timer_start(timer, 1000);
+
+ // ENABLE 5V pin
+ furi_hal_power_enable_otg();
+
+ while(1) {
+ FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever);
+
+ uint8_t screenRefresh = 0;
+
+ if(event_status == FuriStatusOk) {
+ if(event.type == EventTypeInput) {
+ if(event.input.key == InputKeyBack) {
+ break;
+ } else if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) {
+ counter = 0;
+ mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
+
+ geigerMutex->cps = 0;
+ geigerMutex->cpm = 0;
+ for(int i = 0; i < SCREEN_SIZE_X / 2; i++) geigerMutex->line[i] = 0;
+
+ screenRefresh = 1;
+ release_mutex(&state_mutex, geigerMutex);
+ } else if((event.input.key == InputKeyLeft &&
+ event.input.type == InputTypeShort)) {
+ mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
+
+ if(geigerMutex->data != 0)
+ geigerMutex->data--;
+ else
+ geigerMutex->data = 2;
+
+ screenRefresh = 1;
+ release_mutex(&state_mutex, geigerMutex);
+ } else if((event.input.key == InputKeyRight &&
+ event.input.type == InputTypeShort)) {
+ mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
+
+ if(geigerMutex->data != 2)
+ geigerMutex->data++;
+ else
+ geigerMutex->data = 0;
+
+ screenRefresh = 1;
+ release_mutex(&state_mutex, geigerMutex);
+ }
+ } else if(event.type == ClockEventTypeTick) {
+ mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex);
+
+ for(int i = 0; i < SCREEN_SIZE_X / 2 - 1; i++)
+ geigerMutex->line[SCREEN_SIZE_X / 2 - 1 - i] =
+ geigerMutex->line[SCREEN_SIZE_X / 2 - 2 - i];
+
+ geigerMutex->line[0] = counter;
+ geigerMutex->cps = counter;
+ counter = 0;
+
+ geigerMutex->cpm = geigerMutex->line[0];
+ uint32_t max = geigerMutex->line[0];
+ for(int i = 1; i < SCREEN_SIZE_X / 2; i++) {
+ if(i < 60) geigerMutex->cpm += geigerMutex->line[i];
+ if(geigerMutex->line[i] > max) max = geigerMutex->line[i];
+ }
+
+ if(max > 0)
+ geigerMutex->coef = ((float)(SCREEN_SIZE_Y - 15)) / ((float)max);
+ else
+ geigerMutex->coef = 1;
+
+ screenRefresh = 1;
+ release_mutex(&state_mutex, geigerMutex);
+ } else if(event.type == EventGPIO) {
+ counter++;
+ }
+ }
+
+ if(screenRefresh == 1) view_port_update(view_port);
+ }
+
+ furi_hal_power_disable_otg();
+
+ furi_hal_gpio_disable_int_callback(&gpio_ext_pa7);
+ furi_hal_gpio_remove_int_callback(&gpio_ext_pa7);
+ furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
+
+ furi_message_queue_free(event_queue);
+ delete_mutex(&state_mutex);
+ gui_remove_view_port(gui, view_port);
+ view_port_free(view_port);
+ furi_timer_free(timer);
+ furi_record_close(RECORD_GUI);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/applications/plugins/geigercounter/geiger.png b/applications/plugins/geigercounter/geiger.png
new file mode 100644
index 000000000..d41e1915b
Binary files /dev/null and b/applications/plugins/geigercounter/geiger.png differ
diff --git a/applications/plugins/gpioreader2/application.fam b/applications/plugins/gpioreader2/application.fam
index 57792fd0c..bf25c323d 100644
--- a/applications/plugins/gpioreader2/application.fam
+++ b/applications/plugins/gpioreader2/application.fam
@@ -8,4 +8,4 @@ App(
fap_category="GPIO",
fap_icon="icon.png",
order=1,
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/hc_sr04/application.fam b/applications/plugins/hc_sr04/application.fam
index da91a0864..bfbb2daa8 100644
--- a/applications/plugins/hc_sr04/application.fam
+++ b/applications/plugins/hc_sr04/application.fam
@@ -11,4 +11,4 @@ App(
order=20,
fap_icon="dist_sensor10px.png",
fap_category="GPIO",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/hid_app/assets/ButtonF10_5x8.png b/applications/plugins/hid_app/assets/ButtonF10_5x8.png
new file mode 100644
index 000000000..d1a7a04f0
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF10_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF11_5x8.png b/applications/plugins/hid_app/assets/ButtonF11_5x8.png
new file mode 100644
index 000000000..7e177358e
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF11_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF12_5x8.png b/applications/plugins/hid_app/assets/ButtonF12_5x8.png
new file mode 100644
index 000000000..50d2a7dc6
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF12_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF1_5x8.png b/applications/plugins/hid_app/assets/ButtonF1_5x8.png
new file mode 100644
index 000000000..7394d2710
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF1_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF2_5x8.png b/applications/plugins/hid_app/assets/ButtonF2_5x8.png
new file mode 100644
index 000000000..9d922a385
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF2_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF3_5x8.png b/applications/plugins/hid_app/assets/ButtonF3_5x8.png
new file mode 100644
index 000000000..95c2dd4f4
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF3_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF4_5x8.png b/applications/plugins/hid_app/assets/ButtonF4_5x8.png
new file mode 100644
index 000000000..602466f4b
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF4_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF5_5x8.png b/applications/plugins/hid_app/assets/ButtonF5_5x8.png
new file mode 100644
index 000000000..d73b54052
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF5_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF6_5x8.png b/applications/plugins/hid_app/assets/ButtonF6_5x8.png
new file mode 100644
index 000000000..c50748257
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF6_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF7_5x8.png b/applications/plugins/hid_app/assets/ButtonF7_5x8.png
new file mode 100644
index 000000000..396c98f51
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF7_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF8_5x8.png b/applications/plugins/hid_app/assets/ButtonF8_5x8.png
new file mode 100644
index 000000000..6304d7fb8
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF8_5x8.png differ
diff --git a/applications/plugins/hid_app/assets/ButtonF9_5x8.png b/applications/plugins/hid_app/assets/ButtonF9_5x8.png
new file mode 100644
index 000000000..148e69580
Binary files /dev/null and b/applications/plugins/hid_app/assets/ButtonF9_5x8.png differ
diff --git a/applications/plugins/hid_app/views/hid_keyboard.c b/applications/plugins/hid_app/views/hid_keyboard.c
index 8b12e8fd1..82e772b15 100644
--- a/applications/plugins/hid_app/views/hid_keyboard.c
+++ b/applications/plugins/hid_app/views/hid_keyboard.c
@@ -46,11 +46,25 @@ typedef struct {
#define KEY_WIDTH 9
#define KEY_HEIGHT 12
#define KEY_PADDING 1
-#define ROW_COUNT 6
+#define ROW_COUNT 7
#define COLUMN_COUNT 12
// 0 width items are not drawn, but there value is used
const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
+ {
+ {.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1},
+ {.width = 1, .icon = &I_ButtonF2_5x8, .value = HID_KEYBOARD_F2},
+ {.width = 1, .icon = &I_ButtonF3_5x8, .value = HID_KEYBOARD_F3},
+ {.width = 1, .icon = &I_ButtonF4_5x8, .value = HID_KEYBOARD_F4},
+ {.width = 1, .icon = &I_ButtonF5_5x8, .value = HID_KEYBOARD_F5},
+ {.width = 1, .icon = &I_ButtonF6_5x8, .value = HID_KEYBOARD_F6},
+ {.width = 1, .icon = &I_ButtonF7_5x8, .value = HID_KEYBOARD_F7},
+ {.width = 1, .icon = &I_ButtonF8_5x8, .value = HID_KEYBOARD_F8},
+ {.width = 1, .icon = &I_ButtonF9_5x8, .value = HID_KEYBOARD_F9},
+ {.width = 1, .icon = &I_ButtonF10_5x8, .value = HID_KEYBOARD_F10},
+ {.width = 1, .icon = &I_ButtonF11_5x8, .value = HID_KEYBOARD_F11},
+ {.width = 1, .icon = &I_ButtonF12_5x8, .value = HID_KEYBOARD_F12},
+ },
{
{.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1},
{.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2},
@@ -224,7 +238,12 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) {
canvas_set_font(canvas, FontKeyboard);
// Start shifting the all keys up if on the next row (Scrolling)
- uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0;
+ uint8_t initY = model->y == 0 ? 0 : 1;
+
+ if(model->y > 5) {
+ initY = model->y - 4;
+ }
+
for(uint8_t y = initY; y < ROW_COUNT; y++) {
const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y];
uint8_t x = 0;
@@ -365,7 +384,10 @@ HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) {
with_view_model(
hid_keyboard->view,
HidKeyboardModel * model,
- { model->transport = bt_hid->transport; },
+ {
+ model->transport = bt_hid->transport;
+ model->y = 1;
+ },
true);
return hid_keyboard;
diff --git a/applications/plugins/htu21d_temp_sensor/application.fam b/applications/plugins/htu21d_temp_sensor/application.fam
index 5807d7f51..724f55cd5 100644
--- a/applications/plugins/htu21d_temp_sensor/application.fam
+++ b/applications/plugins/htu21d_temp_sensor/application.fam
@@ -9,6 +9,6 @@ App(
],
stack_size=2 * 1024,
order=90,
- fap_icon="temperature_sensor.png",
+ fap_icon="temperature_sensor.png",
fap_category="GPIO",
)
diff --git a/applications/plugins/ifttt/application.fam b/applications/plugins/ifttt/application.fam
index 495627429..75e5beb67 100644
--- a/applications/plugins/ifttt/application.fam
+++ b/applications/plugins/ifttt/application.fam
@@ -11,4 +11,4 @@ App(
order=20,
fap_icon="icon.png",
fap_category="GPIO",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/morse_code/application.fam b/applications/plugins/morse_code/application.fam
index 1cc7bdaa1..73ad2ba81 100644
--- a/applications/plugins/morse_code/application.fam
+++ b/applications/plugins/morse_code/application.fam
@@ -10,6 +10,5 @@ App(
stack_size=1 * 1024,
order=20,
fap_icon="morse_code_10px.png",
- fap_category="Music"
-
-)
\ No newline at end of file
+ fap_category="Music",
+)
diff --git a/applications/plugins/mouse_jiggler/application.fam b/applications/plugins/mouse_jiggler/application.fam
index 6c529a883..6115315f5 100644
--- a/applications/plugins/mouse_jiggler/application.fam
+++ b/applications/plugins/mouse_jiggler/application.fam
@@ -9,4 +9,4 @@ App(
order=150,
fap_icon="mouse_10px.png",
fap_category="Misc",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/mousejacker/icons/badusb_10px.png b/applications/plugins/mousejacker/icons/badkb_10px.png
similarity index 100%
rename from applications/plugins/mousejacker/icons/badusb_10px.png
rename to applications/plugins/mousejacker/icons/badkb_10px.png
diff --git a/applications/plugins/mousejacker/images/badusb_10px.png b/applications/plugins/mousejacker/images/badkb_10px.png
similarity index 100%
rename from applications/plugins/mousejacker/images/badusb_10px.png
rename to applications/plugins/mousejacker/images/badkb_10px.png
diff --git a/applications/plugins/mousejacker/mousejacker.c b/applications/plugins/mousejacker/mousejacker.c
index c45b0e502..3bd772889 100644
--- a/applications/plugins/mousejacker/mousejacker.c
+++ b/applications/plugins/mousejacker/mousejacker.c
@@ -112,7 +112,7 @@ static bool open_ducky_script(Stream* stream, PluginState* plugin_state) {
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
- &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badusb_10px);
+ &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badkb_10px);
browser_options.hide_ext = false;
bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options);
@@ -396,4 +396,4 @@ int32_t mousejacker_app(void* p) {
free(plugin_state);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/mousejacker/mousejacker_ducky.c b/applications/plugins/mousejacker/mousejacker_ducky.c
index 7a57856e1..b3b04d836 100644
--- a/applications/plugins/mousejacker/mousejacker_ducky.c
+++ b/applications/plugins/mousejacker/mousejacker_ducky.c
@@ -291,7 +291,7 @@ static bool mj_process_ducky_line(
strncmp(line_tmp, "CONTROL-SHIFT", strlen("CONTROL-SHIFT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4 | 2, dk.hid, plugin_state);
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1 | 2, dk.hid, plugin_state);
return true;
} else if(
strncmp(line_tmp, "CTRL", strlen("CTRL")) == 0 ||
diff --git a/applications/plugins/music_player/music_player_worker.c b/applications/plugins/music_player/music_player_worker.c
index 60fd33a17..ee350ee80 100644
--- a/applications/plugins/music_player/music_player_worker.c
+++ b/applications/plugins/music_player/music_player_worker.c
@@ -6,6 +6,7 @@
#include
#include
+#include
#include
#define TAG "MusicPlayerWorker"
diff --git a/applications/plugins/namechanger/application.fam b/applications/plugins/namechanger/application.fam
index 545f0b905..704b643c5 100644
--- a/applications/plugins/namechanger/application.fam
+++ b/applications/plugins/namechanger/application.fam
@@ -4,10 +4,10 @@ App(
apptype=FlipperAppType.EXTERNAL,
entry_point="namechanger_app",
cdefines=["APP_NAMECHANGER"],
- requires=["gui","storage"],
+ requires=["gui", "storage"],
stack_size=2 * 1024,
order=90,
fap_icon="namechanger_10px.png",
fap_category="Tools",
- fap_icon_assets="icons",
-)
\ No newline at end of file
+ fap_icon_assets="icons",
+)
diff --git a/applications/plugins/nfc_magic/application.fam b/applications/plugins/nfc_magic/application.fam
index cc85cd386..427524051 100644
--- a/applications/plugins/nfc_magic/application.fam
+++ b/applications/plugins/nfc_magic/application.fam
@@ -2,6 +2,7 @@ App(
appid="NFC_Magic",
name="NFC Magic",
apptype=FlipperAppType.EXTERNAL,
+ targets=["f7"],
entry_point="nfc_magic_app",
requires=[
"storage",
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c
index 69bf9eb50..4b8089693 100644
--- a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c
+++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c
@@ -26,7 +26,7 @@ void nfc_magic_scene_wrong_card_on_enter(void* context) {
AlignLeft,
AlignTop,
FontSecondary,
- "Writing is supported\nonly for 4 bytes UID\nMifare CLassic 1k");
+ "Writing is supported\nonly for 4 bytes UID\nMifare Classic 1k");
widget_add_button_element(
widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wrong_card_widget_callback, nfc_magic);
diff --git a/applications/plugins/nightstand/ClockIcon.png b/applications/plugins/nightstand/ClockIcon.png
new file mode 100644
index 000000000..8a5406e6c
Binary files /dev/null and b/applications/plugins/nightstand/ClockIcon.png differ
diff --git a/applications/plugins/nightstand/application.fam b/applications/plugins/nightstand/application.fam
new file mode 100644
index 000000000..0b8cbf83a
--- /dev/null
+++ b/applications/plugins/nightstand/application.fam
@@ -0,0 +1,12 @@
+App(
+ appid="Nightstand",
+ name="Nightstand Clock",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="clock_app",
+ requires=["gui"],
+ icon="A_Clock_14",
+ stack_size=2 * 1024,
+ fap_icon="ClockIcon.png",
+ fap_category="Misc",
+ order=81,
+)
diff --git a/applications/plugins/nightstand/clock_app.c b/applications/plugins/nightstand/clock_app.c
new file mode 100644
index 000000000..d7a9f7faa
--- /dev/null
+++ b/applications/plugins/nightstand/clock_app.c
@@ -0,0 +1,334 @@
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include "clock_app.h"
+
+/*
+ This is a modified version of the default clock app intended for use overnight
+ Up / Down control the displays brightness. Down at brightness 0 turns the notification LED on and off.
+*/
+
+int brightness = 5;
+bool led = false;
+NotificationApp* notif = 0;
+
+const NotificationMessage message_red_dim = {
+ .type = NotificationMessageTypeLedRed,
+ .data.led.value = 0xFF / 16,
+};
+
+const NotificationMessage message_red_off = {
+ .type = NotificationMessageTypeLedRed,
+ .data.led.value = 0x00,
+};
+
+static const NotificationSequence led_on = {
+ &message_red_dim,
+ &message_do_not_reset,
+ NULL,
+};
+
+static const NotificationSequence led_off = {
+ &message_red_off,
+ &message_do_not_reset,
+ NULL,
+};
+
+static const NotificationSequence led_reset = {
+ &message_red_0,
+ NULL,
+};
+
+void set_backlight_brightness(float brightness) {
+ notif->settings.display_brightness = brightness;
+ notification_message(notif, &sequence_display_backlight_on);
+}
+
+void handle_up() {
+ if(brightness < 100) {
+ led = false;
+ notification_message(notif, &led_off);
+ brightness += 5;
+ }
+ set_backlight_brightness((float)(brightness / 100.f));
+}
+
+void handle_down() {
+ if(brightness > 0) {
+ brightness -= 5;
+ if(brightness == 0) { //trigger only on the first brightness 5 -> 0 transition
+ led = true;
+ notification_message(notif, &led_on);
+ }
+ } else if(brightness == 0) { //trigger on every down press afterwards
+ led = !led;
+ if(led) {
+ notification_message(notif, &led_on);
+ } else {
+ notification_message(notif, &led_off);
+ }
+ }
+ set_backlight_brightness((float)(brightness / 100.f));
+}
+
+static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
+ furi_assert(event_queue);
+ PluginEvent event = {.type = EventTypeKey, .input = *input_event};
+ furi_message_queue_put(event_queue, &event, FuriWaitForever);
+}
+
+static void clock_render_callback(Canvas* const canvas, void* ctx) {
+ //canvas_clear(canvas);
+ //canvas_set_color(canvas, ColorBlack);
+
+ //avoids a bug with the brightness being reverted after the backlight-off period
+ set_backlight_brightness((float)(brightness / 100.f));
+
+ ClockState* state = ctx;
+ if(furi_mutex_acquire(state->mutex, 200) != FuriStatusOk) {
+ //FURI_LOG_D(TAG, "Can't obtain mutex, requeue render");
+ PluginEvent event = {.type = EventTypeTick};
+ furi_message_queue_put(state->event_queue, &event, 0);
+ return;
+ }
+
+ FuriHalRtcDateTime curr_dt;
+ furi_hal_rtc_get_datetime(&curr_dt);
+ uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
+
+ char time_string[TIME_LEN];
+ char date_string[DATE_LEN];
+ char meridian_string[MERIDIAN_LEN];
+ char timer_string[20];
+
+ if(state->time_format == LocaleTimeFormat24h) {
+ snprintf(
+ time_string, TIME_LEN, CLOCK_TIME_FORMAT, curr_dt.hour, curr_dt.minute, curr_dt.second);
+ } else {
+ bool pm = curr_dt.hour > 12;
+ bool pm12 = curr_dt.hour >= 12;
+ snprintf(
+ time_string,
+ TIME_LEN,
+ CLOCK_TIME_FORMAT,
+ pm ? curr_dt.hour - 12 : curr_dt.hour,
+ curr_dt.minute,
+ curr_dt.second);
+
+ snprintf(
+ meridian_string,
+ MERIDIAN_LEN,
+ MERIDIAN_FORMAT,
+ pm12 ? MERIDIAN_STRING_PM : MERIDIAN_STRING_AM);
+ }
+
+ if(state->date_format == LocaleDateFormatYMD) {
+ snprintf(
+ date_string, DATE_LEN, CLOCK_ISO_DATE_FORMAT, curr_dt.year, curr_dt.month, curr_dt.day);
+ } else if(state->date_format == LocaleDateFormatMDY) {
+ snprintf(
+ date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.month, curr_dt.day, curr_dt.year);
+ } else {
+ snprintf(
+ date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.day, curr_dt.month, curr_dt.year);
+ }
+
+ bool timer_running = state->timer_running;
+ uint32_t timer_start_timestamp = state->timer_start_timestamp;
+ uint32_t timer_stopped_seconds = state->timer_stopped_seconds;
+
+ furi_mutex_release(state->mutex);
+
+ canvas_set_font(canvas, FontBigNumbers);
+
+ if(timer_start_timestamp != 0) {
+ int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp) :
+ timer_stopped_seconds;
+ snprintf(timer_string, 20, "%.2ld:%.2ld", elapsed_secs / 60, elapsed_secs % 60);
+ canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, time_string); // DRAW TIME
+ canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, timer_string); // DRAW TIMER
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignTop, date_string); // DRAW DATE
+ elements_button_left(canvas, "Reset");
+ } else {
+ canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, time_string);
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(canvas, 65, 17, AlignCenter, AlignCenter, date_string);
+
+ if(state->time_format == LocaleTimeFormat12h)
+ canvas_draw_str_aligned(canvas, 64, 47, AlignCenter, AlignCenter, meridian_string);
+ }
+ if(timer_running) {
+ elements_button_center(canvas, "Stop");
+ } else if(timer_start_timestamp != 0 && !timer_running) {
+ elements_button_center(canvas, "Start");
+ }
+}
+
+static void clock_state_init(ClockState* const state) {
+ state->time_format = locale_get_time_format();
+
+ state->date_format = locale_get_date_format();
+
+ //FURI_LOG_D(TAG, "Time format: %s", state->settings.time_format == H12 ? "12h" : "24h");
+ //FURI_LOG_D(TAG, "Date format: %s", state->settings.date_format == Iso ? "ISO 8601" : "RFC 5322");
+ //furi_hal_rtc_get_datetime(&state->datetime);
+}
+
+// Runs every 1000ms by default
+static void clock_tick(void* ctx) {
+ furi_assert(ctx);
+ FuriMessageQueue* event_queue = ctx;
+ PluginEvent event = {.type = EventTypeTick};
+ // It's OK to loose this event if system overloaded
+ furi_message_queue_put(event_queue, &event, 0);
+}
+
+void timer_start_stop(ClockState* plugin_state) {
+ // START/STOP TIMER
+ FuriHalRtcDateTime curr_dt;
+ furi_hal_rtc_get_datetime(&curr_dt);
+ uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
+
+ if(plugin_state->timer_running) {
+ // Update stopped seconds
+ plugin_state->timer_stopped_seconds = curr_ts - plugin_state->timer_start_timestamp;
+ } else {
+ if(plugin_state->timer_start_timestamp == 0) {
+ // Set starting timestamp if this is first time
+ plugin_state->timer_start_timestamp = curr_ts;
+ } else {
+ // Timer was already running, need to slightly readjust so we don't
+ // count the intervening time
+ plugin_state->timer_start_timestamp = curr_ts - plugin_state->timer_stopped_seconds;
+ }
+ }
+ plugin_state->timer_running = !plugin_state->timer_running;
+}
+
+void timer_reset_seconds(ClockState* plugin_state) {
+ if(plugin_state->timer_start_timestamp != 0) {
+ // Reset seconds
+ plugin_state->timer_running = false;
+ plugin_state->timer_start_timestamp = 0;
+ plugin_state->timer_stopped_seconds = 0;
+ }
+}
+
+int32_t clock_app(void* p) {
+ UNUSED(p);
+ ClockState* plugin_state = malloc(sizeof(ClockState));
+
+ plugin_state->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
+ if(plugin_state->event_queue == NULL) {
+ FURI_LOG_E(TAG, "Cannot create event queue");
+ free(plugin_state);
+ return 255;
+ }
+ //FURI_LOG_D(TAG, "Event queue created");
+
+ plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(plugin_state->mutex == NULL) {
+ FURI_LOG_E(TAG, "Cannot create mutex");
+ furi_message_queue_free(plugin_state->event_queue);
+ free(plugin_state);
+ return 255;
+ }
+ //FURI_LOG_D(TAG, "Mutex created");
+
+ clock_state_init(plugin_state);
+
+ // Set system callbacks
+ ViewPort* view_port = view_port_alloc();
+ view_port_draw_callback_set(view_port, clock_render_callback, plugin_state);
+ view_port_input_callback_set(view_port, clock_input_callback, plugin_state->event_queue);
+
+ FuriTimer* timer =
+ furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, plugin_state->event_queue);
+
+ if(timer == NULL) {
+ FURI_LOG_E(TAG, "Cannot create timer");
+ furi_mutex_free(plugin_state->mutex);
+ furi_message_queue_free(plugin_state->event_queue);
+ free(plugin_state);
+ return 255;
+ }
+ //FURI_LOG_D(TAG, "Timer created");
+
+ // Open GUI and register view_port
+ Gui* gui = furi_record_open(RECORD_GUI);
+ gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+ furi_timer_start(timer, furi_kernel_get_tick_frequency());
+ //FURI_LOG_D(TAG, "Timer started");
+
+ notif = furi_record_open(RECORD_NOTIFICATION);
+ float tmpBrightness = notif->settings.display_brightness;
+ brightness = tmpBrightness * 100;
+
+ notification_message(notif, &sequence_display_backlight_enforce_on);
+ notification_message(notif, &led_off);
+
+ // Main loop
+ PluginEvent event;
+ for(bool processing = true; processing;) {
+ FuriStatus event_status = furi_message_queue_get(plugin_state->event_queue, &event, 100);
+
+ if(event_status != FuriStatusOk) continue;
+
+ if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) != FuriStatusOk) continue;
+ // press events
+ if(event.type == EventTypeKey) {
+ if(event.input.type == InputTypeShort) {
+ switch(event.input.key) {
+ case InputKeyLeft:
+ // Reset seconds
+ timer_reset_seconds(plugin_state);
+ break;
+ case InputKeyOk:
+ // Toggle timer
+ timer_start_stop(plugin_state);
+ break;
+ case InputKeyBack:
+ // Exit the plugin
+ processing = false;
+ break;
+ case InputKeyUp:
+ handle_up();
+ break;
+ case InputKeyDown:
+ handle_down();
+ break;
+ default:
+ break;
+ }
+ }
+ } /*else if(event.type == EventTypeTick) {
+ furi_hal_rtc_get_datetime(&plugin_state->datetime);
+ }*/
+
+ view_port_update(view_port);
+ furi_mutex_release(plugin_state->mutex);
+ }
+
+ furi_timer_free(timer);
+ view_port_enabled_set(view_port, false);
+ gui_remove_view_port(gui, view_port);
+ furi_record_close(RECORD_GUI);
+ view_port_free(view_port);
+ furi_message_queue_free(plugin_state->event_queue);
+ furi_mutex_free(plugin_state->mutex);
+ free(plugin_state);
+
+ set_backlight_brightness(tmpBrightness);
+ notification_message(notif, &sequence_display_backlight_enforce_auto);
+ notification_message(notif, &led_reset);
+
+ return 0;
+}
diff --git a/applications/plugins/nightstand/clock_app.h b/applications/plugins/nightstand/clock_app.h
new file mode 100644
index 000000000..693bdfac0
--- /dev/null
+++ b/applications/plugins/nightstand/clock_app.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+#include
+
+#define TAG "Clock"
+
+#define CLOCK_ISO_DATE_FORMAT "%.4d-%.2d-%.2d"
+#define CLOCK_RFC_DATE_FORMAT "%.2d-%.2d-%.4d"
+#define CLOCK_TIME_FORMAT "%.2d:%.2d:%.2d"
+
+#define MERIDIAN_FORMAT "%s"
+#define MERIDIAN_STRING_AM "AM"
+#define MERIDIAN_STRING_PM "PM"
+
+#define TIME_LEN 12
+#define DATE_LEN 14
+#define MERIDIAN_LEN 3
+
+typedef enum {
+ EventTypeTick,
+ EventTypeKey,
+} EventType;
+
+typedef struct {
+ EventType type;
+ InputEvent input;
+} PluginEvent;
+
+typedef struct {
+ LocaleDateFormat date_format;
+ LocaleTimeFormat time_format;
+ FuriHalRtcDateTime datetime;
+ FuriMutex* mutex;
+ FuriMessageQueue* event_queue;
+ uint32_t timer_start_timestamp;
+ uint32_t timer_stopped_seconds;
+ bool timer_running;
+} ClockState;
diff --git a/applications/plugins/orgasmotron/orgasmotron.c b/applications/plugins/orgasmotron/orgasmotron.c
index b28f392f5..684fc3d95 100644
--- a/applications/plugins/orgasmotron/orgasmotron.c
+++ b/applications/plugins/orgasmotron/orgasmotron.c
@@ -40,7 +40,7 @@ int32_t orgasmotron_app(void* p) {
PluginState* plugin_state = malloc(sizeof(PluginState));
ValueMutex state_mutex;
- if (!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
+ if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
FURI_LOG_E("Orgasmatron", "cannot create mutex\r\n");
free(plugin_state);
return 255;
@@ -61,10 +61,10 @@ int32_t orgasmotron_app(void* p) {
//int mode = 0;
bool processing = true;
//while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
- while (processing) {
+ while(processing) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
- if (event_status == FuriStatusOk) {
+ if(event_status == FuriStatusOk) {
if(event.key == InputKeyBack && event.type == InputTypeShort) {
//Exit Application
notification_message(notification, &sequence_reset_vibro);
@@ -73,53 +73,58 @@ int32_t orgasmotron_app(void* p) {
processing = false;
//break;
}
- if(event.key == InputKeyOk && (event.type == InputTypePress || event.type == InputTypeRelease)) {
+ if(event.key == InputKeyOk &&
+ (event.type == InputTypePress || event.type == InputTypeRelease)) {
plugin_state->mode = 0;
}
- if(event.key == InputKeyLeft && (event.type == InputTypePress || event.type == InputTypeRelease)) {
+ if(event.key == InputKeyLeft &&
+ (event.type == InputTypePress || event.type == InputTypeRelease)) {
plugin_state->mode = 1;
}
- if(event.key == InputKeyRight && (event.type == InputTypePress || event.type == InputTypeRelease)) {
+ if(event.key == InputKeyRight &&
+ (event.type == InputTypePress || event.type == InputTypeRelease)) {
plugin_state->mode = 3;
}
- if(event.key == InputKeyUp && (event.type == InputTypePress || event.type == InputTypeRelease)) {
+ if(event.key == InputKeyUp &&
+ (event.type == InputTypePress || event.type == InputTypeRelease)) {
plugin_state->mode = 2;
}
- if(event.key == InputKeyDown && (event.type == InputTypePress || event.type == InputTypeRelease)) {
+ if(event.key == InputKeyDown &&
+ (event.type == InputTypePress || event.type == InputTypeRelease)) {
plugin_state->mode = 4;
}
}
-
- if (plugin_state->mode == 0) {
+
+ if(plugin_state->mode == 0) {
//Stop Vibration
notification_message(notification, &sequence_reset_vibro);
notification_message(notification, &sequence_reset_green);
- } else if (plugin_state->mode == 1) {
+ } else if(plugin_state->mode == 1) {
//Full power
notification_message(notification, &sequence_set_vibro_on);
notification_message(notification, &sequence_set_green_255);
- } else if (plugin_state->mode == 2) {
+ } else if(plugin_state->mode == 2) {
//Pulsed Vibration
notification_message(notification, &sequence_set_vibro_on);
notification_message(notification, &sequence_set_green_255);
delay(100);
notification_message(notification, &sequence_reset_vibro);
- } else if (plugin_state->mode == 3) {
+ } else if(plugin_state->mode == 3) {
//Soft power
notification_message(notification, &sequence_set_vibro_on);
notification_message(notification, &sequence_set_green_255);
delay(50);
notification_message(notification, &sequence_reset_vibro);
- } else if (plugin_state->mode == 4) {
+ } else if(plugin_state->mode == 4) {
//Special Sequence
- for (int i = 0;i < 15;i++) {
+ for(int i = 0; i < 15; i++) {
notification_message(notification, &sequence_set_vibro_on);
notification_message(notification, &sequence_set_green_255);
delay(50);
notification_message(notification, &sequence_reset_vibro);
delay(50);
}
- for (int i = 0;i < 2;i++) {
+ for(int i = 0; i < 2; i++) {
notification_message(notification, &sequence_set_vibro_on);
notification_message(notification, &sequence_set_green_255);
delay(400);
diff --git a/applications/plugins/orgasmotron/orgasmotron.fap b/applications/plugins/orgasmotron/orgasmotron.fap
deleted file mode 100644
index 23d7bebb3..000000000
Binary files a/applications/plugins/orgasmotron/orgasmotron.fap and /dev/null differ
diff --git a/applications/plugins/paint/application.fam b/applications/plugins/paint/application.fam
index 0630abbce..d727ab2d2 100644
--- a/applications/plugins/paint/application.fam
+++ b/applications/plugins/paint/application.fam
@@ -9,4 +9,4 @@ App(
order=175,
fap_icon="paintIcon.png",
fap_category="Misc",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/passgen/application.fam b/applications/plugins/passgen/application.fam
index 78d810a1d..94005e716 100644
--- a/applications/plugins/passgen/application.fam
+++ b/applications/plugins/passgen/application.fam
@@ -9,4 +9,4 @@ App(
fap_category="Tools",
fap_icon="icons/passgen_icon.png",
fap_icon_assets="icons",
-)
\ No newline at end of file
+)
diff --git a/applications/plugins/picopass/application.fam b/applications/plugins/picopass/application.fam
index 6cec00d8d..5d331e708 100644
--- a/applications/plugins/picopass/application.fam
+++ b/applications/plugins/picopass/application.fam
@@ -2,6 +2,7 @@ App(
appid="Picopass",
name="PicoPass Reader",
apptype=FlipperAppType.EXTERNAL,
+ targets=["f7"],
entry_point="picopass_app",
requires=[
"storage",
diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam
new file mode 100644
index 000000000..ceb8cccc9
--- /dev/null
+++ b/applications/plugins/pomodoro/application.fam
@@ -0,0 +1,12 @@
+App(
+ appid="Pomodoro",
+ name="Pomodoro",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="flipp_pomodoro_app",
+ requires=["gui", "notification", "dolphin"],
+ stack_size=1 * 1024,
+ fap_category="Tools",
+ fap_icon_assets="images",
+ fap_icon="flipp_pomodoro_10.png",
+ fap_icon_assets_symbol="flipp_pomodoro",
+)
diff --git a/applications/plugins/pomodoro/flipp_pomodoro_10.png b/applications/plugins/pomodoro/flipp_pomodoro_10.png
new file mode 100644
index 000000000..977d16a58
Binary files /dev/null and b/applications/plugins/pomodoro/flipp_pomodoro_10.png differ
diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app.c b/applications/plugins/pomodoro/flipp_pomodoro_app.c
new file mode 100644
index 000000000..ed5b8282b
--- /dev/null
+++ b/applications/plugins/pomodoro/flipp_pomodoro_app.c
@@ -0,0 +1,101 @@
+#include "flipp_pomodoro_app_i.h"
+
+enum {
+ CustomEventConsumed = true,
+ CustomEventNotConsumed = false,
+};
+
+static bool flipp_pomodoro_app_back_event_callback(void* ctx) {
+ furi_assert(ctx);
+ FlippPomodoroApp* app = ctx;
+ return scene_manager_handle_back_event(app->scene_manager);
+};
+
+static void flipp_pomodoro_app_tick_event_callback(void* ctx) {
+ furi_assert(ctx);
+ FlippPomodoroApp* app = ctx;
+
+ scene_manager_handle_custom_event(app->scene_manager, FlippPomodoroAppCustomEventTimerTick);
+};
+
+static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) {
+ furi_assert(ctx);
+ FlippPomodoroApp* app = ctx;
+
+ switch(event) {
+ case FlippPomodoroAppCustomEventStageSkip:
+ flipp_pomodoro__toggle_stage(app->state);
+ view_dispatcher_send_custom_event(
+ app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated);
+ return CustomEventConsumed;
+ case FlippPomodoroAppCustomEventStageComplete:
+ if(flipp_pomodoro__get_stage(app->state) == FlippPomodoroStageFocus) {
+ // REGISTER a deed on work stage complete to get an acheivement
+ DOLPHIN_DEED(DolphinDeedPluginGameWin);
+ };
+
+ flipp_pomodoro__toggle_stage(app->state);
+ notification_message(
+ app->notification_app,
+ stage_start_notification_sequence_map[flipp_pomodoro__get_stage(app->state)]);
+ view_dispatcher_send_custom_event(
+ app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated);
+ return CustomEventConsumed;
+ default:
+ break;
+ }
+ return scene_manager_handle_custom_event(app->scene_manager, event);
+};
+
+FlippPomodoroApp* flipp_pomodoro_app_alloc() {
+ FlippPomodoroApp* app = malloc(sizeof(FlippPomodoroApp));
+ app->state = flipp_pomodoro__new();
+
+ app->scene_manager = scene_manager_alloc(&flipp_pomodoro_scene_handlers, app);
+ app->gui = furi_record_open(RECORD_GUI);
+ app->notification_app = furi_record_open(RECORD_NOTIFICATION);
+
+ app->view_dispatcher = view_dispatcher_alloc();
+ 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, flipp_pomodoro_app_custom_event_callback);
+ view_dispatcher_set_tick_event_callback(
+ app->view_dispatcher, flipp_pomodoro_app_tick_event_callback, 1000);
+ view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+ view_dispatcher_set_navigation_event_callback(
+ app->view_dispatcher, flipp_pomodoro_app_back_event_callback);
+
+ app->timer_view = flipp_pomodoro_view_timer_alloc();
+
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ FlippPomodoroAppViewTimer,
+ flipp_pomodoro_view_timer_get_view(app->timer_view));
+
+ scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer);
+
+ return app;
+};
+
+void flipp_pomodoro_app_free(FlippPomodoroApp* app) {
+ view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewTimer);
+ view_dispatcher_free(app->view_dispatcher);
+ scene_manager_free(app->scene_manager);
+ flipp_pomodoro_view_timer_free(app->timer_view);
+ flipp_pomodoro__destroy(app->state);
+ free(app);
+ furi_record_close(RECORD_GUI);
+ furi_record_close(RECORD_NOTIFICATION);
+};
+
+int32_t flipp_pomodoro_app(void* p) {
+ UNUSED(p);
+ FlippPomodoroApp* app = flipp_pomodoro_app_alloc();
+
+ view_dispatcher_run(app->view_dispatcher);
+
+ flipp_pomodoro_app_free(app);
+
+ return 0;
+};
\ No newline at end of file
diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app.h b/applications/plugins/pomodoro/flipp_pomodoro_app.h
new file mode 100644
index 000000000..54102e1f3
--- /dev/null
+++ b/applications/plugins/pomodoro/flipp_pomodoro_app.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "views/flipp_pomodoro_timer_view.h"
+
+#include "modules/flipp_pomodoro.h"
+
+typedef enum {
+ // Reserve first 100 events for button types and indexes, starting from 0
+ FlippPomodoroAppCustomEventStageSkip = 100,
+ FlippPomodoroAppCustomEventStageComplete, // By Expiration
+ FlippPomodoroAppCustomEventTimerTick,
+ FlippPomodoroAppCustomEventStateUpdated,
+} FlippPomodoroAppCustomEvent;
+
+typedef struct {
+ SceneManager* scene_manager;
+ ViewDispatcher* view_dispatcher;
+ Gui* gui;
+ NotificationApp* notification_app;
+ FlippPomodoroTimerView* timer_view;
+ FlippPomodoroState* state;
+} FlippPomodoroApp;
+
+typedef enum {
+ FlippPomodoroAppViewTimer,
+} FlippPomodoroAppView;
\ No newline at end of file
diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app_i.h b/applications/plugins/pomodoro/flipp_pomodoro_app_i.h
new file mode 100644
index 000000000..492ef9c2d
--- /dev/null
+++ b/applications/plugins/pomodoro/flipp_pomodoro_app_i.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#define FURI_DEBUG 1
+
+/**
+ * Index of dependencies for the main app
+ */
+
+// Platform Imports
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// App resource imports
+
+#include "helpers/time.h"
+#include "helpers/notifications.h"
+#include "modules/flipp_pomodoro.h"
+#include "flipp_pomodoro_app.h"
+#include "scenes/flipp_pomodoro_scene.h"
+#include "views/flipp_pomodoro_timer_view.h"
+
+// Auto-compiled icons
+#include "flipp_pomodoro_icons.h"
diff --git a/applications/plugins/pomodoro/helpers/debug.h b/applications/plugins/pomodoro/helpers/debug.h
new file mode 100644
index 000000000..13b8f2998
--- /dev/null
+++ b/applications/plugins/pomodoro/helpers/debug.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include
+
+#define TAG "FlippPomodoro"
\ No newline at end of file
diff --git a/applications/plugins/pomodoro/helpers/notifications.c b/applications/plugins/pomodoro/helpers/notifications.c
new file mode 100644
index 000000000..388a3f11d
--- /dev/null
+++ b/applications/plugins/pomodoro/helpers/notifications.c
@@ -0,0 +1,49 @@
+#include
+
+const NotificationSequence work_start_notification = {
+ &message_display_backlight_on,
+
+ &message_vibro_on,
+
+ &message_note_b5,
+ &message_delay_250,
+
+ &message_note_d5,
+ &message_delay_250,
+
+ &message_sound_off,
+ &message_vibro_off,
+
+ &message_green_255,
+ &message_delay_1000,
+ &message_green_0,
+ &message_delay_250,
+ &message_green_255,
+ &message_delay_1000,
+
+ NULL,
+};
+
+const NotificationSequence rest_start_notification = {
+ &message_display_backlight_on,
+
+ &message_vibro_on,
+
+ &message_note_d5,
+ &message_delay_250,
+
+ &message_note_b5,
+ &message_delay_250,
+
+ &message_sound_off,
+ &message_vibro_off,
+
+ &message_red_255,
+ &message_delay_1000,
+ &message_red_0,
+ &message_delay_250,
+ &message_red_255,
+ &message_delay_1000,
+
+ NULL,
+};
\ No newline at end of file
diff --git a/applications/plugins/pomodoro/helpers/notifications.h b/applications/plugins/pomodoro/helpers/notifications.h
new file mode 100644
index 000000000..c6cd0428f
--- /dev/null
+++ b/applications/plugins/pomodoro/helpers/notifications.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "../modules/flipp_pomodoro.h"
+#include