/* This file is part of the KDE project Copyright (C) 2007 Alessandro Praduroux This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "x11framebuffer.h" #include "moc_x11framebuffer.cpp" #include #include #include #include #include #include #include #include #ifdef HAVE_XDAMAGE #include #endif #ifdef HAVE_XSHM #include #include #include #endif class X11FrameBuffer::P { public: #ifdef HAVE_XDAMAGE Damage damage; #endif #ifdef HAVE_XSHM XShmSegmentInfo shminfo; #endif XImage *framebufferImage; XImage *updateTile; EvWidget *ev; bool useShm; int xdamageBaseEvent; bool running; }; X11FrameBuffer::X11FrameBuffer(WId id, QObject *parent) : FrameBuffer(id, parent), d(new X11FrameBuffer::P) { #ifdef HAVE_XSHM d->useShm = XShmQueryExtension(QX11Info::display()); kDebug() << "shm: " << d->useShm; #else d->useShm = false; #endif d->running = false; d->framebufferImage = XGetImage(QX11Info::display(), id, 0, 0, QApplication::desktop()->width(), //arg, must get a widget ??? QApplication::desktop()->height(), AllPlanes, ZPixmap); if (d->useShm) { #ifdef HAVE_XSHM d->updateTile = XShmCreateImage(QX11Info::display(), DefaultVisual(QX11Info::display(), 0), d->framebufferImage->bits_per_pixel, ZPixmap, NULL, &d->shminfo, 32, 32); d->shminfo.shmid = shmget(IPC_PRIVATE, d->updateTile->bytes_per_line * d->updateTile->height, IPC_CREAT | 0777); d->shminfo.shmaddr = d->updateTile->data = (char *) shmat(d->shminfo.shmid, 0, 0); d->shminfo.readOnly = False; XShmAttach(QX11Info::display(), &d->shminfo); #endif } else { ; } kDebug() << "Got image. bpp: " << d->framebufferImage->bits_per_pixel << ", depth: " << d->framebufferImage->depth << ", padded width: " << d->framebufferImage->bytes_per_line << " (sent: " << d->framebufferImage->width * 4 << ")"; fb = d->framebufferImage->data; #ifdef HAVE_XDAMAGE d->ev = new EvWidget(this); kapp->installX11EventFilter(d->ev); #endif } X11FrameBuffer::~X11FrameBuffer() { XDestroyImage(d->framebufferImage); #ifdef HAVE_XDAMAGE kapp->removeX11EventFilter(d->ev); #endif #ifdef HAVE_XSHM XShmDetach(QX11Info::display(), &d->shminfo); XDestroyImage(d->updateTile); shmdt(d->shminfo.shmaddr); shmctl(d->shminfo.shmid, IPC_RMID, 0); #endif delete d; fb = 0; // already deleted by XDestroyImage } int X11FrameBuffer::depth() { return d->framebufferImage->depth; } int X11FrameBuffer::height() { return d->framebufferImage->height; } int X11FrameBuffer::width() { return d->framebufferImage->width; } int X11FrameBuffer::paddedWidth() { return d->framebufferImage->bytes_per_line; } void X11FrameBuffer::getServerFormat(rfbPixelFormat &format) { format.bitsPerPixel = d->framebufferImage->bits_per_pixel; format.depth = d->framebufferImage->depth; format.trueColour = true; format.bigEndian = ((d->framebufferImage->bitmap_bit_order == MSBFirst) ? true : false); if (format.bitsPerPixel == 8) { format.redShift = 0; format.greenShift = 3; format.blueShift = 6; format.redMax = 7; format.greenMax = 7; format.blueMax = 3; } else { format.redShift = 0; if (d->framebufferImage->red_mask) while (!(d->framebufferImage->red_mask & (1 << format.redShift))) { format.redShift++; } format.greenShift = 0; if (d->framebufferImage->green_mask) while (!(d->framebufferImage->green_mask & (1 << format.greenShift))) { format.greenShift++; } format.blueShift = 0; if (d->framebufferImage->blue_mask) while (!(d->framebufferImage->blue_mask & (1 << format.blueShift))) { format.blueShift++; } format.redMax = d->framebufferImage->red_mask >> format.redShift; format.greenMax = d->framebufferImage->green_mask >> format.greenShift; format.blueMax = d->framebufferImage->blue_mask >> format.blueShift; } } void X11FrameBuffer::handleXDamage(XEvent *event) { #ifdef HAVE_XDAMAGE XDamageNotifyEvent *dev = (XDamageNotifyEvent *)event; QRect r(dev->area.x, dev->area.y, dev->area.width, dev->area.height); tiles.append(r); /*if (!dev->more) { XDamageSubtract(QX11Info::display(),d->damage, None, None); }*/ #endif } void X11FrameBuffer::cleanupRects() { QList cpy = tiles; bool inserted = false; tiles.clear(); // kDebug() << "before cleanup: " << cpy.size(); foreach(const QRect & r, cpy) { if (tiles.size() > 0) { for (int i = 0; i < tiles.size(); i++) { // kDebug() << r << tiles[i]; if (r.intersects(tiles[i])) { tiles[i] |= r; inserted = true; break; // kDebug() << "merged into " << tiles[i]; } } if (!inserted) { tiles.append(r); // kDebug() << "appended " << r; } } else { // kDebug() << "appended " << r; tiles.append(r); } } for (int i = 0; i < tiles.size(); i++) { tiles[i].adjust(-30, -30, 30, 30); if (tiles[i].top() < 0) { tiles[i].setTop(0); } if (tiles[i].left() < 0) { tiles[i].setLeft(0); } if (tiles[i].bottom() > d->framebufferImage->height) { tiles[i].setBottom(d->framebufferImage->height); } if (tiles[i].right() > d->framebufferImage->width) { tiles[i].setRight(d->framebufferImage->width); } } // kDebug() << "after cleanup: " << tiles.size(); } void X11FrameBuffer::acquireEvents() { XEvent ev; while (XCheckTypedEvent(QX11Info::display(), d->xdamageBaseEvent + XDamageNotify, &ev)) { handleXDamage(&ev); } XDamageSubtract(QX11Info::display(), d->damage, None, None); } QList< QRect > X11FrameBuffer::modifiedTiles() { QList ret; if (!d->running) { return ret; } kapp->processEvents(); // try to make sure every damage event goes trough; cleanupRects(); QRect gl; //d->useShm = false; if (tiles.size() > 0) { if (d->useShm) { #ifdef HAVE_XSHM foreach(const QRect & r, tiles) { // kDebug() << r; gl |= r; int y = r.y(); int x = r.x(); while (x < r.right()) { while (y < r.bottom()) { if (y + d->updateTile->height > d->framebufferImage->height) { y = d->framebufferImage->height - d->updateTile->height; } if (x + d->updateTile->width > d->framebufferImage->width) { x = d->framebufferImage->width - d->updateTile->width; } // kDebug() << "x: " << x << " (" << r.x() << ") y: " << y << " (" << r.y() << ") " << r; XShmGetImage(QX11Info::display(), win, d->updateTile, x, y, AllPlanes); int pxsize = d->framebufferImage->bits_per_pixel / 8; char *dest = fb + ((d->framebufferImage->bytes_per_line * y) + (x * pxsize)); char *src = d->updateTile->data; for (int i = 0; i < d->updateTile->height; i++) { memcpy(dest, src, d->updateTile->bytes_per_line); dest += d->framebufferImage->bytes_per_line; src += d->updateTile->bytes_per_line; } y += d->updateTile->height; } x += d->updateTile->width; y = r.y(); } } #endif } else { foreach(const QRect & r, tiles) { XGetSubImage(QX11Info::display(), win, r.left(), r.top(), r.width(), r.height(), AllPlanes, ZPixmap, d->framebufferImage, r.left(), r.top() ); } } } // kDebug() << "tot: " << gl; // kDebug() << tiles.size(); ret = tiles; tiles.clear(); return ret; } void X11FrameBuffer::startMonitor() { d->running = true; #ifdef HAVE_XDAMAGE d->damage = XDamageCreate(QX11Info::display(), win, XDamageReportRawRectangles); XDamageSubtract(QX11Info::display(), d->damage, None, None); #endif } void X11FrameBuffer::stopMonitor() { d->running = false; #ifdef HAVE_XDAMAGE XDamageDestroy(QX11Info::display(), d->damage); #endif } EvWidget::EvWidget(X11FrameBuffer *x11fb) : QWidget(0), fb(x11fb) { #ifdef HAVE_XDAMAGE int er; XDamageQueryExtension(QX11Info::display(), &xdamageBaseEvent, &er); #endif } bool EvWidget::x11Event(XEvent *event) { #ifdef HAVE_XDAMAGE if (event->type == xdamageBaseEvent + XDamageNotify) { fb->handleXDamage(event); return true; } #endif return false; }