diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index dca31be60..d18cc680e 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -161,9 +161,9 @@ bool EInkDisplay::connect() #if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE) || defined(TTGO_T_ECHO_PLUS) || \ defined(ELECROW_ThinkNode_M8) { - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1); - - adafruitDisplay = new GxEPD2_BW(*lowLevel); + // GxEPD2_BW stores a copy of the driver, so pass a temporary instead of leaking a heap object + adafruitDisplay = new GxEPD2_BW( + EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1)); adafruitDisplay->init(); #if defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE) || defined(ELECROW_ThinkNode_M8) adafruitDisplay->setRotation(4); @@ -178,9 +178,9 @@ bool EInkDisplay::connect() hspi = new SPIClass(HSPI); hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi); - - adafruitDisplay = new GxEPD2_BW(*lowLevel); + // GxEPD2_BW stores a copy of the driver, so pass a temporary instead of leaking a heap object + adafruitDisplay = new GxEPD2_BW( + EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi)); adafruitDisplay->init(); adafruitDisplay->setRotation(4); @@ -189,9 +189,9 @@ bool EInkDisplay::connect() } #elif defined(MESHLINK) { - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1); - - adafruitDisplay = new GxEPD2_BW(*lowLevel); + // GxEPD2_BW stores a copy of the driver, so pass a temporary instead of leaking a heap object + adafruitDisplay = new GxEPD2_BW( + EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1)); adafruitDisplay->init(); adafruitDisplay->setRotation(3); adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); @@ -199,8 +199,9 @@ bool EInkDisplay::connect() #elif defined(RAK4630) || defined(MAKERPYTHON) { if (eink_found) { - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); - adafruitDisplay = new GxEPD2_BW(*lowLevel); + // GxEPD2_BW stores a copy of the driver, so pass a temporary instead of leaking a heap object + adafruitDisplay = new GxEPD2_BW( + EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY)); adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0)); // RAK14000 2.13 inch b/w 250x122 does actually now support fast refresh adafruitDisplay->setRotation(3); @@ -236,9 +237,9 @@ bool EInkDisplay::connect() // VExt already enabled in setup() // RTC GPIO hold disabled in setup() - // Create GxEPD2 objects - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi); - adafruitDisplay = new GxEPD2_BW(*lowLevel); + // Create GxEPD2 objects (GxEPD2_BW stores a copy of the driver, so pass a temporary) + adafruitDisplay = new GxEPD2_BW( + EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi)); // Init GxEPD2 adafruitDisplay->init(); @@ -253,22 +254,25 @@ bool EInkDisplay::connect() } #elif defined(PCA10059) || defined(ME25LS01) { - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); - adafruitDisplay = new GxEPD2_BW(*lowLevel); + // GxEPD2_BW stores a copy of the driver, so pass a temporary instead of leaking a heap object + adafruitDisplay = new GxEPD2_BW( + EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY)); adafruitDisplay->init(115200, true, 40, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0)); adafruitDisplay->setRotation(0); adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); } #elif defined(M5_COREINK) || defined(T_DECK_PRO) - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); - adafruitDisplay = new GxEPD2_BW(*lowLevel); + // GxEPD2_BW stores a copy of the driver, so pass a temporary instead of leaking a heap object + adafruitDisplay = new GxEPD2_BW( + EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY)); adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0)); adafruitDisplay->setRotation(0); adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); #elif defined(my) || defined(ESP32_S3_PICO) { - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); - adafruitDisplay = new GxEPD2_BW(*lowLevel); + // GxEPD2_BW stores a copy of the driver, so pass a temporary instead of leaking a heap object + adafruitDisplay = new GxEPD2_BW( + EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY)); adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0)); adafruitDisplay->setRotation(1); adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); @@ -280,9 +284,9 @@ bool EInkDisplay::connect() // VExt already enabled in setup() // RTC GPIO hold disabled in setup() - // Create GxEPD2 objects - auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *spi1); - adafruitDisplay = new GxEPD2_BW(*lowLevel); + // Create GxEPD2 objects (GxEPD2_BW stores a copy of the driver, so pass a temporary) + adafruitDisplay = new GxEPD2_BW( + EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *spi1)); // Init GxEPD2 adafruitDisplay->init(); diff --git a/src/graphics/EInkParallelDisplay.cpp b/src/graphics/EInkParallelDisplay.cpp index 552b11747..a61b1bae9 100644 --- a/src/graphics/EInkParallelDisplay.cpp +++ b/src/graphics/EInkParallelDisplay.cpp @@ -183,8 +183,10 @@ void EInkParallelDisplay::asyncFullUpdateTask(void *pvParameters) self->resetGhostPixelTracking(); #endif - self->asyncFullRunning.store(false); + // Handle first: once asyncFullRunning reads false, the destructor may act on the handle, so + // it must already be null by then (same ordering fix as eink/Drivers/EInkParallel.cpp). self->asyncTaskHandle = nullptr; + self->asyncFullRunning.store(false); // delete this task vTaskDelete(nullptr); diff --git a/src/graphics/Panel_sdl.cpp b/src/graphics/Panel_sdl.cpp index bad6072f9..f01d7dc68 100644 --- a/src/graphics/Panel_sdl.cpp +++ b/src/graphics/Panel_sdl.cpp @@ -360,7 +360,10 @@ Panel_sdl::Panel_sdl(void) : Panel_FrameBufferBase() bool Panel_sdl::init(bool use_reset) { - initFrameBuffer(_cfg.panel_width * 4, _cfg.panel_height); + // Bail before registering the monitor: continuing with a failed framebuffer allocation + // would leave sdl_update() reading garbage line pointers. + if (!initFrameBuffer(_cfg.panel_width * 4, _cfg.panel_height)) + return false; bool res = Panel_FrameBufferBase::init(use_reset); _list_monitor.push_back(&monitor); @@ -647,6 +650,10 @@ bool Panel_sdl::initFrameBuffer(size_t width, size_t height) } _texturebuf = (rgb888_t *)heap_alloc_dma(width * height * sizeof(rgb888_t)); + if (nullptr == _texturebuf) { + heap_free(lineArray); + return false; + } /// 8byte alignment; width = (width + 7) & ~7u; @@ -655,6 +662,15 @@ bool Panel_sdl::initFrameBuffer(size_t width, size_t height) memset(lineArray, 0, height * sizeof(uint8_t *)); uint8_t *framebuffer = (uint8_t *)heap_alloc_dma(width * height + 16); + if (nullptr == framebuffer) { + // Returning true here would leave _lines_buffer full of null+offset garbage pointers + // and turn the failure into a wild write on the next redraw. + heap_free(_texturebuf); + _texturebuf = nullptr; + heap_free(lineArray); + _lines_buffer = nullptr; + return false; + } auto fb = framebuffer; { diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index c8271ddf1..5e8423886 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -652,6 +652,10 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O Screen::~Screen() { delete[] graphics::normalFrames; + // Owned by the constructor; Screen is genuinely destroyed on the portduino reboot path + // (screen = nullptr in Power.cpp), which previously leaked the display and UI objects. + delete ui; + delete dispdev; } /** diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index ba2d50320..9ee16c85f 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -821,7 +821,7 @@ class LGFX : public lgfx::LGFX_Device { lgfx::Bus_SPI _bus_instance; - lgfx::ITouch *_touch_instance; + lgfx::ITouch *_touch_instance = nullptr; public: lgfx::Panel_Device *_panel_instance; @@ -891,24 +891,28 @@ class LGFX : public lgfx::LGFX_Device } else if (portduino_config.touchscreenModule == ft5x06) { _touch_instance = new lgfx::Touch_FT5x06; } - auto touch_cfg = _touch_instance->config(); + // Not every module in the config enum has a branch above (gt911 is handled by the + // color-UI path in tftSetup.cpp), so the pointer can legitimately still be null here. + if (_touch_instance) { + auto touch_cfg = _touch_instance->config(); - touch_cfg.pin_cs = portduino_config.touchscreenCS.pin; - touch_cfg.x_min = 0; - touch_cfg.x_max = portduino_config.displayHeight - 1; - touch_cfg.y_min = 0; - touch_cfg.y_max = portduino_config.displayWidth - 1; - touch_cfg.pin_int = portduino_config.touchscreenIRQ.pin; - touch_cfg.bus_shared = true; - touch_cfg.offset_rotation = portduino_config.touchscreenRotate; - if (portduino_config.touchscreenI2CAddr != -1) { - touch_cfg.i2c_addr = portduino_config.touchscreenI2CAddr; - } else { - touch_cfg.spi_host = portduino_config.touchscreen_spi_dev_int; + touch_cfg.pin_cs = portduino_config.touchscreenCS.pin; + touch_cfg.x_min = 0; + touch_cfg.x_max = portduino_config.displayHeight - 1; + touch_cfg.y_min = 0; + touch_cfg.y_max = portduino_config.displayWidth - 1; + touch_cfg.pin_int = portduino_config.touchscreenIRQ.pin; + touch_cfg.bus_shared = true; + touch_cfg.offset_rotation = portduino_config.touchscreenRotate; + if (portduino_config.touchscreenI2CAddr != -1) { + touch_cfg.i2c_addr = portduino_config.touchscreenI2CAddr; + } else { + touch_cfg.spi_host = portduino_config.touchscreen_spi_dev_int; + } + + _touch_instance->config(touch_cfg); + _panel_instance->setTouch(_touch_instance); } - - _touch_instance->config(touch_cfg); - _panel_instance->setTouch(_touch_instance); } #if defined(SDL_h_) if (portduino_config.displayPanel == x11) {