forked from qt-creator/qt-creator
Terminal: Rewrite rendering
The rendering has been rewritten to use cached GlyphRuns instead of text layouts. The VTerm specific code was moved into TerminalSurface. Change-Id: I10caa3db4ee932414987c9ddae2dcb777dc1f6e7 Reviewed-by: Cristian Adam <cristian.adam@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
4
src/libs/3rdparty/libvterm/include/vterm.h
vendored
4
src/libs/3rdparty/libvterm/include/vterm.h
vendored
@@ -496,7 +496,7 @@ void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTer
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned int bold : 1;
|
unsigned int bold : 1;
|
||||||
unsigned int underline : 2;
|
unsigned int underline : 3;
|
||||||
unsigned int italic : 1;
|
unsigned int italic : 1;
|
||||||
unsigned int blink : 1;
|
unsigned int blink : 1;
|
||||||
unsigned int reverse : 1;
|
unsigned int reverse : 1;
|
||||||
@@ -514,6 +514,8 @@ enum {
|
|||||||
VTERM_UNDERLINE_SINGLE,
|
VTERM_UNDERLINE_SINGLE,
|
||||||
VTERM_UNDERLINE_DOUBLE,
|
VTERM_UNDERLINE_DOUBLE,
|
||||||
VTERM_UNDERLINE_CURLY,
|
VTERM_UNDERLINE_CURLY,
|
||||||
|
VTERM_UNDERLINE_DOTTED,
|
||||||
|
VTERM_UNDERLINE_DASHED
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|||||||
6
src/libs/3rdparty/libvterm/src/pen.c
vendored
6
src/libs/3rdparty/libvterm/src/pen.c
vendored
@@ -323,6 +323,12 @@ INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argco
|
|||||||
case 3:
|
case 3:
|
||||||
state->pen.underline = VTERM_UNDERLINE_CURLY;
|
state->pen.underline = VTERM_UNDERLINE_CURLY;
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
state->pen.underline = VTERM_UNDERLINE_DOTTED;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
state->pen.underline = VTERM_UNDERLINE_DASHED;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
|
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
|
||||||
|
|||||||
2
src/libs/3rdparty/libvterm/src/screen.c
vendored
2
src/libs/3rdparty/libvterm/src/screen.c
vendored
@@ -18,7 +18,7 @@ typedef struct
|
|||||||
VTermColor fg, bg;
|
VTermColor fg, bg;
|
||||||
|
|
||||||
unsigned int bold : 1;
|
unsigned int bold : 1;
|
||||||
unsigned int underline : 2;
|
unsigned int underline : 3;
|
||||||
unsigned int italic : 1;
|
unsigned int italic : 1;
|
||||||
unsigned int blink : 1;
|
unsigned int blink : 1;
|
||||||
unsigned int reverse : 1;
|
unsigned int reverse : 1;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct VTermPen
|
|||||||
VTermColor fg;
|
VTermColor fg;
|
||||||
VTermColor bg;
|
VTermColor bg;
|
||||||
unsigned int bold:1;
|
unsigned int bold:1;
|
||||||
unsigned int underline:2;
|
unsigned int underline:3;
|
||||||
unsigned int italic:1;
|
unsigned int italic:1;
|
||||||
unsigned int blink:1;
|
unsigned int blink:1;
|
||||||
unsigned int reverse:1;
|
unsigned int reverse:1;
|
||||||
|
|||||||
@@ -3,16 +3,18 @@ add_qtc_plugin(Terminal
|
|||||||
PLUGIN_DEPENDS Core
|
PLUGIN_DEPENDS Core
|
||||||
DEPENDS libvterm
|
DEPENDS libvterm
|
||||||
SOURCES
|
SOURCES
|
||||||
celllayout.cpp celllayout.h
|
celliterator.cpp celliterator.h
|
||||||
|
glyphcache.cpp glyphcache.h
|
||||||
|
keys.cpp keys.h
|
||||||
|
scrollback.cpp scrollback.h
|
||||||
|
shellmodel.cpp shellmodel.h
|
||||||
terminal.qrc
|
terminal.qrc
|
||||||
terminalplugin.cpp terminalplugin.h
|
|
||||||
terminaltr.h
|
|
||||||
terminalpane.cpp terminalpane.h
|
terminalpane.cpp terminalpane.h
|
||||||
terminalwidget.cpp terminalwidget.h
|
terminalplugin.cpp terminalplugin.h
|
||||||
terminalprocessinterface.cpp terminalprocessinterface.h
|
terminalprocessinterface.cpp terminalprocessinterface.h
|
||||||
terminalsettings.cpp terminalsettings.h
|
terminalsettings.cpp terminalsettings.h
|
||||||
terminalsettingspage.cpp terminalsettingspage.h
|
terminalsettingspage.cpp terminalsettingspage.h
|
||||||
scrollback.h scrollback.cpp
|
terminalsurface.cpp terminalsurface.h
|
||||||
shellmodel.cpp shellmodel.h
|
terminaltr.h
|
||||||
keys.cpp keys.h
|
terminalwidget.cpp terminalwidget.h
|
||||||
)
|
)
|
||||||
|
|||||||
89
src/plugins/terminal/celliterator.cpp
Normal file
89
src/plugins/terminal/celliterator.cpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "celliterator.h"
|
||||||
|
|
||||||
|
#include "terminalsurface.h"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace Terminal::Internal {
|
||||||
|
|
||||||
|
CellIterator::CellIterator(const TerminalSurface *surface, QPoint pos)
|
||||||
|
: m_state(State::INSIDE)
|
||||||
|
, m_surface(surface)
|
||||||
|
{
|
||||||
|
m_pos = (pos.x()) + (pos.y() * surface->liveSize().width());
|
||||||
|
m_maxpos = surface->fullSize().width() * (surface->fullSize().height()) - 1;
|
||||||
|
updateChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
CellIterator::CellIterator(const TerminalSurface *surface, int pos)
|
||||||
|
: m_state(State::INSIDE)
|
||||||
|
, m_surface(surface)
|
||||||
|
{
|
||||||
|
m_pos = pos;
|
||||||
|
m_maxpos = surface->fullSize().width() * (surface->fullSize().height()) - 1;
|
||||||
|
updateChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
CellIterator::CellIterator(const TerminalSurface *surface, State state)
|
||||||
|
: m_state(state)
|
||||||
|
, m_surface(surface)
|
||||||
|
, m_pos()
|
||||||
|
{
|
||||||
|
m_maxpos = surface->fullSize().width() * (surface->fullSize().height()) - 1;
|
||||||
|
if (state == State::END) {
|
||||||
|
m_pos = m_maxpos + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint CellIterator::gridPos() const
|
||||||
|
{
|
||||||
|
return m_surface->posToGrid(m_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CellIterator::updateChar()
|
||||||
|
{
|
||||||
|
QPoint cell = m_surface->posToGrid(m_pos);
|
||||||
|
m_char = m_surface->fetchCharAt(cell.x(), cell.y());
|
||||||
|
if (m_char == 0)
|
||||||
|
m_char = U' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
CellIterator &CellIterator::operator-=(int n)
|
||||||
|
{
|
||||||
|
if (n == 0)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
if (m_pos - n < 0)
|
||||||
|
throw new std::runtime_error("-= n too big!");
|
||||||
|
|
||||||
|
m_pos -= n;
|
||||||
|
updateChar();
|
||||||
|
|
||||||
|
m_state = State::INSIDE;
|
||||||
|
|
||||||
|
if (m_pos == 0) {
|
||||||
|
m_state = State::BEGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CellIterator &CellIterator::operator+=(int n)
|
||||||
|
{
|
||||||
|
if (n == 0)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
if (m_pos + n < m_maxpos) {
|
||||||
|
m_state = State::INSIDE;
|
||||||
|
m_pos += n;
|
||||||
|
updateChar();
|
||||||
|
} else {
|
||||||
|
*this = m_surface->end();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Terminal::Internal
|
||||||
95
src/plugins/terminal/celliterator.h
Normal file
95
src/plugins/terminal/celliterator.h
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <QPoint>
|
||||||
|
|
||||||
|
namespace Terminal::Internal {
|
||||||
|
|
||||||
|
class TerminalSurface;
|
||||||
|
|
||||||
|
class CellIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using iterator_category = std::bidirectional_iterator_tag;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using value_type = std::u32string::value_type;
|
||||||
|
using pointer = std::u32string::value_type *;
|
||||||
|
// We need to return copies for std::reverse_iterator to work
|
||||||
|
using reference = std::u32string::value_type;
|
||||||
|
|
||||||
|
enum class State { BEGIN, INSIDE, END } m_state{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
CellIterator(const TerminalSurface *surface, QPoint pos);
|
||||||
|
CellIterator(const TerminalSurface *surface, int pos);
|
||||||
|
CellIterator(const TerminalSurface *surface, State state);
|
||||||
|
|
||||||
|
public:
|
||||||
|
QPoint gridPos() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CellIterator &operator-=(int n);
|
||||||
|
CellIterator &operator+=(int n);
|
||||||
|
|
||||||
|
reference operator*() const { return m_char; }
|
||||||
|
pointer operator->() { return &m_char; }
|
||||||
|
|
||||||
|
CellIterator &operator++() { return *this += 1; }
|
||||||
|
CellIterator operator++(int)
|
||||||
|
{
|
||||||
|
CellIterator tmp = *this;
|
||||||
|
++(*this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
CellIterator &operator--() { return *this -= 1; }
|
||||||
|
CellIterator operator--(int)
|
||||||
|
{
|
||||||
|
CellIterator tmp = *this;
|
||||||
|
--(*this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const CellIterator &other) const
|
||||||
|
{
|
||||||
|
if (other.m_state != m_state)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (other.m_pos != m_pos)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const CellIterator &other) const { return !operator!=(other); }
|
||||||
|
|
||||||
|
CellIterator operator-(int n) const
|
||||||
|
{
|
||||||
|
CellIterator result = *this;
|
||||||
|
result -= n;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CellIterator operator+(int n) const
|
||||||
|
{
|
||||||
|
CellIterator result = *this;
|
||||||
|
result += n;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int position() const { return m_pos; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateChar();
|
||||||
|
|
||||||
|
const TerminalSurface *m_surface{nullptr};
|
||||||
|
int m_pos{-1};
|
||||||
|
int m_maxpos{-1};
|
||||||
|
mutable std::u32string::value_type m_char;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Terminal::Internal
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
// Copyright (C) 2022 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "celllayout.h"
|
|
||||||
|
|
||||||
#include <QFontMetrics>
|
|
||||||
|
|
||||||
namespace Terminal::Internal {
|
|
||||||
|
|
||||||
QColor toQColor(const VTermColor &c)
|
|
||||||
{
|
|
||||||
return QColor(qRgb(c.rgb.red, c.rgb.green, c.rgb.blue));
|
|
||||||
};
|
|
||||||
|
|
||||||
std::u32string cellToString(const VTermScreenCell &cell)
|
|
||||||
{
|
|
||||||
if (cell.chars[0] != 0xFFFFFFFF) {
|
|
||||||
QString ch = QString::fromUcs4(cell.chars);
|
|
||||||
if (ch.size() > 1)
|
|
||||||
ch = ch.normalized(QString::NormalizationForm_C);
|
|
||||||
QList<uint> asUcs4 = ch.toUcs4();
|
|
||||||
|
|
||||||
std::u32string chUcs = std::u32string(asUcs4.begin(), asUcs4.end());
|
|
||||||
if (chUcs.size() > 0) {
|
|
||||||
if (chUcs[0] == 0)
|
|
||||||
chUcs[0] = 0x00a0;
|
|
||||||
|
|
||||||
return chUcs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::u32string(1, (char32_t) 0x00a0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void createTextLayout(QTextLayout &textLayout,
|
|
||||||
std::u32string *resultText,
|
|
||||||
VTermColor defaultBg,
|
|
||||||
QRect cellRect,
|
|
||||||
qreal lineSpacing,
|
|
||||||
std::function<const VTermScreenCell *(int x, int y)> fetchCell)
|
|
||||||
{
|
|
||||||
QList<QTextLayout::FormatRange> formats;
|
|
||||||
|
|
||||||
QTextCharFormat currentFormat;
|
|
||||||
int currentFormatStart = 0;
|
|
||||||
currentFormat.setForeground(QColor(0xff, 0xff, 0xff));
|
|
||||||
currentFormat.clearBackground();
|
|
||||||
|
|
||||||
QString layoutText;
|
|
||||||
if (resultText)
|
|
||||||
resultText->clear();
|
|
||||||
|
|
||||||
for (int y = cellRect.y(); y < cellRect.bottom() + 1; y++) {
|
|
||||||
QTextCharFormat format;
|
|
||||||
|
|
||||||
const auto setNewFormat = [&formats, ¤tFormatStart, &layoutText, ¤tFormat](
|
|
||||||
const QTextCharFormat &format) {
|
|
||||||
if (layoutText.size() != currentFormatStart) {
|
|
||||||
QTextLayout::FormatRange fr;
|
|
||||||
fr.start = currentFormatStart;
|
|
||||||
fr.length = layoutText.size() - currentFormatStart;
|
|
||||||
fr.format = currentFormat;
|
|
||||||
formats.append(fr);
|
|
||||||
|
|
||||||
currentFormat = format;
|
|
||||||
currentFormatStart = layoutText.size();
|
|
||||||
} else {
|
|
||||||
currentFormat = format;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int x = cellRect.x(); x < cellRect.right() + 1; x++) {
|
|
||||||
const VTermScreenCell *cell = fetchCell(x, y);
|
|
||||||
|
|
||||||
const VTermColor *bg = &cell->bg;
|
|
||||||
const VTermColor *fg = &cell->fg;
|
|
||||||
|
|
||||||
if (static_cast<bool>(cell->attrs.reverse)) {
|
|
||||||
bg = &cell->fg;
|
|
||||||
fg = &cell->bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
format = QTextCharFormat();
|
|
||||||
format.setForeground(toQColor(*fg));
|
|
||||||
|
|
||||||
if (!vterm_color_is_equal(bg, &defaultBg))
|
|
||||||
format.setBackground(toQColor(*bg));
|
|
||||||
else
|
|
||||||
format.clearBackground();
|
|
||||||
|
|
||||||
if (cell->attrs.bold)
|
|
||||||
format.setFontWeight(QFont::Bold);
|
|
||||||
if (cell->attrs.underline)
|
|
||||||
format.setFontUnderline(true);
|
|
||||||
if (cell->attrs.italic)
|
|
||||||
format.setFontItalic(true);
|
|
||||||
if (cell->attrs.strike)
|
|
||||||
format.setFontStrikeOut(true);
|
|
||||||
|
|
||||||
if (format != currentFormat)
|
|
||||||
setNewFormat(format);
|
|
||||||
|
|
||||||
if (cell->chars[0] != 0xFFFFFFFF) {
|
|
||||||
QString ch = QString::fromUcs4(cell->chars);
|
|
||||||
if (ch == ' ')
|
|
||||||
ch = QChar::Nbsp;
|
|
||||||
if (ch.size() > 0) {
|
|
||||||
layoutText += ch;
|
|
||||||
} else {
|
|
||||||
layoutText += QChar::Nbsp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resultText)
|
|
||||||
*resultText += cellToString(*cell);
|
|
||||||
} // for x
|
|
||||||
setNewFormat(format);
|
|
||||||
if (y != cellRect.bottom())
|
|
||||||
layoutText.append(QChar::LineSeparator);
|
|
||||||
} // for y
|
|
||||||
|
|
||||||
QTextLayout::FormatRange fr;
|
|
||||||
fr.start = currentFormatStart;
|
|
||||||
fr.length = (layoutText.size() - 1) - currentFormatStart;
|
|
||||||
fr.format = currentFormat;
|
|
||||||
formats.append(fr);
|
|
||||||
|
|
||||||
textLayout.setText(layoutText);
|
|
||||||
textLayout.setFormats(formats);
|
|
||||||
|
|
||||||
qreal height = 0;
|
|
||||||
textLayout.beginLayout();
|
|
||||||
while (1) {
|
|
||||||
QTextLine line = textLayout.createLine();
|
|
||||||
if (!line.isValid())
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Just give it a number that is definitely larger than
|
|
||||||
// the number of columns in a line.
|
|
||||||
line.setNumColumns(std::numeric_limits<int>::max());
|
|
||||||
line.setPosition(QPointF(0, height));
|
|
||||||
height += lineSpacing;
|
|
||||||
}
|
|
||||||
textLayout.endLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Terminal::Internal
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
// Copyright (C) 2022 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vterm.h>
|
|
||||||
|
|
||||||
#include <QColor>
|
|
||||||
#include <QFont>
|
|
||||||
#include <QRect>
|
|
||||||
#include <QString>
|
|
||||||
#include <QTextLayout>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace Terminal::Internal {
|
|
||||||
|
|
||||||
QColor toQColor(const VTermColor &c);
|
|
||||||
|
|
||||||
void createTextLayout(QTextLayout &textLayout,
|
|
||||||
std::u32string *resultText,
|
|
||||||
VTermColor defaultBg,
|
|
||||||
QRect cellRect,
|
|
||||||
qreal lineSpacing,
|
|
||||||
std::function<const VTermScreenCell *(int x, int y)> fetchCell);
|
|
||||||
|
|
||||||
std::u32string cellToString(const VTermScreenCell &cell);
|
|
||||||
|
|
||||||
} // namespace Terminal::Internal
|
|
||||||
44
src/plugins/terminal/glyphcache.cpp
Normal file
44
src/plugins/terminal/glyphcache.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "glyphcache.h"
|
||||||
|
|
||||||
|
#include <QTextLayout>
|
||||||
|
|
||||||
|
namespace Terminal::Internal {
|
||||||
|
|
||||||
|
size_t qHash(const GlyphCacheKey &key, size_t seed = 0)
|
||||||
|
{
|
||||||
|
return qHash(key.font, seed) ^ qHash(key.text, seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
GlyphCache &GlyphCache::instance()
|
||||||
|
{
|
||||||
|
static GlyphCache cache(5000);
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QGlyphRun *GlyphCache::get(const QFont &font, const QString &text)
|
||||||
|
{
|
||||||
|
GlyphCacheKey key{font, text};
|
||||||
|
if (auto *run = object(key))
|
||||||
|
return run;
|
||||||
|
|
||||||
|
QTextLayout layout;
|
||||||
|
|
||||||
|
layout.setText(text);
|
||||||
|
layout.setFont(font);
|
||||||
|
|
||||||
|
layout.beginLayout();
|
||||||
|
layout.createLine().setNumColumns(std::numeric_limits<int>::max());
|
||||||
|
layout.endLayout();
|
||||||
|
|
||||||
|
if (layout.lineCount() > 0) {
|
||||||
|
QGlyphRun *run = new QGlyphRun(layout.lineAt(0).glyphRuns().first());
|
||||||
|
insert(key, run);
|
||||||
|
return run;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Terminal::Internal
|
||||||
34
src/plugins/terminal/glyphcache.h
Normal file
34
src/plugins/terminal/glyphcache.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QCache>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QGlyphRun>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace Terminal::Internal {
|
||||||
|
|
||||||
|
struct GlyphCacheKey
|
||||||
|
{
|
||||||
|
QFont font;
|
||||||
|
QString text;
|
||||||
|
|
||||||
|
bool operator==(const GlyphCacheKey &other) const
|
||||||
|
{
|
||||||
|
return font == other.font && text == other.text;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GlyphCache : public QCache<GlyphCacheKey, QGlyphRun>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using QCache::QCache;
|
||||||
|
|
||||||
|
static GlyphCache &instance();
|
||||||
|
|
||||||
|
const QGlyphRun *get(const QFont &font, const QString &text);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Terminal::Internal
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
#include "scrollback.h"
|
#include "scrollback.h"
|
||||||
#include "celllayout.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -15,15 +14,6 @@ Scrollback::Line::Line(int cols, const VTermScreenCell *cells, VTermState *vts)
|
|||||||
: m_cols(cols)
|
: m_cols(cols)
|
||||||
, m_cells(std::make_unique<VTermScreenCell[]>(cols))
|
, m_cells(std::make_unique<VTermScreenCell[]>(cols))
|
||||||
{
|
{
|
||||||
m_textFuture = std::async(std::launch::async, [this, cols] {
|
|
||||||
std::u32string text;
|
|
||||||
text.reserve(cols);
|
|
||||||
for (int i = 0; i < cols; ++i) {
|
|
||||||
text += cellToString(m_cells[i]);
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
});
|
|
||||||
|
|
||||||
memcpy(m_cells.get(), cells, cols * sizeof(cells[0]));
|
memcpy(m_cells.get(), cells, cols * sizeof(cells[0]));
|
||||||
for (int i = 0; i < cols; ++i) {
|
for (int i = 0; i < cols; ++i) {
|
||||||
vterm_state_convert_color_to_rgb(vts, &m_cells[i].fg);
|
vterm_state_convert_color_to_rgb(vts, &m_cells[i].fg);
|
||||||
@@ -37,34 +27,6 @@ const VTermScreenCell *Scrollback::Line::cell(int i) const
|
|||||||
return &m_cells[i];
|
return &m_cells[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
const QTextLayout &Scrollback::Line::layout(int version, const QFont &font, qreal lineSpacing) const
|
|
||||||
{
|
|
||||||
if (!m_layout)
|
|
||||||
m_layout = std::make_unique<QTextLayout>();
|
|
||||||
|
|
||||||
if (m_layoutVersion != version) {
|
|
||||||
VTermColor defaultBg;
|
|
||||||
defaultBg.type = VTERM_COLOR_DEFAULT_BG;
|
|
||||||
m_layout->clearLayout();
|
|
||||||
m_layout->setFont(font);
|
|
||||||
createTextLayout(*m_layout,
|
|
||||||
nullptr,
|
|
||||||
defaultBg,
|
|
||||||
QRect(0, 0, m_cols, 1),
|
|
||||||
lineSpacing,
|
|
||||||
[this](int x, int) { return &m_cells[x]; });
|
|
||||||
m_layoutVersion = version;
|
|
||||||
}
|
|
||||||
return *m_layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::u32string &Scrollback::Line::text() const
|
|
||||||
{
|
|
||||||
if (!m_text)
|
|
||||||
m_text = m_textFuture.get();
|
|
||||||
return *m_text;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scrollback::Scrollback(size_t capacity)
|
Scrollback::Scrollback(size_t capacity)
|
||||||
: m_capacity(capacity)
|
: m_capacity(capacity)
|
||||||
{}
|
{}
|
||||||
@@ -95,26 +57,9 @@ void Scrollback::popto(int cols, VTermScreenCell *cells)
|
|||||||
m_deque.pop_front();
|
m_deque.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Scrollback::scroll(int delta)
|
|
||||||
{
|
|
||||||
m_offset = std::min(std::max(0, static_cast<int>(m_offset) + delta),
|
|
||||||
static_cast<int>(m_deque.size()));
|
|
||||||
return m_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scrollback::clear()
|
void Scrollback::clear()
|
||||||
{
|
{
|
||||||
m_offset = 0;
|
|
||||||
m_deque.clear();
|
m_deque.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u32string Scrollback::currentText()
|
|
||||||
{
|
|
||||||
std::u32string currentText;
|
|
||||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
|
||||||
currentText += it->text();
|
|
||||||
}
|
|
||||||
return currentText;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Terminal::Internal
|
} // namespace Terminal::Internal
|
||||||
|
|||||||
@@ -29,25 +29,17 @@ public:
|
|||||||
const VTermScreenCell *cell(int i) const;
|
const VTermScreenCell *cell(int i) const;
|
||||||
const VTermScreenCell *cells() const { return &m_cells[0]; };
|
const VTermScreenCell *cells() const { return &m_cells[0]; };
|
||||||
|
|
||||||
const QTextLayout &layout(int version, const QFont &font, qreal lineSpacing) const;
|
|
||||||
const std::u32string &text() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_cols;
|
int m_cols;
|
||||||
std::unique_ptr<VTermScreenCell[]> m_cells;
|
std::unique_ptr<VTermScreenCell[]> m_cells;
|
||||||
mutable std::unique_ptr<QTextLayout> m_layout;
|
|
||||||
mutable int m_layoutVersion{-1};
|
|
||||||
mutable std::optional<std::u32string> m_text;
|
|
||||||
mutable std::future<std::u32string> m_textFuture;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Scrollback(size_t capacity);
|
Scrollback(size_t capacity);
|
||||||
Scrollback() = delete;
|
Scrollback() = delete;
|
||||||
|
|
||||||
size_t capacity() const { return m_capacity; };
|
int capacity() const { return m_capacity; };
|
||||||
size_t size() const { return m_deque.size(); };
|
int size() const { return static_cast<int>(m_deque.size()); };
|
||||||
size_t offset() const { return m_offset; };
|
|
||||||
|
|
||||||
const Line &line(size_t index) const { return m_deque.at(index); };
|
const Line &line(size_t index) const { return m_deque.at(index); };
|
||||||
const std::deque<Line> &lines() const { return m_deque; };
|
const std::deque<Line> &lines() const { return m_deque; };
|
||||||
@@ -56,16 +48,11 @@ public:
|
|||||||
const VTermScreenCell *cells,
|
const VTermScreenCell *cells,
|
||||||
VTermState *vts);
|
VTermState *vts);
|
||||||
void popto(int cols, VTermScreenCell *cells);
|
void popto(int cols, VTermScreenCell *cells);
|
||||||
size_t scroll(int delta);
|
|
||||||
void unscroll() { m_offset = 0; };
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
std::u32string currentText();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t m_capacity;
|
size_t m_capacity;
|
||||||
size_t m_offset{0};
|
|
||||||
std::deque<Line> m_deque;
|
std::deque<Line> m_deque;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
504
src/plugins/terminal/terminalsurface.cpp
Normal file
504
src/plugins/terminal/terminalsurface.cpp
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "terminalsurface.h"
|
||||||
|
|
||||||
|
#include "keys.h"
|
||||||
|
#include "scrollback.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <vterm.h>
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(log, "qtc.terminal.surface", QtWarningMsg);
|
||||||
|
|
||||||
|
namespace Terminal::Internal {
|
||||||
|
|
||||||
|
QColor toQColor(const VTermColor &c)
|
||||||
|
{
|
||||||
|
return QColor(qRgb(c.rgb.red, c.rgb.green, c.rgb.blue));
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TerminalSurfacePrivate
|
||||||
|
{
|
||||||
|
TerminalSurfacePrivate(TerminalSurface *surface, const QSize &initialGridSize)
|
||||||
|
: m_vterm(vterm_new(initialGridSize.height(), initialGridSize.width()), vterm_free)
|
||||||
|
, m_vtermScreen(vterm_obtain_screen(m_vterm.get()))
|
||||||
|
, m_scrollback(std::make_unique<Internal::Scrollback>(5000))
|
||||||
|
, q(surface)
|
||||||
|
{
|
||||||
|
vterm_set_utf8(m_vterm.get(), true);
|
||||||
|
|
||||||
|
static auto writeToPty = [](const char *s, size_t len, void *user) {
|
||||||
|
auto p = static_cast<TerminalSurfacePrivate *>(user);
|
||||||
|
emit p->q->writeToPty(QByteArray(s, static_cast<int>(len)));
|
||||||
|
};
|
||||||
|
|
||||||
|
vterm_output_set_callback(m_vterm.get(), writeToPty, this);
|
||||||
|
|
||||||
|
memset(&m_vtermScreenCallbacks, 0, sizeof(m_vtermScreenCallbacks));
|
||||||
|
|
||||||
|
m_vtermScreenCallbacks.damage = [](VTermRect rect, void *user) {
|
||||||
|
auto p = static_cast<TerminalSurfacePrivate *>(user);
|
||||||
|
rect.start_row += p->m_scrollback->size();
|
||||||
|
rect.end_row += p->m_scrollback->size();
|
||||||
|
p->invalidate(rect);
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
m_vtermScreenCallbacks.sb_pushline = [](int cols, const VTermScreenCell *cells, void *user) {
|
||||||
|
auto p = static_cast<TerminalSurfacePrivate *>(user);
|
||||||
|
return p->sb_pushline(cols, cells);
|
||||||
|
};
|
||||||
|
m_vtermScreenCallbacks.sb_popline = [](int cols, VTermScreenCell *cells, void *user) {
|
||||||
|
auto p = static_cast<TerminalSurfacePrivate *>(user);
|
||||||
|
return p->sb_popline(cols, cells);
|
||||||
|
};
|
||||||
|
m_vtermScreenCallbacks.settermprop = [](VTermProp prop, VTermValue *val, void *user) {
|
||||||
|
auto p = static_cast<TerminalSurfacePrivate *>(user);
|
||||||
|
return p->setTerminalProperties(prop, val);
|
||||||
|
};
|
||||||
|
m_vtermScreenCallbacks.movecursor =
|
||||||
|
[](VTermPos pos, VTermPos oldpos, int visible, void *user) {
|
||||||
|
auto p = static_cast<TerminalSurfacePrivate *>(user);
|
||||||
|
return p->movecursor(pos, oldpos, visible);
|
||||||
|
};
|
||||||
|
|
||||||
|
m_vtermScreenCallbacks.sb_clear = [](void *user) {
|
||||||
|
auto p = static_cast<TerminalSurfacePrivate *>(user);
|
||||||
|
return p->sb_clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
vterm_screen_set_callbacks(m_vtermScreen, &m_vtermScreenCallbacks, this);
|
||||||
|
vterm_screen_set_damage_merge(m_vtermScreen, VTERM_DAMAGE_SCROLL);
|
||||||
|
vterm_screen_enable_altscreen(m_vtermScreen, true);
|
||||||
|
|
||||||
|
VTermState *vts = vterm_obtain_state(m_vterm.get());
|
||||||
|
vterm_state_set_bold_highbright(vts, true);
|
||||||
|
|
||||||
|
vterm_screen_reset(m_vtermScreen, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize liveSize() const
|
||||||
|
{
|
||||||
|
int rows;
|
||||||
|
int cols;
|
||||||
|
vterm_get_size(m_vterm.get(), &rows, &cols);
|
||||||
|
|
||||||
|
return QSize(cols, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalCell toCell(const VTermScreenCell &cell)
|
||||||
|
{
|
||||||
|
TerminalCell result;
|
||||||
|
result.width = cell.width;
|
||||||
|
result.text = QString::fromUcs4(cell.chars);
|
||||||
|
|
||||||
|
const VTermColor *bg = &cell.bg;
|
||||||
|
const VTermColor *fg = &cell.fg;
|
||||||
|
|
||||||
|
if (static_cast<bool>(cell.attrs.reverse))
|
||||||
|
std::swap(fg, bg);
|
||||||
|
|
||||||
|
const QColor cellBgColor = toQColor(*bg);
|
||||||
|
const QColor cellFgColor = toQColor(*fg);
|
||||||
|
|
||||||
|
if (cellBgColor != m_defaultBgColor)
|
||||||
|
result.background = toQColor(*bg);
|
||||||
|
|
||||||
|
result.foreground = cellFgColor;
|
||||||
|
|
||||||
|
result.bold = cell.attrs.bold;
|
||||||
|
result.strikeOut = cell.attrs.strike;
|
||||||
|
|
||||||
|
if (cell.attrs.underline > 0) {
|
||||||
|
result.underlineStyle = QTextCharFormat::NoUnderline;
|
||||||
|
switch (cell.attrs.underline) {
|
||||||
|
case VTERM_UNDERLINE_SINGLE:
|
||||||
|
result.underlineStyle = QTextCharFormat::SingleUnderline;
|
||||||
|
break;
|
||||||
|
case VTERM_UNDERLINE_DOUBLE:
|
||||||
|
// TODO: Double underline
|
||||||
|
result.underlineStyle = QTextCharFormat::SingleUnderline;
|
||||||
|
break;
|
||||||
|
case VTERM_UNDERLINE_CURLY:
|
||||||
|
result.underlineStyle = QTextCharFormat::WaveUnderline;
|
||||||
|
break;
|
||||||
|
case VTERM_UNDERLINE_DASHED:
|
||||||
|
result.underlineStyle = QTextCharFormat::DashUnderline;
|
||||||
|
break;
|
||||||
|
case VTERM_UNDERLINE_DOTTED:
|
||||||
|
result.underlineStyle = QTextCharFormat::DotLine;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.strikeOut = cell.attrs.strike;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
VTermColor defaultBgColor() const
|
||||||
|
{
|
||||||
|
VTermColor defaultBg;
|
||||||
|
if (!m_altscreen) {
|
||||||
|
VTermColor defaultFg;
|
||||||
|
vterm_state_get_default_colors(vterm_obtain_state(m_vterm.get()),
|
||||||
|
&defaultFg,
|
||||||
|
&defaultBg);
|
||||||
|
// We want to compare the cell bg against this later and cells don't
|
||||||
|
// set DEFAULT_BG
|
||||||
|
defaultBg.type = VTERM_COLOR_RGB;
|
||||||
|
return defaultBg;
|
||||||
|
} // This is a slightly better guess when in an altscreen
|
||||||
|
|
||||||
|
VTermPos vtp{0, 0};
|
||||||
|
static VTermScreenCell refCell{};
|
||||||
|
vterm_screen_get_cell(m_vtermScreen, vtp, &refCell);
|
||||||
|
return refCell.bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callbacks from vterm
|
||||||
|
void invalidate(VTermRect rect)
|
||||||
|
{
|
||||||
|
emit q->invalidated(
|
||||||
|
QRect{QPoint{rect.start_col, rect.start_row}, QPoint{rect.end_col, rect.end_row - 1}});
|
||||||
|
}
|
||||||
|
|
||||||
|
int sb_pushline(int cols, const VTermScreenCell *cells)
|
||||||
|
{
|
||||||
|
m_scrollback->emplace(cols, cells, vterm_obtain_state(m_vterm.get()));
|
||||||
|
emit q->fullSizeChanged(q->fullSize());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sb_popline(int cols, VTermScreenCell *cells)
|
||||||
|
{
|
||||||
|
if (m_scrollback->size() == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
m_scrollback->popto(cols, cells);
|
||||||
|
emit q->fullSizeChanged(q->fullSize());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sb_clear()
|
||||||
|
{
|
||||||
|
m_scrollback->clear();
|
||||||
|
emit q->fullSizeChanged(q->fullSize());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int setTerminalProperties(VTermProp prop, VTermValue *val)
|
||||||
|
{
|
||||||
|
switch (prop) {
|
||||||
|
case VTERM_PROP_CURSORVISIBLE:
|
||||||
|
m_cursor.visible = val->boolean;
|
||||||
|
break;
|
||||||
|
case VTERM_PROP_CURSORBLINK:
|
||||||
|
qCDebug(log) << "Ignoring VTERM_PROP_CURSORBLINK" << val->boolean;
|
||||||
|
break;
|
||||||
|
case VTERM_PROP_CURSORSHAPE:
|
||||||
|
qCDebug(log) << "Ignoring VTERM_PROP_CURSORSHAPE" << val->number;
|
||||||
|
break;
|
||||||
|
case VTERM_PROP_ICONNAME:
|
||||||
|
//emit iconTextChanged(val->string);
|
||||||
|
break;
|
||||||
|
case VTERM_PROP_TITLE:
|
||||||
|
break;
|
||||||
|
case VTERM_PROP_ALTSCREEN:
|
||||||
|
m_altscreen = val->boolean;
|
||||||
|
emit q->altscreenChanged(m_altscreen);
|
||||||
|
//setSelection(std::nullopt);
|
||||||
|
break;
|
||||||
|
case VTERM_PROP_MOUSE:
|
||||||
|
qCDebug(log) << "Ignoring VTERM_PROP_MOUSE" << val->number;
|
||||||
|
break;
|
||||||
|
case VTERM_PROP_REVERSE:
|
||||||
|
qCDebug(log) << "Ignoring VTERM_PROP_REVERSE" << val->boolean;
|
||||||
|
break;
|
||||||
|
case VTERM_N_PROPS:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int movecursor(VTermPos pos, VTermPos oldpos, int visible)
|
||||||
|
{
|
||||||
|
Q_UNUSED(oldpos);
|
||||||
|
Cursor oldCursor = q->cursor();
|
||||||
|
m_cursor = {{pos.col, pos.row}, visible > 0};
|
||||||
|
q->cursorChanged(oldCursor, q->cursor());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VTermScreenCell *cellAt(int x, int y)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(y >= 0 && x >= 0, return nullptr);
|
||||||
|
QTC_ASSERT(y < liveSize().height() + m_scrollback->size() && x < liveSize().width(),
|
||||||
|
return nullptr);
|
||||||
|
if (y < m_scrollback->size()) {
|
||||||
|
const auto &sbl = m_scrollback->line((m_scrollback->size() - 1) - y);
|
||||||
|
if (x < sbl.cols()) {
|
||||||
|
return sbl.cell(x);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
y -= m_scrollback->size();
|
||||||
|
|
||||||
|
static VTermScreenCell refCell{};
|
||||||
|
VTermPos vtp{y, x};
|
||||||
|
vterm_screen_get_cell(m_vtermScreen, vtp, &refCell);
|
||||||
|
vterm_screen_convert_color_to_rgb(m_vtermScreen, &refCell.fg);
|
||||||
|
vterm_screen_convert_color_to_rgb(m_vtermScreen, &refCell.bg);
|
||||||
|
|
||||||
|
return &refCell;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<VTerm, void (*)(VTerm *)> m_vterm;
|
||||||
|
VTermScreen *m_vtermScreen;
|
||||||
|
VTermScreenCallbacks m_vtermScreenCallbacks;
|
||||||
|
|
||||||
|
QColor m_defaultBgColor;
|
||||||
|
Cursor m_cursor;
|
||||||
|
|
||||||
|
bool m_altscreen{false};
|
||||||
|
|
||||||
|
std::unique_ptr<Internal::Scrollback> m_scrollback;
|
||||||
|
|
||||||
|
TerminalSurface *q;
|
||||||
|
};
|
||||||
|
|
||||||
|
TerminalSurface::TerminalSurface(QSize initialGridSize)
|
||||||
|
: d(std::make_unique<TerminalSurfacePrivate>(this, initialGridSize))
|
||||||
|
{}
|
||||||
|
|
||||||
|
TerminalSurface::~TerminalSurface() = default;
|
||||||
|
|
||||||
|
int TerminalSurface::cellWidthAt(int x, int y) const
|
||||||
|
{
|
||||||
|
const VTermScreenCell *cell = d->cellAt(x, y);
|
||||||
|
if (!cell)
|
||||||
|
return 0;
|
||||||
|
return cell->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize TerminalSurface::liveSize() const
|
||||||
|
{
|
||||||
|
return d->liveSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize TerminalSurface::fullSize() const
|
||||||
|
{
|
||||||
|
return QSize{d->liveSize().width(), d->liveSize().height() + d->m_scrollback->size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u32string::value_type TerminalSurface::fetchCharAt(int x, int y) const
|
||||||
|
{
|
||||||
|
const VTermScreenCell *cell = d->cellAt(x, y);
|
||||||
|
if (!cell)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (cell->width == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
QString s = QString::fromUcs4(cell->chars, 6).normalized(QString::NormalizationForm_C);
|
||||||
|
const QList<uint> ucs4 = s.toUcs4();
|
||||||
|
return std::u32string(ucs4.begin(), ucs4.end()).front();
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalCell TerminalSurface::fetchCell(int x, int y) const
|
||||||
|
{
|
||||||
|
static TerminalCell emptyCell{1, {}, {}};
|
||||||
|
|
||||||
|
QTC_ASSERT(y >= 0, return emptyCell);
|
||||||
|
QTC_ASSERT(y < d->liveSize().height() + d->m_scrollback->size(), return emptyCell);
|
||||||
|
|
||||||
|
const VTermScreenCell *refCell = d->cellAt(x, y);
|
||||||
|
if (!refCell)
|
||||||
|
return emptyCell;
|
||||||
|
|
||||||
|
return d->toCell(*refCell);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSurface::clearAll()
|
||||||
|
{
|
||||||
|
// Fake a scrollback clearing
|
||||||
|
QByteArray data{"\x1b[3J"};
|
||||||
|
vterm_input_write(d->m_vterm.get(), data.constData(), data.size());
|
||||||
|
|
||||||
|
// Send Ctrl+L which will clear the screen
|
||||||
|
emit writeToPty(QByteArray("\f"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSurface::resize(QSize newSize)
|
||||||
|
{
|
||||||
|
vterm_set_size(d->m_vterm.get(), newSize.height(), newSize.width());
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint TerminalSurface::posToGrid(int pos) const
|
||||||
|
{
|
||||||
|
return {pos % d->liveSize().width(), pos / d->liveSize().width()};
|
||||||
|
}
|
||||||
|
int TerminalSurface::gridToPos(QPoint gridPos) const
|
||||||
|
{
|
||||||
|
return gridPos.y() * d->liveSize().width() + gridPos.x();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSurface::dataFromPty(const QByteArray &data)
|
||||||
|
{
|
||||||
|
vterm_input_write(d->m_vterm.get(), data.constData(), data.size());
|
||||||
|
vterm_screen_flush_damage(d->m_vtermScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSurface::flush()
|
||||||
|
{
|
||||||
|
vterm_screen_flush_damage(d->m_vtermScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSurface::setColors(QColor foreground, QColor background)
|
||||||
|
{
|
||||||
|
VTermState *vts = vterm_obtain_state(d->m_vterm.get());
|
||||||
|
|
||||||
|
VTermColor fg;
|
||||||
|
VTermColor bg;
|
||||||
|
|
||||||
|
vterm_color_rgb(&fg, foreground.red(), foreground.green(), foreground.blue());
|
||||||
|
vterm_color_rgb(&bg, background.red(), background.green(), background.blue());
|
||||||
|
|
||||||
|
d->m_defaultBgColor = background;
|
||||||
|
|
||||||
|
vterm_state_set_default_colors(vts, &fg, &bg);
|
||||||
|
vterm_screen_reset(d->m_vtermScreen, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSurface::setAnsiColor(int index, QColor color)
|
||||||
|
{
|
||||||
|
VTermState *vts = vterm_obtain_state(d->m_vterm.get());
|
||||||
|
|
||||||
|
VTermColor col;
|
||||||
|
vterm_color_rgb(&col, color.red(), color.green(), color.blue());
|
||||||
|
vterm_state_set_palette_color(vts, index, &col);
|
||||||
|
|
||||||
|
vterm_screen_reset(d->m_vtermScreen, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSurface::pasteFromClipboard(const QString &clipboardText)
|
||||||
|
{
|
||||||
|
if (clipboardText.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
vterm_keyboard_start_paste(d->m_vterm.get());
|
||||||
|
for (unsigned int ch : clipboardText.toUcs4())
|
||||||
|
vterm_keyboard_unichar(d->m_vterm.get(), ch, VTERM_MOD_NONE);
|
||||||
|
vterm_keyboard_end_paste(d->m_vterm.get());
|
||||||
|
|
||||||
|
if (!d->m_altscreen) {
|
||||||
|
emit unscroll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSurface::sendKey(Qt::Key key)
|
||||||
|
{
|
||||||
|
if (key == Qt::Key_Escape)
|
||||||
|
vterm_keyboard_key(d->m_vterm.get(), VTERM_KEY_ESCAPE, VTERM_MOD_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSurface::sendKey(const QString &text)
|
||||||
|
{
|
||||||
|
for (const unsigned int ch : text.toUcs4())
|
||||||
|
vterm_keyboard_unichar(d->m_vterm.get(), ch, VTERM_MOD_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalSurface::sendKey(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
bool keypad = event->modifiers() & Qt::KeypadModifier;
|
||||||
|
VTermModifier mod = Internal::qtModifierToVTerm(event->modifiers());
|
||||||
|
VTermKey key = Internal::qtKeyToVTerm(Qt::Key(event->key()), keypad);
|
||||||
|
|
||||||
|
if (key != VTERM_KEY_NONE) {
|
||||||
|
if (mod == VTERM_MOD_SHIFT && (key == VTERM_KEY_ESCAPE || key == VTERM_KEY_BACKSPACE))
|
||||||
|
mod = VTERM_MOD_NONE;
|
||||||
|
|
||||||
|
vterm_keyboard_key(d->m_vterm.get(), key, mod);
|
||||||
|
} else if (event->text().length() == 1) {
|
||||||
|
// This maps to delete word and is way to easy to mistakenly type
|
||||||
|
// if (event->key() == Qt::Key_Space && mod == VTERM_MOD_SHIFT)
|
||||||
|
// mod = VTERM_MOD_NONE;
|
||||||
|
|
||||||
|
// Per https://github.com/justinmk/neovim/commit/317d5ca7b0f92ef42de989b3556ca9503f0a3bf6
|
||||||
|
// libvterm prefers we send the full keycode rather than sending the
|
||||||
|
// ctrl modifier. This helps with ncurses applications which otherwise
|
||||||
|
// do not recognize ctrl+<key> and in the shell for getting common control characters
|
||||||
|
// like ctrl+i for tab or ctrl+j for newline.
|
||||||
|
|
||||||
|
// Workaround for "ALT+SHIFT+/" (\ on german mac keyboards)
|
||||||
|
if (mod == (VTERM_MOD_SHIFT | VTERM_MOD_ALT) && event->key() == Qt::Key_Slash) {
|
||||||
|
mod = VTERM_MOD_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
vterm_keyboard_unichar(d->m_vterm.get(), event->text().toUcs4()[0], VTERM_MOD_NONE);
|
||||||
|
|
||||||
|
// TODO: ??
|
||||||
|
//setSelection(std::nullopt);
|
||||||
|
} else if (mod == VTERM_MOD_CTRL && event->key() >= Qt::Key_A && event->key() < Qt::Key_Z) {
|
||||||
|
vterm_keyboard_unichar(d->m_vterm.get(), 'a' + (event->key() - Qt::Key_A), mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor TerminalSurface::cursor() const
|
||||||
|
{
|
||||||
|
Cursor cursor = d->m_cursor;
|
||||||
|
cursor.position.setY(cursor.position.y() + d->m_scrollback->size());
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor TerminalSurface::defaultBgColor() const
|
||||||
|
{
|
||||||
|
return toQColor(d->defaultBgColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
CellIterator TerminalSurface::begin() const
|
||||||
|
{
|
||||||
|
auto res = CellIterator(this, {0, 0});
|
||||||
|
res.m_state = CellIterator::State::BEGIN;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
CellIterator TerminalSurface::end() const
|
||||||
|
{
|
||||||
|
return CellIterator(this, CellIterator::State::END);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::reverse_iterator<CellIterator> TerminalSurface::rbegin() const
|
||||||
|
{
|
||||||
|
return std::make_reverse_iterator(end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::reverse_iterator<CellIterator> TerminalSurface::rend() const
|
||||||
|
{
|
||||||
|
return std::make_reverse_iterator(begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
CellIterator TerminalSurface::iteratorAt(QPoint pos) const
|
||||||
|
{
|
||||||
|
return CellIterator(this, pos);
|
||||||
|
}
|
||||||
|
CellIterator TerminalSurface::iteratorAt(int pos) const
|
||||||
|
{
|
||||||
|
return CellIterator(this, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::reverse_iterator<CellIterator> TerminalSurface::rIteratorAt(QPoint pos) const
|
||||||
|
{
|
||||||
|
return std::make_reverse_iterator(iteratorAt(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::reverse_iterator<CellIterator> TerminalSurface::rIteratorAt(int pos) const
|
||||||
|
{
|
||||||
|
return std::make_reverse_iterator(iteratorAt(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Terminal::Internal
|
||||||
103
src/plugins/terminal/terminalsurface.h
Normal file
103
src/plugins/terminal/terminalsurface.h
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "celliterator.h"
|
||||||
|
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QTextCharFormat>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Terminal::Internal {
|
||||||
|
|
||||||
|
class Scrollback;
|
||||||
|
|
||||||
|
struct TerminalSurfacePrivate;
|
||||||
|
|
||||||
|
struct TerminalCell
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
QString text;
|
||||||
|
bool bold{false};
|
||||||
|
bool italic{false};
|
||||||
|
QColor foreground;
|
||||||
|
std::optional<QColor> background;
|
||||||
|
QTextCharFormat::UnderlineStyle underlineStyle{QTextCharFormat::NoUnderline};
|
||||||
|
bool strikeOut{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cursor
|
||||||
|
{
|
||||||
|
QPoint position;
|
||||||
|
bool visible;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TerminalSurface : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TerminalSurface(QSize initialGridSize);
|
||||||
|
~TerminalSurface();
|
||||||
|
|
||||||
|
public:
|
||||||
|
CellIterator begin() const;
|
||||||
|
CellIterator end() const;
|
||||||
|
std::reverse_iterator<CellIterator> rbegin() const;
|
||||||
|
std::reverse_iterator<CellIterator> rend() const;
|
||||||
|
|
||||||
|
CellIterator iteratorAt(QPoint pos) const;
|
||||||
|
CellIterator iteratorAt(int pos) const;
|
||||||
|
|
||||||
|
std::reverse_iterator<CellIterator> rIteratorAt(QPoint pos) const;
|
||||||
|
std::reverse_iterator<CellIterator> rIteratorAt(int pos) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void clearAll();
|
||||||
|
|
||||||
|
void resize(QSize newSize);
|
||||||
|
|
||||||
|
TerminalCell fetchCell(int x, int y) const;
|
||||||
|
std::u32string::value_type fetchCharAt(int x, int y) const;
|
||||||
|
int cellWidthAt(int x, int y) const;
|
||||||
|
|
||||||
|
QSize liveSize() const;
|
||||||
|
QSize fullSize() const;
|
||||||
|
|
||||||
|
QPoint posToGrid(int pos) const;
|
||||||
|
int gridToPos(QPoint gridPos) const;
|
||||||
|
|
||||||
|
void dataFromPty(const QByteArray &data);
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
void setColors(QColor foreground, QColor background);
|
||||||
|
void setAnsiColor(int index, QColor color);
|
||||||
|
|
||||||
|
void pasteFromClipboard(const QString &text);
|
||||||
|
|
||||||
|
void sendKey(Qt::Key key);
|
||||||
|
void sendKey(QKeyEvent *event);
|
||||||
|
void sendKey(const QString &text);
|
||||||
|
|
||||||
|
int invertedScrollOffset() const;
|
||||||
|
|
||||||
|
Cursor cursor() const;
|
||||||
|
|
||||||
|
QColor defaultBgColor() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void writeToPty(const QByteArray &data);
|
||||||
|
void invalidated(QRect grid);
|
||||||
|
void fullSizeChanged(QSize newSize);
|
||||||
|
void cursorChanged(Cursor oldCursor, Cursor newCursor);
|
||||||
|
void altscreenChanged(bool altScreen);
|
||||||
|
void unscroll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<TerminalSurfacePrivate> d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Terminal::Internal
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "scrollback.h"
|
#include "terminalsurface.h"
|
||||||
|
|
||||||
#include <utils/qtcprocess.h>
|
#include <utils/qtcprocess.h>
|
||||||
#include <utils/terminalhooks.h>
|
#include <utils/terminalhooks.h>
|
||||||
@@ -13,8 +13,6 @@
|
|||||||
#include <QTextLayout>
|
#include <QTextLayout>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include <vterm.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ namespace Terminal {
|
|||||||
|
|
||||||
class TerminalWidget : public QAbstractScrollArea
|
class TerminalWidget : public QAbstractScrollArea
|
||||||
{
|
{
|
||||||
|
friend class CellIterator;
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
TerminalWidget(QWidget *parent = nullptr,
|
TerminalWidget(QWidget *parent = nullptr,
|
||||||
@@ -49,8 +48,23 @@ public:
|
|||||||
|
|
||||||
struct Selection
|
struct Selection
|
||||||
{
|
{
|
||||||
QPoint start;
|
int start;
|
||||||
QPoint end;
|
int end;
|
||||||
|
|
||||||
|
bool operator!=(const Selection &other) const
|
||||||
|
{
|
||||||
|
return start != other.start || end != other.end;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LinkSelection : public Selection
|
||||||
|
{
|
||||||
|
Utils::FilePath filePath;
|
||||||
|
|
||||||
|
bool operator!=(const LinkSelection &other) const
|
||||||
|
{
|
||||||
|
return filePath != other.filePath || Selection::operator!=(other);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
QString shellName() const;
|
QString shellName() const;
|
||||||
@@ -80,7 +94,7 @@ protected:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onReadyRead(bool forceFlush);
|
void onReadyRead(bool forceFlush);
|
||||||
void setupVTerm();
|
void setupSurface();
|
||||||
void setupFont();
|
void setupFont();
|
||||||
void setupPty();
|
void setupPty();
|
||||||
void setupColors();
|
void setupColors();
|
||||||
@@ -88,23 +102,27 @@ protected:
|
|||||||
|
|
||||||
void writeToPty(const QByteArray &data);
|
void writeToPty(const QByteArray &data);
|
||||||
|
|
||||||
void createTextLayout();
|
int paintCell(QPainter &p,
|
||||||
|
const QRectF &cellRect,
|
||||||
// Callbacks from vterm
|
QPoint gridPos,
|
||||||
void invalidate(VTermRect rect);
|
const Internal::TerminalCell &cell,
|
||||||
int sb_pushline(int cols, const VTermScreenCell *cells);
|
QFont &f) const;
|
||||||
int sb_popline(int cols, VTermScreenCell *cells);
|
void paintCells(QPainter &painter, QPaintEvent *event) const;
|
||||||
int sb_clear();
|
void paintCursor(QPainter &painter) const;
|
||||||
int setTerminalProperties(VTermProp prop, VTermValue *val);
|
void paintPreedit(QPainter &painter) const;
|
||||||
int movecursor(VTermPos pos, VTermPos oldpos, int visible);
|
void paintSelection(QPainter &painter) const;
|
||||||
|
void paintDebugSelection(QPainter &painter, const Selection &selection) const;
|
||||||
const VTermScreenCell *fetchCell(int x, int y) const;
|
|
||||||
|
|
||||||
qreal topMargin() const;
|
qreal topMargin() const;
|
||||||
|
|
||||||
QPoint viewportToGlobal(QPoint p) const;
|
QPoint viewportToGlobal(QPoint p) const;
|
||||||
QPoint globalToViewport(QPoint p) const;
|
QPoint globalToViewport(QPoint p) const;
|
||||||
QPoint globalToGrid(QPoint p) const;
|
QPoint globalToGrid(QPointF p) const;
|
||||||
|
QPointF gridToGlobal(QPoint p, bool bottom = false, bool right = false) const;
|
||||||
|
QRect gridToViewport(QRect rect) const;
|
||||||
|
|
||||||
|
void updateViewport();
|
||||||
|
void updateViewport(const QRect &rect);
|
||||||
|
|
||||||
int textLineFromPixel(int y) const;
|
int textLineFromPixel(int y) const;
|
||||||
std::optional<int> textPosFromPoint(const QTextLayout &textLayout, QPoint p) const;
|
std::optional<int> textPosFromPoint(const QTextLayout &textLayout, QPoint p) const;
|
||||||
@@ -112,6 +130,17 @@ protected:
|
|||||||
std::optional<QTextLayout::FormatRange> selectionToFormatRange(
|
std::optional<QTextLayout::FormatRange> selectionToFormatRange(
|
||||||
TerminalWidget::Selection selection, const QTextLayout &layout, int rowOffset) const;
|
TerminalWidget::Selection selection, const QTextLayout &layout, int rowOffset) const;
|
||||||
|
|
||||||
|
void checkLinkAt(const QPoint &pos);
|
||||||
|
|
||||||
|
struct TextAndOffsets
|
||||||
|
{
|
||||||
|
int start;
|
||||||
|
int end;
|
||||||
|
std::u32string text;
|
||||||
|
};
|
||||||
|
|
||||||
|
TextAndOffsets textAt(const QPoint &pos) const;
|
||||||
|
|
||||||
void applySizeChange();
|
void applySizeChange();
|
||||||
|
|
||||||
void updateScrollBars();
|
void updateScrollBars();
|
||||||
@@ -122,38 +151,25 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Utils::QtcProcess> m_process;
|
std::unique_ptr<Utils::QtcProcess> m_process;
|
||||||
|
std::unique_ptr<Internal::TerminalSurface> m_surface;
|
||||||
|
|
||||||
QString m_shellName;
|
QString m_shellName;
|
||||||
|
|
||||||
std::unique_ptr<VTerm, void (*)(VTerm *)> m_vterm;
|
|
||||||
VTermScreen *m_vtermScreen;
|
|
||||||
QSize m_vtermSize;
|
|
||||||
|
|
||||||
QFont m_font;
|
QFont m_font;
|
||||||
QSizeF m_cellSize;
|
QSizeF m_cellSize;
|
||||||
qreal m_cellBaseline;
|
|
||||||
qreal m_lineSpacing;
|
|
||||||
|
|
||||||
bool m_altscreen{false};
|
|
||||||
bool m_ignoreScroll{false};
|
bool m_ignoreScroll{false};
|
||||||
|
|
||||||
QString m_preEditString;
|
QString m_preEditString;
|
||||||
|
|
||||||
std::optional<Selection> m_selection;
|
std::optional<Selection> m_selection;
|
||||||
QPoint m_selectionStartPos;
|
std::optional<LinkSelection> m_linkSelection;
|
||||||
|
|
||||||
std::unique_ptr<Internal::Scrollback> m_scrollback;
|
|
||||||
|
|
||||||
QTextLayout m_textLayout;
|
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
int row{0};
|
QPoint start;
|
||||||
int col{0};
|
QPoint end;
|
||||||
bool visible{false};
|
} m_activeMouseSelect;
|
||||||
} m_cursor;
|
|
||||||
|
|
||||||
VTermScreenCallbacks m_vtermScreenCallbacks;
|
|
||||||
|
|
||||||
QAction m_copyAction;
|
QAction m_copyAction;
|
||||||
QAction m_pasteAction;
|
QAction m_pasteAction;
|
||||||
@@ -165,8 +181,6 @@ private:
|
|||||||
|
|
||||||
QTimer m_flushDelayTimer;
|
QTimer m_flushDelayTimer;
|
||||||
|
|
||||||
int m_layoutVersion{0};
|
|
||||||
|
|
||||||
std::array<QColor, 18> m_currentColors;
|
std::array<QColor, 18> m_currentColors;
|
||||||
|
|
||||||
Utils::Terminal::OpenTerminalParameters m_openParameters;
|
Utils::Terminal::OpenTerminalParameters m_openParameters;
|
||||||
@@ -174,8 +188,6 @@ private:
|
|||||||
std::chrono::system_clock::time_point m_lastFlush;
|
std::chrono::system_clock::time_point m_lastFlush;
|
||||||
std::chrono::system_clock::time_point m_lastDoubleClick;
|
std::chrono::system_clock::time_point m_lastDoubleClick;
|
||||||
bool m_selectLineMode{false};
|
bool m_selectLineMode{false};
|
||||||
|
|
||||||
std::u32string m_currentLiveText;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Terminal
|
} // namespace Terminal
|
||||||
|
|||||||
112
src/plugins/terminal/tests/colors
Executable file
112
src/plugins/terminal/tests/colors
Executable file
@@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# Source: https://gist.github.com/lilydjwg/fdeaf79e921c2f413f44b6f613f6ad53
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
|
||||||
|
def colors16():
|
||||||
|
for bold in [0, 1]:
|
||||||
|
for i in range(30, 38):
|
||||||
|
for j in range(40, 48):
|
||||||
|
print(f'\x1b[{bold};{i};{j}m {bold};{i};{j} |\x1b[0m', end='')
|
||||||
|
print()
|
||||||
|
print()
|
||||||
|
|
||||||
|
for bold in [0, 1]:
|
||||||
|
for i in range(90, 98):
|
||||||
|
for j in range(100, 108):
|
||||||
|
print(f'\x1b[{bold};{i};{j}m {bold};{i};{j} |\x1b[0m', end='')
|
||||||
|
print()
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def color1(c, n=0):
|
||||||
|
print(f'\x1b[{n};38;5;{c}m{c:4}\x1b[0m', end='')
|
||||||
|
|
||||||
|
|
||||||
|
def color1_sep(c):
|
||||||
|
if (c - 15) % 18 == 0:
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def color2(c):
|
||||||
|
print(f'\x1b[48;5;{c}m \x1b[0m', end='')
|
||||||
|
|
||||||
|
|
||||||
|
def color2_sep(c):
|
||||||
|
if (c - 15) % 36 == 0:
|
||||||
|
print()
|
||||||
|
elif (c - 15) % 6 == 0:
|
||||||
|
print(' ', end='')
|
||||||
|
|
||||||
|
|
||||||
|
def colors256(color, sepfunc):
|
||||||
|
for i in range(0, 8):
|
||||||
|
color(i)
|
||||||
|
print()
|
||||||
|
for i in range(8, 16):
|
||||||
|
color(i)
|
||||||
|
print('\n')
|
||||||
|
|
||||||
|
for i in range(16, 232):
|
||||||
|
color(i)
|
||||||
|
sepfunc(i)
|
||||||
|
print()
|
||||||
|
|
||||||
|
for i in range(232, 256):
|
||||||
|
color(i)
|
||||||
|
print('\n')
|
||||||
|
|
||||||
|
|
||||||
|
def colors_gradient():
|
||||||
|
s = '/\\' * 40
|
||||||
|
for col in range(0, 77):
|
||||||
|
r = 255 - col * 255 // 76
|
||||||
|
g = col * 510 // 76
|
||||||
|
b = col * 255 // 76
|
||||||
|
if g > 255:
|
||||||
|
g = 510 - g
|
||||||
|
print(
|
||||||
|
f'\x1b[48;2;{r};{g};{b}m\x1b[38;2;{255-r};{255-g};{255-b}m{s[col]}\x1b[0m', end='')
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def other_attributes():
|
||||||
|
for i in range(0, 10):
|
||||||
|
print(f' \x1b[{i}mSGR {i:2}\x1b[m', end=' ')
|
||||||
|
print(' \x1b[53mSGR 53\x1b[m', end=' ') # overline
|
||||||
|
print('\n')
|
||||||
|
# https://askubuntu.com/a/985386/235132
|
||||||
|
for i in range(1, 6):
|
||||||
|
print(f' \x1b[4:{i}mSGR 4:{i}\x1b[m', end=' ')
|
||||||
|
print(' \x1b[21mSGR 21\x1b[m', end=' ')
|
||||||
|
|
||||||
|
print(
|
||||||
|
' \x1b[4:3m\x1b[58;2;135;0;255mtruecolor underline\x1b[59m\x1b[4:0m', end=' ')
|
||||||
|
print(' \x1b]8;;https://askubuntu.com/a/985386/235132\x1b\\hyperlink\x1b]8;;\x1b\\')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print('basic 16 colors, foreground & background:\n')
|
||||||
|
colors16()
|
||||||
|
|
||||||
|
print('256 colors:\n')
|
||||||
|
colors256(color1, color1_sep)
|
||||||
|
|
||||||
|
print('256 colors, bold:\n')
|
||||||
|
colors256(partial(color1, n=1), color1_sep)
|
||||||
|
|
||||||
|
print('256 colors, dim:\n')
|
||||||
|
colors256(partial(color1, n=2), color1_sep)
|
||||||
|
|
||||||
|
print('256 colors, bold dim:\n')
|
||||||
|
colors256(partial(color1, n='1;2'), color1_sep)
|
||||||
|
|
||||||
|
print('256 colors, solid background:\n')
|
||||||
|
colors256(color2, color2_sep)
|
||||||
|
|
||||||
|
print('true colors gradient:\n')
|
||||||
|
colors_gradient()
|
||||||
|
|
||||||
|
print('other attributes:\n')
|
||||||
|
other_attributes()
|
||||||
Reference in New Issue
Block a user