This commit is contained in:
Willy-JL
2024-03-29 00:47:07 +00:00
81 changed files with 1260 additions and 970 deletions

View File

@@ -12,7 +12,7 @@ void subghz_test_scene_show_only_rx_on_enter(void* context) {
// Setup view
Popup* popup = app->popup;
const char* header_text = "Transmission is blocked";
const char* header_text = "Transmission is Blocked";
const char* message_text = "Transmission on\nthis frequency is\nrestricted in\nyour region";
if(!furi_hal_region_is_provisioned()) {
header_text = "Firmware update needed";

View File

@@ -345,9 +345,9 @@ BadKbApp* bad_kb_app_alloc(char* arg) {
BadKbAppViewVarItemList,
variable_item_list_get_view(app->var_item_list));
app->bad_kb_view = bad_kb_alloc();
app->bad_kb_view = bad_kb_view_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadKbAppViewWork, bad_kb_get_view(app->bad_kb_view));
app->view_dispatcher, BadKbAppViewWork, bad_kb_view_get_view(app->bad_kb_view));
app->text_input = text_input_alloc();
view_dispatcher_add_view(
@@ -389,7 +389,7 @@ void bad_kb_app_free(BadKbApp* app) {
// Views
view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewWork);
bad_kb_free(app->bad_kb_view);
bad_kb_view_free(app->bad_kb_view);
// Custom Widget
view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewWidget);

View File

