Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qxcbbackingstore.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qxcbbackingstore.h"
5
6#include "qxcbconnection.h"
7#include "qxcbscreen.h"
8#include "qxcbwindow.h"
9
10#include <xcb/shm.h>
11#include <xcb/xcb_image.h>
12#include <xcb/render.h>
13#include <xcb/xcb_renderutil.h>
14
15#include <sys/ipc.h>
16#include <sys/shm.h>
17#include <sys/mman.h>
18
19#include <stdio.h>
20#include <errno.h>
21#include <unistd.h>
22
23#include <qdebug.h>
24#include <qpainter.h>
25#include <qscreen.h>
26#include <QtGui/private/qhighdpiscaling_p.h>
27#include <qpa/qplatformgraphicsbuffer.h>
28#include <private/qimage_p.h>
29#include <qendian.h>
30
31#include <algorithm>
32
33#if (XCB_SHM_MAJOR_VERSION == 1 && XCB_SHM_MINOR_VERSION >= 2) || XCB_SHM_MAJOR_VERSION > 1
34#define XCB_USE_SHM_FD
35#endif
36
38
40
42{
43public:
44 QXcbBackingStoreImage(QXcbBackingStore *backingStore, const QSize &size);
46 ~QXcbBackingStoreImage() { destroy(true); }
47
48 void resize(const QSize &size);
49
50 void flushScrolledRegion(bool clientSideScroll);
51
52 bool scroll(const QRegion &area, int dx, int dy);
53
54 QImage *image() { return &m_qimage; }
55 QPlatformGraphicsBuffer *graphicsBuffer() { return m_graphics_buffer; }
56
57 QSize size() const { return m_qimage.size(); }
58
59 bool hasAlpha() const { return m_hasAlpha; }
60 bool hasShm() const { return m_shm_info.shmaddr != nullptr; }
61
62 void put(xcb_drawable_t dst, const QRegion &region, const QPoint &offset);
63 void preparePaint(const QRegion &region);
64
65 static bool createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize = 1,
66 xcb_shm_segment_info_t *shm_info = nullptr);
67
68private:
69 void init(const QSize &size, uint depth, QImage::Format format);
70
71 void createShmSegment(size_t segmentSize);
72 void destroyShmSegment();
73 void destroy(bool destroyShm);
74
75 void ensureGC(xcb_drawable_t dst);
76 void shmPutImage(xcb_drawable_t drawable, const QRegion &region, const QPoint &offset = QPoint());
77 void flushPixmap(const QRegion &region, bool fullRegion = false);
78 void setClip(const QRegion &region);
79
80 xcb_shm_segment_info_t m_shm_info;
81 size_t m_segmentSize = 0;
82 QXcbBackingStore *m_backingStore = nullptr;
83
84 xcb_image_t *m_xcb_image = nullptr;
85
86 QImage m_qimage;
87 QPlatformGraphicsBuffer *m_graphics_buffer = nullptr;
88
89 xcb_gcontext_t m_gc = 0;
90 xcb_drawable_t m_gc_drawable = 0;
91
92 // When using shared memory these variables are used only for server-side scrolling.
93 // When not using shared memory, we maintain a server-side pixmap with the backing
94 // store as well as repainted content not yet flushed to the pixmap. We only flush
95 // the regions we need and only when these are marked dirty. This way we can just
96 // do a server-side copy on expose instead of sending the pixels every time
97 xcb_pixmap_t m_xcb_pixmap = 0;
98 QRegion m_pendingFlush;
99
100 // This is the scrolled region which is stored in server-side pixmap
101 QRegion m_scrolledRegion;
102
103 // When using shared memory this is the region currently shared with the server
104 QRegion m_dirtyShm;
105
106 // When not using shared memory this is a temporary buffer which is uploaded
107 // as a pixmap region to server
108 QByteArray m_flushBuffer;
109
110 bool m_hasAlpha = false;
111 bool m_clientSideScroll = false;
112
113 const xcb_format_t *m_xcb_format = nullptr;
114 QImage::Format m_qimage_format = QImage::Format_Invalid;
115};
116
118{
119public:
121 : QPlatformGraphicsBuffer(image->size(), QImage::toPixelFormat(image->format()))
122 , m_image(image)
123 { }
124
125 bool doLock(AccessTypes access, const QRect &rect) override
126 {
127 Q_UNUSED(rect);
129 return false;
130
131 m_access_lock |= access;
132 return true;
133 }
134 void doUnlock() override { m_access_lock = None; }
135
136 const uchar *data() const override { return m_image->bits(); }
137 uchar *data() override { return m_image->bits(); }
138 int bytesPerLine() const override { return m_image->bytesPerLine(); }
139
141
142private:
143 AccessTypes m_access_lock = QPlatformGraphicsBuffer::None;
144 QImage *m_image = nullptr;
145};
146
147static inline size_t imageDataSize(const xcb_image_t *image)
148{
149 return static_cast<size_t>(image->stride) * image->height;
150}
151
153 : QXcbObject(backingStore->connection())
154 , m_backingStore(backingStore)
155{
156 auto window = static_cast<QXcbWindow *>(m_backingStore->window()->handle());
157 init(size, window->depth(), window->imageFormat());
158}
159
162 : QXcbObject(backingStore->connection())
163 , m_backingStore(backingStore)
164{
165 init(size, depth, format);
166}
167
168void QXcbBackingStoreImage::init(const QSize &size, uint depth, QImage::Format format)
169{
170 m_xcb_format = connection()->formatForDepth(depth);
171 Q_ASSERT(m_xcb_format);
172
173 m_qimage_format = format;
174 m_hasAlpha = QImage::toPixelFormat(m_qimage_format).alphaUsage() == QPixelFormat::UsesAlpha;
175 if (!m_hasAlpha)
176 m_qimage_format = qt_maybeDataCompatibleAlphaVersion(m_qimage_format);
177
178 memset(&m_shm_info, 0, sizeof m_shm_info);
179
180 resize(size);
181}
182
184{
185 destroy(false);
186
187 auto byteOrder = QSysInfo::ByteOrder == QSysInfo::BigEndian ? XCB_IMAGE_ORDER_MSB_FIRST
188 : XCB_IMAGE_ORDER_LSB_FIRST;
189 m_xcb_image = xcb_image_create(size.width(), size.height(),
190 XCB_IMAGE_FORMAT_Z_PIXMAP,
191 m_xcb_format->scanline_pad,
192 m_xcb_format->depth,
193 m_xcb_format->bits_per_pixel,
194 0, byteOrder,
195 XCB_IMAGE_ORDER_MSB_FIRST,
196 nullptr, ~0, nullptr);
197
198 const size_t segmentSize = imageDataSize(m_xcb_image);
199
200 if (connection()->hasShm()) {
201 if (segmentSize == 0) {
202 if (m_segmentSize > 0) {
203 destroyShmSegment();
204 qCDebug(lcQpaXcb) << "[" << m_backingStore->window()
205 << "] destroyed SHM segment due to resize to" << size;
206 }
207 } else {
208 // Destroy shared memory segment if it is double (or more) of what we actually
209 // need with new window size. Or if the new size is bigger than what we currently
210 // have allocated.
211 if (m_shm_info.shmaddr && (m_segmentSize < segmentSize || m_segmentSize / 2 >= segmentSize))
212 destroyShmSegment();
213 if (!m_shm_info.shmaddr) {
214 qCDebug(lcQpaXcb) << "[" << m_backingStore->window()
215 << "] creating shared memory" << segmentSize << "bytes for"
216 << size << "depth" << m_xcb_format->depth << "bits"
217 << m_xcb_format->bits_per_pixel;
218 createShmSegment(segmentSize);
219 }
220 }
221 }
222
223 if (segmentSize == 0)
224 return;
225
226 m_xcb_image->data = m_shm_info.shmaddr ? m_shm_info.shmaddr : (uint8_t *)malloc(segmentSize);
227 m_qimage = QImage(static_cast<uchar *>(m_xcb_image->data), m_xcb_image->width,
228 m_xcb_image->height, m_xcb_image->stride, m_qimage_format);
229 m_graphics_buffer = new QXcbGraphicsBuffer(&m_qimage);
230
231 m_xcb_pixmap = xcb_generate_id(xcb_connection());
232 auto xcbScreen = static_cast<QXcbScreen *>(m_backingStore->window()->screen()->handle());
233 xcb_create_pixmap(xcb_connection(),
234 m_xcb_image->depth,
235 m_xcb_pixmap,
236 xcbScreen->root(),
237 m_xcb_image->width, m_xcb_image->height);
238}
239
240void QXcbBackingStoreImage::destroy(bool destroyShm)
241{
242 if (m_xcb_image) {
243 if (m_xcb_image->data) {
244 if (m_shm_info.shmaddr) {
245 if (destroyShm)
246 destroyShmSegment();
247 } else {
248 free(m_xcb_image->data);
249 }
250 }
251 xcb_image_destroy(m_xcb_image);
252 }
253
254 if (m_gc) {
255 xcb_free_gc(xcb_connection(), m_gc);
256 m_gc = 0;
257 }
258 m_gc_drawable = 0;
259
260 delete m_graphics_buffer;
261 m_graphics_buffer = nullptr;
262
263 if (m_xcb_pixmap) {
264 xcb_free_pixmap(xcb_connection(), m_xcb_pixmap);
265 m_xcb_pixmap = 0;
266 }
267
268 m_qimage = QImage();
269}
270
272{
273 if (m_clientSideScroll == clientSideScroll)
274 return;
275
276 m_clientSideScroll = clientSideScroll;
277
278 if (m_scrolledRegion.isNull())
279 return;
280
281 if (hasShm() && m_dirtyShm.intersects(m_scrolledRegion)) {
282 connection()->sync();
283 m_dirtyShm = QRegion();
284 }
285
286 if (m_clientSideScroll) {
287 // Copy scrolled image region from server-side pixmap to client-side memory
288 for (const QRect &rect : m_scrolledRegion) {
289 const int w = rect.width();
290 const int h = rect.height();
291
292 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_image,
294 m_xcb_image->format,
295 m_xcb_pixmap,
296 rect.x(), rect.y(),
297 w, h,
298 ~0u);
299
300 if (reply && reply->depth == m_xcb_image->depth) {
301 const QImage img(xcb_get_image_data(reply.get()), w, h, m_qimage.format());
302
303 QPainter p(&m_qimage);
304 p.setCompositionMode(QPainter::CompositionMode_Source);
305 p.drawImage(rect.topLeft(), img);
306 }
307 }
308 m_scrolledRegion = QRegion();
309 } else {
310 // Copy scrolled image region from client-side memory to server-side pixmap
311 ensureGC(m_xcb_pixmap);
312 if (hasShm())
313 shmPutImage(m_xcb_pixmap, m_scrolledRegion);
314 else
315 flushPixmap(m_scrolledRegion, true);
316 }
317}
318
319void QXcbBackingStoreImage::createShmSegment(size_t segmentSize)
320{
322 Q_ASSERT(m_segmentSize == 0);
323
324#ifdef XCB_USE_SHM_FD
325 if (connection()->hasShmFd()) {
326 if (Q_UNLIKELY(segmentSize > std::numeric_limits<uint32_t>::max())) {
327 qCWarning(lcQpaXcb, "xcb_shm_create_segment() can't be called for size %zu, maximum"
328 "allowed size is %u", segmentSize, std::numeric_limits<uint32_t>::max());
329 return;
330 }
331
332 const auto seg = xcb_generate_id(xcb_connection());
333 auto reply = Q_XCB_REPLY(xcb_shm_create_segment,
334 xcb_connection(), seg, segmentSize, false);
335 if (!reply) {
336 qCWarning(lcQpaXcb, "xcb_shm_create_segment() failed for size %zu", segmentSize);
337 return;
338 }
339
340 int *fds = xcb_shm_create_segment_reply_fds(xcb_connection(), reply.get());
341 if (reply->nfd != 1) {
342 for (int i = 0; i < reply->nfd; i++)
343 close(fds[i]);
344
345 qCWarning(lcQpaXcb, "failed to get file descriptor for shm segment of size %zu", segmentSize);
346 return;
347 }
348
349 void *addr = mmap(nullptr, segmentSize, PROT_READ|PROT_WRITE, MAP_SHARED, fds[0], 0);
350 if (addr == MAP_FAILED) {
351 qCWarning(lcQpaXcb, "failed to mmap segment from X server (%d: %s) for size %zu",
352 errno, strerror(errno), segmentSize);
353 close(fds[0]);
354 xcb_shm_detach(xcb_connection(), seg);
355 return;
356 }
357
358 close(fds[0]);
359 m_shm_info.shmseg = seg;
360 m_shm_info.shmaddr = static_cast<quint8 *>(addr);
361 m_segmentSize = segmentSize;
362 } else
363#endif
364 {
365 if (createSystemVShmSegment(xcb_connection(), segmentSize, &m_shm_info))
366 m_segmentSize = segmentSize;
367 }
368}
369
370bool QXcbBackingStoreImage::createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize,
371 xcb_shm_segment_info_t *shmInfo)
372{
373 const int id = shmget(IPC_PRIVATE, segmentSize, IPC_CREAT | 0600);
374 if (id == -1) {
375 qCWarning(lcQpaXcb, "shmget() failed (%d: %s) for size %zu", errno, strerror(errno), segmentSize);
376 return false;
377 }
378
379 void *addr = shmat(id, nullptr, 0);
380 if (addr == (void *)-1) {
381 qCWarning(lcQpaXcb, "shmat() failed (%d: %s) for id %d", errno, strerror(errno), id);
382 return false;
383 }
384
385 if (shmctl(id, IPC_RMID, nullptr) == -1)
386 qCWarning(lcQpaXcb, "Error while marking the shared memory segment to be destroyed");
387
388 const auto seg = xcb_generate_id(c);
389 auto cookie = xcb_shm_attach_checked(c, seg, id, false);
390 auto *error = xcb_request_check(c, cookie);
391 if (error) {
392 qCWarning(lcQpaXcb(), "xcb_shm_attach() failed");
393 free(error);
394 if (shmdt(addr) == -1)
395 qCWarning(lcQpaXcb, "shmdt() failed (%d: %s) for %p", errno, strerror(errno), addr);
396 return false;
397 } else if (!shmInfo) { // this was a test run, free the allocated test segment
398 xcb_shm_detach(c, seg);
399 auto shmaddr = static_cast<quint8 *>(addr);
400 if (shmdt(shmaddr) == -1)
401 qCWarning(lcQpaXcb, "shmdt() failed (%d: %s) for %p", errno, strerror(errno), shmaddr);
402 }
403 if (shmInfo) {
404 shmInfo->shmseg = seg;
405 shmInfo->shmid = id; // unused
406 shmInfo->shmaddr = static_cast<quint8 *>(addr);
407 }
408 return true;
409}
410
411void QXcbBackingStoreImage::destroyShmSegment()
412{
413 auto cookie = xcb_shm_detach_checked(xcb_connection(), m_shm_info.shmseg);
414 xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
415 if (error)
416 connection()->printXcbError("xcb_shm_detach() failed with error", error);
417 m_shm_info.shmseg = 0;
418
419#ifdef XCB_USE_SHM_FD
420 if (connection()->hasShmFd()) {
421 if (munmap(m_shm_info.shmaddr, m_segmentSize) == -1) {
422 qCWarning(lcQpaXcb, "munmap() failed (%d: %s) for %p with size %zu",
423 errno, strerror(errno), m_shm_info.shmaddr, m_segmentSize);
424 }
425 } else
426#endif
427 {
428 if (shmdt(m_shm_info.shmaddr) == -1) {
429 qCWarning(lcQpaXcb, "shmdt() failed (%d: %s) for %p",
430 errno, strerror(errno), m_shm_info.shmaddr);
431 }
432 m_shm_info.shmid = 0; // unused
433 }
434 m_shm_info.shmaddr = nullptr;
435
436 m_segmentSize = 0;
437}
438
439extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
440
441bool QXcbBackingStoreImage::scroll(const QRegion &area, int dx, int dy)
442{
443 const QRect bounds(QPoint(), size());
444 const QRegion scrollArea(area & bounds);
445 const QPoint delta(dx, dy);
446 const QRegion destinationRegion = scrollArea.translated(delta).intersected(bounds);
447
448 if (m_clientSideScroll) {
449 if (m_qimage.isNull())
450 return false;
451
452 if (hasShm())
453 preparePaint(destinationRegion);
454
455 const QRect rect = scrollArea.boundingRect();
456 qt_scrollRectInImage(m_qimage, rect, delta);
457 } else {
458 ensureGC(m_xcb_pixmap);
459
460 if (hasShm()) {
461 QRegion partialFlushRegion = m_pendingFlush.intersected(scrollArea);
462 shmPutImage(m_xcb_pixmap, partialFlushRegion);
463 m_pendingFlush -= partialFlushRegion;
464 } else {
465 flushPixmap(scrollArea);
466 }
467
468 for (const QRect &src : scrollArea) {
469 const QRect dst = src.translated(delta).intersected(bounds);
470 xcb_copy_area(xcb_connection(),
471 m_xcb_pixmap,
472 m_xcb_pixmap,
473 m_gc,
474 src.x(), src.y(),
475 dst.x(), dst.y(),
476 dst.width(), dst.height());
477 }
478
479 if (hasShm())
480 m_pendingFlush -= destinationRegion;
481 }
482
483 m_scrolledRegion |= destinationRegion;
484
485 return true;
486}
487
488void QXcbBackingStoreImage::ensureGC(xcb_drawable_t dst)
489{
490 if (m_gc_drawable != dst) {
491 if (m_gc)
492 xcb_free_gc(xcb_connection(), m_gc);
493
494 static const uint32_t mask = XCB_GC_GRAPHICS_EXPOSURES;
495 static const uint32_t values[] = { 0 };
496
497 m_gc = xcb_generate_id(xcb_connection());
498 xcb_create_gc(xcb_connection(), m_gc, dst, mask, values);
499
500 m_gc_drawable = dst;
501 }
502}
503
504static inline void copy_unswapped(char *dst, int dstBytesPerLine, const QImage &img, const QRect &rect)
505{
506 const uchar *srcData = img.constBits();
507 const qsizetype srcBytesPerLine = img.bytesPerLine();
508
509 const int leftOffset = rect.left() * img.depth() >> 3;
510 const int bottom = rect.bottom() + 1;
511
512 for (int yy = rect.top(); yy < bottom; ++yy) {
513 const uchar *src = srcData + yy * srcBytesPerLine + leftOffset;
514 ::memmove(dst, src, dstBytesPerLine);
515 dst += dstBytesPerLine;
516 }
517}
518
519template <class Pixel>
520static inline void copy_swapped(char *dst, const int dstStride, const QImage &img, const QRect &rect)
521{
522 const uchar *srcData = img.constBits();
523 const qsizetype srcBytesPerLine = img.bytesPerLine();
524
525 const int left = rect.left();
526 const int width = rect.width();
527 const int bottom = rect.bottom() + 1;
528
529 for (int yy = rect.top(); yy < bottom; ++yy) {
530 Pixel *dstPixels = reinterpret_cast<Pixel *>(dst);
531 const Pixel *srcPixels = reinterpret_cast<const Pixel *>(srcData + yy * srcBytesPerLine) + left;
532
533 for (int i = 0; i < width; ++i)
534 dstPixels[i] = qbswap<Pixel>(*srcPixels++);
535
536 dst += dstStride;
537 }
538}
539
540static QImage native_sub_image(QByteArray *buffer, const int dstStride, const QImage &src, const QRect &rect, bool swap)
541{
542 if (!swap && src.rect() == rect && src.bytesPerLine() == dstStride)
543 return src;
544
545 buffer->resize(rect.height() * dstStride);
546
547 if (swap) {
548 switch (src.depth()) {
549 case 32:
550 copy_swapped<quint32>(buffer->data(), dstStride, src, rect);
551 break;
552 case 16:
553 copy_swapped<quint16>(buffer->data(), dstStride, src, rect);
554 break;
555 }
556 } else {
557 copy_unswapped(buffer->data(), dstStride, src, rect);
558 }
559
560 return QImage(reinterpret_cast<const uchar *>(buffer->constData()), rect.width(), rect.height(), dstStride, src.format());
561}
562
564{
565 return (base + pad - 1) & -pad;
566}
567
568void QXcbBackingStoreImage::shmPutImage(xcb_drawable_t drawable, const QRegion &region, const QPoint &offset)
569{
570 for (const QRect &rect : region) {
571 const QPoint source = rect.translated(offset).topLeft();
572 xcb_shm_put_image(xcb_connection(),
573 drawable,
574 m_gc,
575 m_xcb_image->width,
576 m_xcb_image->height,
577 source.x(), source.y(),
578 rect.width(), rect.height(),
579 rect.x(), rect.y(),
580 m_xcb_image->depth,
581 m_xcb_image->format,
582 0, // send event?
583 m_shm_info.shmseg,
584 m_xcb_image->data - m_shm_info.shmaddr);
585 }
586 m_dirtyShm |= region.translated(offset);
587}
588
589void QXcbBackingStoreImage::flushPixmap(const QRegion &region, bool fullRegion)
590{
591 if (!fullRegion) {
592 auto actualRegion = m_pendingFlush.intersected(region);
593 m_pendingFlush -= region;
594 flushPixmap(actualRegion, true);
595 return;
596 }
597
598 xcb_image_t xcb_subimage;
599 memset(&xcb_subimage, 0, sizeof(xcb_image_t));
600
601 xcb_subimage.format = m_xcb_image->format;
602 xcb_subimage.scanline_pad = m_xcb_image->scanline_pad;
603 xcb_subimage.depth = m_xcb_image->depth;
604 xcb_subimage.bpp = m_xcb_image->bpp;
605 xcb_subimage.unit = m_xcb_image->unit;
606 xcb_subimage.plane_mask = m_xcb_image->plane_mask;
607 xcb_subimage.byte_order = (xcb_image_order_t) connection()->setup()->image_byte_order;
608 xcb_subimage.bit_order = m_xcb_image->bit_order;
609
610 const bool needsByteSwap = xcb_subimage.byte_order != m_xcb_image->byte_order;
611 // Ensure that we don't send more than maxPutImageRequestDataBytes per request.
612 const auto maxPutImageRequestDataBytes = connection()->maxRequestDataBytes(sizeof(xcb_put_image_request_t));
613
614 for (const QRect &rect : region) {
615 const quint32 stride = round_up_scanline(rect.width() * m_qimage.depth(), xcb_subimage.scanline_pad) >> 3;
616 const int rows_per_put = maxPutImageRequestDataBytes / stride;
617
618 // This assert could trigger if a single row has more pixels than fit in
619 // a single PutImage request. In the absence of the BIG-REQUESTS extension
620 // the theoretical maximum lengths of maxPutImageRequestDataBytes can be
621 // roughly 256kB.
622 Q_ASSERT(rows_per_put > 0);
623
624 // If we upload the whole image in a single chunk, the result might be
625 // larger than the server's maximum request size and stuff breaks.
626 // To work around that, we upload the image in chunks where each chunk
627 // is small enough for a single request.
628 const int x = rect.x();
629 int y = rect.y();
630 const int width = rect.width();
631 int height = rect.height();
632
633 while (height > 0) {
634 const int rows = std::min(height, rows_per_put);
635 const QRect subRect(x, y, width, rows);
636 const QImage subImage = native_sub_image(&m_flushBuffer, stride, m_qimage, subRect, needsByteSwap);
637
638 Q_ASSERT(static_cast<size_t>(subImage.sizeInBytes()) <= maxPutImageRequestDataBytes);
639
640 xcb_subimage.width = width;
641 xcb_subimage.height = rows;
642 xcb_subimage.data = const_cast<uint8_t *>(subImage.constBits());
643 xcb_image_annotate(&xcb_subimage);
644
645 xcb_image_put(xcb_connection(),
646 m_xcb_pixmap,
647 m_gc,
648 &xcb_subimage,
649 x,
650 y,
651 0);
652
653 y += rows;
654 height -= rows;
655 }
656 }
657}
658
659void QXcbBackingStoreImage::setClip(const QRegion &region)
660{
661 if (region.isEmpty()) {
662 static const uint32_t mask = XCB_GC_CLIP_MASK;
663 static const uint32_t values[] = { XCB_NONE };
664 xcb_change_gc(xcb_connection(), m_gc, mask, values);
665 } else {
666 const auto xcb_rects = qRegionToXcbRectangleList(region);
667 xcb_set_clip_rectangles(xcb_connection(),
668 XCB_CLIP_ORDERING_YX_BANDED,
669 m_gc,
670 0, 0,
671 xcb_rects.size(), xcb_rects.constData());
672 }
673}
674
675void QXcbBackingStoreImage::put(xcb_drawable_t dst, const QRegion &region, const QPoint &offset)
676{
677 Q_ASSERT(!m_clientSideScroll);
678
679 ensureGC(dst);
680
681 if (hasShm()) {
682 setClip(region); // Clip in window local coordinates
683
684 // Copy scrolled area on server-side from pixmap to window
685 const QRegion scrolledRegion = m_scrolledRegion.translated(-offset);
686 for (const QRect &rect : scrolledRegion) {
687 const QPoint source = rect.translated(offset).topLeft();
688 xcb_copy_area(xcb_connection(),
689 m_xcb_pixmap,
690 dst,
691 m_gc,
692 source.x(), source.y(),
693 rect.x(), rect.y(),
694 rect.width(), rect.height());
695 }
696
697 // Copy non-scrolled image from client-side memory to server-side window
698 const QRegion notScrolledArea = region - scrolledRegion;
699 shmPutImage(dst, notScrolledArea, offset);
700 } else {
701 const QRect bounds = region.boundingRect();
702 const QPoint target = bounds.topLeft();
703 const QRect source = bounds.translated(offset);
704
705 // First clip in backingstore-local coordinates, and upload
706 // the changed parts of the backingstore to the server.
707 setClip(source);
708 flushPixmap(source);
709
710 // Then clip in window local coordinates, and copy the updated
711 // parts of the backingstore image server-side to the window.
712 setClip(region);
713 xcb_copy_area(xcb_connection(),
714 m_xcb_pixmap,
715 dst,
716 m_gc,
717 source.x(), source.y(),
718 target.x(), target.y(),
719 source.width(), source.height());
720 }
721
722 setClip(QRegion());
723}
724
726{
727 if (hasShm()) {
728 // to prevent X from reading from the image region while we're writing to it
729 if (m_dirtyShm.intersects(region)) {
730 connection()->sync();
731 m_dirtyShm = QRegion();
732 }
733 }
734 m_scrolledRegion -= region;
735 m_pendingFlush |= region;
736}
737
738bool QXcbBackingStore::createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize, void *shmInfo)
739{
740 auto info = reinterpret_cast<xcb_shm_segment_info_t *>(shmInfo);
742}
743
750
755
757{
758 if (!m_image)
759 return nullptr;
760 return m_rgbImage.isNull() ? m_image->image() : &m_rgbImage;
761}
762
764{
765 if (!m_image)
766 return;
767
768 m_paintRegions.push(region);
769 m_image->preparePaint(region);
770
771 if (m_image->hasAlpha()) {
773 p.setCompositionMode(QPainter::CompositionMode_Source);
774 const QColor blank = Qt::transparent;
775 for (const QRect &rect : region)
776 p.fillRect(rect, blank);
777 }
778}
779
781{
783 qCWarning(lcQpaXcb, "%s: paint regions empty!", Q_FUNC_INFO);
784 return;
785 }
786
787 const QRegion region = m_paintRegions.pop();
788 m_image->preparePaint(region);
789
790 QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window()->handle());
791 if (!platformWindow || !platformWindow->imageNeedsRgbSwap())
792 return;
793
794 // Slow path: the paint device was m_rgbImage. Now copy with swapping red
795 // and blue into m_image.
796 auto it = region.begin();
797 const auto end = region.end();
798 if (it == end)
799 return;
801 while (it != end) {
802 const QRect rect = *(it++);
803 p.drawImage(rect.topLeft(), m_rgbImage.copy(rect).rgbSwapped());
804 }
805}
806
808{
809 // If the backingstore is rgbSwapped, return the internal image type here.
810 if (!m_rgbImage.isNull())
811 return m_rgbImage;
812
813 if (!m_image || !m_image->image())
814 return QImage();
815
817
819
820 // Return an image that does not share QImageData with the original image,
821 // even if they both point to the same data of the m_xcb_image, otherwise
822 // painting to m_qimage would detach it from the m_xcb_image data.
823 return QImage(image.constBits(), image.width(), image.height(), image.format());
824}
825
830
832{
833 if (!m_image || m_image->size().isEmpty())
834 return;
835
837
839
840 QRegion clipped = region;
842 clipped &= QRect(0, 0, imageSize.width(), imageSize.height()).translated(-offset);
843
844 QRect bounds = clipped.boundingRect();
845
846 if (bounds.isNull())
847 return;
848
849 QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
850 if (!platformWindow) {
851 qCWarning(lcQpaXcb, "%s QWindow has no platform window, see QTBUG-32681", Q_FUNC_INFO);
852 return;
853 }
854
855 render(platformWindow->xcb_window(), clipped, offset);
856
857 if (platformWindow->needsSync())
858 platformWindow->updateSyncRequestCounter();
859 else
860 xcb_flush(xcb_connection());
861}
862
863void QXcbBackingStore::render(xcb_window_t window, const QRegion &region, const QPoint &offset)
864{
865 m_image->put(window, region, offset);
866}
867
869 qreal sourceDevicePixelRatio,
870 const QRegion &region,
871 const QPoint &offset,
873 bool translucentBackground)
874{
875 if (!m_image || m_image->size().isEmpty())
876 return FlushFailed;
877
879
880 auto result = QPlatformBackingStore::rhiFlush(window, sourceDevicePixelRatio, region, offset,
881 textures, translucentBackground);
882 if (result != FlushSuccess)
883 return result;
884 QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
885 if (platformWindow->needsSync()) {
886 platformWindow->updateSyncRequestCounter();
887 } else {
888 xcb_flush(xcb_connection());
889 }
890
891 return FlushSuccess;
892}
893
895{
896 if (m_image && size == m_image->size())
897 return;
898
899 QPlatformWindow *pw = window()->handle();
900 if (!pw) {
901 window()->create();
902 pw = window()->handle();
903 }
904 QXcbWindow* win = static_cast<QXcbWindow *>(pw);
905
907}
908
910{
911 if (m_image)
913 else
915
916 // Slow path for bgr888 VNC: Create an additional image, paint into that and
917 // swap R and B while copying to m_image after each paint.
918 if (win->imageNeedsRgbSwap()) {
919 m_rgbImage = QImage(size, win->imageFormat());
920 }
921}
922
923bool QXcbBackingStore::scroll(const QRegion &area, int dx, int dy)
924{
925 if (m_image)
926 return m_image->scroll(area, dx, dy);
927
928 return false;
929}
930
933{
934 // We need three different behaviors depending on whether the X11 visual
935 // for the system tray supports an alpha channel, i.e. is 32 bits, and
936 // whether XRender can be used:
937 // 1) if the visual has an alpha channel, then render the window's buffer
938 // directly to the X11 window as usual
939 // 2) else if XRender can be used, then render the window's buffer to Pixmap,
940 // then render Pixmap's contents to the cleared X11 window with
941 // xcb_render_composite()
942 // 3) else grab the X11 window's content and paint it first each time as a
943 // background before rendering the window's buffer to the X11 window
944
945 auto *platformWindow = static_cast<QXcbWindow *>(window->handle());
946 quint8 depth = connection()->primaryScreen()->depthOfVisual(platformWindow->visualId());
947
948 if (depth != 32) {
949 platformWindow->setParentRelativeBackPixmap();
950 initXRenderMode();
951 m_useGrabbedBackgound = !m_usingXRenderMode;
952 }
953}
954
956{
957 if (m_xrenderPicture) {
958 xcb_render_free_picture(xcb_connection(), m_xrenderPicture);
959 m_xrenderPicture = XCB_NONE;
960 }
961 if (m_xrenderPixmap) {
962 xcb_free_pixmap(xcb_connection(), m_xrenderPixmap);
963 m_xrenderPixmap = XCB_NONE;
964 }
965 if (m_windowPicture) {
966 xcb_render_free_picture(xcb_connection(), m_windowPicture);
967 m_windowPicture = XCB_NONE;
968 }
969}
970
972{
974
975 if (m_useGrabbedBackgound) {
977 p.setCompositionMode(QPainter::CompositionMode_Source);
978 for (const QRect &rect: region)
979 p.drawPixmap(rect, m_grabbedBackground, rect);
980 }
981}
982
983void QXcbSystemTrayBackingStore::render(xcb_window_t window, const QRegion &region, const QPoint &offset)
984{
985 if (!m_usingXRenderMode) {
987 return;
988 }
989
990 m_image->put(m_xrenderPixmap, region, offset);
991 const QRect bounds = region.boundingRect();
992 const QPoint target = bounds.topLeft();
993 const QRect source = bounds.translated(offset);
994 xcb_clear_area(xcb_connection(), false, window,
995 target.x(), target.y(), source.width(), source.height());
996 xcb_render_composite(xcb_connection(), XCB_RENDER_PICT_OP_OVER,
997 m_xrenderPicture, 0, m_windowPicture,
998 target.x(), target.y(), 0, 0, target.x(), target.y(),
999 source.width(), source.height());
1000}
1001
1003{
1004 if (!m_usingXRenderMode) {
1006
1007 if (m_useGrabbedBackgound) {
1008 xcb_clear_area(xcb_connection(), false, win->xcb_window(),
1009 0, 0, size.width(), size.height());
1010 m_grabbedBackground = win->xcbScreen()->grabWindow(win->winId(), 0, 0,
1011 size.width(), size.height());
1012 }
1013 return;
1014 }
1015
1016 if (m_xrenderPicture) {
1017 xcb_render_free_picture(xcb_connection(), m_xrenderPicture);
1018 m_xrenderPicture = XCB_NONE;
1019 }
1020 if (m_xrenderPixmap) {
1021 xcb_free_pixmap(xcb_connection(), m_xrenderPixmap);
1022 m_xrenderPixmap = XCB_NONE;
1023 }
1024
1025 QXcbScreen *screen = win->xcbScreen();
1026
1027 m_xrenderPixmap = xcb_generate_id(xcb_connection());
1028 xcb_create_pixmap(xcb_connection(), 32, m_xrenderPixmap, screen->root(), size.width(), size.height());
1029
1030 m_xrenderPicture = xcb_generate_id(xcb_connection());
1031 xcb_render_create_picture(xcb_connection(), m_xrenderPicture, m_xrenderPixmap, m_xrenderPictFormat, 0, nullptr);
1032
1033 // XRender expects premultiplied alpha
1034 if (m_image)
1036 else
1038}
1039
1040void QXcbSystemTrayBackingStore::initXRenderMode()
1041{
1042 if (!connection()->hasXRender())
1043 return;
1044
1045 xcb_connection_t *conn = xcb_connection();
1046 auto formatsReply = Q_XCB_REPLY(xcb_render_query_pict_formats, conn);
1047
1048 if (!formatsReply) {
1049 qWarning("QXcbSystemTrayBackingStore: xcb_render_query_pict_formats() failed");
1050 return;
1051 }
1052
1053 xcb_render_pictforminfo_t *fmt = xcb_render_util_find_standard_format(formatsReply.get(),
1054 XCB_PICT_STANDARD_ARGB_32);
1055 if (!fmt) {
1056 qWarning("QXcbSystemTrayBackingStore: Failed to find format PICT_STANDARD_ARGB_32");
1057 return;
1058 }
1059
1060 m_xrenderPictFormat = fmt->id;
1061
1062 auto *platformWindow = static_cast<QXcbWindow *>(window()->handle());
1063 xcb_render_pictvisual_t *vfmt = xcb_render_util_find_visual_format(formatsReply.get(), platformWindow->visualId());
1064
1065 if (!vfmt) {
1066 qWarning("QXcbSystemTrayBackingStore: Failed to find format for visual %x", platformWindow->visualId());
1067 return;
1068 }
1069
1070 m_windowPicture = xcb_generate_id(conn);
1071 xcb_void_cookie_t cookie =
1072 xcb_render_create_picture_checked(conn, m_windowPicture, platformWindow->xcb_window(), vfmt->format, 0, nullptr);
1073 xcb_generic_error_t *error = xcb_request_check(conn, cookie);
1074 if (error) {
1075 qWarning("QXcbSystemTrayBackingStore: Failed to create Picture with format %x for window %x, error code %d",
1076 vfmt->format, platformWindow->xcb_window(), error->error_code);
1077 free(error);
1078 return;
1079 }
1080
1081 m_usingXRenderMode = true;
1082}
1083
\inmodule QtCore
Definition qbytearray.h:57
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtGui
Definition qimage.h:37
static QPixelFormat toPixelFormat(QImage::Format format) noexcept
Converts format into a QPixelFormat.
Definition qimage.cpp:6392
qsizetype bytesPerLine() const
Returns the number of bytes per image scanline.
Definition qimage.cpp:1560
QImage copy(const QRect &rect=QRect()) const
Returns a sub-area of the image as a new image.
qsizetype sizeInBytes() const
Definition qimage.cpp:1548
QSize size() const
Returns the size of the image, i.e.
uchar * bits()
Returns a pointer to the first pixel data.
Definition qimage.cpp:1698
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition qimage.cpp:1222
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_Invalid
Definition qimage.h:42
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
QImage rgbSwapped() const &
Definition qimage.h:223
Format format() const
Returns the format of the image.
Definition qimage.cpp:2162
const uchar * constBits() const
Returns a pointer to the first pixel data.
Definition qimage.cpp:1733
int depth() const
bool isEmpty() const noexcept
Definition qlist.h:401
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
@ CompositionMode_Source
Definition qpainter.h:101
The QPlatformBackingStore class provides the drawing area for top-level windows.
QWindow * window() const
Returns a pointer to the top-level window associated with this surface.
virtual FlushResult rhiFlush(QWindow *window, qreal sourceDevicePixelRatio, const QRegion &region, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground)
Flushes the given region from the specified window, and compositing it with the specified textures li...
Origin
This enum describes the origin of the content of the buffer.
The QPlatformWindow class provides an abstraction for top-level windows.
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition qrect.h:164
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:221
constexpr QRect translated(int dx, int dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
Definition qrect.h:261
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
QRect boundingRect() const noexcept
Returns the bounding rectangle of this region.
bool isNull() const
bool intersects(const QRegion &r) const
Definition qregion.cpp:613
const_iterator end() const noexcept
bool isEmpty() const
Returns true if the region is empty; otherwise returns false.
const_iterator begin() const noexcept
QRegion intersected(const QRegion &r) const
QRegion translated(int dx, int dy) const
Definition qregion.cpp:593
QPlatformScreen * handle() const
Get the platform screen handle.
Definition qscreen.cpp:83
\inmodule QtCore
Definition qsize.h:25
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:124
T pop()
Removes the top item from the stack and returns it.
Definition qstack.h:18
void push(const T &t)
Adds element t to the top of the stack.
Definition qstack.h:17
@ BigEndian
Definition qsysinfo.h:29
@ ByteOrder
Definition qsysinfo.h:34
int width
the width of the widget excluding any window frame
Definition qwidget.h:114
WId winId() const
Returns the window system identifier of the widget.
Definition qwidget.cpp:2357
\inmodule QtGui
Definition qwindow.h:63
int x
the x position of the window's geometry
Definition qwindow.h:80
QSize size() const override
Returns the size of the window excluding any window frame.
Definition qwindow.h:210
void flushScrolledRegion(bool clientSideScroll)
QXcbBackingStoreImage(QXcbBackingStore *backingStore, const QSize &size)
void put(xcb_drawable_t dst, const QRegion &region, const QPoint &offset)
void resize(const QSize &size)
void preparePaint(const QRegion &region)
QPlatformGraphicsBuffer * graphicsBuffer()
bool scroll(const QRegion &area, int dx, int dy)
static bool createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize=1, xcb_shm_segment_info_t *shm_info=nullptr)
QXcbBackingStoreImage * m_image
QPlatformGraphicsBuffer * graphicsBuffer() const override
Accessor for a backingstores graphics buffer abstraction.
void resize(const QSize &size, const QRegion &staticContents) override
FlushResult rhiFlush(QWindow *window, qreal sourceDevicePixelRatio, const QRegion &region, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground) override
Flushes the given region from the specified window, and compositing it with the specified textures li...
QPaintDevice * paintDevice() override
Implement this function to return the appropriate paint device.
QXcbBackingStore(QWindow *window)
void endPaint() override
This function is called after painting onto the surface has ended.
virtual void render(xcb_window_t window, const QRegion &region, const QPoint &offset)
void flush(QWindow *window, const QRegion &region, const QPoint &offset) override
Flushes the given region from the specified window.
void beginPaint(const QRegion &) override
This function is called before painting onto the surface begins, with the region in which the paintin...
virtual void recreateImage(QXcbWindow *win, const QSize &size)
QStack< QRegion > m_paintRegions
static bool createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize=1, void *shmInfo=nullptr)
QImage toImage() const override
Implemented in subclasses to return the content of the backingstore as a QImage.
bool scroll(const QRegion &area, int dx, int dy) override
Scrolls the given area dx pixels to the right and dy downward; both dx and dy may be negative.
size_t maxRequestDataBytes(size_t requestSize) const
const xcb_setup_t * setup() const
void printXcbError(const char *message, xcb_generic_error_t *error)
QXcbScreen * primaryScreen() const
const xcb_format_t * formatForDepth(uint8_t depth) const
uchar * data() override
Accessor for the bytes of the buffer.
QXcbGraphicsBuffer(QImage *image)
const uchar * data() const override
Accessor for the bytes of the buffer.
int bytesPerLine() const override
Accessor for bytes per line in the graphics buffer.
Origin origin() const override
In origin of the content of the graphics buffer.
bool doLock(AccessTypes access, const QRect &rect) override
This function should be reimplemented by subclasses.
void doUnlock() override
This function should remove all locks set on the buffer.
QXcbConnection * connection() const
Definition qxcbobject.h:17
xcb_connection_t * xcb_connection() const
Definition qxcbobject.h:20
void setConnection(QXcbConnection *connection)
Definition qxcbobject.h:16
quint8 depthOfVisual(xcb_visualid_t visualid) const
Definition qxcbscreen.h:176
void render(xcb_window_t window, const QRegion &region, const QPoint &offset) override
void beginPaint(const QRegion &) override
This function is called before painting onto the surface begins, with the region in which the paintin...
void recreateImage(QXcbWindow *win, const QSize &size) override
QXcbSystemTrayBackingStore(QWindow *window)
xcb_window_t xcb_window() const
Definition qxcbwindow.h:94
void updateSyncRequestCounter()
bool imageNeedsRgbSwap() const
Definition qxcbwindow.h:97
bool needsSync() const
EGLint EGLint EGLint EGLint int int int int * fds
QSet< QString >::iterator it
rect
[4]
T toNativePixels(const T &value, const C *context)
Combined button and popup list for selecting options.
@ transparent
Definition qnamespace.h:47
Definition image.cpp:4
#define Q_UNLIKELY(x)
#define Q_FUNC_INFO
DBusConnection const char DBusError * error
DBusConnection * connection
static int area(const QSize &s)
Definition qicon.cpp:153
QImage::Format qt_maybeDataCompatibleAlphaVersion(QImage::Format format)
Definition qimage_p.h:478
static const double leftOffset
#define qWarning
Definition qlogging.h:166
#define qCWarning(category,...)
#define qCDebug(category,...)
GLenum GLsizei GLsizei GLint * values
[15]
GLint GLint GLint GLint GLint x
[0]
GLint GLenum GLsizei GLsizei GLsizei depth
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLuint const GLuint GLuint const GLuint * textures
GLenum GLuint id
[7]
GLenum src
const void GLsizei GLsizei stride
GLenum GLuint buffer
GLint GLsizei width
GLint left
GLenum GLenum dst
GLint GLint bottom
GLenum access
GLenum target
GLenum GLuint GLintptr offset
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLint GLsizei GLsizei GLenum format
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei imageSize
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLsizei GLsizei GLchar * source
const GLubyte * c
GLint void * img
Definition qopenglext.h:233
GLenum const void * addr
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define MAP_FAILED
QScreen * screen
[1]
Definition main.cpp:29
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187
unsigned char quint8
Definition qtypes.h:46
static const uint base
Definition qurlidna.cpp:20
QVideoFrameFormat::PixelFormat fmt
static void copy_swapped(char *dst, const int dstStride, const QImage &img, const QRect &rect)
void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset)
static QImage native_sub_image(QByteArray *buffer, const int dstStride, const QImage &src, const QRect &rect, bool swap)
static quint32 round_up_scanline(quint32 base, quint32 pad)
static void copy_unswapped(char *dst, int dstBytesPerLine, const QImage &img, const QRect &rect)
static size_t imageDataSize(const xcb_image_t *image)
#define Q_XCB_REPLY(call,...)
#define Q_XCB_REPLY_UNCHECKED(call,...)
QList< xcb_rectangle_t > qRegionToXcbRectangleList(const QRegion &region)
QWidget * win
Definition settings.cpp:6
this swap(other)
aWidget window() -> setWindowTitle("New Window Title")
[2]
QNetworkReply * reply
QHostInfo info
[0]