nodeinstanceserverproxy.cpp 19.2 KB
Newer Older
1 2 3 4 5 6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
18 19
**
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
28 29 30
**
**************************************************************************/

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

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

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

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

68 69
#include "synchronizecommand.h"

70
#include "nodeinstanceview.h"
71 72

#include "import.h"
73 74 75

#include <utils/hostosinfo.h>

76 77
#include <QMessageBox>

78 79 80

namespace QmlDesigner {

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

    return false;
}

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

   m_localServer->listen(socketToken);
111
   m_localServer->setMaxPendingConnections(3);
112

113 114 115
   QString applicationPath =  pathToQt + QLatin1String("/bin");
   if (runModus == TestModus) {
       applicationPath = QCoreApplication::applicationDirPath() + QLatin1String("/../../../../../bin");
116
   } else {
117
       applicationPath = macOSBundlePath(applicationPath);
Robert Loehning's avatar
Robert Loehning committed
118
       applicationPath += QLatin1Char('/') + qmlPuppetApplicationName();
119 120 121 122
       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
123
           applicationPath += QLatin1Char('/') + qmlPuppetApplicationName();
124
       }
125
   }
126

127 128 129 130 131
   QByteArray envImportPath = qgetenv("QTCREATOR_QMLPUPPET_PATH");
   if (!envImportPath.isEmpty()) {
       applicationPath = envImportPath;
   }

132 133 134
   QProcessEnvironment enviroment = QProcessEnvironment::systemEnvironment();
   enviroment.insert("QML_NO_THREADED_RENDERER", "true");

135 136
   if (QFileInfo(applicationPath).exists()) {
       m_qmlPuppetEditorProcess = new QProcess;
137 138
       m_qmlPuppetEditorProcess->setProcessEnvironment(enviroment);
       m_qmlPuppetEditorProcess->setObjectName("EditorProcess");
139 140 141
       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();
142
       if (fowardQmlpuppetOutput)
143 144
           m_qmlPuppetEditorProcess->setProcessChannelMode(QProcess::ForwardedChannels);
       m_qmlPuppetEditorProcess->start(applicationPath, QStringList() << socketToken << "editormode" << "-graphicssystem raster");
145

146 147
       if (runModus == NormalModus) {
           m_qmlPuppetPreviewProcess = new QProcess;
148 149
           m_qmlPuppetPreviewProcess->setProcessEnvironment(enviroment);
           m_qmlPuppetPreviewProcess->setObjectName("PreviewProcess");
150 151 152 153 154
           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");
155

156
           m_qmlPuppetRenderProcess = new QProcess;
157 158
           m_qmlPuppetRenderProcess->setProcessEnvironment(enviroment);
           m_qmlPuppetRenderProcess->setObjectName("RenderProcess");
159 160 161 162 163
           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");
164

165
       }
166

167
       connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater()));
168

169 170
       if (m_qmlPuppetEditorProcess->waitForStarted(10000)) {
           connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int)), m_qmlPuppetEditorProcess.data(),SLOT(deleteLater()));
171

172 173 174
           if (runModus == NormalModus) {
               m_qmlPuppetPreviewProcess->waitForStarted();
               connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int)), m_qmlPuppetPreviewProcess.data(),SLOT(deleteLater()));
175

176 177 178
               m_qmlPuppetRenderProcess->waitForStarted();
               connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int)), m_qmlPuppetRenderProcess.data(),SLOT(deleteLater()));
           }
179

180 181
           if (!m_localServer->hasPendingConnections())
               m_localServer->waitForNewConnection(10000);
182

183 184
           m_firstSocket = m_localServer->nextPendingConnection();
           connect(m_firstSocket.data(), SIGNAL(readyRead()), this, SLOT(readFirstDataStream()));
185

186 187 188
           if (runModus == NormalModus) {
               if (!m_localServer->hasPendingConnections())
                   m_localServer->waitForNewConnection(10000);
189

190 191
               m_secondSocket = m_localServer->nextPendingConnection();
               connect(m_secondSocket.data(), SIGNAL(readyRead()), this, SLOT(readSecondDataStream()));
192

193 194
               if (!m_localServer->hasPendingConnections())
                   m_localServer->waitForNewConnection(10000);
195

196 197 198
               m_thirdSocket = m_localServer->nextPendingConnection();
               connect(m_thirdSocket.data(), SIGNAL(readyRead()), this, SLOT(readThirdDataStream()));
           }
199

200 201 202 203 204 205
       } else {
           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));
