mirror of
https://github.com/i12bp8/TagTinker.git
synced 2026-08-27 21:10:09 +00:00
Worker (cloud-plugins/): - New plugin 'GitHub Profile': avatar dithered upper-left, display name + @handle (with accent star) + word-wrapped bio in the right column, followers/repos line, and a 53x7 contribution heatmap with an accent palette (red dots for everyday activity, black for the rare hottest days). Tuned for 208x112 ESLs and adapts to 296x128 with a bigger avatar and roomier heatmap. - Identicon plugin gains DiceBear style selector (pixel-art / bottts / lorelei / adventurer / micah / fun-emoji / shapes) on top of the original symmetric grid 'local' mode. Both are rendered through the new shared image_util pipeline. - Crypto plugin redesigned: editorial layout with corner brackets, bold price headline, white-knockout delta badge with a real triangle glyph, currency symbol prefix using the canvas font's new euro / pound / yen glyphs, and a stippled accent fill under the sparkline. Canvas: - setPixel is now last-write-wins across planes, so drawing black on top of an accent-filled area produces a clean black pixel instead of the muddy plane-conflict the e-paper driver was showing previously. Every pixel is therefore exactly one of white / black / accent which is what plugin authors intuit. - New whitePixel + drawTextWhite primitives for knockout text (e.g. white labels punched through a coloured badge). - FONT_EXTRA shipped with the canvas for currency / arrow / shape / star / bracket glyphs. Plugins can now write '\u20AC1234' and have the euro symbol Just Work. Image pipeline (new image_util.ts): - One fetchImageGray() that sniffs PNG vs JPEG, decodes via upng-js / jpeg-js, composites alpha over white, and returns a tightly-packed grayscale buffer. - One blitGrayDither() that nearest-neighbour scales the gray buffer to a destination rect and serpentine Floyd-Steinberg dithers it on the way in. Used by identicon and github. FAP: - WiFi BMP writer is now plane-aware: writes the same biPlanes=1, biBitCount=2, 3-entry palette layout the rest of the IR TX pipeline already understood for web-image-prep BMPs. Tri-colour tags now actually receive accent ink from cloud-rendered plugins. - WiFi run scene un-forces accent: plugins are invoked with the tag's actual accent capability and the BMP is opened with the matching plane count. - Run scene now calls bmp_abort on exit so an aborted transfer doesn't leak the ~10 KB pixel buffer. - Plugin manifest cache capped at TT_WIFI_MAX_FAP_PLUGINS=8 (down from 16) and the UART RX stream buffer shrunk to 8 KB. Together these cut WiFi-flow heap pressure by ~23 KB and fix the OOM crashes that occasionally happened when opening WiFi Plugins. - Broadcast scene auto-transmit fix: enter_callback now filters by the >> Transmit << row index, and the cursor defaults to row 0 instead of Transmit. Previously a stale OK key-release from the prior submenu would land on the Transmit row and blast the radio without any user action.
332 lines
9.8 KiB
C
332 lines
9.8 KiB
C
/*
|
|
* App state.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <furi.h>
|
|
#include <gui/gui.h>
|
|
#include <gui/view_dispatcher.h>
|
|
#include <gui/view.h>
|
|
#include <gui/scene_manager.h>
|
|
#include <gui/modules/submenu.h>
|
|
#include <gui/modules/text_input.h>
|
|
#include <gui/modules/variable_item_list.h>
|
|
#include <gui/modules/popup.h>
|
|
#include <gui/modules/widget.h>
|
|
#include <gui/modules/text_box.h>
|
|
#include <dialogs/dialogs.h>
|
|
#include <notification/notification_messages.h>
|
|
#include <storage/storage.h>
|
|
#include <bt/bt_service/bt.h>
|
|
#include <targets/f7/ble_glue/profiles/serial_profile.h>
|
|
|
|
#include <nfc/nfc.h>
|
|
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
|
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
|
|
|
|
#include "views/numlock_input.h"
|
|
|
|
#include "scenes/tagtinker_scene.h"
|
|
#include "protocol/tagtinker_proto.h"
|
|
#include "ir/tagtinker_ir.h"
|
|
#include "nfc/tagtinker_nfc.h"
|
|
|
|
#define TAGTINKER_TAG "TagTinker"
|
|
#define TAGTINKER_DISPLAY_NAME "TagTinker"
|
|
#define TAGTINKER_VERSION "2.1"
|
|
#define TAGTINKER_BC_LEN 17
|
|
#define TAGTINKER_HEX_LEN 64
|
|
#define TAGTINKER_MAX_TARGETS 16
|
|
#define TAGTINKER_TARGET_NAME_LEN 16
|
|
#define TAGTINKER_MAX_PRESETS 6
|
|
#define TAGTINKER_MAX_SYNCED_IMAGES 24
|
|
#define TAGTINKER_PRESET_TEXT_LEN 32
|
|
#define TAGTINKER_IMAGE_PATH_LEN 255
|
|
#define TAGTINKER_SYNC_JOB_ID_LEN 32
|
|
|
|
typedef enum {
|
|
TagTinkerTxModeNone = 0,
|
|
TagTinkerTxModeTextImage,
|
|
TagTinkerTxModeBmpImage,
|
|
} TagTinkerTxMode;
|
|
|
|
typedef enum {
|
|
TagTinkerSignalPP4 = 0,
|
|
} TagTinkerSignalMode;
|
|
|
|
typedef struct {
|
|
TagTinkerTxMode mode;
|
|
uint8_t plid[4];
|
|
uint8_t page;
|
|
uint16_t width;
|
|
uint16_t height;
|
|
uint16_t pos_x;
|
|
uint16_t pos_y;
|
|
char image_path[TAGTINKER_IMAGE_PATH_LEN + 1];
|
|
} TagTinkerImageTxJob;
|
|
|
|
typedef struct {
|
|
char job_id[TAGTINKER_SYNC_JOB_ID_LEN + 1];
|
|
char barcode[TAGTINKER_BC_LEN + 1];
|
|
uint16_t width;
|
|
uint16_t height;
|
|
uint8_t page;
|
|
/* True when this BMP was authored for a different resolution than the
|
|
* current target, meaning the transmitter will rescale it on the fly. */
|
|
bool resampled;
|
|
char image_path[TAGTINKER_IMAGE_PATH_LEN + 1];
|
|
} TagTinkerSyncedImage;
|
|
|
|
typedef enum {
|
|
TagTinkerTextInputNewText = 0,
|
|
TagTinkerTextInputKeepText = 1,
|
|
TagTinkerTextInputRenameTarget = 2,
|
|
} TagTinkerTextInputMode;
|
|
|
|
/* Views */
|
|
typedef enum {
|
|
TagTinkerViewSubmenu,
|
|
TagTinkerViewVarItemList,
|
|
TagTinkerViewTextInput,
|
|
TagTinkerViewPopup,
|
|
TagTinkerViewWidget,
|
|
TagTinkerViewNumlock,
|
|
TagTinkerViewTextBox,
|
|
TagTinkerViewTargetActions,
|
|
TagTinkerViewWarning,
|
|
TagTinkerViewTransmit,
|
|
TagTinkerViewAbout,
|
|
} TagTinkerView;
|
|
|
|
/* Saved ESL target */
|
|
typedef struct {
|
|
char name[TAGTINKER_TARGET_NAME_LEN + 1];
|
|
char barcode[TAGTINKER_BC_LEN + 1];
|
|
uint8_t plid[4];
|
|
TagTinkerTagProfile profile;
|
|
} TagTinkerTarget;
|
|
|
|
struct TagTinkerApp {
|
|
/* GUI */
|
|
Gui* gui;
|
|
ViewDispatcher* view_dispatcher;
|
|
SceneManager* scene_manager;
|
|
NotificationApp* notifications;
|
|
DialogsApp* dialogs;
|
|
|
|
/* Views */
|
|
Submenu* submenu;
|
|
VariableItemList* var_item_list;
|
|
TextInput* text_input;
|
|
Popup* popup;
|
|
Widget* widget;
|
|
NumlockInput* numlock;
|
|
TextBox* text_box;
|
|
View* target_actions_view;
|
|
View* warning_view;
|
|
View* transmit_view;
|
|
View* about_view;
|
|
bool warning_view_allocated;
|
|
bool transmit_view_allocated;
|
|
bool about_view_allocated;
|
|
|
|
/* TX state */
|
|
bool tx_active;
|
|
FuriThread* tx_thread;
|
|
|
|
/* NFC scan state */
|
|
Nfc* nfc;
|
|
FuriThread* nfc_thread;
|
|
volatile bool nfc_scanning;
|
|
|
|
/* Broadcast settings */
|
|
uint8_t broadcast_type;
|
|
uint8_t page;
|
|
uint16_t duration;
|
|
uint16_t repeats;
|
|
bool forever;
|
|
bool tx_spam;
|
|
|
|
/* Current target */
|
|
char barcode[TAGTINKER_BC_LEN + 1];
|
|
uint8_t plid[4];
|
|
bool barcode_valid;
|
|
int8_t selected_target; /* -1 = none */
|
|
|
|
/* Saved targets */
|
|
TagTinkerTarget targets[TAGTINKER_MAX_TARGETS];
|
|
uint8_t target_count;
|
|
|
|
/* Text to push */
|
|
char text_input_buf[64];
|
|
|
|
/* ESL display size for current target */
|
|
uint16_t esl_width;
|
|
uint16_t esl_height;
|
|
|
|
/* Frame buffer */
|
|
uint8_t frame_buf[TAGTINKER_MAX_FRAME_SIZE];
|
|
size_t frame_len;
|
|
|
|
/* Multi-frame sequence */
|
|
uint8_t** frame_sequence;
|
|
size_t* frame_lengths;
|
|
uint16_t* frame_repeats;
|
|
size_t frame_seq_count;
|
|
bool invert_text;
|
|
bool color_clear;
|
|
uint8_t text_padding_pct;
|
|
|
|
/* Saved recents */
|
|
struct {
|
|
uint16_t width;
|
|
uint16_t height;
|
|
uint8_t page;
|
|
bool invert;
|
|
bool color_clear;
|
|
uint8_t padding;
|
|
uint8_t signal_mode;
|
|
char text[TAGTINKER_PRESET_TEXT_LEN];
|
|
} recents[TAGTINKER_MAX_PRESETS];
|
|
uint8_t recent_count;
|
|
|
|
TagTinkerSyncedImage synced_images[TAGTINKER_MAX_SYNCED_IMAGES];
|
|
uint8_t synced_image_count;
|
|
|
|
/* Image settings */
|
|
uint8_t img_page;
|
|
uint16_t draw_x;
|
|
uint16_t draw_y;
|
|
uint16_t draw_width;
|
|
uint16_t draw_height;
|
|
TagTinkerImageTxJob image_tx_job;
|
|
TagTinkerCompressionMode compression_mode;
|
|
uint8_t data_frame_repeats;
|
|
TagTinkerSignalMode signal_mode;
|
|
bool show_startup_warning;
|
|
|
|
/* Indicates which mode triggered raw cmd (0=broadcast, 1=targeted) */
|
|
uint8_t raw_mode;
|
|
|
|
/* Browser BLE sync state */
|
|
Bt* bt;
|
|
FuriHalBleProfileBase* ble_serial;
|
|
BtStatus ble_status;
|
|
bool ble_sync_active;
|
|
bool ble_sync_start_pending;
|
|
bool ble_serial_configured;
|
|
uint32_t ble_total_rx;
|
|
uint8_t ble_last_bytes[3];
|
|
bool ble_sync_auto_send_pending;
|
|
uint8_t ble_sync_auto_send_delay;
|
|
uint16_t ble_synced_lines;
|
|
char ble_status_text[32];
|
|
char ble_rx_line[1024];
|
|
char ble_rx_pending_line[1024];
|
|
uint16_t ble_rx_len;
|
|
bool ble_rx_pending_ready;
|
|
bool ble_sync_job_active;
|
|
char ble_sync_job_id[TAGTINKER_SYNC_JOB_ID_LEN + 1];
|
|
char ble_sync_barcode[TAGTINKER_BC_LEN + 1];
|
|
char ble_sync_temp_path[TAGTINKER_IMAGE_PATH_LEN + 1];
|
|
char ble_sync_final_path[TAGTINKER_IMAGE_PATH_LEN + 1];
|
|
char ble_sync_last_job_id[TAGTINKER_SYNC_JOB_ID_LEN + 1];
|
|
uint32_t ble_sync_expected_bytes;
|
|
uint32_t ble_sync_received_bytes;
|
|
uint16_t ble_sync_last_chunk;
|
|
File* ble_sync_file;
|
|
uint16_t ble_sync_last_completed_chunks;
|
|
bool ble_sync_compact_protocol;
|
|
bool ble_sync_last_compact_protocol;
|
|
int8_t ble_sync_ready_target;
|
|
|
|
/* ---- WiFi Plugins (ESP32 dev board) -------------------------------- */
|
|
/* Opaque handle (TagTinkerWifi*) - declared in wifi/tagtinker_wifi.h.
|
|
* Stored as void* here so this header doesn't pull in expansion/serial
|
|
* deps for unrelated translation units. */
|
|
void* wifi;
|
|
/* WiFi link state mirrored from the ESP. */
|
|
uint8_t wifi_link_state; /* TT_WIFI_* */
|
|
int8_t wifi_rssi;
|
|
char wifi_ssid[33];
|
|
char wifi_ip[20];
|
|
char wifi_creds_ssid[33]; /* used by setup scene before sending */
|
|
char wifi_creds_pwd[65];
|
|
/* Plugin discovery cache. Up to TT_WIFI_MAX_FAP_PLUGINS slots. */
|
|
void* wifi_plugins; /* TagTinkerWifiPlugin[TT_WIFI_MAX_FAP_PLUGINS], heap-alloced */
|
|
uint8_t wifi_plugin_count;
|
|
bool wifi_plugins_loading;
|
|
int8_t wifi_selected_plugin;
|
|
/* Per-run state. */
|
|
char wifi_progress_msg[64];
|
|
uint8_t wifi_progress_pct;
|
|
char wifi_last_error[80];
|
|
bool wifi_run_in_flight;
|
|
bool wifi_result_ready;
|
|
/* Param values being collected by the run scene; one slot per plugin
|
|
* param, holding the textual value the user picked (string for STRING,
|
|
* stringified int for INT, option name for ENUM, "0"/"1" for BOOL). */
|
|
char wifi_param_values[6][64];
|
|
};
|
|
|
|
/* Main menu items */
|
|
typedef enum {
|
|
TagTinkerMenuBroadcast,
|
|
TagTinkerMenuTargetESL,
|
|
TagTinkerMenuWifiPlugins,
|
|
TagTinkerMenuAbout,
|
|
} TagTinkerMainMenuItem;
|
|
|
|
/* Broadcast menu items */
|
|
typedef enum {
|
|
TagTinkerBroadcastFlipPage,
|
|
TagTinkerBroadcastDebugScreen,
|
|
} TagTinkerBroadcastMenuItem;
|
|
|
|
/* Target action items */
|
|
typedef enum {
|
|
TagTinkerTargetDetails,
|
|
TagTinkerTargetRename,
|
|
TagTinkerTargetPushText,
|
|
TagTinkerTargetPushSyncedImage,
|
|
TagTinkerTargetWifiPlugins,
|
|
TagTinkerTargetDeleteSyncedImages,
|
|
TagTinkerTargetPingFlash,
|
|
TagTinkerTargetDeleteTag,
|
|
} TagTinkerTargetActionItem;
|
|
|
|
void tagtinker_target_refresh_profile(TagTinkerTarget* target);
|
|
void tagtinker_target_set_default_name(TagTinkerApp* app, TagTinkerTarget* target);
|
|
bool tagtinker_target_supports_graphics(const TagTinkerTarget* target);
|
|
bool tagtinker_target_supports_accent(const TagTinkerTarget* target);
|
|
const char* tagtinker_profile_kind_label(TagTinkerTagKind kind);
|
|
const char* tagtinker_profile_color_label(TagTinkerTagColor color);
|
|
int8_t tagtinker_find_target_by_barcode(const TagTinkerApp* app, const char* barcode);
|
|
int8_t tagtinker_ensure_target(TagTinkerApp* app, const char* barcode);
|
|
bool tagtinker_find_latest_synced_image(
|
|
const TagTinkerApp* app,
|
|
const char* barcode,
|
|
TagTinkerSyncedImage* image);
|
|
size_t tagtinker_delete_synced_images_for_barcode(TagTinkerApp* app, const char* barcode);
|
|
bool tagtinker_delete_target(TagTinkerApp* app, uint8_t index);
|
|
|
|
void tagtinker_free_frame_sequence(TagTinkerApp* app);
|
|
uint16_t tagtinker_pick_chunk_height(uint16_t width, bool color_clear);
|
|
void tagtinker_prepare_text_tx(TagTinkerApp* app, const uint8_t plid[4]);
|
|
void tagtinker_prepare_bmp_tx(
|
|
TagTinkerApp* app,
|
|
const uint8_t plid[4],
|
|
const char* image_path,
|
|
uint16_t width,
|
|
uint16_t height,
|
|
uint8_t page);
|
|
void tagtinker_select_target(TagTinkerApp* app, uint8_t index);
|
|
void tagtinker_settings_load(TagTinkerApp* app);
|
|
bool tagtinker_settings_save(const TagTinkerApp* app);
|
|
void tagtinker_targets_load(TagTinkerApp* app);
|
|
bool tagtinker_targets_save(const TagTinkerApp* app);
|
|
void tagtinker_recents_load(TagTinkerApp* app);
|
|
bool tagtinker_recents_save(const TagTinkerApp* app);
|
|
void tagtinker_recents_add(TagTinkerApp* app, const char* text);
|