Compare commits

...

11 Commits

Author SHA1 Message Date
Jonathan Bennett
6d26e64a4d
Merge f8ea9c0e40 into ba296db701 2025-06-07 05:02:56 +00:00
Jonathan Bennett
f8ea9c0e40 Cast to char to satisfy compiler 2025-06-07 00:02:49 -05:00
Jonathan Bennett
67d3cafc6f Banner message state reset 2025-06-06 23:42:05 -05:00
Jonathan Bennett
6c3f24dfe6 Add gps location to fsi 2025-06-06 23:25:53 -05:00
Jonathan Bennett
4dde0a9202 Add temporary clock icon 2025-06-06 23:25:16 -05:00
Jonathan Bennett
652033a0b4 Onebutton Menu Support 2025-06-06 22:33:04 -05:00
Jonathan Bennett
f4c5e31f3d Trunk 2025-06-06 21:55:30 -05:00
Jonathan Bennett
b177329813
Pull OneButton handling from PowerFSM and add MUI switch (#6973) 2025-06-06 21:26:01 -05:00
HarukiToreda
8fc0f1aa13 Merge branch 'unify-tft' of https://github.com/meshtastic/firmware into unify-tft 2025-06-06 22:06:49 -04:00
HarukiToreda
e869e1b146 button thread cleanup 2025-06-06 22:06:45 -04:00
Ben Meadors
1858031ad6 Fix warning 2025-06-06 19:49:06 -05:00
10 changed files with 168 additions and 30 deletions

View File

@ -253,12 +253,6 @@ static void onIdle()
} }
} }
static void screenPress()
{
if (screen)
screen->onPress();
}
static void bootEnter() static void bootEnter()
{ {
LOG_DEBUG("State: BOOT"); LOG_DEBUG("State: BOOT");
@ -302,9 +296,9 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL, "Press"); powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, NULL, "Press"); powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&stateDARK, isPowered() ? &statePOWER : &stateON, EVENT_PRESS, NULL, "Press"); powerFSM.add_transition(&stateDARK, isPowered() ? &statePOWER : &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, screenPress, "Press"); powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, screenPress, "Press"); // reenter On to restart our timers powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, NULL, "Press"); // reenter On to restart our timers
powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, screenPress, powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, NULL,
"Press"); // Allow button to work while in serial API "Press"); // Allow button to work while in serial API
// Handle critically low power battery by forcing deep sleep // Handle critically low power battery by forcing deep sleep

View File

