diff --git a/src/libs/3rdparty/libptyqt/iptyprocess.h b/src/libs/3rdparty/libptyqt/iptyprocess.h index 7c255b9a5e8..6005b0efde1 100644 --- a/src/libs/3rdparty/libptyqt/iptyprocess.h +++ b/src/libs/3rdparty/libptyqt/iptyprocess.h @@ -11,6 +11,8 @@ class IPtyProcess { public: enum PtyType { UnixPty = 0, WinPty = 1, ConPty = 2, AutoPty = 3 }; + enum PtyInputFlag { None = 0x0, InputModeHidden = 0x1, }; + Q_DECLARE_FLAGS(PtyInputFlags, PtyInputFlag) IPtyProcess() = default; IPtyProcess(const IPtyProcess &) = delete; @@ -43,6 +45,8 @@ public: return m_trace; } + PtyInputFlags inputFlags() { return m_inputFlags; } + protected: QString m_shellPath; QString m_lastError; @@ -50,6 +54,9 @@ protected: int m_exitCode{0}; QPair m_size; //cols / rows bool m_trace{false}; + PtyInputFlags m_inputFlags; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(IPtyProcess::PtyInputFlags) + #endif // IPTYPROCESS_H diff --git a/src/libs/3rdparty/libptyqt/unixptyprocess.cpp b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp index 91dbf36d7ec..e9ec1d590f6 100644 --- a/src/libs/3rdparty/libptyqt/unixptyprocess.cpp +++ b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp @@ -172,6 +172,12 @@ bool UnixPtyProcess::startProcess(const QString &shellPath, static std::array buffer; int len = ::read(m_shellProcess.m_handleMaster, buffer.data(), buffer.size()); + + struct termios termAttributes; + tcgetattr(m_shellProcess.m_handleMaster, &termAttributes); + const bool isPasswordEntry = !(termAttributes.c_lflag & ECHO) && (termAttributes.c_lflag & ICANON); + m_inputFlags.setFlag(PtyInputFlag::InputModeHidden, isPasswordEntry); + if (len > 0) { m_shellReadBuffer.append(buffer.data(), len); m_shellProcess.emitReadyRead(); diff --git a/src/libs/solutions/terminal/CMakeLists.txt b/src/libs/solutions/terminal/CMakeLists.txt index 58c3e8780d4..0cf48fefbc7 100644 --- a/src/libs/solutions/terminal/CMakeLists.txt +++ b/src/libs/solutions/terminal/CMakeLists.txt @@ -7,6 +7,7 @@ add_qtc_library(TerminalLib scrollback.cpp scrollback.h surfaceintegration.h terminal_global.h + terminal.qrc terminalsurface.cpp terminalsurface.h terminalview.cpp terminalview.h ) diff --git a/src/libs/solutions/terminal/images/passwordlock.png b/src/libs/solutions/terminal/images/passwordlock.png new file mode 100644 index 00000000000..c548e28cab2 Binary files /dev/null and b/src/libs/solutions/terminal/images/passwordlock.png differ diff --git a/src/libs/solutions/terminal/terminal.qrc b/src/libs/solutions/terminal/terminal.qrc new file mode 100644 index 00000000000..0877ca08b9d --- /dev/null +++ b/src/libs/solutions/terminal/terminal.qrc @@ -0,0 +1,5 @@ + + + images/passwordlock.png + + diff --git a/src/libs/solutions/terminal/terminalview.cpp b/src/libs/solutions/terminal/terminalview.cpp index 2025b4e7546..2244ea62d73 100644 --- a/src/libs/solutions/terminal/terminalview.cpp +++ b/src/libs/solutions/terminal/terminalview.cpp @@ -85,6 +85,7 @@ public: bool m_cursorBlinkState{true}; bool m_allowBlinkingCursor{true}; bool m_allowMouseTracking{true}; + bool m_passwordModeActive{false}; SurfaceIntegration *m_surfaceIntegration{nullptr}; }; @@ -258,6 +259,14 @@ void TerminalView::setColors(const std::array &newColors) update(); } +void TerminalView::setPasswordMode(bool passwordMode) +{ + if (passwordMode != d->m_passwordModeActive) { + d->m_passwordModeActive = passwordMode; + updateViewport(); + } +} + void TerminalView::setFont(const QFont &font) { QAbstractScrollArea::setFont(font); @@ -731,16 +740,37 @@ void TerminalView::paintCursor(QPainter &p) const { auto cursor = d->m_surface->cursor(); - if (!d->m_preEditString.isEmpty()) - cursor.shape = Cursor::Shape::Underline; + const int cursorCellWidth = d->m_surface->cellWidthAt(cursor.position.x(), cursor.position.y()); + if (!d->m_preEditString.isEmpty()) { + cursor.shape = Cursor::Shape::Underline; + } else if (d->m_passwordModeActive) { + QRectF cursorRect = QRectF(gridToGlobal(cursor.position), + gridToGlobal({cursor.position.x() + cursorCellWidth, + cursor.position.y()}, + true)) + .toAlignedRect(); + + const qreal dpr = p.device()->devicePixelRatioF(); + const QString key = QString("terminalpasswordlock-") + % QString::number(cursorRect.size().height()) + % "@" % QString::number(dpr); + QPixmap px; + if (!QPixmapCache::find(key, &px)) { + const QPixmap lock(":/terminal/images/passwordlock.png"); + px = lock.scaledToHeight(cursorRect.size().height() * dpr, Qt::SmoothTransformation); + px.setDevicePixelRatio(dpr); + QPixmapCache::insert(key, px); + } + + p.drawPixmap(cursorRect.topLeft(), px); + + return; + } const bool blinkState = !cursor.blink || d->m_cursorBlinkState || !d->m_allowBlinkingCursor || !d->m_cursorBlinkTimer.isActive(); if (cursor.visible && blinkState) { - const int cursorCellWidth = d->m_surface->cellWidthAt(cursor.position.x(), - cursor.position.y()); - QRectF cursorRect = QRectF(gridToGlobal(cursor.position), gridToGlobal({cursor.position.x() + cursorCellWidth, cursor.position.y()}, @@ -984,7 +1014,10 @@ void TerminalView::updateViewport() void TerminalView::updateViewportRect(const QRect &rect) { - viewport()->update(rect); + if (d->m_passwordModeActive) + viewport()->update(); + else + viewport()->update(rect); } void TerminalView::focusInEvent(QFocusEvent *) diff --git a/src/libs/solutions/terminal/terminalview.h b/src/libs/solutions/terminal/terminalview.h index 59b75c0e7ef..4745c63ee15 100644 --- a/src/libs/solutions/terminal/terminalview.h +++ b/src/libs/solutions/terminal/terminalview.h @@ -87,6 +87,8 @@ public: void setSurfaceIntegration(SurfaceIntegration *surfaceIntegration); void setColors(const std::array &colors); + void setPasswordMode(bool passwordMode); + struct Link { QString text; diff --git a/src/libs/utils/process.cpp b/src/libs/utils/process.cpp index 01e3b7747af..70a1d12b449 100644 --- a/src/libs/utils/process.cpp +++ b/src/libs/utils/process.cpp @@ -410,6 +410,13 @@ public: } connect(m_ptyProcess->notifier(), &QIODevice::readyRead, this, [this] { + if (m_setup.m_ptyData->ptyInputFlagsChangedHandler() + && m_inputFlags != m_ptyProcess->inputFlags()) { + m_inputFlags = m_ptyProcess->inputFlags(); + m_setup.m_ptyData->ptyInputFlagsChangedHandler()( + static_cast(m_inputFlags.toInt())); + } + emit readyRead(m_ptyProcess->readAll(), {}); }); @@ -430,6 +437,7 @@ public: private: std::unique_ptr m_ptyProcess; + IPtyProcess::PtyInputFlags m_inputFlags; }; class QProcessImpl final : public DefaultImpl diff --git a/src/libs/utils/processinterface.h b/src/libs/utils/processinterface.h index 8dedd0183ef..796972d3093 100644 --- a/src/libs/utils/processinterface.h +++ b/src/libs/utils/processinterface.h @@ -18,12 +18,19 @@ namespace Internal { class ProcessPrivate; } namespace Pty { +enum PtyInputFlag { + None = 0x0, + InputModeHidden = 0x1, +}; + using ResizeHandler = std::function; +using PtyInputFlagsChangeHandler = std::function; class QTCREATOR_UTILS_EXPORT SharedData { public: ResizeHandler m_handler; + PtyInputFlagsChangeHandler m_inputFlagsChangedHandler; }; class QTCREATOR_UTILS_EXPORT Data @@ -32,6 +39,15 @@ public: Data() : m_data(new SharedData) {} void setResizeHandler(const ResizeHandler &handler) { m_data->m_handler = handler; } + void setPtyInputFlagsChangedHandler(const PtyInputFlagsChangeHandler &handler) + { + m_data->m_inputFlagsChangedHandler = handler; + } + + PtyInputFlagsChangeHandler ptyInputFlagsChangedHandler() const + { + return m_data->m_inputFlagsChangedHandler; + } QSize size() const { return m_size; } void resize(const QSize &size); diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index 7e4987e3794..3cf390f51cc 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -126,7 +126,12 @@ void TerminalWidget::setupPty() env.unset("CLINK_NOAUTORUN"); m_process->setProcessMode(ProcessMode::Writer); - m_process->setPtyData(Utils::Pty::Data()); + Utils::Pty::Data data; + data.setPtyInputFlagsChangedHandler([this](Pty::PtyInputFlag flags) { + const bool password = (flags & Pty::InputModeHidden); + setPasswordMode(password); + }); + m_process->setPtyData(data); m_process->setCommand(shellCommand); if (m_openParameters.workingDirectory.has_value()) m_process->setWorkingDirectory(*m_openParameters.workingDirectory); diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index 155b5ee2729..0d5cfa9e23b 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -3716,6 +3716,31 @@ id="use6090" transform="rotate(180,2008,576)" /> + + + + + +