Terminal: Show key when input is hidden

Change-Id: I5fca6a8c4f43ea2b95bad4df247b5df0abad521c
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
Marcus Tillmanns
2023-06-15 14:16:53 +02:00
parent c32b7067fe
commit e17866e283
11 changed files with 115 additions and 7 deletions

View File

@@ -11,6 +11,8 @@ class IPtyProcess
{ {
public: public:
enum PtyType { UnixPty = 0, WinPty = 1, ConPty = 2, AutoPty = 3 }; enum PtyType { UnixPty = 0, WinPty = 1, ConPty = 2, AutoPty = 3 };
enum PtyInputFlag { None = 0x0, InputModeHidden = 0x1, };
Q_DECLARE_FLAGS(PtyInputFlags, PtyInputFlag)
IPtyProcess() = default; IPtyProcess() = default;
IPtyProcess(const IPtyProcess &) = delete; IPtyProcess(const IPtyProcess &) = delete;
@@ -43,6 +45,8 @@ public:
return m_trace; return m_trace;
} }
PtyInputFlags inputFlags() { return m_inputFlags; }
protected: protected:
QString m_shellPath; QString m_shellPath;
QString m_lastError; QString m_lastError;
@@ -50,6 +54,9 @@ protected:
int m_exitCode{0}; int m_exitCode{0};
QPair<qint16, qint16> m_size; //cols / rows QPair<qint16, qint16> m_size; //cols / rows
bool m_trace{false}; bool m_trace{false};
PtyInputFlags m_inputFlags;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(IPtyProcess::PtyInputFlags)
#endif // IPTYPROCESS_H #endif // IPTYPROCESS_H

View File

