From e17866e28300e859cd162c3e57fd24ae8eecd646 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 15 Jun 2023 14:16:53 +0200 Subject: [PATCH] Terminal: Show key when input is hidden Change-Id: I5fca6a8c4f43ea2b95bad4df247b5df0abad521c Reviewed-by: Alessandro Portale Reviewed-by: Reviewed-by: Marcus Tillmanns --- src/libs/3rdparty/libptyqt/iptyprocess.h | 7 +++ src/libs/3rdparty/libptyqt/unixptyprocess.cpp | 6 +++ src/libs/solutions/terminal/CMakeLists.txt | 1 + .../terminal/images/passwordlock.png | Bin 0 -> 840 bytes src/libs/solutions/terminal/terminal.qrc | 5 ++ src/libs/solutions/terminal/terminalview.cpp | 45 +++++++++++++++--- src/libs/solutions/terminal/terminalview.h | 2 + src/libs/utils/process.cpp | 8 ++++ src/libs/utils/processinterface.h | 16 +++++++ src/plugins/terminal/terminalwidget.cpp | 7 ++- src/tools/icons/qtcreatoricons.svg | 25 ++++++++++ 11 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 src/libs/solutions/terminal/images/passwordlock.png create mode 100644 src/libs/solutions/terminal/terminal.qrc 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 0000000000000000000000000000000000000000..c548e28cab246dfb1c485e6cfac57c172e84571a GIT binary patch literal 840 zcmeAS@N?(olHy`uVBq!ia0y~yV9;P-U~u4IW?*25Z$FyBz`$r4;1lBde=vbRfByXc z|Nr;z-|ybNd;k9Z$B!RhzI^%i?b~P1p1pYS;?}KOw{PFRbLY;zd-twgyLSBe@slS{ zo;r1E?b@~L)~%a6ckY4(3l=V1ICJLA*|TR)n>KCAlqn4j4ULVBO-)VB&CS)-)pd1s z_4W0kp`k%RLBYYnZf!W==r3eoV2tr}aSZYBetZ77A5$WO zYvP(4)$jKvSSPrKyx|qytt6K6W#5109S+BTx}T9qZsI=o>Dld=`R7l>-@j*WWNLZm za#;AQ-}`2*%Y3uUdiDN9RoNX``sbc$wcp`m)84o16$8V=!>^xQ4*p~%#+I<>)Z0_; zj7sV2tIqF=VU^G|j&(FYQ0CP)ubiQZoA(AsLDtC=R;(EXR{vQx# z%3B8EI`$kkB~8zlk7u5_pP-}rvXV99SBY!#ft^dfsFg`)dWs*2n8=&m_@R4AvW%=( zRU}(h?hURZDymI>5__hWuxz8ZhR`W>v)cU!u0^Trw{cX9FEi2GWAOvzc+*T_jv{U3}v>78qDH04{O-; zst*0-vH8&QU9h3!yQD3XjZ|>iiZ`eKH^rYmJpbVQDc&>Q`&ThvE%gYtUvhLR0|Ntt Mr>mdKI;Vst06@LgeE + + 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)" /> + + + + + +