Allow sending escape code to child in chunks

This commit is contained in:
Kovid Goyal 2021-09-24 16:54:04 +05:30
parent 67cef371dc
commit 1cef544cff
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 134 additions and 62 deletions

View File

@ -228,56 +228,92 @@ add_child(ChildMonitor *self, PyObject *args) {
Py_RETURN_NONE;
}
#define schedule_write_to_child_generic(id, num, va_start, get_next_arg, va_end) \
ChildMonitor *self = the_monitor; \
bool found = false; \
const char *data; \
size_t szval, sz = 0; \
va_start(ap, num); \
for (unsigned int i = 0; i < num; i++) { \
get_next_arg(ap); \
sz += szval; \
} \
va_end(ap); \
children_mutex(lock); \
for (size_t i = 0; i < self->count; i++) { \
if (children[i].id == id) { \
Screen *screen = children[i].screen; \
screen_mutex(lock, write); \
size_t space_left = screen->write_buf_sz - screen->write_buf_used; \
if (space_left < sz) { \
if (screen->write_buf_used + sz > 100 * 1024 * 1024) { \
log_error("Too much data being sent to child with id: %lu, ignoring it", id); \
screen_mutex(unlock, write); \
break; \
} \
screen->write_buf_sz = screen->write_buf_used + sz; \
screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz); \
if (screen->write_buf == NULL) { fatal("Out of memory."); } \
} \
found = true; \
va_start(ap, num); \
for (unsigned int i = 0; i < num; i++) { \
get_next_arg(ap); \
memcpy(screen->write_buf + screen->write_buf_used, data, szval); \
screen->write_buf_used += szval; \
} \
va_end(ap); \
if (screen->write_buf_sz > BUFSIZ && screen->write_buf_used < BUFSIZ) { \
screen->write_buf_sz = BUFSIZ; \
screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz); \
if (screen->write_buf == NULL) { fatal("Out of memory."); } \
} \
if (screen->write_buf_used) wakeup_io_loop(self, false); \
screen_mutex(unlock, write); \
break; \
} \
} \
children_mutex(unlock); \
return found;
bool
schedule_write_to_child(unsigned long id, unsigned int num, ...) {
ChildMonitor *self = the_monitor;
bool found = false;
const char *data;
size_t sz = 0;
va_list ap;
va_start(ap, num);
for (unsigned int i = 0; i < num; i++) {
va_arg(ap, const char*);
sz += va_arg(ap, size_t);
#define get_next_arg(ap) data = va_arg(ap, const char*); szval = va_arg(ap, size_t);
schedule_write_to_child_generic(id, num, va_start, get_next_arg, va_end);
#undef get_next_arg
}
va_end(ap);
children_mutex(lock);
for (size_t i = 0; i < self->count; i++) {
if (children[i].id == id) {
Screen *screen = children[i].screen;
screen_mutex(lock, write);
size_t space_left = screen->write_buf_sz - screen->write_buf_used;
if (space_left < sz) {
if (screen->write_buf_used + sz > 100 * 1024 * 1024) {
log_error("Too much data being sent to child with id: %lu, ignoring it", id);
screen_mutex(unlock, write);
break;
bool
schedule_write_to_child_python(unsigned long id, const char *prefix, PyObject *ap, const char *suffix) {
if (!PyTuple_Check(ap)) return false;
bool has_prefix = prefix && prefix[0], has_suffix = suffix && suffix[0];
const size_t extra = (has_prefix ? 1 : 0) + (has_suffix ? 1 : 0);
size_t num = PyTuple_GET_SIZE(ap) + extra;
Py_ssize_t pidx;
#define py_start(ap, num) pidx = 0;
#define py_end(ap) pidx = 0;
#define get_next_arg(ap) { \
if (pidx == 0 && has_prefix) { data = prefix; szval = strlen(prefix); } \
else { \
size_t pidxf = pidx++; \
if (has_prefix) pidxf--; \
if (has_suffix && pidxf >= (size_t)PyBytes_GET_SIZE(ap)) { data = suffix; szval = strlen(suffix); } \
else { \
PyObject *t = PyTuple_GET_ITEM(ap, pidxf); \
if (PyBytes_Check(t)) { data = PyBytes_AS_STRING(t); szval = PyBytes_GET_SIZE(t); } \
else { \
Py_ssize_t usz; \
data = PyUnicode_AsUTF8AndSize(t, &usz); szval = usz; \
if (!data) fatal("Failed to convert object to bytes in schedule_write_to_child_python"); \
} \
} \
} \
}
screen->write_buf_sz = screen->write_buf_used + sz;
screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz);
if (screen->write_buf == NULL) { fatal("Out of memory."); }
}
found = true;
va_start(ap, num);
for (unsigned int i = 0; i < num; i++) {
data = va_arg(ap, const char*);
size_t dsz = va_arg(ap, size_t);
memcpy(screen->write_buf + screen->write_buf_used, data, dsz);
screen->write_buf_used += dsz;
}
va_end(ap);
if (screen->write_buf_sz > BUFSIZ && screen->write_buf_used < BUFSIZ) {
screen->write_buf_sz = BUFSIZ;
screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz);
if (screen->write_buf == NULL) { fatal("Out of memory."); }
}
if (screen->write_buf_used) wakeup_io_loop(self, false);
screen_mutex(unlock, write);
break;
}
}
children_mutex(unlock);
return found;
schedule_write_to_child_generic(id, num, py_start, get_next_arg, py_end);
#undef py_start
#undef py_end
#undef get_next_arg
}
static PyObject *

