More cleanup

This commit is contained in:
HarukiToreda 2025-10-13 03:21:37 -04:00
parent 0b11f93880
commit f35f72edb1
6 changed files with 113 additions and 123 deletions

View File

@ -16,11 +16,10 @@ using graphics::MessageRenderer::ThreadMode;
// Calculate serialized size for a StoredMessage
static inline size_t getMessageSize(const StoredMessage &m)
{
// serialized size = fixed 16 bytes + text length (capped at MAX_MESSAGE_SIZE)
// NOTE: Using std::string length here (fast path we had originally)
return 16 + std::min(static_cast<size_t>(MAX_MESSAGE_SIZE), m.text.size());
}
// Helper: assign a timestamp (RTC if available, else boot-relative)
static inline void assignTimestamp(StoredMessage &sm)
{
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
@ -33,6 +32,21 @@ static inline void assignTimestamp(StoredMessage &sm)
}
}
// Generic push with cap (used by live + persisted queues)
template <typename T> static inline void pushWithLimit(std::deque<T> &queue, const T &msg)
{
if (queue.size() >= MAX_MESSAGES_SAVED)
queue.pop_front();
queue.push_back(msg);
}
template <typename T> static inline void pushWithLimit(std::deque<T> &queue, T &&msg)
{
if (queue.size() >= MAX_MESSAGES_SAVED)
queue.pop_front();
queue.emplace_back(std::move(msg));
}
void MessageStore::logMemoryUsage(const char *context) const
{
size_t total = 0;
@ -52,37 +66,24 @@ MessageStore::MessageStore(const std::string &label)
// Live message handling (RAM only)
void MessageStore::addLiveMessage(StoredMessage &&msg)
{
if (liveMessages.size() >= MAX_MESSAGES_SAVED) {
liveMessages.pop_front(); // keep only most recent N
}
liveMessages.emplace_back(std::move(msg));
pushWithLimit(liveMessages, std::move(msg));
}
void MessageStore::addLiveMessage(const StoredMessage &msg)
{
if (liveMessages.size() >= MAX_MESSAGES_SAVED) {
liveMessages.pop_front(); // keep only most recent N
}
liveMessages.push_back(msg);
pushWithLimit(liveMessages, msg);
}
// Persistence queue (used only on shutdown/reboot)
void MessageStore::addMessage(StoredMessage &&msg)
{
if (messages.size() >= MAX_MESSAGES_SAVED) {
messages.pop_front();
}
messages.emplace_back(std::move(msg));
pushWithLimit(messages, std::move(msg));
}
void MessageStore::addMessage(const StoredMessage &msg)
{
if (messages.size() >= MAX_MESSAGES_SAVED) {
messages.pop_front();
}
messages.push_back(msg);
pushWithLimit(messages, msg);
}
// Add from incoming/outgoing packet
const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet)
{
StoredMessage sm;
@ -171,17 +172,18 @@ void MessageStore::saveToFlash()
uint8_t count = messages.size();
f.write(&count, 1);
for (uint8_t i = 0; i < messages.size() && i < MAX_MESSAGES_SAVED; i++) {
for (uint8_t i = 0; i < count && i < MAX_MESSAGES_SAVED; i++) {
const StoredMessage &m = messages.at(i);
f.write((uint8_t *)&m.timestamp, sizeof(m.timestamp));
f.write((uint8_t *)&m.sender, sizeof(m.sender));
f.write((uint8_t *)&m.channelIndex, sizeof(m.channelIndex));
f.write((uint8_t *)&m.dest, sizeof(m.dest));
// Write text payload (capped at MAX_MESSAGE_SIZE)
const size_t toWrite = std::min(static_cast<size_t>(MAX_MESSAGE_SIZE), m.text.size());
if (toWrite)
f.write((const uint8_t *)m.text.data(), toWrite);
f.write('\0'); // null terminator
f.write('\0');
uint8_t bootFlag = m.isBootRelative ? 1 : 0;
f.write(&bootFlag, 1); // persist boot-relative flag
@ -224,9 +226,8 @@ void MessageStore::loadFromFlash()
f.readBytes((char *)&m.channelIndex, sizeof(m.channelIndex));
f.readBytes((char *)&m.dest, sizeof(m.dest));
// Fast text read: read until NUL or cap
m.text.clear();
m.text.reserve(64); // small reserve to avoid initial reallocs
m.text.reserve(64);
char c;
size_t readCount = 0;
while (readCount < MAX_MESSAGE_SIZE) {
@ -238,21 +239,19 @@ void MessageStore::loadFromFlash()
++readCount;
}
// Try to read boot-relative flag (new format)
// Try to read boot-relative flag
uint8_t bootFlag = 0;
if (f.available() > 0) {
if (f.readBytes((char *)&bootFlag, 1) == 1) {
m.isBootRelative = (bootFlag != 0);
} else {
// Old format, fallback heuristic
m.isBootRelative = (m.timestamp < 60u * 60u * 24u * 7u);
}
} else {
// Old format, fallback heuristic
m.isBootRelative = (m.timestamp < 60u * 60u * 24u * 7u);
}
// Try to read ackStatus (newer format)
// Try to read ackStatus
if (f.available() > 0) {
uint8_t statusByte = 0;
if (f.readBytes((char *)&statusByte, 1) == 1) {
@ -408,23 +407,18 @@ void MessageStore::upgradeBootRelativeTimestamps()
uint32_t bootNow = millis() / 1000;
for (auto &m : liveMessages) {
if (m.isBootRelative && m.timestamp <= bootNow) {
uint32_t bootOffset = nowSecs - bootNow;
m.timestamp += bootOffset;
m.isBootRelative = false;
auto fix = [&](std::deque<StoredMessage> &dq) {
for (auto &m : dq) {
if (m.isBootRelative && m.timestamp <= bootNow) {
uint32_t bootOffset = nowSecs - bootNow;
m.timestamp += bootOffset;
m.isBootRelative = false;
}
// else: persisted from old boot → stays ??? forever
}
// else: persisted from old boot → stays ??? forever
}
for (auto &m : messages) {
if (m.isBootRelative && m.timestamp <= bootNow) {
uint32_t bootOffset = nowSecs - bootNow;
m.timestamp += bootOffset;
m.isBootRelative = false;
}
// else: persisted from old boot → stays ??? forever
}
};
fix(liveMessages);
fix(messages);
}
// Global definition

View File

@ -7,7 +7,8 @@
#define LOG_DEBUG(...)
#endif
// Disable message persistence to flash if youre short on space
// Enable or disable message persistence (flash storage)
// Define -DENABLE_MESSAGE_PERSISTENCE=0 in build_flags to disable it entirely
#ifndef ENABLE_MESSAGE_PERSISTENCE
#define ENABLE_MESSAGE_PERSISTENCE 1
#endif
@ -17,15 +18,17 @@
#include <deque>
#include <string>
// Max number of messages well keep in history
#ifndef MAX_MESSAGES_SAVED
#define MAX_MESSAGES_SAVED 20
// how many messages are stored (RAM + flash).
// Define -DMESSAGE_HISTORY_LIMIT=N in build_flags to control memory usage.
#ifndef MESSAGE_HISTORY_LIMIT
#define MESSAGE_HISTORY_LIMIT 20
#endif
// Keep a cap for serialized payloads
#ifndef MAX_MESSAGE_SIZE
// Internal alias used everywhere in code do NOT redefine elsewhere.
#define MAX_MESSAGES_SAVED MESSAGE_HISTORY_LIMIT
// Maximum text payload size per message in bytes (fixed).
#define MAX_MESSAGE_SIZE 220
#endif
// Explicit message classification
enum class MessageType : uint8_t { BROADCAST = 0, DM_TO_US = 1 };
@ -39,6 +42,7 @@ enum class AckStatus : uint8_t {
RELAYED = 4 // got an ACK from relay, not destination
};
// A single stored message in RAM and/or flash
struct StoredMessage {
uint32_t timestamp; // When message was created (secs since boot/RTC)
uint32_t sender; // NodeNum of sender
@ -46,8 +50,6 @@ struct StoredMessage {
std::string text; // UTF-8 text payload (dynamic now, no fixed size)
// Destination node.
// 0xffffffff (NODENUM_BROADCAST) means broadcast,
// otherwise this is the NodeNum of the DM recipient.
uint32_t dest;
// Explicit classification (derived from dest when loading old messages)
@ -115,9 +117,9 @@ class MessageStore
void logMemoryUsage(const char *context) const;
private:
std::deque<StoredMessage> liveMessages;
std::deque<StoredMessage> messages; // persisted copy
std::string filename;
std::deque<StoredMessage> liveMessages; // current in-RAM messages
std::deque<StoredMessage> messages; // persisted copy on flash
std::string filename; // flash filename for persistence
};
// Global instance (defined in MessageStore.cpp)

View File

@ -276,9 +276,7 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
} else {
// otherwise, just display the module frame that's aligned with the current frame
module_frame = state->currentFrame;
// LOG_DEBUG("Screen is not in transition. Frame: %d", module_frame);
}
// LOG_DEBUG("Draw Module Frame %d", module_frame);
MeshModule &pi = *moduleFrames.at(module_frame);
pi.drawFrame(display, state, x, y);
}
@ -329,7 +327,6 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
{
graphics::normalFrames = new FrameCallback[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
LOG_INFO("Protobuf Value uiconfig.screen_rgb_color: %d", uiconfig.screen_rgb_color);
int32_t rawRGB = uiconfig.screen_rgb_color;
// Only validate the combined value once
@ -338,9 +335,6 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
int r = (rawRGB >> 16) & 0xFF;
int g = (rawRGB >> 8) & 0xFF;
int b = rawRGB & 0xFF;
LOG_INFO("Values of r,g,b: %d, %d, %d", r, g, b);
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
TFT_MESH = COLOR565(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b));
}
@ -784,17 +778,17 @@ int32_t Screen::runOnce()
break;
case Cmd::ON_PRESS:
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
handleOnPress();
showFrame(FrameDirection::NEXT);
}
break;
case Cmd::SHOW_PREV_FRAME:
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
handleShowPrevFrame();
showFrame(FrameDirection::PREVIOUS);
}
break;
case Cmd::SHOW_NEXT_FRAME:
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
handleShowNextFrame();
showFrame(FrameDirection::NEXT);
}
break;
case Cmd::START_ALERT_FRAME: {
@ -1266,17 +1260,14 @@ void Screen::hideCurrentFrame()
uint8_t currentFrame = ui->getUiState()->currentFrame;
bool dismissed = false;
if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) {
LOG_DEBUG("Hide Waypoint");
if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) { // hide Waypoint
devicestate.has_rx_waypoint = false;
hiddenFrames.waypoint = true;
dismissed = true;
} else if (currentFrame == framesetInfo.positions.wifi) {
LOG_DEBUG("Hide WiFi Screen");
} else if (currentFrame == framesetInfo.positions.wifi) { // hide Wifi Screen
hiddenFrames.wifi = true;
dismissed = true;
} else if (currentFrame == framesetInfo.positions.lora) {
LOG_INFO("Hide LoRa");
} else if (currentFrame == framesetInfo.positions.lora) { // hide lora screen
hiddenFrames.lora = true;
dismissed = true;
}
@ -1371,30 +1362,21 @@ void Screen::handleOnPress()
}
}
void Screen::handleShowPrevFrame()
void Screen::showFrame(FrameDirection direction)
{
// If screen was off, just wake it, otherwise go back to previous frame
// If we are in a transition, the press must have bounced, drop it.
// Only advance frames when UI is stable
if (ui->getUiState()->frameState == FIXED) {
ui->previousFrame();
if (direction == FrameDirection::NEXT) {
ui->nextFrame();
} else {
ui->previousFrame();
}
lastScreenTransition = millis();
setFastFramerate();
// Reset scroll state if were leaving the text message frame
graphics::MessageRenderer::resetScrollState();
}
}
void Screen::handleShowNextFrame()
{
// If screen was off, just wake it, otherwise advance to next frame
// If we are in a transition, the press must have bounced, drop it.
if (ui->getUiState()->frameState == FIXED) {
ui->nextFrame();
lastScreenTransition = millis();
setFastFramerate();
// Reset scroll state if were leaving the text message frame
// Reset scroll state when switching away from text message screen
graphics::MessageRenderer::resetScrollState();
}
}
@ -1419,7 +1401,6 @@ void Screen::setFastFramerate()
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
{
// LOG_DEBUG("Screen got status update %d", arg->getStatusType());
switch (arg->getStatusType()) {
case STATUS_TYPE_NODE:
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
@ -1516,9 +1497,9 @@ int Screen::handleInputEvent(const InputEvent *event)
// If no modules are using the input, move between frames
if (!inputIntercepted) {
if (event->inputEvent == INPUT_BROKER_LEFT || event->inputEvent == INPUT_BROKER_ALT_PRESS) {
showPrevFrame();
showFrame(FrameDirection::PREVIOUS);
} else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) {
showNextFrame();
showFrame(FrameDirection::NEXT);
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
menuHandler::homeBaseMenu();
@ -1557,7 +1538,7 @@ int Screen::handleInputEvent(const InputEvent *event)
menuHandler::wifiBaseMenu();
}
} else if (event->inputEvent == INPUT_BROKER_BACK) {
showPrevFrame();
showFrame(FrameDirection::PREVIOUS);
} else if (event->inputEvent == INPUT_BROKER_CANCEL) {
setOn(false);
}

View File

@ -169,6 +169,8 @@ class Point
namespace graphics
{
enum class FrameDirection { NEXT, PREVIOUS };
// Forward declarations
class Screen;
@ -271,6 +273,7 @@ class Screen : public concurrency::OSThread
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); }
void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); }
void showFrame(FrameDirection direction);
// generic alert start
void startAlert(FrameCallback _alertFrame)
@ -631,8 +634,6 @@ class Screen : public concurrency::OSThread
// Implementations of various commands, called from doTask().
void handleSetOn(bool on, FrameCallback einkScreensaver = NULL);
void handleOnPress();
void handleShowNextFrame();
void handleShowPrevFrame();
void handleStartFirmwareUpdateScreen();
// Info collected by setFrames method.

