mirror of
https://github.com/meshtastic/firmware.git
synced 2026-09-22 06:15:25 -04:00
* Add shared e-ink hardware layer (graphics/eink) alongside legacy drivers Foundation for a target-by-target migration off the GxEPD2-based EInkDisplay2/EInkDynamicDisplay/EInkParallelDisplay stack: - src/graphics/eink/: chipset drivers, panel profiles, backlight helper (promoted from the InkHUD driver set, shared by BaseUI and InkHUD) - src/graphics/BaseUIEInkDisplay: OLEDDisplay adapter driving the new layer, with EINK_* compat macros matching EInkDynamicDisplay - [niche] build helper in platformio.ini; graphics/eink/ excluded from arduino_base so unconverted targets are unaffected - Screen/CannedMessageModule dispatch between the two stacks per env - InkHUD-specific touch code in TouchScreenImpl1 guarded with MESHTASTIC_INCLUDE_INKHUD (no-op today, required once BaseUI variants define MESHTASTIC_INCLUDE_NICHE_GRAPHICS without InkHUD) No variant is converted and no legacy file is removed; every existing env builds identical firmware. * Fix clang-format comment alignment in Screen.cpp * Address review findings in the e-ink driver layer - Screen.cpp: exclude InkHUD builds from all NicheGraphics BaseUI guards - BaseUIEInkDisplay: size the OLEDDisplay buffer from its actual indexing - EInkParallel: defer update() while an async refresh is in flight, honor the selected clear mode in the async task, never delete a live task - ED047TC1: clean up on failed initPanel, fix inverted bbepI2CWrite checks - UC8175: drop bogus 0x12 soft reset (0x12 is display refresh on UC8175) - LCMEN2R13EFC1: guard absent reset pin, bound the busy wait - SSD16XX/SSD1682: build the RAM window from the instance, not statics - Doc corrections in driver banners and Drivers/README * SSD16XX/SSD1682: send inclusive Y-end address (height - 1) * LCMEN213EFC1: adopt the shared wait timeout / fail-through pattern wait() now bounds the busy poll via Throttle and sets the EInk failed flag on timeout; sendCommand/sendData fail through like the SSD16XX and UC8175 drivers. EInk::runOnce clears the flag after the failed cycle.
180 lines
4.8 KiB
C++
180 lines
4.8 KiB
C++
#include "TouchScreenImpl1.h"
|
|
#include "InputBroker.h"
|
|
#include "PowerFSM.h"
|
|
#include "configuration.h"
|
|
#include "main.h"
|
|
#include "modules/ExternalNotificationModule.h"
|
|
#include "sleep.h"
|
|
#include <cstring>
|
|
|
|
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
|
#include "graphics/niche/InkHUD/InkHUD.h"
|
|
#include "graphics/niche/InkHUD/SystemApplet.h"
|
|
#endif
|
|
|
|
#if ARCH_PORTDUINO
|
|
#include "platform/portduino/PortduinoGlue.h"
|
|
#endif
|
|
|
|
TouchScreenImpl1 *touchScreenImpl1;
|
|
|
|
// Hardware-interrupt wake on the touch IRQ line. Some touch boards
|
|
// either drive this pin differently (RAK14014 already owns this interrupt in TFTDisplay) or
|
|
// route it through an IO expander, which can't be used with attachInterrupt().
|
|
|
|
// use ENABLE_TOUCH_INT to indicate that we should enable the interrupt here.
|
|
#if defined(SCREEN_TOUCH_INT) && defined(ENABLE_TOUCH_INT)
|
|
|
|
// The touch controller pulls SCREEN_TOUCH_INT low when a new touch begins. Wake the polling
|
|
// thread immediately so the touch is handled without waiting for the next idle poll. The
|
|
// periodic poll in TouchScreenBase::runOnce() remains as a fallback. Mirrors the button
|
|
// interrupt handling in ButtonThread/InputBroker.
|
|
static void touchScreenInterruptHandler()
|
|
{
|
|
if (touchScreenImpl1) {
|
|
touchScreenImpl1->setIntervalFromNow(0);
|
|
runASAP = true;
|
|
BaseType_t higherWake = 0;
|
|
concurrency::mainDelay.interruptFromISR(&higherWake);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
TouchScreenImpl1::TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTouch)(int16_t *, int16_t *))
|
|
: TouchScreenBase("touchscreen1", width, height), _getTouch(getTouch)
|
|
{
|
|
}
|
|
|
|
void TouchScreenImpl1::init()
|
|
{
|
|
#if ARCH_PORTDUINO
|
|
if (portduino_config.touchscreenModule) {
|
|
TouchScreenBase::init(true);
|
|
inputBroker->registerSource(this);
|
|
attachTouchInterrupt();
|
|
} else {
|
|
TouchScreenBase::init(false);
|
|
}
|
|
#elif !HAS_TOUCHSCREEN
|
|
TouchScreenBase::init(false);
|
|
return;
|
|
#else
|
|
TouchScreenBase::init(true);
|
|
if (inputBroker)
|
|
inputBroker->registerSource(this);
|
|
attachTouchInterrupt();
|
|
#endif
|
|
|
|
#if defined(ENABLE_TOUCH_INT) && defined(ARCH_ESP32)
|
|
// Detach/reattach our interrupt around light sleep, so sleep.cpp can configure the touch
|
|
// pin as a wake source without our handler interfering.
|
|
lsObserver.observe(¬ifyLightSleep);
|
|
lsEndObserver.observe(¬ifyLightSleepEnd);
|
|
#endif
|
|
}
|
|
|
|
// Attach the touch-controller IRQ so a new touch wakes the polling thread immediately.
|
|
// No-op on boards without a usable touch interrupt line.
|
|
void TouchScreenImpl1::attachTouchInterrupt()
|
|
{
|
|
#ifdef ENABLE_TOUCH_INT
|
|
pinMode(SCREEN_TOUCH_INT, INPUT_PULLUP);
|
|
attachInterrupt(SCREEN_TOUCH_INT, touchScreenInterruptHandler, FALLING);
|
|
LOG_INFO("TouchScreen interrupt attached on pin %d", SCREEN_TOUCH_INT);
|
|
#endif
|
|
}
|
|
|
|
#ifdef ARCH_ESP32
|
|
// Detach our interrupt before light sleep; sleep.cpp configures its own wake-on-touch.
|
|
int TouchScreenImpl1::beforeLightSleep(void *unused)
|
|
{
|
|
#ifdef ENABLE_TOUCH_INT
|
|
detachInterrupt(SCREEN_TOUCH_INT);
|
|
#endif
|
|
return 0; // Indicates success
|
|
}
|
|
|
|
// Reattach our interrupt after waking from light sleep.
|
|
int TouchScreenImpl1::afterLightSleep(esp_sleep_wakeup_cause_t cause)
|
|
{
|
|
attachTouchInterrupt();
|
|
return 0; // Indicates success
|
|
}
|
|
#endif
|
|
|
|
bool TouchScreenImpl1::getTouch(int16_t &x, int16_t &y)
|
|
{
|
|
return _getTouch(&x, &y);
|
|
}
|
|
|
|
bool TouchScreenImpl1::fastTapModeEnabled() const
|
|
{
|
|
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
|
const auto *inkhud = NicheGraphics::InkHUD::InkHUD::getInstance();
|
|
if (!inkhud) {
|
|
return false;
|
|
}
|
|
|
|
for (auto *sa : inkhud->systemApplets) {
|
|
if (!sa || !sa->name) {
|
|
continue;
|
|
}
|
|
if (strcmp(sa->name, "Keyboard") == 0) {
|
|
return sa->isForeground();
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool TouchScreenImpl1::longPressEnabled() const
|
|
{
|
|
return !fastTapModeEnabled();
|
|
}
|
|
|
|
/**
|
|
* @brief forward touchscreen event
|
|
*
|
|
* @param event
|
|
*
|
|
* The touchscreen events are translated to input events and reversed
|
|
*/
|
|
void TouchScreenImpl1::onEvent(const TouchEvent &event)
|
|
{
|
|
InputEvent e = {};
|
|
e.source = event.source;
|
|
e.kbchar = 0;
|
|
e.touchX = event.x;
|
|
e.touchY = event.y;
|
|
|
|
switch (event.touchEvent) {
|
|
case TOUCH_ACTION_LEFT: {
|
|
e.inputEvent = INPUT_BROKER_LEFT;
|
|
break;
|
|
}
|
|
case TOUCH_ACTION_RIGHT: {
|
|
e.inputEvent = INPUT_BROKER_RIGHT;
|
|
break;
|
|
}
|
|
case TOUCH_ACTION_UP: {
|
|
e.inputEvent = INPUT_BROKER_UP;
|
|
break;
|
|
}
|
|
case TOUCH_ACTION_DOWN: {
|
|
e.inputEvent = INPUT_BROKER_DOWN;
|
|
break;
|
|
}
|
|
case TOUCH_ACTION_LONG_PRESS: {
|
|
e.inputEvent = INPUT_BROKER_SELECT;
|
|
break;
|
|
}
|
|
case TOUCH_ACTION_TAP: {
|
|
e.inputEvent = INPUT_BROKER_USER_PRESS;
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
this->notifyObservers(&e);
|
|
}
|