Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Palacios GUI Added
[palacios.git] / linux_usr / gui / palacios / vnc_module / vncview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2007-2012 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.
11 **
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.
16 **
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.
21 **
22 ****************************************************************************/
23
24 #include "vncview.h"
25
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 "settings.h"
34     #include <KActionCollection>
35     #include <KMainWindow>
36     #include <KMessageBox>
37     #include <KPasswordDialog>
38     #include <KXMLGUIClient>
39 #endif
40
41 #include <QApplication>
42 #include <QImage>
43 #include <QPainter>
44 #include <QMouseEvent>
45
46 // Definition of key modifier mask constants
47 #define KMOD_Alt_R      0x01
48 #define KMOD_Alt_L      0x02
49 #define KMOD_Meta_L     0x04
50 #define KMOD_Control_L  0x08
51 #define KMOD_Shift_L    0x10
52
53 VncView::VncView(QWidget *parent, const KUrl &url, KConfigGroup configGroup)
54         : RemoteView(parent),
55         m_initDone(false),
56         m_buttonMask(0),
57         m_repaint(false),
58         m_quitFlag(false),
59         m_firstPasswordTry(true),
60         m_dontSendClipboard(false),
61         m_horizontalFactor(1.0),
62         m_verticalFactor(1.0),
63         m_forceLocalCursor(false)
64 {
65     m_url = url;
66     m_host = url.host();
67     m_port = url.port();
68
69     connect(&vncThread, SIGNAL(imageUpdated(int,int,int,int)), this, SLOT(updateImage(int,int,int,int)), Qt::BlockingQueuedConnection);
70     connect(&vncThread, SIGNAL(gotCut(QString)), this, SLOT(setCut(QString)), Qt::BlockingQueuedConnection);
71     connect(&vncThread, SIGNAL(passwordRequest()), this, SLOT(requestPassword()), Qt::BlockingQueuedConnection);
72     connect(&vncThread, SIGNAL(outputErrorMessage(QString)), this, SLOT(outputErrorMessage(QString)));
73
74     m_clipboard = QApplication::clipboard();
75     connect(m_clipboard, SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
76     
77 #ifndef QTONLY
78     m_hostPreferences = new VncHostPreferences(configGroup, this);
79 #else
80     Q_UNUSED(configGroup);
81 #endif
82 }
83
84 VncView::~VncView()
85 {   
86     startQuitting();
87 }
88
89 bool VncView::eventFilter(QObject *obj, QEvent *event)
90 {
91     if (m_viewOnly) {
92         if (event->type() == QEvent::KeyPress ||
93                 event->type() == QEvent::KeyRelease ||
94                 event->type() == QEvent::MouseButtonDblClick ||
95                 event->type() == QEvent::MouseButtonPress ||
96                 event->type() == QEvent::MouseButtonRelease ||
97                 event->type() == QEvent::Wheel ||
98                 event->type() == QEvent::MouseMove)
99             return true;
100     }
101
102     return RemoteView::eventFilter(obj, event);
103 }
104
105 QSize VncView::framebufferSize()
106 {
107     return m_frame.size();
108 }
109
110 QSize VncView::sizeHint() const
111 {
112     return size();
113 }
114
115 QSize VncView::minimumSizeHint() const
116 {
117     return size();
118 }
119
120 void VncView::scaleResize(int w, int h)
121 {
122     RemoteView::scaleResize(w, h);
123     
124     kDebug(5011) << w << h;
125     if (m_scale) {
126         m_verticalFactor = (qreal) h / m_frame.height();
127         m_horizontalFactor = (qreal) w / m_frame.width();
128
129 #ifndef QTONLY
130         if (Settings::keepAspectRatio()) {
131             m_verticalFactor = m_horizontalFactor = qMin(m_verticalFactor, m_horizontalFactor);
132         }
133 #else
134         m_verticalFactor = m_horizontalFactor = qMin(m_verticalFactor, m_horizontalFactor);
135 #endif
136
137         const qreal newW = m_frame.width() * m_horizontalFactor;
138         const qreal newH = m_frame.height() * m_verticalFactor;
139         setMaximumSize(newW, newH); //This is a hack to force Qt to center the view in the scroll area
140         resize(newW, newH);
141     } 
142 }
143
144 void VncView::updateConfiguration()
145 {
146     RemoteView::updateConfiguration();
147
148     // Update the scaling mode in case KeepAspectRatio changed
149     scaleResize(parentWidget()->width(), parentWidget()->height());
150 }
151
152 void VncView::startQuitting()
153 {
154     kDebug(5011) << "about to quit";
155
156     setStatus(Disconnecting);
157
158     m_quitFlag = true;
159
160     vncThread.stop();
161
162     unpressModifiers();
163
164     // Disconnect all signals so that we don't get any more callbacks from the client thread
165     bool imageUpdatedDisconnect = disconnect(&vncThread, SIGNAL(imageUpdated(int,int,int,int)), this, SLOT(updateImage(int,int,int,int)));
166     bool gotCutDisconnect = disconnect(&vncThread, SIGNAL(gotCut(QString)), this, SLOT(setCut(QString)));
167     bool passwordRequestDisconnect = disconnect(&vncThread, SIGNAL(passwordRequest()), this, SLOT(requestPassword()));
168     bool outputErrorMessageDisconnect = disconnect(&vncThread, SIGNAL(outputErrorMessage(QString)), this, SLOT(outputErrorMessage(QString)));
169
170     kDebug(5011) << "Signals disconnected: imageUpdated: " << imageUpdatedDisconnect << "gotCut: " << gotCutDisconnect << "passwordRequest: " << passwordRequestDisconnect << "outputErrorMessage: " << outputErrorMessageDisconnect;
171
172     vncThread.quit();
173
174     const bool quitSuccess = vncThread.wait(1000);
175
176     kDebug(5011) << "Quit VNC thread success:" << quitSuccess;
177
178     setStatus(Disconnected);
179 }
180
181 bool VncView::isQuitting()
182 {
183     return m_quitFlag;
184 }
185
186 bool VncView::start()
187 {
188     vncThread.setHost(m_host);
189     vncThread.setPort(m_port);
190     RemoteView::Quality quality;
191 #ifdef QTONLY
192     quality = (RemoteView::Quality)((QCoreApplication::arguments().count() > 2) ?
193         QCoreApplication::arguments().at(2).toInt() : 2);
194 #else
195     quality = m_hostPreferences->quality();
196 #endif
197
198     vncThread.setQuality(quality);
199
200     kDebug(5011) << "Host: " << m_host;
201     kDebug(5011) << "Port: " << m_port;
202     kDebug(5011) << "Quality: " << quality;
203
204     // set local cursor on by default because low quality mostly means slow internet connection
205     if (quality == RemoteView::Low) {
206         showDotCursor(RemoteView::CursorOn);
207 #ifndef QTONLY
208         // KRDC does always just have one main window, so at(0) is safe
209         KXMLGUIClient *mainWindow = dynamic_cast<KXMLGUIClient*>(KMainWindow::memberList().at(0));
210         if (mainWindow)
211             mainWindow->actionCollection()->action("show_local_cursor")->setChecked(true);
212 #endif
213     }
214
215     setStatus(Connecting);
216
217     vncThread.start();
218     return true;
219 }
220
221 bool VncView::supportsScaling() const
222 {
223     return true;
224 }
225
226 bool VncView::supportsLocalCursor() const
227 {
228     return true;
229 }
230
231 void VncView::requestPassword()
232 {
233     kDebug(5011) << "request password";
234
235     setStatus(Authenticating);
236     
237     kDebug(5011) << "After setting authentication state";
238 /*#ifndef QTONLY
239     // just try to get the passwort from the wallet the first time, otherwise it will loop (see issue #226283)
240     if (m_firstPasswordTry && m_hostPreferences->walletSupport()) {
241         QString walletPassword = readWalletPassword();
242
243         if (!walletPassword.isNull()) {
244             vncThread.setPassword(walletPassword);
245             m_firstPasswordTry = false;
246             return;
247         }
248     }
249 #endif
250
251     if (m_firstPasswordTry && !m_url.password().isNull()) {
252         vncThread.setPassword(m_url.password());
253         m_firstPasswordTry = false;
254         return;
255     }*/
256
257 #ifdef QTONLY
258     bool ok = true;
259     QString password = QInputDialog::getText(this, //krazy:exclude=qclasses
260                                              tr("Password required"),
261                                              tr("Please enter the password for the remote desktop:"),
262                                              QLineEdit::Password, QString(), &ok);
263     m_firstPasswordTry = false;
264     if (ok)
265         vncThread.setPassword(password);
266     else
267         startQuitting();
268 #else
269     KPasswordDialog dialog(this);
270     dialog.setPrompt(m_firstPasswordTry ? i18n("Access to the system requires a password.")
271                                         : i18n("Authentication failed. Please try again."));
272     if (dialog.exec() == KPasswordDialog::Accepted) {
273         m_firstPasswordTry = false;
274         vncThread.setPassword(dialog.password());
275     } else {
276         kDebug(5011) << "password dialog not accepted";
277         startQuitting();
278     }
279 #endif
280 }
281
282 void VncView::outputErrorMessage(const QString &message)
283 {
284     kDebug(5011) << message;
285
286     if (message == "INTERNAL:APPLE_VNC_COMPATIBILTY") {
287         setCursor(localDotCursor());
288         m_forceLocalCursor = true;
289         return;
290     }
291
292     startQuitting();
293
294 #ifndef QTONLY
295     KMessageBox::error(this, message, i18n("VNC failure"));
296 #endif
297     emit errorMessage(i18n("VNC failure"), message);
298 }
299
300 #ifndef QTONLY
301 HostPreferences* VncView::hostPreferences()
302 {
303     return m_hostPreferences;
304 }
305 #endif
306
307 void VncView::updateImage(int x, int y, int w, int h)
308 {
309 //     kDebug(5011) << "got update" << width() << height();
310
311     m_x = x;
312     m_y = y;
313     m_w = w;
314     m_h = h;
315
316     if (m_horizontalFactor != 1.0 || m_verticalFactor != 1.0) {
317         // If the view is scaled, grow the update rectangle to avoid artifacts
318         m_x-=1;
319         m_y-=1;
320         m_w+=2;
321         m_h+=2;
322     }
323
324     m_frame = vncThread.image();
325
326     if (!m_initDone) {
327         setAttribute(Qt::WA_StaticContents);
328         setAttribute(Qt::WA_OpaquePaintEvent);
329         installEventFilter(this);
330
331         setCursor(((m_dotCursorState == CursorOn) || m_forceLocalCursor) ? localDotCursor() : Qt::BlankCursor);
332
333         setMouseTracking(true); // get mouse events even when there is no mousebutton pressed
334         setFocusPolicy(Qt::WheelFocus);
335         setStatus(Connected);
336         emit connected();
337         
338         if (m_scale) {
339 #ifndef QTONLY
340             kDebug(5011) << "Setting initial size w:" <<m_hostPreferences->width() << " h:" << m_hostPreferences->height();
341             emit framebufferSizeChanged(m_hostPreferences->width(), m_hostPreferences->height());
342             scaleResize(m_hostPreferences->width(), m_hostPreferences->height());
343             kDebug() << "m_frame.size():" << m_frame.size() << "size()" << size();
344 #else
345 //TODO: qtonly alternative
346 #endif
347         }
348
349         m_initDone = true;
350
351 #ifndef QTONLY
352         if (m_hostPreferences->walletSupport()) {
353             saveWalletPassword(vncThread.password());
354         }
355 #endif
356     }
357
358     if ((y == 0 && x == 0) && (m_frame.size() != size())) {
359         kDebug(5011) << "Updating framebuffer size";
360         if (m_scale) {
361             setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
362             if (parentWidget())
363                 scaleResize(parentWidget()->width(), parentWidget()->height());
364         } else {
365             kDebug(5011) << "Resizing: " << m_frame.width() << m_frame.height();
366             resize(m_frame.width(), m_frame.height());
367             setMaximumSize(m_frame.width(), m_frame.height()); //This is a hack to force Qt to center the view in the scroll area
368             setMinimumSize(m_frame.width(), m_frame.height());
369             emit framebufferSizeChanged(m_frame.width(), m_frame.height());
370         }
371     }
372
373     m_repaint = true;
374     repaint(qRound(m_x * m_horizontalFactor), qRound(m_y * m_verticalFactor), qRound(m_w * m_horizontalFactor), qRound(m_h * m_verticalFactor));
375     m_repaint = false;
376 }
377
378 void VncView::setViewOnly(bool viewOnly)
379 {
380     RemoteView::setViewOnly(viewOnly);
381
382     m_dontSendClipboard = viewOnly;
383
384     if (viewOnly)
385         setCursor(Qt::ArrowCursor);
386     else
387         setCursor(m_dotCursorState == CursorOn ? localDotCursor() : Qt::BlankCursor);
388 }
389
390 void VncView::showDotCursor(DotCursorState state)
391 {
392     RemoteView::showDotCursor(state);
393
394     setCursor(state == CursorOn ? localDotCursor() : Qt::BlankCursor);
395 }
396
397 void VncView::enableScaling(bool scale)
398 {
399     RemoteView::enableScaling(scale);
400
401     if (scale) {
402         setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
403         setMinimumSize(1, 1);
404         if (parentWidget())
405             scaleResize(parentWidget()->width(), parentWidget()->height());
406     } else {
407         m_verticalFactor = 1.0;
408         m_horizontalFactor = 1.0;
409
410         setMaximumSize(m_frame.width(), m_frame.height()); //This is a hack to force Qt to center the view in the scroll area
411         setMinimumSize(m_frame.width(), m_frame.height());
412         resize(m_frame.width(), m_frame.height());
413     }
414 }
415
416 void VncView::setCut(const QString &text)
417 {
418     m_dontSendClipboard = true;
419     m_clipboard->setText(text, QClipboard::Clipboard);
420     m_dontSendClipboard = false;
421 }
422
423 void VncView::paintEvent(QPaintEvent *event)
424 {
425 //     kDebug(5011) << "paint event: x: " << m_x << ", y: " << m_y << ", w: " << m_w << ", h: " << m_h;
426     if (m_frame.isNull() || m_frame.format() == QImage::Format_Invalid) {
427         kDebug(5011) << "no valid image to paint";
428         RemoteView::paintEvent(event);
429         return;
430     }
431
432     event->accept();
433
434     QPainter painter(this);
435
436     if (m_repaint) {
437 //         kDebug(5011) << "normal repaint";
438         painter.drawImage(QRect(qRound(m_x*m_horizontalFactor), qRound(m_y*m_verticalFactor),
439                                 qRound(m_w*m_horizontalFactor), qRound(m_h*m_verticalFactor)), 
440                           m_frame.copy(m_x, m_y, m_w, m_h).scaled(qRound(m_w*m_horizontalFactor), 
441                                                                   qRound(m_h*m_verticalFactor),
442                                                                   Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
443     } else {
444 //         kDebug(5011) << "resize repaint";
445         QRect rect = event->rect();
446         if (rect.width() != width() || rect.height() != height()) {
447 //             kDebug(5011) << "Partial repaint";
448             const int sx = rect.x()/m_horizontalFactor;
449             const int sy = rect.y()/m_verticalFactor;
450             const int sw = rect.width()/m_horizontalFactor;
451             const int sh = rect.height()/m_verticalFactor;
452             painter.drawImage(rect, 
453                               m_frame.copy(sx, sy, sw, sh).scaled(rect.width(), rect.height(),
454                                                                   Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
455         } else {
456 //             kDebug(5011) << "Full repaint" << width() << height() << m_frame.width() << m_frame.height();
457             painter.drawImage(QRect(0, 0, width(), height()), 
458                               m_frame.scaled(m_frame.width() * m_horizontalFactor, m_frame.height() * m_verticalFactor,
459                                              Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
460         }
461     }
462
463     RemoteView::paintEvent(event);
464 }
465
466 void VncView::resizeEvent(QResizeEvent *event)
467 {
468     RemoteView::resizeEvent(event);
469     update();
470 }
471
472 bool VncView::event(QEvent *event)
473 {
474     switch (event->type()) {
475     case QEvent::KeyPress:
476     case QEvent::KeyRelease:
477 //         kDebug(5011) << "keyEvent";
478         keyEventHandler(static_cast<QKeyEvent*>(event));
479         return true;
480         break;
481     case QEvent::MouseButtonDblClick:
482     case QEvent::MouseButtonPress:
483     case QEvent::MouseButtonRelease:
484     case QEvent::MouseMove:
485 //         kDebug(5011) << "mouseEvent";
486         mouseEventHandler(static_cast<QMouseEvent*>(event));
487         return true;
488         break;
489     case QEvent::Wheel:
490 //         kDebug(5011) << "wheelEvent";
491         wheelEventHandler(static_cast<QWheelEvent*>(event));
492         return true;
493         break;
494     default:
495         return RemoteView::event(event);
496     }
497 }
498
499 void VncView::mouseEventHandler(QMouseEvent *e)
500 {
501     if (e->type() != QEvent::MouseMove) {
502         if ((e->type() == QEvent::MouseButtonPress) ||
503                 (e->type() == QEvent::MouseButtonDblClick)) {
504             if (e->button() & Qt::LeftButton)
505                 m_buttonMask |= 0x01;
506             if (e->button() & Qt::MidButton)
507                 m_buttonMask |= 0x02;
508             if (e->button() & Qt::RightButton)
509                 m_buttonMask |= 0x04;
510         } else if (e->type() == QEvent::MouseButtonRelease) {
511             if (e->button() & Qt::LeftButton)
512                 m_buttonMask &= 0xfe;
513             if (e->button() & Qt::MidButton)
514                 m_buttonMask &= 0xfd;
515             if (e->button() & Qt::RightButton)
516                 m_buttonMask &= 0xfb;
517         }
518     }
519
520     vncThread.mouseEvent(qRound(e->x() / m_horizontalFactor), qRound(e->y() / m_verticalFactor), m_buttonMask);
521 }
522
523 void VncView::wheelEventHandler(QWheelEvent *event)
524 {
525     int eb = 0;
526     if (event->delta() < 0)
527         eb |= 0x10;
528     else
529         eb |= 0x8;
530
531     const int x = qRound(event->x() / m_horizontalFactor);
532     const int y = qRound(event->y() / m_verticalFactor);
533
534     vncThread.mouseEvent(x, y, eb | m_buttonMask);
535     vncThread.mouseEvent(x, y, m_buttonMask);
536 }
537
538 void VncView::keyEventHandler(QKeyEvent *e)
539 {
540     // strip away autorepeating KeyRelease; see bug #206598
541     if (e->isAutoRepeat() && (e->type() == QEvent::KeyRelease))
542         return;
543
544 // parts of this code are based on http://italc.sourcearchive.com/documentation/1.0.9.1/vncview_8cpp-source.html
545     rfbKeySym k = e->nativeVirtualKey();
546
547     // we do not handle Key_Backtab separately as the Shift-modifier
548     // is already enabled
549     if (e->key() == Qt::Key_Backtab) {
550         k = XK_Tab;
551     }
552
553     const bool pressed = (e->type() == QEvent::KeyPress);
554
555     // handle modifiers
556     if (k == XK_Shift_L || k == XK_Control_L || k == XK_Meta_L || k == XK_Alt_L) {
557         if (pressed) {
558             m_mods[k] = true;
559         } else if (m_mods.contains(k)) {
560             m_mods.remove(k);
561         } else {
562             unpressModifiers();
563         }
564     }
565
566     if (k) {
567         vncThread.keyEvent(k, pressed);
568     }
569 }
570
571 void VncView::unpressModifiers()
572 {
573     const QList<unsigned int> keys = m_mods.keys();
574     QList<unsigned int>::const_iterator it = keys.constBegin();
575     while (it != keys.end()) {
576         vncThread.keyEvent(*it, false);
577         it++;
578     }
579     m_mods.clear();
580 }
581
582 void VncView::clipboardDataChanged()
583 {
584     kDebug(5011);
585
586     if (m_status != Connected)
587         return;
588
589     if (m_clipboard->ownsClipboard() || m_dontSendClipboard)
590         return;
591
592     const QString text = m_clipboard->text(QClipboard::Clipboard);
593
594     vncThread.clientCut(text);
595 }
596
597 #include "moc_vncview.cpp"