diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index af7b9de6fa..c86e69d483 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -5420,6 +5420,10 @@ We recommend you use the AppImage available on our downloads page. You must restart the application to apply this setting. Would you like to restart now? + + Allow Screen Capture + + ManageDatabase @@ -7887,10 +7891,6 @@ Kernel: %3 %4 KeePassXC - Error - - Warning: Failed to prevent screenshots on a top level window! - - Database password: @@ -7913,6 +7913,10 @@ Kernel: %3 %4 Failed to sign challenge using Windows Hello. + + Warning: Failed to block screenshot capture on a top-level window. + + Invalid Cipher diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index f7e29df441..3acfcb4a83 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -259,6 +259,7 @@ MainWindow::MainWindow() m_showToolbarSeparator = config()->get(Config::GUI_ApplicationTheme).toString() != "classic"; m_ui->actionEntryAutoType->setVisible(autoType()->isAvailable()); + m_ui->actionAllowScreenCapture->setVisible(osUtils->canPreventScreenCapture()); m_inactivityTimer = new InactivityTimer(this); connect(m_inactivityTimer, SIGNAL(inactivityDetected()), this, SLOT(lockDatabasesAfterInactivity())); @@ -563,6 +564,7 @@ MainWindow::MainWindow() connect(m_ui->actionUserGuide, SIGNAL(triggered()), SLOT(openUserGuide())); connect(m_ui->actionOnlineHelp, SIGNAL(triggered()), SLOT(openOnlineHelp())); connect(m_ui->actionKeyboardShortcuts, SIGNAL(triggered()), SLOT(openKeyboardShortcuts())); + connect(m_ui->actionAllowScreenCapture, &QAction::toggled, this, &MainWindow::setAllowScreenCapture); connect(osUtils, &OSUtilsBase::statusbarThemeChanged, this, &MainWindow::updateTrayIcon); @@ -1658,11 +1660,27 @@ void MainWindow::applySettingsChanges() updateTrayIcon(); } -void MainWindow::focusWindowChanged(QWindow* focusWindow) +void MainWindow::setAllowScreenCapture(bool state) { - if (focusWindow != windowHandle()) { + m_allowScreenCapture = state; + for (auto window : qApp->allWindows()) { + osUtils->setPreventScreenCapture(window, !m_allowScreenCapture); + } + m_ui->actionAllowScreenCapture->blockSignals(true); + m_ui->actionAllowScreenCapture->setChecked(m_allowScreenCapture); + m_ui->actionAllowScreenCapture->blockSignals(false); +} + +void MainWindow::focusWindowChanged(QWindow* window) +{ + if (window != windowHandle()) { m_lastFocusOutTime = Clock::currentMilliSecondsSinceEpoch(); } + + if (!osUtils->setPreventScreenCapture(window, !m_allowScreenCapture) && !m_allowScreenCapture) { + displayGlobalMessage(QObject::tr("Warning: Failed to block screenshot capture on a top-level window."), + MessageWidget::Error); + } } void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason) diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index ed9c506a31..8436bf4ec2 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -52,6 +52,7 @@ class MainWindow : public QMainWindow QList getOpenDatabases(); void restoreConfigState(); + void setAllowScreenCapture(bool state); enum StackedWidgetIndex { @@ -192,6 +193,7 @@ private slots: bool m_restartRequested = false; bool m_contextMenuFocusLock = false; bool m_showToolbarSeparator = false; + bool m_allowScreenCapture = false; qint64 m_lastFocusOutTime = 0; qint64 m_lastShowTime = 0; QTimer m_updateCheckTimer; diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index bbc06c1805..cbaea45a3c 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -385,6 +385,7 @@ + @@ -1113,6 +1114,14 @@ XML Fileā€¦ + + + true + + + Allow Screen Capture + + diff --git a/src/gui/osutils/OSUtilsBase.cpp b/src/gui/osutils/OSUtilsBase.cpp index d0d83f8f5f..8a95b7f555 100644 --- a/src/gui/osutils/OSUtilsBase.cpp +++ b/src/gui/osutils/OSUtilsBase.cpp @@ -27,5 +27,5 @@ OSUtilsBase::~OSUtilsBase() = default; bool OSUtilsBase::setPreventScreenCapture(QWindow*, bool) const { // Do nothing by default - return false; + return true; } diff --git a/src/gui/osutils/macutils/MacUtils.cpp b/src/gui/osutils/macutils/MacUtils.cpp index bc4503a8a1..0f2cc61611 100644 --- a/src/gui/osutils/macutils/MacUtils.cpp +++ b/src/gui/osutils/macutils/MacUtils.cpp @@ -166,11 +166,9 @@ bool MacUtils::canPreventScreenCapture() const bool MacUtils::setPreventScreenCapture(QWindow* window, bool prevent) const { - if (!window) { - return false; + if (window) { + m_appkit->setWindowSecurity(window, prevent); } - - m_appkit->setWindowSecurity(window, prevent); return true; } diff --git a/src/gui/osutils/winutils/WinUtils.cpp b/src/gui/osutils/winutils/WinUtils.cpp index b2dd9b967f..188b91348d 100644 --- a/src/gui/osutils/winutils/WinUtils.cpp +++ b/src/gui/osutils/winutils/WinUtils.cpp @@ -58,11 +58,12 @@ bool WinUtils::canPreventScreenCapture() const bool WinUtils::setPreventScreenCapture(QWindow* window, bool prevent) const { + bool ret = true; if (window) { HWND handle = reinterpret_cast(window->winId()); - return SetWindowDisplayAffinity(handle, prevent ? WDA_EXCLUDEFROMCAPTURE : WDA_NONE); + ret = SetWindowDisplayAffinity(handle, prevent ? WDA_EXCLUDEFROMCAPTURE : WDA_NONE); } - return false; + return ret; } /** diff --git a/src/main.cpp b/src/main.cpp index f426a76e9e..3797e89981 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -183,21 +183,9 @@ int main(int argc, char** argv) MainWindow mainWindow; -#ifndef QT_DEBUG - // Disable screen capture if capable and not explicitly allowed - if (osUtils->canPreventScreenCapture() && !parser.isSet(allowScreenCaptureOption)) { - // This ensures any top-level windows (Main Window, Modal Dialogs, etc.) are excluded from screenshots - QObject::connect(&app, &QGuiApplication::focusWindowChanged, &mainWindow, [&](QWindow* window) { - if (window) { - if (!osUtils->setPreventScreenCapture(window, true)) { - mainWindow.displayGlobalMessage( - QObject::tr("Warning: Failed to prevent screenshots on a top level window!"), - MessageWidget::Error); - } - } - }); - } -#endif + // Disable screen capture if not explicitly allowed + // This ensures any top-level windows (Main Window, Modal Dialogs, etc.) are excluded from screenshots + mainWindow.setAllowScreenCapture(parser.isSet(allowScreenCaptureOption)); const bool pwstdin = parser.isSet(pwstdinOption); if (!fileNames.isEmpty() && pwstdin) {