kde-extraapps/krfb/framebuffers/x11/x11framebuffer.cpp
Ivailo Monev 45dcabf7f8 generic: prepare for Katie changes
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2022-11-13 15:37:30 +02:00

387 lines
10 KiB
C++

/* This file is part of the KDE project
Copyright (C) 2007 Alessandro Praduroux <pradu@pradu.it>
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 <config-krfb.h>
#include <QX11Info>
#include <QApplication>
#include <QDesktopWidget>
#include <KApplication>
#include <KDebug>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef HAVE_XDAMAGE
#include <X11/extensions/Xdamage.h>
#endif
#ifdef HAVE_XSHM
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#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<QRect> 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<QRect> 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;
}