From 96b28e953de4363736e3dfccbedbf93e9fe70b6c Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 23 Oct 2013 16:55:59 +0200 Subject: tsm: remove and depend on libtsm TSM was extracted from kmscon sources so it can more easily be used by other emulators. It is available at: http://cgit.freedesktop.org/~dvdhrm/libtsm Signed-off-by: David Herrmann --- .gitignore | 1 - Makefile.am | 60 +- README | 2 +- configure.ac | 59 +- docs/pc/libtsm.pc.in | 11 - docs/sym/libtsm.sym | 137 --- src/font_freetype2.c | 2 +- src/font_pango.c | 2 +- src/kmscon_conf.c | 1 - src/kmscon_conf.h | 2 - src/kmscon_terminal.c | 13 +- src/text.c | 13 +- src/text.h | 7 +- src/tsm_screen.c | 1970 ---------------------------------- src/tsm_screen.h | 196 ---- src/tsm_unicode.c | 624 ----------- src/tsm_unicode.h | 91 -- src/tsm_vte.c | 2784 ------------------------------------------------ src/tsm_vte.h | 85 -- src/tsm_vte_charsets.c | 501 --------- 20 files changed, 32 insertions(+), 6529 deletions(-) delete mode 100644 docs/pc/libtsm.pc.in delete mode 100644 docs/sym/libtsm.sym delete mode 100644 src/tsm_screen.c delete mode 100644 src/tsm_screen.h delete mode 100644 src/tsm_unicode.c delete mode 100644 src/tsm_unicode.h delete mode 100644 src/tsm_vte.c delete mode 100644 src/tsm_vte.h delete mode 100644 src/tsm_vte_charsets.c diff --git a/.gitignore b/.gitignore index 0aa8fc4..912ec5b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ configure *.tar.xz libtool libeloop.pc -libtsm.pc libuvt.pc libuterm.pc m4/ diff --git a/Makefile.am b/Makefile.am index c35b040..4af0d36 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,10 +11,6 @@ LIBELOOP_CURRENT = 1 LIBELOOP_REVISION = 0 LIBELOOP_AGE = 0 -LIBTSM_CURRENT = 1 -LIBTSM_REVISION = 0 -LIBTSM_AGE = 0 - LIBUVT_CURRENT = 1 LIBUVT_REVISION = 0 LIBUVT_AGE = 0 @@ -44,11 +40,9 @@ EXTRA_DIST = \ docs/kmscon.service \ docs/kmsconvt@.service \ docs/pc/libeloop.pc.in \ - docs/pc/libtsm.pc.in \ docs/pc/libuvt.pc.in \ docs/pc/libuterm.pc.in \ docs/sym/libeloop.sym \ - docs/sym/libtsm.sym \ docs/sym/libuvt.sym \ docs/sym/libuterm.sym CLEANFILES = @@ -242,45 +236,6 @@ libeloop_la_CPPFLAGS += $(DBUS_CFLAGS) libeloop_la_LIBADD += $(DBUS_LIBS) endif -# -# libtsm -# The Terminal-emulator State Machine is a library that implements the whole VTE -# layer and everything related to it. It has no external dependencies so it can -# be used to implement any kind of terminal emulator or debugger. -# - -if BUILD_ENABLE_TSM -lib_LTLIBRARIES += libtsm.la -include_HEADERS += \ - src/tsm_screen.h \ - src/tsm_unicode.h \ - src/tsm_vte.h -pkgconfig_DATA += docs/pc/libtsm.pc -endif - -libtsm_la_SOURCES = \ - src/tsm_screen.h \ - src/tsm_screen.c \ - src/tsm_unicode.h \ - src/tsm_unicode.c \ - src/tsm_vte.h \ - src/tsm_vte.c \ - src/tsm_vte_charsets.c \ - external/wcwidth.h \ - external/wcwidth.c - -libtsm_la_CPPFLAGS = \ - $(AM_CPPFLAGS) \ - $(XKBCOMMON_CFLAGS) -libtsm_la_LIBADD = \ - $(XKBCOMMON_LIBS) \ - libshl.la -EXTRA_libtsm_la_DEPENDENCIES = ${top_srcdir}/docs/sym/libtsm.sym -libtsm_la_LDFLAGS = \ - $(AM_LDFLAGS) \ - -version-info $(LIBTSM_CURRENT):$(LIBTSM_REVISION):$(LIBTSM_AGE) \ - -Wl,--version-script="$(top_srcdir)/docs/sym/libtsm.sym" - # # libuvt # Implementation of Virtual Terminals in user-space with the help of CUSE/FUSE @@ -506,11 +461,12 @@ mod_freetype2_la_SOURCES = \ src/kmscon_mod_freetype2.c mod_freetype2_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(FREETYPE2_CFLAGS) + $(FREETYPE2_CFLAGS) \ + $(TSM_CFLAGS) mod_freetype2_la_LIBADD = \ $(FREETYPE2_LIBS) \ + $(TSM_LIBS) \ -lpthread \ - libtsm.la \ libshl.la mod_freetype2_la_LDFLAGS = \ $(AM_LDFLAGS) \ @@ -527,11 +483,12 @@ mod_pango_la_SOURCES = \ src/kmscon_mod_pango.c mod_pango_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(PANGO_CFLAGS) + $(PANGO_CFLAGS) \ + $(TSM_CFLAGS) mod_pango_la_LIBADD = \ $(PANGO_LIBS) \ + $(TSM_LIBS) \ -lpthread \ - libtsm.la \ libshl.la mod_pango_la_LDFLAGS = \ $(AM_LDFLAGS) \ @@ -657,9 +614,11 @@ nodist_kmscon_SOURCES = kmscon_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(XKBCOMMON_CFLAGS) + $(XKBCOMMON_CFLAGS) \ + $(TSM_CFLAGS) kmscon_LDADD = \ $(XKBCOMMON_LIBS) \ + $(TSM_LIBS) \ libeloop.la \ libuterm.la \ libshl.la \ @@ -675,7 +634,6 @@ endif if BUILD_ENABLE_SESSION_TERMINAL kmscon_SOURCES += src/kmscon_terminal.c -kmscon_LDADD += libtsm.la endif # diff --git a/README b/README index 7aad582..a3cdb09 100644 --- a/README +++ b/README @@ -10,6 +10,7 @@ Website: == Requirements == Kmscon requires the following software: + - libtsm: terminal emulator state machine - libudev: providing input, video, etc. device hotplug support (>=v172) - libxkbcommon: providing internationalized keyboard handling @@ -93,7 +94,6 @@ Released tarballs can be found at: on the command line: --enable-kmscon: Build kmscon application [default: on] --enable-eloop: Build eloop event loop library [default: off] - --enable-tsm: Build TSM terminal state-machine library [default: off] --enable-uterm: Build uterm library [default: off] --enable-uvt: Build UVT library [default: off] diff --git a/configure.ac b/configure.ac index 41afd53..dac4bcd 100644 --- a/configure.ac +++ b/configure.ac @@ -120,6 +120,11 @@ PKG_CHECK_MODULES([PIXMAN], [pixman-1], AC_SUBST(PIXMAN_CFLAGS) AC_SUBST(PIXMAN_LIBS) +PKG_CHECK_MODULES([TSM], [libtsm], + [have_tsm=yes], [have_tsm=no]) +AC_SUBST(TSM_CFLAGS) +AC_SUBST(TSM_LIBS) + # # Parse arguments # This parses all arguments that are given via "--enable-XY" or "--with-XY" and @@ -149,18 +154,6 @@ elif test "x$enable_eloop" = "x" ; then fi AC_MSG_RESULT([$enable_eloop]) -# TSM -AC_MSG_CHECKING([whether user wants TSM]) -AC_ARG_ENABLE([tsm], - [AS_HELP_STRING([--enable-tsm], - [build tsm library])]) -if test "x$enable_all" = "xyes" ; then - enable_tsm="yes" -elif test "x$enable_tsm" = "x" ; then - enable_tsm="no (default)" -fi -AC_MSG_RESULT([$enable_tsm]) - # UVT AC_MSG_CHECKING([whether user wants UVT]) AC_ARG_ENABLE([uvt], @@ -465,25 +458,6 @@ else eloop_missing="enable-eloop" fi -# TSM -tsm_avail=no -tsm_missing="" -if test ! "x$enable_tsm" = "xno" ; then - tsm_avail=yes - if test "x$have_xkbcommon" = "xno" ; then - tsm_avail=no - tsm_missing="libxkbcommon" - fi - - if test "x$tsm_avail" = "xno" ; then - if test "x$enable_tsm" = "xyes" ; then - AC_ERROR([missing for TSM: $tsm_missing]) - fi - fi -else - tsm_missing="enable-tsm" -fi - # UVT uvt_avail=no uvt_missing="" @@ -737,9 +711,9 @@ session_terminal_avail=no session_terminal_missing="" if test ! "x$enable_session_terminal" = "xno" ; then session_terminal_avail=yes - if test "x$tsm_avail" = "xno" ; then + if test "x$have_tsm" = "xno" ; then session_terminal_avail=no - session_terminal_missing="$tsm_missing" + session_terminal_missing="libtsm" fi if test "x$session_terminal_avail" = "xno" ; then @@ -761,9 +735,9 @@ if test ! "x$enable_kmscon" = "xno" ; then kmscon_missing="$eloop_missing,$kmscon_missing" fi - if test "x$tsm_avail" = "xno" ; then + if test "x$have_tsm" = "xno" ; then kmscon_avail=no - kmscon_missing="$tsm_missing,$kmscon_missing" + kmscon_missing="libtsm,$kmscon_missing" fi if test "x$uterm_avail" = "xno" ; then @@ -803,7 +777,6 @@ session_terminal_enabled=no if test "x$session_terminal_avail" = "xyes" ; then if test "x${enable_session_terminal% *}" = "xyes" ; then session_terminal_enabled=yes - enable_tsm=yes fi fi @@ -927,14 +900,6 @@ if test "x$uvt_avail" = "xyes" ; then fi fi -# tsm -tsm_enabled=no -if test "x$tsm_avail" = "xyes" ; then - if test "x${enable_tsm% *}" = "xyes" ; then - tsm_enabled=yes - fi -fi - # eloop eloop_enabled=no if test "x$eloop_avail" = "xyes" ; then @@ -997,10 +962,6 @@ AM_CONDITIONAL([BUILD_ENABLE_ELOOP_DBUS], AM_CONDITIONAL([BUILD_ENABLE_ELOOP], [test "x$eloop_enabled" = "xyes"]) -# TSM -AM_CONDITIONAL([BUILD_ENABLE_TSM], - [test "x$tsm_enabled" = "xyes"]) - # UVT AM_CONDITIONAL([BUILD_ENABLE_UVT], [test "x$uvt_enabled" = "xyes"]) @@ -1202,7 +1163,6 @@ fi AC_CONFIG_FILES([Makefile docs/pc/libeloop.pc - docs/pc/libtsm.pc docs/pc/libuvt.pc docs/pc/libuterm.pc]) AC_OUTPUT @@ -1223,7 +1183,6 @@ AC_MSG_NOTICE([Build configuration: Applications and Libraries: kmscon: $kmscon_enabled ($kmscon_avail: $kmscon_missing) uterm: $uterm_enabled ($uterm_avail: $uterm_missing) - tsm: $tsm_enabled ($tsm_avail: $tsm_missing) uvt: $uvt_enabled ($uvt_avail: $uvt_missing) eloop: $eloop_enabled ($eloop_avail: $eloop_missing) diff --git a/docs/pc/libtsm.pc.in b/docs/pc/libtsm.pc.in deleted file mode 100644 index c113e64..0000000 --- a/docs/pc/libtsm.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: tsm -Description: Terminal-emulator State Machine -URL: @PACKAGE_URL@ -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -ltsm -Cflags: -I${includedir} diff --git a/docs/sym/libtsm.sym b/docs/sym/libtsm.sym deleted file mode 100644 index df605d4..0000000 --- a/docs/sym/libtsm.sym +++ /dev/null @@ -1,137 +0,0 @@ -/*** - * libtsm - Terminal-Emulator State Machine - * - * Copyright (c) 2012-2013 David Herrmann - * - * 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. - ***/ - -LIBTSM_1 { -global: - tsm_symbol_default; -local: - *; -}; - -LIBTSM_2 { -global: - tsm_symbol_table_new; - tsm_symbol_table_ref; - tsm_symbol_table_unref; - - tsm_symbol_make; - tsm_symbol_append; - tsm_symbol_get; - tsm_symbol_get_width; - - tsm_ucs4_get_width; - tsm_ucs4_to_utf8; - tsm_ucs4_to_utf8_alloc; - - tsm_utf8_mach_new; - tsm_utf8_mach_free; - - tsm_utf8_mach_feed; - tsm_utf8_mach_get; - tsm_utf8_mach_reset; - - tsm_screen_new; - tsm_screen_ref; - tsm_screen_unref; - - tsm_screen_set_opts; - tsm_screen_reset_opts; - tsm_screen_get_opts; - - tsm_screen_get_width; - tsm_screen_get_height; - tsm_screen_resize; - tsm_screen_set_margins; - tsm_screen_set_max_sb; - tsm_screen_clear_sb; - - tsm_screen_sb_up; - tsm_screen_sb_down; - tsm_screen_sb_page_up; - tsm_screen_sb_page_down; - tsm_screen_sb_reset; - - tsm_screen_set_def_attr; - tsm_screen_reset; - tsm_screen_set_flags; - tsm_screen_reset_flags; - tsm_screen_get_flags; - - tsm_screen_get_cursor_x; - tsm_screen_get_cursor_y; - - tsm_screen_set_tabstop; - tsm_screen_reset_tabstop; - tsm_screen_reset_all_tabstops; - - tsm_screen_write; - tsm_screen_newline; - tsm_screen_scroll_up; - tsm_screen_scroll_down; - tsm_screen_move_to; - tsm_screen_move_up; - tsm_screen_move_down; - tsm_screen_move_left; - tsm_screen_move_right; - tsm_screen_move_line_end; - tsm_screen_move_line_home; - tsm_screen_tab_right; - tsm_screen_tab_left; - tsm_screen_insert_lines; - tsm_screen_delete_lines; - tsm_screen_insert_chars; - tsm_screen_delete_chars; - tsm_screen_erase_cursor; - tsm_screen_erase_chars; - tsm_screen_erase_cursor_to_end; - tsm_screen_erase_home_to_cursor; - tsm_screen_erase_current_line; - tsm_screen_erase_screen_to_cursor; - tsm_screen_erase_cursor_to_screen; - tsm_screen_erase_screen; - - tsm_screen_selection_reset; - tsm_screen_selection_start; - tsm_screen_selection_target; - tsm_screen_selection_copy; - - tsm_screen_draw; - - tsm_vte_unicode_lower; - - tsm_vte_new; - tsm_vte_ref; - tsm_vte_unref; - - tsm_vte_set_palette; - - tsm_vte_reset; - tsm_vte_hard_reset; - tsm_vte_input; - tsm_vte_handle_keyboard; - tsm_vte_unicode_upper; - tsm_vte_dec_supplemental_graphics; - tsm_vte_dec_special_graphics; -} LIBTSM_1; diff --git a/src/font_freetype2.c b/src/font_freetype2.c index 37800dd..a9747f6 100644 --- a/src/font_freetype2.c +++ b/src/font_freetype2.c @@ -40,6 +40,7 @@ #include #include #include FT_FREETYPE_H +#include #include #include #include @@ -48,7 +49,6 @@ #include "shl_dlist.h" #include "shl_hashtable.h" #include "shl_log.h" -#include "tsm_unicode.h" #include "uterm_video.h" #define LOG_SUBSYSTEM "font_freetype2" diff --git a/src/font_pango.c b/src/font_pango.c index 0964123..171f3bd 100644 --- a/src/font_pango.c +++ b/src/font_pango.c @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -55,7 +56,6 @@ #include "shl_dlist.h" #include "shl_hashtable.h" #include "shl_log.h" -#include "tsm_unicode.h" #include "uterm_video.h" #define LOG_SUBSYSTEM "font_pango" diff --git a/src/kmscon_conf.c b/src/kmscon_conf.c index 2173b44..398dd16 100644 --- a/src/kmscon_conf.c +++ b/src/kmscon_conf.c @@ -597,7 +597,6 @@ int kmscon_conf_new(struct conf_ctx **out) CONF_OPTION_BOOL(0, "hwaccel", &conf->hwaccel, false), CONF_OPTION(0, 0, "gpus", &conf_gpus, NULL, NULL, NULL, &conf->gpus, KMSCON_GPU_ALL), CONF_OPTION_STRING(0, "render-engine", &conf->render_engine, NULL), - CONF_OPTION_BOOL(0, "render-timing", &conf->render_timing, false), /* Font Options */ CONF_OPTION_STRING(0, "font-engine", &conf->font_engine, "pango"), diff --git a/src/kmscon_conf.h b/src/kmscon_conf.h index afca331..d253f4a 100644 --- a/src/kmscon_conf.h +++ b/src/kmscon_conf.h @@ -142,8 +142,6 @@ struct kmscon_conf_t { unsigned int gpus; /* render engine */ char *render_engine; - /* print render-engine timing information */ - bool render_timing; /* Font Options */ /* font engine */ diff --git a/src/kmscon_terminal.c b/src/kmscon_terminal.c index 3a1f8f0..cc8f003 100644 --- a/src/kmscon_terminal.c +++ b/src/kmscon_terminal.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include "conf.h" @@ -43,8 +44,6 @@ #include "shl_dlist.h" #include "shl_log.h" #include "text.h" -#include "tsm_screen.h" -#include "tsm_vte.h" #include "uterm_input.h" #include "uterm_video.h" @@ -121,8 +120,11 @@ static void do_redraw_screen(struct screen *scr) scr->pending = false; do_clear_margins(scr); - tsm_screen_draw(scr->term->console, kmscon_text_prepare_cb, - kmscon_text_draw_cb, kmscon_text_render_cb, scr->txt); + + kmscon_text_prepare(scr->txt); + tsm_screen_draw(scr->term->console, kmscon_text_draw_cb, scr->txt); + kmscon_text_render(scr->txt); + ret = uterm_display_swap(scr->disp, false); if (ret) { log_warning("cannot swap display %p", scr->disp); @@ -613,9 +615,6 @@ int kmscon_terminal_register(struct kmscon_session **out, if (ret) goto err_free; tsm_screen_set_max_sb(term->console, term->conf->sb_size); - if (term->conf->render_timing) - tsm_screen_set_opts(term->console, - TSM_SCREEN_OPT_RENDER_TIMING); ret = tsm_vte_new(&term->vte, term->console, write_event, term, log_llog, NULL); diff --git a/src/text.c b/src/text.c index 5f3c07a..d7e5f0e 100644 --- a/src/text.c +++ b/src/text.c @@ -437,21 +437,12 @@ void kmscon_text_abort(struct kmscon_text *txt) txt->rendering = false; } -int kmscon_text_prepare_cb(struct tsm_screen *con, void *data) -{ - return kmscon_text_prepare(data); -} - int kmscon_text_draw_cb(struct tsm_screen *con, uint32_t id, const uint32_t *ch, size_t len, unsigned int width, unsigned int posx, unsigned int posy, - const struct tsm_screen_attr *attr, void *data) + const struct tsm_screen_attr *attr, + tsm_age_t age, void *data) { return kmscon_text_draw(data, id, ch, len, width, posx, posy, attr); } - -int kmscon_text_render_cb(struct tsm_screen *con, void *data) -{ - return kmscon_text_render(data); -} diff --git a/src/text.h b/src/text.h index 71f830e..bc02c5b 100644 --- a/src/text.h +++ b/src/text.h @@ -34,10 +34,10 @@ #define KMSCON_TEXT_H #include +#include #include #include "font.h" #include "kmscon_module.h" -#include "tsm_screen.h" #include "uterm_video.h" /* text renderer */ @@ -100,13 +100,12 @@ int kmscon_text_draw(struct kmscon_text *txt, int kmscon_text_render(struct kmscon_text *txt); void kmscon_text_abort(struct kmscon_text *txt); -int kmscon_text_prepare_cb(struct tsm_screen *con, void *data); int kmscon_text_draw_cb(struct tsm_screen *con, uint32_t id, const uint32_t *ch, size_t len, unsigned int width, unsigned int posx, unsigned int posy, - const struct tsm_screen_attr *attr, void *data); -int kmscon_text_render_cb(struct tsm_screen *con, void *data); + const struct tsm_screen_attr *attr, + tsm_age_t age, void *data); /* modularized backends */ diff --git a/src/tsm_screen.c b/src/tsm_screen.c deleted file mode 100644 index ebc8982..0000000 --- a/src/tsm_screen.c +++ /dev/null @@ -1,1970 +0,0 @@ -/* - * TSM - Screen Management - * - * Copyright (c) 2011-2012 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. - */ - -/* - * Screen Management - * This provides the screen drawing and manipulation functions. It does not - * provide the terminal emulation. It is just an abstraction layer to draw text - * to a framebuffer as used by terminals and consoles. - */ - -#include -#include -#include -#include -#include -#include "shl_llog.h" -#include "shl_misc.h" -#include "shl_timer.h" -#include "tsm_screen.h" -#include "tsm_unicode.h" - -#define LLOG_SUBSYSTEM "tsm_screen" - -struct cell { - tsm_symbol_t ch; - unsigned int width; - struct tsm_screen_attr attr; -}; - -struct line { - struct line *next; - struct line *prev; - - unsigned int size; - struct cell *cells; - uint64_t sb_id; -}; - -#define SELECTION_TOP -1 -struct selection_pos { - struct line *line; - unsigned int x; - int y; -}; - -struct tsm_screen { - size_t ref; - llog_submit_t llog; - void *llog_data; - unsigned int opts; - unsigned int flags; - struct shl_timer *timer; - - /* default attributes for new cells */ - struct tsm_screen_attr def_attr; - - /* current buffer */ - unsigned int size_x; - unsigned int size_y; - unsigned int margin_top; - unsigned int margin_bottom; - unsigned int line_num; - struct line **lines; - struct line **main_lines; - struct line **alt_lines; - - /* scroll-back buffer */ - unsigned int sb_count; /* number of lines in sb */ - struct line *sb_first; /* first line; was moved first */ - struct line *sb_last; /* last line; was moved last*/ - unsigned int sb_max; /* max-limit of lines in sb */ - struct line *sb_pos; /* current position in sb or NULL */ - uint64_t sb_last_id; /* last id given to sb-line */ - - /* cursor */ - unsigned int cursor_x; - unsigned int cursor_y; - - /* tab ruler */ - bool *tab_ruler; - - /* selection */ - bool sel_active; - struct selection_pos sel_start; - struct selection_pos sel_end; -}; - -static void cell_init(struct tsm_screen *con, struct cell *cell) -{ - cell->ch = 0; - cell->width = 1; - memcpy(&cell->attr, &con->def_attr, sizeof(cell->attr)); -} - -static int line_new(struct tsm_screen *con, struct line **out, - unsigned int width) -{ - struct line *line; - unsigned int i; - - if (!width) - return -EINVAL; - - line = malloc(sizeof(*line)); - if (!line) - return -ENOMEM; - line->next = NULL; - line->prev = NULL; - line->size = width; - - line->cells = malloc(sizeof(struct cell) * width); - if (!line->cells) { - free(line); - return -ENOMEM; - } - - for (i = 0; i < width; ++i) - cell_init(con, &line->cells[i]); - - *out = line; - return 0; -} - -static void line_free(struct line *line) -{ - free(line->cells); - free(line); -} - -static int line_resize(struct tsm_screen *con, struct line *line, - unsigned int width) -{ - struct cell *tmp; - - if (!line || !width) - return -EINVAL; - - if (line->size < width) { - tmp = realloc(line->cells, width * sizeof(struct cell)); - if (!tmp) - return -ENOMEM; - - line->cells = tmp; - - while (line->size < width) { - cell_init(con, &line->cells[line->size]); - ++line->size; - } - } - - return 0; -} - -/* This links the given line into the scrollback-buffer */ -static void link_to_scrollback(struct tsm_screen *con, struct line *line) -{ - struct line *tmp; - - if (con->sb_max == 0) { - if (con->sel_active) { - if (con->sel_start.line == line) { - con->sel_start.line = NULL; - con->sel_start.y = SELECTION_TOP; - } - if (con->sel_end.line == line) { - con->sel_end.line = NULL; - con->sel_end.y = SELECTION_TOP; - } - } - line_free(line); - return; - } - - /* Remove a line from the scrollback buffer if it reaches its maximum. - * We must take care to correctly keep the current position as the new - * line is linked in after we remove the top-most line here. - * sb_max == 0 is tested earlier so we can assume sb_max > 0 here. In - * other words, buf->sb_first is a valid line if sb_count >= sb_max. */ - if (con->sb_count >= con->sb_max) { - tmp = con->sb_first; - con->sb_first = tmp->next; - if (tmp->next) - tmp->next->prev = NULL; - else - con->sb_last = NULL; - --con->sb_count; - - /* (position == tmp && !next) means we have sb_max=1 so set - * position to the new line. Otherwise, set to new first line. - * If position!=tmp and we have a fixed-position then nothing - * needs to be done because we can stay at the same line. If we - * have no fixed-position, we need to set the position to the - * next inserted line, which can be "line", too. */ - if (con->sb_pos) { - if (con->sb_pos == tmp || - !(con->flags & TSM_SCREEN_FIXED_POS)) { - if (con->sb_pos->next) - con->sb_pos = con->sb_pos->next; - else - con->sb_pos = line; - } - } - - if (con->sel_active) { - if (con->sel_start.line == tmp) { - con->sel_start.line = NULL; - con->sel_start.y = SELECTION_TOP; - } - if (con->sel_end.line == tmp) { - con->sel_end.line = NULL; - con->sel_end.y = SELECTION_TOP; - } - } - line_free(tmp); - } - - line->sb_id = ++con->sb_last_id; - line->next = NULL; - line->prev = con->sb_last; - if (con->sb_last) - con->sb_last->next = line; - else - con->sb_first = line; - con->sb_last = line; - ++con->sb_count; -} - -static void screen_scroll_up(struct tsm_screen *con, unsigned int num) -{ - unsigned int i, j, max, pos; - int ret; - - if (!num) - return; - - max = con->margin_bottom + 1 - con->margin_top; - if (num > max) - num = max; - - /* We cache lines on the stack to speed up the scrolling. However, if - * num is too big we might get overflows here so use recursion if num - * exceeds a hard-coded limit. - * 128 seems to be a sane limit that should never be reached but should - * also be small enough so we do not get stack overflows. */ - if (num > 128) { - screen_scroll_up(con, 128); - return screen_scroll_up(con, num - 128); - } - struct line *cache[num]; - - for (i = 0; i < num; ++i) { - pos = con->margin_top + i; - if (!(con->flags & TSM_SCREEN_ALTERNATE)) - ret = line_new(con, &cache[i], con->size_x); - else - ret = -EAGAIN; - - if (!ret) { - link_to_scrollback(con, con->lines[pos]); - } else { - cache[i] = con->lines[pos]; - for (j = 0; j < con->size_x; ++j) - cell_init(con, &cache[i]->cells[j]); - } - } - - if (num < max) { - memmove(&con->lines[con->margin_top], - &con->lines[con->margin_top + num], - (max - num) * sizeof(struct line*)); - } - - memcpy(&con->lines[con->margin_top + (max - num)], - cache, num * sizeof(struct line*)); - - if (con->sel_active) { - if (!con->sel_start.line && con->sel_start.y >= 0) { - con->sel_start.y -= num; - if (con->sel_start.y < 0) { - con->sel_start.line = con->sb_last; - while (con->sel_start.line && ++con->sel_start.y < 0) - con->sel_start.line = con->sel_start.line->prev; - con->sel_start.y = SELECTION_TOP; - } - } - if (!con->sel_end.line && con->sel_end.y >= 0) { - con->sel_end.y -= num; - if (con->sel_end.y < 0) { - con->sel_end.line = con->sb_last; - while (con->sel_end.line && ++con->sel_end.y < 0) - con->sel_end.line = con->sel_end.line->prev; - con->sel_end.y = SELECTION_TOP; - } - } - } -} - -static void screen_scroll_down(struct tsm_screen *con, unsigned int num) -{ - unsigned int i, j, max; - - if (!num) - return; - - max = con->margin_bottom + 1 - con->margin_top; - if (num > max) - num = max; - - /* see screen_scroll_up() for an explanation */ - if (num > 128) { - screen_scroll_down(con, 128); - return screen_scroll_down(con, num - 128); - } - struct line *cache[num]; - - for (i = 0; i < num; ++i) { - cache[i] = con->lines[con->margin_bottom - i]; - for (j = 0; j < con->size_x; ++j) - cell_init(con, &cache[i]->cells[j]); - } - - if (num < max) { - memmove(&con->lines[con->margin_top + num], - &con->lines[con->margin_top], - (max - num) * sizeof(struct line*)); - } - - memcpy(&con->lines[con->margin_top], - cache, num * sizeof(struct line*)); - - if (con->sel_active) { - if (!con->sel_start.line && con->sel_start.y >= 0) - con->sel_start.y += num; - if (!con->sel_end.line && con->sel_end.y >= 0) - con->sel_end.y += num; - } -} - -static void screen_write(struct tsm_screen *con, unsigned int x, - unsigned int y, tsm_symbol_t ch, unsigned int len, - const struct tsm_screen_attr *attr) -{ - struct line *line; - unsigned int i; - - if (!len) - return; - - if (x >= con->size_x || y >= con->size_y) { - llog_warn(con, "writing beyond buffer boundary"); - return; - } - - line = con->lines[y]; - - if ((con->flags & TSM_SCREEN_INSERT_MODE) && - (int)x < ((int)con->size_x - len)) - memmove(&line->cells[x + len], &line->cells[x], - sizeof(struct cell) * (con->size_x - len - x)); - - line->cells[x].ch = ch; - line->cells[x].width = len; - memcpy(&line->cells[x].attr, attr, sizeof(*attr)); - - for (i = 1; i < len && i + x < con->size_x; ++i) - line->cells[x + i].width = 0; -} - -static void screen_erase_region(struct tsm_screen *con, - unsigned int x_from, - unsigned int y_from, - unsigned int x_to, - unsigned int y_to, - bool protect) -{ - unsigned int to; - struct line *line; - - if (y_to >= con->size_y) - y_to = con->size_y - 1; - if (x_to >= con->size_x) - x_to = con->size_x - 1; - - for ( ; y_from <= y_to; ++y_from) { - line = con->lines[y_from]; - if (!line) { - x_from = 0; - continue; - } - - if (y_from == y_to) - to = x_to; - else - to = con->size_x - 1; - for ( ; x_from <= to; ++x_from) { - if (protect && line->cells[x_from].attr.protect) - continue; - - cell_init(con, &line->cells[x_from]); - } - x_from = 0; - } -} - -static inline unsigned int to_abs_x(struct tsm_screen *con, unsigned int x) -{ - return x; -} - -static inline unsigned int to_abs_y(struct tsm_screen *con, unsigned int y) -{ - if (!(con->flags & TSM_SCREEN_REL_ORIGIN)) - return y; - - return con->margin_top + y; -} - -SHL_EXPORT -int tsm_screen_new(struct tsm_screen **out, tsm_log_t log, void *log_data) -{ - struct tsm_screen *con; - int ret; - unsigned int i; - - if (!out) - return -EINVAL; - - con = malloc(sizeof(*con)); - if (!con) - return -ENOMEM; - - memset(con, 0, sizeof(*con)); - con->ref = 1; - con->llog = log; - con->llog_data = log_data; - con->def_attr.fr = 255; - con->def_attr.fg = 255; - con->def_attr.fb = 255; - - ret = shl_timer_new(&con->timer); - if (ret) - goto err_free; - - ret = tsm_screen_resize(con, 80, 24); - if (ret) - goto err_timer; - - llog_debug(con, "new screen"); - *out = con; - - return 0; - -err_timer: - shl_timer_free(con->timer); - for (i = 0; i < con->line_num; ++i) { - line_free(con->main_lines[i]); - line_free(con->alt_lines[i]); - } - free(con->main_lines); - free(con->alt_lines); - free(con->tab_ruler); -err_free: - free(con); - return ret; -} - -SHL_EXPORT -void tsm_screen_ref(struct tsm_screen *con) -{ - if (!con) - return; - - ++con->ref; -} - -SHL_EXPORT -void tsm_screen_unref(struct tsm_screen *con) -{ - unsigned int i; - - if (!con || !con->ref || --con->ref) - return; - - llog_debug(con, "destroying screen"); - - for (i = 0; i < con->line_num; ++i) { - line_free(con->main_lines[i]); - line_free(con->alt_lines[i]); - } - free(con->main_lines); - free(con->alt_lines); - free(con->tab_ruler); - shl_timer_free(con->timer); - free(con); -} - -SHL_EXPORT -void tsm_screen_set_opts(struct tsm_screen *scr, unsigned int opts) -{ - if (!scr || !opts) - return; - - scr->opts |= opts; -} - -SHL_EXPORT -void tsm_screen_reset_opts(struct tsm_screen *scr, unsigned int opts) -{ - if (!scr || !opts) - return; - - scr->opts &= ~opts; -} - -SHL_EXPORT -unsigned int tsm_screen_get_opts(struct tsm_screen *scr) -{ - if (!scr) - return 0; - - return scr->opts; -} - -SHL_EXPORT -unsigned int tsm_screen_get_width(struct tsm_screen *con) -{ - if (!con) - return 0; - - return con->size_x; -} - -SHL_EXPORT -unsigned int tsm_screen_get_height(struct tsm_screen *con) -{ - if (!con) - return 0; - - return con->size_y; -} - -SHL_EXPORT -int tsm_screen_resize(struct tsm_screen *con, unsigned int x, - unsigned int y) -{ - struct line **cache; - unsigned int i, j, width, diff; - int ret; - bool *tab_ruler; - - if (!con || !x || !y) - return -EINVAL; - - if (con->size_x == x && con->size_y == y) - return 0; - - /* First make sure the line buffer is big enough for our new screen. - * That is, allocate all new lines and make sure each line has enough - * cells to hold the new screen or the current screen. If we fail, we - * can safely return -ENOMEM and the buffer is still valid. We must - * allocate the new lines to at least the same size as the current - * lines. Otherwise, if this function fails in later turns, we will have - * invalid lines in the buffer. */ - if (y > con->line_num) { - /* resize main buffer */ - cache = realloc(con->main_lines, sizeof(struct line*) * y); - if (!cache) - return -ENOMEM; - - if (con->lines == con->main_lines) - con->lines = cache; - con->main_lines = cache; - - /* resize alt buffer */ - cache = realloc(con->alt_lines, sizeof(struct line*) * y); - if (!cache) - return -ENOMEM; - - if (con->lines == con->alt_lines) - con->lines = cache; - con->alt_lines = cache; - - /* allocate new lines */ - if (x > con->size_x) - width = x; - else - width = con->size_x; - - while (con->line_num < y) { - ret = line_new(con, &con->main_lines[con->line_num], - width); - if (ret) - return ret; - - ret = line_new(con, &con->alt_lines[con->line_num], - width); - if (ret) { - line_free(con->main_lines[con->line_num]); - return ret; - } - - ++con->line_num; - } - } - - /* Resize all lines in the buffer if we increase screen width. This - * will guarantee that all lines are big enough so we can resize the - * buffer without reallocating them later. */ - if (x > con->size_x) { - tab_ruler = realloc(con->tab_ruler, sizeof(bool) * x); - if (!tab_ruler) - return -ENOMEM; - con->tab_ruler = tab_ruler; - - for (i = 0; i < con->line_num; ++i) { - ret = line_resize(con, con->main_lines[i], x); - if (ret) - return ret; - - ret = line_resize(con, con->alt_lines[i], x); - if (ret) - return ret; - } - } - - for (j = 0; j < con->line_num; ++j) { - if (j >= con->size_y) - i = 0; - else - i = con->size_x; - - if (x < con->main_lines[j]->size) - width = x; - else - width = con->main_lines[j]->size; - for (; i < width; ++i) - cell_init(con, &con->main_lines[j]->cells[i]); - - if (x < con->alt_lines[j]->size) - width = x; - else - width = con->alt_lines[j]->size; - for (; i < width; ++i) - cell_init(con, &con->alt_lines[j]->cells[i]); - } - - /* xterm destroys margins on resize, so do we */ - con->margin_top = 0; - con->margin_bottom = con->size_y - 1; - - /* reset tabs */ - for (i = 0; i < x; ++i) { - if (i % 8 == 0) - con->tab_ruler[i] = true; - else - con->tab_ruler[i] = false; - } - - /* We need to adjust x-size first as screen_scroll_up() and friends may - * have to reallocate lines. The y-size is adjusted after them to avoid - * missing lines when shrinking y-size. - * We need to carefully look for the functions that we call here as they - * have stronger invariants as when called normally. */ - - con->size_x = x; - if (con->cursor_x >= con->size_x) - con->cursor_x = con->size_x - 1; - - /* scroll buffer if screen height shrinks */ - if (con->size_y != 0 && y < con->size_y) { - diff = con->size_y - y; - screen_scroll_up(con, diff); - if (con->cursor_y > diff) - con->cursor_y -= diff; - else - con->cursor_y = 0; - } - - con->size_y = y; - con->margin_bottom = con->size_y - 1; - if (con->cursor_y >= con->size_y) - con->cursor_y = con->size_y - 1; - - return 0; -} - -SHL_EXPORT -int tsm_screen_set_margins(struct tsm_screen *con, - unsigned int top, unsigned int bottom) -{ - unsigned int upper, lower; - - if (!con) - return -EINVAL; - - if (!top) - top = 1; - - if (bottom <= top) { - upper = 0; - lower = con->size_y - 1; - } else if (bottom > con->size_y) { - upper = 0; - lower = con->size_y - 1; - } else { - upper = top - 1; - lower = bottom - 1; - } - - con->margin_top = upper; - con->margin_bottom = lower; - return 0; -} - -/* set maximum scrollback buffer size */ -SHL_EXPORT -void tsm_screen_set_max_sb(struct tsm_screen *con, - unsigned int max) -{ - struct line *line; - - if (!con) - return; - - while (con->sb_count > max) { - line = con->sb_first; - con->sb_first = line->next; - if (line->next) - line->next->prev = NULL; - else - con->sb_last = NULL; - con->sb_count--; - - /* We treat fixed/unfixed position the same here because we - * remove lines from the TOP of the scrollback buffer. */ - if (con->sb_pos == line) - con->sb_pos = con->sb_first; - - if (con->sel_active) { - if (con->sel_start.line == line) { - con->sel_start.line = NULL; - con->sel_start.y = SELECTION_TOP; - } - if (con->sel_end.line == line) { - con->sel_end.line = NULL; - con->sel_end.y = SELECTION_TOP; - } - } - line_free(line); - } - - con->sb_max = max; -} - -/* clear scrollback buffer */ -SHL_EXPORT -void tsm_screen_clear_sb(struct tsm_screen *con) -{ - struct line *iter, *tmp; - - if (!con) - return; - - for (iter = con->sb_first; iter; ) { - tmp = iter; - iter = iter->next; - line_free(tmp); - } - - con->sb_first = NULL; - con->sb_last = NULL; - con->sb_count = 0; - con->sb_pos = NULL; - - if (con->sel_active) { - if (con->sel_start.line) { - con->sel_start.line = NULL; - con->sel_start.y = SELECTION_TOP; - } - if (con->sel_end.line) { - con->sel_end.line = NULL; - con->sel_end.y = SELECTION_TOP; - } - } -} - -SHL_EXPORT -void tsm_screen_sb_up(struct tsm_screen *con, unsigned int num) -{ - if (!con || !num) - return; - - while (num--) { - if (con->sb_pos) { - if (!con->sb_pos->prev) - return; - - con->sb_pos = con->sb_pos->prev; - } else if (!con->sb_last) { - return; - } else { - con->sb_pos = con->sb_last; - } - } -} - -SHL_EXPORT -void tsm_screen_sb_down(struct tsm_screen *con, unsigned int num) -{ - if (!con || !num) - return; - - while (num--) { - if (con->sb_pos) { - con->sb_pos = con->sb_pos->next; - if (!con->sb_pos) - return; - } else { - return; - } - } -} - -SHL_EXPORT -void tsm_screen_sb_page_up(struct tsm_screen *con, unsigned int num) -{ - if (!con || !num) - return; - - tsm_screen_sb_up(con, num * con->size_y); -} - -SHL_EXPORT -void tsm_screen_sb_page_down(struct tsm_screen *con, unsigned int num) -{ - if (!con || !num) - return; - - tsm_screen_sb_down(con, num * con->size_y); -} - -SHL_EXPORT -void tsm_screen_sb_reset(struct tsm_screen *con) -{ - if (!con) - return; - - con->sb_pos = NULL; -} - -SHL_EXPORT -void tsm_screen_set_def_attr(struct tsm_screen *con, - const struct tsm_screen_attr *attr) -{ - if (!con || !attr) - return; - - memcpy(&con->def_attr, attr, sizeof(*attr)); -} - -SHL_EXPORT -void tsm_screen_reset(struct tsm_screen *con) -{ - unsigned int i; - - if (!con) - return; - - con->flags = 0; - con->margin_top = 0; - con->margin_bottom = con->size_y - 1; - - for (i = 0; i < con->size_x; ++i) { - if (i % 8 == 0) - con->tab_ruler[i] = true; - else - con->tab_ruler[i] = false; - } -} - -SHL_EXPORT -void tsm_screen_set_flags(struct tsm_screen *con, unsigned int flags) -{ - unsigned int old; - - if (!con || !flags) - return; - - old = con->flags; - con->flags |= flags; - - if (!(old & TSM_SCREEN_ALTERNATE) && (flags & TSM_SCREEN_ALTERNATE)) - con->lines = con->alt_lines; -} - -SHL_EXPORT -void tsm_screen_reset_flags(struct tsm_screen *con, unsigned int flags) -{ - unsigned int old; - - if (!con || !flags) - return; - - old = con->flags; - con->flags &= ~flags; - - if ((old & TSM_SCREEN_ALTERNATE) && (flags & TSM_SCREEN_ALTERNATE)) - con->lines = con->main_lines; -} - -SHL_EXPORT -unsigned int tsm_screen_get_flags(struct tsm_screen *con) -{ - if (!con) - return 0; - - return con->flags; -} - -SHL_EXPORT -unsigned int tsm_screen_get_cursor_x(struct tsm_screen *con) -{ - if (!con) - return 0; - - return con->cursor_x; -} - -SHL_EXPORT -unsigned int tsm_screen_get_cursor_y(struct tsm_screen *con) -{ - if (!con) - return 0; - - return con->cursor_y; -} - -SHL_EXPORT -void tsm_screen_set_tabstop(struct tsm_screen *con) -{ - if (!con || con->cursor_x >= con->size_x) - return; - - con->tab_ruler[con->cursor_x] = true; -} - -SHL_EXPORT -void tsm_screen_reset_tabstop(struct tsm_screen *con) -{ - if (!con || con->cursor_x >= con->size_x) - return; - - con->tab_ruler[con->cursor_x] = false; -} - -SHL_EXPORT -void tsm_screen_reset_all_tabstops(struct tsm_screen *con) -{ - unsigned int i; - - if (!con) - return; - - for (i = 0; i < con->size_x; ++i) - con->tab_ruler[i] = false; -} - -SHL_EXPORT -void tsm_screen_write(struct tsm_screen *con, tsm_symbol_t ch, - const struct tsm_screen_attr *attr) -{ - unsigned int last, len; - - if (!con) - return; - - len = tsm_symbol_get_width(NULL, ch); - if (!len) - return; - - if (con->cursor_y <= con->margin_bottom || - con->cursor_y >= con->size_y) - last = con->margin_bottom; - else - last = con->size_y - 1; - - if (con->cursor_x >= con->size_x) { - if (con->flags & TSM_SCREEN_AUTO_WRAP) { - con->cursor_x = 0; - ++con->cursor_y; - } else { - con->cursor_x = con->size_x - 1; - } - } - - if (con->cursor_y > last) { - con->cursor_y = last; - screen_scroll_up(con, 1); - } - - screen_write(con, con->cursor_x, con->cursor_y, ch, len, attr); - con->cursor_x += len; -} - -SHL_EXPORT -void tsm_screen_newline(struct tsm_screen *con) -{ - if (!con) - return; - - tsm_screen_move_down(con, 1, true); - tsm_screen_move_line_home(con); -} - -SHL_EXPORT -void tsm_screen_scroll_up(struct tsm_screen *con, unsigned int num) -{ - if (!con || !num) - return; - - screen_scroll_up(con, num); -} - -SHL_EXPORT -void tsm_screen_scroll_down(struct tsm_screen *con, unsigned int num) -{ - if (!con || !num) - return; - - screen_scroll_down(con, num); -} - -SHL_EXPORT -void tsm_screen_move_to(struct tsm_screen *con, unsigned int x, - unsigned int y) -{ - unsigned int last; - - if (!con) - return; - - if (con->flags & TSM_SCREEN_REL_ORIGIN) - last = con->margin_bottom; - else - last = con->size_y - 1; - - con->cursor_x = to_abs_x(con, x); - if (con->cursor_x >= con->size_x) - con->cursor_x = con->size_x - 1; - - con->cursor_y = to_abs_y(con, y); - if (con->cursor_y > last) - con->cursor_y = last; -} - -SHL_EXPORT -void tsm_screen_move_up(struct tsm_screen *con, unsigned int num, - bool scroll) -{ - unsigned int diff, size; - - if (!con || !num) - return; - - if (con->cursor_y >= con->margin_top) - size = con->margin_top; - else - size = 0; - - diff = con->cursor_y - size; - if (num > diff) { - num -= diff; - if (scroll) - screen_scroll_down(con, num); - con->cursor_y = size; - } else { - con->cursor_y -= num; - } -} - -SHL_EXPORT -void tsm_screen_move_down(struct tsm_screen *con, unsigned int num, - bool scroll) -{ - unsigned int diff, size; - - if (!con || !num) - return; - - if (con->cursor_y <= con->margin_bottom) - size = con->margin_bottom + 1; - else - size = con->size_y; - - diff = size - con->cursor_y - 1; - if (num > diff) { - num -= diff; - if (scroll) - screen_scroll_up(con, num); - con->cursor_y = size - 1; - } else { - con->cursor_y += num; - } -} - -SHL_EXPORT -void tsm_screen_move_left(struct tsm_screen *con, unsigned int num) -{ - if (!con || !num) - return; - - if (num > con->size_x) - num = con->size_x; - - if (con->cursor_x >= con->size_x) - con->cursor_x = con->size_x - 1; - - if (num > con->cursor_x) - con->cursor_x = 0; - else - con->cursor_x -= num; -} - -SHL_EXPORT -void tsm_screen_move_right(struct tsm_screen *con, unsigned int num) -{ - if (!con || !num) - return; - - if (num > con->size_x) - num = con->size_x; - - if (num + con->cursor_x >= con->size_x) - con->cursor_x = con->size_x - 1; - else - con->cursor_x += num; -} - -SHL_EXPORT -void tsm_screen_move_line_end(struct tsm_screen *con) -{ - if (!con) - return; - - con->cursor_x = con->size_x - 1; -} - -SHL_EXPORT -void tsm_screen_move_line_home(struct tsm_screen *con) -{ - if (!con) - return; - - con->cursor_x = 0; -} - -SHL_EXPORT -void tsm_screen_tab_right(struct tsm_screen *con, unsigned int num) -{ - unsigned int i, j; - - if (!con || !num) - return; - - for (i = 0; i < num; ++i) { - for (j = con->cursor_x + 1; j < con->size_x; ++j) { - if (con->tab_ruler[j]) - break; - } - - con->cursor_x = j; - if (con->cursor_x + 1 >= con->size_x) - break; - } - - /* tabs never cause pending new-lines */ - if (con->cursor_x >= con->size_x) - con->cursor_x = con->size_x - 1; -} - -SHL_EXPORT -void tsm_screen_tab_left(struct tsm_screen *con, unsigned int num) -{ - unsigned int i; - int j; - - if (!con || !num) - return; - - for (i = 0; i < num; ++i) { - for (j = con->cursor_x - 1; j > 0; --j) { - if (con->tab_ruler[j]) - break; - } - - if (j <= 0) { - con->cursor_x = 0; - break; - } - con->cursor_x = j; - } -} - -SHL_EXPORT -void tsm_screen_insert_lines(struct tsm_screen *con, unsigned int num) -{ - unsigned int i, j, max; - - if (!con || !num) - return; - - if (con->cursor_y < con->margin_top || - con->cursor_y > con->margin_bottom) - return; - - max = con->margin_bottom - con->cursor_y + 1; - if (num > max) - num = max; - - struct line *cache[num]; - - for (i = 0; i < num; ++i) { - cache[i] = con->lines[con->margin_bottom - i]; - for (j = 0; j < con->size_x; ++j) - cell_init(con, &cache[i]->cells[j]); - } - - if (num < max) { - memmove(&con->lines[con->cursor_y + num], - &con->lines[con->cursor_y], - (max - num) * sizeof(struct line*)); - - memcpy(&con->lines[con->cursor_y], - cache, num * sizeof(struct line*)); - } - - con->cursor_x = 0; -} - -SHL_EXPORT -void tsm_screen_delete_lines(struct tsm_screen *con, unsigned int num) -{ - unsigned int i, j, max; - - if (!con || !num) - return; - - if (con->cursor_y < con->margin_top || - con->cursor_y > con->margin_bottom) - return; - - max = con->margin_bottom - con->cursor_y + 1; - if (num > max) - num = max; - - struct line *cache[num]; - - for (i = 0; i < num; ++i) { - cache[i] = con->lines[con->cursor_y + i]; - for (j = 0; j < con->size_x; ++j) - cell_init(con, &cache[i]->cells[j]); - } - - if (num < max) { - memmove(&con->lines[con->cursor_y], - &con->lines[con->cursor_y + num], - (max - num) * sizeof(struct line*)); - - memcpy(&con->lines[con->cursor_y + (max - num)], - cache, num * sizeof(struct line*)); - } - - con->cursor_x = 0; -} - -SHL_EXPORT -void tsm_screen_insert_chars(struct tsm_screen *con, unsigned int num) -{ - struct cell *cells; - unsigned int max, mv, i; - - if (!con || !num || !con->size_y || !con->size_x) - return; - - if (con->cursor_x >= con->size_x) - con->cursor_x = con->size_x - 1; - if (con->cursor_y >= con->size_y) - con->cursor_y = con->size_y - 1; - - max = con->size_x - con->cursor_x; - if (num > max) - num = max; - mv = max - num; - - cells = con->lines[con->cursor_y]->cells; - if (mv) - memmove(&cells[con->cursor_x + num], - &cells[con->cursor_x], - mv * sizeof(*cells)); - - for (i = 0; i < num; ++i) { - cell_init(con, &cells[con->cursor_x + i]); - } -} - -SHL_EXPORT -void tsm_screen_delete_chars(struct tsm_screen *con, unsigned int num) -{ - struct cell *cells; - unsigned int max, mv, i; - - if (!con || !num || !con->size_y || !con->size_x) - return; - - if (con->cursor_x >= con->size_x) - con->cursor_x = con->size_x - 1; - if (con->cursor_y >= con->size_y) - con->cursor_y = con->size_y - 1; - - max = con->size_x - con->cursor_x; - if (num > max) - num = max; - mv = max - num; - - cells = con->lines[con->cursor_y]->cells; - if (mv) - memmove(&cells[con->cursor_x], - &cells[con->cursor_x + num], - mv * sizeof(*cells)); - - for (i = 0; i < num; ++i) { - cell_init(con, &cells[con->cursor_x + mv + i]); - } -} - -SHL_EXPORT -void tsm_screen_erase_cursor(struct tsm_screen *con) -{ - unsigned int x; - - if (!con) - return; - - if (con->cursor_x >= con->size_x) - x = con->size_x - 1; - else - x = con->cursor_x; - - screen_erase_region(con, x, con->cursor_y, x, con->cursor_y, false); -} - -SHL_EXPORT -void tsm_screen_erase_chars(struct tsm_screen *con, unsigned int num) -{ - unsigned int x; - - if (!con || !num) - return; - - if (con->cursor_x >= con->size_x) - x = con->size_x - 1; - else - x = con->cursor_x; - - screen_erase_region(con, x, con->cursor_y, x + num - 1, con->cursor_y, - false); -} - -SHL_EXPORT -void tsm_screen_erase_cursor_to_end(struct tsm_screen *con, - bool protect) -{ - unsigned int x; - - if (!con) - return; - - if (con->cursor_x >= con->size_x) - x = con->size_x - 1; - else - x = con->cursor_x; - - screen_erase_region(con, x, con->cursor_y, con->size_x - 1, - con->cursor_y, protect); -} - -SHL_EXPORT -void tsm_screen_erase_home_to_cursor(struct tsm_screen *con, - bool protect) -{ - if (!con) - return; - - screen_erase_region(con, 0, con->cursor_y, con->cursor_x, - con->cursor_y, protect); -} - -SHL_EXPORT -void tsm_screen_erase_current_line(struct tsm_screen *con, - bool protect) -{ - if (!con) - return; - - screen_erase_region(con, 0, con->cursor_y, con->size_x - 1, - con->cursor_y, protect); -} - -SHL_EXPORT -void tsm_screen_erase_screen_to_cursor(struct tsm_screen *con, - bool protect) -{ - if (!con) - return; - - screen_erase_region(con, 0, 0, con->cursor_x, con->cursor_y, protect); -} - -SHL_EXPORT -void tsm_screen_erase_cursor_to_screen(struct tsm_screen *con, - bool protect) -{ - unsigned int x; - - if (!con) - return; - - if (con->cursor_x >= con->size_x) - x = con->size_x - 1; - else - x = con->cursor_x; - - screen_erase_region(con, x, con->cursor_y, con->size_x - 1, - con->size_y - 1, protect); -} - -SHL_EXPORT -void tsm_screen_erase_screen(struct tsm_screen *con, bool protect) -{ - if (!con) - return; - - screen_erase_region(con, 0, 0, con->size_x - 1, con->size_y - 1, - protect); -} - -/* - * Selection Code - * If a running pty-client does not support mouse-tracking extensions, a - * terminal can manually mark selected areas if it does mouse-tracking itself. - * This tracking is slightly different than the integrated client-tracking: - * - * Initial state is no-selection. At any time selection_reset() can be called to - * clear the selection and go back to initial state. - * If the user presses a mouse-button, the terminal can calculate the selected - * cell and call selection_start() to notify the terminal that the user started - * the selection. While the mouse-button is held down, the terminal should call - * selection_target() whenever a mouse-event occurs. This will tell the screen - * layer to draw the selection from the initial start up to the last given - * target. - * Please note that the selection-start cannot be modified by the terminal - * during a selection. Instead, the screen-layer automatically moves it along - * with any scroll-operations or inserts/deletes. This also means, the terminal - * must _not_ cache the start-position itself as it may change under the hood. - * This selection takes also care of scrollback-buffer selections and correctly - * moves selection state along. - * - * Please note that this is not the kind of selection that some PTY applications - * support. If the client supports the mouse-protocol, then it can also control - * a separate screen-selection which is always inside of the actual screen. This - * is a totally different selection. - */ - -static void selection_set(struct tsm_screen *con, struct selection_pos *sel, - unsigned int x, unsigned int y) -{ - struct line *pos; - - sel->line = NULL; - pos = con->sb_pos; - - while (y && pos) { - --y; - pos = pos->next; - } - - if (pos) - sel->line = pos; - - sel->x = x; - sel->y = y; -} - -SHL_EXPORT -void tsm_screen_selection_reset(struct tsm_screen *con) -{ - if (!con) - return; - - con->sel_active = false; -} - -SHL_EXPORT -void tsm_screen_selection_start(struct tsm_screen *con, - unsigned int posx, - unsigned int posy) -{ - if (!con) - return; - - con->sel_active = true; - selection_set(con, &con->sel_start, posx, posy); - memcpy(&con->sel_end, &con->sel_start, sizeof(con->sel_end)); -} - -SHL_EXPORT -void tsm_screen_selection_target(struct tsm_screen *con, - unsigned int posx, - unsigned int posy) -{ - if (!con || !con->sel_active) - return; - - selection_set(con, &con->sel_end, posx, posy); -} - -/* TODO: tsm_ucs4_to_utf8 expects UCS4 characters, but a cell contains a - * tsm-symbol (which can contain multiple UCS4 chars). Fix this when introducing - * support for combining characters. */ -static unsigned int copy_line(struct line *line, char *buf, - unsigned int start, unsigned int len) -{ - unsigned int i, end; - char *pos = buf; - - end = start + len; - for (i = start; i < line->size && i < end; ++i) { - if (i < line->size || !line->cells[i].ch) - pos += tsm_ucs4_to_utf8(line->cells[i].ch, pos); - else - pos += tsm_ucs4_to_utf8(' ', pos); - } - - return pos - buf; -} - -/* TODO: This beast definitely needs some "beautification", however, it's meant - * as a "proof-of-concept" so its enough for now. */ -SHL_EXPORT -int tsm_screen_selection_copy(struct tsm_screen *con, char **out) -{ - unsigned int len, i; - struct selection_pos *start, *end; - struct line *iter; - char *str, *pos; - - if (!con || !out) - return -EINVAL; - - if (!con->sel_active) - return -ENOENT; - - /* check whether sel_start or sel_end comes first */ - if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP) { - if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) { - str = strdup(""); - if (!str) - return -ENOMEM; - *out = str; - return 0; - } - start = &con->sel_start; - end = &con->sel_end; - } else if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) { - start = &con->sel_end; - end = &con->sel_start; - } else if (con->sel_start.line && con->sel_end.line) { - if (con->sel_start.line->sb_id < con->sel_end.line->sb_id) { - start = &con->sel_start; - end = &con->sel_end; - } else if (con->sel_start.line->sb_id > con->sel_end.line->sb_id) { - start = &con->sel_end; - end = &con->sel_start; - } else if (con->sel_start.x < con->sel_end.x) { - start = &con->sel_start; - end = &con->sel_end; - } else { - start = &con->sel_end; - end = &con->sel_start; - } - } else if (con->sel_start.line) { - start = &con->sel_start; - end = &con->sel_end; - } else if (con->sel_end.line) { - start = &con->sel_end; - end = &con->sel_start; - } else if (con->sel_start.y < con->sel_end.y) { - start = &con->sel_start; - end = &con->sel_end; - } else if (con->sel_start.y > con->sel_end.y) { - start = &con->sel_end; - end = &con->sel_start; - } else if (con->sel_start.x < con->sel_end.x) { - start = &con->sel_start; - end = &con->sel_end; - } else { - start = &con->sel_end; - end = &con->sel_start; - } - - /* calculate size of buffer */ - len = 0; - iter = start->line; - if (!iter && start->y == SELECTION_TOP) - iter = con->sb_first; - - while (iter) { - if (iter == start->line && iter == end->line) { - if (iter->size > start->x) { - if (iter->size > end->x) - len += end->x - start->x + 1; - else - len += iter->size - start->x; - } - break; - } else if (iter == start->line) { - if (iter->size > start->x) - len += iter->size - start->x; - } else if (iter == end->line) { - if (iter->size > end->x) - len += end->x + 1; - else - len += iter->size; - break; - } else { - len += iter->size; - } - - ++len; - iter = iter->next; - } - - if (!end->line) { - if (start->line || start->y == SELECTION_TOP) - i = 0; - else - i = start->y; - for ( ; i < con->size_y; ++i) { - if (!start->line && start->y == i && end->y == i) { - if (con->size_x > start->x) { - if (con->size_x > end->x) - len += end->x - start->x + 1; - else - len += con->size_x - start->x; - } - break; - } else if (!start->line && start->y == i) { - if (con->size_x > start->x) - len += con->size_x - start->x; - } else if (end->y == i) { - if (con->size_x > end->x) - len += end->x + 1; - else - len += con->size_x; - break; - } else { - len += con->size_x; - } - - ++len; - } - } - - /* allocate buffer */ - len *= 4; - ++len; - str = malloc(len); - if (!str) - return -ENOMEM; - pos = str; - - /* copy data into buffer */ - iter = start->line; - if (!iter && start->y == SELECTION_TOP) - iter = con->sb_first; - - while (iter) { - if (iter == start->line && iter == end->line) { - if (iter->size > start->x) { - if (iter->size > end->x) - len = end->x - start->x + 1; - else - len = iter->size - start->x; - pos += copy_line(iter, pos, start->x, len); - } - break; - } else if (iter == start->line) { - if (iter->size > start->x) - pos += copy_line(iter, pos, start->x, - iter->size - start->x); - } else if (iter == end->line) { - if (iter->size > end->x) - len = end->x + 1; - else - len = iter->size; - pos += copy_line(iter, pos, 0, len); - break; - } else { - pos += copy_line(iter, pos, 0, iter->size); - } - - *pos++ = '\n'; - iter = iter->next; - } - - if (!end->line) { - if (start->line || start->y == SELECTION_TOP) - i = 0; - else - i = start->y; - for ( ; i < con->size_y; ++i) { - iter = con->lines[i]; - if (!start->line && start->y == i && end->y == i) { - if (con->size_x > start->x) { - if (con->size_x > end->x) - len = end->x - start->x + 1; - else - len = con->size_x - start->x; - pos += copy_line(iter, pos, start->x, len); - } - break; - } else if (!start->line && start->y == i) { - if (con->size_x > start->x) - pos += copy_line(iter, pos, start->x, - con->size_x - start->x); - } else if (end->y == i) { - if (con->size_x > end->x) - len = end->x + 1; - else - len = con->size_x; - pos += copy_line(iter, pos, 0, len); - break; - } else { - pos += copy_line(iter, pos, 0, con->size_x); - } - - *pos++ = '\n'; - } - } - - /* return buffer */ - *pos = 0; - *out = str; - return pos - str; -} - -SHL_EXPORT -void tsm_screen_draw(struct tsm_screen *con, - tsm_screen_prepare_cb prepare_cb, - tsm_screen_draw_cb draw_cb, - tsm_screen_render_cb render_cb, - void *data) -{ - unsigned int cur_x, cur_y; - unsigned int i, j, k; - struct line *iter, *line = NULL; - struct cell *cell; - struct tsm_screen_attr attr; - bool cursor_done = false; - int ret, warned = 0; - uint64_t time_prep = 0, time_draw = 0, time_rend = 0; - const uint32_t *ch; - size_t len; - struct cell empty; - bool in_sel = false, sel_start = false, sel_end = false; - bool was_sel = false; - - if (!con || !draw_cb) - return; - - cell_init(con, &empty); - - cur_x = con->cursor_x; - if (con->cursor_x >= con->size_x) - cur_x = con->size_x - 1; - cur_y = con->cursor_y; - if (con->cursor_y >= con->size_y) - cur_y = con->size_y - 1; - - /* render preparation */ - - if (prepare_cb) { - if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) - shl_timer_reset(con->timer); - - ret = prepare_cb(con, data); - if (ret) { - llog_warning(con, - "cannot prepare text-renderer for rendering"); - return; - } - - if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) - time_prep = shl_timer_elapsed(con->timer); - } else { - time_prep = 0; - } - - /* push each character into rendering pipeline */ - - if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) - shl_timer_reset(con->timer); - - iter = con->sb_pos; - k = 0; - - if (con->sel_active) { - if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP) - in_sel = !in_sel; - if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) - in_sel = !in_sel; - - if (con->sel_start.line && - (!iter || con->sel_start.line->sb_id < iter->sb_id)) - in_sel = !in_sel; - if (con->sel_end.line && - (!iter || con->sel_end.line->sb_id < iter->sb_id)) - in_sel = !in_sel; - } - - for (i = 0; i < con->size_y; ++i) { - if (iter) { - line = iter; - iter = iter->next; - } else { - line = con->lines[k]; - k++; - } - - if (con->sel_active) { - if (con->sel_start.line == line || - (!con->sel_start.line && - con->sel_start.y == k - 1)) - sel_start = true; - else - sel_start = false; - if (con->sel_end.line == line || - (!con->sel_end.line && - con->sel_end.y == k - 1)) - sel_end = true; - else - sel_end = false; - - was_sel = false; - } - - for (j = 0; j < con->size_x; ++j) { - if (j < line->size) - cell = &line->cells[j]; - else - cell = ∅ - memcpy(&attr, &cell->attr, sizeof(attr)); - - if (con->sel_active) { - if (sel_start && - j == con->sel_start.x) { - was_sel = in_sel; - in_sel = !in_sel; - } - if (sel_end && - j == con->sel_end.x) { - was_sel = in_sel; - in_sel = !in_sel; - } - } - - if (k == cur_y + 1 && - j == cur_x) { - cursor_done = true; - if (!(con->flags & TSM_SCREEN_HIDE_CURSOR)) - attr.inverse = !attr.inverse; - } - - /* TODO: do some more sophisticated inverse here. When - * INVERSE mode is set, we should instead just select - * inverse colors instead of switching background and - * foreground */ - if (con->flags & TSM_SCREEN_INVERSE) - attr.inverse = !attr.inverse; - - if (in_sel || was_sel) { - was_sel = false; - attr.inverse = !attr.inverse; - } - - ch = tsm_symbol_get(NULL, &cell->ch, &len); - if (cell->ch == ' ' || cell->ch == 0) - len = 0; - ret = draw_cb(con, cell->ch, ch, len, cell->width, - j, i, &attr, data); - if (ret && warned++ < 3) { - llog_debug(con, - "cannot draw glyph at %ux%u via text-renderer", - j, i); - if (warned == 3) - llog_debug(con, - "suppressing further warnings during this rendering round"); - } - } - - if (k == cur_y + 1 && !cursor_done) { - cursor_done = true; - if (!(con->flags & TSM_SCREEN_HIDE_CURSOR)) { - if (!(con->flags & TSM_SCREEN_INVERSE)) - attr.inverse = !attr.inverse; - draw_cb(con, 0, NULL, 0, 1, - cur_x, i, &attr, data); - } - } - } - - if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) - time_draw = shl_timer_elapsed(con->timer); - - /* perform final rendering steps */ - - if (render_cb) { - if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) - shl_timer_reset(con->timer); - - ret = render_cb(con, data); - if (ret) - llog_warning(con, - "cannot render via text-renderer"); - - if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) - time_rend = shl_timer_elapsed(con->timer); - } else { - time_rend = 0; - } - - if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) - llog_debug(con, - "timing: sum: %" PRIu64 " prepare: %" PRIu64 " draw: %" PRIu64 " render: %" PRIu64, - time_prep + time_draw + time_rend, - time_prep, time_draw, time_rend); -} diff --git a/src/tsm_screen.h b/src/tsm_screen.h deleted file mode 100644 index b257cf7..0000000 --- a/src/tsm_screen.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * TSM - Screen Management - * - * Copyright (c) 2011-2012 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. - */ - -/* - * Screen Management - * This screen does not emulate any terminal at all. This subsystem just - * provides functions to draw a screen to a framebuffer and modifying the state - * of it. - */ - -#ifndef TSM_SCREEN_H -#define TSM_SCREEN_H - -#include -#include -#include -#include -#include "tsm_unicode.h" - -/* screen objects */ - -struct tsm_screen; - -/** - * tsm_log_t: - * @data: user-provided data - * @file: Source code file where the log message originated or NULL - * @line: Line number in source code or 0 - * @func: C function name or NULL - * @subs: Subsystem where the message came from or NULL - * @sev: Kernel-style severity between 0=FATAL and 7=DEBUG - * @format: printf-formatted message - * @args: arguments for printf-style @format - * - * This is the type of a logging callback function. You can always pass NULL - * instead of such a function to disable logging. - */ -typedef void (*tsm_log_t) (void *data, - const char *file, - int line, - const char *func, - const char *subs, - unsigned int sev, - const char *format, - va_list args); - -#define TSM_SCREEN_INSERT_MODE 0x01 -#define TSM_SCREEN_AUTO_WRAP 0x02 -#define TSM_SCREEN_REL_ORIGIN 0x04 -#define TSM_SCREEN_INVERSE 0x08 -#define TSM_SCREEN_HIDE_CURSOR 0x10 -#define TSM_SCREEN_FIXED_POS 0x20 -#define TSM_SCREEN_ALTERNATE 0x40 - -#define TSM_SCREEN_OPT_RENDER_TIMING 0x01 - -struct tsm_screen_attr { - int8_t fccode; /* foreground color code or <0 for rgb */ - int8_t bccode; /* background color code or <0 for rgb */ - uint8_t fr; /* foreground red */ - uint8_t fg; /* foreground green */ - uint8_t fb; /* foreground blue */ - uint8_t br; /* background red */ - uint8_t bg; /* background green */ - uint8_t bb; /* background blue */ - unsigned int bold : 1; /* bold character */ - unsigned int underline : 1; /* underlined character */ - unsigned int inverse : 1; /* inverse colors */ - unsigned int protect : 1; /* cannot be erased */ -}; - -typedef int (*tsm_screen_prepare_cb) (struct tsm_screen *con, - void *data); -typedef int (*tsm_screen_draw_cb) (struct tsm_screen *con, - uint32_t id, - const uint32_t *ch, - size_t len, - unsigned int width, - unsigned int posx, - unsigned int posy, - const struct tsm_screen_attr *attr, - void *data); -typedef int (*tsm_screen_render_cb) (struct tsm_screen *con, - void *data); - -int tsm_screen_new(struct tsm_screen **out, tsm_log_t log, void *log_data); -void tsm_screen_ref(struct tsm_screen *con); -void tsm_screen_unref(struct tsm_screen *con); - -void tsm_screen_set_opts(struct tsm_screen *scr, unsigned int opts); -void tsm_screen_reset_opts(struct tsm_screen *scr, unsigned int opts); -unsigned int tsm_screen_get_opts(struct tsm_screen *scr); - -unsigned int tsm_screen_get_width(struct tsm_screen *con); -unsigned int tsm_screen_get_height(struct tsm_screen *con); -int tsm_screen_resize(struct tsm_screen *con, unsigned int x, - unsigned int y); -int tsm_screen_set_margins(struct tsm_screen *con, - unsigned int top, unsigned int bottom); -void tsm_screen_set_max_sb(struct tsm_screen *con, unsigned int max); -void tsm_screen_clear_sb(struct tsm_screen *con); - -void tsm_screen_sb_up(struct tsm_screen *con, unsigned int num); -void tsm_screen_sb_down(struct tsm_screen *con, unsigned int num); -void tsm_screen_sb_page_up(struct tsm_screen *con, unsigned int num); -void tsm_screen_sb_page_down(struct tsm_screen *con, unsigned int num); -void tsm_screen_sb_reset(struct tsm_screen *con); - -void tsm_screen_set_def_attr(struct tsm_screen *con, - const struct tsm_screen_attr *attr); -void tsm_screen_reset(struct tsm_screen *con); -void tsm_screen_set_flags(struct tsm_screen *con, unsigned int flags); -void tsm_screen_reset_flags(struct tsm_screen *con, unsigned int flags); -unsigned int tsm_screen_get_flags(struct tsm_screen *con); - -unsigned int tsm_screen_get_cursor_x(struct tsm_screen *con); -unsigned int tsm_screen_get_cursor_y(struct tsm_screen *con); - -void tsm_screen_set_tabstop(struct tsm_screen *con); -void tsm_screen_reset_tabstop(struct tsm_screen *con); -void tsm_screen_reset_all_tabstops(struct tsm_screen *con); - -void tsm_screen_write(struct tsm_screen *con, tsm_symbol_t ch, - const struct tsm_screen_attr *attr); -void tsm_screen_newline(struct tsm_screen *con); -void tsm_screen_scroll_up(struct tsm_screen *con, unsigned int num); -void tsm_screen_scroll_down(struct tsm_screen *con, unsigned int num); -void tsm_screen_move_to(struct tsm_screen *con, unsigned int x, - unsigned int y); -void tsm_screen_move_up(struct tsm_screen *con, unsigned int num, - bool scroll); -void tsm_screen_move_down(struct tsm_screen *con, unsigned int num, - bool scroll); -void tsm_screen_move_left(struct tsm_screen *con, unsigned int num); -void tsm_screen_move_right(struct tsm_screen *con, unsigned int num); -void tsm_screen_move_line_end(struct tsm_screen *con); -void tsm_screen_move_line_home(struct tsm_screen *con); -void tsm_screen_tab_right(struct tsm_screen *con, unsigned int num); -void tsm_screen_tab_left(struct tsm_screen *con, unsigned int num); -void tsm_screen_insert_lines(struct tsm_screen *con, unsigned int num); -void tsm_screen_delete_lines(struct tsm_screen *con, unsigned int num); -void tsm_screen_insert_chars(struct tsm_screen *con, unsigned int num); -void tsm_screen_delete_chars(struct tsm_screen *con, unsigned int num); -void tsm_screen_erase_cursor(struct tsm_screen *con); -void tsm_screen_erase_chars(struct tsm_screen *con, unsigned int num); -void tsm_screen_erase_cursor_to_end(struct tsm_screen *con, - bool protect); -void tsm_screen_erase_home_to_cursor(struct tsm_screen *con, - bool protect); -void tsm_screen_erase_current_line(struct tsm_screen *con, - bool protect); -void tsm_screen_erase_screen_to_cursor(struct tsm_screen *con, - bool protect); -void tsm_screen_erase_cursor_to_screen(struct tsm_screen *con, - bool protect); -void tsm_screen_erase_screen(struct tsm_screen *con, bool protect); - -void tsm_screen_selection_reset(struct tsm_screen *con); -void tsm_screen_selection_start(struct tsm_screen *con, - unsigned int posx, - unsigned int posy); -void tsm_screen_selection_target(struct tsm_screen *con, - unsigned int posx, - unsigned int posy); -int tsm_screen_selection_copy(struct tsm_screen *con, char **out); - -void tsm_screen_draw(struct tsm_screen *con, - tsm_screen_prepare_cb prepare_cb, - tsm_screen_draw_cb draw_cb, - tsm_screen_render_cb render_cb, - void *data); - -#endif /* TSM_SCREEN_H */ diff --git a/src/tsm_unicode.c b/src/tsm_unicode.c deleted file mode 100644 index b53878a..0000000 --- a/src/tsm_unicode.c +++ /dev/null @@ -1,624 +0,0 @@ -/* - * TSM - Unicode Handling - * - * Copyright (c) 2011 David Herrmann - * Copyright (c) 2011-2012 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. - */ - -/* - * The tsm-utf8-state-machine is based on the wayland-compositor demos: - * - * Copyright © 2008 Kristian Høgsberg - * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * Unicode Helpers - * This implements several helpers for Unicode/UTF8/UCS4 input and output. See - * below for comments on each helper. - */ - -#include -#include -#include -#include -#include "external/wcwidth.h" -#include "shl_array.h" -#include "shl_hashtable.h" -#include "shl_misc.h" -#include "tsm_unicode.h" - -/* - * Unicode Symbol Handling - * The main goal of the tsm_symbol_* functions is to provide a datatype which - * can contain the representation of any printable character. This includes all - * basic Unicode characters but also combined characters. - * To avoid all the memory management we still represent a character as a single - * integer value (tsm_symbol_t) but internally we allocate a string which is - * represented by this value. - * - * A tsm_symbol_t is an integer which represents a single character point. - * For most Unicode characters this is simply the UCS4 representation. In fact, - * every UCS4 characters is a valid tsm_symbol_t object. - * However, Unicode standard allows combining marks. Therefore, some characters - * consists of more than one Unicode character. - * A global symbol-table provides all those combined characters as single - * integers. You simply create a valid base character and append your combining - * marks and the table will return a new valid tsm_symbol_t. It is no longer - * a valid UCS4 value, though. But no memory management is needed as all - * tsm_symbol_t objects are simple integers. - * - * The symbol table contains two-way - * references. The Hash Table contains all the symbols with the symbol ucs4 - * string as key and the symbol ID as value. - * The index array contains the symbol ID as key and a pointer to the ucs4 - * string as value. But the hash table owns the ucs4 string. - * This allows fast implementations of *_get() and *_append() without long - * search intervals. - * - * When creating a new symbol, we simply return the UCS4 value as new symbol. We - * do not add it to our symbol table as it is only one character. However, if a - * character is appended to an existing symbol, we create a new ucs4 string and - * push the new symbol into the symbol table. - */ - -SHL_EXPORT -const tsm_symbol_t tsm_symbol_default = 0; - -struct tsm_symbol_table { - unsigned long ref; - uint32_t next_id; - struct shl_array *index; - struct shl_hashtable *symbols; -}; - -/* TODO: remove the default context */ -static struct tsm_symbol_table *tsm_symbol_table_default; - -static unsigned int hash_ucs4(const void *key) -{ - unsigned int val = 5381; - size_t i; - const uint32_t *ucs4 = key; - - i = 0; - while (ucs4[i] <= TSM_UCS4_MAX) { - val = val * 33 + ucs4[i]; - ++i; - } - - return val; -} - -static bool cmp_ucs4(const void *a, const void *b) -{ - size_t i; - const uint32_t *v1, *v2; - - v1 = a; - v2 = b; - i = 0; - - while (1) { - if (v1[i] > TSM_UCS4_MAX && v2[i] > TSM_UCS4_MAX) - return true; - if (v1[i] > TSM_UCS4_MAX && v2[i] <= TSM_UCS4_MAX) - return false; - if (v1[i] <= TSM_UCS4_MAX && v2[i] > TSM_UCS4_MAX) - return false; - if (v1[i] != v2[i]) - return false; - - ++i; - } -} - -SHL_EXPORT -int tsm_symbol_table_new(struct tsm_symbol_table **out) -{ - struct tsm_symbol_table *tbl; - int ret; - static const uint32_t *val = NULL; /* we need a valid lvalue */ - - if (!out) - return -EINVAL; - - tbl = malloc(sizeof(*tbl)); - if (!tbl) - return -ENOMEM; - memset(tbl, 0, sizeof(*tbl)); - tbl->ref = 1; - tbl->next_id = TSM_UCS4_MAX + 2; - - ret = shl_array_new(&tbl->index, sizeof(uint32_t*), 4); - if (ret) - goto err_free; - - /* first entry is not used so add dummy */ - shl_array_push(tbl->index, &val); - - ret = shl_hashtable_new(&tbl->symbols, hash_ucs4, cmp_ucs4, - free, NULL); - if (ret) - goto err_array; - - *out = tbl; - return 0; - -err_array: - shl_array_free(tbl->index); -err_free: - free(tbl); - return ret; -} - -SHL_EXPORT -void tsm_symbol_table_ref(struct tsm_symbol_table *tbl) -{ - if (!tbl || !tbl->ref) - return; - - ++tbl->ref; -} - -SHL_EXPORT -void tsm_symbol_table_unref(struct tsm_symbol_table *tbl) -{ - if (!tbl || !tbl->ref || --tbl->ref) - return; - - shl_hashtable_free(tbl->symbols); - shl_array_free(tbl->index); - free(tbl); -} - -SHL_EXPORT -tsm_symbol_t tsm_symbol_make(uint32_t ucs4) -{ - if (ucs4 > TSM_UCS4_MAX) - return 0; - else - return ucs4; -} - -/* - * This decomposes a symbol into a ucs4 string and a size value. If \sym is a - * valid UCS4 character, this returns a pointer to \sym and writes 1 into \size. - * Therefore, the returned value may get destroyed if your \sym argument gets - * destroyed. - * If \sym is a composed ucs4 string, then the returned value points into the - * hash table of the symbol table and lives as long as the symbol table does. - * - * This always returns a valid value. If an error happens, the default character - * is returned. If \size is NULL, then the size value is omitted. - */ -SHL_EXPORT -const uint32_t *tsm_symbol_get(struct tsm_symbol_table *tbl, - tsm_symbol_t *sym, size_t *size) -{ - uint32_t *ucs4, idx; - int ret; - - if (*sym <= TSM_UCS4_MAX) { - if (size) - *size = 1; - return sym; - } - - if (!tbl) - tbl = tsm_symbol_table_default; - - if (!tbl) { - ret = tsm_symbol_table_new(&tbl); - if (ret) { - if (size) - *size = 1; - return &tsm_symbol_default; - } - tsm_symbol_table_default = tbl; - } - - idx = *sym - (TSM_UCS4_MAX + 1); - if (idx >= shl_array_get_length(tbl->index)) - ucs4 = NULL; - else - ucs4 = *SHL_ARRAY_AT(tbl->index, uint32_t*, idx); - - if (!ucs4) { - if (size) - *size = 1; - return &tsm_symbol_default; - } - - if (size) { - *size = 0; - while (ucs4[*size] <= TSM_UCS4_MAX) - ++*size; - } - - return ucs4; -} - -SHL_EXPORT -tsm_symbol_t tsm_symbol_append(struct tsm_symbol_table *tbl, - tsm_symbol_t sym, uint32_t ucs4) -{ - uint32_t buf[TSM_UCS4_MAXLEN + 1], nsym, *nval; - const uint32_t *ptr; - size_t s; - void *tmp; - bool res; - int ret; - - if (!tbl) - tbl = tsm_symbol_table_default; - - if (!tbl) { - ret = tsm_symbol_table_new(&tbl); - if (ret) - return sym; - tsm_symbol_table_default = tbl; - } - - if (ucs4 > TSM_UCS4_MAX) - return sym; - - ptr = tsm_symbol_get(tbl, &sym, &s); - if (s >= TSM_UCS4_MAXLEN) - return sym; - - memcpy(buf, ptr, s * sizeof(uint32_t)); - buf[s++] = ucs4; - buf[s++] = TSM_UCS4_MAX + 1; - - res = shl_hashtable_find(tbl->symbols, &tmp, buf); - if (res) - return (uint32_t)(long)tmp; - - nval = malloc(sizeof(uint32_t) * s); - if (!nval) - return sym; - - memcpy(nval, buf, s * sizeof(uint32_t)); - nsym = tbl->next_id + 1; - /* Out of IDs; we actually have 2 Billion IDs so this seems - * very unlikely but lets be safe here */ - if (nsym <= tbl->next_id++) - goto err_id; - - ret = shl_hashtable_insert(tbl->symbols, nval, (void*)(long)nsym); - if (ret) - goto err_id; - - ret = shl_array_push(tbl->index, &nval); - if (ret) - goto err_symbol; - - return nsym; - -err_symbol: - shl_hashtable_remove(tbl->symbols, nval); -err_id: - --tbl->next_id; - free(nval); - return sym; -} - -SHL_EXPORT -unsigned int tsm_symbol_get_width(struct tsm_symbol_table *tbl, - tsm_symbol_t sym) -{ - int ret; - const uint32_t *ch; - size_t len; - - if (!tbl) - tbl = tsm_symbol_table_default; - - if (!tbl) { - ret = tsm_symbol_table_new(&tbl); - if (ret) - return sym; - tsm_symbol_table_default = tbl; - } - - ch = tsm_symbol_get(tbl, &sym, &len); - if (len == 0) - return 0; - - return tsm_ucs4_get_width(*ch); -} - -/* - * Convert UCS4 character to UTF-8. This creates one of: - * 0xxxxxxx - * 110xxxxx 10xxxxxx - * 1110xxxx 10xxxxxx 10xxxxxx - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * This is based on the same function from "terminology" from the Enlightenment - * project. See COPYING for more information. - * - * @txt must point to a 4 byte-buffer. A number between 0 and 4 is returned and - * indicates how long the written UTF8 string is. - * - * Please note @g is a real UCS4 code and not a tsm_symbol_t object! - * - * Unicode symbols between 0xD800 and 0xDFFF are not assigned and reserved for - * UTF16 compatibility. It is an error to encode them. Same applies to numbers - * greater than 0x10FFFF, the range 0xFDD0-0xFDEF and codepoints ending with - * 0xFFFF or 0xFFFE. - */ - -SHL_EXPORT -unsigned int tsm_ucs4_get_width(uint32_t ucs4) -{ - int ret; - - ret = mk_wcwidth(ucs4); - if (ret <= 0) - return 0; - - return ret; -} - -SHL_EXPORT -size_t tsm_ucs4_to_utf8(uint32_t g, char *txt) -{ - if (g >= 0xd800 && g <= 0xdfff) - return 0; - if (g > 0x10ffff || (g & 0xffff) == 0xffff || (g & 0xffff) == 0xfffe) - return 0; - if (g >= 0xfdd0 && g <= 0xfdef) - return 0; - - if (g < (1 << 7)) { - txt[0] = g & 0x7f; - return 1; - } else if (g < (1 << (5 + 6))) { - txt[0] = 0xc0 | ((g >> 6) & 0x1f); - txt[1] = 0x80 | ((g ) & 0x3f); - return 2; - } else if (g < (1 << (4 + 6 + 6))) { - txt[0] = 0xe0 | ((g >> 12) & 0x0f); - txt[1] = 0x80 | ((g >> 6) & 0x3f); - txt[2] = 0x80 | ((g ) & 0x3f); - return 3; - } else if (g < (1 << (3 + 6 + 6 + 6))) { - txt[0] = 0xf0 | ((g >> 18) & 0x07); - txt[1] = 0x80 | ((g >> 12) & 0x3f); - txt[2] = 0x80 | ((g >> 6) & 0x3f); - txt[3] = 0x80 | ((g ) & 0x3f); - return 4; - } else { - return 0; - } -} - -SHL_EXPORT -char *tsm_ucs4_to_utf8_alloc(const uint32_t *ucs4, size_t len, size_t *len_out) -{ - char *val; - size_t i, pos; - - val = malloc(4 * len); - if (!val) - return NULL; - - pos = 0; - for (i = 0; i < len; ++i) - pos += tsm_ucs4_to_utf8(ucs4[i], &val[pos]); - - if (!pos) { - free(val); - return NULL; - } - - if (len_out) - *len_out = pos; - return val; -} - -/* - * UTF8 State Machine - * This state machine parses UTF8 and converts it into a stream of Unicode - * characters (UCS4 values). A state-machine is represented by a - * "struct tsm_utf8_mach" object. It has no global state and all functions are - * re-entrant if called with different state-machine objects. - * - * tsm_utf8_mach_new(): This creates a new state-machine and resets it to its - * initial state. Returns 0 on success. - * - * tsm_uft8_mach_free(): This destroys a state-machine and frees all internally - * allocated memory. - * - * tsm_utf8_mach_reset(): Reset a given state-machine to its initial state. This - * is the same state the machine is in after it got created. - * - * tsm_uft8_mach_feed(): Feed one byte of the UTF8 input stream into the - * state-machine. This function returns the new state of the state-machine after - * this character has been parsed. If it is TSM_UTF8_ACCEPT or TSM_UTF8_REJECT, - * then there is a pending UCS4 character that you should retrieve via - * tsm_utf8_mach_get(). If it is TSM_UTF8_ACCEPT, then a character was - * successfully parsed. If it is TSM_UTF8_REJECT, the input was invalid UTF8 and - * some error recovery was tried or a replacement character was choosen. All - * other states mean that the machine needs more input to parse the stream. - * - * tsm_utf8_mach_get(): Returns the last parsed character. It has no effect on - * the state machine so you can call it multiple times. - * - * Internally, we use TSM_UTF8_START whenever the state-machine is reset. This - * can be used to ignore the last read input or to simply reset the machine. - * TSM_UTF8_EXPECT* is used to remember how many bytes are still to be read to - * get a full UTF8 sequence. - * If an error occurs during reading, we go to state TSM_UTF8_REJECT and the - * user will read a replacement character. If further errors occur, we go to - * state TSM_UTF8_START to avoid printing multiple replacement characters for a - * single misinterpreted UTF8 sequence. However, under some circumstances it may - * happen that we stay in TSM_UTF8_REJECT and a next replacement character is - * returned. - * It is difficult to decide how to interpret wrong input but this machine seems - * to be quite good at deciding what to do. Generally, we prefer discarding or - * replacing input instead of trying to decipher ASCII values from the invalid - * data. This guarantees that we do not send wrong values to the terminal - * emulator. Some might argue that an ASCII fallback would be better. However, - * this means that we might send very weird escape-sequences to the VTE layer. - * Especially with C1 codes applications can really break many terminal features - * so we avoid any non-ASCII+non-UTF8 input to prevent this. - */ - -struct tsm_utf8_mach { - int state; - uint32_t ch; -}; - -SHL_EXPORT -int tsm_utf8_mach_new(struct tsm_utf8_mach **out) -{ - struct tsm_utf8_mach *mach; - - if (!out) - return -EINVAL; - - mach = malloc(sizeof(*mach)); - if (!mach) - return -ENOMEM; - - memset(mach, 0, sizeof(*mach)); - mach->state = TSM_UTF8_START; - - *out = mach; - return 0; -} - -SHL_EXPORT -void tsm_utf8_mach_free(struct tsm_utf8_mach *mach) -{ - if (!mach) - return; - - free(mach); -} - -SHL_EXPORT -int tsm_utf8_mach_feed(struct tsm_utf8_mach *mach, char ci) -{ - uint32_t c; - - if (!mach) - return TSM_UTF8_START; - - c = ci; - - switch (mach->state) { - case TSM_UTF8_START: - case TSM_UTF8_ACCEPT: - case TSM_UTF8_REJECT: - if (c == 0xC0 || c == 0xC1) { - /* overlong encoding for ASCII, reject */ - mach->state = TSM_UTF8_REJECT; - } else if ((c & 0x80) == 0) { - /* single byte, accept */ - mach->ch = c; - mach->state = TSM_UTF8_ACCEPT; - } else if ((c & 0xC0) == 0x80) { - /* parser out of sync, ignore byte */ - mach->state = TSM_UTF8_START; - } else if ((c & 0xE0) == 0xC0) { - /* start of two byte sequence */ - mach->ch = (c & 0x1F) << 6; - mach->state = TSM_UTF8_EXPECT1; - } else if ((c & 0xF0) == 0xE0) { - /* start of three byte sequence */ - mach->ch = (c & 0x0F) << 12; - mach->state = TSM_UTF8_EXPECT2; - } else if ((c & 0xF8) == 0xF0) { - /* start of four byte sequence */ - mach->ch = (c & 0x07) << 18; - mach->state = TSM_UTF8_EXPECT3; - } else { - /* overlong encoding, reject */ - mach->state = TSM_UTF8_REJECT; - } - break; - case TSM_UTF8_EXPECT3: - mach->ch |= (c & 0x3F) << 12; - if ((c & 0xC0) == 0x80) - mach->state = TSM_UTF8_EXPECT2; - else - mach->state = TSM_UTF8_REJECT; - break; - case TSM_UTF8_EXPECT2: - mach->ch |= (c & 0x3F) << 6; - if ((c & 0xC0) == 0x80) - mach->state = TSM_UTF8_EXPECT1; - else - mach->state = TSM_UTF8_REJECT; - break; - case TSM_UTF8_EXPECT1: - mach->ch |= c & 0x3F; - if ((c & 0xC0) == 0x80) - mach->state = TSM_UTF8_ACCEPT; - else - mach->state = TSM_UTF8_REJECT; - break; - default: - mach->state = TSM_UTF8_REJECT; - break; - } - - return mach->state; -} - -SHL_EXPORT -uint32_t tsm_utf8_mach_get(struct tsm_utf8_mach *mach) -{ - if (!mach || mach->state != TSM_UTF8_ACCEPT) - return TSM_UCS4_REPLACEMENT; - - return mach->ch; -} - -SHL_EXPORT -void tsm_utf8_mach_reset(struct tsm_utf8_mach *mach) -{ - if (!mach) - return; - - mach->state = TSM_UTF8_START; -} diff --git a/src/tsm_unicode.h b/src/tsm_unicode.h deleted file mode 100644 index 57b7582..0000000 --- a/src/tsm_unicode.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * TSM - Unicode Handling - * - * Copyright (c) 2011-2012 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. - */ - -/* - * Unicode Helpers - * This file provides small helpers to make working with Unicode/UTF8/UCS4 input - * and output much easier. - */ - -#ifndef TSM_UNICODE_H -#define TSM_UNICODE_H - -#include -#include - -/* UCS4 helpers */ - -#define TSM_UCS4_MAX (0x7fffffffUL) -#define TSM_UCS4_INVALID (TSM_UCS4_MAX + 1) -#define TSM_UCS4_REPLACEMENT (0xfffdUL) -#define TSM_UCS4_MAXLEN 10 - -/* symbols */ - -struct tsm_symbol_table; -typedef uint32_t tsm_symbol_t; - -extern const tsm_symbol_t tsm_symbol_default; - -int tsm_symbol_table_new(struct tsm_symbol_table **out); -void tsm_symbol_table_ref(struct tsm_symbol_table *tbl); -void tsm_symbol_table_unref(struct tsm_symbol_table *tbl); - -tsm_symbol_t tsm_symbol_make(uint32_t ucs4); -tsm_symbol_t tsm_symbol_append(struct tsm_symbol_table *tbl, - tsm_symbol_t sym, uint32_t ucs4); -const uint32_t *tsm_symbol_get(struct tsm_symbol_table *tbl, - tsm_symbol_t *sym, size_t *size); -unsigned int tsm_symbol_get_width(struct tsm_symbol_table *tbl, - tsm_symbol_t sym); - -/* ucs4 to utf8 converter */ - -unsigned int tsm_ucs4_get_width(uint32_t ucs4); -size_t tsm_ucs4_to_utf8(uint32_t ucs4, char *out); -char *tsm_ucs4_to_utf8_alloc(const uint32_t *ucs4, size_t len, size_t *len_out); - -/* utf8 state machine */ - -struct tsm_utf8_mach; - -enum tsm_utf8_mach_state { - TSM_UTF8_START, - TSM_UTF8_ACCEPT, - TSM_UTF8_REJECT, - TSM_UTF8_EXPECT1, - TSM_UTF8_EXPECT2, - TSM_UTF8_EXPECT3, -}; - -int tsm_utf8_mach_new(struct tsm_utf8_mach **out); -void tsm_utf8_mach_free(struct tsm_utf8_mach *mach); - -int tsm_utf8_mach_feed(struct tsm_utf8_mach *mach, char c); -uint32_t tsm_utf8_mach_get(struct tsm_utf8_mach *mach); -void tsm_utf8_mach_reset(struct tsm_utf8_mach *mach); - -#endif /* TSM_UNICODE_H */ diff --git a/src/tsm_vte.c b/src/tsm_vte.c deleted file mode 100644 index 18e545d..0000000 --- a/src/tsm_vte.c +++ /dev/null @@ -1,2784 +0,0 @@ -/* - * TSM - 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 the VT implementation. It is written from scratch. It uses the - * screen state-machine as output and is tightly bound to it. It supports - * functionality from vt100 up to vt500 series. It doesn't implement an - * explicitly selected terminal but tries to support the most important commands - * to be compatible with existing implementations. However, full vt102 - * compatibility is the least that is provided. - * - * The main parser in this file controls the parser-state and dispatches the - * actions to the related handlers. The parser is based on the state-diagram - * from Paul Williams: http://vt100.net/emu/ - * It is written from scratch, though. - * This parser is fully compatible up to the vt500 series. It requires UTF-8 and - * does not support any other input encoding. The G0 and G1 sets are therefore - * defined as subsets of UTF-8. You may still map G0-G3 into GL, though. - * - * However, the CSI/DCS/etc handlers are not designed after a specific VT - * series. We try to support all vt102 commands but implement several other - * often used sequences, too. Feel free to add further. - * - * See ./doc/vte.txt for more information on this VT-emulator. - */ - -#include -#include -#include -#include -#include -#include "shl_llog.h" -#include "shl_misc.h" -#include "tsm_screen.h" -#include "tsm_unicode.h" -#include "tsm_vte.h" - -#define LLOG_SUBSYSTEM "tsm_vte" - -/* Input parser states */ -enum parser_state { - STATE_NONE, /* placeholder */ - STATE_GROUND, /* initial state and ground */ - STATE_ESC, /* ESC sequence was started */ - STATE_ESC_INT, /* intermediate escape characters */ - STATE_CSI_ENTRY, /* starting CSI sequence */ - STATE_CSI_PARAM, /* CSI parameters */ - STATE_CSI_INT, /* intermediate CSI characters */ - STATE_CSI_IGNORE, /* CSI error; ignore this CSI sequence */ - STATE_DCS_ENTRY, /* starting DCS sequence */ - STATE_DCS_PARAM, /* DCS parameters */ - STATE_DCS_INT, /* intermediate DCS characters */ - STATE_DCS_PASS, /* DCS data passthrough */ - STATE_DCS_IGNORE, /* DCS error; ignore this DCS sequence */ - STATE_OSC_STRING, /* parsing OCS sequence */ - STATE_ST_IGNORE, /* unimplemented seq; ignore until ST */ - STATE_NUM -}; - -/* Input parser actions */ -enum parser_action { - ACTION_NONE, /* placeholder */ - ACTION_IGNORE, /* ignore the character entirely */ - ACTION_PRINT, /* print the character on the console */ - ACTION_EXECUTE, /* execute single control character (C0/C1) */ - ACTION_CLEAR, /* clear current parameter state */ - ACTION_COLLECT, /* collect intermediate character */ - ACTION_PARAM, /* collect parameter character */ - ACTION_ESC_DISPATCH, /* dispatch escape sequence */ - ACTION_CSI_DISPATCH, /* dispatch csi sequence */ - ACTION_DCS_START, /* start of DCS data */ - ACTION_DCS_COLLECT, /* collect DCS data */ - ACTION_DCS_END, /* end of DCS data */ - ACTION_OSC_START, /* start of OSC data */ - ACTION_OSC_COLLECT, /* collect OSC data */ - ACTION_OSC_END, /* end of OSC data */ - ACTION_NUM -}; - -/* CSI flags */ -#define CSI_BANG 0x0001 /* CSI: ! */ -#define CSI_CASH 0x0002 /* CSI: $ */ -#define CSI_WHAT 0x0004 /* CSI: ? */ -#define CSI_GT 0x0008 /* CSI: > */ -#define CSI_SPACE 0x0010 /* CSI: */ -#define CSI_SQUOTE 0x0020 /* CSI: ' */ -#define CSI_DQUOTE 0x0040 /* CSI: " */ -#define CSI_MULT 0x0080 /* CSI: * */ -#define CSI_PLUS 0x0100 /* CSI: + */ -#define CSI_POPEN 0x0200 /* CSI: ( */ -#define CSI_PCLOSE 0x0400 /* CSI: ) */ - -/* max CSI arguments */ -#define CSI_ARG_MAX 16 - -/* terminal flags */ -#define FLAG_CURSOR_KEY_MODE 0x00000001 /* DEC cursor key mode */ -#define FLAG_KEYPAD_APPLICATION_MODE 0x00000002 /* DEC keypad application mode; TODO: toggle on numlock? */ -#define FLAG_LINE_FEED_NEW_LINE_MODE 0x00000004 /* DEC line-feed/new-line mode */ -#define FLAG_8BIT_MODE 0x00000008 /* Disable UTF-8 mode and enable 8bit compatible mode */ -#define FLAG_7BIT_MODE 0x00000010 /* Disable 8bit mode and use 7bit compatible mode */ -#define FLAG_USE_C1 0x00000020 /* Explicitly use 8bit C1 codes; TODO: implement */ -#define FLAG_KEYBOARD_ACTION_MODE 0x00000040 /* Disable keyboard; TODO: implement? */ -#define FLAG_INSERT_REPLACE_MODE 0x00000080 /* Enable insert mode */ -#define FLAG_SEND_RECEIVE_MODE 0x00000100 /* Disable local echo */ -#define FLAG_TEXT_CURSOR_MODE 0x00000200 /* Show cursor */ -#define FLAG_INVERSE_SCREEN_MODE 0x00000400 /* Inverse colors */ -#define FLAG_ORIGIN_MODE 0x00000800 /* Relative origin for cursor */ -#define FLAG_AUTO_WRAP_MODE 0x00001000 /* Auto line wrap mode */ -#define FLAG_AUTO_REPEAT_MODE 0x00002000 /* Auto repeat key press; TODO: implement */ -#define FLAG_NATIONAL_CHARSET_MODE 0x00004000 /* Send keys from nation charsets; TODO: implement */ -#define FLAG_BACKGROUND_COLOR_ERASE_MODE 0x00008000 /* Set background color on erase (bce) */ -#define FLAG_PREPEND_ESCAPE 0x00010000 /* Prepend escape character to next output */ -#define FLAG_TITE_INHIBIT_MODE 0x00020000 /* Prevent switching to alternate screen buffer */ - -struct vte_saved_state { - unsigned int cursor_x; - unsigned int cursor_y; - struct tsm_screen_attr cattr; - tsm_vte_charset **gl; - tsm_vte_charset **gr; - bool wrap_mode; - bool origin_mode; -}; - -struct tsm_vte { - unsigned long ref; - tsm_log_t llog; - void *llog_data; - struct tsm_screen *con; - tsm_vte_write_cb write_cb; - void *data; - char *palette_name; - - struct tsm_utf8_mach *mach; - unsigned long parse_cnt; - - unsigned int state; - unsigned int csi_argc; - int csi_argv[CSI_ARG_MAX]; - unsigned int csi_flags; - - uint8_t (*palette)[3]; - struct tsm_screen_attr def_attr; - struct tsm_screen_attr cattr; - unsigned int flags; - - tsm_vte_charset **gl; - tsm_vte_charset **gr; - tsm_vte_charset **glt; - tsm_vte_charset **grt; - tsm_vte_charset *g0; - tsm_vte_charset *g1; - tsm_vte_charset *g2; - tsm_vte_charset *g3; - - struct vte_saved_state saved_state; - unsigned int alt_cursor_x; - unsigned int alt_cursor_y; -}; - -enum vte_color { - COLOR_BLACK, - COLOR_RED, - COLOR_GREEN, - COLOR_YELLOW, - COLOR_BLUE, - COLOR_MAGENTA, - COLOR_CYAN, - COLOR_LIGHT_GREY, - COLOR_DARK_GREY, - COLOR_LIGHT_RED, - COLOR_LIGHT_GREEN, - COLOR_LIGHT_YELLOW, - COLOR_LIGHT_BLUE, - COLOR_LIGHT_MAGENTA, - COLOR_LIGHT_CYAN, - COLOR_WHITE, - COLOR_FOREGROUND, - COLOR_BACKGROUND, - COLOR_NUM -}; - -static uint8_t color_palette[COLOR_NUM][3] = { - [COLOR_BLACK] = { 0, 0, 0 }, /* black */ - [COLOR_RED] = { 205, 0, 0 }, /* red */ - [COLOR_GREEN] = { 0, 205, 0 }, /* green */ - [COLOR_YELLOW] = { 205, 205, 0 }, /* yellow */ - [COLOR_BLUE] = { 0, 0, 238 }, /* blue */ - [COLOR_MAGENTA] = { 205, 0, 205 }, /* magenta */ - [COLOR_CYAN] = { 0, 205, 205 }, /* cyan */ - [COLOR_LIGHT_GREY] = { 229, 229, 229 }, /* light grey */ - [COLOR_DARK_GREY] = { 127, 127, 127 }, /* dark grey */ - [COLOR_LIGHT_RED] = { 255, 0, 0 }, /* light red */ - [COLOR_LIGHT_GREEN] = { 0, 255, 0 }, /* light green */ - [COLOR_LIGHT_YELLOW] = { 255, 255, 0 }, /* light yellow */ - [COLOR_LIGHT_BLUE] = { 92, 92, 255 }, /* light blue */ - [COLOR_LIGHT_MAGENTA] = { 255, 0, 255 }, /* light magenta */ - [COLOR_LIGHT_CYAN] = { 0, 255, 255 }, /* light cyan */ - [COLOR_WHITE] = { 255, 255, 255 }, /* white */ - - [COLOR_FOREGROUND] = { 229, 229, 229 }, /* light grey */ - [COLOR_BACKGROUND] = { 0, 0, 0 }, /* black */ -}; - -static uint8_t color_palette_solarized[COLOR_NUM][3] = { - [COLOR_BLACK] = { 7, 54, 66 }, /* black */ - [COLOR_RED] = { 220, 50, 47 }, /* red */ - [COLOR_GREEN] = { 133, 153, 0 }, /* green */ - [COLOR_YELLOW] = { 181, 137, 0 }, /* yellow */ - [COLOR_BLUE] = { 38, 139, 210 }, /* blue */ - [COLOR_MAGENTA] = { 211, 54, 130 }, /* magenta */ - [COLOR_CYAN] = { 42, 161, 152 }, /* cyan */ - [COLOR_LIGHT_GREY] = { 238, 232, 213 }, /* light grey */ - [COLOR_DARK_GREY] = { 0, 43, 54 }, /* dark grey */ - [COLOR_LIGHT_RED] = { 203, 75, 22 }, /* light red */ - [COLOR_LIGHT_GREEN] = { 88, 110, 117 }, /* light green */ - [COLOR_LIGHT_YELLOW] = { 101, 123, 131 }, /* light yellow */ - [COLOR_LIGHT_BLUE] = { 131, 148, 150 }, /* light blue */ - [COLOR_LIGHT_MAGENTA] = { 108, 113, 196 }, /* light magenta */ - [COLOR_LIGHT_CYAN] = { 147, 161, 161 }, /* light cyan */ - [COLOR_WHITE] = { 253, 246, 227 }, /* white */ - - [COLOR_FOREGROUND] = { 238, 232, 213 }, /* light grey */ - [COLOR_BACKGROUND] = { 7, 54, 66 }, /* black */ -}; - -static uint8_t color_palette_solarized_black[COLOR_NUM][3] = { - [COLOR_BLACK] = { 0, 0, 0 }, /* black */ - [COLOR_RED] = { 220, 50, 47 }, /* red */ - [COLOR_GREEN] = { 133, 153, 0 }, /* green */ - [COLOR_YELLOW] = { 181, 137, 0 }, /* yellow */ - [COLOR_BLUE] = { 38, 139, 210 }, /* blue */ - [COLOR_MAGENTA] = { 211, 54, 130 }, /* magenta */ - [COLOR_CYAN] = { 42, 161, 152 }, /* cyan */ - [COLOR_LIGHT_GREY] = { 238, 232, 213 }, /* light grey */ - [COLOR_DARK_GREY] = { 0, 43, 54 }, /* dark grey */ - [COLOR_LIGHT_RED] = { 203, 75, 22 }, /* light red */ - [COLOR_LIGHT_GREEN] = { 88, 110, 117 }, /* light green */ - [COLOR_LIGHT_YELLOW] = { 101, 123, 131 }, /* light yellow */ - [COLOR_LIGHT_BLUE] = { 131, 148, 150 }, /* light blue */ - [COLOR_LIGHT_MAGENTA] = { 108, 113, 196 }, /* light magenta */ - [COLOR_LIGHT_CYAN] = { 147, 161, 161 }, /* light cyan */ - [COLOR_WHITE] = { 253, 246, 227 }, /* white */ - - [COLOR_FOREGROUND] = { 238, 232, 213 }, /* light grey */ - [COLOR_BACKGROUND] = { 0, 0, 0 }, /* black */ -}; - -static uint8_t color_palette_solarized_white[COLOR_NUM][3] = { - [COLOR_BLACK] = { 7, 54, 66 }, /* black */ - [COLOR_RED] = { 220, 50, 47 }, /* red */ - [COLOR_GREEN] = { 133, 153, 0 }, /* green */ - [COLOR_YELLOW] = { 181, 137, 0 }, /* yellow */ - [COLOR_BLUE] = { 38, 139, 210 }, /* blue */ - [COLOR_MAGENTA] = { 211, 54, 130 }, /* magenta */ - [COLOR_CYAN] = { 42, 161, 152 }, /* cyan */ - [COLOR_LIGHT_GREY] = { 238, 232, 213 }, /* light grey */ - [COLOR_DARK_GREY] = { 0, 43, 54 }, /* dark grey */ - [COLOR_LIGHT_RED] = { 203, 75, 22 }, /* light red */ - [COLOR_LIGHT_GREEN] = { 88, 110, 117 }, /* light green */ - [COLOR_LIGHT_YELLOW] = { 101, 123, 131 }, /* light yellow */ - [COLOR_LIGHT_BLUE] = { 131, 148, 150 }, /* light blue */ - [COLOR_LIGHT_MAGENTA] = { 108, 113, 196 }, /* light magenta */ - [COLOR_LIGHT_CYAN] = { 147, 161, 161 }, /* light cyan */ - [COLOR_WHITE] = { 253, 246, 227 }, /* white */ - - [COLOR_FOREGROUND] = { 7, 54, 66 }, /* black */ - [COLOR_BACKGROUND] = { 238, 232, 213 }, /* light grey */ -}; - -static uint8_t (*get_palette(struct tsm_vte *vte))[3] -{ - if (!vte->palette_name) - return color_palette; - - if (!strcmp(vte->palette_name, "solarized")) - return color_palette_solarized; - if (!strcmp(vte->palette_name, "solarized-black")) - return color_palette_solarized_black; - if (!strcmp(vte->palette_name, "solarized-white")) - return color_palette_solarized_white; - - return color_palette; -} - -/* Several effects may occur when non-RGB colors are used. For instance, if bold - * is enabled, then a dark color code is always converted to a light color to - * simulate bold (even though bold may actually be supported!). To support this, - * we need to differentiate between a set color-code and a set rgb-color. - * This function actually converts a set color-code into an RGB color. This must - * be called before passing the attribute to the console layer so the console - * layer can always work with RGB values and does not have to care for color - * codes. */ -static void to_rgb(struct tsm_vte *vte, struct tsm_screen_attr *attr) -{ - int8_t code; - - code = attr->fccode; - if (code >= 0) { - /* bold causes light colors */ - if (attr->bold && code < 8) - code += 8; - if (code >= COLOR_NUM) - code = COLOR_FOREGROUND; - - attr->fr = vte->palette[code][0]; - attr->fg = vte->palette[code][1]; - attr->fb = vte->palette[code][2]; - } - - code = attr->bccode; - if (code >= 0) { - if (code >= COLOR_NUM) - code = COLOR_BACKGROUND; - - attr->br = vte->palette[code][0]; - attr->bg = vte->palette[code][1]; - attr->bb = vte->palette[code][2]; - } -} - -static void copy_fcolor(struct tsm_screen_attr *dest, - const struct tsm_screen_attr *src) -{ - dest->fccode = src->fccode; - dest->fr = src->fr; - dest->fg = src->fg; - dest->fb = src->fb; -} - -static void copy_bcolor(struct tsm_screen_attr *dest, - const struct tsm_screen_attr *src) -{ - dest->bccode = src->bccode; - dest->br = src->br; - dest->bg = src->bg; - dest->bb = src->bb; -} - -SHL_EXPORT -int tsm_vte_new(struct tsm_vte **out, struct tsm_screen *con, - tsm_vte_write_cb write_cb, void *data, - tsm_log_t log, void *log_data) -{ - struct tsm_vte *vte; - int ret; - - if (!out || !con || !write_cb) - return -EINVAL; - - vte = malloc(sizeof(*vte)); - if (!vte) - return -ENOMEM; - - memset(vte, 0, sizeof(*vte)); - vte->ref = 1; - vte->llog = log; - vte->llog_data = log_data; - vte->con = con; - vte->write_cb = write_cb; - vte->data = data; - vte->palette = get_palette(vte); - vte->def_attr.fccode = COLOR_FOREGROUND; - vte->def_attr.bccode = COLOR_BACKGROUND; - to_rgb(vte, &vte->def_attr); - - ret = tsm_utf8_mach_new(&vte->mach); - if (ret) - goto err_free; - - tsm_vte_reset(vte); - tsm_screen_erase_screen(vte->con, false); - - llog_debug(vte, "new vte object"); - tsm_screen_ref(vte->con); - *out = vte; - return 0; - -err_free: - free(vte); - return ret; -} - -SHL_EXPORT -void tsm_vte_ref(struct tsm_vte *vte) -{ - if (!vte) - return; - - vte->ref++; -} - -SHL_EXPORT -void tsm_vte_unref(struct tsm_vte *vte) -{ - if (!vte || !vte->ref) - return; - - if (--vte->ref) - return; - - llog_debug(vte, "destroying vte object"); - tsm_screen_unref(vte->con); - tsm_utf8_mach_free(vte->mach); - free(vte); -} - -SHL_EXPORT -int tsm_vte_set_palette(struct tsm_vte *vte, const char *palette) -{ - char *tmp = NULL; - - if (!vte) - return -EINVAL; - - if (palette) { - tmp = strdup(palette); - if (!tmp) - return -ENOMEM; - } - - free(vte->palette_name); - vte->palette_name = tmp; - - vte->palette = get_palette(vte); - vte->def_attr.fccode = COLOR_FOREGROUND; - vte->def_attr.bccode = COLOR_BACKGROUND; - - to_rgb(vte, &vte->def_attr); - memcpy(&vte->cattr, &vte->def_attr, sizeof(vte->cattr)); - - tsm_screen_set_def_attr(vte->con, &vte->def_attr); - tsm_screen_erase_screen(vte->con, false); - - return 0; -} - -/* - * Write raw byte-stream to pty. - * When writing data to the client we must make sure that we send the correct - * encoding. For backwards-compatibility reasons we should always send 7bit - * characters exclusively. However, when FLAG_7BIT_MODE is not set, then we can - * also send raw 8bit characters. For instance, in FLAG_8BIT_MODE we can use the - * GR characters as keyboard input and send them directly or even use the C1 - * escape characters. In unicode mode (default) we can send multi-byte utf-8 - * characters which are also 8bit. When sending these characters, set the \raw - * flag to true so this function does not perform debug checks on data we send. - * If debugging is disabled, these checks are also disabled and won't affect - * performance. - * For better debugging, we also use the __LINE__ and __FILE__ macros. Use the - * vte_write() and vte_write_raw() macros below for more convenient use. - * - * As a rule of thumb do never send 8bit characters in escape sequences and also - * avoid all 8bit escape codes including the C1 codes. This will guarantee that - * all kind of clients are always compatible to us. - * - * If SEND_RECEIVE_MODE is off (that is, local echo is on) we have to send all - * data directly to ourself again. However, we must avoid recursion when - * tsm_vte_input() itself calls vte_write*(), therefore, we increase the - * PARSER counter when entering tsm_vte_input() and reset it when leaving it - * so we never echo data that origins from tsm_vte_input(). - * But note that SEND_RECEIVE_MODE is inherently broken for escape sequences - * that request answers. That is, if we send a request to the client that awaits - * a response and parse that request via local echo ourself, then we will also - * send a response to the client even though he didn't request one. This - * recursion fix does not avoid this but only prevents us from endless loops - * here. Anyway, only few applications rely on local echo so we can safely - * ignore this. - */ -static void vte_write_debug(struct tsm_vte *vte, const char *u8, size_t len, - bool raw, const char *file, int line) -{ -#ifdef BUILD_ENABLE_DEBUG - /* in debug mode we check that escape sequences are always <0x7f so they - * are correctly parsed by non-unicode and non-8bit-mode clients. */ - size_t i; - - if (!raw) { - for (i = 0; i < len; ++i) { - if (u8[i] & 0x80) - llog_warning(vte, "sending 8bit character inline to client in %s:%d", - file, line); - } - } -#endif - - /* in local echo mode, directly parse the data again */ - if (!vte->parse_cnt && !(vte->flags & FLAG_SEND_RECEIVE_MODE)) { - if (vte->flags & FLAG_PREPEND_ESCAPE) - tsm_vte_input(vte, "\e", 1); - tsm_vte_input(vte, u8, len); - } - - if (vte->flags & FLAG_PREPEND_ESCAPE) - vte->write_cb(vte, "\e", 1, vte->data); - vte->write_cb(vte, u8, len, vte->data); - - vte->flags &= ~FLAG_PREPEND_ESCAPE; -} - -#define vte_write(_vte, _u8, _len) \ - vte_write_debug((_vte), (_u8), (_len), false, __FILE__, __LINE__) -#define vte_write_raw(_vte, _u8, _len) \ - vte_write_debug((_vte), (_u8), (_len), true, __FILE__, __LINE__) - -/* write to console */ -static void write_console(struct tsm_vte *vte, tsm_symbol_t sym) -{ - to_rgb(vte, &vte->cattr); - tsm_screen_write(vte->con, sym, &vte->cattr); -} - -static void reset_state(struct tsm_vte *vte) -{ - vte->saved_state.cursor_x = 0; - vte->saved_state.cursor_y = 0; - vte->saved_state.origin_mode = false; - vte->saved_state.wrap_mode = true; - vte->saved_state.gl = &vte->g0; - vte->saved_state.gr = &vte->g1; - - copy_fcolor(&vte->saved_state.cattr, &vte->def_attr); - copy_bcolor(&vte->saved_state.cattr, &vte->def_attr); - vte->saved_state.cattr.bold = 0; - vte->saved_state.cattr.underline = 0; - vte->saved_state.cattr.inverse = 0; - vte->saved_state.cattr.protect = 0; -} - -static void save_state(struct tsm_vte *vte) -{ - vte->saved_state.cursor_x = tsm_screen_get_cursor_x(vte->con); - vte->saved_state.cursor_y = tsm_screen_get_cursor_y(vte->con); - vte->saved_state.cattr = vte->cattr; - vte->saved_state.gl = vte->gl; - vte->saved_state.gr = vte->gr; - vte->saved_state.wrap_mode = vte->flags & FLAG_AUTO_WRAP_MODE; - vte->saved_state.origin_mode = vte->flags & FLAG_ORIGIN_MODE; -} - -static void restore_state(struct tsm_vte *vte) -{ - tsm_screen_move_to(vte->con, vte->saved_state.cursor_x, - vte->saved_state.cursor_y); - vte->cattr = vte->saved_state.cattr; - to_rgb(vte, &vte->cattr); - if (vte->flags & FLAG_BACKGROUND_COLOR_ERASE_MODE) - tsm_screen_set_def_attr(vte->con, &vte->cattr); - vte->gl = vte->saved_state.gl; - vte->gr = vte->saved_state.gr; - - if (vte->saved_state.wrap_mode) { - vte->flags |= FLAG_AUTO_WRAP_MODE; - tsm_screen_set_flags(vte->con, TSM_SCREEN_AUTO_WRAP); - } else { - vte->flags &= ~FLAG_AUTO_WRAP_MODE; - tsm_screen_reset_flags(vte->con, TSM_SCREEN_AUTO_WRAP); - } - - if (vte->saved_state.origin_mode) { - vte->flags |= FLAG_ORIGIN_MODE; - tsm_screen_set_flags(vte->con, TSM_SCREEN_REL_ORIGIN); - } else { - vte->flags &= ~FLAG_ORIGIN_MODE; - tsm_screen_reset_flags(vte->con, TSM_SCREEN_REL_ORIGIN); - } -} - -/* - * Reset VTE state - * This performs a soft reset of the VTE. That is, everything is reset to the - * same state as when the VTE was created. This does not affect the console, - * though. - */ -SHL_EXPORT -void tsm_vte_reset(struct tsm_vte *vte) -{ - if (!vte) - return; - - vte->flags = 0; - vte->flags |= FLAG_TEXT_CURSOR_MODE; - vte->flags |= FLAG_AUTO_REPEAT_MODE; - vte->flags |= FLAG_SEND_RECEIVE_MODE; - vte->flags |= FLAG_AUTO_WRAP_MODE; - vte->flags |= FLAG_BACKGROUND_COLOR_ERASE_MODE; - tsm_screen_reset(vte->con); - tsm_screen_set_flags(vte->con, TSM_SCREEN_AUTO_WRAP); - - tsm_utf8_mach_reset(vte->mach); - vte->state = STATE_GROUND; - vte->gl = &vte->g0; - vte->gr = &vte->g1; - vte->glt = NULL; - vte->grt = NULL; - vte->g0 = &tsm_vte_unicode_lower; - vte->g1 = &tsm_vte_unicode_upper; - vte->g2 = &tsm_vte_unicode_lower; - vte->g3 = &tsm_vte_unicode_upper; - - memcpy(&vte->cattr, &vte->def_attr, sizeof(vte->cattr)); - to_rgb(vte, &vte->cattr); - tsm_screen_set_def_attr(vte->con, &vte->def_attr); - - reset_state(vte); -} - -SHL_EXPORT -void tsm_vte_hard_reset(struct tsm_vte *vte) -{ - tsm_vte_reset(vte); - tsm_screen_erase_screen(vte->con, false); - tsm_screen_clear_sb(vte->con); - tsm_screen_move_to(vte->con, 0, 0); -} - -static void send_primary_da(struct tsm_vte *vte) -{ - vte_write(vte, "\e[?60;1;6;9;15c", 17); -} - -/* execute control character (C0 or C1) */ -static void do_execute(struct tsm_vte *vte, uint32_t ctrl) -{ - switch (ctrl) { - case 0x00: /* NUL */ - /* Ignore on input */ - break; - case 0x05: /* ENQ */ - /* Transmit answerback message */ - /* TODO: is there a better answer than ACK? */ - vte_write(vte, "\x06", 1); - break; - case 0x07: /* BEL */ - /* Sound bell tone */ - /* TODO: I always considered this annying, however, we - * should at least provide some way to enable it if the - * user *really* wants it. - */ - break; - case 0x08: /* BS */ - /* Move cursor one position left */ - tsm_screen_move_left(vte->con, 1); - break; - case 0x09: /* HT */ - /* Move to next tab stop or end of line */ - tsm_screen_tab_right(vte->con, 1); - break; - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - /* Line feed or newline (CR/NL mode) */ - if (vte->flags & FLAG_LINE_FEED_NEW_LINE_MODE) - tsm_screen_newline(vte->con); - else - tsm_screen_move_down(vte->con, 1, true); - break; - case 0x0d: /* CR */ - /* Move cursor to left margin */ - tsm_screen_move_line_home(vte->con); - break; - case 0x0e: /* SO */ - /* Map G1 character set into GL */ - vte->gl = &vte->g1; - break; - case 0x0f: /* SI */ - /* Map G0 character set into GL */ - vte->gl = &vte->g0; - break; - case 0x11: /* XON */ - /* Resume transmission */ - /* TODO */ - break; - case 0x13: /* XOFF */ - /* Stop transmission */ - /* TODO */ - break; - case 0x18: /* CAN */ - /* Cancel escape sequence */ - /* nothing to do here */ - break; - case 0x1a: /* SUB */ - /* Discard current escape sequence and show err-sym */ - write_console(vte, 0xbf); - break; - case 0x1b: /* ESC */ - /* Invokes an escape sequence */ - /* nothing to do here */ - break; - case 0x1f: /* DEL */ - /* Ignored */ - break; - case 0x84: /* IND */ - /* Move down one row, perform scroll-up if needed */ - tsm_screen_move_down(vte->con, 1, true); - break; - case 0x85: /* NEL */ - /* CR/NL with scroll-up if needed */ - tsm_screen_newline(vte->con); - break; - case 0x88: /* HTS */ - /* Set tab stop at current position */ - tsm_screen_set_tabstop(vte->con); - break; - case 0x8d: /* RI */ - /* Move up one row, perform scroll-down if needed */ - tsm_screen_move_up(vte->con, 1, true); - break; - case 0x8e: /* SS2 */ - /* Temporarily map G2 into GL for next char only */ - vte->glt = &vte->g2; - break; - case 0x8f: /* SS3 */ - /* Temporarily map G3 into GL for next char only */ - vte->glt = &vte->g3; - break; - case 0x9a: /* DECID */ - /* Send device attributes response like ANSI DA */ - send_primary_da(vte); - break; - case 0x9c: /* ST */ - /* End control string */ - /* nothing to do here */ - break; - default: - llog_debug(vte, "unhandled control char %u", ctrl); - } -} - -static void do_clear(struct tsm_vte *vte) -{ - int i; - - vte->csi_argc = 0; - for (i = 0; i < CSI_ARG_MAX; ++i) - vte->csi_argv[i] = -1; - vte->csi_flags = 0; -} - -static void do_collect(struct tsm_vte *vte, uint32_t data) -{ - switch (data) { - case '!': - vte->csi_flags |= CSI_BANG; - break; - case '$': - vte->csi_flags |= CSI_CASH; - break; - case '?': - vte->csi_flags |= CSI_WHAT; - break; - case '>': - vte->csi_flags |= CSI_GT; - break; - case ' ': - vte->csi_flags |= CSI_SPACE; - break; - case '\'': - vte->csi_flags |= CSI_SQUOTE; - break; - case '"': - vte->csi_flags |= CSI_DQUOTE; - break; - case '*': - vte->csi_flags |= CSI_MULT; - break; - case '+': - vte->csi_flags |= CSI_PLUS; - break; - case '(': - vte->csi_flags |= CSI_POPEN; - break; - case ')': - vte->csi_flags |= CSI_PCLOSE; - break; - } -} - -static void do_param(struct tsm_vte *vte, uint32_t data) -{ - int new; - - if (data == ';') { - if (vte->csi_argc < CSI_ARG_MAX) - vte->csi_argc++; - return; - } - - if (vte->csi_argc >= CSI_ARG_MAX) - return; - - /* avoid integer overflows; max allowed value is 16384 anyway */ - if (vte->csi_argv[vte->csi_argc] > 0xffff) - return; - - if (data >= '0' && data <= '9') { - new = vte->csi_argv[vte->csi_argc]; - if (new <= 0) - new = data - '0'; - else - new = new * 10 + data - '0'; - vte->csi_argv[vte->csi_argc] = new; - } -} - -static bool set_charset(struct tsm_vte *vte, tsm_vte_charset *set) -{ - if (vte->csi_flags & CSI_POPEN) - vte->g0 = set; - else if (vte->csi_flags & CSI_PCLOSE) - vte->g1 = set; - else if (vte->csi_flags & CSI_MULT) - vte->g2 = set; - else if (vte->csi_flags & CSI_PLUS) - vte->g3 = set; - else - return false; - - return true; -} - -static void do_esc(struct tsm_vte *vte, uint32_t data) -{ - switch (data) { - case 'B': /* map ASCII into G0-G3 */ - if (set_charset(vte, &tsm_vte_unicode_lower)) - return; - break; - case '<': /* map DEC supplemental into G0-G3 */ - if (set_charset(vte, &tsm_vte_dec_supplemental_graphics)) - return; - break; - case '0': /* map DEC special into G0-G3 */ - if (set_charset(vte, &tsm_vte_dec_special_graphics)) - return; - break; - case 'A': /* map British into G0-G3 */ - /* TODO: create British charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case '4': /* map Dutch into G0-G3 */ - /* TODO: create Dutch charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case 'C': - case '5': /* map Finnish into G0-G3 */ - /* TODO: create Finnish charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case 'R': /* map French into G0-G3 */ - /* TODO: create French charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case 'Q': /* map French-Canadian into G0-G3 */ - /* TODO: create French-Canadian charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case 'K': /* map German into G0-G3 */ - /* TODO: create German charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case 'Y': /* map Italian into G0-G3 */ - /* TODO: create Italian charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case 'E': - case '6': /* map Norwegian/Danish into G0-G3 */ - /* TODO: create Norwegian/Danish charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case 'Z': /* map Spanish into G0-G3 */ - /* TODO: create Spanish charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case 'H': - case '7': /* map Swedish into G0-G3 */ - /* TODO: create Swedish charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case '=': /* map Swiss into G0-G3 */ - /* TODO: create Swiss charset from DEC */ - if (set_charset(vte, &tsm_vte_unicode_upper)) - return; - break; - case 'F': - if (vte->csi_flags & CSI_SPACE) { - /* S7C1T */ - /* Disable 8bit C1 mode */ - vte->flags &= ~FLAG_USE_C1; - return; - } - break; - case 'G': - if (vte->csi_flags & CSI_SPACE) { - /* S8C1T */ - /* Enable 8bit C1 mode */ - vte->flags |= FLAG_USE_C1; - return; - } - break; - } - - /* everything below is only valid without CSI flags */ - if (vte->csi_flags) { - llog_debug(vte, "unhandled escape seq %u", data); - return; - } - - switch (data) { - case 'D': /* IND */ - /* Move down one row, perform scroll-up if needed */ - tsm_screen_move_down(vte->con, 1, true); - break; - case 'E': /* NEL */ - /* CR/NL with scroll-up if needed */ - tsm_screen_newline(vte->con); - break; - case 'H': /* HTS */ - /* Set tab stop at current position */ - tsm_screen_set_tabstop(vte->con); - break; - case 'M': /* RI */ - /* Move up one row, perform scroll-down if needed */ - tsm_screen_move_up(vte->con, 1, true); - break; - case 'N': /* SS2 */ - /* Temporarily map G2 into GL for next char only */ - vte->glt = &vte->g2; - break; - case 'O': /* SS3 */ - /* Temporarily map G3 into GL for next char only */ - vte->glt = &vte->g3; - break; - case 'Z': /* DECID */ - /* Send device attributes response like ANSI DA */ - send_primary_da(vte); - break; - case '\\': /* ST */ - /* End control string */ - /* nothing to do here */ - break; - case '~': /* LS1R */ - /* Invoke G1 into GR */ - vte->gr = &vte->g1; - break; - case 'n': /* LS2 */ - /* Invoke G2 into GL */ - vte->gl = &vte->g2; - break; - case '}': /* LS2R */ - /* Invoke G2 into GR */ - vte->gr = &vte->g2; - break; - case 'o': /* LS3 */ - /* Invoke G3 into GL */ - vte->gl = &vte->g3; - break; - case '|': /* LS3R */ - /* Invoke G3 into GR */ - vte->gr = &vte->g3; - break; - case '=': /* DECKPAM */ - /* Set application keypad mode */ - vte->flags |= FLAG_KEYPAD_APPLICATION_MODE; - break; - case '>': /* DECKPNM */ - /* Set numeric keypad mode */ - vte->flags &= ~FLAG_KEYPAD_APPLICATION_MODE; - break; - case 'c': /* RIS */ - /* hard reset */ - tsm_vte_hard_reset(vte); - break; - case '7': /* DECSC */ - /* save console state */ - save_state(vte); - break; - case '8': /* DECRC */ - /* restore console state */ - restore_state(vte); - break; - default: - llog_debug(vte, "unhandled escape seq %u", data); - } -} - -static void csi_attribute(struct tsm_vte *vte) -{ - static const uint8_t bval[6] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff }; - unsigned int i, code; - - if (vte->csi_argc <= 1 && vte->csi_argv[0] == -1) { - vte->csi_argc = 1; - vte->csi_argv[0] = 0; - } - - for (i = 0; i < vte->csi_argc; ++i) { - switch (vte->csi_argv[i]) { - case -1: - break; - case 0: - copy_fcolor(&vte->cattr, &vte->def_attr); - copy_bcolor(&vte->cattr, &vte->def_attr); - vte->cattr.bold = 0; - vte->cattr.underline = 0; - vte->cattr.inverse = 0; - break; - case 1: - vte->cattr.bold = 1; - break; - case 4: - vte->cattr.underline = 1; - break; - case 7: - vte->cattr.inverse = 1; - break; - case 22: - vte->cattr.bold = 0; - break; - case 24: - vte->cattr.underline = 0; - break; - case 27: - vte->cattr.inverse = 0; - break; - case 30: - vte->cattr.fccode = COLOR_BLACK; - break; - case 31: - vte->cattr.fccode = COLOR_RED; - break; - case 32: - vte->cattr.fccode = COLOR_GREEN; - break; - case 33: - vte->cattr.fccode = COLOR_YELLOW; - break; - case 34: - vte->cattr.fccode = COLOR_BLUE; - break; - case 35: - vte->cattr.fccode = COLOR_MAGENTA; - break; - case 36: - vte->cattr.fccode = COLOR_CYAN; - break; - case 37: - vte->cattr.fccode = COLOR_LIGHT_GREY; - break; - case 39: - copy_fcolor(&vte->cattr, &vte->def_attr); - break; - case 40: - vte->cattr.bccode = COLOR_BLACK; - break; - case 41: - vte->cattr.bccode = COLOR_RED; - break; - case 42: - vte->cattr.bccode = COLOR_GREEN; - break; - case 43: - vte->cattr.bccode = COLOR_YELLOW; - break; - case 44: - vte->cattr.bccode = COLOR_BLUE; - break; - case 45: - vte->cattr.bccode = COLOR_MAGENTA; - break; - case 46: - vte->cattr.bccode = COLOR_CYAN; - break; - case 47: - vte->cattr.bccode = COLOR_LIGHT_GREY; - break; - case 49: - copy_bcolor(&vte->cattr, &vte->def_attr); - break; - case 90: - vte->cattr.fccode = COLOR_DARK_GREY; - break; - case 91: - vte->cattr.fccode = COLOR_LIGHT_RED; - break; - case 92: - vte->cattr.fccode = COLOR_LIGHT_GREEN; - break; - case 93: - vte->cattr.fccode = COLOR_LIGHT_YELLOW; - break; - case 94: - vte->cattr.fccode = COLOR_LIGHT_BLUE; - break; - case 95: - vte->cattr.fccode = COLOR_LIGHT_MAGENTA; - break; - case 96: - vte->cattr.fccode = COLOR_LIGHT_CYAN; - break; - case 97: - vte->cattr.fccode = COLOR_WHITE; - break; - case 100: - vte->cattr.bccode = COLOR_DARK_GREY; - break; - case 101: - vte->cattr.bccode = COLOR_LIGHT_RED; - break; - case 102: - vte->cattr.bccode = COLOR_LIGHT_GREEN; - break; - case 103: - vte->cattr.bccode = COLOR_LIGHT_YELLOW; - break; - case 104: - vte->cattr.bccode = COLOR_LIGHT_BLUE; - break; - case 105: - vte->cattr.bccode = COLOR_LIGHT_MAGENTA; - break; - case 106: - vte->cattr.bccode = COLOR_LIGHT_CYAN; - break; - case 107: - vte->cattr.bccode = COLOR_WHITE; - break; - case 38: - /* fallthrough */ - case 48: - if (i + 2 >= vte->csi_argc || - vte->csi_argv[i + 1] != 5 || - vte->csi_argv[i + 2] < 0) { - llog_debug(vte, "invalid 256color SGR"); - break; - } - - code = vte->csi_argv[i + 2]; - if (vte->csi_argv[i] == 38) { - if (code < 16) { - vte->cattr.fccode = code; - } else if (code < 232) { - vte->cattr.fccode = -1; - code -= 16; - vte->cattr.fb = bval[code % 6]; - code /= 6; - vte->cattr.fg = bval[code % 6]; - code /= 6; - vte->cattr.fr = bval[code % 6]; - } else { - vte->cattr.fccode = -1; - code = (code - 232) * 10 + 8; - vte->cattr.fr = code; - vte->cattr.fg = code; - vte->cattr.fb = code; - } - } else { - if (code < 16) { - vte->cattr.bccode = code; - } else if (code < 232) { - vte->cattr.bccode = -1; - code -= 16; - vte->cattr.bb = bval[code % 6]; - code /= 6; - vte->cattr.bg = bval[code % 6]; - code /= 6; - vte->cattr.br = bval[code % 6]; - } else { - vte->cattr.bccode = -1; - code = (code - 232) * 10 + 8; - vte->cattr.br = code; - vte->cattr.bg = code; - vte->cattr.bb = code; - } - } - - i += 2; - break; - default: - llog_debug(vte, "unhandled SGR attr %i", - vte->csi_argv[i]); - } - } - - to_rgb(vte, &vte->cattr); - if (vte->flags & FLAG_BACKGROUND_COLOR_ERASE_MODE) - tsm_screen_set_def_attr(vte->con, &vte->cattr); -} - -static void csi_soft_reset(struct tsm_vte *vte) -{ - tsm_vte_reset(vte); -} - -static void csi_compat_mode(struct tsm_vte *vte) -{ - /* always perform soft reset */ - csi_soft_reset(vte); - - if (vte->csi_argv[0] == 61) { - /* Switching to VT100 compatibility mode. We do - * not support this mode, so ignore it. In fact, - * we are almost compatible to it, anyway, so - * there is no need to explicitly select it. - * However, we enable 7bit mode to avoid - * character-table problems */ - vte->flags |= FLAG_7BIT_MODE; - vte->g0 = &tsm_vte_unicode_lower; - vte->g1 = &tsm_vte_dec_supplemental_graphics; - } else if (vte->csi_argv[0] == 62 || - vte->csi_argv[0] == 63 || - vte->csi_argv[0] == 64) { - /* Switching to VT2/3/4 compatibility mode. We - * are always compatible with this so ignore it. - * We always send 7bit controls so we also do - * not care for the parameter value here that - * select the control-mode. - * VT220 defines argument 2 as 7bit mode but - * VT3xx up to VT5xx use it as 8bit mode. We - * choose to conform with the latter here. - * We also enable 8bit mode when VT220 - * compatibility is requested explicitly. */ - if (vte->csi_argv[1] == 1 || - vte->csi_argv[1] == 2) - vte->flags |= FLAG_USE_C1; - - vte->flags |= FLAG_8BIT_MODE; - vte->g0 = &tsm_vte_unicode_lower; - vte->g1 = &tsm_vte_dec_supplemental_graphics; - } else { - llog_debug(vte, "unhandled DECSCL 'p' CSI %i, switching to utf-8 mode again", - vte->csi_argv[0]); - } -} - -static inline void set_reset_flag(struct tsm_vte *vte, bool set, - unsigned int flag) -{ - if (set) - vte->flags |= flag; - else - vte->flags &= ~flag; -} - -static void csi_mode(struct tsm_vte *vte, bool set) -{ - unsigned int i; - - for (i = 0; i < vte->csi_argc; ++i) { - if (!(vte->csi_flags & CSI_WHAT)) { - switch (vte->csi_argv[i]) { - case -1: - continue; - case 2: /* KAM */ - set_reset_flag(vte, set, - FLAG_KEYBOARD_ACTION_MODE); - continue; - case 4: /* IRM */ - set_reset_flag(vte, set, - FLAG_INSERT_REPLACE_MODE); - if (set) - tsm_screen_set_flags(vte->con, - TSM_SCREEN_INSERT_MODE); - else - tsm_screen_reset_flags(vte->con, - TSM_SCREEN_INSERT_MODE); - continue; - case 12: /* SRM */ - set_reset_flag(vte, set, - FLAG_SEND_RECEIVE_MODE); - continue; - case 20: /* LNM */ - set_reset_flag(vte, set, - FLAG_LINE_FEED_NEW_LINE_MODE); - continue; - default: - llog_debug(vte, "unknown non-DEC (Re)Set-Mode %d", - vte->csi_argv[i]); - continue; - } - } - - switch (vte->csi_argv[i]) { - case -1: - continue; - case 1: /* DECCKM */ - set_reset_flag(vte, set, FLAG_CURSOR_KEY_MODE); - continue; - case 2: /* DECANM */ - /* Select VT52 mode */ - /* We do not support VT52 mode. Is there any reason why - * we should support it? We ignore it here and do not - * mark it as to-do item unless someone has strong - * arguments to support it. */ - continue; - case 3: /* DECCOLM */ - /* If set, select 132 column mode, otherwise use 80 - * column mode. If neither is selected explicitly, we - * use dynamic mode, that is, we send SIGWCH when the - * size changes and we allow arbitrary buffer - * dimensions. On soft-reset, we automatically fall back - * to the default, that is, dynamic mode. - * Dynamic-mode can be forced to a static mode in the - * config. That is, every time dynamic-mode becomes - * active, the terminal will be set to the dimensions - * that were selected in the config. This allows setting - * a fixed size for the terminal regardless of the - * display size. - * TODO: Implement this */ - continue; - case 4: /* DECSCLM */ - /* Select smooth scrolling. We do not support the - * classic smooth scrolling because we have a scrollback - * buffer. There is no need to implement smooth - * scrolling so ignore this here. */ - continue; - case 5: /* DECSCNM */ - set_reset_flag(vte, set, FLAG_INVERSE_SCREEN_MODE); - if (set) - tsm_screen_set_flags(vte->con, - TSM_SCREEN_INVERSE); - else - tsm_screen_reset_flags(vte->con, - TSM_SCREEN_INVERSE); - continue; - case 6: /* DECOM */ - set_reset_flag(vte, set, FLAG_ORIGIN_MODE); - if (set) - tsm_screen_set_flags(vte->con, - TSM_SCREEN_REL_ORIGIN); - else - tsm_screen_reset_flags(vte->con, - TSM_SCREEN_REL_ORIGIN); - continue; - case 7: /* DECAWN */ - set_reset_flag(vte, set, FLAG_AUTO_WRAP_MODE); - if (set) - tsm_screen_set_flags(vte->con, - TSM_SCREEN_AUTO_WRAP); - else - tsm_screen_reset_flags(vte->con, - TSM_SCREEN_AUTO_WRAP); - continue; - case 8: /* DECARM */ - set_reset_flag(vte, set, FLAG_AUTO_REPEAT_MODE); - continue; - case 12: /* blinking cursor */ - /* TODO: implement */ - continue; - case 18: /* DECPFF */ - /* If set, a form feed (FF) is sent to the printer after - * every screen that is printed. We don't have printers - * these days directly attached to terminals so we - * ignore this here. */ - continue; - case 19: /* DECPEX */ - /* If set, the full screen is printed instead of - * scrolling region only. We have no printer so ignore - * this mode. */ - continue; - case 25: /* DECTCEM */ - set_reset_flag(vte, set, FLAG_TEXT_CURSOR_MODE); - if (set) - tsm_screen_reset_flags(vte->con, - TSM_SCREEN_HIDE_CURSOR); - else - tsm_screen_set_flags(vte->con, - TSM_SCREEN_HIDE_CURSOR); - continue; - case 42: /* DECNRCM */ - set_reset_flag(vte, set, FLAG_NATIONAL_CHARSET_MODE); - continue; - case 47: /* Alternate screen buffer */ - if (vte->flags & FLAG_TITE_INHIBIT_MODE) - continue; - - if (set) - tsm_screen_set_flags(vte->con, - TSM_SCREEN_ALTERNATE); - else - tsm_screen_reset_flags(vte->con, - TSM_SCREEN_ALTERNATE); - continue; - case 1047: /* Alternate screen buffer with post-erase */ - if (vte->flags & FLAG_TITE_INHIBIT_MODE) - continue; - - if (set) { - tsm_screen_set_flags(vte->con, - TSM_SCREEN_ALTERNATE); - } else { - tsm_screen_erase_screen(vte->con, false); - tsm_screen_reset_flags(vte->con, - TSM_SCREEN_ALTERNATE); - } - continue; - case 1048: /* Set/Reset alternate-screen buffer cursor */ - if (vte->flags & FLAG_TITE_INHIBIT_MODE) - continue; - - if (set) { - vte->alt_cursor_x = - tsm_screen_get_cursor_x(vte->con); - vte->alt_cursor_y = - tsm_screen_get_cursor_y(vte->con); - } else { - tsm_screen_move_to(vte->con, vte->alt_cursor_x, - vte->alt_cursor_y); - } - continue; - case 1049: /* Alternate screen buffer with pre-erase+cursor */ - if (vte->flags & FLAG_TITE_INHIBIT_MODE) - continue; - - if (set) { - vte->alt_cursor_x = - tsm_screen_get_cursor_x(vte->con); - vte->alt_cursor_y = - tsm_screen_get_cursor_y(vte->con); - tsm_screen_set_flags(vte->con, - TSM_SCREEN_ALTERNATE); - tsm_screen_erase_screen(vte->con, false); - } else { - tsm_screen_reset_flags(vte->con, - TSM_SCREEN_ALTERNATE); - tsm_screen_move_to(vte->con, vte->alt_cursor_x, - vte->alt_cursor_y); - } - continue; - default: - llog_debug(vte, "unknown DEC %set-Mode %d", - set?"S":"Res", vte->csi_argv[i]); - continue; - } - } -} - -static void csi_dev_attr(struct tsm_vte *vte) -{ - if (vte->csi_argc <= 1 && vte->csi_argv[0] <= 0) { - if (vte->csi_flags == 0) { - send_primary_da(vte); - return; - } else if (vte->csi_flags & CSI_GT) { - vte_write(vte, "\e[>1;1;0c", 9); - return; - } - } - - llog_debug(vte, "unhandled DA: %x %d %d %d...", vte->csi_flags, - vte->csi_argv[0], vte->csi_argv[1], vte->csi_argv[2]); -} - -static void csi_dsr(struct tsm_vte *vte) -{ - char buf[64]; - unsigned int x, y, len; - - if (vte->csi_argv[0] == 5) { - vte_write(vte, "\e[0n", 4); - } else if (vte->csi_argv[0] == 6) { - x = tsm_screen_get_cursor_x(vte->con); - y = tsm_screen_get_cursor_y(vte->con); - len = snprintf(buf, sizeof(buf), "\e[%u;%uR", x, y); - if (len >= sizeof(buf)) - vte_write(vte, "\e[0;0R", 6); - else - vte_write(vte, buf, len); - } -} - -static void do_csi(struct tsm_vte *vte, uint32_t data) -{ - int num, x, y, upper, lower; - bool protect; - - if (vte->csi_argc < CSI_ARG_MAX) - vte->csi_argc++; - - switch (data) { - case 'A': /* CUU */ - /* move cursor up */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_move_up(vte->con, num, false); - break; - case 'B': /* CUD */ - /* move cursor down */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_move_down(vte->con, num, false); - break; - case 'C': /* CUF */ - /* move cursor forward */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_move_right(vte->con, num); - break; - case 'D': /* CUB */ - /* move cursor backward */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_move_left(vte->con, num); - break; - case 'd': /* VPA */ - /* Vertical Line Position Absolute */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - x = tsm_screen_get_cursor_x(vte->con); - tsm_screen_move_to(vte->con, x, num - 1); - break; - case 'e': /* VPR */ - /* Vertical Line Position Relative */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - x = tsm_screen_get_cursor_x(vte->con); - y = tsm_screen_get_cursor_y(vte->con); - tsm_screen_move_to(vte->con, x, y + num); - break; - case 'H': /* CUP */ - case 'f': /* HVP */ - /* position cursor */ - x = vte->csi_argv[0]; - if (x <= 0) - x = 1; - y = vte->csi_argv[1]; - if (y <= 0) - y = 1; - tsm_screen_move_to(vte->con, y - 1, x - 1); - break; - case 'G': /* CHA */ - /* Cursor Character Absolute */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - y = tsm_screen_get_cursor_y(vte->con); - tsm_screen_move_to(vte->con, num - 1, y); - break; - case 'J': - if (vte->csi_flags & CSI_WHAT) - protect = true; - else - protect = false; - - if (vte->csi_argv[0] <= 0) - tsm_screen_erase_cursor_to_screen(vte->con, - protect); - else if (vte->csi_argv[0] == 1) - tsm_screen_erase_screen_to_cursor(vte->con, - protect); - else if (vte->csi_argv[0] == 2) - tsm_screen_erase_screen(vte->con, protect); - else - llog_debug(vte, "unknown parameter to CSI-J: %d", - vte->csi_argv[0]); - break; - case 'K': - if (vte->csi_flags & CSI_WHAT) - protect = true; - else - protect = false; - - if (vte->csi_argv[0] <= 0) - tsm_screen_erase_cursor_to_end(vte->con, protect); - else if (vte->csi_argv[0] == 1) - tsm_screen_erase_home_to_cursor(vte->con, protect); - else if (vte->csi_argv[0] == 2) - tsm_screen_erase_current_line(vte->con, protect); - else - llog_debug(vte, "unknown parameter to CSI-K: %d", - vte->csi_argv[0]); - break; - case 'X': /* ECH */ - /* erase characters */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_erase_chars(vte->con, num); - break; - case 'm': - csi_attribute(vte); - break; - case 'p': - if (vte->csi_flags & CSI_GT) { - /* xterm: select X11 visual cursor mode */ - csi_soft_reset(vte); - } else if (vte->csi_flags & CSI_BANG) { - /* DECSTR: Soft Reset */ - csi_soft_reset(vte); - } else if (vte->csi_flags & CSI_CASH) { - /* DECRQM: Request DEC Private Mode */ - /* If CSI_WHAT is set, then enable, - * otherwise disable */ - csi_soft_reset(vte); - } else { - /* DECSCL: Compatibility Level */ - /* Sometimes CSI_DQUOTE is set here, too */ - csi_compat_mode(vte); - } - break; - case 'h': /* SM: Set Mode */ - csi_mode(vte, true); - break; - case 'l': /* RM: Reset Mode */ - csi_mode(vte, false); - break; - case 'r': /* DECSTBM */ - /* set margin size */ - upper = vte->csi_argv[0]; - if (upper < 0) - upper = 0; - lower = vte->csi_argv[1]; - if (lower < 0) - lower = 0; - tsm_screen_set_margins(vte->con, upper, lower); - break; - case 'c': /* DA */ - /* device attributes */ - csi_dev_attr(vte); - break; - case 'L': /* IL */ - /* insert lines */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_insert_lines(vte->con, num); - break; - case 'M': /* DL */ - /* delete lines */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_delete_lines(vte->con, num); - break; - case 'g': /* TBC */ - /* tabulation clear */ - num = vte->csi_argv[0]; - if (num <= 0) - tsm_screen_reset_tabstop(vte->con); - else if (num == 3) - tsm_screen_reset_all_tabstops(vte->con); - else - llog_debug(vte, "invalid parameter %d to TBC CSI", num); - break; - case '@': /* ICH */ - /* insert characters */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_insert_chars(vte->con, num); - break; - case 'P': /* DCH */ - /* delete characters */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_delete_chars(vte->con, num); - break; - case 'Z': /* CBT */ - /* cursor horizontal backwards tab */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_tab_left(vte->con, num); - break; - case 'I': /* CHT */ - /* cursor horizontal forward tab */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_tab_right(vte->con, num); - break; - case 'n': /* DSR */ - /* device status reports */ - csi_dsr(vte); - break; - case 'S': /* SU */ - /* scroll up */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_scroll_up(vte->con, num); - break; - case 'T': /* SD */ - /* scroll down */ - num = vte->csi_argv[0]; - if (num <= 0) - num = 1; - tsm_screen_scroll_down(vte->con, num); - break; - default: - llog_debug(vte, "unhandled CSI sequence %c", data); - } -} - -/* map a character according to current GL and GR maps */ -static uint32_t vte_map(struct tsm_vte *vte, uint32_t val) -{ - /* 32, 127, 160 and 255 map to identity like all values >255 */ - switch (val) { - case 33 ... 126: - if (vte->glt) { - val = (**vte->glt)[val - 32]; - vte->glt = NULL; - } else { - val = (**vte->gl)[val - 32]; - } - break; - case 161 ... 254: - if (vte->grt) { - val = (**vte->grt)[val - 160]; - vte->grt = NULL; - } else { - val = (**vte->gr)[val - 160]; - } - break; - } - - return val; -} - -/* perform parser action */ -static void do_action(struct tsm_vte *vte, uint32_t data, int action) -{ - tsm_symbol_t sym; - - switch (action) { - case ACTION_NONE: - /* do nothing */ - return; - case ACTION_IGNORE: - /* ignore character */ - break; - case ACTION_PRINT: - sym = tsm_symbol_make(vte_map(vte, data)); - write_console(vte, sym); - break; - case ACTION_EXECUTE: - do_execute(vte, data); - break; - case ACTION_CLEAR: - do_clear(vte); - break; - case ACTION_COLLECT: - do_collect(vte, data); - break; - case ACTION_PARAM: - do_param(vte, data); - break; - case ACTION_ESC_DISPATCH: - do_esc(vte, data); - break; - case ACTION_CSI_DISPATCH: - do_csi(vte, data); - break; - case ACTION_DCS_START: - break; - case ACTION_DCS_COLLECT: - break; - case ACTION_DCS_END: - break; - case ACTION_OSC_START: - break; - case ACTION_OSC_COLLECT: - break; - case ACTION_OSC_END: - break; - default: - llog_warn(vte, "invalid action %d", action); - } -} - -/* entry actions to be performed when entering the selected state */ -static const int entry_action[] = { - [STATE_CSI_ENTRY] = ACTION_CLEAR, - [STATE_DCS_ENTRY] = ACTION_CLEAR, - [STATE_DCS_PASS] = ACTION_DCS_START, - [STATE_ESC] = ACTION_CLEAR, - [STATE_OSC_STRING] = ACTION_OSC_START, - [STATE_NUM] = ACTION_NONE, -}; - -/* exit actions to be performed when leaving the selected state */ -static const int exit_action[] = { - [STATE_DCS_PASS] = ACTION_DCS_END, - [STATE_OSC_STRING] = ACTION_OSC_END, - [STATE_NUM] = ACTION_NONE, -}; - -/* perform state transition and dispatch related actions */ -static void do_trans(struct tsm_vte *vte, uint32_t data, int state, int act) -{ - if (state != STATE_NONE) { - /* A state transition occurs. Perform exit-action, - * transition-action and entry-action. Even when performing a - * transition to the same state as the current state we do this. - * Use STATE_NONE if this is not the desired behavior. - */ - do_action(vte, data, exit_action[vte->state]); - do_action(vte, data, act); - do_action(vte, data, entry_action[state]); - vte->state = state; - } else { - do_action(vte, data, act); - } -} - -/* - * Escape sequence parser - * This parses the new input character \data. It performs state transition and - * calls the right callbacks for each action. - */ -static void parse_data(struct tsm_vte *vte, uint32_t raw) -{ - /* events that may occur in any state */ - switch (raw) { - case 0x18: - case 0x1a: - case 0x80 ... 0x8f: - case 0x91 ... 0x97: - case 0x99: - case 0x9a: - case 0x9c: - do_trans(vte, raw, STATE_GROUND, ACTION_EXECUTE); - return; - case 0x1b: - do_trans(vte, raw, STATE_ESC, ACTION_NONE); - return; - case 0x98: - case 0x9e: - case 0x9f: - do_trans(vte, raw, STATE_ST_IGNORE, ACTION_NONE); - return; - case 0x90: - do_trans(vte, raw, STATE_DCS_ENTRY, ACTION_NONE); - return; - case 0x9d: - do_trans(vte, raw, STATE_OSC_STRING, ACTION_NONE); - return; - case 0x9b: - do_trans(vte, raw, STATE_CSI_ENTRY, ACTION_NONE); - return; - } - - /* events that depend on the current state */ - switch (vte->state) { - case STATE_GROUND: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - case 0x80 ... 0x8f: - case 0x91 ... 0x9a: - case 0x9c: - do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE); - return; - case 0x20 ... 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_PRINT); - return; - } - do_trans(vte, raw, STATE_NONE, ACTION_PRINT); - return; - case STATE_ESC: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE); - return; - case 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x20 ... 0x2f: - do_trans(vte, raw, STATE_ESC_INT, ACTION_COLLECT); - return; - case 0x30 ... 0x4f: - case 0x51 ... 0x57: - case 0x59: - case 0x5a: - case 0x5c: - case 0x60 ... 0x7e: - do_trans(vte, raw, STATE_GROUND, ACTION_ESC_DISPATCH); - return; - case 0x5b: - do_trans(vte, raw, STATE_CSI_ENTRY, ACTION_NONE); - return; - case 0x5d: - do_trans(vte, raw, STATE_OSC_STRING, ACTION_NONE); - return; - case 0x50: - do_trans(vte, raw, STATE_DCS_ENTRY, ACTION_NONE); - return; - case 0x58: - case 0x5e: - case 0x5f: - do_trans(vte, raw, STATE_ST_IGNORE, ACTION_NONE); - return; - } - do_trans(vte, raw, STATE_ESC_INT, ACTION_COLLECT); - return; - case STATE_ESC_INT: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE); - return; - case 0x20 ... 0x2f: - do_trans(vte, raw, STATE_NONE, ACTION_COLLECT); - return; - case 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x30 ... 0x7e: - do_trans(vte, raw, STATE_GROUND, ACTION_ESC_DISPATCH); - return; - } - do_trans(vte, raw, STATE_NONE, ACTION_COLLECT); - return; - case STATE_CSI_ENTRY: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE); - return; - case 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x20 ... 0x2f: - do_trans(vte, raw, STATE_CSI_INT, ACTION_COLLECT); - return; - case 0x3a: - do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE); - return; - case 0x30 ... 0x39: - case 0x3b: - do_trans(vte, raw, STATE_CSI_PARAM, ACTION_PARAM); - return; - case 0x3c ... 0x3f: - do_trans(vte, raw, STATE_CSI_PARAM, ACTION_COLLECT); - return; - case 0x40 ... 0x7e: - do_trans(vte, raw, STATE_GROUND, ACTION_CSI_DISPATCH); - return; - } - do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE); - return; - case STATE_CSI_PARAM: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE); - return; - case 0x30 ... 0x39: - case 0x3b: - do_trans(vte, raw, STATE_NONE, ACTION_PARAM); - return; - case 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x3a: - case 0x3c ... 0x3f: - do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE); - return; - case 0x20 ... 0x2f: - do_trans(vte, raw, STATE_CSI_INT, ACTION_COLLECT); - return; - case 0x40 ... 0x7e: - do_trans(vte, raw, STATE_GROUND, ACTION_CSI_DISPATCH); - return; - } - do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE); - return; - case STATE_CSI_INT: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE); - return; - case 0x20 ... 0x2f: - do_trans(vte, raw, STATE_NONE, ACTION_COLLECT); - return; - case 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x30 ... 0x3f: - do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE); - return; - case 0x40 ... 0x7e: - do_trans(vte, raw, STATE_GROUND, ACTION_CSI_DISPATCH); - return; - } - do_trans(vte, raw, STATE_CSI_IGNORE, ACTION_NONE); - return; - case STATE_CSI_IGNORE: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - do_trans(vte, raw, STATE_NONE, ACTION_EXECUTE); - return; - case 0x20 ... 0x3f: - case 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x40 ... 0x7e: - do_trans(vte, raw, STATE_GROUND, ACTION_NONE); - return; - } - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case STATE_DCS_ENTRY: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - case 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x3a: - do_trans(vte, raw, STATE_DCS_IGNORE, ACTION_NONE); - return; - case 0x20 ... 0x2f: - do_trans(vte, raw, STATE_DCS_INT, ACTION_COLLECT); - return; - case 0x30 ... 0x39: - case 0x3b: - do_trans(vte, raw, STATE_DCS_PARAM, ACTION_PARAM); - return; - case 0x3c ... 0x3f: - do_trans(vte, raw, STATE_DCS_PARAM, ACTION_COLLECT); - return; - case 0x40 ... 0x7e: - do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE); - return; - } - do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE); - return; - case STATE_DCS_PARAM: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - case 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x30 ... 0x39: - case 0x3b: - do_trans(vte, raw, STATE_NONE, ACTION_PARAM); - return; - case 0x3a: - case 0x3c ... 0x3f: - do_trans(vte, raw, STATE_DCS_IGNORE, ACTION_NONE); - return; - case 0x20 ... 0x2f: - do_trans(vte, raw, STATE_DCS_INT, ACTION_COLLECT); - return; - case 0x40 ... 0x7e: - do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE); - return; - } - do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE); - return; - case STATE_DCS_INT: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - case 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x20 ... 0x2f: - do_trans(vte, raw, STATE_NONE, ACTION_COLLECT); - return; - case 0x30 ... 0x3f: - do_trans(vte, raw, STATE_DCS_IGNORE, ACTION_NONE); - return; - case 0x40 ... 0x7e: - do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE); - return; - } - do_trans(vte, raw, STATE_DCS_PASS, ACTION_NONE); - return; - case STATE_DCS_PASS: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - case 0x20 ... 0x7e: - do_trans(vte, raw, STATE_NONE, ACTION_DCS_COLLECT); - return; - case 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x9c: - do_trans(vte, raw, STATE_GROUND, ACTION_NONE); - return; - } - do_trans(vte, raw, STATE_NONE, ACTION_DCS_COLLECT); - return; - case STATE_DCS_IGNORE: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - case 0x20 ... 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x9c: - do_trans(vte, raw, STATE_GROUND, ACTION_NONE); - return; - } - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case STATE_OSC_STRING: - switch (raw) { - case 0x00 ... 0x06: - case 0x08 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x20 ... 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_OSC_COLLECT); - return; - case 0x07: - case 0x9c: - do_trans(vte, raw, STATE_GROUND, ACTION_NONE); - return; - } - do_trans(vte, raw, STATE_NONE, ACTION_OSC_COLLECT); - return; - case STATE_ST_IGNORE: - switch (raw) { - case 0x00 ... 0x17: - case 0x19: - case 0x1c ... 0x1f: - case 0x20 ... 0x7f: - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - case 0x9c: - do_trans(vte, raw, STATE_GROUND, ACTION_NONE); - return; - } - do_trans(vte, raw, STATE_NONE, ACTION_IGNORE); - return; - } - - llog_warn(vte, "unhandled input %u in state %d", raw, vte->state); -} - -SHL_EXPORT -void tsm_vte_input(struct tsm_vte *vte, const char *u8, size_t len) -{ - int state; - uint32_t ucs4; - size_t i; - - if (!vte || !vte->con) - return; - - ++vte->parse_cnt; - for (i = 0; i < len; ++i) { - if (vte->flags & FLAG_7BIT_MODE) { - if (u8[i] & 0x80) - llog_debug(vte, "receiving 8bit character U+%d from pty while in 7bit mode", - (int)u8[i]); - parse_data(vte, u8[i] & 0x7f); - } else if (vte->flags & FLAG_8BIT_MODE) { - parse_data(vte, u8[i]); - } else { - state = tsm_utf8_mach_feed(vte->mach, u8[i]); - if (state == TSM_UTF8_ACCEPT || - state == TSM_UTF8_REJECT) { - ucs4 = tsm_utf8_mach_get(vte->mach); - parse_data(vte, ucs4); - } - } - } - --vte->parse_cnt; -} - -SHL_EXPORT -bool tsm_vte_handle_keyboard(struct tsm_vte *vte, uint32_t keysym, - uint32_t ascii, unsigned int mods, - uint32_t unicode) -{ - char val, u8[4]; - size_t len; - uint32_t sym; - - /* MOD1 (mostly labeled 'Alt') prepends an escape character to every - * input that is sent by a key. - * TODO: Transform this huge handler into a lookup table to save a lot - * of code and make such modifiers easier to implement. - * Also check whether altSendsEscape should be the default (xterm - * disables this by default, why?) and whether we should implement the - * fallback shifting that xterm does. */ - if (mods & TSM_ALT_MASK) - vte->flags |= FLAG_PREPEND_ESCAPE; - - /* A user might actually use multiple layouts for keyboard input. The - * @keysym variable contains the actual keysym that the user used. But - * if this keysym is not in the ascii range, the input handler does - * check all other layouts that the user specified whether one of them - * maps the key to some ASCII keysym and provides this via @ascii. - * We always use the real keysym except when handling CTRL+ - * shortcuts we use the ascii keysym. This is for compatibility to xterm - * et. al. so ctrl+c always works regardless of the currently active - * keyboard layout. - * But if no ascii-sym is found, we still use the real keysym. */ - sym = ascii; - if (sym == XKB_KEY_NoSymbol) - sym = keysym; - - if (mods & TSM_CONTROL_MASK) { - switch (sym) { - case XKB_KEY_2: - case XKB_KEY_space: - vte_write(vte, "\x00", 1); - return true; - case XKB_KEY_a: - case XKB_KEY_A: - vte_write(vte, "\x01", 1); - return true; - case XKB_KEY_b: - case XKB_KEY_B: - vte_write(vte, "\x02", 1); - return true; - case XKB_KEY_c: - case XKB_KEY_C: - vte_write(vte, "\x03", 1); - return true; - case XKB_KEY_d: - case XKB_KEY_D: - vte_write(vte, "\x04", 1); - return true; - case XKB_KEY_e: - case XKB_KEY_E: - vte_write(vte, "\x05", 1); - return true; - case XKB_KEY_f: - case XKB_KEY_F: - vte_write(vte, "\x06", 1); - return true; - case XKB_KEY_g: - case XKB_KEY_G: - vte_write(vte, "\x07", 1); - return true; - case XKB_KEY_h: - case XKB_KEY_H: - vte_write(vte, "\x08", 1); - return true; - case XKB_KEY_i: - case XKB_KEY_I: - vte_write(vte, "\x09", 1); - return true; - case XKB_KEY_j: - case XKB_KEY_J: - vte_write(vte, "\x0a", 1); - return true; - case XKB_KEY_k: - case XKB_KEY_K: - vte_write(vte, "\x0b", 1); - return true; - case XKB_KEY_l: - case XKB_KEY_L: - vte_write(vte, "\x0c", 1); - return true; - case XKB_KEY_m: - case XKB_KEY_M: - vte_write(vte, "\x0d", 1); - return true; - case XKB_KEY_n: - case XKB_KEY_N: - vte_write(vte, "\x0e", 1); - return true; - case XKB_KEY_o: - case XKB_KEY_O: - vte_write(vte, "\x0f", 1); - return true; - case XKB_KEY_p: - case XKB_KEY_P: - vte_write(vte, "\x10", 1); - return true; - case XKB_KEY_q: - case XKB_KEY_Q: - vte_write(vte, "\x11", 1); - return true; - case XKB_KEY_r: - case XKB_KEY_R: - vte_write(vte, "\x12", 1); - return true; - case XKB_KEY_s: - case XKB_KEY_S: - vte_write(vte, "\x13", 1); - return true; - case XKB_KEY_t: - case XKB_KEY_T: - vte_write(vte, "\x14", 1); - return true; - case XKB_KEY_u: - case XKB_KEY_U: - vte_write(vte, "\x15", 1); - return true; - case XKB_KEY_v: - case XKB_KEY_V: - vte_write(vte, "\x16", 1); - return true; - case XKB_KEY_w: - case XKB_KEY_W: - vte_write(vte, "\x17", 1); - return true; - case XKB_KEY_x: - case XKB_KEY_X: - vte_write(vte, "\x18", 1); - return true; - case XKB_KEY_y: - case XKB_KEY_Y: - vte_write(vte, "\x19", 1); - return true; - case XKB_KEY_z: - case XKB_KEY_Z: - vte_write(vte, "\x1a", 1); - return true; - case XKB_KEY_3: - case XKB_KEY_bracketleft: - case XKB_KEY_braceleft: - vte_write(vte, "\x1b", 1); - return true; - case XKB_KEY_4: - case XKB_KEY_backslash: - case XKB_KEY_bar: - vte_write(vte, "\x1c", 1); - return true; - case XKB_KEY_5: - case XKB_KEY_bracketright: - case XKB_KEY_braceright: - vte_write(vte, "\x1d", 1); - return true; - case XKB_KEY_6: - case XKB_KEY_grave: - case XKB_KEY_asciitilde: - vte_write(vte, "\x1e", 1); - return true; - case XKB_KEY_7: - case XKB_KEY_slash: - case XKB_KEY_question: - vte_write(vte, "\x1f", 1); - return true; - case XKB_KEY_8: - vte_write(vte, "\x7f", 1); - return true; - } - } - - switch (keysym) { - case XKB_KEY_BackSpace: - vte_write(vte, "\x08", 1); - return true; - case XKB_KEY_Tab: - case XKB_KEY_KP_Tab: - vte_write(vte, "\x09", 1); - return true; - case XKB_KEY_ISO_Left_Tab: - vte_write(vte, "\e[Z", 3); - return true; - case XKB_KEY_Linefeed: - vte_write(vte, "\x0a", 1); - return true; - case XKB_KEY_Clear: - vte_write(vte, "\x0b", 1); - return true; - /* - TODO: What should we do with this key? Sending XOFF is awful as - there is no simple way on modern keyboards to send XON - again. If someone wants this, we can re-eanble it and set - some flag. - case XKB_KEY_Pause: - vte_write(vte, "\x13", 1); - return true; - */ - /* - TODO: What should we do on scroll-lock? Sending 0x14 is what - the specs say but it is not used today the way most - users would expect so we disable it. If someone wants - this, we can re-enable it and set some flag. - case XKB_KEY_Scroll_Lock: - vte_write(vte, "\x14", 1); - return true; - */ - case XKB_KEY_Sys_Req: - vte_write(vte, "\x15", 1); - return true; - case XKB_KEY_Escape: - vte_write(vte, "\x1b", 1); - return true; - case XKB_KEY_KP_Enter: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) { - vte_write(vte, "\eOM", 3); - return true; - } - /* fallthrough */ - case XKB_KEY_Return: - if (vte->flags & FLAG_LINE_FEED_NEW_LINE_MODE) - vte_write(vte, "\x0d\x0a", 2); - else - vte_write(vte, "\x0d", 1); - return true; - case XKB_KEY_Find: - vte_write(vte, "\e[1~", 4); - return true; - case XKB_KEY_Insert: - vte_write(vte, "\e[2~", 4); - return true; - case XKB_KEY_Delete: - vte_write(vte, "\e[3~", 4); - return true; - case XKB_KEY_Select: - vte_write(vte, "\e[4~", 4); - return true; - case XKB_KEY_Page_Up: - case XKB_KEY_KP_Page_Up: - vte_write(vte, "\e[5~", 4); - return true; - case XKB_KEY_KP_Page_Down: - case XKB_KEY_Page_Down: - vte_write(vte, "\e[6~", 4); - return true; - case XKB_KEY_Up: - case XKB_KEY_KP_Up: - if (vte->flags & FLAG_CURSOR_KEY_MODE) - vte_write(vte, "\eOA", 3); - else - vte_write(vte, "\e[A", 3); - return true; - case XKB_KEY_Down: - case XKB_KEY_KP_Down: - if (vte->flags & FLAG_CURSOR_KEY_MODE) - vte_write(vte, "\eOB", 3); - else - vte_write(vte, "\e[B", 3); - return true; - case XKB_KEY_Right: - case XKB_KEY_KP_Right: - if (vte->flags & FLAG_CURSOR_KEY_MODE) - vte_write(vte, "\eOC", 3); - else - vte_write(vte, "\e[C", 3); - return true; - case XKB_KEY_Left: - case XKB_KEY_KP_Left: - if (vte->flags & FLAG_CURSOR_KEY_MODE) - vte_write(vte, "\eOD", 3); - else - vte_write(vte, "\e[D", 3); - return true; - case XKB_KEY_KP_Insert: - case XKB_KEY_KP_0: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOp", 3); - else - vte_write(vte, "0", 1); - return true; - case XKB_KEY_KP_1: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOq", 3); - else - vte_write(vte, "1", 1); - return true; - case XKB_KEY_KP_2: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOr", 3); - else - vte_write(vte, "2", 1); - return true; - case XKB_KEY_KP_3: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOs", 3); - else - vte_write(vte, "3", 1); - return true; - case XKB_KEY_KP_4: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOt", 3); - else - vte_write(vte, "4", 1); - return true; - case XKB_KEY_KP_5: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOu", 3); - else - vte_write(vte, "5", 1); - return true; - case XKB_KEY_KP_6: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOv", 3); - else - vte_write(vte, "6", 1); - return true; - case XKB_KEY_KP_7: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOw", 3); - else - vte_write(vte, "7", 1); - return true; - case XKB_KEY_KP_8: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOx", 3); - else - vte_write(vte, "8", 1); - return true; - case XKB_KEY_KP_9: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOy", 3); - else - vte_write(vte, "9", 1); - return true; - case XKB_KEY_KP_Subtract: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOm", 3); - else - vte_write(vte, "-", 1); - return true; - case XKB_KEY_KP_Separator: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOl", 3); - else - vte_write(vte, ",", 1); - return true; - case XKB_KEY_KP_Delete: - case XKB_KEY_KP_Decimal: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOn", 3); - else - vte_write(vte, ".", 1); - return true; - case XKB_KEY_KP_Equal: - case XKB_KEY_KP_Divide: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOj", 3); - else - vte_write(vte, "/", 1); - return true; - case XKB_KEY_KP_Multiply: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOo", 3); - else - vte_write(vte, "*", 1); - return true; - case XKB_KEY_KP_Add: - if (vte->flags & FLAG_KEYPAD_APPLICATION_MODE) - vte_write(vte, "\eOk", 3); - else - vte_write(vte, "+", 1); - return true; - case XKB_KEY_Home: - case XKB_KEY_KP_Home: - if (vte->flags & FLAG_CURSOR_KEY_MODE) - vte_write(vte, "\eOH", 3); - else - vte_write(vte, "\e[H", 3); - return true; - case XKB_KEY_End: - case XKB_KEY_KP_End: - if (vte->flags & FLAG_CURSOR_KEY_MODE) - vte_write(vte, "\eOF", 3); - else - vte_write(vte, "\e[F", 3); - return true; - case XKB_KEY_KP_Space: - vte_write(vte, " ", 1); - return true; - /* TODO: check what to transmit for functions keys when - * shift/ctrl etc. are pressed. Every terminal behaves - * differently here which is really weird. - * We now map F4 to F14 if shift is pressed and so on for all - * keys. However, such mappings should rather be done via - * xkb-configurations and we should instead add a flags argument - * to the CSIs as some of the keys here already do. */ - case XKB_KEY_F1: - case XKB_KEY_KP_F1: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[23~", 5); - else - vte_write(vte, "\eOP", 3); - return true; - case XKB_KEY_F2: - case XKB_KEY_KP_F2: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[24~", 5); - else - vte_write(vte, "\eOQ", 3); - return true; - case XKB_KEY_F3: - case XKB_KEY_KP_F3: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[25~", 5); - else - vte_write(vte, "\eOR", 3); - return true; - case XKB_KEY_F4: - case XKB_KEY_KP_F4: - if (mods & TSM_SHIFT_MASK) - //vte_write(vte, "\e[1;2S", 6); - vte_write(vte, "\e[26~", 5); - else - vte_write(vte, "\eOS", 3); - return true; - case XKB_KEY_F5: - if (mods & TSM_SHIFT_MASK) - //vte_write(vte, "\e[15;2~", 7); - vte_write(vte, "\e[28~", 5); - else - vte_write(vte, "\e[15~", 5); - return true; - case XKB_KEY_F6: - if (mods & TSM_SHIFT_MASK) - //vte_write(vte, "\e[17;2~", 7); - vte_write(vte, "\e[29~", 5); - else - vte_write(vte, "\e[17~", 5); - return true; - case XKB_KEY_F7: - if (mods & TSM_SHIFT_MASK) - //vte_write(vte, "\e[18;2~", 7); - vte_write(vte, "\e[31~", 5); - else - vte_write(vte, "\e[18~", 5); - return true; - case XKB_KEY_F8: - if (mods & TSM_SHIFT_MASK) - //vte_write(vte, "\e[19;2~", 7); - vte_write(vte, "\e[32~", 5); - else - vte_write(vte, "\e[19~", 5); - return true; - case XKB_KEY_F9: - if (mods & TSM_SHIFT_MASK) - //vte_write(vte, "\e[20;2~", 7); - vte_write(vte, "\e[33~", 5); - else - vte_write(vte, "\e[20~", 5); - return true; - case XKB_KEY_F10: - if (mods & TSM_SHIFT_MASK) - //vte_write(vte, "\e[21;2~", 7); - vte_write(vte, "\e[34~", 5); - else - vte_write(vte, "\e[21~", 5); - return true; - case XKB_KEY_F11: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[23;2~", 7); - else - vte_write(vte, "\e[23~", 5); - return true; - case XKB_KEY_F12: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[24;2~", 7); - else - vte_write(vte, "\e[24~", 5); - return true; - case XKB_KEY_F13: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[25;2~", 7); - else - vte_write(vte, "\e[25~", 5); - return true; - case XKB_KEY_F14: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[26;2~", 7); - else - vte_write(vte, "\e[26~", 5); - return true; - case XKB_KEY_F15: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[28;2~", 7); - else - vte_write(vte, "\e[28~", 5); - return true; - case XKB_KEY_F16: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[29;2~", 7); - else - vte_write(vte, "\e[29~", 5); - return true; - case XKB_KEY_F17: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[31;2~", 7); - else - vte_write(vte, "\e[31~", 5); - return true; - case XKB_KEY_F18: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[32;2~", 7); - else - vte_write(vte, "\e[32~", 5); - return true; - case XKB_KEY_F19: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[33;2~", 7); - else - vte_write(vte, "\e[33~", 5); - return true; - case XKB_KEY_F20: - if (mods & TSM_SHIFT_MASK) - vte_write(vte, "\e[34;2~", 7); - else - vte_write(vte, "\e[34~", 5); - return true; - } - - if (unicode != TSM_VTE_INVALID) { - if (vte->flags & FLAG_7BIT_MODE) { - val = unicode; - if (unicode & 0x80) { - llog_debug(vte, "invalid keyboard input in 7bit mode U+%x; mapping to '?'", - unicode); - val = '?'; - } - vte_write(vte, &val, 1); - } else if (vte->flags & FLAG_8BIT_MODE) { - val = unicode; - if (unicode > 0xff) { - llog_debug(vte, "invalid keyboard input in 8bit mode U+%x; mapping to '?'", - unicode); - val = '?'; - } - vte_write_raw(vte, &val, 1); - } else { - len = tsm_ucs4_to_utf8(tsm_symbol_make(unicode), u8); - vte_write_raw(vte, u8, len); - } - return true; - } - - vte->flags &= ~FLAG_PREPEND_ESCAPE; - return false; -} diff --git a/src/tsm_vte.h b/src/tsm_vte.h deleted file mode 100644 index 319f063..0000000 --- a/src/tsm_vte.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * TSM - 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 - * screen state-machine as output and is tightly bound to it. - */ - -#ifndef TSM_VTE_H -#define TSM_VTE_H - -#include -#include "tsm_screen.h" -#include "tsm_unicode.h" - -/* available character sets */ - -typedef tsm_symbol_t tsm_vte_charset[96]; - -extern tsm_vte_charset tsm_vte_unicode_lower; -extern tsm_vte_charset tsm_vte_unicode_upper; -extern tsm_vte_charset tsm_vte_dec_supplemental_graphics; -extern tsm_vte_charset tsm_vte_dec_special_graphics; - -/* virtual terminal emulator */ - -struct tsm_vte; - -/* keep in sync with shl_xkb_mods */ -enum tsm_vte_modifier { - TSM_SHIFT_MASK = (1 << 0), - TSM_LOCK_MASK = (1 << 1), - TSM_CONTROL_MASK = (1 << 2), - TSM_ALT_MASK = (1 << 3), - TSM_LOGO_MASK = (1 << 4), -}; - -/* keep in sync with TSM_INPUT_INVALID */ -#define TSM_VTE_INVALID 0xffffffff - -typedef void (*tsm_vte_write_cb) (struct tsm_vte *vte, - const char *u8, - size_t len, - void *data); - -int tsm_vte_new(struct tsm_vte **out, struct tsm_screen *con, - tsm_vte_write_cb write_cb, void *data, - tsm_log_t log, void *log_data); -void tsm_vte_ref(struct tsm_vte *vte); -void tsm_vte_unref(struct tsm_vte *vte); - -int tsm_vte_set_palette(struct tsm_vte *vte, const char *palette); - -void tsm_vte_reset(struct tsm_vte *vte); -void tsm_vte_hard_reset(struct tsm_vte *vte); -void tsm_vte_input(struct tsm_vte *vte, const char *u8, size_t len); -bool tsm_vte_handle_keyboard(struct tsm_vte *vte, uint32_t keysym, - uint32_t ascii, unsigned int mods, - uint32_t unicode); - -#endif /* TSM_VTE_H */ diff --git a/src/tsm_vte_charsets.c b/src/tsm_vte_charsets.c deleted file mode 100644 index cf539c9..0000000 --- a/src/tsm_vte_charsets.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * TSM - VT Emulator - * - * Copyright (c) 2012 David Herrmann - * - * 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. - */ - -/* - * VTE Character Sets - * These are predefined charactersets that can be loaded into GL and GR. By - * default we use unicode_lower and unicode_upper, that is, both sets have the - * exact unicode mapping. unicode_lower is effectively ASCII and unicode_upper - * as defined by the unicode standard. - * Several other character sets are defined here. However, all of them are - * limited to the 96 character space of GL or GR. Everything beyond GR (which - * was not supported by the classic VTs by DEC but is available in VT emulators - * that support unicode/UTF8) is always mapped to unicode and cannot be changed - * by these character sets. Even mapping GL and GR is only available for - * backwards compatibility as new applications can use the Unicode functionality - * of the VTE. - * - * Moreover, mapping GR is almost unnecessary to support. In fact, Unicode UTF-8 - * support in VTE works by reading every incoming data as UTF-8 stream. This - * maps GL/ASCII to ASCII, as UTF-8 is backwards compatible to ASCII, however, - * everything that has the 8th bit set is a >=2-byte haracter in UTF-8. That is, - * this is in no way backwards compatible to >=VT220 8bit support. Therefore, if - * someone maps a character set into GR and wants to use them with this VTE, - * then they must already send UTF-8 characters to use GR (all GR characters are - * 8-bits). Hence, they can easily also send the correct UTF-8 character for the - * unicode mapping. - * The only advantage is that most characters in many sets are 3-byte UTF-8 - * characters and by mapping the set into GR/GL you can use 2 or 1 byte UTF-8 - * characters which saves bandwidth. - * Another reason is, if you have older applications that use the VT220 8-bit - * support and you put a ASCII/8bit-extension to UTF-8 converter in between, you - * need these mappings to have the application behave correctly if it uses GL/GR - * mappings extensively. - * - * Anyway, we support GL/GR mappings so here are the most commonly used maps as - * defined by Unicode-standard, DEC-private maps and other famous charmaps. - * - * Characters 1-32 are always the control characters (part of CL) and cannot be - * mapped. Characters 34-127 (94 characters) are part of GL and can be mapped. - * Characters 33 and 128 are not part of GL and always mapped by VTE but are - * included here in the maps for alignment reasons but always set to 0. - */ - -#include -#include -#include -#include "shl_misc.h" -#include "tsm_vte.h" - -/* - * Lower Unicode character set. This maps the characters to the basic ASCII - * characters 33-126. These are all graphics characters defined in ASCII. The - * first an last entry are never used so we can safely set them to anything. - */ -SHL_EXPORT -tsm_vte_charset tsm_vte_unicode_lower = { - [0] = 0, - [1] = 33, - [2] = 34, - [3] = 35, - [4] = 36, - [5] = 37, - [6] = 38, - [7] = 39, - [8] = 40, - [9] = 41, - [10] = 42, - [11] = 43, - [12] = 44, - [13] = 45, - [14] = 46, - [15] = 47, - [16] = 48, - [17] = 49, - [18] = 50, - [19] = 51, - [20] = 52, - [21] = 53, - [22] = 54, - [23] = 55, - [24] = 56, - [25] = 57, - [26] = 58, - [27] = 59, - [28] = 60, - [29] = 61, - [30] = 62, - [31] = 63, - [32] = 64, - [33] = 65, - [34] = 66, - [35] = 67, - [36] = 68, - [37] = 69, - [38] = 70, - [39] = 71, - [40] = 72, - [41] = 73, - [42] = 74, - [43] = 75, - [44] = 76, - [45] = 77, - [46] = 78, - [47] = 79, - [48] = 80, - [49] = 81, - [50] = 82, - [51] = 83, - [52] = 84, - [53] = 85, - [54] = 86, - [55] = 87, - [56] = 88, - [57] = 89, - [58] = 90, - [59] = 91, - [60] = 92, - [61] = 93, - [62] = 94, - [63] = 95, - [64] = 96, - [65] = 97, - [66] = 98, - [67] = 99, - [68] = 100, - [69] = 101, - [70] = 102, - [71] = 103, - [72] = 104, - [73] = 105, - [74] = 106, - [75] = 107, - [76] = 108, - [77] = 109, - [78] = 110, - [79] = 111, - [80] = 112, - [81] = 113, - [82] = 114, - [83] = 115, - [84] = 116, - [85] = 117, - [86] = 118, - [87] = 119, - [88] = 120, - [89] = 121, - [90] = 122, - [91] = 123, - [92] = 124, - [93] = 125, - [94] = 126, - [95] = 0, -}; - -/* - * Upper Unicode Table - * This maps all characters to the upper unicode characters 161-254. These are - * not compatible to any older 8 bit character sets. See the Unicode standard - * for the definitions of each symbol. Again, the first an last entry are never - * used so set them to 0. - */ -SHL_EXPORT -tsm_vte_charset tsm_vte_unicode_upper = { - [0] = 0, - [1] = 161, - [2] = 162, - [3] = 163, - [4] = 164, - [5] = 165, - [6] = 166, - [7] = 167, - [8] = 168, - [9] = 169, - [10] = 170, - [11] = 171, - [12] = 172, - [13] = 173, - [14] = 174, - [15] = 175, - [16] = 176, - [17] = 177, - [18] = 178, - [19] = 179, - [20] = 180, - [21] = 181, - [22] = 182, - [23] = 183, - [24] = 184, - [25] = 185, - [26] = 186, - [27] = 187, - [28] = 188, - [29] = 189, - [30] = 190, - [31] = 191, - [32] = 192, - [33] = 193, - [34] = 194, - [35] = 195, - [36] = 196, - [37] = 197, - [38] = 198, - [39] = 199, - [40] = 200, - [41] = 201, - [42] = 202, - [43] = 203, - [44] = 204, - [45] = 205, - [46] = 206, - [47] = 207, - [48] = 208, - [49] = 209, - [50] = 210, - [51] = 211, - [52] = 212, - [53] = 213, - [54] = 214, - [55] = 215, - [56] = 216, - [57] = 217, - [58] = 218, - [59] = 219, - [60] = 220, - [61] = 221, - [62] = 222, - [63] = 223, - [64] = 224, - [65] = 225, - [66] = 226, - [67] = 227, - [68] = 228, - [69] = 229, - [70] = 230, - [71] = 231, - [72] = 232, - [73] = 233, - [74] = 234, - [75] = 235, - [76] = 236, - [77] = 237, - [78] = 238, - [79] = 239, - [80] = 240, - [81] = 241, - [82] = 242, - [83] = 243, - [84] = 244, - [85] = 245, - [86] = 246, - [87] = 247, - [88] = 248, - [89] = 249, - [90] = 250, - [91] = 251, - [92] = 252, - [93] = 253, - [94] = 254, - [95] = 0, -}; - -/* - * The DEC supplemental graphics set. For its definition see here: - * http://vt100.net/docs/vt220-rm/table2-3b.html - * Its basically a mixture of common European symbols that are not part of - * ASCII. Most often, this is mapped into GR to extend the basci ASCII part. - * - * This is very similar to unicode_upper, however, few symbols differ so do not - * mix them up! - */ -SHL_EXPORT -tsm_vte_charset tsm_vte_dec_supplemental_graphics = { - [0] = 0, - [1] = 161, - [2] = 162, - [3] = 163, - [4] = 0, - [5] = 165, - [6] = 0, - [7] = 167, - [8] = 164, - [9] = 169, - [10] = 170, - [11] = 171, - [12] = 0, - [13] = 0, - [14] = 0, - [15] = 0, - [16] = 176, - [17] = 177, - [18] = 178, - [19] = 179, - [20] = 0, - [21] = 181, - [22] = 182, - [23] = 183, - [24] = 0, - [25] = 185, - [26] = 186, - [27] = 187, - [28] = 188, - [29] = 189, - [30] = 0, - [31] = 191, - [32] = 192, - [33] = 193, - [34] = 194, - [35] = 195, - [36] = 196, - [37] = 197, - [38] = 198, - [39] = 199, - [40] = 200, - [41] = 201, - [42] = 202, - [43] = 203, - [44] = 204, - [45] = 205, - [46] = 206, - [47] = 207, - [48] = 0, - [49] = 209, - [50] = 210, - [51] = 211, - [52] = 212, - [53] = 213, - [54] = 214, - [55] = 338, - [56] = 216, - [57] = 217, - [58] = 218, - [59] = 219, - [60] = 220, - [61] = 376, - [62] = 0, - [63] = 223, - [64] = 224, - [65] = 225, - [66] = 226, - [67] = 227, - [68] = 228, - [69] = 229, - [70] = 230, - [71] = 231, - [72] = 232, - [73] = 233, - [74] = 234, - [75] = 235, - [76] = 236, - [77] = 237, - [78] = 238, - [79] = 239, - [80] = 0, - [81] = 241, - [82] = 242, - [83] = 243, - [84] = 244, - [85] = 245, - [86] = 246, - [87] = 339, - [88] = 248, - [89] = 249, - [90] = 250, - [91] = 251, - [92] = 252, - [93] = 255, - [94] = 0, - [95] = 0, -}; - -/* - * DEC special graphics character set. See here for its definition: - * http://vt100.net/docs/vt220-rm/table2-4.html - * This contains several characters to create ASCII drawings and similar. Its - * commonly mapped into GR to extend the basic ASCII characters. - * - * Lower 62 characters map to ASCII 33-64, everything beyond is special and - * commonly used for ASCII drawings. It depends on the Unicode Standard 3.2 for - * the extended horizontal scan-line characters 3, 5, 7, and 9. - */ -SHL_EXPORT -tsm_vte_charset tsm_vte_dec_special_graphics = { - [0] = 0, - [1] = 33, - [2] = 34, - [3] = 35, - [4] = 36, - [5] = 37, - [6] = 38, - [7] = 39, - [8] = 40, - [9] = 41, - [10] = 42, - [11] = 43, - [12] = 44, - [13] = 45, - [14] = 46, - [15] = 47, - [16] = 48, - [17] = 49, - [18] = 50, - [19] = 51, - [20] = 52, - [21] = 53, - [22] = 54, - [23] = 55, - [24] = 56, - [25] = 57, - [26] = 58, - [27] = 59, - [28] = 60, - [29] = 61, - [30] = 62, - [31] = 63, - [32] = 64, - [33] = 65, - [34] = 66, - [35] = 67, - [36] = 68, - [37] = 69, - [38] = 70, - [39] = 71, - [40] = 72, - [41] = 73, - [42] = 74, - [43] = 75, - [44] = 76, - [45] = 77, - [46] = 78, - [47] = 79, - [48] = 80, - [49] = 81, - [50] = 82, - [51] = 83, - [52] = 84, - [53] = 85, - [54] = 86, - [55] = 87, - [56] = 88, - [57] = 89, - [58] = 90, - [59] = 91, - [60] = 92, - [61] = 93, - [62] = 94, - [63] = 0, - [64] = 9830, - [65] = 9618, - [66] = 9225, - [67] = 9228, - [68] = 9229, - [69] = 9226, - [70] = 176, - [71] = 177, - [72] = 9252, - [73] = 9227, - [74] = 9496, - [75] = 9488, - [76] = 9484, - [77] = 9492, - [78] = 9532, - [79] = 9146, - [80] = 9147, - [81] = 9472, - [82] = 9148, - [83] = 9149, - [84] = 9500, - [85] = 9508, - [86] = 9524, - [87] = 9516, - [88] = 9474, - [89] = 8804, - [90] = 8805, - [91] = 960, - [92] = 8800, - [93] = 163, - [94] = 8901, - [95] = 0, -}; -- cgit v1.2.3