From 79e839b29ffefa509ee71f78e4b7b057e3a74429 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 25 Jul 2023 09:16:48 +0200 Subject: [PATCH] Terminal: Add mouse support Change-Id: Ibeb8e13b5f8f75f16ec86f64536235587c844ffc Reviewed-by: Cristian Adam --- src/libs/3rdparty/libptyqt/conptyprocess.cpp | 80 ++++++++++++++++++-- src/libs/3rdparty/libptyqt/conptyprocess.h | 77 +------------------ src/libs/3rdparty/libptyqt/iptyprocess.h | 1 - src/libs/3rdparty/libptyqt/ptyqt.cpp | 13 +++- src/libs/3rdparty/libptyqt/ptyqt.h | 2 + src/libs/3rdparty/libptyqt/unixptyprocess.h | 2 +- src/libs/3rdparty/libptyqt/winptyprocess.h | 2 +- src/libs/3rdparty/libvterm/src/state.c | 12 ++- src/plugins/terminal/CMakeLists.txt | 2 +- src/plugins/terminal/terminalpane.cpp | 45 ++++++----- src/plugins/terminal/terminalpane.h | 1 - src/plugins/terminal/terminalsettings.cpp | 15 ++++ src/plugins/terminal/terminalsettings.h | 2 + src/plugins/terminal/terminalsurface.cpp | 33 ++++++++ src/plugins/terminal/terminalsurface.h | 2 + src/plugins/terminal/terminalwidget.cpp | 33 ++++++++ src/plugins/terminal/terminalwidget.h | 1 + 17 files changed, 211 insertions(+), 112 deletions(-) diff --git a/src/libs/3rdparty/libptyqt/conptyprocess.cpp b/src/libs/3rdparty/libptyqt/conptyprocess.cpp index cb18b332066..6464dbeb347 100644 --- a/src/libs/3rdparty/libptyqt/conptyprocess.cpp +++ b/src/libs/3rdparty/libptyqt/conptyprocess.cpp @@ -12,6 +12,76 @@ #define READ_INTERVAL_MSEC 500 +//ConPTY is available only on Windows 10 released after 1903 (19H1) Windows release +class WindowsContext +{ +private: + WindowsContext() {} + +public: + typedef HRESULT (*CreatePseudoConsolePtr)( + COORD size, // ConPty Dimensions + HANDLE hInput, // ConPty Input + HANDLE hOutput, // ConPty Output + DWORD dwFlags, // ConPty Flags + HPCON* phPC); // ConPty Reference + + typedef HRESULT (*ResizePseudoConsolePtr)(HPCON hPC, COORD size); + + typedef VOID (*ClosePseudoConsolePtr)(HPCON hPC); + + static WindowsContext &instance() + { + static WindowsContext ctx; + return ctx; + } + + bool init() + { + //already initialized + if (createPseudoConsole) + return true; + + //try to load symbols from library + //if it fails -> we can't use ConPty API + HANDLE kernel32Handle = LoadLibraryExW(L"kernel32.dll", 0, 0); + + if (kernel32Handle != nullptr) + { + createPseudoConsole = (CreatePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "CreatePseudoConsole"); + resizePseudoConsole = (ResizePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ResizePseudoConsole"); + closePseudoConsole = (ClosePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ClosePseudoConsole"); + if (createPseudoConsole == NULL || resizePseudoConsole == NULL || closePseudoConsole == NULL) + { + m_lastError = QString("WindowsContext/ConPty error: %1").arg("Invalid on load API functions"); + return false; + } + } + else + { + m_lastError = QString("WindowsContext/ConPty error: %1").arg("Unable to load kernel32"); + return false; + } + + return true; + } + + QString lastError() + { + return m_lastError; + } + +public: + //vars + CreatePseudoConsolePtr createPseudoConsole{nullptr}; + ResizePseudoConsolePtr resizePseudoConsole{nullptr}; + ClosePseudoConsolePtr closePseudoConsole{nullptr}; + +private: + QString m_lastError; +}; + + HRESULT ConPtyProcess::createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows) { HRESULT hr{ E_UNEXPECTED }; @@ -23,7 +93,7 @@ HRESULT ConPtyProcess::createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0)) { // Create the Pseudo Console of the required size, attached to the PTY-end of the pipes - hr = m_winContext.createPseudoConsole({cols, rows}, hPipePTYIn, hPipePTYOut, 0, phPC); + hr = WindowsContext::instance().createPseudoConsole({cols, rows}, hPipePTYIn, hPipePTYOut, 0, phPC); // Note: We can close the handles to the PTY-end of the pipes here // because the handles are dup'ed into the ConHost and will be released @@ -108,7 +178,7 @@ bool ConPtyProcess::startProcess(const QString &executable, qint16 rows) { if (!isAvailable()) { - m_lastError = m_winContext.lastError(); + m_lastError = WindowsContext::instance().lastError(); return false; } @@ -228,7 +298,7 @@ bool ConPtyProcess::resize(qint16 cols, qint16 rows) return false; } - bool res = SUCCEEDED(m_winContext.resizePseudoConsole(m_ptyHandler, {cols, rows})); + bool res = SUCCEEDED(WindowsContext::instance().resizePseudoConsole(m_ptyHandler, {cols, rows})); if (res) { @@ -248,7 +318,7 @@ bool ConPtyProcess::kill() m_aboutToDestruct = true; // Close ConPTY - this will terminate client process if running - m_winContext.closePseudoConsole(m_ptyHandler); + WindowsContext::instance().closePseudoConsole(m_ptyHandler); // Clean-up the pipes if (INVALID_HANDLE_VALUE != m_hPipeOut) @@ -334,7 +404,7 @@ bool ConPtyProcess::isAvailable() qint32 buildNumber = QSysInfo::kernelVersion().split(".").last().toInt(); if (buildNumber < CONPTY_MINIMAL_WINDOWS_VERSION) return false; - return m_winContext.init(); + return WindowsContext::instance().init(); } void ConPtyProcess::moveToThread(QThread *targetThread) diff --git a/src/libs/3rdparty/libptyqt/conptyprocess.h b/src/libs/3rdparty/libptyqt/conptyprocess.h index d4ffd62b7ee..aaf56fe76f1 100644 --- a/src/libs/3rdparty/libptyqt/conptyprocess.h +++ b/src/libs/3rdparty/libptyqt/conptyprocess.h @@ -25,80 +25,6 @@ typedef VOID* HPCON; class QWinEventNotifier; -template -std::vector vectorFromString(const std::basic_string &str) -{ - return std::vector(str.begin(), str.end()); -} - -//ConPTY available only on Windows 10 releazed after 1903 (19H1) Windows release -class WindowsContext -{ -public: - typedef HRESULT (*CreatePseudoConsolePtr)( - COORD size, // ConPty Dimensions - HANDLE hInput, // ConPty Input - HANDLE hOutput, // ConPty Output - DWORD dwFlags, // ConPty Flags - HPCON* phPC); // ConPty Reference - - typedef HRESULT (*ResizePseudoConsolePtr)(HPCON hPC, COORD size); - - typedef VOID (*ClosePseudoConsolePtr)(HPCON hPC); - - WindowsContext() - : createPseudoConsole(nullptr) - , resizePseudoConsole(nullptr) - , closePseudoConsole(nullptr) - { - - } - - bool init() - { - //already initialized - if (createPseudoConsole) - return true; - - //try to load symbols from library - //if it fails -> we can't use ConPty API - HANDLE kernel32Handle = LoadLibraryExW(L"kernel32.dll", 0, 0); - - if (kernel32Handle != nullptr) - { - createPseudoConsole = (CreatePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "CreatePseudoConsole"); - resizePseudoConsole = (ResizePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ResizePseudoConsole"); - closePseudoConsole = (ClosePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ClosePseudoConsole"); - if (createPseudoConsole == NULL || resizePseudoConsole == NULL || closePseudoConsole == NULL) - { - m_lastError = QString("WindowsContext/ConPty error: %1").arg("Invalid on load API functions"); - return false; - } - } - else - { - m_lastError = QString("WindowsContext/ConPty error: %1").arg("Unable to load kernel32"); - return false; - } - - return true; - } - - QString lastError() - { - return m_lastError; - } - -public: - //vars - CreatePseudoConsolePtr createPseudoConsole; - ResizePseudoConsolePtr resizePseudoConsole; - ClosePseudoConsolePtr closePseudoConsole; - -private: - QString m_lastError; -}; - class PtyBuffer : public QIODevice { friend class ConPtyProcess; @@ -141,7 +67,7 @@ public: virtual QIODevice *notifier(); virtual QByteArray readAll(); virtual qint64 write(const QByteArray &byteArray); - bool isAvailable(); + static bool isAvailable(); void moveToThread(QThread *targetThread); private: @@ -149,7 +75,6 @@ private: HRESULT initializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC); private: - WindowsContext m_winContext; HPCON m_ptyHandler{INVALID_HANDLE_VALUE}; HANDLE m_hPipeIn{INVALID_HANDLE_VALUE}, m_hPipeOut{INVALID_HANDLE_VALUE}; diff --git a/src/libs/3rdparty/libptyqt/iptyprocess.h b/src/libs/3rdparty/libptyqt/iptyprocess.h index 3d974908c86..7c255b9a5e8 100644 --- a/src/libs/3rdparty/libptyqt/iptyprocess.h +++ b/src/libs/3rdparty/libptyqt/iptyprocess.h @@ -32,7 +32,6 @@ public: virtual QIODevice *notifier() = 0; virtual QByteArray readAll() = 0; virtual qint64 write(const QByteArray &byteArray) = 0; - virtual bool isAvailable() = 0; virtual void moveToThread(QThread *targetThread) = 0; qint64 pid() { return m_pid; } QPair size() { return m_size; } diff --git a/src/libs/3rdparty/libptyqt/ptyqt.cpp b/src/libs/3rdparty/libptyqt/ptyqt.cpp index b3e7aa1b164..06fe4498190 100644 --- a/src/libs/3rdparty/libptyqt/ptyqt.cpp +++ b/src/libs/3rdparty/libptyqt/ptyqt.cpp @@ -10,6 +10,15 @@ #include "unixptyprocess.h" #endif +bool PtyQt::isUsingConPTY() +{ +#ifdef Q_OS_WIN + if (ConPtyProcess::isAvailable() && qgetenv("QTC_USE_WINPTY").isEmpty()) + return true; +#endif + + return false; +} IPtyProcess *PtyQt::createPtyProcess(IPtyProcess::PtyType ptyType) { @@ -34,7 +43,7 @@ IPtyProcess *PtyQt::createPtyProcess(IPtyProcess::PtyType ptyType) } #ifdef Q_OS_WIN - if (ConPtyProcess().isAvailable() && qgetenv("QTC_USE_WINPTY").isEmpty()) + if (isUsingConPTY()) return new ConPtyProcess(); else return new WinPtyProcess(); @@ -43,3 +52,5 @@ IPtyProcess *PtyQt::createPtyProcess(IPtyProcess::PtyType ptyType) return new UnixPtyProcess(); #endif } + + diff --git a/src/libs/3rdparty/libptyqt/ptyqt.h b/src/libs/3rdparty/libptyqt/ptyqt.h index 23b80d346bb..85f27b33a9b 100644 --- a/src/libs/3rdparty/libptyqt/ptyqt.h +++ b/src/libs/3rdparty/libptyqt/ptyqt.h @@ -7,6 +7,8 @@ class PtyQt { public: static IPtyProcess *createPtyProcess(IPtyProcess::PtyType ptyType); + + static bool isUsingConPTY(); }; #endif // PTYQT_H diff --git a/src/libs/3rdparty/libptyqt/unixptyprocess.h b/src/libs/3rdparty/libptyqt/unixptyprocess.h index e4df0d2f74d..f7e3a2e5281 100644 --- a/src/libs/3rdparty/libptyqt/unixptyprocess.h +++ b/src/libs/3rdparty/libptyqt/unixptyprocess.h @@ -54,7 +54,7 @@ public: virtual QIODevice *notifier(); virtual QByteArray readAll(); virtual qint64 write(const QByteArray &byteArray); - virtual bool isAvailable(); + static bool isAvailable(); void moveToThread(QThread *targetThread); private: diff --git a/src/libs/3rdparty/libptyqt/winptyprocess.h b/src/libs/3rdparty/libptyqt/winptyprocess.h index 0bfb27c02c4..ee91fbdf8d6 100644 --- a/src/libs/3rdparty/libptyqt/winptyprocess.h +++ b/src/libs/3rdparty/libptyqt/winptyprocess.h @@ -26,7 +26,7 @@ public: QIODevice *notifier(); QByteArray readAll(); qint64 write(const QByteArray &byteArray); - bool isAvailable(); + static bool isAvailable(); void moveToThread(QThread *targetThread); private: diff --git a/src/libs/3rdparty/libvterm/src/state.c b/src/libs/3rdparty/libvterm/src/state.c index 313e746e77c..5362efbcd35 100644 --- a/src/libs/3rdparty/libvterm/src/state.c +++ b/src/libs/3rdparty/libvterm/src/state.c @@ -1311,8 +1311,10 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha break; case LEADER('?', 0x68): // DEC private mode set - if(!CSI_ARG_IS_MISSING(args[0])) - set_dec_mode(state, CSI_ARG(args[0]), 1); + for (int i = 0; i < argcount; i++) { + if (!CSI_ARG_IS_MISSING(args[i])) + set_dec_mode(state, CSI_ARG(args[i]), 1); + } break; case 0x6a: // HPB - ECMA-48 8.3.58 @@ -1333,8 +1335,10 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha break; case LEADER('?', 0x6c): // DEC private mode reset - if(!CSI_ARG_IS_MISSING(args[0])) - set_dec_mode(state, CSI_ARG(args[0]), 0); + for (int i = 0; i < argcount; i++) { + if (!CSI_ARG_IS_MISSING(args[i])) + set_dec_mode(state, CSI_ARG(args[i]), 0); + } break; case 0x6d: // SGR - ECMA-48 8.3.117 diff --git a/src/plugins/terminal/CMakeLists.txt b/src/plugins/terminal/CMakeLists.txt index c7b64cc8ad4..bf405b672ab 100644 --- a/src/plugins/terminal/CMakeLists.txt +++ b/src/plugins/terminal/CMakeLists.txt @@ -1,7 +1,7 @@ add_qtc_plugin(Terminal PLUGIN_DEPENDS Core ProjectExplorer - DEPENDS libvterm + DEPENDS libvterm ptyqt SOURCES celliterator.cpp celliterator.h glyphcache.cpp glyphcache.h diff --git a/src/plugins/terminal/terminalpane.cpp b/src/plugins/terminal/terminalpane.cpp index 853e1f66a05..3e4327dc235 100644 --- a/src/plugins/terminal/terminalpane.cpp +++ b/src/plugins/terminal/terminalpane.cpp @@ -54,9 +54,6 @@ TerminalPane::TerminalPane(QObject *parent) initActions(); - m_lockKeyboardButton = new QToolButton(); - m_lockKeyboardButton->setDefaultAction(&lockKeyboard); - m_newTerminalButton = new QToolButton(); m_newTerminalButton->setDefaultAction(&newTerminal); @@ -94,11 +91,34 @@ TerminalPane::TerminalPane(QObject *parent) connect(m_escSettingButton, &QToolButton::toggled, this, [this, updateEscButton] { settings().sendEscapeToTerminal.setValue(m_escSettingButton->isChecked()); - settings().writeSettings(); updateEscButton(); }); connect(&settings(), &TerminalSettings::applied, this, updateEscButton); + + const auto updateLockButton = [this] { + m_lockKeyboardButton->setChecked(settings().lockKeyboard()); + if (settings().lockKeyboard()) { + m_lockKeyboardButton->setIcon(LOCK_KEYBOARD_ICON.icon()); + m_lockKeyboardButton->setToolTip( + Tr::tr("Qt Creator shortcuts are blocked when focus is inside the terminal.")); + } else { + m_lockKeyboardButton->setIcon(UNLOCK_KEYBOARD_ICON.icon()); + m_lockKeyboardButton->setToolTip(Tr::tr("Qt Creator shortcuts take precedence.")); + } + }; + + m_lockKeyboardButton = new QToolButton(); + m_lockKeyboardButton->setCheckable(true); + + updateLockButton(); + + connect(m_lockKeyboardButton, &QToolButton::toggled, this, [this, updateLockButton] { + settings().lockKeyboard.setValue(m_lockKeyboardButton->isChecked()); + updateLockButton(); + }); + + connect(&settings(), &TerminalSettings::applied, this, updateLockButton); } TerminalPane::~TerminalPane() {} @@ -249,23 +269,6 @@ void TerminalPane::initActions() { createShellMenu(); - lockKeyboard.setCheckable(true); - lockKeyboard.setChecked(settings().lockKeyboard()); - - auto updateLockKeyboard = [this](bool locked) { - settings().lockKeyboard.setValue(locked); - if (locked) { - lockKeyboard.setIcon(LOCK_KEYBOARD_ICON.icon()); - lockKeyboard.setToolTip(Tr::tr("Sends keyboard shortcuts to Terminal.")); - } else { - lockKeyboard.setIcon(UNLOCK_KEYBOARD_ICON.icon()); - lockKeyboard.setToolTip(Tr::tr("Sends keyboard shortcuts to Qt Creator.")); - } - }; - - updateLockKeyboard(settings().lockKeyboard()); - connect(&lockKeyboard, &QAction::toggled, this, updateLockKeyboard); - newTerminal.setText(Tr::tr("New Terminal")); newTerminal.setIcon(NEW_TERMINAL_ICON.icon()); newTerminal.setToolTip(Tr::tr("Create a new Terminal.")); diff --git a/src/plugins/terminal/terminalpane.h b/src/plugins/terminal/terminalpane.h index 8a24e1cf302..bc25ade2b14 100644 --- a/src/plugins/terminal/terminalpane.h +++ b/src/plugins/terminal/terminalpane.h @@ -68,7 +68,6 @@ private: QAction nextTerminal; QAction prevTerminal; QAction closeTerminal; - QAction lockKeyboard; QMenu m_shellMenu; diff --git a/src/plugins/terminal/terminalsettings.cpp b/src/plugins/terminal/terminalsettings.cpp index f78173aa955..9240a100b60 100644 --- a/src/plugins/terminal/terminalsettings.cpp +++ b/src/plugins/terminal/terminalsettings.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include #include #include @@ -410,12 +412,23 @@ TerminalSettings::TerminalSettings() "instead of closing the terminal.")); sendEscapeToTerminal.setDefaultValue(false); + lockKeyboard.setSettingsKey("LockKeyboard"); + lockKeyboard.setLabelText(Tr::tr("Block shortcuts in terminal")); + lockKeyboard.setToolTip( + Tr::tr("Keeps Qt Creator short cuts from interfering with the terminal.")); + lockKeyboard.setDefaultValue(true); + audibleBell.setSettingsKey("AudibleBell"); audibleBell.setLabelText(Tr::tr("Audible bell")); audibleBell.setToolTip(Tr::tr("Makes the terminal beep when a bell " "character is received.")); audibleBell.setDefaultValue(true); + enableMouseTracking.setSettingsKey("EnableMouseTracking"); + enableMouseTracking.setLabelText(Tr::tr("Enable mouse tracking")); + enableMouseTracking.setToolTip(Tr::tr("Enables mouse tracking in the terminal.")); + enableMouseTracking.setDefaultValue(true); + setupColor(this, foregroundColor, "Foreground", @@ -528,8 +541,10 @@ TerminalSettings::TerminalSettings() Column { enableTerminal, st, sendEscapeToTerminal, st, + lockKeyboard, st, audibleBell, st, allowBlinkingCursor, st, + enableMouseTracking, st, }, }, Group { diff --git a/src/plugins/terminal/terminalsettings.h b/src/plugins/terminal/terminalsettings.h index e253abf69e8..1400339dd8d 100644 --- a/src/plugins/terminal/terminalsettings.h +++ b/src/plugins/terminal/terminalsettings.h @@ -31,6 +31,8 @@ public: Utils::BoolAspect sendEscapeToTerminal{this}; Utils::BoolAspect audibleBell{this}; Utils::BoolAspect lockKeyboard{this}; + + Utils::BoolAspect enableMouseTracking{this}; }; TerminalSettings &settings(); diff --git a/src/plugins/terminal/terminalsurface.cpp b/src/plugins/terminal/terminalsurface.cpp index 802ff59a124..24e881b35cf 100644 --- a/src/plugins/terminal/terminalsurface.cpp +++ b/src/plugins/terminal/terminalsurface.cpp @@ -494,6 +494,39 @@ ShellIntegration *TerminalSurface::shellIntegration() const return d->m_shellIntegration; } +void TerminalSurface::mouseMove(QPoint pos, Qt::KeyboardModifiers modifiers) +{ + vterm_mouse_move(d->m_vterm.get(), pos.y(), pos.x(), Internal::qtModifierToVTerm(modifiers)); +} + +void TerminalSurface::mouseButton(Qt::MouseButton button, + bool pressed, + Qt::KeyboardModifiers modifiers) +{ + int btnIdx = 0; + switch (button) { + case Qt::LeftButton: + btnIdx = 1; + break; + case Qt::RightButton: + btnIdx = 3; + break; + case Qt::MiddleButton: + btnIdx = 2; + break; + case Qt::ExtraButton1: + btnIdx = 4; + break; + case Qt::ExtraButton2: + btnIdx = 5; + break; + default: + return; + } + + vterm_mouse_button(d->m_vterm.get(), btnIdx, pressed, Internal::qtModifierToVTerm(modifiers)); +} + CellIterator TerminalSurface::begin() const { auto res = CellIterator(this, {0, 0}); diff --git a/src/plugins/terminal/terminalsurface.h b/src/plugins/terminal/terminalsurface.h index f6e463ac8dd..09a86f26e73 100644 --- a/src/plugins/terminal/terminalsurface.h +++ b/src/plugins/terminal/terminalsurface.h @@ -95,6 +95,8 @@ public: ShellIntegration *shellIntegration() const; + void mouseMove(QPoint pos, Qt::KeyboardModifiers modifiers); + void mouseButton(Qt::MouseButton button, bool pressed, Qt::KeyboardModifiers modifiers); signals: void writeToPty(const QByteArray &data); void invalidated(QRect grid); diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index 63826ea6d99..72b7e103483 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -1280,6 +1280,14 @@ void TerminalWidget::updateViewportRect(const QRect &rect) void TerminalWidget::wheelEvent(QWheelEvent *event) { verticalScrollBar()->event(event); + + if (!settings().enableMouseTracking()) + return; + + if (event->angleDelta().ry() > 0) + m_surface->mouseButton(Qt::ExtraButton1, true, event->modifiers()); + else if (event->angleDelta().ry() < 0) + m_surface->mouseButton(Qt::ExtraButton2, true, event->modifiers()); } void TerminalWidget::focusInEvent(QFocusEvent *) @@ -1306,8 +1314,18 @@ void TerminalWidget::inputMethodEvent(QInputMethodEvent *event) m_surface->sendKey(event->commitString()); } +QPoint TerminalWidget::toGridPos(QMouseEvent *event) const +{ + return globalToGrid(event->pos().toPointF() + QPointF(0, -topMargin() + 0.5)); +} + void TerminalWidget::mousePressEvent(QMouseEvent *event) { + if (settings().enableMouseTracking()) { + m_surface->mouseMove(toGridPos(event), event->modifiers()); + m_surface->mouseButton(event->button(), true, event->modifiers()); + } + m_scrollDirection = 0; m_activeMouseSelect.start = viewportToGlobal(event->pos()); @@ -1383,8 +1401,12 @@ void TerminalWidget::mousePressEvent(QMouseEvent *event) } } } + void TerminalWidget::mouseMoveEvent(QMouseEvent *event) { + if (settings().enableMouseTracking()) + m_surface->mouseMove(toGridPos(event), event->modifiers()); + if (m_selection && event->buttons() & Qt::LeftButton) { Selection newSelection = *m_selection; int scrollVelocity = 0; @@ -1487,6 +1509,11 @@ bool TerminalWidget::checkLinkAt(const QPoint &pos) void TerminalWidget::mouseReleaseEvent(QMouseEvent *event) { + if (settings().enableMouseTracking()) { + m_surface->mouseMove(toGridPos(event), event->modifiers()); + m_surface->mouseButton(event->button(), false, event->modifiers()); + } + m_scrollTimer.stop(); if (m_selection && event->button() == Qt::LeftButton) { @@ -1528,6 +1555,12 @@ TerminalWidget::TextAndOffsets TerminalWidget::textAt(const QPoint &pos) const void TerminalWidget::mouseDoubleClickEvent(QMouseEvent *event) { + if (settings().enableMouseTracking()) { + m_surface->mouseMove(toGridPos(event), event->modifiers()); + m_surface->mouseButton(event->button(), true, event->modifiers()); + m_surface->mouseButton(event->button(), false, event->modifiers()); + } + const auto hit = textAt(event->pos()); setSelection(Selection{hit.start, hit.end, true}); diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h index 66f721df5da..2801ea4fc16 100644 --- a/src/plugins/terminal/terminalwidget.h +++ b/src/plugins/terminal/terminalwidget.h @@ -156,6 +156,7 @@ protected: 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);