mirror of
https://github.com/lihop/godot-xterm.git
synced 2024-11-10 04:40:25 +01:00
Ok
todos: - Read more than one byte at a time before emitting signal. - Set correct termios and winp values. - Handle SIGWINCH stuff when resizing window.
This commit is contained in:
parent
6b92606d99
commit
78eeacdc22
8 changed files with 169 additions and 1 deletions
Binary file not shown.
|
@ -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/'])
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
Binary file not shown.
|
@ -3,16 +3,58 @@ extends Container
|
|||
# the window and/or screen. It also connects the terminal
|
||||
# to the input/output of the Psuedoterminal.
|
||||
|
||||
const ESCAPE = 27
|
||||
const BACKSPACE = 8
|
||||
const BEEP = 7
|
||||
const SPACE = 32
|
||||
const LEFT_BRACKET = 91
|
||||
const ENTER = 10
|
||||
const BACKSPACE_ALT = 127
|
||||
|
||||
onready var viewport = get_viewport()
|
||||
|
||||
func _ready():
|
||||
$Pseudoterminal.connect("data_received", $Terminal, "write")
|
||||
$Pseudoterminal.connect("data_received", self, "_on_data_received")
|
||||
|
||||
viewport.connect("size_changed", self, "_resize")
|
||||
_resize()
|
||||
|
||||
|
||||
func _input(event):
|
||||
#return
|
||||
if event is InputEventKey and event.pressed:
|
||||
var data = PoolByteArray([])
|
||||
accept_event()
|
||||
|
||||
# TODO: Handle more of these.
|
||||
if (event.control and event.scancode == KEY_C):
|
||||
data.append(3)
|
||||
elif event.unicode:
|
||||
data.append(event.unicode)
|
||||
elif event.scancode == KEY_ENTER:
|
||||
data.append(ENTER)
|
||||
elif event.scancode == KEY_BACKSPACE:
|
||||
data.append(BACKSPACE_ALT)
|
||||
elif event.scancode == KEY_ESCAPE:
|
||||
data.append(27)
|
||||
elif event.scancode == KEY_TAB:
|
||||
data.append(9)
|
||||
elif OS.get_scancode_string(event.scancode) == "Shift":
|
||||
pass
|
||||
elif OS.get_scancode_string(event.scancode) == "Control":
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
#push_warning('Unhandled input. scancode: ' + str(OS.get_scancode_string(event.scancode)))
|
||||
#emit_signal("output", data)
|
||||
$Pseudoterminal.put_data(data)
|
||||
|
||||
|
||||
func _on_data_received(data: PoolByteArray):
|
||||
print("Got data: %s" % data.get_string_from_utf8())
|
||||
|
||||
|
||||
func _resize():
|
||||
rect_size = viewport.size
|
||||
$Terminal.rect_size = rect_size
|
||||
|
|
Loading…
Reference in a new issue