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
|
# 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(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(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.
|
# tweak this if you want to use different folders, or more folders, to store your source code in.
|
||||||
env.Append(CPPPATH=['src/'])
|
env.Append(CPPPATH=['src/'])
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,6 @@
|
||||||
#include "pseudoterminal.h"
|
#include "pseudoterminal.h"
|
||||||
|
#include <pty.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
|
@ -23,6 +25,111 @@ Pseudoterminal::~Pseudoterminal()
|
||||||
|
|
||||||
void Pseudoterminal::_init()
|
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()
|
void Pseudoterminal::_ready()
|
||||||
|
@ -31,4 +138,7 @@ void Pseudoterminal::_ready()
|
||||||
|
|
||||||
void Pseudoterminal::put_data(PoolByteArray data)
|
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 <Godot.hpp>
|
||||||
#include <Node.hpp>
|
#include <Node.hpp>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace godot
|
namespace godot
|
||||||
{
|
{
|
||||||
|
@ -11,6 +13,20 @@ class Pseudoterminal : public Node
|
||||||
{
|
{
|
||||||
GODOT_CLASS(Pseudoterminal, 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:
|
public:
|
||||||
static void _register_methods();
|
static void _register_methods();
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -3,16 +3,58 @@ extends Container
|
||||||
# the window and/or screen. It also connects the terminal
|
# the window and/or screen. It also connects the terminal
|
||||||
# to the input/output of the Psuedoterminal.
|
# 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()
|
onready var viewport = get_viewport()
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
$Pseudoterminal.connect("data_received", $Terminal, "write")
|
$Pseudoterminal.connect("data_received", $Terminal, "write")
|
||||||
|
$Pseudoterminal.connect("data_received", self, "_on_data_received")
|
||||||
|
|
||||||
viewport.connect("size_changed", self, "_resize")
|
viewport.connect("size_changed", self, "_resize")
|
||||||
_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():
|
func _resize():
|
||||||
rect_size = viewport.size
|
rect_size = viewport.size
|
||||||
$Terminal.rect_size = rect_size
|
$Terminal.rect_size = rect_size
|
||||||
|
|
Loading…
Reference in a new issue