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

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

134 135 136
   QProcessEnvironment enviroment = QProcessEnvironment::systemEnvironment();
   enviroment.insert("QML_NO_THREADED_RENDERER", "true");

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

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

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

167
       }
168

169
       connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater()));
170

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

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

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

182 183
           if (!m_localServer->hasPendingConnections())
               m_localServer->waitForNewConnection(10000);
184

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

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

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

195 196
               if (!m_localServer->hasPendingConnections())
                   m_localServer->waitForNewConnection(10000);
197

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

202 203 204 205 206 207
       } 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));
208
       }
209

210 211 212
       m_localServer->close();

   } else {
213 214 215
       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
216
                               "QML Puppet is a process which runs in the background to render the items.").
217
                            arg(applicationPath));
218
   }
219 220 221 222
}

NodeInstanceServerProxy::~NodeInstanceServerProxy()
{
223 224
    disconnect(this, SLOT(processFinished(int,QProcess::ExitStatus)));

225 226 227 228 229 230
    if (m_firstSocket)
        m_firstSocket->close();

    if (m_secondSocket)
        m_secondSocket->close();

231 232 233
    if(m_thirdSocket)
        m_thirdSocket->close();

234

235
    if (m_qmlPuppetEditorProcess)
236 237
        m_qmlPuppetEditorProcess->kill();

238
    if (m_qmlPuppetPreviewProcess)
239
        m_qmlPuppetPreviewProcess->kill();
240 241 242

    if (m_qmlPuppetRenderProcess)
        m_qmlPuppetRenderProcess->kill();
243 244 245 246 247 248 249
}

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

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

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

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

        socket->write(block);
    }
295 296 297 298
}

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

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

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

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

329 330
    if (exitStatus == QProcess::CrashExit)
        emit processCrashed();
331 332
}

333

334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
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;

351 352 353 354 355 356 357 358
        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;


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

        commandList.append(command);
    }

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

void NodeInstanceServerProxy::readSecondDataStream()
372 373 374
{
    QList<QVariant> commandList;

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

379
        QDataStream in(m_secondSocket.data());
380

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

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

388 389 390 391 392 393 394 395
        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;


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

        commandList.append(command);
    }

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

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
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;

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
    if (Utils::HostOsInfo::isWindowsHost())
        appName += QLatin1String(".exe");
455 456 457 458 459 460 461

    return appName;
}

QString NodeInstanceServerProxy::macOSBundlePath(const QString &path) const
{
    QString applicationPath = path;
462 463
    if (Utils::HostOsInfo::isMacHost())
        applicationPath += QLatin1String("/qmlpuppet.app/Contents/MacOS");
464 465
   return applicationPath;
}
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 505 506
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));
}

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

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

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

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

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

542
} // namespace QmlDesigner