nodeinstanceserverproxy.cpp 19.7 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
hjk's avatar
hjk committed
3 4
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29

30 31 32 33 34 35 36
#include "nodeinstanceserverproxy.h"

#include <QLocalServer>
#include <QLocalSocket>
#include <QProcess>
#include <QCoreApplication>
#include <QUuid>
37
#include <QFileInfo>
38 39 40 41 42 43 44 45 46

#include "propertyabstractcontainer.h"
#include "propertyvaluecontainer.h"
#include "propertybindingcontainer.h"
#include "instancecontainer.h"
#include "createinstancescommand.h"
#include "createscenecommand.h"
#include "changevaluescommand.h"
#include "changebindingscommand.h"
47
#include "changeauxiliarycommand.h"
48 49 50 51 52 53 54
#include "changefileurlcommand.h"
#include "removeinstancescommand.h"
#include "clearscenecommand.h"
#include "removepropertiescommand.h"
#include "reparentinstancescommand.h"
#include "changeidscommand.h"
#include "changestatecommand.h"
55
#include "completecomponentcommand.h"
56
#include "changenodesourcecommand.h"
57 58 59 60

#include "informationchangedcommand.h"
#include "pixmapchangedcommand.h"
#include "valueschangedcommand.h"
61
#include "childrenchangedcommand.h"
62 63
#include "imagecontainer.h"
#include "statepreviewimagechangedcommand.h"
64
#include "componentcompletedcommand.h"
65
#include "tokencommand.h"
66

67 68
#include "synchronizecommand.h"

69
#include "nodeinstanceview.h"
70 71 72 73

#include "import.h"
#include <QMessageBox>

74 75 76

