File kde355684.patch of Package xembed-sni-proxy.openSUSE_Leap_42.1_Update

From 41df1bdb8b478eb27fb424d201c075c76ec0ed5a Mon Sep 17 00:00:00 2001
From: Benedikt Gollatz <benedikt@gollatz.net>
Date: Sun, 13 Dec 2015 20:47:57 +0000
Subject: [PATCH 1/1] Mitigate failed icon grabbing in xembed-sni-proxy

If grabbed icons are blank, try to salvage the copied data as well as
possible while leaving setups where image grabbing works fine alone.

Based on a patch by Rakyn Barker.

BUG:355684
REVIEW: 126336
---
 sniproxy.cpp | 143 ++++++++++++++++++++++++++++++++----------
 sniproxy.h   |   5 +-
 2 files changed, 114 insertions(+), 34 deletions(-)

diff --git a/sniproxy.cpp b/sniproxy.cpp
index ca2667f18f01a1f99b52422d8474aaabad107b92..ae6eab72e41832c4105fcf3ecb0249969d2525f7 100644
--- a/sniproxy.cpp
+++ b/sniproxy.cpp
@@ -33,7 +33,7 @@
 #include <QGuiApplication>
 #include <QTimer>
 
-#include <QPainter>
+#include <QBitmap>
 
 #include <KWindowSystem>
 #include <netwm.h>
