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
|
// Calculate serialized size for a StoredMessage
|
||||||
static inline size_t getMessageSize(const StoredMessage &m)
|
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());
|
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)
|
static inline void assignTimestamp(StoredMessage &sm)
|
||||||
{
|
{
|
||||||
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
|
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
|
void MessageStore::logMemoryUsage(const char *context) const
|
||||||
{
|
{
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
@ -52,37 +66,24 @@ MessageStore::MessageStore(const std::string &label)
|
|||||||
// Live message handling (RAM only)
|
// Live message handling (RAM only)
|
||||||
void MessageStore::addLiveMessage(StoredMessage &&msg)
|
void MessageStore::addLiveMessage(StoredMessage &&msg)
|
||||||
{
|
{
|
||||||
if (liveMessages.size() >= MAX_MESSAGES_SAVED) {
|
pushWithLimit(liveMessages, std::move(msg));
|
||||||
liveMessages.pop_front(); // keep only most recent N
|
|
||||||
}
|
|
||||||
liveMessages.emplace_back(std::move(msg));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageStore::addLiveMessage(const StoredMessage &msg)
|
void MessageStore::addLiveMessage(const StoredMessage &msg)
|
||||||
{
|
{
|
||||||
if (liveMessages.size() >= MAX_MESSAGES_SAVED) {
|
pushWithLimit(liveMessages, msg);
|
||||||
liveMessages.pop_front(); // keep only most recent N
|
|
||||||
}
|
|
||||||
liveMessages.push_back(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persistence queue (used only on shutdown/reboot)
|
// Persistence queue (used only on shutdown/reboot)
|
||||||
void MessageStore::addMessage(StoredMessage &&msg)
|
void MessageStore::addMessage(StoredMessage &&msg)
|
||||||
{
|
{
|
||||||
if (messages.size() >= MAX_MESSAGES_SAVED) {
|
pushWithLimit(messages, std::move(msg));
|
||||||
messages.pop_front();
|
|
||||||
}
|
|
||||||
messages.emplace_back(std::move(msg));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageStore::addMessage(const StoredMessage &msg)
|
void MessageStore::addMessage(const StoredMessage &msg)
|
||||||
{
|
{
|
||||||
if (messages.size() >= MAX_MESSAGES_SAVED) {
|
pushWithLimit(messages, msg);
|
||||||
messages.pop_front();
|
|
||||||
}
|
|
||||||
messages.push_back(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add from incoming/outgoing packet
|
||||||
const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet)
|
const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet)
|
||||||
{
|
{
|
||||||
StoredMessage sm;
|
StoredMessage sm;
|
||||||
@ -171,17 +172,18 @@ void MessageStore::saveToFlash()
|
|||||||
uint8_t count = messages.size();
|
uint8_t count = messages.size();
|
||||||
f.write(&count, 1);
|
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);
|
const StoredMessage &m = messages.at(i);
|
||||||
f.write((uint8_t *)&m.timestamp, sizeof(m.timestamp));
|
f.write((uint8_t *)&m.timestamp, sizeof(m.timestamp));
|
||||||
f.write((uint8_t *)&m.sender, sizeof(m.sender));
|
f.write((uint8_t *)&m.sender, sizeof(m.sender));
|
||||||
f.write((uint8_t *)&m.channelIndex, sizeof(m.channelIndex));
|
f.write((uint8_t *)&m.channelIndex, sizeof(m.channelIndex));
|
||||||
f.write((uint8_t *)&m.dest, sizeof(m.dest));
|
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());
|
const size_t toWrite = std::min(static_cast<size_t>(MAX_MESSAGE_SIZE), m.text.size());
|
||||||
if (toWrite)
|
if (toWrite)
|
||||||
f.write((const uint8_t *)m.text.data(), 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;
|
uint8_t bootFlag = m.isBootRelative ? 1 : 0;
|
||||||
f.write(&bootFlag, 1); // persist boot-relative flag
|
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.channelIndex, sizeof(m.channelIndex));
|
||||||
f.readBytes((char *)&m.dest, sizeof(m.dest));
|
f.readBytes((char *)&m.dest, sizeof(m.dest));
|
||||||
|
|
||||||
// Fast text read: read until NUL or cap
|
|
||||||
m.text.clear();
|
m.text.clear();
|
||||||
m.text.reserve(64); // small reserve to avoid initial reallocs
|
m.text.reserve(64);
|
||||||
char c;
|
char c;
|
||||||
size_t readCount = 0;
|
size_t readCount = 0;
|
||||||
while (readCount < MAX_MESSAGE_SIZE) {
|
while (readCount < MAX_MESSAGE_SIZE) {
|
||||||
@ -238,21 +239,19 @@ void MessageStore::loadFromFlash()
|
|||||||
++readCount;
|
++readCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to read boot-relative flag (new format)
|
// Try to read boot-relative flag
|
||||||
uint8_t bootFlag = 0;
|
uint8_t bootFlag = 0;
|
||||||
if (f.available() > 0) {
|
if (f.available() > 0) {
|
||||||
if (f.readBytes((char *)&bootFlag, 1) == 1) {
|
if (f.readBytes((char *)&bootFlag, 1) == 1) {
|
||||||
m.isBootRelative = (bootFlag != 0);
|
m.isBootRelative = (bootFlag != 0);
|
||||||
} else {
|
} else {
|
||||||
// Old format, fallback heuristic
|
|
||||||
m.isBootRelative = (m.timestamp < 60u * 60u * 24u * 7u);
|
m.isBootRelative = (m.timestamp < 60u * 60u * 24u * 7u);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Old format, fallback heuristic
|
|
||||||
m.isBootRelative = (m.timestamp < 60u * 60u * 24u * 7u);
|
m.isBootRelative = (m.timestamp < 60u * 60u * 24u * 7u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to read ackStatus (newer format)
|
// Try to read ackStatus
|
||||||
if (f.available() > 0) {
|
if (f.available() > 0) {
|
||||||
uint8_t statusByte = 0;
|
uint8_t statusByte = 0;
|
||||||
if (f.readBytes((char *)&statusByte, 1) == 1) {
|
if (f.readBytes((char *)&statusByte, 1) == 1) {
|
||||||
@ -408,25 +407,20 @@ void MessageStore::upgradeBootRelativeTimestamps()
|
|||||||
|
|
||||||
uint32_t bootNow = millis() / 1000;
|
uint32_t bootNow = millis() / 1000;
|
||||||
|
|
||||||
for (auto &m : liveMessages) {
|
auto fix = [&](std::deque<StoredMessage> &dq) {
|
||||||
if (m.isBootRelative && m.timestamp <= bootNow) {
|
for (auto &m : dq) {
|
||||||
uint32_t bootOffset = nowSecs - bootNow;
|
if (m.isBootRelative && m.timestamp <= bootNow) {
|
||||||
m.timestamp += bootOffset;
|
uint32_t bootOffset = nowSecs - bootNow;
|
||||||
m.isBootRelative = false;
|
m.timestamp += bootOffset;
|
||||||
|
m.isBootRelative = false;
|
||||||
|
}
|
||||||
|
// else: persisted from old boot → stays ??? forever
|
||||||
}
|
}
|
||||||
// else: persisted from old boot → stays ??? forever
|
};
|
||||||
}
|
fix(liveMessages);
|
||||||
|
fix(messages);
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global definition
|
// Global definition
|
||||||
MessageStore messageStore("default");
|
MessageStore messageStore("default");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -7,7 +7,8 @@
|
|||||||
#define LOG_DEBUG(...)
|
#define LOG_DEBUG(...)
|
||||||
#endif
|
#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
|
#ifndef ENABLE_MESSAGE_PERSISTENCE
|
||||||
#define ENABLE_MESSAGE_PERSISTENCE 1
|
#define ENABLE_MESSAGE_PERSISTENCE 1
|
||||||
#endif
|
#endif
|
||||||
@ -17,15 +18,17 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
// Max number of messages we’ll keep in history
|
// how many messages are stored (RAM + flash).
|
||||||
#ifndef MAX_MESSAGES_SAVED
|
// Define -DMESSAGE_HISTORY_LIMIT=N in build_flags to control memory usage.
|
||||||
#define MAX_MESSAGES_SAVED 20
|
#ifndef MESSAGE_HISTORY_LIMIT
|
||||||
|
#define MESSAGE_HISTORY_LIMIT 20
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Keep a cap for serialized payloads
|
// Internal alias used everywhere in code – do NOT redefine elsewhere.
|
||||||
#ifndef MAX_MESSAGE_SIZE
|
#define MAX_MESSAGES_SAVED MESSAGE_HISTORY_LIMIT
|
||||||
|
|
||||||
|
// Maximum text payload size per message in bytes (fixed).
|
||||||
#define MAX_MESSAGE_SIZE 220
|
#define MAX_MESSAGE_SIZE 220
|
||||||
#endif
|
|
||||||
|
|
||||||
// Explicit message classification
|
// Explicit message classification
|
||||||
enum class MessageType : uint8_t { BROADCAST = 0, DM_TO_US = 1 };
|
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
|
RELAYED = 4 // got an ACK from relay, not destination
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A single stored message in RAM and/or flash
|
||||||
struct StoredMessage {
|
struct StoredMessage {
|
||||||
uint32_t timestamp; // When message was created (secs since boot/RTC)
|
uint32_t timestamp; // When message was created (secs since boot/RTC)
|
||||||
uint32_t sender; // NodeNum of sender
|
uint32_t sender; // NodeNum of sender
|
||||||
@ -46,8 +50,6 @@ struct StoredMessage {
|
|||||||
std::string text; // UTF-8 text payload (dynamic now, no fixed size)
|
std::string text; // UTF-8 text payload (dynamic now, no fixed size)
|
||||||
|
|
||||||
// Destination node.
|
// Destination node.
|
||||||
// 0xffffffff (NODENUM_BROADCAST) means broadcast,
|
|
||||||
// otherwise this is the NodeNum of the DM recipient.
|
|
||||||
uint32_t dest;
|
uint32_t dest;
|
||||||
|
|
||||||
// Explicit classification (derived from dest when loading old messages)
|
// Explicit classification (derived from dest when loading old messages)
|
||||||
@ -115,9 +117,9 @@ class MessageStore
|
|||||||
void logMemoryUsage(const char *context) const;
|
void logMemoryUsage(const char *context) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::deque<StoredMessage> liveMessages;
|
std::deque<StoredMessage> liveMessages; // current in-RAM messages
|
||||||
std::deque<StoredMessage> messages; // persisted copy
|
std::deque<StoredMessage> messages; // persisted copy on flash
|
||||||
std::string filename;
|
std::string filename; // flash filename for persistence
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global instance (defined in MessageStore.cpp)
|
// Global instance (defined in MessageStore.cpp)
|
||||||
|
|||||||
@ -276,9 +276,7 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
|
|||||||
} else {
|
} else {
|
||||||
// otherwise, just display the module frame that's aligned with the current frame
|
// otherwise, just display the module frame that's aligned with the current frame
|
||||||
module_frame = state->currentFrame;
|
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);
|
MeshModule &pi = *moduleFrames.at(module_frame);
|
||||||
pi.drawFrame(display, state, x, y);
|
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];
|
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;
|
int32_t rawRGB = uiconfig.screen_rgb_color;
|
||||||
|
|
||||||
// Only validate the combined value once
|
// 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 r = (rawRGB >> 16) & 0xFF;
|
||||||
int g = (rawRGB >> 8) & 0xFF;
|
int g = (rawRGB >> 8) & 0xFF;
|
||||||
int b = rawRGB & 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) {
|
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));
|
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;
|
break;
|
||||||
case Cmd::ON_PRESS:
|
case Cmd::ON_PRESS:
|
||||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||||
handleOnPress();
|
showFrame(FrameDirection::NEXT);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Cmd::SHOW_PREV_FRAME:
|
case Cmd::SHOW_PREV_FRAME:
|
||||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||||
handleShowPrevFrame();
|
showFrame(FrameDirection::PREVIOUS);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Cmd::SHOW_NEXT_FRAME:
|
case Cmd::SHOW_NEXT_FRAME:
|
||||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||||
handleShowNextFrame();
|
showFrame(FrameDirection::NEXT);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Cmd::START_ALERT_FRAME: {
|
case Cmd::START_ALERT_FRAME: {
|
||||||
@ -1266,17 +1260,14 @@ void Screen::hideCurrentFrame()
|
|||||||
uint8_t currentFrame = ui->getUiState()->currentFrame;
|
uint8_t currentFrame = ui->getUiState()->currentFrame;
|
||||||
bool dismissed = false;
|
bool dismissed = false;
|
||||||
|
|
||||||
if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) {
|
if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) { // hide Waypoint
|
||||||
LOG_DEBUG("Hide Waypoint");
|
|
||||||
devicestate.has_rx_waypoint = false;
|
devicestate.has_rx_waypoint = false;
|
||||||
hiddenFrames.waypoint = true;
|
hiddenFrames.waypoint = true;
|
||||||
dismissed = true;
|
dismissed = true;
|
||||||
} else if (currentFrame == framesetInfo.positions.wifi) {
|
} else if (currentFrame == framesetInfo.positions.wifi) { // hide Wifi Screen
|
||||||
LOG_DEBUG("Hide WiFi Screen");
|
|
||||||
hiddenFrames.wifi = true;
|
hiddenFrames.wifi = true;
|
||||||
dismissed = true;
|
dismissed = true;
|
||||||
} else if (currentFrame == framesetInfo.positions.lora) {
|
} else if (currentFrame == framesetInfo.positions.lora) { // hide lora screen
|
||||||
LOG_INFO("Hide LoRa");
|
|
||||||
hiddenFrames.lora = true;
|
hiddenFrames.lora = true;
|
||||||
dismissed = 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
|
// Only advance frames when UI is stable
|
||||||
// If we are in a transition, the press must have bounced, drop it.
|
|
||||||
if (ui->getUiState()->frameState == FIXED) {
|
if (ui->getUiState()->frameState == FIXED) {
|
||||||
ui->previousFrame();
|
|
||||||
|
if (direction == FrameDirection::NEXT) {
|
||||||
|
ui->nextFrame();
|
||||||
|
} else {
|
||||||
|
ui->previousFrame();
|
||||||
|
}
|
||||||
|
|
||||||
lastScreenTransition = millis();
|
lastScreenTransition = millis();
|
||||||
setFastFramerate();
|
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 we’re leaving the text message frame
|
|
||||||
graphics::MessageRenderer::resetScrollState();
|
graphics::MessageRenderer::resetScrollState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1419,7 +1401,6 @@ void Screen::setFastFramerate()
|
|||||||
|
|
||||||
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||||
{
|
{
|
||||||
// LOG_DEBUG("Screen got status update %d", arg->getStatusType());
|
|
||||||
switch (arg->getStatusType()) {
|
switch (arg->getStatusType()) {
|
||||||
case STATUS_TYPE_NODE:
|
case STATUS_TYPE_NODE:
|
||||||
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
|
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 no modules are using the input, move between frames
|
||||||
if (!inputIntercepted) {
|
if (!inputIntercepted) {
|
||||||
if (event->inputEvent == INPUT_BROKER_LEFT || event->inputEvent == INPUT_BROKER_ALT_PRESS) {
|
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) {
|
} else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
showNextFrame();
|
showFrame(FrameDirection::NEXT);
|
||||||
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
|
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
|
||||||
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
||||||
menuHandler::homeBaseMenu();
|
menuHandler::homeBaseMenu();
|
||||||
@ -1557,7 +1538,7 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
menuHandler::wifiBaseMenu();
|
menuHandler::wifiBaseMenu();
|
||||||
}
|
}
|
||||||
} else if (event->inputEvent == INPUT_BROKER_BACK) {
|
} else if (event->inputEvent == INPUT_BROKER_BACK) {
|
||||||
showPrevFrame();
|
showFrame(FrameDirection::PREVIOUS);
|
||||||
} else if (event->inputEvent == INPUT_BROKER_CANCEL) {
|
} else if (event->inputEvent == INPUT_BROKER_CANCEL) {
|
||||||
setOn(false);
|
setOn(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -169,6 +169,8 @@ class Point
|
|||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
|
enum class FrameDirection { NEXT, PREVIOUS };
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class Screen;
|
class Screen;
|
||||||
|
|
||||||
@ -271,6 +273,7 @@ class Screen : public concurrency::OSThread
|
|||||||
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
||||||
void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); }
|
void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); }
|
||||||
void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); }
|
void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); }
|
||||||
|
void showFrame(FrameDirection direction);
|
||||||
|
|
||||||
// generic alert start
|
// generic alert start
|
||||||
void startAlert(FrameCallback _alertFrame)
|
void startAlert(FrameCallback _alertFrame)
|
||||||
@ -631,8 +634,6 @@ class Screen : public concurrency::OSThread
|
|||||||
// Implementations of various commands, called from doTask().
|
// Implementations of various commands, called from doTask().
|
||||||
void handleSetOn(bool on, FrameCallback einkScreensaver = NULL);
|
void handleSetOn(bool on, FrameCallback einkScreensaver = NULL);
|
||||||
void handleOnPress();
|
void handleOnPress();
|
||||||
void handleShowNextFrame();
|
|
||||||
void handleShowPrevFrame();
|
|
||||||
void handleStartFirmwareUpdateScreen();
|
void handleStartFirmwareUpdateScreen();
|
||||||
|
|
||||||
// Info collected by setFrames method.
|
// Info collected by setFrames method.
|
||||||
|
|||||||
@ -36,7 +36,7 @@ static std::vector<std::string> cachedLines;
|
|||||||
static std::vector<int> cachedHeights;
|
static std::vector<int> cachedHeights;
|
||||||
|
|
||||||
// UTF-8 skip helper
|
// UTF-8 skip helper
|
||||||
inline size_t utf8CharLen(uint8_t c)
|
static inline size_t utf8CharLen(uint8_t c)
|
||||||
{
|
{
|
||||||
if ((c & 0xE0) == 0xC0)
|
if ((c & 0xE0) == 0xC0)
|
||||||
return 2;
|
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)
|
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;
|
int cursorX = x;
|
||||||
const int fontHeight = FONT_HEIGHT_SMALL;
|
const int fontHeight = FONT_HEIGHT_SMALL;
|
||||||
|
|
||||||
@ -260,14 +275,14 @@ const std::vector<uint32_t> &getSeenPeers()
|
|||||||
return seenPeers;
|
return seenPeers;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int centerYForRow(int y, int size)
|
static int centerYForRow(int y, int size)
|
||||||
{
|
{
|
||||||
int midY = y + (FONT_HEIGHT_SMALL / 2);
|
int midY = y + (FONT_HEIGHT_SMALL / 2);
|
||||||
return midY - (size / 2);
|
return midY - (size / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers for drawing status marks (thickened strokes)
|
// 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);
|
int topY = centerYForRow(y, size);
|
||||||
display->setColor(WHITE);
|
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);
|
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);
|
int topY = centerYForRow(y, size);
|
||||||
display->setColor(WHITE);
|
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);
|
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 r = size / 2;
|
||||||
int centerY = centerYForRow(y, size) + r;
|
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);
|
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);
|
std::string normalized = normalizeEmoji(line);
|
||||||
int totalWidth = 0;
|
int totalWidth = 0;
|
||||||
@ -471,35 +486,32 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
// Build header line for this message
|
// Build header line for this message
|
||||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(m.sender);
|
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(m.sender);
|
||||||
|
|
||||||
std::string senderStr_o = "???";
|
char senderBuf[48] = "???";
|
||||||
if (node && node->has_user) {
|
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"
|
// If this is *our own* message, override sender to "Me"
|
||||||
bool mine = (m.sender == nodeDB->getNodeNum());
|
bool mine = (m.sender == nodeDB->getNodeNum());
|
||||||
if (mine) {
|
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) +
|
// If sender width too wide → truncate manually
|
||||||
display->getStringWidth(" @") + display->getStringWidth("... ") - 10 >
|
int availWidth = SCREEN_WIDTH - display->getStringWidth(timeBuf) - display->getStringWidth(chanType) -
|
||||||
SCREEN_WIDTH) {
|
display->getStringWidth(" @") - display->getStringWidth("... ") - 10;
|
||||||
// truncate sender name if too long
|
|
||||||
int availWidth = SCREEN_WIDTH - display->getStringWidth(timeBuf) - display->getStringWidth(chanType) -
|
|
||||||
display->getStringWidth(" @") - display->getStringWidth("... ") - 10;
|
|
||||||
|
|
||||||
int len = static_cast<int>(senderStr_o.size());
|
while (strlen(senderBuf) > 0 && display->getStringWidth(senderBuf) > availWidth) {
|
||||||
while (len > 0 && display->getStringWidth(sender, len) > availWidth) {
|
senderBuf[strlen(senderBuf) - 1] = '\0';
|
||||||
--len;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (len < static_cast<int>(senderStr_o.size())) {
|
// Add ellipsis if needed
|
||||||
senderStr_o = senderStr_o.substr(0, len) + "...";
|
if (display->getStringWidth(senderBuf) > availWidth && strlen(senderBuf) >= 3) {
|
||||||
sender = senderStr_o.c_str();
|
size_t len = strlen(senderBuf);
|
||||||
}
|
strcpy(&senderBuf[len - 3], "...");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final header line
|
// Final header line
|
||||||
|
|||||||
@ -1346,7 +1346,7 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
this->freetext.substring(this->cursor);
|
this->freetext.substring(this->cursor);
|
||||||
}
|
}
|
||||||
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) {
|
if (this->freetext.length() > maxChars) {
|
||||||
this->cursor = maxChars;
|
this->cursor = maxChars;
|
||||||
this->freetext = this->freetext.substring(0, maxChars);
|
this->freetext = this->freetext.substring(0, maxChars);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user