aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/3rdparty/libptyqt/winptyprocess.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/3rdparty/libptyqt/winptyprocess.cpp')
-rw-r--r--src/libs/3rdparty/libptyqt/winptyprocess.cpp278
1 files changed, 278 insertions, 0 deletions
diff --git a/src/libs/3rdparty/libptyqt/winptyprocess.cpp b/src/libs/3rdparty/libptyqt/winptyprocess.cpp
new file mode 100644
index 0000000000..7a781bc195
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/winptyprocess.cpp
@@ -0,0 +1,278 @@
+#include "winptyprocess.h"
+#include <QFile>
+#include <QFileInfo>
+#include <sstream>
+#include <QCoreApplication>
+#include <QLocalSocket>
+#include <QWinEventNotifier>
+
+#define DEBUG_VAR_LEGACY "WINPTYDBG"
+#define DEBUG_VAR_ACTUAL "WINPTY_DEBUG"
+#define SHOW_CONSOLE_VAR "WINPTY_SHOW_CONSOLE"
+#define WINPTY_AGENT_NAME "winpty-agent.exe"
+#define WINPTY_DLL_NAME "winpty.dll"
+
+QString castErrorToString(winpty_error_ptr_t error_ptr)
+{
+ return QString::fromStdWString(winpty_error_msg(error_ptr));
+}
+
+WinPtyProcess::WinPtyProcess()
+ : IPtyProcess()
+ , m_ptyHandler(nullptr)
+ , m_innerHandle(nullptr)
+ , m_inSocket(nullptr)
+ , m_outSocket(nullptr)
+{
+#ifdef PTYQT_DEBUG
+ m_trace = true;
+#endif
+}
+
+WinPtyProcess::~WinPtyProcess()
+{
+ kill();
+}
+
+bool WinPtyProcess::startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows)
+{
+ if (!isAvailable())
+ {
+ m_lastError = QString("WinPty Error: winpty-agent.exe or winpty.dll not found!");
+ return false;
+ }
+
+ //already running
+ if (m_ptyHandler != nullptr)
+ return false;
+
+ QFileInfo fi(executable);
+ if (fi.isRelative() || !QFile::exists(executable))
+ {
+ //todo add auto-find executable in PATH env var
+ m_lastError = QString("WinPty Error: shell file path must be absolute");
+ return false;
+ }
+
+ m_shellPath = executable;
+ m_shellPath.replace('/', '\\');
+ m_size = QPair<qint16, qint16>(cols, rows);
+
+#ifdef PTYQT_DEBUG
+ if (m_trace)
+ {
+ environment.append(QString("%1=1").arg(DEBUG_VAR_LEGACY));
+ environment.append(QString("%1=trace").arg(DEBUG_VAR_ACTUAL));
+ environment.append(QString("%1=1").arg(SHOW_CONSOLE_VAR));
+ SetEnvironmentVariableA(DEBUG_VAR_LEGACY, "1");
+ SetEnvironmentVariableA(DEBUG_VAR_ACTUAL, "trace");
+ SetEnvironmentVariableA(SHOW_CONSOLE_VAR, "1");
+ }
+#endif
+
+ //env
+ std::wstringstream envBlock;
+ for (const QString &line : environment)
+ {
+ envBlock << line.toStdWString() << L'\0';
+ }
+ std::wstring env = envBlock.str();
+
+ //create start config
+ winpty_error_ptr_t errorPtr = nullptr;
+ winpty_config_t* startConfig = winpty_config_new(0, &errorPtr);
+ if (startConfig == nullptr)
+ {
+ m_lastError = QString("WinPty Error: create start config -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ //set params
+ winpty_config_set_initial_size(startConfig, cols, rows);
+ winpty_config_set_mouse_mode(startConfig, WINPTY_MOUSE_MODE_AUTO);
+ //winpty_config_set_agent_timeout();
+
+ //start agent
+ m_ptyHandler = winpty_open(startConfig, &errorPtr);
+ winpty_config_free(startConfig); //start config is local var, free it after use
+
+ if (m_ptyHandler == nullptr)
+ {
+ m_lastError = QString("WinPty Error: start agent -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ //create spawn config
+ winpty_spawn_config_t* spawnConfig = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, m_shellPath.toStdWString().c_str(),
+ //commandLine.toStdWString().c_str(), cwd.toStdWString().c_str(),
+ arguments.join(" ").toStdWString().c_str(), workingDir.toStdWString().c_str(),
+ env.c_str(),
+ &errorPtr);
+
+ if (spawnConfig == nullptr)
+ {
+ m_lastError = QString("WinPty Error: create spawn config -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ //spawn the new process
+ BOOL spawnSuccess = winpty_spawn(m_ptyHandler, spawnConfig, &m_innerHandle, nullptr, nullptr, &errorPtr);
+ winpty_spawn_config_free(spawnConfig); //spawn config is local var, free it after use
+ if (!spawnSuccess)
+ {
+ m_lastError = QString("WinPty Error: start terminal process -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ m_pid = (int)GetProcessId(m_innerHandle);
+
+ m_outSocket = new QLocalSocket();
+
+ // Notify when the shell process has been terminated
+ m_shellCloseWaitNotifier = new QWinEventNotifier(m_innerHandle, notifier());
+ QObject::connect(m_shellCloseWaitNotifier,
+ &QWinEventNotifier::activated,
+ notifier(),
+ [this](HANDLE hEvent) {
+ DWORD exitCode = 0;
+ GetExitCodeProcess(hEvent, &exitCode);
+ m_exitCode = exitCode;
+ // Do not respawn if the object is about to be destructed
+ if (!m_aboutToDestruct)
+ emit notifier()->aboutToClose();
+ m_shellCloseWaitNotifier->setEnabled(false);
+ });
+
+ //get pipe names
+ LPCWSTR conInPipeName = winpty_conin_name(m_ptyHandler);
+ m_conInName = QString::fromStdWString(std::wstring(conInPipeName));
+ m_inSocket = new QLocalSocket();
+ m_inSocket->connectToServer(m_conInName, QIODevice::WriteOnly);
+ m_inSocket->waitForConnected();
+
+ LPCWSTR conOutPipeName = winpty_conout_name(m_ptyHandler);
+ m_conOutName = QString::fromStdWString(std::wstring(conOutPipeName));
+ m_outSocket->connectToServer(m_conOutName, QIODevice::ReadOnly);
+ m_outSocket->waitForConnected();
+
+ if (m_inSocket->state() != QLocalSocket::ConnectedState && m_outSocket->state() != QLocalSocket::ConnectedState)
+ {
+ m_lastError = QString("WinPty Error: Unable to connect local sockets -> %1 / %2").arg(m_inSocket->errorString()).arg(m_outSocket->errorString());
+ m_inSocket->deleteLater();
+ m_outSocket->deleteLater();
+ m_inSocket = nullptr;
+ m_outSocket = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+bool WinPtyProcess::resize(qint16 cols, qint16 rows)
+{
+ if (m_ptyHandler == nullptr)
+ {
+ return false;
+ }
+
+ bool res = winpty_set_size(m_ptyHandler, cols, rows, nullptr);
+
+ if (res)
+ {
+ m_size = QPair<qint16, qint16>(cols, rows);
+ }
+
+ return res;
+}
+
+bool WinPtyProcess::kill()
+{
+ bool exitCode = false;
+ if (m_innerHandle != nullptr && m_ptyHandler != nullptr) {
+ m_aboutToDestruct = true;
+ //disconnect all signals (readyRead, ...)
+ m_inSocket->disconnect();
+ m_outSocket->disconnect();
+
+ //disconnect for server
+ m_inSocket->disconnectFromServer();
+ m_outSocket->disconnectFromServer();
+
+ m_inSocket->deleteLater();
+ m_outSocket->deleteLater();
+
+ m_inSocket = nullptr;
+ m_outSocket = nullptr;
+
+ winpty_free(m_ptyHandler);
+ exitCode = CloseHandle(m_innerHandle);
+
+ delete m_shellCloseWaitNotifier;
+ m_shellCloseWaitNotifier = nullptr;
+
+ m_ptyHandler = nullptr;
+ m_innerHandle = nullptr;
+ m_conInName = QString();
+ m_conOutName = QString();
+ m_pid = 0;
+ }
+ return exitCode;
+}
+
+IPtyProcess::PtyType WinPtyProcess::type()
+{
+ return PtyType::WinPty;
+}
+
+QString WinPtyProcess::dumpDebugInfo()
+{
+#ifdef PTYQT_DEBUG
+ return QString("PID: %1, ConIn: %2, ConOut: %3, Type: %4, Cols: %5, Rows: %6, IsRunning: %7, Shell: %8")
+ .arg(m_pid).arg(m_conInName).arg(m_conOutName).arg(type())
+ .arg(m_size.first).arg(m_size.second).arg(m_ptyHandler != nullptr)
+ .arg(m_shellPath);
+#else
+ return QString("Nothing...");
+#endif
+}
+
+QIODevice *WinPtyProcess::notifier()
+{
+ return m_outSocket;
+}
+
+QByteArray WinPtyProcess::readAll()
+{
+ return m_outSocket->readAll();
+}
+
+qint64 WinPtyProcess::write(const QByteArray &byteArray)
+{
+ return m_inSocket->write(byteArray);
+}
+
+bool WinPtyProcess::isAvailable()
+{
+#ifdef PTYQT_BUILD_STATIC
+ return QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_AGENT_NAME);
+#elif PTYQT_BUILD_DYNAMIC
+ return QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_AGENT_NAME)
+ && QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_DLL_NAME);
+#endif
+ return true;
+}
+
+void WinPtyProcess::moveToThread(QThread *targetThread)
+{
+ m_inSocket->moveToThread(targetThread);
+ m_outSocket->moveToThread(targetThread);
+}