mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-08 14:12:05 +00:00
Compare commits
11 Commits
36373d4f29
...
6d26e64a4d
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6d26e64a4d | ||
![]() |
f8ea9c0e40 | ||
![]() |
67d3cafc6f | ||
![]() |
6c3f24dfe6 | ||
![]() |
4dde0a9202 | ||
![]() |
652033a0b4 | ||
![]() |
f4c5e31f3d | ||
![]() |
b177329813 | ||
![]() |
8fc0f1aa13 | ||
![]() |
e869e1b146 | ||
![]() |
1858031ad6 |
@ -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
|
||||||
|
@ -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 it’s available in FOCUS_PRESERVE block
|
// Declare this early so it’s 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
@ -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
|
@ -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);
|
||||||
|
@ -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"
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user