Preliminary optional support for XDamage, checked both at compile and run time.

Uses less cpu than Qt approach, but visually sucks. I suspect there are too many updates sent to the client, I need to group the rects and create a bigger one when sending the event to the client. 

svn path=/trunk/KDE/kdenetwork/krfb/; revision=653073
This commit is contained in:
Alessandro Praduroux
2007-04-12 16:12:37 +00:00
parent 803f4b8c4d
commit 47c089f937
14 changed files with 457 additions and 88 deletions

View File

@@ -2,6 +2,7 @@ project(krfb)
check_symbol_exists(rfbInitServer "rfb/rfb.h" HAVE_LIBVNCSERVER)
macro_bool_to_01(X11_Xdamage_FOUND HAVE_XDAMAGE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-krfb.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-krfb.h )
include_directories(${CMAKE_CURRENT_BINARY_DIR}) # for config-krfb.h
@@ -23,6 +24,8 @@ set(krfb_SRCS
connectioncontroller.cpp
events.cpp
framebuffer.cpp
qtframebuffer.cpp
x11framebuffer.cpp
)
kde4_automoc(${krfb_SRCS})
@@ -39,7 +42,7 @@ kde4_add_ui_files(krfb_SRCS connectionwidget.ui
kde4_add_executable(krfb ${krfb_SRCS})
target_link_libraries(krfb ${KDE4_KIO_LIBS} ${JPEG_LIBRARIES} ${SLP_LIBRARIES} ${LIBVNCSERVER_LIBRARIES})
target_link_libraries(krfb ${KDE4_KIO_LIBS} ${JPEG_LIBRARIES} ${SLP_LIBRARIES} ${LIBVNCSERVER_LIBRARIES} ${X11_Xdamage_LIB})
install(TARGETS krfb DESTINATION ${BIN_INSTALL_DIR})

View File

@@ -3,3 +3,6 @@
/* Define if SLP is available */
#cmakedefine HAVE_SLP 1
/* Define if XDamage is available */
#cmakedefine HAVE_XDAMAGE 1

View File

@@ -78,9 +78,9 @@ enum rfbNewClientAction ConnectionController::handleNewClient()
bool askOnConnect = KrfbConfig::askOnConnect();
bool allowUninvited = KrfbConfig::allowUninvitedConnections();
int socket = cl->sock;
#if 0
int socket = cl->sock;
// TODO: this drops the connection >.<
QTcpSocket t;
t.setSocketDescriptor(socket); //, QAbstractSocket::ConnectedState, QIODevice::NotOpen);

View File

@@ -10,24 +10,25 @@
#include "framebuffer.h"
#include "framebuffer.moc"
#include <QTimer>
#include <QRegion>
#include <QPixmap>
#include <QBitmap>
#include "config-krfb.h"
#include <QX11Info>
#include "qtframebuffer.h"
#include "x11framebuffer.h"
#include <X11/Xutil.h>
#ifdef HAVE_XDAMAGE
#include <X11/extensions/Xdamage.h>
#endif
const int UPDATE_TIME = 500;
FrameBuffer::FrameBuffer(WId id, QObject *parent)
: QObject(parent), win(id)
{
//TODO: implement reference counting to avoid update the screen
// while no client is connected.
fbImage = QPixmap::grabWindow(win).toImage();
fb = new char[fbImage.numBytes()];
QTimer *t = new QTimer(this);
connect(t, SIGNAL(timeout()), SLOT(updateFrameBuffer()));
t->start(UPDATE_TIME);
}
@@ -41,41 +42,6 @@ char * FrameBuffer::data()
return fb;
}
void FrameBuffer::updateFrameBuffer()
{
QImage img = QPixmap::grabWindow(win).toImage();
QSize imgSize = img.size();
// verify what part of the image need to be marked as changed
// fbImage is the previous version of the image,
// img is the current one
#if 0 // This is actually slower than updating the whole desktop...
QImage map(imgSize, QImage::Format_Mono);
map.fill(0);
for (int x = 0; x < imgSize.width(); x++) {
for (int y = 0; y < imgSize.height(); y++) {
if (img.pixel(x,y) != fbImage.pixel(x,y)) {
map.setPixel(x,y,1);
}
}
}
QRegion r(QBitmap::fromImage(map));
tiles = tiles + r.rects();
#else
tiles.append(img.rect());
#endif
memcpy(fb, (const char*)img.bits(), img.numBytes());
fbImage = img;
}
QVector< QRect > & FrameBuffer::modifiedTiles()
{
return tiles;
@@ -83,32 +49,37 @@ QVector< QRect > & FrameBuffer::modifiedTiles()
int FrameBuffer::width()
{
return fbImage.width();
return 0;
}
int FrameBuffer::height()
{
return fbImage.height();
return 0;
}
void FrameBuffer::getServerFormat(rfbPixelFormat & format)
void FrameBuffer::getServerFormat(rfbPixelFormat &)
{
format.bitsPerPixel = 32;
format.depth = 32;
format.trueColour = true;
format.bigEndian = false;
format.redShift = 16;
format.greenShift = 8;
format.blueShift = 0;
format.redMax = 0xff;
format.greenMax = 0xff;
format.blueMax = 0xff;
}
int FrameBuffer::depth()
{
return fbImage.depth();
return 32;
}
FrameBuffer * FrameBuffer::getFrameBuffer(WId id, QObject * parent)
{
#ifdef HAVE_XDAMAGE
int tmp, er;
if (XDamageQueryExtension(QX11Info::display(), &tmp, &er)) {
return new X11FrameBuffer(id, parent);
}
#endif
return new QtFrameBuffer(id, parent);
}
int FrameBuffer::paddedWidth()
{
return width() * depth() / 8;
}

View File

@@ -11,12 +11,13 @@
#define FRAMEBUFFER_H
#include <QObject>
#include <QImage>
#include <QRect>
#include <QVector>
#include <QWidget>
#include <rfb/rfb.h>
class FrameBuffer;
/**
@author Alessandro Praduroux <pradu@pradu.it>
*/
@@ -24,26 +25,26 @@ class FrameBuffer : public QObject
{
Q_OBJECT
public:
explicit FrameBuffer(WId id, QObject *parent = 0);
~FrameBuffer();
static FrameBuffer* getFrameBuffer(WId id, QObject *parent);
virtual ~FrameBuffer();
char * data();
QVector<QRect> &modifiedTiles();
int width();
int height();
int depth();
virtual int paddedWidth();
virtual int width();
virtual int height();
virtual int depth();
void getServerFormat(rfbPixelFormat &format);
virtual void getServerFormat(rfbPixelFormat &format);
public Q_SLOTS:
void updateFrameBuffer();
protected:
explicit FrameBuffer(WId id, QObject *parent = 0);
private:
WId win;
char *fb;
QImage fbImage;
QVector<QRect> tiles;
};

View File

@@ -12,7 +12,6 @@
#include "krfbserver.h"
#include "krfbserver.moc"
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
@@ -32,6 +31,9 @@
#include "krfbconfig.h"
#include "invitationmanager.h"
#include <X11/Xutil.h>
#include <X11/extensions/XTest.h>
static const char* cur=
" "
@@ -119,6 +121,7 @@ class KrfbServer::KrfbServerP {
FrameBuffer *fb;
QList< QPointer<ConnectionController> > controllers;
rfbScreenInfoPtr screen;
bool running;
};
@@ -134,7 +137,8 @@ KrfbServer::KrfbServer()
:d(new KrfbServerP)
{
kDebug() << "starting " << endl;
d->fb = new FrameBuffer(QApplication::desktop()->winId(), this);
d->running = true;
d->fb = FrameBuffer::getFrameBuffer(QApplication::desktop()->winId(), this);
QTimer::singleShot(0, this, SLOT(startListening()));
connect(InvitationManager::self(), SIGNAL(invitationNumChanged(int)),SLOT(updatePassword()));
@@ -158,7 +162,7 @@ void KrfbServer::startListening()
rfbLogEnable(0);
screen = rfbGetScreen(0, 0, w, h, 8, 3,depth / 8);
screen->paddedWidthInBytes = w * 4;
screen->paddedWidthInBytes = d->fb->paddedWidth();
d->fb->getServerFormat(screen->serverFormat);
@@ -192,7 +196,7 @@ void KrfbServer::startListening()
disconnectAndQuit();
};
while (true) {
while (d->running) {
foreach(QRect r, d->fb->modifiedTiles()) {
rfbMarkRectAsModified(screen, r.top(), r.left(), r.left() + r.width(), r.top() + r.height());
}
@@ -200,6 +204,7 @@ void KrfbServer::startListening()
rfbProcessEvents(screen, 100);
qApp->processEvents();
}
rfbShutdownServer(screen, true);
}
@@ -214,7 +219,7 @@ void KrfbServer::enableDesktopControl(bool enable)
void KrfbServer::disconnectAndQuit()
{
// TODO: cleanup of existing connections
d->running = false;
emit quitApp();
}
@@ -254,6 +259,19 @@ void KrfbServer::updatePassword()
}
}
bool KrfbServer::checkX11Capabilities() {
int bp1, bp2, majorv, minorv;
Bool r = XTestQueryExtension(QX11Info::display(), &bp1, &bp2,
&majorv, &minorv);
if ((!r) || (((majorv*1000)+minorv) < 2002)) {
KMessageBox::error(0,
i18n("Your X11 Server does not support the required XTest extension version 2.2. Sharing your desktop is not possible."),
i18n("Desktop Sharing Error"));
return false;
}
return true;
}

