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