@@ -173,6 +173,49 @@ static int32_t ducky_fnc_release(BadKbScript* bad_kb, const char* line, int32_t
return 0;
}
static int32_t ducky_fnc_media(BadKbScript* bad_kb, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
uint16_t key = ducky_get_media_keycode_by_name(line);
if(key == HID_CONSUMER_UNASSIGNED) {
return ducky_error(bad_kb, "No keycode defined for %s", line);
}
if(bad_kb->bt) {
ble_profile_hid_kb_press(bad_kb->app->ble_hid, key);
furi_delay_ms(bt_timeout);
ble_profile_hid_kb_release(bad_kb->app->ble_hid, key);
} else {
furi_hal_hid_kb_press(key);
furi_hal_hid_kb_release(key);
}
return 0;
}
static int32_t ducky_fnc_globe(BadKbScript* bad_kb, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
uint16_t key = ducky_get_keycode(bad_kb, line, true);
if(key == HID_KEYBOARD_NONE) {
return ducky_error(bad_kb, "No keycode defined for %s", line);
}
if(bad_kb->bt) {
ble_profile_hid_consumer_key_press(bad_kb->app->ble_hid, HID_CONSUMER_FN_GLOBE);
ble_profile_hid_kb_press(bad_kb->app->ble_hid, key);
furi_delay_ms(bt_timeout);
ble_profile_hid_kb_release(bad_kb->app->ble_hid, key);
ble_profile_hid_consumer_key_release(bad_kb->app->ble_hid, HID_CONSUMER_FN_GLOBE);
} else {
furi_hal_hid_consumer_key_press(HID_CONSUMER_FN_GLOBE);
furi_hal_hid_kb_press(key);
furi_hal_hid_kb_release(key);
furi_hal_hid_consumer_key_release(HID_CONSUMER_FN_GLOBE);
}
return 0;
}
static int32_t ducky_fnc_waitforbutton(BadKbScript* bad_kb, const char* line, int32_t param) {
UNUSED(param);
UNUSED(bad_kb);
@@ -202,6 +245,8 @@ static const DuckyCmd ducky_commands[] = {
{"HOLD", ducky_fnc_hold, -1},
{"RELEASE", ducky_fnc_release, -1},
{"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1},
{"MEDIA", ducky_fnc_media, -1},
{"GLOBE", ducky_fnc_globe, -1},
};
#define TAG "BadKb"

View File

@@ -53,6 +53,8 @@ bool ducky_is_line_end(const char chr);
uint16_t ducky_get_keycode_by_name(const char* param);
uint16_t ducky_get_media_keycode_by_name(const char* param);
bool ducky_get_number(const char* param, uint32_t* val);
void ducky_numlock_on(BadKbScript* bad_kb);

View File

@@ -1,5 +1,4 @@
#include <furi_hal.h>
#include <furi_hal_usb_hid.h>
#include "ducky_script_i.h"
typedef struct {
@@ -78,6 +77,37 @@ static const DuckyKey ducky_keys[] = {
{"F24", HID_KEYBOARD_F24},
};
static const DuckyKey ducky_media_keys[] = {
{"POWER", HID_CONSUMER_POWER},
{"REBOOT", HID_CONSUMER_RESET},
{"SLEEP", HID_CONSUMER_SLEEP},
{"LOGOFF", HID_CONSUMER_AL_LOGOFF},
{"EXIT", HID_CONSUMER_AC_EXIT},
{"HOME", HID_CONSUMER_AC_HOME},
{"BACK", HID_CONSUMER_AC_BACK},
{"FORWARD", HID_CONSUMER_AC_FORWARD},
{"REFRESH", HID_CONSUMER_AC_REFRESH},
{"SNAPSHOT", HID_CONSUMER_SNAPSHOT},
{"PLAY", HID_CONSUMER_PLAY},
{"PAUSE", HID_CONSUMER_PAUSE},
{"PLAY_PAUSE", HID_CONSUMER_PLAY_PAUSE},
{"NEXT_TRACK", HID_CONSUMER_SCAN_NEXT_TRACK},
{"PREV_TRACK", HID_CONSUMER_SCAN_PREVIOUS_TRACK},
{"STOP", HID_CONSUMER_STOP},
{"EJECT", HID_CONSUMER_EJECT},
{"MUTE", HID_CONSUMER_MUTE},
{"VOLUME_UP", HID_CONSUMER_VOLUME_INCREMENT},
{"VOLUME_DOWN", HID_CONSUMER_VOLUME_DECREMENT},
{"FN", HID_CONSUMER_FN_GLOBE},
{"BRIGHT_UP", HID_CONSUMER_BRIGHTNESS_INCREMENT},
{"BRIGHT_DOWN", HID_CONSUMER_BRIGHTNESS_DECREMENT},
};
uint16_t ducky_get_keycode_by_name(const char* param) {
for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {
size_t key_cmd_len = strlen(ducky_keys[i].name);
@@ -89,3 +119,15 @@ uint16_t ducky_get_keycode_by_name(const char* param) {
return HID_KEYBOARD_NONE;
}
uint16_t ducky_get_media_keycode_by_name(const char* param) {
for(size_t i = 0; i < COUNT_OF(ducky_media_keys); i++) {
size_t key_cmd_len = strlen(ducky_media_keys[i].name);
if((strncmp(param, ducky_media_keys[i].name, key_cmd_len) == 0) &&
(ducky_is_line_end(param[key_cmd_len]))) {
return ducky_media_keys[i].keycode;
}
}
return HID_CONSUMER_UNASSIGNED;
}

View File

@@ -0,0 +1,80 @@
REM This is BadUSB demo script for ChromeOS by kowalski7cc
REM Open a new tab
CTRL t
REM wait for some slower chromebooks
DELAY 1000
REM Open an empty editable page
DEFAULT_DELAY 50
STRING data:text/html, <html contenteditable autofocus><style>body{font-family:monospace;}
ENTER
DELAY 500
STRING Hello World!
ENTER
REM Copy-Paste previous string
UP
HOME
SHIFT DOWN
CTRL c
RIGHT
CTRL v
CTRL v
STRING =
REPEAT 59
ENTER
ENTER
STRING _.-------.._ -,
ENTER
HOME
STRING .-"```"--..,,_/ /`-, -, \
ENTER
HOME
STRING .:" /:/ /'\ \ ,_..., `. | |
ENTER
HOME
STRING / ,----/:/ /`\ _\~`_-"` _;
ENTER
HOME
STRING ' / /`"""'\ \ \.~`_-' ,-"'/
ENTER
HOME
STRING | | | 0 | | .-' ,/` /
ENTER
HOME
STRING | ,..\ \ ,.-"` ,/` /
ENTER
HOME
STRING ; : `/`""\` ,/--==,/-----,
ENTER
HOME
STRING | `-...| -.___-Z:_______J...---;
ENTER
HOME
STRING : ` _-'
ENTER
HOME
STRING _L_ _ ___ ___ ___ ___ ____--"`
ENTER
HOME
STRING | __|| | |_ _|| _ \| _ \| __|| _ \
ENTER
HOME
STRING | _| | |__ | | | _/| _/| _| | /
ENTER
HOME
STRING |_| |____||___||_| |_| |___||_|_\
ENTER
HOME
ENTER
STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format
ENTER
STRING More information about script syntax can be found here:
ENTER
STRING https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md
ENTER

View File

@@ -1,6 +1,5 @@
#include "../bad_kb_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
enum VarItemListIndex {
VarItemListIndexKeyboardLayout,

View File

@@ -1,6 +1,5 @@
#include "../bad_kb_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
#include <storage/storage.h>
static bool bad_kb_layout_select(BadKbApp* bad_kb) {

View File

@@ -1,6 +1,5 @@
#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) {

View File

@@ -16,7 +16,7 @@ bool bad_kb_scene_work_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InputKeyLeft) {
if(bad_kb_is_idle_state(app->bad_kb_view)) {
if(bad_kb_view_is_idle_state(app->bad_kb_view)) {
scene_manager_next_scene(app->scene_manager, BadKbSceneConfig);
}
consumed = true;
@@ -28,7 +28,7 @@ bool bad_kb_scene_work_on_event(void* context, SceneManagerEvent event) {
consumed = true;
}
} else if(event.type == SceneManagerEventTypeTick) {
bad_kb_set_state(app->bad_kb_view, bad_kb_script_get_state(app->bad_kb_script));
bad_kb_view_set_state(app->bad_kb_view, bad_kb_script_get_state(app->bad_kb_script));
}
return consumed;
}
@@ -39,18 +39,18 @@ void bad_kb_scene_work_on_enter(void* 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));
bad_kb_view_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));
bad_kb_view_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_view_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);
bad_kb_view_set_button_callback(app->bad_kb_view, bad_kb_scene_work_button_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewWork);
}

View File

@@ -75,8 +75,8 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) {
if(state == BadKbStateNotConnected) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect to");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "a device");
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to device");
} else if(state == BadKbStateWillRun) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
@@ -211,7 +211,7 @@ static bool bad_kb_input_callback(InputEvent* event, void* context) {
return consumed;
}
BadKb* bad_kb_alloc(void) {
BadKb* bad_kb_view_alloc(void) {
BadKb* bad_kb = malloc(sizeof(BadKb));
bad_kb->view = view_alloc();
@@ -223,18 +223,18 @@ BadKb* bad_kb_alloc(void) {
return bad_kb;
}
void bad_kb_free(BadKb* bad_kb) {
void bad_kb_view_free(BadKb* bad_kb) {
furi_assert(bad_kb);
view_free(bad_kb->view);
free(bad_kb);
}
View* bad_kb_get_view(BadKb* bad_kb) {
View* bad_kb_view_get_view(BadKb* bad_kb) {
furi_assert(bad_kb);
return bad_kb->view;
}
void bad_kb_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context) {
void bad_kb_view_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context) {
furi_assert(bad_kb);
furi_assert(callback);
with_view_model(
@@ -248,19 +248,19 @@ void bad_kb_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, voi
true);
}
void bad_kb_set_file_name(BadKb* bad_kb, const char* name) {
void bad_kb_view_set_file_name(BadKb* bad_kb, const char* name) {
furi_assert(name);
with_view_model(
bad_kb->view, BadKbModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true);
}
void bad_kb_set_layout(BadKb* bad_kb, const char* layout) {
void bad_kb_view_set_layout(BadKb* bad_kb, const char* layout) {
furi_assert(layout);
with_view_model(
bad_kb->view, BadKbModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true);
}
void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) {
void bad_kb_view_set_state(BadKb* bad_kb, BadKbState* st) {
furi_assert(st);
uint32_t pin = 0;
if(bad_kb->context != NULL) {
@@ -283,7 +283,7 @@ void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) {
true);
}
bool bad_kb_is_idle_state(BadKb* bad_kb) {
bool bad_kb_view_is_idle_state(BadKb* bad_kb) {
bool is_idle = false;
with_view_model(
bad_kb->view,

View File

@@ -6,18 +6,18 @@
typedef struct BadKb BadKb;
typedef void (*BadKbButtonCallback)(InputKey key, void* context);
BadKb* bad_kb_alloc(void);
BadKb* bad_kb_view_alloc(void);
void bad_kb_free(BadKb* bad_kb);
void bad_kb_view_free(BadKb* bad_kb);
View* bad_kb_get_view(BadKb* bad_kb);
View* bad_kb_view_get_view(BadKb* bad_kb);
void bad_kb_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context);
void bad_kb_view_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_view_set_file_name(BadKb* bad_kb, const char* name);
void bad_kb_set_layout(BadKb* bad_kb, const char* layout);
void bad_kb_view_set_layout(BadKb* bad_kb, const char* layout);
void bad_kb_set_state(BadKb* bad_kb, BadKbState* st);
void bad_kb_view_set_state(BadKb* bad_kb, BadKbState* st);
bool bad_kb_is_idle_state(BadKb* bad_kb);
bool bad_kb_view_is_idle_state(BadKb* bad_kb);

View File

@@ -6,7 +6,7 @@ void gpio_scene_usb_uart_close_rpc_on_enter(void* context) {
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
widget_add_string_multiline_element(
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!");
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nIs Active!");
widget_add_string_multiline_element(
app->widget,
3,

View File

@@ -1,7 +1,10 @@
#include "infrared_app_i.h"
#include <furi_hal_power.h>
#include <string.h>
#include <toolbox/path.h>
#include <toolbox/saved_struct.h>
#include <dolphin/dolphin.h>
#define TAG "InfraredApp"
@@ -9,6 +12,13 @@
#define INFRARED_TX_MIN_INTERVAL_MS (50U)
#define INFRARED_TASK_STACK_SIZE (2048UL)
#define INFRARED_SETTINGS_VERSION (0)
#define INFRARED_SETTINGS_MAGIC (0x1F)
typedef struct {
uint8_t tx_pin;
} InfraredSettings;
static const NotificationSequence*
infrared_notification_sequences[InfraredNotificationMessageCount] = {
&sequence_success,
@@ -174,12 +184,6 @@ static InfraredApp* infrared_alloc(void) {
view_dispatcher_add_view(
view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex));
infrared->variable_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
infrared->view_dispatcher,
InfraredViewVariableItemList,
variable_item_list_get_view(infrared->variable_item_list));
infrared->button_menu = button_menu_alloc();
view_dispatcher_add_view(
view_dispatcher, InfraredViewButtonMenu, button_menu_get_view(infrared->button_menu));
@@ -187,6 +191,12 @@ static InfraredApp* infrared_alloc(void) {
infrared->popup = popup_alloc();
view_dispatcher_add_view(view_dispatcher, InfraredViewPopup, popup_get_view(infrared->popup));
infrared->var_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
view_dispatcher,
InfraredViewVariableList,
variable_item_list_get_view(infrared->var_item_list));
infrared->view_stack = view_stack_alloc();
view_dispatcher_add_view(
view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack));
@@ -210,10 +220,6 @@ static InfraredApp* infrared_alloc(void) {
infrared->button_panel = button_panel_alloc();
infrared->progress = infrared_progress_view_alloc();
infrared->last_settings = infrared_last_settings_alloc();
infrared_last_settings_load(infrared->last_settings);
infrared_last_settings_apply(infrared->last_settings);
return infrared;
}
@@ -241,15 +247,15 @@ static void infrared_free(InfraredApp* infrared) {
view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx);
dialog_ex_free(infrared->dialog_ex);
view_dispatcher_remove_view(infrared->view_dispatcher, InfraredViewVariableItemList);
variable_item_list_free(infrared->variable_item_list);
view_dispatcher_remove_view(view_dispatcher, InfraredViewButtonMenu);
button_menu_free(infrared->button_menu);
view_dispatcher_remove_view(view_dispatcher, InfraredViewPopup);
popup_free(infrared->popup);
view_dispatcher_remove_view(view_dispatcher, InfraredViewVariableList);
variable_item_list_free(infrared->var_item_list);
view_dispatcher_remove_view(view_dispatcher, InfraredViewStack);
view_stack_free(infrared->view_stack);
@@ -287,9 +293,6 @@ static void infrared_free(InfraredApp* infrared) {
furi_string_free(infrared->file_path);
furi_string_free(infrared->button_name);
infrared_last_settings_reset(infrared->last_settings);
infrared_last_settings_free(infrared->last_settings);
free(infrared);
}
@@ -447,6 +450,59 @@ void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, .
va_end(args);
}
void infrared_set_tx_pin(InfraredApp* infrared, FuriHalInfraredTxPin tx_pin) {
if(tx_pin < FuriHalInfraredTxPinMax) {
furi_hal_infrared_set_tx_output(tx_pin);
} else {
FuriHalInfraredTxPin tx_pin_detected = furi_hal_infrared_detect_tx_output();
furi_hal_infrared_set_tx_output(tx_pin_detected);
if(tx_pin_detected != FuriHalInfraredTxPinInternal) {
infrared_enable_otg(infrared, true);
}
}
infrared->app_state.tx_pin = tx_pin;
}
void infrared_enable_otg(InfraredApp* infrared, bool enable) {
if(enable) {
furi_hal_power_enable_otg();
} else {
furi_hal_power_disable_otg();
}
infrared->app_state.is_otg_enabled = enable;
}
static void infrared_load_settings(InfraredApp* infrared) {
InfraredSettings settings = {0};
if(!saved_struct_load(
INFRARED_SETTINGS_PATH,
&settings,
sizeof(InfraredSettings),
INFRARED_SETTINGS_MAGIC,
INFRARED_SETTINGS_VERSION)) {
FURI_LOG_D(TAG, "Failed to load settings, using defaults");
}
infrared_set_tx_pin(infrared, settings.tx_pin);
}
void infrared_save_settings(InfraredApp* infrared) {
InfraredSettings settings = {
.tx_pin = infrared->app_state.tx_pin,
};
if(!saved_struct_save(
INFRARED_SETTINGS_PATH,
&settings,
sizeof(InfraredSettings),
INFRARED_SETTINGS_MAGIC,
INFRARED_SETTINGS_VERSION)) {
FURI_LOG_E(TAG, "Failed to save settings");
}
}
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
furi_assert(context);
InfraredApp* infrared = context;
@@ -484,9 +540,10 @@ void infrared_popup_closed_callback(void* context) {
infrared->view_dispatcher, InfraredCustomEventTypePopupClosed);
}
int32_t infrared_app(char* p) {
int32_t infrared_app(void* p) {
InfraredApp* infrared = infrared_alloc();
infrared_load_settings(infrared);
infrared_make_app_folder(infrared);
bool is_remote_loaded = false;
@@ -529,6 +586,9 @@ int32_t infrared_app(char* p) {
view_dispatcher_run(infrared->view_dispatcher);
infrared_set_tx_pin(infrared, FuriHalInfraredTxPinInternal);
infrared_enable_otg(infrared, false);
infrared_free(infrared);
return 0;
}

View File

@@ -13,3 +13,6 @@
* @brief InfraredApp opaque type declaration.
*/
typedef struct InfraredApp InfraredApp;
#include <storage/storage.h>
#define INFRARED_SETTINGS_PATH CFG_PATH("infrared.settings")

View File

@@ -4,6 +4,8 @@
*/
#pragma once
#include <furi_hal_infrared.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <assets_icons.h>
@@ -25,14 +27,12 @@
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <infrared/worker/infrared_worker.h>
#include "infrared_app.h"
#include "infrared_remote.h"
#include "infrared_brute_force.h"
#include "infrared_custom_event.h"
#include "infrared_last_settings.h"
#include "scenes/infrared_scene.h"
#include "views/infrared_progress_view.h"
@@ -84,11 +84,13 @@ typedef struct {
bool is_learning_new_remote; /**< Learning new remote or adding to an existing one. */
bool is_debug_enabled; /**< Whether to enable or disable debugging features. */
bool is_transmitting; /**< Whether a signal is currently being transmitted. */
bool is_otg_enabled; /**< Whether OTG power (external 5V) is enabled. */
InfraredEditTarget edit_target : 8; /**< Selected editing target (a remote or a button). */
InfraredEditMode edit_mode : 8; /**< Selected editing operation (rename or delete). */
int32_t current_button_index; /**< Selected button index (move destination). */
int32_t prev_button_index; /**< Previous button index (move source). */
uint32_t last_transmit_time; /**< Lat time a signal was transmitted. */
FuriHalInfraredTxPin tx_pin;
} InfraredAppState;
/**
@@ -112,7 +114,7 @@ struct InfraredApp {
DialogEx* dialog_ex; /**< Standard view for displaying dialogs. */
ButtonMenu* button_menu; /**< Custom view for interacting with IR remotes. */
Popup* popup; /**< Standard view for displaying messages. */
VariableItemList* variable_item_list;
VariableItemList* var_item_list; /**< Standard view for displaying menus of choice items. */
ViewStack* view_stack; /**< Standard view for displaying stacked interfaces. */
InfraredDebugView* debug_view; /**< Custom view for displaying debug information. */
@@ -128,7 +130,6 @@ struct InfraredApp {
/** Arbitrary text storage for various inputs. */
char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1];
InfraredAppState app_state; /**< Application state. */
InfraredLastSettings* last_settings; /**< Last settings. */
void* rpc_ctx; /**< Pointer to the RPC context object. */
};
@@ -142,10 +143,10 @@ typedef enum {
InfraredViewDialogEx,
InfraredViewButtonMenu,
InfraredViewPopup,
InfraredViewVariableList,
InfraredViewStack,
InfraredViewDebugView,
InfraredViewMove,
InfraredViewVariableItemList,
InfraredViewLoading,
} InfraredView;
@@ -278,6 +279,32 @@ void infrared_play_notification_message(
void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...)
_ATTRIBUTE((__format__(__printf__, 2, 3)));
/**
* @brief Set which pin will be used to transmit infrared signals.
*
* Setting tx_pin to InfraredTxPinInternal will enable transmission via
* the built-in infrared LEDs.
*
* @param[in] infrared pointer to the application instance.
* @param[in] tx_pin pin to be used for signal transmission.
*/
void infrared_set_tx_pin(InfraredApp* infrared, FuriHalInfraredTxPin tx_pin);
/**
* @brief Enable or disable 5V at the GPIO pin 1.
*
* @param[in] infrared pointer to the application instance.
* @param[in] enable boolean value corresponding to OTG state (true = enable, false = disable)
*/
void infrared_enable_otg(InfraredApp* infrared, bool enable);
/**
* @brief Save current settings to a file.
*
* @param[in] infrared pointer to the application instance.
*/
void infrared_save_settings(InfraredApp* infrared);
/**
* @brief Common received signal callback.
*

View File

@@ -22,6 +22,9 @@ enum InfraredCustomEventType {
InfraredCustomEventTypeRpcButtonPressIndex,
InfraredCustomEventTypeRpcButtonRelease,
InfraredCustomEventTypeRpcSessionClose,
InfraredCustomEventTypeGpioTxPinChanged,
InfraredCustomEventTypeGpioOtgChanged,
};
#pragma pack(push, 1)

View File

@@ -1,135 +0,0 @@
#include "infrared_last_settings.h"
#include <furi_hal_infrared.h>
#define TAG "InfraredLastSettings"
#define INFRARED_LAST_SETTINGS_FILE_TYPE "Flipper Infrared Last Settings File"
#define INFRARED_LAST_SETTINGS_FILE_VERSION 1
#define INFRARED_LAST_SETTINGS_PATH EXT_PATH("infrared/assets/last_infrared.settings")
#define INFRARED_LAST_SETTINGS_FIELD_EXTPOWER "External5V"
#define INFRARED_LAST_SETTINGS_FIELD_EXTOUT "ExternalOut"
#define INFRARED_LAST_SETTINGS_FIELD_AUTO_DETECT "AutoDetect"
InfraredLastSettings* infrared_last_settings_alloc(void) {
InfraredLastSettings* instance = malloc(sizeof(InfraredLastSettings));
return instance;
}
void infrared_last_settings_free(InfraredLastSettings* instance) {
furi_assert(instance);
free(instance);
}
void infrared_last_settings_load(InfraredLastSettings* instance) {
furi_assert(instance);
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
bool temp_extpower = false;
bool temp_extout = false;
bool temp_auto_detect = true;
if(FSE_OK == storage_sd_status(storage) && INFRARED_LAST_SETTINGS_PATH &&
flipper_format_file_open_existing(fff_data_file, INFRARED_LAST_SETTINGS_PATH)) {
flipper_format_read_bool(
fff_data_file, INFRARED_LAST_SETTINGS_FIELD_EXTPOWER, (bool*)&temp_extpower, 1);
flipper_format_read_bool(
fff_data_file, INFRARED_LAST_SETTINGS_FIELD_EXTOUT, (bool*)&temp_extout, 1);
flipper_format_read_bool(
fff_data_file, INFRARED_LAST_SETTINGS_FIELD_AUTO_DETECT, (bool*)&temp_auto_detect, 1);
} else {
FURI_LOG_E(TAG, "Error open file %s", INFRARED_LAST_SETTINGS_PATH);
}
instance->ext_5v = temp_extpower;
instance->ext_out = temp_extout;
instance->auto_detect = temp_auto_detect;
flipper_format_file_close(fff_data_file);
flipper_format_free(fff_data_file);
furi_record_close(RECORD_STORAGE);
}
bool infrared_last_settings_save(InfraredLastSettings* instance) {
furi_assert(instance);
bool saved = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file = flipper_format_file_alloc(storage);
do {
if(FSE_OK != storage_sd_status(storage)) {
break;
}
// Open file
if(!flipper_format_file_open_always(file, INFRARED_LAST_SETTINGS_PATH)) break;
// Write header
if(!flipper_format_write_header_cstr(
file, INFRARED_LAST_SETTINGS_FILE_TYPE, INFRARED_LAST_SETTINGS_FILE_VERSION))
break;
if(!flipper_format_insert_or_update_bool(
file, INFRARED_LAST_SETTINGS_FIELD_EXTPOWER, &instance->ext_5v, 1))
break;
if(!flipper_format_insert_or_update_bool(
file, INFRARED_LAST_SETTINGS_FIELD_EXTOUT, &instance->ext_out, 1))
break;
if(!flipper_format_insert_or_update_bool(
file, INFRARED_LAST_SETTINGS_FIELD_AUTO_DETECT, &instance->auto_detect, 1))
break;
saved = true;
} while(0);
if(!saved) {
FURI_LOG_E(TAG, "Error save file %s", INFRARED_LAST_SETTINGS_PATH);
}
flipper_format_file_close(file);
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
return saved;
}
void infrared_last_settings_apply(InfraredLastSettings* instance) {
furi_assert(instance);
instance->_otg_was_enabled = furi_hal_power_is_otg_enabled();
furi_hal_infrared_set_auto_detect(instance->auto_detect);
if(!instance->auto_detect) {
furi_hal_infrared_set_debug_out(instance->ext_out);
if(instance->ext_5v) {
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
} else if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
} else if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
}
void infrared_last_settings_reset(InfraredLastSettings* instance) {
furi_assert(instance);
if(instance->_otg_was_enabled != furi_hal_power_is_otg_enabled()) {
if(instance->_otg_was_enabled) {
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
} else {
furi_hal_power_disable_otg();
}
}
}

View File

@@ -1,20 +0,0 @@
#pragma once
#include <furi_hal.h>
#include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h>
typedef struct {
bool ext_5v;
bool ext_out;
bool auto_detect;
bool _otg_was_enabled;
} InfraredLastSettings;
InfraredLastSettings* infrared_last_settings_alloc(void);
void infrared_last_settings_free(InfraredLastSettings* instance);
void infrared_last_settings_load(InfraredLastSettings* instance);
bool infrared_last_settings_save(InfraredLastSettings* instance);
void infrared_last_settings_apply(InfraredLastSettings* instance);
void infrared_last_settings_reset(InfraredLastSettings* instance);

View File

@@ -4232,3 +4232,52 @@ type: parsed
protocol: NECext
address: 87 7C 00 00
command: 25 DA 00 00
#
# Model: Dutch Originals Sound Bar
name: Power
type: parsed
protocol: NEC
address: 00 00 00 00
command: 07 00 00 00
#
name: Next
type: parsed
protocol: NEC
address: 00 00 00 00
command: 15 00 00 00
#
name: Prev
type: parsed
protocol: NEC
address: 00 00 00 00
command: 15 00 00 00
#
name: Play
type: parsed
protocol: NEC
address: 00 00 00 00
command: 6A 00 00 00
#
name: Pause
type: parsed
protocol: NEC
address: 00 00 00 00
command: 6A 00 00 00
#
name: Mute
type: parsed
protocol: NEC
address: 00 00 00 00
command: 09 00 00 00
#
name: Vol_up
type: parsed
protocol: NEC
address: 00 00 00 00
command: 5E 00 00 00
#
name: Vol_dn
type: parsed
protocol: NEC
address: 00 00 00 00
command: 85 00 00 00

View File

@@ -23,7 +23,7 @@ ADD_SCENE(infrared, universal_audio, UniversalAudio)
ADD_SCENE(infrared, universal_led, UniversalLED)
ADD_SCENE(infrared, universal_ac, UniversalAC)
ADD_SCENE(infrared, universal_fan, UniversalFan)
ADD_SCENE(infrared, gpio_settings, GpioSettings)
ADD_SCENE(infrared, debug, Debug)
ADD_SCENE(infrared, error_databases, ErrorDatabases)
ADD_SCENE(infrared, debug_settings, DebugSettings)
ADD_SCENE(infrared, rpc, Rpc)

View File

@@ -1,141 +0,0 @@
#include "../infrared_app_i.h"
#include <furi_hal_infrared.h>
uint8_t value_index_ir;
#define DEB_PINS_COUNT (sizeof(infrared_debug_cfg_variables_text) / sizeof(char* const))
const char* const infrared_debug_cfg_variables_text[] = {
"Internal",
"2 (A7)",
};
static void infrared_scene_debug_settings_auto_detect_changed(VariableItem* item) {
InfraredApp* infrared = variable_item_get_context(item);
bool value = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, value ? "ON" : "OFF");
// enable/disable other list items
VariableItemList* var_item_list = infrared->variable_item_list;
variable_item_set_locked(variable_item_list_get(var_item_list, 1), value, NULL);
variable_item_set_locked(variable_item_list_get(var_item_list, 2), value, NULL);
infrared->last_settings->auto_detect = value;
infrared_last_settings_save(infrared->last_settings);
furi_hal_infrared_set_auto_detect(infrared->last_settings->auto_detect);
if(!infrared->last_settings->auto_detect) {
furi_hal_infrared_set_debug_out(infrared->last_settings->ext_out);
if(infrared->last_settings->ext_5v) {
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
} else if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
} else if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
}
static void infrared_scene_debug_settings_pin_changed(VariableItem* item) {
InfraredApp* infrared = variable_item_get_context(item);
value_index_ir = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]);
furi_hal_infrared_set_debug_out(value_index_ir);
infrared->last_settings->ext_out = value_index_ir == 1;
infrared_last_settings_save(infrared->last_settings);
}
static void infrared_scene_debug_settings_power_changed(VariableItem* item) {
InfraredApp* infrared = variable_item_get_context(item);
bool value = variable_item_get_current_value_index(item);
if(value) {
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
} else {
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
}
variable_item_set_current_value_text(item, value ? "ON" : "OFF");
infrared->last_settings->ext_5v = value;
infrared_last_settings_save(infrared->last_settings);
}
static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) {
InfraredApp* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
}
void infrared_scene_debug_settings_on_enter(void* context) {
InfraredApp* infrared = context;
VariableItemList* variable_item_list = infrared->variable_item_list;
variable_item_list_set_enter_callback(
variable_item_list, infrared_debug_settings_start_var_list_enter_callback, infrared);
VariableItem* item = variable_item_list_add(
variable_item_list,
"Auto detect",
2,
infrared_scene_debug_settings_auto_detect_changed,
infrared);
bool auto_detect = infrared->last_settings->auto_detect;
variable_item_set_current_value_index(item, auto_detect);
variable_item_set_current_value_text(item, auto_detect ? "ON" : "OFF");
item = variable_item_list_add(
variable_item_list,
"Send signal to",
DEB_PINS_COUNT,
infrared_scene_debug_settings_pin_changed,
infrared);
value_index_ir = infrared->last_settings->ext_out;
variable_item_list_set_enter_callback(
variable_item_list, infrared_debug_settings_start_var_list_enter_callback, infrared);
variable_item_set_current_value_index(item, value_index_ir);
variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]);
variable_item_set_locked(item, auto_detect, "Disable auto detect");
item = variable_item_list_add(
variable_item_list,
"Ext Module 5v",
2,
infrared_scene_debug_settings_power_changed,
infrared);
bool enabled = infrared->last_settings->ext_5v;
variable_item_set_current_value_index(item, enabled);
variable_item_set_current_value_text(item, enabled ? "ON" : "OFF");
variable_item_set_locked(item, auto_detect, "Disable auto detect");
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList);
}
bool infrared_scene_debug_settings_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context;
UNUSED(infrared);
UNUSED(event);
return false;
}
void infrared_scene_debug_settings_on_exit(void* context) {
InfraredApp* infrared = context;
variable_item_list_reset(infrared->variable_item_list);
}

View File

@@ -0,0 +1,102 @@
#include "../infrared_app_i.h"
static const char* infrared_scene_gpio_settings_pin_text[] = {
"Flipper",
"2 (A7)",
"Detect",
};
static const char* infrared_scene_gpio_settings_otg_text[] = {
"OFF",
"ON",
};
static void infrared_scene_gpio_settings_pin_change_callback(VariableItem* item) {
InfraredApp* infrared = variable_item_get_context(item);
const uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, infrared_scene_gpio_settings_pin_text[index]);
view_dispatcher_send_custom_event(
infrared->view_dispatcher,
infrared_custom_event_pack(InfraredCustomEventTypeGpioTxPinChanged, index));
}
static void infrared_scene_gpio_settings_otg_change_callback(VariableItem* item) {
InfraredApp* infrared = variable_item_get_context(item);
const uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, infrared_scene_gpio_settings_otg_text[index]);
view_dispatcher_send_custom_event(
infrared->view_dispatcher,
infrared_custom_event_pack(InfraredCustomEventTypeGpioOtgChanged, index));
}
static void infrared_scene_gpio_settings_init(InfraredApp* infrared) {
VariableItemList* var_item_list = infrared->var_item_list;
VariableItem* item;
uint8_t value_index;
item = variable_item_list_add(
var_item_list,
"Signal Output",
COUNT_OF(infrared_scene_gpio_settings_pin_text),
infrared_scene_gpio_settings_pin_change_callback,
infrared);
value_index = infrared->app_state.tx_pin;
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, infrared_scene_gpio_settings_pin_text[value_index]);
item = variable_item_list_add(
var_item_list,
"5V on GPIO",
COUNT_OF(infrared_scene_gpio_settings_otg_text),
infrared_scene_gpio_settings_otg_change_callback,
infrared);
if(infrared->app_state.tx_pin < FuriHalInfraredTxPinMax) {
value_index = infrared->app_state.is_otg_enabled;
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(
item, infrared_scene_gpio_settings_otg_text[value_index]);
} else {
variable_item_set_values_count(item, 1);
variable_item_set_current_value_index(item, 0);
variable_item_set_current_value_text(item, "Auto");
}
}
void infrared_scene_gpio_settings_on_enter(void* context) {
InfraredApp* infrared = context;
infrared_scene_gpio_settings_init(infrared);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableList);
}
bool infrared_scene_gpio_settings_on_event(void* context, SceneManagerEvent event) {
bool consumed = false;
InfraredApp* infrared = context;
if(event.type == SceneManagerEventTypeCustom) {
const uint16_t custom_event_type = infrared_custom_event_get_type(event.event);
const uint16_t custom_event_value = infrared_custom_event_get_value(event.event);
if(custom_event_type == InfraredCustomEventTypeGpioTxPinChanged) {
infrared_set_tx_pin(infrared, custom_event_value);
variable_item_list_reset(infrared->var_item_list);
infrared_scene_gpio_settings_init(infrared);
} else if(custom_event_type == InfraredCustomEventTypeGpioOtgChanged) {
infrared_enable_otg(infrared, custom_event_value);
}
consumed = true;
}
return consumed;
}
void infrared_scene_gpio_settings_on_exit(void* context) {
InfraredApp* infrared = context;
variable_item_list_reset(infrared->var_item_list);
infrared_save_settings(infrared);
}

View File

@@ -6,7 +6,7 @@ void infrared_scene_learn_done_on_enter(void* context) {
if(infrared->app_state.is_learning_new_remote) {
popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);
popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop);
popup_set_header(popup, "Success!", 10, 12, AlignLeft, AlignTop);
} else {
popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);

View File

@@ -4,7 +4,7 @@ enum SubmenuIndex {
SubmenuIndexUniversalRemotes,
SubmenuIndexLearnNewRemote,
SubmenuIndexSavedRemotes,
SubmenuIndexDebugSettings,
SubmenuIndexGpioSettings,
SubmenuIndexLearnNewRemoteRaw,
SubmenuIndexDebug
};
@@ -40,7 +40,7 @@ void infrared_scene_start_on_enter(void* context) {
submenu_add_item(
submenu,
"GPIO Settings",
SubmenuIndexDebugSettings,
SubmenuIndexGpioSettings,
infrared_scene_start_submenu_callback,
infrared);
@@ -80,7 +80,6 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_set_scene_state(scene_manager, InfraredSceneStart, submenu_index);
if(submenu_index == SubmenuIndexUniversalRemotes) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversal);
consumed = true;
} else if(
submenu_index == SubmenuIndexLearnNewRemote ||
submenu_index == SubmenuIndexLearnNewRemoteRaw) {
@@ -90,18 +89,16 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
infrared->worker, submenu_index == SubmenuIndexLearnNewRemote);
infrared->app_state.is_learning_new_remote = true;
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
consumed = true;
} else if(submenu_index == SubmenuIndexSavedRemotes) {
furi_string_set(infrared->file_path, INFRARED_APP_FOLDER);
scene_manager_next_scene(scene_manager, InfraredSceneRemoteList);
consumed = true;
} else if(submenu_index == SubmenuIndexGpioSettings) {
scene_manager_next_scene(scene_manager, InfraredSceneGpioSettings);
} else if(submenu_index == SubmenuIndexDebug) {
scene_manager_next_scene(scene_manager, InfraredSceneDebug);
consumed = true;
} else if(submenu_index == SubmenuIndexDebugSettings) {
scene_manager_next_scene(scene_manager, InfraredSceneDebugSettings);
consumed = true;
}
consumed = true;
}
return consumed;

View File

@@ -34,7 +34,7 @@ static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(
canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a button to move");
canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a Button to Move");
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);
const bool show_scrollbar = btn_number > LIST_ITEMS;

View File

@@ -72,16 +72,19 @@ bool bt_keys_storage_load(BtKeysStorage* instance) {
bool loaded = false;
do {
// Get payload size
uint8_t magic = 0, version = 0;
size_t payload_size = 0;
if(!saved_struct_get_payload_size(
furi_string_get_cstr(instance->file_path),
BT_KEYS_STORAGE_MAGIC,
BT_KEYS_STORAGE_VERSION,
&payload_size)) {
if(!saved_struct_get_metadata(
furi_string_get_cstr(instance->file_path), &magic, &version, &payload_size)) {
FURI_LOG_E(TAG, "Failed to read payload size");
break;
}
if(magic != BT_KEYS_STORAGE_MAGIC || version != BT_KEYS_STORAGE_VERSION) {
FURI_LOG_E(TAG, "Saved data version is mismatched");
break;
}
if(payload_size > instance->nvm_sram_buff_size) {
FURI_LOG_E(TAG, "Saved data doesn't fit ram buffer");
break;

View File

@@ -14,7 +14,7 @@ bool bt_settings_load(BtSettings* bt_settings) {
BT_SETTINGS_PATH, bt_settings, sizeof(BtSettings), BT_SETTINGS_MAGIC, BT_SETTINGS_VERSION);
}
bool bt_settings_save(BtSettings* bt_settings) {
bool bt_settings_save(const BtSettings* bt_settings) {
furi_assert(bt_settings);
return saved_struct_save(

View File

@@ -15,7 +15,7 @@ typedef struct {
bool bt_settings_load(BtSettings* bt_settings);
bool bt_settings_save(BtSettings* bt_settings);
bool bt_settings_save(const BtSettings* bt_settings);
#ifdef __cplusplus
}

View File

@@ -21,6 +21,7 @@
typedef struct {
FuriTimer* timer;
FuriString* enter_pin_string;
} DesktopScenePinInputState;
static void desktop_scene_locked_light_red(bool value) {
@@ -77,6 +78,18 @@ static void desktop_scene_pin_input_timer_callback(void* context) {
desktop->view_dispatcher, DesktopPinInputEventResetWrongPinLabel);
}
static void
desktop_scene_pin_input_update_wrong_count(DesktopScenePinInputState* state, Desktop* desktop) {
uint32_t attempts = furi_hal_rtc_get_pin_fails();
if(attempts > 0) {
furi_string_printf(state->enter_pin_string, "Wrong Attempts: %lu", attempts);
desktop_view_pin_input_set_label_tertiary(
desktop->pin_input_view, 64, 60, furi_string_get_cstr(state->enter_pin_string));
} else {
desktop_view_pin_input_set_label_tertiary(desktop->pin_input_view, 64, 60, NULL);
}
}
void desktop_scene_pin_input_on_enter(void* context) {
Desktop* desktop = (Desktop*)context;
@@ -89,6 +102,7 @@ void desktop_scene_pin_input_on_enter(void* context) {
desktop->pin_input_view, desktop_scene_pin_input_done_callback);
DesktopScenePinInputState* state = malloc(sizeof(DesktopScenePinInputState));
state->enter_pin_string = furi_string_alloc();
state->timer =
furi_timer_alloc(desktop_scene_pin_input_timer_callback, FuriTimerTypeOnce, desktop);
scene_manager_set_scene_state(desktop->scene_manager, DesktopScenePinInput, (uint32_t)state);
@@ -96,6 +110,7 @@ void desktop_scene_pin_input_on_enter(void* context) {
desktop_view_pin_input_hide_pin(desktop->pin_input_view, true);
desktop_view_pin_input_set_label_button(desktop->pin_input_view, "OK");
desktop_view_pin_input_set_label_secondary(desktop->pin_input_view, 44, 25, "Enter PIN:");
desktop_scene_pin_input_update_wrong_count(state, desktop);
desktop_view_pin_input_set_pin_position(desktop->pin_input_view, 64, 37);
desktop_view_pin_input_reset_pin(desktop->pin_input_view);
@@ -106,7 +121,8 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context;
bool consumed = false;
uint32_t pin_timeout = 0;
DesktopScenePinInputState* state = (DesktopScenePinInputState*)scene_manager_get_scene_state(
desktop->scene_manager, DesktopScenePinInput);
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopPinInputEventUnlockFailed:
@@ -122,6 +138,7 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
desktop_view_pin_input_set_label_secondary(
desktop->pin_input_view, 25, 25, "Wrong PIN try again:");
desktop_scene_pin_input_set_timer(desktop, true, WRONG_PIN_HEADER_TIMEOUT);
desktop_scene_pin_input_update_wrong_count(state, desktop);
desktop_view_pin_input_reset_pin(desktop->pin_input_view);
}
consumed = true;
@@ -131,6 +148,7 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
desktop_view_pin_input_set_label_primary(desktop->pin_input_view, 0, 0, NULL);
desktop_view_pin_input_set_label_secondary(
desktop->pin_input_view, 44, 25, "Enter PIN:");
desktop_scene_pin_input_update_wrong_count(state, desktop);
consumed = true;
break;
case DesktopPinInputEventUnlocked:
@@ -158,5 +176,6 @@ void desktop_scene_pin_input_on_exit(void* context) {
desktop->scene_manager, DesktopScenePinInput);
furi_timer_free(state->timer);
furi_string_free(state->enter_pin_string);
free(state);
}

View File

@@ -35,6 +35,9 @@ typedef struct {
const char* secondary_str;
uint8_t secondary_str_x;
uint8_t secondary_str_y;
const char* tertiary_str;
uint8_t tertiary_str_x;
uint8_t tertiary_str_y;
const char* button_label;
} DesktopViewPinInputModel;
@@ -167,6 +170,17 @@ static void desktop_view_pin_input_draw(Canvas* canvas, void* context) {
canvas_draw_str(
canvas, model->secondary_str_x, model->secondary_str_y, model->secondary_str);
}
if(model->tertiary_str && model->pin.length == 0) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas,
model->tertiary_str_x,
model->tertiary_str_y,
AlignCenter,
AlignBottom,
model->tertiary_str);
}
}
void desktop_view_pin_input_timer_callback(void* context) {
@@ -294,6 +308,20 @@ void desktop_view_pin_input_set_label_secondary(
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_set_label_tertiary(
DesktopViewPinInput* pin_input,
uint8_t x,
uint8_t y,
const char* label) {
furi_assert(pin_input);
DesktopViewPinInputModel* model = view_get_model(pin_input->view);
model->tertiary_str = label;
model->tertiary_str_x = x;
model->tertiary_str_y = y;
view_commit_model(pin_input->view, true);
}
void desktop_view_pin_input_set_pin_position(DesktopViewPinInput* pin_input, uint8_t x, uint8_t y) {
furi_assert(pin_input);

View File

@@ -24,6 +24,11 @@ void desktop_view_pin_input_set_label_secondary(
uint8_t x,
uint8_t y,
const char* label);
void desktop_view_pin_input_set_label_tertiary(
DesktopViewPinInput* pin_input,
uint8_t x,
uint8_t y,
const char* label);
void desktop_view_pin_input_set_pin_position(DesktopViewPinInput* pin_input, uint8_t x, uint8_t y);
View* desktop_view_pin_input_get_view(DesktopViewPinInput*);
void desktop_view_pin_input_set_done_callback(

View File

@@ -18,7 +18,7 @@ bool expansion_settings_load(ExpansionSettings* settings) {
EXPANSION_SETTINGS_VERSION);
}
bool expansion_settings_save(ExpansionSettings* settings) {
bool expansion_settings_save(const ExpansionSettings* settings) {
furi_assert(settings);
return saved_struct_save(
EXPANSION_SETTINGS_PATH,

View File

@@ -36,7 +36,7 @@ bool expansion_settings_load(ExpansionSettings* settings);
* @param[in] settings pointer to an ExpansionSettings instance to save settings from.
* @returns true if the settings were successfully saved, false otherwise.
*/
bool expansion_settings_save(ExpansionSettings* settings);
bool expansion_settings_save(const ExpansionSettings* settings);
#ifdef __cplusplus
}

View File

@@ -100,7 +100,7 @@ static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) {
line_num++;
model->text = furi_string_get_cstr(model->text_formatted);
model->text_pos = (char*)model->text;
uint8_t lines_on_screen = 56 / canvas_current_font_height(canvas);
size_t lines_on_screen = 56 / canvas_current_font_height(canvas);
if(model->focus == TextBoxFocusEnd && line_num > lines_on_screen) {
// Set text position to 5th line from the end
const char* end = model->text + furi_string_size(model->text_formatted);

View File

@@ -151,7 +151,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) {
}
if(power->state == PowerStateCharging) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
// TODO: replace -1 magic for uint8_t with re-framing
if(battery_icon == BatteryIconPercent) {
canvas_set_color(canvas, ColorBlack);
@@ -234,7 +234,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) {
canvas_set_color(canvas, ColorBlack);
canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_9x10);
}
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
}
} else {
canvas_draw_box(canvas, 8, 3, 8, 2);

View File

@@ -26,7 +26,7 @@ void desktop_settings_scene_pin_disable_on_enter(void* context) {
popup_set_context(app->popup, app);
popup_set_callback(app->popup, pin_disable_back_callback);
popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(app->popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_header(app->popup, "PIN\nDeleted!", 100, 0, AlignCenter, AlignTop);
popup_set_timeout(app->popup, 1500);
popup_enable_timeout(app->popup);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup);

View File

@@ -23,7 +23,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) {
if(!desktop_pin_is_valid(&app->desktop->settings.pin_code)) {
submenu_add_item(
submenu,
"Set Pin",
"Set PIN",
SCENE_EVENT_SET_PIN,
desktop_settings_scene_pin_menu_submenu_callback,
app);
@@ -31,7 +31,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) {
} else {
submenu_add_item(
submenu,
"Change Pin",
"Change PIN",
SCENE_EVENT_CHANGE_PIN,
desktop_settings_scene_pin_menu_submenu_callback,
app);
@@ -44,7 +44,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) {
app);
}
submenu_set_header(app->submenu, "Pin Code Settings:");
submenu_set_header(app->submenu, "PIN Code Settings");
submenu_set_selected_item(app->submenu, app->menu_idx);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
}

View File

@@ -11,7 +11,7 @@ void power_settings_scene_power_off_on_enter(void* context) {
PowerSettingsApp* app = context;
DialogEx* dialog = app->dialog;
dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop);
dialog_ex_set_header(dialog, "Turn OFF Device?", 64, 2, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog, " I will be\nwaiting for\n you here", 78, 16, AlignLeft, AlignTop);
dialog_ex_set_icon(dialog, 21, 13, &I_Cry_dolph_55x52);

View File

@@ -6,6 +6,7 @@ App(
"updater_app",
"storage_move_to_sd",
"js_app",
"js_app_start",
"findmy_startup",
# "archive",
],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -4,26 +4,9 @@
#include "views.h"
#include <notification/notification_messages.h>
#include <dolphin/dolphin.h>
#include "hid_icons.h"
#define TAG "HidApp"
enum HidDebugSubmenuIndex {
HidSubmenuIndexKeynote,
HidSubmenuIndexKeynoteVertical,
HidSubmenuIndexKeyboard,
HidSubmenuIndexNumpad,
HidSubmenuIndexMedia,
HidSubmenuIndexMusicMacOs,
HidSubmenuIndexMovie,
HidSubmenuIndexTikTok,
HidSubmenuIndexMouse,
HidSubmenuIndexMouseClicker,
HidSubmenuIndexMouseJiggler,
HidSubmenuIndexPushToTalk,
HidSubmenuIndexRemovePairing,
};
bool hid_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
Hid* app = context;
@@ -33,10 +16,7 @@ bool hid_custom_event_callback(void* context, uint32_t event) {
bool hid_back_event_callback(void* context) {
furi_assert(context);
Hid* app = context;
FURI_LOG_D("HID", "Back event");
app->view_id = HidViewSubmenu;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu);
return true;
return scene_manager_handle_back_event(app->scene_manager);
}
void bt_hid_remove_pairing(Hid* app) {
@@ -53,63 +33,12 @@ void bt_hid_remove_pairing(Hid* app) {
furi_hal_bt_start_advertising();
}
static void hid_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
Hid* app = context;
if(index == HidSubmenuIndexKeynote) {
app->view_id = HidViewKeynote;
hid_keynote_set_orientation(app->hid_keynote, false);
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
} else if(index == HidSubmenuIndexKeynoteVertical) {
app->view_id = HidViewKeynote;
hid_keynote_set_orientation(app->hid_keynote, true);
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
} else if(index == HidSubmenuIndexKeyboard) {
app->view_id = HidViewKeyboard;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard);
} else if(index == HidSubmenuIndexNumpad) {
app->view_id = HidViewNumpad;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewNumpad);
} else if(index == HidSubmenuIndexMedia) {
app->view_id = HidViewMedia;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia);
} else if(index == HidSubmenuIndexMusicMacOs) {
app->view_id = HidViewMusicMacOs;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMusicMacOs);
} else if(index == HidSubmenuIndexMovie) {
app->view_id = HidViewMovie;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMovie);
} else if(index == HidSubmenuIndexMouse) {
app->view_id = HidViewMouse;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse);
} else if(index == HidSubmenuIndexTikTok) {
app->view_id = BtHidViewTikTok;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
} else if(index == HidSubmenuIndexMouseClicker) {
app->view_id = HidViewMouseClicker;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker);
} else if(index == HidSubmenuIndexMouseJiggler) {
app->view_id = HidViewMouseJiggler;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler);
} else if(index == HidSubmenuIndexPushToTalk) {
app->view_id = HidViewPushToTalkMenu;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewPushToTalkMenu);
} else if(index == HidSubmenuIndexRemovePairing) {
scene_manager_next_scene(app->scene_manager, HidSceneUnpair);
}
}
static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {
furi_assert(context);
Hid* hid = context;
bool connected = (status == BtStatusConnected);
#ifdef HID_TRANSPORT_BLE
if(connected) {
notification_internal_message(hid->notifications, &sequence_set_blue_255);
} else {
notification_internal_message(hid->notifications, &sequence_reset_blue);
}
#endif
const bool connected = (status == BtStatusConnected);
notification_internal_message(
hid->notifications, connected ? &sequence_set_blue_255 : &sequence_reset_blue);
hid_keynote_set_connected_status(hid->hid_keynote, connected);
hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
hid_numpad_set_connected_status(hid->hid_numpad, connected);
@@ -123,17 +52,12 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
hid_tiktok_set_connected_status(hid->hid_tiktok, connected);
}
static uint32_t hid_exit(void* context) {
UNUSED(context);
return VIEW_NONE;
}
static uint32_t hid_ptt_menu_view(void* context) {
UNUSED(context);
return HidViewPushToTalkMenu;
}
Hid* hid_alloc(void) {
Hid* hid_alloc() {
Hid* app = malloc(sizeof(Hid));
// Gui
@@ -148,81 +72,18 @@ Hid* hid_alloc(void) {
// View dispatcher
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_set_navigation_event_callback(app->view_dispatcher, hid_back_event_callback);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(app->view_dispatcher, hid_custom_event_callback);
view_dispatcher_set_navigation_event_callback(app->view_dispatcher, hid_back_event_callback);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Scene Manager
app->scene_manager = scene_manager_alloc(&hid_scene_handlers, app);
// Device Type Submenu view
app->device_type_submenu = submenu_alloc();
submenu_add_item(
app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu,
"Keynote Vertical",
HidSubmenuIndexKeynoteVertical,
hid_submenu_callback,
app);
submenu_add_item(
app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu, "Numpad", HidSubmenuIndexNumpad, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu,
"Apple Music macOS",
HidSubmenuIndexMusicMacOs,
hid_submenu_callback,
app);
submenu_add_item(
app->device_type_submenu, "Movie", HidSubmenuIndexMovie, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu,
"TikTok / YT Shorts",
HidSubmenuIndexTikTok,
hid_submenu_callback,
app);
submenu_add_item(
app->device_type_submenu,
"Mouse Clicker",
HidSubmenuIndexMouseClicker,
hid_submenu_callback,
app);
submenu_add_item(
app->device_type_submenu,
"Mouse Jiggler",
HidSubmenuIndexMouseJiggler,
hid_submenu_callback,
app);
submenu_add_item(
app->device_type_submenu,
"PushToTalk",
HidSubmenuIndexPushToTalk,
hid_submenu_callback,
app);
#ifdef HID_TRANSPORT_BLE
submenu_add_item(
app->device_type_submenu,
"Remove Pairing",
HidSubmenuIndexRemovePairing,
hid_submenu_callback,
app);
#endif
view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit);
view_dispatcher_add_view(
app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu));
app->view_id = HidViewSubmenu;
return app;
}
app->submenu = submenu_alloc();
Hid* hid_app_alloc_view(void* context) {
furi_assert(context);
Hid* app = context;
view_dispatcher_add_view(app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->submenu));
// Dialog view
app->dialog = dialog_ex_alloc();
@@ -307,7 +168,7 @@ void hid_free(Hid* app) {
#endif
// Free views
view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu);
submenu_free(app->device_type_submenu);
submenu_free(app->submenu);
view_dispatcher_remove_view(app->view_dispatcher, HidViewDialog);
dialog_ex_free(app->dialog);
view_dispatcher_remove_view(app->view_dispatcher, HidViewPopup);
@@ -354,18 +215,16 @@ void hid_free(Hid* app) {
int32_t hid_usb_app(void* p) {
UNUSED(p);
Hid* app = hid_alloc();
app = hid_app_alloc_view(app);
FURI_LOG_D("HID", "Starting as USB app");
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
furi_hal_usb_unlock();
furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true);
bt_hid_connection_status_changed_callback(BtStatusConnected, app);
dolphin_deed(DolphinDeedPluginStart);
scene_manager_next_scene(app->scene_manager, HidSceneMain);
scene_manager_next_scene(app->scene_manager, HidSceneStart);
view_dispatcher_run(app->view_dispatcher);
@@ -379,7 +238,6 @@ int32_t hid_usb_app(void* p) {
int32_t hid_ble_app(void* p) {
UNUSED(p);
Hid* app = hid_alloc();
app = hid_app_alloc_view(app);
FURI_LOG_D("HID", "Starting as BLE app");
@@ -409,7 +267,7 @@ int32_t hid_ble_app(void* p) {
dolphin_deed(DolphinDeedPluginStart);
scene_manager_next_scene(app->scene_manager, HidSceneMain);
scene_manager_next_scene(app->scene_manager, HidSceneStart);
view_dispatcher_run(app->view_dispatcher);

View File

@@ -35,11 +35,6 @@
#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
typedef enum {
HidTransportUsb,
HidTransportBle,
} HidTransport;
typedef struct Hid Hid;
struct Hid {
@@ -49,7 +44,7 @@ struct Hid {
NotificationApp* notifications;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
Submenu* device_type_submenu;
Submenu* submenu;
DialogEx* dialog;
Popup* popup;
HidKeynote* hid_keynote;
@@ -64,10 +59,8 @@ struct Hid {
HidTikTok* hid_tiktok;
HidPushToTalk* hid_ptt;
HidPushToTalkMenu* hid_ptt_menu;
HidTransport transport;
uint32_t view_id;
};
void bt_hid_remove_pairing(Hid* app);
void hid_hal_keyboard_press(Hid* instance, uint16_t event);
@@ -82,4 +75,4 @@ void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy);
void hid_hal_mouse_scroll(Hid* instance, int8_t delta);
void hid_hal_mouse_press(Hid* instance, uint16_t event);
void hid_hal_mouse_release(Hid* instance, uint16_t event);
void hid_hal_mouse_release_all(Hid* instance);
void hid_hal_mouse_release_all(Hid* instance);

View File

@@ -1,3 +1,3 @@
ADD_SCENE(hid, start, Start)
ADD_SCENE(hid, main, Main)
ADD_SCENE(hid, unpair, Unpair)
ADD_SCENE(hid, exit_confirm, ExitConfirm)

View File

@@ -1,45 +0,0 @@
#include "../hid.h"
#include "../views.h"
static void hid_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) {
furi_assert(context);
Hid* app = context;
if(result == DialogExResultLeft) {
view_dispatcher_stop(app->view_dispatcher);
} else if(result == DialogExResultRight) {
scene_manager_previous_scene(app->scene_manager);
} else if(result == DialogExResultCenter) {
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, HidSceneMain);
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu);
}
}
void hid_scene_exit_confirm_on_enter(void* context) {
Hid* app = context;
// Exit dialog view
dialog_ex_reset(app->dialog);
dialog_ex_set_result_callback(app->dialog, hid_scene_exit_confirm_dialog_callback);
dialog_ex_set_context(app->dialog, app);
dialog_ex_set_left_button_text(app->dialog, "Exit");
dialog_ex_set_right_button_text(app->dialog, "Stay");
dialog_ex_set_center_button_text(app->dialog, "Menu");
dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop);
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewDialog);
}
bool hid_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
Hid* app = context;
bool consumed = false;
UNUSED(app);
UNUSED(event);
return consumed;
}
void hid_scene_exit_confirm_on_exit(void* context) {
Hid* app = context;
dialog_ex_reset(app->dialog);
}