namespace QmlDesigner {

77
static bool hasQtQuick2(NodeInstanceView *nodeInstanceView)
78 79 80
{
    if (nodeInstanceView && nodeInstanceView->model()) {
        foreach (const Import &import ,nodeInstanceView->model()->imports()) {
81
            if (import.url() ==  "QtQuick" && import.version().toDouble() >= 2.0)
82 83 84 85 86 87 88
                return true;
        }
    }

    return false;
}

89
NodeInstanceServerProxy::NodeInstanceServerProxy(NodeInstanceView *nodeInstanceView, RunModus runModus, const QString &pathToQt)
90 91 92
    : NodeInstanceServerInterface(nodeInstanceView),
      m_localServer(new QLocalServer(this)),
      m_nodeInstanceView(nodeInstanceView),
93
      m_firstBlockSize(0),
94
      m_secondBlockSize(0),
95
      m_thirdBlockSize(0),
96 97 98 99
      m_writeCommandCounter(0),
      m_firstLastReadCommandCounter(0),
      m_secondLastReadCommandCounter(0),
      m_thirdLastReadCommandCounter(0),
100 101
      m_runModus(runModus),
      m_synchronizeId(-1)
102
{
103
   Q_UNUSED(pathToQt);
104 105 106
   QString socketToken(QUuid::createUuid().toString());

   m_localServer->listen(socketToken);
107
   m_localServer->setMaxPendingConnections(3);
108

109 110 111
   QString applicationPath =  pathToQt + QLatin1String("/bin");
   if (runModus == TestModus) {
       applicationPath = QCoreApplication::applicationDirPath() + QLatin1String("/../../../../../bin");
112
   } else {
113
       applicationPath = macOSBundlePath(applicationPath);
Robert Loehning's avatar
Robert Loehning committed
114
       applicationPath += QLatin1Char('/') + qmlPuppetApplicationName();
115 116 117 118
       if (!QFileInfo(applicationPath).exists()) { //No qmlpuppet in Qt
           //We have to find out how to give not too intrusive feedback
           applicationPath =  QCoreApplication::applicationDirPath();
           applicationPath = macOSBundlePath(applicationPath);
Robert Loehning's avatar
Robert Loehning committed
119
           applicationPath += QLatin1Char('/') + qmlPuppetApplicationName();
120
       }
121
   }
122

123 124 125 126 127
   QByteArray envImportPath = qgetenv("QTCREATOR_QMLPUPPET_PATH");
   if (!envImportPath.isEmpty()) {
       applicationPath = envImportPath;
   }

128 129 130
   QProcessEnvironment enviroment = QProcessEnvironment::systemEnvironment();
   enviroment.insert("QML_NO_THREADED_RENDERER", "true");

131 132
   if (QFileInfo(applicationPath).exists()) {
       m_qmlPuppetEditorProcess = new QProcess;
133 134
       m_qmlPuppetEditorProcess->setProcessEnvironment(enviroment);
       m_qmlPuppetEditorProcess->setObjectName("EditorProcess");
135 136 137
       connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
       connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetEditorProcess.data(), SLOT(kill()));
       bool fowardQmlpuppetOutput = !qgetenv("FORWARD_QMLPUPPET_OUTPUT").isEmpty();
138
       if (fowardQmlpuppetOutput)
139 140
           m_qmlPuppetEditorProcess->setProcessChannelMode(QProcess::ForwardedChannels);
       m_qmlPuppetEditorProcess->start(applicationPath, QStringList() << socketToken << "editormode" << "-graphicssystem raster");
141

142 143
       if (runModus == NormalModus) {
           m_qmlPuppetPreviewProcess = new QProcess;
144 145
           m_qmlPuppetPreviewProcess->setProcessEnvironment(enviroment);
           m_qmlPuppetPreviewProcess->setObjectName("PreviewProcess");
146 147 148 149 150
           connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
           connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetPreviewProcess.data(), SLOT(kill()));
           if (fowardQmlpuppetOutput)
               m_qmlPuppetPreviewProcess->setProcessChannelMode(QProcess::ForwardedChannels);
           m_qmlPuppetPreviewProcess->start(applicationPath, QStringList() << socketToken << "previewmode" << "-graphicssystem raster");
151

152
           m_qmlPuppetRenderProcess = new QProcess;
153 154
           m_qmlPuppetRenderProcess->setProcessEnvironment(enviroment);
           m_qmlPuppetRenderProcess->setObjectName("RenderProcess");
155 156 157 158 159
           connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
           connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetRenderProcess.data(), SLOT(kill()));
           if (fowardQmlpuppetOutput)
               m_qmlPuppetRenderProcess->setProcessChannelMode(QProcess::ForwardedChannels);
           m_qmlPuppetRenderProcess->start(applicationPath, QStringList() << socketToken << "rendermode" << "-graphicssystem raster");
160

161
       }
162

163
       connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater()));
164

165 166
       if (m_qmlPuppetEditorProcess->waitForStarted(10000)) {
           connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int)), m_qmlPuppetEditorProcess.data(),SLOT(deleteLater()));
167

168 169 170
           if (runModus == NormalModus) {
               m_qmlPuppetPreviewProcess->waitForStarted();
               connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int)), m_qmlPuppetPreviewProcess.data(),SLOT(deleteLater()));
171

172 173 174
               m_qmlPuppetRenderProcess->waitForStarted();
               connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int)), m_qmlPuppetRenderProcess.data(),SLOT(deleteLater()));
           }
175

176 177
           if (!m_localServer->hasPendingConnections())
               m_localServer->waitForNewConnection(10000);
178

179 180
           m_firstSocket = m_localServer->nextPendingConnection();
           connect(m_firstSocket.data(), SIGNAL(readyRead()), this, SLOT(readFirstDataStream()));
181

182 183 184
           if (runModus == NormalModus) {
               if (!m_localServer->hasPendingConnections())
                   m_localServer->waitForNewConnection(10000);
185

186 187
               m_secondSocket = m_localServer->nextPendingConnection();
               connect(m_secondSocket.data(), SIGNAL(readyRead()), this, SLOT(readSecondDataStream()));
188

189 190
               if (!m_localServer->hasPendingConnections())
                   m_localServer->waitForNewConnection(10000);
191

192 193 194
               m_thirdSocket = m_localServer->nextPendingConnection();
               connect(m_thirdSocket.data(), SIGNAL(readyRead()), this, SLOT(readThirdDataStream()));
           }
