diff --git a/Makefile.in b/Makefile.in index da1e9904..ecc357ee 100644 --- a/Makefile.in +++ b/Makefile.in @@ -42,6 +42,8 @@ gears : gears.o window.o wayland-glib.o cairo-util.o screenshot : screenshot.o wayland-glib.o terminal : terminal.o window.o wayland-glib.o cairo-util.o +terminal : LDLIBS += -lutil + $(clients) : CFLAGS += @CLIENT_CFLAGS@ $(clients) : LDLIBS += @CLIENT_LIBS@ -L. -lwayland -lrt diff --git a/terminal.c b/terminal.c index 0ecb304f..acd4f2bd 100644 --- a/terminal.c +++ b/terminal.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -48,9 +49,10 @@ struct terminal { struct wl_display *display; int resize_scheduled; char *data; - int width, height; + int width, height, tail, row, column; int fd; struct buffer *buffer; + GIOChannel *channel; }; static void @@ -60,7 +62,7 @@ terminal_draw_contents(struct terminal *terminal) cairo_surface_t *surface; cairo_t *cr; cairo_font_extents_t extents; - int i; + int i, line; window_get_child_rectangle(terminal->window, &rectangle); @@ -76,11 +78,13 @@ terminal_draw_contents(struct terminal *terminal) cairo_select_font_face (cr, "mono", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 14); cairo_font_extents(cr, &extents); for (i = 0; i < terminal->height; i++) { + line = (terminal->tail + i) % terminal->height; cairo_move_to(cr, 0, extents.ascent + extents.height * i); - cairo_show_text(cr, &terminal->data[i * (terminal->width + 1)]); + cairo_show_text(cr, &terminal->data[line * (terminal->width + 1)]); } cairo_destroy(cr); @@ -132,16 +136,47 @@ acknowledge_handler(struct window *window, uint32_t key, void *data) terminal->resize_scheduled = 0; } +static void +terminal_data(struct terminal *terminal, const char *data, size_t length) +{ + int i; + char *row; + + for (i = 0; i < length; i++) { + row = &terminal->data[terminal->row * (terminal->width + 1)]; + switch (data[i]) { + case '\r': + terminal->column = 0; + break; + case '\n': + terminal->row++; + terminal->column = 0; + if (terminal->row == terminal->height) + terminal->row = 0; + break; + case '\t': + memset(&row[terminal->column], ' ', -terminal->column & 7); + terminal->column = (terminal->column + 7) & ~7; + break; + default: + if (terminal->column < terminal->width) + row[terminal->column++] = data[i]; + break; + } + } +} + static struct terminal * terminal_create(struct wl_display *display, int fd) { struct terminal *terminal; - int size, i; + int size; terminal = malloc(sizeof *terminal); if (terminal == NULL) return terminal; + memset(terminal, 0, sizeof *terminal); terminal->fd = fd; terminal->window = window_create(display, fd, "Wayland Terminal", 500, 100, 500, 400); @@ -153,17 +188,60 @@ terminal_create(struct wl_display *display, int fd) terminal->data = malloc(size); memset(terminal->data, 0, size); - for (i = 0; i < terminal->height; i++) { - snprintf(&terminal->data[i * (terminal->width + 1)], terminal->width, - "hello world, line %d", i); - } - window_set_resize_handler(terminal->window, resize_handler, terminal); window_set_acknowledge_handler(terminal->window, acknowledge_handler, terminal); return terminal; } +static gboolean +io_handler(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + struct terminal *terminal = data; + gchar buffer[256]; + gsize bytes_read; + GError *error = NULL; + + g_io_channel_read_chars(source, buffer, sizeof buffer, + &bytes_read, &error); + printf("got data: %.*s\n", bytes_read, buffer); + + terminal_data(terminal, buffer, bytes_read); + + if (!terminal->resize_scheduled) { + g_idle_add(idle_redraw, terminal); + terminal->resize_scheduled = 1; + } + + return TRUE; +} + +static int +terminal_run(struct terminal *terminal, const char *path) +{ + int master, slave; + pid_t pid; + + pid = forkpty(&master, NULL, NULL, NULL); + if (pid == 0) { + close(master); + if (execl(path, path, NULL)) { + printf("exec failed: %m\n"); + exit(EXIT_FAILURE); + } + } + + close(slave); + terminal->channel = g_io_channel_unix_new(master); + fcntl(master, F_SETFL, O_NONBLOCK); + g_io_add_watch(terminal->channel, G_IO_IN, + io_handler, terminal); + + return 0; +} + int main(int argc, char *argv[]) { struct wl_display *display; @@ -189,6 +267,7 @@ int main(int argc, char *argv[]) g_source_attach(source, NULL); terminal = terminal_create(display, fd); + terminal_run(terminal, "/bin/bash"); terminal_draw(terminal); g_main_loop_run(loop);