cleanup
[presencevnc] / src / vncview.cpp
index b31aeae..aa021c7 100644 (file)
 
 #include "vncview.h"
 
-#include <QMessageBox>
-#include <QInputDialog>
-#define KMessageBox QMessageBox
-#define error(parent, message, caption) \
-critical(parent, caption, message)
-
-#include <QApplication>
-#include <QBitmap>
-#include <QCheckBox>
-#include <QDialog>
-#include <QImage>
-#include <QHBoxLayout>
-#include <QVBoxLayout>
-#include <QPainter>
-#include <QMouseEvent>
-#include <QPushButton>
-#include <QEvent>
-#include <QSettings>
-#include <QTime>
-#include <QTimer>
+#include <QtGui>
 
 
 // Definition of key modifier mask constants
@@ -53,7 +34,12 @@ critical(parent, caption, message)
 #define KMOD_Shift_L   0x10
 
 //local cursor width/height in px, should be an odd number
-const int cursor_size = 7;
+const int CURSOR_SIZE = 7;
+
+//in miliseconds
+const int TAP_PRESS_TIME = 180;
+const int DOUBLE_TAP_UP_TIME = 500;
+
 
 VncView::VncView(QWidget *parent, const KUrl &url, RemoteView::Quality quality, int listen_port)
         : RemoteView(parent),
