diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 7f9c905a8..b2059b71c 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -41,6 +41,7 @@ along with this program. If not, see .
#include "mesh/Channels.h"
#include "mesh/generated/meshtastic/deviceonly.pb.h"
#include "meshUtils.h"
+#include "modules/AdminModule.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
#include "sleep.h"
@@ -1725,6 +1726,7 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
+ adminMessageObserver.observe(adminModule);
if (textMessageModule)
textMessageObserver.observe(textMessageModule);
if (inputBroker)
@@ -1953,9 +1955,6 @@ void Screen::setWelcomeFrames()
/// Determine which screensaver frame to use, then set the FrameCallback
void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
{
- // Remember current frame, restore position at power-on
- uint8_t frameNumber = ui->getUiState()->currentFrame;
-
// Retain specified frame / overlay callback beyond scope of this method
static FrameCallback screensaverFrame;
static OverlayCallback screensaverOverlay;
@@ -1993,9 +1992,8 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
#endif
// Prepare now for next frame, shown when display wakes
- ui->setOverlays(NULL, 0); // Clear overlay
- setFrames(); // Return to normal display updates
- ui->switchToFrame(frameNumber); // Attempt to return to same frame after power-on
+ ui->setOverlays(NULL, 0); // Clear overlay
+ setFrames(FOCUS_PRESERVE); // Return to normal display updates, showing same frame as before screensaver, ideally
// Pick a refresh method, for when display wakes
#ifdef EINK_HASQUIRK_GHOSTING
@@ -2006,9 +2004,13 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
}
#endif
-// restore our regular frame list
-void Screen::setFrames()
+// Regenerate the normal set of frames, focusing a specific frame if requested
+// Called when a frame should be added / removed, or custom frames should be cleared
+void Screen::setFrames(FrameFocus focus)
{
+ uint8_t originalPosition = ui->getUiState()->currentFrame;
+ FramesetInfo fsi; // Location of specific frames, for applying focus parameter
+
LOG_DEBUG("showing standard frames\n");
showingNormalScreen = true;
@@ -2042,20 +2044,33 @@ void Screen::setFrames()
// is the same offset into the moduleFrames vector
// so that we can invoke the module's callback
for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) {
- normalFrames[numframes++] = drawModuleFrame;
+ // Draw the module frame, using the hack described above
+ normalFrames[numframes] = drawModuleFrame;
+
+ // Check if the module being drawn has requested focus
+ // We will honor this request later, if setFrames was triggered by a UIFrameEvent
+ MeshModule *m = *i;
+ if (m->isRequestingFocus())
+ fsi.positions.focusedModule = numframes;
+
+ numframes++;
}
LOG_DEBUG("Added modules. numframes: %d\n", numframes);
// If we have a critical fault, show it first
- if (error_code)
+ fsi.positions.fault = numframes;
+ if (error_code) {
normalFrames[numframes++] = drawCriticalFaultFrame;
+ focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame
+ }
#ifdef T_WATCH_S3
normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame;
#endif
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
+ fsi.positions.textMessage = numframes;
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
normalFrames[numframes++] = drawTextMessageFrame;
}
@@ -2070,11 +2085,14 @@ void Screen::setFrames()
//
// Since frames are basic function pointers, we have to use a helper to
// call a method on debugInfo object.
+ fsi.positions.log = numframes;
normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline;
// call a method on debugInfoScreen object (for more details)
+ fsi.positions.settings = numframes;
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
+ fsi.positions.wifi = numframes;
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
if (isWifiAvailable()) {
// call a method on debugInfoScreen object (for more details)
@@ -2082,6 +2100,7 @@ void Screen::setFrames()
}
#endif
+ fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
LOG_DEBUG("Finished building frames. numframes: %d\n", numframes);
ui->setFrames(normalFrames, numframes);
@@ -2095,6 +2114,55 @@ void Screen::setFrames()
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
// just changed)
+ // Focus on a specific frame, in the frame set we just created
+ switch (focus) {
+ case FOCUS_DEFAULT:
+ ui->switchToFrame(0); // First frame
+ break;
+ case FOCUS_FAULT:
+ ui->switchToFrame(fsi.positions.fault);
+ break;
+ case FOCUS_TEXTMESSAGE:
+ ui->switchToFrame(fsi.positions.textMessage);
+ break;
+ case FOCUS_MODULE:
+ // Whichever frame was marked by MeshModule::requestFocus(), if any
+ // If no module requested focus, will show the first frame instead
+ ui->switchToFrame(fsi.positions.focusedModule);
+ break;
+
+ case FOCUS_PRESERVE:
+ // If we can identify which type of frame "originalPosition" was, can move directly to it in the new frameset
+ FramesetInfo &oldFsi = this->framesetInfo;
+ if (originalPosition == oldFsi.positions.log)
+ ui->switchToFrame(fsi.positions.log);
+ else if (originalPosition == oldFsi.positions.settings)
+ ui->switchToFrame(fsi.positions.settings);
+ else if (originalPosition == oldFsi.positions.wifi)
+ ui->switchToFrame(fsi.positions.wifi);
+
+ // If frame count has decreased
+ else if (fsi.frameCount < oldFsi.frameCount) {
+ uint8_t numDropped = oldFsi.frameCount - fsi.frameCount;
+ // Move n frames backwards
+ if (numDropped <= originalPosition)
+ ui->switchToFrame(originalPosition - numDropped);
+ // Unless that would put us "out of bounds" (< 0)
+ else
+ ui->switchToFrame(0);
+ }
+
+ // If we're not sure exactly which frame we were on, at least return to the same frame number
+ // (node frames; module frames)
+ else
+ ui->switchToFrame(originalPosition);
+
+ break;
+ }
+
+ // Store the info about this frameset, for future setFrames calls
+ this->framesetInfo = fsi;
+
setFastFramerate(); // Draw ASAP
}
@@ -2549,7 +2617,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
switch (arg->getStatusType()) {
case STATUS_TYPE_NODE:
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
- setFrames(); // Regen the list of screens
+ setFrames(FOCUS_PRESERVE); // Regen the list of screen frames (returning to same frame, if possible)
}
nodeDB->updateGUI = false;
break;
@@ -2561,23 +2629,33 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
{
if (showingNormalScreen) {
- setFrames(); // Regen the list of screens (will show new text message)
+ // Outgoing message
+ if (packet->from == 0)
+ setFrames(FOCUS_PRESERVE); // Return to same frame (quietly hiding the rx text message frame)
+
+ // Incoming message
+ else
+ setFrames(FOCUS_TEXTMESSAGE); // Focus on the new message
}
return 0;
}
+// Triggered by MeshModules
int Screen::handleUIFrameEvent(const UIFrameEvent *event)
{
if (showingNormalScreen) {
- if (event->frameChanged) {
- setFrames(); // Regen the list of screens (will show new text message)
- } else if (event->needRedraw) {
+ // Regenerate the frameset, potentially honoring a module's internal requestFocus() call
+ if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET)
+ setFrames(FOCUS_MODULE);
+
+ // Regenerate the frameset, while attempting to maintain focus on the current frame
+ else if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND)
+ setFrames(FOCUS_PRESERVE);
+
+ // Don't regenerate the frameset, just re-draw whatever is on screen ASAP
+ else if (event->action == UIFrameEvent::Action::REDRAW_ONLY)
setFastFramerate();
- // TODO: We might also want switch to corresponding frame,
- // but we don't know the exact frame number.
- // ui->switchToFrame(0);
- }
}
return 0;
@@ -2612,6 +2690,24 @@ int Screen::handleInputEvent(const InputEvent *event)
return 0;
}
+int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
+{
+ // Note: only selected admin messages notify this observer
+ // If you wish to handle a new type of message, you should modify AdminModule.cpp first
+
+ switch (arg->which_payload_variant) {
+ // Node removed manually (i.e. via app)
+ case meshtastic_AdminMessage_remove_by_nodenum_tag:
+ setFrames(FOCUS_PRESERVE);
+ break;
+
+ // Default no-op, in case the admin message observable gets used by other classes in future
+ default:
+ break;
+ }
+ return 0;
+}
+
} // namespace graphics
#else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h
index b89a2917e..93e5f2ef7 100644
--- a/src/graphics/Screen.h
+++ b/src/graphics/Screen.h
@@ -173,9 +173,11 @@ class Screen : public concurrency::OSThread
CallbackObserver textMessageObserver =
CallbackObserver(this, &Screen::handleTextMessage);
CallbackObserver uiFrameEventObserver =
- CallbackObserver(this, &Screen::handleUIFrameEvent);
+ CallbackObserver(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules
CallbackObserver inputObserver =
CallbackObserver(this, &Screen::handleInputEvent);
+ CallbackObserver adminMessageObserver =
+ CallbackObserver(this, &Screen::handleAdminMessage);
public:
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
@@ -394,6 +396,7 @@ class Screen : public concurrency::OSThread
int handleTextMessage(const meshtastic_MeshPacket *arg);
int handleUIFrameEvent(const UIFrameEvent *arg);
int handleInputEvent(const InputEvent *arg);
+ int handleAdminMessage(const meshtastic_AdminMessage *arg);
/// Used to force (super slow) eink displays to draw critical frames
void forceDisplay(bool forceUiUpdate = false);
@@ -450,8 +453,34 @@ class Screen : public concurrency::OSThread
void handleShowPrevFrame();
void handlePrint(const char *text);
void handleStartFirmwareUpdateScreen();
- /// Rebuilds our list of frames (screens) to default ones.
- void setFrames();
+
+ // Info collected by setFrames method.
+ // Index location of specific frames. Used to apply the FrameFocus parameter of setFrames
+ struct FramesetInfo {
+ struct FramePositions {
+ uint8_t fault = 0;
+ uint8_t textMessage = 0;
+ uint8_t focusedModule = 0;
+ uint8_t log = 0;
+ uint8_t settings = 0;
+ uint8_t wifi = 0;
+ } positions;
+
+ uint8_t frameCount = 0;
+ } framesetInfo;
+
+ // Which frame we want to be displayed, after we regen the frameset by calling setFrames
+ enum FrameFocus : uint8_t {
+ FOCUS_DEFAULT, // No specific frame
+ FOCUS_PRESERVE, // Return to the previous frame
+ FOCUS_FAULT,
+ FOCUS_TEXTMESSAGE,
+ FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
+ };
+
+ // Regenerate the normal set of frames, focusing a specific frame if requested
+ // Call when a frame should be added / removed, or custom frames should be cleared
+ void setFrames(FrameFocus focus = FOCUS_DEFAULT);
/// Try to start drawing ASAP
void setFastFramerate();
diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp
index 04fa250bf..1ef4f60d8 100644
--- a/src/mesh/MeshModule.cpp
+++ b/src/mesh/MeshModule.cpp
@@ -284,4 +284,17 @@ AdminMessageHandleResult MeshModule::handleAdminMessageForAllModules(const mesht
}
}
return handled;
-}
\ No newline at end of file
+}
+
+#if HAS_SCREEN
+// Would our module like its frame to be focused after Screen::setFrames has regenerated the list of frames?
+// Only considered if setFrames is triggered by a UIFrameEvent
+bool MeshModule::isRequestingFocus()
+{
+ if (_requestingFocus) {
+ _requestingFocus = false; // Consume the request
+ return true;
+ } else
+ return false;
+}
+#endif
\ No newline at end of file
diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h
index 2e2af33e0..c341b301a 100644
--- a/src/mesh/MeshModule.h
+++ b/src/mesh/MeshModule.h
@@ -35,10 +35,16 @@ enum class AdminMessageHandleResult {
/*
* This struct is used by Screen to figure out whether screen frame should be updated.
*/
-typedef struct _UIFrameEvent {
- bool frameChanged;
- bool needRedraw;
-} UIFrameEvent;
+struct UIFrameEvent {
+ // What do we actually want to happen?
+ enum Action {
+ REDRAW_ONLY, // Don't change which frames are show, just redraw, asap
+ REGENERATE_FRAMESET, // Regenerate (change? add? remove?) screen frames, honoring requestFocus()
+ REGENERATE_FRAMESET_BACKGROUND, // Regenerate screen frames, attempting to remain on the same frame throughout
+ } action = REDRAW_ONLY;
+
+ // We might want to pass additional data inside this struct at some point
+};
/** A baseclass for any mesh "module".
*
@@ -73,6 +79,7 @@ class MeshModule
meshtastic_AdminMessage *response);
#if HAS_SCREEN
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; }
+ virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset
#endif
protected:
const char *name;
@@ -176,6 +183,19 @@ class MeshModule
return AdminMessageHandleResult::NOT_HANDLED;
};
+#if HAS_SCREEN
+ /** Request that our module's screen frame be focused when Screen::setFrames runs
+ * Only considered if Screen::setFrames is triggered via a UIFrameEvent
+ *
+ * Having this as a separate call, instead of part of the UIFrameEvent, allows the module to delay decision
+ * until drawFrame() is called. This required less restructuring.
+ */
+ bool _requestingFocus = false;
+ void requestFocus() { _requestingFocus = true; }
+#else
+ void requestFocus(){}; // No-op
+#endif
+
private:
/**
* If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index e24c62712..cab63e559 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -200,6 +200,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
case meshtastic_AdminMessage_remove_by_nodenum_tag: {
LOG_INFO("Client is receiving a remove_nodenum command.\n");
nodeDB->removeNodeByNum(r->remove_by_nodenum);
+ this->notifyObservers(r); // Observed by screen
break;
}
case meshtastic_AdminMessage_set_favorite_node_tag: {
diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h
index 6ecc88829..a5ffeb7d6 100644
--- a/src/modules/AdminModule.h
+++ b/src/modules/AdminModule.h
@@ -7,7 +7,7 @@
/**
* Admin module for admin messages
*/
-class AdminModule : public ProtobufModule
+class AdminModule : public ProtobufModule, public Observable
{
public:
/** Constructor
diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp
index be414dce1..84b5a3260 100644
--- a/src/modules/CannedMessageModule.cpp
+++ b/src/modules/CannedMessageModule.cpp
@@ -148,8 +148,9 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
if (this->currentMessageIndex == 0) {
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
- UIFrameEvent e = {false, true};
- e.frameChanged = true;
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs
+ UIFrameEvent e;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->notifyObservers(&e);
return 0;
@@ -166,8 +167,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
}
}
if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) {
- UIFrameEvent e = {false, true};
- e.frameChanged = true;
+ UIFrameEvent e;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1;
#if !defined(T_WATCH_S3) && !defined(RAK14014)
@@ -353,6 +354,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
}
if (validEvent) {
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs
+
// Let runOnce to be called immediately.
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) {
setIntervalFromNow(0); // on fast keypresses, this isn't fast enough.
@@ -378,6 +381,11 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
p->decoded.payload.size++;
}
+ // Only receive routing messages when expecting ACK for a canned message
+ // Prevents the canned message module from regenerating the screen's frameset at unexpected times,
+ // or raising a UIFrameEvent before another module has the chance
+ this->waitingForAck = true;
+
LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
service.sendToMesh(
@@ -393,13 +401,13 @@ int32_t CannedMessageModule::runOnce()
return INT32_MAX;
}
// LOG_DEBUG("Check status\n");
- UIFrameEvent e = {false, true};
+ UIFrameEvent e;
if ((this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) ||
(this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) || (this->runState == CANNED_MESSAGE_RUN_STATE_MESSAGE)) {
// TODO: might have some feedback of sending state
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
temporaryMessage = "";
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext
this->cursor = 0;
@@ -412,7 +420,7 @@ int32_t CannedMessageModule::runOnce()
} else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) &&
((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) {
// Reset module
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext
this->cursor = 0;
@@ -449,7 +457,7 @@ int32_t CannedMessageModule::runOnce()
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
}
}
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext
this->cursor = 0;
@@ -463,7 +471,7 @@ int32_t CannedMessageModule::runOnce()
} else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) {
this->currentMessageIndex = 0;
LOG_DEBUG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) {
if (this->messagesCount > 0) {
@@ -567,7 +575,7 @@ int32_t CannedMessageModule::runOnce()
break;
}
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
switch (this->payload) { // code below all trigger the freetext window (where you type to send a message) or reset the
// display back to the default window
case 0x08: // backspace
@@ -706,8 +714,8 @@ int CannedMessageModule::getPrevIndex()
void CannedMessageModule::showTemporaryMessage(const String &message)
{
temporaryMessage = message;
- UIFrameEvent e = {false, true};
- e.frameChanged = true;
+ UIFrameEvent e;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
notifyObservers(&e);
runState = CANNED_MESSAGE_RUN_STATE_MESSAGE;
// run this loop again in 2 seconds, next iteration will clear the display
@@ -914,11 +922,13 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
char buffer[50];
if (temporaryMessage.length() != 0) {
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame
LOG_DEBUG("Drawing temporary message: %s", temporaryMessage.c_str());
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage);
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) {
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
String displayString;
@@ -940,6 +950,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi);
}
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending...");
@@ -948,7 +959,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->setFont(FONT_SMALL);
display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled.");
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
-
+ requestFocus(); // Tell Screen::setFrames to move to our module's frame
#if defined(T_WATCH_S3) || defined(RAK14014)
drawKeyboard(display, state, 0, 0);
#else
@@ -1030,16 +1041,18 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
{
- if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP) {
+ if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP && waitingForAck) {
// look for a request_id
if (mp.decoded.request_id != 0) {
- UIFrameEvent e = {false, true};
- e.frameChanged = true;
+ UIFrameEvent e;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
+ requestFocus(); // Tell Screen::setFrames that our module's frame should be shown, even if not "first" in the frameset
this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED;
this->incoming = service.getNodenumFromRequestId(mp.decoded.request_id);
meshtastic_Routing decoded = meshtastic_Routing_init_default;
pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded);
this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE;
+ waitingForAck = false; // No longer want routing packets
this->notifyObservers(&e);
// run the next time 2 seconds later
setIntervalFromNow(2000);
diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h
index 00e8c2bf9..797b9f7cf 100644
--- a/src/modules/CannedMessageModule.h
+++ b/src/modules/CannedMessageModule.h
@@ -81,9 +81,8 @@ class CannedMessageModule : public SinglePortModule, public Observabledecoded.portnum) {
- case meshtastic_PortNum_TEXT_MESSAGE_APP:
case meshtastic_PortNum_ROUTING_APP:
- return true;
+ return waitingForAck;
default:
return false;
}
@@ -140,7 +139,8 @@ class CannedMessageModule : public SinglePortModule, public ObservablenotifyObservers(&e);
}
} else {
@@ -209,7 +209,7 @@ int32_t AudioModule::runOnce()
}
tx_encode_frame_index = sizeof(tx_header);
radio_state = RadioState::rx;
- e.frameChanged = true;
+ e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->notifyObservers(&e);
}
}