terminal: use an async thread to handle input

This commit is contained in:
K. Lange 2019-12-23 13:54:35 +09:00
parent 00493418a3
commit 0fbf35438a
4 changed files with 137 additions and 35 deletions

View File

@ -14,6 +14,7 @@
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
@ -30,6 +31,8 @@
#include <toaru/graphics.h>
#include <toaru/termemu.h>
#include <toaru/mouse.h>
#include <toaru/list.h>
#include <toaru/spinlock.h>
#include "vga-palette.h"
@ -389,9 +392,63 @@ char * copy_selection(void) {
return selection_text;
}
void input_buffer_stuff(char * str) {
size_t s = strlen(str) + 1;
write(fd_master, str, s);
static volatile int input_buffer_lock = 0;
static int input_buffer_semaphore[2];
static list_t * input_buffer_queue = NULL;
struct input_data {
size_t len;
char data[];
};
void * handle_input_writing(void * unused) {
(void)unused;
while (1) {
/* Read one byte from semaphore; as long as semaphore has data,
* there is another input blob to write to the TTY */
char tmp[1];
int c = read(input_buffer_semaphore[0],tmp,1);
if (c > 0) {
/* Retrieve blob */
spin_lock(&input_buffer_lock);
node_t * blob = list_dequeue(input_buffer_queue);
spin_unlock(&input_buffer_lock);
/* No blobs? This shouldn't happen, but just in case, just continue */
if (!blob) {
continue;
}
/* Write blob data to the tty */
struct input_data * value = blob->value;
write(fd_master, value->data, value->len);
free(blob->value);
free(blob);
} else {
/* The pipe has closed, terminal is exiting */
break;
}
}
return NULL;
}
static void write_input_buffer(char * data, size_t len) {
struct input_data * d = malloc(sizeof(struct input_data) + len);
d->len = len;
memcpy(&d->data, data, len);
spin_lock(&input_buffer_lock);
list_insert(input_buffer_queue, d);
spin_unlock(&input_buffer_lock);
write(input_buffer_semaphore[1], d, 1);
}
void handle_input(char c) {
write_input_buffer(&c, 1);
}
void handle_input_s(char * c) {
size_t len = strlen(c);
write_input_buffer(c, len);
}
unsigned short * textmemptr = (unsigned short *)0xB8000;
@ -705,25 +762,8 @@ void term_clear(int i) {
}
}
#define INPUT_SIZE 1024
char input_buffer[INPUT_SIZE];
int input_collected = 0;
void clear_input() {
memset(input_buffer, 0x0, INPUT_SIZE);
input_collected = 0;
}
pid_t child_pid = 0;
void handle_input(char c) {
write(fd_master, &c, 1);
}
void handle_input_s(char * c) {
write(fd_master, c, strlen(c));
}
void key_event(int ret, key_event_t * event) {
if (ret) {
/* Special keys */
@ -933,7 +973,7 @@ term_callbacks_t term_callbacks = {
term_clear,
term_scroll,
term_redraw_cursor,
input_buffer_stuff,
handle_input_s,
set_title,
unsupported,
unsupported_int,
@ -984,6 +1024,7 @@ void check_for_exit(void) {
/* Exit */
char exit_message[] = "[Process terminated]\n";
write(fd_slave, exit_message, sizeof(exit_message));
close(input_buffer_semaphore[1]);
}
static int mouse_x = 0;
@ -1156,6 +1197,11 @@ int main(int argc, char ** argv) {
reinit();
pthread_t input_buffer_thread;
pipe(input_buffer_semaphore);
input_buffer_queue = list_create();
pthread_create(&input_buffer_thread, NULL, handle_input_writing, NULL);
fflush(stdin);
system("cursor-off"); /* Might GPF */
@ -1268,5 +1314,6 @@ int main(int argc, char ** argv) {
}
close(input_buffer_semaphore[1]);
return 0;
}

View File

@ -28,6 +28,7 @@
#include <pty.h>
#include <wchar.h>
#include <dlfcn.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
@ -513,13 +514,64 @@ static char * copy_selection(void) {
return selection_text;
}
static volatile int input_buffer_lock = 0;
static int input_buffer_semaphore[2];
static list_t * input_buffer_queue = NULL;
struct input_data {
size_t len;
char data[];
};
void * handle_input_writing(void * unused) {
(void)unused;
while (1) {
/* Read one byte from semaphore; as long as semaphore has data,
* there is another input blob to write to the TTY */
char tmp[1];
int c = read(input_buffer_semaphore[0],tmp,1);
if (c > 0) {
/* Retrieve blob */
spin_lock(&input_buffer_lock);
node_t * blob = list_dequeue(input_buffer_queue);
spin_unlock(&input_buffer_lock);
/* No blobs? This shouldn't happen, but just in case, just continue */
if (!blob) {
continue;
}
/* Write blob data to the tty */
struct input_data * value = blob->value;
write(fd_master, value->data, value->len);
free(blob->value);
free(blob);
} else {
/* The pipe has closed, terminal is exiting */
break;
}
}
return NULL;
}
static void write_input_buffer(char * data, size_t len) {
struct input_data * d = malloc(sizeof(struct input_data) + len);
d->len = len;
memcpy(&d->data, data, len);
spin_lock(&input_buffer_lock);
list_insert(input_buffer_queue, d);
spin_unlock(&input_buffer_lock);
write(input_buffer_semaphore[1], d, 1);
}
/* Stuffs a string into the stdin of the terminal's child process
* Useful for things like the ANSI DSR command. */
static void input_buffer_stuff(char * str) {
size_t s = strlen(str) + 1;
write(fd_master, str, s);
size_t len = strlen(str);
write_input_buffer(str, len);
}
/* Redraw the decorations */
static void render_decors(void) {
/* Don't draw decorations or bother advertising the window if in "fullscreen mode" */
@ -1432,9 +1484,8 @@ term_callbacks_t term_callbacks = {
insert_delete_lines,
};
/* Write data into the PTY */
static void handle_input(char c) {
write(fd_master, &c, 1);
write_input_buffer(&c, 1);
display_flip();
if (scrollback_offset != 0) {
scrollback_offset = 0;
@ -1442,9 +1493,9 @@ static void handle_input(char c) {
}
}
/* Write a string into the PTY */
static void handle_input_s(char * c) {
write(fd_master, c, strlen(c));
size_t len = strlen(c);
write_input_buffer(c, len);
display_flip();
if (scrollback_offset != 0) {
scrollback_offset = 0;
@ -1452,6 +1503,7 @@ static void handle_input_s(char * c) {
}
}
/* Scroll the view up (scrollback) */
static void scroll_up(int amount) {
int i = 0;
@ -1689,6 +1741,7 @@ static void check_for_exit(void) {
/* Write [Process terminated] */
char exit_message[] = "[Process terminated]\n";
write(fd_slave, exit_message, sizeof(exit_message));
close(input_buffer_semaphore[1]);
}
static term_cell_t * copy_terminal(int old_width, int old_height, term_cell_t * term_buffer) {
@ -2344,6 +2397,12 @@ int main(int argc, char ** argv) {
/* Initialize the terminal buffer and ANSI library for the first time. */
reinit();
/* Run thread to handle asynchronous writes to the tty */
pthread_t input_buffer_thread;
pipe(input_buffer_semaphore);
input_buffer_queue = list_create();
pthread_create(&input_buffer_thread, NULL, handle_input_writing, NULL);
/* Make sure we're not passing anything to stdin on the child */
fflush(stdin);
@ -2412,6 +2471,8 @@ int main(int argc, char ** argv) {
}
}
close(input_buffer_semaphore[1]);
/* Windows will close automatically on exit. */
return 0;
}

View File

@ -42,8 +42,9 @@ int set_clipboard_from_file(char * file) {
size_t size = ftell(f);
fseek(f, 0, SEEK_SET);
char * tmp = malloc(size);
char * tmp = malloc(size+1);
fread(tmp, 1, size, f);
tmp[size] = '\0';
yutani_set_clipboard(yctx, tmp);

View File

@ -74,11 +74,6 @@ void tty_output_process_slave(pty_t * pty, uint8_t c) {
}
void tty_output_process(pty_t * pty, uint8_t c) {
/* XXX hack to allow ^C to work when the tty is full */
while (pty->write_out == pty_write_out && ring_buffer_available(pty->out) < 16) {
unsigned char garbage[1];
ring_buffer_read(pty->out, 1, garbage);
}
output_process_slave(pty, c);
}
@ -655,8 +650,6 @@ pty_t * pty_new(struct winsize * size) {
pty->in = ring_buffer_create(TTY_BUFFER_SIZE);
pty->out = ring_buffer_create(TTY_BUFFER_SIZE);
pty->in->discard = 1;
/* Master endpoint - writes go to stdin, reads come from stdout */
pty->master = pty_master_create(pty);