File indexing completed on 2024-04-28 04:58:58

0001 /* This file is part of the KDE project
0002    Copyright (C) 2017 Alexey Min <alexey.min@gmail.com>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 */
0009 
0010 #include "xcb_framebuffer.h"
0011 #include "krfb_fb_xcb_debug.h"
0012 
0013 #include <xcb/xproto.h>
0014 #include <xcb/damage.h>
0015 #include <xcb/shm.h>
0016 #include <xcb/xcb_image.h>
0017 
0018 #include <sys/ipc.h>
0019 #include <sys/shm.h>
0020 
0021 #include <QApplication>
0022 #include <QGuiApplication>
0023 #include <QScreen>
0024 #include <QAbstractNativeEventFilter>
0025 #include <qpa/qplatformnativeinterface.h>
0026 #include <QtGui/private/qtx11extras_p.h>
0027 
0028 class KrfbXCBEventFilter: public QAbstractNativeEventFilter
0029 {
0030 public:
0031     KrfbXCBEventFilter(XCBFrameBuffer *owner);
0032 
0033 public:
0034     bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
0035 
0036 public:
0037     int xdamageBaseEvent;
0038     int xdamageBaseError;
0039     int xshmBaseEvent;
0040     int xshmBaseError;
0041     bool xshmAvail;
0042     XCBFrameBuffer *fb_owner;
0043 };
0044 
0045 
0046 
0047 KrfbXCBEventFilter::KrfbXCBEventFilter(XCBFrameBuffer *owner):
0048     xdamageBaseEvent(0), xdamageBaseError(0),
0049     xshmBaseEvent(0), xshmBaseError(0), xshmAvail(false),
0050     fb_owner(owner)
0051 {
0052     const xcb_query_extension_reply_t *xdamage_data = xcb_get_extension_data(
0053                 QX11Info::connection(), &xcb_damage_id);
0054     if (xdamage_data) {
0055         // also query extension version!
0056         // ATTENTION: if we don't do that, xcb_damage_create() will always FAIL!
0057         xcb_damage_query_version_reply_t *xdamage_version = xcb_damage_query_version_reply(
0058                     QX11Info::connection(),
0059                     xcb_damage_query_version(
0060                         QX11Info::connection(),
0061                         XCB_DAMAGE_MAJOR_VERSION,
0062                         XCB_DAMAGE_MINOR_VERSION),
0063                     nullptr);
0064         if (!xdamage_version) {
0065             qWarning() << "xcb framebuffer: ERROR: Failed to get XDamage extension version!\n";
0066             return;
0067         }
0068 
0069 #ifdef _DEBUG
0070         qCDebug(KRFB_FB_XCB) << "xcb framebuffer: XDamage extension version:" <<
0071                xdamage_version->major_version << "." << xdamage_version->minor_version;
0072 #endif
0073 
0074         free(xdamage_version);
0075 
0076         xdamageBaseEvent = xdamage_data->first_event;
0077         xdamageBaseError = xdamage_data->first_error;
0078 
0079         // XShm presence is optional. If it is present, all image getting
0080         // operations will be faster, without XShm it will only be slower.
0081         const xcb_query_extension_reply_t *xshm_data = xcb_get_extension_data(
0082                     QX11Info::connection(), &xcb_shm_id);
0083         if (xshm_data) {
0084             xshmAvail = true;
0085             xshmBaseEvent = xshm_data->first_event;
0086             xshmBaseError = xshm_data->first_error;
0087         } else {
0088             xshmAvail = false;
0089             qWarning() << "xcb framebuffer: WARNING: XSHM extension not available!";
0090         }
0091     } else {
0092         // if there is no xdamage available, this plugin can be considered useless anyway.
0093         // you can use just qt framebuffer plugin instead...
0094         qWarning() << "xcb framebuffer: ERROR: no XDamage extension available. I am useless.";
0095         qWarning() << "xcb framebuffer:        use qt framebuffer plugin instead.";
0096     }
0097 }
0098 
0099 
0100 bool KrfbXCBEventFilter::nativeEventFilter(const QByteArray &eventType,
0101                                            void *message, qintptr *result) {
0102     Q_UNUSED(result);  // "result" is only used on windows
0103 
0104     if (xdamageBaseEvent == 0) return false;  // no xdamage extension
0105 
0106     if (eventType == "xcb_generic_event_t") {
0107         auto ev = static_cast<xcb_generic_event_t *>(message);
0108         if ((ev->response_type & 0x7F) == (xdamageBaseEvent + XCB_DAMAGE_NOTIFY)) {
0109             // this is xdamage notification
0110             this->fb_owner->handleXDamageNotify(ev);
0111             return true;  // filter out this event, stop its processing
0112         }
0113     }
0114 
0115     // continue event processing
0116     return false;
0117 }
0118 
0119 
0120 class XCBFrameBuffer::P {
0121 public:
0122     xcb_damage_damage_t     damage;
0123     xcb_shm_segment_info_t  shminfo;
0124     xcb_screen_t           *rootScreen;   // X screen info (all monitors)
0125     xcb_image_t            *framebufferImage;
0126     xcb_image_t            *updateTile;
0127 
0128     KrfbXCBEventFilter     *x11EvtFilter;
0129 
0130     bool                    running;
0131 
0132     QRect                   area;  // capture area, primary monitor coordinates
0133     WId                     win;
0134 };
0135 
0136 
0137 static xcb_screen_t *get_xcb_screen(xcb_connection_t *conn, int screen_num) {
0138     xcb_screen_t *screen = nullptr;
0139     xcb_screen_iterator_t screens_iter = xcb_setup_roots_iterator(xcb_get_setup(conn));
0140     for (; screens_iter.rem; --screen_num, xcb_screen_next(&screens_iter))
0141         if (screen_num == 0)
0142             screen = screens_iter.data;
0143     return screen;
0144 }
0145 
0146 
0147 
0148 XCBFrameBuffer::XCBFrameBuffer(QObject *parent):
0149     FrameBuffer(parent), d(new XCBFrameBuffer::P)
0150 {
0151     d->running = false;
0152     d->damage = XCB_NONE;
0153     d->framebufferImage = nullptr;
0154     d->shminfo.shmaddr = nullptr;
0155     d->shminfo.shmid = XCB_NONE;
0156     d->shminfo.shmseg = XCB_NONE;
0157     d->updateTile = nullptr;
0158     d->area.setRect(0, 0, 0, 0);
0159     d->x11EvtFilter = new KrfbXCBEventFilter(this);
0160     d->rootScreen = get_xcb_screen(QX11Info::connection(), QX11Info::appScreen());
0161 
0162     this->fb = nullptr;
0163 
0164     QScreen *primaryScreen = QGuiApplication::primaryScreen();
0165     if (primaryScreen) {
0166         QPlatformNativeInterface* native = qApp->platformNativeInterface();
0167         d->win = reinterpret_cast<WId>(native->nativeResourceForScreen(QByteArrayLiteral("rootwindow"), primaryScreen));
0168         qreal scaleFactor = primaryScreen->devicePixelRatio();
0169         d->area = { primaryScreen->geometry().topLeft() * scaleFactor,
0170                     primaryScreen->geometry().bottomRight() * scaleFactor };
0171 
0172         qCDebug(KRFB_FB_XCB) << "xcb framebuffer: Primary screen: " << primaryScreen->name()
0173                  << ", geometry: " << primaryScreen->geometry()
0174                  << ", device scaling: " << scaleFactor
0175                  << ", native size: " << d->area
0176                  << ", depth: " << primaryScreen->depth();
0177     } else {
0178         qWarning() << "xcb framebuffer: ERROR: Failed to get application's primary screen info!";
0179         return;
0180     }
0181 
0182     d->framebufferImage = xcb_image_get(QX11Info::connection(),
0183                                         d->win,
0184                                         d->area.left(),
0185                                         d->area.top(),
0186                                         d->area.width(),
0187                                         d->area.height(),
0188                                         0xFFFFFFFF, // == Xlib: AllPlanes ((unsigned long)~0L)
0189                                         XCB_IMAGE_FORMAT_Z_PIXMAP);
0190     if (d->framebufferImage) {
0191 #ifdef _DEBUG
0192         qCDebug(KRFB_FB_XCB) << "xcb framebuffer: Got primary screen image. bpp: " << d->framebufferImage->bpp
0193                  << ", size (" << d->framebufferImage->width << d->framebufferImage->height << ")"
0194                  << ", depth: " << d->framebufferImage->depth
0195                  << ", padded width: " << d->framebufferImage->stride;
0196 #endif
0197         this->fb = (char *)d->framebufferImage->data;
0198     } else {
0199         qWarning() << "xcb framebuffer: ERROR: Failed to get primary screen image!";
0200         return;
0201     }
0202 
0203     // all XShm operations should take place only if Xshm extension was loaded
0204     if (d->x11EvtFilter->xshmAvail) {
0205         // Create xcb_image_t structure, but do not automatically allocate memory
0206         // for image data storage - it will be allocated as shared memory.
0207         // "If base == 0 and bytes == ~0 and data == 0, no storage will be auto-allocated."
0208         // Width and height of the image = size of the capture area.
0209         d->updateTile = xcb_image_create_native(
0210                     QX11Info::connection(),
0211                     d->area.width(),            // width
0212                     d->area.height(),           // height
0213                     XCB_IMAGE_FORMAT_Z_PIXMAP,  // image format
0214                     d->rootScreen->root_depth,  // depth
0215                     nullptr,                    // base address = 0
0216                     (uint32_t)~0,               // bytes = 0xffffffff
0217                     nullptr);                   // data = 0
0218         if (d->updateTile) {
0219 #ifdef _DEBUG
0220             qCDebug(KRFB_FB_XCB) << "xcb framebuffer: Successfully created new empty image in native format"
0221                 << "\n    size: " << d->updateTile->width << "x" << d->updateTile->height
0222                     << "(stride: " << d->updateTile->stride << ")"
0223                 << "\n    bpp, depth:  " << d->updateTile->bpp << d->updateTile->depth // 32, 24
0224                 << "\n    addr of base, data:  " << d->updateTile->base << (void *)d->updateTile->data
0225                 << "\n    size:  " << d->updateTile->size
0226                 << "\n    image byte order = " << d->updateTile->byte_order   // == 0 .._LSB_FIRST
0227                 << "\n    image bit order = " << d->updateTile->bit_order     // == 1 .._MSB_FIRST
0228                 << "\n    image plane_mask = " << d->updateTile->plane_mask;  // == 16777215 == 0x00FFFFFF
0229 #endif
0230 
0231             // allocate shared memory block only once, make its size large enough
0232             // to fit whole capture area (d->area rect)
0233             // so, we get as many bytes as needed for image (updateTile->size)
0234             d->shminfo.shmid = shmget(IPC_PRIVATE, d->updateTile->size, IPC_CREAT | 0777);
0235             // attached shared memory address is stored both in shminfo structure and in xcb_image_t->data
0236             d->shminfo.shmaddr = (uint8_t *)shmat(d->shminfo.shmid, nullptr, 0);
0237             d->updateTile->data = d->shminfo.shmaddr;
0238             // we keep updateTile->base == NULL here, so xcb_image_destroy() will not attempt
0239             // to free this block, just in case.
0240 
0241             // attach this shm segment also to X server
0242             d->shminfo.shmseg = xcb_generate_id(QX11Info::connection());
0243             xcb_shm_attach(QX11Info::connection(), d->shminfo.shmseg, d->shminfo.shmid, 0);
0244 
0245 #ifdef _DEBUG
0246             qCDebug(KRFB_FB_XCB) << "    shm id: " << d->shminfo.shmseg << ", addr: " << (void *)d->shminfo.shmaddr;
0247 #endif
0248 
0249             // will return 1 on success (yes!)
0250             int shmget_res = xcb_image_shm_get(
0251                         QX11Info::connection(),
0252                         d->win,
0253                         d->updateTile,
0254                         d->shminfo,
0255                         d->area.left(), // x
0256                         d->area.top(),  // y (size taken from image structure itself)?
0257                         0xFFFFFFFF);
0258 
0259             if (shmget_res == 0) {
0260                 // error! shared mem not working?
0261                 // will not use shared mem! detach and cleanup
0262                 xcb_shm_detach(QX11Info::connection(), d->shminfo.shmseg);
0263                 shmdt(d->shminfo.shmaddr);
0264                 shmctl(d->shminfo.shmid, IPC_RMID, nullptr); // mark shm segment as removed
0265                 d->x11EvtFilter->xshmAvail = false;
0266                 d->shminfo.shmaddr = nullptr;
0267                 d->shminfo.shmid = XCB_NONE;
0268                 d->shminfo.shmseg = XCB_NONE;
0269                 qWarning() << "xcb framebuffer: ERROR: xcb_image_shm_get() result: " << shmget_res;
0270             }
0271 
0272             // image is freed, and recreated again for every new damage rectangle
0273             // data was allocated manually and points to shared mem;
0274             // tell xcb_image_destroy() do not free image data
0275             d->updateTile->data = nullptr;
0276             xcb_image_destroy(d->updateTile);
0277             d->updateTile = nullptr;
0278         }
0279     }
0280 
0281 #ifdef _DEBUG
0282     qCDebug(KRFB_FB_XCB) << "xcb framebuffer: XCBFrameBuffer(), xshm base event = " << d->x11EvtFilter->xshmBaseEvent
0283              << ", xshm base error = " << d->x11EvtFilter->xdamageBaseError
0284              << ", xdamage base event = " << d->x11EvtFilter->xdamageBaseEvent
0285              << ", xdamage base error = " << d->x11EvtFilter->xdamageBaseError;
0286 #endif
0287 
0288     QCoreApplication::instance()->installNativeEventFilter(d->x11EvtFilter);
0289 }
0290 
0291 
0292 XCBFrameBuffer::~XCBFrameBuffer() {
0293     // first - uninstall x11 event filter
0294     QCoreApplication::instance()->removeNativeEventFilter(d->x11EvtFilter);
0295     //
0296     if (d->framebufferImage) {
0297         xcb_image_destroy(d->framebufferImage);
0298         fb = nullptr;  // image data was already destroyed by above call
0299     }
0300     if (d->x11EvtFilter->xshmAvail) {
0301         // detach shared memory
0302         if (d->shminfo.shmseg != XCB_NONE)
0303             xcb_shm_detach(QX11Info::connection(), d->shminfo.shmseg); // detach from X server
0304         if (d->shminfo.shmaddr)
0305             shmdt(d->shminfo.shmaddr); // detach addr from our address space
0306         if (d->shminfo.shmid != XCB_NONE)
0307             shmctl(d->shminfo.shmid, IPC_RMID, nullptr); // mark shm segment as removed
0308     }
0309     // and delete image used for shared mem
0310     if (d->updateTile) {
0311         d->updateTile->base = nullptr;
0312         d->updateTile->data = nullptr;
0313         xcb_image_destroy(d->updateTile);
0314     }
0315     // we don't use d->x11EvtFilter anymore, can delete it now
0316     if (d->x11EvtFilter) {
0317         delete d->x11EvtFilter;
0318     }
0319     delete d;
0320 }
0321 
0322 
0323 int XCBFrameBuffer::depth() {
0324     if (d->framebufferImage) {
0325         return d->framebufferImage->depth;
0326     }
0327     return 0;
0328 }
0329 
0330 
0331 int XCBFrameBuffer::height() {
0332     if (d->framebufferImage) {
0333         return d->framebufferImage->height;
0334     }
0335     return 0;
0336 }
0337 
0338 
0339 int XCBFrameBuffer::width() {
0340     if (d->framebufferImage) {
0341         return d->framebufferImage->width;
0342     }
0343     return 0;
0344 }
0345 
0346 int XCBFrameBuffer::paddedWidth() {
0347     if (d->framebufferImage) {
0348         return d->framebufferImage->stride;
0349     }
0350     return 0;
0351 }
0352 
0353 
0354 void XCBFrameBuffer::getServerFormat(rfbPixelFormat &format) {
0355     if (!d->framebufferImage) return;
0356 
0357     // get information about XCB visual params
0358     xcb_visualtype_t *root_visualtype = nullptr;  // visual info
0359     if (d->rootScreen) {
0360         xcb_visualid_t root_visual = d->rootScreen->root_visual;
0361         xcb_depth_iterator_t depth_iter;
0362         // To get the xcb_visualtype_t structure, it's a bit less easy.
0363         // You have to get the xcb_screen_t structure that you want, get its
0364         // root_visual member, then iterate over the xcb_depth_t's and the
0365         // xcb_visualtype_t's, and compare the xcb_visualid_t of these
0366         // xcb_visualtype_ts: with root_visual
0367         depth_iter = xcb_screen_allowed_depths_iterator(d->rootScreen);
0368         for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
0369             xcb_visualtype_iterator_t visual_iter;
0370             visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
0371             for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) {
0372                 if (root_visual == visual_iter.data->visual_id) {
0373                     root_visualtype = visual_iter.data;
0374                     break;
0375                 }
0376             }
0377         }
0378     }
0379 
0380 
0381     // fill in format common info
0382     format.bitsPerPixel = d->framebufferImage->bpp;
0383     format.depth = d->framebufferImage->depth;
0384     format.trueColour = true;  // not using color palettes
0385     format.bigEndian = false;  // always false for ZPIXMAP format!
0386 
0387     // information about pixels layout
0388 
0389     if (root_visualtype) {
0390 #ifdef _DEBUG
0391         qDebug("xcb framebuffer: Got info about root visual:\n"
0392                "    bits per rgb value: %d\n"
0393                "    red   mask: %08x\n"
0394                "    green mask: %08x\n"
0395                "    blue  mask: %08x\n",
0396                (int)root_visualtype->bits_per_rgb_value,
0397                root_visualtype->red_mask,
0398                root_visualtype->green_mask,
0399                root_visualtype->blue_mask);
0400 #endif
0401 
0402         // calculate shifts
0403         format.redShift = 0;
0404         if (root_visualtype->red_mask) {
0405             while (!(root_visualtype->red_mask & (1 << format.redShift))) {
0406                 format.redShift++;
0407             }
0408         }
0409         format.greenShift = 0;
0410         if (root_visualtype->green_mask) {
0411             while (!(root_visualtype->green_mask & (1 << format.greenShift))) {
0412                 format.greenShift++;
0413             }
0414         }
0415         format.blueShift = 0;
0416         if (root_visualtype->blue_mask) {
0417             while (!(root_visualtype->blue_mask & (1 << format.blueShift))) {
0418                 format.blueShift++;
0419             }
0420         }
0421 
0422         // calculate pixel max value.
0423         // NOTE: bits_per_rgb_value is unreliable, thus should be avoided.
0424         format.redMax   = root_visualtype->red_mask >> format.redShift;
0425         format.greenMax = root_visualtype->green_mask >> format.greenShift;
0426         format.blueMax  = root_visualtype->blue_mask >> format.blueShift;
0427 
0428 #ifdef _DEBUG
0429         qCDebug(KRFB_FB_XCB,
0430             "    Calculated redShift   = %d\n"
0431             "    Calculated greenShift = %d\n"
0432             "    Calculated blueShift  = %d\n"
0433             "    Calculated max values: R%d G%d B%d",
0434                format.redShift, format.greenShift, format.blueShift
0435                format.redMax, format.greenMax, format.blueMax);
0436 #endif
0437     } else {
0438         // some kind of fallback (unlikely code execution will go this way)
0439         // idea taken from qt framefuffer sources
0440         if (format.bitsPerPixel == 8) {
0441             format.redShift   = 0;
0442             format.greenShift = 3;
0443             format.blueShift  = 6;
0444             format.redMax   = 7;
0445             format.greenMax = 7;
0446             format.blueMax  = 3;
0447         } else if (format.bitsPerPixel == 16) {
0448             // TODO: 16 bits per pixel format ??
0449             // what format of pixels does X server use for 16-bpp?
0450         } else if (format.bitsPerPixel == 32) {
0451             format.redMax   = 0xff;
0452             format.greenMax = 0xff;
0453             format.blueMax  = 0xff;
0454             if (format.bigEndian) {
0455                 format.redShift   = 0;
0456                 format.greenShift = 8;
0457                 format.blueShift  = 16;
0458             } else {
0459                 format.redShift   = 16;
0460                 format.greenShift = 8;
0461                 format.blueShift  = 0;
0462             }
0463         }
0464     }
0465 }
0466 
0467 
0468 /**
0469  * This function contents was taken from X11 framebuffer source code.
0470  * It simply several intersecting rectangles into one bigger rect.
0471  * Non-intersecting rects are treated as different rects and exist
0472  * separately in this->tiles QList.
0473  */
0474 void XCBFrameBuffer::cleanupRects() {
0475     QList<QRect> cpy = tiles;
0476     bool inserted = false;
0477     tiles.clear();
0478 
0479     QListIterator<QRect> iter(cpy);
0480     while (iter.hasNext()) {
0481         const QRect &r = iter.next();
0482         // skip rects not intersecting with primary monitor
0483         if (!r.intersects(d->area)) continue;
0484         // only take intersection of this rect with primary monitor rect
0485         QRect ri = r.intersected(d->area);
0486 
0487         if (tiles.size() > 0) {
0488             for (auto &tile : tiles) {
0489                 // if current rect has intersection with tile, unite them
0490                 if (ri.intersects(tile)) {
0491                     tile |= ri;
0492                     inserted = true;
0493                     break;
0494                 }
0495             }
0496 
0497             if (!inserted) {
0498                 // else, append to list as different rect
0499                 tiles.append(ri);
0500             }
0501         } else {
0502             // tiles list is empty, append first item
0503             tiles.append(ri);
0504         }
0505     }
0506 
0507     // increase all rectangles size by 30 pixels each side.
0508     // limit coordinates to primary monitor boundaries.
0509     for (auto &tile : tiles) {
0510         tile.adjust(-30, -30, 30, 30);
0511         if (tile.top() < d->area.top()) {
0512             tile.setTop(d->area.top());
0513         }
0514         if (tile.bottom() > d->area.bottom()) {
0515             tile.setBottom(d->area.bottom());
0516         }
0517         //
0518         if (tile.left() < d->area.left()) {
0519             tile.setLeft(d->area.left());
0520         }
0521         if (tile.right() > d->area.right()) {
0522             tile.setRight(d->area.right());
0523         }
0524         // move update rects so that they are positioned relative to
0525         //   framebuffer image, not whole screen
0526         tile.moveTo(tile.left() - d->area.left(),
0527                     tile.top() - d->area.top());
0528     }
0529 }
0530 
0531 
0532 /**
0533  * This function is called by RfbServerManager::updateScreens()
0534  * approximately every 50ms (!!), driven by QTimer to get all
0535  * modified rectangles on the screen
0536  */
0537 QList<QRect> XCBFrameBuffer::modifiedTiles() {
0538     QList<QRect> ret;
0539     if (!d->running) {
0540         return ret;
0541     }
0542 
0543     cleanupRects();
0544 
0545     if (tiles.size() > 0) {
0546         if (d->x11EvtFilter->xshmAvail) {
0547 
0548             // loop over all damage rectangles gathered up to this time
0549             QListIterator<QRect> iter(tiles);
0550             //foreach(const QRect &r, tiles) {
0551             while (iter.hasNext()) {
0552                 const QRect &r = iter.next();
0553 
0554                 // get image data into shared memory segment
0555                 // now rects are positioned relative to framebufferImage,
0556                 // but we need to get image from the whole screen, so
0557                 // translate whe coordinates
0558                 xcb_shm_get_image_cookie_t sgi_cookie = xcb_shm_get_image(
0559                             QX11Info::connection(),
0560                             d->win,
0561                             d->area.left() + r.left(),
0562                             d->area.top()  + r.top(),
0563                             r.width(),
0564                             r.height(),
0565                             0xFFFFFFFF,
0566                             XCB_IMAGE_FORMAT_Z_PIXMAP,
0567                             d->shminfo.shmseg,
0568                             0);
0569 
0570                 xcb_shm_get_image_reply_t *sgi_reply = xcb_shm_get_image_reply(
0571                             QX11Info::connection(), sgi_cookie, nullptr);
0572                 if (sgi_reply) {
0573                     // create temporary image to get update rect contents into
0574                     d->updateTile = xcb_image_create_native(
0575                                 QX11Info::connection(),
0576                                 r.width(),
0577                                 r.height(),
0578                                 XCB_IMAGE_FORMAT_Z_PIXMAP,
0579                                 d->rootScreen->root_depth,
0580                                 nullptr,      // base == 0
0581                                 (uint32_t)~0, // bytes == ~0
0582                                 nullptr);
0583 
0584                     if (d->updateTile) {
0585                         d->updateTile->data = d->shminfo.shmaddr;
0586 
0587                         // copy pixels from this damage rectangle image
0588                         // to our total framebuffer image
0589                         int pxsize =  d->framebufferImage->bpp / 8;
0590                         char *dest = fb + ((d->framebufferImage->stride * r.top()) + (r.left() * pxsize));
0591                         char *src = (char *)d->updateTile->data;
0592                         for (int i = 0; i < d->updateTile->height; i++) {
0593                             memcpy(dest, src, d->updateTile->stride);  // copy whole row of pixels
0594                             dest += d->framebufferImage->stride;
0595                             src += d->updateTile->stride;
0596                         }
0597 
0598                         // delete temporary image
0599                         d->updateTile->data = nullptr;
0600                         xcb_image_destroy(d->updateTile);
0601                         d->updateTile = nullptr;
0602                     }
0603 
0604                     free(sgi_reply);
0605                 }
0606             } // foreach
0607         } else {
0608             // not using shared memory
0609             // will use just xcb_image_get() and copy pixels
0610             for (const QRect& r : std::as_const(tiles)) {
0611                 // I did not find XGetSubImage() analog in XCB!!
0612                 // need function that copies pixels from one image to another
0613                 xcb_image_t *damagedImage = xcb_image_get(
0614                             QX11Info::connection(),
0615                             d->win,
0616                             r.left(),
0617                             r.top(),
0618                             r.width(),
0619                             r.height(),
0620                             0xFFFFFFFF, // AllPlanes
0621                             XCB_IMAGE_FORMAT_Z_PIXMAP);
0622                 // manually copy pixels
0623                 int pxsize =  d->framebufferImage->bpp / 8;
0624                 char *dest = fb + ((d->framebufferImage->stride * r.top()) + (r.left() * pxsize));
0625                 char *src = (char *)damagedImage->data;
0626                 // loop every row in damaged image
0627                 for (int i = 0; i < damagedImage->height; i++) {
0628                     // copy whole row of pixels from src image to dest
0629                     memcpy(dest, src, damagedImage->stride);
0630                     dest += d->framebufferImage->stride;  // move 1 row down in dest
0631                     src += damagedImage->stride;          // move 1 row down in src
0632                 }
0633                 //
0634                 xcb_image_destroy(damagedImage);
0635             }
0636         }
0637     } // if (tiles.size() > 0)
0638 
0639     ret = tiles;
0640     tiles.clear();
0641     // ^^ If we clear here all our known "damage areas", then we can also clear
0642     //    damaged area for xdamage? No, we don't need to in our case
0643     //    (XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES report mode)
0644     //xcb_damage_subtract(QX11Info::connection(), d->damage, XCB_NONE, XCB_NONE);
0645 
0646     return ret;
0647 }
0648 
0649 
0650 void XCBFrameBuffer::startMonitor() {
0651     if (d->running) return;
0652 
0653     d->running = true;
0654     d->damage = xcb_generate_id(QX11Info::connection());
0655     xcb_damage_create(QX11Info::connection(), d->damage, d->win,
0656             XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES);
0657 
0658     // (currently) we do not call xcb_damage_subtract() EVER, because
0659     // RAW rectangles are reported. every time some area of the screen
0660     // was changed, we get only that rectangle
0661     //xcb_damage_subtract(QX11Info::connection(), d->damage, XCB_NONE, XCB_NONE);
0662 }
0663 
0664 
0665 void XCBFrameBuffer::stopMonitor() {
0666     if (!d->running) return;
0667     d->running = false;
0668     xcb_damage_destroy(QX11Info::connection(), d->damage);
0669 }
0670 
0671 
0672 // void XCBFrameBuffer::acquireEvents() {} // this function was totally unused
0673 // in X11 framebuffer, but it was the only function where XDamageSubtract() was called?
0674 // Also it had a blocking event loop like:
0675 //
0676 //     XEvent ev;
0677 //     while (XCheckTypedEvent(QX11Info::display(), d->xdamageBaseEvent + XDamageNotify, &ev)) {
0678 //         handleXDamage(&ev);
0679 //     }
0680 //     XDamageSubtract(QX11Info::display(), d->damage, None, None);
0681 //
0682 // This loop takes all available Xdamage events from queue, and ends if there are no
0683 // more such events in input queue.
0684 
0685 
0686 void XCBFrameBuffer::handleXDamageNotify(xcb_generic_event_t *xevent) {
0687     auto xdevt = (xcb_damage_notify_event_t *)xevent;
0688 
0689     QRect r((int)xdevt->area.x,     (int)xdevt->area.y,
0690             (int)xdevt->area.width, (int)xdevt->area.height);
0691     this->tiles.append(r);
0692 }
0693