From f95f815e9817e4daf927f621ea56a48efe7dcb99 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Sun, 5 Mar 2023 23:55:37 +0100 Subject: [PATCH] Terminal: Add support for Cursor shape and blink Change-Id: I7f71578a714d375bcd4ef8ae431f4127cbc57a55 Reviewed-by: Cristian Adam Reviewed-by: --- src/plugins/terminal/terminalsettings.cpp | 6 ++ src/plugins/terminal/terminalsettings.h | 2 + src/plugins/terminal/terminalsettingspage.cpp | 6 ++ src/plugins/terminal/terminalsurface.cpp | 32 ++++++++--- src/plugins/terminal/terminalsurface.h | 7 +++ src/plugins/terminal/terminalwidget.cpp | 55 ++++++++++++++++++- src/plugins/terminal/terminalwidget.h | 6 ++ src/plugins/terminal/tests/cursor/bar | 3 + src/plugins/terminal/tests/cursor/blinkbar | 3 + src/plugins/terminal/tests/cursor/blinkblock | 3 + .../terminal/tests/cursor/blinkunderline | 3 + src/plugins/terminal/tests/cursor/block | 3 + src/plugins/terminal/tests/cursor/underline | 3 + 13 files changed, 122 insertions(+), 10 deletions(-) create mode 100755 src/plugins/terminal/tests/cursor/bar create mode 100755 src/plugins/terminal/tests/cursor/blinkbar create mode 100755 src/plugins/terminal/tests/cursor/blinkblock create mode 100755 src/plugins/terminal/tests/cursor/blinkunderline create mode 100755 src/plugins/terminal/tests/cursor/block create mode 100755 src/plugins/terminal/tests/cursor/underline diff --git a/src/plugins/terminal/terminalsettings.cpp b/src/plugins/terminal/terminalsettings.cpp index d19b488f3e2..cbb43d78bbe 100644 --- a/src/plugins/terminal/terminalsettings.cpp +++ b/src/plugins/terminal/terminalsettings.cpp @@ -71,6 +71,11 @@ TerminalSettings::TerminalSettings() fontSize.setDefaultValue(defaultFontSize()); fontSize.setRange(1, 100); + allowBlinkingCursor.setSettingsKey("AllowBlinkingCursor"); + allowBlinkingCursor.setLabelText(Tr::tr("Allow blinking cursor")); + allowBlinkingCursor.setToolTip(Tr::tr("Allow the cursor to blink.")); + allowBlinkingCursor.setDefaultValue(false); + shell.setSettingsKey("ShellPath"); shell.setLabelText(Tr::tr("Shell path:")); shell.setExpectedKind(PathChooser::ExistingCommand); @@ -110,6 +115,7 @@ TerminalSettings::TerminalSettings() registerAspect(&font); registerAspect(&fontSize); registerAspect(&shell); + registerAspect(&allowBlinkingCursor); registerAspect(&foregroundColor); registerAspect(&backgroundColor); diff --git a/src/plugins/terminal/terminalsettings.h b/src/plugins/terminal/terminalsettings.h index 3f92f1aa7aa..de5f0a32428 100644 --- a/src/plugins/terminal/terminalsettings.h +++ b/src/plugins/terminal/terminalsettings.h @@ -23,6 +23,8 @@ public: Utils::ColorAspect selectionColor; Utils::ColorAspect colors[16]; + + Utils::BoolAspect allowBlinkingCursor; }; } // namespace Terminal diff --git a/src/plugins/terminal/terminalsettingspage.cpp b/src/plugins/terminal/terminalsettingspage.cpp index f3986878380..bf73cddd0f4 100644 --- a/src/plugins/terminal/terminalsettingspage.cpp +++ b/src/plugins/terminal/terminalsettingspage.cpp @@ -54,6 +54,12 @@ QWidget *TerminalSettingsPage::widget() settings.fontSize, st, }, }, + Group { + title(Tr::tr("Cursor")), + Row { + settings.allowBlinkingCursor, st, + }, + }, Group { title(Tr::tr("Colors")), Column { diff --git a/src/plugins/terminal/terminalsurface.cpp b/src/plugins/terminal/terminalsurface.cpp index 2bd7327c26b..4c9021d5965 100644 --- a/src/plugins/terminal/terminalsurface.cpp +++ b/src/plugins/terminal/terminalsurface.cpp @@ -28,6 +28,9 @@ struct TerminalSurfacePrivate , m_vtermScreen(vterm_obtain_screen(m_vterm.get())) , m_scrollback(std::make_unique(5000)) , q(surface) + {} + + void init() { vterm_set_utf8(m_vterm.get(), true); @@ -193,15 +196,24 @@ struct TerminalSurfacePrivate int setTerminalProperties(VTermProp prop, VTermValue *val) { switch (prop) { - case VTERM_PROP_CURSORVISIBLE: + case VTERM_PROP_CURSORVISIBLE: { + Cursor old = q->cursor(); m_cursor.visible = val->boolean; + q->cursorChanged(old, q->cursor()); break; - case VTERM_PROP_CURSORBLINK: - qCDebug(log) << "Ignoring VTERM_PROP_CURSORBLINK" << val->boolean; + } + case VTERM_PROP_CURSORBLINK: { + Cursor old = q->cursor(); + m_cursor.blink = val->boolean; + emit q->cursorChanged(old, q->cursor()); break; - case VTERM_PROP_CURSORSHAPE: - qCDebug(log) << "Ignoring VTERM_PROP_CURSORSHAPE" << val->number; + } + case VTERM_PROP_CURSORSHAPE: { + Cursor old = q->cursor(); + m_cursor.shape = (Cursor::Shape) val->number; + emit q->cursorChanged(old, q->cursor()); break; + } case VTERM_PROP_ICONNAME: //emit iconTextChanged(val->string); break; @@ -227,7 +239,8 @@ struct TerminalSurfacePrivate { Q_UNUSED(oldpos); Cursor oldCursor = q->cursor(); - m_cursor = {{pos.col, pos.row}, visible > 0}; + m_cursor.position = {pos.col, pos.row}; + m_cursor.visible = visible > 0; q->cursorChanged(oldCursor, q->cursor()); return 1; } @@ -272,7 +285,9 @@ struct TerminalSurfacePrivate TerminalSurface::TerminalSurface(QSize initialGridSize) : d(std::make_unique(this, initialGridSize)) -{} +{ + d->init(); +} TerminalSurface::~TerminalSurface() = default; @@ -310,7 +325,8 @@ std::u32string::value_type TerminalSurface::fetchCharAt(int x, int y) const TerminalCell TerminalSurface::fetchCell(int x, int y) const { - static TerminalCell emptyCell{1, {}, {}}; + static TerminalCell + emptyCell{1, {}, {}, false, {}, std::nullopt, QTextCharFormat::NoUnderline, false}; QTC_ASSERT(y >= 0, return emptyCell); QTC_ASSERT(y < d->liveSize().height() + d->m_scrollback->size(), return emptyCell); diff --git a/src/plugins/terminal/terminalsurface.h b/src/plugins/terminal/terminalsurface.h index b92cc9da833..354a3a028d4 100644 --- a/src/plugins/terminal/terminalsurface.h +++ b/src/plugins/terminal/terminalsurface.h @@ -31,8 +31,15 @@ struct TerminalCell struct Cursor { + enum class Shape { + Block = 1, + Underline, + LeftBar, + }; QPoint position; bool visible; + Shape shape; + bool blink{false}; }; class TerminalSurface : public QObject diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index 0be7bc7b72a..9d515972b0a 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -67,6 +67,17 @@ TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &op setupColors(); setupActions(); + m_cursorBlinkTimer.setInterval(750); + m_cursorBlinkTimer.setSingleShot(false); + + connect(&m_cursorBlinkTimer, &QTimer::timeout, this, [this]() { + if (hasFocus()) + m_cursorBlinkState = !m_cursorBlinkState; + else + m_cursorBlinkState = true; + updateViewport(gridToViewport(QRect{m_cursor.position, m_cursor.position})); + }); + setAttribute(Qt::WA_InputMethodEnabled); setAttribute(Qt::WA_MouseTracking); @@ -88,6 +99,7 @@ TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &op // Setup colors first, as setupFont will redraw the screen. setupColors(); setupFont(); + configBlinkTimer(); }); } @@ -261,7 +273,10 @@ void TerminalWidget::setupSurface() if (startY > endY) std::swap(startY, endY); + m_cursor = newCursor; + updateViewport(gridToViewport(QRect{QPoint{startX, startY}, QPoint{endX, endY}})); + configBlinkTimer(); }); connect(m_surface.get(), &Internal::TerminalSurface::altscreenChanged, this, [this] { setSelection(std::nullopt); @@ -271,6 +286,18 @@ void TerminalWidget::setupSurface() }); } +void TerminalWidget::configBlinkTimer() +{ + bool shouldRun = m_cursor.visible && m_cursor.blink && hasFocus() + && TerminalSettings::instance().allowBlinkingCursor.value(); + if (shouldRun != m_cursorBlinkTimer.isActive()) { + if (shouldRun) + m_cursorBlinkTimer.start(); + else + m_cursorBlinkTimer.stop(); + } +} + void TerminalWidget::setFont(const QFont &font) { m_font = font; @@ -658,17 +685,33 @@ void TerminalWidget::paintCursor(QPainter &p) const { auto cursor = m_surface->cursor(); - if (cursor.visible) { + const bool blinkState = !cursor.blink || m_cursorBlinkState + || !TerminalSettings::instance().allowBlinkingCursor.value(); + + if (cursor.visible && blinkState) { const int cursorCellWidth = m_surface->cellWidthAt(cursor.position.x(), cursor.position.y()); QRectF cursorRect = QRectF(gridToGlobal(cursor.position), gridToGlobal({cursor.position.x() + cursorCellWidth, cursor.position.y()}, true)); + + cursorRect.adjust(0, 0, 0, -1); + if (hasFocus()) { QPainter::CompositionMode oldMode = p.compositionMode(); p.setCompositionMode(QPainter::RasterOp_NotDestination); - p.fillRect(cursorRect, p.pen().brush()); + switch (cursor.shape) { + case Internal::Cursor::Shape::Block: + p.fillRect(cursorRect, p.pen().brush()); + break; + case Internal::Cursor::Shape::Underline: + p.drawLine(cursorRect.bottomLeft(), cursorRect.bottomRight()); + break; + case Internal::Cursor::Shape::LeftBar: + p.drawLine(cursorRect.topLeft(), cursorRect.bottomLeft()); + break; + } p.setCompositionMode(oldMode); } else { p.drawRect(cursorRect); @@ -809,6 +852,12 @@ void TerminalWidget::paintEvent(QPaintEvent *event) void TerminalWidget::keyPressEvent(QKeyEvent *event) { + // Don't blink during typing + if (m_cursorBlinkTimer.isActive()) { + m_cursorBlinkTimer.start(); + m_cursorBlinkState = true; + } + bool actionTriggered = false; for (const auto &action : actions()) { if (!action->isEnabled()) @@ -918,10 +967,12 @@ void TerminalWidget::wheelEvent(QWheelEvent *event) void TerminalWidget::focusInEvent(QFocusEvent *) { updateViewport(); + configBlinkTimer(); } void TerminalWidget::focusOutEvent(QFocusEvent *) { updateViewport(); + configBlinkTimer(); } void TerminalWidget::inputMethodEvent(QInputMethodEvent *event) diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h index 058e0e2cb38..07355b95bc2 100644 --- a/src/plugins/terminal/terminalwidget.h +++ b/src/plugins/terminal/terminalwidget.h @@ -149,6 +149,8 @@ protected: void setSelection(const std::optional &selection); + void configBlinkTimer(); + private: std::unique_ptr m_process; std::unique_ptr m_surface; @@ -188,6 +190,10 @@ private: std::chrono::system_clock::time_point m_lastFlush; std::chrono::system_clock::time_point m_lastDoubleClick; bool m_selectLineMode{false}; + + Internal::Cursor m_cursor; + QTimer m_cursorBlinkTimer; + bool m_cursorBlinkState{true}; }; } // namespace Terminal diff --git a/src/plugins/terminal/tests/cursor/bar b/src/plugins/terminal/tests/cursor/bar new file mode 100755 index 00000000000..a7bd99b55dd --- /dev/null +++ b/src/plugins/terminal/tests/cursor/bar @@ -0,0 +1,3 @@ +#!/bin/sh + +echo -e -n "\x1b[\x36 q" # Steady bar diff --git a/src/plugins/terminal/tests/cursor/blinkbar b/src/plugins/terminal/tests/cursor/blinkbar new file mode 100755 index 00000000000..0acf6179d9c --- /dev/null +++ b/src/plugins/terminal/tests/cursor/blinkbar @@ -0,0 +1,3 @@ +#!/bin/sh + +echo -e -n "\x1b[\x35 q" # Blinking bar diff --git a/src/plugins/terminal/tests/cursor/blinkblock b/src/plugins/terminal/tests/cursor/blinkblock new file mode 100755 index 00000000000..c536c83b8b7 --- /dev/null +++ b/src/plugins/terminal/tests/cursor/blinkblock @@ -0,0 +1,3 @@ +#!/bin/sh + +echo -e -n "\x1b[\x31 q" # Blinking block (default) diff --git a/src/plugins/terminal/tests/cursor/blinkunderline b/src/plugins/terminal/tests/cursor/blinkunderline new file mode 100755 index 00000000000..745d9e2a29c --- /dev/null +++ b/src/plugins/terminal/tests/cursor/blinkunderline @@ -0,0 +1,3 @@ +#!/bin/sh + +echo -e -n "\x1b[\x33 q" # Blinking underline diff --git a/src/plugins/terminal/tests/cursor/block b/src/plugins/terminal/tests/cursor/block new file mode 100755 index 00000000000..421df3b8c5a --- /dev/null +++ b/src/plugins/terminal/tests/cursor/block @@ -0,0 +1,3 @@ +#!/bin/sh + +echo -e -n "\x1b[\x32 q" # Steady block diff --git a/src/plugins/terminal/tests/cursor/underline b/src/plugins/terminal/tests/cursor/underline new file mode 100755 index 00000000000..7638b5358d0 --- /dev/null +++ b/src/plugins/terminal/tests/cursor/underline @@ -0,0 +1,3 @@ +#!/bin/sh + +echo -e -n "\x1b[\x34 q" # Steady underline