195

196
       } else {
197 198 199 200 201 202
           if (!hasQtQuick2(m_nodeInstanceView.data()))
               QMessageBox::warning(0, tr("Cannot Start QML Puppet Executable"),
                                    tr("The executable of the QML Puppet process (%1) cannot be started. "
                                       "Please check your installation. "
                                       "QML Puppet is a process which runs in the background to render the items.").
                                    arg(applicationPath));
203
       }
204

205 206 207
       m_localServer->close();

   } else {
208 209 210 211 212 213
       if (!hasQtQuick2(m_nodeInstanceView.data()))
           QMessageBox::warning(0, tr("Cannot Find QML Puppet Executable"),
                                tr("The executable of the QML Puppet process (%1) cannot be found. "
                                   "Please check your installation. "
                                   "QML Puppet is a process which runs in the background to render the items.").
                                arg(applicationPath));
214
   }
215 216 217 218
}

NodeInstanceServerProxy::~NodeInstanceServerProxy()
{
219 220
    disconnect(this, SLOT(processFinished(int,QProcess::ExitStatus)));

221 222 223 224 225 226
    if (m_firstSocket)
        m_firstSocket->close();

    if (m_secondSocket)
        m_secondSocket->close();

227 228 229
    if(m_thirdSocket)
        m_thirdSocket->close();

230

231
    if (m_qmlPuppetEditorProcess)
232 233
        m_qmlPuppetEditorProcess->kill();

234
    if (m_qmlPuppetPreviewProcess)
235
        m_qmlPuppetPreviewProcess->kill();
236 237 238

    if (m_qmlPuppetRenderProcess)
        m_qmlPuppetRenderProcess->kill();
239 240 241 242 243 244 245
}

void NodeInstanceServerProxy::dispatchCommand(const QVariant &command)
{
    static const int informationChangedCommandType = QMetaType::type("InformationChangedCommand");
    static const int valuesChangedCommandType = QMetaType::type("ValuesChangedCommand");
    static const int pixmapChangedCommandType = QMetaType::type("PixmapChangedCommand");
246
    static const int childrenChangedCommandType = QMetaType::type("ChildrenChangedCommand");
247
    static const int statePreviewImageChangedCommandType = QMetaType::type("StatePreviewImageChangedCommand");
248
    static const int componentCompletedCommandType = QMetaType::type("ComponentCompletedCommand");
249
    static const int synchronizeCommandType = QMetaType::type("SynchronizeCommand");
250
    static const int tokenCommandType = QMetaType::type("TokenCommand");
251 252 253 254 255 256 257

    if (command.userType() ==  informationChangedCommandType)
        nodeInstanceClient()->informationChanged(command.value<InformationChangedCommand>());
    else if (command.userType() ==  valuesChangedCommandType)
        nodeInstanceClient()->valuesChanged(command.value<ValuesChangedCommand>());
    else if (command.userType() ==  pixmapChangedCommandType)
        nodeInstanceClient()->pixmapChanged(command.value<PixmapChangedCommand>());
258 259
    else if (command.userType() == childrenChangedCommandType)
        nodeInstanceClient()->childrenChanged(command.value<ChildrenChangedCommand>());
260 261
    else if (command.userType() == statePreviewImageChangedCommandType)
        nodeInstanceClient()->statePreviewImagesChanged(command.value<StatePreviewImageChangedCommand>());
262 263
    else if (command.userType() == componentCompletedCommandType)
        nodeInstanceClient()->componentCompleted(command.value<ComponentCompletedCommand>());
264 265
    else if (command.userType() == tokenCommandType)
        nodeInstanceClient()->token(command.value<TokenCommand>());
266 267 268 269
    else if (command.userType() == synchronizeCommandType) {
        SynchronizeCommand synchronizeCommand = command.value<SynchronizeCommand>();
        m_synchronizeId = synchronizeCommand.synchronizeId();
    }  else
270 271 272 273 274 275 276 277
        Q_ASSERT(false);
}

