2
* vncview.cpp - VNC-viewer-widget
4
* Copyright (c) 2006-2008 Tobias Doerffel <tobydox/at/users/dot/sf/dot/net>
6
* This file is part of iTALC - http://italc.sourceforge.net
8
* This program is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU General Public
10
* License as published by the Free Software Foundation; either
11
* version 2 of the License, or (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* General Public License for more details.
18
* You should have received a copy of the GNU General Public
19
* License along with this program (see COPYING); if not, write to the
20
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21
* Boston, MA 02111-1307, USA.
27
#include "rfb/keysym.h"
30
#include "ivs_connection.h"
31
#include "messagebox.h"
32
#include "qt_user_events.h"
33
#include "progress_widget.h"
34
#include "system_key_trapper.h"
35
#include "qt_features.h"
36
#include "lock_widget.h"
38
#include <QtCore/QTimer>
39
#include <QtGui/QApplication>
40
#include <QtGui/QMouseEvent>
41
#include <QtGui/QPainter>
45
vncView::vncView( const QString & _host, QWidget * _parent,
46
bool _progress_widget ) :
50
m_viewOnlyFocus( TRUE ),
53
m_viewOffset( QPoint( 0, 0 ) ),
55
m_establishingConnection( NULL ),
56
m_sysKeyTrapper( new systemKeyTrapper( FALSE ) )
58
if( _progress_widget )
60
m_establishingConnection = new progressWidget(
61
tr( "Establishing connection to %1 ..." ).arg( _host ),
62
":/resources/watch%1.png", 16, this );
65
m_connection = new ivsConnection( _host, ivsConnection::QualityMedium,
67
connect( m_connection, SIGNAL( cursorShapeChanged() ),
68
this, SLOT( updateCursorShape() ) );
69
setMouseTracking( TRUE );
70
//setWidgetAttribute( Qt::WA_OpaquePaintEvent );
71
setAttribute( Qt::WA_NoSystemBackground, true );
72
setAttribute( Qt::WA_DeleteOnClose, true );
75
QSize parent_size = size();
76
if( parentWidget() != NULL )
78
parent_size = parentWidget()->size();
80
resize( parent_size );
82
setFocusPolicy( Qt::StrongFocus );
85
new vncViewThread( this );
96
findChild<vncViewThread *>()->quit();
97
findChild<vncViewThread *>()->wait();
99
delete m_sysKeyTrapper;
105
QSize vncView::scaledSize( const QSize & _default ) const
107
const QSize s = size();
108
QSize fbs = m_connection->framebufferSize();
109
if( ( s.width() >= fbs.width() && s.height() >= fbs.height() ) ||
110
m_scaledView == FALSE )
114
fbs.scale( s, Qt::KeepAspectRatio );
121
void vncView::setViewOnly( bool _vo )
123
if( _vo == m_viewOnly )
133
m_sysKeyTrapper->setEnabled( FALSE );
134
setCursor( Qt::ArrowCursor );
139
// for some reason we have to grab mouse and then release
140
// again to make complete keyboard-grabbing working ... ??
145
m_sysKeyTrapper->setEnabled( TRUE );
153
void vncView::setScaledView( bool _sv )
156
if( m_connection != NULL )
158
m_connection->setScaledSize( scaledSize() );
166
void vncView::framebufferUpdate( void )
168
if( m_connection == NULL )
170
QTimer::singleShot( 40, this, SLOT( framebufferUpdate() ) );
174
const QPoint mp = mapFromGlobal( QCursor::pos() );
175
// not yet connected or connection lost while handling messages?
176
if( m_connection->state() != ivsConnection::Connected && m_running )
179
if( m_establishingConnection )
181
m_establishingConnection->show();
183
emit startConnection();
184
QTimer::singleShot( 40, this, SLOT( framebufferUpdate() ) );
187
// special signal for allowing parent-widgets to
188
// show a toolbar etc.
194
if( m_connection->state() == ivsConnection::Connected && !m_running )
196
if( m_establishingConnection )
198
m_establishingConnection->hide();
201
emit connectionEstablished();
203
m_connection->setScaledSize( scaledSize() );
207
// if we have a parent it's likely remoteControlWidget
208
// which needs resize-events for updating its toolbar
210
parentWidget()->resize( parentWidget()->size() );
214
if( m_scaledView == FALSE )
216
// check whether to scroll because mouse-cursor is at an egde which
217
// doesn't correspond to the framebuffer's edge
218
const QPoint old_vo = m_viewOffset;
219
const int MAGIC_MARGIN = 15;
220
if( mp.x() <= MAGIC_MARGIN && m_viewOffset.x() > 0 )
222
m_viewOffset.setX( qMax( 0, m_viewOffset.x() -
223
( MAGIC_MARGIN - mp.x() ) ) );
225
else if( mp.x() > width() - MAGIC_MARGIN && m_viewOffset.x() <=
226
m_connection->framebufferSize().width() -
229
m_viewOffset.setX( qMin( m_viewOffset.x() +
230
( MAGIC_MARGIN + mp.x() - width() ),
231
m_connection->framebufferSize().width() -
235
if( mp.y() <= MAGIC_MARGIN )
237
if( m_viewOffset.y() > 0 )
239
m_viewOffset.setY( qMax( 0, m_viewOffset.y() -
240
( MAGIC_MARGIN - mp.y() ) ) );
242
else if( mp.y() < 2 )
244
// special signal for allowing parent-widgets to
245
// show a toolbar etc.
249
else if( mp.y() > height() - MAGIC_MARGIN && m_viewOffset.y() <=
250
m_connection->framebufferSize().height() -
253
m_viewOffset.setY( qMin( m_viewOffset.y() +
254
( MAGIC_MARGIN + mp.y() - height() ),
255
m_connection->framebufferSize().height() -
259
if( old_vo != m_viewOffset )
264
else if( mp.y() <= 2 )
269
QTimer::singleShot( 40, this, SLOT( framebufferUpdate() ) );
275
void vncView::updateCursorShape( void )
277
if( !viewOnly() && !m_connection->cursorShape().isNull() )
279
setCursor( QCursor( QPixmap::fromImage(
280
m_connection->cursorShape() ),
281
m_connection->cursorHotSpot().x(),
282
m_connection->cursorHotSpot().y() ) );
289
// we have to filter key-events as QWidget's event()-implementation filters out
290
// Tab and Shift-Tab for changing focus but as we need Tab, we have to do
291
// key-event-dispatching on our own
292
bool vncView::event( QEvent * e )
298
case QEvent::KeyPress:
299
case QEvent::KeyRelease:
300
keyEvent( (QKeyEvent *)( e ) );
301
return( e->isAccepted() );
306
return( QWidget::event( e ) );
312
void vncView::focusInEvent( QFocusEvent * _e )
314
if( !m_viewOnlyFocus )
316
setViewOnly( FALSE );
318
QWidget::focusInEvent( _e );
324
void vncView::focusOutEvent( QFocusEvent * _e )
326
m_viewOnlyFocus = viewOnly();
331
QWidget::focusOutEvent( _e );
337
// our builtin keyboard-handler
338
void vncView::keyEvent( QKeyEvent * _ke )
340
bool pressed = _ke->type() == QEvent::KeyPress;
342
#ifdef NATIVE_VIRTUAL_KEY_SUPPORT
343
// the Trolls seem to love us! With Qt 4.2 they introduced this cute
344
// function returning the key-code of the key-event (platform-dependent)
345
// so when operating under Linux/X11, key-codes are equal to the ones
346
// used by VNC-protocol
347
int key = _ke->nativeVirtualKey();
349
// we do not handle Key_Backtab separately as the Shift-modifier
350
// is already enabled
351
if( _ke->key() == Qt::Key_Backtab )
357
// hmm, either Win32-platform or too old Qt so we have to handle and
358
// translate Qt-key-codes to X-keycodes
362
// modifiers are handled separately
363
case Qt::Key_Shift: key = XK_Shift_L; break;
364
case Qt::Key_Control: key = XK_Control_L; break;
365
case Qt::Key_Meta: key = XK_Meta_L; break;
366
case Qt::Key_Alt: key = XK_Alt_L; break;
367
case Qt::Key_Escape: key = XK_Escape; break;
368
case Qt::Key_Tab: key = XK_Tab; break;
369
case Qt::Key_Backtab: key = XK_Tab; break;
370
case Qt::Key_Backspace: key = XK_BackSpace; break;
371
case Qt::Key_Return: key = XK_Return; break;
372
case Qt::Key_Insert: key = XK_Insert; break;
373
case Qt::Key_Delete: key = XK_Delete; break;
374
case Qt::Key_Pause: key = XK_Pause; break;
375
case Qt::Key_Print: key = XK_Print; break;
376
case Qt::Key_Home: key = XK_Home; break;
377
case Qt::Key_End: key = XK_End; break;
378
case Qt::Key_Left: key = XK_Left; break;
379
case Qt::Key_Up: key = XK_Up; break;
380
case Qt::Key_Right: key = XK_Right; break;
381
case Qt::Key_Down: key = XK_Down; break;
382
case Qt::Key_PageUp: key = XK_Prior; break;
383
case Qt::Key_PageDown: key = XK_Next; break;
384
case Qt::Key_CapsLock: key = XK_Caps_Lock; break;
385
case Qt::Key_NumLock: key = XK_Num_Lock; break;
386
case Qt::Key_ScrollLock: key = XK_Scroll_Lock; break;
387
case Qt::Key_Super_L: key = XK_Super_L; break;
388
case Qt::Key_Super_R: key = XK_Super_R; break;
389
case Qt::Key_Menu: key = XK_Menu; break;
390
case Qt::Key_Hyper_L: key = XK_Hyper_L; break;
391
case Qt::Key_Hyper_R: key = XK_Hyper_R; break;
392
case Qt::Key_Help: key = XK_Help; break;
393
case Qt::Key_AltGr: key = XK_ISO_Level3_Shift; break;
394
case Qt::Key_Multi_key: key = XK_Multi_key; break;
395
case Qt::Key_SingleCandidate: key = XK_SingleCandidate; break;
396
case Qt::Key_MultipleCandidate: key = XK_MultipleCandidate; break;
397
case Qt::Key_PreviousCandidate: key = XK_PreviousCandidate; break;
398
case Qt::Key_Mode_switch: key = XK_Mode_switch; break;
399
case Qt::Key_Kanji: key = XK_Kanji; break;
400
case Qt::Key_Muhenkan: key = XK_Muhenkan; break;
401
case Qt::Key_Henkan: key = XK_Henkan; break;
402
case Qt::Key_Romaji: key = XK_Romaji; break;
403
case Qt::Key_Hiragana: key = XK_Hiragana; break;
404
case Qt::Key_Katakana: key = XK_Katakana; break;
405
case Qt::Key_Hiragana_Katakana: key = XK_Hiragana_Katakana; break;
406
case Qt::Key_Zenkaku: key = XK_Zenkaku; break;
407
case Qt::Key_Hankaku: key = XK_Hankaku; break;
408
case Qt::Key_Zenkaku_Hankaku: key = XK_Zenkaku_Hankaku; break;
409
case Qt::Key_Touroku: key = XK_Touroku; break;
410
case Qt::Key_Massyo: key = XK_Massyo; break;
411
case Qt::Key_Kana_Lock: key = XK_Kana_Lock; break;
412
case Qt::Key_Kana_Shift: key = XK_Kana_Shift; break;
413
case Qt::Key_Eisu_Shift: key = XK_Eisu_Shift; break;
414
case Qt::Key_Eisu_toggle: key = XK_Eisu_toggle; break;
415
case Qt::Key_Hangul: key = XK_Hangul; break;
416
case Qt::Key_Hangul_Start: key = XK_Hangul_Start; break;
417
case Qt::Key_Hangul_End: key = XK_Hangul_End; break;
418
case Qt::Key_Hangul_Hanja: key = XK_Hangul_Hanja; break;
419
case Qt::Key_Hangul_Jamo: key = XK_Hangul_Jamo; break;
420
case Qt::Key_Hangul_Romaja: key = XK_Hangul_Romaja; break;
421
case Qt::Key_Hangul_Jeonja: key = XK_Hangul_Jeonja; break;
422
case Qt::Key_Hangul_Banja: key = XK_Hangul_Banja; break;
423
case Qt::Key_Hangul_PreHanja: key = XK_Hangul_PreHanja; break;
424
case Qt::Key_Hangul_PostHanja: key = XK_Hangul_PostHanja; break;
425
case Qt::Key_Hangul_Special: key = XK_Hangul_Special; break;
426
case Qt::Key_Dead_Grave: key = XK_dead_grave; break;
427
case Qt::Key_Dead_Acute: key = XK_dead_acute; break;
428
case Qt::Key_Dead_Circumflex: key = XK_dead_circumflex; break;
429
case Qt::Key_Dead_Tilde: key = XK_dead_tilde; break;
430
case Qt::Key_Dead_Macron: key = XK_dead_macron; break;
431
case Qt::Key_Dead_Breve: key = XK_dead_breve; break;
432
case Qt::Key_Dead_Abovedot: key = XK_dead_abovedot; break;
433
case Qt::Key_Dead_Diaeresis: key = XK_dead_diaeresis; break;
434
case Qt::Key_Dead_Abovering: key = XK_dead_abovering; break;
435
case Qt::Key_Dead_Doubleacute: key = XK_dead_doubleacute; break;
436
case Qt::Key_Dead_Caron: key = XK_dead_caron; break;
437
case Qt::Key_Dead_Cedilla: key = XK_dead_cedilla; break;
438
case Qt::Key_Dead_Ogonek: key = XK_dead_ogonek; break;
439
case Qt::Key_Dead_Iota: key = XK_dead_iota; break;
440
case Qt::Key_Dead_Voiced_Sound: key = XK_dead_voiced_sound; break;
441
case Qt::Key_Dead_Semivoiced_Sound: key = XK_dead_semivoiced_sound; break;
442
case Qt::Key_Dead_Belowdot: key = XK_dead_belowdot; break;
445
if( _ke->key() >= Qt::Key_F1 && _ke->key() <= Qt::Key_F35 )
447
key = XK_F1 + _ke->key() - Qt::Key_F1;
451
if( m_mods.contains( XK_Control_L ) &&
452
QKeySequence( _ke->key() ).toString().length() == 1 )
454
QString s = QKeySequence( _ke->key() ).toString();
455
if( !m_mods.contains( XK_Shift_L ) )
463
key = _ke->text().utf16()[0];
467
// correct translation of AltGr+<character key> (non-US-keyboard layout
468
// such as German keyboard layout)
469
if( m_mods.contains( XK_Alt_L ) && m_mods.contains( XK_Control_L ) &&
470
key >= 64 && key < 0xF000 )
473
emit keyEvent( XK_ISO_Level3_Shift, TRUE );
478
if( key == XK_Shift_L || key == XK_Control_L || key == XK_Meta_L ||
485
else if( m_mods.contains( key ) )
487
m_mods.remove( key );
497
emit keyEvent( key, pressed );
505
void vncView::unpressModifiers( void )
507
QList<unsigned int> keys = m_mods.keys();
508
QList<unsigned int>::const_iterator it = keys.begin();
509
while( it != keys.end() )
511
emit keyEvent( *it, FALSE );
520
QPoint vncView::mapToFramebuffer( const QPoint & _pos )
522
const QSize fbs = m_connection ? m_connection->framebufferSize() :
524
const int x = m_scaledView && fbs.isValid() ?
525
_pos.x() * fbs.width() / scaledSize( fbs ).width()
527
_pos.x() + m_viewOffset.x();
528
const int y = m_scaledView && m_connection ?
529
_pos.y() * fbs.height() / scaledSize( fbs ).height()
531
_pos.y() + m_viewOffset.y();
532
return( QPoint( x, y ) );
538
QRect vncView::mapFromFramebuffer( const QRect & _r )
540
if( m_scaledView && m_connection )
542
const float dx = width() / (float)
543
m_connection->framebufferSize().width();
544
const float dy = height() / (float)
545
m_connection->framebufferSize().height();
546
return( QRect( (int)(_r.x()*dx), (int)(_r.y()*dy),
547
(int)(_r.width()*dx), (int)(_r.height()*dy) ) );
549
return( _r.translated( -m_viewOffset ) );
555
void vncView::mouseMoveEvent( QMouseEvent * _me )
564
void vncView::mousePressEvent( QMouseEvent * _me )
573
void vncView::mouseReleaseEvent( QMouseEvent * _me )
582
void vncView::mouseDoubleClickEvent( QMouseEvent * _me )
592
void vncView::paintEvent( QPaintEvent * _pe )
596
// avoid nasty through-shining-window-effect when not connected yet
597
if( m_connection->screen().isNull() )
599
p.fillRect( _pe->rect(), Qt::black );
603
const QSize ss = scaledSize();
605
// only paint requested region of image
606
p.drawImage( _pe->rect().topLeft(),
608
m_connection->scaledScreen() :
609
m_connection->screen(),
610
_pe->rect().translated( m_viewOffset ),
611
Qt::ThresholdDither );
613
if( viewOnly() && !m_connection->cursorShape().isNull() )
615
const QImage & cursor = m_connection->cursorShape();
616
const QRect cursor_rect = mapFromFramebuffer(
617
QRect( m_connection->cursorPos() -
618
m_connection->cursorHotSpot(),
620
// parts of cursor within updated region?
621
if( _pe->rect().intersects( cursor_rect ) )
624
p.drawImage( cursor_rect.topLeft(), cursor );
628
// draw black borders if neccessary
629
const int fbw = ss.isValid() ? ss.width() :
630
m_connection->framebufferSize().width();
633
p.fillRect( fbw, 0, width() - fbw, height(), Qt::black );
635
const int fbh = ss.isValid() ? ss.height() :
636
m_connection->framebufferSize().height();
639
p.fillRect( 0, fbh, fbw, height() - fbh, Qt::black );
646
void vncView::resizeEvent( QResizeEvent * _re )
648
m_connection->setScaledSize( scaledSize() );
649
const int max_x = m_connection->framebufferSize().width() - width();
650
const int max_y = m_connection->framebufferSize().height() - height();
651
if( m_viewOffset.x() > max_x || m_viewOffset.y() > max_y )
653
m_viewOffset = QPoint(
654
qMax( 0, qMin( m_viewOffset.x(), max_x ) ),
655
qMax( 0, qMin( m_viewOffset.y(), max_y ) ) );
659
if( m_establishingConnection )
661
m_establishingConnection->move( 10, 10 );
664
QWidget::resizeEvent( _re );
670
void vncView::wheelEvent( QWheelEvent * _we )
672
const QPoint p = mapToFramebuffer( _we->pos() );
673
emit pointerEvent( p.x(), p.y(), m_buttonMask |
674
( ( _we->delta() < 0 ) ? rfbButton5Mask : rfbButton4Mask ) );
675
emit pointerEvent( p.x(), p.y(), m_buttonMask );
683
void vncView::customEvent( QEvent * _e )
685
if( _e->type() == regionChangedEvent().type() )
692
QWidget::customEvent( _e );
699
void vncView::mouseEvent( QMouseEvent * _me )
707
{ Qt::LeftButton, rfbButton1Mask },
708
{ Qt::MidButton, rfbButton2Mask },
709
{ Qt::RightButton, rfbButton3Mask }
712
if( _me->type() != QEvent::MouseMove )
714
for( Q_UINT8 i = 0; i < sizeof(map)/sizeof(buttonXlate); ++i )
716
if( _me->button() == map[i].qt )
718
if( _me->type() == QEvent::MouseButtonPress ||
719
_me->type() == QEvent::MouseButtonDblClick )
721
m_buttonMask |= map[i].rfb;
725
m_buttonMask &= ~map[i].rfb;
731
const QPoint p = mapToFramebuffer( _me->pos() );
732
emit pointerEvent( p.x(), p.y(), m_buttonMask );
742
vncViewThread::vncViewThread( vncView * _vv ) :
746
start( QThread::HighPriority );
752
void vncViewThread::run( void )
754
vncWorker vw( m_vncView );
763
vncWorker::vncWorker( vncView * _vv ) :
767
qRegisterMetaType<Q_UINT16>( "Q_UINT16" );
768
qRegisterMetaType<Q_UINT32>( "Q_UINT32" );
769
connect( m_vncView->m_sysKeyTrapper,
770
SIGNAL( keyEvent( Q_UINT32, bool ) ),
771
this, SLOT( sendKeyEvent( Q_UINT32, bool ) ) );
774
SIGNAL( pointerEvent( Q_UINT16, Q_UINT16, Q_UINT16 ) ),
775
this, SLOT( sendPointerEvent( Q_UINT16, Q_UINT16,
777
connect( m_vncView, SIGNAL( keyEvent( Q_UINT32, bool ) ),
778
this, SLOT( sendKeyEvent( Q_UINT32, bool ) ) );
781
ivsConnection * conn = m_vncView->m_connection;
782
QTimer * t = new QTimer( this );
783
connect( t, SIGNAL( timeout() ), conn,
784
SLOT( sendIncrementalFramebufferUpdateRequest() ),
785
Qt::DirectConnection );
788
t = new QTimer( this );
789
connect( t, SIGNAL( timeout() ), this,
790
SLOT( framebufferUpdate() ) );
797
vncWorker::~vncWorker()
799
// have to close the connection here for destroying/closing everything
800
// within the same thread - otherwise Qt might complain or even let us
802
m_vncView->m_connection->close();
808
void vncWorker::framebufferUpdate( void )
810
ivsConnection * conn = m_vncView->m_connection;
811
if( conn->state() != ivsConnection::Connected ||
812
!conn->handleServerMessages( FALSE, 3 ) )
821
void vncWorker::sendPointerEvent( Q_UINT16 _x, Q_UINT16 _y,
822
Q_UINT16 _button_mask )
824
if( !m_vncView->viewOnly() )
826
m_vncView->m_connection->sendPointerEvent( _x, _y,
834
void vncWorker::sendKeyEvent( Q_UINT32 _key, bool _down )
836
if( !m_vncView->viewOnly() )
838
m_vncView->m_connection->sendKeyEvent( _key, _down );
845
#include "vncview.moc"