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:
parent
69d46a3e20
commit
98aae7428d
src
plugins/platforms/xcb
widgets/util
@ -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()) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user