Prevent painting an invalid image. Should hopefully fix a crash with drawImage().
[kdenetwork.git] / krdc / vnc / vncview.cpp
blobfc378d75649d1e785a33e3e73ec1d18e8797adda
1 /****************************************************************************
2 **
3 ** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
4 **
5 ** This file is part of KDE.
6 **
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 ****************************************************************************/
24 #include "vncview.h"
26 #ifdef QTONLY
27 #include <QMessageBox>
28 #include <QInputDialog>
29 #define KMessageBox QMessageBox
30 #define error(parent, message, caption) \
31 critical(parent, caption, message)
32 #else
33 #include <KMessageBox>
34 #include <KPasswordDialog>
35 #endif
37 #include <QApplication>
38 #include <QImage>
39 #include <QPainter>
40 #include <QMouseEvent>
42 VncView::VncView(QWidget *parent, const KUrl &url)
43 : RemoteView(parent),
44 m_initDone(false),
45 m_buttonMask(0),
46 m_repaint(false),
47 m_quitFlag(false),
48 m_firstPasswordTry(true),
49 m_dontSendClipboard(false),
50 m_horizontalFactor(1.0),
51 m_verticalFactor(1.0)
53 m_url = url;
54 m_host = url.host();
55 m_port = url.port();
57 connect(&vncThread, SIGNAL(imageUpdated(int, int, int, int)), this, SLOT(updateImage(int, int, int, int)), Qt::BlockingQueuedConnection);
58 connect(&vncThread, SIGNAL(gotCut(const QString&)), this, SLOT(setCut(const QString&)), Qt::BlockingQueuedConnection);
59 connect(&vncThread, SIGNAL(passwordRequest()), this, SLOT(requestPassword()), Qt::BlockingQueuedConnection);
60 connect(&vncThread, SIGNAL(outputErrorMessage(QString)), this, SLOT(outputErrorMessage(QString)));
62 m_clipboard = QApplication::clipboard();
63 connect(m_clipboard, SIGNAL(selectionChanged()), this, SLOT(clipboardSelectionChanged()));
64 connect(m_clipboard, SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
67 VncView::~VncView()
69 startQuitting();
72 bool VncView::eventFilter(QObject *obj, QEvent *event)
74 if (m_viewOnly) {
75 if (event->type() == QEvent::KeyPress ||
76 event->type() == QEvent::KeyRelease ||
77 event->type() == QEvent::MouseButtonDblClick ||
78 event->type() == QEvent::MouseButtonPress ||
79 event->type() == QEvent::MouseButtonRelease ||
80 event->type() == QEvent::Wheel ||
81 event->type() == QEvent::MouseMove)
82 return true;
85 return RemoteView::eventFilter(obj, event);
88 QSize VncView::framebufferSize()
90 return size();
93 QSize VncView::sizeHint() const
95 return size();
98 QSize VncView::minimumSizeHint() const
100 return size();
103 void VncView::scaleResize(int w, int h)
105 kDebug(5011) << w << h;
106 if (m_scale)
107 resize(w, h);
110 void VncView::startQuitting()
112 kDebug(5011) << "about to quit";
114 bool connected = status() == RemoteView::Connected;
116 setStatus(Disconnecting);
118 m_quitFlag = true;
120 if (connected) {
121 vncThread.stop();
122 } else {
123 vncThread.quit();
126 vncThread.wait(500);
128 setStatus(Disconnected);
131 bool VncView::isQuitting()
133 return m_quitFlag;
136 bool VncView::start()
138 vncThread.setHost(m_host);
139 vncThread.setPort(m_port);
140 #ifdef QTONLY
141 int quality = (QCoreApplication::arguments().count() > 2) ? QCoreApplication::arguments().at(2).toInt() : 2;
142 vncThread.setQuality((RemoteView::Quality)quality);
143 #else
144 m_hostPreferences = new VncHostPreferences(m_url.prettyUrl(KUrl::RemoveTrailingSlash), false, this);
145 vncThread.setQuality(m_hostPreferences->quality());
146 #endif
148 setStatus(Connecting);
150 vncThread.start();
151 return true;
154 bool VncView::supportsScaling() const
156 return true;
159 bool VncView::supportsLocalCursor() const
161 return true;
164 void VncView::requestPassword()
166 kDebug(5011) << "request password";
168 setStatus(Authenticating);
170 #ifndef QTONLY
171 if (m_hostPreferences->walletSupport()) {
172 QString walletPassword = readWalletPassword();
174 if (!walletPassword.isNull()) {
175 vncThread.setPassword(walletPassword);
176 return;
179 #endif
181 if (!m_url.password().isNull()) {
182 vncThread.setPassword(m_url.password());
183 return;
186 #ifdef QTONLY
187 QString password = QInputDialog::getText(this, //krazy:exclude=qclasses
188 tr("Password required"),
189 tr("Please enter the password for the remote desktop:"),
190 QLineEdit::Password);
191 vncThread.setPassword(password);
192 #else
193 KPasswordDialog dialog(this);
194 dialog.setPixmap(KIcon("dialog-password").pixmap(48));
195 dialog.setPrompt(m_firstPasswordTry ? i18n("Access to the system requires a password.")
196 : i18n("Authentication failed. Please try again."));
197 if (dialog.exec() == KPasswordDialog::Accepted) {
198 m_firstPasswordTry = false;
199 vncThread.setPassword(dialog.password());
201 #endif
204 void VncView::outputErrorMessage(const QString &message)
206 kDebug(5011) << message;
208 startQuitting();
210 KMessageBox::error(this, message, i18n("VNC failure"));
213 void VncView::updateImage(int x, int y, int w, int h)
215 // kDebug(5011) << "got update";
217 m_x = x;
218 m_y = y;
219 m_w = w;
220 m_h = h;
222 m_frame = vncThread.image();
224 if (!m_initDone) {
225 setAttribute(Qt::WA_StaticContents);
226 setAttribute(Qt::WA_OpaquePaintEvent);
227 installEventFilter(this);
229 setCursor(m_dotCursorState == CursorOn ? localDotCursor() : Qt::BlankCursor);
231 setMouseTracking(true); // get mouse events even when there is no mousebutton pressed
232 setFocusPolicy(Qt::WheelFocus);
233 resize(m_frame.width(), m_frame.height());
234 setStatus(Connected);
235 emit changeSize(m_frame.width(), m_frame.height());
236 emit connected();
237 m_initDone = true;
239 #ifndef QTONLY
240 if (m_hostPreferences->walletSupport()) {
241 saveWalletPassword(vncThread.password());
243 #endif
246 if ((y == 0 && x == 0) && (m_frame.size() != size())) {
247 resize(m_frame.width(), m_frame.height());
248 emit changeSize(m_frame.width(), m_frame.height());
251 m_repaint = true;
252 repaint(qRound(x * m_horizontalFactor), qRound(y * m_verticalFactor), qRound(w * m_horizontalFactor), qRound(h * m_verticalFactor));
253 m_repaint = false;
256 void VncView::setViewOnly(bool viewOnly)
258 RemoteView::setViewOnly(viewOnly);
260 if (viewOnly)
261 setCursor(Qt::ArrowCursor);
262 else
263 setCursor(m_dotCursorState == CursorOn ? localDotCursor() : Qt::BlankCursor);
266 void VncView::showDotCursor(DotCursorState state)
268 RemoteView::showDotCursor(state);
270 setCursor(state == CursorOn ? localDotCursor() : Qt::BlankCursor);
273 void VncView::enableScaling(bool scale)
275 RemoteView::enableScaling(scale);
277 if (scale) {
278 if (parentWidget())
279 resize(parentWidget()->width(), parentWidget()->height());
280 } else {
281 resize(m_frame.width(), m_frame.height());
282 emit changeSize(m_frame.width(), m_frame.height());
286 void VncView::setCut(const QString &text)
288 m_dontSendClipboard = true;
289 m_clipboard->setText(text, QClipboard::Clipboard);
290 m_clipboard->setText(text, QClipboard::Selection);
291 m_dontSendClipboard = false;
294 void VncView::paintEvent(QPaintEvent *event)
296 // kDebug(5011) << "paint event: x: " << m_x << ", y: " << m_y << ", w: " << m_w << ", h: " << m_h;
297 if (m_frame.isNull() || m_frame.format() == QImage::Format_Invalid) {
298 kDebug(5011) << "no valid image to paint";
299 RemoteView::paintEvent(event);
300 return;
303 event->accept();
305 QPainter painter(this);
307 if (m_repaint) {
308 // kDebug(5011) << "normal repaint";
309 painter.drawImage(QRect(qRound(m_x*m_horizontalFactor), qRound(m_y*m_verticalFactor),
310 qRound(m_w*m_horizontalFactor), qRound(m_h*m_verticalFactor)),
311 m_frame.copy(m_x, m_y, m_w, m_h).scaled(qRound(m_w*m_horizontalFactor),
312 qRound(m_h*m_verticalFactor),
313 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
314 } else {
315 // kDebug(5011) << "resize repaint";
316 painter.drawImage(QRect(0, 0, width(), height()), m_frame.scaled(width(), height(),
317 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
320 RemoteView::paintEvent(event);
323 void VncView::resizeEvent(QResizeEvent *event)
325 RemoteView::resizeEvent(event);
327 m_verticalFactor = (qreal) height() / m_frame.height();
328 m_horizontalFactor = (qreal) width() / m_frame.width();
330 update();
333 void VncView::focusOutEvent(QFocusEvent *event)
335 // kDebug(5011) << "focusOutEvent";
337 if (event->reason() == Qt::TabFocusReason) {
338 // kDebug(5011) << "event->reason() == Qt::TabFocusReason";
339 event->ignore();
340 setFocus(); // get focus back and send tab key event to remote desktop
341 vncThread.keyEvent(XK_Tab, true);
342 vncThread.keyEvent(XK_Tab, false);
345 RemoteView::focusOutEvent(event);
348 void VncView::mouseMoveEvent(QMouseEvent *event)
350 // kDebug(5011) << "mouse move";
352 mouseEvent(event);
354 RemoteView::mouseMoveEvent(event);
357 void VncView::mousePressEvent(QMouseEvent *event)
359 // kDebug(5011) << "mouse press";
361 mouseEvent(event);
363 RemoteView::mousePressEvent(event);
366 void VncView::mouseDoubleClickEvent(QMouseEvent *event)
368 // kDebug(5011) << "mouse double click";
370 mouseEvent(event);
372 RemoteView::mouseDoubleClickEvent(event);
375 void VncView::mouseReleaseEvent(QMouseEvent *event)
377 // kDebug(5011) << "mouse release";
379 mouseEvent(event);
381 RemoteView::mouseReleaseEvent(event);
384 void VncView::mouseEvent(QMouseEvent *e)
386 if (e->type() != QEvent::MouseMove) {
387 if ((e->type() == QEvent::MouseButtonPress) ||
388 (e->type() == QEvent::MouseButtonDblClick)) {
389 if (e->button() & Qt::LeftButton)
390 m_buttonMask |= 0x01;
391 if (e->button() & Qt::MidButton)
392 m_buttonMask |= 0x02;
393 if (e->button() & Qt::RightButton)
394 m_buttonMask |= 0x04;
395 } else if (e->type() == QEvent::MouseButtonRelease) {
396 if (e->button() & Qt::LeftButton)
397 m_buttonMask &= 0xfe;
398 if (e->button() & Qt::MidButton)
399 m_buttonMask &= 0xfd;
400 if (e->button() & Qt::RightButton)
401 m_buttonMask &= 0xfb;
405 vncThread.mouseEvent(qRound(e->x() / m_horizontalFactor), qRound(e->y() / m_verticalFactor), m_buttonMask);
408 void VncView::wheelEvent(QWheelEvent *event)
410 int eb = 0;
411 if (event->delta() < 0)
412 eb |= 0x10;
413 else
414 eb |= 0x8;
416 int x = event->x();
417 int y = event->y();
419 vncThread.mouseEvent(x, y, eb | m_buttonMask);
420 vncThread.mouseEvent(x, y, m_buttonMask);
422 RemoteView::wheelEvent(event);
425 void VncView::keyEvent(QKeyEvent *e)
427 rfbKeySym k = 0;
428 switch (e->key()) {
429 case Qt::Key_Backspace: k = XK_BackSpace; break;
430 case Qt::Key_Tab: k = XK_Tab; break;
431 case Qt::Key_Clear: k = XK_Clear; break;
432 case Qt::Key_Return: k = XK_Return; break;
433 case Qt::Key_Pause: k = XK_Pause; break;
434 case Qt::Key_Escape: k = XK_Escape; break;
435 case Qt::Key_Space: k = XK_space; break;
436 case Qt::Key_Delete: k = XK_Delete; break;
437 case Qt::Key_Enter: k = XK_KP_Enter; break;
438 case Qt::Key_Equal: k = XK_KP_Equal; break;
439 case Qt::Key_Up: k = XK_Up; break;
440 case Qt::Key_Down: k = XK_Down; break;
441 case Qt::Key_Right: k = XK_Right; break;
442 case Qt::Key_Left: k = XK_Left; break;
443 case Qt::Key_Insert: k = XK_Insert; break;
444 case Qt::Key_Home: k = XK_Home; break;
445 case Qt::Key_End: k = XK_End; break;
446 case Qt::Key_PageUp: k = XK_Page_Up; break;
447 case Qt::Key_PageDown: k = XK_Page_Down; break;
448 case Qt::Key_F1: k = XK_F1; break;
449 case Qt::Key_F2: k = XK_F2; break;
450 case Qt::Key_F3: k = XK_F3; break;
451 case Qt::Key_F4: k = XK_F4; break;
452 case Qt::Key_F5: k = XK_F5; break;
453 case Qt::Key_F6: k = XK_F6; break;
454 case Qt::Key_F7: k = XK_F7; break;
455 case Qt::Key_F8: k = XK_F8; break;
456 case Qt::Key_F9: k = XK_F9; break;
457 case Qt::Key_F10: k = XK_F10; break;
458 case Qt::Key_F11: k = XK_F11; break;
459 case Qt::Key_F12: k = XK_F12; break;
460 case Qt::Key_F13: k = XK_F13; break;
461 case Qt::Key_F14: k = XK_F14; break;
462 case Qt::Key_F15: k = XK_F15; break;
463 case Qt::Key_NumLock: k = XK_Num_Lock; break;
464 case Qt::Key_CapsLock: k = XK_Caps_Lock; break;
465 case Qt::Key_ScrollLock: k = XK_Scroll_Lock; break;
466 case Qt::Key_Shift: k = XK_Shift_L; break;
467 case Qt::Key_Control: k = XK_Control_L; break;
468 case Qt::Key_AltGr: k = XK_Alt_R; break;
469 case Qt::Key_Alt: k = XK_Alt_L; break;
470 case Qt::Key_Meta: k = XK_Meta_L; break;
471 case Qt::Key_Mode_switch: k = XK_Mode_switch; break;
472 case Qt::Key_Help: k = XK_Help; break;
473 case Qt::Key_Print: k = XK_Print; break;
474 case Qt::Key_SysReq: k = XK_Sys_Req; break;
475 default: break;
478 if (k == 0) {
479 if (e->key() < 0x100)
480 k = QChar(e->text().at(0)).unicode(); //respect upper- / lowercase
481 else
482 rfbClientLog("Unknown keysym: %d\n", e->key());
485 if (k < 26) // workaround for modified keys by pressing CTRL
486 k += 96;
488 vncThread.keyEvent(k, (e->type() == QEvent::KeyPress) ? true : false);
490 RemoteView::keyEvent(e);
493 void VncView::keyPressEvent(QKeyEvent *event)
495 // kDebug(5011) << "key press" << event->key();
497 keyEvent(event);
499 RemoteView::keyPressEvent(event);
502 void VncView::keyReleaseEvent(QKeyEvent *event)
504 // kDebug(5011) << "key release" << event->key();
506 keyEvent(event);
508 RemoteView::keyReleaseEvent(event);
511 void VncView::clipboardSelectionChanged()
513 kDebug(5011);
515 if (m_status != Connected)
516 return;
518 if (m_clipboard->ownsSelection() || m_dontSendClipboard)
519 return;
521 QString text = m_clipboard->text(QClipboard::Selection);
523 vncThread.clientCut(text);
526 void VncView::clipboardDataChanged()
528 kDebug(5011);
530 if (m_status != Connected)
531 return;
533 if (m_clipboard->ownsClipboard() || m_dontSendClipboard)
534 return;
536 QString text = m_clipboard->text(QClipboard::Clipboard);
538 vncThread.clientCut(text);
541 #include "moc_vncview.cpp"