todos:
	- Read more than one byte at a time before emitting signal.
	- Set correct termios and winp values.
	- Handle SIGWINCH stuff when resizing window.

Former-commit-id: 78eeacdc22
This commit is contained in:
Leroy Hopson 2020-07-12 00:35:12 +07:00
parent bc86b92412
commit 5cc2b2c718
8 changed files with 170 additions and 2 deletions

View file

@ -98,7 +98,7 @@ cpp_library += '.' + str(bits)
# make sure our binding library is properly includes
env.Append(CPPPATH=['.', godot_headers_path, cpp_bindings_path + 'include/', cpp_bindings_path + 'include/core/', cpp_bindings_path + 'include/gen/'])
env.Append(LIBPATH=[cpp_bindings_path + 'bin/'] + os.environ['LD_LIBRARY_PATH'].split(':'))
env.Append(LIBS=[cpp_library, 'tsm'])
env.Append(LIBS=[cpp_library, 'tsm', 'util']) # Note util used by pseudoterminal, tsm used by terminal.
# tweak this if you want to use different folders, or more folders, to store your source code in.
env.Append(CPPPATH=['src/'])

View file

@ -1 +1 @@
0ecaef63a90e5b7cc5f1af692f4e6658886dcbcf
da09712f58aeddfe9ec323c9fa07fba8be5ff7b5

View file

@ -1,4 +1,6 @@
#include "pseudoterminal.h"
#include <pty.h>
#include <unistd.h>
using namespace godot;
@ -23,6 +25,111 @@ Pseudoterminal::~Pseudoterminal()
void Pseudoterminal::_init()
{
pty_thread = std::thread(&Pseudoterminal::process_pty, this);
bytes_to_write = 0;
}
void Pseudoterminal::process_pty()
{
int fd;
char *name;
should_process_pty = true;
pid_t pty_pid = forkpty(&fd, NULL, NULL, NULL);
if (pty_pid == -1)
{
ERR_PRINT(String("Error forking pty: {0}").format(Array::make(strerror(errno))));
should_process_pty = false;
return;
}
else if (pty_pid == 0)
{
/* Child */
char termenv[11] = {"TERM=xterm"};
putenv(termenv);
char colortermenv[20] = {"COLORTERM=truecolor"};
putenv(colortermenv);
char *shell = getenv("SHELL");
execvp(shell, NULL);
}
else
{
/* Parent */
while (1)
{
int ready = -1;
fd_set read_fds;
fd_set write_fds;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
FD_SET(fd, &write_fds);
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
ready = select(fd + 1, &read_fds, &write_fds, NULL, &timeout);
if (ready > 0)
{
if (FD_ISSET(fd, &read_fds))
{
std::lock_guard<std::mutex> guard(read_buffer_mutex);
int ret;
int bytes_read = 0;
bytes_read = read(fd, read_buffer, 1);
if (bytes_read <= 0)
continue;
//while (1)
//{
// ret = read(fd, read_buffer, 1);
// if (ret == -1 || ret == 0)
// {
// break;
// }
// else
// {
// bytes_read += ret;
// }
//}
PoolByteArray data = PoolByteArray();
data.resize(bytes_read);
memcpy(data.write().ptr(), read_buffer, bytes_read);
emit_signal("data_received", PoolByteArray(data));
if (bytes_read > 0)
{
Godot::print(String("read {0} bytes").format(Array::make(bytes_read)));
}
}
if (FD_ISSET(fd, &write_fds))
{
std::lock_guard<std::mutex> guard(write_buffer_mutex);
if (bytes_to_write > 0)
{
write(fd, write_buffer, bytes_to_write);
bytes_to_write = 0;
}
}
}
}
}
}
void Pseudoterminal::_ready()
@ -31,4 +138,7 @@ void Pseudoterminal::_ready()
void Pseudoterminal::put_data(PoolByteArray data)
{
std::lock_guard<std::mutex> guard(write_buffer_mutex);
bytes_to_write = data.size();
memcpy(write_buffer, data.read().ptr(), bytes_to_write);
}

View file

@ -3,6 +3,8 @@
#include <Godot.hpp>
#include <Node.hpp>
#include <thread>
#include <mutex>
namespace godot
{
@ -11,6 +13,20 @@ class Pseudoterminal : public Node
{
GODOT_CLASS(Pseudoterminal, Node)
private:
std::thread pty_thread;
bool should_process_pty;
char write_buffer[4096];
int bytes_to_write;
std::mutex write_buffer_mutex;
char read_buffer[4096];
int bytes_to_read;
std::mutex read_buffer_mutex;
void process_pty();
public:
static void _register_methods();