2023-01-08 10:41:48 +01:00
|
|
|
// SPDX-FileCopyrightText: 2021-2023 Leroy Hopson <godot-xterm@leroy.geek.nz>
|
|
|
|
// SDPX-License-Identifier: MIT
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
#include "pipe.h"
|
|
|
|
#include "libuv_utils.h"
|
|
|
|
|
2021-07-16 09:31:13 +02:00
|
|
|
#ifndef ULONG
|
|
|
|
#define ULONG size_t
|
|
|
|
#endif
|
|
|
|
|
2021-07-02 19:27:34 +02:00
|
|
|
using namespace godot;
|
|
|
|
|
2022-12-29 10:52:13 +01:00
|
|
|
void Pipe::_bind_methods() {
|
2023-01-08 10:41:48 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("_init"), &Pipe::_init);
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-12-29 10:52:13 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("poll"), &Pipe::_poll_connection);
|
2023-01-08 10:41:48 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("open", "fd", "ipc"), &Pipe::open);
|
2022-12-29 10:52:13 +01:00
|
|
|
ClassDB::bind_method(D_METHOD("write"), &Pipe::write);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_status"), &Pipe::get_status);
|
|
|
|
ClassDB::bind_method(D_METHOD("close"), &Pipe::close);
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2023-01-08 10:41:48 +01:00
|
|
|
ADD_SIGNAL(MethodInfo("data_received",
|
|
|
|
PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
|
2021-07-02 19:27:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Pipe::Pipe() {}
|
2021-07-22 07:17:23 +02:00
|
|
|
Pipe::~Pipe() { close(); }
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
void Pipe::_init() {}
|
|
|
|
|
|
|
|
void _poll_connection();
|
|
|
|
|
|
|
|
void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf);
|
|
|
|
|
2021-07-22 07:17:23 +02:00
|
|
|
void _close_cb(uv_handle_t *handle);
|
|
|
|
|
2021-07-02 19:27:34 +02:00
|
|
|
void _write_cb(uv_write_t *req, int status);
|
|
|
|
|
|
|
|
void _alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf);
|
|
|
|
|
2022-12-29 10:52:13 +01:00
|
|
|
Error Pipe::open(int fd, bool ipc = false) {
|
2021-07-02 19:27:34 +02:00
|
|
|
RETURN_IF_UV_ERR(uv_pipe_init(uv_default_loop(), &handle, ipc));
|
|
|
|
|
|
|
|
handle.data = this;
|
|
|
|
|
|
|
|
RETURN_IF_UV_ERR(uv_pipe_open(&handle, fd));
|
|
|
|
RETURN_IF_UV_ERR(uv_stream_set_blocking((uv_stream_t *)&handle, false));
|
|
|
|
RETURN_IF_UV_ERR(
|
|
|
|
uv_read_start((uv_stream_t *)&handle, _alloc_buffer, _read_cb));
|
|
|
|
|
2022-06-23 15:30:27 +02:00
|
|
|
status = 1;
|
2022-12-29 10:52:13 +01:00
|
|
|
return OK;
|
2021-07-02 19:27:34 +02:00
|
|
|
}
|
|
|
|
|
2021-07-22 07:17:23 +02:00
|
|
|
void Pipe::close() {
|
2022-06-23 15:30:27 +02:00
|
|
|
uv_close((uv_handle_t *)&handle, _close_cb);
|
2021-07-22 07:17:23 +02:00
|
|
|
uv_run(uv_default_loop(), UV_RUN_NOWAIT);
|
|
|
|
}
|
|
|
|
|
2022-12-29 10:52:13 +01:00
|
|
|
Error Pipe::write(PackedByteArray data) {
|
|
|
|
char *s = (char *)data.ptr();
|
2022-08-14 09:45:17 +02:00
|
|
|
ULONG len = data.size();
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-08-14 09:45:17 +02:00
|
|
|
uv_buf_t buf;
|
|
|
|
uv_write_t *req = (uv_write_t *)malloc(sizeof(uv_write_t));
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-08-14 09:45:17 +02:00
|
|
|
buf.base = s;
|
|
|
|
buf.len = len;
|
|
|
|
req->data = (void *)buf.base;
|
2021-07-02 19:27:34 +02:00
|
|
|
|
2022-08-14 09:45:17 +02:00
|
|
|
uv_write(req, (uv_stream_t *)&handle, &buf, 1, _write_cb);
|
2021-07-02 19:27:34 +02:00
|
|
|
uv_run(uv_default_loop(), UV_RUN_NOWAIT);
|
|
|
|
|
2022-12-29 10:52:13 +01:00
|
|
|
return OK;
|
2021-07-02 19:27:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int Pipe::get_status() {
|
2022-06-23 15:30:27 +02:00
|
|
|
if (!uv_is_active((uv_handle_t *)&handle))
|
|
|
|
status = 0;
|
2021-07-02 19:27:34 +02:00
|
|
|
_poll_connection();
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2022-08-14 09:45:17 +02:00
|
|
|
void Pipe::_poll_connection() {
|
|
|
|
if (status == 1 && !uv_is_active((uv_handle_t *)&handle))
|
|
|
|
uv_read_start((uv_stream_t *)&handle, _alloc_buffer, _read_cb);
|
|
|
|
|
|
|
|
uv_run(uv_default_loop(), UV_RUN_NOWAIT);
|
|
|
|
}
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
void _read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) {
|
|
|
|
Pipe *pipe = static_cast<Pipe *>(handle->data);
|
2022-08-14 09:45:17 +02:00
|
|
|
|
2021-07-02 19:27:34 +02:00
|
|
|
if (nread < 0) {
|
|
|
|
switch (nread) {
|
|
|
|
case UV_EOF:
|
|
|
|
// Normal after shell exits.
|
|
|
|
case UV_EIO:
|
|
|
|
// Can happen when the process exits.
|
|
|
|
// As long as PTY has caught it, we should be fine.
|
2021-07-22 07:17:23 +02:00
|
|
|
uv_read_stop(handle);
|
2022-08-14 09:45:17 +02:00
|
|
|
pipe->status = 0;
|
2021-07-02 19:27:34 +02:00
|
|
|
return;
|
|
|
|
default:
|
|
|
|
UV_ERR_PRINT(nread);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-12-29 10:52:13 +01:00
|
|
|
PackedByteArray data;
|
2021-07-02 19:27:34 +02:00
|
|
|
data.resize(nread);
|
2022-12-29 10:52:13 +01:00
|
|
|
{ memcpy(data.ptrw(), buf->base, nread); }
|
2022-08-14 09:45:17 +02:00
|
|
|
std::free((char *)buf->base);
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
pipe->emit_signal("data_received", data);
|
2022-08-14 09:45:17 +02:00
|
|
|
|
|
|
|
// Stop reading until the next poll, otherwise _read_cb could be called
|
|
|
|
// repeatedly, blocking Godot, and eventually resulting in a memory pool
|
|
|
|
// allocation error. This can be triggered with the command `cat /dev/urandom`
|
|
|
|
// if reading is not stopped.
|
|
|
|
uv_read_stop(handle);
|
2021-07-02 19:27:34 +02:00
|
|
|
}
|
|
|
|
|
2022-08-14 09:45:17 +02:00
|
|
|
void _write_cb(uv_write_t *req, int status) { std::free(req); }
|
2021-07-02 19:27:34 +02:00
|
|
|
|
|
|
|
void _alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
|
|
|
|
buf->base = (char *)malloc(suggested_size);
|
|
|
|
buf->len = suggested_size;
|
|
|
|
}
|
2022-06-23 15:30:27 +02:00
|
|
|
|
|
|
|
void _close_cb(uv_handle_t *handle) {
|
|
|
|
Pipe *pipe = static_cast<Pipe *>(handle->data);
|
|
|
|
pipe->status = 0;
|
2022-08-14 09:45:17 +02:00
|
|
|
}
|