2023-03-03 17:18:56 +01:00
|
|
|
// 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>
|
|
|
|
|
|
|
|
|
|
namespace Terminal::Internal {
|
|
|
|
|
|
2023-05-25 17:32:29 +02:00
|
|
|
Q_LOGGING_CATEGORY(log, "qtc.terminal.surface", QtWarningMsg);
|
|
|
|
|
|
2023-03-03 17:18:56 +01:00
|
|
|
QColor toQColor(const VTermColor &c)
|
|
|
|
|
{
|
|
|
|
|
return QColor(qRgb(c.rgb.red, c.rgb.green, c.rgb.blue));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct TerminalSurfacePrivate
|
|
|
|
|
{
|
2023-03-10 13:55:17 +01:00
|
|
|
TerminalSurfacePrivate(TerminalSurface *surface,
|
|
|
|
|
const QSize &initialGridSize,
|
|
|
|
|
ShellIntegration *shellIntegration)
|
2023-03-03 17:18:56 +01:00
|
|
|
: 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))
|
2023-03-10 13:55:17 +01:00
|
|
|
, m_shellIntegration(shellIntegration)
|
2023-03-03 17:18:56 +01:00
|
|
|
, q(surface)
|
2023-03-05 23:55:37 +01:00
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
void init()
|
2023-03-03 17:18:56 +01:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
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();
|
|
|
|
|
};
|
2023-03-24 17:05:08 +01:00
|
|
|
m_vtermScreenCallbacks.bell = [](void *user) {
|
|
|
|
|
auto p = static_cast<TerminalSurfacePrivate *>(user);
|
|
|
|
|
emit p->q->bell();
|
|
|
|
|
return 1;
|
|
|
|
|
};
|
2023-03-03 17:18:56 +01:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2023-03-10 13:55:17 +01:00
|
|
|
memset(&m_vtermStateFallbacks, 0, sizeof(m_vtermStateFallbacks));
|
|
|
|
|
|
|
|
|
|
m_vtermStateFallbacks.osc = [](int cmd, VTermStringFragment fragment, void *user) {
|
|
|
|
|
auto p = static_cast<TerminalSurfacePrivate *>(user);
|
|
|
|
|
return p->osc(cmd, fragment);
|
|
|
|
|
};
|
|
|
|
|
|
2023-03-03 17:18:56 +01:00
|
|
|
VTermState *vts = vterm_obtain_state(m_vterm.get());
|
2023-03-10 13:55:17 +01:00
|
|
|
vterm_state_set_unrecognised_fallbacks(vts, &m_vtermStateFallbacks, this);
|
2023-03-03 17:18:56 +01:00
|
|
|
vterm_state_set_bold_highbright(vts, true);
|
|
|
|
|
|
2023-03-22 15:39:55 +01:00
|
|
|
VTermColor fg;
|
|
|
|
|
VTermColor bg;
|
|
|
|
|
vterm_color_indexed(&fg, ColorIndex::Foreground);
|
|
|
|
|
vterm_color_indexed(&bg, ColorIndex::Background);
|
|
|
|
|
vterm_state_set_default_colors(vts, &fg, &bg);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 16; ++i) {
|
|
|
|
|
VTermColor col;
|
|
|
|
|
vterm_color_indexed(&col, i);
|
|
|
|
|
vterm_state_set_palette_color(vts, i, &col);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-03 17:18:56 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-22 15:39:55 +01:00
|
|
|
std::variant<int, QColor> toVariantColor(const VTermColor &color)
|
|
|
|
|
{
|
|
|
|
|
if (color.type & VTERM_COLOR_DEFAULT_BG)
|
|
|
|
|
return ColorIndex::Background;
|
|
|
|
|
else if (color.type & VTERM_COLOR_DEFAULT_FG)
|
|
|
|
|
return ColorIndex::Foreground;
|
|
|
|
|
else if (color.type & VTERM_COLOR_INDEXED) {
|
|
|
|
|
if (color.indexed.idx >= 16) {
|
|
|
|
|
VTermColor c = color;
|
|
|
|
|
vterm_state_convert_color_to_rgb(vterm_obtain_state(m_vterm.get()), &c);
|
|
|
|
|
return toQColor(c);
|
|
|
|
|
}
|
|
|
|
|
return color.indexed.idx;
|
|
|
|
|
} else if (color.type == VTERM_COLOR_RGB)
|
|
|
|
|
return toQColor(color);
|
|
|
|
|
else
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-03 17:18:56 +01:00
|
|
|
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);
|
|
|
|
|
|
2023-03-22 15:39:55 +01:00
|
|
|
result.backgroundColor = toVariantColor(*bg);
|
|
|
|
|
result.foregroundColor = toVariantColor(*fg);
|
2023-03-03 17:18:56 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Callbacks from vterm
|
|
|
|
|
void invalidate(VTermRect rect)
|
|
|
|
|
{
|
2023-03-06 18:16:28 +01:00
|
|
|
if (!m_altscreen) {
|
|
|
|
|
rect.start_row += m_scrollback->size();
|
|
|
|
|
rect.end_row += m_scrollback->size();
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-03 17:18:56 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2023-03-22 15:39:55 +01:00
|
|
|
m_scrollback->emplace(cols, cells);
|
2023-03-03 17:18:56 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-10 13:55:17 +01:00
|
|
|
int osc(int cmd, const VTermStringFragment &fragment)
|
|
|
|
|
{
|
|
|
|
|
if (m_shellIntegration)
|
|
|
|
|
m_shellIntegration->onOsc(cmd, fragment);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-03 17:18:56 +01:00
|
|
|
int setTerminalProperties(VTermProp prop, VTermValue *val)
|
|
|
|
|
{
|
|
|
|
|
switch (prop) {
|
2023-03-05 23:55:37 +01:00
|
|
|
case VTERM_PROP_CURSORVISIBLE: {
|
|
|
|
|
Cursor old = q->cursor();
|
2023-03-03 17:18:56 +01:00
|
|
|
m_cursor.visible = val->boolean;
|
2023-03-05 23:55:37 +01:00
|
|
|
q->cursorChanged(old, q->cursor());
|
2023-03-03 17:18:56 +01:00
|
|
|
break;
|
2023-03-05 23:55:37 +01:00
|
|
|
}
|
|
|
|
|
case VTERM_PROP_CURSORBLINK: {
|
|
|
|
|
Cursor old = q->cursor();
|
|
|
|
|
m_cursor.blink = val->boolean;
|
|
|
|
|
emit q->cursorChanged(old, q->cursor());
|
2023-03-03 17:18:56 +01:00
|
|
|
break;
|
2023-03-05 23:55:37 +01:00
|
|
|
}
|
|
|
|
|
case VTERM_PROP_CURSORSHAPE: {
|
|
|
|
|
Cursor old = q->cursor();
|
|
|
|
|
m_cursor.shape = (Cursor::Shape) val->number;
|
|
|
|
|
emit q->cursorChanged(old, q->cursor());
|
2023-03-03 17:18:56 +01:00
|
|
|
break;
|
2023-03-05 23:55:37 +01:00
|
|
|
}
|
2023-03-03 17:18:56 +01:00
|
|
|
case VTERM_PROP_ICONNAME:
|
|
|
|
|
break;
|
|
|
|
|
case VTERM_PROP_TITLE:
|
2023-07-03 12:21:30 +03:00
|
|
|
emit q->titleChanged(QString::fromUtf8(val->string.str, val->string.len));
|
2023-03-03 17:18:56 +01:00
|
|
|
break;
|
|
|
|
|
case VTERM_PROP_ALTSCREEN:
|
|
|
|
|
m_altscreen = val->boolean;
|
|
|
|
|
emit q->altscreenChanged(m_altscreen);
|
|
|
|
|
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();
|
2023-03-05 23:55:37 +01:00
|
|
|
m_cursor.position = {pos.col, pos.row};
|
|
|
|
|
m_cursor.visible = visible > 0;
|
2023-03-03 17:18:56 +01:00
|
|
|
q->cursorChanged(oldCursor, q->cursor());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const VTermScreenCell *cellAt(int x, int y)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(y >= 0 && x >= 0, return nullptr);
|
2023-03-06 18:16:28 +01:00
|
|
|
QTC_ASSERT(y < q->fullSize().height() && x < liveSize().width(), return nullptr);
|
|
|
|
|
|
|
|
|
|
if (!m_altscreen && y < m_scrollback->size()) {
|
2023-03-03 17:18:56 +01:00
|
|
|
const auto &sbl = m_scrollback->line((m_scrollback->size() - 1) - y);
|
|
|
|
|
if (x < sbl.cols()) {
|
|
|
|
|
return sbl.cell(x);
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 18:16:28 +01:00
|
|
|
if (!m_altscreen)
|
|
|
|
|
y -= m_scrollback->size();
|
2023-03-03 17:18:56 +01:00
|
|
|
|
|
|
|
|
static VTermScreenCell refCell{};
|
|
|
|
|
VTermPos vtp{y, x};
|
|
|
|
|
vterm_screen_get_cell(m_vtermScreen, vtp, &refCell);
|
|
|
|
|
|
|
|
|
|
return &refCell;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<VTerm, void (*)(VTerm *)> m_vterm;
|
|
|
|
|
VTermScreen *m_vtermScreen;
|
|
|
|
|
VTermScreenCallbacks m_vtermScreenCallbacks;
|
2023-03-10 13:55:17 +01:00
|
|
|
VTermStateFallbacks m_vtermStateFallbacks;
|
2023-03-03 17:18:56 +01:00
|
|
|
|
|
|
|
|
Cursor m_cursor;
|
2023-03-10 13:55:17 +01:00
|
|
|
QString m_currentCommand;
|
2023-03-03 17:18:56 +01:00
|
|
|
|
|
|
|
|
bool m_altscreen{false};
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<Internal::Scrollback> m_scrollback;
|
|
|
|
|
|
2023-03-10 13:55:17 +01:00
|
|
|
ShellIntegration *m_shellIntegration{nullptr};
|
|
|
|
|
|
2023-03-03 17:18:56 +01:00
|
|
|
TerminalSurface *q;
|
|
|
|
|
};
|
|
|
|
|
|
2023-03-10 13:55:17 +01:00
|
|
|
TerminalSurface::TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration)
|
|
|
|
|
: d(std::make_unique<TerminalSurfacePrivate>(this, initialGridSize, shellIntegration))
|
2023-03-05 23:55:37 +01:00
|
|
|
{
|
|
|
|
|
d->init();
|
|
|
|
|
}
|
2023-03-03 17:18:56 +01:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
2023-03-06 18:16:28 +01:00
|
|
|
if (d->m_altscreen)
|
|
|
|
|
return liveSize();
|
2023-03-03 17:18:56 +01:00
|
|
|
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
|
|
|
|
|
{
|
2023-03-22 15:39:55 +01:00
|
|
|
static TerminalCell emptyCell{1,
|
|
|
|
|
{},
|
|
|
|
|
{},
|
|
|
|
|
false,
|
|
|
|
|
ColorIndex::Foreground,
|
|
|
|
|
ColorIndex::Background,
|
|
|
|
|
QTextCharFormat::NoUnderline,
|
|
|
|
|
false};
|
2023-03-03 17:18:56 +01:00
|
|
|
|
|
|
|
|
QTC_ASSERT(y >= 0, return emptyCell);
|
2023-03-06 18:16:28 +01:00
|
|
|
QTC_ASSERT(y < fullSize().height() && x < fullSize().width(), return emptyCell);
|
2023-03-03 17:18:56 +01:00
|
|
|
|
|
|
|
|
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::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);
|
|
|
|
|
} 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;
|
2023-03-06 18:16:28 +01:00
|
|
|
if (!d->m_altscreen)
|
|
|
|
|
cursor.position.setY(cursor.position.y() + d->m_scrollback->size());
|
2023-03-03 17:18:56 +01:00
|
|
|
|
|
|
|
|
return cursor;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-10 13:55:17 +01:00
|
|
|
ShellIntegration *TerminalSurface::shellIntegration() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_shellIntegration;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-03 17:18:56 +01:00
|
|
|
CellIterator TerminalSurface::begin() const
|
|
|
|
|
{
|
|
|
|
|
auto res = CellIterator(this, {0, 0});
|
|
|
|
|
res.m_state = CellIterator::State::BEGIN;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CellIterator TerminalSurface::end() const
|
|
|
|
|
{
|
2023-05-09 07:37:30 +02:00
|
|
|
return CellIterator(this);
|
2023-03-03 17:18:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|