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; 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()) { 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