diff --git a/src/cmd_line.cpp b/src/cmd_line.cpp index 33ec9b2..154939e 100644 --- a/src/cmd_line.cpp +++ b/src/cmd_line.cpp @@ -43,6 +43,16 @@ CmdLine::CmdLine(QWidget *parent) : QComboBox(parent) connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(checkForHistReset(QString))); } +void CmdLine::cacheTxt(quint8 typeId, QString txt) +{ + Shared::cacheTxt(Shared::TXT_IN, typeId, txt); + + if (!(*Shared::activeDisp)) + { + emit txtInCache(); + } +} + void CmdLine::setFlags(int flgs) { flags |= flgs; @@ -50,7 +60,7 @@ void CmdLine::setFlags(int flgs) void CmdLine::unsetFlags(int flgs) { - flags ^= flgs; + flags &= ~flgs; } void CmdLine::selectionCheck() @@ -105,7 +115,7 @@ void CmdLine::echo(const QString &line) { if (!line.trimmed().isEmpty()) { - emit mainTxtOut(line + "\n\n"); + cacheTxt(TEXT, line + "\n\n"); } } @@ -162,7 +172,7 @@ void CmdLine::toHost(const QString &cmdName, const QString &args) if (!Shared::hostCmds->values().contains(cmdName)) { - emit errTxtOut("err: No such command: " + cmdName + "\n\n"); + cacheTxt(ERR, "err: No such command: " + cmdName + "\n\n"); } else if (Shared::genfileCmds->contains(cmdId)) { diff --git a/src/cmd_line.h b/src/cmd_line.h index 65cf81e..d60a405 100644 --- a/src/cmd_line.h +++ b/src/cmd_line.h @@ -35,6 +35,7 @@ private: void echo(const QString &line); void syncHistToFile(); void syncHistFromFile(); + void cacheTxt(quint8 typeId, QString txt); void duplicateScan(const QString &txt); void toHost(const QString &cmdName, const QString &args); void toLocalCmd(const QString &cmdName, const QString &args); @@ -64,8 +65,7 @@ signals: void dataToHookedHost(const QByteArray &data, uchar dType); void dataToGenFile(quint16 cmdId, const QByteArray &data); void dataToHookedGenFile(const QByteArray &data); - void mainTxtOut(const QString &txt); - void errTxtOut(const QString &txt); + void txtInCache(); }; #endif // CMDLINE_H diff --git a/src/cmd_objs/bookmarks.cpp b/src/cmd_objs/bookmarks.cpp index 649b1b6..a6bbda9 100644 --- a/src/cmd_objs/bookmarks.cpp +++ b/src/cmd_objs/bookmarks.cpp @@ -91,7 +91,7 @@ void SaveBookmark::run(const QString &name, QStringList &args) } else { - emit errTxtOut("err: Could not open the bookmark file for writing, reason: " + file.errorString() + "\n"); + cacheTxt(ERR, "err: Could not open the bookmark file for writing, reason: " + file.errorString() + "\n"); } file.close(); @@ -99,7 +99,8 @@ void SaveBookmark::run(const QString &name, QStringList &args) void SaveBookmark::askOverwrite() { - emit mainTxtOut("'" + baseName + "' already exists. do you want to overwrite? (y/n): "); + cacheTxt(TEXT, "'" + baseName + "' already exists. do you want to overwrite? (y/n): "); + emit setUserIO(LOCAL_HOOK); } @@ -129,7 +130,7 @@ void SaveBookmark::dataIn(const QString &argsLine) if (name.isEmpty()) { - emit errTxtOut("err: Bookmark name (-name) not given.\n"); + cacheTxt(ERR, "err: Bookmark name (-name) not given.\n"); } else { @@ -151,21 +152,21 @@ void SaveBookmark::dataIn(const QString &argsLine) void ListBookmarks::dataIn(const QString &argsLine) { - Q_UNUSED(argsLine); + Q_UNUSED(argsLine) QStringList list = QDir(appDataDir() + BOOKMARK_FOLDER).entryList(QStringList() << "*.json", QDir::Files, QDir::Name); if (list.isEmpty()) { - emit mainTxtOut("Empty...\n"); + cacheTxt(TEXT, "Empty...\n"); } else { - emit mainTxtOut("Bookmarks:\n\n"); + cacheTxt(TEXT, "Bookmarks:\n\n"); for (int i = 0; i < list.size(); ++i) { - emit mainTxtOut(" " + QFileInfo(list[i]).baseName() + "\n"); + cacheTxt(TEXT, " " + QFileInfo(list[i]).baseName() + "\n"); } } } @@ -177,7 +178,7 @@ void DeleteBookmark::dataIn(const QString &argsLine) if (name.isEmpty()) { - emit errTxtOut("err: Bookmark name (-name) not given.\n"); + cacheTxt(ERR, "err: Bookmark name (-name) not given.\n"); } else { @@ -185,7 +186,7 @@ void DeleteBookmark::dataIn(const QString &argsLine) if (!QFile::remove(path)) { - emit errTxtOut("err: Could not delete the bookmark, the file might not exist or you don't have write permissions.\n"); + cacheTxt(ERR, "err: Could not delete the bookmark, the file might not exist or you don't have write permissions.\n"); } } } @@ -197,7 +198,7 @@ void SeeBookmark::dataIn(const QString &argsLine) if (name.isEmpty()) { - emit errTxtOut("err: Bookmark name (-name) not given.\n"); + cacheTxt(ERR, "err: Bookmark name (-name) not given.\n"); } else { @@ -209,12 +210,12 @@ void SeeBookmark::dataIn(const QString &argsLine) { QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); - emit mainTxtOut("address: " + doc.object().value("address").toString() + "\n"); - emit mainTxtOut("port: " + QString::number(doc.object().value("port").toInt()) + "\n"); + cacheTxt(TEXT, "address: " + doc.object().value("address").toString() + "\n"); + cacheTxt(TEXT, "port: " + QString::number(doc.object().value("port").toInt()) + "\n"); } else { - emit errTxtOut("err: Could not open the requested bookmark file for reading, reason: " + file.errorString() + '\n'); + cacheTxt(ERR, "err: Could not open the requested bookmark file for reading, reason: " + file.errorString() + '\n'); } file.close(); diff --git a/src/cmd_objs/command.cpp b/src/cmd_objs/command.cpp index 6069ce8..77bb92e 100644 --- a/src/cmd_objs/command.cpp +++ b/src/cmd_objs/command.cpp @@ -21,10 +21,7 @@ Command::Command(QObject *parent) : QObject(parent) // the QObject::objectName property determines the command // name for any object inheriting this object. avoid using // spaces and keep in mind that all command names are case - // insensitive. avoid starting the command with the char - // defined in CMD_ESCAPE, this char is used to send - // commands directly to the host in case of naming - // conflicts with the host commands. + // insensitive. setObjectName("do_nothing"); @@ -32,6 +29,16 @@ Command::Command(QObject *parent) : QObject(parent) connect(this, &Command::unsetUserIO, this, &Command::unsetHook); } +void Command::cacheTxt(quint8 typeId, QString txt) +{ + Shared::cacheTxt(Shared::TXT_IN, typeId, txt); + + if (!(*Shared::activeDisp)) + { + emit txtInCache(); + } +} + void Command::setHook(int flgs) { if (flgs & LOCAL_HOOK) @@ -112,5 +119,5 @@ void Command::hookedCmdCall(const QString &argsLine) void Command::postExec() { - emit mainTxtOut("\nFinished: " + objectName() + "\n\n"); + cacheTxt(TEXT, "\nFinished: " + objectName() + "\n\n"); } diff --git a/src/cmd_objs/command.h b/src/cmd_objs/command.h index 9da099c..f3adc6e 100644 --- a/src/cmd_objs/command.h +++ b/src/cmd_objs/command.h @@ -31,6 +31,7 @@ private slots: protected: bool activeHook(); + void cacheTxt(quint8 typeId, QString txt); public: @@ -54,9 +55,6 @@ public slots: signals: - void mainTxtOut(const QString &txt); - void errTxtOut(const QString &txt); - void bigTxtOut(const QString &txt); void setUserIO(int flgs); void unsetUserIO(int flgs); void setMaxLines(int value); @@ -68,6 +66,7 @@ signals: void disconnectHost(); void colorsChanged(); void fontChanged(); + void txtInCache(); }; #endif // COMMAND_H diff --git a/src/cmd_objs/exec.cpp b/src/cmd_objs/exec.cpp index 4dfbe64..7e8d8d0 100644 --- a/src/cmd_objs/exec.cpp +++ b/src/cmd_objs/exec.cpp @@ -115,7 +115,7 @@ void Connect::dataIn(const QString &argsLine) } else { - emit errTxtOut("err: Could not open the requested bookmark for reading, reason: " + file.errorString() + "\n"); + cacheTxt(ERR, "err: Could not open the requested bookmark for reading, reason: " + file.errorString() + "\n"); } file.close(); @@ -123,15 +123,15 @@ void Connect::dataIn(const QString &argsLine) if (Shared::hostAddress->isEmpty()) { - emit errTxtOut("err: Host address is empty.\n"); + cacheTxt(ERR, "err: Host address is empty.\n"); } else if (QHostAddress(*Shared::hostAddress).isNull()) { - emit errTxtOut("err: '" + *Shared::hostAddress + "' is not a valid address.\n"); + cacheTxt(ERR, "err: '" + *Shared::hostAddress + "' is not a valid address.\n"); } else if (*Shared::hostPort == 0) { - emit errTxtOut("err: The host port cannot be 0.\n"); + cacheTxt(ERR, "err: The host port cannot be 0.\n"); } else { @@ -160,6 +160,8 @@ void Term::dataIn(const QString &argsLine) { Q_UNUSED(argsLine) + Shared::cacheTxt(Shared::TXT_CLEAR); + emit termHostCmd(); } diff --git a/src/cmd_objs/info.cpp b/src/cmd_objs/info.cpp index b708305..f8a1249 100644 --- a/src/cmd_objs/info.cpp +++ b/src/cmd_objs/info.cpp @@ -39,7 +39,7 @@ void About::dispInfo(Command *cmdObj) txtOut << "" << endl; wordWrap("usage: ", txtOut, cmdObj->longText(), Shared::mainWidget); - emit mainTxtOut(txt); + cacheTxt(TEXT, txt); } bool About::dispClientCmd(const QString &cmdName) @@ -108,7 +108,7 @@ void About::dataIn(const QString &argsLine) if (!dispInfo(cmdName)) { - emit errTxtOut("err: No such command: '" + cmdName + "'\n"); + cacheTxt(ERR, "err: No such command: '" + cmdName + "'\n"); } } else @@ -136,7 +136,7 @@ void About::dataIn(const QString &argsLine) txtOut << endl << endl << "for more detailed information about a command type: about " << endl << endl; - emit mainTxtOut(txt); + cacheTxt(TEXT, txt); } } diff --git a/src/cmd_objs/status.cpp b/src/cmd_objs/status.cpp index 7e31013..c249fad 100644 --- a/src/cmd_objs/status.cpp +++ b/src/cmd_objs/status.cpp @@ -66,5 +66,5 @@ void Status::dataIn(const QString &argsLine) } } - emit mainTxtOut(txt); + cacheTxt(TEXT, txt); } diff --git a/src/cmd_objs/style.cpp b/src/cmd_objs/style.cpp index bf3a7e9..670f748 100644 --- a/src/cmd_objs/style.cpp +++ b/src/cmd_objs/style.cpp @@ -117,9 +117,9 @@ void SetColors::dataIn(const QString &argsLine) QJsonObject obj = localData->value("text_settings").toObject(); - emit mainTxtOut("text: " + obj.value("text_color").toString() + "\n"); - emit mainTxtOut("error: " + obj.value("err_color").toString() + "\n"); - emit mainTxtOut("bg: " + obj.value("bg_color").toString() + "\n"); + cacheTxt(TEXT, "text: " + obj.value("text_color").toString() + "\n"); + cacheTxt(TEXT, "error: " + obj.value("err_color").toString() + "\n"); + cacheTxt(TEXT, "bg: " + obj.value("bg_color").toString() + "\n"); if (changed) emit colorsChanged(); } @@ -134,8 +134,8 @@ void SetFont::dataIn(const QString &argsLine) QJsonObject obj = localData->value("text_settings").toObject(); - emit mainTxtOut("family: " + obj.value("font_family").toString() + "\n"); - emit mainTxtOut("size: " + obj.value("font_size").toString() + "\n"); + cacheTxt(TEXT, "family: " + obj.value("font_family").toString() + "\n"); + cacheTxt(TEXT, "size: " + obj.value("font_size").toString() + "\n"); if (changed) emit fontChanged(); } @@ -147,7 +147,7 @@ void SetMaxLines::dataIn(const QString &argsLine) if (valStr.isEmpty()) { - emit mainTxtOut(QString::number(localData->value("max_lines").toInt()) + "\n"); + cacheTxt(TEXT, QString::number(localData->value("max_lines").toInt()) + "\n"); } else { @@ -156,11 +156,11 @@ void SetMaxLines::dataIn(const QString &argsLine) if (!ok) { - emit errTxtOut("err: '" + valStr + "' is not a valid integer.\n"); + cacheTxt(ERR, "err: '" + valStr + "' is not a valid integer.\n"); } else if ((val < 50) || (val > 100000)) { - emit errTxtOut("err: The value must range 50 - 100000.\n"); + cacheTxt(ERR, "err: The value must range 50 - 100000.\n"); } else { diff --git a/src/common.cpp b/src/common.cpp index 69923be..c7848be 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -383,6 +383,7 @@ bool argExists(const QString &key, const QStringList &args) } bool *Shared::connectedToHost = nullptr; +bool *Shared::activeDisp = nullptr; QJsonObject *Shared::localData = nullptr; QHash *Shared::clientCmds = nullptr; QHash *Shared::hostCmds = nullptr; @@ -403,5 +404,75 @@ Genfile *Shared::genFile = nullptr; MainWindow *Shared::mainWin = nullptr; Session *Shared::session = nullptr; TextBody *Shared::textBody = nullptr; +TextWorker *Shared::textWorker = nullptr; ContextReloader *Shared::contextReloader = nullptr; QWidget *Shared::mainWidget = nullptr; +QList *Shared::idCache = nullptr; +QList *Shared::txtCache = nullptr; +ThreadKiller *Shared::theadKiller = nullptr; + +bool Shared::cacheTxt(CacheOp op, quint8 &typeId, QString &txt) +{ + QMutex mutex; + bool ret = false; + + mutex.lock(); + + if (op == TXT_IN) + { + if ((typeId == TEXT) || (typeId == ERR) || (typeId == BIG_TEXT) || (typeId == PRIV_TEXT)) + { + idCache->append(typeId); + txtCache->append(txt); + + ret = true; + } + } + else if (op == TXT_OUT) + { + if (!idCache->isEmpty() && !txtCache->isEmpty()) + { + typeId = idCache->takeFirst(); + txt = txtCache->takeFirst(); + ret = true; + } + } + else if (op == TXT_CLEAR) + { + *Shared::activeDisp = false; + + idCache->clear(); + txtCache->clear(); + } + else if (op == TXT_IS_EMPTY) + { + ret = idCache->isEmpty() && txtCache->isEmpty(); + } + + mutex.unlock(); + + return ret; +} + +bool Shared::cacheTxt(CacheOp op) +{ + quint8 unusedId = 0; + QString unusedTxt; + + return cacheTxt(op, unusedId, unusedTxt); +} + +ThreadKiller::ThreadKiller(QObject *parent) : QObject(parent) +{ + threadCount = 3; +} + +void ThreadKiller::threadFinished() +{ + threadCount--; + + if (threadCount == 0) + { + QCoreApplication::instance()->quit(); + } +} diff --git a/src/common.h b/src/common.h index 00dc474..8dca158 100644 --- a/src/common.h +++ b/src/common.h @@ -62,18 +62,19 @@ #include #include #include +#include #include "cmd_objs/long_txt.h" #define DEFAULT_HIST_LIMIT 100 -#define DEFAULT_MAX_LINES 5000 +#define DEFAULT_MAX_LINES 1000 #define RDBUFF 128000 #define TXT_CODEC "UTF-16LE" #define BOOKMARK_FOLDER "bookmarks" #define CONFIG_FILENAME "config.json" #define APP_NAME "Cmdr" #define APP_TARGET "cmdr" -#define APP_VERSION "2.0.0" +#define APP_VERSION "2.1.0" enum TypeID : quint8 { @@ -177,8 +178,10 @@ class Genfile; class MainWindow; class Session; class TextBody; +class TextWorker; class ContextReloader; class HostDoc; +class ThreadKiller; class Shared : public QObject { @@ -187,6 +190,7 @@ class Shared : public QObject public: static bool *connectedToHost; + static bool *activeDisp; static QByteArray *sessionId; static ushort *servMajor; static ushort *servMinor; @@ -200,6 +204,8 @@ public: static QHash *hostCmds; static QHash *genfileCmds; static QHash *hostDocs; + static QList *idCache; + static QList *txtCache; static QJsonObject *localData; static quint16 *termCmdId; static CmdLine *cmdLine; @@ -207,8 +213,21 @@ public: static MainWindow *mainWin; static Session *session; static TextBody *textBody; + static TextWorker *textWorker; static ContextReloader *contextReloader; static QWidget *mainWidget; + static ThreadKiller *theadKiller; + + enum CacheOp + { + TXT_IN, + TXT_OUT, + TXT_CLEAR, + TXT_IS_EMPTY + }; + + static bool cacheTxt(CacheOp op); + static bool cacheTxt(CacheOp op, quint8 &typeId, QString &txt); explicit Shared(QObject *parent = nullptr); }; @@ -226,5 +245,26 @@ public slots: void reloadCmdLine(); }; +class ThreadKiller : public QObject +{ + Q_OBJECT + +private: + + int threadCount; + +public: + + explicit ThreadKiller(QObject *parent = nullptr); + +public slots: + + void threadFinished(); + +signals: + + void quitThreads(); +}; + #endif // COMMON_H diff --git a/src/gen_file.cpp b/src/gen_file.cpp index 3d19a48..33544c2 100644 --- a/src/gen_file.cpp +++ b/src/gen_file.cpp @@ -16,16 +16,27 @@ // along with Cmdr under the LICENSE.md file. If not, see // . -Genfile::Genfile(QObject *parent) : QObject(parent) +Genfile::Genfile(QObject *parent) : QObject(nullptr) { file = new QFile(this); hook = 0; finished(); + connect(parent, &QObject::destroyed, this, &QObject::deleteLater); connect(this, &Genfile::rdFileLoop, this, &Genfile::hookedDataIn); } +void Genfile::cacheTxt(quint8 typeId, QString txt) +{ + Shared::cacheTxt(Shared::TXT_IN, typeId, txt); + + if (!(*Shared::activeDisp)) + { + emit txtInCache(); + } +} + void Genfile::finished() { flags = 0; @@ -42,6 +53,7 @@ void Genfile::finished() file->close(); } + emit unsetUserIO(GEN_HOOK); emit enableGenFile(false); } @@ -50,7 +62,7 @@ void Genfile::askOverwrite() flags |= CONFIRM_NEEDED; emit setUserIO(GEN_HOOK); - emit mainTxtOut("About to overwrite file: '" + localFile + "' do you want to continue? (y/n): "); + emit cacheTxt(TEXT, "About to overwrite file: '" + localFile + "' do you want to continue? (y/n): "); } bool Genfile::seekToOffset() @@ -68,7 +80,7 @@ bool Genfile::seekToOffset() if (!ok) { - emit errTxtOut("err: Invalid offset was provided: " + offs + "\n"); + cacheTxt(ERR, "err: Invalid offset was provided: " + offs + "\n"); } ret = file->seek(pos); @@ -88,7 +100,7 @@ bool Genfile::lenOk() } else if (len.isEmpty()) { - emit errTxtOut("err: The -len parameter is empty.\n"); + cacheTxt(ERR, "err: The -len parameter is empty.\n"); } else { @@ -113,20 +125,17 @@ void Genfile::setupForWriting() if (!file->open(mode)) { - emit errTxtOut("err: Could not open the client file for writing. reason: " + file->errorString() + "\n"); - + cacheTxt(ERR, "err: Could not open the client file for writing. reason: " + file->errorString() + "\n"); finished(); } else if (!seekToOffset()) { - emit errTxtOut("err: Could not seek to offset postion: " + offs + " of the client file.\n"); - + cacheTxt(ERR, "err: Could not seek to offset postion: " + offs + " of the client file.\n"); finished(); } else if (!lenOk()) { - emit errTxtOut("err: The -len parameter (" + len + ") is invalid.\n"); - + cacheTxt(ERR, "err: The -len parameter (" + len + ") is invalid.\n"); finished(); } else @@ -143,20 +152,17 @@ void Genfile::setupForReading() if (!file->open(QFile::ReadOnly)) { - emit errTxtOut("err: Could not open the client file for reading. reason: " + file->errorString() + "\n"); - + cacheTxt(ERR, "err: Could not open the client file for reading. reason: " + file->errorString() + "\n"); finished(); } else if (!seekToOffset()) { - emit errTxtOut("err: Could not seek to offset postion: " + offs + " of the client file.\n"); - + cacheTxt(ERR, "err: Could not seek to offset postion: " + offs + " of the client file.\n"); finished(); } else if (!lenOk()) { - emit errTxtOut("err: The -len parameter (" + len + ") is invalid.\n"); - + cacheTxt(ERR, "err: The -len parameter (" + len + ") is invalid.\n"); finished(); } } @@ -187,7 +193,7 @@ bool Genfile::wrToFile(const QByteArray &data) } else { - emit errTxtOut("err: Client write failure - " + file->errorString() + "\n"); + cacheTxt(ERR, "err: Client write failure - " + file->errorString() + "\n"); } return ret; @@ -231,7 +237,7 @@ bool Genfile::rdFromFile() } else { - emit errTxtOut("err: Client read failure - " + file->errorString() + "\n"); + cacheTxt(ERR, "err: Client read failure - " + file->errorString() + "\n"); } return ret; @@ -322,8 +328,7 @@ void Genfile::dataIn(quint16 cmdId, const QByteArray &data) } else { - emit errTxtOut("err: The host did not return -to_host or -from_host, making this command call ambiguous.\n"); - + cacheTxt(ERR, "err: The host did not return -to_host or -from_host, making this command call ambiguous.\n"); finished(); } } diff --git a/src/gen_file.h b/src/gen_file.h index 793ebda..d158b41 100644 --- a/src/gen_file.h +++ b/src/gen_file.h @@ -54,6 +54,7 @@ private: void askOverwrite(); void setupForWriting(); void setupForReading(); + void cacheTxt(quint8 typeId, QString txt); QByteArray autoFill(const QByteArray &data); qint64 getRdBuff(); @@ -69,9 +70,8 @@ public: signals: + void txtInCache(); void enableGenFile(bool state); - void mainTxtOut(const QString &txt); - void errTxtOut(const QString &txt); void setUserIO(int flgs); void unsetUserIO(int flgs); void dataOut(quint16 cmdId, const QByteArray &data, uchar typeID = GEN_FILE); diff --git a/src/main.cpp b/src/main.cpp index 1f81244..fe02173 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,6 +37,7 @@ void setupClientCmds(); void setupCmdLine(); void setupGenFile(); void setupSession(); +void setupText(); int main(int argc, char *argv[]) { @@ -46,6 +47,7 @@ int main(int argc, char *argv[]) app.setApplicationVersion(APP_VERSION); bool connected = false; + bool activeDisp = false; ushort servMajor = 0; ushort servMinor = 0; ushort servPatch = 0; @@ -59,6 +61,8 @@ int main(int argc, char *argv[]) QHash clientCmds; QHash hostDocs; QHash hostCmds; + QList idCache; + QList txtCache; QJsonObject localData; loadLocalData(&localData); @@ -79,9 +83,14 @@ int main(int argc, char *argv[]) Shared::termCmdId = &termCmdId; Shared::localData = &localData; Shared::clientHookedCmd = &clientHookCmd; + Shared::idCache = &idCache; + Shared::txtCache = &txtCache; + Shared::activeDisp = &activeDisp; Shared::contextReloader = new ContextReloader(&app); + Shared::theadKiller = new ThreadKiller(&app); Shared::session = new Session(&app); Shared::mainWin = new MainWindow(); + Shared::textWorker = new TextWorker(&app); Shared::textBody = new TextBody(Shared::mainWin); Shared::cmdLine = new CmdLine(Shared::mainWin); Shared::genFile = new Genfile(&app); @@ -109,6 +118,7 @@ int main(int argc, char *argv[]) setupCmdLine(); setupGenFile(); setupSession(); + setupText(); Shared::mainWin->setTextBody(Shared::textBody); Shared::mainWin->setCmdLine(Shared::cmdLine); @@ -138,18 +148,21 @@ void ContextReloader::reloadCmdLine() void setupClientCmds() { - Session *session = Shared::session; - CmdLine *cmdLine = Shared::cmdLine; - TextBody *textBody = Shared::textBody; - Genfile *genFile = Shared::genFile; - MainWindow *mainWin = Shared::mainWin; - ContextReloader *reloader = Shared::contextReloader; + Session *session = Shared::session; + CmdLine *cmdLine = Shared::cmdLine; + TextWorker *textWorker = Shared::textWorker; + TextBody *textBody = Shared::textBody; + Genfile *genFile = Shared::genFile; + MainWindow *mainWin = Shared::mainWin; + ContextReloader *reloader = Shared::contextReloader; + ThreadKiller *killer = Shared::theadKiller; + + QObject::connect(mainWin, &MainWindow::closeApp, killer, &ThreadKiller::quitThreads); for (auto command : Shared::clientCmds->values()) { - QObject::connect(command, &Command::mainTxtOut, textBody, &TextBody::addMainTxt); - QObject::connect(command, &Command::errTxtOut, textBody, &TextBody::addErrTxt); - QObject::connect(command, &Command::bigTxtOut, textBody, &TextBody::addBigTxt); + QObject::connect(command, &Command::colorsChanged, textWorker, &TextWorker::loadSettings); + QObject::connect(command, &Command::txtInCache, textWorker, &TextWorker::dumpTxtCache); QObject::connect(cmdLine, &CmdLine::dataToCommandObj, command, &Command::cmdCall); QObject::connect(cmdLine, &CmdLine::dataToHookedCmdObj, command, &Command::hookedCmdCall); @@ -164,7 +177,7 @@ void setupClientCmds() QObject::connect(command, &Command::quitApp, session, &Session::disconnectFromServ); QObject::connect(command, &Command::disconnectHost, session, &Session::disconnectFromServ); - QObject::connect(command, &Command::quitApp, QCoreApplication::instance(), &QCoreApplication::quit); + QObject::connect(command, &Command::quitApp, killer, &ThreadKiller::quitThreads); QObject::connect(command, &Command::termHostCmd, genFile, &Genfile::finished); QObject::connect(command, &Command::quitApp, genFile, &Genfile::finished); @@ -182,27 +195,28 @@ void setupClientCmds() void setupCmdLine() { - Session *session = Shared::session; - CmdLine *cmdLine = Shared::cmdLine; - TextBody *textBody = Shared::textBody; - Genfile *genFile = Shared::genFile; + Session *session = Shared::session; + CmdLine *cmdLine = Shared::cmdLine; + TextWorker *textWorker = Shared::textWorker; + Genfile *genFile = Shared::genFile; QObject::connect(cmdLine, &CmdLine::dataToHost, session, &Session::binToServer); QObject::connect(cmdLine, &CmdLine::dataToHookedHost, session, &Session::hookedBinToServer); - QObject::connect(cmdLine, &CmdLine::mainTxtOut, textBody, &TextBody::addMainTxt); - QObject::connect(cmdLine, &CmdLine::errTxtOut, textBody, &TextBody::addErrTxt); - QObject::connect(cmdLine, &CmdLine::dataToGenFile, genFile, &Genfile::dataIn); QObject::connect(cmdLine, &CmdLine::dataToHookedGenFile, genFile, &Genfile::hookedDataIn); + + QObject::connect(cmdLine, &CmdLine::txtInCache, textWorker, &TextWorker::dumpTxtCache); } void setupGenFile() { - Session *session = Shared::session; - CmdLine *cmdLine = Shared::cmdLine; - TextBody *textBody = Shared::textBody; - Genfile *genFile = Shared::genFile; + Session *session = Shared::session; + CmdLine *cmdLine = Shared::cmdLine; + TextWorker *textWorker = Shared::textWorker; + Genfile *genFile = Shared::genFile; + ThreadKiller *killer = Shared::theadKiller; + QThread *genThr = new QThread(QCoreApplication::instance()); QObject::connect(genFile, &Genfile::dataOut, session, &Session::binToServer); QObject::connect(genFile, &Genfile::hookedDataOut, session, &Session::hookedBinToServer); @@ -211,19 +225,23 @@ void setupGenFile() QObject::connect(genFile, &Genfile::setUserIO, cmdLine, &CmdLine::setFlags); QObject::connect(genFile, &Genfile::unsetUserIO, cmdLine, &CmdLine::unsetFlags); - QObject::connect(genFile, &Genfile::mainTxtOut, textBody, &TextBody::addMainTxt); - QObject::connect(genFile, &Genfile::errTxtOut, textBody, &TextBody::addErrTxt); + QObject::connect(genFile, &Genfile::txtInCache, textWorker, &TextWorker::dumpTxtCache); + + QObject::connect(genThr, &QThread::finished, killer, &ThreadKiller::threadFinished); + QObject::connect(killer, &ThreadKiller::quitThreads, genThr, &QThread::quit); + + genFile->moveToThread(genThr); + genThr->start(); } void setupSession() { - Session *session = Shared::session; - CmdLine *cmdLine = Shared::cmdLine; - TextBody *textBody = Shared::textBody; - Genfile *genFile = Shared::genFile; - QThread *sesThr = new QThread(nullptr); - - QObject::connect(session, &Session::destroyed, sesThr, &QThread::quit); + Session *session = Shared::session; + CmdLine *cmdLine = Shared::cmdLine; + TextWorker *textWorker = Shared::textWorker; + Genfile *genFile = Shared::genFile; + ThreadKiller *killer = Shared::theadKiller; + QThread *sesThr = new QThread(QCoreApplication::instance()); QObject::connect(session, &Session::hostFinished, genFile, &Genfile::finished); QObject::connect(session, &Session::toGenFile, genFile, &Genfile::hookedDataIn); @@ -231,12 +249,31 @@ void setupSession() QObject::connect(session, &Session::setUserIO, cmdLine, &CmdLine::setFlags); QObject::connect(session, &Session::unsetUserIO, cmdLine, &CmdLine::unsetFlags); - QObject::connect(session, &Session::bigTxtOut, textBody, &TextBody::addBigTxt); - QObject::connect(session, &Session::mainTxtOut, textBody, &TextBody::addMainTxt); - QObject::connect(session, &Session::errTxtOut, textBody, &TextBody::addErrTxt); + QObject::connect(session, &Session::txtInCache, textWorker, &TextWorker::dumpTxtCache); - QObject::connect(sesThr, &QThread::finished, sesThr, &QThread::deleteLater); + QObject::connect(sesThr, &QThread::finished, killer, &ThreadKiller::threadFinished); + QObject::connect(killer, &ThreadKiller::quitThreads, sesThr, &QThread::quit); session->moveToThread(sesThr); sesThr->start(); } + +void setupText() +{ + TextWorker *textWorker = Shared::textWorker; + CmdLine *cmdLine = Shared::cmdLine; + TextBody *textBody = Shared::textBody; + ThreadKiller *killer = Shared::theadKiller; + QThread *txtThr = new QThread(QCoreApplication::instance()); + + QObject::connect(textWorker, &TextWorker::setUserIO, cmdLine, &CmdLine::setFlags); + QObject::connect(textWorker, &TextWorker::unsetUserIO, cmdLine, &CmdLine::unsetFlags); + + QObject::connect(textWorker, &TextWorker::htmlOut, textBody, &TextBody::htmlIn); + + QObject::connect(txtThr, &QThread::finished, killer, &ThreadKiller::threadFinished); + QObject::connect(killer, &ThreadKiller::quitThreads, txtThr, &QThread::quit); + + textWorker->moveToThread(txtThr); + txtThr->start(); +} diff --git a/src/main_ui.cpp b/src/main_ui.cpp index 2972963..0f2f052 100644 --- a/src/main_ui.cpp +++ b/src/main_ui.cpp @@ -75,6 +75,13 @@ void MainWindow::resizeEvent(QResizeEvent *event) QMainWindow::resizeEvent(event); } +void MainWindow::closeEvent(QCloseEvent *event) +{ + emit closeApp(); + + event->ignore(); +} + void MainWindow::setCmdLine(QWidget *widget) { widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); diff --git a/src/main_ui.h b/src/main_ui.h index e869f45..3159c3c 100644 --- a/src/main_ui.h +++ b/src/main_ui.h @@ -29,6 +29,7 @@ private: QVBoxLayout *mainLayout; void resizeEvent(QResizeEvent *event); + void closeEvent(QCloseEvent *event); void saveWindowSize(); public: @@ -42,6 +43,7 @@ public: signals: void startup(); + void closeApp(); }; #endif // USER_IO_H diff --git a/src/session.cpp b/src/session.cpp index 06a6fe7..17c44bb 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -30,6 +30,7 @@ Session::Session(QObject *parent) : QSslSocket(nullptr) connect(this, &Session::connected, this, &Session::isConnected); connect(this, &Session::disconnected, this, &Session::isDisconnected); connect(this, &Session::readyRead, this, &Session::dataIn); + connect(this, &Session::loopDataIn, this, &Session::dataIn); connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sockerr(QAbstractSocket::SocketError))); } @@ -42,7 +43,7 @@ void Session::hookedBinToServer(const QByteArray &data, quint8 typeId) } else { - emit errTxtOut("err: not connected to a host.\n"); + cacheTxt(ERR, "err: not connected to a host.\n"); } } @@ -58,7 +59,7 @@ void Session::binToServer(quint16 cmdId, const QByteArray &data, quint8 typeId) } else { - emit errTxtOut("err: not connected to a host.\n"); + cacheTxt(ERR, "err: not connected to a host.\n"); } } @@ -66,11 +67,11 @@ void Session::sockerr(QAbstractSocket::SocketError err) { if (err == QAbstractSocket::RemoteHostClosedError) { - emit mainTxtOut("\nThe remote host closed the session.\n"); + cacheTxt(TEXT, "\nThe remote host closed the session.\n"); } else { - emit errTxtOut("\nerr: " + errorString() + "\n"); + cacheTxt(ERR, "\nerr: " + errorString() + "\n"); if (state() == QAbstractSocket::UnconnectedState) { @@ -87,7 +88,7 @@ void Session::isConnected() // appName = UTF16LE string (padded with 0x00) // coName = UTF16LE string (padded with 0x00) - emit mainTxtOut("Connected.\n"); + cacheTxt(TEXT, "Connected.\n"); QByteArray header; QString appName = QString(APP_NAME) + " v" + QString(APP_VERSION); @@ -102,8 +103,7 @@ void Session::isConnected() } else { - emit errTxtOut("\nerr: client bug! - header len not equal to " + QString::number(CLIENT_HEADER_LEN) + "\n"); - + cacheTxt(ERR, "\nerr: client bug! - header len not equal to " + QString::number(CLIENT_HEADER_LEN) + "\n"); disconnectFromHost(); } } @@ -123,7 +123,7 @@ void Session::handShakeDone() << " #key_exchange - " << cipher.keyExchangeMethod() << endl << " #authentication - " << cipher.authenticationMethod() << endl; - emit mainTxtOut(txt); + cacheTxt(TEXT, txt); } void Session::isDisconnected() @@ -142,7 +142,7 @@ void Session::isDisconnected() Shared::hostCmds->clear(); Shared::genfileCmds->clear(); - emit mainTxtOut("\nHost session ended. (disconnected)\n\n"); + cacheTxt(TEXT, "\nHost session ended. (disconnected)\n\n"); if (reconnect) { @@ -162,8 +162,7 @@ void Session::connectToServ() } else { - emit mainTxtOut("Connecting to address: " + *Shared::hostAddress + " port: " + QString::number(*Shared::hostPort) + "\n"); - + cacheTxt(TEXT, "Connecting to address: " + *Shared::hostAddress + " port: " + QString::number(*Shared::hostPort) + "\n"); connectToHost(*Shared::hostAddress, *Shared::hostPort); } } @@ -174,8 +173,7 @@ void Session::disconnectFromServ() if (state() == QAbstractSocket::ConnectedState) { - emit mainTxtOut("Disconnecting.\n"); - + cacheTxt(TEXT, "Disconnecting.\n"); disconnectFromHost(); } @@ -225,22 +223,14 @@ bool Session::isAsync(quint16 id) void Session::procAsync(const QByteArray &data) { - if (dType == TEXT) + if ((dType == TEXT) || (dType == BIG_TEXT) || (dType == ERR)) { + cacheTxt(dType, fromTEXT(data)); + if (cmdId == ASYNC_RDY) { hook = 0; } - - emit mainTxtOut(fromTEXT(data)); - } - else if (dType == BIG_TEXT) - { - emit bigTxtOut(fromTEXT(data)); - } - else if (dType == ERR) - { - emit errTxtOut(fromTEXT(data)); } else if (cmdId == ASYNC_SYS_MSG) { @@ -249,11 +239,11 @@ void Session::procAsync(const QByteArray &data) QSslCertificate cert(data, QSsl::Pem); QSslError selfSigned(QSslError::SelfSignedCertificate, cert); - emit mainTxtOut("SSL cert received.\n\n"); + cacheTxt(TEXT, "SSL cert received.\n\n"); if (cert.isSelfSigned()) { - emit mainTxtOut("WARNING: the cert is self signed. be careful if in a public network.\n\n"); + cacheTxt(TEXT, "WARNING: the host cert is self signed.\n\n"); } ignoreSslErrors(QList() << selfSigned); @@ -291,6 +281,16 @@ void Session::procAsync(const QByteArray &data) } } +void Session::cacheTxt(quint8 typeId, QString txt) +{ + Shared::cacheTxt(Shared::TXT_IN, typeId, txt); + + if (!(*Shared::activeDisp)) + { + emit txtInCache(); + } +} + void Session::dataFromHost(const QByteArray &data) { if (isAsync(cmdId)) @@ -299,22 +299,9 @@ void Session::dataFromHost(const QByteArray &data) } else if ((cmdId == hook) && (branId == 0) && (hook != 0)) { - if ((dType == TEXT) || (dType == PRIV_TEXT)) + if ((dType == TEXT) || (dType == PRIV_TEXT) || (dType == BIG_TEXT) || (dType == ERR)) { - emit mainTxtOut(fromTEXT(data)); - - if (dType == PRIV_TEXT) - { - emit setUserIO(HIDDEN); - } - } - else if (dType == BIG_TEXT) - { - emit bigTxtOut(fromTEXT(data)); - } - else if (dType == ERR) - { - emit errTxtOut(fromTEXT(data)); + cacheTxt(dType, fromTEXT(data)); } else if (dType == IDLE) { @@ -325,11 +312,11 @@ void Session::dataFromHost(const QByteArray &data) if (Shared::hostCmds->contains(cmdId)) { - emit mainTxtOut("\nFinished: " + Shared::hostCmds->value(cmdId) + "\n\n"); + cacheTxt(TEXT, "\nFinished: " + Shared::hostCmds->value(cmdId) + "\n\n"); } else { - emit mainTxtOut("\nFinished: Unknown\n\n"); + cacheTxt(TEXT, "\nFinished: Unknown\n\n"); } } else if ((dType == GEN_FILE) && (flags & GEN_FILE_ON)) @@ -348,7 +335,8 @@ void Session::dataIn() flags ^= DSIZE_RDY; dataFromHost(read(dSize)); - dataIn(); + + emit loopDataIn(); } } else if (flags & VER_OK) @@ -363,7 +351,7 @@ void Session::dataIn() dSize = static_cast(rdInt(header.mid(5, 3))); flags |= DSIZE_RDY; - dataIn(); + emit loopDataIn(); } } else if (bytesAvailable() >= SERVER_HEADER_LEN) @@ -386,7 +374,7 @@ void Session::dataIn() *Shared::servMinor = static_cast(rdInt(read(2))); *Shared::servPatch = static_cast(rdInt(read(2))); - emit mainTxtOut("Detected host version: " + verText(*Shared::servMajor, *Shared::servMinor, *Shared::servPatch) + "\n"); + cacheTxt(TEXT, "Detected host version: " + verText(*Shared::servMajor, *Shared::servMinor, *Shared::servPatch) + "\n"); if (*Shared::servMajor == 2) { @@ -395,35 +383,34 @@ void Session::dataIn() flags |= VER_OK; - emit mainTxtOut("Host assigned session id: " + Shared::sessionId->toHex() + "\n"); + cacheTxt(TEXT, "Host assigned session id: " + Shared::sessionId->toHex() + "\n"); if (reply == 2) { - emit mainTxtOut("Awaiting SSL cert from the host.\n"); + cacheTxt(TEXT, "Awaiting SSL cert from the host.\n"); } else { - emit mainTxtOut("The host claims SSL is not needed. the connection will not be encrypted.\n"); + cacheTxt(TEXT, "The host claims SSL is not needed. the connection will not be encrypted.\n"); } } else { - emit errTxtOut("err: Host not compatible.\n"); - + cacheTxt(ERR, "err: Host not compatible.\n"); disconnectFromHost(); } } else if (reply == 4) { - emit errTxtOut("err: The host was unable to find a SSL cert for common name: " + *Shared::hostAddress + ".\n"); + cacheTxt(ERR, "err: The host was unable to find a SSL cert for common name: " + *Shared::hostAddress + ".\n"); } else { - emit errTxtOut("err: Invalid reply id from the host: " + QString::number(reply) + ".\n"); + cacheTxt(ERR, "err: Invalid reply id from the host: " + QString::number(reply) + ".\n"); } if ((reply != 1) && (reply != 2)) disconnectFromHost(); - dataIn(); + emit loopDataIn(); } } diff --git a/src/session.h b/src/session.h index f7c2a2d..3828b8d 100644 --- a/src/session.h +++ b/src/session.h @@ -58,6 +58,7 @@ private: quint8 dType; bool reconnect; + void cacheTxt(quint8 typeId, QString txt); void dataFromHost(const QByteArray &data); void procAsync(const QByteArray &data); bool isAsync(quint16 id); @@ -95,13 +96,12 @@ public slots: signals: void hostFinished(); + void txtInCache(); + void loopDataIn(); void setUserIO(int flgs); void unsetUserIO(int flgs); void hostCmdRemoved(quint16 id); void toGenFile(const QByteArray &data); - void mainTxtOut(const QString &txt); - void errTxtOut(const QString &txt); - void bigTxtOut(const QString &txt); }; #endif // SOCKET_H diff --git a/src/text_body.cpp b/src/text_body.cpp index 9bb7c19..e2fd49e 100644 --- a/src/text_body.cpp +++ b/src/text_body.cpp @@ -16,6 +16,97 @@ // along with Cmdr under the LICENSE.md file. If not, see // . +TextWorker::TextWorker(QObject *parent) : QObject(nullptr) +{ + localData = Shared::localData; + + connect(parent, &QObject::destroyed, this, &QObject::deleteLater); + connect(this, &TextWorker::loop, this, &TextWorker::dumpTxtCache); + loadSettings(); +} + +QString TextWorker::htmlEsc(const QString &txt) +{ + QString ret = txt; + + ret.replace(">", ">"); + ret.replace("<", "<"); + + return ret; +} + +void TextWorker::dumpTxtCache() +{ + QString txt; + quint8 typeId; + + if (Shared::cacheTxt(Shared::TXT_OUT, typeId, txt)) + { + *Shared::activeDisp = !Shared::cacheTxt(Shared::TXT_IS_EMPTY); + + if ((typeId == TEXT) || (typeId == PRIV_TEXT)) + { + addMainTxt(txt); + + if (typeId == PRIV_TEXT) + { + emit setUserIO(HIDDEN); + } + } + else if (typeId == ERR) + { + addErrTxt(txt); + } + else if (typeId == BIG_TEXT) + { + addBigTxt(txt); + } + + if (*Shared::activeDisp) + { + emit loop(); + } + } +} + +void TextWorker::toHtmlLines(const QString &txt, const QString &color) +{ + QStringList lines = htmlEsc(txt).split("\n"); + + for (int i = 0; i < lines.size(); ++i) + { + emit htmlOut("
" + lines[i] + "
", i == lines.size() - 1); + + thread()->usleep(1000); + } +} + +void TextWorker::addMainTxt(const QString &txt) +{ + toHtmlLines(txt, mainColor); +} + +void TextWorker::addErrTxt(const QString &txt) +{ + toHtmlLines(txt, errColor); +} + +void TextWorker::addBigTxt(const QString &txt) +{ + QString lines; + QTextStream txtOut(&lines); + + wordWrap("", txtOut, txt, Shared::mainWidget); + + addMainTxt(lines); +} + +void TextWorker::loadSettings() +{ + mainColor = localData->value("text_settings").toObject().value("text_color").toString(); + errColor = localData->value("text_settings").toObject().value("err_color").toString(); +} + TextBody::TextBody(QWidget *parent) : QTextEdit(parent) { localData = Shared::localData; @@ -31,66 +122,14 @@ TextBody::TextBody(QWidget *parent) : QTextEdit(parent) void TextBody::loadTextBodySettings() { - mainColor = localData->value("text_settings").toObject().value("text_color").toString(); - errColor = localData->value("text_settings").toObject().value("err_color").toString(); - txtDocument->setMaximumBlockCount(localData->value("max_lines").toInt()); } -QString TextBody::htmlEsc(const QString &txt) -{ - QString ret = txt; - - ret.replace(">", ">"); - ret.replace("<", "<"); - - return ret; -} - -void TextBody::addTextBlock(const QString &txt, const QString &color) -{ - moveCursor(QTextCursor::End); - - QStringList lines = htmlEsc(txt).split("\n"); - - for (int i = 0; i < lines.size(); ++i) - { - txtCursor->insertHtml("
" + lines[i] + "
"); - - if (i != lines.size() - 1) - { - txtCursor->insertBlock(); - } - } - - ensureCursorVisible(); -} - void TextBody::setMaxLines(int value) { txtDocument->setMaximumBlockCount(value); } -void TextBody::addMainTxt(const QString &txt) -{ - addTextBlock(txt, mainColor); -} - -void TextBody::addErrTxt(const QString &txt) -{ - addTextBlock(txt, errColor); -} - -void TextBody::addBigTxt(const QString &txt) -{ - QString lines; - QTextStream txtOut(&lines); - - wordWrap("", txtOut, txt, Shared::mainWidget); - - addMainTxt(lines); -} - void TextBody::reload() { clear(); @@ -108,3 +147,17 @@ void TextBody::contextMenuEvent(QContextMenuEvent *event) menu->exec(event->globalPos()); menu->deleteLater(); } + +void TextBody::htmlIn(const QString &line, bool lastLine) +{ + moveCursor(QTextCursor::End); + + txtCursor->insertHtml(line); + + if (!lastLine) + { + txtCursor->insertBlock(); + } + + ensureCursorVisible(); +} diff --git a/src/text_body.h b/src/text_body.h index d7645be..9178d00 100644 --- a/src/text_body.h +++ b/src/text_body.h @@ -19,28 +19,53 @@ #include "common.h" +class TextWorker : public QObject +{ + Q_OBJECT + +private: + + QJsonObject *localData; + QString errColor; + QString mainColor; + + QString htmlEsc(const QString &txt); + void toHtmlLines(const QString &txt, const QString &color); + void addMainTxt(const QString &txt); + void addErrTxt(const QString &txt); + void addBigTxt(const QString &txt); + +public: + + explicit TextWorker(QObject *parent = nullptr); + +public slots: + + void loadSettings(); + void dumpTxtCache(); + +signals: + + void htmlOut(const QString &line, bool lastLine); + void setUserIO(int flgs); + void unsetUserIO(int flgs); + void loop(); +}; + +//----------------------------------------- + class TextBody : public QTextEdit { Q_OBJECT private: - enum TextType - { - ERROR, - MAIN - }; - QTextDocument *txtDocument; QTextCursor *txtCursor; QJsonObject *localData; - QString errColor; - QString mainColor; - QString htmlEsc(const QString &txt); - void loadTextBodySettings(); - void contextMenuEvent(QContextMenuEvent *event); - void addTextBlock(const QString &txt, const QString &color); + void loadTextBodySettings(); + void contextMenuEvent(QContextMenuEvent *event); public: @@ -48,9 +73,7 @@ public: public slots: - void addMainTxt(const QString &txt); - void addErrTxt(const QString &txt); - void addBigTxt(const QString &txt); + void htmlIn(const QString &line, bool lastLine); void setMaxLines(int value); void reload(); };