@@ -191,48 +191,51 @@ SNIProxy::~SNIProxy()
 void SNIProxy::update()
 {
     const QImage image = getImageNonComposite();
+    if (image.isNull()) {
+        qCDebug(SNIPROXY) << "No xembed icon for" << m_windowId << Title();
+        return;
+    }
 
     int w = image.width();
     int h = image.height();
 
+    m_pixmap = QPixmap::fromImage(image);
+    if (w != s_embedSize || h != s_embedSize) {
+        qCDebug(SNIPROXY) << "Scaling pixmap of window" << m_windowId << Title() << "from w*h" << w << h;
+        m_pixmap = m_pixmap.scaled(s_embedSize, s_embedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+    }
+    emit NewIcon();
+    emit NewToolTip();
+}
+
+void sni_cleanup_xcb_image(void *data) {
+    xcb_image_destroy(static_cast<xcb_image_t*>(data));
+}
+
+bool SNIProxy::isTransparentImage(const QImage& image) const
+{
+    int w = image.width();
+    int h = image.height();
+
     // check for the center and sub-center pixels first and avoid full image scan
-    bool isTransparentImage = qAlpha(image.pixel(w >> 1, h >> 1)) + qAlpha(image.pixel(w >> 2, h >> 2)) == 0;
+    if (! (qAlpha(image.pixel(w >> 1, h >> 1)) + qAlpha(image.pixel(w >> 2, h >> 2)) == 0))
+        return false;
 
     // skip scan altogether if sub-center pixel found to be opaque
     // and break out from the outer loop too on full scan
-    for (int x = 0; x < w && isTransparentImage; ++x) {
-	for (int y = 0; y < h; ++y) {
-	    if (qAlpha(image.pixel(x, y))) {
-		// Found an opaque pixel.
-		isTransparentImage = false;
-		break;
-	    }
-	}
+    for (int x = 0; x < w; ++x) {
+        for (int y = 0; y < h; ++y) {
+            if (qAlpha(image.pixel(x, y))) {
+                // Found an opaque pixel.
+                return false;
+            }
+        }
     }
 
-    // Update icon only if it is at least partially opaque.
-    // This is just a workaround for X11 bug: xembed icon may suddenly
-    // become transparent for a one or few frames. Reproducible at least
-    // with WINE applications.
-    if (!isTransparentImage) {
-        m_pixmap = QPixmap::fromImage(image);
-	if (w != s_embedSize || h != s_embedSize) {
-	    qCDebug(SNIPROXY) << "Scaling pixmap of window" << m_windowId << Title() << "from w*h" << w << h;
-	    m_pixmap = m_pixmap.scaled(s_embedSize, s_embedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
-	}
-        emit NewIcon();
-        emit NewToolTip();
-    }
-    else {
-        qCDebug(SNIPROXY) << "Skip transparent xembed icon for" << m_windowId << Title();
-    }
+    return true;
 }
 
-void sni_cleanup_xcb_image(void *data) {
-    xcb_image_destroy(static_cast<xcb_image_t*>(data));
-}
-
-QImage SNIProxy::getImageNonComposite()
+QImage SNIProxy::getImageNonComposite() const
 {
     auto c = QX11Info::connection();
     auto cookie = xcb_get_geometry(c, m_windowId);
@@ -240,9 +243,83 @@ QImage SNIProxy::getImageNonComposite()
 
     xcb_image_t *image = xcb_image_get(c, m_windowId, 0, 0, geom->width, geom->height, 0xFFFFFF, XCB_IMAGE_FORMAT_Z_PIXMAP);
 
-    QImage qimage(image->data, image->width, image->height, image->stride, QImage::Format_ARGB32, sni_cleanup_xcb_image, image);
+    // Don't hook up cleanup yet, we may use a different QImage after all
+    QImage naiveConversion = QImage(image->data, image->width, image->height, QImage::Format_ARGB32);
+
+    if (isTransparentImage(naiveConversion)) {
+        QImage elaborateConversion = QImage(convertFromNative(image));
+
+        // Update icon only if it is at least partially opaque.
+        // This is just a workaround for X11 bug: xembed icon may suddenly
+        // become transparent for a one or few frames. Reproducible at least
+        // with WINE applications.
+        if (isTransparentImage(elaborateConversion)) {
+            qCDebug(SNIPROXY) << "Skip transparent xembed icon for" << m_windowId << Title();
+            return QImage();
+        } else
+            return elaborateConversion;
+    } else {
+        // Now we are sure we can eventually delete the xcb_image_t with this version
+        return QImage(image->data, image->width, image->height, image->stride, QImage::Format_ARGB32, sni_cleanup_xcb_image, image);
+    }
+}
+
+QImage SNIProxy::convertFromNative(xcb_image_t *xcbImage) const
+{
+    QImage::Format format = QImage::Format_Invalid;
+
+    switch (xcbImage->depth) {
+    case 1:
+        format = QImage::Format_MonoLSB;
+        break;
+    case 16:
+        format = QImage::Format_RGB16;
+        break;
+    case 24:
+        format = QImage::Format_RGB32;
+        break;
+    case 30: {
+        // Qt doesn't have a matching image format. We need to convert manually
+        quint32 *pixels = reinterpret_cast<quint32 *>(xcbImage->data);
+        for (uint i = 0; i < (xcbImage->size / 4); i++) {
+            int r = (pixels[i] >> 22) & 0xff;
+            int g = (pixels[i] >> 12) & 0xff;
+            int b = (pixels[i] >>  2) & 0xff;
+
+            pixels[i] = qRgba(r, g, b, 0xff);
+        }
+        // fall through, Qt format is still Format_ARGB32_Premultiplied
+    }
+    case 32:
+        format = QImage::Format_ARGB32_Premultiplied;
+        break;
+    default:
+        return QImage(); // we don't know
+    }
+
+    QImage image(xcbImage->data, xcbImage->width, xcbImage->height, xcbImage->stride, format, sni_cleanup_xcb_image, xcbImage);
+
+    if (image.isNull()) {
+        return QImage();
+    }
+
+    if (format == QImage::Format_RGB32 && xcbImage->bpp == 32)
+    {
+        QImage m = image.createHeuristicMask();
+        QBitmap mask(QPixmap::fromImage(m));
+        QPixmap p = QPixmap::fromImage(image);
+        p.setMask(mask);
+        image = p.toImage();
+    }
+
+    // work around an abort in QImage::color
+    if (image.format() == QImage::Format_MonoLSB) {
+        image.setColorCount(2);
+        image.setColor(0, QColor(Qt::white).rgb());
+        image.setColor(1, QColor(Qt::black).rgb());
+    }
 
-    return qimage;
+    return image;
 }
 
 //____________properties__________
diff --git a/sniproxy.h b/sniproxy.h
index 29aa56e381b034513d4683f047e1556679b57910..6ab5b7d6988b3e975ac562f7b591c2d9307574f0 100644
--- a/sniproxy.h
+++ b/sniproxy.h
@@ -28,6 +28,7 @@
 #include <QPixmap>
 
 #include <xcb/xcb.h>
+#include <xcb/xcb_image.h>
 
 #include "snidbus.h"
 
@@ -140,7 +141,9 @@ Q_SIGNALS:
 
 private:
     void sendClick(uint8_t mouseButton, int x, int y);
-    QImage getImageNonComposite();
+    QImage getImageNonComposite() const;
+    bool isTransparentImage(const QImage &image) const;
+    QImage convertFromNative(xcb_image_t *xcbImage) const;
 
     QDBusConnection m_dbus;
     xcb_window_t m_windowId;
-- 
2.6.2

openSUSE Build Service is sponsored by