Terminal: Create Terminal solution

Change-Id: If271fd23a84c49bbc25fcc3b9bc0939c7237d095
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
Marcus Tillmanns
2023-07-17 13:51:56 +02:00
parent 209f3ba579
commit 42ed82973c
26 changed files with 1885 additions and 1468 deletions

View File

@@ -1,2 +1,3 @@
add_subdirectory(spinner) add_subdirectory(spinner)
add_subdirectory(tasking) add_subdirectory(tasking)
add_subdirectory(terminal)

View File

@@ -0,0 +1,12 @@
add_qtc_library(TerminalLib
DEPENDS Qt::Core Qt::Widgets libvterm
SOURCES
celliterator.cpp celliterator.h
glyphcache.cpp glyphcache.h
keys.cpp keys.h
scrollback.cpp scrollback.h
surfaceintegration.h
terminal_global.h
terminalsurface.cpp terminalsurface.h
terminalview.cpp terminalview.h
)

View File

@@ -7,7 +7,7 @@
#include <stdexcept> #include <stdexcept>
namespace Terminal::Internal { namespace TerminalSolution {
CellIterator::CellIterator(const TerminalSurface *surface, QPoint pos) CellIterator::CellIterator(const TerminalSurface *surface, QPoint pos)
: CellIterator(surface, pos.x() + (pos.y() * surface->liveSize().width())) : CellIterator(surface, pos.x() + (pos.y() * surface->liveSize().width()))
@@ -91,4 +91,4 @@ CellIterator &CellIterator::operator+=(int n)
return *this; return *this;
} }
} // namespace Terminal::Internal } // namespace TerminalSolution

View File

