1 /****************************************************************************
3 ** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
5 ** This file is part of KDE.
7 ** This program is free software; you can redistribute it and/or modify
8 ** it under the terms of the GNU General Public License as published by
9 ** the Free Software Foundation; either version 2 of the License, or
10 ** (at your option) any later version.
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; see the file COPYING. If not, write to
19 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 ** Boston, MA 02110-1301, USA.
22 ****************************************************************************/
27 #include <QMessageBox>
28 #include <QInputDialog>
29 #define KMessageBox QMessageBox
30 #define error(parent, message, caption) \
31 critical(parent, caption, message)
33 #include <KMessageBox>
34 #include <KPasswordDialog>
37 #include <QApplication>
40 #include <QMouseEvent>
42 VncView::VncView(QWidget
*parent
, const KUrl
&url
)
48 m_firstPasswordTry(true),
49 m_authenticaionCanceled(false),
50 m_dontSendClipboard(false),
51 m_horizontalFactor(1.0),
52 m_verticalFactor(1.0),
53 m_forceLocalCursor(false)
59 connect(&vncThread
, SIGNAL(imageUpdated(int, int, int, int)), this, SLOT(updateImage(int, int, int, int)), Qt::BlockingQueuedConnection
);
60 connect(&vncThread
, SIGNAL(gotCut(const QString
&)), this, SLOT(setCut(const QString
&)), Qt::BlockingQueuedConnection
);
61 connect(&vncThread
, SIGNAL(passwordRequest()), this, SLOT(requestPassword()), Qt::BlockingQueuedConnection
);
62 connect(&vncThread
, SIGNAL(outputErrorMessage(QString
)), this, SLOT(outputErrorMessage(QString
)));
64 m_clipboard
= QApplication::clipboard();
65 connect(m_clipboard
, SIGNAL(selectionChanged()), this, SLOT(clipboardSelectionChanged()));
66 connect(m_clipboard
, SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
74 bool VncView::eventFilter(QObject
*obj
, QEvent
*event
)
77 if (event
->type() == QEvent::KeyPress
||
78 event
->type() == QEvent::KeyRelease
||
79 event
->type() == QEvent::MouseButtonDblClick
||
80 event
->type() == QEvent::MouseButtonPress
||
81 event
->type() == QEvent::MouseButtonRelease
||
82 event
->type() == QEvent::Wheel
||
83 event
->type() == QEvent::MouseMove
)
87 return RemoteView::eventFilter(obj
, event
);
90 QSize
VncView::framebufferSize()
92 return m_frame
.size();
95 QSize
VncView::sizeHint() const
100 QSize
VncView::minimumSizeHint() const
105 void VncView::scaleResize(int w
, int h
)
107 kDebug(5011) << w
<< h
;
112 void VncView::startQuitting()
114 kDebug(5011) << "about to quit";
116 bool connected
= status() == RemoteView::Connected
;
118 setStatus(Disconnecting
);
130 setStatus(Disconnected
);
133 bool VncView::isQuitting()
138 bool VncView::start()
140 vncThread
.setHost(m_host
);
141 vncThread
.setPort(m_port
);
143 int quality
= (QCoreApplication::arguments().count() > 2) ? QCoreApplication::arguments().at(2).toInt() : 2;
144 vncThread
.setQuality((RemoteView::Quality
)quality
);
146 m_hostPreferences
= new VncHostPreferences(m_url
.prettyUrl(KUrl::RemoveTrailingSlash
), false, this);
147 vncThread
.setQuality(m_hostPreferences
->quality());
150 setStatus(Connecting
);
156 bool VncView::supportsScaling() const
161 bool VncView::supportsLocalCursor() const
166 void VncView::requestPassword()
168 kDebug(5011) << "request password";
170 if (m_authenticaionCanceled
) {
175 setStatus(Authenticating
);
178 if (m_hostPreferences
->walletSupport()) {
179 QString walletPassword
= readWalletPassword();
181 if (!walletPassword
.isNull()) {
182 vncThread
.setPassword(walletPassword
);
188 if (!m_url
.password().isNull()) {
189 vncThread
.setPassword(m_url
.password());
195 QString password
= QInputDialog::getText(this, //krazy:exclude=qclasses
196 tr("Password required"),
197 tr("Please enter the password for the remote desktop:"),
198 QLineEdit::Password
, QString(), &ok
);
199 m_firstPasswordTry
= false;
201 vncThread
.setPassword(password
);
203 m_authenticaionCanceled
= true;
205 KPasswordDialog
dialog(this);
206 dialog
.setPrompt(m_firstPasswordTry
? i18n("Access to the system requires a password.")
207 : i18n("Authentication failed. Please try again."));
208 if (dialog
.exec() == KPasswordDialog::Accepted
) {
209 m_firstPasswordTry
= false;
210 vncThread
.setPassword(dialog
.password());
212 kDebug(5011) << "password dialog not accepted";
213 m_authenticaionCanceled
= true;
218 void VncView::outputErrorMessage(const QString
&message
)
220 kDebug(5011) << message
;
222 if (message
== "INTERNAL:APPLE_VNC_COMPATIBILTY") {
223 setCursor(localDotCursor());
224 m_forceLocalCursor
= true;
230 KMessageBox::error(this, message
, i18n("VNC failure"));
233 void VncView::updateImage(int x
, int y
, int w
, int h
)
235 // kDebug(5011) << "got update";
242 m_frame
= vncThread
.image();
245 setAttribute(Qt::WA_StaticContents
);
246 setAttribute(Qt::WA_OpaquePaintEvent
);
247 installEventFilter(this);
249 setCursor(((m_dotCursorState
== CursorOn
) || m_forceLocalCursor
) ? localDotCursor() : Qt::BlankCursor
);
251 setMouseTracking(true); // get mouse events even when there is no mousebutton pressed
252 setFocusPolicy(Qt::WheelFocus
);
253 resize(m_frame
.width(), m_frame
.height());
254 setStatus(Connected
);
255 emit
changeSize(m_frame
.width(), m_frame
.height());
260 if (m_hostPreferences
->walletSupport()) {
261 saveWalletPassword(vncThread
.password());
266 if ((y
== 0 && x
== 0) && (m_frame
.size() != size())) {
267 resize(m_frame
.width(), m_frame
.height());
268 emit
changeSize(m_frame
.width(), m_frame
.height());
272 repaint(qRound(x
* m_horizontalFactor
), qRound(y
* m_verticalFactor
), qRound(w
* m_horizontalFactor
), qRound(h
* m_verticalFactor
));
276 void VncView::setViewOnly(bool viewOnly
)
278 RemoteView::setViewOnly(viewOnly
);
281 setCursor(Qt::ArrowCursor
);
283 setCursor(m_dotCursorState
== CursorOn
? localDotCursor() : Qt::BlankCursor
);
286 void VncView::showDotCursor(DotCursorState state
)
288 RemoteView::showDotCursor(state
);
290 setCursor(state
== CursorOn
? localDotCursor() : Qt::BlankCursor
);
293 void VncView::enableScaling(bool scale
)
295 RemoteView::enableScaling(scale
);
299 resize(parentWidget()->width(), parentWidget()->height());
301 resize(m_frame
.width(), m_frame
.height());
302 emit
changeSize(m_frame
.width(), m_frame
.height());
306 void VncView::setCut(const QString
&text
)
308 m_dontSendClipboard
= true;
309 m_clipboard
->setText(text
, QClipboard::Clipboard
);
310 m_clipboard
->setText(text
, QClipboard::Selection
);
311 m_dontSendClipboard
= false;
314 void VncView::paintEvent(QPaintEvent
*event
)
316 // kDebug(5011) << "paint event: x: " << m_x << ", y: " << m_y << ", w: " << m_w << ", h: " << m_h;
317 if (m_frame
.isNull() || m_frame
.format() == QImage::Format_Invalid
) {
318 kDebug(5011) << "no valid image to paint";
319 RemoteView::paintEvent(event
);
325 QPainter
painter(this);
328 // kDebug(5011) << "normal repaint";
329 painter
.drawImage(QRect(qRound(m_x
*m_horizontalFactor
), qRound(m_y
*m_verticalFactor
),
330 qRound(m_w
*m_horizontalFactor
), qRound(m_h
*m_verticalFactor
)),
331 m_frame
.copy(m_x
, m_y
, m_w
, m_h
).scaled(qRound(m_w
*m_horizontalFactor
),
332 qRound(m_h
*m_verticalFactor
),
333 Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
));
335 // kDebug(5011) << "resize repaint";
336 painter
.drawImage(QRect(0, 0, width(), height()), m_frame
.scaled(width(), height(),
337 Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
));
340 RemoteView::paintEvent(event
);
343 void VncView::resizeEvent(QResizeEvent
*event
)
345 RemoteView::resizeEvent(event
);
347 m_verticalFactor
= (qreal
) height() / m_frame
.height();
348 m_horizontalFactor
= (qreal
) width() / m_frame
.width();
353 void VncView::focusOutEvent(QFocusEvent
*event
)
355 // kDebug(5011) << "focusOutEvent";
357 if (event
->reason() == Qt::TabFocusReason
) {
358 // kDebug(5011) << "event->reason() == Qt::TabFocusReason";
360 setFocus(); // get focus back and send tab key event to remote desktop
361 vncThread
.keyEvent(XK_Tab
, true);
362 vncThread
.keyEvent(XK_Tab
, false);
365 RemoteView::focusOutEvent(event
);
368 void VncView::mouseMoveEvent(QMouseEvent
*event
)
370 // kDebug(5011) << "mouse move";
374 RemoteView::mouseMoveEvent(event
);
377 void VncView::mousePressEvent(QMouseEvent
*event
)
379 // kDebug(5011) << "mouse press";
383 RemoteView::mousePressEvent(event
);
386 void VncView::mouseDoubleClickEvent(QMouseEvent
*event
)
388 // kDebug(5011) << "mouse double click";
392 RemoteView::mouseDoubleClickEvent(event
);
395 void VncView::mouseReleaseEvent(QMouseEvent
*event
)
397 // kDebug(5011) << "mouse release";
401 RemoteView::mouseReleaseEvent(event
);
404 void VncView::mouseEvent(QMouseEvent
*e
)
406 if (e
->type() != QEvent::MouseMove
) {
407 if ((e
->type() == QEvent::MouseButtonPress
) ||
408 (e
->type() == QEvent::MouseButtonDblClick
)) {
409 if (e
->button() & Qt::LeftButton
)
410 m_buttonMask
|= 0x01;
411 if (e
->button() & Qt::MidButton
)
412 m_buttonMask
|= 0x02;
413 if (e
->button() & Qt::RightButton
)
414 m_buttonMask
|= 0x04;
415 } else if (e
->type() == QEvent::MouseButtonRelease
) {
416 if (e
->button() & Qt::LeftButton
)
417 m_buttonMask
&= 0xfe;
418 if (e
->button() & Qt::MidButton
)
419 m_buttonMask
&= 0xfd;
420 if (e
->button() & Qt::RightButton
)
421 m_buttonMask
&= 0xfb;
425 vncThread
.mouseEvent(qRound(e
->x() / m_horizontalFactor
), qRound(e
->y() / m_verticalFactor
), m_buttonMask
);
428 void VncView::wheelEvent(QWheelEvent
*event
)
431 if (event
->delta() < 0)
439 vncThread
.mouseEvent(x
, y
, eb
| m_buttonMask
);
440 vncThread
.mouseEvent(x
, y
, m_buttonMask
);
442 RemoteView::wheelEvent(event
);
445 void VncView::keyEvent(QKeyEvent
*e
)
449 case Qt::Key_Backspace
: k
= XK_BackSpace
; break;
450 case Qt::Key_Tab
: k
= XK_Tab
; break;
451 case Qt::Key_Clear
: k
= XK_Clear
; break;
452 case Qt::Key_Return
: k
= XK_Return
; break;
453 case Qt::Key_Pause
: k
= XK_Pause
; break;
454 case Qt::Key_Escape
: k
= XK_Escape
; break;
455 case Qt::Key_Space
: k
= XK_space
; break;
456 case Qt::Key_Delete
: k
= XK_Delete
; break;
457 case Qt::Key_Enter
: k
= XK_KP_Enter
; break;
458 case Qt::Key_Equal
: k
= XK_KP_Equal
; break;
459 case Qt::Key_Up
: k
= XK_Up
; break;
460 case Qt::Key_Down
: k
= XK_Down
; break;
461 case Qt::Key_Right
: k
= XK_Right
; break;
462 case Qt::Key_Left
: k
= XK_Left
; break;
463 case Qt::Key_Insert
: k
= XK_Insert
; break;
464 case Qt::Key_Home
: k
= XK_Home
; break;
465 case Qt::Key_End
: k
= XK_End
; break;
466 case Qt::Key_PageUp
: k
= XK_Page_Up
; break;
467 case Qt::Key_PageDown
: k
= XK_Page_Down
; break;
468 case Qt::Key_F1
: k
= XK_F1
; break;
469 case Qt::Key_F2
: k
= XK_F2
; break;
470 case Qt::Key_F3
: k
= XK_F3
; break;
471 case Qt::Key_F4
: k
= XK_F4
; break;
472 case Qt::Key_F5
: k
= XK_F5
; break;
473 case Qt::Key_F6
: k
= XK_F6
; break;
474 case Qt::Key_F7
: k
= XK_F7
; break;
475 case Qt::Key_F8
: k
= XK_F8
; break;
476 case Qt::Key_F9
: k
= XK_F9
; break;
477 case Qt::Key_F10
: k
= XK_F10
; break;
478 case Qt::Key_F11
: k
= XK_F11
; break;
479 case Qt::Key_F12
: k
= XK_F12
; break;
480 case Qt::Key_F13
: k
= XK_F13
; break;
481 case Qt::Key_F14
: k
= XK_F14
; break;
482 case Qt::Key_F15
: k
= XK_F15
; break;
483 case Qt::Key_NumLock
: k
= XK_Num_Lock
; break;
484 case Qt::Key_CapsLock
: k
= XK_Caps_Lock
; break;
485 case Qt::Key_ScrollLock
: k
= XK_Scroll_Lock
; break;
486 case Qt::Key_Shift
: k
= XK_Shift_L
; break;
487 case Qt::Key_Control
: k
= XK_Control_L
; break;
488 case Qt::Key_AltGr
: k
= XK_Alt_R
; break;
489 case Qt::Key_Alt
: k
= XK_Alt_L
; break;
490 case Qt::Key_Meta
: k
= XK_Meta_L
; break;
491 case Qt::Key_Mode_switch
: k
= XK_Mode_switch
; break;
492 case Qt::Key_Help
: k
= XK_Help
; break;
493 case Qt::Key_Print
: k
= XK_Print
; break;
494 case Qt::Key_SysReq
: k
= XK_Sys_Req
; break;
499 if (e
->key() < 0x100)
500 k
= QChar(e
->text().at(0)).unicode(); //respect upper- / lowercase
502 rfbClientLog("Unknown keysym: %d\n", e
->key());
505 if (k
< 26) // workaround for modified keys by pressing CTRL
508 vncThread
.keyEvent(k
, (e
->type() == QEvent::KeyPress
) ? true : false);
510 RemoteView::keyEvent(e
);
513 void VncView::keyPressEvent(QKeyEvent
*event
)
515 // kDebug(5011) << "key press" << event->key();
519 RemoteView::keyPressEvent(event
);
522 void VncView::keyReleaseEvent(QKeyEvent
*event
)
524 // kDebug(5011) << "key release" << event->key();
528 RemoteView::keyReleaseEvent(event
);
531 void VncView::clipboardSelectionChanged()
535 if (m_status
!= Connected
)
538 if (m_clipboard
->ownsSelection() || m_dontSendClipboard
)
541 QString text
= m_clipboard
->text(QClipboard::Selection
);
543 vncThread
.clientCut(text
);
546 void VncView::clipboardDataChanged()
550 if (m_status
!= Connected
)
553 if (m_clipboard
->ownsClipboard() || m_dontSendClipboard
)
556 QString text
= m_clipboard
->text(QClipboard::Clipboard
);
558 vncThread
.clientCut(text
);
561 #include "moc_vncview.cpp"