2
* demo_server.cpp - multi-threaded slim VNC-server for demo-purposes (optimized
3
* for lot of clients accessing server in read-only-mode)
5
* Copyright (c) 2006-2008 Tobias Doerffel <tobydox/at/users/dot/sf/dot/net>
7
* This file is part of iTALC - http://italc.sourceforge.net
9
* This program is free software; you can redistribute it and/or
10
* modify it under the terms of the GNU General Public
11
* License as published by the Free Software Foundation; either
12
* version 2 of the License, or (at your option) any later version.
14
* This program is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
* General Public License for more details.
19
* You should have received a copy of the GNU General Public
20
* License along with this program (see COPYING); if not, write to the
21
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22
* Boston, MA 02111-1307, USA.
27
#include <QtCore/QDateTime>
28
#include <QtCore/QTimer>
29
#include <QtCore/QVector>
30
#include <QtGui/QCursor>
33
#include "demo_server.h"
34
#include "isd_server.h"
35
#include "italc_rfb_ext.h"
40
const int CURSOR_UPDATE_TIME = 30;
42
int demoServer::s_numOfInstances = 0;
45
demoServer::demoServer( IVS * _ivs_conn, int _quality, quint16 _port,
46
QTcpSocket * _parent ) :
47
QTcpServer( _parent ),
48
m_conn( new ivsConnection(
49
QHostAddress( QHostAddress::LocalHost ).toString() +
50
":" + QString::number(
51
_ivs_conn->serverPort() ),
52
static_cast<ivsConnection::quality>(
53
ivsConnection::QualityDemoLow +
54
qBound( 0, _quality, 2 ) ),
55
_ivs_conn->runningInSeparateProcess() ) ),
56
m_updaterThread( new updaterThread( m_conn ) )
60
if( listen( QHostAddress::Any, _port ) == FALSE )
62
qCritical( "demoServer::demoServer(): "
63
"could not start demo-server!" );
67
m_updaterThread->start(/* QThread::HighPriority*/ );
74
demoServer::~demoServer()
78
delete m_updaterThread;
85
void demoServer::incomingConnection( int _sd )
87
new demoServerClient( _sd, m_conn );
95
demoServer::updaterThread::updaterThread( ivsConnection * _ic ) :
105
demoServer::updaterThread::~updaterThread()
114
void demoServer::updaterThread::run( void )
119
while( !m_quit && m_conn->state() != ivsConnection::Connected &&
120
m_conn->open() != ivsConnection::Connected )
122
qWarning( "demoServer::updaterThread::run(): "
123
"could not connect to local IVS!" );
127
m_conn->handleServerMessages( ( i = ( i + 1 ) % 2 ) == 0 );
129
m_conn->gracefulClose();
142
demoServerClient::demoServerClient( int _sd, const ivsConnection * _conn ) :
147
m_cursorShapeChanged( TRUE ),
148
m_cursorPosChanged( FALSE ),
149
m_socketDescriptor( _sd ),
152
m_otherEndianess( FALSE ),
153
m_lzoWorkMem( new Q_UINT8[sizeof( lzo_align_t ) *
154
( ( ( LZO1X_1_MEM_COMPRESS ) +
155
( sizeof( lzo_align_t ) - 1 ) ) /
156
sizeof( lzo_align_t ) ) ] )
158
QTimer::singleShot( CURSOR_UPDATE_TIME, this,
159
SLOT( checkForCursorMovement() ) );
166
demoServerClient::~demoServerClient()
170
delete[] m_lzoWorkMem;
176
void demoServerClient::updateRegion( const QRegion & _reg )
179
m_changedRegion += _reg;
180
m_dataMutex.unlock();
186
void demoServerClient::updateCursorShape( void )
188
m_cursorShapeChanged = TRUE;
194
void demoServerClient::checkForCursorMovement( void )
197
if( m_lastCursorPos != QCursor::pos() )
199
m_lastCursorPos = QCursor::pos();
200
m_cursorPosChanged = TRUE;
202
m_dataMutex.unlock();
203
QTimer::singleShot( CURSOR_UPDATE_TIME, this,
204
SLOT( checkForCursorMovement() ) );
210
void demoServerClient::moveCursor( void )
213
if( m_cursorPosChanged )
215
m_cursorPosChanged = FALSE;
216
const rfbFramebufferUpdateMsg m =
218
rfbFramebufferUpdate,
223
m_sock->write( (const char *) &m, sizeof( m ) );
225
const rfbRectangle rr =
227
swap16IfLE( m_lastCursorPos.x() ),
228
swap16IfLE( m_lastCursorPos.y() ),
233
const rfbFramebufferUpdateRectHeader rh =
236
swap32IfLE( rfbEncodingPointerPos )
239
m_sock->write( (const char *) &rh, sizeof( rh ) );
242
m_dataMutex.unlock();
248
void demoServerClient::processClient( void )
251
while( m_sock->bytesAvailable() > 0 )
254
if( m_sock->read( (char *) &cmd, sizeof( cmd ) ) <= 0 )
256
qWarning( "demoServerClient::processClient(): "
257
"could not read cmd" );
261
if( cmd != rfbFramebufferUpdateRequest )
266
if( m_changedRegion.isEmpty() )
271
// extract single (non-overlapping) rects out of changed region
272
// this way we avoid lot of simliar/overlapping rectangles,
273
// e.g. if we didn't get an update-request for a quite long time
274
// and there were a lot of updates - at the end we don't send
275
// more than the whole screen one time
276
const QVector<QRect> r = m_changedRegion.rects();
278
// no we gonna post all changed rects!
279
const rfbFramebufferUpdateMsg m =
281
rfbFramebufferUpdate,
283
swap16IfLE( r.size() +
284
( m_cursorShapeChanged ? 1 : 0 ) )
287
m_sock->write( (const char *) &m, sizeof( m ) );
289
for( QVector<QRect>::const_iterator it = r.begin();
290
it != r.end(); ++it )
292
const rfbRectangle rr =
294
swap16IfLE( it->x() ),
295
swap16IfLE( it->y() ),
296
swap16IfLE( it->width() ),
297
swap16IfLE( it->height() )
300
const rfbFramebufferUpdateRectHeader rh =
303
swap32IfLE( rfbEncodingItalc )
306
m_sock->write( (const char *) &rh, sizeof( rh ) );
308
const QImage & i = m_conn->screen();
309
italcRectEncodingHeader hdr = { 0, 0, 0 } ;
311
const Q_UINT16 w = it->width();
312
const Q_UINT16 h = it->height();
314
// we only compress if it's enough data, otherwise
315
// there's too much overhead
320
QRgb last_pix = *( (QRgb *) i.scanLine( it->y() ) + it->x() );
323
Q_UINT8 * out = new Q_UINT8[w * h * sizeof( QRgb )+16];
324
Q_UINT8 * out_ptr = out;
325
for( Q_UINT16 y = it->y(); y < it->y() + h; ++y )
327
const QRgb * data = ( (const QRgb *) i.scanLine( y ) ) +
329
for( Q_UINT16 x = 0; x < w; ++x )
331
if( data[x] != last_pix || rle_cnt > 254 )
333
*( (QRgb *) out_ptr ) = swap32IfBE( last_pix );
334
*( out_ptr + 3 ) = rle_cnt - rle_sub;
337
rle_cnt = rle_sub = 0;
347
*( (QRgb *) out_ptr ) = last_pix;
348
*( out_ptr + 3 ) = rle_cnt;
351
hdr.bytesRLE = out_ptr - out;
352
lzo_uint bytes_lzo = hdr.bytesRLE + hdr.bytesRLE / 16 + 67;
353
Q_UINT8 * comp = new Q_UINT8[bytes_lzo];
354
lzo1x_1_compress( (const unsigned char *) out, (lzo_uint) hdr.bytesRLE,
355
(unsigned char *) comp,
356
&bytes_lzo, m_lzoWorkMem );
357
hdr.bytesRLE = swap32IfLE( hdr.bytesRLE );
358
hdr.bytesLZO = swap32IfLE( bytes_lzo );
360
m_sock->write( (const char *) &hdr, sizeof( hdr ) );
361
m_sock->write( (const char *) comp, swap32IfLE( hdr.bytesLZO ) );
368
m_sock->write( (const char *) &hdr, sizeof( hdr ) );
369
if( m_otherEndianess )
371
Q_UINT32 * buf = new Q_UINT32[w];
372
for( Q_UINT16 y = 0; y < h; ++y )
374
const QRgb * src = (const QRgb *) i.scanLine( it->y() + y ) +
376
for( Q_UINT16 x = 0; x < w; ++x, ++src )
378
buf[x] = swap32( *src );
380
m_sock->write( (const char *) buf, w * sizeof( QRgb ) );
386
for( Q_UINT16 y = 0; y < h; ++y )
388
m_sock->write( (const char *)
389
( (const QRgb *) i.scanLine( it->y() + y ) + it->x() ),
390
w * sizeof( QRgb ) );
396
if( m_cursorShapeChanged )
398
const QImage cur = m_conn->cursorShape();
399
const rfbRectangle rr =
401
swap16IfLE( m_conn->cursorHotSpot().x() ),
402
swap16IfLE( m_conn->cursorHotSpot().y() ),
403
swap16IfLE( cur.width() ),
404
swap16IfLE( cur.height() )
407
const rfbFramebufferUpdateRectHeader rh =
410
swap32IfLE( rfbEncodingItalcCursor )
413
m_sock->write( (const char *) &rh, sizeof( rh ) );
415
QDataStream ds( m_sock );
420
//m_changedRegion.clear();
421
m_changedRegion = QRegion();
422
m_cursorShapeChanged = FALSE;
424
//m_sock->waitForBytesWritten();
427
m_dataMutex.unlock();
428
//QTimer::singleShot( 40, this, SLOT( processClient() ) );
434
void demoServerClient::run( void )
436
QMutexLocker ml( &m_dataMutex );
440
if( !m_sock->setSocketDescriptor( m_socketDescriptor ) )
442
qCritical( "demoServerClient::run(): "
443
"could not set socket-descriptor - aborting" );
448
socketDevice sd( qtcpsocketDispatcher, m_sock );
449
if( !isdServer::protocolInitialization( sd,
450
ItalcAuthHostBased, TRUE ) )
452
qCritical( "demoServerClient:::run(): "
453
"protocol initialization failed" );
460
if( !sd.read( (char *) &ci, sizeof( ci ) ) )
466
rfbServerInitMsg si = m_conn->m_si;
467
si.framebufferWidth = swap16IfLE( si.framebufferWidth );
468
si.framebufferHeight = swap16IfLE( si.framebufferHeight );
469
si.format.redMax = swap16IfLE( si.format.redMax );
470
si.format.greenMax = swap16IfLE( si.format.greenMax );
471
si.format.blueMax = swap16IfLE( si.format.blueMax );
472
si.nameLength = swap32IfLE( si.nameLength );
473
si.format.bigEndian = ( QSysInfo::ByteOrder == QSysInfo::BigEndian )
475
if( !sd.write( ( const char *) &si, sizeof( si ) ) )
481
char * desktop_name = new char[m_conn->m_si.nameLength+1];
484
if( !sd.write( desktop_name, m_conn->m_si.nameLength ) )
490
delete[] desktop_name;
493
rfbSetPixelFormatMsg spf;
495
if( !sd.read( (char *) &spf, sizeof( spf ) ) )
501
// we have to do server-side endianess-conversion in case it differs
502
// between client and server
503
if( spf.format.bigEndian != si.format.bigEndian )
505
m_otherEndianess = TRUE;
508
char buf[sizeof( rfbSetPixelFormatMsg ) + MAX_ENCODINGS *
510
rfbSetEncodingsMsg * se = (rfbSetEncodingsMsg *) buf;
512
if( !sd.read( (char *) se, sizeof( *se ) ) )
517
se->nEncodings = swap16IfLE( se->nEncodings );
519
Q_UINT32 * encs = (Q_UINT32 *)( &buf[sizeof(rfbSetEncodingsMsg)] );
521
if( !sd.read( (char *) encs, se->nEncodings * sizeof( Q_UINT32 ) ) )
527
bool has_italc_encoding = FALSE;
528
for( Q_UINT32 i = 0; i < se->nEncodings; ++i )
530
if( swap32IfLE( encs[i] ) == rfbEncodingItalc )
532
has_italc_encoding = TRUE;
536
if( !has_italc_encoding )
538
qCritical( "demoServerClient::run(): "
539
"client has no italc-encoding" );
544
// for some reason we have to do this to make the following connection
547
connect( m_conn, SIGNAL( cursorShapeChanged() ),
548
this, SLOT( updateCursorShape() ) );
549
connect( m_conn, SIGNAL( regionUpdated( const QRegion & ) ),
550
this, SLOT( updateRegion( const QRegion & ) ) );
554
// first time send a key-frame
555
updateRegion( m_conn->screen().rect() );
557
//connect( m_sock, SIGNAL( readyRead() ), this, SLOT( processClient() ) );
558
connect( m_sock, SIGNAL( disconnected() ), this,
559
SLOT( deleteLater() ) );
562
connect( &t, SIGNAL( timeout() ), this, SLOT( moveCursor() ),
563
Qt::DirectConnection );
564
t.start( CURSOR_UPDATE_TIME );
566
connect( &t2, SIGNAL( timeout() ), this, SLOT( processClient() ),
567
Qt::DirectConnection );
571
// now run our own event-loop for optimal scheduling
577
#include "demo_server.moc"