mirror of
https://github.com/meshtastic/firmware.git
synced 2025-10-27 15:02:41 +00:00
More cleanup
This commit is contained in:
parent
0b11f93880
commit
f35f72edb1
@ -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,16 +407,8 @@ 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;
|
||||
}
|
||||
// else: persisted from old boot → stays ??? forever
|
||||
}
|
||||
|
||||
for (auto &m : messages) {
|
||||
auto fix = [&](std::deque<StoredMessage> &dq) {
|
||||
for (auto &m : dq) {
|
||||
if (m.isBootRelative && m.timestamp <= bootNow) {
|
||||
uint32_t bootOffset = nowSecs - bootNow;
|
||||
m.timestamp += bootOffset;
|
||||
@ -425,6 +416,9 @@ void MessageStore::upgradeBootRelativeTimestamps()
|
||||
}
|
||||
// else: persisted from old boot → stays ??? forever
|
||||
}
|
||||
};
|
||||
fix(liveMessages);
|
||||
fix(messages);
|
||||
}
|
||||
|
||||
// Global definition
|
||||
|
||||
@ -7,7 +7,8 @@
|
||||
#define LOG_DEBUG(...)
|
||||
#endif
|
||||
|
||||
// Disable message persistence to flash if you’re 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 we’ll 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)
|
||||
|
||||
@ -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();
|
||||
lastScreenTransition = millis();
|
||||
setFastFramerate();
|
||||
|
||||
// Reset scroll state if we’re 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) {
|
||||
if (direction == FrameDirection::NEXT) {
|
||||
ui->nextFrame();
|
||||
} else {
|
||||
ui->previousFrame();
|
||||
}
|
||||
|
||||
lastScreenTransition = millis();
|
||||
setFastFramerate();
|
||||
|
||||
// Reset scroll state if we’re 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);
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
// 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
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user