@@ -3,15 +3,17 @@
#pragma once #pragma once
#include "terminal_global.h"
#include <string> #include <string>
#include <QPoint> #include <QPoint>
namespace Terminal::Internal { namespace TerminalSolution {
class TerminalSurface; class TerminalSurface;
class CellIterator class TERMINAL_EXPORT CellIterator
{ {
public: public:
using iterator_category = std::bidirectional_iterator_tag; using iterator_category = std::bidirectional_iterator_tag;
@@ -94,4 +96,4 @@ private:
mutable std::u32string::value_type m_char; mutable std::u32string::value_type m_char;
}; };
} // namespace Terminal::Internal } // namespace TerminalSolution

View File

@@ -5,7 +5,7 @@
#include <QTextLayout> #include <QTextLayout>
namespace Terminal::Internal { namespace TerminalSolution {
size_t qHash(const GlyphCacheKey &key, size_t seed = 0) size_t qHash(const GlyphCacheKey &key, size_t seed = 0)
{ {
@@ -45,4 +45,4 @@ const QGlyphRun *GlyphCache::get(const QFont &font, const QString &text)
return nullptr; return nullptr;
} }
} // namespace Terminal::Internal } // namespace TerminalSolution

View File

@@ -8,7 +8,7 @@
#include <QGlyphRun> #include <QGlyphRun>
#include <QString> #include <QString>
namespace Terminal::Internal { namespace TerminalSolution {
struct GlyphCacheKey struct GlyphCacheKey
{ {
@@ -31,4 +31,4 @@ public:
const QGlyphRun *get(const QFont &font, const QString &text); const QGlyphRun *get(const QFont &font, const QString &text);
}; };
} // namespace Terminal::Internal } // namespace TerminalSolution

View File

@@ -1,11 +1,9 @@
// Copyright (C) 2022 The Qt Company Ltd. // 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 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include <utils/hostosinfo.h>
#include "keys.h" #include "keys.h"
namespace Terminal::Internal { namespace TerminalSolution {
VTermModifier qtModifierToVTerm(Qt::KeyboardModifiers mod) VTermModifier qtModifierToVTerm(Qt::KeyboardModifiers mod)
{ {
@@ -77,8 +75,9 @@ VTermKey qtKeyToVTerm(Qt::Key key, bool keypad)
case Qt::Key_Enter: { case Qt::Key_Enter: {
VTermKey enterKey = VTERM_KEY_KP_ENTER; VTermKey enterKey = VTERM_KEY_KP_ENTER;
if (Utils::HostOsInfo::isWindowsHost()) #ifdef Q_OS_WIN
enterKey = VTERM_KEY_ENTER; enterKey = VTERM_KEY_ENTER;
#endif
return keypad ? enterKey : VTERM_KEY_NONE; return keypad ? enterKey : VTERM_KEY_NONE;
} }
@@ -88,4 +87,4 @@ VTermKey qtKeyToVTerm(Qt::Key key, bool keypad)
return VTERM_KEY_NONE; return VTERM_KEY_NONE;
} }
} }
} // namespace Terminal::Internal } // namespace TerminalSolution

View File

@@ -7,9 +7,9 @@
#include <QKeyEvent> #include <QKeyEvent>
namespace Terminal::Internal { namespace TerminalSolution {
VTermKey qtKeyToVTerm(Qt::Key key, bool keypad); VTermKey qtKeyToVTerm(Qt::Key key, bool keypad);
VTermModifier qtModifierToVTerm(Qt::KeyboardModifiers mod); VTermModifier qtModifierToVTerm(Qt::KeyboardModifiers mod);
} // namespace Terminal::Internal } // namespace TerminalSolution

View File

@@ -8,7 +8,7 @@
#include <cstring> #include <cstring>
#include <future> #include <future>
namespace Terminal::Internal { namespace TerminalSolution {
Scrollback::Line::Line(int cols, const VTermScreenCell *cells) Scrollback::Line::Line(int cols, const VTermScreenCell *cells)
: m_cols(cols) : m_cols(cols)
@@ -58,4 +58,4 @@ void Scrollback::clear()
m_deque.clear(); m_deque.clear();
} }
} // namespace Terminal::Internal } // namespace TerminalSolution

View File

@@ -13,7 +13,7 @@
#include <QFont> #include <QFont>
#include <QTextLayout> #include <QTextLayout>
namespace Terminal::Internal { namespace TerminalSolution {
class Scrollback class Scrollback
{ {
@@ -54,4 +54,4 @@ private:
std::deque<Line> m_deque; std::deque<Line> m_deque;
}; };
} // namespace Terminal::Internal } // namespace TerminalSolution

View File

@@ -0,0 +1,19 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <QString>
namespace TerminalSolution {
class SurfaceIntegration
{
public:
virtual void onOsc(int cmd, std::string_view str, bool initial, bool final) = 0;
virtual void onBell() {}
virtual void onTitle(const QString &title) { Q_UNUSED(title); }
};
} // namespace TerminalSolution

View File

@@ -0,0 +1,14 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <qglobal.h>
#if defined(TERMINALLIB_LIBRARY)
#define TERMINAL_EXPORT Q_DECL_EXPORT
#elif defined(TERMINALLIB_STATIC_LIBRARY)
#define TERMINAL_EXPORT
#else
#define TERMINAL_EXPORT Q_DECL_IMPORT
#endif

View File

@@ -2,17 +2,16 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "terminalsurface.h" #include "terminalsurface.h"
#include "surfaceintegration.h"
#include "keys.h" #include "keys.h"
#include "scrollback.h" #include "scrollback.h"
#include <utils/qtcassert.h>
#include <vterm.h> #include <vterm.h>
#include <QLoggingCategory> #include <QLoggingCategory>
namespace Terminal::Internal { namespace TerminalSolution {
Q_LOGGING_CATEGORY(log, "qtc.terminal.surface", QtWarningMsg); Q_LOGGING_CATEGORY(log, "qtc.terminal.surface", QtWarningMsg);
@@ -23,13 +22,10 @@ QColor toQColor(const VTermColor &c)
struct TerminalSurfacePrivate struct TerminalSurfacePrivate
{ {
TerminalSurfacePrivate(TerminalSurface *surface, TerminalSurfacePrivate(TerminalSurface *surface, const QSize &initialGridSize)
const QSize &initialGridSize,
ShellIntegration *shellIntegration)
: m_vterm(vterm_new(initialGridSize.height(), initialGridSize.width()), vterm_free) : m_vterm(vterm_new(initialGridSize.height(), initialGridSize.width()), vterm_free)
, m_vtermScreen(vterm_obtain_screen(m_vterm.get())) , m_vtermScreen(vterm_obtain_screen(m_vterm.get()))
, m_scrollback(std::make_unique<Internal::Scrollback>(5000)) , m_scrollback(std::make_unique<Scrollback>(5000))
, m_shellIntegration(shellIntegration)
, q(surface) , q(surface)
{} {}
@@ -74,7 +70,8 @@ struct TerminalSurfacePrivate
}; };
m_vtermScreenCallbacks.bell = [](void *user) { m_vtermScreenCallbacks.bell = [](void *user) {
auto p = static_cast<TerminalSurfacePrivate *>(user); auto p = static_cast<TerminalSurfacePrivate *>(user);
emit p->q->bell(); if (p->m_surfaceIntegration)
p->m_surfaceIntegration->onBell();
return 1; return 1;
}; };
@@ -219,8 +216,12 @@ struct TerminalSurfacePrivate
int osc(int cmd, const VTermStringFragment &fragment) int osc(int cmd, const VTermStringFragment &fragment)
{ {
if (m_shellIntegration) if (m_surfaceIntegration) {
m_shellIntegration->onOsc(cmd, fragment); m_surfaceIntegration->onOsc(cmd,
{fragment.str, fragment.len},
fragment.initial,
fragment.final);
}
return 1; return 1;
} }
@@ -249,7 +250,8 @@ struct TerminalSurfacePrivate
case VTERM_PROP_ICONNAME: case VTERM_PROP_ICONNAME:
break; break;
case VTERM_PROP_TITLE: case VTERM_PROP_TITLE:
emit q->titleChanged(QString::fromUtf8(val->string.str, val->string.len)); if (m_surfaceIntegration)
m_surfaceIntegration->onTitle(QString::fromUtf8(val->string.str, val->string.len));
break; break;
case VTERM_PROP_ALTSCREEN: case VTERM_PROP_ALTSCREEN:
m_altscreen = val->boolean; m_altscreen = val->boolean;
@@ -278,8 +280,11 @@ struct TerminalSurfacePrivate
const VTermScreenCell *cellAt(int x, int y) const VTermScreenCell *cellAt(int x, int y)
{ {
QTC_ASSERT(y >= 0 && x >= 0, return nullptr); if (y < 0 || x < 0 || y >= q->fullSize().height() || x >= liveSize().width()) {
QTC_ASSERT(y < q->fullSize().height() && x < liveSize().width(), return nullptr); qCWarning(log) << "Invalid Parameter for cellAt:" << x << y << "liveSize:" << liveSize()
<< "fullSize:" << q->fullSize();
return nullptr;
}
if (!m_altscreen && y < m_scrollback->size()) { if (!m_altscreen && y < m_scrollback->size()) {
const auto &sbl = m_scrollback->line((m_scrollback->size() - 1) - y); const auto &sbl = m_scrollback->line((m_scrollback->size() - 1) - y);
@@ -309,15 +314,15 @@ struct TerminalSurfacePrivate
bool m_altscreen{false}; bool m_altscreen{false};
std::unique_ptr<Internal::Scrollback> m_scrollback; std::unique_ptr<Scrollback> m_scrollback;
ShellIntegration *m_shellIntegration{nullptr}; SurfaceIntegration *m_surfaceIntegration{nullptr};
TerminalSurface *q; TerminalSurface *q;
}; };
TerminalSurface::TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration) TerminalSurface::TerminalSurface(QSize initialGridSize)
: d(std::make_unique<TerminalSurfacePrivate>(this, initialGridSize, shellIntegration)) : d(std::make_unique<TerminalSurfacePrivate>(this, initialGridSize))
{ {
d->init(); d->init();
} }
@@ -369,8 +374,10 @@ TerminalCell TerminalSurface::fetchCell(int x, int y) const
QTextCharFormat::NoUnderline, QTextCharFormat::NoUnderline,
false}; false};
QTC_ASSERT(y >= 0, return emptyCell); if (y < 0 || y >= fullSize().height() || x >= fullSize().width()) {
QTC_ASSERT(y < fullSize().height() && x < fullSize().width(), return emptyCell); qCWarning(log) << "Invalid Parameter for fetchCell:" << x << y << "fullSize:" << fullSize();
return emptyCell;
}
const VTermScreenCell *refCell = d->cellAt(x, y); const VTermScreenCell *refCell = d->cellAt(x, y);
if (!refCell) if (!refCell)
@@ -450,8 +457,8 @@ void TerminalSurface::sendKey(const QString &text)
void TerminalSurface::sendKey(QKeyEvent *event) void TerminalSurface::sendKey(QKeyEvent *event)
{ {
bool keypad = event->modifiers() & Qt::KeypadModifier; bool keypad = event->modifiers() & Qt::KeypadModifier;
VTermModifier mod = Internal::qtModifierToVTerm(event->modifiers()); VTermModifier mod = qtModifierToVTerm(event->modifiers());
VTermKey key = Internal::qtKeyToVTerm(Qt::Key(event->key()), keypad); VTermKey key = qtKeyToVTerm(Qt::Key(event->key()), keypad);
if (key != VTERM_KEY_NONE) { if (key != VTERM_KEY_NONE) {
if (mod == VTERM_MOD_SHIFT && (key == VTERM_KEY_ESCAPE || key == VTERM_KEY_BACKSPACE)) if (mod == VTERM_MOD_SHIFT && (key == VTERM_KEY_ESCAPE || key == VTERM_KEY_BACKSPACE))
@@ -489,14 +496,19 @@ Cursor TerminalSurface::cursor() const
return cursor; return cursor;
} }
ShellIntegration *TerminalSurface::shellIntegration() const SurfaceIntegration *TerminalSurface::surfaceIntegration() const
{ {
return d->m_shellIntegration; return d->m_surfaceIntegration;
}
void TerminalSurface::setSurfaceIntegration(SurfaceIntegration *surfaceIntegration)
{
d->m_surfaceIntegration = surfaceIntegration;
} }
void TerminalSurface::mouseMove(QPoint pos, Qt::KeyboardModifiers modifiers) void TerminalSurface::mouseMove(QPoint pos, Qt::KeyboardModifiers modifiers)
{ {
vterm_mouse_move(d->m_vterm.get(), pos.y(), pos.x(), Internal::qtModifierToVTerm(modifiers)); vterm_mouse_move(d->m_vterm.get(), pos.y(), pos.x(), qtModifierToVTerm(modifiers));
} }
void TerminalSurface::mouseButton(Qt::MouseButton button, void TerminalSurface::mouseButton(Qt::MouseButton button,
@@ -524,7 +536,7 @@ void TerminalSurface::mouseButton(Qt::MouseButton button,
return; return;
} }
vterm_mouse_button(d->m_vterm.get(), btnIdx, pressed, Internal::qtModifierToVTerm(modifiers)); vterm_mouse_button(d->m_vterm.get(), btnIdx, pressed, qtModifierToVTerm(modifiers));
} }
CellIterator TerminalSurface::begin() const CellIterator TerminalSurface::begin() const
@@ -568,4 +580,4 @@ std::reverse_iterator<CellIterator> TerminalSurface::rIteratorAt(int pos) const
return std::make_reverse_iterator(iteratorAt(pos)); return std::make_reverse_iterator(iteratorAt(pos));
} }
} // namespace Terminal::Internal } // namespace TerminalSolution

View File

@@ -3,8 +3,9 @@
#pragma once #pragma once
#include "terminal_global.h"
#include "celliterator.h" #include "celliterator.h"
#include "shellintegration.h"
#include <QKeyEvent> #include <QKeyEvent>
#include <QSize> #include <QSize>
@@ -12,9 +13,10 @@
#include <memory> #include <memory>
namespace Terminal::Internal { namespace TerminalSolution {
class Scrollback; class Scrollback;
class SurfaceIntegration;
struct TerminalSurfacePrivate; struct TerminalSurfacePrivate;
@@ -45,12 +47,12 @@ struct Cursor
bool blink{false}; bool blink{false};
}; };
class TerminalSurface : public QObject class TERMINAL_EXPORT TerminalSurface : public QObject
{ {
Q_OBJECT; Q_OBJECT;
public: public:
TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration); TerminalSurface(QSize initialGridSize);
~TerminalSurface(); ~TerminalSurface();
public: public:
@@ -93,7 +95,8 @@ public:
Cursor cursor() const; Cursor cursor() const;
ShellIntegration *shellIntegration() const; SurfaceIntegration *surfaceIntegration() const;
void setSurfaceIntegration(SurfaceIntegration *surfaceIntegration);
void mouseMove(QPoint pos, Qt::KeyboardModifiers modifiers); void mouseMove(QPoint pos, Qt::KeyboardModifiers modifiers);
void mouseButton(Qt::MouseButton button, bool pressed, Qt::KeyboardModifiers modifiers); void mouseButton(Qt::MouseButton button, bool pressed, Qt::KeyboardModifiers modifiers);
@@ -104,11 +107,9 @@ signals:
void cursorChanged(Cursor oldCursor, Cursor newCursor); void cursorChanged(Cursor oldCursor, Cursor newCursor);
void altscreenChanged(bool altScreen); void altscreenChanged(bool altScreen);
void unscroll(); void unscroll();
void bell();
void titleChanged(const QString &title);
private: private:
std::unique_ptr<TerminalSurfacePrivate> d; std::unique_ptr<TerminalSurfacePrivate> d;
}; };
} // namespace Terminal::Internal } // namespace TerminalSolution

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,221 @@
// 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 "terminal_global.h"
#include "terminalsurface.h"
#include <QAbstractScrollArea>
#include <QAction>
#include <QTextLayout>
#include <QTimer>
#include <chrono>
#include <memory>
#include <optional>
namespace TerminalSolution {
class SurfaceIntegration;
class TerminalViewPrivate;
struct SearchHit
{
int start{-1};
int end{-1};
bool operator!=(const SearchHit &other) const
{
return start != other.start || end != other.end;
}
bool operator==(const SearchHit &other) const { return !operator!=(other); }
};
QString defaultFontFamily();
int defaultFontSize();
class TERMINAL_EXPORT TerminalView : public QAbstractScrollArea
{
friend class CellIterator;
Q_OBJECT
public:
enum class WidgetColorIdx {
Foreground = ColorIndex::Foreground,
Background = ColorIndex::Background,
Selection,
FindMatch,
};
TerminalView(QWidget *parent = nullptr);
~TerminalView() override;
void setAllowBlinkingCursor(bool allow);
bool allowBlinkingCursor() const;
void setFont(const QFont &font);
void copyToClipboard();
void pasteFromClipboard();
void copyLinkToClipboard();
struct Selection
{
int start;
int end;
bool final{false};
bool operator!=(const Selection &other) const
{
return start != other.start || end != other.end || final != other.final;
}
bool operator==(const Selection &other) const { return !operator!=(other); }
};
std::optional<Selection> selection() const;
void clearSelection();
void zoomIn();
void zoomOut();
void moveCursorWordLeft();
void moveCursorWordRight();
void clearContents();
void setSurfaceIntegration(SurfaceIntegration *surfaceIntegration);
void setColors(const std::array<QColor, 20> &colors);
struct Link
{
QString text;
int targetLine = 0;
int targetColumn = 0;
};
struct LinkSelection : public Selection
{
Link link;
bool operator!=(const LinkSelection &other) const
{
return link.text != other.link.text || link.targetLine != other.link.targetLine
|| link.targetColumn != other.link.targetColumn || Selection::operator!=(other);
}
};
virtual void writeToPty(const QByteArray &data) { Q_UNUSED(data); }
void writeToTerminal(const QByteArray &data, bool forceFlush);
void restart();
virtual const QList<SearchHit> &searchHits() const
{
static QList<SearchHit> noHits;
return noHits;
}
virtual void resizePty(QSize newSize) { Q_UNUSED(newSize); }
virtual void setClipboard(const QString &text) { Q_UNUSED(text); }
virtual std::optional<Link> toLink(const QString &text)
{
Q_UNUSED(text);
return std::nullopt;
}
virtual void selectionChanged(const std::optional<Selection> &newSelection)
{
Q_UNUSED(newSelection);
}
virtual void linkActivated(const Link &link) { Q_UNUSED(link); }
virtual void contextMenuRequested(const QPoint &pos) { Q_UNUSED(pos); }
virtual void surfaceChanged(){};
TerminalSurface *surface() const;
protected:
void paintEvent(QPaintEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void focusInEvent(QFocusEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
void inputMethodEvent(QInputMethodEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
bool event(QEvent *event) override;
protected:
void setupSurface();
int paintCell(QPainter &p,
const QRectF &cellRect,
QPoint gridPos,
const TerminalCell &cell,
QFont &f,
QList<SearchHit>::const_iterator &searchIt) const;
void paintCells(QPainter &painter, QPaintEvent *event) const;
void paintCursor(QPainter &painter) const;
void paintPreedit(QPainter &painter) const;
bool paintFindMatches(QPainter &painter,
QList<SearchHit>::const_iterator &searchIt,
const QRectF &cellRect,
const QPoint gridPos) const;
bool paintSelection(QPainter &painter, const QRectF &cellRect, const QPoint gridPos) const;
void paintDebugSelection(QPainter &painter, const Selection &selection) const;
qreal topMargin() const;
QPoint viewportToGlobal(QPoint p) const;
QPoint globalToViewport(QPoint p) const;
QPoint globalToGrid(QPointF p) const;
QPointF gridToGlobal(QPoint p, bool bottom = false, bool right = false) const;
QRect gridToViewport(QRect rect) const;
QPoint toGridPos(QMouseEvent *event) const;
void updateViewport();
void updateViewportRect(const QRect &rect);
int textLineFromPixel(int y) const;
std::optional<int> textPosFromPoint(const QTextLayout &textLayout, QPoint p) const;
std::optional<QTextLayout::FormatRange> selectionToFormatRange(Selection selection,
const QTextLayout &layout,
int rowOffset) const;
bool checkLinkAt(const QPoint &pos);
struct TextAndOffsets
{
int start;
int end;
std::u32string text;
};
TextAndOffsets textAt(const QPoint &pos) const;
void applySizeChange();
void updateScrollBars();
void flushVTerm(bool force);
bool setSelection(const std::optional<Selection> &selection, bool scroll = true);
QString textFromSelection() const;
void configBlinkTimer();
QColor toQColor(std::variant<int, QColor> color) const;
private:
std::unique_ptr<TerminalViewPrivate> d;
};
} // namespace TerminalSolution

View File

@@ -1,12 +1,8 @@
add_qtc_plugin(Terminal add_qtc_plugin(Terminal
PLUGIN_DEPENDS Core ProjectExplorer PLUGIN_DEPENDS Core ProjectExplorer
DEPENDS libvterm ptyqt DEPENDS TerminalLib
SOURCES SOURCES
celliterator.cpp celliterator.h
glyphcache.cpp glyphcache.h
keys.cpp keys.h
scrollback.cpp scrollback.h
shellintegration.cpp shellintegration.h shellintegration.cpp shellintegration.h
shellmodel.cpp shellmodel.h shellmodel.cpp shellmodel.h
shortcutmap.cpp shortcutmap.h shortcutmap.cpp shortcutmap.h
@@ -18,7 +14,6 @@ add_qtc_plugin(Terminal
terminalprocessimpl.cpp terminalprocessimpl.h terminalprocessimpl.cpp terminalprocessimpl.h
terminalsearch.cpp terminalsearch.h terminalsearch.cpp terminalsearch.h
terminalsettings.cpp terminalsettings.h terminalsettings.cpp terminalsettings.h
terminalsurface.cpp terminalsurface.h
terminaltr.h terminaltr.h
terminalwidget.cpp terminalwidget.h terminalwidget.cpp terminalwidget.h
) )

View File

@@ -3,10 +3,13 @@
#include "shellintegration.h" #include "shellintegration.h"
#include "terminalsettings.h"
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/filepath.h> #include <utils/filepath.h>
#include <utils/stringutils.h> #include <utils/stringutils.h>
#include <QApplication>
#include <QLoggingCategory> #include <QLoggingCategory>
Q_LOGGING_CATEGORY(integrationLog, "qtc.terminal.shellintegration", QtWarningMsg) Q_LOGGING_CATEGORY(integrationLog, "qtc.terminal.shellintegration", QtWarningMsg)
@@ -74,9 +77,12 @@ bool ShellIntegration::canIntegrate(const Utils::CommandLine &cmdLine)
return false; return false;
} }
void ShellIntegration::onOsc(int cmd, const VTermStringFragment &fragment) void ShellIntegration::onOsc(int cmd, std::string_view str, bool initial, bool final)
{ {
QString d = QString::fromLocal8Bit(fragment.str, fragment.len); Q_UNUSED(initial);
Q_UNUSED(final);
QString d = QString::fromLocal8Bit(str);
const auto [command, data] = Utils::splitAtFirst(d, ';'); const auto [command, data] = Utils::splitAtFirst(d, ';');
if (cmd == 1337) { if (cmd == 1337) {
@@ -103,6 +109,17 @@ void ShellIntegration::onOsc(int cmd, const VTermStringFragment &fragment)
} }
} }
void ShellIntegration::onBell()
{
if (settings().audibleBell.value())
QApplication::beep();
}
void ShellIntegration::onTitle(const QString &title)
{
emit titleChanged(title);
}
void ShellIntegration::prepareProcess(Utils::Process &process) void ShellIntegration::prepareProcess(Utils::Process &process)
{ {
Environment env = process.environment().hasChanges() ? process.environment() Environment env = process.environment().hasChanges() ? process.environment()

View File

@@ -7,25 +7,28 @@
#include <utils/commandline.h> #include <utils/commandline.h>
#include <utils/process.h> #include <utils/process.h>
#include <vterm.h> #include <solutions/terminal/surfaceintegration.h>
#include <QTemporaryDir> #include <QTemporaryDir>
namespace Terminal { namespace Terminal {
class ShellIntegration : public QObject class ShellIntegration : public QObject, public TerminalSolution::SurfaceIntegration
{ {
Q_OBJECT Q_OBJECT
public: public:
static bool canIntegrate(const Utils::CommandLine &cmdLine); static bool canIntegrate(const Utils::CommandLine &cmdLine);
void onOsc(int cmd, const VTermStringFragment &fragment); void onOsc(int cmd, std::string_view str, bool initial, bool final) override;
void onBell() override;
void onTitle(const QString &title) override;
void prepareProcess(Utils::Process &process); void prepareProcess(Utils::Process &process);
signals: signals:
void commandChanged(const Utils::CommandLine &command); void commandChanged(const Utils::CommandLine &command);
void currentDirChanged(const QString &dir); void currentDirChanged(const QString &dir);
void titleChanged(const QString &title);
private: private:
QTemporaryDir m_tempDir; QTemporaryDir m_tempDir;

View File

@@ -80,7 +80,8 @@ TerminalPane::TerminalPane(QObject *parent)
m_escSettingButton->setToolTip(Tr::tr("Sends Esc to terminal instead of Qt Creator.")); m_escSettingButton->setToolTip(Tr::tr("Sends Esc to terminal instead of Qt Creator."));
} else { } else {
m_escSettingButton->setText(shiftEsc); m_escSettingButton->setText(shiftEsc);
m_escSettingButton->setToolTip(Tr::tr("Press %1 to send Esc to terminal.").arg(shiftEsc)); m_escSettingButton->setToolTip(
Tr::tr("Press %1 to send Esc to terminal.").arg(shiftEsc));
} }
}; };
@@ -161,8 +162,8 @@ void TerminalPane::openTerminal(const OpenTerminalParameters &parameters)
if (icon.isNull()) { if (icon.isNull()) {
QFileIconProvider iconProvider; QFileIconProvider iconProvider;
const FilePath command = parametersCopy.shellCommand const FilePath command = parametersCopy.shellCommand
? parametersCopy.shellCommand->executable() ? parametersCopy.shellCommand->executable()
: settings().shell(); : settings().shell();
icon = iconProvider.icon(command.toFileInfo()); icon = iconProvider.icon(command.toFileInfo());
} }
} }

View File

@@ -17,17 +17,18 @@ using namespace std::chrono_literals;
namespace Terminal { namespace Terminal {
using namespace Terminal::Internal;
constexpr std::chrono::milliseconds debounceInterval = 100ms; constexpr std::chrono::milliseconds debounceInterval = 100ms;
TerminalSearch::TerminalSearch(TerminalSurface *surface) TerminalSearch::TerminalSearch(TerminalSolution::TerminalSurface *surface)
: m_surface(surface) : m_surface(surface)
{ {
m_debounceTimer.setInterval(debounceInterval); m_debounceTimer.setInterval(debounceInterval);
m_debounceTimer.setSingleShot(true); m_debounceTimer.setSingleShot(true);
connect(surface, &TerminalSurface::invalidated, this, &TerminalSearch::updateHits); connect(surface,
&TerminalSolution::TerminalSurface::invalidated,
this,
&TerminalSearch::updateHits);
connect(&m_debounceTimer, &QTimer::timeout, this, &TerminalSearch::debouncedUpdateHits); connect(&m_debounceTimer, &QTimer::timeout, this, &TerminalSearch::debouncedUpdateHits);
} }
@@ -85,9 +86,9 @@ bool isSpace(char32_t a, char32_t b)
return false; return false;
} }
QList<SearchHit> TerminalSearch::search() QList<TerminalSolution::SearchHit> TerminalSearch::search()
{ {
QList<SearchHit> hits; QList<TerminalSolution::SearchHit> hits;
std::function<bool(char32_t, char32_t)> compare; std::function<bool(char32_t, char32_t)> compare;
@@ -108,12 +109,12 @@ QList<SearchHit> TerminalSearch::search()
searchString.insert(searchString.begin(), std::numeric_limits<char32_t>::max()); searchString.insert(searchString.begin(), std::numeric_limits<char32_t>::max());
} }
Internal::CellIterator it = m_surface->begin(); TerminalSolution::CellIterator it = m_surface->begin();
while (it != m_surface->end()) { while (it != m_surface->end()) {
it = std::search(it, m_surface->end(), searchString.begin(), searchString.end(), compare); it = std::search(it, m_surface->end(), searchString.begin(), searchString.end(), compare);
if (it != m_surface->end()) { if (it != m_surface->end()) {
auto hit = SearchHit{it.position(), auto hit = TerminalSolution::SearchHit{it.position(),
static_cast<int>(it.position() + searchString.size())}; static_cast<int>(it.position() + searchString.size())};
if (m_findFlags.testFlag(FindFlag::FindWholeWords)) { if (m_findFlags.testFlag(FindFlag::FindWholeWords)) {
hit.start++; hit.start++;
@@ -127,9 +128,9 @@ QList<SearchHit> TerminalSearch::search()
return hits; return hits;
} }
QList<SearchHit> TerminalSearch::searchRegex() QList<TerminalSolution::SearchHit> TerminalSearch::searchRegex()
{ {
QList<SearchHit> hits; QList<TerminalSolution::SearchHit> hits;
QString allText; QString allText;
allText.reserve(1000); allText.reserve(1000);
@@ -170,7 +171,7 @@ QList<SearchHit> TerminalSearch::searchRegex()
} }
e -= adjust; e -= adjust;
} }
hits << SearchHit{s, e}; hits << TerminalSolution::SearchHit{s, e};
} }
return hits; return hits;
@@ -185,7 +186,7 @@ void TerminalSearch::debouncedUpdateHits()
const bool regex = m_findFlags.testFlag(FindFlag::FindRegularExpression); const bool regex = m_findFlags.testFlag(FindFlag::FindRegularExpression);
QList<SearchHit> hits = regex ? searchRegex() : search(); QList<TerminalSolution::SearchHit> hits = regex ? searchRegex() : search();
if (hits != m_hits) { if (hits != m_hits) {
m_currentHit = -1; m_currentHit = -1;

View File

@@ -3,7 +3,9 @@
#pragma once #pragma once
#include "terminalsurface.h" #include <terminal/terminalsurface.h>
#include <solutions/terminal/terminalview.h>
#include <coreplugin/find/ifindsupport.h> #include <coreplugin/find/ifindsupport.h>
#include <coreplugin/find/textfindconstants.h> #include <coreplugin/find/textfindconstants.h>
@@ -12,19 +14,7 @@
namespace Terminal { namespace Terminal {
struct SearchHit struct SearchHitWithText : TerminalSolution::SearchHit
{
int start{-1};
int end{-1};
bool operator!=(const SearchHit &other) const
{
return start != other.start || end != other.end;
}
bool operator==(const SearchHit &other) const { return !operator!=(other); }
};
struct SearchHitWithText : SearchHit
{ {
QString text; QString text;
}; };
@@ -33,17 +23,17 @@ class TerminalSearch : public Core::IFindSupport
{ {
Q_OBJECT Q_OBJECT
public: public:
TerminalSearch(Internal::TerminalSurface *surface); TerminalSearch(TerminalSolution::TerminalSurface *surface);
void setCurrentSelection(std::optional<SearchHitWithText> selection); void setCurrentSelection(std::optional<SearchHitWithText> selection);
void setSearchString(const QString &searchString, Utils::FindFlags findFlags); void setSearchString(const QString &searchString, Utils::FindFlags findFlags);
void nextHit(); void nextHit();
void previousHit(); void previousHit();
const QList<SearchHit> &hits() const { return m_hits; } const QList<TerminalSolution::SearchHit> &hits() const { return m_hits; }
SearchHit currentHit() const TerminalSolution::SearchHit currentHit() const
{ {
return m_currentHit >= 0 ? m_hits.at(m_currentHit) : SearchHit{}; return m_currentHit >= 0 ? m_hits.at(m_currentHit) : TerminalSolution::SearchHit{};
} }
public: public:
@@ -65,17 +55,17 @@ signals:
protected: protected:
void updateHits(); void updateHits();
void debouncedUpdateHits(); void debouncedUpdateHits();
QList<SearchHit> search(); QList<TerminalSolution::SearchHit> search();
QList<SearchHit> searchRegex(); QList<TerminalSolution::SearchHit> searchRegex();
private: private:
std::optional<SearchHitWithText> m_currentSelection; std::optional<SearchHitWithText> m_currentSelection;
QString m_currentSearchString; QString m_currentSearchString;
Utils::FindFlags m_findFlags; Utils::FindFlags m_findFlags;
Internal::TerminalSurface *m_surface; TerminalSolution::TerminalSurface *m_surface;
int m_currentHit{-1}; int m_currentHit{-1};
QList<SearchHit> m_hits; QList<TerminalSolution::SearchHit> m_hits;
QTimer m_debounceTimer; QTimer m_debounceTimer;
}; };

View File

@@ -8,8 +8,6 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/dialogs/ioptionspage.h>
#include <libptyqt/ptyqt.h>
#include <utils/dropsupport.h> #include <utils/dropsupport.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/expected.h> #include <utils/expected.h>
@@ -72,7 +70,6 @@ void setupColor(TerminalSettings *settings,
color.setSettingsKey(label); color.setSettingsKey(label);
color.setDefaultValue(defaultColor); color.setDefaultValue(defaultColor);
color.setToolTip(Tr::tr("The color used for %1.").arg(label)); color.setToolTip(Tr::tr("The color used for %1.").arg(label));
settings->registerAspect(&color); settings->registerAspect(&color);
} }
@@ -479,7 +476,7 @@ TerminalSettings::TerminalSettings()
fontComboBox->setCurrentFont(font()); fontComboBox->setCurrentFont(font());
connect(fontComboBox, &QFontComboBox::currentFontChanged, this, [this](const QFont &f) { connect(fontComboBox, &QFontComboBox::currentFontChanged, this, [this](const QFont &f) {
font.setValue(f.family()); font.setVolatileValue(f.family());
}); });
auto loadThemeButton = new QPushButton(Tr::tr("Load Theme...")); auto loadThemeButton = new QPushButton(Tr::tr("Load Theme..."));

File diff suppressed because it is too large Load Diff

View File

@@ -3,83 +3,36 @@
#pragma once #pragma once
#include "shellintegration.h"
#include "shortcutmap.h" #include "shortcutmap.h"
#include "terminalsearch.h" #include "terminalsearch.h"
#include "terminalsurface.h"
#include <solutions/terminal/terminalview.h>
#include <aggregation/aggregate.h> #include <aggregation/aggregate.h>
#include <coreplugin/icontext.h>
#include <coreplugin/actionmanager/command.h> #include <coreplugin/actionmanager/command.h>
#include <coreplugin/icontext.h>
#include <utils/link.h> #include <utils/link.h>
#include <utils/process.h> #include <utils/process.h>
#include <utils/terminalhooks.h> #include <utils/terminalhooks.h>
#include <QAbstractScrollArea>
#include <QAction>
#include <QTextLayout>
#include <QTimer>
#include <chrono>
#include <memory>
namespace Terminal { namespace Terminal {
using RegisteredAction = std::unique_ptr<QAction, std::function<void(QAction *)>>; using RegisteredAction = std::unique_ptr<QAction, std::function<void(QAction *)>>;
class TerminalWidget : public QAbstractScrollArea class TerminalWidget : public TerminalSolution::TerminalView
{ {
friend class CellIterator;
Q_OBJECT Q_OBJECT
public: public:
TerminalWidget(QWidget *parent = nullptr, TerminalWidget(QWidget *parent = nullptr,
const Utils::Terminal::OpenTerminalParameters &openParameters = {}); const Utils::Terminal::OpenTerminalParameters &openParameters = {});
void setFont(const QFont &font);
void copyToClipboard();
void pasteFromClipboard();
void copyLinkToClipboard();
void clearSelection();
void zoomIn();
void zoomOut();
void moveCursorWordLeft();
void moveCursorWordRight();
void clearContents();
void closeTerminal(); void closeTerminal();
TerminalSearch *search() { return m_search.get(); } TerminalSearch *search() { return m_search.get(); }
struct Selection
{
int start;
int end;
bool final{false};
bool operator!=(const Selection &other) const
{
return start != other.start || end != other.end || final != other.final;
}
bool operator==(const Selection &other) const { return !operator!=(other); }
};
struct LinkSelection : public Selection
{
Utils::Link link;
bool operator!=(const LinkSelection &other) const
{
return link != other.link || Selection::operator!=(other);
}
};
void setShellName(const QString &shellName); void setShellName(const QString &shellName);
QString shellName() const; QString shellName() const;
QString title() const; QString title() const;
@@ -102,142 +55,52 @@ signals:
void titleChanged(); void titleChanged();
protected: protected:
void paintEvent(QPaintEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void focusInEvent(QFocusEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
void inputMethodEvent(QInputMethodEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override; void dropEvent(QDropEvent *event) override;
void showEvent(QShowEvent *event) override; void showEvent(QShowEvent *event) override;
void focusInEvent(QFocusEvent *event) override;
bool event(QEvent *event) override; bool event(QEvent *event) override;
protected:
void onReadyRead(bool forceFlush); void onReadyRead(bool forceFlush);
void setupSurface();
void setupFont(); void setupFont();
void setupPty(); void setupPty();
void setupColors(); void setupColors();
void setupActions(); void setupActions();
void writeToPty(const QByteArray &data); void handleEscKey(QKeyEvent *event);
int paintCell(QPainter &p, void surfaceChanged() override;
const QRectF &cellRect,
QPoint gridPos,
const Internal::TerminalCell &cell,
QFont &f,
QList<SearchHit>::const_iterator &searchIt) const;
void paintCells(QPainter &painter, QPaintEvent *event) const;
void paintCursor(QPainter &painter) const;
void paintPreedit(QPainter &painter) const;
bool paintFindMatches(QPainter &painter,
QList<SearchHit>::const_iterator &searchIt,
const QRectF &cellRect,
const QPoint gridPos) const;
bool paintSelection(QPainter &painter, const QRectF &cellRect, const QPoint gridPos) const;
void paintDebugSelection(QPainter &painter, const Selection &selection) const;
qreal topMargin() const; void selectionChanged(const std::optional<Selection> &newSelection) override;
void linkActivated(const Link &link) override;
void contextMenuRequested(const QPoint &pos) override;
QPoint viewportToGlobal(QPoint p) const; void writeToPty(const QByteArray &data) override;
QPoint globalToViewport(QPoint p) const; void resizePty(QSize newSize) override;
QPoint globalToGrid(QPointF p) const; void setClipboard(const QString &text) override;
QPointF gridToGlobal(QPoint p, bool bottom = false, bool right = false) const; std::optional<TerminalView::Link> toLink(const QString &text) override;
QRect gridToViewport(QRect rect) const;
QPoint toGridPos(QMouseEvent *event) const;
void updateViewport(); const QList<TerminalSolution::SearchHit> &searchHits() const override;
void updateViewportRect(const QRect &rect);
int textLineFromPixel(int y) const;
std::optional<int> textPosFromPoint(const QTextLayout &textLayout, QPoint p) const;
std::optional<QTextLayout::FormatRange> selectionToFormatRange(
TerminalWidget::Selection selection, const QTextLayout &layout, int rowOffset) const;
bool checkLinkAt(const QPoint &pos);
struct TextAndOffsets
{
int start;
int end;
std::u32string text;
};
TextAndOffsets textAt(const QPoint &pos) const;
void applySizeChange();
void updateScrollBars();
void flushVTerm(bool force);
bool setSelection(const std::optional<Selection> &selection, bool scroll = true);
QString textFromSelection() const;
void configBlinkTimer();
QColor toQColor(std::variant<int, QColor> color) const;
void updateCopyState();
RegisteredAction registerAction(Utils::Id commandId, const Core::Context &context); RegisteredAction registerAction(Utils::Id commandId, const Core::Context &context);
void registerShortcut(Core::Command *command); void registerShortcut(Core::Command *command);
void updateCopyState();
private: private:
Core::Context m_context; Core::Context m_context;
std::unique_ptr<Utils::Process> m_process; std::unique_ptr<Utils::Process> m_process;
std::unique_ptr<Internal::TerminalSurface> m_surface;
std::unique_ptr<ShellIntegration> m_shellIntegration; std::unique_ptr<ShellIntegration> m_shellIntegration;
QString m_shellName; QString m_shellName;
Utils::Id m_identifier;
QFont m_font;
QSizeF m_cellSize;
bool m_ignoreScroll{false};
QString m_preEditString;
QString m_title; QString m_title;
std::optional<Selection> m_selection; TerminalSolution::SearchHit m_lastSelectedHit{};
std::optional<LinkSelection> m_linkSelection;
struct Utils::Id m_identifier;
{
QPoint start;
QPoint end;
} m_activeMouseSelect;
QTimer m_flushDelayTimer;
QTimer m_scrollTimer;
int m_scrollDirection{0};
std::array<QColor, 20> m_currentColors;
Utils::Terminal::OpenTerminalParameters m_openParameters; Utils::Terminal::OpenTerminalParameters m_openParameters;
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};
Utils::FilePath m_cwd; Utils::FilePath m_cwd;
Utils::CommandLine m_currentCommand; Utils::CommandLine m_currentCommand;
@@ -245,7 +108,6 @@ private:
TerminalSearchPtr m_search; TerminalSearchPtr m_search;
Aggregation::Aggregate *m_aggregate{nullptr}; Aggregation::Aggregate *m_aggregate{nullptr};
SearchHit m_lastSelectedHit{};
RegisteredAction m_copy; RegisteredAction m_copy;
RegisteredAction m_paste; RegisteredAction m_paste;

View File

@@ -0,0 +1,67 @@
#!/bin/sh
function cleanup {
stty -echo
printf "\e[?1006;1000l"
}
trap cleanup EXIT
stty -echo
# Enable SGR protocol and button press and release events
printf "\e[?1006;1000h"
while read -n 1 line
do
printf -v ch "%d" \'$line
if [ "27" != "$ch" ]; then
continue
fi
read -n 1 line
if [ "[" != "$line" ]; then
continue
fi
read -n 1 line
if [ "<" != "$line" ]; then
continue
fi
# Read button state
modifier=
while read -n 1 line
do
if [ ";" = "$line" ]; then
# End
break
fi
printf -v modifier "$modifier$line"
done
# Read column
col=
while read -n 1 line
do
if [ ";" = "$line" ]; then
# End
break
fi
printf -v col "$col$line"
done
# Read row
row=
while read -n 1 line
do
if [ "M" = "$line" ] || [ "m" = "$line" ]; then
# End
btn=$line
break
fi
printf -v row "$row$line"
done
if [ "M" = "$btn" ]; then
echo "You pressed at $col x $row (mods: $modifier)"
else
echo "You released at $col x $row (mods: $modifier)"
fi
done < "${1:-/dev/stdin}"