@@ -172,6 +172,12 @@ bool UnixPtyProcess::startProcess(const QString &shellPath,
static std::array<char, maxRead> buffer; static std::array<char, maxRead> buffer;
int len = ::read(m_shellProcess.m_handleMaster, buffer.data(), buffer.size()); 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) { if (len > 0) {
m_shellReadBuffer.append(buffer.data(), len); m_shellReadBuffer.append(buffer.data(), len);
m_shellProcess.emitReadyRead(); m_shellProcess.emitReadyRead();

View File

@@ -7,6 +7,7 @@ add_qtc_library(TerminalLib
scrollback.cpp scrollback.h scrollback.cpp scrollback.h
surfaceintegration.h surfaceintegration.h
terminal_global.h terminal_global.h
terminal.qrc
terminalsurface.cpp terminalsurface.h terminalsurface.cpp terminalsurface.h
terminalview.cpp terminalview.h terminalview.cpp terminalview.h
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/terminal">
<file>images/passwordlock.png</file>
</qresource>
</RCC>

View File

@@ -85,6 +85,7 @@ public:
bool m_cursorBlinkState{true}; bool m_cursorBlinkState{true};
bool m_allowBlinkingCursor{true}; bool m_allowBlinkingCursor{true};
bool m_allowMouseTracking{true}; bool m_allowMouseTracking{true};
bool m_passwordModeActive{false};
SurfaceIntegration *m_surfaceIntegration{nullptr}; SurfaceIntegration *m_surfaceIntegration{nullptr};
}; };
@@ -258,6 +259,14 @@ void TerminalView::setColors(const std::array<QColor, 20> &newColors)
update(); update();
} }
void TerminalView::setPasswordMode(bool passwordMode)
{
if (passwordMode != d->m_passwordModeActive) {
d->m_passwordModeActive = passwordMode;
updateViewport();
}
}
void TerminalView::setFont(const QFont &font) void TerminalView::setFont(const QFont &font)
{ {
QAbstractScrollArea::setFont(font); QAbstractScrollArea::setFont(font);
@@ -731,16 +740,37 @@ void TerminalView::paintCursor(QPainter &p) const
{ {
auto cursor = d->m_surface->cursor(); auto cursor = d->m_surface->cursor();
if (!d->m_preEditString.isEmpty()) const int cursorCellWidth = d->m_surface->cellWidthAt(cursor.position.x(), cursor.position.y());
cursor.shape = Cursor::Shape::Underline;
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 const bool blinkState = !cursor.blink || d->m_cursorBlinkState || !d->m_allowBlinkingCursor
|| !d->m_cursorBlinkTimer.isActive(); || !d->m_cursorBlinkTimer.isActive();
if (cursor.visible && blinkState) { if (cursor.visible && blinkState) {
const int cursorCellWidth = d->m_surface->cellWidthAt(cursor.position.x(),
cursor.position.y());
QRectF cursorRect = QRectF(gridToGlobal(cursor.position), QRectF cursorRect = QRectF(gridToGlobal(cursor.position),
gridToGlobal({cursor.position.x() + cursorCellWidth, gridToGlobal({cursor.position.x() + cursorCellWidth,
cursor.position.y()}, cursor.position.y()},
@@ -984,7 +1014,10 @@ void TerminalView::updateViewport()
void TerminalView::updateViewportRect(const QRect &rect) void TerminalView::updateViewportRect(const QRect &rect)
{ {
viewport()->update(rect); if (d->m_passwordModeActive)
viewport()->update();
else
viewport()->update(rect);
} }
void TerminalView::focusInEvent(QFocusEvent *) void TerminalView::focusInEvent(QFocusEvent *)

View File

@@ -87,6 +87,8 @@ public:
void setSurfaceIntegration(SurfaceIntegration *surfaceIntegration); void setSurfaceIntegration(SurfaceIntegration *surfaceIntegration);
void setColors(const std::array<QColor, 20> &colors); void setColors(const std::array<QColor, 20> &colors);
void setPasswordMode(bool passwordMode);
struct Link struct Link
{ {
QString text; QString text;

View File

@@ -410,6 +410,13 @@ public:
} }
connect(m_ptyProcess->notifier(), &QIODevice::readyRead, this, [this] { 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<Pty::PtyInputFlag>(m_inputFlags.toInt()));
}
emit readyRead(m_ptyProcess->readAll(), {}); emit readyRead(m_ptyProcess->readAll(), {});
}); });
@@ -430,6 +437,7 @@ public:
private: private:
std::unique_ptr<IPtyProcess> m_ptyProcess; std::unique_ptr<IPtyProcess> m_ptyProcess;
IPtyProcess::PtyInputFlags m_inputFlags;
}; };
class QProcessImpl final : public DefaultImpl class QProcessImpl final : public DefaultImpl

View File

@@ -18,12 +18,19 @@ namespace Internal { class ProcessPrivate; }
namespace Pty { namespace Pty {
enum PtyInputFlag {
None = 0x0,
InputModeHidden = 0x1,
};
using ResizeHandler = std::function<void(const QSize &)>; using ResizeHandler = std::function<void(const QSize &)>;
using PtyInputFlagsChangeHandler = std::function<void(PtyInputFlag)>;
class QTCREATOR_UTILS_EXPORT SharedData class QTCREATOR_UTILS_EXPORT SharedData
{ {
public: public:
ResizeHandler m_handler; ResizeHandler m_handler;
PtyInputFlagsChangeHandler m_inputFlagsChangedHandler;
}; };
class QTCREATOR_UTILS_EXPORT Data class QTCREATOR_UTILS_EXPORT Data
@@ -32,6 +39,15 @@ public:
Data() : m_data(new SharedData) {} Data() : m_data(new SharedData) {}
void setResizeHandler(const ResizeHandler &handler) { m_data->m_handler = handler; } 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; } QSize size() const { return m_size; }
void resize(const QSize &size); void resize(const QSize &size);

View File

@@ -126,7 +126,12 @@ void TerminalWidget::setupPty()
env.unset("CLINK_NOAUTORUN"); env.unset("CLINK_NOAUTORUN");
m_process->setProcessMode(ProcessMode::Writer); 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); m_process->setCommand(shellCommand);
if (m_openParameters.workingDirectory.has_value()) if (m_openParameters.workingDirectory.has_value())
m_process->setWorkingDirectory(*m_openParameters.workingDirectory); m_process->setWorkingDirectory(*m_openParameters.workingDirectory);

View File

@@ -3716,6 +3716,31 @@
id="use6090" id="use6090"
transform="rotate(180,2008,576)" /> transform="rotate(180,2008,576)" />
</g> </g>
<g
id="src/libs/solutions/terminal/images/passwordlock"
transform="translate(-5,4)">
<use
x="0"
y="0"
xlink:href="#transparentBackgroundRect"
id="use1"
transform="matrix(2.5,0,0,4,586,-1405)" />
<path
style="opacity:0.75;fill:#ffffff"
d="m 582,423 v -5 c 0,-4 -4,-15 -16,-15 -12,-0.0312 -16,11 -16,15 0,4 0,5 0,5 h -4 v 36 h 40 v -36 z"
id="path167"
sodipodi:nodetypes="czzzcccccc" />
<path
style="display:inline;fill:#000000;fill-opacity:1;stroke-width:4"
inkscape:connector-curvature="0"
d="m 566,407 c -6.624,0 -12,5.372 -12,12 v 12 h 4 v -12 c 0,-4.412 3.584,-8 8,-8 4.412,0 8,3.588 8,8 v 12 h 4 v -12 c 0,-6.628 -5.372,-12 -12,-12 z"
id="lockbow-1" />
<path
style="display:inline;fill:#000000;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 550,427 v 28 h 32 v -28 z m 18,15.388 V 447 h -4 v -4.612 c -1.172,-0.7 -2,-1.92 -2,-3.388 0,-2.212 1.792,-4 4,-4 2.208,0 4,1.788 4,4 0,1.464 -0.828,2.688 -2,3.388 z"
id="lockbody-1" />
</g>
</g> </g>
<g <g
inkscape:groupmode="layer" inkscape:groupmode="layer"

Before

Width:  |  Height:  |  Size: 365 KiB

After

Width:  |  Height:  |  Size: 366 KiB