1515#include < QScreen>
1616#include < QGuiApplication>
1717#include < QPainter>
18+ #include < QImage>
1819#include < algorithm>
1920#include < QMap>
2021
22+ // X11 headers for XGetImage workaround
23+ #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
24+ #include < X11/Xlib.h>
25+ #include < X11/Xutil.h>
26+ #endif
27+
2128ScreenGrabber::ScreenGrabber (QObject *parent) : QObject(parent)
2229{
2330 qCDebug (dsrApp) << " ScreenGrabber initialized." ;
@@ -103,6 +110,14 @@ QPixmap ScreenGrabber::grabWaylandScreenshot(bool &ok, const QRect &rect, const
103110QPixmap ScreenGrabber::grabX11Screenshot (bool &ok, const QRect &rect, const qreal devicePixelRatio)
104111{
105112 qCDebug (dsrApp) << " Grabbing X11 screenshot for rect:" << rect;
113+
114+ #ifdef USE_X11_GETIMAGE
115+ // Qt6 workaround: 使用 XGetImage 直接抓取 X11 根窗口
116+ // Qt6 的 QScreen::grabWindow 在多屏+高DPI+非对齐布局下存在 bug
117+ qCDebug (dsrApp) << " Qt6: Using XGetImage workaround for X11 screenshot" ;
118+ return grabWithXGetImage (ok, rect);
119+ #else
120+ // Qt5: 使用原有逻辑
106121 const QList<QScreen*> intersectingScreens = findIntersectingScreens (rect);
107122
108123 if (intersectingScreens.isEmpty ()) {
@@ -117,6 +132,7 @@ QPixmap ScreenGrabber::grabX11Screenshot(bool &ok, const QRect &rect, const qrea
117132
118133 qCDebug (dsrApp) << " Multiple intersecting screens found, grabbing multiple screens." ;
119134 return grabMultipleScreens (ok, rect, intersectingScreens, devicePixelRatio);
135+ #endif
120136}
121137
122138/* *
@@ -538,3 +554,85 @@ QPixmap ScreenGrabber::grabMultipleScreens(bool &ok, const QRect &rect, const QL
538554 ok = true ;
539555 return result;
540556}
557+
558+ #ifdef USE_X11_GETIMAGE
559+ /* *
560+ * @brief Qt6 workaround: 使用 XGetImage 直接抓取 X11 根窗口
561+ * @param ok Output parameter indicating screenshot success
562+ * @param rect The rectangular area to capture (in physical coordinates, since DPI scaling is disabled)
563+ * @return The captured desktop image
564+ *
565+ * Qt6 的 QScreen::grabWindow 在多屏+高DPI+非对齐布局的 X11 环境下存在 bug,
566+ * 返回的整屏截图是花屏/错乱的。此方法绕过 Qt6 的抓图 API,直接使用 X11 原生 API。
567+ */
568+ QPixmap ScreenGrabber::grabWithXGetImage (bool &ok, const QRect &rect)
569+ {
570+ qCDebug (dsrApp) << " [XGetImage] Starting X11 screenshot for rect:" << rect;
571+
572+ Display *dpy = XOpenDisplay (nullptr );
573+ if (!dpy) {
574+ qCWarning (dsrApp) << " [XGetImage] Failed to open X11 Display" ;
575+ ok = false ;
576+ return QPixmap ();
577+ }
578+
579+ Window root = DefaultRootWindow (dpy);
580+
581+ // 获取根窗口尺寸
582+ XWindowAttributes rootAttr;
583+ XGetWindowAttributes (dpy, root, &rootAttr);
584+ qCDebug (dsrApp) << " [XGetImage] X11 root window size:" << rootAttr.width << " x" << rootAttr.height ;
585+
586+ // 抓取整个根窗口
587+ XImage *ximg = XGetImage (dpy, root, 0 , 0 , rootAttr.width , rootAttr.height , AllPlanes, ZPixmap);
588+ if (!ximg) {
589+ qCWarning (dsrApp) << " [XGetImage] XGetImage returned NULL" ;
590+ XCloseDisplay (dpy);
591+ ok = false ;
592+ return QPixmap ();
593+ }
594+
595+ qCDebug (dsrApp) << " [XGetImage] XImage captured, size:" << ximg->width << " x" << ximg->height
596+ << " depth:" << ximg->depth << " bits_per_pixel:" << ximg->bits_per_pixel ;
597+
598+ // 将 XImage 转换为 QImage
599+ QImage qimg (ximg->width , ximg->height , QImage::Format_RGB32);
600+ for (int row = 0 ; row < ximg->height ; ++row) {
601+ for (int col = 0 ; col < ximg->width ; ++col) {
602+ unsigned long pixel = XGetPixel (ximg, col, row);
603+ int r = (pixel >> 16 ) & 0xFF ;
604+ int g = (pixel >> 8 ) & 0xFF ;
605+ int b = pixel & 0xFF ;
606+ qimg.setPixel (col, row, qRgb (r, g, b));
607+ }
608+ }
609+
610+ XDestroyImage (ximg);
611+ XCloseDisplay (dpy);
612+
613+ // 转换为 QPixmap
614+ QPixmap fullPixmap = QPixmap::fromImage (qimg);
615+ qCDebug (dsrApp) << " [XGetImage] Full pixmap size:" << fullPixmap.size ();
616+
617+ // 如果请求的是整个桌面,直接返回
618+ if (rect.isNull () || rect == QRect (0 , 0 , rootAttr.width , rootAttr.height )) {
619+ qCDebug (dsrApp) << " [XGetImage] Returning full desktop screenshot" ;
620+ ok = true ;
621+ return fullPixmap;
622+ }
623+
624+ // 裁剪请求的区域
625+ QRect cropRect = rect.intersected (QRect (0 , 0 , rootAttr.width , rootAttr.height ));
626+ if (cropRect.isEmpty ()) {
627+ qCWarning (dsrApp) << " [XGetImage] Crop rect is empty or outside root window" ;
628+ ok = false ;
629+ return QPixmap ();
630+ }
631+
632+ QPixmap result = fullPixmap.copy (cropRect);
633+ qCDebug (dsrApp) << " [XGetImage] Cropped result size:" << result.size ();
634+
635+ ok = !result.isNull ();
636+ return result;
637+ }
638+ #endif
0 commit comments