summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbbackingstore.cpp
diff options
context:
space:
mode:
authorAlexander Volkov <a.volkov@rusbitech.ru>2015-10-27 15:08:41 +0300
committerGatis Paeglis <gatis.paeglis@qt.io>2018-07-10 20:03:53 +0000
commit5cf6f51b4c73cb1309279aa42d828097b24aa119 (patch)
treec5ecc96c74ba59d62c623fc096abacaa11113aad /src/plugins/platforms/xcb/qxcbbackingstore.cpp
parentd743df975db3403ec83d67bd376d8a6bfea3dfb0 (diff)
xcb: Fix artifacts on the tray background with lock screens
With the current method of painting the tray icon with 24 bpp visuals we grab the window's background once on the first show and then use it in all paint operations. This leads to a wrong background if an application shows the system tray icon when the lock screen is active. We can avoid this by painting with XRender when it's available. This change introduces QXcbSystemTrayBackingStore and moves the selection of a suitable painting method from QSystemTrayIconSys into it. In addition the visual for the window is selected according to the system tray specification and the platform window for the tray icon is created without needless OpenGL and Vulkan support. Task-number: QTBUG-55540 Change-Id: Ib3ca42bc02dcbdd4ccfe5d6e23f870ef22f0d25a Reviewed-by: Gatis Paeglis <gatis.paeglis@qt.io>
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbbackingstore.cpp')
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.cpp205
1 files changed, 193 insertions, 12 deletions
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
index b81cb8efa1..e73e6f3e61 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -45,6 +45,16 @@
#include <xcb/shm.h>
#include <xcb/xcb_image.h>
+#if QT_CONFIG(xcb_render)
+#include <xcb/render.h>
+// 'template' is used as a function argument name in xcb_renderutil.h
+#define template template_param
+// extern "C" is missing too
+extern "C" {
+#include <xcb/xcb_renderutil.h>
+}
+#undef template
+#endif
#include <sys/ipc.h>
#include <sys/shm.h>
@@ -75,7 +85,7 @@ class QXcbBackingStore;
class QXcbBackingStoreImage : public QXcbObject
{
public:
- QXcbBackingStoreImage(QXcbBackingStore *backingStore, const QSize &size);
+ QXcbBackingStoreImage(QXcbScreen *screen, const QSize &size, uint depth, QImage::Format format);
~QXcbBackingStoreImage() { destroy(true); }
void resize(const QSize &size);
@@ -110,8 +120,9 @@ private:
void flushPixmap(const QRegion &region, bool fullRegion = false);
void setClip(const QRegion &region);
+ xcb_window_t m_screen_root;
+
xcb_shm_segment_info_t m_shm_info;
- QXcbBackingStore *m_backingStore = nullptr;
size_t m_segmentSize = 0;
xcb_image_t *m_xcb_image = nullptr;
@@ -179,17 +190,15 @@ static inline size_t imageDataSize(const xcb_image_t *image)
return static_cast<size_t>(image->stride) * image->height;
}
-QXcbBackingStoreImage::QXcbBackingStoreImage(QXcbBackingStore *backingStore, const QSize &size)
- : QXcbObject(backingStore->connection())
- , m_backingStore(backingStore)
+QXcbBackingStoreImage::QXcbBackingStoreImage(QXcbScreen *screen, const QSize &size, uint depth, QImage::Format format)
+ : QXcbObject(screen->connection())
+ , m_screen_root(screen->screen()->root)
{
- QXcbWindow *window = static_cast<QXcbWindow *>(backingStore->window()->handle());
- const xcb_format_t *fmt = connection()->formatForDepth(window->depth());
+ const xcb_format_t *fmt = connection()->formatForDepth(depth);
Q_ASSERT(fmt);
memset(&m_shm_info, 0, sizeof m_shm_info);
- QImage::Format format = window->imageFormat();
m_hasAlpha = QImage::toPixelFormat(format).alphaUsage() == QPixelFormat::UsesAlpha;
if (!m_hasAlpha)
create(size, fmt, qt_maybeAlphaVersionWithSameDepth(format));
@@ -238,11 +247,10 @@ void QXcbBackingStoreImage::create(const QSize &size, const xcb_format_t *fmt, Q
m_graphics_buffer = new QXcbGraphicsBuffer(&m_qimage);
m_xcb_pixmap = xcb_generate_id(xcb_connection());
- auto xcbScreen = static_cast<QXcbScreen *>(m_backingStore->window()->screen()->handle());
xcb_create_pixmap(xcb_connection(),
m_xcb_image->depth,
m_xcb_pixmap,
- xcbScreen->root(),
+ m_screen_root,
m_xcb_image->width, m_xcb_image->height);
}
@@ -832,7 +840,7 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
return;
}
- m_image->put(platformWindow->xcb_window(), clipped, offset);
+ render(platformWindow->xcb_window(), clipped, offset);
if (platformWindow->needsSync())
platformWindow->updateSyncRequestCounter();
@@ -840,6 +848,11 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
xcb_flush(xcb_connection());
}
+void QXcbBackingStore::render(xcb_window_t window, const QRegion &region, const QPoint &offset)
+{
+ m_image->put(window, region, offset);
+}
+
#ifndef QT_NO_OPENGL
void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures,
@@ -873,10 +886,15 @@ void QXcbBackingStore::resize(const QSize &size, const QRegion &)
}
QXcbWindow* win = static_cast<QXcbWindow *>(pw);
+ recreateImage(win, size);
+}
+
+void QXcbBackingStore::recreateImage(QXcbWindow *win, const QSize &size)
+{
if (m_image)
m_image->resize(size);
else
- m_image = new QXcbBackingStoreImage(this, size);
+ m_image = new QXcbBackingStoreImage(win->xcbScreen(), size, win->depth(), win->imageFormat());
// Slow path for bgr888 VNC: Create an additional image, paint into that and
// swap R and B while copying to m_image after each paint.
@@ -893,4 +911,167 @@ bool QXcbBackingStore::scroll(const QRegion &area, int dx, int dy)
return false;
}
+QXcbSystemTrayBackingStore::QXcbSystemTrayBackingStore(QWindow *window)
+ : QXcbBackingStore(window)
+{
+ // We need three different behaviors depending on whether the X11 visual
+ // for the system tray supports an alpha channel, i.e. is 32 bits, and
+ // whether XRender can be used:
+ // 1) if the visual has an alpha channel, then render the window's buffer
+ // directly to the X11 window as usual
+ // 2) else if XRender can be used, then render the window's buffer to Pixmap,
+ // then render Pixmap's contents to the cleared X11 window with
+ // xcb_render_composite()
+ // 3) else grab the X11 window's content and paint it first each time as a
+ // background before rendering the window's buffer to the X11 window
+
+ auto *platformWindow = static_cast<QXcbWindow *>(window->handle());
+ quint8 depth = connection()->primaryScreen()->depthOfVisual(platformWindow->visualId());
+
+ if (depth != 32) {
+ platformWindow->setParentRelativeBackPixmap();
+#if QT_CONFIG(xcb_render)
+ initXRenderMode();
+#endif
+ m_useGrabbedBackgound = !m_usingXRenderMode;
+ }
+}
+
+QXcbSystemTrayBackingStore::~QXcbSystemTrayBackingStore()
+{
+#if QT_CONFIG(xcb_render)
+ if (m_xrenderPicture) {
+ xcb_render_free_picture(xcb_connection(), m_xrenderPicture);
+ m_xrenderPicture = XCB_NONE;
+ }
+ if (m_xrenderPixmap) {
+ xcb_free_pixmap(xcb_connection(), m_xrenderPixmap);
+ m_xrenderPixmap = XCB_NONE;
+ }
+ if (m_windowPicture) {
+ xcb_render_free_picture(xcb_connection(), m_windowPicture);
+ m_windowPicture = XCB_NONE;
+ }
+#endif // QT_CONFIG(xcb_render)
+}
+
+void QXcbSystemTrayBackingStore::beginPaint(const QRegion &region)
+{
+ QXcbBackingStore::beginPaint(region);
+
+ if (m_useGrabbedBackgound) {
+ QPainter p(paintDevice());
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ for (const QRect &rect: region)
+ p.drawPixmap(rect, m_grabbedBackground, rect);
+ }
+}
+
+void QXcbSystemTrayBackingStore::render(xcb_window_t window, const QRegion &region, const QPoint &offset)
+{
+ if (!m_usingXRenderMode) {
+ QXcbBackingStore::render(window, region, offset);
+ return;
+ }
+
+#if QT_CONFIG(xcb_render)
+ m_image->put(m_xrenderPixmap, region, offset);
+ const QRect bounds = region.boundingRect();
+ const QPoint target = bounds.topLeft();
+ const QRect source = bounds.translated(offset);
+ xcb_clear_area(xcb_connection(), false, window,
+ target.x(), target.y(), source.width(), source.height());
+ xcb_render_composite(xcb_connection(), XCB_RENDER_PICT_OP_OVER,
+ m_xrenderPicture, 0, m_windowPicture,
+ target.x(), target.y(), 0, 0, target.x(), target.y(),
+ source.width(), source.height());
+#endif // QT_CONFIG(xcb_render)
+}
+
+void QXcbSystemTrayBackingStore::recreateImage(QXcbWindow *win, const QSize &size)
+{
+ if (!m_usingXRenderMode) {
+ QXcbBackingStore::recreateImage(win, size);
+
+ if (m_useGrabbedBackgound) {
+ xcb_clear_area(xcb_connection(), false, win->xcb_window(),
+ 0, 0, size.width(), size.height());
+ m_grabbedBackground = win->xcbScreen()->grabWindow(win->winId(), 0, 0,
+ size.width(), size.height());
+ }
+ return;
+ }
+
+#if QT_CONFIG(xcb_render)
+ if (m_xrenderPicture) {
+ xcb_render_free_picture(xcb_connection(), m_xrenderPicture);
+ m_xrenderPicture = XCB_NONE;
+ }
+ if (m_xrenderPixmap) {
+ xcb_free_pixmap(xcb_connection(), m_xrenderPixmap);
+ m_xrenderPixmap = XCB_NONE;
+ }
+
+ QXcbScreen *screen = win->xcbScreen();
+
+ m_xrenderPixmap = xcb_generate_id(xcb_connection());
+ xcb_create_pixmap(xcb_connection(), 32, m_xrenderPixmap, screen->root(), size.width(), size.height());
+
+ m_xrenderPicture = xcb_generate_id(xcb_connection());
+ xcb_render_create_picture(xcb_connection(), m_xrenderPicture, m_xrenderPixmap, m_xrenderPictFormat, 0, 0);
+
+ // XRender expects premultiplied alpha
+ if (m_image)
+ m_image->resize(size);
+ else
+ m_image = new QXcbBackingStoreImage(screen, size, 32, QImage::Format_ARGB32_Premultiplied);
+#endif // QT_CONFIG(xcb_render)
+}
+
+#if QT_CONFIG(xcb_render)
+void QXcbSystemTrayBackingStore::initXRenderMode()
+{
+ if (!connection()->hasXRender())
+ return;
+
+ xcb_connection_t *conn = xcb_connection();
+ auto formatsReply = Q_XCB_REPLY(xcb_render_query_pict_formats, conn);
+
+ if (!formatsReply) {
+ qWarning("QXcbSystemTrayBackingStore: xcb_render_query_pict_formats() failed");
+ return;
+ }
+
+ xcb_render_pictforminfo_t *fmt = xcb_render_util_find_standard_format(formatsReply.get(),
+ XCB_PICT_STANDARD_ARGB_32);
+ if (!fmt) {
+ qWarning("QXcbSystemTrayBackingStore: Failed to find format PICT_STANDARD_ARGB_32");
+ return;
+ }
+
+ m_xrenderPictFormat = fmt->id;
+
+ auto *platformWindow = static_cast<QXcbWindow *>(window()->handle());
+ xcb_render_pictvisual_t *vfmt = xcb_render_util_find_visual_format(formatsReply.get(), platformWindow->visualId());
+
+ if (!vfmt) {
+ qWarning("QXcbSystemTrayBackingStore: Failed to find format for visual %x", platformWindow->visualId());
+ return;
+ }
+
+ m_windowPicture = xcb_generate_id(conn);
+ xcb_void_cookie_t cookie =
+ xcb_render_create_picture_checked(conn, m_windowPicture, platformWindow->xcb_window(), vfmt->format, 0, 0);
+ xcb_generic_error_t *error = xcb_request_check(conn, cookie);
+ if (error) {
+ qWarning("QXcbSystemTrayBackingStore: Failed to create Picture with format %x for window %x, error code %d",
+ vfmt->format, platformWindow->xcb_window(), error->error_code);
+ free(error);
+ return;
+ }
+
+ m_usingXRenderMode = true;
+}
+#endif // QT_CONFIG(xcb_render)
+
QT_END_NAMESPACE