Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4409057
Updated setFrames in Screen.cpp
slash-bit Jun 5, 2024
38a9f33
Space added Screen.cpp
slash-bit Jun 5, 2024
cbd49ed
Update Screen.cpp
slash-bit Jun 5, 2024
f4a88c2
Update Screen.cpp
slash-bit Jun 12, 2024
3f3db83
Hold position, even if number of frames increases
todd-herbert Jun 12, 2024
64d8cdf
Hold position, if handling an outgoing text message
todd-herbert Jun 12, 2024
da32505
Merge branch 'master' into screen-setFrames-update
thebentern Jun 12, 2024
3b2febb
Merge pull request #1 from todd-herbert/screen-setFrames-update
slash-bit Jun 12, 2024
de6e8a1
Update Screen.cpp
slash-bit Jun 13, 2024
1243987
Reverted chnages related to devicestate.has_rx_text_message
slash-bit Jun 14, 2024
e5fb193
Merge branch 'master' of https://github.com/meshtastic/firmware into …
todd-herbert Jun 27, 2024
bc233d3
Reset to master
todd-herbert Jun 27, 2024
c66e7f2
Merge upstream master
todd-herbert Jul 1, 2024
53e6155
Merge branch 'master' of https://github.com/meshtastic/firmware into …
todd-herbert Jul 7, 2024
c198c90
Merge branch 'master' of https://github.com/meshtastic/firmware into …
todd-herbert Jul 10, 2024
2eb40aa
CannedMessages only handles routing packets when waiting for ACK
todd-herbert Jul 10, 2024
1b90ac0
Gather position info about screen frames while regenerating
todd-herbert Jul 10, 2024
0056d7c
Make admin module observable
todd-herbert Jul 10, 2024
4f9eeb9
Optionally specify which frame to focus when setFrames runs
todd-herbert Jul 10, 2024
3ac3ba7
UIFrameEvent uses enum instead of multiple booleans
todd-herbert Jul 10, 2024
8aa1741
Allow modules to request their own frame to be focussed
todd-herbert Jul 10, 2024
3c98092
E-Ink screensaver uses FOCUS_PRESERVE
todd-herbert Jul 10, 2024
f5b35eb
Merge branch 'master' of https://github.com/meshtastic/firmware into …
todd-herbert Jul 11, 2024
afd0ba2
Spelling: regional variant
todd-herbert Jul 11, 2024
e649ff7
trunk
todd-herbert Jul 11, 2024
6b6c8c1
Fix HAS_SCREEN guarding
todd-herbert Jul 11, 2024
0f1fa95
More HAS_SCREEN guarding
todd-herbert Jul 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 115 additions & 19 deletions src/graphics/Screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -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;
}
Expand All @@ -2070,18 +2085,22 @@ 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)
normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline;
}
#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);
Expand All @@ -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
}

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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) {}
Expand Down
35 changes: 32 additions & 3 deletions src/graphics/Screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,11 @@ class Screen : public concurrency::OSThread
CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver =
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent);
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules
CallbackObserver<Screen, const InputEvent *> inputObserver =
CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent);
CallbackObserver<Screen, const meshtastic_AdminMessage *> adminMessageObserver =
CallbackObserver<Screen, const meshtastic_AdminMessage *>(this, &Screen::handleAdminMessage);

public:
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down
15 changes: 14 additions & 1 deletion src/mesh/MeshModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,17 @@ AdminMessageHandleResult MeshModule::handleAdminMessageForAllModules(const mesht
}
}
return handled;
}
}

#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
28 changes: 24 additions & 4 deletions src/mesh/MeshModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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".
*
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/modules/AdminModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
2 changes: 1 addition & 1 deletion src/modules/AdminModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/**
* Admin module for admin messages
*/
class AdminModule : public ProtobufModule<meshtastic_AdminMessage>
class AdminModule : public ProtobufModule<meshtastic_AdminMessage>, public Observable<const meshtastic_AdminMessage *>
{
public:
/** Constructor
Expand Down
Loading