View File

@@ -3,8 +3,8 @@
void hid_scene_main_on_enter(void* context) {
Hid* app = context;
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
view_dispatcher_switch_to_view(
app->view_dispatcher, scene_manager_get_scene_state(app->scene_manager, HidSceneMain));
}
bool hid_scene_main_on_event(void* context, SceneManagerEvent event) {

View File

@@ -0,0 +1,157 @@
#include "../hid.h"
#include "../views.h"
enum HidSubmenuIndex {
HidSubmenuIndexKeynote,
HidSubmenuIndexKeynoteVertical,
HidSubmenuIndexKeyboard,
HidSubmenuIndexNumpad,
HidSubmenuIndexMedia,
HidSubmenuIndexMusicMacOs,
HidSubmenuIndexMovie,
HidSubmenuIndexTikTok,
HidSubmenuIndexMouse,
HidSubmenuIndexMouseClicker,
HidSubmenuIndexMouseJiggler,
HidSubmenuIndexPushToTalk,
HidSubmenuIndexRemovePairing,
};
static void hid_scene_start_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
Hid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void hid_scene_start_on_enter(void* context) {
Hid* app = context;
submenu_add_item(
app->submenu, "Keynote", HidSubmenuIndexKeynote, hid_scene_start_submenu_callback, app);
submenu_add_item(
app->submenu,
"Keynote Vertical",
HidSubmenuIndexKeynoteVertical,
hid_scene_start_submenu_callback,
app);
submenu_add_item(
app->submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_scene_start_submenu_callback, app);
submenu_add_item(
app->submenu, "Numpad", HidSubmenuIndexNumpad, hid_scene_start_submenu_callback, app);
submenu_add_item(
app->submenu, "Media", HidSubmenuIndexMedia, hid_scene_start_submenu_callback, app);
submenu_add_item(
app->submenu,
"Apple Music macOS",
HidSubmenuIndexMusicMacOs,
hid_scene_start_submenu_callback,
app);
submenu_add_item(
app->submenu, "Movie", HidSubmenuIndexMovie, hid_scene_start_submenu_callback, app);
submenu_add_item(
app->submenu, "Mouse", HidSubmenuIndexMouse, hid_scene_start_submenu_callback, app);
submenu_add_item(
app->submenu,
"TikTok / YT Shorts",
HidSubmenuIndexTikTok,
hid_scene_start_submenu_callback,
app);
submenu_add_item(
app->submenu,
"Mouse Clicker",
HidSubmenuIndexMouseClicker,
hid_scene_start_submenu_callback,
app);
submenu_add_item(
app->submenu,
"Mouse Jiggler",
HidSubmenuIndexMouseJiggler,
hid_scene_start_submenu_callback,
app);
submenu_add_item(
app->submenu,
"PushToTalk",
HidSubmenuIndexPushToTalk,
hid_scene_start_submenu_callback,
app);
#ifdef HID_TRANSPORT_BLE
submenu_add_item(
app->submenu,
"Bluetooth Unpairing",
HidSubmenuIndexRemovePairing,
hid_scene_start_submenu_callback,
app);
#endif
submenu_set_selected_item(
app->submenu, scene_manager_get_scene_state(app->scene_manager, HidSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu);
}
bool hid_scene_start_on_event(void* context, SceneManagerEvent event) {
Hid* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == HidSubmenuIndexRemovePairing) {
scene_manager_next_scene(app->scene_manager, HidSceneUnpair);
} else {
HidView view_id;
switch(event.event) {
case HidSubmenuIndexKeynote:
view_id = HidViewKeynote;
hid_keynote_set_orientation(app->hid_keynote, false);
break;
case HidSubmenuIndexKeynoteVertical:
view_id = HidViewKeynote;
hid_keynote_set_orientation(app->hid_keynote, true);
break;
case HidSubmenuIndexKeyboard:
view_id = HidViewKeyboard;
break;
case HidSubmenuIndexNumpad:
view_id = HidViewNumpad;
break;
case HidSubmenuIndexMedia:
view_id = HidViewMedia;
break;
case HidSubmenuIndexMusicMacOs:
view_id = HidViewMusicMacOs;
break;
case HidSubmenuIndexMovie:
view_id = HidViewMovie;
break;
case HidSubmenuIndexTikTok:
view_id = BtHidViewTikTok;
break;
case HidSubmenuIndexMouse:
view_id = HidViewMouse;
break;
case HidSubmenuIndexMouseClicker:
view_id = HidViewMouseClicker;
break;
case HidSubmenuIndexMouseJiggler:
view_id = HidViewMouseJiggler;
break;
case HidSubmenuIndexPushToTalk:
view_id = HidViewPushToTalkMenu;
break;
default:
furi_crash();
}
scene_manager_set_scene_state(app->scene_manager, HidSceneMain, view_id);
scene_manager_next_scene(app->scene_manager, HidSceneMain);
}
scene_manager_set_scene_state(app->scene_manager, HidSceneStart, event.event);
consumed = true;
}
return consumed;
}
void hid_scene_start_on_exit(void* context) {
Hid* app = context;
submenu_reset(app->submenu);
}

