V41 Release Candidate Changes (#130)

This commit is contained in:
Clara K
2023-02-14 09:45:26 +01:00
committed by GitHub
3084 changed files with 31592 additions and 7076 deletions
+160
View File
@@ -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
+47
View File
@@ -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
+46
View File
@@ -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.
+93
View File
@@ -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
+10 -2
View File
@@ -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/
+5 -2
View File
@@ -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": []
}
"unwantedRecommendations": [
"twxs.cmake",
"ms-vscode.cmake-tools"
]
}
+1 -1
View File
@@ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept.
## Naming
### Type names are CamelCase
### Type names are PascalCase
Examples:
+4 -11
View File
@@ -4,7 +4,7 @@
<img src="https://user-images.githubusercontent.com/55334727/215170306-051eeb8f-8f72-437f-8c2d-0e4be009bdad.png">
</p>
[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
<br><code>Anim Speed:</code> Speed in which the animations play
<br><code>Cycle Anims:</code> Duration of how long animations are played before switching to next
<br><code>Unlock Anims:</code> Custom setting just for NSFW fallback animations. Figure it out ;)
<br><code>Battery style:</code> Classic Firmware battery style toggle, just at a more convenient place
<br><code>Battery Icon:</code> Classic Firmware battery style toggle, just at a more convenient place
<br><code>XP Level:</code> Changes your Flippers level
<br><code>SubGhz Extend:</code> Allows you to extend the subghz range beyond what FZ devs planned
<br><code>SubGhz Bypass:</code> 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
```
----
<br>
<h2 align="center">Known Bugs:</h2>
```txt
- Nothing rn. Hopefully that wont change
```
----
<br>
<h2 align="center">Install:</h2>
+1 -1
View File
@@ -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
@@ -2,6 +2,7 @@ App(
appid="accessor",
name="Accessor",
apptype=FlipperAppType.DEBUG,
targets=["f7"],
entry_point="accessor_app",
cdefines=["APP_ACCESSOR"],
requires=["gui"],
@@ -11,4 +11,5 @@ App(
stack_size=1 * 1024,
order=130,
fap_category="Debug",
fap_libs=["assets"],
)
@@ -0,0 +1,148 @@
#include "battery_info.h"
#include <furi.h>
#include <gui/elements.h>
#include <assets_icons.h>
#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);
}
@@ -0,0 +1,23 @@
#pragma once
#include <gui/view.h>
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);
@@ -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);
@@ -4,15 +4,14 @@
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/submenu.h>
#include <dialogs/dialogs.h>
#include <gui/modules/submenu.h>
#include "views/bt_carrier_test.h"
#include "views/bt_packet_test.h"
#include <bt/bt_settings.h>
typedef struct {
BtSettings settings;
Gui* gui;
ViewDispatcher* view_dispatcher;
Submenu* submenu;
@@ -1,7 +1,7 @@
#include "bt_carrier_test.h"
#include "bt_test.h"
#include "bt_test_types.h"
#include "furi_hal_bt.h"
#include <furi_hal_bt.h>
struct BtCarrierTest {
BtTest* bt_test;
@@ -1,7 +1,7 @@
#include "bt_packet_test.h"
#include "bt_test.h"
#include "bt_test_types.h"
#include "furi_hal_bt.h"
#include <furi_hal_bt.h>
struct BtPacketTest {
BtTest* bt_test;
@@ -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",
@@ -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) {
@@ -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",
)
@@ -0,0 +1,98 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
//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<R\0n\7\223\310\250d\5o\7\223\310U\252\2p\10\227"
"\307\250\244V\4q\10\227\307-\225d\0r\6\223\310\315\22s\10\223\310\215\70\22\0t\10\227\310\245"
"\25\243\0u\7\223\310$+\11v\10\223\310$\65R\2w\7\223\310\244q\4x\7\223\310\244\62\25"
"y\11\227\307$\225dJ\0z\7\223\310\254\221\6{\10\227\310\251\32D\1|\6\265\310(\1}\11"
"\227\310\310\14RR\0~\6\213\313\215\4\0\0\0\4\377\377\0";
// Screen is 128x64 px
static void app_draw_callback(Canvas* canvas, void* ctx) {
UNUSED(ctx);
canvas_clear(canvas);
canvas_set_custom_u8g2_font(canvas, u8g2_font_tom_thumb_4x6_tr);
canvas_draw_str(canvas, 0, 6, "This is a tiny custom font");
canvas_draw_str(canvas, 0, 12, "012345.?! ,:;\"\'@#$%");
}
static void app_input_callback(InputEvent* input_event, void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
}
int32_t example_custom_font_main(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
// Configure view port
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, app_draw_callback, view_port);
view_port_input_callback_set(view_port, app_input_callback, event_queue);
// Register view port in GUI
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
InputEvent event;
bool running = true;
while(running) {
if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {
switch(event.key) {
case InputKeyBack:
running = false;
break;
default:
break;
}
}
}
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_record_close(RECORD_GUI);
return 0;
}
@@ -1,10 +1,11 @@
#include <file_browser_test_icons.h>
#include "file_browser_app_i.h"
#include "gui/modules/file_browser.h"
#include <furi.h>
#include <furi_hal.h>
#include <file_browser_test_icons.h>
#include <gui/modules/file_browser.h>
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#include <furi.h>
#include <furi_hal.h>
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));

Before

Width:  |  Height:  |  Size: 576 B

After

Width:  |  Height:  |  Size: 576 B

@@ -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) {
@@ -2,6 +2,7 @@ App(
appid="lfrfid_debug",
name="LF-RFID Debug",
apptype=FlipperAppType.DEBUG,
targets=["f7"],
entry_point="lfrfid_debug_app",
requires=[
"gui",
@@ -8,4 +8,4 @@ App(
stack_size=2 * 1024,
order=70,
fap_category="Debug",
)
)
@@ -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;
}
+31 -2
View File
@@ -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);
@@ -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.
@@ -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",
)
@@ -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 <gui/gui.h>
#include <gui/view_port.h>
#include <core/thread.h>
#include <core/kernel.h>
#include <locale/locale.h>
#include <one_wire/maxim_crc.h>
#include <one_wire/one_wire_host.h>
#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;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

+4 -4
View File
@@ -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",
],
@@ -1,10 +1,11 @@
#include <archive/views/archive_browser_view.h>
#include "archive_files.h"
#include "archive_apps.h"
#include "archive_browser.h"
#include "../views/archive_browser_view.h"
#include <core/common_defines.h>
#include <core/log.h>
#include "gui/modules/file_browser_worker.h"
#include <gui/modules/file_browser_worker.h>
#include <fap_loader/fap_loader_app.h>
#include <math.h>
@@ -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;
@@ -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,
};
@@ -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;
@@ -3,9 +3,9 @@
#include <m-array.h>
#include <furi.h>
#include <m-algo.h>
#include <m-string.h>
#include <storage/storage.h>
#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() \
@@ -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",
@@ -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);
}
}
@@ -1,10 +1,13 @@
#pragma once
#include "../helpers/archive_files.h"
#include "../helpers/archive_favorites.h"
#include <gui/gui_i.h>
#include <gui/view.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
#include <gui/modules/file_browser_worker.h>
#include <storage/storage.h>
#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;
@@ -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"],
)
+223
View File
@@ -0,0 +1,223 @@
#include "bad_kb_app_i.h"
#include "bad_kb_settings_filename.h"
#include <furi.h>
#include <furi_hal.h>
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#include <xtreme/settings.h>
#include <bt/bt_service/bt_i.h>
#include <bt/bt_service/bt.h>
#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;
}
+13
View File
@@ -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
+80
View File
@@ -0,0 +1,80 @@
#pragma once
#include "bad_kb_app.h"
#include "scenes/bad_kb_scene.h"
#include "bad_kb_script.h"
#include <gui/gui.h>
#include <assets_icons.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/widget.h>
#include <gui/modules/text_input.h>
#include <gui/modules/byte_input.h>
#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;
+976
View File
@@ -0,0 +1,976 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <lib/toolbox/args.h>
#include <furi_hal_bt_hid.h>
#include <furi_hal_usb_hid.h>
#include <storage/storage.h>
#include "bad_kb_script.h"
#include <dolphin/dolphin.h>
#include <bt/bt_service/bt.h>
#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);
}
+53
View File
@@ -0,0 +1,53 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <furi.h>
#include <bt/bt_service/bt_i.h>
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
@@ -0,0 +1,3 @@
#pragma once
#define BAD_KB_SETTINGS_FILE_NAME ".badkb.settings"
@@ -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,
};
@@ -3,27 +3,27 @@
#include <gui/scene_manager.h>
// 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
@@ -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)
@@ -0,0 +1,87 @@
#include "../bad_kb_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
#include <xtreme/settings.h>
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);
}
@@ -0,0 +1,51 @@
#include "../bad_kb_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
#include <storage/storage.h>
#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;
}
@@ -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, "");
}
@@ -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);
}
@@ -0,0 +1,77 @@
#include "../bad_kb_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
#include <xtreme/settings.h>
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);
}
@@ -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);
}
@@ -0,0 +1,53 @@
#include "../bad_kb_app_i.h"
#include <furi_hal_power.h>
#include <furi_hal_usb.h>
#include <storage/storage.h>
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;
}
@@ -0,0 +1,58 @@
#include "../bad_kb_script.h"
#include "../bad_kb_app_i.h"
#include "../views/bad_kb_view.h"
#include <furi_hal.h>
#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);
}
@@ -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 <toolbox/path.h>
#include <gui/elements.h>
#include <assets_icons.h>
#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);
@@ -0,0 +1,21 @@
#pragma once
#include <gui/view.h>
#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);
-160
View File
@@ -1,160 +0,0 @@
#include "bad_usb_app_i.h"
#include "bad_usb_settings_filename.h"
#include <furi.h>
#include <furi_hal.h>
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#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;
}
-11
View File
@@ -1,11 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct BadUsbApp BadUsbApp;
#ifdef __cplusplus
}
#endif
-48
View File
@@ -1,48 +0,0 @@
#pragma once
#include "bad_usb_app.h"
#include "scenes/bad_usb_scene.h"
#include "bad_usb_script.h"
#include <gui/gui.h>
#include <assets_icons.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/widget.h>
#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;
-724
View File
@@ -1,724 +0,0 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <lib/toolbox/args.h>
#include <furi_hal_usb_hid.h>
#include <storage/storage.h>
#include "bad_usb_script.h"
#include <dolphin/dolphin.h>
#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);
}
@@ -1,48 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <furi.h>
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
@@ -1,3 +0,0 @@
#pragma once
#define BAD_USB_SETTINGS_FILE_NAME ".badusb.settings"
@@ -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,
};
@@ -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);
}
@@ -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)
@@ -1,48 +0,0 @@
#include "../bad_usb_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
#include <storage/storage.h>
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;
}
@@ -1,52 +0,0 @@
#include "../bad_usb_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
#include <storage/storage.h>
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;
}
@@ -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);
}
@@ -1,21 +0,0 @@
#pragma once
#include <gui/view.h>
#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);
+3 -1
View File
@@ -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);
}
+2 -1
View File
@@ -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;
-51
View File
@@ -1,51 +0,0 @@
#include "gpio_item.h"
#include <furi_hal_resources.h>
typedef struct {
const char* name;
const GpioPin* pin;
} GpioItem;
static const GpioItem gpio_item[GPIO_ITEM_COUNT] = {
{"1.2: PA7", &gpio_ext_pa7},
{"1.3: PA6", &gpio_ext_pa6},
{"1.4: PA4", &gpio_ext_pa4},
{"1.5: PB3", &gpio_ext_pb3},
{"1.6: PB2", &gpio_ext_pb2},
{"1.7: PC3", &gpio_ext_pc3},
{"2.7: PC1", &gpio_ext_pc1},
{"2.8: PC0", &gpio_ext_pc0},
};
void gpio_item_configure_pin(uint8_t index, GpioMode mode) {
furi_assert(index < GPIO_ITEM_COUNT);
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;
}
}
-15
View File
@@ -1,15 +0,0 @@
#pragma once
#include <furi_hal_gpio.h>
#define GPIO_ITEM_COUNT 8
void gpio_item_configure_pin(uint8_t index, GpioMode mode);
void gpio_item_configure_all_pins(GpioMode mode);
void gpio_item_set_pin(uint8_t index, bool level);
void gpio_item_set_all_pins(bool level);
const char* gpio_item_get_pin_name(uint8_t index);
+69
View File
@@ -0,0 +1,69 @@
#include "gpio_items.h"
#include <furi_hal_resources.h>
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;
}
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include <furi_hal_gpio.h>
#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
@@ -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 <furi_hal_power.h>
#include <furi_hal_usb.h>
#include <dolphin/dolphin.h>
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__
@@ -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);
}
@@ -1,6 +1,6 @@
#include "../usb_uart_bridge.h"
#include "../gpio_app_i.h"
#include "furi_hal.h"
#include <furi_hal.h>
typedef enum {
UsbUartLineIndexVcp,
+4 -4
View File
@@ -1,10 +1,10 @@
#include "usb_uart_bridge.h"
#include "furi_hal.h"
#include <furi_hal_usb_cdc.h>
#include "usb_cdc.h"
#include "cli/cli_vcp.h"
#include <cli/cli_vcp.h>
#include <cli/cli.h>
#include <toolbox/api_lock.h>
#include "cli/cli.h"
#include <furi_hal.h>
#include <furi_hal_usb_cdc.h>
#define USB_CDC_PKT_LEN CDC_DATA_SZ
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
@@ -1,6 +1,6 @@
#include <gui/elements.h>
#include "gpio_i2c_scanner.h"
#include "../gpio_item.h"
#include "../gpio_items.h"
#include <string.h>
+1 -1
View File
@@ -1,6 +1,6 @@
#include <gui/elements.h>
#include "gpio_i2c_sfp.h"
#include "../gpio_item.h"
#include "../gpio_items.h"
#include <string.h>
+20 -10
View File
@@ -1,5 +1,5 @@
#include "gpio_test.h"
#include "../gpio_item.h"
#include "../gpio_items.h"
#include <gui/elements.h>
@@ -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);
+3 -1
View File
@@ -1,11 +1,13 @@
#pragma once
#include "../gpio_items.h"
#include <gui/view.h>
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);
+1 -1
View File
@@ -1,6 +1,6 @@
#include "../usb_uart_bridge.h"
#include "../gpio_app_i.h"
#include "furi_hal.h"
#include <furi_hal.h>
#include <gui/elements.h>
struct GpioUsbUart {
@@ -2,6 +2,7 @@ App(
appid="ibutton",
name="iButton",
apptype=FlipperAppType.APP,
targets=["f7"],
entry_point="ibutton_app",
cdefines=["APP_IBUTTON"],
requires=[
@@ -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;
@@ -1,6 +1,6 @@
#include "../ibutton_i.h"
#include <dolphin/dolphin.h>
#include "../../../settings/xtreme_settings/xtreme_assets.h"
#include "xtreme/assets.h"
static void ibutton_scene_read_callback(void* context) {
iButton* ibutton = context;
@@ -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;
@@ -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;
@@ -3,6 +3,7 @@ App(
name="Infrared",
apptype=FlipperAppType.APP,
entry_point="infrared_app",
targets=["f7"],
cdefines=["APP_INFRARED"],
requires=[
"gui",
@@ -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;
@@ -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;
@@ -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) {
@@ -1,5 +1,5 @@
#include "../infrared_i.h"
#include "gui/canvas.h"
#include <gui/canvas.h>
typedef enum {
InfraredRpcStateIdle,
@@ -1,11 +1,11 @@
#include "infrared_debug_view.h"
#include <stdlib.h>
#include <string.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <stdlib.h>
#include <string.h>
#define INFRARED_DEBUG_TEXT_LENGTH 64
struct InfraredDebugView {
@@ -1,13 +1,15 @@
#include <core/check.h>
#include "furi_hal_resources.h"
#include "assets_icons.h"
#include "gui/canvas.h"
#include "gui/view.h"
#include "input/input.h"
#include <gui/elements.h>
#include <furi.h>
#include "infrared_progress_view.h"
#include "gui/modules/button_panel.h"
#include <assets_icons.h>
#include <gui/canvas.h>
#include <gui/view.h>
#include <gui/elements.h>
#include <gui/modules/button_panel.h>
#include <input/input.h>
#include <furi.h>
#include <furi_hal_resources.h>
#include <core/check.h>
#include <stdint.h>
struct InfraredProgressView {

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