From e4f25e4a8db2c8a8a71a4ba2a68540b3ab341e42 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 7 Jul 2017 11:54:15 +0200 Subject: patch 8.0.0693: no terminal emulator support Problem: No terminal emulator support. Cannot properly run commands in the GUI. Cannot run a job interactively with an ssh connection. Solution: Very early implementation of the :terminal command. Includes libvterm converted to ANSI C. Many parts still missing. --- Filelist | 72 ++ runtime/doc/Makefile | 2 + runtime/doc/terminal.txt | 130 ++ src/Makefile | 57 +- src/auto/configure | 33 + src/config.h.in | 3 + src/config.mk.in | 2 + src/configure.ac | 22 + src/evalfunc.c | 3 + src/ex_cmdidxs.h | 16 +- src/ex_docmd.c | 3 + src/feature.h | 7 + src/libvterm/.bzrignore | 13 + src/libvterm/.gitignore | 18 + src/libvterm/LICENSE | 23 + src/libvterm/Makefile | 145 +++ src/libvterm/README | 13 + src/libvterm/bin/unterm.c | 287 +++++ src/libvterm/bin/vterm-ctrl.c | 362 ++++++ src/libvterm/bin/vterm-dump.c | 231 ++++ src/libvterm/doc/URLs | 11 + src/libvterm/doc/seqs.txt | 226 ++++ src/libvterm/include/vterm.h | 370 ++++++ src/libvterm/include/vterm_keycodes.h | 58 + src/libvterm/src/encoding.c | 232 ++++ src/libvterm/src/encoding/DECdrawing.tbl | 31 + src/libvterm/src/encoding/uk.tbl | 1 + src/libvterm/src/keyboard.c | 228 ++++ src/libvterm/src/mouse.c | 96 ++ src/libvterm/src/parser.c | 346 ++++++ src/libvterm/src/pen.c | 504 ++++++++ src/libvterm/src/rect.h | 56 + src/libvterm/src/screen.c | 937 ++++++++++++++ src/libvterm/src/state.c | 1851 ++++++++++++++++++++++++++++ src/libvterm/src/unicode.c | 331 +++++ src/libvterm/src/utf8.h | 47 + src/libvterm/src/vterm.c | 385 ++++++ src/libvterm/src/vterm_internal.h | 237 ++++ src/libvterm/t/02parser.test | 200 +++ src/libvterm/t/03encoding_utf8.test | 122 ++ src/libvterm/t/10state_putglyph.test | 55 + src/libvterm/t/11state_movecursor.test | 224 ++++ src/libvterm/t/12state_scroll.test | 150 +++ src/libvterm/t/13state_edit.test | 300 +++++ src/libvterm/t/14state_encoding.test | 105 ++ src/libvterm/t/15state_mode.test | 86 ++ src/libvterm/t/16state_resize.test | 48 + src/libvterm/t/17state_mouse.test | 172 +++ src/libvterm/t/18state_termprops.test | 36 + src/libvterm/t/20state_wrapping.test | 69 ++ src/libvterm/t/21state_tabstops.test | 60 + src/libvterm/t/22state_save.test | 64 + src/libvterm/t/25state_input.test | 132 ++ src/libvterm/t/26state_query.test | 62 + src/libvterm/t/27state_reset.test | 32 + src/libvterm/t/28state_dbl_wh.test | 61 + src/libvterm/t/29state_fallback.test | 19 + src/libvterm/t/30pen.test | 106 ++ src/libvterm/t/40screen_ascii.test | 69 ++ src/libvterm/t/41screen_unicode.test | 47 + src/libvterm/t/42screen_damage.test | 155 +++ src/libvterm/t/43screen_resize.test | 90 ++ src/libvterm/t/44screen_pen.test | 55 + src/libvterm/t/45screen_protect.test | 16 + src/libvterm/t/46screen_extent.test | 11 + src/libvterm/t/47screen_dbl_wh.test | 32 + src/libvterm/t/48screen_termprops.test | 17 + src/libvterm/t/90vttest_01-movement-1.test | 87 ++ src/libvterm/t/90vttest_01-movement-2.test | 40 + src/libvterm/t/90vttest_01-movement-3.test | 21 + src/libvterm/t/90vttest_01-movement-4.test | 36 + src/libvterm/t/90vttest_02-screen-1.test | 18 + src/libvterm/t/90vttest_02-screen-2.test | 29 + src/libvterm/t/90vttest_02-screen-3.test | 16 + src/libvterm/t/90vttest_02-screen-4.test | 17 + src/libvterm/t/92lp1640917.test | 13 + src/libvterm/t/harness.c | 929 ++++++++++++++ src/libvterm/t/run-test.pl | 196 +++ src/libvterm/tbl2inc_c.pl | 51 + src/libvterm/vterm.pc.in | 9 + src/option.c | 28 +- src/option.h | 3 + src/proto.h | 3 + src/proto/terminal.pro | 3 + src/structs.h | 10 + src/terminal.c | 211 ++++ src/version.c | 2 + 87 files changed, 11693 insertions(+), 13 deletions(-) create mode 100644 runtime/doc/terminal.txt create mode 100644 src/libvterm/.bzrignore create mode 100644 src/libvterm/.gitignore create mode 100644 src/libvterm/LICENSE create mode 100644 src/libvterm/Makefile create mode 100644 src/libvterm/README create mode 100644 src/libvterm/bin/unterm.c create mode 100644 src/libvterm/bin/vterm-ctrl.c create mode 100644 src/libvterm/bin/vterm-dump.c create mode 100644 src/libvterm/doc/URLs create mode 100644 src/libvterm/doc/seqs.txt create mode 100644 src/libvterm/include/vterm.h create mode 100644 src/libvterm/include/vterm_keycodes.h create mode 100644 src/libvterm/src/encoding.c create mode 100644 src/libvterm/src/encoding/DECdrawing.tbl create mode 100644 src/libvterm/src/encoding/uk.tbl create mode 100644 src/libvterm/src/keyboard.c create mode 100644 src/libvterm/src/mouse.c create mode 100644 src/libvterm/src/parser.c create mode 100644 src/libvterm/src/pen.c create mode 100644 src/libvterm/src/rect.h create mode 100644 src/libvterm/src/screen.c create mode 100644 src/libvterm/src/state.c create mode 100644 src/libvterm/src/unicode.c create mode 100644 src/libvterm/src/utf8.h create mode 100644 src/libvterm/src/vterm.c create mode 100644 src/libvterm/src/vterm_internal.h create mode 100644 src/libvterm/t/02parser.test create mode 100644 src/libvterm/t/03encoding_utf8.test create mode 100644 src/libvterm/t/10state_putglyph.test create mode 100644 src/libvterm/t/11state_movecursor.test create mode 100644 src/libvterm/t/12state_scroll.test create mode 100644 src/libvterm/t/13state_edit.test create mode 100644 src/libvterm/t/14state_encoding.test create mode 100644 src/libvterm/t/15state_mode.test create mode 100644 src/libvterm/t/16state_resize.test create mode 100644 src/libvterm/t/17state_mouse.test create mode 100644 src/libvterm/t/18state_termprops.test create mode 100644 src/libvterm/t/20state_wrapping.test create mode 100644 src/libvterm/t/21state_tabstops.test create mode 100644 src/libvterm/t/22state_save.test create mode 100644 src/libvterm/t/25state_input.test create mode 100644 src/libvterm/t/26state_query.test create mode 100644 src/libvterm/t/27state_reset.test create mode 100644 src/libvterm/t/28state_dbl_wh.test create mode 100644 src/libvterm/t/29state_fallback.test create mode 100644 src/libvterm/t/30pen.test create mode 100644 src/libvterm/t/40screen_ascii.test create mode 100644 src/libvterm/t/41screen_unicode.test create mode 100644 src/libvterm/t/42screen_damage.test create mode 100644 src/libvterm/t/43screen_resize.test create mode 100644 src/libvterm/t/44screen_pen.test create mode 100644 src/libvterm/t/45screen_protect.test create mode 100644 src/libvterm/t/46screen_extent.test create mode 100644 src/libvterm/t/47screen_dbl_wh.test create mode 100644 src/libvterm/t/48screen_termprops.test create mode 100644 src/libvterm/t/90vttest_01-movement-1.test create mode 100644 src/libvterm/t/90vttest_01-movement-2.test create mode 100644 src/libvterm/t/90vttest_01-movement-3.test create mode 100644 src/libvterm/t/90vttest_01-movement-4.test create mode 100644 src/libvterm/t/90vttest_02-screen-1.test create mode 100644 src/libvterm/t/90vttest_02-screen-2.test create mode 100644 src/libvterm/t/90vttest_02-screen-3.test create mode 100644 src/libvterm/t/90vttest_02-screen-4.test create mode 100644 src/libvterm/t/92lp1640917.test create mode 100644 src/libvterm/t/harness.c create mode 100644 src/libvterm/t/run-test.pl create mode 100644 src/libvterm/tbl2inc_c.pl create mode 100644 src/libvterm/vterm.pc.in create mode 100644 src/proto/terminal.pro create mode 100644 src/terminal.c diff --git a/Filelist b/Filelist index 6d8e2e8896..5038e67687 100644 --- a/Filelist +++ b/Filelist @@ -85,6 +85,7 @@ SRC_ALL = \ src/syntax.c \ src/tag.c \ src/term.c \ + src/terminal.c \ src/term.h \ src/termlib.c \ src/ui.c \ @@ -187,6 +188,7 @@ SRC_ALL = \ src/proto/syntax.pro \ src/proto/tag.pro \ src/proto/term.pro \ + src/proto/terminal.pro \ src/proto/termlib.pro \ src/proto/ui.pro \ src/proto/undo.pro \ @@ -194,6 +196,76 @@ SRC_ALL = \ src/proto/version.pro \ src/proto/winclip.pro \ src/proto/window.pro \ + src/libvterm/.bzrignore \ + src/libvterm/.gitignore \ + src/libvterm/LICENSE \ + src/libvterm/Makefile \ + src/libvterm/README \ + src/libvterm/tbl2inc_c.pl \ + src/libvterm/vterm.pc.in \ + src/libvterm/bin/unterm.c \ + src/libvterm/bin/vterm-ctrl.c \ + src/libvterm/bin/vterm-dump.c \ + src/libvterm/doc/URLs \ + src/libvterm/doc/seqs.txt \ + src/libvterm/include/vterm.h \ + src/libvterm/include/vterm_keycodes.h \ + src/libvterm/src/encoding.c \ + src/libvterm/src/encoding/DECdrawing.inc \ + src/libvterm/src/encoding/DECdrawing.tbl \ + src/libvterm/src/encoding/uk.inc \ + src/libvterm/src/encoding/uk.tbl \ + src/libvterm/src/keyboard.c \ + src/libvterm/src/mouse.c \ + src/libvterm/src/parser.c \ + src/libvterm/src/pen.c \ + src/libvterm/src/rect.h \ + src/libvterm/src/screen.c \ + src/libvterm/src/state.c \ + src/libvterm/src/unicode.c \ + src/libvterm/src/utf8.h \ + src/libvterm/src/vterm.c \ + src/libvterm/src/vterm_internal.h \ + src/libvterm/t/02parser.test \ + src/libvterm/t/03encoding_utf8.test \ + src/libvterm/t/10state_putglyph.test \ + src/libvterm/t/11state_movecursor.test \ + src/libvterm/t/12state_scroll.test \ + src/libvterm/t/13state_edit.test \ + src/libvterm/t/14state_encoding.test \ + src/libvterm/t/15state_mode.test \ + src/libvterm/t/16state_resize.test \ + src/libvterm/t/17state_mouse.test \ + src/libvterm/t/18state_termprops.test \ + src/libvterm/t/20state_wrapping.test \ + src/libvterm/t/21state_tabstops.test \ + src/libvterm/t/22state_save.test \ + src/libvterm/t/25state_input.test \ + src/libvterm/t/26state_query.test \ + src/libvterm/t/27state_reset.test \ + src/libvterm/t/28state_dbl_wh.test \ + src/libvterm/t/29state_fallback.test \ + src/libvterm/t/30pen.test \ + src/libvterm/t/40screen_ascii.test \ + src/libvterm/t/41screen_unicode.test \ + src/libvterm/t/42screen_damage.test \ + src/libvterm/t/43screen_resize.test \ + src/libvterm/t/44screen_pen.test \ + src/libvterm/t/45screen_protect.test \ + src/libvterm/t/46screen_extent.test \ + src/libvterm/t/47screen_dbl_wh.test \ + src/libvterm/t/48screen_termprops.test \ + src/libvterm/t/90vttest_01-movement-1.test \ + src/libvterm/t/90vttest_01-movement-2.test \ + src/libvterm/t/90vttest_01-movement-3.test \ + src/libvterm/t/90vttest_01-movement-4.test \ + src/libvterm/t/90vttest_02-screen-1.test \ + src/libvterm/t/90vttest_02-screen-2.test \ + src/libvterm/t/90vttest_02-screen-3.test \ + src/libvterm/t/90vttest_02-screen-4.test \ + src/libvterm/t/92lp1640917.test \ + src/libvterm/t/harness.c \ + src/libvterm/t/run-test.pl \ # source files for Unix only diff --git a/runtime/doc/Makefile b/runtime/doc/Makefile index 1a0e841973..46b6125a9e 100644 --- a/runtime/doc/Makefile +++ b/runtime/doc/Makefile @@ -101,6 +101,7 @@ DOCS = \ tabpage.txt \ tagsrch.txt \ term.txt \ + terminal.txt \ tips.txt \ todo.txt \ uganda.txt \ @@ -236,6 +237,7 @@ HTMLS = \ tabpage.html \ tagsrch.html \ term.html \ + terminal.html \ tips.html \ todo.html \ uganda.html \ diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt new file mode 100644 index 0000000000..c16a793193 --- /dev/null +++ b/runtime/doc/terminal.txt @@ -0,0 +1,130 @@ +*terminal.txt* For Vim version 8.0. Last change: 2017 Jul 04 + + + VIM REFERENCE MANUAL by Bram Moolenaar + + +Terminal window support *terminal* + + +WARNING: THIS IS ONLY PARTLY IMPLEMENTED, ANYTHING CAN STILL CHANGE + + +1. Basic use |terminal-use| +2. Remote testing |terminal-testing| +3. Debugging |terminal-debug| + +{Vi does not have any of these commands} + +============================================================================== +1. Basic use *terminal-use* + +This feature is for running a terminal emulator in a Vim window. A job can be +started connected to the terminal emulator. For example, to run a shell: > + :term bash + +Or to run a debugger: > + :term gdb vim + +The job runs asynchronously from Vim, the window will be updated to show +output from the job, also while editing in any other window. + +When the keyboard focus is in the terminal window, typed keys will be send to +the job. This uses a pty when possible. + +Navigate between windows with CTRL-W commands (and mouse). +E.g. CTRL-W CTRL-W moves focus to the next window. + +Option 'termkey' +Specify key for Vim command in terminal window. local to window. +Default is CTRL-W. + +Option 'termsize' +Specify terminal size. Local to window. +When empty the terminal gets the size from the window. +When set (e.g., "24x80") the terminal size is fixed. If the window is smaller +only the top-left part is displayed. (TODO: scrolling?) + +Syntax ~ + *:ter* *:terminal* +:terminal[!] [command] Open a new terminal window. + + If [command] is provided run it as a job and connect + the input and output to the terminal. + If [command] is not given the 'shell' option is used. + + A new buffer will be created, using [command] or + 'shell' as the name. If a buffer by this name already + exists a number is added in parenthesis. + E.g. if "gdb" exists the second terminal buffer will + use "gdb (1)". + + The window can be closed, in which case the buffer + becomes hidden. The command will not be stopped. The + `:buffer` command can be used to turn the current + window into a terminal window, using the existing + buffer. If there are unsaved changes this fails, use + ! to force, as usual. + +Resizing ~ + +The size of the terminal can be in one of three modes: + +1. The 'termsize' option is empty: The terminal size follows the window size. + The minimal size is 2 screen lines with 10 cells. + +2. The 'termsize' option is "rows*cols", where "rows" is the minimal number of + screen rows and "cols" is the minial number of cells. + +3. The 'termsize' option is "rowsXcols" (where the x is upper or lower case). + The terminal size is fixed to the specified number of screen lines and + cells. If the window is bigger there will be unused empty space. + +If the window is smaller than the terminal size, only part of the terminal can +be seen (the lower-left part). + +The |term_getsize()| function can be used to get the current size of the +terminal. |term_setsize()| can be used only when in the first or second mode, +not when 'termsize' is "rowsXcols". + +============================================================================== +2. Remote testing *terminal-testing* + +Most Vim tests execute a script inside Vim. For some tests this does not +work, running the test interferes with the code being tested. To avoid this +Vim is executed in a terminal window. The test sends keystrokes to it and +inspects the resulting screen state. + +Functions ~ + +term_sendkeys() send keystrokes to a terminal +term_wait() wait for screen to be updated +term_scrape() inspect terminal screen + + +============================================================================== +3. Debugging *terminal-debug* + +The Terminal debugging plugin can be used to debug a program with gdb and view +the source code in a Vim window. For example: > + + :TermDebug vim + +This opens three windows: +- A terminal window in which "gdb vim" is executed. Here you can directly + interact with gdb. +- A terminal window for the executed program. When "run" is used in gdb the + program I/O will happen in this window, so that it does not interfere with + controlling gdb. +- A normal Vim window used to show the source code. When gdb jumps to a + source file location this window will display the code, if possible. Values + of variables can be inspected, breakpoints set and cleared, etc. + +This uses two terminal windows. To open the gdb window: > + :term gdb [arguments] +To open the terminal to run the tested program |term_open()| is used. + +TODO + + + vim:tw=78:ts=8:ft=help:norl: diff --git a/src/Makefile b/src/Makefile index 2df82ecedc..907fdb127e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -482,6 +482,11 @@ CClink = $(CC) # Uncomment this when you do not want inter process communication. #CONF_OPT_CHANNEL = --disable-channel +# TERMINAL - Terminal emulator support, :terminal command. Requires the +# channel feature. +# Uncomment this when you want terminal emulator support. +#CONF_OPT_TERMINAL = --enable-terminal + # MULTIBYTE - To edit multi-byte characters. # Uncomment this when you want to edit a multibyte language. # It's automatically enabled with normal features, GTK or IME support. @@ -598,6 +603,9 @@ CClink = $(CC) # Use this with GCC to check for mistakes, unused arguments, etc. #CFLAGS = -g -Wall -Wextra -Wshadow -Wmissing-prototypes -Wunreachable-code -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 +# Add -Wpedantic to find // comments and other C99 constructs. +# Better disable Perl and Python to avoid a lot of warnings. +#CFLAGS = -g -Wall -Wextra -Wshadow -Wmissing-prototypes -Wpedantic -Wunreachable-code -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 #CFLAGS = -g -O2 -Wall -Wextra -Wmissing-prototypes -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -DU_DEBUG #PYTHON_CFLAGS_EXTRA = -Wno-missing-field-initializers #MZSCHEME_CFLAGS_EXTRA = -Wno-unreachable-code -Wno-unused-parameter @@ -1371,6 +1379,13 @@ ALL_GUI_PRO = gui.pro gui_gtk.pro gui_motif.pro gui_xmdlg.pro gui_athena.pro gu # }}} +TERM_DEPS = \ + libvterm/include/vterm.h \ + libvterm/include/vterm_keycodes.h \ + libvterm/src/rect.h \ + libvterm/src/utf8.h \ + libvterm/src/vterm_internal.h + ### Command to create dependencies based on #include "..." ### prototype headers are ignored due to -DPROTO, system ### headers #include <...> are ignored if we use the -MM option, as @@ -1560,6 +1575,7 @@ BASIC_SRC = \ syntax.c \ tag.c \ term.c \ + terminal.c \ ui.c \ undo.c \ userfunc.c \ @@ -1569,6 +1585,7 @@ BASIC_SRC = \ SRC = $(BASIC_SRC) \ $(GUI_SRC) \ + $(TERM_SRC) \ $(HANGULIN_SRC) \ $(LUA_SRC) \ $(MZSCHEME_SRC) \ @@ -1610,7 +1627,7 @@ ALL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(UNITTEST_SRC) $(EXTRA_SRC) LINT_SRC = $(BASIC_SRC) $(GUI_SRC) $(HANGULIN_SRC) \ $(PYTHON_SRC) $(PYTHON3_SRC) $(TCL_SRC) \ $(WORKSHOP_SRC) $(WSDEBUG_SRC) \ - $(NETBEANS_SRC) $(CHANNEL_SRC) + $(NETBEANS_SRC) $(CHANNEL_SRC) $(TERM_SRC) #LINT_SRC = $(SRC) #LINT_SRC = $(ALL_SRC) #LINT_SRC = $(BASIC_SRC) @@ -1665,12 +1682,14 @@ OBJ_COMMON = \ objects/syntax.o \ objects/tag.o \ objects/term.o \ + objects/terminal.o \ objects/ui.o \ objects/undo.o \ objects/userfunc.o \ objects/version.o \ objects/window.o \ $(GUI_OBJ) \ + $(TERM_OBJ) \ $(LUA_OBJ) \ $(MZSCHEME_OBJ) \ $(PERL_OBJ) \ @@ -1795,6 +1814,7 @@ PRO_AUTO = \ syntax.pro \ tag.pro \ term.pro \ + terminal.pro \ termlib.pro \ ui.pro \ undo.pro \ @@ -1848,7 +1868,7 @@ config auto/config.mk: auto/configure config.mk.in config.h.in $(CONF_OPT_OUTPUT) $(CONF_OPT_GPM) $(CONF_OPT_WORKSHOP) \ $(CONF_OPT_FEAT) $(CONF_TERM_LIB) \ $(CONF_OPT_COMPBY) $(CONF_OPT_ACL) $(CONF_OPT_NETBEANS) \ - $(CONF_OPT_CHANNEL) \ + $(CONF_OPT_CHANNEL) $(CONF_OPT_TERMINAL) \ $(CONF_ARGS) $(CONF_OPT_MZSCHEME) $(CONF_OPT_PLTHOME) \ $(CONF_OPT_LUA) $(CONF_OPT_LUA_PREFIX) \ $(CONF_OPT_SYSMOUSE); \ @@ -3228,6 +3248,9 @@ objects/tag.o: tag.c objects/term.o: term.c $(CCC) -o $@ term.c +objects/terminal.o: terminal.c $(TERM_DEPS) + $(CCC) -o $@ terminal.c + objects/ui.o: ui.c $(CCC) -o $@ ui.c @@ -3255,6 +3278,34 @@ objects/channel.o: channel.c Makefile: @echo The name of the makefile MUST be "Makefile" (with capital M)!!!! +CCCTERM = $(CCC) -Ilibvterm/include -DINLINE="" +objects/term_encoding.o: libvterm/src/encoding.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/encoding.c + +objects/term_keyboard.o: libvterm/src/keyboard.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/keyboard.c + +objects/term_mouse.o: libvterm/src/mouse.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/mouse.c + +objects/term_parser.o: libvterm/src/parser.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/parser.c + +objects/term_pen.o: libvterm/src/pen.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/pen.c + +objects/term_screen.o: libvterm/src/screen.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/screen.c + +objects/term_state.o: libvterm/src/state.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/state.c + +objects/term_unicode.o: libvterm/src/unicode.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/unicode.c + +objects/term_vterm.o: libvterm/src/vterm.c $(TERM_DEPS) + $(CCCTERM) -o $@ libvterm/src/vterm.c + ############################################################################### ### MacOS X installation ### @@ -3399,7 +3450,7 @@ objects/ex_cmds2.o: ex_cmds2.c vim.h auto/config.h feature.h os_unix.h \ objects/ex_docmd.o: ex_docmd.c vim.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \ regexp.h gui.h gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h spell.h \ - proto.h globals.h farsi.h arabic.h + proto.h globals.h farsi.h arabic.h ex_cmdidxs.h objects/ex_eval.o: ex_eval.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \ diff --git a/src/auto/configure b/src/auto/configure index 1980a95b20..c575a44130 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -655,6 +655,8 @@ X_PRE_LIBS X_CFLAGS XMKMF xmkmfpath +TERM_OBJ +TERM_SRC CHANNEL_OBJ CHANNEL_SRC NETBEANS_OBJ @@ -814,6 +816,7 @@ enable_cscope enable_workshop enable_netbeans enable_channel +enable_terminal enable_multibyte enable_hangulinput enable_xim @@ -1491,6 +1494,7 @@ Optional Features: --enable-workshop Include Sun Visual Workshop support. --disable-netbeans Disable NetBeans integration support. --disable-channel Disable process communication support. + --enable-terminal Disable terminal emulation support. --enable-multibyte Include multibyte editing support. --enable-hangulinput Include Hangul input support. --enable-xim Include XIM input support. @@ -7464,6 +7468,35 @@ if test "$enable_channel" = "yes"; then fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-terminal argument" >&5 +$as_echo_n "checking --enable-terminal argument... " >&6; } +# Check whether --enable-terminal was given. +if test "${enable_terminal+set}" = set; then : + enableval=$enable_terminal; enable_terminal="yes" +fi + +if test "$enable_terminal" = "yes"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot use terminal emulator with tiny or small features" >&5 +$as_echo "cannot use terminal emulator with tiny or small features" >&6; } + enable_terminal="no" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +if test "$enable_terminal" = "yes"; then + $as_echo "#define FEAT_TERMINAL 1" >>confdefs.h + + TERM_SRC="libvterm/src/encoding.c libvterm/src/keyboard.c libvterm/src/mouse.c libvterm/src/parser.c libvterm/src/pen.c libvterm/src/screen.c libvterm/src/state.c libvterm/src/unicode.c libvterm/src/vterm.c" + + TERM_OBJ="objects/term_encoding.o objects/term_keyboard.o objects/term_mouse.o objects/term_parser.o objects/term_pen.o objects/term_screen.o objects/term_state.o objects/term_unicode.o objects/term_vterm.o" + +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-multibyte argument" >&5 $as_echo_n "checking --enable-multibyte argument... " >&6; } # Check whether --enable-multibyte was given. diff --git a/src/config.h.in b/src/config.h.in index a93450710f..fe68d49b14 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -431,6 +431,9 @@ /* Define if you want to include process communication. */ #undef FEAT_JOB_CHANNEL +/* Define if you want to include terminal emulator support. */ +#undef FEAT_TERMINAL + /* Define default global runtime path */ #undef RUNTIME_GLOBAL diff --git a/src/config.mk.in b/src/config.mk.in index 90a3337789..1e7fbd5106 100644 --- a/src/config.mk.in +++ b/src/config.mk.in @@ -91,6 +91,8 @@ NETBEANS_SRC = @NETBEANS_SRC@ NETBEANS_OBJ = @NETBEANS_OBJ@ CHANNEL_SRC = @CHANNEL_SRC@ CHANNEL_OBJ = @CHANNEL_OBJ@ +TERM_SRC = @TERM_SRC@ +TERM_OBJ = @TERM_OBJ@ RUBY = @vi_cv_path_ruby@ RUBY_SRC = @RUBY_SRC@ diff --git a/src/configure.ac b/src/configure.ac index 1c1b1c7e42..6ffdab2910 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -2028,6 +2028,28 @@ if test "$enable_channel" = "yes"; then AC_SUBST(CHANNEL_OBJ) fi +AC_MSG_CHECKING(--enable-terminal argument) +AC_ARG_ENABLE(terminal, + [ --enable-terminal Disable terminal emulation support.], + [enable_terminal="yes"], ) +if test "$enable_terminal" = "yes"; then + if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + AC_MSG_RESULT([cannot use terminal emulator with tiny or small features]) + enable_terminal="no" + else + AC_MSG_RESULT(yes) + fi +else + AC_MSG_RESULT(no) +fi +if test "$enable_terminal" = "yes"; then + AC_DEFINE(FEAT_TERMINAL) + TERM_SRC="libvterm/src/encoding.c libvterm/src/keyboard.c libvterm/src/mouse.c libvterm/src/parser.c libvterm/src/pen.c libvterm/src/screen.c libvterm/src/state.c libvterm/src/unicode.c libvterm/src/vterm.c" + AC_SUBST(TERM_SRC) + TERM_OBJ="objects/term_encoding.o objects/term_keyboard.o objects/term_mouse.o objects/term_parser.o objects/term_pen.o objects/term_screen.o objects/term_state.o objects/term_unicode.o objects/term_vterm.o" + AC_SUBST(TERM_OBJ) +fi + AC_MSG_CHECKING(--enable-multibyte argument) AC_ARG_ENABLE(multibyte, [ --enable-multibyte Include multibyte editing support.], , diff --git a/src/evalfunc.c b/src/evalfunc.c index 2d796f7e13..3af5cd74be 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -5870,6 +5870,9 @@ f_has(typval_T *argvars, typval_T *rettv) #ifdef FEAT_TERMGUICOLORS "termguicolors", #endif +#ifdef FEAT_TERMINAL + "terminal", +#endif #ifdef TERMINFO "terminfo", #endif diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h index 3b6120e864..4d3bcbebb5 100644 --- a/src/ex_cmdidxs.h +++ b/src/ex_cmdidxs.h @@ -25,12 +25,12 @@ static const unsigned short cmdidxs1[26] = /* r */ 351, /* s */ 370, /* t */ 437, - /* u */ 472, - /* v */ 483, - /* w */ 501, - /* x */ 516, - /* y */ 525, - /* z */ 526 + /* u */ 473, + /* v */ 484, + /* w */ 502, + /* x */ 517, + /* y */ 526, + /* z */ 527 }; /* @@ -60,7 +60,7 @@ static const unsigned char cmdidxs2[26][26] = /* q */ { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* r */ { 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 18, 0, 0, 0, 0 }, /* s */ { 2, 6, 15, 0, 18, 22, 0, 24, 25, 0, 0, 28, 30, 34, 38, 40, 0, 48, 0, 49, 0, 61, 62, 0, 63, 0 }, - /* t */ { 2, 0, 19, 0, 22, 23, 0, 24, 0, 25, 0, 26, 27, 28, 29, 30, 0, 31, 33, 0, 34, 0, 0, 0, 0, 0 }, + /* t */ { 2, 0, 19, 0, 22, 24, 0, 25, 0, 26, 0, 27, 28, 29, 30, 31, 0, 32, 34, 0, 35, 0, 0, 0, 0, 0 }, /* u */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* v */ { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 12, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0 }, /* w */ { 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, 8, 0, 9, 10, 0, 12, 0, 13, 14, 0, 0, 0, 0 }, @@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] = /* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; -static const int command_count = 539; +static const int command_count = 540; diff --git a/src/ex_docmd.c b/src/ex_docmd.c index bdd152dfd7..6ff578e655 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -488,6 +488,9 @@ static void ex_folddo(exarg_T *eap); #ifndef FEAT_PROFILE # define ex_profile ex_ni #endif +#ifndef FEAT_TERMINAL +# define ex_terminal ex_ni +#endif /* * Declare cmdnames[]. diff --git a/src/feature.h b/src/feature.h index 138279e462..f77ffd6fe0 100644 --- a/src/feature.h +++ b/src/feature.h @@ -1267,6 +1267,13 @@ # undef FEAT_JOB_CHANNEL #endif +/* + * +terminal ":terminal" command. Runs a terminal in a window. + */ +#if !defined(FEAT_JOB_CHANNEL) && defined(FEAT_TERMINAL) +# undef FEAT_TERMINAL +#endif + /* * +signs Allow signs to be displayed to the left of text lines. * Adds the ":sign" command. diff --git a/src/libvterm/.bzrignore b/src/libvterm/.bzrignore new file mode 100644 index 0000000000..e58c036693 --- /dev/null +++ b/src/libvterm/.bzrignore @@ -0,0 +1,13 @@ +.libs +*.lo +*.la + +bin/* +!bin/*.c + +pangoterm +t/test +t/suites.h +t/externs.h +t/harness +src/encoding/*.inc diff --git a/src/libvterm/.gitignore b/src/libvterm/.gitignore new file mode 100644 index 0000000000..725c6f16f4 --- /dev/null +++ b/src/libvterm/.gitignore @@ -0,0 +1,18 @@ +*~ +*.swp + +tags +src/*.o +src/*.lo +src/encoding/*.inc + +libvterm.la +bin/unterm +bin/vterm-ctrl +bin/vterm-dump + +t/harness +t/harness.lo +t/harness.o + +.libs/ diff --git a/src/libvterm/LICENSE b/src/libvterm/LICENSE new file mode 100644 index 0000000000..0d051634b2 --- /dev/null +++ b/src/libvterm/LICENSE @@ -0,0 +1,23 @@ + + +The MIT License + +Copyright (c) 2008 Paul Evans + +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. diff --git a/src/libvterm/Makefile b/src/libvterm/Makefile new file mode 100644 index 0000000000..05f68dd605 --- /dev/null +++ b/src/libvterm/Makefile @@ -0,0 +1,145 @@ +ifeq ($(shell uname),Darwin) + LIBTOOL ?= glibtool +else + LIBTOOL ?= libtool +endif + +ifneq ($(VERBOSE),1) + LIBTOOL +=--quiet +endif + +# override CFLAGS +=-Wall -Iinclude -std=c99 -DINLINE="static inline" -DUSE_INLINE +override CFLAGS +=-Wall -Iinclude -std=c90 -Wpedantic -DINLINE="" + +ifeq ($(shell uname),SunOS) + override CFLAGS +=-D__EXTENSIONS__ -D_XPG6 -D__XOPEN_OR_POSIX +endif + +ifeq ($(DEBUG),1) + override CFLAGS +=-ggdb -DDEBUG +endif + +ifeq ($(PROFILE),1) + override CFLAGS +=-pg + override LDFLAGS+=-pg +endif + +CFILES=$(sort $(wildcard src/*.c)) +HFILES=$(sort $(wildcard include/*.h)) +OBJECTS=$(CFILES:.c=.lo) +LIBRARY=libvterm.la + +BINFILES_SRC=$(sort $(wildcard bin/*.c)) +BINFILES=$(BINFILES_SRC:.c=) + +TBLFILES=$(sort $(wildcard src/encoding/*.tbl)) +INCFILES=$(TBLFILES:.tbl=.inc) + +HFILES_INT=$(sort $(wildcard src/*.h)) $(HFILES) + +VERSION_MAJOR=0 +VERSION_MINOR=0 + +VERSION_CURRENT=0 +VERSION_REVISION=0 +VERSION_AGE=0 + +VERSION=0 + +PREFIX=/usr/local +BINDIR=$(PREFIX)/bin +LIBDIR=$(PREFIX)/lib +INCDIR=$(PREFIX)/include +MANDIR=$(PREFIX)/share/man +MAN3DIR=$(MANDIR)/man3 + +all: $(LIBRARY) $(BINFILES) + +$(LIBRARY): $(OBJECTS) + @echo LINK $@ + @$(LIBTOOL) --mode=link --tag=CC $(CC) -rpath $(LIBDIR) -version-info $(VERSION_CURRENT):$(VERSION_REVISION):$(VERSION_AGE) -o $@ $^ $(LDFLAGS) + +src/%.lo: src/%.c $(HFILES_INT) + @echo CC $< + @$(LIBTOOL) --mode=compile --tag=CC $(CC) $(CFLAGS) -o $@ -c $< + +src/encoding/%.inc: src/encoding/%.tbl + @echo TBL $< + @perl -CSD tbl2inc_c.pl $< >$@ + +src/encoding.lo: $(INCFILES) + +bin/%: bin/%.c $(LIBRARY) + @echo CC $< + @$(LIBTOOL) --mode=link --tag=CC $(CC) $(CFLAGS) -o $@ $< -lvterm $(LDFLAGS) + +t/harness.lo: t/harness.c $(HFILES) + @echo CC $< + @$(LIBTOOL) --mode=compile --tag=CC $(CC) $(CFLAGS) -o $@ -c $< + +t/harness: t/harness.lo $(LIBRARY) + @echo LINK $@ + @$(LIBTOOL) --mode=link --tag=CC $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +.PHONY: test +test: $(LIBRARY) t/harness + for T in `ls t/[0-9]*.test`; do echo "** $$T **"; perl t/run-test.pl $$T $(if $(VALGRIND),--valgrind) || exit 1; done + +.PHONY: clean +clean: + $(LIBTOOL) --mode=clean rm -f $(OBJECTS) $(INCFILES) + $(LIBTOOL) --mode=clean rm -f t/harness.lo t/harness + $(LIBTOOL) --mode=clean rm -f $(LIBRARY) $(BINFILES) + +.PHONY: install +install: install-inc install-lib install-bin + +install-inc: + install -d $(DESTDIR)$(INCDIR) + install -m644 $(HFILES) $(DESTDIR)$(INCDIR) + install -d $(DESTDIR)$(LIBDIR)/pkgconfig + sed -e "s,@PREFIX@,$(PREFIX)," -e "s,@LIBDIR@,$(LIBDIR)," -e "s,@VERSION@,$(VERSION)," $(DESTDIR)$(LIBDIR)/pkgconfig/vterm.pc + +install-lib: $(LIBRARY) + install -d $(DESTDIR)$(LIBDIR) + $(LIBTOOL) --mode=install install $(LIBRARY) $(DESTDIR)$(LIBDIR)/$(LIBRARY) + $(LIBTOOL) --mode=finish $(DESTDIR)$(LIBDIR) + +install-bin: $(BINFILES) + install -d $(DESTDIR)$(BINDIR) + $(LIBTOOL) --mode=install install $(BINFILES) $(DESTDIR)$(BINDIR)/ + +# DIST CUT + +VERSION=$(VERSION_MAJOR).$(VERSION_MINOR) + +DISTDIR=libvterm-$(VERSION) + +distdir: $(INCFILES) + mkdir __distdir + cp LICENSE __distdir + mkdir __distdir/src + cp src/*.c src/*.h __distdir/src + mkdir __distdir/src/encoding + cp src/encoding/*.inc __distdir/src/encoding + mkdir __distdir/include + cp include/*.h __distdir/include + mkdir __distdir/bin + cp bin/*.c __distdir/bin + mkdir __distdir/t + cp t/*.test t/harness.c t/run-test.pl __distdir/t + sed "s,@VERSION@,$(VERSION)," __distdir/vterm.pc.in + sed "/^# DIST CUT/Q" __distdir/Makefile + mv __distdir $(DISTDIR) + +TARBALL=$(DISTDIR).tar.gz + +dist: distdir + tar -czf $(TARBALL) $(DISTDIR) + rm -rf $(DISTDIR) + +dist+bzr: + $(MAKE) dist VERSION=$(VERSION)+bzr`bzr revno` + +distdir+bzr: + $(MAKE) distdir VERSION=$(VERSION)+bzr`bzr revno` diff --git a/src/libvterm/README b/src/libvterm/README new file mode 100644 index 0000000000..ef74b011aa --- /dev/null +++ b/src/libvterm/README @@ -0,0 +1,13 @@ +This is a MODIFIED version of libvterm. + +The original can be found: +On the original site (tar archive and Bazaar repository): + http://www.leonerd.org.uk/code/libvterm/ +Cloned on Github: + https://github.com/neovim/libvterm + +Modifications: +- Add a .gitignore file. +- Convert from C99 to C90. +- Other changes to support embedding in Vim. +- diff --git a/src/libvterm/bin/unterm.c b/src/libvterm/bin/unterm.c new file mode 100644 index 0000000000..acaf2d2d44 --- /dev/null +++ b/src/libvterm/bin/unterm.c @@ -0,0 +1,287 @@ +#include +#include + +#include +#include +#include +#include + +#include "vterm.h" + +#define DEFINE_INLINES +#include "../src/utf8.h" /* fill_utf8 */ + +#define streq(a,b) (!strcmp(a,b)) + +static VTerm *vt; +static VTermScreen *vts; + +static int cols; +static int rows; + +static enum { + FORMAT_PLAIN, + FORMAT_SGR +} format = FORMAT_PLAIN; + +static int col2index(VTermColor target) +{ + int index; + + for(index = 0; index < 256; index++) { + VTermColor col; + vterm_state_get_palette_color(NULL, index, &col); + if(col.red == target.red && col.green == target.green && col.blue == target.blue) + return index; + } + return -1; +} + +static void dump_cell(const VTermScreenCell *cell, const VTermScreenCell *prevcell) +{ + switch(format) { + case FORMAT_PLAIN: + break; + case FORMAT_SGR: + { + /* If all 7 attributes change, that means 7 SGRs max */ + /* Each colour could consume up to 3 */ + int sgr[7 + 2*3]; int sgri = 0; + + if(!prevcell->attrs.bold && cell->attrs.bold) + sgr[sgri++] = 1; + if(prevcell->attrs.bold && !cell->attrs.bold) + sgr[sgri++] = 22; + + if(!prevcell->attrs.underline && cell->attrs.underline) + sgr[sgri++] = 4; + if(prevcell->attrs.underline && !cell->attrs.underline) + sgr[sgri++] = 24; + + if(!prevcell->attrs.italic && cell->attrs.italic) + sgr[sgri++] = 3; + if(prevcell->attrs.italic && !cell->attrs.italic) + sgr[sgri++] = 23; + + if(!prevcell->attrs.blink && cell->attrs.blink) + sgr[sgri++] = 5; + if(prevcell->attrs.blink && !cell->attrs.blink) + sgr[sgri++] = 25; + + if(!prevcell->attrs.reverse && cell->attrs.reverse) + sgr[sgri++] = 7; + if(prevcell->attrs.reverse && !cell->attrs.reverse) + sgr[sgri++] = 27; + + if(!prevcell->attrs.strike && cell->attrs.strike) + sgr[sgri++] = 9; + if(prevcell->attrs.strike && !cell->attrs.strike) + sgr[sgri++] = 29; + + if(!prevcell->attrs.font && cell->attrs.font) + sgr[sgri++] = 10 + cell->attrs.font; + if(prevcell->attrs.font && !cell->attrs.font) + sgr[sgri++] = 10; + + if(prevcell->fg.red != cell->fg.red || + prevcell->fg.green != cell->fg.green || + prevcell->fg.blue != cell->fg.blue) { + int index = col2index(cell->fg); + if(index == -1) + sgr[sgri++] = 39; + else if(index < 8) + sgr[sgri++] = 30 + index; + else if(index < 16) + sgr[sgri++] = 90 + (index - 8); + else { + sgr[sgri++] = 38; + sgr[sgri++] = 5 | (1<<31); + sgr[sgri++] = index | (1<<31); + } + } + + if(prevcell->bg.red != cell->bg.red || + prevcell->bg.green != cell->bg.green || + prevcell->bg.blue != cell->bg.blue) { + int index = col2index(cell->bg); + if(index == -1) + sgr[sgri++] = 49; + else if(index < 8) + sgr[sgri++] = 40 + index; + else if(index < 16) + sgr[sgri++] = 100 + (index - 8); + else { + sgr[sgri++] = 48; + sgr[sgri++] = 5 | (1<<31); + sgr[sgri++] = index | (1<<31); + } + } + + if(!sgri) + break; + + printf("\x1b["); + { + int i; + for(i = 0; i < sgri; i++) + printf(!i ? "%d" : + sgr[i] & (1<<31) ? ":%d" : + ";%d", + sgr[i] & ~(1<<31)); + } + printf("m"); + } + break; + } + + { + int i; + for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) { + char bytes[6]; + bytes[fill_utf8(cell->chars[i], bytes)] = 0; + printf("%s", bytes); + } + } +} + +static void dump_eol(const VTermScreenCell *prevcell) +{ + switch(format) { + case FORMAT_PLAIN: + break; + case FORMAT_SGR: + if(prevcell->attrs.bold || prevcell->attrs.underline || prevcell->attrs.italic || + prevcell->attrs.blink || prevcell->attrs.reverse || prevcell->attrs.strike || + prevcell->attrs.font) + printf("\x1b[m"); + break; + } + + printf("\n"); +} + +void dump_row(int row) +{ + VTermPos pos; + VTermScreenCell prevcell; + pos.row = row; + pos.col = 0; + memset(&prevcell, 0, sizeof(prevcell)); + vterm_state_get_default_colors(vterm_obtain_state(vt), &prevcell.fg, &prevcell.bg); + + while(pos.col < cols) { + VTermScreenCell cell; + vterm_screen_get_cell(vts, pos, &cell); + + dump_cell(&cell, &prevcell); + + pos.col += cell.width; + prevcell = cell; + } + + dump_eol(&prevcell); +} + +static int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user) +{ + VTermScreenCell prevcell; + int col; + + memset(&prevcell, 0, sizeof(prevcell)); + vterm_state_get_default_colors(vterm_obtain_state(vt), &prevcell.fg, &prevcell.bg); + + for(col = 0; col < cols; col++) { + dump_cell(cells + col, &prevcell); + prevcell = cells[col]; + } + + dump_eol(&prevcell); + + return 1; +} + +static int screen_resize(int new_rows, int new_cols, void *user) +{ + rows = new_rows; + cols = new_cols; + return 1; +} + +static VTermScreenCallbacks cb_screen = { + NULL, /* damage */ + NULL, /* moverect */ + NULL, /* movecursor */ + NULL, /* settermprop */ + NULL, /* bell */ + &screen_resize, /* resize */ + &screen_sb_pushline, /* sb_pushline */ + NULL, /* popline */ +}; + +int main(int argc, char *argv[]) +{ + int opt; + const char *file; + int fd; + int len; + char buffer[1024]; + int row; + + rows = 25; + cols = 80; + + while((opt = getopt(argc, argv, "f:l:c:")) != -1) { + switch(opt) { + case 'f': + if(streq(optarg, "plain")) + format = FORMAT_PLAIN; + else if(streq(optarg, "sgr")) + format = FORMAT_SGR; + else { + fprintf(stderr, "Unrecognised format '%s'\n", optarg); + exit(1); + } + break; + + case 'l': + rows = atoi(optarg); + if(!rows) + rows = 25; + break; + + case 'c': + cols = atoi(optarg); + if(!cols) + cols = 80; + break; + } + } + + file = argv[optind++]; + fd = open(file, O_RDONLY); + if(fd == -1) { + fprintf(stderr, "Cannot open %s - %s\n", file, strerror(errno)); + exit(1); + } + + vt = vterm_new(rows, cols); + vterm_set_utf8(vt, true); + + vts = vterm_obtain_screen(vt); + vterm_screen_set_callbacks(vts, &cb_screen, NULL); + + vterm_screen_reset(vts, 1); + + while((len = read(fd, buffer, sizeof(buffer))) > 0) { + vterm_input_write(vt, buffer, len); + } + + for(row = 0; row < rows; row++) { + dump_row(row); + } + + close(fd); + + vterm_free(vt); + return 0; +} diff --git a/src/libvterm/bin/vterm-ctrl.c b/src/libvterm/bin/vterm-ctrl.c new file mode 100644 index 0000000000..14e35cda52 --- /dev/null +++ b/src/libvterm/bin/vterm-ctrl.c @@ -0,0 +1,362 @@ +#define _XOPEN_SOURCE 500 /* strdup */ + +#include +#include +#include +#include +#define streq(a,b) (strcmp(a,b)==0) + +#include + +static char *getvalue(int *argip, int argc, char *argv[]) +{ + if(*argip >= argc) { + fprintf(stderr, "Expected an option value\n"); + exit(1); + } + + return argv[(*argip)++]; +} + +static int getchoice(int *argip, int argc, char *argv[], const char *options[]) +{ + const char *arg = getvalue(argip, argc, argv); + + int value = -1; + while(options[++value]) + if(streq(arg, options[value])) + return value; + + fprintf(stderr, "Unrecognised option value %s\n", arg); + exit(1); +} + +typedef enum { + OFF, + ON, + QUERY +} BoolQuery; + +static BoolQuery getboolq(int *argip, int argc, char *argv[]) +{ + const char *choices[] = {"off", "on", "query", NULL}; + return getchoice(argip, argc, argv, choices); +} + +static char *helptext[] = { + "reset", + "s8c1t [off|on]", + "keypad [app|num]", + "screen [off|on|query]", + "cursor [off|on|query]", + "curblink [off|on|query]", + "curshape [block|under|bar|query]", + "mouse [off|click|clickdrag|motion]", + "altscreen [off|on|query]", + "bracketpaste [off|on|query]", + "icontitle [STR]", + "icon [STR]", + "title [STR]", + NULL +}; + +static bool seticanon(bool icanon, bool echo) +{ + struct termios termios; + + tcgetattr(0, &termios); + + bool ret = (termios.c_lflag & ICANON); + + if(icanon) termios.c_lflag |= ICANON; + else termios.c_lflag &= ~ICANON; + + if(echo) termios.c_lflag |= ECHO; + else termios.c_lflag &= ~ECHO; + + tcsetattr(0, TCSANOW, &termios); + + return ret; +} + +static void await_c1(int c1) +{ + int c; + + /* await CSI - 8bit or 2byte 7bit form */ + bool in_esc = false; + while((c = getchar())) { + if(c == c1) + break; + if(in_esc && c == (char)(c1 - 0x40)) + break; + if(!in_esc && c == 0x1b) + in_esc = true; + else + in_esc = false; + } +} + +static char *read_csi() +{ + unsigned char csi[32]; + int i = 0; + + await_c1(0x9B); /* CSI */ + + /* TODO: This really should be a more robust CSI parser + */ + for(; i < sizeof(csi)-1; i++) { + int c = csi[i] = getchar(); + if(c >= 0x40 && c <= 0x7e) + break; + } + csi[++i] = 0; + + /* TODO: returns longer than 32? */ + + return strdup((char *)csi); +} + +static char *read_dcs() +{ + unsigned char dcs[32]; + bool in_esc = false; + int i; + + await_c1(0x90); + + for(i = 0; i < sizeof(dcs)-1; ) { + char c = getchar(); + if(c == 0x9c) /* ST */ + break; + if(in_esc && c == 0x5c) + break; + if(!in_esc && c == 0x1b) + in_esc = true; + else { + dcs[i++] = c; + in_esc = false; + } + } + dcs[++i] = 0; + + return strdup((char *)dcs); +} + +static void usage(int exitcode) +{ + char **p; + + fprintf(stderr, "Control a libvterm-based terminal\n" + "\n" + "Options:\n"); + + for(p = helptext; *p; p++) + fprintf(stderr, " %s\n", *p); + + exit(exitcode); +} + +static bool query_dec_mode(int mode) +{ + char *s = NULL; + + printf("\x1b[?%d$p", mode); + + do { + int reply_mode, reply_value; + char reply_cmd; + + if(s) + free(s); + s = read_csi(); + + /* expect "?" mode ";" value "$y" */ + + /* If the sscanf format string ends in a literal, we can't tell from + * its return value if it matches. Hence we'll %c the cmd and check it + * explicitly + */ + if(sscanf(s, "?%d;%d$%c", &reply_mode, &reply_value, &reply_cmd) < 3) + continue; + if(reply_cmd != 'y') + continue; + + if(reply_mode != mode) + continue; + + free(s); + + if(reply_value == 1 || reply_value == 3) + return true; + if(reply_value == 2 || reply_value == 4) + return false; + + printf("Unrecognised reply to DECRQM: %d\n", reply_value); + return false; + } while(1); +} + +static void do_dec_mode(int mode, BoolQuery val, const char *name) +{ + switch(val) { + case OFF: + case ON: + printf("\x1b[?%d%c", mode, val == ON ? 'h' : 'l'); + break; + + case QUERY: + if(query_dec_mode(mode)) + printf("%s on\n", name); + else + printf("%s off\n", name); + break; + } +} + +static int query_rqss_numeric(char *cmd) +{ + char *s = NULL; + + printf("\x1bP$q%s\x1b\\", cmd); + + do { + int num; + + if(s) + free(s); + s = read_dcs(); + + if(!s) + return -1; + if(strlen(s) < strlen(cmd)) + return -1; + if(strcmp(s + strlen(s) - strlen(cmd), cmd) != 0) { + printf("No match\n"); + continue; + } + + if(s[0] != '1' || s[1] != '$' || s[2] != 'r') + return -1; + + if(sscanf(s + 3, "%d", &num) != 1) + return -1; + + return num; + } while(1); +} + +bool wasicanon; + +void restoreicanon(void) +{ + seticanon(wasicanon, true); +} + +int main(int argc, char *argv[]) +{ + int argi = 1; + + if(argc == 1) + usage(0); + + wasicanon = seticanon(false, false); + atexit(restoreicanon); + + while(argi < argc) { + const char *arg = argv[argi++]; + + if(streq(arg, "reset")) { + printf("\x1b" "c"); + } + else if(streq(arg, "s8c1t")) { + const char *choices[] = {"off", "on", NULL}; + switch(getchoice(&argi, argc, argv, choices)) { + case 0: + printf("\x1b F"); break; + case 1: + printf("\x1b G"); break; + } + } + else if(streq(arg, "keypad")) { + const char *choices[] = {"app", "num", NULL}; + switch(getchoice(&argi, argc, argv, choices)) { + case 0: + printf("\x1b="); break; + case 1: + printf("\x1b>"); break; + } + } + else if(streq(arg, "screen")) { + do_dec_mode(5, getboolq(&argi, argc, argv), "screen"); + } + else if(streq(arg, "cursor")) { + do_dec_mode(25, getboolq(&argi, argc, argv), "cursor"); + } + else if(streq(arg, "curblink")) { + do_dec_mode(12, getboolq(&argi, argc, argv), "curblink"); + } + else if(streq(arg, "curshape")) { + /* TODO: This ought to query the current value of DECSCUSR because it */ + /* may need blinking on or off */ + const char *choices[] = {"block", "under", "bar", "query", NULL}; + int shape = getchoice(&argi, argc, argv, choices); + switch(shape) { + case 3: /* query */ + shape = query_rqss_numeric(" q"); + switch(shape) { + case 1: case 2: + printf("curshape block\n"); + break; + case 3: case 4: + printf("curshape under\n"); + break; + case 5: case 6: + printf("curshape bar\n"); + break; + } + break; + + case 0: + case 1: + case 2: + printf("\x1b[%d q", 1 + (shape * 2)); + break; + } + } + else if(streq(arg, "mouse")) { + const char *choices[] = {"off", "click", "clickdrag", "motion", NULL}; + switch(getchoice(&argi, argc, argv, choices)) { + case 0: + printf("\x1b[?1000l"); break; + case 1: + printf("\x1b[?1000h"); break; + case 2: + printf("\x1b[?1002h"); break; + case 3: + printf("\x1b[?1003h"); break; + } + } + else if(streq(arg, "altscreen")) { + do_dec_mode(1049, getboolq(&argi, argc, argv), "altscreen"); + } + else if(streq(arg, "bracketpaste")) { + do_dec_mode(2004, getboolq(&argi, argc, argv), "bracketpaste"); + } + else if(streq(arg, "icontitle")) { + printf("\x1b]0;%s\a", getvalue(&argi, argc, argv)); + } + else if(streq(arg, "icon")) { + printf("\x1b]1;%s\a", getvalue(&argi, argc, argv)); + } + else if(streq(arg, "title")) { + printf("\x1b]2;%s\a", getvalue(&argi, argc, argv)); + } + else { + fprintf(stderr, "Unrecognised command %s\n", arg); + exit(1); + } + } + return 0; +} diff --git a/src/libvterm/bin/vterm-dump.c b/src/libvterm/bin/vterm-dump.c new file mode 100644 index 0000000000..84a957a682 --- /dev/null +++ b/src/libvterm/bin/vterm-dump.c @@ -0,0 +1,231 @@ +/* Require getopt(3) */ +#define _XOPEN_SOURCE + +#include +#include +#define streq(a,b) (strcmp(a,b)==0) + +#include +#include +#include +#include +#include + +#include "vterm.h" + +static const char *special_begin = "{"; +static const char *special_end = "}"; + +static int parser_text(const char bytes[], size_t len, void *user) +{ + unsigned char *b = (unsigned char *)bytes; + + int i; + for(i = 0; i < len; /* none */) { + if(b[i] < 0x20) /* C0 */ + break; + else if(b[i] < 0x80) /* ASCII */ + i++; + else if(b[i] < 0xa0) /* C1 */ + break; + else if(b[i] < 0xc0) /* UTF-8 continuation */ + break; + else if(b[i] < 0xe0) { /* UTF-8 2-byte */ + /* 2-byte UTF-8 */ + if(len < i+2) break; + i += 2; + } + else if(b[i] < 0xf0) { /* UTF-8 3-byte */ + if(len < i+3) break; + i += 3; + } + else if(b[i] < 0xf8) { /* UTF-8 4-byte */ + if(len < i+4) break; + i += 4; + } + else /* otherwise invalid */ + break; + } + + printf("%.*s", i, b); + return i; +} + +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ +static const char *name_c0[] = { + "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "LS0", "LS1", + "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US", +}; +static const char *name_c1[] = { + NULL, NULL, "BPH", "NBH", NULL, "NEL", "SSA", "ESA", "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3", + "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA", "SOS", NULL, "SCI", "CSI", "ST", "OSC", "PM", "APC", +}; + +static int parser_control(unsigned char control, void *user) +{ + if(control < 0x20) + printf("%s%s%s", special_begin, name_c0[control], special_end); + else if(control >= 0x80 && control < 0xa0 && name_c1[control - 0x80]) + printf("%s%s%s", special_begin, name_c1[control - 0x80], special_end); + else + printf("%sCONTROL 0x%02x%s", special_begin, control, special_end); + + if(control == 0x0a) + printf("\n"); + return 1; +} + +static int parser_escape(const char bytes[], size_t len, void *user) +{ + if(bytes[0] >= 0x20 && bytes[0] < 0x30) { + if(len < 2) + return -1; + len = 2; + } + else { + len = 1; + } + + printf("%sESC %.*s%s", special_begin, (int)len, bytes, special_end); + + return len; +} + +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ +static const char *name_csi_plain[] = { + "ICH", "CUU", "CUD", "CUF", "CUB", "CNL", "CPL", "CHA", "CUP", "CHT", "ED", "EL", "IL", "DL", "EF", "EA", + "DCH", "SSE", "CPR", "SU", "SD", "NP", "PP", "CTC", "ECH", "CVT", "CBT", "SRS", "PTX", "SDS", "SIMD",NULL, + "HPA", "HPR", "REP", "DA", "VPA", "VPR", "HVP", "TBC", "SM", "MC", "HPB", "VPB", "RM", "SGR", "DSR", "DAQ", +}; + +/*0 4 8 B */ +static const int newline_csi_plain[] = { + 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, +}; + +static int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user) +{ + const char *name = NULL; + if(!leader && !intermed && command < 0x70) + name = name_csi_plain[command - 0x40]; + else if(leader && streq(leader, "?") && !intermed) { + /* DEC */ + switch(command) { + case 'h': name = "DECSM"; break; + case 'l': name = "DECRM"; break; + } + if(name) + leader = NULL; + } + + if(!leader && !intermed && command < 0x70 && newline_csi_plain[command - 0x40]) + printf("\n"); + + if(name) + printf("%s%s", special_begin, name); + else + printf("%sCSI", special_begin); + + if(leader && leader[0]) + printf(" %s", leader); + + { + int i; + for(i = 0; i < argcount; i++) { + printf(i ? "," : " "); + } + + if(args[i] == CSI_ARG_MISSING) + printf("*"); + else { + while(CSI_ARG_HAS_MORE(args[i])) + printf("%ld+", CSI_ARG(args[i++])); + printf("%ld", CSI_ARG(args[i])); + } + } + + if(intermed && intermed[0]) + printf(" %s", intermed); + + if(name) + printf("%s", special_end); + else + printf(" %c%s", command, special_end); + + return 1; +} + +static int parser_osc(const char *command, size_t cmdlen, void *user) +{ + printf("%sOSC %.*s%s", special_begin, (int)cmdlen, command, special_end); + + return 1; +} + +static int parser_dcs(const char *command, size_t cmdlen, void *user) +{ + printf("%sDCS %.*s%s", special_begin, (int)cmdlen, command, special_end); + + return 1; +} + +static VTermParserCallbacks parser_cbs = { + &parser_text, /* text */ + &parser_control, /* control */ + &parser_escape, /* escape */ + &parser_csi, /* csi */ + &parser_osc, /* osc */ + &parser_dcs, /* dcs */ + NULL /* resize */ +}; + +int main(int argc, char *argv[]) +{ + int use_colour = isatty(1); + const char *file; + int fd; + VTerm *vt; + int len; + char buffer[1024]; + + int opt; + while((opt = getopt(argc, argv, "c")) != -1) { + switch(opt) { + case 'c': use_colour = 1; break; + } + } + + file = argv[optind++]; + + if(!file || streq(file, "-")) + fd = 0; /* stdin */ + else { + fd = open(file, O_RDONLY); + if(fd == -1) { + fprintf(stderr, "Cannot open %s - %s\n", file, strerror(errno)); + exit(1); + } + } + + if(use_colour) { + special_begin = "\x1b[7m{"; + special_end = "}\x1b[m"; + } + + /* Size matters not for the parser */ + vt = vterm_new(25, 80); + vterm_set_utf8(vt, 1); + vterm_parser_set_callbacks(vt, &parser_cbs, NULL); + + while((len = read(fd, buffer, sizeof(buffer))) > 0) { + vterm_input_write(vt, buffer, len); + } + + printf("\n"); + + close(fd); + vterm_free(vt); + return 0; +} diff --git a/src/libvterm/doc/URLs b/src/libvterm/doc/URLs new file mode 100644 index 0000000000..3a6512f04b --- /dev/null +++ b/src/libvterm/doc/URLs @@ -0,0 +1,11 @@ +ECMA-48: + http://www.ecma-international.org/publications/standards/Ecma-048.htm + +Xterm Control Sequences: + http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + +Digital VT100 User Guide: + http://vt100.net/docs/vt100-ug/ + +Digital VT220 Programmer Reference Manual + http://vt100.net/docs/vt220-rm/ diff --git a/src/libvterm/doc/seqs.txt b/src/libvterm/doc/seqs.txt new file mode 100644 index 0000000000..e5372d0266 --- /dev/null +++ b/src/libvterm/doc/seqs.txt @@ -0,0 +1,226 @@ +Sequences documented in parens are implicit ones from parser.c, which move +between states. + +1 = VT100 +2 = VT220 +3 = VT320 + + C0 controls + +123 0x00 = NUL +123 0x07 = BEL +123 0x08 = BS +123 0x09 = HT +123 0x0A = LF +123 0x0B = VT +123 0x0C = FF +123 0x0D = CR +123 0x0E = LS1 +123 0x0F = LS0 + (0x18 = CAN) + (0x1A = SUB) + (0x1B = ESC) + +123 0x7f = DEL (ignored) + + C1 controls + +123 0x84 = IND +123 0x85 = NEL +123 0x88 = HTS +123 0x8D = RI + 23 0x8e = SS2 + 23 0x8f = SS3 + (0x90 = DCS) + (0x9B = CSI) + (0x9C = ST) + (0x9D = OSC) + + Escape sequences + - excluding sequences that are C1 aliases + +123 ESC () = SCS, select character set (G0, G1) + 23 ESC *+ = SCS, select character set (G2, G3) +123 ESC 7 = DECSC - save cursor +123 ESC 8 = DECRC - restore cursor +123 ESC # 3 = DECDHL, double-height line (top half) +123 ESC # 4 = DECDHL, double-height line (bottom half) +123 ESC # 5 = DECSWL, single-width single-height line +123 ESC # 6 = DECDWL, double-width single-height line +123 ESC # 8 = DECALN +123 ESC < = Ignored (used by VT100 to exit VT52 mode) +123 ESC = = DECKPAM, keypad application mode +123 ESC > = DECKPNM, keypad numeric mode + 23 ESC Sp F = S7C1T + 23 ESC Sp G = S8C1T + (ESC P = DCS) + (ESC [ = CSI) + (ESC \ = ST) + (ESC ] = OSC) +123 ESC c = RIS, reset initial state + 3 ESC n = LS2 + 3 ESC o = LS3 + 3 ESC ~ = LS1R + 3 ESC } = LS2R + 3 ESC | = LS3R + + DCSes + + 3 DCS $ q ST = DECRQSS + 3 m = Request SGR + Sp q = Request DECSCUSR + 3 " q = Request DECSCA + 3 r = Request DECSTBM + s = Request DECSLRM + + CSIs + 23 CSI @ = ICH +123 CSI A = CUU +123 CSI B = CUD +123 CSI C = CUF +123 CSI D = CUB + CSI E = CNL + CSI F = CPL + CSI G = CHA +123 CSI H = CUP + CSI I = CHT +123 CSI J = ED + 23 CSI ? J = DECSED, selective erase in display +123 CSI K = EL + 23 CSI ? K = DECSEL, selective erase in line + 23 CSI L = IL + 23 CSI M = DL + 23 CSI P = DCH + CSI S = SU + CSI T = SD + 23 CSI X = ECH + CSI Z = CBT + CSI ` = HPA + CSI a = HPR +123 CSI c = DA, device attributes +123 0 = DA + 23 CSI > c = DECSDA + 23 0 = SDA + CSI d = VPA + CSI e = VPR +123 CSI f = HVP +123 CSI g = TBC +123 CSI h = SM, Set mode +123 CSI ? h = DECSM, DEC set mode + CSI j = HPB + CSI k = VPB +123 CSI l = RM, Reset mode +123 CSI ? l = DECRM, DEC reset mode +123 CSI m = SGR, Set Graphic Rendition +123 CSI n = DSR, Device Status Report + 23 5 = operating status + 23 6 = CPR = cursor position + 23 CSI ? n = DECDSR; behaves as DSR but uses CSI ? instead of CSI to respond + 23 CSI ! p = DECSTR, soft terminal reset + 3 CSI ? $ p = DECRQM, request mode + CSI Sp q = DECSCUSR (odd numbers blink, even numbers solid) + 1 or 2 = block + 3 or 4 = underline + 5 or 6 = I-beam to left + 23 CSI " q = DECSCA, select character attributes +123 CSI r = DECSTBM + CSI s = DECSLRM + CSI ' } = DECIC + CSI ' ~ = DECDC + + OSCs + + OSC 0; = Set icon name and title + OSC 1; = Set icon name + OSC 2; = Set title + + Standard modes + + 23 SM 4 = IRM +123 SM 20 = NLM, linefeed/newline + + DEC modes + +123 DECSM 1 = DECCKM, cursor keys +123 DECSM 5 = DECSCNM, screen +123 DECSM 6 = DECOM, origin +123 DECSM 7 = DECAWM, autowrap + DECSM 12 = Cursor blink + 23 DECSM 25 = DECTCEM, text cursor enable + DECSM 69 = DECVSSM, vertical screen split + DECSM 1000 = Mouse click/release tracking + DECSM 1002 = Mouse click/release/drag tracking + DECSM 1003 = Mouse all movements tracking + DECSM 1005 = Mouse protocol extended (UTF-8) - not recommended + DECSM 1006 = Mouse protocol SGR + DECSM 1015 = Mouse protocol rxvt + DECSM 1047 = Altscreen + DECSM 1048 = Save cursor + DECSM 1049 = 1047 + 1048 + DECSM 2004 = Bracketed paste + + Graphic Renditions + +123 SGR 0 = Reset +123 SGR 1 = Bold on + SGR 3 = Italic on +123 SGR 4 = Underline single +123 SGR 5 = Blink on +123 SGR 7 = Reverse on + SGR 9 = Strikethrough on + SGR 10-19 = Select font + SGR 21 = Underline double + 23 SGR 22 = Bold off + SGR 23 = Italic off + 23 SGR 24 = Underline off + 23 SGR 25 = Blink off + 23 SGR 27 = Reverse off + SGR 29 = Strikethrough off + SGR 30-37 = Foreground ANSI + SGR 38 = Foreground alternative palette + SGR 39 = Foreground default + SGR 40-47 = Background ANSI + SGR 48 = Background alternative palette + SGR 49 = Background default + SGR 90-97 = Foreground ANSI high-intensity + SGR 100-107 = Background ANSI high-