Source: https://github.com/flameshot-org/flameshot/pull/4169 From 06f41a86cc91d53d68871fcdc67053239ff1e87b Mon Sep 17 00:00:00 2001 From: Yurii Puchkov Date: Sat, 30 Aug 2025 16:36:17 +0300 Subject: [PATCH] fix: Screen positions in multi-monitor configuration when devicePixelRatio is other than one (#4169) * fix: screen positions in multi-monitor configuration when devicePixelRatio is other than one (Xorg and probably Windows, Wayland is not fixed) * fix: poor screenshot quality with the scale > 1.0 * fix(win): tool-buttons location (cherry picked from commit 52be77f401b107b6c8d06dc2b5485f3b00b18d40) --------- Co-authored-by: Yuriy Puchkov --- src/utils/screengrabber.cpp | 28 +++++++++++-------------- src/widgets/capture/buttonhandler.cpp | 6 +++--- src/widgets/capture/capturewidget.cpp | 30 ++++++++++++++++++++------- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/utils/screengrabber.cpp b/src/utils/screengrabber.cpp index cdbc598497..d787ace48b 100644 --- a/src/utils/screengrabber.cpp +++ b/src/utils/screengrabber.cpp @@ -137,6 +137,7 @@ void ScreenGrabber::freeDesktopPortal(bool& ok, QPixmap& res) } #endif } + QPixmap ScreenGrabber::grabEntireDesktop(bool& ok) { ok = true; @@ -210,24 +211,16 @@ QPixmap ScreenGrabber::grabEntireDesktop(bool& ok) // multi-monitor setups where screens have different positions/heights. // This fixes the dual monitor offset bug and handles edge cases where // the desktop bounding box includes virtual space. + QScreen* primaryScreen = QGuiApplication::primaryScreen(); + QRect r = primaryScreen->geometry(); QPixmap desktop(geometry.size()); desktop.fill(Qt::black); // Fill with black background - - QPainter painter(&desktop); - for (QScreen* screen : QGuiApplication::screens()) { - QRect screenGeom = screen->geometry(); - QPixmap screenCapture = screen->grabWindow( - wid, 0, 0, screenGeom.width(), screenGeom.height()); - - // Calculate position relative to desktop top-left - QPoint relativePos = screenGeom.topLeft() - geometry.topLeft(); - painter.drawPixmap(relativePos, screenCapture); - } - painter.end(); - - // Set device pixel ratio based on the primary screen - desktop.setDevicePixelRatio( - QApplication::primaryScreen()->devicePixelRatio()); + desktop = + primaryScreen->grabWindow(wid, + -r.x() / primaryScreen->devicePixelRatio(), + -r.y() / primaryScreen->devicePixelRatio(), + geometry.width(), + geometry.height()); return desktop; #endif } @@ -281,6 +274,9 @@ QRect ScreenGrabber::desktopGeometry() // Qt6 fix: Don't divide by devicePixelRatio for multi-monitor setups // This was causing coordinate offset issues in dual monitor // configurations + // But it still has a screen position in real pixels, not logical ones + qreal dpr = screen->devicePixelRatio(); + scrRect.moveTo(QPointF(scrRect.x() / dpr, scrRect.y() / dpr).toPoint()); geometry = geometry.united(scrRect); } return geometry; diff --git a/src/widgets/capture/buttonhandler.cpp b/src/widgets/capture/buttonhandler.cpp index 4a003714c1..4624fdcfad 100644 --- a/src/widgets/capture/buttonhandler.cpp +++ b/src/widgets/capture/buttonhandler.cpp @@ -64,7 +64,7 @@ size_t ButtonHandler::size() const // updatePosition updates the position of the buttons around the // selection area. Ignores the sides blocked by the end of the screen. -// When the selection is too small it works on a virtual selection with +// When the selection is too small, it works on a virtual selection with // the original in the center. void ButtonHandler::updatePosition(const QRect& selection) { @@ -122,7 +122,7 @@ void ButtonHandler::updatePosition(const QRect& selection) horizontalPoints(center, addCounter, true); moveButtonsToPoints(positions, elemIndicator); } - // Add buttons at the right side of the selection + // Add buttons to the right side of the selection if (!m_blockedRight && elemIndicator < vecLength) { int addCounter = buttonsPerCol; addCounter = qBound(0, addCounter, vecLength - elemIndicator); @@ -146,7 +146,7 @@ void ButtonHandler::updatePosition(const QRect& selection) horizontalPoints(center, addCounter, false); moveButtonsToPoints(positions, elemIndicator); } - // Add buttons at the left side of the selection + // Add buttons to the left side of the selection if (!m_blockedLeft && elemIndicator < vecLength) { int addCounter = buttonsPerCol; addCounter = qBound(0, addCounter, vecLength - elemIndicator); diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 4cb3b2c6e7..a152bb352c 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -13,11 +13,8 @@ #include "abstractlogger.h" #include "copytool.h" #include "src/config/cacheutils.h" -#include "src/config/generalconf.h" #include "src/core/flameshot.h" #include "src/core/qguiappcurrentscreen.h" -#include "src/tools/toolfactory.h" -#include "src/utils/colorutils.h" #include "src/utils/screengrabber.h" #include "src/utils/screenshotsaver.h" #include "src/utils/systemnotification.h" @@ -32,9 +29,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -150,6 +145,7 @@ CaptureWidget::CaptureWidget(const CaptureRequest& req, QScreen* currentScreen = QGuiAppCurrentScreen().currentScreen(); move(currentScreen->geometry().x(), currentScreen->geometry().y()); resize(currentScreen->size()); +// LINUX #else // Call cmake with -DFLAMESHOT_DEBUG_CAPTURE=ON to enable easier debugging #if !defined(FLAMESHOT_DEBUG_CAPTURE) @@ -161,6 +157,18 @@ CaptureWidget::CaptureWidget(const CaptureRequest& req, move(desktopGeom.topLeft()); resize(desktopGeom.size()); #endif + // Need to move to the top left screen + QPoint topLeft(0, INT_MAX); + for (QScreen* const screen : QGuiApplication::screens()) { + qreal dpr = screen->devicePixelRatio(); + QPoint topLeftScreen = screen->geometry().topLeft() / dpr; + if (topLeftScreen.x() == 0) { + if (topLeftScreen.y() < topLeft.y()) { + topLeft.setY(topLeftScreen.y()); + } + } + } + move(topLeft); #endif } QVector areas; @@ -182,6 +190,7 @@ CaptureWidget::CaptureWidget(const CaptureRequest& req, r.moveTo(0, 0); areas.append(r); #else + // LINUX & WINDOWS for (QScreen* const screen : QGuiApplication::screens()) { QRect r = screen->geometry(); r.moveTo(r.x() / screen->devicePixelRatio(), @@ -259,8 +268,15 @@ CaptureWidget::CaptureWidget(const CaptureRequest& req, OverlayMessage::instance()->update(); }); - OverlayMessage::init(this, - QGuiAppCurrentScreen().currentScreen()->geometry()); + // Qt6 has only sizes in logical values, position is in physical values. + // Move Help message to the logical pixel with devicePixelRatio. + QScreen* currentScreen = QGuiAppCurrentScreen().currentScreen(); + QRect currentScreenGeometry = currentScreen->geometry(); + qreal currentScreenDpr = currentScreen->devicePixelRatio(); + currentScreenGeometry.moveTo( + int(currentScreenGeometry.x() / currentScreenDpr), + int(currentScreenGeometry.y() / currentScreenDpr)); + OverlayMessage::init(this, currentScreenGeometry); if (m_config.showHelp()) { initHelpMessage();