View File

@@ -29,9 +29,7 @@ void hid_scene_unpair_on_enter(void* context) {
dialog_ex_reset(app->dialog);
dialog_ex_set_result_callback(app->dialog, hid_scene_unpair_dialog_callback);
dialog_ex_set_context(app->dialog, app);
dialog_ex_set_header(app->dialog, "Unpair All Devices?", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
app->dialog, "All previous pairings\nwill be lost!", 64, 22, AlignCenter, AlignTop);
dialog_ex_set_header(app->dialog, "Unpair the Device?", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_left_button_text(app->dialog, "Back");
dialog_ex_set_right_button_text(app->dialog, "Unpair");

View File

@@ -23,7 +23,6 @@ typedef struct {
bool ok_pressed;
bool back_pressed;
bool connected;
HidTransport transport;
} HidKeyboardModel;
typedef struct {
@@ -230,7 +229,8 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) {
HidKeyboardModel* model = context;
// Header
if((!model->connected) && (model->transport == HidTransportBle)) {
#ifdef HID_TRANSPORT_BLE
if(!model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keyboard");
@@ -243,6 +243,7 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) {
canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection...");
return; // Dont render the keyboard if we are not yet connected
}
#endif
canvas_set_font(canvas, FontKeyboard);
// Start shifting the all keys up if on the next row (Scrolling)
@@ -400,13 +401,7 @@ HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) {
view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback);
with_view_model(
hid_keyboard->view,
HidKeyboardModel * model,
{
model->transport = bt_hid->transport;
model->y = 1;
},
true);
hid_keyboard->view, HidKeyboardModel * model, { model->y = 1; }, true);
return hid_keyboard;
}

View File

@@ -19,7 +19,6 @@ typedef struct {
bool ok_pressed;
bool back_pressed;
bool connected;
HidTransport transport;
} HidKeynoteModel;
static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
@@ -40,13 +39,13 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) {
HidKeynoteModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#endif
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote");
@@ -92,12 +91,12 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) {
canvas_set_color(canvas, ColorBlack);
// Ok
canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
canvas_draw_icon(canvas, 63, 24, &I_Space_65x18);
if(model->ok_pressed) {
elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
elements_slightly_rounded_box(canvas, 66, 26, 60, 13);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
canvas_draw_icon(canvas, 74, 28, &I_Ok_btn_9x9);
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space");
canvas_set_color(canvas, ColorBlack);
@@ -116,18 +115,18 @@ static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) {
HidKeynoteModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote");
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote");
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote");
#else
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote");
#endif
canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
@@ -274,10 +273,6 @@ HidKeynote* hid_keynote_alloc(Hid* hid) {
view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel));
view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback);
view_set_input_callback(hid_keynote->view, hid_keynote_input_callback);
with_view_model(
hid_keynote->view, HidKeynoteModel * model, { model->transport = hid->transport; }, true);
return hid_keynote;
}

