3 Copyright (c) 2012 Jakob Leben & Tim Blechmann
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <QCoreApplication>
24 #include "SC_DirUtils.h"
27 #include "main_window.hpp"
28 #include "sc_introspection.hpp"
29 #include "sc_process.hpp"
30 #include "sc_server.hpp"
31 #include "settings/manager.hpp"
33 #include "yaml-cpp/node.h"
34 #include "yaml-cpp/parser.h"
38 SCProcess::SCProcess( Main
*parent
, ScResponder
* responder
, Settings::Manager
* settings
):
40 mIpcServer( new QLocalServer(this) ),
42 mIpcServerName("SCIde_" + QString::number(QCoreApplication::applicationPid()))
44 mIntrospectionParser
= new ScIntrospectionParser( responder
, this );
45 mIntrospectionParser
->start();
47 prepareActions(settings
);
49 connect(this, SIGNAL( readyRead() ),
50 this, SLOT( onReadyRead() ));
51 connect(mIpcServer
, SIGNAL(newConnection()), this, SLOT(onNewIpcConnection()));
52 connect(mIntrospectionParser
, SIGNAL(done(ScLanguage::Introspection
*)),
53 this, SLOT(swapIntrospection(ScLanguage::Introspection
*)));
56 void SCProcess::prepareActions(Settings::Manager
* settings
)
59 mActions
[StartSCLang
] = action
= new QAction(
60 QIcon::fromTheme("system-run"), tr("Start SCLang"), this);
61 connect(action
, SIGNAL(triggered()), this, SLOT(startLanguage()) );
63 mActions
[RecompileClassLibrary
] = action
= new QAction(
64 QIcon::fromTheme("system-reboot"), tr("Recompile Class Library"), this);
65 action
->setShortcut(tr("Ctrl+Shift+l", "Recompile Class Library)"));
66 connect(action
, SIGNAL(triggered()), this, SLOT(recompileClassLibrary()) );
68 mActions
[StopSCLang
] = action
= new QAction(
69 QIcon::fromTheme("system-shutdown"), tr("Stop SCLang"), this);
70 connect(action
, SIGNAL(triggered()), this, SLOT(stopLanguage()) );
72 mActions
[RestartSCLang
] = action
= new QAction(
73 QIcon::fromTheme("system-reboot"), tr("Restart SCLang"), this);
74 connect(action
, SIGNAL(triggered()), this, SLOT(restartLanguage()) );
76 mActions
[RunMain
] = action
= new QAction(
77 QIcon::fromTheme("media-playback-start"), tr("Run Main"), this);
78 connect(action
, SIGNAL(triggered()), this, SLOT(runMain()));
80 mActions
[StopMain
] = action
= new QAction(
81 QIcon::fromTheme("process-stop"), tr("Stop Main"), this);
82 action
->setShortcut(tr("Ctrl+.", "Stop Main (a.k.a. cmd-period)"));
83 connect(action
, SIGNAL(triggered()), this, SLOT(stopMain()));
85 for (int i
= 0; i
< SCProcessActionCount
; ++i
)
86 settings
->addAction( mActions
[i
] );
89 void SCProcess::startLanguage (void)
91 if (state() != QProcess::NotRunning
) {
92 statusMessage("Interpreter is already running.");
96 Settings::Manager
*settings
= Main::settings();
97 settings
->beginGroup("IDE/interpreter");
99 QString workingDirectory
= settings
->value("runtimeDir").toString();
100 QString configFile
= settings
->value("configFile").toString();
102 settings
->endGroup();
104 QString sclangCommand
;
106 char resourcePath
[PATH_MAX
];
107 sc_GetResourceDirectory(resourcePath
, PATH_MAX
);
109 sclangCommand
= QString(resourcePath
) + "/sclang";
111 sclangCommand
= "sclang";
114 QStringList sclangArguments
;
115 if(!configFile
.isEmpty())
116 sclangArguments
<< "-l" << configFile
;
117 sclangArguments
<< "-i" << "scqt";
119 if(!workingDirectory
.isEmpty())
120 setWorkingDirectory(workingDirectory
);
122 QProcess::start(sclangCommand
, sclangArguments
);
123 bool processStarted
= QProcess::waitForStarted();
124 if (!processStarted
) {
125 emit
statusMessage(tr("Failed to start interpreter!"));
130 void SCProcess::recompileClassLibrary (void)
132 if(state() != QProcess::Running
) {
133 emit
statusMessage("Interpreter is not running!");
141 void SCProcess::stopLanguage (void)
143 if(state() != QProcess::Running
) {
144 emit
statusMessage("Interpreter is not running!");
150 bool finished
= waitForFinished(200);
151 if ( !finished
&& (state() != QProcess::NotRunning
) ) {
157 bool reallyFinished
= waitForFinished(200);
159 emit
statusMessage(tr("Failed to stop interpreter!"));
163 void SCProcess::restartLanguage()
171 void SCProcess::onReadyRead(void)
173 QByteArray out
= QProcess::readAll();
174 QString postString
= QString::fromUtf8(out
);
175 emit
scPost(postString
);
178 void SCProcess::evaluateCode(QString
const & commandString
, bool silent
)
180 if(state() != QProcess::Running
) {
181 emit
statusMessage("Interpreter is not running!");
185 QByteArray bytesToWrite
= commandString
.toUtf8();
186 size_t writtenBytes
= write(bytesToWrite
);
187 if (writtenBytes
!= bytesToWrite
.size()) {
188 emit
statusMessage("Error when passing data to interpreter!");
192 char commandChar
= silent
? '\x1b' : '\x0c';
194 write( &commandChar
, 1 );
197 void SCProcess::onNewIpcConnection()
200 // we can handle only one ipc connection at a time
201 mIpcSocket
->disconnect();
203 mIpcSocket
= mIpcServer
->nextPendingConnection();
204 connect(mIpcSocket
, SIGNAL(disconnected()), this, SLOT(finalizeConnection()));
205 connect(mIpcSocket
, SIGNAL(readyRead()), this, SLOT(onIpcData()));
208 void SCProcess::finalizeConnection()
211 mIpcSocket
->deleteLater();
215 void SCProcess::onIpcData()
217 mIpcData
.append(mIpcSocket
->readAll());
219 QBuffer
receivedData ( &mIpcData
);
220 receivedData
.open ( QIODevice::ReadOnly
);
222 QDataStream
in ( &receivedData
);
223 in
.setVersion ( QDataStream::Qt_4_6
);
226 if ( in
.status() != QDataStream::Ok
)
230 if ( in
.status() != QDataStream::Ok
)
233 mIpcData
.remove ( 0, receivedData
.pos() );
235 emit
response(id
, message
);
238 void SCProcess::onSclangStart()
240 if(!mIpcServer
->isListening()) // avoid a warning on stderr
241 mIpcServer
->listen(mIpcServerName
);
243 QString command
= QString("ScIDE.connect(\"%1\")").arg(mIpcServerName
);
244 evaluateCode ( command
, true );
245 sendActiveDocument();
248 void SCProcess::setActiveDocument(Document
* document
)
251 mCurrentDocumentPath
= document
->filePath();
253 mCurrentDocumentPath
.clear();
255 sendActiveDocument();
258 void SCProcess::sendActiveDocument()
260 if (state() != QProcess::Running
)
263 if (!mCurrentDocumentPath
.isEmpty())
264 evaluateCode(QString("ScIDE.currentPath_(\"%1\")").arg(mCurrentDocumentPath
), true);
266 evaluateCode(QString("ScIDE.currentPath_(nil)"), true);
269 void ScResponder::onResponse( const QString
& selector
, const QString
& data
)
271 static QString
defaultServerRunningChangedSymbol("defaultServerRunningChanged");
272 static QString
introspectionSymbol("introspection");
273 static QString
requestCurrentPathSymbol("requestCurrentPath");
274 static QString
openFileSymbol("openFile");
275 static QString
classLibraryRecompiled("classLibraryRecompiled");
277 if (selector
== defaultServerRunningChangedSymbol
)
278 handleServerRunningChanged(data
);
280 else if (selector
== introspectionSymbol
)
281 emit
newIntrospectionData(data
);
283 else if (selector
== requestCurrentPathSymbol
)
284 Main::scProcess()->sendActiveDocument();
286 else if (selector
== classLibraryRecompiled
)
287 Main::scProcess()->emitClassLibraryRecompiled();
289 else if (selector
== openFileSymbol
)
290 handleOpenFile(data
);
294 void ScResponder::handleOpenFile( const QString
& data
) const
296 std::stringstream stream
;
297 stream
<< data
.toStdString();
298 YAML::Parser
parser(stream
);
301 if (parser
.GetNextDocument(doc
)) {
302 if (doc
.Type() != YAML::NodeType::Sequence
)
306 bool success
= doc
[0].Read(path
);
311 doc
[1].Read(position
);
313 int selectionLength
= 0;
314 doc
[2].Read(selectionLength
);
316 Main::documentManager()->open(QString(path
.c_str()), position
, selectionLength
);
320 void ScResponder::handleServerRunningChanged( const QString
& data
)
322 std::stringstream stream
;
323 stream
<< data
.toStdString();
324 YAML::Parser
parser(stream
);
326 bool serverRunningState
;
327 std::string hostName
;
331 while(parser
.GetNextDocument(doc
)) {
332 assert(doc
.Type() == YAML::NodeType::Sequence
);
334 bool success
= doc
[0].Read(serverRunningState
);
335 if (!success
) return; // LATER: report error?
337 success
= doc
[1].Read(hostName
);
338 if (!success
) return; // LATER: report error?
340 success
= doc
[2].Read(port
);
341 if (!success
) return; // LATER: report error?
344 emit
serverRunningChanged (serverRunningState
, QString(hostName
.c_str()), port
);
348 void ScIntrospectionParserWorker::process(const QString
&input
)
351 ScLanguage::Introspection
*introspection
= new ScLanguage::Introspection (input
);
352 emit
done(introspection
);
353 } catch (std::exception
& e
) {
354 MainWindow::instance()->showStatusMessage(e
.what());