From 1e7044c9322a702a87e61fd7a8b4e18af3d49919 Mon Sep 17 00:00:00 2001 From: JavaZeroo <71128095+JavaZeroo@users.noreply.github.com> Date: Fri, 30 Oct 2026 12:46:04 +0800 Subject: [PATCH 1/2] feat(qt): honour DECCKM and DECTCEM in the product entry refs #28 The model forwards both mode bits; the widget hides the cursor when DECTCEM clears it and sends SS3 arrows when DECCKM is set. --- src/qt/terminal_session.hpp | 2 ++ src/qt/terminal_widget.hpp | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/qt/terminal_session.hpp b/src/qt/terminal_session.hpp index 13a98f69..a6093b45 100644 --- a/src/qt/terminal_session.hpp +++ b/src/qt/terminal_session.hpp @@ -291,6 +291,8 @@ class SessionModel final : public QObject { started_ && accepting && requested_revision_ != transport_revision_, session_ ? session_->pid : 0}; } const Screen& screen() const { return parser_.screen; } + bool cursor_visible() const { return parser_.cursor_visible(); } + bool application_cursor_keys() const { return parser_.application_cursor_keys(); } bool running() const { return started_ && !finished_ && !close_requested_.load(std::memory_order_acquire); } bool finished() const { return finished_; } bool natural_exit() const { return finished_ && natural_exit_; } diff --git a/src/qt/terminal_widget.hpp b/src/qt/terminal_widget.hpp index 5d83e0c2..152378cc 100644 --- a/src/qt/terminal_widget.hpp +++ b/src/qt/terminal_widget.hpp @@ -109,10 +109,10 @@ class TerminalWidget final : public QWidget { case Qt::Key_Return: case Qt::Key_Enter: bytes = "\r"; break; case Qt::Key_Backspace: bytes = std::string(1, '\x7f'); break; case Qt::Key_Tab: bytes = "\t"; break; - case Qt::Key_Up: bytes = "\x1b[A"; break; - case Qt::Key_Down: bytes = "\x1b[B"; break; - case Qt::Key_Right: bytes = "\x1b[C"; break; - case Qt::Key_Left: bytes = "\x1b[D"; break; + case Qt::Key_Up: bytes = model_.application_cursor_keys() ? "\x1bOA" : "\x1b[A"; break; + case Qt::Key_Down: bytes = model_.application_cursor_keys() ? "\x1bOB" : "\x1b[B"; break; + case Qt::Key_Right: bytes = model_.application_cursor_keys() ? "\x1bOC" : "\x1b[C"; break; + case Qt::Key_Left: bytes = model_.application_cursor_keys() ? "\x1bOD" : "\x1b[D"; break; default: if (event->text().isEmpty()) { QWidget::keyPressEvent(event); return; } character_text = true; @@ -216,7 +216,7 @@ class TerminalWidget final : public QWidget { painter.restore(); } } - if (model_.running()) { + if (model_.running() && model_.cursor_visible()) { painter.fillRect(QRect(margin + screen.column * cell_width_, margin + (screen.row + 1) * cell_height_ - 2, cell_width_, 2), colour(-1, true)); } From ab0352fc07a3f5d5a132fe837eacecba43758ed6 Mon Sep 17 00:00:00 2001 From: JavaZeroo <71128095+JavaZeroo@users.noreply.github.com> Date: Fri, 30 Oct 2026 12:47:52 +0800 Subject: [PATCH 2/2] feat(shell): hide the cursor when DECTCEM clears it refs #28 Every renderer draw call now takes the parser cursor-visibility flag, including the retained-versus-full comparison and the evidence capture. --- experiments/shell/shell.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/experiments/shell/shell.cpp b/experiments/shell/shell.cpp index db148aa7..6941aa2a 100644 --- a/experiments/shell/shell.cpp +++ b/experiments/shell/shell.cpp @@ -829,11 +829,13 @@ class ShellWindow { } #endif #ifdef TERMFORGE_CANDIDATE_CORE - // The candidate terminal does not expose DECCKM yet; keep the previous CSI - // encoding instead of guessing at its mode state. + // The candidate terminal does not expose these modes yet; keep the previous + // CSI encoding and always draw the cursor instead of guessing its state. bool application_cursor_keys() const { return false; } + bool cursor_visible() const { return true; } #else bool application_cursor_keys() const { return parser_.application_cursor_keys(); } + bool cursor_visible() const { return parser_.cursor_visible(); } #endif void tick() { const auto began = Clock::now(); ++result_.ticks; @@ -1032,7 +1034,7 @@ class ShellWindow { const int last = (std::min)(self->snapshot_->rows(), (std::max)(self->dirty_last_, (int(paint.rcPaint.bottom)+cell_height-1)/cell_height)); FillRect(dc, &paint.rcPaint, static_cast(GetStockObject(BLACK_BRUSH))); - self->renderer_->draw(*self->surface_, *self->snapshot_, first, last); + self->renderer_->draw(*self->surface_, *self->snapshot_, first, last, self->cursor_visible()); if (self->mode_ == "resize" || self->mode_ == "resize-close") { Bitmap expected(self->surface_->width, self->surface_->height); self->renderer_->draw(expected, *self->snapshot_, 0, self->snapshot_->rows()); @@ -1390,14 +1392,14 @@ class ShellWindow { // including hidden tabs whose WM_PAINT may not be dispatched. if (surface_->width != renderer_->x(snapshot_->columns()) || surface_->height != renderer_->y(snapshot_->rows())) return false; - renderer_->draw(*surface_, *snapshot_, dirty_first_, dirty_last_); + renderer_->draw(*surface_, *snapshot_, dirty_first_, dirty_last_, cursor_visible()); dirty_first_=snapshot_->rows(); dirty_last_=0; Bitmap expected(surface_->width,surface_->height); - renderer_->draw(expected,*snapshot_,0,snapshot_->rows()); + renderer_->draw(expected,*snapshot_,0,snapshot_->rows(),cursor_visible()); return surface_->equals(expected); } void render_to(HDC dc, int x, int y) { - renderer_->draw(*surface_, *snapshot_, 0, snapshot_->rows()); + renderer_->draw(*surface_, *snapshot_, 0, snapshot_->rows(), cursor_visible()); require(BitBlt(dc,x,y,surface_->width,surface_->height,surface_->dc,0,0,SRCCOPY) != FALSE, "workspace terminal capture failed"); } Result run() {