Merge remote-tracking branch 'origin/dev' into release-candidate
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Thank you for investing your time in contributing to our project!
|
Thank you for investing your time in contributing to our project!
|
||||||
|
|
||||||
Read our [Code of Coduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable.
|
Read our [Code of Conduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable.
|
||||||
|
|
||||||
In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR.
|
In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR.
|
||||||
|
|
||||||
@ -17,12 +17,12 @@ See the [ReadMe](ReadMe.md) to get an overview of the project. Here are some hel
|
|||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
Before writing code and creating PR make sure that it aligns with our mission and guidlines:
|
Before writing code and creating PR make sure that it aligns with our mission and guidelines:
|
||||||
|
|
||||||
- All our devices are intended for research and education.
|
- All our devices are intended for research and education.
|
||||||
- PR that contains code intended to commit crimes is not going to be accepted.
|
- PR that contains code intended to commit crimes is not going to be accepted.
|
||||||
- Your PR must comply with our [Coding Style](CODING_STYLE.md)
|
- Your PR must comply with our [Coding Style](CODING_STYLE.md)
|
||||||
- Your PR must contain code compatiable with project [LICENSE](LICENSE).
|
- Your PR must contain code compatible with project [LICENSE](LICENSE).
|
||||||
- PR will only be merged if it pass CI/CD.
|
- PR will only be merged if it pass CI/CD.
|
||||||
- PR will only be merged if it pass review by code owner.
|
- PR will only be merged if it pass review by code owner.
|
||||||
|
|
||||||
|
|||||||
@ -71,7 +71,7 @@ void WIEGAND::end() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WIEGAND::ReadD0() {
|
void WIEGAND::ReadD0() {
|
||||||
_bitCount++; // Increament bit count for Interrupt connected to D0
|
_bitCount++; // Increment bit count for Interrupt connected to D0
|
||||||
if(_bitCount > 31) // If bit count more than 31, process high bits
|
if(_bitCount > 31) // If bit count more than 31, process high bits
|
||||||
{
|
{
|
||||||
_cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits
|
_cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits
|
||||||
|
|||||||
@ -98,7 +98,7 @@ void bt_debug_app_free(BtDebugApp* app) {
|
|||||||
int32_t bt_debug_app(void* p) {
|
int32_t bt_debug_app(void* p) {
|
||||||
UNUSED(p);
|
UNUSED(p);
|
||||||
if(!furi_hal_bt_is_testing_supported()) {
|
if(!furi_hal_bt_is_testing_supported()) {
|
||||||
FURI_LOG_E(TAG, "Incorrect radio stack: radio testing fetures are absent.");
|
FURI_LOG_E(TAG, "Incorrect radio stack: radio testing features are absent.");
|
||||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack");
|
dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack");
|
||||||
return 255;
|
return 255;
|
||||||
|
|||||||
@ -145,7 +145,7 @@ DisplayTest* display_test_alloc() {
|
|||||||
view_set_previous_callback(view, display_test_previous_callback);
|
view_set_previous_callback(view, display_test_previous_callback);
|
||||||
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view);
|
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view);
|
||||||
|
|
||||||
// Configurtion items
|
// Configuration items
|
||||||
VariableItem* item;
|
VariableItem* item;
|
||||||
instance->config_bias = false;
|
instance->config_bias = false;
|
||||||
instance->config_contrast = 32;
|
instance->config_contrast = 32;
|
||||||
|
|||||||
10
applications/debug/rpc_debug_app/application.fam
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
App(
|
||||||
|
appid="rpc_debug",
|
||||||
|
name="RPC Debug",
|
||||||
|
apptype=FlipperAppType.DEBUG,
|
||||||
|
entry_point="rpc_debug_app",
|
||||||
|
requires=["gui", "rpc_start", "notification"],
|
||||||
|
stack_size=2 * 1024,
|
||||||
|
order=10,
|
||||||
|
fap_category="Debug",
|
||||||
|
)
|
||||||
138
applications/debug/rpc_debug_app/rpc_debug_app.c
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
#include "rpc_debug_app.h"
|
||||||
|
#include <core/log.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static bool rpc_debug_app_custom_event_callback(void* context, uint32_t event) {
|
||||||
|
furi_assert(context);
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rpc_debug_app_back_event_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
return scene_manager_handle_back_event(app->scene_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_debug_app_tick_event_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
scene_manager_handle_tick_event(app->scene_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_debug_app_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
furi_assert(app->rpc);
|
||||||
|
|
||||||
|
if(event == RpcAppEventSessionClose) {
|
||||||
|
scene_manager_stop(app->scene_manager);
|
||||||
|
view_dispatcher_stop(app->view_dispatcher);
|
||||||
|
rpc_system_app_set_callback(app->rpc, NULL, NULL);
|
||||||
|
app->rpc = NULL;
|
||||||
|
} else if(event == RpcAppEventAppExit) {
|
||||||
|
scene_manager_stop(app->scene_manager);
|
||||||
|
view_dispatcher_stop(app->view_dispatcher);
|
||||||
|
rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true);
|
||||||
|
} else {
|
||||||
|
rpc_system_app_confirm(app->rpc, event, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rpc_debug_app_rpc_init_rpc(RpcDebugApp* app, const char* args) {
|
||||||
|
bool ret = false;
|
||||||
|
if(args && strlen(args)) {
|
||||||
|
uint32_t rpc = 0;
|
||||||
|
if(sscanf(args, "RPC %lX", &rpc) == 1) {
|
||||||
|
app->rpc = (RpcAppSystem*)rpc;
|
||||||
|
rpc_system_app_set_callback(app->rpc, rpc_debug_app_rpc_command_callback, app);
|
||||||
|
rpc_system_app_send_started(app->rpc);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RpcDebugApp* rpc_debug_app_alloc() {
|
||||||
|
RpcDebugApp* app = malloc(sizeof(RpcDebugApp));
|
||||||
|
|
||||||
|
app->gui = furi_record_open(RECORD_GUI);
|
||||||
|
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||||
|
app->scene_manager = scene_manager_alloc(&rpc_debug_app_scene_handlers, app);
|
||||||
|
app->view_dispatcher = view_dispatcher_alloc();
|
||||||
|
|
||||||
|
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||||
|
view_dispatcher_set_custom_event_callback(
|
||||||
|
app->view_dispatcher, rpc_debug_app_custom_event_callback);
|
||||||
|
view_dispatcher_set_navigation_event_callback(
|
||||||
|
app->view_dispatcher, rpc_debug_app_back_event_callback);
|
||||||
|
view_dispatcher_set_tick_event_callback(
|
||||||
|
app->view_dispatcher, rpc_debug_app_tick_event_callback, 100);
|
||||||
|
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||||
|
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||||
|
|
||||||
|
app->widget = widget_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RpcDebugAppViewWidget, widget_get_view(app->widget));
|
||||||
|
app->submenu = submenu_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RpcDebugAppViewSubmenu, submenu_get_view(app->submenu));
|
||||||
|
app->text_box = text_box_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RpcDebugAppViewTextBox, text_box_get_view(app->text_box));
|
||||||
|
app->text_input = text_input_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RpcDebugAppViewTextInput, text_input_get_view(app->text_input));
|
||||||
|
app->byte_input = byte_input_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, RpcDebugAppViewByteInput, byte_input_get_view(app->byte_input));
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_debug_app_free(RpcDebugApp* app) {
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewByteInput);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextInput);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextBox);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewWidget);
|
||||||
|
|
||||||
|
free(app->byte_input);
|
||||||
|
free(app->text_input);
|
||||||
|
free(app->text_box);
|
||||||
|
free(app->submenu);
|
||||||
|
free(app->widget);
|
||||||
|
|
||||||
|
free(app->scene_manager);
|
||||||
|
free(app->view_dispatcher);
|
||||||
|
|
||||||
|
furi_record_close(RECORD_NOTIFICATION);
|
||||||
|
app->notifications = NULL;
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
app->gui = NULL;
|
||||||
|
|
||||||
|
if(app->rpc) {
|
||||||
|
rpc_system_app_set_callback(app->rpc, NULL, NULL);
|
||||||
|
rpc_system_app_send_exited(app->rpc);
|
||||||
|
app->rpc = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t rpc_debug_app(void* args) {
|
||||||
|
RpcDebugApp* app = rpc_debug_app_alloc();
|
||||||
|
|
||||||
|
if(rpc_debug_app_rpc_init_rpc(app, args)) {
|
||||||
|
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||||
|
scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStart);
|
||||||
|
} else {
|
||||||
|
scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStartDummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
view_dispatcher_run(app->view_dispatcher);
|
||||||
|
|
||||||
|
rpc_debug_app_free(app);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
54
applications/debug/rpc_debug_app/rpc_debug_app.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include <gui/scene_manager.h>
|
||||||
|
#include <gui/view_dispatcher.h>
|
||||||
|
|
||||||
|
#include <gui/modules/widget.h>
|
||||||
|
#include <gui/modules/submenu.h>
|
||||||
|
#include <gui/modules/text_box.h>
|
||||||
|
#include <gui/modules/text_input.h>
|
||||||
|
#include <gui/modules/byte_input.h>
|
||||||
|
|
||||||
|
#include <rpc/rpc_app.h>
|
||||||
|
#include <notification/notification_messages.h>
|
||||||
|
|
||||||
|
#include "scenes/rpc_debug_app_scene.h"
|
||||||
|
|
||||||
|
#define DATA_STORE_SIZE 64U
|
||||||
|
#define TEXT_STORE_SIZE 64U
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Gui* gui;
|
||||||
|
RpcAppSystem* rpc;
|
||||||
|
SceneManager* scene_manager;
|
||||||
|
ViewDispatcher* view_dispatcher;
|
||||||
|
NotificationApp* notifications;
|
||||||
|
|
||||||
|
Widget* widget;
|
||||||
|
Submenu* submenu;
|
||||||
|
TextBox* text_box;
|
||||||
|
TextInput* text_input;
|
||||||
|
ByteInput* byte_input;
|
||||||
|
|
||||||
|
char text_store[TEXT_STORE_SIZE];
|
||||||
|
uint8_t data_store[DATA_STORE_SIZE];
|
||||||
|
} RpcDebugApp;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RpcDebugAppViewWidget,
|
||||||
|
RpcDebugAppViewSubmenu,
|
||||||
|
RpcDebugAppViewTextBox,
|
||||||
|
RpcDebugAppViewTextInput,
|
||||||
|
RpcDebugAppViewByteInput,
|
||||||
|
} RpcDebugAppView;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// Reserve first 100 events for button types and indexes, starting from 0
|
||||||
|
RpcDebugAppCustomEventInputErrorCode = 100,
|
||||||
|
RpcDebugAppCustomEventInputErrorText,
|
||||||
|
RpcDebugAppCustomEventInputDataExchange,
|
||||||
|
RpcDebugAppCustomEventRpcDataExchange,
|
||||||
|
} RpcDebugAppCustomEvent;
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
#include "rpc_debug_app_scene.h"
|
||||||
|
|
||||||
|
// Generate scene on_enter handlers array
|
||||||
|
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||||
|
void (*const rpc_debug_app_on_enter_handlers[])(void*) = {
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
};
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_event handlers array
|
||||||
|
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||||
|
bool (*const rpc_debug_app_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
};
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_exit handlers array
|
||||||
|
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||||
|
void (*const rpc_debug_app_on_exit_handlers[])(void* context) = {
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
};
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Initialize scene handlers configuration structure
|
||||||
|
const SceneManagerHandlers rpc_debug_app_scene_handlers = {
|
||||||
|
.on_enter_handlers = rpc_debug_app_on_enter_handlers,
|
||||||
|
.on_event_handlers = rpc_debug_app_on_event_handlers,
|
||||||
|
.on_exit_handlers = rpc_debug_app_on_exit_handlers,
|
||||||
|
.scene_num = RpcDebugAppSceneNum,
|
||||||
|
};
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/scene_manager.h>
|
||||||
|
|
||||||
|
// Generate scene id and total number
|
||||||
|
#define ADD_SCENE(prefix, name, id) RpcDebugAppScene##id,
|
||||||
|
typedef enum {
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
RpcDebugAppSceneNum,
|
||||||
|
} RpcDebugAppScene;
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
extern const SceneManagerHandlers rpc_debug_app_scene_handlers;
|
||||||
|
|
||||||
|
// Generate scene on_enter handlers declaration
|
||||||
|
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_event handlers declaration
|
||||||
|
#define ADD_SCENE(prefix, name, id) \
|
||||||
|
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
#undef ADD_SCENE
|
||||||
|
|
||||||
|
// Generate scene on_exit handlers declaration
|
||||||
|
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||||
|
#include "rpc_debug_app_scene_config.h"
|
||||||
|
#undef ADD_SCENE
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
ADD_SCENE(rpc_debug_app, start, Start)
|
||||||
|
ADD_SCENE(rpc_debug_app, start_dummy, StartDummy)
|
||||||
|
ADD_SCENE(rpc_debug_app, test_app_error, TestAppError)
|
||||||
|
ADD_SCENE(rpc_debug_app, test_data_exchange, TestDataExchange)
|
||||||
|
ADD_SCENE(rpc_debug_app, input_error_code, InputErrorCode)
|
||||||
|
ADD_SCENE(rpc_debug_app, input_error_text, InputErrorText)
|
||||||
|
ADD_SCENE(rpc_debug_app, input_data_exchange, InputDataExchange)
|
||||||
|
ADD_SCENE(rpc_debug_app, receive_data_exchange, ReceiveDataExchange)
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_input_data_exchange_result_callback(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
app->view_dispatcher, RpcDebugAppCustomEventInputDataExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_data_exchange_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
byte_input_set_header_text(app->byte_input, "Enter data to exchange");
|
||||||
|
byte_input_set_result_callback(
|
||||||
|
app->byte_input,
|
||||||
|
rpc_debug_app_scene_input_data_exchange_result_callback,
|
||||||
|
NULL,
|
||||||
|
app,
|
||||||
|
app->data_store,
|
||||||
|
DATA_STORE_SIZE);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewByteInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_input_data_exchange_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == RpcDebugAppCustomEventInputDataExchange) {
|
||||||
|
rpc_system_app_exchange_data(app->rpc, app->data_store, DATA_STORE_SIZE);
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_data_exchange_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
UNUSED(app);
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
static bool rpc_debug_app_scene_input_error_code_validator_callback(
|
||||||
|
const char* text,
|
||||||
|
FuriString* error,
|
||||||
|
void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
|
||||||
|
for(; *text; ++text) {
|
||||||
|
const char c = *text;
|
||||||
|
if(c < '0' || c > '9') {
|
||||||
|
furi_string_printf(error, "%s", "Please enter\na number!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_input_error_code_result_callback(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_error_code_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
strncpy(app->text_store, "666", TEXT_STORE_SIZE);
|
||||||
|
text_input_set_header_text(app->text_input, "Enter error code");
|
||||||
|
text_input_set_validator(
|
||||||
|
app->text_input, rpc_debug_app_scene_input_error_code_validator_callback, NULL);
|
||||||
|
text_input_set_result_callback(
|
||||||
|
app->text_input,
|
||||||
|
rpc_debug_app_scene_input_error_code_result_callback,
|
||||||
|
app,
|
||||||
|
app->text_store,
|
||||||
|
TEXT_STORE_SIZE,
|
||||||
|
true);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == RpcDebugAppCustomEventInputErrorCode) {
|
||||||
|
rpc_system_app_set_error_code(app->rpc, (uint32_t)atol(app->text_store));
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_error_code_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
text_input_reset(app->text_input);
|
||||||
|
text_input_set_validator(app->text_input, NULL, NULL);
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_input_error_text_result_callback(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_error_text_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
strncpy(app->text_store, "I'm a scary error message!", TEXT_STORE_SIZE);
|
||||||
|
text_input_set_header_text(app->text_input, "Enter error text");
|
||||||
|
text_input_set_result_callback(
|
||||||
|
app->text_input,
|
||||||
|
rpc_debug_app_scene_input_error_text_result_callback,
|
||||||
|
app,
|
||||||
|
app->text_store,
|
||||||
|
TEXT_STORE_SIZE,
|
||||||
|
true);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_input_error_text_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == RpcDebugAppCustomEventInputErrorText) {
|
||||||
|
rpc_system_app_set_error_text(app->rpc, app->text_store);
|
||||||
|
scene_manager_previous_scene(app->scene_manager);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_input_error_text_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
text_input_reset(app->text_input);
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_start_format_hex(
|
||||||
|
const uint8_t* data,
|
||||||
|
size_t data_size,
|
||||||
|
char* buf,
|
||||||
|
size_t buf_size) {
|
||||||
|
furi_assert(data);
|
||||||
|
furi_assert(buf);
|
||||||
|
|
||||||
|
const size_t byte_width = 3;
|
||||||
|
const size_t line_width = 7;
|
||||||
|
|
||||||
|
data_size = MIN(data_size, buf_size / (byte_width + 1));
|
||||||
|
|
||||||
|
for(size_t i = 0; i < data_size; ++i) {
|
||||||
|
char* p = buf + (i * byte_width);
|
||||||
|
char sep = !((i + 1) % line_width) ? '\n' : ' ';
|
||||||
|
snprintf(p, byte_width + 1, "%02X%c", data[i], sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[buf_size - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_receive_data_exchange_callback(
|
||||||
|
const uint8_t* data,
|
||||||
|
size_t data_size,
|
||||||
|
void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
if(data) {
|
||||||
|
rpc_debug_app_scene_start_format_hex(data, data_size, app->text_store, TEXT_STORE_SIZE);
|
||||||
|
} else {
|
||||||
|
strncpy(app->text_store, "<Data empty>", TEXT_STORE_SIZE);
|
||||||
|
}
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE);
|
||||||
|
|
||||||
|
text_box_set_text(app->text_box, app->text_store);
|
||||||
|
text_box_set_font(app->text_box, TextBoxFontHex);
|
||||||
|
|
||||||
|
rpc_system_app_set_data_exchange_callback(
|
||||||
|
app->rpc, rpc_debug_app_scene_receive_data_exchange_callback, app);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == RpcDebugAppCustomEventRpcDataExchange) {
|
||||||
|
notification_message(app->notifications, &sequence_blink_cyan_100);
|
||||||
|
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||||
|
text_box_set_text(app->text_box, app->text_store);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
text_box_reset(app->text_box);
|
||||||
|
rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL);
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
enum SubmenuIndex {
|
||||||
|
SubmenuIndexTestAppError,
|
||||||
|
SubmenuIndexTestDataExchange,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_start_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
Submenu* submenu = app->submenu;
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Test App Error",
|
||||||
|
SubmenuIndexTestAppError,
|
||||||
|
rpc_debug_app_scene_start_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Test Data Exchange",
|
||||||
|
SubmenuIndexTestDataExchange,
|
||||||
|
rpc_debug_app_scene_start_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
submenu_set_selected_item(submenu, SubmenuIndexTestAppError);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
SceneManager* scene_manager = app->scene_manager;
|
||||||
|
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
const uint32_t submenu_index = event.event;
|
||||||
|
if(submenu_index == SubmenuIndexTestAppError) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestAppError);
|
||||||
|
consumed = true;
|
||||||
|
} else if(submenu_index == SubmenuIndexTestDataExchange) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestDataExchange);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_start_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
submenu_reset(app->submenu);
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_start_dummy_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
widget_add_text_box_element(
|
||||||
|
app->widget,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
128,
|
||||||
|
64,
|
||||||
|
AlignCenter,
|
||||||
|
AlignCenter,
|
||||||
|
"This application\nis meant to be run\nin \e#RPC\e# mode.",
|
||||||
|
false);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_start_dummy_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
UNUSED(app);
|
||||||
|
UNUSED(event);
|
||||||
|
|
||||||
|
bool consumed = false;
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_start_dummy_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
widget_reset(app->widget);
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubmenuIndexSetErrorCode,
|
||||||
|
SubmenuIndexSetErrorText,
|
||||||
|
} SubmenuIndex;
|
||||||
|
|
||||||
|
static void rpc_debug_app_scene_test_app_error_submenu_callback(void* context, uint32_t index) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_test_app_error_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
Submenu* submenu = app->submenu;
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Set Error Code",
|
||||||
|
SubmenuIndexSetErrorCode,
|
||||||
|
rpc_debug_app_scene_test_app_error_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Set Error Text",
|
||||||
|
SubmenuIndexSetErrorText,
|
||||||
|
rpc_debug_app_scene_test_app_error_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
submenu_set_selected_item(submenu, SubmenuIndexSetErrorCode);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_test_app_error_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
SceneManager* scene_manager = app->scene_manager;
|
||||||
|
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
const uint32_t submenu_index = event.event;
|
||||||
|
if(submenu_index == SubmenuIndexSetErrorCode) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorCode);
|
||||||
|
consumed = true;
|
||||||
|
} else if(submenu_index == SubmenuIndexSetErrorText) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorText);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_test_app_error_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
submenu_reset(app->submenu);
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
#include "../rpc_debug_app.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubmenuIndexSendData,
|
||||||
|
SubmenuIndexReceiveData,
|
||||||
|
} SubmenuIndex;
|
||||||
|
|
||||||
|
static void
|
||||||
|
rpc_debug_app_scene_test_data_exchange_submenu_callback(void* context, uint32_t index) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_test_data_exchange_on_enter(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
Submenu* submenu = app->submenu;
|
||||||
|
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Send Data",
|
||||||
|
SubmenuIndexSendData,
|
||||||
|
rpc_debug_app_scene_test_data_exchange_submenu_callback,
|
||||||
|
app);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Receive Data",
|
||||||
|
SubmenuIndexReceiveData,
|
||||||
|
rpc_debug_app_scene_test_data_exchange_submenu_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
submenu_set_selected_item(submenu, SubmenuIndexSendData);
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rpc_debug_app_scene_test_data_exchange_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
SceneManager* scene_manager = app->scene_manager;
|
||||||
|
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
const uint32_t submenu_index = event.event;
|
||||||
|
if(submenu_index == SubmenuIndexSendData) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputDataExchange);
|
||||||
|
consumed = true;
|
||||||
|
} else if(submenu_index == SubmenuIndexReceiveData) {
|
||||||
|
scene_manager_next_scene(scene_manager, RpcDebugAppSceneReceiveDataExchange);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpc_debug_app_scene_test_data_exchange_on_exit(void* context) {
|
||||||
|
RpcDebugApp* app = context;
|
||||||
|
submenu_reset(app->submenu);
|
||||||
|
}
|
||||||
@ -220,11 +220,7 @@ static UartEchoApp* uart_echo_app_alloc() {
|
|||||||
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
|
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
|
||||||
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
|
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
|
||||||
|
|
||||||
app->worker_thread = furi_thread_alloc();
|
app->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 1024, uart_echo_worker, app);
|
||||||
furi_thread_set_name(app->worker_thread, "UsbUartWorker");
|
|
||||||
furi_thread_set_stack_size(app->worker_thread, 1024);
|
|
||||||
furi_thread_set_context(app->worker_thread, app);
|
|
||||||
furi_thread_set_callback(app->worker_thread, uart_echo_worker);
|
|
||||||
furi_thread_start(app->worker_thread);
|
furi_thread_start(app->worker_thread);
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
extern size_t memmgr_get_free_heap(void);
|
extern size_t memmgr_get_free_heap(void);
|
||||||
extern size_t memmgr_get_minimum_free_heap(void);
|
extern size_t memmgr_get_minimum_free_heap(void);
|
||||||
|
|
||||||
// current heap managment realization consume:
|
// current heap management realization consume:
|
||||||
// X bytes after allocate and 0 bytes after allocate and free,
|
// X bytes after allocate and 0 bytes after allocate and free,
|
||||||
// where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t
|
// where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t
|
||||||
const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t);
|
const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t);
|
||||||
@ -21,7 +21,7 @@ bool heap_equal(size_t heap_size, size_t heap_size_old) {
|
|||||||
const size_t heap_low = heap_size_old - heap_overhead_max_size;
|
const size_t heap_low = heap_size_old - heap_overhead_max_size;
|
||||||
const size_t heap_high = heap_size_old + heap_overhead_max_size;
|
const size_t heap_high = heap_size_old + heap_overhead_max_size;
|
||||||
|
|
||||||
// not extact, so we must test it against bigger numbers than "overhead size"
|
// not exact, so we must test it against bigger numbers than "overhead size"
|
||||||
const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high));
|
const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high));
|
||||||
|
|
||||||
// debug allocation info
|
// debug allocation info
|
||||||
|
|||||||
@ -136,7 +136,7 @@ static bool nfc_test_digital_signal_test_encode(
|
|||||||
ref_timings_sum += ref[i];
|
ref_timings_sum += ref[i];
|
||||||
if(timings_diff > timing_tolerance) {
|
if(timings_diff > timing_tolerance) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
TAG, "Too big differece in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]);
|
TAG, "Too big difference in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]);
|
||||||
timing_check_success = false;
|
timing_check_success = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,11 +43,8 @@ MU_TEST(storage_file_open_lock) {
|
|||||||
File* file = storage_file_alloc(storage);
|
File* file = storage_file_alloc(storage);
|
||||||
|
|
||||||
// file_locker thread start
|
// file_locker thread start
|
||||||
FuriThread* locker_thread = furi_thread_alloc();
|
FuriThread* locker_thread =
|
||||||
furi_thread_set_name(locker_thread, "StorageFileLocker");
|
furi_thread_alloc_ex("StorageFileLocker", 2048, storage_file_locker, semaphore);
|
||||||
furi_thread_set_stack_size(locker_thread, 2048);
|
|
||||||
furi_thread_set_context(locker_thread, semaphore);
|
|
||||||
furi_thread_set_callback(locker_thread, storage_file_locker);
|
|
||||||
furi_thread_start(locker_thread);
|
furi_thread_start(locker_thread);
|
||||||
|
|
||||||
// wait for file lock
|
// wait for file lock
|
||||||
@ -133,11 +130,8 @@ MU_TEST(storage_dir_open_lock) {
|
|||||||
File* file = storage_file_alloc(storage);
|
File* file = storage_file_alloc(storage);
|
||||||
|
|
||||||
// file_locker thread start
|
// file_locker thread start
|
||||||
FuriThread* locker_thread = furi_thread_alloc();
|
FuriThread* locker_thread =
|
||||||
furi_thread_set_name(locker_thread, "StorageDirLocker");
|
furi_thread_alloc_ex("StorageDirLocker", 2048, storage_dir_locker, semaphore);
|
||||||
furi_thread_set_stack_size(locker_thread, 2048);
|
|
||||||
furi_thread_set_context(locker_thread, semaphore);
|
|
||||||
furi_thread_set_callback(locker_thread, storage_dir_locker);
|
|
||||||
furi_thread_start(locker_thread);
|
furi_thread_start(locker_thread);
|
||||||
|
|
||||||
// wait for dir lock
|
// wait for dir lock
|
||||||
|
|||||||
@ -219,21 +219,21 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) {
|
|||||||
mu_check(stream_eof(stream));
|
mu_check(stream_eof(stream));
|
||||||
mu_assert_int_eq(0, stream_tell(stream));
|
mu_assert_int_eq(0, stream_tell(stream));
|
||||||
|
|
||||||
// insert formated string at the end of stream
|
// insert formatted string at the end of stream
|
||||||
// "" -> "dio666"
|
// "" -> "dio666"
|
||||||
mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
|
mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
|
||||||
mu_assert_int_eq(6, stream_size(stream));
|
mu_assert_int_eq(6, stream_size(stream));
|
||||||
mu_check(stream_eof(stream));
|
mu_check(stream_eof(stream));
|
||||||
mu_assert_int_eq(6, stream_tell(stream));
|
mu_assert_int_eq(6, stream_tell(stream));
|
||||||
|
|
||||||
// insert formated string at the end of stream
|
// insert formatted string at the end of stream
|
||||||
// "dio666" -> "dio666zlo555"
|
// "dio666" -> "dio666zlo555"
|
||||||
mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
|
mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
|
||||||
mu_assert_int_eq(12, stream_size(stream));
|
mu_assert_int_eq(12, stream_size(stream));
|
||||||
mu_check(stream_eof(stream));
|
mu_check(stream_eof(stream));
|
||||||
mu_assert_int_eq(12, stream_tell(stream));
|
mu_assert_int_eq(12, stream_tell(stream));
|
||||||
|
|
||||||
// insert formated string at the 6 pos
|
// insert formatted string at the 6 pos
|
||||||
// "dio666" -> "dio666baba13zlo555"
|
// "dio666" -> "dio666baba13zlo555"
|
||||||
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
|
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
|
||||||
mu_check(stream_insert_format(stream, "%s%d", "baba", 13));
|
mu_check(stream_insert_format(stream, "%s%d", "baba", 13));
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
|
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
|
||||||
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
|
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
|
||||||
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
|
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
|
||||||
#define TEST_RANDOM_COUNT_PARSE 232
|
#define TEST_RANDOM_COUNT_PARSE 244
|
||||||
#define TEST_TIMEOUT 10000
|
#define TEST_TIMEOUT 10000
|
||||||
|
|
||||||
static SubGhzEnvironment* environment_handler;
|
static SubGhzEnvironment* environment_handler;
|
||||||
@ -209,6 +209,149 @@ MU_TEST(subghz_keystore_test) {
|
|||||||
"Test keystore error");
|
"Test keystore error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubGhzHalAsyncTxTestTypeNormal,
|
||||||
|
SubGhzHalAsyncTxTestTypeInvalidStart,
|
||||||
|
SubGhzHalAsyncTxTestTypeInvalidMid,
|
||||||
|
SubGhzHalAsyncTxTestTypeInvalidEnd,
|
||||||
|
SubGhzHalAsyncTxTestTypeResetStart,
|
||||||
|
SubGhzHalAsyncTxTestTypeResetMid,
|
||||||
|
SubGhzHalAsyncTxTestTypeResetEnd,
|
||||||
|
} SubGhzHalAsyncTxTestType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SubGhzHalAsyncTxTestType type;
|
||||||
|
size_t pos;
|
||||||
|
} SubGhzHalAsyncTxTest;
|
||||||
|
|
||||||
|
#define SUBGHZ_HAL_TEST_DURATION 1
|
||||||
|
|
||||||
|
static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
|
||||||
|
SubGhzHalAsyncTxTest* test = context;
|
||||||
|
bool is_odd = test->pos % 2;
|
||||||
|
|
||||||
|
if(test->type == SubGhzHalAsyncTxTestTypeNormal) {
|
||||||
|
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidStart) {
|
||||||
|
if(test->pos == 0) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) {
|
||||||
|
if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) {
|
||||||
|
if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeResetStart) {
|
||||||
|
if(test->pos == 0) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) {
|
||||||
|
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) {
|
||||||
|
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
|
||||||
|
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
|
||||||
|
test->pos++;
|
||||||
|
return level_duration_reset();
|
||||||
|
} else {
|
||||||
|
furi_crash("Yield after reset");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
furi_crash("Programming error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
|
||||||
|
SubGhzHalAsyncTxTest test = {0};
|
||||||
|
test.type = type;
|
||||||
|
furi_hal_subghz_reset();
|
||||||
|
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
|
||||||
|
furi_hal_subghz_set_frequency_and_path(433920000);
|
||||||
|
|
||||||
|
furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test);
|
||||||
|
while(!furi_hal_subghz_is_async_tx_complete()) {
|
||||||
|
furi_delay_ms(10);
|
||||||
|
}
|
||||||
|
furi_hal_subghz_stop_async_tx();
|
||||||
|
furi_hal_subghz_sleep();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_hal_async_tx_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeNormal),
|
||||||
|
"Test furi_hal_async_tx normal");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidStart),
|
||||||
|
"Test furi_hal_async_tx invalid start");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidMid),
|
||||||
|
"Test furi_hal_async_tx invalid mid");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidEnd),
|
||||||
|
"Test furi_hal_async_tx invalid end");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetStart),
|
||||||
|
"Test furi_hal_async_tx reset start");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetMid),
|
||||||
|
"Test furi_hal_async_tx reset mid");
|
||||||
|
mu_assert(
|
||||||
|
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetEnd),
|
||||||
|
"Test furi_hal_async_tx reset end");
|
||||||
|
}
|
||||||
|
|
||||||
//test decoders
|
//test decoders
|
||||||
MU_TEST(subghz_decoder_came_atomo_test) {
|
MU_TEST(subghz_decoder_came_atomo_test) {
|
||||||
mu_assert(
|
mu_assert(
|
||||||
@ -437,6 +580,13 @@ MU_TEST(subghz_decoder_clemsa_test) {
|
|||||||
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_decoder_ansonic_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_decoder_test(
|
||||||
|
EXT_PATH("unit_tests/subghz/ansonic_raw.sub"), SUBGHZ_PROTOCOL_ANSONIC_NAME),
|
||||||
|
"Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
//test encoders
|
//test encoders
|
||||||
MU_TEST(subghz_encoder_princeton_test) {
|
MU_TEST(subghz_encoder_princeton_test) {
|
||||||
mu_assert(
|
mu_assert(
|
||||||
@ -558,6 +708,12 @@ MU_TEST(subghz_encoder_clemsa_test) {
|
|||||||
"Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
"Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MU_TEST(subghz_encoder_ansonic_test) {
|
||||||
|
mu_assert(
|
||||||
|
subghz_encoder_test(EXT_PATH("unit_tests/subghz/ansonic.sub")),
|
||||||
|
"Test encoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
MU_TEST(subghz_random_test) {
|
MU_TEST(subghz_random_test) {
|
||||||
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
|
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
|
||||||
}
|
}
|
||||||
@ -566,6 +722,8 @@ MU_TEST_SUITE(subghz) {
|
|||||||
subghz_test_init();
|
subghz_test_init();
|
||||||
MU_RUN_TEST(subghz_keystore_test);
|
MU_RUN_TEST(subghz_keystore_test);
|
||||||
|
|
||||||
|
MU_RUN_TEST(subghz_hal_async_tx_test);
|
||||||
|
|
||||||
MU_RUN_TEST(subghz_decoder_came_atomo_test);
|
MU_RUN_TEST(subghz_decoder_came_atomo_test);
|
||||||
MU_RUN_TEST(subghz_decoder_came_test);
|
MU_RUN_TEST(subghz_decoder_came_test);
|
||||||
MU_RUN_TEST(subghz_decoder_came_twee_test);
|
MU_RUN_TEST(subghz_decoder_came_twee_test);
|
||||||
@ -598,6 +756,7 @@ MU_TEST_SUITE(subghz) {
|
|||||||
MU_RUN_TEST(subghz_decoder_magellan_test);
|
MU_RUN_TEST(subghz_decoder_magellan_test);
|
||||||
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
|
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
|
||||||
MU_RUN_TEST(subghz_decoder_clemsa_test);
|
MU_RUN_TEST(subghz_decoder_clemsa_test);
|
||||||
|
MU_RUN_TEST(subghz_decoder_ansonic_test);
|
||||||
|
|
||||||
MU_RUN_TEST(subghz_encoder_princeton_test);
|
MU_RUN_TEST(subghz_encoder_princeton_test);
|
||||||
MU_RUN_TEST(subghz_encoder_came_test);
|
MU_RUN_TEST(subghz_encoder_came_test);
|
||||||
@ -619,6 +778,7 @@ MU_TEST_SUITE(subghz) {
|
|||||||
MU_RUN_TEST(subghz_encoder_magellan_test);
|
MU_RUN_TEST(subghz_encoder_magellan_test);
|
||||||
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
|
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
|
||||||
MU_RUN_TEST(subghz_encoder_clemsa_test);
|
MU_RUN_TEST(subghz_encoder_clemsa_test);
|
||||||
|
MU_RUN_TEST(subghz_encoder_ansonic_test);
|
||||||
|
|
||||||
MU_RUN_TEST(subghz_random_test);
|
MU_RUN_TEST(subghz_random_test);
|
||||||
subghz_test_deinit();
|
subghz_test_deinit();
|
||||||
|
|||||||
@ -265,8 +265,7 @@ void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) {
|
|||||||
offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1;
|
offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1;
|
||||||
}
|
}
|
||||||
if(offset_new > 0) {
|
if(offset_new > 0) {
|
||||||
offset_new =
|
offset_new = CLAMP(offset_new, (int32_t)model->item_cnt, 0);
|
||||||
CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0);
|
|
||||||
} else {
|
} else {
|
||||||
offset_new = 0;
|
offset_new = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
#include "../archive_i.h"
|
#include "../archive_i.h"
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
|
|
||||||
#define TAB_RIGHT InputKeyRight // Default tab swith direction
|
#define TAB_RIGHT InputKeyRight // Default tab switch direction
|
||||||
#define TAB_DEFAULT ArchiveTabFavorites // Start tab
|
#define TAB_DEFAULT ArchiveTabFavorites // Start tab
|
||||||
#define FILE_LIST_BUF_LEN 100
|
#define FILE_LIST_BUF_LEN 50
|
||||||
|
|
||||||
static const char* tab_default_paths[] = {
|
static const char* tab_default_paths[] = {
|
||||||
[ArchiveTabFavorites] = "/app:favorites",
|
[ArchiveTabFavorites] = "/app:favorites",
|
||||||
|
|||||||
@ -657,12 +657,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
|
|||||||
bad_usb->st.state = BadUsbStateInit;
|
bad_usb->st.state = BadUsbStateInit;
|
||||||
bad_usb->st.error[0] = '\0';
|
bad_usb->st.error[0] = '\0';
|
||||||
|
|
||||||
bad_usb->thread = furi_thread_alloc();
|
bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
|
||||||
furi_thread_set_name(bad_usb->thread, "BadUsbWorker");
|
|
||||||
furi_thread_set_stack_size(bad_usb->thread, 2048);
|
|
||||||
furi_thread_set_context(bad_usb->thread, bad_usb);
|
|
||||||
furi_thread_set_callback(bad_usb->thread, bad_usb_worker);
|
|
||||||
|
|
||||||
furi_thread_start(bad_usb->thread);
|
furi_thread_start(bad_usb->thread);
|
||||||
return bad_usb;
|
return bad_usb;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ bool elf_resolve_from_hashtable(const char* name, Elf32_Addr* address) {
|
|||||||
|
|
||||||
auto find_res = std::lower_bound(elf_api_table.cbegin(), elf_api_table.cend(), key);
|
auto find_res = std::lower_bound(elf_api_table.cbegin(), elf_api_table.cend(), key);
|
||||||
if((find_res == elf_api_table.cend() || (find_res->hash != gnu_sym_hash))) {
|
if((find_res == elf_api_table.cend() || (find_res->hash != gnu_sym_hash))) {
|
||||||
FURI_LOG_W(TAG, "Cant find symbol '%s' (hash %lx)!", name, gnu_sym_hash);
|
FURI_LOG_W(TAG, "Can't find symbol '%s' (hash %lx)!", name, gnu_sym_hash);
|
||||||
result = false;
|
result = false;
|
||||||
} else {
|
} else {
|
||||||
result = true;
|
result = true;
|
||||||
|
|||||||
@ -29,6 +29,7 @@ struct GpioApp {
|
|||||||
GpioTest* gpio_test;
|
GpioTest* gpio_test;
|
||||||
GpioUsbUart* gpio_usb_uart;
|
GpioUsbUart* gpio_usb_uart;
|
||||||
UsbUartBridge* usb_uart_bridge;
|
UsbUartBridge* usb_uart_bridge;
|
||||||
|
UsbUartConfig* usb_uart_cfg;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|||||||
@ -9,4 +9,5 @@ typedef enum {
|
|||||||
GpioCustomEventErrorBack,
|
GpioCustomEventErrorBack,
|
||||||
|
|
||||||
GpioUsbUartEventConfig,
|
GpioUsbUartEventConfig,
|
||||||
|
GpioUsbUartEventConfigSet,
|
||||||
} GpioCustomEvent;
|
} GpioCustomEvent;
|
||||||
|
|||||||
@ -9,8 +9,6 @@ typedef enum {
|
|||||||
UsbUartLineIndexFlow,
|
UsbUartLineIndexFlow,
|
||||||
} LineIndex;
|
} LineIndex;
|
||||||
|
|
||||||
static UsbUartConfig* cfg_set;
|
|
||||||
|
|
||||||
static const char* vcp_ch[] = {"0 (CLI)", "1"};
|
static const char* vcp_ch[] = {"0 (CLI)", "1"};
|
||||||
static const char* uart_ch[] = {"13,14", "15,16"};
|
static const char* uart_ch[] = {"13,14", "15,16"};
|
||||||
static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"};
|
static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"};
|
||||||
@ -28,8 +26,14 @@ static const uint32_t baudrate_list[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
|
bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
|
||||||
UNUSED(context);
|
GpioApp* app = context;
|
||||||
UNUSED(event);
|
furi_assert(app);
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == GpioUsbUartEventConfigSet) {
|
||||||
|
usb_uart_set_config(app->usb_uart_bridge, app->usb_uart_cfg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,55 +42,59 @@ void line_ensure_flow_invariant(GpioApp* app) {
|
|||||||
// selected. This function enforces that invariant by resetting flow_pins
|
// selected. This function enforces that invariant by resetting flow_pins
|
||||||
// to None if it is configured to 16,15 when LPUART is selected.
|
// to None if it is configured to 16,15 when LPUART is selected.
|
||||||
|
|
||||||
uint8_t available_flow_pins = cfg_set->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
|
uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
|
||||||
VariableItem* item = app->var_item_flow;
|
VariableItem* item = app->var_item_flow;
|
||||||
variable_item_set_values_count(item, available_flow_pins);
|
variable_item_set_values_count(item, available_flow_pins);
|
||||||
|
|
||||||
if(cfg_set->flow_pins >= available_flow_pins) {
|
if(app->usb_uart_cfg->flow_pins >= available_flow_pins) {
|
||||||
cfg_set->flow_pins = 0;
|
app->usb_uart_cfg->flow_pins = 0;
|
||||||
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
|
|
||||||
|
|
||||||
variable_item_set_current_value_index(item, cfg_set->flow_pins);
|
variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins);
|
||||||
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]);
|
variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_vcp_cb(VariableItem* item) {
|
static void line_vcp_cb(VariableItem* item) {
|
||||||
GpioApp* app = variable_item_get_context(item);
|
GpioApp* app = variable_item_get_context(item);
|
||||||
|
furi_assert(app);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
variable_item_set_current_value_text(item, vcp_ch[index]);
|
variable_item_set_current_value_text(item, vcp_ch[index]);
|
||||||
|
|
||||||
cfg_set->vcp_ch = index;
|
app->usb_uart_cfg->vcp_ch = index;
|
||||||
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
|
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_port_cb(VariableItem* item) {
|
static void line_port_cb(VariableItem* item) {
|
||||||
GpioApp* app = variable_item_get_context(item);
|
GpioApp* app = variable_item_get_context(item);
|
||||||
|
furi_assert(app);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
variable_item_set_current_value_text(item, uart_ch[index]);
|
variable_item_set_current_value_text(item, uart_ch[index]);
|
||||||
|
|
||||||
if(index == 0)
|
if(index == 0)
|
||||||
cfg_set->uart_ch = FuriHalUartIdUSART1;
|
app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1;
|
||||||
else if(index == 1)
|
else if(index == 1)
|
||||||
cfg_set->uart_ch = FuriHalUartIdLPUART1;
|
app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1;
|
||||||
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
|
|
||||||
line_ensure_flow_invariant(app);
|
line_ensure_flow_invariant(app);
|
||||||
|
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_flow_cb(VariableItem* item) {
|
static void line_flow_cb(VariableItem* item) {
|
||||||
GpioApp* app = variable_item_get_context(item);
|
GpioApp* app = variable_item_get_context(item);
|
||||||
|
furi_assert(app);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
variable_item_set_current_value_text(item, flow_pins[index]);
|
variable_item_set_current_value_text(item, flow_pins[index]);
|
||||||
|
|
||||||
cfg_set->flow_pins = index;
|
app->usb_uart_cfg->flow_pins = index;
|
||||||
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
|
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_baudrate_cb(VariableItem* item) {
|
static void line_baudrate_cb(VariableItem* item) {
|
||||||
GpioApp* app = variable_item_get_context(item);
|
GpioApp* app = variable_item_get_context(item);
|
||||||
|
furi_assert(app);
|
||||||
uint8_t index = variable_item_get_current_value_index(item);
|
uint8_t index = variable_item_get_current_value_index(item);
|
||||||
|
|
||||||
char br_text[8];
|
char br_text[8];
|
||||||
@ -94,28 +102,29 @@ static void line_baudrate_cb(VariableItem* item) {
|
|||||||
if(index > 0) {
|
if(index > 0) {
|
||||||
snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
|
snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
|
||||||
variable_item_set_current_value_text(item, br_text);
|
variable_item_set_current_value_text(item, br_text);
|
||||||
cfg_set->baudrate = baudrate_list[index - 1];
|
app->usb_uart_cfg->baudrate = baudrate_list[index - 1];
|
||||||
} else {
|
} else {
|
||||||
variable_item_set_current_value_text(item, baudrate_mode[index]);
|
variable_item_set_current_value_text(item, baudrate_mode[index]);
|
||||||
cfg_set->baudrate = 0;
|
app->usb_uart_cfg->baudrate = 0;
|
||||||
}
|
}
|
||||||
cfg_set->baudrate_mode = index;
|
app->usb_uart_cfg->baudrate_mode = index;
|
||||||
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
|
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpio_scene_usb_uart_cfg_on_enter(void* context) {
|
void gpio_scene_usb_uart_cfg_on_enter(void* context) {
|
||||||
GpioApp* app = context;
|
GpioApp* app = context;
|
||||||
|
furi_assert(app);
|
||||||
VariableItemList* var_item_list = app->var_item_list;
|
VariableItemList* var_item_list = app->var_item_list;
|
||||||
|
|
||||||
cfg_set = malloc(sizeof(UsbUartConfig));
|
app->usb_uart_cfg = malloc(sizeof(UsbUartConfig));
|
||||||
usb_uart_get_config(app->usb_uart_bridge, cfg_set);
|
usb_uart_get_config(app->usb_uart_bridge, app->usb_uart_cfg);
|
||||||
|
|
||||||
VariableItem* item;
|
VariableItem* item;
|
||||||
char br_text[8];
|
char br_text[8];
|
||||||
|
|
||||||
item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app);
|
item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app);
|
||||||
variable_item_set_current_value_index(item, cfg_set->vcp_ch);
|
variable_item_set_current_value_index(item, app->usb_uart_cfg->vcp_ch);
|
||||||
variable_item_set_current_value_text(item, vcp_ch[cfg_set->vcp_ch]);
|
variable_item_set_current_value_text(item, vcp_ch[app->usb_uart_cfg->vcp_ch]);
|
||||||
|
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
var_item_list,
|
var_item_list,
|
||||||
@ -123,22 +132,23 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) {
|
|||||||
sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
|
sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
|
||||||
line_baudrate_cb,
|
line_baudrate_cb,
|
||||||
app);
|
app);
|
||||||
variable_item_set_current_value_index(item, cfg_set->baudrate_mode);
|
variable_item_set_current_value_index(item, app->usb_uart_cfg->baudrate_mode);
|
||||||
if(cfg_set->baudrate_mode > 0) {
|
if(app->usb_uart_cfg->baudrate_mode > 0) {
|
||||||
snprintf(br_text, 7, "%lu", baudrate_list[cfg_set->baudrate_mode - 1]);
|
snprintf(br_text, 7, "%lu", baudrate_list[app->usb_uart_cfg->baudrate_mode - 1]);
|
||||||
variable_item_set_current_value_text(item, br_text);
|
variable_item_set_current_value_text(item, br_text);
|
||||||
} else {
|
} else {
|
||||||
variable_item_set_current_value_text(item, baudrate_mode[cfg_set->baudrate_mode]);
|
variable_item_set_current_value_text(
|
||||||
|
item, baudrate_mode[app->usb_uart_cfg->baudrate_mode]);
|
||||||
}
|
}
|
||||||
|
|
||||||
item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app);
|
item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app);
|
||||||
variable_item_set_current_value_index(item, cfg_set->uart_ch);
|
variable_item_set_current_value_index(item, app->usb_uart_cfg->uart_ch);
|
||||||
variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]);
|
variable_item_set_current_value_text(item, uart_ch[app->usb_uart_cfg->uart_ch]);
|
||||||
|
|
||||||
item = variable_item_list_add(
|
item = variable_item_list_add(
|
||||||
var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app);
|
var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app);
|
||||||
variable_item_set_current_value_index(item, cfg_set->flow_pins);
|
variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins);
|
||||||
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]);
|
variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);
|
||||||
app->var_item_flow = item;
|
app->var_item_flow = item;
|
||||||
line_ensure_flow_invariant(app);
|
line_ensure_flow_invariant(app);
|
||||||
|
|
||||||
@ -155,5 +165,5 @@ void gpio_scene_usb_uart_cfg_on_exit(void* context) {
|
|||||||
GpioAppViewUsbUartCfg,
|
GpioAppViewUsbUartCfg,
|
||||||
variable_item_list_get_selected_item_index(app->var_item_list));
|
variable_item_list_get_selected_item_index(app->var_item_list));
|
||||||
variable_item_list_reset(app->var_item_list);
|
variable_item_list_reset(app->var_item_list);
|
||||||
free(cfg_set);
|
free(app->usb_uart_cfg);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include <furi_hal_usb_cdc.h>
|
#include <furi_hal_usb_cdc.h>
|
||||||
#include "usb_cdc.h"
|
#include "usb_cdc.h"
|
||||||
#include "cli/cli_vcp.h"
|
#include "cli/cli_vcp.h"
|
||||||
|
#include <toolbox/api_lock.h>
|
||||||
#include "cli/cli.h"
|
#include "cli/cli.h"
|
||||||
|
|
||||||
#define USB_CDC_PKT_LEN CDC_DATA_SZ
|
#define USB_CDC_PKT_LEN CDC_DATA_SZ
|
||||||
@ -51,6 +52,8 @@ struct UsbUartBridge {
|
|||||||
|
|
||||||
UsbUartState st;
|
UsbUartState st;
|
||||||
|
|
||||||
|
FuriApiLock cfg_lock;
|
||||||
|
|
||||||
uint8_t rx_buf[USB_CDC_PKT_LEN];
|
uint8_t rx_buf[USB_CDC_PKT_LEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -159,11 +162,8 @@ static int32_t usb_uart_worker(void* context) {
|
|||||||
usb_uart->tx_sem = furi_semaphore_alloc(1, 1);
|
usb_uart->tx_sem = furi_semaphore_alloc(1, 1);
|
||||||
usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
|
|
||||||
usb_uart->tx_thread = furi_thread_alloc();
|
usb_uart->tx_thread =
|
||||||
furi_thread_set_name(usb_uart->tx_thread, "UsbUartTxWorker");
|
furi_thread_alloc_ex("UsbUartTxWorker", 512, usb_uart_tx_thread, usb_uart);
|
||||||
furi_thread_set_stack_size(usb_uart->tx_thread, 512);
|
|
||||||
furi_thread_set_context(usb_uart->tx_thread, usb_uart);
|
|
||||||
furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread);
|
|
||||||
|
|
||||||
usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);
|
usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);
|
||||||
usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch);
|
usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch);
|
||||||
@ -247,6 +247,7 @@ static int32_t usb_uart_worker(void* context) {
|
|||||||
usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;
|
usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;
|
||||||
events |= WorkerEvtCtrlLineSet;
|
events |= WorkerEvtCtrlLineSet;
|
||||||
}
|
}
|
||||||
|
api_lock_unlock(usb_uart->cfg_lock);
|
||||||
}
|
}
|
||||||
if(events & WorkerEvtLineCfgSet) {
|
if(events & WorkerEvtLineCfgSet) {
|
||||||
if(usb_uart->cfg.baudrate == 0)
|
if(usb_uart->cfg.baudrate == 0)
|
||||||
@ -338,11 +339,7 @@ UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) {
|
|||||||
|
|
||||||
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
|
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
|
||||||
|
|
||||||
usb_uart->thread = furi_thread_alloc();
|
usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart);
|
||||||
furi_thread_set_name(usb_uart->thread, "UsbUartWorker");
|
|
||||||
furi_thread_set_stack_size(usb_uart->thread, 1024);
|
|
||||||
furi_thread_set_context(usb_uart->thread, usb_uart);
|
|
||||||
furi_thread_set_callback(usb_uart->thread, usb_uart_worker);
|
|
||||||
|
|
||||||
furi_thread_start(usb_uart->thread);
|
furi_thread_start(usb_uart->thread);
|
||||||
return usb_uart;
|
return usb_uart;
|
||||||
@ -359,8 +356,10 @@ void usb_uart_disable(UsbUartBridge* usb_uart) {
|
|||||||
void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
|
void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
|
||||||
furi_assert(usb_uart);
|
furi_assert(usb_uart);
|
||||||
furi_assert(cfg);
|
furi_assert(cfg);
|
||||||
|
usb_uart->cfg_lock = api_lock_alloc_locked();
|
||||||
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
|
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
|
||||||
furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange);
|
furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange);
|
||||||
|
api_lock_wait_unlock_and_free(usb_uart->cfg_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
|
void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
|
||||||
|
|||||||
@ -24,7 +24,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
|
|||||||
&I_Power_hvr_25x27,
|
&I_Power_hvr_25x27,
|
||||||
infrared_scene_universal_common_item_callback,
|
infrared_scene_universal_common_item_callback,
|
||||||
context);
|
context);
|
||||||
infrared_brute_force_add_record(brute_force, i++, "POWER");
|
infrared_brute_force_add_record(brute_force, i++, "Power");
|
||||||
button_panel_add_item(
|
button_panel_add_item(
|
||||||
button_panel,
|
button_panel,
|
||||||
i,
|
i,
|
||||||
@ -36,7 +36,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
|
|||||||
&I_Mute_hvr_25x27,
|
&I_Mute_hvr_25x27,
|
||||||
infrared_scene_universal_common_item_callback,
|
infrared_scene_universal_common_item_callback,
|
||||||
context);
|
context);
|
||||||
infrared_brute_force_add_record(brute_force, i++, "MUTE");
|
infrared_brute_force_add_record(brute_force, i++, "Mute");
|
||||||
button_panel_add_item(
|
button_panel_add_item(
|
||||||
button_panel,
|
button_panel,
|
||||||
i,
|
i,
|
||||||
@ -48,7 +48,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
|
|||||||
&I_Vol_up_hvr_25x27,
|
&I_Vol_up_hvr_25x27,
|
||||||
infrared_scene_universal_common_item_callback,
|
infrared_scene_universal_common_item_callback,
|
||||||
context);
|
context);
|
||||||
infrared_brute_force_add_record(brute_force, i++, "VOL+");
|
infrared_brute_force_add_record(brute_force, i++, "Vol_up");
|
||||||
button_panel_add_item(
|
button_panel_add_item(
|
||||||
button_panel,
|
button_panel,
|
||||||
i,
|
i,
|
||||||
@ -60,7 +60,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
|
|||||||
&I_Up_hvr_25x27,
|
&I_Up_hvr_25x27,
|
||||||
infrared_scene_universal_common_item_callback,
|
infrared_scene_universal_common_item_callback,
|
||||||
context);
|
context);
|
||||||
infrared_brute_force_add_record(brute_force, i++, "CH+");
|
infrared_brute_force_add_record(brute_force, i++, "Ch_next");
|
||||||
button_panel_add_item(
|
button_panel_add_item(
|
||||||
button_panel,
|
button_panel,
|
||||||
i,
|
i,
|
||||||
@ -72,7 +72,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
|
|||||||
&I_Vol_down_hvr_25x27,
|
&I_Vol_down_hvr_25x27,
|
||||||
infrared_scene_universal_common_item_callback,
|
infrared_scene_universal_common_item_callback,
|
||||||
context);
|
context);
|
||||||
infrared_brute_force_add_record(brute_force, i++, "VOL-");
|
infrared_brute_force_add_record(brute_force, i++, "Vol_dn");
|
||||||
button_panel_add_item(
|
button_panel_add_item(
|
||||||
button_panel,
|
button_panel,
|
||||||
i,
|
i,
|
||||||
@ -84,7 +84,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
|
|||||||
&I_Down_hvr_25x27,
|
&I_Down_hvr_25x27,
|
||||||
infrared_scene_universal_common_item_callback,
|
infrared_scene_universal_common_item_callback,
|
||||||
context);
|
context);
|
||||||
infrared_brute_force_add_record(brute_force, i++, "CH-");
|
infrared_brute_force_add_record(brute_force, i++, "Ch_prev");
|
||||||
|
|
||||||
button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote");
|
button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote");
|
||||||
button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol");
|
button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol");
|
||||||
|
|||||||
@ -21,6 +21,7 @@ ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate)
|
|||||||
ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth)
|
ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth)
|
||||||
ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult)
|
ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult)
|
||||||
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
||||||
|
ADD_SCENE(nfc, mf_ultralight_unlock_auto, MfUltralightUnlockAuto)
|
||||||
ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
|
ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
|
||||||
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
||||||
ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess)
|
ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess)
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexSave,
|
SubmenuIndexSave,
|
||||||
SubmenuIndexEmulate,
|
SubmenuIndexEmulate,
|
||||||
|
SubmenuIndexDetectReader,
|
||||||
SubmenuIndexInfo,
|
SubmenuIndexInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,6 +22,14 @@ void nfc_scene_mf_classic_menu_on_enter(void* context) {
|
|||||||
submenu, "Save", SubmenuIndexSave, nfc_scene_mf_classic_menu_submenu_callback, nfc);
|
submenu, "Save", SubmenuIndexSave, nfc_scene_mf_classic_menu_submenu_callback, nfc);
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mf_classic_menu_submenu_callback, nfc);
|
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mf_classic_menu_submenu_callback, nfc);
|
||||||
|
if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) {
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Detect reader",
|
||||||
|
SubmenuIndexDetectReader,
|
||||||
|
nfc_scene_mf_classic_menu_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
}
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_classic_menu_submenu_callback, nfc);
|
submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_classic_menu_submenu_callback, nfc);
|
||||||
|
|
||||||
@ -35,17 +44,14 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
|
|||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicMenu, event.event);
|
||||||
if(event.event == SubmenuIndexSave) {
|
if(event.event == SubmenuIndexSave) {
|
||||||
scene_manager_set_scene_state(
|
|
||||||
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave);
|
|
||||||
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
|
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
|
||||||
// Clear device name
|
// Clear device name
|
||||||
nfc_device_set_name(nfc->dev, "");
|
nfc_device_set_name(nfc->dev, "");
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event.event == SubmenuIndexEmulate) {
|
} else if(event.event == SubmenuIndexEmulate) {
|
||||||
scene_manager_set_scene_state(
|
|
||||||
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate);
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
|
||||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
|
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
|
||||||
DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
|
DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
|
||||||
@ -53,9 +59,11 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
|
|||||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexDetectReader) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcDetectReader);
|
||||||
|
consumed = true;
|
||||||
} else if(event.event == SubmenuIndexInfo) {
|
} else if(event.event == SubmenuIndexInfo) {
|
||||||
scene_manager_set_scene_state(
|
|
||||||
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexInfo);
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,10 +19,10 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) {
|
|||||||
Submenu* submenu = nfc->submenu;
|
Submenu* submenu = nfc->submenu;
|
||||||
MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data;
|
MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data;
|
||||||
|
|
||||||
if(data->data_read != data->data_size) {
|
if(!mf_ul_is_full_capture(data)) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Unlock With Password",
|
"Unlock",
|
||||||
SubmenuIndexUnlock,
|
SubmenuIndexUnlock,
|
||||||
nfc_scene_mf_ultralight_menu_submenu_callback,
|
nfc_scene_mf_ultralight_menu_submenu_callback,
|
||||||
nfc);
|
nfc);
|
||||||
|
|||||||
@ -24,25 +24,29 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState
|
|||||||
if(curr_state != state) {
|
if(curr_state != state) {
|
||||||
if(state == NfcSceneMfUlReadStateDetecting) {
|
if(state == NfcSceneMfUlReadStateDetecting) {
|
||||||
popup_reset(nfc->popup);
|
popup_reset(nfc->popup);
|
||||||
popup_set_text(
|
popup_set_text(nfc->popup, "Apply the\ntarget card", 97, 24, AlignCenter, AlignTop);
|
||||||
nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
|
|
||||||
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
|
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
|
||||||
|
nfc_blink_read_start(nfc);
|
||||||
} else if(state == NfcSceneMfUlReadStateReading) {
|
} else if(state == NfcSceneMfUlReadStateReading) {
|
||||||
popup_reset(nfc->popup);
|
popup_reset(nfc->popup);
|
||||||
popup_set_header(
|
popup_set_header(
|
||||||
nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop);
|
nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop);
|
||||||
popup_set_icon(nfc->popup, 12, 23, &A_Loading_24);
|
popup_set_icon(nfc->popup, 12, 23, &A_Loading_24);
|
||||||
|
nfc_blink_detect_start(nfc);
|
||||||
} else if(state == NfcSceneMfUlReadStateNotSupportedCard) {
|
} else if(state == NfcSceneMfUlReadStateNotSupportedCard) {
|
||||||
popup_reset(nfc->popup);
|
popup_reset(nfc->popup);
|
||||||
popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop);
|
popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop);
|
||||||
popup_set_text(
|
popup_set_text(
|
||||||
nfc->popup,
|
nfc->popup,
|
||||||
"Only MIFARE\nUltralight & NTAG\n are supported",
|
"Only MIFARE\nUltralight & NTAG\nare supported",
|
||||||
4,
|
4,
|
||||||
22,
|
22,
|
||||||
AlignLeft,
|
AlignLeft,
|
||||||
AlignTop);
|
AlignTop);
|
||||||
popup_set_icon(nfc->popup, 73, 20, &I_DolphinCommon_56x48);
|
popup_set_icon(nfc->popup, 73, 20, &I_DolphinCommon_56x48);
|
||||||
|
nfc_blink_stop(nfc);
|
||||||
|
notification_message(nfc->notifications, &sequence_error);
|
||||||
|
notification_message(nfc->notifications, &sequence_set_red_255);
|
||||||
}
|
}
|
||||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state);
|
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state);
|
||||||
}
|
}
|
||||||
@ -62,8 +66,6 @@ void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) {
|
|||||||
&nfc->dev->dev_data,
|
&nfc->dev->dev_data,
|
||||||
nfc_scene_mf_ultralight_read_auth_worker_callback,
|
nfc_scene_mf_ultralight_read_auth_worker_callback,
|
||||||
nfc);
|
nfc);
|
||||||
|
|
||||||
nfc_blink_read_start(nfc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) {
|
bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) {
|
||||||
@ -86,8 +88,17 @@ bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent
|
|||||||
nfc, NfcSceneMfUlReadStateNotSupportedCard);
|
nfc, NfcSceneMfUlReadStateNotSupportedCard);
|
||||||
}
|
}
|
||||||
} else if(event.type == SceneManagerEventTypeBack) {
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
|
||||||
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
NfcScene next_scene;
|
||||||
|
if(mf_ul_data->auth_method == MfUltralightAuthMethodManual) {
|
||||||
|
next_scene = NfcSceneMfUltralightKeyInput;
|
||||||
|
} else if(mf_ul_data->auth_method == MfUltralightAuthMethodAuto) {
|
||||||
|
next_scene = NfcSceneMfUltralightUnlockAuto;
|
||||||
|
} else {
|
||||||
|
next_scene = NfcSceneMfUltralightUnlockMenu;
|
||||||
|
}
|
||||||
|
consumed =
|
||||||
|
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, next_scene);
|
||||||
}
|
}
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,16 +19,20 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
|
|||||||
MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
|
MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
|
||||||
MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data);
|
MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data);
|
||||||
Widget* widget = nfc->widget;
|
Widget* widget = nfc->widget;
|
||||||
|
const char* title;
|
||||||
FuriString* temp_str;
|
FuriString* temp_str;
|
||||||
temp_str = furi_string_alloc();
|
temp_str = furi_string_alloc();
|
||||||
|
|
||||||
if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) {
|
if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) {
|
||||||
widget_add_string_element(
|
if(mf_ul_data->auth_success) {
|
||||||
widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!");
|
title = "All pages are unlocked!";
|
||||||
|
} else {
|
||||||
|
title = "All unlocked but failed auth!";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
widget_add_string_element(
|
title = "Not all pages unlocked!";
|
||||||
widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!");
|
|
||||||
}
|
}
|
||||||
|
widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, title);
|
||||||
furi_string_set(temp_str, "UID:");
|
furi_string_set(temp_str, "UID:");
|
||||||
for(size_t i = 0; i < nfc_data->uid_len; i++) {
|
for(size_t i = 0; i < nfc_data->uid_len; i++) {
|
||||||
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
|
||||||
@ -65,6 +69,7 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
|
|||||||
nfc);
|
nfc);
|
||||||
|
|
||||||
furi_string_free(temp_str);
|
furi_string_free(temp_str);
|
||||||
|
notification_message(nfc->notifications, &sequence_set_green_255);
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,8 +86,21 @@ bool nfc_scene_mf_ultralight_read_auth_result_on_event(void* context, SceneManag
|
|||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
} else if(event.type == SceneManagerEventTypeBack) {
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
|
||||||
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
if(mf_ul_data->auth_method == MfUltralightAuthMethodManual ||
|
||||||
|
mf_ul_data->auth_method == MfUltralightAuthMethodAuto) {
|
||||||
|
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||||
|
} else {
|
||||||
|
NfcScene next_scene;
|
||||||
|
if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) {
|
||||||
|
next_scene = NfcSceneMfUltralightMenu;
|
||||||
|
} else {
|
||||||
|
next_scene = NfcSceneMfUltralightUnlockMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
consumed =
|
||||||
|
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, next_scene);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
@ -93,4 +111,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_exit(void* context) {
|
|||||||
|
|
||||||
// Clean views
|
// Clean views
|
||||||
widget_reset(nfc->widget);
|
widget_reset(nfc->widget);
|
||||||
|
|
||||||
|
notification_message_block(nfc->notifications, &sequence_reset_green);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,64 @@
|
|||||||
|
#include "../nfc_i.h"
|
||||||
|
|
||||||
|
bool nfc_scene_mf_ultralight_unlock_auto_worker_callback(NfcWorkerEvent event, void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_unlock_auto_on_enter(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
|
||||||
|
// Setup view
|
||||||
|
widget_add_string_multiline_element(
|
||||||
|
nfc->widget,
|
||||||
|
54,
|
||||||
|
30,
|
||||||
|
AlignLeft,
|
||||||
|
AlignCenter,
|
||||||
|
FontPrimary,
|
||||||
|
"Touch the\nreader to get\npassword...");
|
||||||
|
widget_add_icon_element(nfc->widget, 0, 15, &I_Modern_reader_18x34);
|
||||||
|
widget_add_icon_element(nfc->widget, 20, 12, &I_Move_flipper_26x39);
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
|
|
||||||
|
// Start worker
|
||||||
|
nfc_worker_start(
|
||||||
|
nfc->worker,
|
||||||
|
NfcWorkerStateMfUltralightEmulate,
|
||||||
|
&nfc->dev->dev_data,
|
||||||
|
nfc_scene_mf_ultralight_unlock_auto_worker_callback,
|
||||||
|
nfc);
|
||||||
|
|
||||||
|
nfc_blink_read_start(nfc);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_ultralight_unlock_auto_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if((event.event == NfcWorkerEventMfUltralightPwdAuth)) {
|
||||||
|
MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth;
|
||||||
|
memcpy(nfc->byte_input_store, auth->pwd.raw, sizeof(auth->pwd.raw));
|
||||||
|
nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAuto;
|
||||||
|
nfc_worker_stop(nfc->worker);
|
||||||
|
notification_message(nfc->notifications, &sequence_success);
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_unlock_auto_on_exit(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
|
||||||
|
// Stop worker
|
||||||
|
nfc_worker_stop(nfc->worker);
|
||||||
|
// Clear view
|
||||||
|
widget_reset(nfc->widget);
|
||||||
|
|
||||||
|
nfc_blink_stop(nfc);
|
||||||
|
}
|
||||||
@ -1,9 +1,10 @@
|
|||||||
#include "../nfc_i.h"
|
#include "../nfc_i.h"
|
||||||
|
|
||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexMfUlUnlockMenuManual,
|
SubmenuIndexMfUlUnlockMenuAuto,
|
||||||
SubmenuIndexMfUlUnlockMenuAmeebo,
|
SubmenuIndexMfUlUnlockMenuAmeebo,
|
||||||
SubmenuIndexMfUlUnlockMenuXiaomi,
|
SubmenuIndexMfUlUnlockMenuXiaomi,
|
||||||
|
SubmenuIndexMfUlUnlockMenuManual,
|
||||||
};
|
};
|
||||||
|
|
||||||
void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) {
|
void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) {
|
||||||
@ -18,12 +19,14 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) {
|
|||||||
|
|
||||||
uint32_t state =
|
uint32_t state =
|
||||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
||||||
submenu_add_item(
|
if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) {
|
||||||
submenu,
|
submenu_add_item(
|
||||||
"Enter Password Manually",
|
submenu,
|
||||||
SubmenuIndexMfUlUnlockMenuManual,
|
"Unlock With Reader",
|
||||||
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
|
SubmenuIndexMfUlUnlockMenuAuto,
|
||||||
nfc);
|
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
}
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Auth As Ameebo",
|
"Auth As Ameebo",
|
||||||
@ -32,10 +35,16 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) {
|
|||||||
nfc);
|
nfc);
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Auth As Xiaomi",
|
"Auth As Xiaomi Air Purifier",
|
||||||
SubmenuIndexMfUlUnlockMenuXiaomi,
|
SubmenuIndexMfUlUnlockMenuXiaomi,
|
||||||
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
|
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
|
||||||
nfc);
|
nfc);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Enter Password Manually",
|
||||||
|
SubmenuIndexMfUlUnlockMenuManual,
|
||||||
|
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
|
||||||
|
nfc);
|
||||||
submenu_set_selected_item(submenu, state);
|
submenu_set_selected_item(submenu, state);
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||||
}
|
}
|
||||||
@ -57,8 +66,12 @@ bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEve
|
|||||||
nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi;
|
nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi;
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexMfUlUnlockMenuAuto) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto);
|
||||||
|
consumed = true;
|
||||||
}
|
}
|
||||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event);
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event);
|
||||||
}
|
}
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,15 +10,43 @@ void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result,
|
|||||||
void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) {
|
void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) {
|
||||||
Nfc* nfc = context;
|
Nfc* nfc = context;
|
||||||
DialogEx* dialog_ex = nfc->dialog_ex;
|
DialogEx* dialog_ex = nfc->dialog_ex;
|
||||||
|
MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method;
|
||||||
|
|
||||||
dialog_ex_set_context(dialog_ex, nfc);
|
dialog_ex_set_context(dialog_ex, nfc);
|
||||||
dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback);
|
dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback);
|
||||||
|
|
||||||
dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop);
|
if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) {
|
||||||
dialog_ex_set_text(
|
// Build dialog text
|
||||||
dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop);
|
MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth;
|
||||||
dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48);
|
FuriString* password_str =
|
||||||
dialog_ex_set_center_button_text(dialog_ex, "OK");
|
furi_string_alloc_set_str("Try to unlock the card with\npassword: ");
|
||||||
|
for(size_t i = 0; i < sizeof(auth->pwd.raw); ++i) {
|
||||||
|
furi_string_cat_printf(password_str, "%02X ", nfc->byte_input_store[i]);
|
||||||
|
}
|
||||||
|
furi_string_cat_str(password_str, "?\nCaution, a wrong password\ncan block the card!");
|
||||||
|
nfc_text_store_set(nfc, furi_string_get_cstr(password_str));
|
||||||
|
furi_string_free(password_str);
|
||||||
|
|
||||||
|
dialog_ex_set_header(
|
||||||
|
dialog_ex,
|
||||||
|
auth_method == MfUltralightAuthMethodAuto ? "Password captured!" : "Risky function!",
|
||||||
|
64,
|
||||||
|
0,
|
||||||
|
AlignCenter,
|
||||||
|
AlignTop);
|
||||||
|
dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 12, AlignCenter, AlignTop);
|
||||||
|
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
|
||||||
|
dialog_ex_set_right_button_text(dialog_ex, "Continue");
|
||||||
|
|
||||||
|
if(auth_method == MfUltralightAuthMethodAuto)
|
||||||
|
notification_message(nfc->notifications, &sequence_set_green_255);
|
||||||
|
} else {
|
||||||
|
dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop);
|
||||||
|
dialog_ex_set_text(
|
||||||
|
dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop);
|
||||||
|
dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48);
|
||||||
|
dialog_ex_set_center_button_text(dialog_ex, "OK");
|
||||||
|
}
|
||||||
|
|
||||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
|
||||||
}
|
}
|
||||||
@ -28,12 +56,33 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve
|
|||||||
|
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method;
|
||||||
if(event.event == DialogExResultCenter) {
|
if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth);
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
DOLPHIN_DEED(DolphinDeedNfcRead);
|
if(event.event == DialogExResultRight) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth);
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcRead);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == DialogExResultLeft) {
|
||||||
|
if(auth_method == MfUltralightAuthMethodAuto) {
|
||||||
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
||||||
|
} else {
|
||||||
|
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
// Cannot press back
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == DialogExResultCenter) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth);
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcRead);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
@ -43,5 +92,7 @@ void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) {
|
|||||||
Nfc* nfc = context;
|
Nfc* nfc = context;
|
||||||
|
|
||||||
dialog_ex_reset(nfc->dialog_ex);
|
dialog_ex_reset(nfc->dialog_ex);
|
||||||
submenu_reset(nfc->submenu);
|
nfc_text_store_clear(nfc);
|
||||||
|
|
||||||
|
notification_message_block(nfc->notifications, &sequence_reset_green);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,6 +87,20 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
|
|||||||
temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4);
|
temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4);
|
||||||
if(data->data_size > data->data_read) {
|
if(data->data_size > data->data_read) {
|
||||||
furi_string_cat_printf(temp_str, "\nPassword-protected");
|
furi_string_cat_printf(temp_str, "\nPassword-protected");
|
||||||
|
} else if(data->auth_success) {
|
||||||
|
MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
temp_str,
|
||||||
|
"\nPassword: %02X %02X %02X %02X",
|
||||||
|
config_pages->auth_data.pwd.raw[0],
|
||||||
|
config_pages->auth_data.pwd.raw[1],
|
||||||
|
config_pages->auth_data.pwd.raw[2],
|
||||||
|
config_pages->auth_data.pwd.raw[3]);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
temp_str,
|
||||||
|
"\nPACK: %02X %02X",
|
||||||
|
config_pages->auth_data.pack.raw[0],
|
||||||
|
config_pages->auth_data.pack.raw[1]);
|
||||||
}
|
}
|
||||||
} else if(protocol == NfcDeviceProtocolMifareClassic) {
|
} else if(protocol == NfcDeviceProtocolMifareClassic) {
|
||||||
MfClassicData* data = &dev_data->mf_classic_data;
|
MfClassicData* data = &dev_data->mf_classic_data;
|
||||||
@ -115,7 +129,7 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
|
|||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == GuiButtonTypeRight) {
|
if(event.event == GuiButtonTypeRight) {
|
||||||
if(protocol == NfcDeviceProtocolMifareDesfire) {
|
if(protocol == NfcDeviceProtocolMifareDesfire) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(protocol == NfcDeviceProtocolMifareUl) {
|
} else if(protocol == NfcDeviceProtocolMifareUl) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData);
|
||||||
|
|||||||
@ -70,6 +70,8 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
|
|||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event.event == NfcWorkerEventReadMfUltralight) {
|
} else if(event.event == NfcWorkerEventReadMfUltralight) {
|
||||||
notification_message(nfc->notifications, &sequence_success);
|
notification_message(nfc->notifications, &sequence_success);
|
||||||
|
// Set unlock password input to 0xFFFFFFFF only on fresh read
|
||||||
|
memset(nfc->byte_input_store, 0xFF, 4);
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess);
|
||||||
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
|||||||
@ -11,6 +11,8 @@ enum SubmenuIndex {
|
|||||||
SubmenuIndexDelete,
|
SubmenuIndexDelete,
|
||||||
SubmenuIndexInfo,
|
SubmenuIndexInfo,
|
||||||
SubmenuIndexRestoreOriginal,
|
SubmenuIndexRestoreOriginal,
|
||||||
|
SubmenuIndexMfUlUnlockByReader,
|
||||||
|
SubmenuIndexMfUlUnlockByPassword,
|
||||||
};
|
};
|
||||||
|
|
||||||
void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
|
void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
|
||||||
@ -69,6 +71,21 @@ void nfc_scene_saved_menu_on_enter(void* context) {
|
|||||||
}
|
}
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
|
submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
|
||||||
|
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl &&
|
||||||
|
!mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) {
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Unlock With Reader",
|
||||||
|
SubmenuIndexMfUlUnlockByReader,
|
||||||
|
nfc_scene_saved_menu_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Unlock With Password",
|
||||||
|
SubmenuIndexMfUlUnlockByPassword,
|
||||||
|
nfc_scene_saved_menu_submenu_callback,
|
||||||
|
nfc);
|
||||||
|
}
|
||||||
if(nfc->dev->shadow_file_exist) {
|
if(nfc->dev->shadow_file_exist) {
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
@ -106,6 +123,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event.event == SubmenuIndexDetectReader) {
|
} else if(event.event == SubmenuIndexDetectReader) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcDetectReader);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event.event == SubmenuIndexWrite) {
|
} else if(event.event == SubmenuIndexWrite) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite);
|
||||||
@ -141,6 +159,12 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
} else if(event.event == SubmenuIndexRestoreOriginal) {
|
} else if(event.event == SubmenuIndexRestoreOriginal) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexMfUlUnlockByReader) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexMfUlUnlockByPassword) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
||||||
|
consumed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,11 +59,8 @@ SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) {
|
|||||||
|
|
||||||
instance->cli = cli;
|
instance->cli = cli;
|
||||||
|
|
||||||
instance->thread = furi_thread_alloc();
|
instance->thread =
|
||||||
furi_thread_set_name(instance->thread, "SubGhzChat");
|
furi_thread_alloc_ex("SubGhzChat", 2048, subghz_chat_worker_thread, instance);
|
||||||
furi_thread_set_stack_size(instance->thread, 2048);
|
|
||||||
furi_thread_set_context(instance->thread, instance);
|
|
||||||
furi_thread_set_callback(instance->thread, subghz_chat_worker_thread);
|
|
||||||
instance->subghz_txrx = subghz_tx_rx_worker_alloc();
|
instance->subghz_txrx = subghz_tx_rx_worker_alloc();
|
||||||
instance->event_queue = furi_message_queue_alloc(80, sizeof(SubGhzChatEvent));
|
instance->event_queue = furi_message_queue_alloc(80, sizeof(SubGhzChatEvent));
|
||||||
return instance;
|
return instance;
|
||||||
|
|||||||
@ -25,7 +25,7 @@ TUPLE_DEF2(
|
|||||||
(frequency, uint32_t),
|
(frequency, uint32_t),
|
||||||
(count, uint8_t),
|
(count, uint8_t),
|
||||||
(rssi_max, uint8_t))
|
(rssi_max, uint8_t))
|
||||||
/* Register globaly the oplist */
|
/* Register globally the oplist */
|
||||||
#define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \
|
#define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \
|
||||||
TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
#define TAG "SubghzFrequencyAnalyzerWorker"
|
#define TAG "SubghzFrequencyAnalyzerWorker"
|
||||||
|
|
||||||
#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -95.0f
|
|
||||||
|
|
||||||
static const uint8_t subghz_preset_ook_58khz[][2] = {
|
static const uint8_t subghz_preset_ook_58khz[][2] = {
|
||||||
{CC1101_MDMCFG4, 0b11110111}, // Rx BW filter is 58.035714kHz
|
{CC1101_MDMCFG4, 0b11110111}, // Rx BW filter is 58.035714kHz
|
||||||
/* End */
|
/* End */
|
||||||
@ -71,7 +69,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
|||||||
.frequency_coarse = 0, .rssi_coarse = 0, .frequency_fine = 0, .rssi_fine = 0};
|
.frequency_coarse = 0, .rssi_coarse = 0, .frequency_fine = 0, .rssi_fine = 0};
|
||||||
float rssi = 0;
|
float rssi = 0;
|
||||||
uint32_t frequency = 0;
|
uint32_t frequency = 0;
|
||||||
float rssi_temp = 0;
|
float rssi_temp = -127.0f;
|
||||||
uint32_t frequency_temp = 0;
|
uint32_t frequency_temp = 0;
|
||||||
CC1101Status status;
|
CC1101Status status;
|
||||||
|
|
||||||
@ -196,7 +194,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
|||||||
TAG, "=:%lu:%f", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine);
|
TAG, "=:%lu:%f", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine);
|
||||||
|
|
||||||
instance->sample_hold_counter = 20;
|
instance->sample_hold_counter = 20;
|
||||||
rssi_temp = frequency_rssi.rssi_fine;
|
rssi_temp = (rssi_temp + frequency_rssi.rssi_fine) / 2;
|
||||||
frequency_temp = frequency_rssi.frequency_fine;
|
frequency_temp = frequency_rssi.frequency_fine;
|
||||||
|
|
||||||
if(instance->filVal) {
|
if(instance->filVal) {
|
||||||
@ -207,10 +205,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
|||||||
// Deliver callback
|
// Deliver callback
|
||||||
if(instance->pair_callback) {
|
if(instance->pair_callback) {
|
||||||
instance->pair_callback(
|
instance->pair_callback(
|
||||||
instance->context,
|
instance->context, frequency_rssi.frequency_fine, rssi_temp, true);
|
||||||
frequency_rssi.frequency_fine,
|
|
||||||
frequency_rssi.rssi_fine,
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
} else if( // Deliver results coarse
|
} else if( // Deliver results coarse
|
||||||
(frequency_rssi.rssi_coarse > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) &&
|
(frequency_rssi.rssi_coarse > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) &&
|
||||||
@ -222,7 +217,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
|||||||
(double)frequency_rssi.rssi_coarse);
|
(double)frequency_rssi.rssi_coarse);
|
||||||
|
|
||||||
instance->sample_hold_counter = 20;
|
instance->sample_hold_counter = 20;
|
||||||
rssi_temp = frequency_rssi.rssi_coarse;
|
rssi_temp = (rssi_temp + frequency_rssi.rssi_coarse) / 2;
|
||||||
frequency_temp = frequency_rssi.frequency_coarse;
|
frequency_temp = frequency_rssi.frequency_coarse;
|
||||||
if(instance->filVal) {
|
if(instance->filVal) {
|
||||||
frequency_rssi.frequency_coarse =
|
frequency_rssi.frequency_coarse =
|
||||||
@ -232,15 +227,12 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
|||||||
// Deliver callback
|
// Deliver callback
|
||||||
if(instance->pair_callback) {
|
if(instance->pair_callback) {
|
||||||
instance->pair_callback(
|
instance->pair_callback(
|
||||||
instance->context,
|
instance->context, frequency_rssi.frequency_coarse, rssi_temp, true);
|
||||||
frequency_rssi.frequency_coarse,
|
|
||||||
frequency_rssi.rssi_coarse,
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(instance->sample_hold_counter > 0) {
|
if(instance->sample_hold_counter > 0) {
|
||||||
instance->sample_hold_counter--;
|
instance->sample_hold_counter--;
|
||||||
if(instance->sample_hold_counter == 18) {
|
if(instance->sample_hold_counter == 15) {
|
||||||
if(instance->pair_callback) {
|
if(instance->pair_callback) {
|
||||||
instance->pair_callback(
|
instance->pair_callback(
|
||||||
instance->context, frequency_temp, rssi_temp, false);
|
instance->context, frequency_temp, rssi_temp, false);
|
||||||
@ -248,8 +240,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
instance->filVal = 0;
|
instance->filVal = 0;
|
||||||
if(instance->pair_callback)
|
rssi_temp = -127.0f;
|
||||||
instance->pair_callback(instance->context, 0, 0, false);
|
instance->pair_callback(instance->context, 0, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,12 +257,8 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont
|
|||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
SubGhzFrequencyAnalyzerWorker* instance = malloc(sizeof(SubGhzFrequencyAnalyzerWorker));
|
SubGhzFrequencyAnalyzerWorker* instance = malloc(sizeof(SubGhzFrequencyAnalyzerWorker));
|
||||||
|
|
||||||
instance->thread = furi_thread_alloc();
|
instance->thread = furi_thread_alloc_ex(
|
||||||
furi_thread_set_name(instance->thread, "SubGhzFAWorker");
|
"SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance);
|
||||||
furi_thread_set_stack_size(instance->thread, 2048);
|
|
||||||
furi_thread_set_context(instance->thread, instance);
|
|
||||||
furi_thread_set_callback(instance->thread, subghz_frequency_analyzer_worker_thread);
|
|
||||||
|
|
||||||
SubGhz* subghz = context;
|
SubGhz* subghz = context;
|
||||||
instance->setting = subghz->setting;
|
instance->setting = subghz->setting;
|
||||||
return instance;
|
return instance;
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
|
|
||||||
|
#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -93.0f
|
||||||
|
|
||||||
typedef struct SubGhzFrequencyAnalyzerWorker SubGhzFrequencyAnalyzerWorker;
|
typedef struct SubGhzFrequencyAnalyzerWorker SubGhzFrequencyAnalyzerWorker;
|
||||||
|
|
||||||
typedef void (*SubGhzFrequencyAnalyzerWorkerPairCallback)(
|
typedef void (*SubGhzFrequencyAnalyzerWorkerPairCallback)(
|
||||||
|
|||||||
@ -300,7 +300,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
|
|||||||
|
|
||||||
furi_hal_power_suppress_charge_exit();
|
furi_hal_power_suppress_charge_exit();
|
||||||
|
|
||||||
printf("\r\nPackets recieved %u\r\n", instance->packet_count);
|
printf("\r\nPackets received %u\r\n", instance->packet_count);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
subghz_receiver_free(receiver);
|
subghz_receiver_free(receiver);
|
||||||
@ -408,7 +408,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\r\nPackets recieved \033[0;32m%u\033[0m\r\n", instance->packet_count);
|
printf("\r\nPackets received \033[0;32m%u\033[0m\r\n", instance->packet_count);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
subghz_receiver_free(receiver);
|
subghz_receiver_free(receiver);
|
||||||
@ -438,7 +438,7 @@ static void subghz_cli_command_print_usage() {
|
|||||||
printf("\r\n");
|
printf("\r\n");
|
||||||
printf(" debug cmd:\r\n");
|
printf(" debug cmd:\r\n");
|
||||||
printf("\ttx_carrier <frequency:in Hz>\t - Transmit carrier\r\n");
|
printf("\ttx_carrier <frequency:in Hz>\t - Transmit carrier\r\n");
|
||||||
printf("\trx_carrier <frequency:in Hz>\t - Receiv carrier\r\n");
|
printf("\trx_carrier <frequency:in Hz>\t - Receive carrier\r\n");
|
||||||
printf(
|
printf(
|
||||||
"\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n");
|
"\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n");
|
||||||
printf(
|
printf(
|
||||||
|
|||||||
@ -13,8 +13,6 @@
|
|||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
|
|
||||||
#define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem)
|
#define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem)
|
||||||
#define RSSI_OFFSET 74
|
|
||||||
#define RSSI_MAX 53 // 127 - RSSI_OFFSET
|
|
||||||
|
|
||||||
#define SNPRINTF_FREQUENCY(buff, freq) \
|
#define SNPRINTF_FREQUENCY(buff, freq) \
|
||||||
snprintf(buff, sizeof(buff), "%03ld.%03ld", freq / 1000000 % 1000, freq / 1000 % 1000);
|
snprintf(buff, sizeof(buff), "%03ld.%03ld", freq / 1000000 % 1000, freq / 1000 % 1000);
|
||||||
@ -49,7 +47,7 @@ typedef struct {
|
|||||||
} SubGhzFrequencyAnalyzerModel;
|
} SubGhzFrequencyAnalyzerModel;
|
||||||
|
|
||||||
static inline uint8_t rssi_sanitize(float rssi) {
|
static inline uint8_t rssi_sanitize(float rssi) {
|
||||||
return (rssi * -1.0f) - RSSI_OFFSET;
|
return (rssi ? (uint8_t)(rssi - SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void subghz_frequency_analyzer_set_callback(
|
void subghz_frequency_analyzer_set_callback(
|
||||||
@ -65,12 +63,25 @@ void subghz_frequency_analyzer_set_callback(
|
|||||||
void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) {
|
void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) {
|
||||||
uint8_t column_number = 0;
|
uint8_t column_number = 0;
|
||||||
if(rssi) {
|
if(rssi) {
|
||||||
rssi = rssi / 3;
|
rssi = rssi / 3 + 2;
|
||||||
|
if(rssi > 20) rssi = 20;
|
||||||
for(uint8_t i = 1; i < rssi; i++) {
|
for(uint8_t i = 1; i < rssi; i++) {
|
||||||
if(i > 20) break;
|
|
||||||
if(i % 4) {
|
if(i % 4) {
|
||||||
column_number++;
|
column_number++;
|
||||||
canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, 4 + column_number);
|
canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, column_number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_frequency_analyzer_draw_log_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) {
|
||||||
|
uint8_t column_height = 6;
|
||||||
|
if(rssi) {
|
||||||
|
//rssi = rssi
|
||||||
|
if(rssi > 54) rssi = 54;
|
||||||
|
for(uint8_t i = 1; i < rssi; i++) {
|
||||||
|
if(i % 5) {
|
||||||
|
canvas_draw_box(canvas, x + i, y - column_height, 1, column_height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,9 +97,9 @@ static void subghz_frequency_analyzer_log_frequency_draw(
|
|||||||
|
|
||||||
const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency);
|
const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency);
|
||||||
if(items_count == 0) {
|
if(items_count == 0) {
|
||||||
canvas_draw_rframe(canvas, offset_x + 27u, offset_y - 3u, 73u, 16u, 5u);
|
canvas_draw_rframe(canvas, offset_x + 27, offset_y - 3, 73, 16, 5);
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, offset_x + 64u, offset_y + 8u, AlignCenter, AlignBottom, "No records");
|
canvas, offset_x + 64, offset_y + 8, AlignCenter, AlignBottom, "No records");
|
||||||
return;
|
return;
|
||||||
} else if(items_count > 3) {
|
} else if(items_count > 3) {
|
||||||
elements_scrollbar_pos(
|
elements_scrollbar_pos(
|
||||||
@ -117,7 +128,7 @@ static void subghz_frequency_analyzer_log_frequency_draw(
|
|||||||
canvas_draw_str(canvas, offset_x + 48, offset_y + i * 10, buffer);
|
canvas_draw_str(canvas, offset_x + 48, offset_y + i * 10, buffer);
|
||||||
|
|
||||||
// Max RSSI
|
// Max RSSI
|
||||||
subghz_frequency_analyzer_draw_rssi(
|
subghz_frequency_analyzer_draw_log_rssi(
|
||||||
canvas, (*log_frequency_item)->rssi_max, offset_x + 69, (offset_y + i * 10));
|
canvas, (*log_frequency_item)->rssi_max, offset_x + 69, (offset_y + i * 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,25 +178,20 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel
|
|||||||
} else {
|
} else {
|
||||||
canvas_draw_str(canvas, 20, 8, "Frequency Analyzer");
|
canvas_draw_str(canvas, 20, 8, "Frequency Analyzer");
|
||||||
canvas_draw_str(canvas, 0, 64, "RSSI");
|
canvas_draw_str(canvas, 0, 64, "RSSI");
|
||||||
subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20u, 64u);
|
subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20, 64);
|
||||||
|
|
||||||
subghz_frequency_analyzer_history_frequency_draw(canvas, model);
|
subghz_frequency_analyzer_history_frequency_draw(canvas, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frequency
|
// Frequency
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
canvas_set_font(canvas, FontBigNumbers);
|
||||||
snprintf(
|
SNPRINTF_FREQUENCY(buffer, model->frequency);
|
||||||
buffer,
|
|
||||||
sizeof(buffer),
|
|
||||||
"%03ld.%03ld",
|
|
||||||
model->frequency / 1000000 % 1000,
|
|
||||||
model->frequency / 1000 % 1000);
|
|
||||||
if(model->signal) {
|
if(model->signal) {
|
||||||
canvas_draw_box(canvas, 4, 12, 121, 22);
|
canvas_draw_box(canvas, 4, 11, 121, 22);
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
}
|
}
|
||||||
canvas_draw_str(canvas, 8, 30, buffer);
|
canvas_draw_str(canvas, 8, 29, buffer);
|
||||||
canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11);
|
canvas_draw_icon(canvas, 96, 18, &I_MHz_25x11);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void subghz_frequency_analyzer_log_frequency_sort(SubGhzFrequencyAnalyzerModel* model) {
|
static void subghz_frequency_analyzer_log_frequency_sort(SubGhzFrequencyAnalyzerModel* model) {
|
||||||
@ -292,7 +298,7 @@ static bool subghz_frequency_analyzer_log_frequency_insert(SubGhzFrequencyAnalyz
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
(*item)->frequency = model->frequency;
|
(*item)->frequency = model->frequency;
|
||||||
(*item)->count = 1u;
|
(*item)->count = 1;
|
||||||
(*item)->rssi_max = model->rssi;
|
(*item)->rssi_max = model->rssi;
|
||||||
(*item)->seq = items_count;
|
(*item)->seq = items_count;
|
||||||
return true;
|
return true;
|
||||||
@ -394,9 +400,9 @@ void subghz_frequency_analyzer_enter(void* context) {
|
|||||||
model->frequency = 0;
|
model->frequency = 0;
|
||||||
model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain;
|
model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain;
|
||||||
model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc;
|
model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc;
|
||||||
model->log_frequency_scroll_offset = 0u;
|
model->log_frequency_scroll_offset = 0;
|
||||||
model->history_frequency[0] = model->history_frequency[1] =
|
model->history_frequency[0] = model->history_frequency[1] =
|
||||||
model->history_frequency[2] = 0u;
|
model->history_frequency[2] = 0;
|
||||||
SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency);
|
SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency);
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
@ -416,13 +422,13 @@ void subghz_frequency_analyzer_exit(void* context) {
|
|||||||
instance->view,
|
instance->view,
|
||||||
SubGhzFrequencyAnalyzerModel * model,
|
SubGhzFrequencyAnalyzerModel * model,
|
||||||
{
|
{
|
||||||
model->rssi = 0u;
|
model->rssi = 0;
|
||||||
model->frequency = 0;
|
model->frequency = 0;
|
||||||
model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain;
|
model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain;
|
||||||
model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc;
|
model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc;
|
||||||
model->log_frequency_scroll_offset = 0u;
|
model->log_frequency_scroll_offset = 0;
|
||||||
model->history_frequency[0] = model->history_frequency[1] =
|
model->history_frequency[0] = model->history_frequency[1] =
|
||||||
model->history_frequency[2] = 0u;
|
model->history_frequency[2] = 0;
|
||||||
SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency);
|
SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency);
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
|
|||||||
@ -58,13 +58,13 @@ struct U2fHid_packet {
|
|||||||
struct U2fHid {
|
struct U2fHid {
|
||||||
FuriThread* thread;
|
FuriThread* thread;
|
||||||
FuriTimer* lock_timer;
|
FuriTimer* lock_timer;
|
||||||
struct U2fHid_packet packet;
|
|
||||||
uint8_t seq_id_last;
|
uint8_t seq_id_last;
|
||||||
uint16_t req_buf_ptr;
|
uint16_t req_buf_ptr;
|
||||||
uint32_t req_len_left;
|
uint32_t req_len_left;
|
||||||
uint32_t lock_cid;
|
uint32_t lock_cid;
|
||||||
bool lock;
|
bool lock;
|
||||||
U2fData* u2f_instance;
|
U2fData* u2f_instance;
|
||||||
|
struct U2fHid_packet packet;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void u2f_hid_event_callback(HidU2fEvent ev, void* context) {
|
static void u2f_hid_event_callback(HidU2fEvent ev, void* context) {
|
||||||
@ -215,10 +215,21 @@ static int32_t u2f_hid_worker(void* context) {
|
|||||||
}
|
}
|
||||||
if(flags & WorkerEvtRequest) {
|
if(flags & WorkerEvtRequest) {
|
||||||
uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf);
|
uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf);
|
||||||
if(len_cur > 0) {
|
do {
|
||||||
|
if(len_cur == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if((packet_buf[4] & U2F_HID_TYPE_MASK) == U2F_HID_TYPE_INIT) {
|
if((packet_buf[4] & U2F_HID_TYPE_MASK) == U2F_HID_TYPE_INIT) {
|
||||||
|
if(len_cur < 7) {
|
||||||
|
u2f_hid->req_len_left = 0;
|
||||||
|
break; // Wrong chunk len
|
||||||
|
}
|
||||||
// Init packet
|
// Init packet
|
||||||
u2f_hid->packet.len = (packet_buf[5] << 8) | (packet_buf[6]);
|
u2f_hid->packet.len = (packet_buf[5] << 8) | (packet_buf[6]);
|
||||||
|
if(u2f_hid->packet.len > U2F_HID_MAX_PAYLOAD_LEN) {
|
||||||
|
u2f_hid->req_len_left = 0;
|
||||||
|
break; // Wrong packet len
|
||||||
|
}
|
||||||
if(u2f_hid->packet.len > (len_cur - 7)) {
|
if(u2f_hid->packet.len > (len_cur - 7)) {
|
||||||
u2f_hid->req_len_left = u2f_hid->packet.len - (len_cur - 7);
|
u2f_hid->req_len_left = u2f_hid->packet.len - (len_cur - 7);
|
||||||
len_cur = len_cur - 7;
|
len_cur = len_cur - 7;
|
||||||
@ -232,6 +243,10 @@ static int32_t u2f_hid_worker(void* context) {
|
|||||||
u2f_hid->req_buf_ptr = len_cur;
|
u2f_hid->req_buf_ptr = len_cur;
|
||||||
if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur);
|
if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur);
|
||||||
} else {
|
} else {
|
||||||
|
if(len_cur < 5) {
|
||||||
|
u2f_hid->req_len_left = 0;
|
||||||
|
break; // Wrong chunk len
|
||||||
|
}
|
||||||
// Continuation packet
|
// Continuation packet
|
||||||
if(u2f_hid->req_len_left > 0) {
|
if(u2f_hid->req_len_left > 0) {
|
||||||
uint32_t cid_temp = 0;
|
uint32_t cid_temp = 0;
|
||||||
@ -260,7 +275,7 @@ static int32_t u2f_hid_worker(void* context) {
|
|||||||
u2f_hid_send_error(u2f_hid, U2F_HID_ERR_INVALID_CMD);
|
u2f_hid_send_error(u2f_hid, U2F_HID_ERR_INVALID_CMD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} while(0);
|
||||||
}
|
}
|
||||||
if(flags & WorkerEvtUnlock) {
|
if(flags & WorkerEvtUnlock) {
|
||||||
u2f_hid->lock = false;
|
u2f_hid->lock = false;
|
||||||
@ -282,11 +297,7 @@ U2fHid* u2f_hid_start(U2fData* u2f_inst) {
|
|||||||
|
|
||||||
u2f_hid->u2f_instance = u2f_inst;
|
u2f_hid->u2f_instance = u2f_inst;
|
||||||
|
|
||||||
u2f_hid->thread = furi_thread_alloc();
|
u2f_hid->thread = furi_thread_alloc_ex("U2fHidWorker", 2048, u2f_hid_worker, u2f_hid);
|
||||||
furi_thread_set_name(u2f_hid->thread, "U2fHidWorker");
|
|
||||||
furi_thread_set_stack_size(u2f_hid->thread, 2048);
|
|
||||||
furi_thread_set_context(u2f_hid->thread, u2f_hid);
|
|
||||||
furi_thread_set_callback(u2f_hid->thread, u2f_hid_worker);
|
|
||||||
furi_thread_start(u2f_hid->thread);
|
furi_thread_start(u2f_hid->thread);
|
||||||
return u2f_hid;
|
return u2f_hid;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,10 +37,10 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
} else if(model->display_msg == U2fMsgSuccess) {
|
} else if(model->display_msg == U2fMsgSuccess) {
|
||||||
canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31);
|
canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31);
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successfull!");
|
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successful!");
|
||||||
} else if(model->display_msg == U2fMsgError) {
|
} else if(model->display_msg == U2fMsgError) {
|
||||||
canvas_draw_icon(canvas, 22, 15, &I_Error_62x31);
|
canvas_draw_icon(canvas, 22, 15, &I_Error_62x31);
|
||||||
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Ceritficate error");
|
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Certificate error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
App(
|
|
||||||
appid="bt_hid",
|
|
||||||
name="Bluetooth Remote",
|
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
|
||||||
entry_point="bt_hid_app",
|
|
||||||
stack_size=1 * 1024,
|
|
||||||
fap_category="Tools",
|
|
||||||
fap_icon="bt_remote_10px.png",
|
|
||||||
fap_icon_assets="assets",
|
|
||||||
)
|
|
||||||
@ -1,216 +0,0 @@
|
|||||||
#include "bt_hid.h"
|
|
||||||
#include <furi_hal_bt.h>
|
|
||||||
#include <notification/notification_messages.h>
|
|
||||||
#include <dolphin/dolphin.h>
|
|
||||||
|
|
||||||
#define TAG "BtHidApp"
|
|
||||||
|
|
||||||
enum BtDebugSubmenuIndex {
|
|
||||||
BtHidSubmenuIndexKeynote,
|
|
||||||
BtHidSubmenuIndexKeyboard,
|
|
||||||
BtHidSubmenuIndexMedia,
|
|
||||||
BtHidSubmenuIndexTikTok,
|
|
||||||
BtHidSubmenuIndexMouse,
|
|
||||||
};
|
|
||||||
|
|
||||||
void bt_hid_submenu_callback(void* context, uint32_t index) {
|
|
||||||
furi_assert(context);
|
|
||||||
BtHid* app = context;
|
|
||||||
if(index == BtHidSubmenuIndexKeynote) {
|
|
||||||
app->view_id = BtHidViewKeynote;
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote);
|
|
||||||
} else if(index == BtHidSubmenuIndexKeyboard) {
|
|
||||||
app->view_id = BtHidViewKeyboard;
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeyboard);
|
|
||||||
} else if(index == BtHidSubmenuIndexMedia) {
|
|
||||||
app->view_id = BtHidViewMedia;
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMedia);
|
|
||||||
} else if(index == BtHidSubmenuIndexMouse) {
|
|
||||||
app->view_id = BtHidViewMouse;
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse);
|
|
||||||
} else if(index == BtHidSubmenuIndexTikTok) {
|
|
||||||
app->view_id = BtHidViewTikTok;
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bt_hid_dialog_callback(DialogExResult result, void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
BtHid* app = context;
|
|
||||||
if(result == DialogExResultLeft) {
|
|
||||||
view_dispatcher_stop(app->view_dispatcher);
|
|
||||||
} else if(result == DialogExResultRight) {
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view
|
|
||||||
} else if(result == DialogExResultCenter) {
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewSubmenu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t bt_hid_exit_confirm_view(void* context) {
|
|
||||||
UNUSED(context);
|
|
||||||
return BtHidViewExitConfirm;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t bt_hid_exit(void* context) {
|
|
||||||
UNUSED(context);
|
|
||||||
return VIEW_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {
|
|
||||||
furi_assert(context);
|
|
||||||
BtHid* bt_hid = context;
|
|
||||||
bool connected = (status == BtStatusConnected);
|
|
||||||
if(connected) {
|
|
||||||
notification_internal_message(bt_hid->notifications, &sequence_set_blue_255);
|
|
||||||
} else {
|
|
||||||
notification_internal_message(bt_hid->notifications, &sequence_reset_blue);
|
|
||||||
}
|
|
||||||
bt_hid_keynote_set_connected_status(bt_hid->bt_hid_keynote, connected);
|
|
||||||
bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected);
|
|
||||||
bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected);
|
|
||||||
bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected);
|
|
||||||
bt_hid_tiktok_set_connected_status(bt_hid->bt_hid_tiktok, connected);
|
|
||||||
}
|
|
||||||
|
|
||||||
BtHid* bt_hid_app_alloc() {
|
|
||||||
BtHid* app = malloc(sizeof(BtHid));
|
|
||||||
|
|
||||||
// Gui
|
|
||||||
app->gui = furi_record_open(RECORD_GUI);
|
|
||||||
|
|
||||||
// Bt
|
|
||||||
app->bt = furi_record_open(RECORD_BT);
|
|
||||||
|
|
||||||
// Notifications
|
|
||||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Submenu view
|
|
||||||
app->submenu = submenu_alloc();
|
|
||||||
submenu_add_item(
|
|
||||||
app->submenu, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app);
|
|
||||||
submenu_add_item(
|
|
||||||
app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app);
|
|
||||||
submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app);
|
|
||||||
submenu_add_item(
|
|
||||||
app->submenu, "TikTok Controller", BtHidSubmenuIndexTikTok, bt_hid_submenu_callback, app);
|
|
||||||
submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app);
|
|
||||||
view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit);
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher, BtHidViewSubmenu, submenu_get_view(app->submenu));
|
|
||||||
|
|
||||||
// Dialog view
|
|
||||||
app->dialog = dialog_ex_alloc();
|
|
||||||
dialog_ex_set_result_callback(app->dialog, bt_hid_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_add_view(
|
|
||||||
app->view_dispatcher, BtHidViewExitConfirm, dialog_ex_get_view(app->dialog));
|
|
||||||
|
|
||||||
// Keynote view
|
|
||||||
app->bt_hid_keynote = bt_hid_keynote_alloc();
|
|
||||||
view_set_previous_callback(
|
|
||||||
bt_hid_keynote_get_view(app->bt_hid_keynote), bt_hid_exit_confirm_view);
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher, BtHidViewKeynote, bt_hid_keynote_get_view(app->bt_hid_keynote));
|
|
||||||
|
|
||||||
// Keyboard view
|
|
||||||
app->bt_hid_keyboard = bt_hid_keyboard_alloc();
|
|
||||||
view_set_previous_callback(
|
|
||||||
bt_hid_keyboard_get_view(app->bt_hid_keyboard), bt_hid_exit_confirm_view);
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher, BtHidViewKeyboard, bt_hid_keyboard_get_view(app->bt_hid_keyboard));
|
|
||||||
|
|
||||||
// Media view
|
|
||||||
app->bt_hid_media = bt_hid_media_alloc();
|
|
||||||
view_set_previous_callback(bt_hid_media_get_view(app->bt_hid_media), bt_hid_exit_confirm_view);
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media));
|
|
||||||
|
|
||||||
// TikTok view
|
|
||||||
app->bt_hid_tiktok = bt_hid_tiktok_alloc();
|
|
||||||
view_set_previous_callback(
|
|
||||||
bt_hid_tiktok_get_view(app->bt_hid_tiktok), bt_hid_exit_confirm_view);
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher, BtHidViewTikTok, bt_hid_tiktok_get_view(app->bt_hid_tiktok));
|
|
||||||
|
|
||||||
// Mouse view
|
|
||||||
app->bt_hid_mouse = bt_hid_mouse_alloc();
|
|
||||||
view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view);
|
|
||||||
view_dispatcher_add_view(
|
|
||||||
app->view_dispatcher, BtHidViewMouse, bt_hid_mouse_get_view(app->bt_hid_mouse));
|
|
||||||
|
|
||||||
// TODO switch to menu after Media is done
|
|
||||||
app->view_id = BtHidViewSubmenu;
|
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bt_hid_app_free(BtHid* app) {
|
|
||||||
furi_assert(app);
|
|
||||||
|
|
||||||
// Reset notification
|
|
||||||
notification_internal_message(app->notifications, &sequence_reset_blue);
|
|
||||||
|
|
||||||
// Free views
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewSubmenu);
|
|
||||||
submenu_free(app->submenu);
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewExitConfirm);
|
|
||||||
dialog_ex_free(app->dialog);
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeynote);
|
|
||||||
bt_hid_keynote_free(app->bt_hid_keynote);
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeyboard);
|
|
||||||
bt_hid_keyboard_free(app->bt_hid_keyboard);
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMedia);
|
|
||||||
bt_hid_media_free(app->bt_hid_media);
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse);
|
|
||||||
bt_hid_mouse_free(app->bt_hid_mouse);
|
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
|
|
||||||
bt_hid_tiktok_free(app->bt_hid_tiktok);
|
|
||||||
view_dispatcher_free(app->view_dispatcher);
|
|
||||||
|
|
||||||
// Close records
|
|
||||||
furi_record_close(RECORD_GUI);
|
|
||||||
app->gui = NULL;
|
|
||||||
furi_record_close(RECORD_NOTIFICATION);
|
|
||||||
app->notifications = NULL;
|
|
||||||
furi_record_close(RECORD_BT);
|
|
||||||
app->bt = NULL;
|
|
||||||
|
|
||||||
// Free rest
|
|
||||||
free(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t bt_hid_app(void* p) {
|
|
||||||
UNUSED(p);
|
|
||||||
// Switch profile to Hid
|
|
||||||
BtHid* app = bt_hid_app_alloc();
|
|
||||||
bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app);
|
|
||||||
// Change profile
|
|
||||||
if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) {
|
|
||||||
FURI_LOG_E(TAG, "Failed to switch profile");
|
|
||||||
bt_hid_app_free(app);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
furi_hal_bt_start_advertising();
|
|
||||||
|
|
||||||
DOLPHIN_DEED(DolphinDeedPluginStart);
|
|
||||||
|
|
||||||
view_dispatcher_run(app->view_dispatcher);
|
|
||||||
|
|
||||||
bt_set_status_changed_callback(app->bt, NULL, NULL);
|
|
||||||
// Change back profile to Serial
|
|
||||||
bt_set_profile(app->bt, BtProfileSerial);
|
|
||||||
|
|
||||||
bt_hid_app_free(app);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <furi.h>
|
|
||||||
#include <bt/bt_service/bt.h>
|
|
||||||
#include <gui/gui.h>
|
|
||||||
#include <gui/view.h>
|
|
||||||
#include <gui/view_dispatcher.h>
|
|
||||||
#include <notification/notification.h>
|
|
||||||
|
|
||||||
#include <gui/modules/submenu.h>
|
|
||||||
#include <gui/modules/dialog_ex.h>
|
|
||||||
#include "views/bt_hid_keynote.h"
|
|
||||||
#include "views/bt_hid_keyboard.h"
|
|
||||||
#include "views/bt_hid_media.h"
|
|
||||||
#include "views/bt_hid_mouse.h"
|
|
||||||
#include "views/bt_hid_tiktok.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Bt* bt;
|
|
||||||
Gui* gui;
|
|
||||||
NotificationApp* notifications;
|
|
||||||
ViewDispatcher* view_dispatcher;
|
|
||||||
Submenu* submenu;
|
|
||||||
DialogEx* dialog;
|
|
||||||
BtHidKeynote* bt_hid_keynote;
|
|
||||||
BtHidKeyboard* bt_hid_keyboard;
|
|
||||||
BtHidMedia* bt_hid_media;
|
|
||||||
BtHidMouse* bt_hid_mouse;
|
|
||||||
BtHidTikTok* bt_hid_tiktok;
|
|
||||||
uint32_t view_id;
|
|
||||||
} BtHid;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
BtHidViewSubmenu,
|
|
||||||
BtHidViewKeynote,
|
|
||||||
BtHidViewKeyboard,
|
|
||||||
BtHidViewMedia,
|
|
||||||
BtHidViewMouse,
|
|
||||||
BtHidViewTikTok,
|
|
||||||
BtHidViewExitConfirm,
|
|
||||||
} BtHidView;
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gui/view.h>
|
|
||||||
|
|
||||||
typedef struct BtHidKeyboard BtHidKeyboard;
|
|
||||||
|
|
||||||
BtHidKeyboard* bt_hid_keyboard_alloc();
|
|
||||||
|
|
||||||
void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard);
|
|
||||||
|
|
||||||
View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard);
|
|
||||||
|
|
||||||
void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected);
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gui/view.h>
|
|
||||||
|
|
||||||
typedef struct BtHidKeynote BtHidKeynote;
|
|
||||||
|
|
||||||
BtHidKeynote* bt_hid_keynote_alloc();
|
|
||||||
|
|
||||||
void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote);
|
|
||||||
|
|
||||||
View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote);
|
|
||||||
|
|
||||||
void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected);
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gui/view.h>
|
|
||||||
|
|
||||||
typedef struct BtHidMedia BtHidMedia;
|
|
||||||
|
|
||||||
BtHidMedia* bt_hid_media_alloc();
|
|
||||||
|
|
||||||
void bt_hid_media_free(BtHidMedia* bt_hid_media);
|
|
||||||
|
|
||||||
View* bt_hid_media_get_view(BtHidMedia* bt_hid_media);
|
|
||||||
|
|
||||||
void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected);
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gui/view.h>
|
|
||||||
|
|
||||||
typedef struct BtHidMouse BtHidMouse;
|
|
||||||
|
|
||||||
BtHidMouse* bt_hid_mouse_alloc();
|
|
||||||
|
|
||||||
void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse);
|
|
||||||
|
|
||||||
View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse);
|
|
||||||
|
|
||||||
void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected);
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gui/view.h>
|
|
||||||
|
|
||||||
typedef struct BtHidTikTok BtHidTikTok;
|
|
||||||
|
|
||||||
BtHidTikTok* bt_hid_tiktok_alloc();
|
|
||||||
|
|
||||||
void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok);
|
|
||||||
|
|
||||||
View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok);
|
|
||||||
|
|
||||||
void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected);
|
|
||||||
@ -247,7 +247,6 @@ static int32_t dap_process(void* p) {
|
|||||||
|
|
||||||
// deinit usb
|
// deinit usb
|
||||||
furi_hal_usb_set_config(usb_config_prev, NULL);
|
furi_hal_usb_set_config(usb_config_prev, NULL);
|
||||||
dap_common_wait_for_deinit();
|
|
||||||
dap_common_usb_free_name();
|
dap_common_usb_free_name();
|
||||||
dap_deinit_gpio(swd_pins_prev);
|
dap_deinit_gpio(swd_pins_prev);
|
||||||
return 0;
|
return 0;
|
||||||
@ -441,19 +440,6 @@ static int32_t cdc_process(void* p) {
|
|||||||
/******************************* MAIN APP **********************************/
|
/******************************* MAIN APP **********************************/
|
||||||
/***************************************************************************/
|
/***************************************************************************/
|
||||||
|
|
||||||
static FuriThread* furi_thread_alloc_ex(
|
|
||||||
const char* name,
|
|
||||||
uint32_t stack_size,
|
|
||||||
FuriThreadCallback callback,
|
|
||||||
void* context) {
|
|
||||||
FuriThread* thread = furi_thread_alloc();
|
|
||||||
furi_thread_set_name(thread, name);
|
|
||||||
furi_thread_set_stack_size(thread, stack_size);
|
|
||||||
furi_thread_set_callback(thread, callback);
|
|
||||||
furi_thread_set_context(thread, context);
|
|
||||||
return thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DapApp* dap_app_alloc() {
|
static DapApp* dap_app_alloc() {
|
||||||
DapApp* dap_app = malloc(sizeof(DapApp));
|
DapApp* dap_app = malloc(sizeof(DapApp));
|
||||||
dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app);
|
dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app);
|
||||||
|
|||||||
@ -618,23 +618,12 @@ static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) {
|
|||||||
if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1);
|
if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1);
|
||||||
if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1);
|
if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1);
|
||||||
|
|
||||||
usb_hid.dev_descr->idVendor = DAP_HID_VID;
|
|
||||||
usb_hid.dev_descr->idProduct = DAP_HID_PID;
|
|
||||||
|
|
||||||
usbd_reg_config(dev, hid_ep_config);
|
usbd_reg_config(dev, hid_ep_config);
|
||||||
usbd_reg_control(dev, hid_control);
|
usbd_reg_control(dev, hid_control);
|
||||||
|
|
||||||
usbd_connect(dev, true);
|
usbd_connect(dev, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool deinit_flag = false;
|
|
||||||
|
|
||||||
void dap_common_wait_for_deinit() {
|
|
||||||
while(!deinit_flag) {
|
|
||||||
furi_delay_ms(50);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hid_deinit(usbd_device* dev) {
|
static void hid_deinit(usbd_device* dev) {
|
||||||
dap_state.usb_dev = NULL;
|
dap_state.usb_dev = NULL;
|
||||||
|
|
||||||
@ -647,12 +636,6 @@ static void hid_deinit(usbd_device* dev) {
|
|||||||
|
|
||||||
usbd_reg_config(dev, NULL);
|
usbd_reg_config(dev, NULL);
|
||||||
usbd_reg_control(dev, NULL);
|
usbd_reg_control(dev, NULL);
|
||||||
|
|
||||||
free(usb_hid.str_manuf_descr);
|
|
||||||
free(usb_hid.str_prod_descr);
|
|
||||||
|
|
||||||
FURI_SW_MEMBARRIER();
|
|
||||||
deinit_flag = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hid_on_wakeup(usbd_device* dev) {
|
static void hid_on_wakeup(usbd_device* dev) {
|
||||||
|
|||||||
@ -51,5 +51,3 @@ void dap_common_usb_set_state_callback(DapStateCallback callback);
|
|||||||
void dap_common_usb_alloc_name(const char* name);
|
void dap_common_usb_alloc_name(const char* name);
|
||||||
|
|
||||||
void dap_common_usb_free_name();
|
void dap_common_usb_free_name();
|
||||||
|
|
||||||
void dap_common_wait_for_deinit();
|
|
||||||
24
applications/plugins/hid_app/application.fam
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
App(
|
||||||
|
appid="hid_usb",
|
||||||
|
name="USB Remote",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="hid_usb_app",
|
||||||
|
stack_size=1 * 1024,
|
||||||
|
fap_category="Tools",
|
||||||
|
fap_icon="hid_usb_10px.png",
|
||||||
|
fap_icon_assets="assets",
|
||||||
|
fap_icon_assets_symbol="hid",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="hid_ble",
|
||||||
|
name="Bluetooth Remote",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="hid_ble_app",
|
||||||
|
stack_size=1 * 1024,
|
||||||
|
fap_category="Tools",
|
||||||
|
fap_icon="hid_ble_10px.png",
|
||||||
|
fap_icon_assets="assets",
|
||||||
|
fap_icon_assets_symbol="hid",
|
||||||
|
)
|
||||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 102 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 102 B |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
365
applications/plugins/hid_app/hid.c
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
#include "hid.h"
|
||||||
|
#include "views.h"
|
||||||
|
#include <notification/notification_messages.h>
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
|
#define TAG "HidApp"
|
||||||
|
|
||||||
|
enum HidDebugSubmenuIndex {
|
||||||
|
HidSubmenuIndexKeynote,
|
||||||
|
HidSubmenuIndexKeyboard,
|
||||||
|
HidSubmenuIndexMedia,
|
||||||
|
BtHidSubmenuIndexTikTok,
|
||||||
|
HidSubmenuIndexMouse,
|
||||||
|
};
|
||||||
|
typedef enum { ConnTypeSubmenuIndexBluetooth, ConnTypeSubmenuIndexUsb } ConnTypeDebugSubmenuIndex;
|
||||||
|
|
||||||
|
static void hid_submenu_callback(void* context, uint32_t index) {
|
||||||
|
furi_assert(context);
|
||||||
|
Hid* app = context;
|
||||||
|
if(index == HidSubmenuIndexKeynote) {
|
||||||
|
app->view_id = HidViewKeynote;
|
||||||
|
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 == HidSubmenuIndexMedia) {
|
||||||
|
app->view_id = HidViewMedia;
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia);
|
||||||
|
} else if(index == HidSubmenuIndexMouse) {
|
||||||
|
app->view_id = HidViewMouse;
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse);
|
||||||
|
} else if(index == BtHidSubmenuIndexTikTok) {
|
||||||
|
app->view_id = BtHidViewTikTok;
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
Hid* hid = context;
|
||||||
|
bool connected = (status == BtStatusConnected);
|
||||||
|
if(connected) {
|
||||||
|
notification_internal_message(hid->notifications, &sequence_set_blue_255);
|
||||||
|
} else {
|
||||||
|
notification_internal_message(hid->notifications, &sequence_reset_blue);
|
||||||
|
}
|
||||||
|
hid_keynote_set_connected_status(hid->hid_keynote, connected);
|
||||||
|
hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
|
||||||
|
hid_media_set_connected_status(hid->hid_media, connected);
|
||||||
|
hid_mouse_set_connected_status(hid->hid_mouse, connected);
|
||||||
|
hid_tiktok_set_connected_status(hid->hid_tiktok, connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hid_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) {
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view
|
||||||
|
} else if(result == DialogExResultCenter) {
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t hid_exit_confirm_view(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
return HidViewExitConfirm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t hid_exit(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
return VIEW_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hid* hid_alloc(HidTransport transport) {
|
||||||
|
Hid* app = malloc(sizeof(Hid));
|
||||||
|
app->transport = transport;
|
||||||
|
|
||||||
|
// Gui
|
||||||
|
app->gui = furi_record_open(RECORD_GUI);
|
||||||
|
|
||||||
|
// Bt
|
||||||
|
app->bt = furi_record_open(RECORD_BT);
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
// 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, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app);
|
||||||
|
submenu_add_item(
|
||||||
|
app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app);
|
||||||
|
submenu_add_item(
|
||||||
|
app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app);
|
||||||
|
if(app->transport == HidTransportBle) {
|
||||||
|
submenu_add_item(
|
||||||
|
app->device_type_submenu,
|
||||||
|
"TikTok Controller",
|
||||||
|
BtHidSubmenuIndexTikTok,
|
||||||
|
hid_submenu_callback,
|
||||||
|
app);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hid* hid_app_alloc_view(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
Hid* app = context;
|
||||||
|
// Dialog view
|
||||||
|
app->dialog = dialog_ex_alloc();
|
||||||
|
dialog_ex_set_result_callback(app->dialog, hid_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_add_view(
|
||||||
|
app->view_dispatcher, HidViewExitConfirm, dialog_ex_get_view(app->dialog));
|
||||||
|
|
||||||
|
// Keynote view
|
||||||
|
app->hid_keynote = hid_keynote_alloc(app);
|
||||||
|
view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_exit_confirm_view);
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote));
|
||||||
|
|
||||||
|
// Keyboard view
|
||||||
|
app->hid_keyboard = hid_keyboard_alloc(app);
|
||||||
|
view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view);
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard));
|
||||||
|
|
||||||
|
// Media view
|
||||||
|
app->hid_media = hid_media_alloc(app);
|
||||||
|
view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view);
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media));
|
||||||
|
|
||||||
|
// TikTok view
|
||||||
|
app->hid_tiktok = hid_tiktok_alloc(app);
|
||||||
|
view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_exit_confirm_view);
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok));
|
||||||
|
|
||||||
|
// Mouse view
|
||||||
|
app->hid_mouse = hid_mouse_alloc(app);
|
||||||
|
view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view);
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse));
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_free(Hid* app) {
|
||||||
|
furi_assert(app);
|
||||||
|
|
||||||
|
// Reset notification
|
||||||
|
notification_internal_message(app->notifications, &sequence_reset_blue);
|
||||||
|
|
||||||
|
// Free views
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu);
|
||||||
|
submenu_free(app->device_type_submenu);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, HidViewExitConfirm);
|
||||||
|
dialog_ex_free(app->dialog);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote);
|
||||||
|
hid_keynote_free(app->hid_keynote);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard);
|
||||||
|
hid_keyboard_free(app->hid_keyboard);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia);
|
||||||
|
hid_media_free(app->hid_media);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse);
|
||||||
|
hid_mouse_free(app->hid_mouse);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
|
||||||
|
hid_tiktok_free(app->hid_tiktok);
|
||||||
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
|
|
||||||
|
// Close records
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
app->gui = NULL;
|
||||||
|
furi_record_close(RECORD_NOTIFICATION);
|
||||||
|
app->notifications = NULL;
|
||||||
|
furi_record_close(RECORD_BT);
|
||||||
|
app->bt = NULL;
|
||||||
|
|
||||||
|
// Free rest
|
||||||
|
free(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_keyboard_press(Hid* instance, uint16_t event) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_kb_press(event);
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_kb_press(event);
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_keyboard_release(Hid* instance, uint16_t event) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_kb_release(event);
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_kb_release(event);
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_keyboard_release_all(Hid* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_kb_release_all();
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_kb_release_all();
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_consumer_key_press(Hid* instance, uint16_t event) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_consumer_key_press(event);
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_consumer_key_press(event);
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_consumer_key_release(Hid* instance, uint16_t event) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_consumer_key_release(event);
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_consumer_key_release(event);
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_consumer_key_release_all(Hid* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_consumer_key_release_all();
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_kb_release_all();
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_mouse_move(dx, dy);
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_mouse_move(dx, dy);
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_mouse_scroll(Hid* instance, int8_t delta) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_mouse_scroll(delta);
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_mouse_scroll(delta);
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_mouse_press(Hid* instance, uint16_t event) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_mouse_press(event);
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_mouse_press(event);
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_mouse_release(Hid* instance, uint16_t event) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_mouse_release(event);
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_mouse_release(event);
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hid_hal_mouse_release_all(Hid* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(instance->transport == HidTransportBle) {
|
||||||
|
furi_hal_bt_hid_mouse_release_all();
|
||||||
|
} else if(instance->transport == HidTransportUsb) {
|
||||||
|
furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT);
|
||||||
|
furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT);
|
||||||
|
} else {
|
||||||
|
furi_crash(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t hid_usb_app(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
Hid* app = hid_alloc(HidTransportUsb);
|
||||||
|
app = hid_app_alloc_view(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);
|
||||||
|
|
||||||
|
view_dispatcher_run(app->view_dispatcher);
|
||||||
|
|
||||||
|
furi_hal_usb_set_config(usb_mode_prev, NULL);
|
||||||
|
|
||||||
|
hid_free(app);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t hid_ble_app(void* p) {
|
||||||
|
UNUSED(p);
|
||||||
|
Hid* app = hid_alloc(HidTransportBle);
|
||||||
|
app = hid_app_alloc_view(app);
|
||||||
|
|
||||||
|
if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) {
|
||||||
|
FURI_LOG_E(TAG, "Failed to switch profile");
|
||||||
|
}
|
||||||
|
furi_hal_bt_start_advertising();
|
||||||
|
bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app);
|
||||||
|
|
||||||
|
DOLPHIN_DEED(DolphinDeedPluginStart);
|
||||||
|
|
||||||
|
view_dispatcher_run(app->view_dispatcher);
|
||||||
|
|
||||||
|
bt_set_status_changed_callback(app->bt, NULL, NULL);
|
||||||
|
bt_set_profile(app->bt, BtProfileSerial);
|
||||||
|
|
||||||
|
hid_free(app);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
60
applications/plugins/hid_app/hid.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal_bt.h>
|
||||||
|
#include <furi_hal_bt_hid.h>
|
||||||
|
#include <furi_hal_usb.h>
|
||||||
|
#include <furi_hal_usb_hid.h>
|
||||||
|
|
||||||
|
#include <bt/bt_service/bt.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include <gui/view_dispatcher.h>
|
||||||
|
#include <notification/notification.h>
|
||||||
|
|
||||||
|
#include <gui/modules/submenu.h>
|
||||||
|
#include <gui/modules/dialog_ex.h>
|
||||||
|
#include <gui/modules/popup.h>
|
||||||
|
#include "views/hid_keynote.h"
|
||||||
|
#include "views/hid_keyboard.h"
|
||||||
|
#include "views/hid_media.h"
|
||||||
|
#include "views/hid_mouse.h"
|
||||||
|
#include "views/hid_tiktok.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
HidTransportUsb,
|
||||||
|
HidTransportBle,
|
||||||
|
} HidTransport;
|
||||||
|
|
||||||
|
typedef struct Hid Hid;
|
||||||
|
|
||||||
|
struct Hid {
|
||||||
|
Bt* bt;
|
||||||
|
Gui* gui;
|
||||||
|
NotificationApp* notifications;
|
||||||
|
ViewDispatcher* view_dispatcher;
|
||||||
|
Submenu* device_type_submenu;
|
||||||
|
DialogEx* dialog;
|
||||||
|
HidKeynote* hid_keynote;
|
||||||
|
HidKeyboard* hid_keyboard;
|
||||||
|
HidMedia* hid_media;
|
||||||
|
HidMouse* hid_mouse;
|
||||||
|
HidTikTok* hid_tiktok;
|
||||||
|
|
||||||
|
HidTransport transport;
|
||||||
|
uint32_t view_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
void hid_hal_keyboard_press(Hid* instance, uint16_t event);
|
||||||
|
void hid_hal_keyboard_release(Hid* instance, uint16_t event);
|
||||||
|
void hid_hal_keyboard_release_all(Hid* instance);
|
||||||
|
|
||||||
|
void hid_hal_consumer_key_press(Hid* instance, uint16_t event);
|
||||||
|
void hid_hal_consumer_key_release(Hid* instance, uint16_t event);
|
||||||
|
void hid_hal_consumer_key_release_all(Hid* instance);
|
||||||
|
|
||||||
|
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);
|
||||||
|
Before Width: | Height: | Size: 151 B After Width: | Height: | Size: 151 B |
BIN
applications/plugins/hid_app/hid_usb_10px.png
Normal file
|
After Width: | Height: | Size: 969 B |
9
applications/plugins/hid_app/views.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
typedef enum {
|
||||||
|
HidViewSubmenu,
|
||||||
|
HidViewKeynote,
|
||||||
|
HidViewKeyboard,
|
||||||
|
HidViewMedia,
|
||||||
|
HidViewMouse,
|
||||||
|
BtHidViewTikTok,
|
||||||
|
HidViewExitConfirm,
|
||||||
|
} HidView;
|
||||||
@ -1,14 +1,15 @@
|
|||||||
#include "bt_hid_keyboard.h"
|
#include "hid_keyboard.h"
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal_bt_hid.h>
|
|
||||||
#include <furi_hal_usb_hid.h>
|
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
#include <gui/icon_i.h>
|
#include <gui/icon_i.h>
|
||||||
|
#include "../hid.h"
|
||||||
|
#include "hid_icons.h"
|
||||||
|
|
||||||
#include "bt_hid_icons.h"
|
#define TAG "HidKeyboard"
|
||||||
|
|
||||||
struct BtHidKeyboard {
|
struct HidKeyboard {
|
||||||
View* view;
|
View* view;
|
||||||
|
Hid* hid;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -24,7 +25,7 @@ typedef struct {
|
|||||||
bool back_pressed;
|
bool back_pressed;
|
||||||
bool connected;
|
bool connected;
|
||||||
char key_string[5];
|
char key_string[5];
|
||||||
} BtHidKeyboardModel;
|
} HidKeyboardModel;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t width;
|
uint8_t width;
|
||||||
@ -32,13 +33,12 @@ typedef struct {
|
|||||||
const Icon* icon;
|
const Icon* icon;
|
||||||
char* shift_key;
|
char* shift_key;
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
} BtHidKeyboardKey;
|
} HidKeyboardKey;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int8_t x;
|
int8_t x;
|
||||||
int8_t y;
|
int8_t y;
|
||||||
} BtHidKeyboardPoint;
|
} HidKeyboardPoint;
|
||||||
|
|
||||||
// 4 BY 12
|
// 4 BY 12
|
||||||
#define MARGIN_TOP 0
|
#define MARGIN_TOP 0
|
||||||
#define MARGIN_LEFT 4
|
#define MARGIN_LEFT 4
|
||||||
@ -49,7 +49,7 @@ typedef struct {
|
|||||||
#define COLUMN_COUNT 12
|
#define COLUMN_COUNT 12
|
||||||
|
|
||||||
// 0 width items are not drawn, but there value is used
|
// 0 width items are not drawn, but there value is used
|
||||||
const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
|
const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
|
||||||
{
|
{
|
||||||
{.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1},
|
{.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1},
|
||||||
{.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2},
|
{.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2},
|
||||||
@ -112,7 +112,7 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
{.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT},
|
{.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT},
|
||||||
{.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA},
|
{.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYBOARD_COMMA},
|
||||||
{.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT},
|
{.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT},
|
||||||
{.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR},
|
{.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR},
|
||||||
{.width = 0, .value = HID_KEYBOARD_SPACEBAR},
|
{.width = 0, .value = HID_KEYBOARD_SPACEBAR},
|
||||||
@ -140,19 +140,19 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bt_hid_keyboard_to_upper(char* str) {
|
static void hid_keyboard_to_upper(char* str) {
|
||||||
while(*str) {
|
while(*str) {
|
||||||
*str = toupper((unsigned char)*str);
|
*str = toupper((unsigned char)*str);
|
||||||
str++;
|
str++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_hid_keyboard_draw_key(
|
static void hid_keyboard_draw_key(
|
||||||
Canvas* canvas,
|
Canvas* canvas,
|
||||||
BtHidKeyboardModel* model,
|
HidKeyboardModel* model,
|
||||||
uint8_t x,
|
uint8_t x,
|
||||||
uint8_t y,
|
uint8_t y,
|
||||||
BtHidKeyboardKey key,
|
HidKeyboardKey key,
|
||||||
bool selected) {
|
bool selected) {
|
||||||
if(!key.width) return;
|
if(!key.width) return;
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ static void bt_hid_keyboard_draw_key(
|
|||||||
if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) ||
|
if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) ||
|
||||||
(model->alt && key.value == HID_KEYBOARD_L_ALT) ||
|
(model->alt && key.value == HID_KEYBOARD_L_ALT) ||
|
||||||
(model->gui && key.value == HID_KEYBOARD_L_GUI)) {
|
(model->gui && key.value == HID_KEYBOARD_L_GUI)) {
|
||||||
bt_hid_keyboard_to_upper(model->key_string);
|
hid_keyboard_to_upper(model->key_string);
|
||||||
}
|
}
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas,
|
canvas,
|
||||||
@ -202,9 +202,9 @@ static void bt_hid_keyboard_draw_key(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) {
|
static void hid_keyboard_draw_callback(Canvas* canvas, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
BtHidKeyboardModel* model = context;
|
HidKeyboardModel* model = context;
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
if(!model->connected) {
|
if(!model->connected) {
|
||||||
@ -225,17 +225,17 @@ static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) {
|
|||||||
// Start shifting the all keys up if on the next row (Scrolling)
|
// Start shifting the all keys up if on the next row (Scrolling)
|
||||||
uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0;
|
uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0;
|
||||||
for(uint8_t y = initY; y < ROW_COUNT; y++) {
|
for(uint8_t y = initY; y < ROW_COUNT; y++) {
|
||||||
const BtHidKeyboardKey* keyboardKeyRow = bt_hid_keyboard_keyset[y];
|
const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y];
|
||||||
uint8_t x = 0;
|
uint8_t x = 0;
|
||||||
for(uint8_t i = 0; i < COLUMN_COUNT; i++) {
|
for(uint8_t i = 0; i < COLUMN_COUNT; i++) {
|
||||||
BtHidKeyboardKey key = keyboardKeyRow[i];
|
HidKeyboardKey key = keyboardKeyRow[i];
|
||||||
// Select when the button is hovered
|
// Select when the button is hovered
|
||||||
// Select if the button is hovered within its width
|
// Select if the button is hovered within its width
|
||||||
// Select if back is clicked and its the backspace key
|
// Select if back is clicked and its the backspace key
|
||||||
// Deselect when the button clicked or not hovered
|
// Deselect when the button clicked or not hovered
|
||||||
bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y;
|
bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y;
|
||||||
bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE;
|
bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE;
|
||||||
bt_hid_keyboard_draw_key(
|
hid_keyboard_draw_key(
|
||||||
canvas,
|
canvas,
|
||||||
model,
|
model,
|
||||||
x,
|
x,
|
||||||
@ -247,8 +247,8 @@ static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) {
|
static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) {
|
||||||
BtHidKeyboardKey key = bt_hid_keyboard_keyset[model->y][model->x];
|
HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x];
|
||||||
// Use upper case if shift is toggled
|
// Use upper case if shift is toggled
|
||||||
bool useUppercase = model->shift;
|
bool useUppercase = model->shift;
|
||||||
// Check if the key has an upper case version
|
// Check if the key has an upper case version
|
||||||
@ -259,34 +259,34 @@ static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) {
|
|||||||
return key.value;
|
return key.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_hid_keyboard_get_select_key(BtHidKeyboardModel* model, BtHidKeyboardPoint delta) {
|
static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) {
|
||||||
// Keep going until a valid spot is found, this allows for nulls and zero width keys in the map
|
// Keep going until a valid spot is found, this allows for nulls and zero width keys in the map
|
||||||
do {
|
do {
|
||||||
if(((int8_t)model->y) + delta.y < 0)
|
if(((int8_t)model->y) + delta.y < 0)
|
||||||
model->y = ROW_COUNT - 1;
|
model->y = ROW_COUNT - 1;
|
||||||
else
|
else
|
||||||
model->y = (model->y + delta.y) % ROW_COUNT;
|
model->y = (model->y + delta.y) % ROW_COUNT;
|
||||||
} while(delta.y != 0 && bt_hid_keyboard_keyset[model->y][model->x].value == 0);
|
} while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if(((int8_t)model->x) + delta.x < 0)
|
if(((int8_t)model->x) + delta.x < 0)
|
||||||
model->x = COLUMN_COUNT - 1;
|
model->x = COLUMN_COUNT - 1;
|
||||||
else
|
else
|
||||||
model->x = (model->x + delta.x) % COLUMN_COUNT;
|
model->x = (model->x + delta.x) % COLUMN_COUNT;
|
||||||
} while(delta.x != 0 && bt_hid_keyboard_keyset[model->y][model->x].width ==
|
} while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width ==
|
||||||
0); // Skip zero width keys, pretend they are one key
|
0); // Skip zero width keys, pretend they are one key
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* event) {
|
static void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
bt_hid_keyboard->view,
|
hid_keyboard->view,
|
||||||
BtHidKeyboardModel * model,
|
HidKeyboardModel * model,
|
||||||
{
|
{
|
||||||
if(event->key == InputKeyOk) {
|
if(event->key == InputKeyOk) {
|
||||||
if(event->type == InputTypePress) {
|
if(event->type == InputTypePress) {
|
||||||
model->ok_pressed = true;
|
model->ok_pressed = true;
|
||||||
} else if(event->type == InputTypeLong || event->type == InputTypeShort) {
|
} else if(event->type == InputTypeLong || event->type == InputTypeShort) {
|
||||||
model->last_key_code = bt_hid_keyboard_get_selected_key(model);
|
model->last_key_code = hid_keyboard_get_selected_key(model);
|
||||||
|
|
||||||
// Toggle the modifier key when clicked, and click the key
|
// Toggle the modifier key when clicked, and click the key
|
||||||
if(model->last_key_code == HID_KEYBOARD_L_SHIFT) {
|
if(model->last_key_code == HID_KEYBOARD_L_SHIFT) {
|
||||||
@ -314,10 +314,12 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent*
|
|||||||
else
|
else
|
||||||
model->modifier_code &= ~KEY_MOD_LEFT_GUI;
|
model->modifier_code &= ~KEY_MOD_LEFT_GUI;
|
||||||
}
|
}
|
||||||
furi_hal_bt_hid_kb_press(model->modifier_code | model->last_key_code);
|
hid_hal_keyboard_press(
|
||||||
|
hid_keyboard->hid, model->modifier_code | model->last_key_code);
|
||||||
} else if(event->type == InputTypeRelease) {
|
} else if(event->type == InputTypeRelease) {
|
||||||
// Release happens after short and long presses
|
// Release happens after short and long presses
|
||||||
furi_hal_bt_hid_kb_release(model->modifier_code | model->last_key_code);
|
hid_hal_keyboard_release(
|
||||||
|
hid_keyboard->hid, model->modifier_code | model->last_key_code);
|
||||||
model->ok_pressed = false;
|
model->ok_pressed = false;
|
||||||
}
|
}
|
||||||
} else if(event->key == InputKeyBack) {
|
} else if(event->key == InputKeyBack) {
|
||||||
@ -325,66 +327,67 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent*
|
|||||||
if(event->type == InputTypePress) {
|
if(event->type == InputTypePress) {
|
||||||
model->back_pressed = true;
|
model->back_pressed = true;
|
||||||
} else if(event->type == InputTypeShort) {
|
} else if(event->type == InputTypeShort) {
|
||||||
furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE);
|
hid_hal_keyboard_press(hid_keyboard->hid, HID_KEYBOARD_DELETE);
|
||||||
furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE);
|
hid_hal_keyboard_release(hid_keyboard->hid, HID_KEYBOARD_DELETE);
|
||||||
} else if(event->type == InputTypeRelease) {
|
} else if(event->type == InputTypeRelease) {
|
||||||
model->back_pressed = false;
|
model->back_pressed = false;
|
||||||
}
|
}
|
||||||
} else if(event->type == InputTypePress || event->type == InputTypeRepeat) {
|
} else if(event->type == InputTypePress || event->type == InputTypeRepeat) {
|
||||||
// Cycle the selected keys
|
// Cycle the selected keys
|
||||||
if(event->key == InputKeyUp) {
|
if(event->key == InputKeyUp) {
|
||||||
bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = -1});
|
hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = -1});
|
||||||
} else if(event->key == InputKeyDown) {
|
} else if(event->key == InputKeyDown) {
|
||||||
bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = 1});
|
hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = 1});
|
||||||
} else if(event->key == InputKeyLeft) {
|
} else if(event->key == InputKeyLeft) {
|
||||||
bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = -1, .y = 0});
|
hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = -1, .y = 0});
|
||||||
} else if(event->key == InputKeyRight) {
|
} else if(event->key == InputKeyRight) {
|
||||||
bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 1, .y = 0});
|
hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 1, .y = 0});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool bt_hid_keyboard_input_callback(InputEvent* event, void* context) {
|
static bool hid_keyboard_input_callback(InputEvent* event, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
BtHidKeyboard* bt_hid_keyboard = context;
|
HidKeyboard* hid_keyboard = context;
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event->type == InputTypeLong && event->key == InputKeyBack) {
|
if(event->type == InputTypeLong && event->key == InputKeyBack) {
|
||||||
furi_hal_bt_hid_kb_release_all();
|
hid_hal_keyboard_release_all(hid_keyboard->hid);
|
||||||
} else {
|
} else {
|
||||||
bt_hid_keyboard_process(bt_hid_keyboard, event);
|
hid_keyboard_process(hid_keyboard, event);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
BtHidKeyboard* bt_hid_keyboard_alloc() {
|
HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) {
|
||||||
BtHidKeyboard* bt_hid_keyboard = malloc(sizeof(BtHidKeyboard));
|
HidKeyboard* hid_keyboard = malloc(sizeof(HidKeyboard));
|
||||||
bt_hid_keyboard->view = view_alloc();
|
hid_keyboard->view = view_alloc();
|
||||||
view_set_context(bt_hid_keyboard->view, bt_hid_keyboard);
|
hid_keyboard->hid = bt_hid;
|
||||||
view_allocate_model(bt_hid_keyboard->view, ViewModelTypeLocking, sizeof(BtHidKeyboardModel));
|
view_set_context(hid_keyboard->view, hid_keyboard);
|
||||||
view_set_draw_callback(bt_hid_keyboard->view, bt_hid_keyboard_draw_callback);
|
view_allocate_model(hid_keyboard->view, ViewModelTypeLocking, sizeof(HidKeyboardModel));
|
||||||
view_set_input_callback(bt_hid_keyboard->view, bt_hid_keyboard_input_callback);
|
view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback);
|
||||||
|
view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback);
|
||||||
|
|
||||||
return bt_hid_keyboard;
|
return hid_keyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard) {
|
void hid_keyboard_free(HidKeyboard* hid_keyboard) {
|
||||||
furi_assert(bt_hid_keyboard);
|
furi_assert(hid_keyboard);
|
||||||
view_free(bt_hid_keyboard->view);
|
view_free(hid_keyboard->view);
|
||||||
free(bt_hid_keyboard);
|
free(hid_keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard) {
|
View* hid_keyboard_get_view(HidKeyboard* hid_keyboard) {
|
||||||
furi_assert(bt_hid_keyboard);
|
furi_assert(hid_keyboard);
|
||||||
return bt_hid_keyboard->view;
|
return hid_keyboard->view;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected) {
|
void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected) {
|
||||||
furi_assert(bt_hid_keyboard);
|
furi_assert(hid_keyboard);
|
||||||
with_view_model(
|
with_view_model(
|
||||||
bt_hid_keyboard->view, BtHidKeyboardModel * model, { model->connected = connected; }, true);
|
hid_keyboard->view, HidKeyboardModel * model, { model->connected = connected; }, true);
|
||||||
}
|
}
|
||||||
14
applications/plugins/hid_app/views/hid_keyboard.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
|
||||||
|
typedef struct Hid Hid;
|
||||||
|
typedef struct HidKeyboard HidKeyboard;
|
||||||
|
|
||||||
|
HidKeyboard* hid_keyboard_alloc(Hid* bt_hid);
|
||||||
|
|
||||||
|
void hid_keyboard_free(HidKeyboard* hid_keyboard);
|
||||||
|
|
||||||
|
View* hid_keyboard_get_view(HidKeyboard* hid_keyboard);
|
||||||
|
|
||||||
|
void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected);
|
||||||
@ -1,13 +1,14 @@
|
|||||||
#include "bt_hid_keynote.h"
|
#include "hid_keynote.h"
|
||||||
#include <furi.h>
|
|
||||||
#include <furi_hal_bt_hid.h>
|
|
||||||
#include <furi_hal_usb_hid.h>
|
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
|
#include "../hid.h"
|
||||||
|
|
||||||
#include "bt_hid_icons.h"
|
#include "hid_icons.h"
|
||||||
|
|
||||||
struct BtHidKeynote {
|
#define TAG "HidKeynote"
|
||||||
|
|
||||||
|
struct HidKeynote {
|
||||||
View* view;
|
View* view;
|
||||||
|
Hid* hid;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -18,9 +19,9 @@ typedef struct {
|
|||||||
bool ok_pressed;
|
bool ok_pressed;
|
||||||
bool back_pressed;
|
bool back_pressed;
|
||||||
bool connected;
|
bool connected;
|
||||||
} BtHidKeynoteModel;
|
} HidKeynoteModel;
|
||||||
|
|
||||||
static void bt_hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
|
static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
|
||||||
canvas_draw_triangle(canvas, x, y, 5, 3, dir);
|
canvas_draw_triangle(canvas, x, y, 5, 3, dir);
|
||||||
if(dir == CanvasDirectionBottomToTop) {
|
if(dir == CanvasDirectionBottomToTop) {
|
||||||
canvas_draw_line(canvas, x, y + 6, x, y - 1);
|
canvas_draw_line(canvas, x, y + 6, x, y - 1);
|
||||||
@ -33,9 +34,9 @@ static void bt_hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, Canv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
static void hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
BtHidKeynoteModel* model = context;
|
HidKeynoteModel* model = context;
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
if(model->connected) {
|
if(model->connected) {
|
||||||
@ -56,7 +57,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
|||||||
elements_slightly_rounded_box(canvas, 24, 26, 13, 13);
|
elements_slightly_rounded_box(canvas, 24, 26, 13, 13);
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
}
|
}
|
||||||
bt_hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop);
|
hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
// Down
|
// Down
|
||||||
@ -65,7 +66,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
|||||||
elements_slightly_rounded_box(canvas, 24, 47, 13, 13);
|
elements_slightly_rounded_box(canvas, 24, 47, 13, 13);
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
}
|
}
|
||||||
bt_hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom);
|
hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
// Left
|
// Left
|
||||||
@ -74,7 +75,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
|||||||
elements_slightly_rounded_box(canvas, 3, 47, 13, 13);
|
elements_slightly_rounded_box(canvas, 3, 47, 13, 13);
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
}
|
}
|
||||||
bt_hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft);
|
hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
// Right
|
// Right
|
||||||
@ -83,7 +84,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
|||||||
elements_slightly_rounded_box(canvas, 45, 47, 13, 13);
|
elements_slightly_rounded_box(canvas, 45, 47, 13, 13);
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
}
|
}
|
||||||
bt_hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight);
|
hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
// Ok
|
// Ok
|
||||||
@ -106,100 +107,101 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
|||||||
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
|
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) {
|
static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
bt_hid_keynote->view,
|
hid_keynote->view,
|
||||||
BtHidKeynoteModel * model,
|
HidKeynoteModel * model,
|
||||||
{
|
{
|
||||||
if(event->type == InputTypePress) {
|
if(event->type == InputTypePress) {
|
||||||
if(event->key == InputKeyUp) {
|
if(event->key == InputKeyUp) {
|
||||||
model->up_pressed = true;
|
model->up_pressed = true;
|
||||||
furi_hal_bt_hid_kb_press(HID_KEYBOARD_UP_ARROW);
|
hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_UP_ARROW);
|
||||||
} else if(event->key == InputKeyDown) {
|
} else if(event->key == InputKeyDown) {
|
||||||
model->down_pressed = true;
|
model->down_pressed = true;
|
||||||
furi_hal_bt_hid_kb_press(HID_KEYBOARD_DOWN_ARROW);
|
hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW);
|
||||||
} else if(event->key == InputKeyLeft) {
|
} else if(event->key == InputKeyLeft) {
|
||||||
model->left_pressed = true;
|
model->left_pressed = true;
|
||||||
furi_hal_bt_hid_kb_press(HID_KEYBOARD_LEFT_ARROW);
|
hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW);
|
||||||
} else if(event->key == InputKeyRight) {
|
} else if(event->key == InputKeyRight) {
|
||||||
model->right_pressed = true;
|
model->right_pressed = true;
|
||||||
furi_hal_bt_hid_kb_press(HID_KEYBOARD_RIGHT_ARROW);
|
hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW);
|
||||||
} else if(event->key == InputKeyOk) {
|
} else if(event->key == InputKeyOk) {
|
||||||
model->ok_pressed = true;
|
model->ok_pressed = true;
|
||||||
furi_hal_bt_hid_kb_press(HID_KEYBOARD_SPACEBAR);
|
hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_SPACEBAR);
|
||||||
} else if(event->key == InputKeyBack) {
|
} else if(event->key == InputKeyBack) {
|
||||||
model->back_pressed = true;
|
model->back_pressed = true;
|
||||||
}
|
}
|
||||||
} else if(event->type == InputTypeRelease) {
|
} else if(event->type == InputTypeRelease) {
|
||||||
if(event->key == InputKeyUp) {
|
if(event->key == InputKeyUp) {
|
||||||
model->up_pressed = false;
|
model->up_pressed = false;
|
||||||
furi_hal_bt_hid_kb_release(HID_KEYBOARD_UP_ARROW);
|
hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_UP_ARROW);
|
||||||
} else if(event->key == InputKeyDown) {
|
} else if(event->key == InputKeyDown) {
|
||||||
model->down_pressed = false;
|
model->down_pressed = false;
|
||||||
furi_hal_bt_hid_kb_release(HID_KEYBOARD_DOWN_ARROW);
|
hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW);
|
||||||
} else if(event->key == InputKeyLeft) {
|
} else if(event->key == InputKeyLeft) {
|
||||||
model->left_pressed = false;
|
model->left_pressed = false;
|
||||||
furi_hal_bt_hid_kb_release(HID_KEYBOARD_LEFT_ARROW);
|
hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW);
|
||||||
} else if(event->key == InputKeyRight) {
|
} else if(event->key == InputKeyRight) {
|
||||||
model->right_pressed = false;
|
model->right_pressed = false;
|
||||||
furi_hal_bt_hid_kb_release(HID_KEYBOARD_RIGHT_ARROW);
|
hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW);
|
||||||
} else if(event->key == InputKeyOk) {
|
} else if(event->key == InputKeyOk) {
|
||||||
model->ok_pressed = false;
|
model->ok_pressed = false;
|
||||||
furi_hal_bt_hid_kb_release(HID_KEYBOARD_SPACEBAR);
|
hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_SPACEBAR);
|
||||||
} else if(event->key == InputKeyBack) {
|
} else if(event->key == InputKeyBack) {
|
||||||
model->back_pressed = false;
|
model->back_pressed = false;
|
||||||
}
|
}
|
||||||
} else if(event->type == InputTypeShort) {
|
} else if(event->type == InputTypeShort) {
|
||||||
if(event->key == InputKeyBack) {
|
if(event->key == InputKeyBack) {
|
||||||
furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE);
|
hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DELETE);
|
||||||
furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE);
|
hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DELETE);
|
||||||
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_AC_BACK);
|
hid_hal_consumer_key_press(hid_keynote->hid, HID_CONSUMER_AC_BACK);
|
||||||
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_AC_BACK);
|
hid_hal_consumer_key_release(hid_keynote->hid, HID_CONSUMER_AC_BACK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool bt_hid_keynote_input_callback(InputEvent* event, void* context) {
|
static bool hid_keynote_input_callback(InputEvent* event, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
BtHidKeynote* bt_hid_keynote = context;
|
HidKeynote* hid_keynote = context;
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event->type == InputTypeLong && event->key == InputKeyBack) {
|
if(event->type == InputTypeLong && event->key == InputKeyBack) {
|
||||||
furi_hal_bt_hid_kb_release_all();
|
hid_hal_keyboard_release_all(hid_keynote->hid);
|
||||||
} else {
|
} else {
|
||||||
bt_hid_keynote_process(bt_hid_keynote, event);
|
hid_keynote_process(hid_keynote, event);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
BtHidKeynote* bt_hid_keynote_alloc() {
|
HidKeynote* hid_keynote_alloc(Hid* hid) {
|
||||||
BtHidKeynote* bt_hid_keynote = malloc(sizeof(BtHidKeynote));
|
HidKeynote* hid_keynote = malloc(sizeof(HidKeynote));
|
||||||
bt_hid_keynote->view = view_alloc();
|
hid_keynote->view = view_alloc();
|
||||||
view_set_context(bt_hid_keynote->view, bt_hid_keynote);
|
hid_keynote->hid = hid;
|
||||||
view_allocate_model(bt_hid_keynote->view, ViewModelTypeLocking, sizeof(BtHidKeynoteModel));
|
view_set_context(hid_keynote->view, hid_keynote);
|
||||||
view_set_draw_callback(bt_hid_keynote->view, bt_hid_keynote_draw_callback);
|
view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel));
|
||||||
view_set_input_callback(bt_hid_keynote->view, bt_hid_keynote_input_callback);
|
view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback);
|
||||||
|
view_set_input_callback(hid_keynote->view, hid_keynote_input_callback);
|
||||||
|
|
||||||
return bt_hid_keynote;
|
return hid_keynote;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote) {
|
void hid_keynote_free(HidKeynote* hid_keynote) {
|
||||||
furi_assert(bt_hid_keynote);
|
furi_assert(hid_keynote);
|
||||||
view_free(bt_hid_keynote->view);
|
view_free(hid_keynote->view);
|
||||||
free(bt_hid_keynote);
|
free(hid_keynote);
|
||||||
}
|
}
|
||||||
|
|
||||||
View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote) {
|
View* hid_keynote_get_view(HidKeynote* hid_keynote) {
|
||||||
furi_assert(bt_hid_keynote);
|
furi_assert(hid_keynote);
|
||||||
return bt_hid_keynote->view;
|
return hid_keynote->view;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected) {
|
void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) {
|
||||||
furi_assert(bt_hid_keynote);
|
furi_assert(hid_keynote);
|
||||||
with_view_model(
|
with_view_model(
|
||||||
bt_hid_keynote->view, BtHidKeynoteModel * model, { model->connected = connected; }, true);
|
hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true);
|
||||||
}
|
}
|
||||||
14
applications/plugins/hid_app/views/hid_keynote.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
|
||||||
|
typedef struct Hid Hid;
|
||||||
|
typedef struct HidKeynote HidKeynote;
|
||||||
|
|
||||||
|
HidKeynote* hid_keynote_alloc(Hid* bt_hid);
|
||||||
|
|
||||||
|
void hid_keynote_free(HidKeynote* hid_keynote);
|
||||||
|
|
||||||
|
View* hid_keynote_get_view(HidKeynote* hid_keynote);
|
||||||
|
|
||||||
|
void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected);
|
||||||
@ -1,13 +1,17 @@
|
|||||||
#include "bt_hid_media.h"
|
#include "hid_media.h"
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal_bt_hid.h>
|
#include <furi_hal_bt_hid.h>
|
||||||
#include <furi_hal_usb_hid.h>
|
#include <furi_hal_usb_hid.h>
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
|
#include "../hid.h"
|
||||||
|
|
||||||
#include "bt_hid_icons.h"
|
#include "hid_icons.h"
|
||||||
|
|
||||||
struct BtHidMedia {
|
#define TAG "HidMedia"
|
||||||
|
|
||||||
|
struct HidMedia {
|
||||||
View* view;
|
View* view;
|
||||||
|
Hid* hid;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -17,9 +21,9 @@ typedef struct {
|
|||||||
bool down_pressed;
|
bool down_pressed;
|
||||||
bool ok_pressed;
|
bool ok_pressed;
|
||||||
bool connected;
|
bool connected;
|
||||||
} BtHidMediaModel;
|
} HidMediaModel;
|
||||||
|
|
||||||
static void bt_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
|
static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
|
||||||
canvas_draw_triangle(canvas, x, y, 5, 3, dir);
|
canvas_draw_triangle(canvas, x, y, 5, 3, dir);
|
||||||
if(dir == CanvasDirectionBottomToTop) {
|
if(dir == CanvasDirectionBottomToTop) {
|
||||||
canvas_draw_dot(canvas, x, y - 1);
|
canvas_draw_dot(canvas, x, y - 1);
|
||||||
@ -32,9 +36,9 @@ static void bt_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, Canvas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
|
static void hid_media_draw_callback(Canvas* canvas, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
BtHidMediaModel* model = context;
|
HidMediaModel* model = context;
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
if(model->connected) {
|
if(model->connected) {
|
||||||
@ -76,8 +80,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
|
|||||||
canvas_set_bitmap_mode(canvas, 0);
|
canvas_set_bitmap_mode(canvas, 0);
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
}
|
}
|
||||||
bt_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft);
|
hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft);
|
||||||
bt_hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft);
|
hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
// Right
|
// Right
|
||||||
@ -87,8 +91,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
|
|||||||
canvas_set_bitmap_mode(canvas, 0);
|
canvas_set_bitmap_mode(canvas, 0);
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
}
|
}
|
||||||
bt_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight);
|
hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight);
|
||||||
bt_hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight);
|
hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
// Ok
|
// Ok
|
||||||
@ -96,7 +100,7 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
|
|||||||
canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13);
|
canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13);
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
}
|
}
|
||||||
bt_hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight);
|
hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight);
|
||||||
canvas_draw_line(canvas, 100, 29, 100, 33);
|
canvas_draw_line(canvas, 100, 29, 100, 33);
|
||||||
canvas_draw_line(canvas, 102, 29, 102, 33);
|
canvas_draw_line(canvas, 102, 29, 102, 33);
|
||||||
canvas_set_color(canvas, ColorBlack);
|
canvas_set_color(canvas, ColorBlack);
|
||||||
@ -107,100 +111,101 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
|
|||||||
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
|
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* event) {
|
static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
bt_hid_media->view,
|
hid_media->view,
|
||||||
BtHidMediaModel * model,
|
HidMediaModel * model,
|
||||||
{
|
{
|
||||||
if(event->key == InputKeyUp) {
|
if(event->key == InputKeyUp) {
|
||||||
model->up_pressed = true;
|
model->up_pressed = true;
|
||||||
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT);
|
hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT);
|
||||||
} else if(event->key == InputKeyDown) {
|
} else if(event->key == InputKeyDown) {
|
||||||
model->down_pressed = true;
|
model->down_pressed = true;
|
||||||
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT);
|
hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT);
|
||||||
} else if(event->key == InputKeyLeft) {
|
} else if(event->key == InputKeyLeft) {
|
||||||
model->left_pressed = true;
|
model->left_pressed = true;
|
||||||
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_PREVIOUS_TRACK);
|
hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK);
|
||||||
} else if(event->key == InputKeyRight) {
|
} else if(event->key == InputKeyRight) {
|
||||||
model->right_pressed = true;
|
model->right_pressed = true;
|
||||||
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_NEXT_TRACK);
|
hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK);
|
||||||
} else if(event->key == InputKeyOk) {
|
} else if(event->key == InputKeyOk) {
|
||||||
model->ok_pressed = true;
|
model->ok_pressed = true;
|
||||||
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_PLAY_PAUSE);
|
hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_hid_media_process_release(BtHidMedia* bt_hid_media, InputEvent* event) {
|
static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) {
|
||||||
with_view_model(
|
with_view_model(
|
||||||
bt_hid_media->view,
|
hid_media->view,
|
||||||
BtHidMediaModel * model,
|
HidMediaModel * model,
|
||||||
{
|
{
|
||||||
if(event->key == InputKeyUp) {
|
if(event->key == InputKeyUp) {
|
||||||
model->up_pressed = false;
|
model->up_pressed = false;
|
||||||
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT);
|
hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT);
|
||||||
} else if(event->key == InputKeyDown) {
|
} else if(event->key == InputKeyDown) {
|
||||||
model->down_pressed = false;
|
model->down_pressed = false;
|
||||||
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT);
|
hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT);
|
||||||
} else if(event->key == InputKeyLeft) {
|
} else if(event->key == InputKeyLeft) {
|
||||||
model->left_pressed = false;
|
model->left_pressed = false;
|
||||||
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_PREVIOUS_TRACK);
|
hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK);
|
||||||
} else if(event->key == InputKeyRight) {
|
} else if(event->key == InputKeyRight) {
|
||||||
model->right_pressed = false;
|
model->right_pressed = false;
|
||||||
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_NEXT_TRACK);
|
hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK);
|
||||||
} else if(event->key == InputKeyOk) {
|
} else if(event->key == InputKeyOk) {
|
||||||
model->ok_pressed = false;
|
model->ok_pressed = false;
|
||||||
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_PLAY_PAUSE);
|
hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool bt_hid_media_input_callback(InputEvent* event, void* context) {
|
static bool hid_media_input_callback(InputEvent* event, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
BtHidMedia* bt_hid_media = context;
|
HidMedia* hid_media = context;
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event->type == InputTypePress) {
|
if(event->type == InputTypePress) {
|
||||||
bt_hid_media_process_press(bt_hid_media, event);
|
hid_media_process_press(hid_media, event);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event->type == InputTypeRelease) {
|
} else if(event->type == InputTypeRelease) {
|
||||||
bt_hid_media_process_release(bt_hid_media, event);
|
hid_media_process_release(hid_media, event);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(event->type == InputTypeShort) {
|
} else if(event->type == InputTypeShort) {
|
||||||
if(event->key == InputKeyBack) {
|
if(event->key == InputKeyBack) {
|
||||||
furi_hal_bt_hid_consumer_key_release_all();
|
hid_hal_consumer_key_release_all(hid_media->hid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
BtHidMedia* bt_hid_media_alloc() {
|
HidMedia* hid_media_alloc(Hid* hid) {
|
||||||
BtHidMedia* bt_hid_media = malloc(sizeof(BtHidMedia));
|
HidMedia* hid_media = malloc(sizeof(HidMedia));
|
||||||
bt_hid_media->view = view_alloc();
|
hid_media->view = view_alloc();
|
||||||
view_set_context(bt_hid_media->view, bt_hid_media);
|
hid_media->hid = hid;
|
||||||
view_allocate_model(bt_hid_media->view, ViewModelTypeLocking, sizeof(BtHidMediaModel));
|
view_set_context(hid_media->view, hid_media);
|
||||||
view_set_draw_callback(bt_hid_media->view, bt_hid_media_draw_callback);
|
view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel));
|
||||||
view_set_input_callback(bt_hid_media->view, bt_hid_media_input_callback);
|
view_set_draw_callback(hid_media->view, hid_media_draw_callback);
|
||||||
|
view_set_input_callback(hid_media->view, hid_media_input_callback);
|
||||||
|
|
||||||
return bt_hid_media;
|
return hid_media;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_hid_media_free(BtHidMedia* bt_hid_media) {
|
void hid_media_free(HidMedia* hid_media) {
|
||||||
furi_assert(bt_hid_media);
|
furi_assert(hid_media);
|
||||||
view_free(bt_hid_media->view);
|
view_free(hid_media->view);
|
||||||
free(bt_hid_media);
|
free(hid_media);
|
||||||
}
|
}
|
||||||
|
|
||||||
View* bt_hid_media_get_view(BtHidMedia* bt_hid_media) {
|
View* hid_media_get_view(HidMedia* hid_media) {
|
||||||
furi_assert(bt_hid_media);
|
furi_assert(hid_media);
|
||||||
return bt_hid_media->view;
|
return hid_media->view;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected) {
|
void hid_media_set_connected_status(HidMedia* hid_media, bool connected) {
|
||||||
furi_assert(bt_hid_media);
|
furi_assert(hid_media);
|
||||||
with_view_model(
|
with_view_model(
|
||||||
bt_hid_media->view, BtHidMediaModel * model, { model->connected = connected; }, true);
|
hid_media->view, HidMediaModel * model, { model->connected = connected; }, true);
|
||||||
}
|
}
|
||||||
13
applications/plugins/hid_app/views/hid_media.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
|
||||||
|
typedef struct HidMedia HidMedia;
|
||||||
|
|
||||||
|
HidMedia* hid_media_alloc();
|
||||||
|
|
||||||
|
void hid_media_free(HidMedia* hid_media);
|
||||||
|
|
||||||
|
View* hid_media_get_view(HidMedia* hid_media);
|
||||||
|
|
||||||
|
void hid_media_set_connected_status(HidMedia* hid_media, bool connected);
|
||||||