From f60356d429ca9931e940387d1aef8df34bee7c54 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Fri, 30 Dec 2011 18:01:00 +0100 Subject: [PATCH] terminal: add dummy terminal implementation vte: This adds a dummy VTE subsystem. It will be used to emulate a vt100 terminal based on our console subsystem. terminal: This ties together several subsystems including the output-, the console- and the vte-subsystem. The test_terminal test application can be used to test the terminal. It is supposed to provide a full vt100 implementation which is displayed on all available screens. Most of the functionality here is not implemented yet and only a dummy function. These will be added in later commits. Signed-off-by: David Herrmann --- Makefile.am | 9 +- src/terminal.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/terminal.h | 57 +++++++++++ src/vte.c | 121 ++++++++++++++++++++++ src/vte.h | 48 +++++++++ tests/test_terminal.c | 197 ++++++++++++++++++++++++++++++++++++ 6 files changed, 703 insertions(+), 2 deletions(-) create mode 100644 src/terminal.c create mode 100644 src/terminal.h create mode 100644 src/vte.c create mode 100644 src/vte.h create mode 100644 tests/test_terminal.c diff --git a/Makefile.am b/Makefile.am index 86b4a2a..84ea14e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ ACLOCAL_AMFLAGS = -I m4 bin_PROGRAMS = kmscon -check_PROGRAMS = test_console test_output test_vt test_buffer +check_PROGRAMS = test_console test_output test_vt test_buffer test_terminal noinst_LTLIBRARIES = libkmscon-core.la AM_CFLAGS = \ @@ -24,7 +24,9 @@ libkmscon_core_la_SOURCES = \ src/console_cell.c \ src/log.c src/log.h \ src/eloop.c src/eloop.h \ - src/vt.c src/vt.h + src/vt.c src/vt.h \ + src/vte.c src/vte.h \ + src/terminal.c src/terminal.h libkmscon_core_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ @@ -68,4 +70,7 @@ test_vt_LDADD = libkmscon-core.la test_buffer_SOURCES = tests/test_buffer.c test_buffer_LDADD = libkmscon-core.la +test_terminal_SOURCES = tests/test_terminal.c +test_terminal_LDADD = libkmscon-core.la + dist_doc_DATA = README TODO diff --git a/src/terminal.c b/src/terminal.c new file mode 100644 index 0000000..4164aea --- /dev/null +++ b/src/terminal.c @@ -0,0 +1,273 @@ +/* + * kmscon - Terminal + * + * Copyright (c) 2011 David Herrmann + * Copyright (c) 2011 University of Tuebingen + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Terminal + * A terminal gets assigned an input stream and several output objects and then + * runs a fully functional terminal emulation on it. + */ + +#include +#include +#include +#include +#include + +#include "console.h" +#include "eloop.h" +#include "log.h" +#include "terminal.h" +#include "vte.h" + +struct term_out { + struct term_out *next; + struct kmscon_output *output; +}; + +struct kmscon_terminal { + unsigned long ref; + struct kmscon_eloop *eloop; + + struct term_out *outputs; + unsigned int max_height; + + struct kmscon_console *console; + struct kmscon_idle *redraw; + struct kmscon_vte *vte; +}; + +static void draw_all(struct kmscon_idle *idle, void *data) +{ + struct kmscon_terminal *term = data; + struct term_out *iter; + struct kmscon_output *output; + int ret; + + kmscon_eloop_rm_idle(idle); + kmscon_console_draw(term->console); + + iter = term->outputs; + for (; iter; iter = iter->next) { + output = iter->output; + if (!kmscon_output_is_awake(output)) + continue; + + ret = kmscon_output_use(output); + if (ret) + continue; + + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + kmscon_console_map(term->console); + kmscon_output_swap(output); + } +} + +static void schedule_redraw(struct kmscon_terminal *term) +{ + int ret; + + if (!term || !term->eloop) + return; + + ret = kmscon_eloop_add_idle(term->eloop, term->redraw, draw_all, term); + if (ret && ret != -EALREADY) + log_warning("terminal: cannot schedule redraw\n"); +} + +static const char help_text[] = +"terminal subsystem - KMS based console test\n" +"This is some default text to test the drawing operations.\n\n"; + +static void print_help(struct kmscon_terminal *term) +{ + int ret; + unsigned int i, len; + struct kmscon_char *ch; + + ret = kmscon_char_new_u8(&ch, NULL, 0); + if (ret) + return; + + len = sizeof(help_text) - 1; + for (i = 0; i < len; ++i) { + kmscon_char_set_u8(ch, &help_text[i], 1); + kmscon_terminal_input(term, ch); + } + + kmscon_char_free(ch); +} + +int kmscon_terminal_new(struct kmscon_terminal **out) +{ + struct kmscon_terminal *term; + int ret; + + if (!out) + return -EINVAL; + + log_debug("terminal: new terminal object\n"); + + term = malloc(sizeof(*term)); + if (!term) + return -ENOMEM; + + memset(term, 0, sizeof(*term)); + term->ref = 1; + + ret = kmscon_idle_new(&term->redraw); + if (ret) + goto err_free; + + ret = kmscon_console_new(&term->console); + if (ret) + goto err_idle; + + ret = kmscon_vte_new(&term->vte); + if (ret) + goto err_con; + kmscon_vte_bind(term->vte, term->console); + print_help(term); + + *out = term; + return 0; + +err_con: + kmscon_console_unref(term->console); +err_idle: + kmscon_idle_unref(term->redraw); +err_free: + free(term); + return ret; +} + +void kmscon_terminal_ref(struct kmscon_terminal *term) +{ + if (!term) + return; + + term->ref++; +} + +void kmscon_terminal_unref(struct kmscon_terminal *term) +{ + if (!term || !term->ref) + return; + + if (--term->ref) + return; + + kmscon_terminal_rm_all_outputs(term); + kmscon_vte_unref(term->vte); + kmscon_console_unref(term->console); + kmscon_terminal_disconnect_eloop(term); + free(term); + log_debug("terminal: destroying terminal object\n"); +} + +int kmscon_terminal_connect_eloop(struct kmscon_terminal *term, + struct kmscon_eloop *eloop) +{ + if (!term || !eloop) + return -EINVAL; + + if (term->eloop) + return -EALREADY; + + kmscon_eloop_ref(eloop); + term->eloop = eloop; + + return 0; +} + +void kmscon_terminal_disconnect_eloop(struct kmscon_terminal *term) +{ + if (!term) + return; + + kmscon_eloop_unref(term->eloop); + term->eloop = NULL; +} + +int kmscon_terminal_add_output(struct kmscon_terminal *term, + struct kmscon_output *output) +{ + struct term_out *out; + unsigned int height; + struct kmscon_mode *mode; + + if (!term || !output) + return -EINVAL; + + mode = kmscon_output_get_current(output); + if (!mode) { + log_warning("terminal: invalid output added to terminal\n"); + return -EINVAL; + } + + out = malloc(sizeof(*out)); + if (!out) + return -ENOMEM; + + memset(out, 0, sizeof(*out)); + kmscon_output_ref(output); + out->output = output; + out->next = term->outputs; + term->outputs = out; + + height = kmscon_mode_get_height(mode); + if (term->max_height < height) { + term->max_height = height; + kmscon_console_resize(term->console, 0, 0, term->max_height); + } + + schedule_redraw(term); + + return 0; +} + +void kmscon_terminal_rm_all_outputs(struct kmscon_terminal *term) +{ + struct term_out *tmp; + + if (!term) + return; + + while (term->outputs) { + tmp = term->outputs; + term->outputs = tmp->next; + kmscon_output_unref(tmp->output); + free(tmp); + } +} + +void kmscon_terminal_input(struct kmscon_terminal *term, + const struct kmscon_char *ch) +{ + kmscon_vte_input(term->vte, ch); + schedule_redraw(term); +} diff --git a/src/terminal.h b/src/terminal.h new file mode 100644 index 0000000..2844253 --- /dev/null +++ b/src/terminal.h @@ -0,0 +1,57 @@ +/* + * kmscon - Terminal + * + * Copyright (c) 2011 David Herrmann + * Copyright (c) 2011 University of Tuebingen + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Terminal + * This provides the basic terminal object. This ties together the vt emulation + * and the output console. + */ + +#ifndef KMSCON_TERMINAL_H +#define KMSCON_TERMINAL_H + +#include +#include "console.h" +#include "output.h" + +struct kmscon_terminal; + +int kmscon_terminal_new(struct kmscon_terminal **out); +void kmscon_terminal_ref(struct kmscon_terminal *term); +void kmscon_terminal_unref(struct kmscon_terminal *term); + +int kmscon_terminal_connect_eloop(struct kmscon_terminal *term, + struct kmscon_eloop *eloop); +void kmscon_terminal_disconnect_eloop(struct kmscon_terminal *term); + +int kmscon_terminal_add_output(struct kmscon_terminal *term, + struct kmscon_output *output); +void kmscon_terminal_rm_all_outputs(struct kmscon_terminal *term); + +void kmscon_terminal_input(struct kmscon_terminal *term, + const struct kmscon_char *ch); + +#endif /* KMSCON_TERMINAL_H */ diff --git a/src/vte.c b/src/vte.c new file mode 100644 index 0000000..d569bdf --- /dev/null +++ b/src/vte.c @@ -0,0 +1,121 @@ +/* + * kmscon - VT Emulator + * + * Copyright (c) 2011 David Herrmann + * Copyright (c) 2011 University of Tuebingen + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Virtual Terminal Emulator + * This is a vt100 implementation. It is written from scratch. It uses the + * console subsystem as output and is tightly bound to it. + */ + +#include +#include +#include + +#include "console.h" +#include "log.h" +#include "vte.h" + +struct kmscon_vte { + unsigned long ref; + struct kmscon_console *con; +}; + +int kmscon_vte_new(struct kmscon_vte **out) +{ + struct kmscon_vte *vte; + + if (!out) + return -EINVAL; + + log_debug("vte: new vte object\n"); + + vte = malloc(sizeof(*vte)); + if (!vte) + return -ENOMEM; + + memset(vte, 0, sizeof(*vte)); + vte->ref = 1; + + *out = vte; + return 0; +} + +void kmscon_vte_ref(struct kmscon_vte *vte) +{ + if (!vte) + return; + + vte->ref++; +} + +void kmscon_vte_unref(struct kmscon_vte *vte) +{ + if (!vte || !vte->ref) + return; + + if (--vte->ref) + return; + + kmscon_console_unref(vte->con); + free(vte); + log_debug("vte: destroying vte object\n"); +} + +void kmscon_vte_bind(struct kmscon_vte *vte, struct kmscon_console *con) +{ + if (!vte) + return; + + kmscon_console_unref(vte->con); + vte->con = con; + kmscon_console_ref(vte->con); +} + +void kmscon_vte_input(struct kmscon_vte *vte, const struct kmscon_char *ch) +{ + size_t len; + const char *val; + + if (!vte || !vte->con) + return; + + len = kmscon_char_get_len(ch); + val = kmscon_char_get_u8(ch); + + if (len == 1) { + if (*val == '\n') + kmscon_console_newline(vte->con); + else + goto write_default; + } else { + goto write_default; + } + + return; + +write_default: + kmscon_console_write(vte->con, ch); +} diff --git a/src/vte.h b/src/vte.h new file mode 100644 index 0000000..a573bf7 --- /dev/null +++ b/src/vte.h @@ -0,0 +1,48 @@ +/* + * kmscon - VT Emulator + * + * Copyright (c) 2011 David Herrmann + * Copyright (c) 2011 University of Tuebingen + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Virtual Terminal Emulator + * This is a vt100 implementation. It is written from scratch. It uses the + * console subsystem as output and is tightly bound to it. + */ + +#ifndef KMSCON_VTE_H +#define KMSCON_VTE_H + +#include +#include "console.h" + +struct kmscon_vte; + +int kmscon_vte_new(struct kmscon_vte **out); +void kmscon_vte_ref(struct kmscon_vte *vte); +void kmscon_vte_unref(struct kmscon_vte *vte); + +void kmscon_vte_bind(struct kmscon_vte *vte, struct kmscon_console *con); +void kmscon_vte_input(struct kmscon_vte *vte, const struct kmscon_char *ch); + +#endif /* KMSCON_VTE_H */ diff --git a/tests/test_terminal.c b/tests/test_terminal.c new file mode 100644 index 0000000..239ff08 --- /dev/null +++ b/tests/test_terminal.c @@ -0,0 +1,197 @@ +/* + * test_terminal - Test Terminal + * + * Copyright (c) 2011 David Herrmann + * Copyright (c) 2011 University of Tuebingen + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Test Terminal + * This runs a terminal emulator with default settings on all connected outputs. + * This is supposed to be a fully functional VT. It's only missing + * configurability and extended features. + */ + +#include +#include +#include +#include +#include + +#include "eloop.h" +#include "log.h" +#include "output.h" +#include "terminal.h" +#include "vt.h" + +struct app { + struct kmscon_eloop *eloop; + struct kmscon_signal *sig_term; + struct kmscon_signal *sig_int; + struct kmscon_compositor *comp; + struct kmscon_vt *vt; + struct kmscon_terminal *term; +}; + +static volatile sig_atomic_t terminate; + +static void sig_term(struct kmscon_signal *sig, int signum, void *data) +{ + terminate = 1; +} + +static void activate_outputs(struct app *app) +{ + struct kmscon_output *iter; + int ret; + + iter = kmscon_compositor_get_outputs(app->comp); + + for ( ; iter; iter = kmscon_output_next(iter)) { + if (!kmscon_output_is_active(iter)) { + ret = kmscon_output_activate(iter, NULL); + if (ret) { + log_err("test: cannot activate output: %d\n", + ret); + continue; + } + } + + ret = kmscon_terminal_add_output(app->term, iter); + if (ret) { + log_err("test: cannot assign output: %d\n", ret); + continue; + } + } +} + +static bool vt_switch(struct kmscon_vt *vt, int action, void *data) +{ + struct app *app = data; + int ret; + + if (action == KMSCON_VT_ENTER) { + ret = kmscon_compositor_wake_up(app->comp); + if (ret == 0) + log_info("test: running without active outputs\n"); + else if (ret > 0) + activate_outputs(app); + } else if (action == KMSCON_VT_LEAVE) { + kmscon_terminal_rm_all_outputs(app->term); + kmscon_compositor_sleep(app->comp); + } + + return true; +} + +static void destroy_app(struct app *app) +{ + kmscon_terminal_unref(app->term); + kmscon_vt_unref(app->vt); + kmscon_compositor_unref(app->comp); + kmscon_eloop_rm_signal(app->sig_int); + kmscon_eloop_rm_signal(app->sig_term); + kmscon_eloop_unref(app->eloop); +} + +static int setup_app(struct app *app) +{ + int ret; + + ret = kmscon_eloop_new(&app->eloop); + if (ret) + goto err_loop; + + ret = kmscon_eloop_new_signal(app->eloop, &app->sig_term, SIGTERM, + sig_term, NULL); + if (ret) + goto err_loop; + + ret = kmscon_eloop_new_signal(app->eloop, &app->sig_int, SIGINT, + sig_term, NULL); + if (ret) + goto err_loop; + + ret = kmscon_compositor_new(&app->comp); + if (ret) + goto err_loop; + + ret = kmscon_compositor_use(app->comp); + if (ret) + goto err_loop; + + ret = kmscon_vt_new(&app->vt, vt_switch, app); + if (ret) + goto err_loop; + + ret = kmscon_vt_open(app->vt, KMSCON_VT_NEW, app->eloop); + if (ret) + goto err_loop; + + ret = kmscon_terminal_new(&app->term); + if (ret) + goto err_loop; + + ret = kmscon_terminal_connect_eloop(app->term, app->eloop); + if (ret) + goto err_loop; + + return 0; + +err_loop: + destroy_app(app); + return ret; +} + +int main(int argc, char **argv) +{ + struct app app; + int ret; + + log_info("test: starting\n"); + memset(&app, 0, sizeof(app)); + + ret = setup_app(&app); + if (ret) + goto err_out; + + log_info("test: starting main-loop\n"); + + while (!terminate) { + ret = kmscon_eloop_dispatch(app.eloop, -1); + if (ret) + break; + } + + log_info("test: stopping main-loop\n"); + +err_out: + destroy_app(&app); + + if (ret) { + log_err("test: failed with: %d\n", ret); + return EXIT_FAILURE; + } else { + log_info("test: terminating\n"); + return EXIT_SUCCESS; + } +} -- 2.7.4