@ -36,7 +36,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#if !MESHTASTIC_EXCLUDE_GPS #if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h" #include "GPS.h"
#endif #endif
#include "ButtonThread.h"
#include "FSCommon.h" #include "FSCommon.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
@ -48,6 +47,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graphics/SharedUIDisplay.h" #include "graphics/SharedUIDisplay.h"
#include "graphics/emotes.h" #include "graphics/emotes.h"
#include "graphics/images.h" #include "graphics/images.h"
#include "input/ButtonThread.h"
#include "input/ScanAndSelect.h" #include "input/ScanAndSelect.h"
#include "input/TouchScreenImpl1.h" #include "input/TouchScreenImpl1.h"
#include "main.h" #include "main.h"
@ -144,6 +144,7 @@ void Screen::showOverlayBanner(const char *message, uint32_t durationMs, uint8_t
NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs; NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs;
NotificationRenderer::alertBannerOptions = options; NotificationRenderer::alertBannerOptions = options;
NotificationRenderer::alertBannerCallback = bannerCallback; NotificationRenderer::alertBannerCallback = bannerCallback;
NotificationRenderer::curSelected = 0;
} }
static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
@ -1390,6 +1391,7 @@ void Screen::setFrames(FrameFocus focus)
#if defined(DISPLAY_CLOCK_FRAME) #if defined(DISPLAY_CLOCK_FRAME)
normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame; normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame;
indicatorIcons.push_back(icon_compass);
#endif #endif
// Declare this early so its available in FOCUS_PRESERVE block // Declare this early so its available in FOCUS_PRESERVE block
@ -1424,6 +1426,7 @@ void Screen::setFrames(FrameFocus focus)
normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses; normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses;
indicatorIcons.push_back(icon_list); indicatorIcons.push_back(icon_list);
fsi.positions.gps = numframes;
normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen; normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen;
indicatorIcons.push_back(icon_compass); indicatorIcons.push_back(icon_compass);
@ -1782,6 +1785,12 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
int Screen::handleInputEvent(const InputEvent *event) int Screen::handleInputEvent(const InputEvent *event)
{ {
if (NotificationRenderer::isOverlayBannerShowing()) {
NotificationRenderer::inEvent = event->inputEvent;
setFrames();
ui->update();
return 0;
}
#if defined(DISPLAY_CLOCK_FRAME) #if defined(DISPLAY_CLOCK_FRAME)
// For the T-Watch, intercept touches to the 'toggle digital/analog watch face' button // For the T-Watch, intercept touches to the 'toggle digital/analog watch face' button
uint8_t watchFaceFrame = error_code ? 1 : 0; uint8_t watchFaceFrame = error_code ? 1 : 0;
@ -1795,12 +1804,7 @@ int Screen::handleInputEvent(const InputEvent *event)
return 0; return 0;
} }
#endif #endif
if (NotificationRenderer::isOverlayBannerShowing()) {
NotificationRenderer::inEvent = event->inputEvent;
setFrames();
ui->update();
return 0;
}
// Use left or right input from a keyboard to move between frames, // Use left or right input from a keyboard to move between frames,
// so long as a mesh module isn't using these events for some other purpose // so long as a mesh module isn't using these events for some other purpose
if (showingNormalScreen) { if (showingNormalScreen) {
@ -1812,12 +1816,52 @@ int Screen::handleInputEvent(const InputEvent *event)
inputIntercepted = true; inputIntercepted = true;
} }
// Only allow BUTTON_PRESSED and BUTTON_LONG_PRESSED to trigger frame changes if no module is handling input
if (!inputIntercepted) {
if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_PRESSED) {
showNextFrame();
return 0;
} else if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_LONG_PRESSED) {
// Optional: Define alternate screen action or no-op
return 0;
}
}
// 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 == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
showPrevFrame(); showPrevFrame();
else if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) } else if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
showNextFrame(); showNextFrame();
} else if (event->inputEvent ==
static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT) ||
event->inputEvent == INPUT_BROKER_MSG_BUTTON_DOUBLE_PRESSED) {
#if HAS_TFT
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.memory) {
showOverlayBanner("Switch to MUI?\nYES\nNO", 30000, 2, [](int selected) -> void {
if (selected == 0) {
config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR;
config.bluetooth.enabled = false;
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
});
}
#endif
#if HAS_GPS
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.gps) {
showOverlayBanner("Toggle GPS\nENABLED\nDISABLED", 30000, 2, [](int selected) -> void {
if (selected == 0) {
config.position.gps_enabled = true;
gps->enable();
} else if (selected == 1) {
config.position.gps_enabled = false;
gps->disable();
}
});
}
#endif
}
} }
} }

View File

@ -632,6 +632,7 @@ class Screen : public concurrency::OSThread
uint8_t wifi = 255; uint8_t wifi = 255;
uint8_t deviceFocused = 255; uint8_t deviceFocused = 255;
uint8_t memory = 255; uint8_t memory = 255;
uint8_t gps = 255;
} positions; } positions;
uint8_t frameCount = 0; uint8_t frameCount = 0;

View File

