All pastes #919362 Raw Edit

Someone

public text v1 · immutable
#919362 ·published 2008-02-26 19:38 UTC
rendered paste body
/****************************************************************************
**
** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
**
** This file is part of KDE.
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; see the file COPYING. If not, write to
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
** Boston, MA 02110-1301, USA.
**
****************************************************************************/

#include "vncview.h"

#ifdef QTONLY
    #include <QMessageBox>
    #include <QInputDialog>
    #define KMessageBox QMessageBox
    #define error(parent, message, caption) \
        critical(parent, caption, message)
#else
    #include <KMessageBox>
    #include <KPasswordDialog>
#endif

#include <QApplication>
#include <QImage>
#include <QPainter>
#include <QMouseEvent>

VncView::VncView(QWidget *parent, const KUrl &url)
        : RemoteView(parent),
        m_initDone(false),
        m_buttonMask(0),
        m_repaint(false),
        m_quitFlag(false),
        m_firstPasswordTry(true),
        m_dontSendClipboard(false),
        m_horizontalFactor(1.0),
        m_verticalFactor(1.0)
{
    m_url = url;
    m_host = url.host();
    m_port = url.port();

    connect(&vncThread, SIGNAL(imageUpdated(int, int, int, int)), this, SLOT(updateImage(int, int, int, int)), Qt::BlockingQueuedConnection);
    connect(&vncThread, SIGNAL(gotCut(const QString&)), this, SLOT(setCut(const QString&)), Qt::BlockingQueuedConnection);
    connect(&vncThread, SIGNAL(passwordRequest()), this, SLOT(requestPassword()), Qt::BlockingQueuedConnection);
    connect(&vncThread, SIGNAL(outputErrorMessage(QString)), this, SLOT(outputErrorMessage(QString)));

    m_clipboard = QApplication::clipboard();
    connect(m_clipboard, SIGNAL(selectionChanged()), this, SLOT(clipboardSelectionChanged()));
    connect(m_clipboard, SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
}

VncView::~VncView()
{
    startQuitting();
}

bool VncView::eventFilter(QObject *obj, QEvent *event)
{
    if (m_viewOnly) {
        setCursor(Qt::ArrowCursor);

        if (event->type() == QEvent::KeyPress ||
                event->type() == QEvent::KeyRelease ||
                event->type() == QEvent::MouseButtonDblClick ||
                event->type() == QEvent::MouseButtonPress ||
                event->type() == QEvent::MouseButtonRelease ||
                event->type() == QEvent::Wheel ||
                event->type() == QEvent::MouseMove)
            return true;
    }

    setCursor(m_dotCursorState == CursorOn ? localDotCursor() : Qt::BlankCursor);

    return RemoteView::eventFilter(obj, event);
}

QSize VncView::framebufferSize()
{
    return size();
}

QSize VncView::sizeHint() const
{
    return size();
}

QSize VncView::minimumSizeHint() const
{
    return size();
}

void VncView::startQuitting()
{
    kDebug(5011) << "about to quit";

    bool connected = status() == RemoteView::Connected;

    setStatus(Disconnecting);

    m_quitFlag = true;

    if (connected) {
        vncThread.stop();
    } else {
        vncThread.quit();
    }

    vncThread.wait(500);

    setStatus(Disconnected);
}

bool VncView::isQuitting()
{
    return m_quitFlag;
}

bool VncView::start()
{
    vncThread.setHost(m_host);
    vncThread.setPort(m_port);
#ifdef QTONLY
    int quality = (QCoreApplication::arguments().count() > 2) ? QCoreApplication::arguments().at(2).toInt() : 2;
    vncThread.setQuality((RemoteView::Quality)quality);
#else
    m_hostPreferences = new VncHostPreferences(m_url.prettyUrl(KUrl::RemoveTrailingSlash), false, this);
    vncThread.setQuality(m_hostPreferences->quality());
#endif

    setStatus(Connecting);

    vncThread.start();
    return true;
}

bool VncView::supportsLocalCursor() const
{
    return true;
}

void VncView::requestPassword()
{
    kDebug(5011) << "request password";

    setStatus(Authenticating);

#ifndef QTONLY
    if (m_hostPreferences->walletSupport()) {
        QString walletPassword = readWalletPassword();

        if (!walletPassword.isNull()) {
            vncThread.setPassword(walletPassword);
            return;
        }
    }
#endif

    if (!m_url.password().isNull()) {
        vncThread.setPassword(m_url.password());
        return;
    }

#ifdef QTONLY
    QString password = QInputDialog::getText(this, //krazy:exclude=qclasses
                                             tr("Password required"),
                                             tr("Please enter the password for the remote desktop:"),
                                             QLineEdit::Password);
    vncThread.setPassword(password);
#else
    KPasswordDialog dialog(this);
    dialog.setPixmap(KIcon("dialog-password").pixmap(48));
    dialog.setPrompt(m_firstPasswordTry ? i18n("Access to the system requires a password.")
                                        : i18n("Authentication failed. Please try again."));
    if (dialog.exec() == KPasswordDialog::Accepted) {
        m_firstPasswordTry = false;
        vncThread.setPassword(dialog.password());
    }
#endif
}

void VncView::outputErrorMessage(const QString &message)
{
    kDebug(5011) << message;

    startQuitting();

    KMessageBox::error(this, message, i18n("VNC failure"));
}

void VncView::updateImage(int x, int y, int w, int h)
{
//     kDebug(5011) << "got update";

    m_x = x;
    m_y = y;
    m_w = w;
    m_h = h;

    m_frame = vncThread.image();

    if (!m_initDone) {
        setAttribute(Qt::WA_StaticContents);
        setAttribute(Qt::WA_OpaquePaintEvent);
        installEventFilter(this);

        setCursor(m_dotCursorState == CursorOn ? localDotCursor() : Qt::BlankCursor);

        setMouseTracking(true); // get mouse events even when there is no mousebutton pressed
        setFocusPolicy(Qt::WheelFocus);
        resize(m_frame.width(), m_frame.height());
        setStatus(Connected);
        emit changeSize(m_frame.width(), m_frame.height());
        emit connected();
        m_initDone = true;

#ifndef QTONLY
        if (m_hostPreferences->walletSupport()) {
            saveWalletPassword(vncThread.password());
        }
#endif
    }

    if ((y == 0 && x == 0) && (m_frame.size() != size())) {
        resize(m_frame.width(), m_frame.height());
        emit changeSize(m_frame.width(), m_frame.height());
    }

    m_repaint = true;
    repaint(qRound(x * m_horizontalFactor), qRound(y * m_verticalFactor), qRound(w * m_horizontalFactor), qRound(h * m_verticalFactor));
    m_repaint = false;
}

void VncView::setCut(const QString &text)
{
    m_dontSendClipboard = true;
    m_clipboard->setText(text, QClipboard::Clipboard);
    m_clipboard->setText(text, QClipboard::Selection);
    m_dontSendClipboard = false;
}

void VncView::paintEvent(QPaintEvent *event)
{
//     kDebug(5011) << "paint event: x: " << m_x << ", y: " << m_y << ", w: " << m_w << ", h: " << m_h;

    event->accept();

    QPainter painter(this);

    if (m_repaint) {
    // we have the x, y, width, and height of the changed region, we need to expand this area to tile boundaries
    
    //find out what area to update on tile boundaries
    m_x = (m_x / horizontalTileSize) * horizontalTileSize;
    m_y = (m_y / verticalTileSize) * verticalTileSize;
    m_w = (((m_x + m_w) / horizontalTileSize) + (int)((m_x + m_w) % horizontalTileSize != 0)) * horizontalTileSize;
    m_h = (((m_y + m_h) / verticalTileSize) + (int)((m_y + m_h) % verticalTileSize != 0)) * verticalTileSize;


    
//qDebug()<<"h:" << m_verticalFactor<<height()<<" w:" << m_horizontalFactor<<width();
//qDebug()<<"tiles:" << numVerticalTiles <<"x"<<numHorizontalTiles<<"@"<<verticalTileSize<<"x"<<horizontalTileSize;
//qDebug()<<"this tile:"<<m_x<<","<<m_y<<","<<m_w<<","<<m_h;
//         kDebug(5011) << "normal repaint";


        painter.drawImage(QRect((int)(m_x*m_horizontalFactor), (int)(m_y*m_verticalFactor), (int)(m_w*m_horizontalFactor), (int)(m_h*m_verticalFactor)), 
                          m_frame.copy(m_x, m_y, m_w, m_h).scaled((int)(m_w*m_horizontalFactor), (int)(m_h*m_verticalFactor), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));

    } else {
//         kDebug(5011) << "resize repaint";
        painter.drawImage(QRect(0, 0, width(), height()), m_frame.scaled(width(), height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
    }

    QWidget::paintEvent(event);
}

void VncView::resizeEvent(QResizeEvent *event)
{
    RemoteView::resizeEvent(event);

    //find ratios
    m_verticalFactor = (qreal) height() / m_frame.height();
    m_horizontalFactor = (qreal) width() / m_frame.width();

    //calculate tile dimensions
    //TODO experiment with lower values of i
    for (int i = 3; i <= m_frame.height(); i++)
    {
        if (m_frame.height() % i == 0)
        {
            numVerticalTiles = m_frame.height() / i;
            verticalTileSize = i;
            if (height() % numVerticalTiles == 0)
            {
                canvasVerticalTileSize = height() / numVerticalTiles;
                break;
            }
        }
    }
    for (int i = 3; i <= m_frame.width(); i++)
    {
        if (m_frame.width() % i == 0)
        {
            numHorizontalTiles = m_frame.width() / i;
            horizontalTileSize = i;
            if (width() % numHorizontalTiles == 0)
            {
                canvasHorizontalTileSize = width() / numHorizontalTiles;
                break;
            }
        }
    }

    update();
}

void VncView::focusOutEvent(QFocusEvent *event)
{
//     kDebug(5011) << "focusOutEvent";

    if (event->reason() == Qt::TabFocusReason) {
//         kDebug(5011) << "event->reason() == Qt::TabFocusReason";
        event->ignore();
        setFocus(); // get focus back and send tab key event to remote desktop
        vncThread.keyEvent(XK_Tab, true);
        vncThread.keyEvent(XK_Tab, false);
    }

    event->accept();
}

void VncView::mouseMoveEvent(QMouseEvent *event)
{
//     kDebug(5011) << "mouse move";

    mouseEvent(event);

    event->accept();
}

void VncView::mousePressEvent(QMouseEvent *event)
{
//     kDebug(5011) << "mouse press";

    mouseEvent(event);

    event->accept();
}

void VncView::mouseDoubleClickEvent(QMouseEvent *event)
{
//     kDebug(5011) << "mouse double click";

    mouseEvent(event);

    event->accept();
}

void VncView::mouseReleaseEvent(QMouseEvent *event)
{
//     kDebug(5011) << "mouse release";

    mouseEvent(event);

    event->accept();
}

void VncView::mouseEvent(QMouseEvent *e)
{
    if (e->type() != QEvent::MouseMove) {
        if ((e->type() == QEvent::MouseButtonPress) ||
                (e->type() == QEvent::MouseButtonDblClick)) {
            if (e->button() & Qt::LeftButton)
                m_buttonMask |= 0x01;
            if (e->button() & Qt::MidButton)
                m_buttonMask |= 0x02;
            if (e->button() & Qt::RightButton)
                m_buttonMask |= 0x04;
        } else if (e->type() == QEvent::MouseButtonRelease) {
            if (e->button() & Qt::LeftButton)
                m_buttonMask &= 0xfe;
            if (e->button() & Qt::MidButton)
                m_buttonMask &= 0xfd;
            if (e->button() & Qt::RightButton)
                m_buttonMask &= 0xfb;
        }
    }
//qDebug()<< e->x() - ((qreal)width() * ((qreal)m_horizontalFactor - 1.0))<<m_horizontalFactor<<width();
//     vncThread.mouseEvent(qRound(e->x() - ((qreal)(width() * ((qreal)(m_horizontalFactor - 1.0))))), e->y() - ((qreal)height() * ((qreal)m_verticalFactor - 1.0)), m_buttonMask);
    vncThread.mouseEvent(e->x(), e->y(), m_buttonMask);
}

void VncView::wheelEvent(QWheelEvent *event)
{
    int eb = 0;
    if (event->delta() < 0)
        eb |= 0x10;
    else
        eb |= 0x8;

    int x = event->x();
    int y = event->y();

    vncThread.mouseEvent(x, y, eb | m_buttonMask);
    vncThread.mouseEvent(x, y, m_buttonMask);
    event->accept();
}

void VncView::keyEvent(QKeyEvent *e)
{
    rfbKeySym k = 0;
    switch (e->key()) {
    case Qt::Key_Backspace: k = XK_BackSpace; break;
    case Qt::Key_Tab: k = XK_Tab; break;
    case Qt::Key_Clear: k = XK_Clear; break;
    case Qt::Key_Return: k = XK_Return; break;
    case Qt::Key_Pause: k = XK_Pause; break;
    case Qt::Key_Escape: k = XK_Escape; break;
    case Qt::Key_Space: k = XK_space; break;
    case Qt::Key_Delete: k = XK_Delete; break;
    case Qt::Key_Enter: k = XK_KP_Enter; break;
    case Qt::Key_Equal: k = XK_KP_Equal; break;
    case Qt::Key_Up: k = XK_Up; break;
    case Qt::Key_Down: k = XK_Down; break;
    case Qt::Key_Right: k = XK_Right; break;
    case Qt::Key_Left: k = XK_Left; break;
    case Qt::Key_Insert: k = XK_Insert; break;
    case Qt::Key_Home: k = XK_Home; break;
    case Qt::Key_End: k = XK_End; break;
    case Qt::Key_PageUp: k = XK_Page_Up; break;
    case Qt::Key_PageDown: k = XK_Page_Down; break;
    case Qt::Key_F1: k = XK_F1; break;
    case Qt::Key_F2: k = XK_F2; break;
    case Qt::Key_F3: k = XK_F3; break;
    case Qt::Key_F4: k = XK_F4; break;
    case Qt::Key_F5: k = XK_F5; break;
    case Qt::Key_F6: k = XK_F6; break;
    case Qt::Key_F7: k = XK_F7; break;
    case Qt::Key_F8: k = XK_F8; break;
    case Qt::Key_F9: k = XK_F9; break;
    case Qt::Key_F10: k = XK_F10; break;
    case Qt::Key_F11: k = XK_F11; break;
    case Qt::Key_F12: k = XK_F12; break;
    case Qt::Key_F13: k = XK_F13; break;
    case Qt::Key_F14: k = XK_F14; break;
    case Qt::Key_F15: k = XK_F15; break;
    case Qt::Key_NumLock: k = XK_Num_Lock; break;
    case Qt::Key_CapsLock: k = XK_Caps_Lock; break;
    case Qt::Key_ScrollLock: k = XK_Scroll_Lock; break;
    case Qt::Key_Shift: k = XK_Shift_L; break;
    case Qt::Key_Control: k = XK_Control_L; break;
    case Qt::Key_AltGr: k = XK_Alt_R; break;
    case Qt::Key_Alt: k = XK_Alt_L; break;
    case Qt::Key_Meta: k = XK_Meta_L; break;
    case Qt::Key_Mode_switch: k = XK_Mode_switch; break;
    case Qt::Key_Help: k = XK_Help; break;
    case Qt::Key_Print: k = XK_Print; break;
    case Qt::Key_SysReq: k = XK_Sys_Req; break;
    default: break;
    }

    if (k == 0) {
        if (e->key() < 0x100)
            k = QChar(e->text().at(0)).unicode(); //respect upper- / lowercase
        else
            rfbClientLog("Unknown keysym: %d\n", e->key());
    }

    if (k < 26) // workaround for modified keys by pressing CTRL
        k += 96;

    vncThread.keyEvent(k, (e->type() == QEvent::KeyPress) ? true : false);
}

void VncView::keyPressEvent(QKeyEvent *event)
{
//     kDebug(5011) << "key press" << event->key();

    keyEvent(event);

    event->accept();
}

void VncView::keyReleaseEvent(QKeyEvent *event)
{
//     kDebug(5011) << "key release" << event->key();

    keyEvent(event);

    event->accept();
}

void VncView::clipboardSelectionChanged()
{
    kDebug(5011);

    if (m_status != Connected)
        return;

    if (m_clipboard->ownsSelection() || m_dontSendClipboard)
        return;

    QString text = m_clipboard->text(QClipboard::Selection);

    vncThread.clientCut(text);
}

void VncView::clipboardDataChanged()
{
    kDebug(5011);

    if (m_status != Connected)
        return;

    if (m_clipboard->ownsClipboard() || m_dontSendClipboard)
        return;

    QString text = m_clipboard->text(QClipboard::Clipboard);

    vncThread.clientCut(text);
}

#include "moc_vncview.cpp"