View File

@@ -30,6 +30,7 @@ public:
~KrfbServer();
enum rfbNewClientAction handleNewClient(struct _rfbClientRec *cl);
bool checkX11Capabilities();
signals:
void sessionEstablished(QString);

View File

@@ -40,6 +40,7 @@
static const char description[] = I18N_NOOP("VNC-compatible server to share "
"KDE desktops");
int main(int argc, char *argv[])
{
KAboutData aboutData( "krfb", I18N_NOOP("Desktop Sharing"),
@@ -78,6 +79,9 @@ int main(int argc, char *argv[])
TrayIcon trayicon(new ManageInvitationsDialog);
KrfbServer *server = KrfbServer::self(); // initialize the server manager
if (!server->checkX11Capabilities()) {
return 1;
}
QObject::connect(&app, SIGNAL(lastWindowClosed()), // do not show passivepopup
&trayicon, SLOT(prepareQuit()));
@@ -97,13 +101,6 @@ int main(int argc, char *argv[])
QObject::connect(server, SIGNAL(quitApp()),
&app, SLOT(quit()));
//TODO: implement some error reporting mechanism between server and tray icon
/*
QObject::connect(&trayicon, SIGNAL(diconnectedMessageDisplayed()),
&app, SLOT(quit()));
QObject::connect(&server, SIGNAL(sessionRefused()),
&app, SLOT(quit()));
*/
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGPIPE);

105
qtframebuffer.cpp Normal file
View File

@@ -0,0 +1,105 @@
/* 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; version 2
of the License.
*/
#include "qtframebuffer.h"
#include "qtframebuffer.moc"
#include <QTimer>
#include <QRegion>
#include <QPixmap>
#include <QBitmap>
const int UPDATE_TIME = 500;
QtFrameBuffer::QtFrameBuffer(WId id, QObject *parent)
: FrameBuffer(id, parent)
{
fbImage = QPixmap::grabWindow(win).toImage();
fb = new char[fbImage.numBytes()];
QTimer *t = new QTimer(this);
connect(t, SIGNAL(timeout()), SLOT(updateFrameBuffer()));
t->start(UPDATE_TIME);
}
QtFrameBuffer::~QtFrameBuffer()
{
}
int QtFrameBuffer::depth()
{
return fbImage.depth();
}
int QtFrameBuffer::height()
{
return fbImage.height();
}
int QtFrameBuffer::width()
{
return fbImage.width();
}
void QtFrameBuffer::getServerFormat(rfbPixelFormat& format)
{
format.bitsPerPixel = 32;
format.depth = 32;
format.trueColour = true;
format.bigEndian = false;
format.redShift = 16;
format.greenShift = 8;
format.blueShift = 0;
format.redMax = 0xff;
format.greenMax = 0xff;
format.blueMax = 0xff;
}
void QtFrameBuffer::updateFrameBuffer()
{
QImage img = QPixmap::grabWindow(win).toImage();
QSize imgSize = img.size();
// verify what part of the image need to be marked as changed
// fbImage is the previous version of the image,
// img is the current one
#if 0 // This is actually slower than updating the whole desktop...
QImage map(imgSize, QImage::Format_Mono);
map.fill(0);
for (int x = 0; x < imgSize.width(); x++) {
for (int y = 0; y < imgSize.height(); y++) {
if (img.pixel(x,y) != fbImage.pixel(x,y)) {
map.setPixel(x,y,1);
}
}
}
QRegion r(QBitmap::fromImage(map));
tiles = tiles + r.rects();
#else
tiles.append(img.rect());
#endif
memcpy(fb, (const char*)img.bits(), img.numBytes());
fbImage = img;
}
int QtFrameBuffer::paddedWidth()
{
return fbImage.width() * 4;
}

41
qtframebuffer.h Normal file
View File

@@ -0,0 +1,41 @@
/* 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; version 2
of the License.
*/
#ifndef QTFRAMEBUFFER_H
#define QTFRAMEBUFFER_H
#include <QImage>
#include "framebuffer.h"
/**
@author Alessandro Praduroux <pradu@pradu.it>
*/
class QtFrameBuffer : public FrameBuffer
{
Q_OBJECT
public:
QtFrameBuffer(WId id, QObject *parent = 0);
~QtFrameBuffer();
virtual int depth();
virtual int height();
virtual int width();
virtual int paddedWidth();
virtual void getServerFormat(rfbPixelFormat& format);
public Q_SLOTS:
void updateFrameBuffer();
private:
QImage fbImage;
};
#endif

View File

@@ -72,7 +72,7 @@ void TrayIcon::prepareQuit() {
quitting = true;
}
void TrayIcon::showConnectedMessage(QString host) {
void TrayIcon::showConnectedMessage(const QString &host) {
setIcon(KIcon("eyes-open24"));
KPassivePopup::message(i18n("Desktop Sharing"),

View File

@@ -44,7 +44,7 @@ signals:
public Q_SLOTS:
void prepareQuit();
void showConnectedMessage(QString host);
void showConnectedMessage(const QString &host);
void showDisconnectedMessage();
void setDesktopControlSetting(bool);
void showManageInvitations();

175
x11framebuffer.cpp Normal file
View File

@@ -0,0 +1,175 @@
/* 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; version 2
of the License.
*/
#include "x11framebuffer.h"
#include "x11framebuffer.moc"
#include "config-krfb.h"
#include <QX11Info>
#include <QApplication>
#include <QDesktopWidget>
#include <KApplication>
#include <X11/Xutil.h>
#ifdef HAVE_XDAMAGE
#include <X11/extensions/Xdamage.h>
#endif
class X11FrameBuffer::P {
public:
#ifdef HAVE_XDAMAGE
Damage damage;
#endif
XImage *framebufferImage;
EvWidget *ev;
};
X11FrameBuffer::X11FrameBuffer(WId id, QObject* parent)
:FrameBuffer(id, parent), d(new X11FrameBuffer::P)
{
d->framebufferImage = XGetImage(QX11Info::display(),
id,
0,
0,
QApplication::desktop()->width(), //arg, must get a widget ???
QApplication::desktop()->height(),
AllPlanes,
ZPixmap);
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 << ")"
<< endl;
fb = d->framebufferImage->data;
#ifdef HAVE_XDAMAGE
d->ev = new EvWidget(this);
d->damage = XDamageCreate(QX11Info::display(), id, XDamageReportRawRectangles);
XDamageSubtract(QX11Info::display(),d->damage, None, None);
kapp->installX11EventFilter(d->ev);
#endif
}
X11FrameBuffer::~X11FrameBuffer()
{
XDestroyImage(d->framebufferImage);
#ifdef HAVE_XDAMAGE
kapp->removeX11EventFilter(d->ev);
XDamageDestroy(QX11Info::display(),d->damage);
#endif
delete d;
}
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);
XGetSubImage(QX11Info::display(),
win,
dev->area.x,
dev->area.y,
dev->area.width,
dev->area.height,
AllPlanes,
ZPixmap,
d->framebufferImage,
dev->area.x,
dev->area.y);
if (!dev->more) {
XDamageSubtract(QX11Info::display(),d->damage, None, None);
}
#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;
}

54
x11framebuffer.h Normal file
View File

@@ -0,0 +1,54 @@
/* 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; version 2
of the License.
*/
#ifndef X11FRAMEBUFFER_H
#define X11FRAMEBUFFER_H
#include <framebuffer.h>
#include <QWidget>
class X11FrameBuffer;
class EvWidget: public QWidget {
Q_OBJECT
public:
EvWidget(X11FrameBuffer *x11fb);
protected:
bool x11Event ( XEvent * event );
private:
X11FrameBuffer *fb;
int xdamageBaseEvent;
};
/**
@author Alessandro Praduroux <pradu@pradu.it>
*/
class X11FrameBuffer : public FrameBuffer
{
Q_OBJECT
public:
X11FrameBuffer(WId id, QObject* parent);
~X11FrameBuffer();
virtual int depth();
virtual int height();
virtual int width();
virtual int paddedWidth();
virtual void getServerFormat(rfbPixelFormat& format);
void handleXDamage( XEvent *event);
private:
class P;
P * const d;
};
#endif