@ -59,7 +59,7 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
// Exit if no message is active or duration has passed // Exit if no message is active or duration has passed
if (!isOverlayBannerShowing()) if (!isOverlayBannerShowing())
return; return;
LOG_DEBUG("event: %u, curSelected: %d", inEvent, curSelected);
// === Layout Configuration === // === Layout Configuration ===
constexpr uint16_t padding = 5; // Padding around text inside the box constexpr uint16_t padding = 5; // Padding around text inside the box
constexpr uint16_t vPadding = 2; // Padding around text inside the box constexpr uint16_t vPadding = 2; // Padding around text inside the box
@ -105,9 +105,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
// respond to input // respond to input
if (inEvent == meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP) { if (inEvent == meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP) {
curSelected--; curSelected--;
} else if (inEvent == meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN) { } else if (inEvent == meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN ||
inEvent == INPUT_BROKER_MSG_BUTTON_PRESSED) {
curSelected++; curSelected++;
} else if (inEvent == meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT) { } else if (inEvent == meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT ||
inEvent == INPUT_BROKER_MSG_BUTTON_DOUBLE_PRESSED) {
alertBannerCallback(curSelected); alertBannerCallback(curSelected);
alertBannerMessage[0] = '\0'; alertBannerMessage[0] = '\0';
} }
@ -172,8 +174,6 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
// === Draw each line centered in the box === // === Draw each line centered in the box ===
int16_t lineY = boxTop + vPadding; int16_t lineY = boxTop + vPadding;
LOG_DEBUG("firstOptionToShow: %u, firstOption: %u", firstOptionToShow, firstOption);
//
for (int i = 0; i < lineCount; i++) { for (int i = 0; i < lineCount; i++) {
// is this line selected? // is this line selected?
// if so, start the buffer with -> and strncpy to the 4th location // if so, start the buffer with -> and strncpy to the 4th location

View File

@ -8,6 +8,7 @@
#include "PowerFSM.h" #include "PowerFSM.h"
#include "RadioLibInterface.h" #include "RadioLibInterface.h"
#include "buzz.h" #include "buzz.h"
#include "input/InputBroker.h"
#include "main.h" #include "main.h"
#include "modules/CannedMessageModule.h" #include "modules/CannedMessageModule.h"
#include "modules/ExternalNotificationModule.h" #include "modules/ExternalNotificationModule.h"
@ -262,6 +263,64 @@ int32_t ButtonThread::runOnce()
#endif #endif
if (btnEvent != BUTTON_EVENT_NONE) { if (btnEvent != BUTTON_EVENT_NONE) {
#if HAS_SCREEN
switch (btnEvent) {
case BUTTON_EVENT_PRESSED: {
LOG_WARN("press!");
// Play boop sound for every button press
playBoop();
// Forward single press to InputBroker (but NOT as DOWN/SELECT, just forward a "button press" event)
if (inputBroker) {
InputEvent evt = {"button", (char)INPUT_BROKER_MSG_BUTTON_PRESSED, 0, 0, 0};
inputBroker->injectInputEvent(&evt);
}
// Start tracking for potential combination
waitingForLongPress = true;
shortPressTime = millis();
break;
}
case BUTTON_EVENT_DOUBLE_PRESSED: {
LOG_WARN("press!");
// Play boop sound for every button press
playBoop();
// Forward single press to InputBroker (but NOT as DOWN/SELECT, just forward a "button press" event)
if (inputBroker) {
InputEvent evt = {"button", (char)INPUT_BROKER_MSG_BUTTON_DOUBLE_PRESSED, 0, 0, 0};
inputBroker->injectInputEvent(&evt);
}
waitingForLongPress = false;
break;
}
case BUTTON_EVENT_LONG_PRESSED: {
LOG_WARN("Long press!");
// Play beep sound
playBeep();
// Forward long press to InputBroker (but NOT as DOWN/SELECT, just forward a "button long press" event)
if (inputBroker) {
InputEvent evt = {"button", (char)INPUT_BROKER_MSG_BUTTON_LONG_PRESSED, 0, 0, 0};
inputBroker->injectInputEvent(&evt);
}
waitingForLongPress = false;
break;
}
default:
// Ignore all other events on screen devices
break;
}
btnEvent = BUTTON_EVENT_NONE;
#else
// On devices without screen: full legacy logic
switch (btnEvent) { switch (btnEvent) {
case BUTTON_EVENT_PRESSED: { case BUTTON_EVENT_PRESSED: {
LOG_BUTTON("press!"); LOG_BUTTON("press!");
@ -489,6 +548,7 @@ int32_t ButtonThread::runOnce()
break; break;
} }
btnEvent = BUTTON_EVENT_NONE; btnEvent = BUTTON_EVENT_NONE;
#endif
} }
return 50; return 50;

View File

@ -8,9 +8,14 @@
#define BUTTON_CLICK_MS 250 #define BUTTON_CLICK_MS 250
#endif #endif
#ifdef HAS_SCREEN
#undef BUTTON_LONGPRESS_MS
#define BUTTON_LONGPRESS_MS 500
#else
#ifndef BUTTON_LONGPRESS_MS #ifndef BUTTON_LONGPRESS_MS
#define BUTTON_LONGPRESS_MS 5000 #define BUTTON_LONGPRESS_MS 5000
#endif #endif
#endif
#ifndef BUTTON_TOUCH_MS #ifndef BUTTON_TOUCH_MS
#define BUTTON_TOUCH_MS 400 #define BUTTON_TOUCH_MS 400

View File

@ -21,6 +21,9 @@
#define INPUT_BROKER_MSG_BLUETOOTH_TOGGLE 0xAA #define INPUT_BROKER_MSG_BLUETOOTH_TOGGLE 0xAA
#define INPUT_BROKER_MSG_TAB 0x09 #define INPUT_BROKER_MSG_TAB 0x09
#define INPUT_BROKER_MSG_EMOTE_LIST 0x8F #define INPUT_BROKER_MSG_EMOTE_LIST 0x8F
#define INPUT_BROKER_MSG_BUTTON_PRESSED 0xe1
#define INPUT_BROKER_MSG_BUTTON_LONG_PRESSED 0xe2
#define INPUT_BROKER_MSG_BUTTON_DOUBLE_PRESSED 0xe3
typedef struct _InputEvent { typedef struct _InputEvent {
const char *source; const char *source;
@ -37,6 +40,7 @@ class InputBroker : public Observable<const InputEvent *>
public: public:
InputBroker(); InputBroker();
void registerSource(Observable<const InputEvent *> *source); void registerSource(Observable<const InputEvent *> *source);
void injectInputEvent(const InputEvent *event) { handleInputEvent(event); }
protected: protected:
int handleInputEvent(const InputEvent *event); int handleInputEvent(const InputEvent *event);

View File

@ -99,7 +99,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
#endif #endif
#if HAS_BUTTON || defined(ARCH_PORTDUINO) #if HAS_BUTTON || defined(ARCH_PORTDUINO)
#include "ButtonThread.h" #include "input/ButtonThread.h"
#endif #endif
#include "AmbientLightingThread.h" #include "AmbientLightingThread.h"

View File

@ -292,7 +292,9 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
switch (runState) { switch (runState) {
// Node/Channel destination selection mode: Handles character search, arrows, select, cancel, backspace // Node/Channel destination selection mode: Handles character search, arrows, select, cancel, backspace
case CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION: case CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION:
return handleDestinationSelectionInput(event, isUp, isDown, isSelect); // All allowed input for this state if (handleDestinationSelectionInput(event, isUp, isDown, isSelect))
return 1;
return 0; // prevent fall-through to selector input
// Free text input mode: Handles character input, cancel, backspace, select, etc. // Free text input mode: Handles character input, cancel, backspace, select, etc.
case CANNED_MESSAGE_RUN_STATE_FREETEXT: case CANNED_MESSAGE_RUN_STATE_FREETEXT:
@ -310,7 +312,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
if (isSelect) { if (isSelect) {
// When inactive, call the onebutton shortpress instead. Activate module only on up/down // When inactive, call the onebutton shortpress instead. Activate module only on up/down
powerFSM.trigger(EVENT_PRESS); powerFSM.trigger(EVENT_PRESS);
return 1; // Let caller know we handled it return 0; // Main button press no longer runs through powerFSM
} }
// Let LEFT/RIGHT pass through so frame navigation works // Let LEFT/RIGHT pass through so frame navigation works
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT) || if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT) ||
@ -408,6 +410,15 @@ bool CannedMessageModule::handleTabSwitch(const InputEvent *event)
int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event, bool isUp, bool isDown, bool isSelect) int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event, bool isUp, bool isDown, bool isSelect)
{ {
// Override isDown and isSelect ONLY for destination selector behavior
if (runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION) {
if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_PRESSED) {
isDown = true;
} else if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_LONG_PRESSED) {
isSelect = true;
}
}
if (event->kbchar >= 32 && event->kbchar <= 126 && !isUp && !isDown && if (event->kbchar >= 32 && event->kbchar <= 126 && !isUp && !isDown &&
event->inputEvent != static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT) && event->inputEvent != static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT) &&
event->inputEvent != static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT) && event->inputEvent != static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT) &&
@ -503,6 +514,15 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bool isUp, bool isDown, bool isSelect) bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bool isUp, bool isDown, bool isSelect)
{ {
// Override isDown and isSelect ONLY for canned message list behavior
if (runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) {
if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_PRESSED) {
isDown = true;
} else if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_LONG_PRESSED) {
isSelect = true;
}
}
if (runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION) if (runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION)
return false; return false;
@ -591,7 +611,6 @@ bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bo
return handled; return handled;
} }
bool CannedMessageModule::handleFreeTextInput(const InputEvent *event) bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
{ {
// Always process only if in FREETEXT mode // Always process only if in FREETEXT mode
@ -732,9 +751,18 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
int CannedMessageModule::handleEmotePickerInput(const InputEvent *event) int CannedMessageModule::handleEmotePickerInput(const InputEvent *event)
{ {
int numEmotes = graphics::numEmotes; int numEmotes = graphics::numEmotes;
// Override isDown and isSelect ONLY for emote picker behavior
bool isUp = isUpEvent(event); bool isUp = isUpEvent(event);
bool isDown = isDownEvent(event); bool isDown = isDownEvent(event);
bool isSelect = isSelectEvent(event); bool isSelect = isSelectEvent(event);
if (runState == CANNED_MESSAGE_RUN_STATE_EMOTE_PICKER) {
if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_PRESSED) {
isDown = true;
} else if (event->inputEvent == INPUT_BROKER_MSG_BUTTON_LONG_PRESSED) {
isSelect = true;
}
}
// Scroll emote list // Scroll emote list
if (isUp && emotePickerIndex > 0) { if (isUp && emotePickerIndex > 0) {
@ -747,6 +775,7 @@ int CannedMessageModule::handleEmotePickerInput(const InputEvent *event)
screen->forceDisplay(); screen->forceDisplay();
return 1; return 1;
} }
// Select emote: insert into freetext at cursor and return to freetext // Select emote: insert into freetext at cursor and return to freetext
if (isSelect) { if (isSelect) {
String label = graphics::emotes[emotePickerIndex].label; String label = graphics::emotes[emotePickerIndex].label;
@ -761,12 +790,14 @@ int CannedMessageModule::handleEmotePickerInput(const InputEvent *event)
screen->forceDisplay(); screen->forceDisplay();
return 1; return 1;
} }
// Cancel returns to freetext // Cancel returns to freetext
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) { if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) {
runState = CANNED_MESSAGE_RUN_STATE_FREETEXT; runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
screen->forceDisplay(); screen->forceDisplay();
return 1; return 1;
} }
return 0; return 0;
} }
@ -1777,7 +1808,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
} else { } else {
// Text: split by words and wrap inside word if needed // Text: split by words and wrap inside word if needed
String text = token.second; String text = token.second;
int pos = 0; uint16_t pos = 0;
while (pos < text.length()) { while (pos < text.length()) {
// Find next space (or end) // Find next space (or end)
int spacePos = text.indexOf(' ', pos); int spacePos = text.indexOf(' ', pos);

View File

@ -7,9 +7,8 @@
#define SENSOR_PORT_NUM 2 #define SENSOR_PORT_NUM 2
#define SENSOR_BAUD_RATE 115200 #define SENSOR_BAUD_RATE 115200
#if !HAS_TFT
#define BUTTON_PIN 38 #define BUTTON_PIN 38
#endif
// #define BUTTON_NEED_PULLUP // #define BUTTON_NEED_PULLUP
// #define BATTERY_PIN 27 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage // #define BATTERY_PIN 27 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage