2006-04-02 05:19:13 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2006 Justin Karneges <justin@affinix.com>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "qca_support.h"
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
# include <windows.h>
|
|
|
|
#else
|
|
|
|
# include <sys/termios.h>
|
|
|
|
# include <unistd.h>
|
|
|
|
# include <fcntl.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "qpipe.h"
|
|
|
|
|
|
|
|
namespace QCA {
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// SyncThread
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
static QByteArray getReturnType(const QMetaObject *obj, const QByteArray &method, const QList<QByteArray> argTypes)
|
|
|
|
{
|
|
|
|
for(int n = obj->methodOffset(); n < obj->methodCount(); ++n)
|
|
|
|
{
|
|
|
|
QMetaMethod m = obj->method(n);
|
|
|
|
QByteArray sig = m.signature();
|
|
|
|
int n = sig.indexOf('(');
|
|
|
|
if(n == -1)
|
|
|
|
continue;
|
|
|
|
QByteArray name = sig.mid(0, n);
|
|
|
|
if(name != method)
|
|
|
|
continue;
|
|
|
|
if(m.parameterTypes() != argTypes)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return m.typeName();
|
|
|
|
}
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool invokeMethodWithVariants(QObject *obj, const QByteArray &method, const QVariantList &args, QVariant *ret, Qt::ConnectionType type = Qt::AutoConnection)
|
|
|
|
{
|
|
|
|
// QMetaObject::invokeMethod() has a 10 argument maximum
|
|
|
|
if(args.count() > 10)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
QList<QByteArray> argTypes;
|
|
|
|
for(int n = 0; n < args.count(); ++n)
|
|
|
|
argTypes += args[n].typeName();
|
|
|
|
|
|
|
|
// get return type
|
|
|
|
int metatype = 0;
|
|
|
|
QByteArray retTypeName = getReturnType(obj->metaObject(), method, argTypes);
|
|
|
|
if(!retTypeName.isEmpty())
|
|
|
|
{
|
|
|
|
metatype = QMetaType::type(retTypeName.data());
|
|
|
|
if(metatype == 0) // lookup failed
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QGenericArgument arg[10];
|
|
|
|
for(int n = 0; n < args.count(); ++n)
|
|
|
|
arg[n] = QGenericArgument(args[n].typeName(), args[n].constData());
|
|
|
|
|
|
|
|
QGenericReturnArgument retarg;
|
|
|
|
QVariant retval;
|
|
|
|
if(metatype != 0)
|
|
|
|
{
|
|
|
|
retval = QVariant(metatype, (const void *)0);
|
|
|
|
retarg = QGenericReturnArgument(retval.typeName(), retval.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!QMetaObject::invokeMethod(obj, method.data(), type, retarg, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7], arg[8], arg[9]))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if(retval.isValid() && ret)
|
|
|
|
*ret = retval;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
class SyncThreadAgent;
|
|
|
|
|
|
|
|
class SyncThread : public QThread
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
private:
|
|
|
|
QMutex m;
|
|
|
|
QWaitCondition w;
|
|
|
|
QEventLoop *loop;
|
|
|
|
SyncThreadAgent *agent;
|
|
|
|
bool last_success;
|
|
|
|
QVariant last_ret;
|
|
|
|
|
|
|
|
public:
|
|
|
|
SyncThread(QObject *parent = 0);
|
|
|
|
~SyncThread();
|
|
|
|
|
|
|
|
void start();
|
|
|
|
void stop();
|
|
|
|
|
|
|
|
QVariant call(QObject *obj, const char *method, const QVariantList &args = QVariantList(), bool *ok = 0);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual void run();
|
|
|
|
virtual void atStart() = 0;
|
|
|
|
virtual void atEnd() = 0;
|
|
|
|
|
|
|
|
private slots:
|
|
|
|
void agent_started();
|
|
|
|
void agent_call_ret(bool success, const QVariant &ret);
|
|
|
|
};
|
|
|
|
|
|
|
|
class SyncThreadAgent : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
SyncThreadAgent(QObject *parent = 0) : QObject(parent)
|
|
|
|
{
|
|
|
|
QMetaObject::invokeMethod(this, "started", Qt::QueuedConnection);
|
|
|
|
}
|
|
|
|
|
|
|
|
signals:
|
|
|
|
void started();
|
|
|
|
void call_ret(bool success, const QVariant &ret);
|
|
|
|
|
|
|
|
public slots:
|
|
|
|
void call_do(QObject *obj, const QByteArray &method, const QVariantList &args)
|
|
|
|
{
|
|
|
|
QVariant ret;
|
|
|
|
bool ok = invokeMethodWithVariants(obj, method, args, &ret, Qt::DirectConnection);
|
|
|
|
emit call_ret(ok, ret);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
SyncThread::SyncThread(QObject *parent)
|
|
|
|
:QThread(parent)
|
|
|
|
{
|
|
|
|
qRegisterMetaType<QVariant>("QVariant");
|
|
|
|
qRegisterMetaType<QVariantList>("QVariantList");
|
|
|
|
|
|
|
|
loop = 0;
|
|
|
|
agent = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SyncThread::~SyncThread()
|
|
|
|
{
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SyncThread::start()
|
|
|
|
{
|
|
|
|
QMutexLocker locker(&m);
|
|
|
|
Q_ASSERT(!loop);
|
|
|
|
QThread::start();
|
|
|
|
w.wait(&m);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SyncThread::stop()
|
|
|
|
{
|
|
|
|
QMutexLocker locker(&m);
|
|
|
|
if(!loop)
|
|
|
|
return;
|
|
|
|
QMetaObject::invokeMethod(loop, "quit");
|
|
|
|
w.wait(&m);
|
|
|
|
wait();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant SyncThread::call(QObject *obj, const char *method, const QVariantList &args, bool *ok)
|
|
|
|
{
|
|
|
|
QMutexLocker locker(&m);
|
|
|
|
Q_ASSERT(QMetaObject::invokeMethod(agent, "call_do", Qt::QueuedConnection,
|
|
|
|
Q_ARG(QObject*, obj), Q_ARG(QByteArray, QByteArray(method)), Q_ARG(QVariantList, args)));
|
|
|
|
w.wait(&m);
|
|
|
|
if(ok)
|
|
|
|
*ok = last_success;
|
|
|
|
QVariant v = last_ret;
|
|
|
|
last_ret = QVariant();
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SyncThread::run()
|
|
|
|
{
|
|
|
|
m.lock();
|
|
|
|
loop = new QEventLoop;
|
|
|
|
agent = new SyncThreadAgent;
|
|
|
|
connect(agent, SIGNAL(started()), SLOT(agent_started()), Qt::DirectConnection);
|
|
|
|
connect(agent, SIGNAL(call_ret(bool, const QVariant &)), SLOT(agent_call_ret(bool, const QVariant &)), Qt::DirectConnection);
|
|
|
|
loop->exec();
|
|
|
|
m.lock();
|
|
|
|
atEnd();
|
|
|
|
delete agent;
|
|
|
|
delete loop;
|
|
|
|
agent = 0;
|
|
|
|
loop = 0;
|
|
|
|
w.wakeOne();
|
|
|
|
m.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SyncThread::agent_started()
|
|
|
|
{
|
|
|
|
atStart();
|
|
|
|
w.wakeOne();
|
|
|
|
m.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SyncThread::agent_call_ret(bool success, const QVariant &ret)
|
|
|
|
{
|
|
|
|
QMutexLocker locker(&m);
|
|
|
|
last_success = success;
|
|
|
|
last_ret = ret;
|
|
|
|
w.wakeOne();
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// ConsoleWorker
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class ConsoleWorker : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
private:
|
|
|
|
QPipeEnd in, out;
|
|
|
|
bool started;
|
|
|
|
QByteArray in_left, out_left;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ConsoleWorker(QObject *parent = 0) : QObject(parent), in(this), out(this)
|
|
|
|
{
|
|
|
|
started = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
~ConsoleWorker()
|
|
|
|
{
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void start(Q_PIPE_ID in_id, Q_PIPE_ID out_id)
|
|
|
|
{
|
|
|
|
Q_ASSERT(!started);
|
|
|
|
|
|
|
|
if(in_id != INVALID_Q_PIPE_ID)
|
|
|
|
{
|
|
|
|
in.take(in_id, QPipeDevice::Read);
|
|
|
|
connect(&in, SIGNAL(readyRead()), SLOT(in_readyRead()));
|
|
|
|
connect(&in, SIGNAL(closed()), SLOT(in_closed()));
|
|
|
|
connect(&in, SIGNAL(error(QCA::QPipeEnd::Error)), SLOT(in_error(QCA::QPipeEnd::Error)));
|
|
|
|
in.enable();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(out_id != INVALID_Q_PIPE_ID)
|
|
|
|
{
|
|
|
|
out.take(out_id, QPipeDevice::Write);
|
|
|
|
connect(&out, SIGNAL(bytesWritten(int)), SLOT(out_bytesWritten(int)));
|
|
|
|
out.enable();
|
|
|
|
}
|
|
|
|
|
|
|
|
started = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stop()
|
|
|
|
{
|
|
|
|
if(!started)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(in.isValid())
|
|
|
|
in.finalizeAndRelease();
|
|
|
|
if(out.isValid())
|
|
|
|
out.release();
|
|
|
|
|
|
|
|
in_left = in.read();
|
|
|
|
out_left = out.takeBytesToWrite();
|
|
|
|
|
|
|
|
started = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public slots:
|
2006-04-03 07:24:57 +00:00
|
|
|
bool isValid() const
|
|
|
|
{
|
|
|
|
return in.isValid();
|
|
|
|
}
|
|
|
|
|
2006-04-02 05:19:13 +00:00
|
|
|
void setSecurityEnabled(bool enabled)
|
|
|
|
{
|
|
|
|
in.setSecurityEnabled(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray read(int bytes = -1)
|
|
|
|
{
|
|
|
|
return in.read(bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void write(const QByteArray &a)
|
|
|
|
{
|
|
|
|
out.write(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
QSecureArray readSecure(int bytes = -1)
|
|
|
|
{
|
|
|
|
return in.readSecure(bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void writeSecure(const QSecureArray &a)
|
|
|
|
{
|
|
|
|
out.writeSecure(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
int bytesAvailable() const
|
|
|
|
{
|
|
|
|
return in.bytesAvailable();
|
|
|
|
}
|
|
|
|
|
|
|
|
int bytesToWrite() const
|
|
|
|
{
|
|
|
|
return in.bytesToWrite();
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
QByteArray takeBytesToRead()
|
|
|
|
{
|
|
|
|
QByteArray a = in_left;
|
|
|
|
in_left.clear();
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray takeBytesToWrite()
|
|
|
|
{
|
|
|
|
QByteArray a = out_left;
|
|
|
|
out_left.clear();
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
signals:
|
|
|
|
void readyRead();
|
|
|
|
void bytesWritten(int bytes);
|
|
|
|
void closed();
|
|
|
|
|
|
|
|
private slots:
|
|
|
|
void in_readyRead()
|
|
|
|
{
|
|
|
|
emit readyRead();
|
|
|
|
}
|
|
|
|
|
|
|
|
void out_bytesWritten(int bytes)
|
|
|
|
{
|
|
|
|
emit bytesWritten(bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void in_closed()
|
|
|
|
{
|
|
|
|
emit closed();
|
|
|
|
}
|
|
|
|
|
|
|
|
void in_error(QCA::QPipeEnd::Error)
|
|
|
|
{
|
|
|
|
emit closed();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// ConsoleThread
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class ConsoleThread : public SyncThread
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
ConsoleWorker *worker;
|
|
|
|
QMutex m;
|
|
|
|
QWaitCondition w;
|
|
|
|
Q_PIPE_ID _in_id, _out_id;
|
|
|
|
QByteArray in_left, out_left;
|
|
|
|
|
|
|
|
ConsoleThread(QObject *parent = 0) : SyncThread(parent)
|
|
|
|
{
|
|
|
|
qRegisterMetaType<QSecureArray>("QSecureArray");
|
|
|
|
}
|
|
|
|
|
|
|
|
~ConsoleThread()
|
|
|
|
{
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void start(Q_PIPE_ID in_id, Q_PIPE_ID out_id)
|
|
|
|
{
|
|
|
|
_in_id = in_id;
|
|
|
|
_out_id = out_id;
|
|
|
|
SyncThread::start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void stop()
|
|
|
|
{
|
|
|
|
SyncThread::stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant mycall(QObject *obj, const char *method, const QVariantList &args = QVariantList())
|
|
|
|
{
|
|
|
|
QVariant ret;
|
|
|
|
bool ok;
|
|
|
|
ret = call(obj, method, args, &ok);
|
|
|
|
Q_ASSERT(ok);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-04-03 07:24:57 +00:00
|
|
|
bool isValid()
|
|
|
|
{
|
|
|
|
return mycall(worker, "isValid").toBool();
|
|
|
|
}
|
|
|
|
|
2006-04-02 05:19:13 +00:00
|
|
|
void setSecurityEnabled(bool enabled)
|
|
|
|
{
|
|
|
|
mycall(worker, "setSecurityEnabled", QVariantList() << enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray read(int bytes = -1)
|
|
|
|
{
|
|
|
|
return mycall(worker, "read", QVariantList() << bytes).toByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
void write(const QByteArray &a)
|
|
|
|
{
|
|
|
|
mycall(worker, "write", QVariantList() << a);
|
|
|
|
}
|
|
|
|
|
|
|
|
QSecureArray readSecure(int bytes = -1)
|
|
|
|
{
|
|
|
|
return qVariantValue<QSecureArray>(mycall(worker, "readSecure", QVariantList() << bytes));
|
|
|
|
}
|
|
|
|
|
|
|
|
void writeSecure(const QSecureArray &a)
|
|
|
|
{
|
|
|
|
mycall(worker, "writeSecure", QVariantList() << qVariantFromValue<QSecureArray>(a));
|
|
|
|
}
|
|
|
|
|
|
|
|
int bytesAvailable()
|
|
|
|
{
|
|
|
|
return mycall(worker, "bytesAvailable").toInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
int bytesToWrite()
|
|
|
|
{
|
|
|
|
return mycall(worker, "bytesToWrite").toInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray takeBytesToRead()
|
|
|
|
{
|
|
|
|
QByteArray a = in_left;
|
|
|
|
in_left.clear();
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray takeBytesToWrite()
|
|
|
|
{
|
|
|
|
QByteArray a = out_left;
|
|
|
|
out_left.clear();
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
signals:
|
|
|
|
void readyRead();
|
|
|
|
void bytesWritten(int);
|
|
|
|
void closed();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual void atStart()
|
|
|
|
{
|
|
|
|
worker = new ConsoleWorker;
|
|
|
|
|
|
|
|
// use direct connections here, so that the emits come from
|
|
|
|
// the other thread. we can also connect to our own
|
|
|
|
// signals to avoid having to make slots just to emit.
|
|
|
|
connect(worker, SIGNAL(readyRead()), SIGNAL(readyRead()), Qt::DirectConnection);
|
|
|
|
connect(worker, SIGNAL(bytesWritten(int)), SIGNAL(bytesWritten(int)), Qt::DirectConnection);
|
|
|
|
connect(worker, SIGNAL(closed()), SIGNAL(closed()), Qt::DirectConnection);
|
|
|
|
|
|
|
|
worker->start(_in_id, _out_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void atEnd()
|
|
|
|
{
|
|
|
|
in_left = worker->takeBytesToRead();
|
|
|
|
out_left = worker->takeBytesToWrite();
|
|
|
|
delete worker;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Console
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class ConsolePrivate : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
Console *q;
|
|
|
|
|
|
|
|
bool started;
|
|
|
|
Console::TerminalMode mode;
|
|
|
|
ConsoleThread *thread;
|
|
|
|
ConsoleReference *ref;
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
DWORD old_mode;
|
|
|
|
#else
|
|
|
|
struct termios old_term_attr;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ConsolePrivate(Console *_q) : QObject(_q), q(_q)
|
|
|
|
{
|
|
|
|
started = false;
|
|
|
|
mode = Console::Default;
|
|
|
|
thread = new ConsoleThread(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
~ConsolePrivate()
|
|
|
|
{
|
|
|
|
delete thread;
|
|
|
|
setInteractive(Console::Default);
|
|
|
|
}
|
|
|
|
|
|
|
|
void setInteractive(Console::TerminalMode m)
|
|
|
|
{
|
|
|
|
// no change
|
|
|
|
if(m == mode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(m == Console::Interactive)
|
|
|
|
{
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
GetConsoleMode(h, &old_mode);
|
|
|
|
SetConsoleMode(h, old_mode & (~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT));
|
|
|
|
#else
|
|
|
|
int fd = 0; // stdin
|
|
|
|
struct termios attr;
|
|
|
|
tcgetattr(fd, &attr);
|
|
|
|
old_term_attr = attr;
|
|
|
|
|
|
|
|
attr.c_lflag &= ~(ECHO); // turn off the echo flag
|
|
|
|
attr.c_lflag &= ~(ICANON); // no wait for a newline
|
|
|
|
attr.c_cc[VMIN] = 1; // read at least 1 char
|
2006-04-03 07:24:57 +00:00
|
|
|
attr.c_cc[VTIME] = 0; // set wait time to zero
|
2006-04-02 05:19:13 +00:00
|
|
|
|
|
|
|
// set the new attributes
|
|
|
|
tcsetattr(fd, TCSAFLUSH, &attr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
SetConsoleMode(h, old_mode);
|
|
|
|
#else
|
|
|
|
int fd = 0; // stdin
|
|
|
|
tcsetattr(fd, TCSANOW, &old_term_attr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
mode = m;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static Console *g_console = 0;
|
|
|
|
|
|
|
|
Console::Console(ChannelMode cmode, TerminalMode tmode, QObject *parent)
|
|
|
|
:QObject(parent)
|
|
|
|
{
|
|
|
|
Q_ASSERT(g_console == 0);
|
|
|
|
g_console = this;
|
|
|
|
|
|
|
|
d = new ConsolePrivate(this);
|
|
|
|
|
|
|
|
Q_PIPE_ID in = INVALID_Q_PIPE_ID;
|
|
|
|
Q_PIPE_ID out = INVALID_Q_PIPE_ID;
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
in = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
#else
|
|
|
|
in = 0;
|
|
|
|
#endif
|
|
|
|
if(cmode == ReadWrite)
|
|
|
|
{
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
out = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
#else
|
|
|
|
out = 1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
d->setInteractive(tmode);
|
|
|
|
d->thread->start(in, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
Console::~Console()
|
|
|
|
{
|
|
|
|
shutdown();
|
|
|
|
delete d;
|
|
|
|
g_console = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Console *Console::instance()
|
|
|
|
{
|
|
|
|
return g_console;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::shutdown()
|
|
|
|
{
|
|
|
|
d->thread->stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray Console::bytesLeftToRead()
|
|
|
|
{
|
|
|
|
return d->thread->takeBytesToRead();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray Console::bytesLeftToWrite()
|
|
|
|
{
|
|
|
|
return d->thread->takeBytesToWrite();
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// ConsoleReference
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class ConsoleReferencePrivate : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
ConsoleReference *q;
|
|
|
|
|
|
|
|
Console *console;
|
|
|
|
ConsoleThread *thread;
|
2006-04-03 07:24:57 +00:00
|
|
|
QTimer lateTrigger;
|
|
|
|
bool late_read, late_close;
|
2006-04-02 05:19:13 +00:00
|
|
|
|
2006-04-03 07:24:57 +00:00
|
|
|
ConsoleReferencePrivate(ConsoleReference *_q) : QObject(_q), q(_q), lateTrigger(this)
|
2006-04-02 05:19:13 +00:00
|
|
|
{
|
|
|
|
console = 0;
|
|
|
|
thread = 0;
|
2006-04-03 07:24:57 +00:00
|
|
|
connect(&lateTrigger, SIGNAL(timeout()), SLOT(doLate()));
|
|
|
|
lateTrigger.setSingleShot(true);
|
2006-04-02 05:19:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private slots:
|
2006-04-03 07:24:57 +00:00
|
|
|
void doLate()
|
2006-04-02 05:19:13 +00:00
|
|
|
{
|
2006-04-03 07:24:57 +00:00
|
|
|
QPointer<QObject> self = this;
|
|
|
|
if(late_read)
|
|
|
|
emit q->readyRead();
|
|
|
|
if(!self)
|
|
|
|
return;
|
|
|
|
if(late_close)
|
|
|
|
emit q->closed();
|
2006-04-02 05:19:13 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ConsoleReference::ConsoleReference(QObject *parent)
|
|
|
|
:QObject(parent)
|
|
|
|
{
|
|
|
|
d = new ConsoleReferencePrivate(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
ConsoleReference::~ConsoleReference()
|
|
|
|
{
|
|
|
|
stop();
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
2006-04-03 07:24:57 +00:00
|
|
|
bool ConsoleReference::start(SecurityMode mode)
|
2006-04-02 05:19:13 +00:00
|
|
|
{
|
|
|
|
Console *c = Console::instance();
|
2006-04-03 07:24:57 +00:00
|
|
|
if(!c)
|
|
|
|
return false;
|
2006-04-02 05:19:13 +00:00
|
|
|
|
|
|
|
// one console reference at a time
|
|
|
|
Q_ASSERT(c->d->ref == 0);
|
|
|
|
|
|
|
|
d->console = c;
|
|
|
|
d->thread = d->console->d->thread;
|
|
|
|
d->console->d->ref = this;
|
|
|
|
|
2006-04-03 07:24:57 +00:00
|
|
|
bool valid = d->thread->isValid();
|
|
|
|
int avail = d->thread->bytesAvailable();
|
|
|
|
|
|
|
|
// pipe already closed and no data? consider this an error
|
|
|
|
if(!valid && avail == 0)
|
|
|
|
return false;
|
|
|
|
|
2006-04-02 05:19:13 +00:00
|
|
|
// enable security? it will last for this active session only
|
|
|
|
if(mode == SecurityEnabled)
|
|
|
|
d->thread->setSecurityEnabled(true);
|
|
|
|
|
|
|
|
connect(d->thread, SIGNAL(readyRead()), SIGNAL(readyRead()));
|
|
|
|
connect(d->thread, SIGNAL(bytesWritten(int)), SIGNAL(bytesWritten(int)));
|
|
|
|
connect(d->thread, SIGNAL(closed()), SIGNAL(closed()));
|
|
|
|
|
2006-04-03 07:24:57 +00:00
|
|
|
d->late_read = false;
|
|
|
|
d->late_close = false;
|
|
|
|
|
|
|
|
if(avail > 0)
|
|
|
|
d->late_read = true;
|
|
|
|
|
|
|
|
if(!valid)
|
|
|
|
d->late_close = true;
|
|
|
|
|
|
|
|
if(d->late_read || d->late_close)
|
|
|
|
d->lateTrigger.start();
|
|
|
|
|
|
|
|
return true;
|
2006-04-02 05:19:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ConsoleReference::stop()
|
|
|
|
{
|
2006-04-03 07:24:57 +00:00
|
|
|
d->lateTrigger.stop();
|
2006-04-02 05:19:13 +00:00
|
|
|
|
|
|
|
disconnect(d->thread, 0, this, 0);
|
|
|
|
|
|
|
|
// automatically disable security when we go inactive
|
|
|
|
d->thread->setSecurityEnabled(false);
|
|
|
|
|
|
|
|
d->console->d->ref = 0;
|
|
|
|
d->thread = 0;
|
|
|
|
d->console = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray ConsoleReference::read(int bytes)
|
|
|
|
{
|
|
|
|
return d->thread->read(bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsoleReference::write(const QByteArray &a)
|
|
|
|
{
|
|
|
|
d->thread->write(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
QSecureArray ConsoleReference::readSecure(int bytes)
|
|
|
|
{
|
|
|
|
return d->thread->readSecure(bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConsoleReference::writeSecure(const QSecureArray &a)
|
|
|
|
{
|
|
|
|
d->thread->writeSecure(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ConsoleReference::bytesAvailable() const
|
|
|
|
{
|
|
|
|
return d->thread->bytesAvailable();
|
|
|
|
}
|
|
|
|
|
|
|
|
int ConsoleReference::bytesToWrite() const
|
|
|
|
{
|
|
|
|
return d->thread->bytesToWrite();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "console.moc"
|