fix(graphics): make on-screen keyboard lifecycle safe and RAII-managed (#11460)

- VirtualKeyboard::handleLongPress VK_ESC invoked the onTextEntered
  member std::function directly, but that callback path reaches
  OnScreenKeyboardModule::stop(), which destroys the keyboard - and
  with it the std::function whose invocation is still on the stack.
  handlePress and submitText already deliberately copy-and-clear before
  invoking for exactly this reason (CannedMessageModule documents the
  same hazard); do the same here.

- OnScreenKeyboardModule's keyboard becomes unique_ptr, replacing the
  delete-in-destructor / delete-then-new-in-start / delete-in-stop
  bookkeeping that runs on every keyboard open/close. The
  NotificationRenderer legacy hook receives a non-owning raw pointer,
  as before.
This commit is contained in:
Ben Meadors
2026-08-14 10:05:16 +00:00
committed by GitHub
parent 0ff10318ad
commit fb6a212b44
3 files changed
+14 -20

No files matched your search

+7 -1
View File
@@ -666,7 +666,13 @@ void VirtualKeyboard::handleLongPress()
break;
case VK_ESC:
if (onTextEntered) {
onTextEntered("");
// Copy-and-clear before invoking, like handlePress/submitText: the callback can
// destroy this keyboard (OnScreenKeyboardModule::stop), so the member must not be
// the std::function still executing on the stack.
std::function<void(const std::string &)> callback = onTextEntered;
onTextEntered = nullptr;
inputText = "";
callback("");
}
break;
default:
+5 -18
View File
@@ -18,22 +18,12 @@ OnScreenKeyboardModule &OnScreenKeyboardModule::instance()
return inst;
}
OnScreenKeyboardModule::~OnScreenKeyboardModule()
{
if (keyboard) {
delete keyboard;
keyboard = nullptr;
}
}
OnScreenKeyboardModule::~OnScreenKeyboardModule() = default;
void OnScreenKeyboardModule::start(const char *header, const char *initialText, uint32_t durationMs,
std::function<void(const std::string &)> cb)
{
if (keyboard) {
delete keyboard;
keyboard = nullptr;
}
keyboard = new VirtualKeyboard();
keyboard = std::make_unique<VirtualKeyboard>();
callback = cb;
if (header)
keyboard->setHeader(header);
@@ -50,7 +40,7 @@ void OnScreenKeyboardModule::start(const char *header, const char *initialText,
});
// Maintain legacy compatibility hooks
NotificationRenderer::virtualKeyboard = keyboard;
NotificationRenderer::virtualKeyboard = keyboard.get();
NotificationRenderer::textInputCallback = callback;
}
@@ -58,10 +48,7 @@ void OnScreenKeyboardModule::stop(bool callEmptyCallback)
{
auto cb = callback;
callback = nullptr;
if (keyboard) {
delete keyboard;
keyboard = nullptr;
}
keyboard.reset();
// Keep NotificationRenderer legacy pointers in sync
NotificationRenderer::virtualKeyboard = nullptr;
NotificationRenderer::textInputCallback = nullptr;
@@ -74,7 +61,7 @@ void OnScreenKeyboardModule::handleInput(const InputEvent &event)
if (!keyboard)
return;
if (processVirtualKeyboardInput(event, keyboard))
if (processVirtualKeyboardInput(event, keyboard.get()))
return;
if (event.inputEvent == INPUT_BROKER_CANCEL)
+2 -1
View File
@@ -7,6 +7,7 @@
#include "graphics/VirtualKeyboard.h"
#include <OLEDDisplay.h>
#include <functional>
#include <memory>
#include <string>
namespace graphics
@@ -34,7 +35,7 @@ class OnScreenKeyboardModule
void onSubmit(const std::string &text);
void onCancel();
VirtualKeyboard *keyboard = nullptr;
std::unique_ptr<VirtualKeyboard> keyboard;
std::function<void(const std::string &)> callback;
};