Remove old ConPty stuff, fix Windows build, update submodule
This commit is contained in:
+5
-2
@@ -13,6 +13,8 @@ environment:
|
||||
matrix:
|
||||
- PLATFORM: x64
|
||||
QT_DIR: C:\Qt\5.12.1\msvc2017_64
|
||||
- PLATFORM: x86
|
||||
QT_DIR: C:\Qt\5.12.1\msvc2017
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
@@ -36,5 +38,6 @@ build_script:
|
||||
- cmake.exe --build .
|
||||
- dir thirdparty\winpty\src\winpty
|
||||
|
||||
#test_script:
|
||||
# - ctest -C %CONFIGURATION% --output-on-failure
|
||||
test_script:
|
||||
- set PATH=%PATH%;%QT_DIR%
|
||||
- ctest -C %CONFIGURATION% --output-on-failure
|
||||
|
||||
+10
-19
@@ -11,6 +11,10 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
#params
|
||||
#available params:
|
||||
# - NO_BUILD_TESTS=1
|
||||
# - NO_BUILD_EXAMPLES=1
|
||||
# - WINPTY_ROOT_DIR=<path/to/winpty> - if you set this parameter, winpty doesn't be auto-downloaded like dependency
|
||||
IF(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(PTYQT_DEBUG TRUE)
|
||||
add_definitions(-DPTYQT_DEBUG)
|
||||
@@ -64,22 +68,9 @@ enable_testing()
|
||||
|
||||
#sub projects
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(examples)
|
||||
|
||||
#add_executable(${PROJECT_NAME} "main.cpp")
|
||||
|
||||
#target_link_libraries(${PROJECT_NAME} Qt5::Core)
|
||||
|
||||
#Ubuntu package generator
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
|
||||
|
||||
set(CPACK_PACKAGE_CONTACT v31337@gmail.com)
|
||||
|
||||
set(CPACK_GENERATOR DEB)
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
|
||||
include (CPack)
|
||||
if (NOT "${NO_BUILD_TESTS}" STREQUAL "1")
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
if (NOT "${NO_BUILD_EXAMPLES}" STREQUAL "1")
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
+2
-5
@@ -11,11 +11,8 @@ if (MSVC)
|
||||
${SOURCE_FILES}
|
||||
winptyprocess.h
|
||||
winptyprocess.cpp
|
||||
#conptynamedpipeprocess.h
|
||||
#conptynamedpipeprocess.cpp
|
||||
conptyanonymouspipeprocess.h
|
||||
conptyanonymouspipeprocess.cpp
|
||||
conpty_shared.h
|
||||
conptyprocess.h
|
||||
conptyprocess.cpp
|
||||
)
|
||||
else()
|
||||
set(SOURCE_FILES
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
#ifndef CONPTYANONYMOUSPIPEPROCESS_H
|
||||
#define CONPTYANONYMOUSPIPEPROCESS_H
|
||||
|
||||
#include "conpty_shared.h"
|
||||
#include <QMutex>
|
||||
#include <QTimer>
|
||||
#include <QThread>
|
||||
|
||||
class PtyBuffer : public QIODevice
|
||||
{
|
||||
friend class ConPtyAnonymousPipeProcess;
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
PtyBuffer() { }
|
||||
~PtyBuffer() { }
|
||||
|
||||
//just empty realization, we need only 'readyRead' signal of this class
|
||||
qint64 readData(char *data, qint64 maxlen) { return 0; }
|
||||
qint64 writeData(const char *data, qint64 len) { return 0; }
|
||||
|
||||
bool isSequential() { return true; }
|
||||
qint64 bytesAvailable() { return m_readBuffer.size(); }
|
||||
qint64 size() { return m_readBuffer.size(); }
|
||||
|
||||
void emitReadyRead()
|
||||
{
|
||||
//for emit signal from PtyBuffer own thread
|
||||
QTimer::singleShot(1, this, [this]()
|
||||
{
|
||||
emit readyRead();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
QByteArray m_readBuffer;
|
||||
};
|
||||
|
||||
class ConPtyAnonymousPipeProcess : public IPtyProcess
|
||||
{
|
||||
public:
|
||||
ConPtyAnonymousPipeProcess();
|
||||
~ConPtyAnonymousPipeProcess();
|
||||
|
||||
bool startProcess(const QString &shellPath, QStringList environment, qint16 cols, qint16 rows);
|
||||
bool resize(qint16 cols, qint16 rows);
|
||||
bool kill();
|
||||
PtyType type();
|
||||
#ifdef PTYQT_DEBUG
|
||||
QString dumpDebugInfo();
|
||||
#endif
|
||||
virtual QIODevice *notifier();
|
||||
virtual QByteArray readAll();
|
||||
virtual qint64 write(const QByteArray &byteArray);
|
||||
bool isAvailable();
|
||||
|
||||
private:
|
||||
HRESULT createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows);
|
||||
HRESULT initializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC);
|
||||
|
||||
private:
|
||||
WindowsContext m_winContext;
|
||||
HPCON m_ptyHandler;
|
||||
HANDLE m_hPipeIn, m_hPipeOut;
|
||||
|
||||
QThread *m_readThread;
|
||||
QMutex m_bufferMutex;
|
||||
PtyBuffer m_buffer;
|
||||
|
||||
};
|
||||
|
||||
#endif // CONPTYANONYMOUSPIPEPROCESS_H
|
||||
@@ -1,274 +0,0 @@
|
||||
#include "conptynamedpipeprocess.h"
|
||||
#include <QFileInfo>
|
||||
#include <QMutex>
|
||||
#include <sstream>
|
||||
#include <QCryptographicHash>
|
||||
#include <QUuid>
|
||||
#include <QSocketNotifier>
|
||||
#include<QThread>
|
||||
#include <QSysInfo>
|
||||
|
||||
ConPtyNamedPipeProcess::ConPtyNamedPipeProcess()
|
||||
: IWindowsPtyProcess()
|
||||
, m_ptyHandler(INVALID_HANDLE_VALUE)
|
||||
, m_hShell(INVALID_HANDLE_VALUE)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ConPtyNamedPipeProcess::~ConPtyNamedPipeProcess()
|
||||
{
|
||||
kill();
|
||||
}
|
||||
|
||||
bool ConPtyNamedPipeProcess::startProcess(const QString &shellPath, QStringList environment, qint16 cols, qint16 rows)
|
||||
{
|
||||
//shared between all objects in all threads
|
||||
static QMutex pipeNameCounterMutex;
|
||||
static qint64 pipeNameCounter = 0;
|
||||
|
||||
if (!isAvailable())
|
||||
{
|
||||
m_lastError = m_winContext.lastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
//already running
|
||||
if (m_ptyHandler != INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
QFileInfo fi(shellPath);
|
||||
if (fi.isRelative() || !QFile::exists(shellPath))
|
||||
{
|
||||
//todo add auto-find executable in PATH env var
|
||||
m_lastError = QString("WinPty Error: shell file path must be absolute");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_shellPath = shellPath;
|
||||
m_size = QPair<qint16, qint16>(cols, rows);
|
||||
|
||||
auto createNamedPipe = [] (bool writeChannel,
|
||||
HANDLE *hNamedPipe,
|
||||
QString &resultName,
|
||||
const QString &pipeName) -> bool
|
||||
{
|
||||
*hNamedPipe = INVALID_HANDLE_VALUE;
|
||||
resultName = "\\\\.\\pipe\\" + pipeName;
|
||||
|
||||
const DWORD winOpenMode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE/* | FILE_FLAG_OVERLAPPED */;
|
||||
|
||||
SECURITY_ATTRIBUTES sa = {};
|
||||
sa.nLength = sizeof(sa);
|
||||
|
||||
*hNamedPipe = CreateNamedPipeW(
|
||||
resultName.toStdWString().c_str(),
|
||||
/*dwOpenMode=*/winOpenMode,
|
||||
/*dwPipeMode=*/PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
||||
/*nMaxInstances=*/1,
|
||||
/*nOutBufferSize=*/0,
|
||||
/*nInBufferSize=*/0,
|
||||
/*nDefaultTimeOut=*/30000,
|
||||
&sa);
|
||||
|
||||
return *hNamedPipe != INVALID_HANDLE_VALUE;
|
||||
};
|
||||
|
||||
//shared between all objects in all threads
|
||||
qint64 pipeCounterValue = 0;
|
||||
{
|
||||
QMutexLocker locker(&pipeNameCounterMutex);
|
||||
pipeCounterValue = pipeNameCounter;
|
||||
pipeNameCounter++;
|
||||
}
|
||||
//
|
||||
|
||||
QByteArray randPart = QCryptographicHash::hash(QByteArray::number(qrand()) + QByteArray::number(pipeCounterValue) + QUuid::createUuid().toByteArray(), QCryptographicHash::Sha256).left(16);
|
||||
bool createPipeRes = createNamedPipe(true, &m_inPipeShellSide, m_conInName, QString("conpty-conin-%1-%2").arg(pipeCounterValue).arg(QString::fromLatin1(randPart.toHex())));
|
||||
if (!createPipeRes)
|
||||
{
|
||||
m_lastError = QString("ConPty Error: Unable to create IN pipe -> %1").arg(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
randPart = QCryptographicHash::hash(QByteArray::number(qrand()) + QByteArray::number(pipeCounterValue) + QUuid::createUuid().toByteArray(), QCryptographicHash::Sha256).left(16);
|
||||
createPipeRes = createNamedPipe(false, &m_outPipeShellSide, m_conOutName, QString("conpty-conout-%1-%2").arg(pipeCounterValue).arg(QString::fromLatin1(randPart.toHex())));
|
||||
if (!createPipeRes)
|
||||
{
|
||||
m_lastError = QString("ConPty Error: Unable to create OUT pipe -> %1").arg(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
HRESULT result = m_winContext.createPseudoConsole({cols, rows}, m_inPipeShellSide, m_outPipeShellSide, 0, &m_ptyHandler);
|
||||
|
||||
if (result != S_OK)
|
||||
{
|
||||
//error can be E_HANDLE
|
||||
m_lastError = QString("ConPty Error: Unable to launch ConPty -> %1").arg(QString::number(result, 16));
|
||||
return false;
|
||||
}
|
||||
|
||||
//console created
|
||||
|
||||
//env
|
||||
std::wstringstream envBlock;
|
||||
foreach (QString line, environment)
|
||||
{
|
||||
envBlock << line.toStdWString() << L'\0';
|
||||
}
|
||||
envBlock << L'\0';
|
||||
std::wstring env = envBlock.str();
|
||||
auto envV = vectorFromString(env);
|
||||
LPWSTR envArg = envV.empty() ? nullptr : envV.data();
|
||||
LPWSTR cmdArg = (LPWSTR)m_shellPath.utf16();
|
||||
|
||||
//this code runned in separate thread, because current thread will stops on API call 'ConnectNamedPipe'
|
||||
QThread *thread = QThread::create([this]()
|
||||
{
|
||||
QThread::msleep(10);
|
||||
m_inSocket.connectToServer(m_conInName, QIODevice::WriteOnly);
|
||||
m_inSocket.waitForConnected();
|
||||
m_outSocket.connectToServer(m_conOutName, QIODevice::ReadOnly);
|
||||
m_outSocket.waitForConnected();
|
||||
|
||||
if (m_inSocket.state() != QLocalSocket::ConnectedState || m_outSocket.state() != QLocalSocket::ConnectedState)
|
||||
{
|
||||
m_lastError = QString("ConPty Error: Unable to connect local sockets -> %1 / %2").arg(m_inSocket.errorString()).arg(m_outSocket.errorString());
|
||||
}
|
||||
|
||||
//qDebug() << "socketDescriptors" << m_inSocket.fullServerName() << m_inSocket.socketDescriptor() << m_outSocket.socketDescriptor() << m_inSocket.errorString() << m_outSocket.errorString();
|
||||
QThread::currentThread()->deleteLater();
|
||||
});
|
||||
//connect to Pty
|
||||
thread->start();
|
||||
|
||||
bool connected = ConnectNamedPipe(m_inPipeShellSide, nullptr) && ConnectNamedPipe(m_outPipeShellSide, nullptr);
|
||||
|
||||
if (!connected)
|
||||
{
|
||||
m_lastError = QString("ConPty Error: Unable to connect named pipes");
|
||||
return false;
|
||||
}
|
||||
|
||||
//attach the pseudoconsole to the client application we're creating
|
||||
STARTUPINFOEXW pStartupInfo{0};
|
||||
pStartupInfo.StartupInfo.cb = sizeof(STARTUPINFOEXW);
|
||||
|
||||
//get the size of the thread attribute list.
|
||||
size_t size;
|
||||
InitializeProcThreadAttributeList(NULL, 1, 0, reinterpret_cast<PSIZE_T>(&size));
|
||||
pStartupInfo.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(malloc(size));
|
||||
|
||||
bool fSuccess = InitializeProcThreadAttributeList(pStartupInfo.lpAttributeList, 1, 0, reinterpret_cast<PSIZE_T>(&size));
|
||||
if (!fSuccess)
|
||||
{
|
||||
m_lastError = QString("ConPty Error: InitializeProcThreadAttributeList failed");
|
||||
return false;
|
||||
}
|
||||
fSuccess = UpdateProcThreadAttribute(pStartupInfo.lpAttributeList,
|
||||
0,
|
||||
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
|
||||
m_ptyHandler,
|
||||
sizeof(HPCON),
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
m_lastError = QString("ConPty Error: UpdateProcThreadAttribute failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
//create process
|
||||
PROCESS_INFORMATION piClient{};
|
||||
fSuccess = !!CreateProcessW(
|
||||
nullptr,
|
||||
cmdArg,
|
||||
nullptr, // lpProcessAttributes
|
||||
nullptr, // lpThreadAttributes
|
||||
false, // bInheritHandles VERY IMPORTANT that this is false
|
||||
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
|
||||
envArg, // lpEnvironment
|
||||
nullptr, // lpCurrentDirectory
|
||||
&pStartupInfo.StartupInfo, // lpStartupInfo
|
||||
&piClient // lpProcessInformation
|
||||
);
|
||||
|
||||
if (!fSuccess)
|
||||
{
|
||||
m_lastError = QString("ConPty Error: Cannot create process -> %1").arg(QString::number(GetLastError(), 10));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update handle
|
||||
m_hShell = piClient.hProcess;
|
||||
m_pid = piClient.dwProcessId;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConPtyNamedPipeProcess::resize(qint16 cols, qint16 rows)
|
||||
{
|
||||
if (m_ptyHandler == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = SUCCEEDED(m_winContext.resizePseudoConsole(m_ptyHandler, {cols, rows}));
|
||||
|
||||
if (res)
|
||||
{
|
||||
m_size = QPair<qint16, qint16>(cols, rows);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ConPtyNamedPipeProcess::kill()
|
||||
{
|
||||
bool exitCode = false;
|
||||
if (m_hShell != 0 && m_ptyHandler != 0)
|
||||
{
|
||||
m_inSocket.disconnectFromServer();
|
||||
m_outSocket.disconnectFromServer();
|
||||
|
||||
m_winContext.closePseudoConsole(m_ptyHandler);
|
||||
exitCode = CloseHandle(m_hShell);
|
||||
|
||||
m_hShell = INVALID_HANDLE_VALUE;
|
||||
m_ptyHandler = INVALID_HANDLE_VALUE;
|
||||
m_conInName = QString();
|
||||
m_conOutName = QString();
|
||||
m_inPipeShellSide = INVALID_HANDLE_VALUE;
|
||||
m_outPipeShellSide = INVALID_HANDLE_VALUE;
|
||||
m_pid = 0;
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
IPtyProcess::PtyType ConPtyNamedPipeProcess::type()
|
||||
{
|
||||
return PtyType::ConPtyNamedPipe;
|
||||
}
|
||||
|
||||
#ifdef PTYQT_DEBUG
|
||||
QString ConPtyNamedPipeProcess::dumpDebugInfo()
|
||||
{
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ConPtyNamedPipeProcess::isAvailable()
|
||||
{
|
||||
#ifdef TOO_OLD_WINSDK
|
||||
return false; //very importnant! ConPty can be built, but it doesn't work if built with old sdk and Win10 < 1903
|
||||
#endif
|
||||
|
||||
qint32 buildNumber = QSysInfo::kernelVersion().split(".").last().toInt();
|
||||
if (buildNumber < CONPTY_MINIMAL_WINDOWS_VERSION)
|
||||
return false;
|
||||
return m_winContext.init();
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef CONPTYNAMEDPIPEPROCESS_H
|
||||
#define CONPTYNAMEDPIPEPROCESS_H
|
||||
|
||||
#include "conpty_shared.h"
|
||||
|
||||
class ConPtyNamedPipeProcess : public IWindowsPtyProcess
|
||||
{
|
||||
public:
|
||||
ConPtyNamedPipeProcess();
|
||||
~ConPtyNamedPipeProcess();
|
||||
|
||||
bool startProcess(const QString &shellPath, QStringList environment, qint16 cols, qint16 rows);
|
||||
bool resize(qint16 cols, qint16 rows);
|
||||
bool kill();
|
||||
PtyType type();
|
||||
#ifdef PTYQT_DEBUG
|
||||
QString dumpDebugInfo();
|
||||
#endif
|
||||
bool isAvailable();
|
||||
|
||||
private:
|
||||
WindowsContext m_winContext;
|
||||
HPCON m_ptyHandler;
|
||||
HANDLE m_hShell;
|
||||
HANDLE m_inPipeShellSide, m_outPipeShellSide;
|
||||
};
|
||||
|
||||
#endif // CONPTYNAMEDPIPEPROCESS_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "conptyanonymouspipeprocess.h"
|
||||
#include "conptyprocess.h"
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QThread>
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#define READ_INTERVAL_MSEC 500
|
||||
|
||||
HRESULT ConPtyAnonymousPipeProcess::createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows)
|
||||
HRESULT ConPtyProcess::createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows)
|
||||
{
|
||||
HRESULT hr{ E_UNEXPECTED };
|
||||
HANDLE hPipePTYIn{ INVALID_HANDLE_VALUE };
|
||||
@@ -34,7 +34,7 @@ HRESULT ConPtyAnonymousPipeProcess::createPseudoConsoleAndPipes(HPCON* phPC, HAN
|
||||
|
||||
// Initializes the specified startup info struct with the required properties and
|
||||
// updates its thread attribute list with the specified ConPTY handle
|
||||
HRESULT ConPtyAnonymousPipeProcess::initializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC)
|
||||
HRESULT ConPtyProcess::initializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC)
|
||||
{
|
||||
HRESULT hr{ E_UNEXPECTED };
|
||||
|
||||
@@ -75,7 +75,7 @@ HRESULT ConPtyAnonymousPipeProcess::initializeStartupInfoAttachedToPseudoConsole
|
||||
return hr;
|
||||
}
|
||||
|
||||
ConPtyAnonymousPipeProcess::ConPtyAnonymousPipeProcess()
|
||||
ConPtyProcess::ConPtyProcess()
|
||||
: IPtyProcess()
|
||||
, m_ptyHandler { INVALID_HANDLE_VALUE }
|
||||
, m_hPipeIn { INVALID_HANDLE_VALUE }
|
||||
@@ -85,12 +85,12 @@ ConPtyAnonymousPipeProcess::ConPtyAnonymousPipeProcess()
|
||||
|
||||
}
|
||||
|
||||
ConPtyAnonymousPipeProcess::~ConPtyAnonymousPipeProcess()
|
||||
ConPtyProcess::~ConPtyProcess()
|
||||
{
|
||||
kill();
|
||||
}
|
||||
|
||||
bool ConPtyAnonymousPipeProcess::startProcess(const QString &shellPath, QStringList environment, qint16 cols, qint16 rows)
|
||||
bool ConPtyProcess::startProcess(const QString &shellPath, QStringList environment, qint16 cols, qint16 rows)
|
||||
{
|
||||
if (!isAvailable())
|
||||
{
|
||||
@@ -213,7 +213,7 @@ bool ConPtyAnonymousPipeProcess::startProcess(const QString &shellPath, QStringL
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConPtyAnonymousPipeProcess::resize(qint16 cols, qint16 rows)
|
||||
bool ConPtyProcess::resize(qint16 cols, qint16 rows)
|
||||
{
|
||||
if (m_ptyHandler == nullptr)
|
||||
{
|
||||
@@ -232,7 +232,7 @@ bool ConPtyAnonymousPipeProcess::resize(qint16 cols, qint16 rows)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConPtyAnonymousPipeProcess::kill()
|
||||
bool ConPtyProcess::kill()
|
||||
{
|
||||
bool exitCode = false;
|
||||
|
||||
@@ -261,13 +261,13 @@ bool ConPtyAnonymousPipeProcess::kill()
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
IPtyProcess::PtyType ConPtyAnonymousPipeProcess::type()
|
||||
IPtyProcess::PtyType ConPtyProcess::type()
|
||||
{
|
||||
return PtyType::ConPty;
|
||||
}
|
||||
|
||||
#ifdef PTYQT_DEBUG
|
||||
QString ConPtyAnonymousPipeProcess::dumpDebugInfo()
|
||||
QString ConPtyProcess::dumpDebugInfo()
|
||||
{
|
||||
return QString("PID: %1, Type: %2, Cols: %3, Rows: %4")
|
||||
.arg(m_pid).arg(type())
|
||||
@@ -275,25 +275,25 @@ QString ConPtyAnonymousPipeProcess::dumpDebugInfo()
|
||||
}
|
||||
#endif
|
||||
|
||||
QIODevice *ConPtyAnonymousPipeProcess::notifier()
|
||||
QIODevice *ConPtyProcess::notifier()
|
||||
{
|
||||
return &m_buffer;
|
||||
}
|
||||
|
||||
QByteArray ConPtyAnonymousPipeProcess::readAll()
|
||||
QByteArray ConPtyProcess::readAll()
|
||||
{
|
||||
QMutexLocker locker(&m_bufferMutex);
|
||||
return m_buffer.m_readBuffer;
|
||||
}
|
||||
|
||||
qint64 ConPtyAnonymousPipeProcess::write(const QByteArray &byteArray)
|
||||
qint64 ConPtyProcess::write(const QByteArray &byteArray)
|
||||
{
|
||||
DWORD dwBytesWritten{};
|
||||
WriteFile(m_hPipeOut, byteArray.data(), byteArray.size(), &dwBytesWritten, NULL);
|
||||
return dwBytesWritten;
|
||||
}
|
||||
|
||||
bool ConPtyAnonymousPipeProcess::isAvailable()
|
||||
bool ConPtyProcess::isAvailable()
|
||||
{
|
||||
#ifdef TOO_OLD_WINSDK
|
||||
return false; //very importnant! ConPty can be built, but it doesn't work if built with old sdk and Win10 < 1903
|
||||
@@ -1,11 +1,14 @@
|
||||
#ifndef CONPTY_SHARED_H
|
||||
#define CONPTY_SHARED_H
|
||||
#ifndef CONPTYPROCESS_H
|
||||
#define CONPTYPROCESS_H
|
||||
|
||||
#include "iptyprocess.h"
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#include <QLibrary>
|
||||
#include <stdio.h>
|
||||
#include <QMutex>
|
||||
#include <QTimer>
|
||||
#include <QThread>
|
||||
|
||||
//Taken from the RS5 Windows SDK, but redefined here in case we're targeting <= 17733
|
||||
//Just for compile, ConPty doesn't work with Windows SDK < 17733
|
||||
@@ -92,4 +95,67 @@ private:
|
||||
QString m_lastError;
|
||||
};
|
||||
|
||||
#endif // CONPTY_SHARED_H
|
||||
class PtyBuffer : public QIODevice
|
||||
{
|
||||
friend class ConPtyProcess;
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
PtyBuffer() { }
|
||||
~PtyBuffer() { }
|
||||
|
||||
//just empty realization, we need only 'readyRead' signal of this class
|
||||
qint64 readData(char *data, qint64 maxlen) { return 0; }
|
||||
qint64 writeData(const char *data, qint64 len) { return 0; }
|
||||
|
||||
bool isSequential() { return true; }
|
||||
qint64 bytesAvailable() { return m_readBuffer.size(); }
|
||||
qint64 size() { return m_readBuffer.size(); }
|
||||
|
||||
void emitReadyRead()
|
||||
{
|
||||
//for emit signal from PtyBuffer own thread
|
||||
QTimer::singleShot(1, this, [this]()
|
||||
{
|
||||
emit readyRead();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
QByteArray m_readBuffer;
|
||||
};
|
||||
|
||||
class ConPtyProcess : public IPtyProcess
|
||||
{
|
||||
public:
|
||||
ConPtyProcess();
|
||||
~ConPtyProcess();
|
||||
|
||||
bool startProcess(const QString &shellPath, QStringList environment, qint16 cols, qint16 rows);
|
||||
bool resize(qint16 cols, qint16 rows);
|
||||
bool kill();
|
||||
PtyType type();
|
||||
#ifdef PTYQT_DEBUG
|
||||
QString dumpDebugInfo();
|
||||
#endif
|
||||
virtual QIODevice *notifier();
|
||||
virtual QByteArray readAll();
|
||||
virtual qint64 write(const QByteArray &byteArray);
|
||||
bool isAvailable();
|
||||
|
||||
private:
|
||||
HRESULT createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows);
|
||||
HRESULT initializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC);
|
||||
|
||||
private:
|
||||
WindowsContext m_winContext;
|
||||
HPCON m_ptyHandler;
|
||||
HANDLE m_hPipeIn, m_hPipeOut;
|
||||
|
||||
QThread *m_readThread;
|
||||
QMutex m_bufferMutex;
|
||||
PtyBuffer m_buffer;
|
||||
|
||||
};
|
||||
|
||||
#endif // CONPTYPROCESS_H
|
||||
+2
-33
@@ -17,9 +17,8 @@ public:
|
||||
{
|
||||
UnixPty = 0,
|
||||
WinPty = 1,
|
||||
//ConPtyNamedPipe = 2,
|
||||
ConPty = 3,
|
||||
AutoPty = 4
|
||||
ConPty = 2,
|
||||
AutoPty = 3
|
||||
};
|
||||
|
||||
IPtyProcess()
|
||||
@@ -49,34 +48,4 @@ protected:
|
||||
QPair<qint16, qint16> m_size; //cols / rows
|
||||
};
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
class IWindowsPtyProcess : public IPtyProcess
|
||||
{
|
||||
protected:
|
||||
IWindowsPtyProcess()
|
||||
: IPtyProcess()
|
||||
{ }
|
||||
|
||||
QIODevice *notifier()
|
||||
{
|
||||
return &m_outSocket;
|
||||
}
|
||||
|
||||
QByteArray readAll()
|
||||
{
|
||||
return m_outSocket.readAll();
|
||||
}
|
||||
|
||||
qint64 write(const QByteArray &byteArray)
|
||||
{
|
||||
return m_inSocket.write(byteArray);
|
||||
}
|
||||
protected:
|
||||
QString m_conInName;
|
||||
QString m_conOutName;
|
||||
QLocalSocket m_inSocket;
|
||||
QLocalSocket m_outSocket;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // IPTYPROCESS_H
|
||||
|
||||
+4
-8
@@ -3,8 +3,7 @@
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "winptyprocess.h"
|
||||
//#include "conptynamedpipeprocess.h"
|
||||
#include "conptyanonymouspipeprocess.h"
|
||||
#include "conptyprocess.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
@@ -19,11 +18,8 @@ IPtyProcess *PtyQt::createPtyProcess(IPtyProcess::PtyType ptyType)
|
||||
case IPtyProcess::PtyType::WinPty:
|
||||
return new WinPtyProcess();
|
||||
break;
|
||||
// case IPtyProcess::PtyType::ConPtyNamedPipe:
|
||||
// return new ConPtyNamedPipeProcess();
|
||||
// break;
|
||||
case IPtyProcess::PtyType::ConPty:
|
||||
return new ConPtyAnonymousPipeProcess();
|
||||
return new ConPtyProcess();
|
||||
break;
|
||||
#endif
|
||||
#ifdef Q_OS_UNIX
|
||||
@@ -37,8 +33,8 @@ IPtyProcess *PtyQt::createPtyProcess(IPtyProcess::PtyType ptyType)
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (ConPtyAnonymousPipeProcess().isAvailable())
|
||||
return new ConPtyAnonymousPipeProcess();
|
||||
if (ConPtyProcess().isAvailable())
|
||||
return new ConPtyProcess();
|
||||
else
|
||||
return new WinPtyProcess();
|
||||
#endif
|
||||
|
||||
+16
-1
@@ -16,7 +16,7 @@ QString castErrorToString(winpty_error_ptr_t error_ptr)
|
||||
}
|
||||
|
||||
WinPtyProcess::WinPtyProcess()
|
||||
: IWindowsPtyProcess()
|
||||
: IPtyProcess()
|
||||
, m_ptyHandler(nullptr)
|
||||
, m_innerHandle(nullptr)
|
||||
{
|
||||
@@ -189,6 +189,21 @@ QString WinPtyProcess::dumpDebugInfo()
|
||||
.arg(m_size.first).arg(m_size.second).arg(m_ptyHandler != nullptr)
|
||||
.arg(m_shellPath);
|
||||
}
|
||||
|
||||
QIODevice *WinPtyProcess::notifier()
|
||||
{
|
||||
return &m_outSocket;
|
||||
}
|
||||
|
||||
QByteArray WinPtyProcess::readAll()
|
||||
{
|
||||
return m_outSocket.readAll();
|
||||
}
|
||||
|
||||
qint64 WinPtyProcess::write(const QByteArray &byteArray)
|
||||
{
|
||||
return m_inSocket.write(byteArray);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool WinPtyProcess::isAvailable()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "iptyprocess.h"
|
||||
#include "winpty.h"
|
||||
|
||||
class WinPtyProcess : public IWindowsPtyProcess
|
||||
class WinPtyProcess : public IPtyProcess
|
||||
{
|
||||
public:
|
||||
WinPtyProcess();
|
||||
@@ -17,11 +17,18 @@ public:
|
||||
#ifdef PTYQT_DEBUG
|
||||
QString dumpDebugInfo();
|
||||
#endif
|
||||
QIODevice *notifier();
|
||||
QByteArray readAll();
|
||||
qint64 write(const QByteArray &byteArray);
|
||||
bool isAvailable();
|
||||
|
||||
private:
|
||||
winpty_t *m_ptyHandler;
|
||||
HANDLE m_innerHandle;
|
||||
QString m_conInName;
|
||||
QString m_conOutName;
|
||||
QLocalSocket m_inSocket;
|
||||
QLocalSocket m_outSocket;
|
||||
};
|
||||
|
||||
#endif // WINPTYPROCESS_H
|
||||
|
||||
@@ -193,7 +193,6 @@ private slots:
|
||||
{
|
||||
qDebug() << "Test" << shellPath;
|
||||
IPtyProcess::PtyType ptyType = IPtyProcess::ConPty;
|
||||
//ptyType = IPtyProcess::ConPtyNamedPipe;
|
||||
QScopedPointer<IPtyProcess> conPty(PtyQt::createPtyProcess(ptyType));
|
||||
QCOMPARE(conPty->type(), ptyType);
|
||||
QVERIFY(conPty->isAvailable());
|
||||
|
||||
Reference in New Issue
Block a user