Commit 18fa4865 authored by Fawzi Mohamed's avatar Fawzi Mohamed

consoleprocess: support Terminal.app on mac

Distinguishes the process that starts the terminal from the stub
process, as on mac to support Terminal.app they are different.

Handle the stub not through the process that starts the terminal,
but through the local socket (on *nix).

Replace the blocking wait(...) in the main thread, with a nonblocking
wait in the signal handler when receiving a SIGCHLD, to leave the
main thread able to handle communication with creator.

This change allows the use of terminal emulator commands
that share a single instance or that fork.
So this is also the real fix for QTCREATORBUG-1633 on linux.

If creator crashes the stub and the debugged program live on.
This was done on purpose, it could be changed if considered better.

Task-number: QTCREATORBUG-6371
Task-number: QTCREATORBUG-1633

Change-Id: I4d4fb3a67b1987f4e46e2c603dcefe8c15152ad2
Reviewed-by: default avatarOswald Buddenhagen <oswald.buddenhagen@digia.com>
parent 7db3b660
#! /bin/bash
i=`pwd`
i=${i//\\/\\\\\\\\}
i=${i//\"/\\\\\\\"}
i=${i//\$/\\\\\\\$}
i=${i//\`/\\\\\\\`}
i=\\\"$i\\\"
# ugly escaping: for apple script \ and " need to be escaped, whereas %q takes care of all bash escaping
declare -a args
mydir=`pwd`
mydir=$(printf '%q' "$mydir")
mydir="${mydir//\\/\\\\}"
args[0]="cd ${mydir//\"/\\\"};"
for a in "$@" ; do
x=$(printf '%q ' "$a")
x="${x//\\/\\\\}"
args[${#args[@]}]="${x//\"/\\\"}"
done
mArgs=${args[@]:0}
osascript <<EOF
--Terminal opens a window by default when it is not running, so check
on applicationIsRunning(applicationName)
......@@ -14,7 +21,7 @@ osascript <<EOF
end applicationIsRunning
set terminalWasRunning to applicationIsRunning("Terminal")
set cdScript to "cd $i"
set cdScript to "$mArgs"
tell application "Terminal"
--do script will open a new window if none given, but terminal already opens one if not running
if terminalWasRunning then
......
......@@ -29,6 +29,8 @@
#include "consoleprocess_p.h"
#include <utils/hostosinfo.h>
#include <QSettings>
namespace Utils {
......
......@@ -62,16 +62,23 @@ public:
Environment environment() const;
bool start(const QString &program, const QString &args);
public slots:
void stop();
public:
void setMode(Mode m);
Mode mode() const;
bool isRunning() const; // This reflects the state of the console+stub
qint64 applicationPID() const;
void killProcess();
void killStub();
#ifdef Q_OS_WIN
qint64 applicationMainThreadID() const;
#else
void detachStub();
#endif
int exitCode() const;
......@@ -99,8 +106,8 @@ signals:
void processStopped();
// These reflect the state of the console+stub
void wrapperStarted();
void wrapperStopped();
void stubStarted();
void stubStopped();
private slots:
void stubConnectionAvailable();
......
......@@ -37,6 +37,10 @@
#include <QLocalSocket>
#include <QLocalServer>
QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
#ifdef Q_OS_WIN
# if QT_VERSION >= 0x050000
# include <QWinEventNotifier>
......@@ -51,6 +55,7 @@ namespace Utils {
struct ConsoleProcessPrivate {
ConsoleProcessPrivate();
static QString m_defaultConsoleProcess;
ConsoleProcess::Mode m_mode;
QString m_workingDir;
Environment m_environment;
......@@ -66,6 +71,9 @@ struct ConsoleProcessPrivate {
QProcess m_process;
QByteArray m_stubServerDir;
QSettings *m_settings;
bool m_stubConnected;
qint64 m_stubPid;
QTimer *m_stubConnectTimer;
#else
qint64 m_appMainThreadId;
PROCESS_INFORMATION *m_pid;
......
......@@ -37,6 +37,7 @@
#include <QCoreApplication>
#include <QDir>
#include <QSettings>
#include <QTimer>
#include <sys/stat.h>
#include <sys/types.h>
......@@ -51,7 +52,10 @@ ConsoleProcessPrivate::ConsoleProcessPrivate() :
m_appPid(0),
m_stubSocket(0),
m_tempFile(0),
m_settings(0)
m_settings(0),
m_stubConnected(false),
m_stubPid(0),
m_stubConnectTimer(0)
{
}
......@@ -61,8 +65,6 @@ ConsoleProcess::ConsoleProcess(QObject *parent) :
connect(&d->m_stubServer, SIGNAL(newConnection()), SLOT(stubConnectionAvailable()));
d->m_process.setProcessChannelMode(QProcess::ForwardedChannels);
connect(&d->m_process, SIGNAL(finished(int,QProcess::ExitStatus)),
SLOT(stubExited()));
}
void ConsoleProcess::setSettings(QSettings *settings)
......@@ -159,21 +161,54 @@ bool ConsoleProcess::start(const QString &program, const QString &args)
d->m_tempFile = 0;
return false;
}
d->m_stubConnectTimer = new QTimer(this);
connect(d->m_stubConnectTimer, SIGNAL(timeout()), SLOT(stop()));
d->m_stubConnectTimer->setSingleShot(true);
d->m_stubConnectTimer->start(10000);
d->m_executable = program;
emit wrapperStarted();
return true;
}
void ConsoleProcess::stop()
void ConsoleProcess::killProcess()
{
if (!isRunning())
return;
stubServerShutdown();
if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
d->m_stubSocket->write("k", 1);
d->m_stubSocket->flush();
}
d->m_appPid = 0;
d->m_process.terminate();
if (!d->m_process.waitForFinished(1000))
d->m_process.kill();
d->m_process.waitForFinished();
}
void ConsoleProcess::killStub()
{
if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
d->m_stubSocket->write("s", 1);
d->m_stubSocket->flush();
}
stubServerShutdown();
d->m_stubPid = 0;
}
void ConsoleProcess::detachStub()
{
if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
d->m_stubSocket->write("d", 1);
d->m_stubSocket->flush();
}
stubServerShutdown();
d->m_stubPid = 0;
}
void ConsoleProcess::stop()
{
killProcess();
killStub();
if (isRunning()) {
d->m_process.terminate();
if (!d->m_process.waitForFinished(1000)) {
d->m_process.kill();
d->m_process.waitForFinished();
}
}
}
bool ConsoleProcess::isRunning() const
......@@ -210,7 +245,8 @@ QString ConsoleProcess::stubServerListen()
void ConsoleProcess::stubServerShutdown()
{
delete d->m_stubSocket;
if (d->m_stubSocket)
d->m_stubSocket->deleteLater(); // we might be called from the disconnected signal of m_stubSocket
d->m_stubSocket = 0;
if (d->m_stubServer.isListening()) {
d->m_stubServer.close();
......@@ -220,8 +256,15 @@ void ConsoleProcess::stubServerShutdown()
void ConsoleProcess::stubConnectionAvailable()
{
if (d->m_stubConnectTimer) {
delete d->m_stubConnectTimer;
d->m_stubConnectTimer = 0;
}
d->m_stubConnected = true;
emit stubStarted();
d->m_stubSocket = d->m_stubServer.nextPendingConnection();
connect(d->m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput()));
connect(d->m_stubSocket, SIGNAL(disconnected()), SLOT(stubExited()));
}
static QString errorMsg(int code)
......@@ -238,11 +281,12 @@ void ConsoleProcess::readStubOutput()
emit processError(msgCannotChangeToWorkDir(workingDirectory(), errorMsg(out.mid(10).toInt())));
} else if (out.startsWith("err:exec ")) {
emit processError(msgCannotExecute(d->m_executable, errorMsg(out.mid(9).toInt())));
} else if (out.startsWith("pid ")) {
// Will not need it any more
} else if (out.startsWith("spid ")) {
delete d->m_tempFile;
d->m_tempFile = 0;
d->m_stubPid = out.mid(4).toInt();
} else if (out.startsWith("pid ")) {
d->m_appPid = out.mid(4).toInt();
emit processStarted();
} else if (out.startsWith("exit ")) {
......@@ -257,6 +301,7 @@ void ConsoleProcess::readStubOutput()
emit processStopped();
} else {
emit processError(msgUnexpectedOutput(out));
d->m_stubPid = 0;
d->m_process.terminate();
break;
}
......@@ -269,6 +314,7 @@ void ConsoleProcess::stubExited()
if (d->m_stubSocket && d->m_stubSocket->state() == QLocalSocket::ConnectedState)
d->m_stubSocket->waitForDisconnected();
stubServerShutdown();
d->m_stubPid = 0;
delete d->m_tempFile;
d->m_tempFile = 0;
if (d->m_appPid) {
......@@ -277,7 +323,7 @@ void ConsoleProcess::stubExited()
d->m_appPid = 0;
emit processStopped(); // Maybe it actually did not, but keep state consistent
}
emit wrapperStopped();
emit stubStopped();
}
struct Terminal {
......@@ -293,15 +339,18 @@ static const Terminal knownTerminals[] =
{"rxvt", "-e"},
{"urxvt", "-e"},
{"xfce4-terminal", "-x"},
{"konsole", "--nofork -e"},
{"konsole", "-e"},
{"gnome-terminal", "-x"}
};
QString ConsoleProcess::defaultTerminalEmulator()
{
if (Utils::HostOsInfo::isMacHost())
if (Utils::HostOsInfo::isMacHost()) {
QString termCmd = QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/scripts/openTerminal.command");
if (QFile(termCmd).exists())
return termCmd.replace(QLatin1Char(' '), QLatin1String("\\ "));
return QLatin1String("/usr/X11/bin/xterm");
}
const Environment env = Environment::systemEnvironment();
const int terminalCount = int(sizeof(knownTerminals) / sizeof(knownTerminals[0]));
for (int i = 0; i < terminalCount; ++i) {
......@@ -317,9 +366,6 @@ QString ConsoleProcess::defaultTerminalEmulator()
QStringList ConsoleProcess::availableTerminalEmulators()
{
if (Utils::HostOsInfo::isMacHost())
return QStringList(defaultTerminalEmulator());
QStringList result;
const Environment env = Environment::systemEnvironment();
const int terminalCount = int(sizeof(knownTerminals) / sizeof(knownTerminals[0]));
......@@ -331,6 +377,8 @@ QStringList ConsoleProcess::availableTerminalEmulators()
result.push_back(terminal);
}
}
if (!result.contains(defaultTerminalEmulator()))
result.append(defaultTerminalEmulator());
result.sort();
return result;
}
......
......@@ -151,16 +151,20 @@ bool ConsoleProcess::start(const QString &program, const QString &args)
d->processFinishedNotifier = new QWinEventNotifier(d->m_pid->hProcess, this);
connect(d->processFinishedNotifier, SIGNAL(activated(HANDLE)), SLOT(stubExited()));
emit wrapperStarted();
return true;
}
void ConsoleProcess::stop()
void ConsoleProcess::killProcess()
{
if (d->m_hInferior != NULL) {
TerminateProcess(d->m_hInferior, (unsigned)-1);
cleanupInferior();
}
}
void ConsoleProcess::killStub()
{
if (d->m_pid) {
TerminateProcess(d->m_pid->hProcess, (unsigned)-1);
WaitForSingleObject(d->m_pid->hProcess, INFINITE);
......@@ -168,6 +172,12 @@ void ConsoleProcess::stop()
}
}
void ConsoleProcess::stop()
{
killProcess();
killStub();
}
bool ConsoleProcess::isRunning() const
{
return d->m_pid != 0;
......@@ -192,6 +202,7 @@ void ConsoleProcess::stubServerShutdown()
void ConsoleProcess::stubConnectionAvailable()
{
emit stubStarted();
d->m_stubSocket = d->m_stubServer.nextPendingConnection();
connect(d->m_stubSocket, SIGNAL(readyRead()), SLOT(readStubOutput()));
}
......@@ -281,7 +292,7 @@ void ConsoleProcess::stubExited()
d->m_appCode = -1;
emit processStopped();
}
emit wrapperStopped();
emit stubStopped();
}
QStringList ConsoleProcess::fixWinEnvironment(const QStringList &env)
......
This diff is collapsed.
......@@ -546,7 +546,7 @@ bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMe
SLOT(consoleStubError(QString)));
connect(m_consoleStub.data(), SIGNAL(processStarted()),
SLOT(consoleStubProcessStarted()));
connect(m_consoleStub.data(), SIGNAL(wrapperStopped()),
connect(m_consoleStub.data(), SIGNAL(stubStopped()),
SLOT(consoleStubExited()));
m_consoleStub->setWorkingDirectory(sp.workingDirectory);
if (sp.environment.size())
......
......@@ -103,7 +103,7 @@ void GdbTermEngine::setupEngine()
connect(&m_stubProc, SIGNAL(processError(QString)), SLOT(stubError(QString)));
connect(&m_stubProc, SIGNAL(processStarted()), SLOT(stubStarted()));
connect(&m_stubProc, SIGNAL(wrapperStopped()), SLOT(stubExited()));
connect(&m_stubProc, SIGNAL(stubStopped()), SLOT(stubExited()));
// FIXME: Starting the stub implies starting the inferior. This is
// fairly unclean as far as the state machine and error reporting go.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment