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 \ EXTRA_DIST = admin AUTHORS COPYING ChangeLog INSTALL README TODO NOTES \
README_KDE3 krfb.lsm README_KDE3 krfb.lsm

View File

@@ -1,8 +1 @@
For KDE 3.0, you need to do the following: 3.0 support has been removed for this release, but will re-appear soon.
* 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.

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_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_CHECK_HEADER(zlib.h,
[], [],
AC_MSG_ERROR([ZLib header not found])) 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 METASOURCES = AUTO
bin_PROGRAMS = krfb bin_PROGRAMS = krfb
krfb_SOURCES = rfbcontroller.cc configuration.cc trayicon.cpp XUpdateScanner.cc rfbconnection.cc main.cpp configurationdialog.ui newconnectiondialog.ui krfb_SOURCES = rfbcontroller.cc configuration.cc trayicon.cpp XUpdateScanner.cc main.cpp configurationdialog.ui newconnectiondialog.ui
krfb_LDADD = ../lib/librfbserver.a -lz $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIBSOCKET) 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: install-data-local:
$(mkinstalldirs) $(kde_appsdir)/Applications/ $(mkinstalldirs) $(kde_appsdir)/Applications/
@@ -35,10 +35,12 @@ KDE_ICON = krfb
# kde_wallpaperdir Where general wallpapers should go to. # kde_wallpaperdir Where general wallpapers should go to.
# set the include path for X, qt and KDE # set the include path for X, qt and KDE
INCLUDES= $(all_includes) -I../include INCLUDES= $(all_includes) -I../libvncserver
# the library search path. # 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 # Uncomment the following two lines if you add a ui.rc file for your application to make use of
# KDE´s XML GUI builing # KDE´s XML GUI builing

View File

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

View File

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

View File

@@ -34,6 +34,8 @@
#include <signal.h> #include <signal.h>
#include <X11/extensions/XTest.h>
#define VERSION "0.6" #define VERSION "0.6"
static const char *description = I18N_NOOP("VNC-compatible server to share " 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); TrayIcon trayicon(new KAboutApplication(&aboutData), config);
RFBController controller(config); RFBController controller(config);
if (controller.state() == RFB_ERROR)
return 1;
QObject::connect(&app, SIGNAL(lastWindowClosed()), QObject::connect(&app, SIGNAL(lastWindowClosed()),
&controller, SLOT(closeSession())); &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 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 * * This program is free software; you can redistribute it and/or modify *
@@ -26,6 +21,7 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <kapp.h>
#include <kdebug.h> #include <kdebug.h>
#include <kmessagebox.h> #include <kmessagebox.h>
#include <klocale.h> #include <klocale.h>
@@ -37,21 +33,78 @@
#include <qglobal.h> #include <qglobal.h>
#include <qlabel.h> #include <qlabel.h>
#include <X11/Xutil.h>
#include <X11/extensions/XTest.h>
#ifndef ASSERT #ifndef ASSERT
#define ASSERT(x) Q_ASSERT(x) #define ASSERT(x) Q_ASSERT(x)
#endif #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 *) void ConnectionDialog::closeEvent(QCloseEvent *)
{ {
emit closed(); emit closed();
} }
RFBController::RFBController(Configuration *c) : RFBController::RFBController(Configuration *c) :
configuration(c), allowRemoteControl(false),
socket(0), connectionNum(0),
connection(0), configuration(c)
idleUpdateScheduled(false)
{ {
self = this;
connect(dialog.acceptConnectionButton, SIGNAL(clicked()), connect(dialog.acceptConnectionButton, SIGNAL(clicked()),
SLOT(dialogAccepted())); SLOT(dialogAccepted()));
connect(dialog.refuseConnectionButton, SIGNAL(clicked()), connect(dialog.refuseConnectionButton, SIGNAL(clicked()),
@@ -61,178 +114,302 @@ RFBController::RFBController(Configuration *c) :
startServer(); startServer();
} }
RFBController::~RFBController() { RFBController::~RFBController()
if (serversocket) {
delete serversocket; stopServer();
if (socket)
delete socket;
if (connection)
delete connection;
} }
void RFBController::startServer() {
serversocket = new KServerSocket(configuration->port(), false);
connect(serversocket, SIGNAL(accepted(KSocket*)), SLOT(accepted(KSocket*))); void RFBController::startServer(bool xtestGrab)
if (!serversocket->bindAndListen()) { {
delete serversocket; framebufferImage = XGetImage(qt_xdisplay(),
serversocket = 0; QApplication::desktop()->winId(),
KMessageBox::error(0, 0,
i18n("KRfb Server cannot run, port %1 is already in use. ") 0,
.arg(configuration->port()), QApplication::desktop()->width(),
i18n("KRfb Error")); 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);
} }
rfbRunEventLoop(server, -1, TRUE);
} }
RFBState RFBController::state() { void RFBController::stopServer(bool xtestUngrab) {
if (!serversocket) rfbScreenCleanup(server);
return RFB_ERROR; state = RFB_STOPPED;
if (!socket) delete scanner;
return RFB_WAITING;
if (!connection) XDestroyImage(framebufferImage);
return RFB_CONNECTING;
return RFB_CONNECTED; if (xtestUngrab) {
disabler.disable = true;
QTimer::singleShot(0, &disabler, SLOT(exec()));
}
} }
void RFBController::rebind() { void RFBController::rebind() {
delete serversocket; stopServer(false);
startServer(); startServer(false);
} }
/* called when KServerSocket accepted a connection. void RFBController::acceptConnection(bool aRC) {
Refuses the connection if there is already a connection. if (state != RFB_CONNECTING)
*/
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;
return; return;
}
int one = 1; allowRemoteControl = aRC;
setsockopt(sockFd, IPPROTO_TCP, TCP_NODELAY, connectionNum++;
(char *)&one, sizeof(one)); idleTimer.start(IDLE_PAUSE);
fcntl(sockFd, F_SETFL, O_NONBLOCK);
socket = s;
if (configuration->askOnConnect()) { client->clientGoneHook = clientGoneHook;
QString host, port; rfbStartOnHoldClient(client);
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);
state = RFB_CONNECTED;
emit sessionEstablished(); emit sessionEstablished();
} }
void RFBController::idleSlot() { void RFBController::refuseConnection() {
idleUpdateScheduled = false; if (state != RFB_CONNECTING)
if (connection) { return;
connection->scanUpdates(); rfbRefuseOnHoldClient(client);
connection->sendIncrementalFramebufferUpdate(); state = RFB_WAITING;
connection->connection->write();
checkWriteBuffer();
}
} }
void RFBController::checkWriteBuffer() { void RFBController::connectionClosed()
BufferedConnection *bc = connection->connection; {
bool bufferEmpty = !bc->hasSenderBufferData(); idleTimer.stop();
socket->enableWrite(!bufferEmpty); connectionNum--;
if (bufferEmpty && !idleUpdateScheduled && connection) { state = RFB_WAITING;
QTimer::singleShot(0, this, SLOT(idleSlot())); client = 0;
idleUpdateScheduled = true; emit sessionFinished();
}
} }
void RFBController::socketReadable() { void RFBController::closeConnection()
if ((!socket) || (!connection)) {
return; rfbCloseClient(client);
BufferedConnection *bc = connection->connection;
int count = bc->read();
if (count < 0) {
closeSession();
return;
}
while (connection->currentState && bc->hasReceiverBufferData()) {
connection->update();
checkWriteBuffer();
}
if (!connection->currentState) {
closeSession();
}
} }
void RFBController::socketWritable() { void RFBController::idleSlot()
if ((!socket) || (!connection)) {
return; rfbUndrawCursor(server);
BufferedConnection *bc = connection->connection; QList<Hint> v;
int count = bc->write(); v.setAutoDelete(true);
if (count >= 0) scanner->searchUpdates(v);
checkWriteBuffer();
else Hint *h;
closeSession();
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::closeSession() { void RFBController::dialogAccepted()
if (!socket) {
return;
if (connection) {
delete connection;
connection = 0;
emit sessionFinished();
}
closeSocket();
}
void RFBController::dialogAccepted() {
if (!socket)
return;
ASSERT(!connection);
dialog.hide(); dialog.hide();
acceptConnection(dialog.allowRemoteControlCB->isChecked()); acceptConnection(dialog.allowRemoteControlCB->isChecked());
} }
void RFBController::dialogRefused() { void RFBController::dialogRefused()
if (!socket) {
return; refuseConnection();
ASSERT(!connection);
closeSocket();
dialog.hide(); dialog.hide();
emit sessionRefused(); emit sessionRefused();
} }
void RFBController::closeSocket() { bool RFBController::handleCheckPassword(const char *p, int len)
delete socket; {
socket = 0; 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" #include "rfbcontroller.moc"

View File

@@ -25,17 +25,21 @@
#include "configuration.h" #include "configuration.h"
#include "newconnectiondialog.h" #include "newconnectiondialog.h"
#include "XUpdateScanner.h"
#include <ksock.h> #include <ksock.h>
#include <qobject.h> #include <qobject.h>
#include <qtimer.h>
// rfbconnection must be last because of X11 headers // rfbconnection must be last because of X11 headers
#include "rfbconnection.h" #include "rfb.h"
#include <X11/Xlib.h>
class QCloseEvent; class QCloseEvent;
using namespace rfb;
typedef enum { typedef enum {
RFB_ERROR, RFB_STOPPED,
RFB_WAITING, RFB_WAITING,
RFB_CONNECTING, RFB_CONNECTING,
RFB_CONNECTED RFB_CONNECTED
@@ -65,11 +69,20 @@ public:
RFBController(Configuration *c); RFBController(Configuration *c);
virtual ~RFBController(); 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: public slots:
void rebind(); void rebind();
void closeSession(); void closeConnection();
signals: signals:
void sessionEstablished(); void sessionEstablished();
@@ -77,25 +90,45 @@ signals:
void sessionRefused(); void sessionRefused();
private: private:
void startServer(); void startServer(bool xtestGrab = true);
void checkWriteBuffer(); void stopServer(bool xtestUngrab = true);
void acceptConnection(bool ask); void tweakModifiers(char mod, bool down);
void closeSocket(); void initKeycodes();
bool allowRemoteControl;
int connectionNum;
QTimer idleTimer;
Configuration *configuration; Configuration *configuration;
KServerSocket *serversocket; XUpdateScanner *scanner;
KSocket *socket;
RFBConnection *connection;
ConnectionDialog dialog; ConnectionDialog dialog;
bool idleUpdateScheduled;
rfbScreenInfoPtr server;
rfbClientPtr client;
XImage *framebufferImage;
int buttonMask;
char modifiers[0x100];
KeyCode keycodes[0x100], leftShiftCode, rightShiftCode, altGrCode;
private slots: private slots:
void idleSlot(); void idleSlot();
void accepted(KSocket*);
void socketReadable();
void socketWritable();
void dialogAccepted(); void dialogAccepted();
void dialogRefused(); 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 #endif