From 3537c715cc38c55d391f99428ad4f9b17291dabb Mon Sep 17 00:00:00 2001 From: Jialu Date: Sat, 1 Aug 2026 06:48:59 +0800 Subject: [PATCH 1/3] fix: replace PCRE2 {,1} with ? for broader compatibility (#4792) --- src/utils/valuehandler.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/valuehandler.cpp b/src/utils/valuehandler.cpp index 6913d1670f..6d24488f22 100644 --- a/src/utils/valuehandler.cpp +++ b/src/utils/valuehandler.cpp @@ -546,13 +546,13 @@ QVariant Region::process(const QVariant& val) QString str = val.toString(); static const QRegularExpression regex( - "(-{,1}\\d+)" // number (any sign) + "(-?\\d+)" // number (any sign) "[x,\\.\\s]" // separator ('x', ',', '.', or whitespace) - "(-{,1}\\d+)" // number (any sign) + "(-?\\d+)" // number (any sign) "[\\+,\\.\\s]*" // separator ('+',',', '.', or whitespace) - "(-{,1}\\d+)" // number (non-negative) + "(-?\\d+)" // number (non-negative) "[\\+,\\.\\s]*" // separator ('+', ',', '.', or whitespace) - "(-{,1}\\d+)" // number (non-negative) + "(-?\\d+)" // number (non-negative) ); if (!regex.match(str).hasMatch()) { From f55a3bdf10e412c6039503e5a832f76081ff17e8 Mon Sep 17 00:00:00 2001 From: El Thoro Date: Sat, 1 Aug 2026 00:49:55 +0200 Subject: [PATCH 2/3] Fix aspect ratio in selection widget when pressing CTRL (#4651) * Fix aspect ratio in selection widget when pressing CTRL * Check for height > 0 --- src/widgets/capture/selectionwidget.cpp | 40 ++++++++++++++----------- src/widgets/capture/selectionwidget.h | 1 + 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/widgets/capture/selectionwidget.cpp b/src/widgets/capture/selectionwidget.cpp index 35a4022c59..84584155d8 100644 --- a/src/widgets/capture/selectionwidget.cpp +++ b/src/widgets/capture/selectionwidget.cpp @@ -20,6 +20,7 @@ SelectionWidget::SelectionWidget(QColor c, QWidget* parent) , m_color(std::move(c)) , m_activeSide(NO_SIDE) , m_ignoreMouse(false) + , m_aspectRatio(1) { // prevents this widget from consuming CaptureToolButton mouse events setAttribute(Qt::WA_TransparentForMouseEvents); @@ -176,6 +177,9 @@ void SelectionWidget::parentMousePressEvent(QMouseEvent* e) m_dragStartPos = e->pos(); m_activeSide = getMouseSide(e->pos()); + if ((float)geometry().height() > 0) { + m_aspectRatio = (float)geometry().width() / (float)geometry().height(); + } } void SelectionWidget::parentMouseReleaseEvent(QMouseEvent* e) @@ -215,7 +219,6 @@ void SelectionWidget::parentMouseMoveEvent(QMouseEvent* e) } auto geom = geometry(); - float aspectRatio = (float)geom.width() / (float)geom.height(); bool symmetryMod = qApp->keyboardModifiers() & Qt::ShiftModifier; bool preserveAspect = qApp->keyboardModifiers() & Qt::ControlModifier; @@ -230,20 +233,20 @@ void SelectionWidget::parentMouseMoveEvent(QMouseEvent* e) if (preserveAspect) { if ((float)(oldRight - pos.x()) / (float)(oldBottom - pos.y()) > - aspectRatio) { + m_aspectRatio) { /* width longer than expected width, hence increase * height to compensate for the aspect ratio */ newLeft = pos.x(); newTop = oldBottom - - (int)(((float)(oldRight - pos.x())) / aspectRatio); + (int)(((float)(oldRight - pos.x())) / m_aspectRatio); } else { /* height longer than expected height, hence increase * width to compensate for the aspect ratio */ newTop = pos.y(); newLeft = oldRight - - (int)(((float)(oldBottom - pos.y())) * aspectRatio); + (int)(((float)(oldBottom - pos.y())) * m_aspectRatio); } } else { newTopLeft = pos; @@ -254,15 +257,15 @@ void SelectionWidget::parentMouseMoveEvent(QMouseEvent* e) if (m_activeSide) { if (preserveAspect) { if ((float)(pos.x() - oldLeft) / (float)(pos.y() - oldTop) > - aspectRatio) { + m_aspectRatio) { newRight = pos.x(); newBottom = oldTop + - (int)(((float)(pos.x() - oldLeft)) / aspectRatio); + (int)(((float)(pos.x() - oldLeft)) / m_aspectRatio); } else { newBottom = pos.y(); newRight = oldLeft + (int)(((float)(pos.y() - oldTop)) * - aspectRatio); + m_aspectRatio); } } else { newBottomRight = pos; @@ -274,16 +277,16 @@ void SelectionWidget::parentMouseMoveEvent(QMouseEvent* e) if (preserveAspect) { if ((float)(pos.x() - oldLeft) / (float)(oldBottom - pos.y()) > - aspectRatio) { + m_aspectRatio) { newRight = pos.x(); newTop = oldBottom - - (int)(((float)(pos.x() - oldLeft)) / aspectRatio); + (int)(((float)(pos.x() - oldLeft)) / m_aspectRatio); } else { newTop = pos.y(); newRight = oldLeft + - (int)(((float)(oldBottom - pos.y())) * aspectRatio); + (int)(((float)(oldBottom - pos.y())) * m_aspectRatio); } } else { newTop = pos.y(); @@ -296,15 +299,15 @@ void SelectionWidget::parentMouseMoveEvent(QMouseEvent* e) if (preserveAspect) { if ((float)(oldRight - pos.x()) / (float)(pos.y() - oldTop) > - aspectRatio) { + m_aspectRatio) { newLeft = pos.x(); newBottom = oldTop + - (int)(((float)(oldRight - pos.x())) / aspectRatio); + (int)(((float)(oldRight - pos.x())) / m_aspectRatio); } else { newBottom = pos.y(); newLeft = oldRight - (int)(((float)(pos.y() - oldTop)) * - aspectRatio); + m_aspectRatio); } } else { newBottom = pos.y(); @@ -319,7 +322,7 @@ void SelectionWidget::parentMouseMoveEvent(QMouseEvent* e) /* By default bottom edge moves when dragging sides, this * behavior feels natural */ newBottom = oldTop + (int)(((float)(oldRight - pos.x())) / - aspectRatio); + m_aspectRatio); } } break; @@ -328,7 +331,7 @@ void SelectionWidget::parentMouseMoveEvent(QMouseEvent* e) newRight = pos.x(); if (preserveAspect) { newBottom = oldTop + (int)(((float)(pos.x() - oldLeft)) / - aspectRatio); + m_aspectRatio); } } break; @@ -340,7 +343,7 @@ void SelectionWidget::parentMouseMoveEvent(QMouseEvent* e) * behavior feels natural */ newRight = oldLeft + - (int)(((float)(oldBottom - pos.y()) * aspectRatio)); + (int)(((float)(oldBottom - pos.y()) * m_aspectRatio)); } } break; @@ -348,8 +351,9 @@ void SelectionWidget::parentMouseMoveEvent(QMouseEvent* e) if (m_activeSide) { newBottom = pos.y(); if (preserveAspect) { - newRight = oldLeft + - (int)(((float)(pos.y() - oldTop) * aspectRatio)); + newRight = + oldLeft + + (int)(((float)(pos.y() - oldTop) * m_aspectRatio)); } } break; diff --git a/src/widgets/capture/selectionwidget.h b/src/widgets/capture/selectionwidget.h index fa7238a470..3fb1e5139e 100644 --- a/src/widgets/capture/selectionwidget.h +++ b/src/widgets/capture/selectionwidget.h @@ -93,6 +93,7 @@ public slots: QCursor m_idleCentralCursor; bool m_ignoreMouse; bool m_mouseStartMove; + float m_aspectRatio; // naming convention for handles // T top, B bottom, R Right, L left From cbab9b41774bf11606349ca1d2d356e22f671bff Mon Sep 17 00:00:00 2001 From: Jake McArthur Date: Fri, 31 Jul 2026 19:01:02 -0400 Subject: [PATCH 3/3] Fail fast when the screenshot portal cannot take the capture (#4826) Since v14 every capture on Linux goes through the org.freedesktop.portal.Screenshot D-Bus portal, but flameshot discarded the reply of the Screenshot call. When no portal backend provides that interface, the call fails within milliseconds. Flameshot then waited for a Response signal that could never arrive and reported a misleading "portal timed out" error, while taking no screenshot at all. Check the reply of the Screenshot call and fail at once with the real D-Bus error. The failure now shows up immediately and names the actual problem, on both Wayland and X11. This does not change which systems can take screenshots. Helps diagnose the reports in #4639. --- src/utils/screengrabber.cpp | 111 ++++++++++++++++++++++-------------- src/utils/screengrabber.h | 9 ++- 2 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/utils/screengrabber.cpp b/src/utils/screengrabber.cpp index 0cafea6d3b..b64553edd8 100644 --- a/src/utils/screengrabber.cpp +++ b/src/utils/screengrabber.cpp @@ -29,6 +29,7 @@ #if !(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) #include "request.h" #include +#include #include #include #include @@ -50,7 +51,9 @@ ScreenGrabber::ScreenGrabber(QObject* parent) QImageReader::setAllocationLimit(1024); } -void ScreenGrabber::freeDesktopPortal(bool& ok, QPixmap& res) +ScreenGrabber::PortalStatus ScreenGrabber::freeDesktopPortal( + QPixmap& res, + QString& errorDetail) { #if !(defined(Q_OS_MACOS) || defined(Q_OS_WIN)) @@ -58,10 +61,9 @@ void ScreenGrabber::freeDesktopPortal(bool& ok, QPixmap& res) auto service = QStringLiteral("org.freedesktop.portal.Desktop"); if (!connectionInterface->isServiceRegistered(service)) { - ok = false; - AbstractLogger::error() << tr( - "Could not locate the `org.freedesktop.portal.Desktop` service"); - return; + errorDetail = + tr("Could not locate the `org.freedesktop.portal.Desktop` service"); + return PortalStatus::Unavailable; } QDBusInterface screenshotInterface( @@ -132,12 +134,24 @@ void ScreenGrabber::freeDesktopPortal(bool& ok, QPixmap& res) QStringLiteral("x11:0x%1").arg(parentDummy.winId(), 0, 16); } - screenshotInterface.call( + QDBusMessage reply = screenshotInterface.call( QStringLiteral("Screenshot"), parentWindow, QMap({ { "handle_token", QVariant(token) }, { "interactive", QVariant(false) } })); + if (reply.type() == QDBusMessage::ErrorMessage) { + // No backend provides org.freedesktop.portal.Screenshot (or the + // portal rejected the request outright); the Response signal will + // never arrive, so fail now instead of waiting for the timeout. + QObject::disconnect(conn); + request->deleteLater(); + errorDetail = + tr("The `org.freedesktop.portal.Screenshot` request failed: %1") + .arg(reply.errorMessage()); + return PortalStatus::Unavailable; + } + loop.exec(); timeout.stop(); QObject::disconnect(conn); @@ -145,20 +159,17 @@ void ScreenGrabber::freeDesktopPortal(bool& ok, QPixmap& res) request->deleteLater(); if (timedOut) { - ok = false; - - AbstractLogger::error() - << tr("The xdg-desktop-portal backend did not respond " - "If you are on wayland make sure an xdg-desktop-portal backend " - "for your desktop is " - "installed and properly configured.\n \n" - "If on X11 enable Legacy X11 method in the General Settings"); - return; + errorDetail = + tr("The xdg-desktop-portal backend did not respond " + "If you are on wayland make sure an xdg-desktop-portal backend " + "for your desktop is " + "installed and properly configured.\n \n" + "If on X11 enable Legacy X11 method in the General Settings"); + return PortalStatus::Failed; } if (res.isNull()) { - ok = false; - return; + return PortalStatus::Failed; } #ifdef FLAMESHOT_DEBUG_CAPTURE @@ -167,6 +178,43 @@ void ScreenGrabber::freeDesktopPortal(bool& ok, QPixmap& res) .arg(res.height()) .arg(res.devicePixelRatio()); #endif + return PortalStatus::Success; +#else + Q_UNUSED(res) + Q_UNUSED(errorDetail) + return PortalStatus::Failed; +#endif +} + +QPixmap ScreenGrabber::unixScreenshot(bool& ok) +{ +#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) + QPixmap screenshot; + + if (!m_info.waylandDetected() && ConfigHandler().useX11LegacyScreenshot()) { + screenshot = x11LegacyScreenshot(); + ok = !screenshot.isNull(); + if (!ok) { + AbstractLogger::error() << tr("Unable to capture screen"); + } + return screenshot; + } + + QString portalError; + const PortalStatus status = freeDesktopPortal(screenshot, portalError); + ok = status == PortalStatus::Success; + + if (!ok) { + if (!portalError.isEmpty()) { + AbstractLogger::error() << portalError; + } + AbstractLogger::error() << tr("Unable to capture screen"); + } + + return screenshot; +#else + ok = false; + return QPixmap(); #endif } @@ -268,19 +316,9 @@ QPixmap ScreenGrabber::grabEntireDesktop(bool& ok, int preSelectedMonitor) return screenshot; #elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) - if (!m_info.waylandDetected() && ConfigHandler().useX11LegacyScreenshot()) { - screenshot = x11LegacyScreenshot(); - ok = !screenshot.isNull(); - if (!ok) { - AbstractLogger::error() << tr("Unable to capture screen"); - return QPixmap(); - } - } else { - freeDesktopPortal(ok, screenshot); - if (!ok) { - AbstractLogger::error() << tr("Unable to capture screen"); - return QPixmap(); - } + screenshot = unixScreenshot(ok); + if (!ok) { + return QPixmap(); } #elif defined(Q_OS_WIN) screenshot = windowsScreenshot(wid); @@ -327,18 +365,7 @@ QPixmap ScreenGrabber::grabFullDesktop(bool& ok) } painter.end(); #elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) - if (!m_info.waylandDetected() && ConfigHandler().useX11LegacyScreenshot()) { - screenshot = x11LegacyScreenshot(); - ok = !screenshot.isNull(); - if (!ok) { - AbstractLogger::error() << tr("Unable to capture screen"); - } - } else { - freeDesktopPortal(ok, screenshot); - if (!ok) { - AbstractLogger::error() << tr("Unable to capture screen"); - } - } + screenshot = unixScreenshot(ok); #elif defined(Q_OS_WIN) screenshot = windowsScreenshot(0); #endif diff --git a/src/utils/screengrabber.h b/src/utils/screengrabber.h index 7ca6997462..7c68f7f0ac 100644 --- a/src/utils/screengrabber.h +++ b/src/utils/screengrabber.h @@ -20,11 +20,17 @@ class ScreenGrabber : public QObject Q_OBJECT public: explicit ScreenGrabber(QObject* parent = nullptr); + enum class PortalStatus + { + Success, + Unavailable, + Failed + }; QPixmap grabEntireDesktop(bool& ok, int preSelectedMonitor = -1); QPixmap grabFullDesktop(bool& ok); QRect screenGeometry(QScreen* screen); QPixmap grabScreen(QScreen* screenNumber, bool& ok); - void freeDesktopPortal(bool& ok, QPixmap& res); + PortalStatus freeDesktopPortal(QPixmap& res, QString& errorDetail); QRect desktopGeometry(); QRect logicalDesktopGeometry(); int getSelectedMonitor() const { return m_selectedMonitor; } @@ -46,6 +52,7 @@ class ScreenGrabber : public QObject QPixmap cropToMonitor(const QPixmap& fullScreenshot, int monitorIndex); QPixmap windowsScreenshot(int wid); QPixmap x11LegacyScreenshot(); + QPixmap unixScreenshot(bool& ok); DesktopInfo m_info; QPixmap Screenshot;