xcb: QSystemTrayIcon: grab background pixmap for compositing only once

Followup to 2203d9d93e: clearRegion takes
a little time, so doing grabWindow() too soon afterwards can result in
grabbing a pixmap with undesired leftovers.  And doing it every time
we render the icon causes flicker.  So, do clearRegion() ASAP when showing
the icon, wait for it to happen, then grabWindow() only the first time we
render, and reuse the resulting pixmap for all future calls to paintEvent().
(The downside is, if there is any corruption during the first grabWindow(),
it's going to stay there as long as the app is running.)

Task-number: QTBUG-35658
Change-Id: If881ab192dba43758dcbb0d080663ff42057fa4f
Reviewed-by: Jørgen Lind <jorgen.lind@theqtcompany.com>
This commit is contained in:
Shawn Rutledge 2015-02-11 12:14:13 +01:00
parent 69d46a3e20
commit 98aae7428d
4 changed files with 16 additions and 23 deletions

View File

@ -191,16 +191,6 @@ bool QXcbNativeInterface::systrayVisualHasAlphaChannel() {
}
}
void QXcbNativeInterface::clearRegion(const QWindow *qwindow, const QRect& rect)
{
if (const QPlatformWindow *platformWindow = qwindow->handle()) {
const QXcbWindow *qxwindow = static_cast<const QXcbWindow *>(platformWindow);
xcb_connection_t *xcb_conn = qxwindow->xcb_connection();
xcb_clear_area(xcb_conn, false, qxwindow->xcb_window(), rect.x(), rect.y(), rect.width(), rect.height());
}
}
void QXcbNativeInterface::setParentRelativeBackPixmap(const QWindow *qwindow)
{
if (const QPlatformWindow *platformWindow = qwindow->handle()) {

View File

@ -104,7 +104,6 @@ public:
Q_INVOKABLE void beep();
Q_INVOKABLE bool systemTrayAvailable(const QScreen *screen) const;
Q_INVOKABLE void clearRegion(const QWindow *qwindow, const QRect& rect);
Q_INVOKABLE void setParentRelativeBackPixmap(const QWindow *window);
Q_INVOKABLE bool systrayVisualHasAlphaChannel();
Q_INVOKABLE bool requestSystemTrayWindowDock(const QWindow *window);

View File

@ -2307,6 +2307,11 @@ void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event)
case XEMBED_EMBEDDED_NOTIFY:
Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
xcbScreen()->windowShown(this);
// Without Qt::WA_TranslucentBackground, we use a ParentRelative BackPixmap.
// Clear the whole tray icon window to its background color as early as possible
// so that we can get a clean result from grabWindow() later.
Q_XCB_CALL(xcb_clear_area(xcb_connection(), false, m_window, 0, 0, geometry().width(), geometry().height()));
xcb_flush(xcb_connection());
break;
case XEMBED_FOCUS_IN:
Qt::FocusReason reason;

View File

@ -88,6 +88,7 @@ private:
bool addToTray();
QSystemTrayIcon *q;
QPixmap background;
};
QSystemTrayIconSys::QSystemTrayIconSys(QSystemTrayIcon *qIn)
@ -151,6 +152,8 @@ bool QSystemTrayIconSys::addToTray()
qWarning("requestSystemTrayWindowDock failed.");
return false;
}
if (!background.isNull())
background = QPixmap();
show();
return true;
}
@ -230,18 +233,14 @@ void QSystemTrayIconSys::paintEvent(QPaintEvent *)
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.fillRect(rect, Qt::transparent);
} else {
// Without Qt::WA_TranslucentBackground, we use a ParentRelative BackPixmap and jump through
// some hops to draw this background below our icon. This clears the whole tray icon to its
// background color and thus causes flickering (you can see that the icon is being
// repainted). However, we can't really do much about this.
QMetaObject::invokeMethod(QGuiApplication::platformNativeInterface(),
"clearRegion", Qt::DirectConnection,
Q_ARG(const QWindow *, windowHandle()),
Q_ARG(const QRect&, rect)
);
painter.drawPixmap(QPoint(0, 0),
QGuiApplication::primaryScreen()->grabWindow(winId(),
0, 0, rect.size().width(), rect.size().height()));
// clearRegion() was called on XEMBED_EMBEDDED_NOTIFY, so we hope that got done by now.
// Grab the tray background pixmap, before rendering the icon for the first time.
if (background.isNull()) {
background = QGuiApplication::primaryScreen()->grabWindow(winId(),
0, 0, rect.size().width(), rect.size().height());
}
// Then paint over the icon area with the background before compositing the icon on top.
painter.drawPixmap(QPoint(0, 0), background);
}
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
q->icon().paint(&painter, rect);