Move eterm emulation off the UI thread and add tabbed frontend

- introduce a per-terminal worker controller with ordered command queues,
    coalesced events, immutable snapshots, and deterministic shutdown
  - move PTY reads, parsing, history, selection, resize, reflow, and process
    status handling onto the terminal worker
  - publish terminal snapshots at the host refresh rate while preserving
    immediate idle, resize, interaction, and final-exit presentation
  - migrate TerminalDisplay, UITerminal, eterm, ecode, debugger, and ACP
    integrations away from direct mutable emulator access
  - synchronize scrollbar updates with acknowledged scroll commands
  - preserve title, clipboard, bell, prompt, color, restart, keep-alive,
    selection, input, and buffered process-exit behavior
  - add bounded synchronous selection requests and idempotent worker shutdown
  - make POSIX process launch safe after fork in multithreaded hosts and mark
    original PTY descriptors close-on-exec
  - add an UIApplication-based tabbed eterm with legacy CLI parity, tab
    lifecycle handling, close warnings, and default navigation shortcuts
  - add premake targets and deterministic controller, snapshot, lifecycle,
    concurrency, presentation, and exit-drain tests
This commit is contained in:
Martín Lucas Golini
2026-08-30 20:25:44 -03:00
parent 7c92b59e00
commit 8bcec85da0
21 changed files with 2274 additions and 402 deletions
+2 -2
View File
@@ -77,7 +77,7 @@ App* App::instance() {
bool App::isAnyTerminalDirty() const {
bool dirty = false;
mSplitter->forEachWidgetTypeStoppable( UI_TYPE_TERMINAL, [&dirty]( UIWidget* widget ) -> bool {
ProcessID pid = widget->asType<UITerminal>()->getTerm()->getTerminal()->getProcess()->pid();
ProcessID pid = widget->asType<UITerminal>()->getTerm()->getProcessId();
if ( Sys::processHasChildren( pid ) ) {
dirty = true;
return true;
@@ -5063,7 +5063,7 @@ void App::init( InitParameters& params ) {
focusTabBehavior, onMsgBoxCloseCb );
} else if ( mConfig.term.warnBeforeClosingTab && widget->isType( UI_TYPE_TERMINAL ) ) {
UITerminal* term = widget->asType<UITerminal>();
ProcessID pid = term->getTerm()->getTerminal()->getProcess()->pid();
ProcessID pid = term->getTerm()->getProcessId();
std::string msgBoxId = String::format( "msgbox_%p", this );
if ( Sys::processHasChildren( pid ) ) {
if ( nullptr != getUISceneNode()->find( msgBoxId ) )