View File

@ -326,6 +326,7 @@ const char* cursor_as_sgr(const Cursor *);
PyObject* cm_thread_write(PyObject *self, PyObject *args);
bool schedule_write_to_child(unsigned long id, unsigned int num, ...);
bool schedule_write_to_child_python(unsigned long id, const char *prefix, PyObject* tuple_of_str_or_bytes, const char *suffix);
bool set_iutf8(int, bool);
color_type colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval);

View File

@ -993,7 +993,7 @@ class Screen:
def resize(self, width: int, height: int) -> None:
pass
def send_escape_code_to_child(self, code: int, text: Union[str, bytes]) -> bool:
def send_escape_code_to_child(self, code: int, text: Union[str, bytes, Tuple[Union[str, bytes], ...]]) -> bool:
pass
def reset_callbacks(self) -> None:

View File

@ -780,29 +780,35 @@ write_to_child(Screen *self, const char *data, size_t sz) {
return written;
}
bool
write_escape_code_to_child(Screen *self, unsigned char which, const char *data) {
bool written = false;
const char *prefix, *suffix = self->modes.eight_bit_controls ? "\x9c" : "\033\\";
static void
get_prefix_and_suffix_for_escape_code(const Screen *self, unsigned char which, const char ** prefix, const char ** suffix) {
*suffix = self->modes.eight_bit_controls ? "\x9c" : "\033\\";
switch(which) {
case DCS:
prefix = self->modes.eight_bit_controls ? "\x90" : "\033P";
*prefix = self->modes.eight_bit_controls ? "\x90" : "\033P";
break;
case CSI:
prefix = self->modes.eight_bit_controls ? "\x9b" : "\033["; suffix = "";
*prefix = self->modes.eight_bit_controls ? "\x9b" : "\033["; *suffix = "";
break;
case OSC:
prefix = self->modes.eight_bit_controls ? "\x9d" : "\033]";
*prefix = self->modes.eight_bit_controls ? "\x9d" : "\033]";
break;
case PM:
prefix = self->modes.eight_bit_controls ? "\x9e" : "\033^";
*prefix = self->modes.eight_bit_controls ? "\x9e" : "\033^";
break;
case APC:
prefix = self->modes.eight_bit_controls ? "\x9f" : "\033_";
*prefix = self->modes.eight_bit_controls ? "\x9f" : "\033_";
break;
default:
fatal("Unknown escape code to write: %u", which);
}
}
bool
write_escape_code_to_child(Screen *self, unsigned char which, const char *data) {
bool written = false;
const char *prefix, *suffix;
get_prefix_and_suffix_for_escape_code(self, which, &prefix, &suffix);
if (self->window_id) {
if (suffix[0]) {
written = schedule_write_to_child(self->window_id, 3, prefix, strlen(prefix), data, strlen(data), suffix, strlen(suffix));
@ -818,6 +824,28 @@ write_escape_code_to_child(Screen *self, unsigned char which, const char *data)
return written;
}
static bool
write_escape_code_to_child_python(Screen *self, unsigned char which, PyObject *data) {
bool written = false;
const char *prefix, *suffix;
get_prefix_and_suffix_for_escape_code(self, which, &prefix, &suffix);
if (self->window_id) written = schedule_write_to_child_python(self->window_id, prefix, data, suffix);
if (self->test_child != Py_None) {
write_to_test_child(self, prefix, strlen(prefix));
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(data); i++) {
PyObject *t = PyTuple_GET_ITEM(data, i);
if (PyBytes_Check(t)) write_to_test_child(self, PyBytes_AS_STRING(t), PyBytes_GET_SIZE(t));
else {
Py_ssize_t sz;
const char *d = PyUnicode_AsUTF8AndSize(t, &sz);
if (d) write_to_test_child(self, d, sz);
}
}
if (suffix[0]) write_to_test_child(self, suffix, strlen(suffix));
}
return written;
}
static bool
cursor_within_margins(Screen *self) {
return self->margin_top <= self->cursor->y && self->cursor->y <= self->margin_bottom;
@ -3234,11 +3262,18 @@ toggle_alt_screen(Screen *self, PyObject *a UNUSED) {
static PyObject*
send_escape_code_to_child(Screen *self, PyObject *args) {
int code;
char *text;
Py_ssize_t sz;
if (!PyArg_ParseTuple(args, "is#", &code, &text, &sz)) return NULL;
if (write_escape_code_to_child(self, code, text)) Py_RETURN_TRUE;
Py_RETURN_FALSE;
PyObject *O;
if (!PyArg_ParseTuple(args, "iO", &code, &O)) return NULL;
bool written = false;
if (PyBytes_Check(O)) written = write_escape_code_to_child(self, code, PyBytes_AS_STRING(O));
else if (PyUnicode_Check(O)) {
const char *t = PyUnicode_AsUTF8(O);
if (t) written = write_escape_code_to_child(self, code, t);
else return NULL;
} else if (PyTuple_Check(O)) written = write_escape_code_to_child_python(self, code, O);
else PyErr_SetString(PyExc_TypeError, "escape code must be str, bytes or tuple");
if (PyErr_Occurred()) return NULL;
if (written) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; }
}
static void