View File

@ -36,7 +36,7 @@ static std::vector<std::string> cachedLines;
static std::vector<int> cachedHeights;
// UTF-8 skip helper
inline size_t utf8CharLen(uint8_t c)
static inline size_t utf8CharLen(uint8_t c)
{
if ((c & 0xE0) == 0xC0)
return 2;
@ -75,7 +75,22 @@ std::string normalizeEmoji(const std::string &s)
void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string &line, const Emote *emotes, int emoteCount)
{
std::string renderLine = normalizeEmoji(line);
std::string renderLine;
for (size_t i = 0; i < line.size();) {
uint8_t c = (uint8_t)line[i];
size_t len = utf8CharLen(c);
if (c == 0xEF && i + 2 < line.size() && (uint8_t)line[i + 1] == 0xB8 && (uint8_t)line[i + 2] == 0x8F) {
i += 3;
continue;
}
if (c == 0xF0 && i + 3 < line.size() && (uint8_t)line[i + 1] == 0x9F && (uint8_t)line[i + 2] == 0x8F &&
((uint8_t)line[i + 3] >= 0xBB && (uint8_t)line[i + 3] <= 0xBF)) {
i += 4;
continue;
}
renderLine.append(line, i, len);
i += len;
}
int cursorX = x;
const int fontHeight = FONT_HEIGHT_SMALL;
@ -260,14 +275,14 @@ const std::vector<uint32_t> &getSeenPeers()
return seenPeers;
}
inline int centerYForRow(int y, int size)
static int centerYForRow(int y, int size)
{
int midY = y + (FONT_HEIGHT_SMALL / 2);
return midY - (size / 2);
}
// Helpers for drawing status marks (thickened strokes)
void drawCheckMark(OLEDDisplay *display, int x, int y, int size = 8)
static void drawCheckMark(OLEDDisplay *display, int x, int y, int size)
{
int topY = centerYForRow(y, size);
display->setColor(WHITE);
@ -277,7 +292,7 @@ void drawCheckMark(OLEDDisplay *display, int x, int y, int size = 8)
display->drawLine(x + size / 3, topY + size + 1, x + size, topY + 1);
}
void drawXMark(OLEDDisplay *display, int x, int y, int size = 8)
static void drawXMark(OLEDDisplay *display, int x, int y, int size = 8)
{
int topY = centerYForRow(y, size);
display->setColor(WHITE);
@ -287,7 +302,7 @@ void drawXMark(OLEDDisplay *display, int x, int y, int size = 8)
display->drawLine(x + size, topY + 1, x, topY + size + 1);
}
void drawRelayMark(OLEDDisplay *display, int x, int y, int size = 8)
static void drawRelayMark(OLEDDisplay *display, int x, int y, int size = 8)
{
int r = size / 2;
int centerY = centerYForRow(y, size) + r;
@ -299,7 +314,7 @@ void drawRelayMark(OLEDDisplay *display, int x, int y, int size = 8)
display->drawLine(centerX - 1, centerY - 4, centerX + 1, centerY - 4);
}
int getRenderedLineWidth(OLEDDisplay *display, const std::string &line, const Emote *emotes, int emoteCount)
static inline int getRenderedLineWidth(OLEDDisplay *display, const std::string &line, const Emote *emotes, int emoteCount)
{
std::string normalized = normalizeEmoji(line);
int totalWidth = 0;
@ -471,35 +486,32 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// Build header line for this message
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(m.sender);
std::string senderStr_o = "???";
char senderBuf[48] = "???";
if (node && node->has_user) {
senderStr_o = node->user.long_name;
strncpy(senderBuf, node->user.long_name, sizeof(senderBuf) - 1);
senderBuf[sizeof(senderBuf) - 1] = '\0';
}
// If this is *our own* message, override sender to "Me"
bool mine = (m.sender == nodeDB->getNodeNum());
if (mine) {
senderStr_o = "Me";
strcpy(senderBuf, "Me");
}
const char *sender = senderStr_o.c_str();
const char *sender = senderBuf;
if (display->getStringWidth(sender) + display->getStringWidth(timeBuf) + display->getStringWidth(chanType) +
display->getStringWidth(" @") + display->getStringWidth("... ") - 10 >
SCREEN_WIDTH) {
// truncate sender name if too long
int availWidth = SCREEN_WIDTH - display->getStringWidth(timeBuf) - display->getStringWidth(chanType) -
display->getStringWidth(" @") - display->getStringWidth("... ") - 10;
// If sender width too wide → truncate manually
int availWidth = SCREEN_WIDTH - display->getStringWidth(timeBuf) - display->getStringWidth(chanType) -
display->getStringWidth(" @") - display->getStringWidth("... ") - 10;
int len = static_cast<int>(senderStr_o.size());
while (len > 0 && display->getStringWidth(sender, len) > availWidth) {
--len;
}
while (strlen(senderBuf) > 0 && display->getStringWidth(senderBuf) > availWidth) {
senderBuf[strlen(senderBuf) - 1] = '\0';
}
if (len < static_cast<int>(senderStr_o.size())) {
senderStr_o = senderStr_o.substr(0, len) + "...";
sender = senderStr_o.c_str();
}
// Add ellipsis if needed
if (display->getStringWidth(senderBuf) > availWidth && strlen(senderBuf) >= 3) {
size_t len = strlen(senderBuf);
strcpy(&senderBuf[len - 3], "...");
}
// Final header line

View File

@ -1346,7 +1346,7 @@ int32_t CannedMessageModule::runOnce()
this->freetext.substring(this->cursor);
}
this->cursor++;
uint16_t maxChars = meshtastic_Constants_DATA_PAYLOAD_LEN - (moduleConfig.canned_message.send_bell ? 1 : 0);
const uint16_t maxChars = 200 - (moduleConfig.canned_message.send_bell ? 1 : 0);
if (this->freetext.length() > maxChars) {
this->cursor = maxChars;
this->freetext = this->freetext.substring(0, maxChars);