almost working

svn path=/trunk/kdenetwork/krfb/; revision=135195
This commit is contained in:
Tim Jansen
2002-02-03 17:44:31 +00:00
parent 19c65bd406
commit 14add1de2b
12 changed files with 512 additions and 533 deletions

View File

@@ -1,4 +1,4 @@
SUBDIRS = lib krfb po doc include
SUBDIRS = libvncserver krfb po doc
EXTRA_DIST = admin AUTHORS COPYING ChangeLog INSTALL README TODO NOTES \
README_KDE3 krfb.lsm

View File

@@ -1,8 +1 @@
For KDE 3.0, you need to do the following:
* the .ui files must be recompiled, you can enforce this by touching them:
touch krfb/*.ui
* main.cpp and trayicon.cpp include the file kapp.h. It has been renamed to
kapplication.h, so you must change the sources accordingly.
3.0 support has been removed for this release, but will re-appear soon.

26
ROADMAP Normal file
View File

@@ -0,0 +1,26 @@
Version 0.5 (2002/01/02): First release
- first release
- port x0rfbserver wiith simplified KDE/Qt interface
Version 0.6 (2002/02/??): Improve RFB backend
- improve RFB backend
- replace x0rfbserver's encoders with those from TightVNCs WinVNC
<- port ZLib and Tight encodings from TightVNCs WinVNC
<- clip framebuffer updates to requested region
<- rewrite Connection to use linked buffers
<- knotify
<- UI for multiple users (if libvncserver is chosen)
Version 0.7 (2002/??/??): Better integration with KDE, more end-user friendly
- i18n
- kded module or some other way to have a small module listening on the Rfb
port (like inetd) that then starts KRfb
- kcontrol module to configure the kded thing
- on startup a dialog appears that allows the user to invite somebody
else using email (and later other mechanisms). Dialog can be turned
off, of course
Version 0.8 (2002/??/??):
- docs

View File

@@ -4,9 +4,15 @@ AM_INIT_AUTOMAKE(krfb,0.6)
AC_CHECK_HEADER(X11/extensions/XTest.h,
[],
AC_MSG_ERROR([XTest extension header not found]))
AC_MSG_ERROR([XTest extension header not found / no xlib headers]))
AC_CHECK_HEADER(zlib.h,
[],
AC_MSG_ERROR([ZLib header not found]))
AC_CHECK_HEADER(jpeglib.h,
[],
AC_MSG_ERROR([libjpeg header not found]))

View File

@@ -1,10 +1,10 @@
METASOURCES = AUTO
bin_PROGRAMS = krfb
krfb_SOURCES = rfbcontroller.cc configuration.cc trayicon.cpp XUpdateScanner.cc rfbconnection.cc main.cpp configurationdialog.ui newconnectiondialog.ui
krfb_LDADD = ../lib/librfbserver.a -lz $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIBSOCKET)
krfb_SOURCES = rfbcontroller.cc configuration.cc trayicon.cpp XUpdateScanner.cc main.cpp configurationdialog.ui newconnectiondialog.ui
krfb_LDADD = ../libvncserver/libvncserver.a -lz -lpthread -ljpeg -lXtst $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIBSOCKET)
EXTRA_DIST = $(krfb_SOURCES) krfb.desktop lo32-app-krfb.png lo16-app-krfb.png rfbcontroller.h rfbconnection.h eyes-closed24.png eyes-open24.png XUpdateScanner.h trayicon.h configuration.h
EXTRA_DIST = $(krfb_SOURCES) krfb.desktop lo32-app-krfb.png lo16-app-krfb.png rfbcontroller.h eyes-closed24.png eyes-open24.png XUpdateScanner.h trayicon.h configuration.h
install-data-local:
$(mkinstalldirs) $(kde_appsdir)/Applications/
@@ -35,10 +35,12 @@ KDE_ICON = krfb
# kde_wallpaperdir Where general wallpapers should go to.
# set the include path for X, qt and KDE
INCLUDES= $(all_includes) -I../include
INCLUDES= $(all_includes) -I../libvncserver
# the library search path.
krfb_LDFLAGS = $(all_libraries) $(KDE_RPATH) -lXtst
krfb_LDFLAGS = $(all_libraries) $(KDE_RPATH)
CXXFLAGS = @CXXFLAGS@ -DQT_THREAD_SUPPORT
# Uncomment the following two lines if you add a ui.rc file for your application to make use of
# KDE´s XML GUI builing

View File

@@ -31,8 +31,6 @@
#include "XUpdateScanner.h"
namespace rfb {
unsigned int scanlines[32] = { 0, 16, 8, 24,
4, 20, 12, 28,
10, 26, 18, 2,
@@ -44,25 +42,29 @@ unsigned int scanlines[32] = { 0, 16, 8, 24,
XUpdateScanner::XUpdateScanner(Display *_dpy,
Window _window,
Framebuffer *_fb,
unsigned char *_fb,
int _width,
int _height,
int _bitsPerPixel,
int _bytesPerLine,
unsigned int _tileWidth,
unsigned int _tileHeight,
unsigned int _blockWidth,
unsigned int _blockHeight) :
unsigned int _tileHeight) :
dpy(_dpy),
window(_window),
fb(_fb),
width(_width),
height(_height),
bitsPerPixel(_bitsPerPixel),
bytesPerLine(_bytesPerLine),
tileWidth(_tileWidth),
tileHeight(_tileHeight),
blockWidth(_blockWidth),
blockHeight(_blockHeight),
count (0),
scanline(NULL),
tile(NULL)
{
tile = XShmCreateImage(dpy,
DefaultVisual( dpy, 0 ),
fb->pixelFormat.bits_per_pixel,
bitsPerPixel,
ZPixmap,
NULL,
&shminfo_tile,
@@ -78,8 +80,8 @@ XUpdateScanner::XUpdateScanner(Display *_dpy,
XShmAttach(dpy, &shminfo_tile);
tilesX = (fb->width + tileWidth - 1) / tileWidth;
tilesY = (fb->height + tileHeight - 1) / tileHeight;
tilesX = (width + tileWidth - 1) / tileWidth;
tilesY = (height + tileHeight - 1) / tileHeight;
tileMap = new bool[tilesX * tilesY];
unsigned int i;
@@ -88,11 +90,11 @@ XUpdateScanner::XUpdateScanner(Display *_dpy,
scanline = XShmCreateImage(dpy,
DefaultVisual(dpy, 0),
fb->pixelFormat.bits_per_pixel,
bitsPerPixel,
ZPixmap,
NULL,
&shminfo_scanline,
fb->width,
width,
1);
shminfo_scanline.shmid = shmget(IPC_PRIVATE,
@@ -122,8 +124,8 @@ XUpdateScanner::~XUpdateScanner()
void XUpdateScanner::copyTile(int x, int y)
{
unsigned int maxWidth = fb->width - x;
unsigned int maxHeight = fb->height - y;
unsigned int maxWidth = width - x;
unsigned int maxHeight = height - y;
if (maxWidth > tileWidth)
maxWidth = tileWidth;
if (maxHeight > tileHeight)
@@ -136,13 +138,13 @@ void XUpdateScanner::copyTile(int x, int y)
AllPlanes, ZPixmap, tile, 0, 0);
}
unsigned int line;
int pixelsize = fb->pixelFormat.bits_per_pixel >> 3;
int pixelsize = bitsPerPixel >> 3;
unsigned char *src = (unsigned char*) tile->data;
unsigned char *dest = fb->data + y * fb->bytesPerLine + x * pixelsize;
unsigned char *dest = fb + y * bytesPerLine + x * pixelsize;
for (line = 0; line < maxHeight; line++) {
memcpy(dest, src, maxWidth * pixelsize );
src += tile->bytes_per_line;
dest += fb->bytesPerLine;
dest += bytesPerLine;
}
}
@@ -159,60 +161,51 @@ void XUpdateScanner::copyAllTiles()
}
void XUpdateScanner::initHint(Hint &hint)
{
hint.type = hintRefresh;
hint.hint.refresh.x = 0;
hint.hint.refresh.y = 0;
hint.hint.refresh.width = 0;
hint.hint.refresh.height = 0;
}
void XUpdateScanner::createHintFromTile(int x, int y, Hint &hint)
{
unsigned int w = fb->width - x;
unsigned int h = fb->height - y;
unsigned int w = width - x;
unsigned int h = height - y;
if (w > tileWidth)
w = tileWidth;
if (h > tileHeight)
h = tileHeight;
hint.hint.refresh.x = x;
hint.hint.refresh.y = y;
hint.hint.refresh.width = w;
hint.hint.refresh.height = h;
hint.x = x;
hint.y = y;
hint.w = w;
hint.h = h;
}
void XUpdateScanner::addTileToHint(int x, int y, Hint &hint)
{
unsigned int w = fb->width - x;
unsigned int h = fb->height - y;
unsigned int w = width - x;
unsigned int h = height - y;
if (w > tileWidth)
w = tileWidth;
if (h > tileHeight)
h = tileHeight;
if (hint.hint.refresh.x > x) {
hint.hint.refresh.width += hint.hint.refresh.x - x;
hint.hint.refresh.x = x;
if (hint.x > x) {
hint.w += hint.x - x;
hint.x = x;
}
if (hint.hint.refresh.y > y) {
hint.hint.refresh.height += hint.hint.refresh.y - y;
hint.hint.refresh.y = y;
if (hint.y > y) {
hint.h += hint.y - y;
hint.y = y;
}
if ((hint.hint.refresh.x+hint.hint.refresh.width) < (x+w)) {
hint.hint.refresh.width = (x+w) - hint.hint.refresh.x;
if ((hint.x+hint.w) < (x+w)) {
hint.w = (x+w) - hint.x;
}
if ((hint.hint.refresh.y+hint.hint.refresh.height) < (y+h)) {
hint.hint.refresh.height = (y+h) - hint.hint.refresh.y;
if ((hint.y+hint.h) < (y+h)) {
hint.h = (y+h) - hint.y;
}
}
void XUpdateScanner::flushHint(int x, int y, int &x0,
Hint &hint, list<Hint> &hintList)
Hint &hint, QList<Hint> &hintList)
{
if (x0 < 0)
return;
@@ -234,20 +227,18 @@ void XUpdateScanner::flushHint(int x, int y, int &x0,
h++;
}
hint.hint.refresh.height = h * tileHeight;
if ((hint.hint.refresh.y + hint.hint.refresh.height) > fb->height)
hint.hint.refresh.height = fb->height - hint.hint.refresh.y;
hint.h = h * tileHeight;
if ((hint.y + hint.h) > height)
h = height - hint.y;
x0 = -1;
hintList.push_back(hint);
initHint(hint);
hintList.append(new Hint(hint));
}
void XUpdateScanner::createHints(list<Hint> &hintList)
void XUpdateScanner::createHints(QList<Hint> &hintList)
{
Hint hint;
initHint(hint);
int x0 = -1;
for (int y = 0; y < tilesY; y++) {
@@ -272,7 +263,7 @@ void XUpdateScanner::createHints(list<Hint> &hintList)
}
}
void XUpdateScanner::searchUpdates(list<Hint> &hintList)
void XUpdateScanner::searchUpdates(QList<Hint> &hintList)
{
count++;
count %= 32;
@@ -285,16 +276,16 @@ void XUpdateScanner::searchUpdates(list<Hint> &hintList)
}
y = scanlines[count];
while (y < fb->height) {
while (y < height) {
XShmGetImage(dpy, window, scanline, 0, y, AllPlanes);
x = 0;
while (x < fb->width) {
int pixelsize = fb->pixelFormat.bits_per_pixel >> 3;
while (x < width) {
int pixelsize = bitsPerPixel >> 3;
unsigned char *src = (unsigned char*) scanline->data +
x * pixelsize;
unsigned char *dest = fb->data +
y * fb->bytesPerLine + x * pixelsize;
int w = (x + 32) > fb->width ? (fb->width-x) : 32;
unsigned char *dest = fb +
y * bytesPerLine + x * pixelsize;
int w = (x + 32) > width ? (width-x) : 32;
if (memcmp(dest, src, w * pixelsize))
tileMap[(x / tileWidth) +
(y / tileHeight) * tilesX] = true;
@@ -308,6 +299,5 @@ void XUpdateScanner::searchUpdates(list<Hint> &hintList)
createHints(hintList);
}
} // namespace rfb

View File

@@ -21,41 +21,69 @@
#ifndef _hexonet_rfb_XUpdateScanner_h_
#define _hexonet_rfb_XUpdateScanner_h_
#include "../include/rfbServer.h"
#include <qlist.h>
#include <X11/Xlib.h>
#include <X11/extensions/XShm.h>
namespace rfb {
class Hint {
public:
int x, y, w, h;
Hint() :
x(0),
y(0),
w(0),
h(0)
{}
Hint(Hint &h) :
x(h.x),
y(h.y),
w(h.w),
h(h.h)
{
}
int left() {
return x;
}
int right() {
return x+w;
}
int top() {
return y;
}
int bottom() {
return y+h;
}
};
class XUpdateScanner
{
public:
XUpdateScanner( Display *_dpy,
Window _window,
Framebuffer *_fb,
unsigned char *_fb,
int _width, int _height,
int _bitsPerPixel, int _bytesPerLine,
unsigned int _tileWidth = 32,
unsigned int _tileHeight = 32,
unsigned int _blockWidth = 9,
unsigned int _blockHeight = 9);
unsigned int _tileHeight = 32);
~XUpdateScanner();
void copyTile( int x, int y);
void copyAllTiles();
void searchUpdates( list< Hint > &hintList);
void initHint(Hint &hint);
void flushHint(int x, int y, int &x0, Hint &hint, list<Hint> &hintList);
void createHints(list<Hint> &hintList);
void searchUpdates( QList<Hint> &hintList);
void flushHint(int x, int y, int &x0, Hint &hint, QList<Hint> &hintList);
void createHints(QList<Hint> &hintList);
void addTileToHint(int x, int y, Hint &hint);
void createHintFromTile(int x, int y, Hint &hint);
Display *dpy;
Window window;
Framebuffer *fb;
unsigned int tileWidth, tileHeight, blockWidth, blockHeight;
unsigned char *fb;
int width, height;
int bitsPerPixel, bytesPerLine;
unsigned int tileWidth, tileHeight;
unsigned int count;
XImage *scanline;
@@ -69,6 +97,4 @@ class XUpdateScanner
};
} // namespace rfb
#endif // _hexonet_rfb_XUpdateScanner_h_

View File

@@ -34,6 +34,8 @@
#include <signal.h>
#include <X11/extensions/XTest.h>
#define VERSION "0.6"
static const char *description = I18N_NOOP("VNC-compatible server to share "
@@ -137,9 +139,6 @@ int main(int argc, char *argv[])
TrayIcon trayicon(new KAboutApplication(&aboutData), config);
RFBController controller(config);
if (controller.state() == RFB_ERROR)
return 1;
QObject::connect(&app, SIGNAL(lastWindowClosed()),
&controller, SLOT(closeSession()));

View File

@@ -1,183 +0,0 @@
/***************************************************************************
rfbconnection.cpp
-------------------
begin : Sun Dec 9 2001
copyright : (C) 2001 by Tim Jansen
email : tim@tjansen.de
***************************************************************************/
/***************************************************************************
* Contains portions & concept from rfb's x0rfbserver.cc
* Copyright (C) 2000 heXoNet Support GmbH, D-66424 Homburg.
***************************************************************************/
/***************************************************************************
* *
* 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 "rfbconnection.h"
#include <kdebug.h>
#include <qapplication.h>
#include <qtimer.h>
#include <X11/Xutil.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
static XTestDisabler disabler;
RFBConnection::RFBConnection(Display *_dpy,
int _fd,
const QString &cpassword,
bool _allowInput) :
Server(),
fd(_fd),
buttonMask(0),
allowInput(_allowInput),
dpy(_dpy)
{
memcpy(password, "\0\0\0\0\0\0\0\0", 8);
if (!cpassword.isNull())
strncpy(password, cpassword.latin1(),
(8 <= cpassword.length()) ? 8 : cpassword.length());
connection = new BufferedConnection(fd, 32768, 16384);
XTestGrabControl(dpy, true);
disabler.disable = false;
createFramebuffer();
sendFirstHandshake(connection);
}
RFBConnection::~RFBConnection() {
destroyFramebuffer();
delete connection;
disabler.disable = true;
disabler.dpy = dpy;
QTimer::singleShot(0, &disabler, SLOT(exec()));
}
void RFBConnection::handleKeyEvent(KeyEvent &keyEvent) {
if (!allowInput)
return;
KeyCode kc = XKeysymToKeycode(dpy, keyEvent.key);
if (kc != NoSymbol)
XTestFakeKeyEvent(dpy,
XKeysymToKeycode( dpy, keyEvent.key ),
keyEvent.down_flag,
CurrentTime);
}
void RFBConnection::handlePointerEvent(PointerEvent &pointerEvent) {
if (!allowInput)
return;
XTestFakeMotionEvent(dpy,
0,
pointerEvent.x_position,
pointerEvent.y_position,
CurrentTime);
int i = 1;
while (i <= 5) {
if ( (buttonMask & (1 << (i-1))) != (pointerEvent.button_mask & (1 << (i-1))) )
XTestFakeButtonEvent( dpy, i,
(pointerEvent.button_mask & (1 << (i-1)))? True : False,
CurrentTime );
i++;
}
buttonMask = pointerEvent.button_mask;
}
void RFBConnection::createFramebuffer()
{
framebufferImage = XGetImage(dpy,
QApplication::desktop()->winId(),
0,
0,
QApplication::desktop()->width(),
QApplication::desktop()->height(),
AllPlanes,
ZPixmap);
framebuffer = new Framebuffer();
framebuffer->width = framebufferImage->width;
framebuffer->height = framebufferImage->height;
framebuffer->bytesPerLine = framebufferImage->bytes_per_line;
framebuffer->data = (unsigned char*) framebufferImage->data;
framebuffer->pixelFormat.bits_per_pixel =
framebufferImage->bits_per_pixel;
framebuffer->pixelFormat.depth = framebufferImage->depth;
framebuffer->pixelFormat.big_endian_flag =
(framebufferImage->bitmap_bit_order == MSBFirst);
framebuffer->pixelFormat.true_colour_flag = true;
if (framebuffer->pixelFormat.bits_per_pixel == 8) {
framebuffer->pixelFormat.red_shift = 0;
framebuffer->pixelFormat.green_shift = 2;
framebuffer->pixelFormat.blue_shift = 5;
framebuffer->pixelFormat.red_max = 3;
framebuffer->pixelFormat.green_max = 7;
framebuffer->pixelFormat.blue_max = 3;
} else {
framebuffer->pixelFormat.red_shift = 0;
if ( framebufferImage->red_mask )
while (!(framebufferImage->red_mask & (1 << framebuffer->pixelFormat.red_shift)))
framebuffer->pixelFormat.red_shift++;
framebuffer->pixelFormat.green_shift = 0;
if (framebufferImage->green_mask)
while (!(framebufferImage->green_mask & (1 << framebuffer->pixelFormat.green_shift)))
framebuffer->pixelFormat.green_shift++;
framebuffer->pixelFormat.blue_shift = 0;
if (framebufferImage->blue_mask)
while (!(framebufferImage->blue_mask & (1 << framebuffer->pixelFormat.blue_shift)))
framebuffer->pixelFormat.blue_shift++;
framebuffer->pixelFormat.red_max = framebufferImage->red_mask >> framebuffer->pixelFormat.red_shift;
framebuffer->pixelFormat.green_max = framebufferImage->green_mask >> framebuffer->pixelFormat.green_shift;
framebuffer->pixelFormat.blue_max = framebufferImage->blue_mask >> framebuffer->pixelFormat.blue_shift;
}
scanner = new XUpdateScanner( dpy, QApplication::desktop()->winId(), framebuffer );
}
void RFBConnection::destroyFramebuffer()
{
delete scanner;
delete framebuffer;
XDestroyImage(framebufferImage);
}
void RFBConnection::scanUpdates()
{
list<Hint> hintList;
scanner->searchUpdates(hintList);
list<Hint>::iterator i;
for (i = hintList.begin(); i != hintList.end(); i++)
handleHint(*i);
};
void RFBConnection::getServerInitialisation( ServerInitialisation &_serverInit )
{
Server::getServerInitialisation( _serverInit );
_serverInit.name_length = strlen( getenv("HOSTNAME") );
_serverInit.name_string = (CARD8 *) malloc( _serverInit.name_length + 1 );
strcpy( (char*) _serverInit.name_string, getenv( "HOSTNAME" ) );
}
XTestDisabler::XTestDisabler() :
disable(false) {
}
void XTestDisabler::exec() {
if (disable)
XTestDiscard(dpy);
}
#include "rfbconnection.moc"

View File

@@ -1,90 +0,0 @@
/***************************************************************************
rfbconnection.h
-------------------
begin : Sun Dec 9 2001
copyright : (C) 2001 by Tim Jansen
email : tim@tjansen.de
***************************************************************************/
/***************************************************************************
* Contains portions & concept from rfb's x0rfbserver.cc
* Copyright (C) 2000 heXoNet Support GmbH, D-66424 Homburg.
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef RFBCONNECTION_H
#define RFBCONNECTION_H
// QT must be first because of conflicts with X11
#include <qobject.h>
#include <qstring.h>
#include "XUpdateScanner.h"
#include "../include/rfbServer.h"
#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
using namespace rfb;
/**
* This is a port from x0rfbserver's BaseServer to KDE. It is called
* RFBConnection because the original Server, despite its name, handles only
* a single connection after it has been established. This class and its
* connections are created and destroyed by RFBController. RFBController is the real
* serverk, but I did not want to call it Server and create more confusion.
* Unlike the original this one allows only one client, making stuff a little bit
* simpler.
* @author Tim Jansen
*/
class RFBConnection : public QObject, public Server {
Q_OBJECT
public:
RFBConnection(Display *dpy, int fd,
const QString &cpassword,
bool allowInput);
virtual ~RFBConnection();
virtual void handleKeyEvent(KeyEvent &keyEvent);
virtual void handlePointerEvent(PointerEvent &pointerEvent);
virtual void getServerInitialisation( ServerInitialisation &_serverInitialisation );
void scanUpdates();
private:
void createFramebuffer();
void destroyFramebuffer();
int fd;
int buttonMask;
bool allowInput;
XUpdateScanner *scanner;
Display *dpy;
XImage *framebufferImage;
};
/*
* Class to calls XTestDiscard at idle time (because otherwise
* it will not work with QT)
*/
class XTestDisabler : public QObject {
Q_OBJECT
public:
XTestDisabler();
bool disable;
Display *dpy;
public slots:
void exec();
};
#endif

