mirror of
https://github.com/kingToolbox/WindTerm.git
synced 2024-12-26 04:10:08 +08:00
Add WinPty and ConPty
This commit is contained in:
parent
21662a08f6
commit
23570ead27
261
src/Pty/ConPty.cpp
Normal file
261
src/Pty/ConPty.cpp
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 2020, WindTerm.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ConPty.h"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#define CONPTY_MINIMAL_WINDOWS_VERSION 18309
|
||||
|
||||
class PipeThread : public QThread {
|
||||
public:
|
||||
PipeThread(ConPty *conpty, LPVOID pipe)
|
||||
: QThread(conpty)
|
||||
, m_conpty(conpty)
|
||||
, m_pipe(pipe)
|
||||
{}
|
||||
|
||||
void run() final {
|
||||
constexpr DWORD BUFF_SIZE = 512;
|
||||
char szBuffer[BUFF_SIZE];
|
||||
|
||||
while (isInterruptionRequested() == false) {
|
||||
if (isInterruptionRequested()) {
|
||||
return;
|
||||
}
|
||||
DWORD bytesRead;
|
||||
bool readSuccess = ReadFile(m_pipe, szBuffer, BUFF_SIZE, &bytesRead, NULL);
|
||||
|
||||
if (readSuccess == false) {
|
||||
m_conpty->setErrorCode(GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInterruptionRequested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (readSuccess && bytesRead > 0) {
|
||||
m_conpty->appendBuffer(QByteArray(szBuffer, bytesRead));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ConPty *m_conpty;
|
||||
LPVOID m_pipe;
|
||||
};
|
||||
|
||||
ConPty::ConPty(QObject *parent /*= nullptr*/)
|
||||
: m_inPipe(INVALID_HANDLE_VALUE)
|
||||
, m_outPipe(INVALID_HANDLE_VALUE)
|
||||
, m_pipeThread(nullptr)
|
||||
, m_ptyHandler(INVALID_HANDLE_VALUE)
|
||||
{}
|
||||
|
||||
ConPty::~ConPty() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void ConPty::appendBuffer(const QByteArray &buffer) {
|
||||
if (buffer.isEmpty() == false) {
|
||||
{
|
||||
ThreadLocker<SpinMutex> locker(m_mutex);
|
||||
m_buffer.append(buffer);
|
||||
}
|
||||
emit readyRead();
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT ConPty::createPseudoConsoleAndPipes(HPCON *phPC, HANDLE *phPipeIn, HANDLE *phPipeOut,
|
||||
qint16 rows, qint16 columns) {
|
||||
HRESULT hr = E_UNEXPECTED;
|
||||
HANDLE hPipePTYIn = INVALID_HANDLE_VALUE;
|
||||
HANDLE hPipePTYOut = INVALID_HANDLE_VALUE;
|
||||
|
||||
if (CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) && CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0)) {
|
||||
#if COMPILE_CONPTY_ENABLED
|
||||
hr = CreatePseudoConsole({columns, rows}, hPipePTYIn, hPipePTYOut, 0, phPC);
|
||||
#endif
|
||||
if (INVALID_HANDLE_VALUE != hPipePTYOut) CloseHandle(hPipePTYOut);
|
||||
if (INVALID_HANDLE_VALUE != hPipePTYIn) CloseHandle(hPipePTYIn);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT ConPty::initStartupInfoAttachedToPseudoConsole(STARTUPINFOEX *pStartupInfo, HPCON hPC) {
|
||||
HRESULT hr = E_UNEXPECTED;
|
||||
|
||||
if (pStartupInfo) {
|
||||
SIZE_T attrListSize;
|
||||
|
||||
pStartupInfo->StartupInfo.cb = sizeof(STARTUPINFOEX);
|
||||
|
||||
InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize);
|
||||
|
||||
pStartupInfo->lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attrListSize));
|
||||
|
||||
if (pStartupInfo->lpAttributeList
|
||||
&& InitializeProcThreadAttributeList(pStartupInfo->lpAttributeList, 1, 0, &attrListSize)) {
|
||||
hr = UpdateProcThreadAttribute(
|
||||
pStartupInfo->lpAttributeList,
|
||||
0,
|
||||
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
|
||||
hPC,
|
||||
sizeof(HPCON),
|
||||
NULL,
|
||||
NULL
|
||||
) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
|
||||
} else {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
bool ConPty::createProcess(QString command, const QString &arguments,
|
||||
const QString &workingDirectory, const QStringList &environment,
|
||||
qint16 rows, qint16 columns) {
|
||||
if (isAvailable() == false) {
|
||||
setErrorString(tr("Windows 10 version below 1809 is not supported."));
|
||||
return false;
|
||||
}
|
||||
stop();
|
||||
|
||||
HRESULT hr = createPseudoConsoleAndPipes(&m_ptyHandler, &m_inPipe, &m_outPipe, rows, columns);
|
||||
|
||||
if (hr == S_OK) {
|
||||
m_startupInfo = std::make_unique<STARTUPINFOEX>();
|
||||
m_processInformation = std::make_unique<PROCESS_INFORMATION>();
|
||||
|
||||
m_pipeThread = new PipeThread(this, m_inPipe);
|
||||
m_pipeThread->start();
|
||||
|
||||
if (initStartupInfoAttachedToPseudoConsole(m_startupInfo.get(), m_ptyHandler) == S_OK) {
|
||||
std::wstring env = environment.join(QChar('\0')).append(QChar('\0')).toStdWString();
|
||||
|
||||
if (arguments.isEmpty() == false) {
|
||||
command.append(" ").append(arguments);
|
||||
}
|
||||
|
||||
LPWSTR szCommand = new wchar_t[command.size() + 1];
|
||||
int commandLength = command.toWCharArray(szCommand);
|
||||
szCommand[commandLength] = '\0';
|
||||
|
||||
hr = CreateProcess(
|
||||
NULL,
|
||||
szCommand,
|
||||
NULL,
|
||||
NULL,
|
||||
FALSE,
|
||||
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT,
|
||||
LPWSTR(env.data()),
|
||||
workingDirectory.isEmpty() ? NULL : workingDirectory.toStdWString().c_str(),
|
||||
&m_startupInfo->StartupInfo,
|
||||
m_processInformation.get()
|
||||
) ? S_OK : GetLastError();
|
||||
|
||||
delete szCommand;
|
||||
szCommand = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (hr == S_OK) {
|
||||
m_rows = rows;
|
||||
m_columns = columns;
|
||||
|
||||
installWinProcessEventNotifier(m_processInformation->hProcess);
|
||||
} else {
|
||||
setErrorCode(GetLastError());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConPty::isAvailable() {
|
||||
qint32 buildNumber = QSysInfo::kernelVersion().split(".").last().toInt();
|
||||
return (buildNumber >= CONPTY_MINIMAL_WINDOWS_VERSION) ? true : false;
|
||||
}
|
||||
|
||||
QByteArray ConPty::readAll() {
|
||||
ThreadLocker<SpinMutex> locker(m_mutex);
|
||||
return std::move(m_buffer);
|
||||
}
|
||||
|
||||
bool ConPty::resizeWindow(qint16 rows, qint16 columns) {
|
||||
bool success = true;
|
||||
|
||||
if (rows != m_rows && columns != m_columns) {
|
||||
#if COMPILE_CONPTY_ENABLED
|
||||
HRESULT hr = (m_ptyHandler != INVALID_HANDLE_VALUE)
|
||||
? ResizePseudoConsole(m_ptyHandler, { columns, rows })
|
||||
: S_FALSE;
|
||||
success = (hr == S_OK) ? true : false;
|
||||
#endif
|
||||
if (success) {
|
||||
rows = m_rows;
|
||||
columns = m_columns;
|
||||
}
|
||||
}
|
||||
Q_ASSERT(success);
|
||||
return success;
|
||||
}
|
||||
|
||||
void ConPty::stop() {
|
||||
if (m_pipeThread) {
|
||||
m_pipeThread->requestInterruption();
|
||||
}
|
||||
|
||||
if (m_processInformation) {
|
||||
uninstallWinProcessEventNotifier(m_processInformation->hProcess);
|
||||
CloseHandle(m_processInformation->hThread);
|
||||
CloseHandle(m_processInformation->hProcess);
|
||||
}
|
||||
|
||||
if (m_startupInfo) {
|
||||
DeleteProcThreadAttributeList(m_startupInfo->lpAttributeList);
|
||||
free(m_startupInfo->lpAttributeList);
|
||||
}
|
||||
|
||||
if (m_ptyHandler != INVALID_HANDLE_VALUE) {
|
||||
#if COMPILE_CONPTY_ENABLED
|
||||
ClosePseudoConsole(m_ptyHandler);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (m_inPipe != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(m_inPipe);
|
||||
}
|
||||
|
||||
if (m_outPipe != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(m_outPipe);
|
||||
}
|
||||
|
||||
if (m_pipeThread) {
|
||||
m_pipeThread->wait(1000);
|
||||
m_pipeThread->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
qint64 ConPty::write(const QByteArray &text) {
|
||||
DWORD bytesWritten;
|
||||
|
||||
WriteFile(m_outPipe, text.data(), text.size(), &bytesWritten, NULL);
|
||||
return bytesWritten;
|
||||
}
|
66
src/Pty/ConPty.h
Normal file
66
src/Pty/ConPty.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2020, WindTerm.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef CONPTY_H
|
||||
#define CONPTY_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Pty.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
class PipeThread;
|
||||
|
||||
class ConPty
|
||||
: public Pty
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConPty(QObject *parent = nullptr);
|
||||
virtual ~ConPty();
|
||||
|
||||
void appendBuffer(const QByteArray &buffer);
|
||||
bool createProcess(QString command, const QString &arguments,
|
||||
const QString &workingDirectory, const QStringList &environment,
|
||||
qint16 rows, qint16 columns) final;
|
||||
static bool isAvailable();
|
||||
QByteArray readAll() final;
|
||||
bool resizeWindow(qint16 rows, qint16 columns) final;
|
||||
qint64 write(const QByteArray &text) final;
|
||||
|
||||
private:
|
||||
HRESULT createPseudoConsoleAndPipes(HPCON *phPC, HANDLE *phPipeIn, HANDLE *phPipeOut, qint16 rows, qint16 columns);
|
||||
HRESULT initStartupInfoAttachedToPseudoConsole(STARTUPINFOEX *pStartupInfo, HPCON hPC);
|
||||
void stop();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(ConPty)
|
||||
|
||||
QByteArray m_buffer;
|
||||
PipeThread *m_pipeThread;
|
||||
|
||||
HANDLE m_inPipe;
|
||||
HANDLE m_outPipe;
|
||||
HPCON m_ptyHandler;
|
||||
std::unique_ptr<PROCESS_INFORMATION> m_processInformation;
|
||||
std::unique_ptr<STARTUPINFOEX> m_startupInfo;
|
||||
};
|
||||
|
||||
#endif // CONPTY_H
|
95
src/Pty/Pty.cpp
Normal file
95
src/Pty/Pty.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2020, WindTerm.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Pty.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <QWinEventNotifier>
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
Pty::Pty()
|
||||
: m_columns(-1)
|
||||
, m_errorCode(0)
|
||||
, m_rows(-1)
|
||||
#ifdef Q_OS_WIN
|
||||
, m_winProcessEventNotifier(nullptr)
|
||||
#endif
|
||||
{}
|
||||
|
||||
QString Pty::errorString() {
|
||||
ThreadLocker<SpinMutex> locker(m_mutex);
|
||||
return std::move(m_errorString);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
void Pty::installWinProcessEventNotifier(void *handle) {
|
||||
if (m_winProcessEventNotifier == nullptr) {
|
||||
m_winProcessEventNotifier = new QWinEventNotifier(handle, this);
|
||||
|
||||
connect(m_winProcessEventNotifier, &QWinEventNotifier::activated, this, [this](HANDLE handle) {
|
||||
if (handle) {
|
||||
DWORD exitCode;
|
||||
|
||||
if (GetExitCodeProcess(handle, &exitCode)) {
|
||||
setErrorString(QString("Process exited with code %1").arg(
|
||||
QString::number(exitCode, (exitCode >= 0xFF) ? 16 : 10).prepend((exitCode >= 0xFF) ? "0x" : "")
|
||||
));
|
||||
}
|
||||
m_winProcessEventNotifier->setEnabled(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (m_winProcessEventNotifier->handle() != handle) {
|
||||
m_winProcessEventNotifier->setHandle(handle);
|
||||
m_winProcessEventNotifier->setEnabled(true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Pty::setErrorCode(int errorCode) {
|
||||
constexpr int bufferLength = 512;
|
||||
wchar_t buffer[bufferLength];
|
||||
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode,
|
||||
LANG_NEUTRAL, buffer, bufferLength, NULL);
|
||||
|
||||
QString lastError = QString::fromWCharArray(buffer);
|
||||
setErrorString(lastError);
|
||||
|
||||
m_errorCode = errorCode;
|
||||
}
|
||||
|
||||
void Pty::setErrorString(const QString &errorString) {
|
||||
if (errorString.isEmpty() == false) {
|
||||
{
|
||||
ThreadLocker<SpinMutex> locker(m_mutex);
|
||||
m_errorString = errorString;
|
||||
}
|
||||
emit errorOccurred();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
void Pty::uninstallWinProcessEventNotifier(void *handle) {
|
||||
if (m_winProcessEventNotifier != nullptr
|
||||
&& m_winProcessEventNotifier->handle() == handle) {
|
||||
m_winProcessEventNotifier->deleteLater();
|
||||
m_winProcessEventNotifier = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
73
src/Pty/Pty.h
Normal file
73
src/Pty/Pty.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2020, WindTerm.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PTY_H
|
||||
#define PTY_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "Public/Spin.h"
|
||||
|
||||
class QWinEventNotifier;
|
||||
|
||||
class Pty
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Pty();
|
||||
virtual ~Pty() = default;
|
||||
|
||||
virtual bool createProcess(QString command, const QString &arguments,
|
||||
const QString &workingDirectory, const QStringList &environment,
|
||||
qint16 rows, qint16 columns) = 0;
|
||||
int errorCode() const { return m_errorCode; }
|
||||
QString errorString();
|
||||
virtual QByteArray readAll() = 0;
|
||||
virtual bool resizeWindow(qint16 rows, qint16 columns) = 0;
|
||||
void setErrorCode(int errorCode);
|
||||
void setErrorString(const QString &errorString);
|
||||
virtual qint64 write(const QByteArray &text) = 0;
|
||||
|
||||
protected:
|
||||
#ifdef Q_OS_WIN
|
||||
void installWinProcessEventNotifier(void *handle);
|
||||
void uninstallWinProcessEventNotifier(void *handle);
|
||||
#endif
|
||||
|
||||
Q_SIGNALS:
|
||||
void errorOccurred();
|
||||
void readyRead();
|
||||
|
||||
protected:
|
||||
SpinMutex m_mutex;
|
||||
|
||||
qint16 m_columns;
|
||||
qint16 m_rows;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(Pty)
|
||||
|
||||
int m_errorCode;
|
||||
QString m_errorString;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QWinEventNotifier *m_winProcessEventNotifier;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // PTY_H
|
200
src/Pty/WinPty.cpp
Normal file
200
src/Pty/WinPty.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2020, WindTerm.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "WinPty.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QLocalSocket>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
const char *WINPTY_AGENT_NAME = "winpty-agent.exe";
|
||||
const char *WINPTY_DLL_NAME = "winpty.dll";
|
||||
|
||||
QString castErrorToString(winpty_error_ptr_t error_ptr) {
|
||||
return QString::fromStdWString(winpty_error_msg(error_ptr));
|
||||
}
|
||||
|
||||
WinPty::WinPty(QObject *parent /*= nullptr*/)
|
||||
: m_ptyHandler(nullptr)
|
||||
, m_innerHandle(INVALID_HANDLE_VALUE)
|
||||
, m_outSocket(nullptr)
|
||||
, m_inSocket(nullptr)
|
||||
{}
|
||||
|
||||
WinPty::~WinPty() {
|
||||
stop();
|
||||
}
|
||||
|
||||
bool WinPty::createProcess(QString command, const QString &arguments,
|
||||
const QString &workingDirectory, const QStringList &environment,
|
||||
qint16 rows, qint16 columns) {
|
||||
bool success = false;
|
||||
winpty_error_ptr_t errorPtr = nullptr;
|
||||
QString errorString;
|
||||
|
||||
do {
|
||||
stop();
|
||||
|
||||
if (isAvailable() == false) {
|
||||
errorString = tr("Winpty-agent.exe or winpty.dll not found!.");
|
||||
break;
|
||||
}
|
||||
QString commandWithArguments = command;
|
||||
|
||||
if (arguments.isEmpty() == false) {
|
||||
commandWithArguments.append(" ").append(arguments);
|
||||
}
|
||||
std::wstring env = environment.join(QChar('\0')).append(QChar('\0')).toStdWString();
|
||||
winpty_config_t* startConfig = winpty_config_new(0, &errorPtr);
|
||||
|
||||
if (startConfig == nullptr) {
|
||||
errorString = QString("WinPty Error: create start config -> %1").arg(castErrorToString(errorPtr));
|
||||
break;
|
||||
}
|
||||
winpty_config_set_initial_size(startConfig, columns, rows);
|
||||
winpty_config_set_mouse_mode(startConfig, WINPTY_MOUSE_MODE_AUTO);
|
||||
|
||||
m_ptyHandler = winpty_open(startConfig, &errorPtr);
|
||||
winpty_config_free(startConfig);
|
||||
|
||||
if (m_ptyHandler == nullptr) {
|
||||
errorString = QString("WinPty Error: start agent -> %1").arg(castErrorToString(errorPtr));
|
||||
break;
|
||||
}
|
||||
|
||||
QString m_conInName = QString::fromWCharArray(winpty_conin_name(m_ptyHandler));
|
||||
QString m_conOutName = QString::fromWCharArray(winpty_conout_name(m_ptyHandler));
|
||||
m_outSocket = std::make_unique<QLocalSocket>();
|
||||
m_inSocket = std::make_unique<QLocalSocket>();
|
||||
|
||||
m_outSocket->connectToServer(m_conInName, QIODevice::WriteOnly);
|
||||
m_outSocket->waitForConnected();
|
||||
|
||||
m_inSocket->connectToServer(m_conOutName, QIODevice::ReadOnly);
|
||||
m_inSocket->waitForConnected();
|
||||
|
||||
if (m_outSocket->state() != QLocalSocket::ConnectedState
|
||||
&& m_inSocket->state() != QLocalSocket::ConnectedState) {
|
||||
errorString = QString("WinPty Error: Unable to connect local sockets -> %1 / %2")
|
||||
.arg(m_outSocket->errorString())
|
||||
.arg(m_inSocket->errorString());
|
||||
|
||||
m_inSocket.reset(nullptr);
|
||||
m_outSocket.reset(nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
connect(m_inSocket.get(), &QLocalSocket::readyRead, this, [this]() {
|
||||
emit readyRead();
|
||||
});
|
||||
|
||||
winpty_spawn_config_t* spawnConfig = winpty_spawn_config_new(
|
||||
WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN,
|
||||
command.toStdWString().c_str(),
|
||||
commandWithArguments.toStdWString().c_str(),
|
||||
workingDirectory.isEmpty() ? NULL : workingDirectory.toStdWString().c_str(),
|
||||
env.c_str(),
|
||||
&errorPtr
|
||||
);
|
||||
|
||||
if (spawnConfig == nullptr) {
|
||||
errorString = QString("WinPty Error: create spawn config -> %1").arg(castErrorToString(errorPtr));
|
||||
break;
|
||||
}
|
||||
|
||||
BOOL spawnSuccess = winpty_spawn(m_ptyHandler, spawnConfig, &m_innerHandle, nullptr, nullptr, &errorPtr);
|
||||
winpty_spawn_config_free(spawnConfig);
|
||||
|
||||
if (spawnSuccess == FALSE) {
|
||||
errorString = QString("WinPty Error: start terminal process -> %1").arg(castErrorToString(errorPtr));
|
||||
break;
|
||||
}
|
||||
success = true;
|
||||
} while (0);
|
||||
|
||||
if (errorString.isEmpty() == false) {
|
||||
Q_ASSERT(success == false);
|
||||
|
||||
winpty_error_free(errorPtr);
|
||||
setErrorString(errorString);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
m_columns = columns;
|
||||
m_rows = rows;
|
||||
|
||||
installWinProcessEventNotifier(m_innerHandle);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
QByteArray WinPty::readAll() {
|
||||
QByteArray buffer;
|
||||
|
||||
if (m_inSocket) {
|
||||
buffer = m_inSocket->readAll();
|
||||
Q_ASSERT(buffer.isEmpty() == false);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool WinPty::resizeWindow(qint16 rows, qint16 columns) {
|
||||
bool success = true;
|
||||
|
||||
if (rows != m_rows && columns != m_columns) {
|
||||
success = m_ptyHandler ? winpty_set_size(m_ptyHandler, columns, rows, nullptr) : false;
|
||||
|
||||
if (success) {
|
||||
m_rows = rows;
|
||||
m_columns = columns;
|
||||
}
|
||||
}
|
||||
Q_ASSERT(success);
|
||||
return success;
|
||||
}
|
||||
|
||||
void WinPty::stop() {
|
||||
if (m_ptyHandler != nullptr) {
|
||||
winpty_free(m_ptyHandler);
|
||||
m_ptyHandler = nullptr;
|
||||
}
|
||||
|
||||
if (m_innerHandle != INVALID_HANDLE_VALUE) {
|
||||
uninstallWinProcessEventNotifier(m_innerHandle);
|
||||
CloseHandle(m_innerHandle);
|
||||
m_innerHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
m_outSocket.reset(nullptr);
|
||||
m_inSocket.reset(nullptr);
|
||||
}
|
||||
|
||||
qint64 WinPty::write(const QByteArray &text) {
|
||||
qint64 bytesWritten = -1;
|
||||
|
||||
if (m_outSocket) {
|
||||
bytesWritten = m_outSocket->write(text);
|
||||
Q_ASSERT(bytesWritten != -1);
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
bool WinPty::isAvailable() {
|
||||
return QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_AGENT_NAME)
|
||||
&& QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_DLL_NAME);
|
||||
}
|
61
src/Pty/WinPty.h
Normal file
61
src/Pty/WinPty.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2020, WindTerm.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef WINPTY_H
|
||||
#define WINPTY_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Pty.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
#include "winpty_api.h"
|
||||
|
||||
class QLocalSocket;
|
||||
|
||||
class WinPty
|
||||
: public Pty
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WinPty(QObject *parent = nullptr);
|
||||
virtual ~WinPty();
|
||||
|
||||
bool createProcess(QString command, const QString &arguments,
|
||||
const QString &workingDirectory, const QStringList &environment,
|
||||
qint16 rows, qint16 columns) final;
|
||||
static bool isAvailable();
|
||||
QByteArray readAll() final;
|
||||
bool resizeWindow(qint16 rows, qint16 columns) final;
|
||||
qint64 write(const QByteArray &text) final;
|
||||
|
||||
private:
|
||||
void stop();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(WinPty)
|
||||
|
||||
winpty_t *m_ptyHandler;
|
||||
HANDLE m_innerHandle;
|
||||
std::unique_ptr<QLocalSocket> m_inSocket;
|
||||
std::unique_ptr<QLocalSocket> m_outSocket;
|
||||
};
|
||||
|
||||
#endif // WINPTY_H
|
21
src/Pty/ptyqt LICENSE
Normal file
21
src/Pty/ptyqt LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Vitaly Petrov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -16,6 +16,10 @@ A very safe encryption class using the PBKDF2-algorithm as defined in RFC 8018.
|
||||
|
||||
An improved version based on Onigmo 5.13.5. In particular, **the addition of iterator makes it possible to match gap buffer or nonadjacent memory blocks.** Please refer to the sample files for how to use.
|
||||
|
||||
## Pty
|
||||
|
||||
An improved version based on ptyqt[https://github.com/kafeg/ptyqt]. **Almost all the code was rewritten to make the pty more robust and stable.**
|
||||
|
||||
## ScopeGuard.h
|
||||
|
||||
A class of which the sole purpose is to run the function f in its destructor. This is useful for guaranteeing your cleanup code is executed.
|
||||
|
Loading…
Reference in New Issue
Block a user