Compare commits

..
Author SHA1 Message Date
HarukiToredaandCopilot Autofix powered by AI 5e1f9b5bc7 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-11 13:22:06 -04:00
HarukiToredaandCopilot Autofix powered by AI fd3002b9a3 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-11 13:20:01 -04:00
HarukiToredaandCopilot Autofix powered by AI 25ac520fb8 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-11 13:19:43 -04:00
HarukiToreda d39bf2f8a0 cleanup 2026-05-10 20:44:28 -04:00
94bb21ecc7 2.8: NodeDB shrink, decoupling, and restructuring (#10413)
* 2.8: NodeDB refactor to decouple satellite entries and decrease size

* Regen

* Refactor node mute handling to use dedicated functions for clarity and consistency

* Develop ref

* Fix NodeDB review follow-ups

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/6b1d6cf6-ed6b-43b6-95cb-8e141757664e

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Address review validation nits

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/6b1d6cf6-ed6b-43b6-95cb-8e141757664e

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Trunk

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Extract legacy NodeDatabase migration

* Fix remaining NodeDB review issues

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/c76b9a5a-7244-4fbc-9ef0-98091d8caaea

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Fixes

* Trunk

* Fix latest review compile follow-ups

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/5198da01-ec4c-4c16-8a09-68b8e6d5d410

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Fix cppcheck style warnings

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/e60287ba-4ece-46e0-83d8-a6d89664c0bb

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Change pointer type for mesh node in set_favorite function

* Change pointer types for mesh node references to const in multiple applets

* Add NodeDB layout v25 documentation and migration guidelines

* Remove tests for uninitialized PacketHistory state due to undefined behavior

* Fix code block formatting in copilot instructions

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-09 15:12:10 -05:00
SylwesterandBen Meadors 1bcabb893b mesh: bound the user-facing notification sprintf calls (#10437)
Two sites built ClientNotification messages with sprintf into a
fixed-size proto buffer with no length cap. The current format strings
fit comfortably, but a future caller editing either format string
without rechecking the buffer size would get a silent stack/heap
overrun. Switch to snprintf with sizeof so the bound is enforced at
the call site.

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-05-09 13:32:42 -05:00
BJK fefd424901 Fix screen geometry update for SH1107 display (#10444)
Added conditional block to update screen geometry for SH1107 128x128.
2026-05-09 12:53:07 -05:00
15 changed files with 122 additions and 214 deletions
+26 -31
View File
@@ -6,7 +6,6 @@
#include "SPILock.h"
#include "SafeFile.h"
#include "gps/RTC.h"
#include "graphics/draw/MessageRenderer.h"
#include <cstring> // memcpy
#ifndef MESSAGE_TEXT_POOL_SIZE
@@ -181,13 +180,8 @@ const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &pa
bool isDM = (sm.dest != 0 && sm.dest != NODENUM_BROADCAST);
if (packet.from == 0) {
sm.type = isDM ? MessageType::DM_TO_US : MessageType::BROADCAST;
sm.ackStatus = AckStatus::NONE;
} else {
sm.type = isDM ? MessageType::DM_TO_US : MessageType::BROADCAST;
sm.ackStatus = AckStatus::ACKED;
}
sm.type = isDM ? MessageType::DM_TO_US : MessageType::BROADCAST;
sm.ackStatus = (packet.from == 0) ? AckStatus::NONE : AckStatus::ACKED;
addLiveMessage(sm);
@@ -372,26 +366,25 @@ void MessageStore::clearAllMessages()
#endif
}
// Internal helper: erase first or last message matching a predicate
template <typename Predicate> static void eraseIf(std::deque<StoredMessage> &deque, Predicate pred, bool fromBack = false)
// Internal helpers for targeted erasure.
template <typename Predicate> static bool eraseFirstMatch(std::deque<StoredMessage> &deque, Predicate pred)
{
if (fromBack) {
// Iterate from the back and erase all matches from the end
for (auto it = deque.rbegin(); it != deque.rend();) {
if (pred(*it)) {
it = std::deque<StoredMessage>::reverse_iterator(deque.erase(std::next(it).base()));
} else {
++it;
}
for (auto it = deque.begin(); it != deque.end(); ++it) {
if (pred(*it)) {
deque.erase(it);
return true;
}
} else {
// Manual forward search to erase all matches
for (auto it = deque.begin(); it != deque.end();) {
if (pred(*it)) {
it = deque.erase(it);
} else {
++it;
}
}
return false;
}
template <typename Predicate> static void eraseAllMatches(std::deque<StoredMessage> &deque, Predicate pred)
{
for (auto it = deque.begin(); it != deque.end();) {
if (pred(*it)) {
it = deque.erase(it);
} else {
++it;
}
}
}
@@ -399,7 +392,9 @@ template <typename Predicate> static void eraseIf(std::deque<StoredMessage> &deq
// Delete oldest message (RAM + persisted queue)
void MessageStore::deleteOldestMessage()
{
eraseIf(liveMessages, [](StoredMessage &) { return true; });
if (!liveMessages.empty()) {
liveMessages.pop_front();
}
saveToFlash();
}
@@ -407,14 +402,14 @@ void MessageStore::deleteOldestMessage()
void MessageStore::deleteOldestMessageInChannel(uint8_t channel)
{
auto pred = [channel](const StoredMessage &m) { return m.type == MessageType::BROADCAST && m.channelIndex == channel; };
eraseIf(liveMessages, pred);
eraseFirstMatch(liveMessages, pred);
saveToFlash();
}
void MessageStore::deleteAllMessagesInChannel(uint8_t channel)
{
auto pred = [channel](const StoredMessage &m) { return m.type == MessageType::BROADCAST && m.channelIndex == channel; };
eraseIf(liveMessages, pred, false /* delete ALL, not just first */);
eraseAllMatches(liveMessages, pred);
saveToFlash();
}
@@ -427,7 +422,7 @@ void MessageStore::deleteAllMessagesWithPeer(uint32_t peer)
uint32_t other = (m.sender == local) ? m.dest : m.sender;
return other == peer;
};
eraseIf(liveMessages, pred, false);
eraseAllMatches(liveMessages, pred);
saveToFlash();
}
@@ -440,7 +435,7 @@ void MessageStore::deleteOldestMessageWithPeer(uint32_t peer)
uint32_t other = (m.sender == nodeDB->getNodeNum()) ? m.dest : m.sender;
return other == peer;
};
eraseIf(liveMessages, pred);
eraseFirstMatch(liveMessages, pred);
saveToFlash();
}
-3
View File
@@ -124,9 +124,6 @@ class MessageStore
// Allocate text into pool (used by sender-side code)
static uint16_t storeText(const char *src, size_t len);
// Used when loading from flash to rebuild the text pool
static uint16_t rebuildTextFromFlash(const char *src, size_t len);
private:
std::deque<StoredMessage> liveMessages; // Single in-RAM message buffer (also used for persistence)
std::string filename; // Flash filename for persistence
-133
View File
@@ -65,7 +65,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mesh/Default.h"
#include "mesh/generated/meshtastic/deviceonly.pb.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
#include "modules/WaypointModule.h"
#include "sleep.h"
#include "target_specific.h"
@@ -1643,138 +1642,6 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
return 0;
}
// Handles when message is received; will jump to text message frame.
int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
{
if (showingNormalScreen) {
if (packet->from == 0) {
// Outgoing message (likely sent from phone)
devicestate.has_rx_text_message = false;
memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
hiddenFrames.textMessage = true;
hasUnreadMessage = false; // Clear unread state when user replies
setFrames(FOCUS_PRESERVE); // Stay on same frame, silently update frame list
} else {
// Incoming message
devicestate.has_rx_text_message = true; // Needed to include the message frame
hasUnreadMessage = true; // Enables mail icon in the header
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view (no-op during text_input)
// Only wake/force display if the configuration allows it
if (shouldWakeOnReceivedMessage()) {
setOn(true); // Wake up the screen first
forceDisplay(); // Forces screen redraw
}
// === Prepare banner/popup content ===
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
const meshtastic_Channel channel =
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
const char *longName = nodeInfoLiteHasUser(node) ? node->long_name : nullptr;
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
char banner[256];
bool isAlert = false;
if (moduleConfig.external_notification.alert_bell || moduleConfig.external_notification.alert_bell_vibra ||
moduleConfig.external_notification.alert_bell_buzzer)
// Check for bell character to determine if this message is an alert
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
if (msgRaw[i] == ASCII_BELL) {
isAlert = true;
break;
}
}
// Unlike generic messages, alerts (when enabled via the ext notif module) ignore any
// 'mute' preferences set to any specific node or channel.
// If on-screen keyboard is active, show a transient popup over keyboard instead of interrupting it
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
// Wake and force redraw so popup is visible immediately
if (shouldWakeOnReceivedMessage()) {
setOn(true);
forceDisplay();
}
// Build popup: title = message source name, content = message text (sanitized)
// Title
char titleBuf[64] = {0};
if (longName && longName[0]) {
// Sanitize sender name
std::string t = sanitizeString(longName);
strncpy(titleBuf, t.c_str(), sizeof(titleBuf) - 1);
} else {
strncpy(titleBuf, "Message", sizeof(titleBuf) - 1);
}
// Content: payload bytes may not be null-terminated, remove ASCII_BELL and sanitize
char content[256] = {0};
{
std::string raw;
raw.reserve(packet->decoded.payload.size);
for (size_t i = 0; i < packet->decoded.payload.size; ++i) {
char c = msgRaw[i];
if (c == ASCII_BELL)
continue; // strip bell
raw.push_back(c);
}
std::string sanitized = sanitizeString(raw);
strncpy(content, sanitized.c_str(), sizeof(content) - 1);
}
NotificationRenderer::showKeyboardMessagePopupWithTitle(titleBuf, content, 3000);
// Maintain existing buzzer behavior on M5 if applicable
#if defined(M5STACK_UNITC6L)
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
(!isBroadcast(packet->to) && isToUs(packet))) {
playLongBeep();
}
#endif
} else {
// No keyboard active: use regular banner flow, respecting mute settings
if (isAlert) {
if (longName && longName[0]) {
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
} else {
strcpy(banner, "Alert Received");
}
screen->showSimpleBanner(banner, 3000);
} else if (!channel.settings.has_module_settings || !channel.settings.module_settings.is_muted) {
if (longName && longName[0]) {
if (currentResolution == ScreenResolution::UltraLow) {
strcpy(banner, "New Message");
} else {
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
}
} else {
strcpy(banner, "New Message");
}
#if defined(M5STACK_UNITC6L)
screen->setOn(true);
screen->showSimpleBanner(banner, 1500);
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
(!isBroadcast(packet->to) && isToUs(packet))) {
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
// - packet contains an alert and alert bell buzzer is enabled
// - packet is a non-broadcast that is addressed to this node
playLongBeep();
}
#else
screen->showSimpleBanner(banner, 3000);
#endif
}
}
}
}
return 0;
}
// Triggered by MeshModules
int Screen::handleUIFrameEvent(const UIFrameEvent *event)
{
-1
View File
@@ -609,7 +609,6 @@ class Screen : public concurrency::OSThread
// Handle observer events
int handleStatusUpdate(const meshtastic::Status *arg);
int handleTextMessage(const meshtastic_MeshPacket *packet);
int handleUIFrameEvent(const UIFrameEvent *arg);
int handleInputEvent(const InputEvent *arg);
int handleAdminMessage(AdminModule_ObserverData *arg);
+74 -23
View File
@@ -57,6 +57,70 @@ BannerOverlayOptions createStaticBannerOptions(const char *message, const MenuOp
return bannerOptions;
}
const StoredMessage *getNewestMessageForActiveThread()
{
const auto &messages = messageStore.getMessages();
if (messages.empty()) {
return nullptr;
}
const auto mode = graphics::MessageRenderer::getThreadMode();
const int channel = graphics::MessageRenderer::getThreadChannel();
const uint32_t peer = graphics::MessageRenderer::getThreadPeer();
const uint32_t localNode = nodeDB->getNodeNum();
if (mode == graphics::MessageRenderer::ThreadMode::ALL) {
return &messages.back();
}
for (auto it = messages.rbegin(); it != messages.rend(); ++it) {
const StoredMessage &m = *it;
if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) {
if (m.type == MessageType::BROADCAST && static_cast<int>(m.channelIndex) == channel) {
return &m;
}
continue;
}
if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
if (m.type != MessageType::DM_TO_US) {
continue;
}
const uint32_t other = (m.sender == localNode) ? m.dest : m.sender;
if (other == peer) {
return &m;
}
}
}
return nullptr;
}
void launchReplyForMessage(const StoredMessage &message, bool freetext)
{
if (message.type == MessageType::BROADCAST || message.dest == NODENUM_BROADCAST) {
if (freetext) {
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, message.channelIndex);
} else {
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, message.channelIndex);
}
return;
}
const uint32_t localNode = nodeDB->getNodeNum();
const uint32_t peer = (message.sender == localNode) ? message.dest : message.sender;
if (peer == 0 || peer == NODENUM_BROADCAST) {
return;
}
if (freetext) {
cannedMessageModule->LaunchFreetextWithDestination(peer);
} else {
cannedMessageModule->LaunchWithDestination(peer);
}
}
} // namespace
menuHandler::screenMenus menuHandler::menuQueue = MenuNone;
@@ -594,9 +658,12 @@ void menuHandler::messageResponseMenu()
#ifdef HAS_I2S
} else if (selected == Aloud) {
const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
const char *msg = reinterpret_cast<const char *>(mp.decoded.payload.bytes);
audioThread->readAloud(msg);
if (const StoredMessage *latest = getNewestMessageForActiveThread()) {
const char *msg = MessageStore::getText(*latest);
if (msg && msg[0]) {
audioThread->readAloud(msg);
}
}
#endif
}
};
@@ -656,20 +723,12 @@ void menuHandler::replyMenu()
// Preset reply
if (selected == ReplyPreset) {
if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) {
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, ch);
} else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
cannedMessageModule->LaunchWithDestination(peer);
} else {
// Fallback for last received message
if (devicestate.rx_text_message.to == NODENUM_BROADCAST) {
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel);
} else {
cannedMessageModule->LaunchWithDestination(devicestate.rx_text_message.from);
}
} else if (const StoredMessage *latest = getNewestMessageForActiveThread()) {
launchReplyForMessage(*latest, false);
}
return;
@@ -677,20 +736,12 @@ void menuHandler::replyMenu()
// Freetext reply
if (selected == ReplyFreetext) {
if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) {
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, ch);
} else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
cannedMessageModule->LaunchFreetextWithDestination(peer);
} else {
// Fallback for last received message
if (devicestate.rx_text_message.to == NODENUM_BROADCAST) {
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel);
} else {
cannedMessageModule->LaunchFreetextWithDestination(devicestate.rx_text_message.from);
}
} else if (const StoredMessage *latest = getNewestMessageForActiveThread()) {
launchReplyForMessage(*latest, true);
}
return;
@@ -5,9 +5,8 @@
Shows the latest incoming text message, as well as sender.
Both broadcast and direct messages will be shown here, from all channels.
This module doesn't doesn't use the devicestate.rx_text_message,' as this is overwritten to contain outgoing messages
This module doesn't collect its own text message. Instead, the WindowManager stores the most recent incoming text message.
This is available to any interested modules (SingeMessageApplet, NotificationApplet etc.) via InkHUD::latestMessage
This is available to any interested modules (SingleMessageApplet, NotificationApplet etc.) via InkHUD::latestMessage
We do still receive notifications from the text message module though,
to know when a new message has arrived, and trigger the update.
@@ -46,4 +45,4 @@ class AllMessageApplet : public Applet
} // namespace NicheGraphics::InkHUD
#endif
#endif
@@ -3,11 +3,10 @@
/*
Shows the latest incoming *Direct Message* (DM), as well as sender.
This compliments the threaded message applets
This complements the threaded message applets
This module doesn't doesn't use the devicestate.rx_text_message,' as this is overwritten to contain outgoing messages
This module doesn't collect its own text message. Instead, the WindowManager stores the most recent incoming text message.
This is available to any interested modules (SingeMessageApplet, NotificationApplet etc.) via InkHUD::latestMessage
This is available to any interested modules (SingleMessageApplet, NotificationApplet etc.) via InkHUD::latestMessage
We do still receive notifications from the text message module though,
to know when a new message has arrived, and trigger the update.
@@ -46,4 +45,4 @@ class DMApplet : public Applet
} // namespace NicheGraphics::InkHUD
#endif
#endif
+1 -1
View File
@@ -525,7 +525,7 @@ int InkHUD::Events::beforeReboot(void *unused)
// Callback when a new text message is received
// Caches the most recently received message, for use by applets
// Rx does not trigger a save to flash, however the data *will* be saved alongside other during shutdown, etc.
// Note: this is different from devicestate.rx_text_message, which may contain an *outgoing* message
// Note: this is intentionally separate from device-state message fields.
int InkHUD::Events::onReceiveTextMessage(const meshtastic_MeshPacket *packet)
{
// Short circuit: don't store outgoing messages
+1 -2
View File
@@ -121,8 +121,7 @@ class Persistence
// Most recently received text message
// Value is updated by InkHUD::WindowManager, as a courtesy to applets
// Note: different from devicestate.rx_text_message,
// which may contain an *outgoing message* to broadcast
// InkHUD keeps its own latest-message cache for applets.
struct LatestMessage {
MessageStore::Message broadcast; // Most recent message received broadcast
MessageStore::Message dm; // Most recent received DM
+1 -1
View File
@@ -464,7 +464,7 @@ Most recently received text message
Collected here, so various user applets don't all have to store their own copy of this info.
We are unable to use `devicestate.rx_text_message` for this purpose, because:
We keep this separate latest-message cache for this purpose, because:
- it is cleared by an outgoing text message
- we want to store both a recent broadcast and a recent DM
+8 -1
View File
@@ -731,8 +731,15 @@ void setup()
#elif defined(USE_SH1107_128_64)
screen_model = meshtastic_Config_DisplayConfig_OledType_OLED_SH1107; // keep dimension of 128x64
#else
if (config.display.oled != meshtastic_Config_DisplayConfig_OledType_OLED_AUTO)
if (config.display.oled != meshtastic_Config_DisplayConfig_OledType_OLED_AUTO) {
screen_model = config.display.oled;
// Fix: update geometry for SH1107 128x128 selected via menu
if (screen_model == meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_128_128) {
screen_geometry = GEOMETRY_128_128;
screen_model = meshtastic_Config_DisplayConfig_OledType_OLED_SH1107; // normalize
}
}
#endif
#endif
+1 -3
View File
@@ -1161,7 +1161,6 @@ void NodeDB::resetNodes(bool keepFavorites)
std::fill(nodeDatabase.nodes.begin() + 1, nodeDatabase.nodes.end(), meshtastic_NodeInfoLite());
}
(void)ourNum;
devicestate.has_rx_text_message = false;
devicestate.has_rx_waypoint = false;
saveNodeDatabaseToDisk();
saveDeviceStateToDisk();
@@ -1368,7 +1367,6 @@ void NodeDB::installDefaultDeviceState()
devicestate.version = DEVICESTATE_CUR_VER;
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
devicestate.has_rx_waypoint = false;
devicestate.has_rx_text_message = false;
generatePacketId(); // FIXME - ugly way to init current_packet_id;
@@ -2298,7 +2296,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING;
cn->time = getValidTime(RTCQualityFromNet);
sprintf(cn->message, warning, p.long_name);
snprintf(cn->message, sizeof(cn->message), warning, p.long_name);
service->sendClientNotification(cn);
}
return false;
+1 -1
View File
@@ -329,7 +329,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
cn->reply_id = p->id;
cn->level = meshtastic_LogRecord_Level_WARNING;
cn->time = getValidTime(RTCQualityFromNet);
sprintf(cn->message, "Duty cycle limit exceeded. You can send again in %d mins", silentMinutes);
snprintf(cn->message, sizeof(cn->message), "Duty cycle limit exceeded. You can send again in %d mins", silentMinutes);
service->sendClientNotification(cn);
meshtastic_Routing_Error err = meshtastic_Routing_Error_DUTY_CYCLE_LIMIT;
+1 -4
View File
@@ -21,9 +21,6 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp
textPacketList[textPacketListIndex] = mp.id;
textPacketListIndex = (textPacketListIndex + 1) % TEXT_PACKET_LIST_SIZE;
// We only store/display messages destined for us.
devicestate.rx_text_message = mp;
devicestate.has_rx_text_message = true;
IF_SCREEN(
// Guard against running in MeshtasticUI or with no screen
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
@@ -59,4 +56,4 @@ bool TextMessageModule::recentlySeen(uint32_t id)
}
}
return false;
}
}
+3 -3
View File
@@ -7,8 +7,8 @@
* Text message handling for Meshtastic.
*
* This module is responsible for receiving and storing incoming text messages
* from the mesh. It updates device state and notifies observers so that other
* components (such as the MessageRenderer) can later display or process them.
* from the mesh. It notifies observers so that other components (such as the
* MessageRenderer) can later display or process them.
*
* Rendering of messages on screen is no longer done here.
*/
@@ -36,4 +36,4 @@ class TextMessageModule : public SinglePortModule, public Observable<const mesht
size_t textPacketListIndex = 0;
};
extern TextMessageModule *textMessageModule;
extern TextMessageModule *textMessageModule;