NodeInstanceClientInterface *NodeInstanceServerProxy::nodeInstanceClient() const
{
    return m_nodeInstanceView.data();
}

278
static void writeCommandToSocket(const QVariant &command, QLocalSocket *socket, unsigned int commandCounter)
279
{
280 281 282
    if(socket) {
        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
283
        out.setVersion(QDataStream::Qt_4_8);
284
        out << quint32(0);
285
        out << quint32(commandCounter);
286 287 288 289 290 291
        out << command;
        out.device()->seek(0);
        out << quint32(block.size() - sizeof(quint32));

        socket->write(block);
    }
292 293 294 295
}

void NodeInstanceServerProxy::writeCommand(const QVariant &command)
{
296 297 298 299
    writeCommandToSocket(command, m_firstSocket.data(), m_writeCommandCounter);
    writeCommandToSocket(command, m_secondSocket.data(), m_writeCommandCounter);
    writeCommandToSocket(command, m_thirdSocket.data(), m_writeCommandCounter);
    m_writeCommandCounter++;
300 301 302 303 304
    if (m_runModus == TestModus) {
        static int synchronizeId = 0;
        synchronizeId++;
        SynchronizeCommand synchronizeCommand(synchronizeId);

305 306
        writeCommandToSocket(QVariant::fromValue(synchronizeCommand), m_firstSocket.data(), m_writeCommandCounter);
        m_writeCommandCounter++;
307 308 309 310 311 312 313

        while(m_firstSocket->waitForReadyRead()) {
                readFirstDataStream();
                if (m_synchronizeId == synchronizeId)
                    return;
        }
    }
314 315
}

316
void NodeInstanceServerProxy::processFinished(int /*exitCode*/, QProcess::ExitStatus exitStatus)
317
{
318
    qDebug() << "Process finished:" << sender();
319 320 321 322
    if (m_firstSocket)
        m_firstSocket->close();
    if (m_secondSocket)
        m_secondSocket->close();
323 324 325
    if (m_thirdSocket)
        m_thirdSocket->close();

326 327
    if (exitStatus == QProcess::CrashExit)
        emit processCrashed();
328 329
}

330

331 332 333 334 335 336 337 338 339
void NodeInstanceServerProxy::readFirstDataStream()
{
    QList<QVariant> commandList;

    while (!m_firstSocket->atEnd()) {
        if (m_firstSocket->bytesAvailable() < int(sizeof(quint32)))
            break;

        QDataStream in(m_firstSocket.data());
340
        in.setVersion(QDataStream::Qt_4_8);
341 342 343 344 345 346 347 348

        if (m_firstBlockSize == 0) {
            in >> m_firstBlockSize;
        }

        if (m_firstSocket->bytesAvailable() < m_firstBlockSize)
            break;

349 350 351 352 353 354 355 356
        quint32 commandCounter;
        in >> commandCounter;
        bool commandLost = !((m_firstLastReadCommandCounter == 0 && commandCounter == 0) || (m_firstLastReadCommandCounter + 1 == commandCounter));
        if (commandLost)
            qDebug() << "server command lost: " << m_firstLastReadCommandCounter <<  commandCounter;
        m_firstLastReadCommandCounter = commandCounter;


357 358 359 360 361 362 363 364 365 366 367 368 369
        QVariant command;
        in >> command;
        m_firstBlockSize = 0;

        commandList.append(command);
    }

    foreach (const QVariant &command, commandList) {
        dispatchCommand(command);
    }
}