View File

@@ -22,7 +22,6 @@ typedef struct {
bool ok_pressed;
bool connected;
bool back_pressed;
HidTransport transport;
} HidMediaModel;
static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
@@ -43,13 +42,13 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
HidMediaModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#endif
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Media");
@@ -60,9 +59,9 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
// Up
if(model->up_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6);
@@ -70,9 +69,9 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
// Down
if(model->down_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6);
@@ -80,9 +79,9 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
// Left
if(model->left_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
hid_media_draw_arrow(canvas, 67, 28, CanvasDirectionRightToLeft);
@@ -92,9 +91,9 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
// Right
if(model->right_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
hid_media_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight);
@@ -104,9 +103,9 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
// Ok
if(model->ok_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
hid_media_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight);
@@ -116,9 +115,9 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
// Exit
if(model->back_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10);
@@ -209,10 +208,6 @@ HidMedia* hid_media_alloc(Hid* hid) {
view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel));
view_set_draw_callback(hid_media->view, hid_media_draw_callback);
view_set_input_callback(hid_media->view, hid_media_input_callback);
with_view_model(
hid_media->view, HidMediaModel * model, { model->transport = hid->transport; }, true);
return hid_media;
}

View File

@@ -3,7 +3,6 @@
#include <gui/view.h>
typedef struct Hid Hid;
typedef struct HidMedia HidMedia;
HidMedia* hid_media_alloc(Hid* hid);

View File

@@ -21,7 +21,6 @@ typedef struct {
bool right_mouse_pressed;
bool connected;
uint8_t acceleration;
HidTransport transport;
} HidMouseModel;
static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
@@ -29,13 +28,13 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
HidMouseModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#endif
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse");
@@ -54,9 +53,9 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Up
if(model->up_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 80, 8, &I_Pin_arrow_up_7x9);
@@ -64,9 +63,9 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Down
if(model->down_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 80, 40, &I_Pin_arrow_down_7x9);
@@ -74,9 +73,9 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Left
if(model->left_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 63, 25, &I_Pin_arrow_left_9x7);
@@ -84,9 +83,9 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Right
if(model->right_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 95, 25, &I_Pin_arrow_right_9x7);
@@ -94,9 +93,9 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Ok
if(model->left_mouse_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 79, 24, &I_Left_mouse_icon_9x9);
@@ -104,9 +103,9 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
// Back
if(model->right_mouse_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 112, 38, &I_Right_mouse_icon_9x9);
@@ -202,6 +201,15 @@ static bool hid_mouse_input_callback(InputEvent* event, void* context) {
if(event->type == InputTypeLong && event->key == InputKeyBack) {
hid_hal_mouse_release_all(hid_mouse->hid);
with_view_model(
hid_mouse->view,
HidMouseModel * model,
{
model->left_mouse_held = false;
model->left_mouse_pressed = false;
},
false);
} else {
hid_mouse_process(hid_mouse, event);
consumed = true;
@@ -218,10 +226,6 @@ HidMouse* hid_mouse_alloc(Hid* hid) {
view_allocate_model(hid_mouse->view, ViewModelTypeLocking, sizeof(HidMouseModel));
view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback);
view_set_input_callback(hid_mouse->view, hid_mouse_input_callback);
with_view_model(
hid_mouse->view, HidMouseModel * model, { model->transport = hid->transport; }, true);
return hid_mouse;
}

View File

@@ -18,7 +18,6 @@ typedef struct {
bool connected;
bool running;
int rate;
HidTransport transport;
} HidMouseClickerModel;
static void hid_mouse_clicker_start_or_restart_timer(void* context) {
@@ -44,46 +43,46 @@ static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) {
HidMouseClickerModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#endif
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Clicker");
canvas_set_font(canvas, FontSecondary);
// Ok
canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
canvas_draw_icon(canvas, 58, 25, &I_Space_65x18);
if(model->running) {
canvas_set_font(canvas, FontPrimary);
FuriString* rate_label = furi_string_alloc();
furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate);
elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label));
canvas_set_font(canvas, FontSecondary);
furi_string_free(rate_label);
elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
elements_slightly_rounded_box(canvas, 61, 27, 60, 13);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking");
canvas_set_font(canvas, FontSecondary);
}
canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
canvas_draw_icon(canvas, 69, 29, &I_Ok_btn_9x9);
if(model->running) {
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop");
elements_multiline_text_aligned(canvas, 86, 37, AlignLeft, AlignBottom, "Stop");
} else {
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start");
elements_multiline_text_aligned(canvas, 86, 37, AlignLeft, AlignBottom, "Start");
}
canvas_set_color(canvas, ColorBlack);
// Clicks/s
char label[20];
snprintf(label, sizeof(label), "%d clicks/s", model->rate);
elements_multiline_text_aligned(canvas, 28, 37, AlignCenter, AlignBottom, label);
canvas_draw_icon(canvas, 25, 20, &I_ButtonUp_7x4);
canvas_draw_icon(canvas, 25, 44, &I_ButtonDown_7x4);
// Back
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit");
canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Exit");
}
static void hid_mouse_clicker_timer_callback(void* context) {
@@ -145,6 +144,9 @@ static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) {
rate_changed = true;
consumed = true;
break;
case InputKeyBack:
model->running = false;
break;
default:
consumed = true;
break;
@@ -179,10 +181,7 @@ HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) {
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
model->transport = hid->transport;
model->rate = DEFAULT_CLICK_RATE;
},
{ model->rate = DEFAULT_CLICK_RATE; },
true);
return hid_mouse_clicker;