View File

@@ -6,11 +6,6 @@
email : tim@tjansen.de
***************************************************************************/
/***************************************************************************
* Contains portions & concept from rfb's x0rfbserver.cc
* Copyright (C) 2000 heXoNet Support GmbH, D-66424 Homburg.
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
@@ -26,6 +21,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <kapp.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <klocale.h>
@@ -37,21 +33,78 @@
#include <qglobal.h>
#include <qlabel.h>
#include <X11/Xutil.h>
#include <X11/extensions/XTest.h>
#ifndef ASSERT
#define ASSERT(x) Q_ASSERT(x)
#endif
#define IDLE_PAUSE (1000/50)
static XTestDisabler disabler;
// only one controller exists, so we can do this workaround for functions:
static RFBController *self;
class AppLocker
{
public:
AppLocker() {
KApplication::kApplication()->lock();
}
~AppLocker() {
KApplication::kApplication()->unlock();
}
};
static void keyboardHook(Bool down, KeySym keySym, rfbClientPtr cl)
{
AppLocker a;
self->handleKeyEvent(down?true:false, keySym);
}
static void pointerHook(int bm, int x, int y, rfbClientPtr cl)
{
AppLocker a;
self->handlePointerEvent(bm, x, y);
}
static enum rfbNewClientAction newClientHook(struct _rfbClientRec *cl)
{
AppLocker a;
return self->handleNewClient(cl);
}
static Bool passwordCheck(rfbClientPtr cl,
char* encryptedPassword,
int len)
{
AppLocker a;
self->handleCheckPassword(encryptedPassword, len);
}
static void clientGoneHook(rfbClientPtr cl)
{
AppLocker a;
self->handleClientGone();
}
void ConnectionDialog::closeEvent(QCloseEvent *)
{
emit closed();
}
RFBController::RFBController(Configuration *c) :
configuration(c),
socket(0),
connection(0),
idleUpdateScheduled(false)
allowRemoteControl(false),
connectionNum(0),
configuration(c)
{
self = this;
connect(dialog.acceptConnectionButton, SIGNAL(clicked()),
SLOT(dialogAccepted()));
connect(dialog.refuseConnectionButton, SIGNAL(clicked()),
@@ -61,178 +114,302 @@ RFBController::RFBController(Configuration *c) :
startServer();
}
RFBController::~RFBController() {
if (serversocket)
delete serversocket;
if (socket)
delete socket;
if (connection)
delete connection;
RFBController::~RFBController()
{
stopServer();
}
void RFBController::startServer() {
serversocket = new KServerSocket(configuration->port(), false);
connect(serversocket, SIGNAL(accepted(KSocket*)), SLOT(accepted(KSocket*)));
if (!serversocket->bindAndListen()) {
delete serversocket;
serversocket = 0;
KMessageBox::error(0,
i18n("KRfb Server cannot run, port %1 is already in use. ")
.arg(configuration->port()),
i18n("KRfb Error"));
}
void RFBController::startServer(bool xtestGrab)
{
framebufferImage = XGetImage(qt_xdisplay(),
QApplication::desktop()->winId(),
0,
0,
QApplication::desktop()->width(),
QApplication::desktop()->height(),
AllPlanes,
ZPixmap);
int w = framebufferImage->width;
int h = framebufferImage->height;
int bpp = framebufferImage->depth;
char *fb = framebufferImage->data;
server = rfbGetScreen(0, 0, w, h,
(bpp==4) ? 8 : 5,
0, bpp);
server->frameBuffer = fb;
server->rfbPort = configuration->port();
//server->udpPort = configuration->port();
server->kbdAddEvent = keyboardHook;
server->ptrAddEvent = pointerHook;
server->newClientHook = newClientHook;
server->passwordCheck = passwordCheck;
scanner = new XUpdateScanner(qt_xdisplay(),
QApplication::desktop()->winId(),
(unsigned char*)fb,
w, h, bpp, (bpp/8)*w);
rfbInitServer(server);
state = RFB_WAITING;
if (xtestGrab) {
disabler.disable = false;
XTestGrabControl(qt_xdisplay(), true);
}
RFBState RFBController::state() {
if (!serversocket)
return RFB_ERROR;
if (!socket)
return RFB_WAITING;
if (!connection)
return RFB_CONNECTING;
return RFB_CONNECTED;
rfbRunEventLoop(server, -1, TRUE);
}
void RFBController::stopServer(bool xtestUngrab) {
rfbScreenCleanup(server);
state = RFB_STOPPED;
delete scanner;
XDestroyImage(framebufferImage);
if (xtestUngrab) {
disabler.disable = true;
QTimer::singleShot(0, &disabler, SLOT(exec()));
}
}
void RFBController::rebind() {
delete serversocket;
startServer();
stopServer(false);
startServer(false);
}
/* called when KServerSocket accepted a connection.
Refuses the connection if there is already a connection.
*/
void RFBController::accepted(KSocket *s) {
int sockFd = s->socket();
if (sockFd < 0)
kdError() << "Got negative socket (error)" << endl;
if (socket) {
kdWarning() << "refuse 2nd connection" << endl;
// TODO: send connection failed with reason
delete s;
void RFBController::acceptConnection(bool aRC) {
if (state != RFB_CONNECTING)
return;
}
int one = 1;
setsockopt(sockFd, IPPROTO_TCP, TCP_NODELAY,
(char *)&one, sizeof(one));
fcntl(sockFd, F_SETFL, O_NONBLOCK);
socket = s;
allowRemoteControl = aRC;
connectionNum++;
idleTimer.start(IDLE_PAUSE);
if (configuration->askOnConnect()) {
QString host, port;
KExtendedSocket::resolve(KExtendedSocket::peerAddress(sockFd),
host, port);
dialog.ipLabel->setText(host);
dialog.allowRemoteControlCB->setChecked(
configuration->allowDesktopControl());
dialog.setFixedSize(dialog.sizeHint());
dialog.show();
}
else {
acceptConnection(configuration->allowDesktopControl());
}
}
void RFBController::acceptConnection(bool allowDesktopControl) {
KSocket *s = socket;
connect(s, SIGNAL(readEvent(KSocket*)), SLOT(socketReadable()));
connect(s, SIGNAL(writeEvent(KSocket*)), SLOT(socketWritable()));
connect(s, SIGNAL(closeEvent(KSocket*)), SLOT(closeSession()));
s->enableRead(true);
s->enableWrite(true);
connection = new RFBConnection(qt_xdisplay(), s->socket(),
configuration->password(),
allowDesktopControl);
client->clientGoneHook = clientGoneHook;
rfbStartOnHoldClient(client);
state = RFB_CONNECTED;
emit sessionEstablished();
}
void RFBController::idleSlot() {
idleUpdateScheduled = false;
if (connection) {
connection->scanUpdates();
connection->sendIncrementalFramebufferUpdate();
connection->connection->write();
checkWriteBuffer();
}
}
void RFBController::checkWriteBuffer() {
BufferedConnection *bc = connection->connection;
bool bufferEmpty = !bc->hasSenderBufferData();
socket->enableWrite(!bufferEmpty);
if (bufferEmpty && !idleUpdateScheduled && connection) {
QTimer::singleShot(0, this, SLOT(idleSlot()));
idleUpdateScheduled = true;
}
}
void RFBController::socketReadable() {
if ((!socket) || (!connection))
void RFBController::refuseConnection() {
if (state != RFB_CONNECTING)
return;
BufferedConnection *bc = connection->connection;
int count = bc->read();
if (count < 0) {
closeSession();
return;
}
while (connection->currentState && bc->hasReceiverBufferData()) {
connection->update();
checkWriteBuffer();
rfbRefuseOnHoldClient(client);
state = RFB_WAITING;
}
if (!connection->currentState) {
closeSession();
}
}
void RFBController::socketWritable() {
if ((!socket) || (!connection))
return;
BufferedConnection *bc = connection->connection;
int count = bc->write();
if (count >= 0)
checkWriteBuffer();
else
closeSession();
}
void RFBController::closeSession() {
if (!socket)
return;
if (connection) {
delete connection;
connection = 0;
void RFBController::connectionClosed()
{
idleTimer.stop();
connectionNum--;
state = RFB_WAITING;
client = 0;
emit sessionFinished();
}
closeSocket();
void RFBController::closeConnection()
{
rfbCloseClient(client);
}
void RFBController::dialogAccepted() {
if (!socket)
return;
ASSERT(!connection);
void RFBController::idleSlot()
{
rfbUndrawCursor(server);
QList<Hint> v;
v.setAutoDelete(true);
scanner->searchUpdates(v);
Hint *h;
for (h = v.first(); h != 0; h = v.next())
rfbMarkRectAsModified(server, h->left(),
h->top(),
h->right(),
h->bottom());
QPoint p = QCursor::pos();
defaultPtrAddEvent(0, p.x(),p.y(), client);
}
void RFBController::dialogAccepted()
{
dialog.hide();
acceptConnection(dialog.allowRemoteControlCB->isChecked());
}
void RFBController::dialogRefused() {
if (!socket)
return;
ASSERT(!connection);
closeSocket();
void RFBController::dialogRefused()
{
refuseConnection();
dialog.hide();
emit sessionRefused();
}
void RFBController::closeSocket() {
delete socket;
socket = 0;
bool RFBController::handleCheckPassword(const char *p, int len)
{
return TRUE;
// TODO
}
enum rfbNewClientAction RFBController::handleNewClient(rfbClientPtr cl)
{
int socket = cl->sock;
if ((connectionNum > 0) ||
(state != RFB_WAITING))
return RFB_CLIENT_REFUSE;
client = cl;
state = RFB_CONNECTING;
if (!configuration->askOnConnect()) {
acceptConnection(configuration->allowDesktopControl());
return RFB_CLIENT_ACCEPT;
}
dialog.allowRemoteControlCB->setChecked(configuration->allowDesktopControl());
// TODO: get & set client host name
dialog.show();
return RFB_CLIENT_ON_HOLD;
}
void RFBController::handleClientGone()
{
connectionClosed();
}
#define LEFTSHIFT 1
#define RIGHTSHIFT 2
#define ALTGR 4
char ModifierState = 0;
/* this function adjusts the modifiers according to mod (as from modifiers) and ModifierState */
void RFBController::tweakModifiers(char mod, bool down)
{
Display *dpy = qt_xdisplay();
bool isShift = ModifierState & (LEFTSHIFT|RIGHTSHIFT);
if(mod < 0)
return;
if(isShift && mod != 1) {
if(ModifierState & LEFTSHIFT)
XTestFakeKeyEvent(dpy, leftShiftCode,
!down, CurrentTime);
if(ModifierState & RIGHTSHIFT)
XTestFakeKeyEvent(dpy, rightShiftCode,
!down, CurrentTime);
}
if(!isShift && mod==1)
XTestFakeKeyEvent(dpy, leftShiftCode,
down, CurrentTime);
if(ModifierState&ALTGR && mod != 2)
XTestFakeKeyEvent(dpy, altGrCode,
!down, CurrentTime);
if(!(ModifierState&ALTGR) && mod==2)
XTestFakeKeyEvent(dpy, altGrCode,
down, CurrentTime);
}
void RFBController::initKeycodes()
{
Display *dpy = qt_xdisplay();
KeySym key,*keymap;
int i,j,minkey,maxkey,syms_per_keycode;
memset(modifiers,-1,sizeof(modifiers));
XDisplayKeycodes(dpy,&minkey,&maxkey);
keymap=XGetKeyboardMapping(dpy,minkey,(maxkey - minkey + 1),&syms_per_keycode);
for (i = minkey; i <= maxkey; i++)
for(j=0;j<syms_per_keycode;j++) {
key=keymap[(i-minkey)*syms_per_keycode+j];
if(key>=' ' && key<0x100 && i==XKeysymToKeycode(dpy,key)) {
keycodes[key]=i;
modifiers[key]=j;
}
}
leftShiftCode = XKeysymToKeycode(dpy,XK_Shift_L);
rightShiftCode = XKeysymToKeycode(dpy,XK_Shift_R);
altGrCode = XKeysymToKeycode(dpy,XK_Mode_switch);
XFree ((char *)keymap);
}
void RFBController::handleKeyEvent(bool down, KeySym keySym) {
if (!allowRemoteControl)
return;
Display *dpy = qt_xdisplay();
#define ADJUSTMOD(sym,state) \
if(keySym==sym) { if(down) ModifierState|=state; else ModifierState&=~state; }
ADJUSTMOD(XK_Shift_L,LEFTSHIFT);
ADJUSTMOD(XK_Shift_R,RIGHTSHIFT);
ADJUSTMOD(XK_Mode_switch,ALTGR);
if(keySym>=' ' && keySym<0x100) {
KeyCode k;
if (down)
tweakModifiers(modifiers[keySym],True);
//tweakModifiers(modifiers[keySym],down);
//k = XKeysymToKeycode( dpy,keySym );
k = keycodes[keySym];
if(k!=NoSymbol)
XTestFakeKeyEvent(dpy,k,down,CurrentTime);
/*XTestFakeKeyEvent(dpy,keycodes[keySym],down,CurrentTime);*/
if (down)
tweakModifiers(modifiers[keySym],False);
} else {
KeyCode k = XKeysymToKeycode( dpy,keySym );
if(k!=NoSymbol)
XTestFakeKeyEvent(dpy,k,down,CurrentTime);
}
}
void RFBController::handlePointerEvent(int button_mask, int x, int y) {
if (!allowRemoteControl)
return;
Display *dpy = qt_xdisplay();
XTestFakeMotionEvent(dpy, 0, x, y, CurrentTime);
for(int i = 0; i < 5; i++)
if ((buttonMask&(1<<i))!=(button_mask&(1<<i)))
XTestFakeButtonEvent(dpy,
i+1,
(button_mask&(1<<i))?True:False,
CurrentTime);
buttonMask = button_mask;
}
XTestDisabler::XTestDisabler() :
disable(false) {
}
void XTestDisabler::exec() {
if (disable)
XTestDiscard(qt_xdisplay());
}
#include "rfbcontroller.moc"

View File

@@ -25,17 +25,21 @@
#include "configuration.h"
#include "newconnectiondialog.h"
#include "XUpdateScanner.h"
#include <ksock.h>
#include <qobject.h>
#include <qtimer.h>
// rfbconnection must be last because of X11 headers
#include "rfbconnection.h"
#include "rfb.h"
#include <X11/Xlib.h>
class QCloseEvent;
using namespace rfb;
typedef enum {
RFB_ERROR,
RFB_STOPPED,
RFB_WAITING,
RFB_CONNECTING,
RFB_CONNECTED
@@ -65,11 +69,20 @@ public:
RFBController(Configuration *c);
virtual ~RFBController();
RFBState state();
RFBState state;
void acceptConnection(bool allowRemoteConnection);
void refuseConnection();
void connectionClosed();
bool handleCheckPassword(const char *p, int len);
void handleKeyEvent(bool down, KeySym keySym);
void handlePointerEvent(int button_mask, int x, int y);
enum rfbNewClientAction handleNewClient(rfbClientPtr cl);
void handleClientGone();
public slots:
void rebind();
void closeSession();
void closeConnection();
signals:
void sessionEstablished();
@@ -77,25 +90,45 @@ signals:
void sessionRefused();
private:
void startServer();
void checkWriteBuffer();
void acceptConnection(bool ask);
void closeSocket();
void startServer(bool xtestGrab = true);
void stopServer(bool xtestUngrab = true);
void tweakModifiers(char mod, bool down);
void initKeycodes();
bool allowRemoteControl;
int connectionNum;
QTimer idleTimer;
Configuration *configuration;
KServerSocket *serversocket;
KSocket *socket;
RFBConnection *connection;
XUpdateScanner *scanner;
ConnectionDialog dialog;
bool idleUpdateScheduled;
rfbScreenInfoPtr server;
rfbClientPtr client;
XImage *framebufferImage;
int buttonMask;
char modifiers[0x100];
KeyCode keycodes[0x100], leftShiftCode, rightShiftCode, altGrCode;
private slots:
void idleSlot();
void accepted(KSocket*);
void socketReadable();
void socketWritable();
void dialogAccepted();
void dialogRefused();
};
/*
* Class to call XTestDiscard at idle time (because otherwise
* it will not work with QT)
*/
class XTestDisabler : public QObject {
Q_OBJECT
public:
XTestDisabler();
bool disable;
Display *dpy;
public slots:
void exec();
};
#endif