2
* ivs_connection.cpp - class ivsConnection, an implementation of the
3
* RFB-protocol with iTALC-extensions for Qt
5
* Copyright (c) 2004-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.
34
#include <QtCore/QCoreApplication>
35
#include <QtCore/QDateTime>
36
#include <QtGui/QImage>
37
#include <QtGui/QPainter>
40
#include "ivs_connection.h"
41
#include "qt_user_events.h"
42
#include "local_system.h"
43
#include "messagebox.h"
48
static const rfbPixelFormat __localDisplayFormat =
52
#ifdef WORDS_BIGENDIAN
64
0, 0 // only for padding
70
// =============================================================================
71
// implementation of IVS-connection
72
// =============================================================================
76
ivsConnection::ivsConnection( const QString & _host, quality _q,
79
isdConnection( ( _host.contains( ':' ) ? _host : _host + ":5900" ),
81
m_isDemoServer( FALSE ),
82
m_useAuthFile( _use_auth_file ),
87
m_scaledScreenNeedsUpdate( FALSE ),
89
m_softwareCursor( FALSE ),
92
m_rawBufferSize( -1 ),
94
m_decompStreamInited( FALSE )
97
m_zlibStreamActive[0] = m_zlibStreamActive[1] = m_zlibStreamActive[2] =
98
m_zlibStreamActive[3] = FALSE;
105
ivsConnection::~ivsConnection()
107
delete[] m_rawBuffer;
113
ivsConnection::states ivsConnection::protocolInitialization( void )
115
rfbProtocolVersionMsg protocol_version;
117
if( !readFromServer( protocol_version, sz_rfbProtocolVersionMsg ) )
119
return( state_ref() = ConnectionFailed );
122
protocol_version[sz_rfbProtocolVersionMsg] = 0;
125
if( sscanf( protocol_version, rfbProtocolVersionFormat, &major,
128
// not a standard VNC server - check whether it is an iTALC-
130
if( sscanf( protocol_version, idsProtocolVersionFormat, &major,
133
qCritical( "not a server I can deal with" );
134
return( state_ref() = InvalidServer );
136
m_isDemoServer = TRUE;
139
if( !writeToServer( protocol_version, sz_rfbProtocolVersionMsg ) )
141
return( state_ref() = ConnectionFailed );
144
if( authAgainstServer( m_quality >= QualityDemoLow ?
146
ItalcAuthChallengeViaAuthFile
148
ItalcAuthAppInternalChallenge
157
const rfbClientInitMsg ci = { 1 };
159
if( !writeToServer( (const char *) &ci, sizeof( ci ) ) )
161
return( state_ref() = ConnectionFailed );
164
if( !readFromServer( (char *)&m_si, sizeof( m_si ) ) )
166
return( state_ref() = ConnectionFailed );
169
m_si.framebufferWidth = swap16IfLE( m_si.framebufferWidth );
170
m_si.framebufferHeight = swap16IfLE( m_si.framebufferHeight );
171
m_si.format.redMax = swap16IfLE( m_si.format.redMax );
172
m_si.format.greenMax = swap16IfLE( m_si.format.greenMax );
173
m_si.format.blueMax = swap16IfLE( m_si.format.blueMax );
174
m_si.nameLength = swap32IfLE( m_si.nameLength );
176
char * desktop_name = new char[m_si.nameLength+1];
178
if( !readFromServer( desktop_name, m_si.nameLength ) )
180
return( state_ref() = ConnectionFailed );
183
//desktop_name[m_si.nameLength] = 0;
184
delete[] desktop_name;
187
rfbSetPixelFormatMsg spf;
189
spf.type = rfbSetPixelFormat;
190
spf.format = __localDisplayFormat;
191
spf.format.redMax = swap16IfLE( spf.format.redMax );
192
spf.format.greenMax = swap16IfLE( spf.format.greenMax );
193
spf.format.blueMax = swap16IfLE( spf.format.blueMax );
195
if( !writeToServer( (const char *) &spf, sizeof( spf ) ) )
197
return( state_ref() = ConnectionFailed );
201
char buf[sizeof( rfbSetPixelFormatMsg ) + MAX_ENCODINGS *
203
rfbSetEncodingsMsg * se = (rfbSetEncodingsMsg *) buf;
204
se->type = rfbSetEncodings;
207
Q_UINT32 * encs = (Q_UINT32 *)( &buf[sizeof(rfbSetEncodingsMsg)] );
209
if( m_quality >= QualityDemoLow )
211
encs[se->nEncodings++] = swap32IfLE( rfbEncodingRaw );
217
encs[se->nEncodings++] = swap32IfLE( rfbEncodingTight );
219
encs[se->nEncodings++] = swap32IfLE( rfbEncodingZlib );
221
encs[se->nEncodings++] = swap32IfLE( rfbEncodingCoRRE );
222
encs[se->nEncodings++] = swap32IfLE( rfbEncodingCopyRect );
223
encs[se->nEncodings++] = swap32IfLE( rfbEncodingRaw );
224
encs[se->nEncodings++] = swap32IfLE( rfbEncodingRichCursor );
225
//encs[se->nEncodings++] = swap32IfLE( rfbEncodingXCursor );
226
encs[se->nEncodings++] = swap32IfLE( rfbEncodingPointerPos );
227
//encs[se->nEncodings++] = swap32IfLE( rfbEncodingRRE );
233
encs[se->nEncodings++] = swap32IfLE(
234
rfbEncodingQualityLevel4 );
237
encs[se->nEncodings++] = swap32IfLE(
238
rfbEncodingQualityLevel9 );
247
encs[se->nEncodings++] = swap32IfLE(
248
rfbEncodingCompressLevel4 );
252
//encs[se->nEncodings++] = swap32IfLE( rfbEncodingLastRect );
253
encs[se->nEncodings++] = swap32IfLE( rfbEncodingItalc );
254
encs[se->nEncodings++] = swap32IfLE( rfbEncodingItalcCursor );
257
unsigned int len = sizeof( rfbSetEncodingsMsg ) +
258
se->nEncodings * sizeof( Q_UINT32 );
260
se->nEncodings = swap16IfLE( se->nEncodings );
262
if( !writeToServer( buf, len ) )
264
return( state_ref() = ConnectionFailed );
267
state_ref() = Connected;
269
m_screen = QImage( m_si.framebufferWidth, m_si.framebufferHeight,
270
QImage::Format_RGB32 );
271
m_screen.fill( Qt::black );
273
sendFramebufferUpdateRequest();
274
sendGetUserInformationRequest();
281
void ivsConnection::close( void )
284
m_zlibStreamActive[0] = m_zlibStreamActive[1] = m_zlibStreamActive[2] =
285
m_zlibStreamActive[3] = FALSE;
287
isdConnection::close();
293
bool ivsConnection::takeSnapshot( void )
295
if( user().isEmpty() || state() != ivsConnection::Connected )
301
QString txt = user() + "@" + host() + " " +
302
QDate( QDate::currentDate() ).toString( Qt::ISODate ) +
303
" " + QTime( QTime::currentTime() ).
304
toString( Qt::ISODate );
305
const QString dir = localSystem::snapshotDir();
306
if( !localSystem::ensurePathExists( dir ) )
308
messageBox::information( tr( "Snapshot" ),
309
tr( "Could not take a snapshot as directory %1 doesn't "
310
"exist and couldn't be created." ).arg( dir ) );
314
// construct filename
315
QString file_name = "_" + host() + "_" + QDate( QDate::currentDate() ).
316
toString( Qt::ISODate ) +
317
"_" + QTime( QTime::currentTime() ).
318
toString( Qt::ISODate ) + ".png";
319
file_name.replace( ':', '-' );
320
file_name = dir + user().section( '(', 1, 1 ).section( ')', 0, 0 ) +
322
const int FONT_SIZE = 14;
323
const int RECT_MARGIN = 10;
324
const int RECT_INNER_MARGIN = 5;
326
QImage img( screen() );
328
QPixmap italc_icon( QPixmap(
329
":/resources/client_observed.png" ) );
332
QFont fnt = p.font();
333
fnt.setPointSize( FONT_SIZE );
336
QFontMetrics fm( p.font() );
338
const int rx = RECT_MARGIN;
339
const int ry = img.height() - RECT_MARGIN - 2 * RECT_INNER_MARGIN -
341
const int rw = RECT_MARGIN + 4 * RECT_INNER_MARGIN +
342
fm.size( Qt::TextSingleLine, txt ).width() +
344
const int rh = 2 * RECT_INNER_MARGIN + FONT_SIZE;
345
const int ix = rx + RECT_INNER_MARGIN;
346
const int iy = ry + RECT_INNER_MARGIN;
347
const int tx = ix + italc_icon.width() + 2 * RECT_INNER_MARGIN;
348
const int ty = ry + RECT_INNER_MARGIN + FONT_SIZE - 2;
350
p.fillRect( rx, ry, rw, rh, QColor( 255, 255, 255, 128 ) );
351
p.drawPixmap( ix, iy, italc_icon );
352
p.drawText( tx, ty, txt );
353
img.save( file_name, "PNG", 50 );
361
bool ivsConnection::sendFramebufferUpdateRequest( void )
363
return( sendFramebufferUpdateRequest( 0, 0, m_si.framebufferWidth,
364
m_si.framebufferHeight, FALSE ) );
370
bool ivsConnection::sendIncrementalFramebufferUpdateRequest( void )
372
return( sendFramebufferUpdateRequest( 0, 0, m_si.framebufferWidth,
373
m_si.framebufferHeight, TRUE ) );
379
bool ivsConnection::sendFramebufferUpdateRequest( Q_UINT16 _x, Q_UINT16 _y,
380
Q_UINT16 _w, Q_UINT16 _h, bool _incremental )
382
if( state() != Connected )
387
rfbFramebufferUpdateRequestMsg fur;
389
fur.type = rfbFramebufferUpdateRequest;
390
fur.incremental = ( _incremental ) ? 1 : 0;
391
fur.x = swap16IfLE( _x );
392
fur.y = swap16IfLE( _y );
393
fur.w = swap16IfLE( _w );
394
fur.h = swap16IfLE( _h );
396
return( writeToServer( (char *) &fur, sizeof( fur ) ) );
402
bool ivsConnection::sendPointerEvent( Q_UINT16 _x, Q_UINT16 _y,
403
Q_UINT16 _button_mask )
405
if( state() != Connected )
410
rfbPointerEventMsg pe;
412
pe.type = rfbPointerEvent;
413
pe.buttonMask = _button_mask;
414
//if (_x < 0) _x = 0;
415
//if (_y < 0) _y = 0;
416
pe.x = swap16IfLE( _x );
417
pe.y = swap16IfLE( _y );
419
// make sure our own pointer is updated when remote-controlling
420
handleCursorPos( _x, _y );
422
return( writeToServer( (char *) &pe, sizeof( pe ) ) );
428
bool ivsConnection::sendKeyEvent( Q_UINT32 key, bool down )
430
if( state() != Connected )
437
ke.type = rfbKeyEvent;
438
ke.down = ( down )? 1 : 0;
439
ke.key = swap32IfLE( key );
441
return( writeToServer( (char *) &ke, sizeof( ke ) ) );
447
void ivsConnection::postRegionChangedEvent( const QRegion & _rgn )
449
if( parent() != NULL )
451
regionChangedEvent * rche = new regionChangedEvent( _rgn );
452
QCoreApplication::postEvent( parent(), rche );
459
void ivsConnection::rescaleScreen( void )
461
if( m_scaledScreenNeedsUpdate )
463
QWriteLocker swl( &m_scaledImageLock );
464
if( m_scaledScreen.size() != m_scaledSize )
466
m_scaledScreen = QImage( m_scaledSize,
467
QImage::Format_RGB32 );
469
if( m_screen.size().isValid() )
471
m_screen.scaleTo( m_scaledScreen );
475
m_scaledScreen.fill( Qt::black );
477
m_scaledScreenNeedsUpdate = FALSE;
484
bool ivsConnection::handleServerMessages( bool _send_screen_update, int _tries )
486
while( hasData() && --_tries >= 0 )
489
rfbServerToClientMsg msg;
490
if( !readFromServer( (char *) &msg, sizeof( Q_UINT8 ) ) )
492
qWarning( "ivsConnection::handleServerMessage(...): "
493
"reading message-type failed" );
498
case rfbSetColourMapEntries:
499
qWarning( "ivsConnection::handleServerMessage(...): "
500
"setting colormap entries requested - ignoring" );
503
case rfbFramebufferUpdate:
505
QWriteLocker wl( &m_imageLock );
506
if( !readFromServer( ( (char *)&msg.fu ) + 1,
507
sizeof( rfbFramebufferUpdateMsg ) - 1 ) )
509
qCritical( "ivsConnection::handleServerMessage(...): "
510
"reading framebuffer-update-msg failed" );
514
msg.fu.nRects = swap16IfLE( msg.fu.nRects );
516
QRegion updated_region;
518
rfbFramebufferUpdateRectHeader rect;
519
for( Q_UINT16 i = 0; i < msg.fu.nRects; i++ )
521
if( !readFromServer( (char *)&rect,
527
rect.r.x = swap16IfLE( rect.r.x );
528
rect.r.y = swap16IfLE( rect.r.y );
529
rect.r.w = swap16IfLE( rect.r.w );
530
rect.r.h = swap16IfLE( rect.r.h );
532
rect.encoding = swap32IfLE( rect.encoding );
533
if( rect.encoding == rfbEncodingLastRect )
538
if( ( rect.r.x + rect.r.w >
539
m_si.framebufferWidth ) ||
540
( rect.r.y + rect.r.h >
541
m_si.framebufferHeight ) )
543
qWarning( "ivsConnection::handleServerMessage(...): "
544
"rect too large: %dx%d at (%d, %d) (encoding: %d)",
545
rect.r.w, rect.r.h, rect.r.x, rect.r.y, rect.encoding );
549
if ( rect.encoding != rfbEncodingPointerPos &&
550
rect.encoding != rfbEncodingXCursor &&
551
rect.encoding != rfbEncodingRichCursor )
553
if( ( rect.r.h * rect.r.w ) == 0 )
555
qWarning( "ivsConnection::handleServerMessage(...): zero size rect - "
559
updated_region += QRect( rect.r.x,
566
m_softwareCursor = TRUE;
569
switch( rect.encoding )
572
if( !handleRaw( rect.r.x, rect.r.y, rect.r.w, rect.r.h ) )
578
case rfbEncodingCopyRect:
581
if( !readFromServer( (char *) &cr, sizeof( cr ) ) )
585
cr.srcX = swap16IfLE( cr.srcX );
586
cr.srcY = swap16IfLE( cr.srcY );
588
m_screen.copyExistingRect( cr.srcX, cr.srcY, rect.r.w,
589
rect.r.h, rect.r.x, rect.r.y );
594
if( !handleRRE( rect.r.x, rect.r.y, rect.r.w, rect.r.h ) )
600
case rfbEncodingCoRRE:
601
if( !handleCoRRE( rect.r.x, rect.r.y, rect.r.w, rect.r.h ) )
607
case rfbEncodingZlib:
608
if( !handleZlib( rect.r.x, rect.r.y, rect.r.w, rect.r.h ) )
613
case rfbEncodingTight:
614
if( !handleTight( rect.r.x, rect.r.y, rect.r.w, rect.r.h ) )
620
case rfbEncodingPointerPos:
621
if( !handleCursorPos( rect.r.x, rect.r.y ) )
627
case rfbEncodingRichCursor:
628
case rfbEncodingXCursor:
629
if( !handleCursorShape( rect.r.x, rect.r.y, rect.r.w,
630
rect.r.h, rect.encoding ) )
636
case rfbEncodingItalc:
637
if( !handleItalc( rect.r.x, rect.r.y, rect.r.w, rect.r.h ) )
643
case rfbEncodingItalcCursor:
645
QRegion ch_reg = QRect( m_cursorPos - m_cursorHotSpot,
646
m_cursorShape.size() );
647
m_cursorLock.lockForWrite();
648
//m_cursorShape = socketDev().read().value<QImage>();
649
QDataStream ds( static_cast<QTcpSocket *>(
650
socketDev().user() ) );
652
m_cursorLock.unlock();
653
m_cursorHotSpot = QPoint( rect.r.x, rect.r.y );
656
ch_reg += QRect( m_cursorPos - m_cursorHotSpot,
657
m_cursorShape.size() );
659
// make sure, area around old cursor is updated and new cursor
661
postRegionChangedEvent( ch_reg );
666
qCritical( "ivsConnection::handleServerMessage(...): "
667
"unknown rect encoding %d",
668
(int) rect.encoding );
674
if( updated_region.isEmpty() )
680
m_scaledScreenNeedsUpdate = TRUE;
682
if( m_quality >= QualityDemoLow &&
683
m_quality != QualityDemoHigh )
685
const QRgb and_value = ( m_quality == QualityDemoLow ) ?
687
// if we're providing data for demo-purposes,
688
// we perform a simple color-reduction for
689
// better compression-results
690
const QVector<QRect> rects = updated_region.rects();
691
for( QVector<QRect>::const_iterator it = rects.begin();
692
it != rects.end(); ++it )
694
for( Q_UINT16 y = 0; y < it->height(); ++y )
696
QRgb * data = ( (QRgb *) m_screen.scanLine( y +
697
it->y() ) ) + it->x();
698
for( Q_UINT16 x = 0; x < it->width(); ++x )
700
data[x] &= and_value;
705
postRegionChangedEvent( updated_region );
708
emit regionUpdated( updated_region );
714
// FIXME: bell-action
717
case rfbServerCutText:
719
if( !readFromServer( ( (char *) &msg ) + 1,
720
sizeof( rfbServerCutTextMsg ) - 1 ) )
724
msg.sct.length = swap32IfLE( msg.sct.length );
725
char * server_cut_text = new char[msg.sct.length+1];
726
if( !readFromServer( server_cut_text, msg.sct.length ) )
728
delete[] server_cut_text;
731
delete[] server_cut_text;
736
if( !isdConnection::handleServerMessage( msg.type ) )
742
} // end while( ... )
744
if( !m_scaledSize.isEmpty() )
749
if( _send_screen_update )
751
return( sendIncrementalFramebufferUpdateRequest() );
761
// =============================================================================
762
// functions for decoding rects
763
// =============================================================================
766
bool ivsConnection::handleRaw( Q_UINT16 rx, Q_UINT16 ry, Q_UINT16 rw,
769
const int bytes_per_line = rw * sizeof( QRgb );
770
Q_UINT16 lines_to_read = BUFFER_SIZE / bytes_per_line;
771
const Q_UINT16 img_width = m_screen.width();
774
if( lines_to_read > rh )
778
if( !readFromServer( m_buffer, bytes_per_line *
783
const QRgb * src = (const QRgb *) m_buffer;
784
QRgb * dst = (QRgb *) m_screen.scanLine( ry ) + rx;
785
for( Q_UINT16 y = 0; y < lines_to_read; ++y )
787
memcpy( dst, src, rw * sizeof( QRgb ) );
800
bool ivsConnection::handleCoRRE( Q_UINT16 rx, Q_UINT16 ry, Q_UINT16 rw,
805
if( !readFromServer( (char *) &hdr, sizeof( hdr ) ) )
810
hdr.nSubrects = swap32IfLE( hdr.nSubrects );
813
if( !readFromServer( (char *) &pix, sizeof( pix ) ) )
818
m_screen.fillRect( rx, ry, rw, rh, pix );
820
if( !readFromServer( m_buffer, hdr.nSubrects *
821
( sizeof( rfbCoRRERectangle) +
822
sizeof( Q_UINT32 ) ) ) )
827
Q_UINT8 * ptr = (Q_UINT8 *) m_buffer;
829
for( Q_UINT32 i = 0; i < hdr.nSubrects; i++ )
832
ptr += sizeof( pix );
837
m_screen.fillRect( rx+x, ry+y, w, h, pix );
845
bool ivsConnection::handleRRE( Q_UINT16, Q_UINT16, Q_UINT16, Q_UINT16 )
847
qCritical( "ivsConnection:handleRRE(...): got RRE-encoded rect. "
853
#define RGB_TO_PIXEL(r,g,b) \
854
(((Q_UINT32)(r) & __localDisplayFormat.redMax ) << \
855
__localDisplayFormat.redShift | \
856
((Q_UINT32)(g) & __localDisplayFormat.greenMax ) << \
857
__localDisplayFormat.greenShift | \
858
((Q_UINT32)(b) & __localDisplayFormat.blueMax ) << \
859
__localDisplayFormat.blueShift )
862
#define RGB24_TO_PIXEL(r,g,b) \
863
((((uint32_t)(r) & 0xFF) * __localDisplayFormat.redMax + 127) / 255 \
864
<< __localDisplayFormat.redShift | \
865
(((uint32_t)(g) & 0xFF) * __localDisplayFormat.greenMax + 127) / 255 \
866
<< __localDisplayFormat.greenShift | \
867
(((uint32_t)(b) & 0xFF) * __localDisplayFormat.blueMax + 127) / 255 \
868
<< __localDisplayFormat.blueShift)
873
bool ivsConnection::handleZlib( Q_UINT16 rx, Q_UINT16 ry, Q_UINT16 rw,
876
/* First make sure we have a large enough raw buffer to hold the
877
* decompressed data. In practice, with a fixed BPP, fixed frame
878
* buffer size and the first update containing the entire frame
879
* buffer, this buffer allocation should only happen once, on the
882
if( m_rawBufferSize < (int) rw * rh * 4 )
884
delete[] m_rawBuffer;
885
m_rawBufferSize = (int) rw * rh * 4;
886
m_rawBuffer = new char[m_rawBufferSize];
890
if( !readFromServer( (char *) &hdr, sz_rfbZlibHeader ) )
895
int remaining = swap32IfLE( hdr.nBytes );
897
// Need to initialize the decompressor state
898
m_decompStream.next_in = ( Bytef * ) m_buffer;
899
m_decompStream.avail_in = 0;
900
m_decompStream.next_out = ( Bytef * ) m_rawBuffer;
901
m_decompStream.avail_out = m_rawBufferSize;
902
m_decompStream.data_type = Z_BINARY;
905
// Initialize the decompression stream structures on the first
907
if( !m_decompStreamInited )
909
inflateResult = inflateInit( &m_decompStream );
911
if ( inflateResult != Z_OK )
913
qCritical( "inflateInit returned error: %d, msg: %s",
914
inflateResult, m_decompStream.msg );
917
m_decompStreamInited = TRUE;
920
inflateResult = Z_OK;
922
// Process buffer full of data until no more to process, or
923
// some type of inflater error, or Z_STREAM_END.
924
while( remaining > 0 && inflateResult == Z_OK )
927
if( remaining > BUFFER_SIZE )
929
toRead = BUFFER_SIZE;
936
// Fill the buffer, obtaining data from the server.
937
if( !readFromServer( m_buffer, toRead ) )
942
m_decompStream.next_in = ( Bytef * ) m_buffer;
943
m_decompStream.avail_in = toRead;
945
// Need to uncompress buffer full.
946
inflateResult = inflate( &m_decompStream, Z_SYNC_FLUSH );
948
// We never supply a dictionary for compression. */
949
if( inflateResult == Z_NEED_DICT )
951
qCritical( "ivsConnection::handleZlib(...): "
952
"zlib inflate needs a dictionary!" );
955
if ( inflateResult < 0 )
957
qCritical( "ivsConnection::handleZlib(...): "
958
"zlib inflate returned error: %d, msg: %s",
959
inflateResult, m_decompStream.msg );
963
// Result buffer allocated to be at least large enough.
964
// We should never run out of space!
965
if( m_decompStream.avail_in > 0 &&
966
m_decompStream.avail_out <= 0 )
968
qCritical( "ivsConnection::handleZlib(...): "
969
"zlib inflate ran out of space!" );
975
} // while ( remaining > 0 )
977
if( inflateResult == Z_OK )
979
m_screen.copyRect( rx, ry, rw, rh, (QRgb *) m_rawBuffer );
983
qCritical( "ivsConnection::handleZlib(...): "
984
"zlib inflate returned error: %d, msg: %s",
985
inflateResult, m_decompStream.msg );
994
#define TIGHT_MIN_TO_COMPRESS 12
1000
typedef void( ivsConnection:: *filterPtr )( Q_UINT16, Q_UINT32 * );
1006
bool ivsConnection::handleTight( Q_UINT16 rx, Q_UINT16 ry, Q_UINT16 rw,
1012
if( !readFromServer( (char *) &comp_ctl, 1 ) )
1017
// Flush zlib streams if we are told by the server to do so.
1018
for( Q_UINT8 stream_id = 0; stream_id < 4; stream_id++ )
1020
if( ( comp_ctl & 1 ) && m_zlibStreamActive[stream_id] )
1022
if( inflateEnd( &m_zlibStream[stream_id] ) != Z_OK &&
1023
m_zlibStream[stream_id].msg != NULL )
1025
qCritical( "inflateEnd: %s",
1026
m_zlibStream[stream_id].msg );
1028
m_zlibStreamActive[stream_id] = FALSE;
1033
// Handle solid rectangles.
1034
if( comp_ctl == rfbTightFill )
1036
if( !readFromServer( (char*)&fill_color,
1037
sizeof( fill_color ) ) )
1041
m_screen.fillRect( rx, ry, rw, rh, fill_color );
1045
if( comp_ctl == rfbTightJpeg )
1048
return( decompressJpegRect( rx, ry, rw, rh ) );
1055
// Quit on unsupported subencoding value.
1056
if( comp_ctl > rfbTightMaxSubencoding)
1058
qCritical( "tight encoding: bad subencoding value received." );
1062
// Here primary compression mode handling begins.
1063
// Data was processed with optional filter + zlib compression.
1064
filterPtr filter_function;
1067
// First, we should identify a filter to use.
1068
if( ( comp_ctl & rfbTightExplicitFilter ) != 0 )
1071
if( !readFromServer( (char*) &filter_id, 1 ) )
1078
case rfbTightFilterCopy:
1079
filter_function = &ivsConnection::filterCopy;
1080
bits_pixel = initFilterCopy( rw, rh );
1082
case rfbTightFilterPalette:
1083
filter_function = &ivsConnection::filterPalette;
1084
bits_pixel = initFilterPalette( rw, rh );
1086
case rfbTightFilterGradient:
1088
&ivsConnection::filterGradient;
1089
bits_pixel = initFilterGradient( rw, rh );
1092
qCritical( "Tight encoding: unknown filter "
1099
filter_function = &ivsConnection::filterCopy;
1100
bits_pixel = initFilterCopy( rw, rh );
1103
if( bits_pixel == 0 )
1105
qCritical( "Tight encoding: error receiving palette." );
1110
// Determine if the data should be decompressed or just copied.
1111
Q_UINT16 row_size = ( (int) rw * bits_pixel + 7 ) / 8;
1112
if( rh * row_size < TIGHT_MIN_TO_COMPRESS )
1114
if( !readFromServer( (char*)m_buffer, rh * row_size ) )
1119
QRgb * buffer2 = (QRgb *) &m_buffer[TIGHT_MIN_TO_COMPRESS * 4];
1120
( this->*( filter_function ) )( rh, (Q_UINT32 *)buffer2 );
1121
m_screen.copyRect( rx, ry, rw, rh, buffer2 );
1125
// Read the length (1..3 bytes) of compressed data following.
1126
int compressed_len = (int)readCompactLen();
1127
if( compressed_len <= 0 )
1129
qCritical( "Incorrect data received from the server." );
1134
// Now let's initialize compression stream if needed.
1135
Q_UINT8 stream_id = comp_ctl & 0x03;
1136
z_streamp zs = &m_zlibStream[stream_id];
1137
if( !m_zlibStreamActive[stream_id] )
1139
zs->zalloc = Z_NULL;
1141
zs->opaque = Z_NULL;
1142
int err = inflateInit( zs );
1145
if( zs->msg != NULL )
1147
qCritical( "InflateInit error: %s", zs->msg );
1151
m_zlibStreamActive[stream_id] = TRUE;
1155
// Read, decode and draw actual pixel data in a loop.
1156
int buffer_size = BUFFER_SIZE * bits_pixel / ( bits_pixel+32 ) &
1158
if( row_size > buffer_size )
1160
// Should be impossible when BUFFER_SIZE >= 16384
1161
qCritical( "Internal error: incorrect buffer size." );
1164
QRgb * buffer2 = (QRgb *) &m_buffer[buffer_size];
1167
Q_UINT16 rows_processed = 0;
1168
int extra_bytes = 0;
1171
while( compressed_len > 0 )
1173
if( compressed_len > ZLIB_BUFFER_SIZE )
1175
portion_len = ZLIB_BUFFER_SIZE;
1179
portion_len = compressed_len;
1182
if( !readFromServer( (char*)m_zlibBuffer, portion_len ) )
1187
compressed_len -= portion_len;
1189
zs->next_in = (Bytef *)m_zlibBuffer;
1190
zs->avail_in = portion_len;
1194
zs->next_out = (Bytef *) &m_buffer[extra_bytes];
1195
zs->avail_out = buffer_size - extra_bytes;
1197
int err = inflate(zs, Z_SYNC_FLUSH);
1198
if( err == Z_BUF_ERROR ) // Input exhausted --
1203
if( err != Z_OK && err != Z_STREAM_END )
1205
if( zs->msg != NULL )
1207
qCritical( "Inflate error: %s",
1212
qCritical( "Inflate error: %d", err );
1217
const Q_UINT16 num_rows = (Q_UINT16)( ( buffer_size -
1221
( this->*( filter_function ) )( num_rows,
1222
(Q_UINT32 *)buffer2 );
1223
extra_bytes = buffer_size - zs->avail_out - num_rows *
1225
if( extra_bytes > 0 )
1228
&m_buffer[num_rows * row_size],
1233
m_screen.copyRect( rx, ry+rows_processed, rw,
1234
num_rows, buffer2 );
1236
rows_processed += num_rows;
1238
while( zs->avail_out == 0 );
1241
if( rows_processed != rh )
1243
qCritical( "Incorrect number of scan lines after "
1253
/*----------------------------------------------------------------------------
1259
int ivsConnection::initFilterCopy( Q_UINT16 rw, Q_UINT16/* rh*/ )
1269
void ivsConnection::filterCopy( Q_UINT16 num_rows, Q_UINT32 * dst )
1271
memcpy( dst, m_buffer, num_rows * m_rectWidth * 4 );
1277
int ivsConnection::initFilterGradient( Q_UINT16 rw, Q_UINT16/* rh*/ )
1279
const int bits = initFilterCopy( rw, 0 );
1280
memset( m_tightPrevRow, 0, rw * 3 * sizeof( uint16_t ) );
1287
void ivsConnection::filterGradient( Q_UINT16 num_rows, Q_UINT32 * dst )
1289
Q_UINT32 * src = (Q_UINT32 *) m_buffer;
1290
Q_UINT16 * that_row = (Q_UINT16 *) m_tightPrevRow;
1291
Q_UINT16 this_row[2048*3];
1293
const Q_UINT16 max[3] =
1295
__localDisplayFormat.redMax,
1296
__localDisplayFormat.greenMax,
1297
__localDisplayFormat.blueMax
1299
const int shift[3] =
1301
__localDisplayFormat.redShift,
1302
__localDisplayFormat.greenShift,
1303
__localDisplayFormat.blueShift
1308
for( Q_UINT16 y = 0; y < num_rows; ++y )
1310
// First pixel in a row
1311
for( Q_UINT8 c = 0; c < 3; ++c )
1313
pix[c] = (Q_UINT16)(((src[y*m_rectWidth] >> shift[c]) +
1314
that_row[c]) & max[c]);
1315
this_row[c] = pix[c];
1317
dst[y*m_rectWidth] = RGB_TO_PIXEL( pix[0], pix[1], pix[2] );
1318
// Remaining pixels of a row
1319
for( Q_UINT16 x = 1; x < m_rectWidth; ++x )
1321
for( Q_UINT8 c = 0; c < 3; ++c )
1323
est[c] = (int)that_row[x*3+c] +
1325
(int)that_row[(x-1)*3+c];
1326
if( est[c] > (int)max[c] )
1328
est[c] = (int)max[c];
1330
else if( est[c] < 0 )
1334
pix[c] = (Q_UINT16)(((src[y*m_rectWidth+x] >>
1335
shift[c]) + est[c]) & max[c]);
1336
this_row[x*3+c] = pix[c];
1338
dst[y*m_rectWidth+x] = RGB_TO_PIXEL( pix[0], pix[1],
1341
memcpy( that_row, this_row, m_rectWidth * 3 *
1342
sizeof( Q_UINT16 ) );
1349
int ivsConnection::initFilterPalette( Q_UINT16 rw, Q_UINT16/* rh*/ )
1355
if( !readFromServer( (char*)&num_colors, 1 ) )
1360
m_rectColors = (Q_UINT16) num_colors;
1361
if( ++m_rectColors < 2 )
1366
if( !readFromServer( (char*)&m_tightPalette, m_rectColors *
1367
sizeof( Q_UINT32 ) ) )
1372
return( ( m_rectColors == 2 ) ? 1 : 8 );
1378
void ivsConnection::filterPalette( Q_UINT16 num_rows, Q_UINT32 * dst )
1380
Q_UINT8 * src = (Q_UINT8 *)m_buffer;
1381
Q_UINT32 * palette = (Q_UINT32 *) m_tightPalette;
1383
if( m_rectColors == 2 )
1385
const Q_UINT16 w = (m_rectWidth + 7) / 8;
1386
for( Q_UINT16 y = 0; y < num_rows; ++y )
1389
for( x = 0; x < m_rectWidth/8; x++ )
1391
for( int b = 7; b >= 0; b-- )
1393
dst[y*m_rectWidth+x*8+7-b] =
1394
palette[src[y*w+x] >> b & 1];
1397
for( int b = 7; b >= 8 - m_rectWidth % 8; b-- )
1399
dst[y*m_rectWidth+x*8+7-b] =
1400
palette[src[y*w+x] >> b & 1];
1406
for( Q_UINT16 y = 0; y < num_rows; y++ )
1408
for( Q_UINT16 x = 0; x < m_rectWidth; x++ )
1410
dst[y*m_rectWidth+x] =
1411
palette[(int)src[y*m_rectWidth+x]];
1419
/*----------------------------------------------------------------------------
1421
* JPEG decompression.
1426
void jpegInitSource( jpeglib::j_decompress_ptr )
1433
jpeglib::boolean jpegFillInputBuffer( jpeglib::j_decompress_ptr )
1435
qWarning( "jpegFillInputBuffer(...) called (not implemented, "
1436
"because it should not be needed" );
1443
void jpegSkipInputData( jpeglib::j_decompress_ptr, long )
1445
qWarning( "jpegSkipInputData(...) called (not implemented, "
1446
"because it should not be needed" );
1452
void jpegTermSource( jpeglib::j_decompress_ptr )
1458
using jpeglib::jpeg_decompress_struct;
1460
bool ivsConnection::decompressJpegRect( Q_UINT16 x, Q_UINT16 y, Q_UINT16 w,
1463
int compressed_len = (int) readCompactLen();
1464
if( compressed_len <= 0 )
1466
qCritical( "ivsConnection::decompressJpegRect(...): "
1467
"Incorrect data received from the server." );
1471
Q_UINT8 * compressed_data = new Q_UINT8[compressed_len];
1473
if( !readFromServer( (char*)compressed_data, compressed_len ) )
1475
delete[] compressed_data;
1479
struct jpeglib::jpeg_error_mgr jerr;
1480
struct jpeglib::jpeg_decompress_struct cinfo;
1481
cinfo.err = jpeglib::jpeg_std_error( &jerr );
1482
jpeglib::jpeg_create_decompress( &cinfo );
1484
//jpegSetSrcManager (&cinfo, compressed_data, compressed_len);
1485
m_jpegSrcManager.init_source = jpegInitSource;
1486
m_jpegSrcManager.fill_input_buffer = jpegFillInputBuffer;
1487
m_jpegSrcManager.skip_input_data = jpegSkipInputData;
1488
m_jpegSrcManager.resync_to_restart = jpeglib::jpeg_resync_to_restart;
1489
m_jpegSrcManager.term_source = jpegTermSource;
1490
m_jpegSrcManager.next_input_byte = (jpeglib::JOCTET *) compressed_data;
1491
m_jpegSrcManager.bytes_in_buffer = (size_t) compressed_len;
1493
cinfo.src = &m_jpegSrcManager;
1496
jpeglib::jpeg_read_header( &cinfo, TRUE );
1497
cinfo.out_color_space = jpeglib::JCS_RGB;
1499
jpeglib::jpeg_start_decompress( &cinfo );
1500
if( cinfo.output_width != w || cinfo.output_height != h ||
1501
cinfo.output_components != 3 )
1503
qCritical( "Tight Encoding: Wrong JPEG data received." );
1504
delete[] compressed_data;
1505
jpeglib::jpeg_destroy_decompress( &cinfo );
1509
jpeglib::JSAMPROW row_pointer[1];
1510
row_pointer[0] = (jpeglib::JSAMPROW) m_buffer;
1512
while( cinfo.output_scanline < cinfo.output_height )
1514
jpeglib::jpeg_read_scanlines( &cinfo, row_pointer, 1 );
1515
Q_UINT32 * pixel_ptr = (Q_UINT32 *) &m_buffer[BUFFER_SIZE / 2];
1516
for( Q_UINT16 dx = 0; dx < w; dx++ )
1518
*pixel_ptr++ = RGB_TO_PIXEL( m_buffer[dx*3],
1522
m_screen.copyRect( x, y+dy, w, 1, (QRgb *)
1523
&m_buffer[BUFFER_SIZE / 2] );
1527
jpeglib::jpeg_finish_decompress( &cinfo );
1529
jpeglib::jpeg_destroy_decompress( &cinfo );
1531
delete[] compressed_data;
1536
#endif /* LIBJPEG */
1543
bool ivsConnection::handleCursorPos( const Q_UINT16 _x, const Q_UINT16 _y )
1545
// move cursor and update appropriate region
1546
QRegion ch_reg = QRect( m_cursorPos - m_cursorHotSpot,
1547
m_cursorShape.size() );
1548
m_cursorPos = QPoint( _x, _y );
1549
ch_reg += QRect( m_cursorPos - m_cursorHotSpot, m_cursorShape.size() );
1551
postRegionChangedEvent( ch_reg );
1553
if( m_quality < QualityDemoLow )
1555
emit regionUpdated( ch_reg );
1564
bool ivsConnection::handleCursorShape( const Q_UINT16 _xhot,
1565
const Q_UINT16 _yhot,
1566
const Q_UINT16 _width,
1567
const Q_UINT16 _height,
1568
const Q_UINT32 _enc )
1570
const int bytesPerPixel = __localDisplayFormat.bitsPerPixel / 8;
1571
const int bytesPerRow = ( _width + 7 ) / 8;
1572
const int bytesMaskData = bytesPerRow * _height;
1573
if( _width * _height == 0 )
1578
// Allocate memory for pixel data and temporary mask data.
1580
Q_UINT8 * rcSource = new Q_UINT8[_width * _height * bytesPerPixel];
1581
if( rcSource == NULL )
1586
Q_UINT8 * rcMask = new Q_UINT8[bytesMaskData];
1587
if( rcMask == NULL )
1593
// Read and decode cursor pixel data, depending on the encoding type.
1595
if( _enc == rfbEncodingXCursor )
1597
rfbXCursorColors rgb;
1598
// Read and convert background and foreground colors.
1599
if( !readFromServer( (char *) &rgb, sz_rfbXCursorColors ) )
1605
const Q_UINT32 colors[2] = {
1606
RGB24_TO_PIXEL( rgb.backRed, rgb.backGreen, rgb.backBlue ),
1607
RGB24_TO_PIXEL( rgb.foreRed, rgb.foreGreen, rgb.foreBlue )
1610
// Read 1bpp pixel data into a temporary buffer.
1611
if( !readFromServer( (char*) rcMask, bytesMaskData ) )
1618
// Convert 1bpp data to byte-wide color indices.
1619
Q_UINT8 * ptr = rcSource;
1620
for( int y = 0; y < _height; ++y )
1623
for( ; x < _width / 8; ++x )
1625
for( int b = 7; b >= 0; --b )
1627
*ptr = rcMask[y * bytesPerRow + x]
1629
ptr += bytesPerPixel;
1632
for( int b = 7; b > 7 - _width % 8; --b )
1634
*ptr = rcMask[y * bytesPerRow + x] >> b & 1;
1635
ptr += bytesPerPixel;
1639
// Convert indices into the actual pixel values.
1640
switch( bytesPerPixel )
1644
for( int x = 0; x < _width * _height; ++x )
1646
rcSource[x] = (Q_UINT8)
1647
colors[rcSource[x]];
1653
for( int x = 0; x < _width * _height; ++x )
1655
((Q_UINT16*) rcSource)[x] =
1656
(Q_UINT16)colors[rcSource[x*2]];
1662
for( int x = 0; x < _width * _height; ++x )
1664
((Q_UINT32 *) rcSource)[x] =
1665
colors[rcSource[x*4]];
1671
else // rich-cursor encoding
1673
if( !readFromServer((char *) rcSource, _width * _height *
1684
if( !readFromServer( (char*) rcMask, bytesMaskData ) )
1691
QImage alpha( _width, _height, QImage::Format_Mono );
1692
// make data 32-bit-aligned for making it usable with QImage
1693
for( Q_UINT16 y = 0; y < _height; ++y )
1695
memcpy( alpha.scanLine( y ), rcMask + bytesPerRow*y,
1700
QRegion ch_reg = QRect( m_cursorPos - m_cursorHotSpot,
1701
m_cursorShape.size() );
1702
m_cursorLock.lockForWrite();
1703
m_cursorShape = QImage( rcSource, _width, _height,
1704
QImage::Format_RGB32 ).
1705
convertToFormat( QImage::Format_ARGB32 );
1706
m_cursorShape.setAlphaChannel( alpha );
1707
m_cursorLock.unlock();
1709
m_cursorHotSpot = QPoint( _xhot, _yhot );
1712
ch_reg += QRect( m_cursorPos - m_cursorHotSpot, m_cursorShape.size() );
1714
// make sure, area around old cursor is updated and new cursor is
1716
postRegionChangedEvent( ch_reg );
1718
emit cursorShapeChanged();
1719
if( m_quality < QualityDemoLow )
1721
emit regionUpdated( ch_reg );
1733
bool ivsConnection::handleItalc( Q_UINT16 rx, Q_UINT16 ry, Q_UINT16 rw,
1736
italcRectEncodingHeader hdr;
1737
if( !readFromServer( (char *) &hdr, sizeof( hdr ) ) )
1742
if( !hdr.compressed )
1744
return( handleRaw( rx, ry, rw, rh ) );
1747
hdr.bytesLZO = swap32IfLE( hdr.bytesLZO );
1748
hdr.bytesRLE = swap32IfLE( hdr.bytesRLE );
1750
Q_UINT8 * lzo_data = new Q_UINT8[hdr.bytesLZO];
1752
if( !readFromServer( (char *) lzo_data, hdr.bytesLZO ) )
1758
Q_UINT8 * rle_data = new Q_UINT8[hdr.bytesRLE];
1760
lzo_uint decomp_bytes = 0;
1761
lzo1x_decompress( (const unsigned char *) lzo_data,
1762
(lzo_uint) hdr.bytesLZO,
1763
(unsigned char *) rle_data,
1764
(lzo_uint *) &decomp_bytes, NULL );
1765
if( decomp_bytes != hdr.bytesRLE )
1767
qCritical( "ivsConnection::handleItalc(...): expected and real "
1768
"size of decompressed data do not match!" );
1772
QRgb * dst = (QRgb *) m_screen.scanLine( ry ) + rx;
1775
const Q_UINT16 sh = m_screen.height();
1776
for( Q_UINT32 i = 0; i < hdr.bytesRLE && done == FALSE; i+=4 )
1778
const QRgb val = swap32IfBE( *( (QRgb*)( rle_data + i ) ) ) &
1780
for( Q_UINT16 j = 0; j <= rle_data[i+3]; ++j )
1789
m_screen.scanLine( ++ry ) + rx;
1806
qWarning( "ivsConnection::handleItalc(...): dx(%d) != 0", dx );
1817
#include "ivs_connection.moc"