You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
7.4 KiB
225 lines
7.4 KiB
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 10/10] 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
|
|
---
|
|
xembed-sni-proxy/sniproxy.cpp | 143 ++++++++++++++++++++++++++++++++----------
|
|
xembed-sni-proxy/sniproxy.h | 5 +-
|
|
2 files changed, 114 insertions(+), 34 deletions(-)
|
|
|
|
diff --git a/xembed-sni-proxy/sniproxy.cpp b/xembed-sni-proxy/sniproxy.cpp
|
|
index ca2667f..ae6eab7 100644
|
|
--- a/xembed-sni-proxy/sniproxy.cpp
|
|
+++ b/xembed-sni-proxy/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/xembed-sni-proxy/sniproxy.h b/xembed-sni-proxy/sniproxy.h
|
|
index 29aa56e..6ab5b7d 100644
|
|
--- a/xembed-sni-proxy/sniproxy.h
|
|
+++ b/xembed-sni-proxy/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.5.0
|
|
|