void NodeInstanceServerProxy::readSecondDataStream()
370 371 372
{
    QList<QVariant> commandList;

373 374
    while (!m_secondSocket->atEnd()) {
        if (m_secondSocket->bytesAvailable() < int(sizeof(quint32)))
375 376
            break;

377
        QDataStream in(m_secondSocket.data());
378
        in.setVersion(QDataStream::Qt_4_8);
379

380 381
        if (m_secondBlockSize == 0) {
            in >> m_secondBlockSize;
382 383
        }

384
        if (m_secondSocket->bytesAvailable() < m_secondBlockSize)
385 386
            break;

387 388 389 390 391 392 393 394
        quint32 commandCounter;
        in >> commandCounter;
        bool commandLost = !((m_secondLastReadCommandCounter == 0 && commandCounter == 0) || (m_secondLastReadCommandCounter + 1 == commandCounter));
        if (commandLost)
            qDebug() << "server command lost: " << m_secondLastReadCommandCounter <<  commandCounter;
        m_secondLastReadCommandCounter = commandCounter;


395 396
        QVariant command;
        in >> command;
397
        m_secondBlockSize = 0;
398 399 400 401 402 403 404 405 406

        commandList.append(command);
    }

    foreach (const QVariant &command, commandList) {
        dispatchCommand(command);
    }
}

407 408 409 410 411 412 413 414 415
void NodeInstanceServerProxy::readThirdDataStream()
{
    QList<QVariant> commandList;

    while (!m_thirdSocket->atEnd()) {
        if (m_thirdSocket->bytesAvailable() < int(sizeof(quint32)))
            break;

        QDataStream in(m_thirdSocket.data());
416
        in.setVersion(QDataStream::Qt_4_8);
417 418 419 420 421 422 423 424

        if (m_thirdBlockSize == 0) {
            in >> m_thirdBlockSize;
        }

        if (m_thirdSocket->bytesAvailable() < m_thirdBlockSize)
            break;

425 426 427 428 429 430 431 432
        quint32 commandCounter;
        in >> commandCounter;
        bool commandLost = !((m_thirdLastReadCommandCounter == 0 && commandCounter == 0) || (m_thirdLastReadCommandCounter + 1 == commandCounter));
        if (commandLost)
            qDebug() << "server command lost: " << m_thirdLastReadCommandCounter <<  commandCounter;
        m_thirdLastReadCommandCounter = commandCounter;


433 434 435 436 437 438 439 440 441 442 443 444
        QVariant command;
        in >> command;
        m_thirdBlockSize = 0;

        commandList.append(command);
    }

    foreach (const QVariant &command, commandList) {
        dispatchCommand(command);
    }
}

445 446 447
QString NodeInstanceServerProxy::qmlPuppetApplicationName() const
{
    QString appName;
448
    if (hasQtQuick2(m_nodeInstanceView.data())) {
449
        appName = QLatin1String("qml2puppet");
450 451
    } else {
        appName = QLatin1String("qmlpuppet");
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
    }
 #ifdef Q_OS_WIN
    appName += QLatin1String(".exe");
 #endif

    return appName;
}

QString NodeInstanceServerProxy::macOSBundlePath(const QString &path) const
{
    QString applicationPath = path;
#ifdef Q_OS_MACX
   applicationPath += QLatin1String("/qmlpuppet.app/Contents/MacOS");
#endif
   return applicationPath;
}
468

469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
void NodeInstanceServerProxy::createInstances(const CreateInstancesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changeFileUrl(const ChangeFileUrlCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::createScene(const CreateSceneCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::clearScene(const ClearSceneCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::removeInstances(const RemoveInstancesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::removeProperties(const RemovePropertiesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changePropertyBindings(const ChangeBindingsCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changePropertyValues(const ChangeValuesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

509 510 511 512 513
void NodeInstanceServerProxy::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
void NodeInstanceServerProxy::reparentInstances(const ReparentInstancesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changeIds(const ChangeIdsCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changeState(const ChangeStateCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

529 530 531 532
void NodeInstanceServerProxy::completeComponent(const CompleteComponentCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}
533

534
void NodeInstanceServerProxy::changeNodeSource(const ChangeNodeSourceCommand &command)
535 536 537 538
{
    writeCommand(QVariant::fromValue(command));
}

539 540 541 542 543
void NodeInstanceServerProxy::token(const TokenCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

544
} // namespace QmlDesigner