@@ -61,16 +47,15 @@ VncView::VncView(QWidget *parent, const KUrl &url, RemoteView::Quality quality,
         m_buttonMask(0),
        cursor_x(0),
        cursor_y(0),
-        m_repaint(false),
         m_quitFlag(false),
         m_firstPasswordTry(true),
         m_dontSendClipboard(false),
         m_horizontalFactor(1.0),
         m_verticalFactor(1.0),
         m_forceLocalCursor(false),
-       force_full_repaint(false),
        quality(quality),
-       listen_port(listen_port)
+       listen_port(listen_port),
+       transformation_mode(Qt::FastTransformation)
 {
     m_url = url;
     m_host = url.host();
@@ -82,6 +67,9 @@ VncView::VncView(QWidget *parent, const KUrl &url, RemoteView::Quality quality,
     connect(&vncThread, SIGNAL(passwordRequest()), this, SLOT(requestPassword()), Qt::BlockingQueuedConnection);
     connect(&vncThread, SIGNAL(outputErrorMessage(QString)), this, SLOT(outputErrorMessage(QString)));
 
+       //don't miss early connection failures
+       connect(&vncThread, SIGNAL(finished()), this, SLOT(startQuitting()));
+
     m_clipboard = QApplication::clipboard();
     connect(m_clipboard, SIGNAL(selectionChanged()), this, SLOT(clipboardSelectionChanged()));
     connect(m_clipboard, SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
@@ -99,12 +87,6 @@ VncView::~VncView()
     startQuitting();
 }
 
-void VncView::forceFullRepaint()
-{
-       force_full_repaint = true;
-       repaint();
-}
-
 bool VncView::eventFilter(QObject *obj, QEvent *event)
 {
     if (m_viewOnly) {
@@ -114,9 +96,13 @@ bool VncView::eventFilter(QObject *obj, QEvent *event)
                 event->type() == QEvent::MouseButtonPress ||
                 event->type() == QEvent::MouseButtonRelease ||
                 event->type() == QEvent::Wheel ||
-                event->type() == QEvent::MouseMove)
+                event->type() == QEvent::MouseMove) {
+
+                       event->ignore();
             return true;
+               }
     }
+
     return RemoteView::eventFilter(obj, event);
 }
 
@@ -137,15 +123,15 @@ QSize VncView::minimumSizeHint() const
 
 void VncView::startQuitting()
 {
-    kDebug(5011) << "about to quit";
+       if(isQuitting())
+               return;
 
-    //const bool connected = status() == RemoteView::Connected;
+    kDebug(5011) << "about to quit";
 
     setStatus(Disconnecting);
 
     m_quitFlag = true;
 
-       //if(connected) //remove if things work without it
        vncThread.stop();
 
     const bool quitSuccess = vncThread.wait(700);
@@ -206,8 +192,9 @@ void VncView::requestPassword()
 
        QSettings settings;
        settings.beginGroup("hosts");
-       QString password = settings.value(QString("%1/password").arg(m_host), "").toString();
+
        //check for saved password
+       QString password = settings.value(QString("%1/password").arg(m_host), "").toString();
        if(m_firstPasswordTry and !password.isEmpty()) {
                kDebug(5011) << "Trying saved password";
                m_firstPasswordTry = false;
@@ -233,7 +220,8 @@ void VncView::requestPassword()
        QHBoxLayout layout1;
        QVBoxLayout layout2;
        layout2.addWidget(&passwordbox);
-       layout2.addWidget(&save_password);
+       if(!m_host.isEmpty()) //don't save incoming connections
+               layout2.addWidget(&save_password);
        layout1.addLayout(&layout2);
        layout1.addWidget(&ok_button);
        dialog.setLayout(&layout1);
@@ -241,7 +229,7 @@ void VncView::requestPassword()
        if(dialog.exec()) { //dialog accepted
                password = passwordbox.text();
 
-               if(save_password.isChecked()) {
+               if(!m_host.isEmpty() and save_password.isChecked()) {
                        kDebug(5011) << "Saving password for host '" << m_host << "'";
 
                        settings.setValue(QString("%1/password").arg(m_host), password);
@@ -250,14 +238,12 @@ void VncView::requestPassword()
 
                vncThread.setPassword(password);
        } else {
-               startQuitting();
+               vncThread.setPassword(QString()); //null string to exit
        }
 }
 
 void VncView::outputErrorMessage(const QString &message)
 {
-    kDebug(5011) << message;
-
     if (message == "INTERNAL:APPLE_VNC_COMPATIBILTY") {
         setCursor(localDotCursor());
         m_forceLocalCursor = true;
@@ -277,44 +263,11 @@ void VncView::updateImage(int x, int y, int w, int h)
 
      //kDebug(5011) << "got update" << width() << height();
 
-     /*
-     static unsigned int frames = 0;
-     static unsigned int updates = 0;
-     static QTime time = QTime::currentTime();
-     updates++;
-     if(updates % 100 == 0)
-            kDebug(5011) << "u/s: " << updates/double(time.elapsed()) * 1000.0;
-if(x == 0 and y == 0) {
-       frames++;
-     if(frames % 100 == 0)
-            kDebug(5011) << "f/s: " << frames/double(time.elapsed()) * 1000.0;
-}
-*/
-
     m_x = x;
     m_y = y;
     m_w = w;
     m_h = h;
 
-
-       //with scaled view, artefacts occur because of rounding errors
-       //we'll try to only update chunks of screen space that correspond to integer pixel sizes in m_frame
-       //put this into paintEvent
-       /*
-       int frame_x = x/m_horizontalFactor;
-       int frame_w = w/m_horizontalFactor;
-       int frame_y = y/m_verticalFactor;
-       int frame_h = h/m_verticalFactor;
-       
-       m_x = frame_x*m_horizontalFactor;
-       m_y = frame_y*m_verticalFactor;
-
-       m_w = (frame_w+2)*m_horizontalFactor;
-       m_h = (frame_h+2)*m_verticalFactor;
-       kDebug(5011)<< "update);
-       */
-
-
     if (m_horizontalFactor != 1.0 || m_verticalFactor != 1.0) {
         // If the view is scaled, grow the update rectangle to avoid artifacts
         int x_extrapixels = 1.0/m_horizontalFactor + 1;
@@ -328,7 +281,7 @@ if(x == 0 and y == 0) {
 
     m_frame = vncThread.image();
 
-    if (!m_initDone) {
+    if (!m_initDone) { //TODO this seems an odd place for initialization
         setAttribute(Qt::WA_StaticContents);
         setAttribute(Qt::WA_OpaquePaintEvent);
         installEventFilter(this);
@@ -338,7 +291,6 @@ if(x == 0 and y == 0) {
         setMouseTracking(true); // get mouse events even when there is no mousebutton pressed
         setFocusPolicy(Qt::WheelFocus);
         setStatus(Connected);
-//         emit framebufferSizeChanged(m_frame.width(), m_frame.height());
         emit connected();
         
                resize(width(), height());
@@ -352,12 +304,12 @@ if(x == 0 and y == 0) {
            old_frame_size = m_frame.size();
         kDebug(5011) << "Updating framebuffer size";
                setZoomLevel();
+               useFastTransformations(false);
+
         emit framebufferSizeChanged(m_frame.width(), m_frame.height());
     }
 
-    m_repaint = true;
     repaint(qRound(m_x * m_horizontalFactor), qRound(m_y * m_verticalFactor), qRound(m_w * m_horizontalFactor), qRound(m_h * m_verticalFactor));
-    m_repaint = false;
 }
 
 void VncView::setViewOnly(bool viewOnly)
@@ -379,11 +331,6 @@ void VncView::showDotCursor(DotCursorState state)
     setCursor(state == CursorOn ? localDotCursor() : Qt::BlankCursor);
 }
 
-void VncView::enableScaling(bool scale)
-{
-    RemoteView::enableScaling(scale);
-}
-
 //level should be in [0, 100]
 void VncView::setZoomLevel(int level)
 {
@@ -394,32 +341,26 @@ void VncView::setZoomLevel(int level)
                return;
        }
 
-       double factor; //actual magnification
-       if(level > 95) {
-               factor = 2.0;
-       } else if(level > 90) {
-               factor = 1.0;
+       double magnification;
+       if(level == 100) {
+               magnification = 2.0;
+       } else if(level >= 90) {
+               magnification = 1.0;
        } else {
-               const double min_horiz_factor = double(parentWidget()->width())/m_frame.width();
-               const double min_vert_factor = double(parentWidget()->height())/m_frame.height();
-               const double fit_screen_factor = qMin(min_horiz_factor, min_vert_factor);
+               const double min_horiz_magnification = double(parentWidget()->width())/m_frame.width();
+               const double min_vert_magnification = double(parentWidget()->height())/m_frame.height();
+               const double fit_screen_magnification = qMin(min_horiz_magnification, min_vert_magnification);
 
-               //level=900 => factor=1.0, level=0 => factor=fit_screen_factor
-               factor = (level)/90.0*(1.0 - fit_screen_factor) + fit_screen_factor;
+               //level=90 => magnification=1.0, level=0 => magnification=fit_screen_magnification
+               magnification = (level)/90.0*(1.0 - fit_screen_magnification) + fit_screen_magnification;
        }
 
-       if(factor < 0) {
-               //remote display smaller than local?
-               kDebug(5011) << "remote display smaller than local?";
-               factor = 1.0;
-       }
-       if(factor != factor) //nan
-               factor = 1.0;
+       if(magnification < 0                    //remote display smaller than local?
+       or magnification != magnification)      //nan
+               magnification = 1.0;
        
-       kDebug(5011) << "factor" << factor;
-
-       m_verticalFactor = m_horizontalFactor = factor;
-       resize(m_frame.width()*factor, m_frame.height()*factor);
+       m_verticalFactor = m_horizontalFactor = magnification;
+       resize(m_frame.width()*magnification, m_frame.height()*magnification);
 }
 
 void VncView::setCut(const QString &text)
@@ -432,47 +373,31 @@ void VncView::setCut(const QString &text)
 
 void VncView::paintEvent(QPaintEvent *event)
 {
-     //kDebug(5011) << "paint event: x: " << m_x << ", y: " << m_y << ", w: " << m_w << ", h: " << m_h;
     if (m_frame.isNull() || m_frame.format() == QImage::Format_Invalid) {
-        kDebug(5011) << "no valid image to paint";
+        //no valid image to paint
         RemoteView::paintEvent(event);
         return;
     }
 
     event->accept();
 
+       const QRect update_rect = event->rect();
     QPainter painter(this);
+       if (update_rect != rect()) {
+               // kDebug(5011) << "Partial repaint";
+               const int sx = qRound(update_rect.x()/m_horizontalFactor);
+               const int sy = qRound(update_rect.y()/m_verticalFactor);
+               const int sw = qRound(update_rect.width()/m_horizontalFactor);
+               const int sh = qRound(update_rect.height()/m_verticalFactor);
+
+               painter.drawImage(update_rect, 
+                         m_frame.copy(sx, sy, sw, sh)
+                         .scaled(update_rect.size(), Qt::IgnoreAspectRatio, transformation_mode));
+       } else {
+               //kDebug(5011) << "Full repaint" << width() << height() << m_frame.width() << m_frame.height();
 
-       Qt::TransformationMode transformation_mode = Qt::SmoothTransformation;
-       if( m_horizontalFactor >= 1.0 )
-               transformation_mode = Qt::FastTransformation;
-
-    if (m_repaint and !force_full_repaint) {
-//         kDebug(5011) << "normal repaint";
-        painter.drawImage(QRect(qRound(m_x*m_horizontalFactor), qRound(m_y*m_verticalFactor),
-                                qRound(m_w*m_horizontalFactor), qRound(m_h*m_verticalFactor)), 
-                          m_frame.copy(m_x, m_y, m_w, m_h).scaled(qRound(m_w*m_horizontalFactor), 
-                                                                  qRound(m_h*m_verticalFactor),
-                                                                  Qt::IgnoreAspectRatio, transformation_mode));
-    } else {
-         //kDebug(5011) << "resize repaint";
-        QRect rect = event->rect();
-        if (!force_full_repaint and (rect.width() != width() || rect.height() != height())) {
-          //   kDebug(5011) << "Partial repaint";
-            const int sx = rect.x()/m_horizontalFactor;
-            const int sy = rect.y()/m_verticalFactor;
-            const int sw = rect.width()/m_horizontalFactor;
-            const int sh = rect.height()/m_verticalFactor;
-            painter.drawImage(rect, 
-                              m_frame.copy(sx, sy, sw, sh).scaled(rect.width(), rect.height(),
-                                                                  Qt::IgnoreAspectRatio, transformation_mode));
-        } else {
-             kDebug(5011) << "Full repaint" << width() << height() << m_frame.width() << m_frame.height();
-            painter.drawImage(QRect(0, 0, width(), height()), 
-                              m_frame.scaled(m_frame.width() * m_horizontalFactor, m_frame.height() * m_verticalFactor,
-                                             Qt::IgnoreAspectRatio, transformation_mode));
-                       force_full_repaint = false;
-        }
+               painter.drawImage(rect(),
+                       m_frame.scaled(size(), Qt::IgnoreAspectRatio, transformation_mode));
     }
 
        //draw local cursor ourselves, normal mouse pointer doesn't deal with scrolling
@@ -481,7 +406,7 @@ void VncView::paintEvent(QPaintEvent *event)
                painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
 #endif
                //rectangle size includes 1px pen width
-               painter.drawRect(cursor_x*m_horizontalFactor - cursor_size/2, cursor_y*m_verticalFactor - cursor_size/2, cursor_size-1, cursor_size-1);
+               painter.drawRect(cursor_x*m_horizontalFactor - CURSOR_SIZE/2, cursor_y*m_verticalFactor - CURSOR_SIZE/2, CURSOR_SIZE-1, CURSOR_SIZE-1);
        }
 
     RemoteView::paintEvent(event);
@@ -490,36 +415,34 @@ void VncView::paintEvent(QPaintEvent *event)
 void VncView::resizeEvent(QResizeEvent *event)
 {
     RemoteView::resizeEvent(event);
+    update();
 }
 
 bool VncView::event(QEvent *event)
 {
-    switch (event->type()) {
-    case QEvent::KeyPress:
-    case QEvent::KeyRelease:
-//         kDebug(5011) << "keyEvent";
-        keyEventHandler(static_cast<QKeyEvent*>(event));
-        return true;
-        break;
-    case QEvent::MouseButtonDblClick:
-    case QEvent::MouseButtonPress:
-    case QEvent::MouseButtonRelease:
-    case QEvent::MouseMove:
-//         kDebug(5011) << "mouseEvent";
-        mouseEventHandler(static_cast<QMouseEvent*>(event));
-        return true;
-        break;
-    case QEvent::Wheel:
-//         kDebug(5011) << "wheelEvent";
-        wheelEventHandler(static_cast<QWheelEvent*>(event));
-        return true;
-        break;
-    case QEvent::WindowActivate: //input panel may have been closed, prevent IM from interfering with hardware keyboard
-       setAttribute(Qt::WA_InputMethodEnabled, false);
-       //fall through
-    default:
-        return RemoteView::event(event);
-    }
+       switch (event->type()) {
+       case QEvent::KeyPress:
+       case QEvent::KeyRelease:
+               keyEventHandler(static_cast<QKeyEvent*>(event));
+               return true;
+
+       case QEvent::MouseButtonDblClick:
+       case QEvent::MouseButtonPress:
+       case QEvent::MouseButtonRelease:
+       case QEvent::MouseMove:
+               mouseEventHandler(static_cast<QMouseEvent*>(event));
+               return true;
+
+       case QEvent::Wheel:
+               wheelEventHandler(static_cast<QWheelEvent*>(event));
+               return true;
+
+       case QEvent::WindowActivate: //input panel may have been closed, prevent IM from interfering with hardware keyboard
+               setAttribute(Qt::WA_InputMethodEnabled, false);
+               //fall through
+       default:
+               return RemoteView::event(event);
+       }
 }
 
 //call with e == 0 to flush held events
@@ -531,9 +454,6 @@ void VncView::mouseEventHandler(QMouseEvent *e)
        static QTime press_time;
        static QTime up_time; //used for double clicks/tap&drag, for time after first tap
 
-       const int TAP_PRESS_TIME = 180;
-       const int DOUBLE_TAP_UP_TIME = 500;
-
        if(!e) { //flush held taps
                if(tap_detected) {
                        m_buttonMask |= 0x01;
@@ -613,9 +533,9 @@ void VncView::mouseEventHandler(QMouseEvent *e)
        if(((m_dotCursorState == CursorOn) || m_forceLocalCursor)
        and (cursor_x != old_cursor_x or cursor_y != old_cursor_y)) {
                //clear last position
-               repaint(old_cursor_x*m_horizontalFactor - cursor_size/2, old_cursor_y*m_verticalFactor - cursor_size/2, cursor_size, cursor_size);
+               repaint(old_cursor_x*m_horizontalFactor - CURSOR_SIZE/2, old_cursor_y*m_verticalFactor - CURSOR_SIZE/2, CURSOR_SIZE, CURSOR_SIZE);
                //and refresh new one
-               repaint(cursor_x*m_horizontalFactor - cursor_size/2, cursor_y*m_verticalFactor - cursor_size/2, cursor_size, cursor_size);
+               repaint(cursor_x*m_horizontalFactor - CURSOR_SIZE/2, cursor_y*m_verticalFactor - CURSOR_SIZE/2, CURSOR_SIZE, CURSOR_SIZE);
 
                old_cursor_x = cursor_x; old_cursor_y = cursor_y;
        }
@@ -682,7 +602,7 @@ void VncView::keyEventHandler(QKeyEvent *e)
        else if(e->key() == Qt::Key_F7)
                current_zoom = right_zoom;
        else if (k) {
-       //      kDebug(5011) << "got '" << e->text() << "'.";
+               // kDebug(5011) << "got '" << e->text() << "'.";
                vncThread.keyEvent(k, pressed);
        } else {
                kDebug(5011) << "nativeVirtualKey() for '" << e->text() << "' failed.";
@@ -859,10 +779,12 @@ void VncView::sendKeySequence(QKeySequence keys)
 
        //to get at individual key presses, we split 'keys' into its components
        QList<int> key_list;
-       for(int i = 0; ; i++) {
-               QString k = keys.toString().section('+', i, i);
+       int pos = 0;
+       while(true) {
+               QString k = keys.toString().section('+', pos, pos);
                if(k.isEmpty())
                        break;
+
                //kDebug(5011) << "found key: " << k;
                if(k == "Alt") {
                        key_list.append(Qt::Key_Alt);
@@ -873,6 +795,8 @@ void VncView::sendKeySequence(QKeySequence keys)
                } else {
                        key_list.append(QKeySequence(k)[0]);
                }
+               
+               pos++;
        }
        
        for(int i = 0; i < key_list.count(); i++)
@@ -906,7 +830,7 @@ void VncView::inputMethodEvent(QInputMethodEvent *event)
        //kDebug(5011) << event->commitString() << "|" << event->preeditString() << "|" << event->replacementLength() << "|" << event->replacementStart();
        QString letters = event->commitString();
        for(int i = 0; i < letters.length(); i++) {
-               char k = letters.at(i).toLatin1(); //works with all 'normal' keys, not umlauts.
+               char k = letters.at(i).toLatin1();
                if(!k) {
                        kDebug(5011) << "unhandled key";
                        continue;
@@ -916,5 +840,12 @@ void VncView::inputMethodEvent(QInputMethodEvent *event)
        }
 }
 
-
-#include "moc_vncview.cpp"
+void VncView::useFastTransformations(bool enabled)
+{
+       if(enabled or zoomFactor() >= 1.0) {
+               transformation_mode = Qt::FastTransformation;
+       } else {
+               transformation_mode = Qt::SmoothTransformation;
+               update();
+       }
+}