View File

@@ -17,7 +17,6 @@ typedef struct {
bool running;
int interval_idx;
uint8_t counter;
HidTransport transport;
} HidMouseJigglerModel;
const int intervals[6] = {500, 2000, 5000, 10000, 30000, 60000};
@@ -27,13 +26,13 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
HidMouseJigglerModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#endif
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 2, AlignLeft, AlignTop, "Mouse Jiggler");
@@ -146,13 +145,7 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler);
with_view_model(
hid_mouse_jiggler->view,
HidMouseJigglerModel * model,
{
model->transport = hid->transport;
model->interval_idx = 2;
},
true);
hid_mouse_jiggler->view, HidMouseJigglerModel * model, { model->interval_idx = 2; }, true);
return hid_mouse_jiggler;
}

View File

@@ -22,7 +22,6 @@ typedef struct {
bool ok_pressed;
bool connected;
bool back_pressed;
HidTransport transport;
} HidMovieModel;
static void hid_movie_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
@@ -43,13 +42,13 @@ static void hid_movie_draw_callback(Canvas* canvas, void* context) {
HidMovieModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#endif
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Movie");
@@ -60,9 +59,9 @@ static void hid_movie_draw_callback(Canvas* canvas, void* context) {
// Up
if(model->up_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6);
@@ -70,9 +69,9 @@ static void hid_movie_draw_callback(Canvas* canvas, void* context) {
// Down
if(model->down_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6);
@@ -80,9 +79,9 @@ static void hid_movie_draw_callback(Canvas* canvas, void* context) {
// Left
if(model->left_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
hid_movie_draw_arrow(canvas, 65, 28, CanvasDirectionRightToLeft);
@@ -91,9 +90,9 @@ static void hid_movie_draw_callback(Canvas* canvas, void* context) {
// Right
if(model->right_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
hid_movie_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight);
@@ -102,9 +101,9 @@ static void hid_movie_draw_callback(Canvas* canvas, void* context) {
// Ok
if(model->ok_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
hid_movie_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight);
@@ -114,9 +113,9 @@ static void hid_movie_draw_callback(Canvas* canvas, void* context) {
// Exit
if(model->back_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10);
@@ -211,9 +210,6 @@ HidMovie* hid_movie_alloc(Hid* hid) {
view_set_draw_callback(hid_movie->view, hid_movie_draw_callback);
view_set_input_callback(hid_movie->view, hid_movie_input_callback);
with_view_model(
hid_movie->view, HidMovieModel * model, { model->transport = hid->transport; }, true);
return hid_movie;
}

View File

@@ -22,7 +22,6 @@ typedef struct {
bool ok_pressed;
bool connected;
bool back_pressed;
HidTransport transport;
} HidMusicMacosModel;
static void hid_music_macos_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
@@ -43,13 +42,13 @@ static void hid_music_macos_draw_callback(Canvas* canvas, void* context) {
HidMusicMacosModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#endif
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Music");
@@ -60,9 +59,9 @@ static void hid_music_macos_draw_callback(Canvas* canvas, void* context) {
// Up
if(model->up_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6);
@@ -70,9 +69,9 @@ static void hid_music_macos_draw_callback(Canvas* canvas, void* context) {
// Down
if(model->down_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6);
@@ -80,9 +79,9 @@ static void hid_music_macos_draw_callback(Canvas* canvas, void* context) {
// Left
if(model->left_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
hid_music_macos_draw_arrow(canvas, 67, 28, CanvasDirectionRightToLeft);
@@ -92,9 +91,9 @@ static void hid_music_macos_draw_callback(Canvas* canvas, void* context) {
// Right
if(model->right_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
hid_music_macos_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight);
@@ -104,9 +103,9 @@ static void hid_music_macos_draw_callback(Canvas* canvas, void* context) {
// Ok
if(model->ok_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
hid_music_macos_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight);
@@ -116,9 +115,9 @@ static void hid_music_macos_draw_callback(Canvas* canvas, void* context) {
// Exit
if(model->back_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10);
@@ -215,12 +214,6 @@ HidMusicMacos* hid_music_macos_alloc(Hid* hid) {
view_set_draw_callback(hid_music_macos->view, hid_music_macos_draw_callback);
view_set_input_callback(hid_music_macos->view, hid_music_macos_input_callback);
with_view_model(
hid_music_macos->view,
HidMusicMacosModel * model,
{ model->transport = hid->transport; },
true);
return hid_music_macos;
}

View File

@@ -23,7 +23,6 @@ typedef struct {
bool back_pressed;
bool connected;
char key_string[5];
HidTransport transport;
} HidNumpadModel;
typedef struct {
@@ -135,27 +134,28 @@ static void hid_numpad_draw_callback(Canvas* canvas, void* context) {
// Header
canvas_set_font(canvas, FontPrimary);
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
elements_multiline_text_aligned(
canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection...");
}
elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad");
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad");
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
elements_multiline_text_aligned(
canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection...");
}
elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad");
#else
elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad");
#endif
canvas_draw_icon(canvas, 3, 18, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit");
if(!model->connected && (model->transport == HidTransportBle)) {
#ifdef HID_TRANSPORT_BLE
if(!model->connected) {
return;
}
#endif
canvas_set_font(canvas, FontKeyboard);
uint8_t initY = 0; // = model->y == 0 ? 0 : 1;
@@ -289,13 +289,7 @@ HidNumpad* hid_numpad_alloc(Hid* bt_hid) {
view_set_input_callback(hid_numpad->view, hid_numpad_input_callback);
with_view_model(
hid_numpad->view,
HidNumpadModel * model,
{
model->transport = bt_hid->transport;
model->y = 0;
},
true);
hid_numpad->view, HidNumpadModel * model, { model->y = 0; }, true);
return hid_numpad;
}

View File

@@ -32,7 +32,6 @@ typedef struct {
size_t osIndex;
size_t appIndex;
size_t window_position;
HidTransport transport;
PushToTalkActionCallback callback_trigger_mute;
PushToTalkActionCallback callback_trigger_camera;
PushToTalkActionCallback callback_trigger_hand;
@@ -575,13 +574,13 @@ static void hid_ptt_draw_callback(Canvas* canvas, void* context) {
// Header
canvas_set_font(canvas, FontPrimary);
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#endif
// OS and App labels
canvas_set_font(canvas, FontSecondary);
@@ -816,7 +815,6 @@ HidPushToTalk* hid_ptt_alloc(Hid* hid) {
hid_ptt->view,
HidPushToTalkModel * model,
{
model->transport = hid->transport;
model->muted = true; // assume we're muted
model->os = furi_string_alloc();
model->app = furi_string_alloc();

View File

@@ -20,7 +20,6 @@ typedef struct {
bool connected;
bool is_cursor_set;
bool back_mouse_pressed;
HidTransport transport;
} HidTikTokModel;
static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
@@ -28,13 +27,13 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
HidTikTokModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#ifdef HID_TRANSPORT_BLE
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
#endif
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok /");
@@ -46,9 +45,9 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
// Pause
if(model->back_mouse_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 113, 37, &I_Pause_icon_9x9);
@@ -56,9 +55,9 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
// Up
if(model->up_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 80, 8, &I_Arr_up_7x9);
@@ -66,9 +65,9 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
// Down
if(model->down_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 80, 40, &I_Arr_dwn_7x9);
@@ -76,9 +75,9 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
// Left
if(model->left_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 64, 25, &I_Voldwn_6x6);
@@ -86,9 +85,9 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
// Right
if(model->right_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 95, 25, &I_Volup_8x6);
@@ -96,9 +95,9 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
// Ok
if(model->ok_pressed) {
canvas_set_bitmap_mode(canvas, 1);
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
canvas_set_bitmap_mode(canvas, 0);
canvas_set_bitmap_mode(canvas, false);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 78, 25, &I_Like_def_11x9);
@@ -234,9 +233,6 @@ HidTikTok* hid_tiktok_alloc(Hid* bt_hid) {
view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback);
view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback);
with_view_model(
hid_tiktok->view, HidTikTokModel * model, { model->transport = bt_hid->transport; }, true);
return hid_tiktok;
}

View File

@@ -15,6 +15,27 @@ App(
fap_category="assets",
)
App(
appid="js_cli",
targets=["f7"],
apptype=FlipperAppType.PLUGIN,
entry_point="js_cli_plugin_ep",
requires=["cli"],
sources=[
"*.c*",
"!modules",
"modules/js_flipper.c",
],
)
App(
appid="js_app_start",
apptype=FlipperAppType.STARTUP,
entry_point="js_app_on_system_start",
sources=["js_start.c"],
order=160,
)
App(
appid="js_dialog",
apptype=FlipperAppType.PLUGIN,

View File

@@ -4,6 +4,7 @@
#include "js_app_i.h"
#include <toolbox/path.h>
#include <assets_icons.h>
#include <cli/cli.h>
#define TAG "JS app"
@@ -128,4 +129,90 @@ int32_t js_app(void* arg) {
js_app_free(app);
return 0;
} //-V773
} //-V773
typedef struct {
Cli* cli;
FuriSemaphore* exit_sem;
} JsCliContext;
static void js_cli_print(JsCliContext* ctx, const char* msg) {
cli_write(ctx->cli, (uint8_t*)msg, strlen(msg));
}
static void js_cli_exit(JsCliContext* ctx) {
furi_check(furi_semaphore_release(ctx->exit_sem) == FuriStatusOk);
}
static void js_cli_callback(JsThreadEvent event, const char* msg, void* context) {
JsCliContext* ctx = context;
switch(event) {
case JsThreadEventError:
js_cli_print(ctx, "---- ERROR ----\r\n");
js_cli_print(ctx, msg);
js_cli_print(ctx, "\r\n");
break;
case JsThreadEventErrorTrace:
js_cli_print(ctx, "Trace:\r\n");
js_cli_print(ctx, msg);
js_cli_print(ctx, "\r\n");
js_cli_exit(ctx); // Exit when an error occurs
break;
case JsThreadEventPrint:
js_cli_print(ctx, msg);
js_cli_print(ctx, "\r\n");
break;
case JsThreadEventDone:
js_cli_print(ctx, "Script done!\r\n");
js_cli_exit(ctx);
break;
}
}
void js_cli_execute(Cli* cli, FuriString* args, void* context) {
UNUSED(context);
const char* path = furi_string_get_cstr(args);
Storage* storage = furi_record_open(RECORD_STORAGE);
do {
if(furi_string_size(args) == 0) {
printf("Usage:\r\njs <path>\r\n");
break;
}
if(!storage_file_exists(storage, path)) {
printf("Can not open file %s\r\n", path);
break;
}
JsCliContext ctx = {.cli = cli};
ctx.exit_sem = furi_semaphore_alloc(1, 0);
printf("Running script %s, press CTRL+C to stop\r\n", path);
JsThread* js_thread = js_thread_run(path, js_cli_callback, &ctx);
while(furi_semaphore_acquire(ctx.exit_sem, 100) != FuriStatusOk) {
if(cli_cmd_interrupt_received(cli)) break;
}
js_thread_stop(js_thread);
furi_semaphore_free(ctx.exit_sem);
} while(false);
furi_record_close(RECORD_STORAGE);
}
#include <flipper_application/flipper_application.h>
static const FlipperAppPluginDescriptor plugin_descriptor = {
.appid = "js_cli",
.ep_api_version = 1,
.entry_point = &js_cli_execute,
};
const FlipperAppPluginDescriptor* js_cli_plugin_ep(void) {
return &plugin_descriptor;
}

View File

@@ -5,6 +5,9 @@
#define TAG "JS modules"
// Absolute path is used to make possible plugin load from CLI
#define MODULES_PATH "/ext/apps_data/js_app/plugins"
typedef struct {
JsModeConstructor create;
JsModeDestructor destroy;
@@ -81,7 +84,7 @@ mjs_val_t js_module_require(JsModules* modules, const char* name, size_t name_le
// External module load
if(!module_found) {
FuriString* module_path = furi_string_alloc();
furi_string_printf(module_path, "%s/js_%s.fal", APP_DATA_PATH("plugins"), name);
furi_string_printf(module_path, "%s/js_%s.fal", MODULES_PATH, name);
FURI_LOG_I(TAG, "Loading external module %s", furi_string_get_cstr(module_path));
do {
uint32_t plugin_cnt_last = plugin_manager_get_count(modules->plugin_manager);

View File

@@ -0,0 +1,11 @@
#include <cli/cli_i.h>
static void js_cli_execute_wrapper(Cli* cli, FuriString* args, void* context) {
cli_plugin_wrapper("js_cli", 1, cli, args, context);
}
void js_app_on_system_start(void) {
Cli* cli = furi_record_open(RECORD_CLI);
cli_add_command(cli, "js", CliCommandFlagDefault, js_cli_execute_wrapper, NULL);
furi_record_close(RECORD_CLI);
}

View File

@@ -45,12 +45,12 @@ static void js_print(struct mjs* mjs) {
FuriString* msg_str = furi_string_alloc();
js_str_print(msg_str, mjs);
printf("%s\r\n", furi_string_get_cstr(msg_str));
JsThread* worker = mjs_get_context(mjs);
furi_assert(worker);
if(worker->app_callback) {
worker->app_callback(JsThreadEventPrint, furi_string_get_cstr(msg_str), worker->context);
} else {
FURI_LOG_D(TAG, "%s\r\n", furi_string_get_cstr(msg_str));
}
furi_string_free(msg_str);

View File

@@ -79,15 +79,7 @@ Up to 5 keys can be hold simultaneously.
| HOLD | Special key or single character | Press and hold key until RELEASE command |
| RELEASE | Special key or single character | Release key |
## Wait for button press
Will wait indefinitely for a button to be pressed
| Command | Parameters | Notes |
| --------------------- | ------------ | --------------------------------------------------------------------- |
| WAIT_FOR_BUTTON_PRESS | None | Will wait for the user to press a button to continue script execution |
### String
## String
| Command | Parameters | Notes |
| ------- | ----------- | ----------------- |
@@ -126,7 +118,54 @@ Send [SysRq command](https://en.wikipedia.org/wiki/Magic_SysRq_key)
| ------- | ---------------- | ----- |
| SYSRQ | Single character | |
### USB device ID
## Media keys
Some Media/Consumer Control keys can be pressed with "MEDIA" command
| Command | Parameters | Notes |
| ------- | ------------------------- | ----- |
| MEDIA | Media key, see list below | |
| Key name | Notes |
| ----------------- | ----------------------------- |
| POWER | |
| REBOOT | |
| SLEEP | |
| LOGOFF | |
| EXIT | |
| HOME | |
| BACK | |
| FORWARD | |
| REFRESH | |
| SNAPSHOT | Take photo in a camera app |
| PLAY | |
| PAUSE | |
| PLAY_PAUSE | |
| NEXT_TRACK | |
| PREV_TRACK | |
| STOP | |
| EJECT | |
| MUTE | |
| VOLUME_UP | |
| VOLUME_DOWN | |
| FN | Fn/Globe key on Mac keyboard |
| BRIGHT_UP | Increase display brightness |
| BRIGHT_DOWN | Decrease display brightness |
## Fn/Globe key commands (Mac/iPad)
| Command | Parameters | Notes |
| ------- | ------------------------------- | ----- |
| GLOBE | Special key or single character | |
## Wait for button press
Will wait indefinitely for a button to be pressed
| Command | Parameters | Notes |
| --------------------- | ------------ | --------------------------------------------------------------------- |
| WAIT_FOR_BUTTON_PRESS | None | Will wait for the user to press a button to continue script execution |
## USB device ID
You can set the custom ID of the Flipper USB HID device. ID command should be in the **first line** of script, it is executed before script run.

View File

@@ -40,6 +40,7 @@ static void flipper_print_version(const char* target, const Version* version) {
#include <notification/notification_app.h>
#include <dolphin/helpers/dolphin_state.h>
#include <applications/main/u2f/u2f_data.h>
#include <applications/main/infrared/infrared_app.h>
#include <expansion/expansion_settings_filename.h>
#include <applications/main/archive/helpers/archive_favorites.h>
#include <momentum/namespoof.h>
@@ -72,6 +73,7 @@ void flipper_migrate_files() {
{INT_PATH(".bt.keys"), BT_KEYS_STORAGE_PATH, true},
{INT_PATH(".expansion.settings"), EXPANSION_SETTINGS_PATH, true},
{INT_PATH(".notification.settings"), NOTIFICATION_SETTINGS_PATH, true},
{INT_PATH(".infrared.settings"), INFRARED_SETTINGS_PATH, true},
// Ext -> Int
{CFG_PATH("desktop.settings"), DESKTOP_SETTINGS_PATH, true},
};

View File

@@ -1,3 +1,5 @@
#pragma once
#include <furi_hal_infrared.h>
#include <infrared.h>
#include <stdint.h>

View File

@@ -225,7 +225,7 @@ static bool mf_desfire_poller_detect(NfcGenericEvent event, void* context) {
bool protocol_detected = false;
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
MfDesfireKeyVersion key_version = 0;
MfDesfireKeyVersion key_version = {0};
MfDesfireError error = mf_desfire_poller_read_key_version(instance, 0, &key_version);
protocol_detected = (error == MfDesfireErrorNone);
}

View File

@@ -143,8 +143,8 @@ MfDesfireError mf_desfire_poller_read_key_version(
MfDesfirePoller* instance,
uint8_t key_num,
MfDesfireKeyVersion* data) {
furi_assert(instance);
furi_assert(data);
furi_check(instance);
furi_check(data);
bit_buffer_set_size_bytes(instance->input_buffer, sizeof(uint8_t) * 2);
bit_buffer_set_byte(instance->input_buffer, 0, MF_DESFIRE_CMD_GET_KEY_VERSION);

View File

@@ -13,7 +13,12 @@ typedef struct {
uint32_t timestamp;
} SavedStructHeader;
bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, uint8_t version) {
bool saved_struct_save(
const char* path,
const void* data,
size_t size,
uint8_t magic,
uint8_t version) {
furi_check(path);
furi_check(data);
furi_check(size);
@@ -35,7 +40,7 @@ bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic,
if(result) {
// Calculate checksum
uint8_t checksum = 0;
uint8_t* source = data;
const uint8_t* source = data;
for(size_t i = 0; i < size; i++) {
checksum += source[i];
}
@@ -63,6 +68,10 @@ bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic,
}
bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, uint8_t version) {
furi_check(path);
furi_check(data);
furi_check(size);
FURI_LOG_I(TAG, "Loading \"%s\"", path);
SavedStructHeader header;
@@ -131,13 +140,12 @@ bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic,
return result;
}
bool saved_struct_get_payload_size(
bool saved_struct_get_metadata(
const char* path,
uint8_t magic,
uint8_t version,
uint8_t* magic,
uint8_t* version,
size_t* payload_size) {
furi_check(path);
furi_check(payload_size);
SavedStructHeader header;
Storage* storage = furi_record_open(RECORD_STORAGE);
@@ -151,26 +159,22 @@ bool saved_struct_get_payload_size(
break;
}
size_t bytes_count = storage_file_read(file, &header, sizeof(SavedStructHeader));
if(bytes_count != sizeof(SavedStructHeader)) {
if(storage_file_read(file, &header, sizeof(SavedStructHeader)) !=
sizeof(SavedStructHeader)) {
FURI_LOG_E(TAG, "Failed to read header");
break;
}
if((header.magic != magic) || (header.version != version)) {
FURI_LOG_E(
TAG,
"Magic(%d != %d) or Version(%d != %d) mismatch of file \"%s\"",
header.magic,
magic,
header.version,
version,
path);
break;
if(magic) {
*magic = header.magic;
}
if(version) {
*version = header.version;
}
if(payload_size) {
uint64_t file_size = storage_file_size(file);
*payload_size = file_size - sizeof(SavedStructHeader);
}
uint64_t file_size = storage_file_size(file);
*payload_size = file_size - sizeof(SavedStructHeader);
result = true;
} while(false);

View File

@@ -1,3 +1,8 @@
/**
* @file saved_struct.h
* @brief SavedStruct - data serialization/de-serialization
*
*/
#pragma once
#include <stdint.h>
@@ -8,14 +13,50 @@
extern "C" {
#endif
/** Load data from the file in saved structure format
*
* @param[in] path The path to the file
* @param[out] data Pointer to the memory where to load data
* @param[in] size The size of the data
* @param[in] magic The magic to embed into metadata
* @param[in] version The version to embed into metadata
*
* @return true on success, false otherwise
*/
bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, uint8_t version);
bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, uint8_t version);
bool saved_struct_get_payload_size(
/** Save data in saved structure format
*
* @param[in] path The path to the file
* @param[in] data Pointer to the memory where data
* @param[in] size The size of the data
* @param[in] magic The magic to embed into metadata
* @param[in] version The version to embed into metadata
*
* @return true on success, false otherwise
*/
bool saved_struct_save(
const char* path,
const void* data,
size_t size,
uint8_t magic,
uint8_t version,
uint8_t version);
/** Get SavedStructure file metadata
*
* @param[in] path The path to the file
* @param[out] magic Pointer to store magic or NULL if you don't need it
* @param[out] version Pointer to store version or NULL if you don't need
* it
* @param[out] payload_size Pointer to store payload size or NULL if you don't
* need it
*
* @return true on success, false otherwise
*/
bool saved_struct_get_metadata(
const char* path,
uint8_t* magic,
uint8_t* version,
size_t* payload_size);
#ifdef __cplusplus

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,60.0,,
Version,+,60.3,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,,
@@ -1205,6 +1205,7 @@ Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode"
Function,+,furi_hal_gpio_remove_int_callback,void,const GpioPin*
Function,+,furi_hal_hid_consumer_key_press,_Bool,uint16_t
Function,+,furi_hal_hid_consumer_key_release,_Bool,uint16_t
Function,+,furi_hal_hid_consumer_key_release_all,_Bool,
Function,+,furi_hal_hid_get_led_state,uint8_t,
Function,+,furi_hal_hid_is_connected,_Bool,
Function,+,furi_hal_hid_kb_press,_Bool,uint16_t
@@ -2302,9 +2303,9 @@ Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback
Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t"
Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*"
Function,-,rpmatch,int,const char*
Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*"
Function,+,saved_struct_get_metadata,_Bool,"const char*, uint8_t*, uint8_t*, size_t*"
Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t"
Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t"
Function,+,saved_struct_save,_Bool,"const char*, const void*, size_t, uint8_t, uint8_t"
Function,-,scalbln,double,"double, long int"
Function,-,scalblnf,float,"float, long int"
Function,-,scalblnl,long double,"long double, long"
1 entry status name type params
2 Version + 60.0 60.3
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
1205 Function + furi_hal_gpio_remove_int_callback void const GpioPin*
1206 Function + furi_hal_hid_consumer_key_press _Bool uint16_t
1207 Function + furi_hal_hid_consumer_key_release _Bool uint16_t
1208 Function + furi_hal_hid_consumer_key_release_all _Bool
1209 Function + furi_hal_hid_get_led_state uint8_t
1210 Function + furi_hal_hid_is_connected _Bool
1211 Function + furi_hal_hid_kb_press _Bool uint16_t
2303 Function + rpc_system_app_set_error_code void RpcAppSystem*, uint32_t
2304 Function + rpc_system_app_set_error_text void RpcAppSystem*, const char*
2305 Function - rpmatch int const char*
2306 Function + saved_struct_get_payload_size saved_struct_get_metadata _Bool const char*, uint8_t, uint8_t, size_t* const char*, uint8_t*, uint8_t*, size_t*
2307 Function + saved_struct_load _Bool const char*, void*, size_t, uint8_t, uint8_t
2308 Function + saved_struct_save _Bool const char*, void*, size_t, uint8_t, uint8_t const char*, const void*, size_t, uint8_t, uint8_t
2309 Function - scalbln double double, long int
2310 Function - scalblnf float float, long int
2311 Function - scalblnl long double long double, long

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,60.0,,
Version,+,60.3,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/main/archive/helpers/archive_helpers_ext.h,,
Header,+,applications/main/subghz/subghz_fap.h,,
@@ -784,7 +784,7 @@ Function,+,bt_profile_start,FuriHalBleProfileBase*,"Bt*, const FuriHalBleProfile
Function,+,bt_remote_rssi,_Bool,"Bt*, uint8_t*"
Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*"
Function,-,bt_settings_load,_Bool,BtSettings*
Function,+,bt_settings_save,_Bool,BtSettings*
Function,+,bt_settings_save,_Bool,const BtSettings*
Function,+,buffered_file_stream_alloc,Stream*,Storage*
Function,+,buffered_file_stream_close,_Bool,Stream*
Function,+,buffered_file_stream_get_error,FS_Error,Stream*
@@ -1036,7 +1036,7 @@ Function,+,expansion_get_settings,ExpansionSettings*,Expansion*
Function,+,expansion_is_connected,_Bool,Expansion*
Function,+,expansion_set_listen_serial,void,"Expansion*, FuriHalSerialId"
Function,-,expansion_settings_load,_Bool,ExpansionSettings*
Function,+,expansion_settings_save,_Bool,ExpansionSettings*
Function,+,expansion_settings_save,_Bool,const ExpansionSettings*
Function,-,expf,float,float
Function,-,expl,long double,long double
Function,-,explicit_bzero,void,"void*, size_t"
@@ -1358,6 +1358,7 @@ Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode"
Function,+,furi_hal_gpio_remove_int_callback,void,const GpioPin*
Function,+,furi_hal_hid_consumer_key_press,_Bool,uint16_t
Function,+,furi_hal_hid_consumer_key_release,_Bool,uint16_t
Function,+,furi_hal_hid_consumer_key_release_all,_Bool,
Function,+,furi_hal_hid_get_led_state,uint8_t,
Function,+,furi_hal_hid_is_connected,_Bool,
Function,+,furi_hal_hid_kb_press,_Bool,uint16_t
@@ -1408,12 +1409,9 @@ Function,+,furi_hal_infrared_async_tx_set_signal_sent_isr_callback,void,"FuriHal
Function,+,furi_hal_infrared_async_tx_start,void,"uint32_t, float"
Function,+,furi_hal_infrared_async_tx_stop,void,
Function,+,furi_hal_infrared_async_tx_wait_termination,void,
Function,+,furi_hal_infrared_get_debug_out_status,_Bool,
Function,+,furi_hal_infrared_is_auto_detect_enabled,_Bool,
Function,+,furi_hal_infrared_detect_tx_output,FuriHalInfraredTxPin,
Function,+,furi_hal_infrared_is_busy,_Bool,
Function,+,furi_hal_infrared_is_external_connected,_Bool,
Function,+,furi_hal_infrared_set_auto_detect,void,_Bool
Function,+,furi_hal_infrared_set_debug_out,void,_Bool
Function,+,furi_hal_infrared_set_tx_output,void,FuriHalInfraredTxPin
Function,-,furi_hal_init,void,
Function,-,furi_hal_init_early,void,
Function,-,furi_hal_interrupt_init,void,
@@ -3006,9 +3004,9 @@ Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t"
Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*"
Function,-,rpmatch,int,const char*
Function,-,run_with_default_app,void,const char*
Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*"
Function,+,saved_struct_get_metadata,_Bool,"const char*, uint8_t*, uint8_t*, size_t*"
Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t"
Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t"
Function,+,saved_struct_save,_Bool,"const char*, const void*, size_t, uint8_t, uint8_t"
Function,-,scalbln,double,"double, long int"
Function,-,scalblnf,float,"float, long int"
Function,-,scalblnl,long double,"long double, long"
1 entry status name type params
2 Version + 60.0 60.3
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/main/archive/helpers/archive_helpers_ext.h
5 Header + applications/main/subghz/subghz_fap.h
784 Function + bt_remote_rssi _Bool Bt*, uint8_t*
785 Function + bt_set_status_changed_callback void Bt*, BtStatusChangedCallback, void*
786 Function - bt_settings_load _Bool BtSettings*
787 Function + bt_settings_save _Bool BtSettings* const BtSettings*
788 Function + buffered_file_stream_alloc Stream* Storage*
789 Function + buffered_file_stream_close _Bool Stream*
790 Function + buffered_file_stream_get_error FS_Error Stream*
1036 Function + expansion_is_connected _Bool Expansion*
1037 Function + expansion_set_listen_serial void Expansion*, FuriHalSerialId
1038 Function - expansion_settings_load _Bool ExpansionSettings*
1039 Function + expansion_settings_save _Bool ExpansionSettings* const ExpansionSettings*
1040 Function - expf float float
1041 Function - expl long double long double
1042 Function - explicit_bzero void void*, size_t
1358 Function + furi_hal_gpio_remove_int_callback void const GpioPin*
1359 Function + furi_hal_hid_consumer_key_press _Bool uint16_t
1360 Function + furi_hal_hid_consumer_key_release _Bool uint16_t
1361 Function + furi_hal_hid_consumer_key_release_all _Bool
1362 Function + furi_hal_hid_get_led_state uint8_t
1363 Function + furi_hal_hid_is_connected _Bool
1364 Function + furi_hal_hid_kb_press _Bool uint16_t
1409 Function + furi_hal_infrared_async_tx_start void uint32_t, float
1410 Function + furi_hal_infrared_async_tx_stop void
1411 Function + furi_hal_infrared_async_tx_wait_termination void
1412 Function + furi_hal_infrared_get_debug_out_status furi_hal_infrared_detect_tx_output _Bool FuriHalInfraredTxPin
Function + furi_hal_infrared_is_auto_detect_enabled _Bool
1413 Function + furi_hal_infrared_is_busy _Bool
1414 Function + furi_hal_infrared_is_external_connected furi_hal_infrared_set_tx_output _Bool void FuriHalInfraredTxPin
Function + furi_hal_infrared_set_auto_detect void _Bool
Function + furi_hal_infrared_set_debug_out void _Bool
1415 Function - furi_hal_init void
1416 Function - furi_hal_init_early void
1417 Function - furi_hal_interrupt_init void
3004 Function + rpc_system_app_set_error_text void RpcAppSystem*, const char*
3005 Function - rpmatch int const char*
3006 Function - run_with_default_app void const char*
3007 Function + saved_struct_get_payload_size saved_struct_get_metadata _Bool const char*, uint8_t, uint8_t, size_t* const char*, uint8_t*, uint8_t*, size_t*
3008 Function + saved_struct_load _Bool const char*, void*, size_t, uint8_t, uint8_t
3009 Function + saved_struct_save _Bool const char*, void*, size_t, uint8_t, uint8_t const char*, const void*, size_t, uint8_t, uint8_t
3010 Function - scalbln double double, long int
3011 Function - scalblnf float float, long int
3012 Function - scalblnl long double long double, long

View File

@@ -1,8 +1,8 @@
#include <furi_hal_infrared.h>
#include <furi_hal_interrupt.h>
#include <furi_hal_resources.h>
#include <furi_hal_cortex.h>
#include <furi_hal_bus.h>
#include <furi_hal_power.h>
#include <stm32wbxx_ll_tim.h>
#include <stm32wbxx_ll_dma.h>
@@ -76,11 +76,15 @@ typedef enum {
InfraredStateMAX,
} InfraredState;
static FuriHalInfraredTxPin infrared_tx_output = FuriHalInfraredTxPinInternal;
static volatile InfraredState furi_hal_infrared_state = InfraredStateIdle;
static InfraredTimTx infrared_tim_tx;
static InfraredTimRx infrared_tim_rx;
static bool infrared_external_output;
static bool auto_detect;
static const GpioPin* infrared_tx_pins[FuriHalInfraredTxPinMax] = {
[FuriHalInfraredTxPinInternal] = &gpio_infrared_tx,
[FuriHalInfraredTxPinExtPA7] = &gpio_ext_pa7,
};
static void furi_hal_infrared_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift);
static void furi_hal_infrared_async_tx_free_resources(void);
@@ -91,14 +95,6 @@ static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void);
static void furi_hal_infrared_tx_dma_polarity_isr();
static void furi_hal_infrared_tx_dma_isr();
void furi_hal_infrared_set_debug_out(bool enable) {
infrared_external_output = enable;
}
bool furi_hal_infrared_get_debug_out_status(void) {
return infrared_external_output;
}
static void furi_hal_infrared_tim_rx_isr(void* context) {
UNUSED(context);
@@ -357,18 +353,8 @@ static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cyc
LL_TIM_SetAutoReload(
INFRARED_DMA_TIMER,
__LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(INFRARED_DMA_TIMER), freq));
if(infrared_external_output) {
LL_TIM_OC_SetCompareCH1(
INFRARED_DMA_TIMER,
((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle)));
LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1);
/* LL_TIM_OCMODE_PWM2 set by DMA */
LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_FORCED_INACTIVE);
LL_TIM_OC_SetPolarity(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH);
LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N);
LL_TIM_DisableIT_CC1(INFRARED_DMA_TIMER);
} else {
if(infrared_tx_output == FuriHalInfraredTxPinInternal) {
LL_TIM_OC_SetCompareCH3(
INFRARED_DMA_TIMER,
((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle)));
@@ -379,7 +365,19 @@ static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cyc
LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3);
LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3N);
LL_TIM_DisableIT_CC3(INFRARED_DMA_TIMER);
} else if(infrared_tx_output == FuriHalInfraredTxPinExtPA7) {
LL_TIM_OC_SetCompareCH1(
INFRARED_DMA_TIMER,
((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle)));
LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1);
/* LL_TIM_OCMODE_PWM2 set by DMA */
LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_FORCED_INACTIVE);
LL_TIM_OC_SetPolarity(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH);
LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N);
LL_TIM_DisableIT_CC1(INFRARED_DMA_TIMER);
}
LL_TIM_DisableMasterSlaveMode(INFRARED_DMA_TIMER);
LL_TIM_EnableAllOutputs(INFRARED_DMA_TIMER);
LL_TIM_DisableIT_UPDATE(INFRARED_DMA_TIMER);
@@ -388,11 +386,13 @@ static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cyc
static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) {
LL_DMA_InitTypeDef dma_config = {0};
if(infrared_external_output) {
dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (INFRARED_DMA_TIMER->CCMR1);
} else {
dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (INFRARED_DMA_TIMER->CCMR2);
if(infrared_tx_output == FuriHalInfraredTxPinInternal) {
dma_config.PeriphOrM2MSrcAddress = (uint32_t)(&(INFRARED_DMA_TIMER->CCMR2));
} else if(infrared_tx_output == FuriHalInfraredTxPinExtPA7) {
dma_config.PeriphOrM2MSrcAddress = (uint32_t)(&(INFRARED_DMA_TIMER->CCMR1));
}
dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL;
dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
dma_config.Mode = LL_DMA_MODE_NORMAL;
@@ -589,11 +589,8 @@ static void furi_hal_infrared_async_tx_free_resources(void) {
(furi_hal_infrared_state == InfraredStateIdle) ||
(furi_hal_infrared_state == InfraredStateAsyncTxStopped));
if(infrared_external_output) {
furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
} else {
furi_hal_gpio_init(&gpio_infrared_tx, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
}
furi_hal_gpio_init(
infrared_tx_pins[infrared_tx_output], GpioModeAnalog, GpioPullDown, GpioSpeedLow);
furi_hal_interrupt_set_isr(INFRARED_DMA_CH1_IRQ, NULL, NULL);
furi_hal_interrupt_set_isr(INFRARED_DMA_CH2_IRQ, NULL, NULL);
@@ -655,37 +652,10 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) {
LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* DMA -> TIMx_RCR */
furi_delay_us(5);
if(auto_detect) {
infrared_external_output = furi_hal_infrared_is_external_connected();
if(infrared_external_output) {
if(!furi_hal_power_is_otg_enabled()) {
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
}
} else if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
}
if(infrared_external_output) {
LL_GPIO_ResetOutputPin(
gpio_ext_pa7.port, gpio_ext_pa7.pin); /* when disable it prevents false pulse */
furi_hal_gpio_init_ex(
&gpio_ext_pa7, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1);
} else {
LL_GPIO_ResetOutputPin(
gpio_infrared_tx.port,
gpio_infrared_tx.pin); /* when disable it prevents false pulse */
furi_hal_gpio_init_ex(
&gpio_infrared_tx,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedHigh,
GpioAltFn1TIM1);
}
const GpioPin* tx_gpio = infrared_tx_pins[infrared_tx_output];
LL_GPIO_ResetOutputPin(tx_gpio->port, tx_gpio->pin); /* when disable it prevents false pulse */
furi_hal_gpio_init_ex(
tx_gpio, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1);
FURI_CRITICAL_ENTER();
LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* TIMx_RCR -> Repetition counter */
@@ -731,18 +701,22 @@ void furi_hal_infrared_async_tx_set_signal_sent_isr_callback(
infrared_tim_tx.signal_sent_context = context;
}
bool furi_hal_infrared_is_external_connected(void) {
furi_hal_gpio_init(&gpio_ext_pa7, GpioModeInput, GpioPullUp, GpioSpeedHigh);
furi_delay_ms(1);
bool is_external_connected = !furi_hal_gpio_read(&gpio_ext_pa7);
furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
return is_external_connected;
FuriHalInfraredTxPin furi_hal_infrared_detect_tx_output(void) {
for(FuriHalInfraredTxPin pin = FuriHalInfraredTxPinInternal + 1; //-V1008
pin < FuriHalInfraredTxPinMax;
++pin) {
const GpioPin* gpio = infrared_tx_pins[pin];
furi_hal_gpio_init(gpio, GpioModeInput, GpioPullUp, GpioSpeedLow);
furi_hal_cortex_delay_us(1000U);
const bool level = furi_hal_gpio_read(gpio);
furi_hal_gpio_init(gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
if(!level) return pin;
}
return FuriHalInfraredTxPinInternal;
}
void furi_hal_infrared_set_auto_detect(bool enable) {
auto_detect = enable;
void furi_hal_infrared_set_tx_output(FuriHalInfraredTxPin tx_pin) {
furi_check(tx_pin < FuriHalInfraredTxPinMax);
infrared_tx_output = tx_pin;
}
bool furi_hal_infrared_is_auto_detect_enabled(void) {
return auto_detect;
}

View File

@@ -351,6 +351,13 @@ bool furi_hal_hid_consumer_key_release(uint16_t button) {
return hid_send_report(ReportIdConsumer);
}
bool furi_hal_hid_consumer_key_release_all(void) {
for(uint8_t key_nb = 0; key_nb < HID_CONSUMER_MAX_KEYS; key_nb++) {
hid_report.consumer.btn[key_nb] = 0;
}
return hid_send_report(ReportIdConsumer);
}
static void* hid_set_string_descr(char* str) {
furi_assert(str);

View File

@@ -16,6 +16,12 @@ extern "C" {
#define INFRARED_MAX_FREQUENCY 56000
#define INFRARED_MIN_FREQUENCY 10000
typedef enum {
FuriHalInfraredTxPinInternal,
FuriHalInfraredTxPinExtPA7,
FuriHalInfraredTxPinMax,
} FuriHalInfraredTxPin;
typedef enum {
FuriHalInfraredTxGetDataStateOk, /**< New data obtained */
FuriHalInfraredTxGetDataStateDone, /**< New data obtained, and this is end of package */
@@ -48,12 +54,6 @@ typedef void (*FuriHalInfraredRxCaptureCallback)(void* ctx, bool level, uint32_t
*/
typedef void (*FuriHalInfraredRxTimeoutCallback)(void* ctx);
// Debug TX pin set
void furi_hal_infrared_set_debug_out(bool enable);
// Debug TX pin get status
bool furi_hal_infrared_get_debug_out_status(void);
/** Initialize INFRARED RX timer to receive interrupts.
*
* It provides interrupts for every RX-signal edge changing with its duration.
@@ -149,23 +149,28 @@ void furi_hal_infrared_async_tx_set_signal_sent_isr_callback(
FuriHalInfraredTxSignalSentISRCallback callback,
void* context);
/** Check if a module (like IR Blaster) is connected to PA7
*
* return true if a module is connected, false otherwise
/** Detect which pin has an external IR module connected.
*
* External IR modules are detected by enabling a weak pull-up
* on supported pins and testing whether the input is still low.
*
* This method works best on modules that employ a FET with a
* strong pull-down or a BJT for driving IR LEDs.
*
* The module MUST pull the input voltage down to at least 0.9V
* or lower in order for it to be detected.
*
* If no module has been detected, FuriHalInfraredTxPinInternal is returned.
*
* @return numeric identifier of the first pin with a module detected.
*/
bool furi_hal_infrared_is_external_connected();
FuriHalInfraredTxPin furi_hal_infrared_detect_tx_output(void);
/** Set auto detect
*
* if enabled, external IR is used automatic if connected, otherwise internal IR is used
*/
void furi_hal_infrared_set_auto_detect(bool enable);
/** Check if auto detect is enabled
*
* @return true if enabled, false otherwise
/** Set which pin will be used to transmit infrared signals.
*
* @param[in] tx_pin pin to be used for signal transmission.
*/
bool furi_hal_infrared_is_auto_detect_enabled();
void furi_hal_infrared_set_tx_output(FuriHalInfraredTxPin tx_pin);
#ifdef __cplusplus
}

View File

@@ -19,6 +19,11 @@ extern "C" {
/** Max number of simultaneously pressed keys (consumer control) */
#define HID_CONSUMER_MAX_KEYS 2
/** OS-specific consumer keys, defined as "Reserved" in HID Usage Tables document */
#define HID_CONSUMER_BRIGHTNESS_INCREMENT 0x006F
#define HID_CONSUMER_BRIGHTNESS_DECREMENT 0x0070
#define HID_CONSUMER_FN_GLOBE 0x029D
#define HID_KEYBOARD_NONE 0x00
/** HID keyboard modifier keys */
@@ -265,6 +270,11 @@ bool furi_hal_hid_consumer_key_press(uint16_t button);
*/
bool furi_hal_hid_consumer_key_release(uint16_t button);
/** Clear all pressed consumer keys and send HID report
*
*/
bool furi_hal_hid_consumer_key_release_all(void);
#ifdef __cplusplus
}
#endif