206
       }
207

208 209 210
       m_localServer->close();

   } else {
211 212 213
       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. "
Jarek Kobus's avatar
Jarek Kobus committed
214
                               "QML Puppet is a process which runs in the background to render the items.").
215
                            arg(applicationPath));
216
   }
217 218 219 220
}

NodeInstanceServerProxy::~NodeInstanceServerProxy()
{
221 222
    disconnect(this, SLOT(processFinished(int,QProcess::ExitStatus)));

223 224 225 226 227 228
    if (m_firstSocket)
        m_firstSocket->close();

    if (m_secondSocket)
        m_secondSocket->close();

229 230 231
    if(m_thirdSocket)
        m_thirdSocket->close();

232

233
    if (m_qmlPuppetEditorProcess)
234 235
        m_qmlPuppetEditorProcess->kill();

236
    if (m_qmlPuppetPreviewProcess)
237
        m_qmlPuppetPreviewProcess->kill();
238 239 240

    if (m_qmlPuppetRenderProcess)
        m_qmlPuppetRenderProcess->kill();
241 242 243 244 245 246 247
}

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");
248
    static const int childrenChangedCommandType = QMetaType::type("ChildrenChangedCommand");
249
    static const int statePreviewImageChangedCommandType = QMetaType::type("StatePreviewImageChangedCommand");
250
    static const int componentCompletedCommandType = QMetaType::type("ComponentCompletedCommand");
251
    static const int synchronizeCommandType = QMetaType::type("SynchronizeCommand");
252
    static const int tokenCommandType = QMetaType::type("TokenCommand");
253 254 255 256 257 258 259

    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>());
260 261
    else if (command.userType() == childrenChangedCommandType)
        nodeInstanceClient()->childrenChanged(command.value<ChildrenChangedCommand>());
262 263
    else if (command.userType() == statePreviewImageChangedCommandType)
        nodeInstanceClient()->statePreviewImagesChanged(command.value<StatePreviewImageChangedCommand>());
264 265
    else if (command.userType() == componentCompletedCommandType)
        nodeInstanceClient()->componentCompleted(command.value<ComponentCompletedCommand>());
266 267
    else if (command.userType() == tokenCommandType)
        nodeInstanceClient()->token(command.value<TokenCommand>());
268 269 270 271
    else if (command.userType() == synchronizeCommandType) {
        SynchronizeCommand synchronizeCommand = command.value<SynchronizeCommand>();
        m_synchronizeId = synchronizeCommand.synchronizeId();
    }  else
272 273 274 275 276 277 278 279
        Q_ASSERT(false);
}

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

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

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

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

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

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

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

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

331

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
void NodeInstanceServerProxy::readFirstDataStream()
{
    QList<QVariant> commandList;

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

        QDataStream in(m_firstSocket.data());

        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

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

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

386 387 388 389 390 391 392 393
        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;


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

        commandList.append(command);
    }

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

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
void NodeInstanceServerProxy::readThirdDataStream()
{
    QList<QVariant> commandList;

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

        QDataStream in(m_thirdSocket.data());

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

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

423 424 425 426 427 428 429 430
        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;


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

        commandList.append(command);
    }

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

443 444 445
QString NodeInstanceServerProxy::qmlPuppetApplicationName() const
{
    QString appName;
446
    if (hasQtQuick2(m_nodeInstanceView.data())) {
447
        appName = QLatin1String("qml2puppet");
448 449
    } else {
        appName = QLatin1String("qmlpuppet");
450
    }
451 452
    if (Utils::HostOsInfo::isWindowsHost())
        appName += QLatin1String(".exe");
453 454 455 456 457 458 459

    return appName;
}

QString NodeInstanceServerProxy::macOSBundlePath(const QString &path) const
{
    QString applicationPath = path;
460 461
    if (Utils::HostOsInfo::isMacHost())
        applicationPath += QLatin1String("/qmlpuppet.app/Contents/MacOS");
462 463
   return applicationPath;
}
464

465 466 467 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
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));
}

505 506 507 508 509
void NodeInstanceServerProxy::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
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));
}

525 526 527 528
void NodeInstanceServerProxy::completeComponent(const CompleteComponentCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}
529

530
void NodeInstanceServerProxy::changeNodeSource(const ChangeNodeSourceCommand &command)
531 532 533 534
{
    writeCommand(QVariant::fromValue(command));
}

535 536 537 538 539
void NodeInstanceServerProxy::token(const TokenCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

540
} // namespace QmlDesigner