mirror of
https://github.com/vim/vim
synced 2025-07-16 01:01:58 +00:00
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.
This commit is contained in:
72
Filelist
72
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
|
||||
|
@ -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 \
|
||||
|
130
runtime/doc/terminal.txt
Normal file
130
runtime/doc/terminal.txt
Normal file
@ -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:
|
57
src/Makefile
57
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 \
|
||||
|
33
src/auto/configure
vendored
33
src/auto/configure
vendored
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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@
|
||||
|
@ -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.], ,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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[].
|
||||
|
@ -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.
|
||||
|
13
src/libvterm/.bzrignore
Normal file
13
src/libvterm/.bzrignore
Normal file
@ -0,0 +1,13 @@
|
||||
.libs
|
||||
*.lo
|
||||
*.la
|
||||
|
||||
bin/*
|
||||
!bin/*.c
|
||||
|
||||
pangoterm
|
||||
t/test
|
||||
t/suites.h
|
||||
t/externs.h
|
||||
t/harness
|
||||
src/encoding/*.inc
|
18
src/libvterm/.gitignore
vendored
Normal file
18
src/libvterm/.gitignore
vendored
Normal file
@ -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/
|
23
src/libvterm/LICENSE
Normal file
23
src/libvterm/LICENSE
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2008 Paul Evans <leonerd@leonerd.org.uk>
|
||||
|
||||
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.
|
145
src/libvterm/Makefile
Normal file
145
src/libvterm/Makefile
Normal file
@ -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)," <vterm.pc.in >$(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)," <vterm.pc.in >__distdir/vterm.pc.in
|
||||
sed "/^# DIST CUT/Q" <Makefile >__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`
|
13
src/libvterm/README
Normal file
13
src/libvterm/README
Normal file
@ -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.
|
||||
-
|
287
src/libvterm/bin/unterm.c
Normal file
287
src/libvterm/bin/unterm.c
Normal file
@ -0,0 +1,287 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
362
src/libvterm/bin/vterm-ctrl.c
Normal file
362
src/libvterm/bin/vterm-ctrl.c
Normal file
@ -0,0 +1,362 @@
|
||||
#define _XOPEN_SOURCE 500 /* strdup */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define streq(a,b) (strcmp(a,b)==0)
|
||||
|
||||
#include <termios.h>
|
||||
|
||||
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;
|
||||
}
|
231
src/libvterm/bin/vterm-dump.c
Normal file
231
src/libvterm/bin/vterm-dump.c
Normal file
@ -0,0 +1,231 @@
|
||||
/* Require getopt(3) */
|
||||
#define _XOPEN_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#define streq(a,b) (strcmp(a,b)==0)
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
11
src/libvterm/doc/URLs
Normal file
11
src/libvterm/doc/URLs
Normal file
@ -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/
|
226
src/libvterm/doc/seqs.txt
Normal file
226
src/libvterm/doc/seqs.txt
Normal file
@ -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-intensity
|
||||
|
||||
The state storage used by ESC 7 and DECSM 1048/1049 is shared.
|
||||
|
||||
Unimplemented sequences:
|
||||
|
||||
The following sequences are not recognised by libvterm.
|
||||
|
||||
123 0x05 = ENQ
|
||||
3 0x11 = DC1 (XON)
|
||||
3 0x13 = DC3 (XOFF)
|
||||
12 ESC Z = DECID, identify terminal
|
||||
DCS $ q = [DECRQSS]
|
||||
3 " p = Request DECSCL
|
||||
3 $ } = Request DECSASD
|
||||
3 $ ~ = Request DECSSDT
|
||||
23 DCS { = DECDLD, down-line-loadable character set
|
||||
23 DCS | = DECUDK, user-defined key
|
||||
23 CSI i = DEC printer control
|
||||
23 CSI " p = DECSCL, set compatibility level
|
||||
1 CSI q = DECLL, load LEDs
|
||||
3 CSI $ u = DECRQTSR, request terminal state report
|
||||
3 1 = terminal state report
|
||||
3 CSI & u = DECRQUPSS, request user-preferred supplemental set
|
||||
3 CSI $ w = DECRQPSR, request presentation state report
|
||||
3 1 = cursor information report
|
||||
3 2 = tab stop report
|
||||
1 CSI x = DECREQTPARM, request terminal parameters
|
||||
123 CSI y = DECTST, invoke confidence test
|
||||
3 CSI $ } = DECSASD, select active status display
|
||||
3 CSI $ ~ = DECSSDT, select status line type
|
||||
23 SM 2 = KAM, keyboard action
|
||||
123 SM 12 = SRM, send/receive
|
||||
123 DECSM 2 = DECANM, ANSI/VT52
|
||||
123 DECSM 3 = DECCOLM, 132 column
|
||||
123 DECSM 4 = DECSCLM, scrolling
|
||||
123 DECSM 8 = DECARM, auto-repeat
|
||||
12 DECSM 9 = DECINLM, interlace
|
||||
23 DECSM 18 = DECPFF, print form feed
|
||||
23 DECSM 19 = DECPEX, print extent
|
||||
23 DECSM 42 = DECNRCM, national/multinational character
|
370
src/libvterm/include/vterm.h
Normal file
370
src/libvterm/include/vterm.h
Normal file
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* NOTE: This is a MODIFIED version of libvterm, see the README file.
|
||||
*/
|
||||
#ifndef __VTERM_H__
|
||||
#define __VTERM_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "vterm_keycodes.h"
|
||||
|
||||
typedef struct VTerm VTerm;
|
||||
typedef struct VTermState VTermState;
|
||||
typedef struct VTermScreen VTermScreen;
|
||||
|
||||
/* Specifies a screen point. */
|
||||
typedef struct {
|
||||
int row;
|
||||
int col;
|
||||
} VTermPos;
|
||||
|
||||
/*
|
||||
* Some small utility functions; we can just keep these static here.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Order points by on-screen flow order:
|
||||
* Return < 0 if "a" is before "b"
|
||||
* Return 0 if "a" and "b" are equal
|
||||
* Return > 0 if "a" is after "b".
|
||||
*/
|
||||
int vterm_pos_cmp(VTermPos a, VTermPos b);
|
||||
|
||||
#if defined(DEFINE_INLINES) || USE_INLINE
|
||||
INLINE int vterm_pos_cmp(VTermPos a, VTermPos b)
|
||||
{
|
||||
return (a.row == b.row) ? a.col - b.col : a.row - b.row;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Specifies a rectangular screen area. */
|
||||
typedef struct {
|
||||
int start_row;
|
||||
int end_row;
|
||||
int start_col;
|
||||
int end_col;
|
||||
} VTermRect;
|
||||
|
||||
/* Return true if the rect "r" contains the point "p". */
|
||||
int vterm_rect_contains(VTermRect r, VTermPos p);
|
||||
|
||||
#if defined(DEFINE_INLINES) || USE_INLINE
|
||||
INLINE int vterm_rect_contains(VTermRect r, VTermPos p)
|
||||
{
|
||||
return p.row >= r.start_row && p.row < r.end_row &&
|
||||
p.col >= r.start_col && p.col < r.end_col;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Move "rect" "row_delta" down and "col_delta" right.
|
||||
* Does not check boundaries. */
|
||||
void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta);
|
||||
|
||||
#if defined(DEFINE_INLINES) || USE_INLINE
|
||||
INLINE void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
|
||||
{
|
||||
rect->start_row += row_delta; rect->end_row += row_delta;
|
||||
rect->start_col += col_delta; rect->end_col += col_delta;
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t red, green, blue;
|
||||
} VTermColor;
|
||||
|
||||
typedef enum {
|
||||
/* VTERM_VALUETYPE_NONE = 0 */
|
||||
VTERM_VALUETYPE_BOOL = 1,
|
||||
VTERM_VALUETYPE_INT,
|
||||
VTERM_VALUETYPE_STRING,
|
||||
VTERM_VALUETYPE_COLOR
|
||||
} VTermValueType;
|
||||
|
||||
typedef union {
|
||||
int boolean;
|
||||
int number;
|
||||
char *string;
|
||||
VTermColor color;
|
||||
} VTermValue;
|
||||
|
||||
typedef enum {
|
||||
/* VTERM_ATTR_NONE = 0 */
|
||||
VTERM_ATTR_BOLD = 1, /* bool: 1, 22 */
|
||||
VTERM_ATTR_UNDERLINE, /* number: 4, 21, 24 */
|
||||
VTERM_ATTR_ITALIC, /* bool: 3, 23 */
|
||||
VTERM_ATTR_BLINK, /* bool: 5, 25 */
|
||||
VTERM_ATTR_REVERSE, /* bool: 7, 27 */
|
||||
VTERM_ATTR_STRIKE, /* bool: 9, 29 */
|
||||
VTERM_ATTR_FONT, /* number: 10-19 */
|
||||
VTERM_ATTR_FOREGROUND, /* color: 30-39 90-97 */
|
||||
VTERM_ATTR_BACKGROUND /* color: 40-49 100-107 */
|
||||
} VTermAttr;
|
||||
|
||||
typedef enum {
|
||||
/* VTERM_PROP_NONE = 0 */
|
||||
VTERM_PROP_CURSORVISIBLE = 1, /* bool */
|
||||
VTERM_PROP_CURSORBLINK, /* bool */
|
||||
VTERM_PROP_ALTSCREEN, /* bool */
|
||||
VTERM_PROP_TITLE, /* string */
|
||||
VTERM_PROP_ICONNAME, /* string */
|
||||
VTERM_PROP_REVERSE, /* bool */
|
||||
VTERM_PROP_CURSORSHAPE, /* number */
|
||||
VTERM_PROP_MOUSE /* number */
|
||||
} VTermProp;
|
||||
|
||||
enum {
|
||||
VTERM_PROP_CURSORSHAPE_BLOCK = 1,
|
||||
VTERM_PROP_CURSORSHAPE_UNDERLINE,
|
||||
VTERM_PROP_CURSORSHAPE_BAR_LEFT
|
||||
};
|
||||
|
||||
enum {
|
||||
VTERM_PROP_MOUSE_NONE = 0,
|
||||
VTERM_PROP_MOUSE_CLICK,
|
||||
VTERM_PROP_MOUSE_DRAG,
|
||||
VTERM_PROP_MOUSE_MOVE
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const uint32_t *chars;
|
||||
int width;
|
||||
unsigned int protected_cell:1; /* DECSCA-protected against DECSEL/DECSED */
|
||||
unsigned int dwl:1; /* DECDWL or DECDHL double-width line */
|
||||
unsigned int dhl:2; /* DECDHL double-height line (1=top 2=bottom) */
|
||||
} VTermGlyphInfo;
|
||||
|
||||
typedef struct {
|
||||
unsigned int doublewidth:1; /* DECDWL or DECDHL line */
|
||||
unsigned int doubleheight:2; /* DECDHL line (1=top 2=bottom) */
|
||||
} VTermLineInfo;
|
||||
|
||||
typedef struct {
|
||||
/* libvterm relies on the allocated memory to be zeroed out before it is
|
||||
* returned by the allocator. */
|
||||
void *(*malloc)(size_t size, void *allocdata);
|
||||
void (*free)(void *ptr, void *allocdata);
|
||||
} VTermAllocatorFunctions;
|
||||
|
||||
/* Allocate and initialize a new terminal with default allocators. */
|
||||
VTerm *vterm_new(int rows, int cols);
|
||||
|
||||
/* Allocate and initialize a new terminal with specified allocators. */
|
||||
VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata);
|
||||
|
||||
/* Free and cleanup a terminal and all its data. */
|
||||
void vterm_free(VTerm* vt);
|
||||
|
||||
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp);
|
||||
void vterm_set_size(VTerm *vt, int rows, int cols);
|
||||
|
||||
int vterm_get_utf8(const VTerm *vt);
|
||||
void vterm_set_utf8(VTerm *vt, int is_utf8);
|
||||
|
||||
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len);
|
||||
|
||||
size_t vterm_output_get_buffer_size(const VTerm *vt);
|
||||
size_t vterm_output_get_buffer_current(const VTerm *vt);
|
||||
size_t vterm_output_get_buffer_remaining(const VTerm *vt);
|
||||
|
||||
size_t vterm_output_read(VTerm *vt, char *buffer, size_t len);
|
||||
|
||||
void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod);
|
||||
void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod);
|
||||
|
||||
void vterm_keyboard_start_paste(VTerm *vt);
|
||||
void vterm_keyboard_end_paste(VTerm *vt);
|
||||
|
||||
void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod);
|
||||
void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod);
|
||||
|
||||
/* ------------
|
||||
* Parser layer
|
||||
* ------------ */
|
||||
|
||||
/* Flag to indicate non-final subparameters in a single CSI parameter.
|
||||
* Consider
|
||||
* CSI 1;2:3:4;5a
|
||||
* 1 4 and 5 are final.
|
||||
* 2 and 3 are non-final and will have this bit set
|
||||
*
|
||||
* Don't confuse this with the final byte of the CSI escape; 'a' in this case.
|
||||
*/
|
||||
#define CSI_ARG_FLAG_MORE (1<<31)
|
||||
#define CSI_ARG_MASK (~(1<<31))
|
||||
|
||||
#define CSI_ARG_HAS_MORE(a) ((a) & CSI_ARG_FLAG_MORE)
|
||||
#define CSI_ARG(a) ((a) & CSI_ARG_MASK)
|
||||
|
||||
/* Can't use -1 to indicate a missing argument; use this instead */
|
||||
#define CSI_ARG_MISSING ((1UL<<31)-1)
|
||||
|
||||
#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
|
||||
#define CSI_ARG_OR(a,def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a))
|
||||
#define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a))
|
||||
|
||||
typedef struct {
|
||||
int (*text)(const char *bytes, size_t len, void *user);
|
||||
int (*control)(unsigned char control, void *user);
|
||||
int (*escape)(const char *bytes, size_t len, void *user);
|
||||
int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
|
||||
int (*osc)(const char *command, size_t cmdlen, void *user);
|
||||
int (*dcs)(const char *command, size_t cmdlen, void *user);
|
||||
int (*resize)(int rows, int cols, void *user);
|
||||
} VTermParserCallbacks;
|
||||
|
||||
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user);
|
||||
void *vterm_parser_get_cbdata(VTerm *vt);
|
||||
|
||||
/* -----------
|
||||
* State layer
|
||||
* ----------- */
|
||||
|
||||
typedef struct {
|
||||
int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
|
||||
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
|
||||
int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
|
||||
int (*moverect)(VTermRect dest, VTermRect src, void *user);
|
||||
int (*erase)(VTermRect rect, int selective, void *user);
|
||||
int (*initpen)(void *user);
|
||||
int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
|
||||
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
|
||||
int (*bell)(void *user);
|
||||
int (*resize)(int rows, int cols, VTermPos *delta, void *user);
|
||||
int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
|
||||
} VTermStateCallbacks;
|
||||
|
||||
VTermState *vterm_obtain_state(VTerm *vt);
|
||||
|
||||
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
|
||||
void *vterm_state_get_cbdata(VTermState *state);
|
||||
|
||||
/* Only invokes control, csi, osc, dcs */
|
||||
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermParserCallbacks *fallbacks, void *user);
|
||||
void *vterm_state_get_unrecognised_fbdata(VTermState *state);
|
||||
|
||||
void vterm_state_reset(VTermState *state, int hard);
|
||||
void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
|
||||
void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg);
|
||||
void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col);
|
||||
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg);
|
||||
void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col);
|
||||
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
|
||||
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
|
||||
int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val);
|
||||
const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row);
|
||||
|
||||
/* ------------
|
||||
* Screen layer
|
||||
* ------------ */
|
||||
|
||||
typedef struct {
|
||||
unsigned int bold : 1;
|
||||
unsigned int underline : 2;
|
||||
unsigned int italic : 1;
|
||||
unsigned int blink : 1;
|
||||
unsigned int reverse : 1;
|
||||
unsigned int strike : 1;
|
||||
unsigned int font : 4; /* 0 to 9 */
|
||||
unsigned int dwl : 1; /* On a DECDWL or DECDHL line */
|
||||
unsigned int dhl : 2; /* On a DECDHL line (1=top 2=bottom) */
|
||||
} VTermScreenCellAttrs;
|
||||
|
||||
typedef struct {
|
||||
#define VTERM_MAX_CHARS_PER_CELL 6
|
||||
uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
|
||||
char width;
|
||||
VTermScreenCellAttrs attrs;
|
||||
VTermColor fg, bg;
|
||||
} VTermScreenCell;
|
||||
|
||||
/* All fields are optional, NULL when not used. */
|
||||
typedef struct {
|
||||
int (*damage)(VTermRect rect, void *user);
|
||||
int (*moverect)(VTermRect dest, VTermRect src, void *user);
|
||||
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
|
||||
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
|
||||
int (*bell)(void *user);
|
||||
int (*resize)(int rows, int cols, void *user);
|
||||
int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
|
||||
int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
|
||||
} VTermScreenCallbacks;
|
||||
|
||||
VTermScreen *vterm_obtain_screen(VTerm *vt);
|
||||
|
||||
/*
|
||||
* Install screen callbacks. These are invoked when the screen contents is
|
||||
* changed. "user" is passed into to the callback.
|
||||
*/
|
||||
void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user);
|
||||
void *vterm_screen_get_cbdata(VTermScreen *screen);
|
||||
|
||||
/* Only invokes control, csi, osc, dcs */
|
||||
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermParserCallbacks *fallbacks, void *user);
|
||||
void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen);
|
||||
|
||||
void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen);
|
||||
|
||||
typedef enum {
|
||||
VTERM_DAMAGE_CELL, /* every cell */
|
||||
VTERM_DAMAGE_ROW, /* entire rows */
|
||||
VTERM_DAMAGE_SCREEN, /* entire screen */
|
||||
VTERM_DAMAGE_SCROLL /* entire screen + scrollrect */
|
||||
} VTermDamageSize;
|
||||
|
||||
void vterm_screen_flush_damage(VTermScreen *screen);
|
||||
void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size);
|
||||
|
||||
void vterm_screen_reset(VTermScreen *screen, int hard);
|
||||
|
||||
/* Neither of these functions NUL-terminate the buffer */
|
||||
size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
|
||||
size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect);
|
||||
|
||||
typedef enum {
|
||||
VTERM_ATTR_BOLD_MASK = 1 << 0,
|
||||
VTERM_ATTR_UNDERLINE_MASK = 1 << 1,
|
||||
VTERM_ATTR_ITALIC_MASK = 1 << 2,
|
||||
VTERM_ATTR_BLINK_MASK = 1 << 3,
|
||||
VTERM_ATTR_REVERSE_MASK = 1 << 4,
|
||||
VTERM_ATTR_STRIKE_MASK = 1 << 5,
|
||||
VTERM_ATTR_FONT_MASK = 1 << 6,
|
||||
VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
|
||||
VTERM_ATTR_BACKGROUND_MASK = 1 << 8
|
||||
} VTermAttrMask;
|
||||
|
||||
int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
|
||||
|
||||
int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell);
|
||||
|
||||
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
|
||||
|
||||
/* ---------
|
||||
* Utilities
|
||||
* --------- */
|
||||
|
||||
VTermValueType vterm_get_attr_type(VTermAttr attr);
|
||||
VTermValueType vterm_get_prop_type(VTermProp prop);
|
||||
|
||||
void vterm_scroll_rect(VTermRect rect,
|
||||
int downward,
|
||||
int rightward,
|
||||
int (*moverect)(VTermRect src, VTermRect dest, void *user),
|
||||
int (*eraserect)(VTermRect rect, int selective, void *user),
|
||||
void *user);
|
||||
|
||||
void vterm_copy_cells(VTermRect dest,
|
||||
VTermRect src,
|
||||
void (*copycell)(VTermPos dest, VTermPos src, void *user),
|
||||
void *user);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
58
src/libvterm/include/vterm_keycodes.h
Normal file
58
src/libvterm/include/vterm_keycodes.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef __VTERM_INPUT_H__
|
||||
#define __VTERM_INPUT_H__
|
||||
|
||||
typedef enum {
|
||||
VTERM_MOD_NONE = 0x00,
|
||||
VTERM_MOD_SHIFT = 0x01,
|
||||
VTERM_MOD_ALT = 0x02,
|
||||
VTERM_MOD_CTRL = 0x04
|
||||
} VTermModifier;
|
||||
|
||||
typedef enum {
|
||||
VTERM_KEY_NONE,
|
||||
|
||||
VTERM_KEY_ENTER,
|
||||
VTERM_KEY_TAB,
|
||||
VTERM_KEY_BACKSPACE,
|
||||
VTERM_KEY_ESCAPE,
|
||||
|
||||
VTERM_KEY_UP,
|
||||
VTERM_KEY_DOWN,
|
||||
VTERM_KEY_LEFT,
|
||||
VTERM_KEY_RIGHT,
|
||||
|
||||
VTERM_KEY_INS,
|
||||
VTERM_KEY_DEL,
|
||||
VTERM_KEY_HOME,
|
||||
VTERM_KEY_END,
|
||||
VTERM_KEY_PAGEUP,
|
||||
VTERM_KEY_PAGEDOWN,
|
||||
|
||||
VTERM_KEY_FUNCTION_0 = 256,
|
||||
VTERM_KEY_FUNCTION_MAX = VTERM_KEY_FUNCTION_0 + 255,
|
||||
|
||||
VTERM_KEY_KP_0,
|
||||
VTERM_KEY_KP_1,
|
||||
VTERM_KEY_KP_2,
|
||||
VTERM_KEY_KP_3,
|
||||
VTERM_KEY_KP_4,
|
||||
VTERM_KEY_KP_5,
|
||||
VTERM_KEY_KP_6,
|
||||
VTERM_KEY_KP_7,
|
||||
VTERM_KEY_KP_8,
|
||||
VTERM_KEY_KP_9,
|
||||
VTERM_KEY_KP_MULT,
|
||||
VTERM_KEY_KP_PLUS,
|
||||
VTERM_KEY_KP_COMMA,
|
||||
VTERM_KEY_KP_MINUS,
|
||||
VTERM_KEY_KP_PERIOD,
|
||||
VTERM_KEY_KP_DIVIDE,
|
||||
VTERM_KEY_KP_ENTER,
|
||||
VTERM_KEY_KP_EQUAL,
|
||||
|
||||
VTERM_KEY_MAX /* Must be last */
|
||||
} VTermKey;
|
||||
|
||||
#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0+(n))
|
||||
|
||||
#endif
|
232
src/libvterm/src/encoding.c
Normal file
232
src/libvterm/src/encoding.c
Normal file
@ -0,0 +1,232 @@
|
||||
#include "vterm_internal.h"
|
||||
|
||||
#define UNICODE_INVALID 0xFFFD
|
||||
|
||||
#if defined(DEBUG) && DEBUG > 1
|
||||
# define DEBUG_PRINT_UTF8
|
||||
#endif
|
||||
|
||||
struct UTF8DecoderData {
|
||||
/* number of bytes remaining in this codepoint */
|
||||
int bytes_remaining;
|
||||
|
||||
/* number of bytes total in this codepoint once it's finished
|
||||
(for detecting overlongs) */
|
||||
int bytes_total;
|
||||
|
||||
int this_cp;
|
||||
};
|
||||
|
||||
static void init_utf8(VTermEncoding *enc UNUSED, void *data_)
|
||||
{
|
||||
struct UTF8DecoderData *data = data_;
|
||||
|
||||
data->bytes_remaining = 0;
|
||||
data->bytes_total = 0;
|
||||
}
|
||||
|
||||
static void decode_utf8(VTermEncoding *enc UNUSED, void *data_,
|
||||
uint32_t cp[], int *cpi, int cplen,
|
||||
const char bytes[], size_t *pos, size_t bytelen)
|
||||
{
|
||||
struct UTF8DecoderData *data = data_;
|
||||
|
||||
#ifdef DEBUG_PRINT_UTF8
|
||||
printf("BEGIN UTF-8\n");
|
||||
#endif
|
||||
|
||||
for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
|
||||
unsigned char c = bytes[*pos];
|
||||
|
||||
#ifdef DEBUG_PRINT_UTF8
|
||||
printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
|
||||
#endif
|
||||
|
||||
if(c < 0x20) /* C0 */
|
||||
return;
|
||||
|
||||
else if(c >= 0x20 && c < 0x7f) {
|
||||
if(data->bytes_remaining)
|
||||
cp[(*cpi)++] = UNICODE_INVALID;
|
||||
|
||||
cp[(*cpi)++] = c;
|
||||
#ifdef DEBUG_PRINT_UTF8
|
||||
printf(" UTF-8 char: U+%04x\n", c);
|
||||
#endif
|
||||
data->bytes_remaining = 0;
|
||||
}
|
||||
|
||||
else if(c == 0x7f) /* DEL */
|
||||
return;
|
||||
|
||||
else if(c >= 0x80 && c < 0xc0) {
|
||||
if(!data->bytes_remaining) {
|
||||
cp[(*cpi)++] = UNICODE_INVALID;
|
||||
continue;
|
||||
}
|
||||
|
||||
data->this_cp <<= 6;
|
||||
data->this_cp |= c & 0x3f;
|
||||
data->bytes_remaining--;
|
||||
|
||||
if(!data->bytes_remaining) {
|
||||
#ifdef DEBUG_PRINT_UTF8
|
||||
printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, data->bytes_total);
|
||||
#endif
|
||||
/* Check for overlong sequences */
|
||||
switch(data->bytes_total) {
|
||||
case 2:
|
||||
if(data->this_cp < 0x0080) data->this_cp = UNICODE_INVALID;
|
||||
break;
|
||||
case 3:
|
||||
if(data->this_cp < 0x0800) data->this_cp = UNICODE_INVALID;
|
||||
break;
|
||||
case 4:
|
||||
if(data->this_cp < 0x10000) data->this_cp = UNICODE_INVALID;
|
||||
break;
|
||||
case 5:
|
||||
if(data->this_cp < 0x200000) data->this_cp = UNICODE_INVALID;
|
||||
break;
|
||||
case 6:
|
||||
if(data->this_cp < 0x4000000) data->this_cp = UNICODE_INVALID;
|
||||
break;
|
||||
}
|
||||
/* Now look for plain invalid ones */
|
||||
if((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF) ||
|
||||
data->this_cp == 0xFFFE ||
|
||||
data->this_cp == 0xFFFF)
|
||||
data->this_cp = UNICODE_INVALID;
|
||||
#ifdef DEBUG_PRINT_UTF8
|
||||
printf(" char: U+%04x\n", data->this_cp);
|
||||
#endif
|
||||
cp[(*cpi)++] = data->this_cp;
|
||||
}
|
||||
}
|
||||
|
||||
else if(c >= 0xc0 && c < 0xe0) {
|
||||
if(data->bytes_remaining)
|
||||
cp[(*cpi)++] = UNICODE_INVALID;
|
||||
|
||||
data->this_cp = c & 0x1f;
|
||||
data->bytes_total = 2;
|
||||
data->bytes_remaining = 1;
|
||||
}
|
||||
|
||||
else if(c >= 0xe0 && c < 0xf0) {
|
||||
if(data->bytes_remaining)
|
||||
cp[(*cpi)++] = UNICODE_INVALID;
|
||||
|
||||
data->this_cp = c & 0x0f;
|
||||
data->bytes_total = 3;
|
||||
data->bytes_remaining = 2;
|
||||
}
|
||||
|
||||
else if(c >= 0xf0 && c < 0xf8) {
|
||||
if(data->bytes_remaining)
|
||||
cp[(*cpi)++] = UNICODE_INVALID;
|
||||
|
||||
data->this_cp = c & 0x07;
|
||||
data->bytes_total = 4;
|
||||
data->bytes_remaining = 3;
|
||||
}
|
||||
|
||||
else if(c >= 0xf8 && c < 0xfc) {
|
||||
if(data->bytes_remaining)
|
||||
cp[(*cpi)++] = UNICODE_INVALID;
|
||||
|
||||
data->this_cp = c & 0x03;
|
||||
data->bytes_total = 5;
|
||||
data->bytes_remaining = 4;
|
||||
}
|
||||
|
||||
else if(c >= 0xfc && c < 0xfe) {
|
||||
if(data->bytes_remaining)
|
||||
cp[(*cpi)++] = UNICODE_INVALID;
|
||||
|
||||
data->this_cp = c & 0x01;
|
||||
data->bytes_total = 6;
|
||||
data->bytes_remaining = 5;
|
||||
}
|
||||
|
||||
else {
|
||||
cp[(*cpi)++] = UNICODE_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static VTermEncoding encoding_utf8 = {
|
||||
&init_utf8, /* init */
|
||||
&decode_utf8 /* decode */
|
||||
};
|
||||
|
||||
static void decode_usascii(VTermEncoding *enc UNUSED, void *data UNUSED,
|
||||
uint32_t cp[], int *cpi, int cplen,
|
||||
const char bytes[], size_t *pos, size_t bytelen)
|
||||
{
|
||||
int is_gr = bytes[*pos] & 0x80;
|
||||
|
||||
for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
|
||||
unsigned char c = bytes[*pos] ^ is_gr;
|
||||
|
||||
if(c < 0x20 || c == 0x7f || c >= 0x80)
|
||||
return;
|
||||
|
||||
cp[(*cpi)++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
static VTermEncoding encoding_usascii = {
|
||||
NULL, /* init */
|
||||
&decode_usascii /* decode */
|
||||
};
|
||||
|
||||
struct StaticTableEncoding {
|
||||
const VTermEncoding enc;
|
||||
const uint32_t chars[128];
|
||||
};
|
||||
|
||||
static void decode_table(VTermEncoding *enc, void *data UNUSED,
|
||||
uint32_t cp[], int *cpi, int cplen,
|
||||
const char bytes[], size_t *pos, size_t bytelen)
|
||||
{
|
||||
struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
|
||||
int is_gr = bytes[*pos] & 0x80;
|
||||
|
||||
for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
|
||||
unsigned char c = bytes[*pos] ^ is_gr;
|
||||
|
||||
if(c < 0x20 || c == 0x7f || c >= 0x80)
|
||||
return;
|
||||
|
||||
if(table->chars[c])
|
||||
cp[(*cpi)++] = table->chars[c];
|
||||
else
|
||||
cp[(*cpi)++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
#include "encoding/DECdrawing.inc"
|
||||
#include "encoding/uk.inc"
|
||||
|
||||
static struct {
|
||||
VTermEncodingType type;
|
||||
char designation;
|
||||
VTermEncoding *enc;
|
||||
}
|
||||
encodings[] = {
|
||||
{ ENC_UTF8, 'u', &encoding_utf8 },
|
||||
{ ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing },
|
||||
{ ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk },
|
||||
{ ENC_SINGLE_94, 'B', &encoding_usascii },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
/* This ought to be INTERNAL but isn't because it's used by unit testing */
|
||||
VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; encodings[i].designation; i++)
|
||||
if(encodings[i].type == type && encodings[i].designation == designation)
|
||||
return encodings[i].enc;
|
||||
return NULL;
|
||||
}
|
31
src/libvterm/src/encoding/DECdrawing.tbl
Normal file
31
src/libvterm/src/encoding/DECdrawing.tbl
Normal file
@ -0,0 +1,31 @@
|
||||
6/0 = U+25C6 # BLACK DIAMOND
|
||||
6/1 = U+2592 # MEDIUM SHADE (checkerboard)
|
||||
6/2 = U+2409 # SYMBOL FOR HORIZONTAL TAB
|
||||
6/3 = U+240C # SYMBOL FOR FORM FEED
|
||||
6/4 = U+240D # SYMBOL FOR CARRIAGE RETURN
|
||||
6/5 = U+240A # SYMBOL FOR LINE FEED
|
||||
6/6 = U+00B0 # DEGREE SIGN
|
||||
6/7 = U+00B1 # PLUS-MINUS SIGN (plus or minus)
|
||||
6/8 = U+2424 # SYMBOL FOR NEW LINE
|
||||
6/9 = U+240B # SYMBOL FOR VERTICAL TAB
|
||||
6/10 = U+2518 # BOX DRAWINGS LIGHT UP AND LEFT (bottom-right corner)
|
||||
6/11 = U+2510 # BOX DRAWINGS LIGHT DOWN AND LEFT (top-right corner)
|
||||
6/12 = U+250C # BOX DRAWINGS LIGHT DOWN AND RIGHT (top-left corner)
|
||||
6/13 = U+2514 # BOX DRAWINGS LIGHT UP AND RIGHT (bottom-left corner)
|
||||
6/14 = U+253C # BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL (crossing lines)
|
||||
6/15 = U+23BA # HORIZONTAL SCAN LINE-1
|
||||
7/0 = U+23BB # HORIZONTAL SCAN LINE-3
|
||||
7/1 = U+2500 # BOX DRAWINGS LIGHT HORIZONTAL
|
||||
7/2 = U+23BC # HORIZONTAL SCAN LINE-7
|
||||
7/3 = U+23BD # HORIZONTAL SCAN LINE-9
|
||||
7/4 = U+251C # BOX DRAWINGS LIGHT VERTICAL AND RIGHT
|
||||
7/5 = U+2524 # BOX DRAWINGS LIGHT VERTICAL AND LEFT
|
||||
7/6 = U+2534 # BOX DRAWINGS LIGHT UP AND HORIZONTAL
|
||||
7/7 = U+252C # BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
|
||||
7/8 = U+2502 # BOX DRAWINGS LIGHT VERTICAL
|
||||
7/9 = U+2A7D # LESS-THAN OR SLANTED EQUAL-TO
|
||||
7/10 = U+2A7E # GREATER-THAN OR SLANTED EQUAL-TO
|
||||
7/11 = U+03C0 # GREEK SMALL LETTER PI
|
||||
7/12 = U+2260 # NOT EQUAL TO
|
||||
7/13 = U+00A3 # POUND SIGN
|
||||
7/14 = U+00B7 # MIDDLE DOT
|
1
src/libvterm/src/encoding/uk.tbl
Normal file
1
src/libvterm/src/encoding/uk.tbl
Normal file
@ -0,0 +1 @@
|
||||
2/3 = "£"
|
228
src/libvterm/src/keyboard.c
Normal file
228
src/libvterm/src/keyboard.c
Normal file
@ -0,0 +1,228 @@
|
||||
#include "vterm_internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "utf8.h"
|
||||
|
||||
void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
|
||||
{
|
||||
int needs_CSIu;
|
||||
|
||||
/* The shift modifier is never important for Unicode characters
|
||||
* apart from Space
|
||||
*/
|
||||
if(c != ' ')
|
||||
mod &= ~VTERM_MOD_SHIFT;
|
||||
|
||||
if(mod == 0) {
|
||||
/* Normal text - ignore just shift */
|
||||
char str[6];
|
||||
int seqlen = fill_utf8(c, str);
|
||||
vterm_push_output_bytes(vt, str, seqlen);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(c) {
|
||||
/* Special Ctrl- letters that can't be represented elsewise */
|
||||
case 'i': case 'j': case 'm': case '[':
|
||||
needs_CSIu = 1;
|
||||
break;
|
||||
/* Ctrl-\ ] ^ _ don't need CSUu */
|
||||
case '\\': case ']': case '^': case '_':
|
||||
needs_CSIu = 0;
|
||||
break;
|
||||
/* Shift-space needs CSIu */
|
||||
case ' ':
|
||||
needs_CSIu = !!(mod & VTERM_MOD_SHIFT);
|
||||
break;
|
||||
/* All other characters needs CSIu except for letters a-z */
|
||||
default:
|
||||
needs_CSIu = (c < 'a' || c > 'z');
|
||||
}
|
||||
|
||||
/* ALT we can just prefix with ESC; anything else requires CSI u */
|
||||
if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mod & VTERM_MOD_CTRL)
|
||||
c &= 0x1f;
|
||||
|
||||
vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
KEYCODE_NONE,
|
||||
KEYCODE_LITERAL,
|
||||
KEYCODE_TAB,
|
||||
KEYCODE_ENTER,
|
||||
KEYCODE_SS3,
|
||||
KEYCODE_CSI,
|
||||
KEYCODE_CSI_CURSOR,
|
||||
KEYCODE_CSINUM,
|
||||
KEYCODE_KEYPAD
|
||||
} type;
|
||||
char literal;
|
||||
int csinum;
|
||||
} keycodes_s;
|
||||
|
||||
static keycodes_s keycodes[] = {
|
||||
{ KEYCODE_NONE, 0, 0 }, /* NONE */
|
||||
|
||||
{ KEYCODE_ENTER, '\r', 0 }, /* ENTER */
|
||||
{ KEYCODE_TAB, '\t', 0 }, /* TAB */
|
||||
{ KEYCODE_LITERAL, '\x7f', 0 }, /* BACKSPACE == ASCII DEL */
|
||||
{ KEYCODE_LITERAL, '\x1b', 0 }, /* ESCAPE */
|
||||
|
||||
{ KEYCODE_CSI_CURSOR, 'A', 0 }, /* UP */
|
||||
{ KEYCODE_CSI_CURSOR, 'B', 0 }, /* DOWN */
|
||||
{ KEYCODE_CSI_CURSOR, 'D', 0 }, /* LEFT */
|
||||
{ KEYCODE_CSI_CURSOR, 'C', 0 }, /* RIGHT */
|
||||
|
||||
{ KEYCODE_CSINUM, '~', 2 }, /* INS */
|
||||
{ KEYCODE_CSINUM, '~', 3 }, /* DEL */
|
||||
{ KEYCODE_CSI_CURSOR, 'H', 0 }, /* HOME */
|
||||
{ KEYCODE_CSI_CURSOR, 'F', 0 }, /* END */
|
||||
{ KEYCODE_CSINUM, '~', 5 }, /* PAGEUP */
|
||||
{ KEYCODE_CSINUM, '~', 6 }, /* PAGEDOWN */
|
||||
};
|
||||
|
||||
static keycodes_s keycodes_fn[] = {
|
||||
{ KEYCODE_NONE, 0, 0 }, /* F0 - shouldn't happen */
|
||||
{ KEYCODE_CSI_CURSOR, 'P', 0 }, /* F1 */
|
||||
{ KEYCODE_CSI_CURSOR, 'Q', 0 }, /* F2 */
|
||||
{ KEYCODE_CSI_CURSOR, 'R', 0 }, /* F3 */
|
||||
{ KEYCODE_CSI_CURSOR, 'S', 0 }, /* F4 */
|
||||
{ KEYCODE_CSINUM, '~', 15 }, /* F5 */
|
||||
{ KEYCODE_CSINUM, '~', 17 }, /* F6 */
|
||||
{ KEYCODE_CSINUM, '~', 18 }, /* F7 */
|
||||
{ KEYCODE_CSINUM, '~', 19 }, /* F8 */
|
||||
{ KEYCODE_CSINUM, '~', 20 }, /* F9 */
|
||||
{ KEYCODE_CSINUM, '~', 21 }, /* F10 */
|
||||
{ KEYCODE_CSINUM, '~', 23 }, /* F11 */
|
||||
{ KEYCODE_CSINUM, '~', 24 }, /* F12 */
|
||||
};
|
||||
|
||||
static keycodes_s keycodes_kp[] = {
|
||||
{ KEYCODE_KEYPAD, '0', 'p' }, /* KP_0 */
|
||||
{ KEYCODE_KEYPAD, '1', 'q' }, /* KP_1 */
|
||||
{ KEYCODE_KEYPAD, '2', 'r' }, /* KP_2 */
|
||||
{ KEYCODE_KEYPAD, '3', 's' }, /* KP_3 */
|
||||
{ KEYCODE_KEYPAD, '4', 't' }, /* KP_4 */
|
||||
{ KEYCODE_KEYPAD, '5', 'u' }, /* KP_5 */
|
||||
{ KEYCODE_KEYPAD, '6', 'v' }, /* KP_6 */
|
||||
{ KEYCODE_KEYPAD, '7', 'w' }, /* KP_7 */
|
||||
{ KEYCODE_KEYPAD, '8', 'x' }, /* KP_8 */
|
||||
{ KEYCODE_KEYPAD, '9', 'y' }, /* KP_9 */
|
||||
{ KEYCODE_KEYPAD, '*', 'j' }, /* KP_MULT */
|
||||
{ KEYCODE_KEYPAD, '+', 'k' }, /* KP_PLUS */
|
||||
{ KEYCODE_KEYPAD, ',', 'l' }, /* KP_COMMA */
|
||||
{ KEYCODE_KEYPAD, '-', 'm' }, /* KP_MINUS */
|
||||
{ KEYCODE_KEYPAD, '.', 'n' }, /* KP_PERIOD */
|
||||
{ KEYCODE_KEYPAD, '/', 'o' }, /* KP_DIVIDE */
|
||||
{ KEYCODE_KEYPAD, '\n', 'M' }, /* KP_ENTER */
|
||||
{ KEYCODE_KEYPAD, '=', 'X' }, /* KP_EQUAL */
|
||||
};
|
||||
|
||||
void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
|
||||
{
|
||||
keycodes_s k;
|
||||
|
||||
if(key == VTERM_KEY_NONE)
|
||||
return;
|
||||
|
||||
if(key < VTERM_KEY_FUNCTION_0) {
|
||||
if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
|
||||
return;
|
||||
k = keycodes[key];
|
||||
}
|
||||
else if(key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
|
||||
if((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0]))
|
||||
return;
|
||||
k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
|
||||
}
|
||||
else if(key >= VTERM_KEY_KP_0) {
|
||||
if((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0]))
|
||||
return;
|
||||
k = keycodes_kp[key - VTERM_KEY_KP_0];
|
||||
}
|
||||
|
||||
switch(k.type) {
|
||||
case KEYCODE_NONE:
|
||||
break;
|
||||
|
||||
case KEYCODE_TAB:
|
||||
/* Shift-Tab is CSI Z but plain Tab is 0x09 */
|
||||
if(mod == VTERM_MOD_SHIFT)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
|
||||
else if(mod & VTERM_MOD_SHIFT)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod+1);
|
||||
else
|
||||
goto case_LITERAL;
|
||||
break;
|
||||
|
||||
case KEYCODE_ENTER:
|
||||
/* Enter is CRLF in newline mode, but just LF in linefeed */
|
||||
if(vt->state->mode.newline)
|
||||
vterm_push_output_sprintf(vt, "\r\n");
|
||||
else
|
||||
goto case_LITERAL;
|
||||
break;
|
||||
|
||||
case KEYCODE_LITERAL: case_LITERAL:
|
||||
if(mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL))
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod+1);
|
||||
else
|
||||
vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
|
||||
break;
|
||||
|
||||
case KEYCODE_SS3: case_SS3:
|
||||
if(mod == 0)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
|
||||
else
|
||||
goto case_CSI;
|
||||
break;
|
||||
|
||||
case KEYCODE_CSI: case_CSI:
|
||||
if(mod == 0)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
|
||||
else
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
|
||||
break;
|
||||
|
||||
case KEYCODE_CSINUM:
|
||||
if(mod == 0)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
|
||||
else
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
|
||||
break;
|
||||
|
||||
case KEYCODE_CSI_CURSOR:
|
||||
if(vt->state->mode.cursor)
|
||||
goto case_SS3;
|
||||
else
|
||||
goto case_CSI;
|
||||
|
||||
case KEYCODE_KEYPAD:
|
||||
if(vt->state->mode.keypad) {
|
||||
k.literal = k.csinum;
|
||||
goto case_SS3;
|
||||
}
|
||||
else
|
||||
goto case_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_keyboard_start_paste(VTerm *vt)
|
||||
{
|
||||
if(vt->state->mode.bracketpaste)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~");
|
||||
}
|
||||
|
||||
void vterm_keyboard_end_paste(VTerm *vt)
|
||||
{
|
||||
if(vt->state->mode.bracketpaste)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~");
|
||||
}
|
96
src/libvterm/src/mouse.c
Normal file
96
src/libvterm/src/mouse.c
Normal file
@ -0,0 +1,96 @@
|
||||
#include "vterm_internal.h"
|
||||
|
||||
#include "utf8.h"
|
||||
|
||||
static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row)
|
||||
{
|
||||
modifiers <<= 2;
|
||||
|
||||
switch(state->mouse_protocol) {
|
||||
case MOUSE_X10:
|
||||
if(col + 0x21 > 0xff)
|
||||
col = 0xff - 0x21;
|
||||
if(row + 0x21 > 0xff)
|
||||
row = 0xff - 0x21;
|
||||
|
||||
if(!pressed)
|
||||
code = 3;
|
||||
|
||||
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c",
|
||||
(code | modifiers) + 0x20, col + 0x21, row + 0x21);
|
||||
break;
|
||||
|
||||
case MOUSE_UTF8:
|
||||
{
|
||||
char utf8[18]; size_t len = 0;
|
||||
|
||||
if(!pressed)
|
||||
code = 3;
|
||||
|
||||
len += fill_utf8((code | modifiers) + 0x20, utf8 + len);
|
||||
len += fill_utf8(col + 0x21, utf8 + len);
|
||||
len += fill_utf8(row + 0x21, utf8 + len);
|
||||
utf8[len] = 0;
|
||||
|
||||
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8);
|
||||
}
|
||||
break;
|
||||
|
||||
case MOUSE_SGR:
|
||||
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c",
|
||||
code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm');
|
||||
break;
|
||||
|
||||
case MOUSE_RXVT:
|
||||
if(!pressed)
|
||||
code = 3;
|
||||
|
||||
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM",
|
||||
code | modifiers, col + 1, row + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod)
|
||||
{
|
||||
VTermState *state = vt->state;
|
||||
|
||||
if(col == state->mouse_col && row == state->mouse_row)
|
||||
return;
|
||||
|
||||
state->mouse_col = col;
|
||||
state->mouse_row = row;
|
||||
|
||||
if((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons) ||
|
||||
(state->mouse_flags & MOUSE_WANT_MOVE)) {
|
||||
int button = state->mouse_buttons & 0x01 ? 1 :
|
||||
state->mouse_buttons & 0x02 ? 2 :
|
||||
state->mouse_buttons & 0x04 ? 3 : 4;
|
||||
output_mouse(state, button-1 + 0x20, 1, mod, col, row);
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod)
|
||||
{
|
||||
VTermState *state = vt->state;
|
||||
|
||||
int old_buttons = state->mouse_buttons;
|
||||
|
||||
if(button > 0 && button <= 3) {
|
||||
if(pressed)
|
||||
state->mouse_buttons |= (1 << (button-1));
|
||||
else
|
||||
state->mouse_buttons &= ~(1 << (button-1));
|
||||
}
|
||||
|
||||
/* Most of the time we don't get button releases from 4/5 */
|
||||
if(state->mouse_buttons == old_buttons && button < 4)
|
||||
return;
|
||||
|
||||
if(button < 4) {
|
||||
output_mouse(state, button-1, pressed, mod, state->mouse_col, state->mouse_row);
|
||||
}
|
||||
else if(button < 6) {
|
||||
output_mouse(state, button-4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row);
|
||||
}
|
||||
}
|
346
src/libvterm/src/parser.c
Normal file
346
src/libvterm/src/parser.c
Normal file
@ -0,0 +1,346 @@
|
||||
#include "vterm_internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CSI_ARGS_MAX 16
|
||||
#define CSI_LEADER_MAX 16
|
||||
#define CSI_INTERMED_MAX 16
|
||||
|
||||
static void do_control(VTerm *vt, unsigned char control)
|
||||
{
|
||||
if(vt->parser_callbacks && vt->parser_callbacks->control)
|
||||
if((*vt->parser_callbacks->control)(control, vt->cbdata))
|
||||
return;
|
||||
|
||||
DEBUG_LOG1("libvterm: Unhandled control 0x%02x\n", control);
|
||||
}
|
||||
|
||||
static void do_string_csi(VTerm *vt, const char *args, size_t arglen, char command)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
int leaderlen = 0;
|
||||
char leader[CSI_LEADER_MAX];
|
||||
int argcount = 1; /* Always at least 1 arg */
|
||||
long csi_args[CSI_ARGS_MAX];
|
||||
int argi;
|
||||
int intermedlen = 0;
|
||||
char intermed[CSI_INTERMED_MAX];
|
||||
|
||||
/* Extract leader bytes 0x3c to 0x3f */
|
||||
for( ; i < (int)arglen; i++) {
|
||||
if(args[i] < 0x3c || args[i] > 0x3f)
|
||||
break;
|
||||
if(leaderlen < CSI_LEADER_MAX-1)
|
||||
leader[leaderlen++] = args[i];
|
||||
}
|
||||
|
||||
leader[leaderlen] = 0;
|
||||
|
||||
for( ; i < (int)arglen; i++)
|
||||
if(args[i] == 0x3b || args[i] == 0x3a) /* ; or : */
|
||||
argcount++;
|
||||
|
||||
/* TODO: Consider if these buffers should live in the VTerm struct itself */
|
||||
if(argcount > CSI_ARGS_MAX)
|
||||
argcount = CSI_ARGS_MAX;
|
||||
|
||||
for(argi = 0; argi < argcount; argi++)
|
||||
csi_args[argi] = CSI_ARG_MISSING;
|
||||
|
||||
argi = 0;
|
||||
for(i = leaderlen; i < (int)arglen && argi < argcount; i++) {
|
||||
switch(args[i]) {
|
||||
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
|
||||
case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
|
||||
if(csi_args[argi] == CSI_ARG_MISSING)
|
||||
csi_args[argi] = 0;
|
||||
csi_args[argi] *= 10;
|
||||
csi_args[argi] += args[i] - '0';
|
||||
break;
|
||||
case 0x3a:
|
||||
csi_args[argi] |= CSI_ARG_FLAG_MORE;
|
||||
/* FALLTHROUGH */
|
||||
case 0x3b:
|
||||
argi++;
|
||||
break;
|
||||
default:
|
||||
goto done_leader;
|
||||
}
|
||||
}
|
||||
done_leader: ;
|
||||
|
||||
for( ; i < (int)arglen; i++) {
|
||||
if((args[i] & 0xf0) != 0x20)
|
||||
break;
|
||||
|
||||
if(intermedlen < CSI_INTERMED_MAX-1)
|
||||
intermed[intermedlen++] = args[i];
|
||||
}
|
||||
|
||||
intermed[intermedlen] = 0;
|
||||
|
||||
if(i < (int)arglen) {
|
||||
DEBUG_LOG2("libvterm: TODO unhandled CSI bytes \"%.*s\"\n", (int)(arglen - i), args + i);
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("Parsed CSI args %.*s as:\n", arglen, args);
|
||||
printf(" leader: %s\n", leader);
|
||||
for(argi = 0; argi < argcount; argi++) {
|
||||
printf(" %lu", CSI_ARG(csi_args[argi]));
|
||||
if(!CSI_ARG_HAS_MORE(csi_args[argi]))
|
||||
printf("\n");
|
||||
printf(" intermed: %s\n", intermed);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(vt->parser_callbacks && vt->parser_callbacks->csi)
|
||||
if((*vt->parser_callbacks->csi)(leaderlen ? leader : NULL, csi_args, argcount, intermedlen ? intermed : NULL, command, vt->cbdata))
|
||||
return;
|
||||
|
||||
DEBUG_LOG3("libvterm: Unhandled CSI %.*s %c\n", (int)arglen, args, command);
|
||||
}
|
||||
|
||||
static void append_strbuffer(VTerm *vt, const char *str, size_t len)
|
||||
{
|
||||
if(len > vt->strbuffer_len - vt->strbuffer_cur) {
|
||||
len = vt->strbuffer_len - vt->strbuffer_cur;
|
||||
DEBUG_LOG1("Truncating strbuffer preserve to %zd bytes\n", len);
|
||||
}
|
||||
|
||||
if(len > 0) {
|
||||
strncpy(vt->strbuffer + vt->strbuffer_cur, str, len);
|
||||
vt->strbuffer_cur += len;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t do_string(VTerm *vt, const char *str_frag, size_t len)
|
||||
{
|
||||
size_t eaten;
|
||||
|
||||
if(vt->strbuffer_cur) {
|
||||
if(str_frag)
|
||||
append_strbuffer(vt, str_frag, len);
|
||||
|
||||
str_frag = vt->strbuffer;
|
||||
len = vt->strbuffer_cur;
|
||||
}
|
||||
else if(!str_frag) {
|
||||
DEBUG_LOG("parser.c: TODO: No strbuffer _and_ no final fragment???\n");
|
||||
len = 0;
|
||||
}
|
||||
|
||||
vt->strbuffer_cur = 0;
|
||||
|
||||
switch(vt->parser_state) {
|
||||
case NORMAL:
|
||||
if(vt->parser_callbacks && vt->parser_callbacks->text)
|
||||
if((eaten = (*vt->parser_callbacks->text)(str_frag, len, vt->cbdata)))
|
||||
return eaten;
|
||||
|
||||
DEBUG_LOG1("libvterm: Unhandled text (%zu chars)\n", len);
|
||||
return 0;
|
||||
|
||||
case ESC:
|
||||
if(len == 1 && str_frag[0] >= 0x40 && str_frag[0] < 0x60) {
|
||||
/* C1 emulations using 7bit clean */
|
||||
/* ESC 0x40 == 0x80 */
|
||||
do_control(vt, str_frag[0] + 0x40);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(vt->parser_callbacks && vt->parser_callbacks->escape)
|
||||
if((*vt->parser_callbacks->escape)(str_frag, len, vt->cbdata))
|
||||
return 0;
|
||||
|
||||
DEBUG_LOG1("libvterm: Unhandled escape ESC 0x%02x\n", str_frag[len-1]);
|
||||
return 0;
|
||||
|
||||
case CSI:
|
||||
do_string_csi(vt, str_frag, len - 1, str_frag[len - 1]);
|
||||
return 0;
|
||||
|
||||
case OSC:
|
||||
if(vt->parser_callbacks && vt->parser_callbacks->osc)
|
||||
if((*vt->parser_callbacks->osc)(str_frag, len, vt->cbdata))
|
||||
return 0;
|
||||
|
||||
DEBUG_LOG2("libvterm: Unhandled OSC %.*s\n", (int)len, str_frag);
|
||||
return 0;
|
||||
|
||||
case DCS:
|
||||
if(vt->parser_callbacks && vt->parser_callbacks->dcs)
|
||||
if((*vt->parser_callbacks->dcs)(str_frag, len, vt->cbdata))
|
||||
return 0;
|
||||
|
||||
DEBUG_LOG2("libvterm: Unhandled DCS %.*s\n", (int)len, str_frag);
|
||||
return 0;
|
||||
|
||||
case ESC_IN_OSC:
|
||||
case ESC_IN_DCS:
|
||||
DEBUG_LOG("libvterm: ARGH! Should never do_string() in ESC_IN_{OSC,DCS}\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
|
||||
{
|
||||
size_t pos = 0;
|
||||
const char *string_start;
|
||||
|
||||
switch(vt->parser_state) {
|
||||
case NORMAL:
|
||||
string_start = NULL;
|
||||
break;
|
||||
case ESC:
|
||||
case ESC_IN_OSC:
|
||||
case ESC_IN_DCS:
|
||||
case CSI:
|
||||
case OSC:
|
||||
case DCS:
|
||||
string_start = bytes;
|
||||
break;
|
||||
}
|
||||
|
||||
#define ENTER_STRING_STATE(st) do { vt->parser_state = st; string_start = bytes + pos + 1; } while(0)
|
||||
#define ENTER_NORMAL_STATE() do { vt->parser_state = NORMAL; string_start = NULL; } while(0)
|
||||
|
||||
for( ; pos < len; pos++) {
|
||||
unsigned char c = bytes[pos];
|
||||
|
||||
if(c == 0x00 || c == 0x7f) { /* NUL, DEL */
|
||||
if(vt->parser_state != NORMAL) {
|
||||
append_strbuffer(vt, string_start, bytes + pos - string_start);
|
||||
string_start = bytes + pos + 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(c == 0x18 || c == 0x1a) { /* CAN, SUB */
|
||||
ENTER_NORMAL_STATE();
|
||||
continue;
|
||||
}
|
||||
else if(c == 0x1b) { /* ESC */
|
||||
if(vt->parser_state == OSC)
|
||||
vt->parser_state = ESC_IN_OSC;
|
||||
else if(vt->parser_state == DCS)
|
||||
vt->parser_state = ESC_IN_DCS;
|
||||
else
|
||||
ENTER_STRING_STATE(ESC);
|
||||
continue;
|
||||
}
|
||||
else if(c == 0x07 && /* BEL, can stand for ST in OSC or DCS state */
|
||||
(vt->parser_state == OSC || vt->parser_state == DCS)) {
|
||||
/* fallthrough */
|
||||
}
|
||||
else if(c < 0x20) { /* other C0 */
|
||||
if(vt->parser_state != NORMAL)
|
||||
append_strbuffer(vt, string_start, bytes + pos - string_start);
|
||||
do_control(vt, c);
|
||||
if(vt->parser_state != NORMAL)
|
||||
string_start = bytes + pos + 1;
|
||||
continue;
|
||||
}
|
||||
/* else fallthrough */
|
||||
|
||||
switch(vt->parser_state) {
|
||||
case ESC_IN_OSC:
|
||||
case ESC_IN_DCS:
|
||||
if(c == 0x5c) { /* ST */
|
||||
switch(vt->parser_state) {
|
||||
case ESC_IN_OSC: vt->parser_state = OSC; break;
|
||||
case ESC_IN_DCS: vt->parser_state = DCS; break;
|
||||
default: break;
|
||||
}
|
||||
do_string(vt, string_start, bytes + pos - string_start - 1);
|
||||
ENTER_NORMAL_STATE();
|
||||
break;
|
||||
}
|
||||
vt->parser_state = ESC;
|
||||
string_start = bytes + pos;
|
||||
/* else fallthrough */
|
||||
|
||||
case ESC:
|
||||
switch(c) {
|
||||
case 0x50: /* DCS */
|
||||
ENTER_STRING_STATE(DCS);
|
||||
break;
|
||||
case 0x5b: /* CSI */
|
||||
ENTER_STRING_STATE(CSI);
|
||||
break;
|
||||
case 0x5d: /* OSC */
|
||||
ENTER_STRING_STATE(OSC);
|
||||
break;
|
||||
default:
|
||||
if(c >= 0x30 && c < 0x7f) {
|
||||
/* +1 to pos because we want to include this command byte as well */
|
||||
do_string(vt, string_start, bytes + pos - string_start + 1);
|
||||
ENTER_NORMAL_STATE();
|
||||
}
|
||||
else if(c >= 0x20 && c < 0x30) {
|
||||
/* intermediate byte */
|
||||
}
|
||||
else {
|
||||
DEBUG_LOG1("TODO: Unhandled byte %02x in Escape\n", c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CSI:
|
||||
if(c >= 0x40 && c <= 0x7f) {
|
||||
/* +1 to pos because we want to include this command byte as well */
|
||||
do_string(vt, string_start, bytes + pos - string_start + 1);
|
||||
ENTER_NORMAL_STATE();
|
||||
}
|
||||
break;
|
||||
|
||||
case OSC:
|
||||
case DCS:
|
||||
if(c == 0x07 || (c == 0x9c && !vt->mode.utf8)) {
|
||||
do_string(vt, string_start, bytes + pos - string_start);
|
||||
ENTER_NORMAL_STATE();
|
||||
}
|
||||
break;
|
||||
|
||||
case NORMAL:
|
||||
if(c >= 0x80 && c < 0xa0 && !vt->mode.utf8) {
|
||||
switch(c) {
|
||||
case 0x90: /* DCS */
|
||||
ENTER_STRING_STATE(DCS);
|
||||
break;
|
||||
case 0x9b: /* CSI */
|
||||
ENTER_STRING_STATE(CSI);
|
||||
break;
|
||||
case 0x9d: /* OSC */
|
||||
ENTER_STRING_STATE(OSC);
|
||||
break;
|
||||
default:
|
||||
do_control(vt, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t text_eaten = do_string(vt, bytes + pos, len - pos);
|
||||
|
||||
if(text_eaten == 0) {
|
||||
string_start = bytes + pos;
|
||||
goto pause;
|
||||
}
|
||||
|
||||
pos += (text_eaten - 1); /* we'll ++ it again in a moment */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pause:
|
||||
if(string_start && string_start < len + bytes) {
|
||||
size_t remaining = len - (string_start - bytes);
|
||||
append_strbuffer(vt, string_start, remaining);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
504
src/libvterm/src/pen.c
Normal file
504
src/libvterm/src/pen.c
Normal file
@ -0,0 +1,504 @@
|
||||
#include "vterm_internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static const VTermColor ansi_colors[] = {
|
||||
/* R G B */
|
||||
{ 0, 0, 0 }, /* black */
|
||||
{ 224, 0, 0 }, /* red */
|
||||
{ 0, 224, 0 }, /* green */
|
||||
{ 224, 224, 0 }, /* yellow */
|
||||
{ 0, 0, 224 }, /* blue */
|
||||
{ 224, 0, 224 }, /* magenta */
|
||||
{ 0, 224, 224 }, /* cyan */
|
||||
{ 224, 224, 224 }, /* white == light grey */
|
||||
|
||||
/* high intensity */
|
||||
{ 128, 128, 128 }, /* black */
|
||||
{ 255, 64, 64 }, /* red */
|
||||
{ 64, 255, 64 }, /* green */
|
||||
{ 255, 255, 64 }, /* yellow */
|
||||
{ 64, 64, 255 }, /* blue */
|
||||
{ 255, 64, 255 }, /* magenta */
|
||||
{ 64, 255, 255 }, /* cyan */
|
||||
{ 255, 255, 255 }, /* white for real */
|
||||
};
|
||||
|
||||
static int ramp6[] = {
|
||||
0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
|
||||
};
|
||||
|
||||
static int ramp24[] = {
|
||||
0x00, 0x0B, 0x16, 0x21, 0x2C, 0x37, 0x42, 0x4D, 0x58, 0x63, 0x6E, 0x79,
|
||||
0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
|
||||
};
|
||||
|
||||
static bool lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
|
||||
{
|
||||
if(index >= 0 && index < 16) {
|
||||
*col = state->colors[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool lookup_colour_palette(const VTermState *state, long index, VTermColor *col)
|
||||
{
|
||||
if(index >= 0 && index < 16) {
|
||||
/* Normal 8 colours or high intensity - parse as palette 0 */
|
||||
return lookup_colour_ansi(state, index, col);
|
||||
}
|
||||
else if(index >= 16 && index < 232) {
|
||||
/* 216-colour cube */
|
||||
index -= 16;
|
||||
|
||||
col->blue = ramp6[index % 6];
|
||||
col->green = ramp6[index/6 % 6];
|
||||
col->red = ramp6[index/6/6 % 6];
|
||||
|
||||
return true;
|
||||
}
|
||||
else if(index >= 232 && index < 256) {
|
||||
/* 24 greyscales */
|
||||
index -= 232;
|
||||
|
||||
col->blue = ramp24[index];
|
||||
col->green = ramp24[index];
|
||||
col->red = ramp24[index];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col, int *index)
|
||||
{
|
||||
switch(palette) {
|
||||
case 2: /* RGB mode - 3 args contain colour values directly */
|
||||
if(argcount < 3)
|
||||
return argcount;
|
||||
|
||||
col->red = CSI_ARG(args[0]);
|
||||
col->green = CSI_ARG(args[1]);
|
||||
col->blue = CSI_ARG(args[2]);
|
||||
|
||||
return 3;
|
||||
|
||||
case 5: /* XTerm 256-colour mode */
|
||||
if(index)
|
||||
*index = CSI_ARG_OR(args[0], -1);
|
||||
|
||||
lookup_colour_palette(state, argcount ? CSI_ARG_OR(args[0], -1) : -1, col);
|
||||
|
||||
return argcount ? 1 : 0;
|
||||
|
||||
default:
|
||||
DEBUG_LOG1("Unrecognised colour palette %d\n", palette);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Some conveniences */
|
||||
|
||||
static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type UNUSED, VTermValue *val)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if(type != vterm_get_attr_type(attr)) {
|
||||
DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
|
||||
attr, vterm_get_attr_type(attr), type);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if(state->callbacks && state->callbacks->setpenattr)
|
||||
(*state->callbacks->setpenattr)(attr, val, state->cbdata);
|
||||
}
|
||||
|
||||
static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean)
|
||||
{
|
||||
VTermValue val;
|
||||
val.boolean = boolean;
|
||||
setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
|
||||
}
|
||||
|
||||
static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
|
||||
{
|
||||
VTermValue val;
|
||||
val.number = number;
|
||||
setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
|
||||
}
|
||||
|
||||
static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
|
||||
{
|
||||
VTermValue val;
|
||||
val.color = color;
|
||||
setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
|
||||
}
|
||||
|
||||
static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
|
||||
{
|
||||
VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
|
||||
|
||||
lookup_colour_ansi(state, col, colp);
|
||||
|
||||
setpenattr_col(state, attr, *colp);
|
||||
}
|
||||
|
||||
INTERNAL void vterm_state_newpen(VTermState *state)
|
||||
{
|
||||
int col;
|
||||
|
||||
/* 90% grey so that pure white is brighter */
|
||||
state->default_fg.red = state->default_fg.green = state->default_fg.blue = 240;
|
||||
state->default_bg.red = state->default_bg.green = state->default_bg.blue = 0;
|
||||
|
||||
for(col = 0; col < 16; col++)
|
||||
state->colors[col] = ansi_colors[col];
|
||||
}
|
||||
|
||||
INTERNAL void vterm_state_resetpen(VTermState *state)
|
||||
{
|
||||
state->pen.bold = 0; setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
|
||||
state->pen.underline = 0; setpenattr_int( state, VTERM_ATTR_UNDERLINE, 0);
|
||||
state->pen.italic = 0; setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
|
||||
state->pen.blink = 0; setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
|
||||
state->pen.reverse = 0; setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
|
||||
state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
|
||||
state->pen.font = 0; setpenattr_int( state, VTERM_ATTR_FONT, 0);
|
||||
|
||||
state->fg_index = -1;
|
||||
state->bg_index = -1;
|
||||
state->pen.fg = state->default_fg; setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
|
||||
state->pen.bg = state->default_bg; setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
|
||||
}
|
||||
|
||||
INTERNAL void vterm_state_savepen(VTermState *state, int save)
|
||||
{
|
||||
if(save) {
|
||||
state->saved.pen = state->pen;
|
||||
}
|
||||
else {
|
||||
state->pen = state->saved.pen;
|
||||
|
||||
setpenattr_bool(state, VTERM_ATTR_BOLD, state->pen.bold);
|
||||
setpenattr_int( state, VTERM_ATTR_UNDERLINE, state->pen.underline);
|
||||
setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
|
||||
setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
|
||||
setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
|
||||
setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
|
||||
setpenattr_int( state, VTERM_ATTR_FONT, state->pen.font);
|
||||
setpenattr_col( state, VTERM_ATTR_FOREGROUND, state->pen.fg);
|
||||
setpenattr_col( state, VTERM_ATTR_BACKGROUND, state->pen.bg);
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg)
|
||||
{
|
||||
*default_fg = state->default_fg;
|
||||
*default_bg = state->default_bg;
|
||||
}
|
||||
|
||||
void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col)
|
||||
{
|
||||
lookup_colour_palette(state, index, col);
|
||||
}
|
||||
|
||||
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg)
|
||||
{
|
||||
state->default_fg = *default_fg;
|
||||
state->default_bg = *default_bg;
|
||||
}
|
||||
|
||||
void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
|
||||
{
|
||||
if(index >= 0 && index < 16)
|
||||
state->colors[index] = *col;
|
||||
}
|
||||
|
||||
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
|
||||
{
|
||||
state->bold_is_highbright = bold_is_highbright;
|
||||
}
|
||||
|
||||
INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount)
|
||||
{
|
||||
/* SGR - ECMA-48 8.3.117 */
|
||||
|
||||
int argi = 0;
|
||||
int value;
|
||||
|
||||
while(argi < argcount) {
|
||||
/* This logic is easier to do 'done' backwards; set it true, and make it
|
||||
false again in the 'default' case */
|
||||
int done = 1;
|
||||
|
||||
long arg;
|
||||
switch(arg = CSI_ARG(args[argi])) {
|
||||
case CSI_ARG_MISSING:
|
||||
case 0: /* Reset */
|
||||
vterm_state_resetpen(state);
|
||||
break;
|
||||
|
||||
case 1: /* Bold on */
|
||||
state->pen.bold = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
|
||||
if(state->fg_index > -1 && state->fg_index < 8 && state->bold_is_highbright)
|
||||
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, state->fg_index + (state->pen.bold ? 8 : 0));
|
||||
break;
|
||||
|
||||
case 3: /* Italic on */
|
||||
state->pen.italic = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
|
||||
break;
|
||||
|
||||
case 4: /* Underline single */
|
||||
state->pen.underline = 1;
|
||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, 1);
|
||||
break;
|
||||
|
||||
case 5: /* Blink */
|
||||
state->pen.blink = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
|
||||
break;
|
||||
|
||||
case 7: /* Reverse on */
|
||||
state->pen.reverse = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
|
||||
break;
|
||||
|
||||
case 9: /* Strikethrough on */
|
||||
state->pen.strike = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
|
||||
break;
|
||||
|
||||
case 10: case 11: case 12: case 13: case 14:
|
||||
case 15: case 16: case 17: case 18: case 19: /* Select font */
|
||||
state->pen.font = CSI_ARG(args[argi]) - 10;
|
||||
setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
|
||||
break;
|
||||
|
||||
case 21: /* Underline double */
|
||||
state->pen.underline = 2;
|
||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, 2);
|
||||
break;
|
||||
|
||||
case 22: /* Bold off */
|
||||
state->pen.bold = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
|
||||
break;
|
||||
|
||||
case 23: /* Italic and Gothic (currently unsupported) off */
|
||||
state->pen.italic = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
|
||||
break;
|
||||
|
||||
case 24: /* Underline off */
|
||||
state->pen.underline = 0;
|
||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
|
||||
break;
|
||||
|
||||
case 25: /* Blink off */
|
||||
state->pen.blink = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
|
||||
break;
|
||||
|
||||
case 27: /* Reverse off */
|
||||
state->pen.reverse = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
|
||||
break;
|
||||
|
||||
case 29: /* Strikethrough off */
|
||||
state->pen.strike = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
|
||||
break;
|
||||
|
||||
case 30: case 31: case 32: case 33:
|
||||
case 34: case 35: case 36: case 37: /* Foreground colour palette */
|
||||
value = CSI_ARG(args[argi]) - 30;
|
||||
state->fg_index = value;
|
||||
if(state->pen.bold && state->bold_is_highbright)
|
||||
value += 8;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
|
||||
break;
|
||||
|
||||
case 38: /* Foreground colour alternative palette */
|
||||
state->fg_index = -1;
|
||||
if(argcount - argi < 1)
|
||||
return;
|
||||
argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg, &state->fg_index);
|
||||
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
|
||||
break;
|
||||
|
||||
case 39: /* Foreground colour default */
|
||||
state->fg_index = -1;
|
||||
state->pen.fg = state->default_fg;
|
||||
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
|
||||
break;
|
||||
|
||||
case 40: case 41: case 42: case 43:
|
||||
case 44: case 45: case 46: case 47: /* Background colour palette */
|
||||
value = CSI_ARG(args[argi]) - 40;
|
||||
state->bg_index = value;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
|
||||
break;
|
||||
|
||||
case 48: /* Background colour alternative palette */
|
||||
state->bg_index = -1;
|
||||
if(argcount - argi < 1)
|
||||
return;
|
||||
argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg, &state->bg_index);
|
||||
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
|
||||
break;
|
||||
|
||||
case 49: /* Default background */
|
||||
state->bg_index = -1;
|
||||
state->pen.bg = state->default_bg;
|
||||
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
|
||||
break;
|
||||
|
||||
case 90: case 91: case 92: case 93:
|
||||
case 94: case 95: case 96: case 97: /* Foreground colour high-intensity palette */
|
||||
value = CSI_ARG(args[argi]) - 90 + 8;
|
||||
state->fg_index = value;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
|
||||
break;
|
||||
|
||||
case 100: case 101: case 102: case 103:
|
||||
case 104: case 105: case 106: case 107: /* Background colour high-intensity palette */
|
||||
value = CSI_ARG(args[argi]) - 100 + 8;
|
||||
state->bg_index = value;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
done = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!done)
|
||||
{
|
||||
DEBUG_LOG1("libvterm: Unhandled CSI SGR %lu\n", arg);
|
||||
}
|
||||
|
||||
while(CSI_ARG_HAS_MORE(args[argi++]));
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount UNUSED)
|
||||
{
|
||||
int argi = 0;
|
||||
|
||||
if(state->pen.bold)
|
||||
args[argi++] = 1;
|
||||
|
||||
if(state->pen.italic)
|
||||
args[argi++] = 3;
|
||||
|
||||
if(state->pen.underline == 1)
|
||||
args[argi++] = 4;
|
||||
|
||||
if(state->pen.blink)
|
||||
args[argi++] = 5;
|
||||
|
||||
if(state->pen.reverse)
|
||||
args[argi++] = 7;
|
||||
|
||||
if(state->pen.strike)
|
||||
args[argi++] = 9;
|
||||
|
||||
if(state->pen.font)
|
||||
args[argi++] = 10 + state->pen.font;
|
||||
|
||||
if(state->pen.underline == 2)
|
||||
args[argi++] = 21;
|
||||
|
||||
if(state->fg_index >= 0 && state->fg_index < 8)
|
||||
args[argi++] = 30 + state->fg_index;
|
||||
else if(state->fg_index >= 8 && state->fg_index < 16)
|
||||
args[argi++] = 90 + state->fg_index - 8;
|
||||
else if(state->fg_index >= 16 && state->fg_index < 256) {
|
||||
args[argi++] = CSI_ARG_FLAG_MORE|38;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE|5;
|
||||
args[argi++] = state->fg_index;
|
||||
}
|
||||
else if(state->fg_index == -1) {
|
||||
/* Send palette 2 if the actual FG colour is not default */
|
||||
if(state->pen.fg.red != state->default_fg.red ||
|
||||
state->pen.fg.green != state->default_fg.green ||
|
||||
state->pen.fg.blue != state->default_fg.blue ) {
|
||||
args[argi++] = CSI_ARG_FLAG_MORE|38;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE|2;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | state->pen.fg.red;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | state->pen.fg.green;
|
||||
args[argi++] = state->pen.fg.blue;
|
||||
}
|
||||
}
|
||||
|
||||
if(state->bg_index >= 0 && state->bg_index < 8)
|
||||
args[argi++] = 40 + state->bg_index;
|
||||
else if(state->bg_index >= 8 && state->bg_index < 16)
|
||||
args[argi++] = 100 + state->bg_index - 8;
|
||||
else if(state->bg_index >= 16 && state->bg_index < 256) {
|
||||
args[argi++] = CSI_ARG_FLAG_MORE|48;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE|5;
|
||||
args[argi++] = state->bg_index;
|
||||
}
|
||||
else if(state->bg_index == -1) {
|
||||
/* Send palette 2 if the actual BG colour is not default */
|
||||
if(state->pen.bg.red != state->default_bg.red ||
|
||||
state->pen.bg.green != state->default_bg.green ||
|
||||
state->pen.bg.blue != state->default_bg.blue ) {
|
||||
args[argi++] = CSI_ARG_FLAG_MORE|48;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE|2;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | state->pen.bg.red;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | state->pen.bg.green;
|
||||
args[argi++] = state->pen.bg.blue;
|
||||
}
|
||||
}
|
||||
|
||||
return argi;
|
||||
}
|
||||
|
||||
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
|
||||
{
|
||||
switch(attr) {
|
||||
case VTERM_ATTR_BOLD:
|
||||
val->boolean = state->pen.bold;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_UNDERLINE:
|
||||
val->number = state->pen.underline;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_ITALIC:
|
||||
val->boolean = state->pen.italic;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_BLINK:
|
||||
val->boolean = state->pen.blink;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_REVERSE:
|
||||
val->boolean = state->pen.reverse;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_STRIKE:
|
||||
val->boolean = state->pen.strike;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_FONT:
|
||||
val->number = state->pen.font;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_FOREGROUND:
|
||||
val->color = state->pen.fg;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_BACKGROUND:
|
||||
val->color = state->pen.bg;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
56
src/libvterm/src/rect.h
Normal file
56
src/libvterm/src/rect.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Some utility functions on VTermRect structures
|
||||
*/
|
||||
|
||||
#define STRFrect "(%d,%d-%d,%d)"
|
||||
#define ARGSrect(r) (r).start_row, (r).start_col, (r).end_row, (r).end_col
|
||||
|
||||
/* Expand dst to contain src as well */
|
||||
static void rect_expand(VTermRect *dst, VTermRect *src)
|
||||
{
|
||||
if(dst->start_row > src->start_row) dst->start_row = src->start_row;
|
||||
if(dst->start_col > src->start_col) dst->start_col = src->start_col;
|
||||
if(dst->end_row < src->end_row) dst->end_row = src->end_row;
|
||||
if(dst->end_col < src->end_col) dst->end_col = src->end_col;
|
||||
}
|
||||
|
||||
/* Clip the dst to ensure it does not step outside of bounds */
|
||||
static void rect_clip(VTermRect *dst, VTermRect *bounds)
|
||||
{
|
||||
if(dst->start_row < bounds->start_row) dst->start_row = bounds->start_row;
|
||||
if(dst->start_col < bounds->start_col) dst->start_col = bounds->start_col;
|
||||
if(dst->end_row > bounds->end_row) dst->end_row = bounds->end_row;
|
||||
if(dst->end_col > bounds->end_col) dst->end_col = bounds->end_col;
|
||||
/* Ensure it doesn't end up negatively-sized */
|
||||
if(dst->end_row < dst->start_row) dst->end_row = dst->start_row;
|
||||
if(dst->end_col < dst->start_col) dst->end_col = dst->start_col;
|
||||
}
|
||||
|
||||
/* True if the two rectangles are equal */
|
||||
static int rect_equal(VTermRect *a, VTermRect *b)
|
||||
{
|
||||
return (a->start_row == b->start_row) &&
|
||||
(a->start_col == b->start_col) &&
|
||||
(a->end_row == b->end_row) &&
|
||||
(a->end_col == b->end_col);
|
||||
}
|
||||
|
||||
/* True if small is contained entirely within big */
|
||||
static int rect_contains(VTermRect *big, VTermRect *small)
|
||||
{
|
||||
if(small->start_row < big->start_row) return 0;
|
||||
if(small->start_col < big->start_col) return 0;
|
||||
if(small->end_row > big->end_row) return 0;
|
||||
if(small->end_col > big->end_col) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* True if the rectangles overlap at all */
|
||||
static int rect_intersects(VTermRect *a, VTermRect *b)
|
||||
{
|
||||
if(a->start_row > b->end_row || b->start_row > a->end_row)
|
||||
return 0;
|
||||
if(a->start_col > b->end_col || b->start_col > a->end_col)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
937
src/libvterm/src/screen.c
Normal file
937
src/libvterm/src/screen.c
Normal file
@ -0,0 +1,937 @@
|
||||
#include "vterm_internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rect.h"
|
||||
#include "utf8.h"
|
||||
|
||||
#define UNICODE_SPACE 0x20
|
||||
#define UNICODE_LINEFEED 0x0a
|
||||
|
||||
/* State of the pen at some moment in time, also used in a cell */
|
||||
typedef struct
|
||||
{
|
||||
/* After the bitfield */
|
||||
VTermColor fg, bg;
|
||||
|
||||
unsigned int bold : 1;
|
||||
unsigned int underline : 2;
|
||||
unsigned int italic : 1;
|
||||
unsigned int blink : 1;
|
||||
unsigned int reverse : 1;
|
||||
unsigned int strike : 1;
|
||||
unsigned int font : 4; /* 0 to 9 */
|
||||
|
||||
/* Extra state storage that isn't strictly pen-related */
|
||||
unsigned int protected_cell : 1;
|
||||
unsigned int dwl : 1; /* on a DECDWL or DECDHL line */
|
||||
unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */
|
||||
} ScreenPen;
|
||||
|
||||
/* Internal representation of a screen cell */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
|
||||
ScreenPen pen;
|
||||
} ScreenCell;
|
||||
|
||||
static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell);
|
||||
|
||||
struct VTermScreen
|
||||
{
|
||||
VTerm *vt;
|
||||
VTermState *state;
|
||||
|
||||
const VTermScreenCallbacks *callbacks;
|
||||
void *cbdata;
|
||||
|
||||
VTermDamageSize damage_merge;
|
||||
/* start_row == -1 => no damage */
|
||||
VTermRect damaged;
|
||||
VTermRect pending_scrollrect;
|
||||
int pending_scroll_downward, pending_scroll_rightward;
|
||||
|
||||
int rows;
|
||||
int cols;
|
||||
int global_reverse;
|
||||
|
||||
/* Primary and Altscreen. buffers[1] is lazily allocated as needed */
|
||||
ScreenCell *buffers[2];
|
||||
|
||||
/* buffer will == buffers[0] or buffers[1], depending on altscreen */
|
||||
ScreenCell *buffer;
|
||||
|
||||
/* buffer for a single screen row used in scrollback storage callbacks */
|
||||
VTermScreenCell *sb_buffer;
|
||||
|
||||
ScreenPen pen;
|
||||
};
|
||||
|
||||
static ScreenCell *getcell(const VTermScreen *screen, int row, int col)
|
||||
{
|
||||
if(row < 0 || row >= screen->rows)
|
||||
return NULL;
|
||||
if(col < 0 || col >= screen->cols)
|
||||
return NULL;
|
||||
return screen->buffer + (screen->cols * row) + col;
|
||||
}
|
||||
|
||||
static ScreenCell *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int new_rows, int new_cols)
|
||||
{
|
||||
ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols);
|
||||
int row, col;
|
||||
|
||||
for(row = 0; row < new_rows; row++) {
|
||||
for(col = 0; col < new_cols; col++) {
|
||||
ScreenCell *new_cell = new_buffer + row*new_cols + col;
|
||||
|
||||
if(buffer && row < screen->rows && col < screen->cols)
|
||||
*new_cell = buffer[row * screen->cols + col];
|
||||
else {
|
||||
new_cell->chars[0] = 0;
|
||||
new_cell->pen = screen->pen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(buffer)
|
||||
vterm_allocator_free(screen->vt, buffer);
|
||||
|
||||
return new_buffer;
|
||||
}
|
||||
|
||||
static void damagerect(VTermScreen *screen, VTermRect rect)
|
||||
{
|
||||
VTermRect emit;
|
||||
|
||||
switch(screen->damage_merge) {
|
||||
case VTERM_DAMAGE_CELL:
|
||||
/* Always emit damage event */
|
||||
emit = rect;
|
||||
break;
|
||||
|
||||
case VTERM_DAMAGE_ROW:
|
||||
/* Emit damage longer than one row. Try to merge with existing damage in
|
||||
* the same row */
|
||||
if(rect.end_row > rect.start_row + 1) {
|
||||
/* Bigger than 1 line - flush existing, emit this */
|
||||
vterm_screen_flush_damage(screen);
|
||||
emit = rect;
|
||||
}
|
||||
else if(screen->damaged.start_row == -1) {
|
||||
/* None stored yet */
|
||||
screen->damaged = rect;
|
||||
return;
|
||||
}
|
||||
else if(rect.start_row == screen->damaged.start_row) {
|
||||
/* Merge with the stored line */
|
||||
if(screen->damaged.start_col > rect.start_col)
|
||||
screen->damaged.start_col = rect.start_col;
|
||||
if(screen->damaged.end_col < rect.end_col)
|
||||
screen->damaged.end_col = rect.end_col;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
/* Emit the currently stored line, store a new one */
|
||||
emit = screen->damaged;
|
||||
screen->damaged = rect;
|
||||
}
|
||||
break;
|
||||
|
||||
case VTERM_DAMAGE_SCREEN:
|
||||
case VTERM_DAMAGE_SCROLL:
|
||||
/* Never emit damage event */
|
||||
if(screen->damaged.start_row == -1)
|
||||
screen->damaged = rect;
|
||||
else {
|
||||
rect_expand(&screen->damaged, &rect);
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
DEBUG_LOG1("TODO: Maybe merge damage for level %d\n", screen->damage_merge);
|
||||
return;
|
||||
}
|
||||
|
||||
if(screen->callbacks && screen->callbacks->damage)
|
||||
(*screen->callbacks->damage)(emit, screen->cbdata);
|
||||
}
|
||||
|
||||
static void damagescreen(VTermScreen *screen)
|
||||
{
|
||||
VTermRect rect = {0,0,0,0};
|
||||
rect.end_row = screen->rows;
|
||||
rect.end_col = screen->cols;
|
||||
|
||||
damagerect(screen, rect);
|
||||
}
|
||||
|
||||
static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
|
||||
{
|
||||
int i;
|
||||
int col;
|
||||
VTermRect rect;
|
||||
|
||||
VTermScreen *screen = user;
|
||||
ScreenCell *cell = getcell(screen, pos.row, pos.col);
|
||||
|
||||
if(!cell)
|
||||
return 0;
|
||||
|
||||
for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) {
|
||||
cell->chars[i] = info->chars[i];
|
||||
cell->pen = screen->pen;
|
||||
}
|
||||
if(i < VTERM_MAX_CHARS_PER_CELL)
|
||||
cell->chars[i] = 0;
|
||||
|
||||
for(col = 1; col < info->width; col++)
|
||||
getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1;
|
||||
|
||||
rect.start_row = pos.row;
|
||||
rect.end_row = pos.row+1;
|
||||
rect.start_col = pos.col;
|
||||
rect.end_col = pos.col+info->width;
|
||||
|
||||
cell->pen.protected_cell = info->protected_cell;
|
||||
cell->pen.dwl = info->dwl;
|
||||
cell->pen.dhl = info->dhl;
|
||||
|
||||
damagerect(screen, rect);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int moverect_internal(VTermRect dest, VTermRect src, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
if(screen->callbacks && screen->callbacks->sb_pushline &&
|
||||
dest.start_row == 0 && dest.start_col == 0 && /* starts top-left corner */
|
||||
dest.end_col == screen->cols && /* full width */
|
||||
screen->buffer == screen->buffers[0]) { /* not altscreen */
|
||||
VTermPos pos;
|
||||
for(pos.row = 0; pos.row < src.start_row; pos.row++) {
|
||||
for(pos.col = 0; pos.col < screen->cols; pos.col++)
|
||||
vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
|
||||
|
||||
(screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int cols = src.end_col - src.start_col;
|
||||
int downward = src.start_row - dest.start_row;
|
||||
int init_row, test_row, inc_row;
|
||||
int row;
|
||||
|
||||
if(downward < 0) {
|
||||
init_row = dest.end_row - 1;
|
||||
test_row = dest.start_row - 1;
|
||||
inc_row = -1;
|
||||
}
|
||||
else {
|
||||
init_row = dest.start_row;
|
||||
test_row = dest.end_row;
|
||||
inc_row = +1;
|
||||
}
|
||||
|
||||
for(row = init_row; row != test_row; row += inc_row)
|
||||
memmove(getcell(screen, row, dest.start_col),
|
||||
getcell(screen, row + downward, src.start_col),
|
||||
cols * sizeof(ScreenCell));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int moverect_user(VTermRect dest, VTermRect src, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
if(screen->callbacks && screen->callbacks->moverect) {
|
||||
if(screen->damage_merge != VTERM_DAMAGE_SCROLL)
|
||||
/* Avoid an infinite loop */
|
||||
vterm_screen_flush_damage(screen);
|
||||
|
||||
if((*screen->callbacks->moverect)(dest, src, screen->cbdata))
|
||||
return 1;
|
||||
}
|
||||
|
||||
damagerect(screen, dest);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int erase_internal(VTermRect rect, int selective, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
int row, col;
|
||||
|
||||
for(row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) {
|
||||
const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row);
|
||||
|
||||
for(col = rect.start_col; col < rect.end_col; col++) {
|
||||
ScreenCell *cell = getcell(screen, row, col);
|
||||
|
||||
if(selective && cell->pen.protected_cell)
|
||||
continue;
|
||||
|
||||
cell->chars[0] = 0;
|
||||
cell->pen = screen->pen;
|
||||
cell->pen.dwl = info->doublewidth;
|
||||
cell->pen.dhl = info->doubleheight;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int erase_user(VTermRect rect, int selective UNUSED, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
damagerect(screen, rect);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int erase(VTermRect rect, int selective, void *user)
|
||||
{
|
||||
erase_internal(rect, selective, user);
|
||||
return erase_user(rect, 0, user);
|
||||
}
|
||||
|
||||
static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
if(screen->damage_merge != VTERM_DAMAGE_SCROLL) {
|
||||
vterm_scroll_rect(rect, downward, rightward,
|
||||
moverect_internal, erase_internal, screen);
|
||||
|
||||
vterm_screen_flush_damage(screen);
|
||||
|
||||
vterm_scroll_rect(rect, downward, rightward,
|
||||
moverect_user, erase_user, screen);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(screen->damaged.start_row != -1 &&
|
||||
!rect_intersects(&rect, &screen->damaged)) {
|
||||
vterm_screen_flush_damage(screen);
|
||||
}
|
||||
|
||||
if(screen->pending_scrollrect.start_row == -1) {
|
||||
screen->pending_scrollrect = rect;
|
||||
screen->pending_scroll_downward = downward;
|
||||
screen->pending_scroll_rightward = rightward;
|
||||
}
|
||||
else if(rect_equal(&screen->pending_scrollrect, &rect) &&
|
||||
((screen->pending_scroll_downward == 0 && downward == 0) ||
|
||||
(screen->pending_scroll_rightward == 0 && rightward == 0))) {
|
||||
screen->pending_scroll_downward += downward;
|
||||
screen->pending_scroll_rightward += rightward;
|
||||
}
|
||||
else {
|
||||
vterm_screen_flush_damage(screen);
|
||||
|
||||
screen->pending_scrollrect = rect;
|
||||
screen->pending_scroll_downward = downward;
|
||||
screen->pending_scroll_rightward = rightward;
|
||||
}
|
||||
|
||||
vterm_scroll_rect(rect, downward, rightward,
|
||||
moverect_internal, erase_internal, screen);
|
||||
|
||||
if(screen->damaged.start_row == -1)
|
||||
return 1;
|
||||
|
||||
if(rect_contains(&rect, &screen->damaged)) {
|
||||
/* Scroll region entirely contains the damage; just move it */
|
||||
vterm_rect_move(&screen->damaged, -downward, -rightward);
|
||||
rect_clip(&screen->damaged, &rect);
|
||||
}
|
||||
/* There are a number of possible cases here, but lets restrict this to only
|
||||
* the common case where we might actually gain some performance by
|
||||
* optimising it. Namely, a vertical scroll that neatly cuts the damage
|
||||
* region in half.
|
||||
*/
|
||||
else if(rect.start_col <= screen->damaged.start_col &&
|
||||
rect.end_col >= screen->damaged.end_col &&
|
||||
rightward == 0) {
|
||||
if(screen->damaged.start_row >= rect.start_row &&
|
||||
screen->damaged.start_row < rect.end_row) {
|
||||
screen->damaged.start_row -= downward;
|
||||
if(screen->damaged.start_row < rect.start_row)
|
||||
screen->damaged.start_row = rect.start_row;
|
||||
if(screen->damaged.start_row > rect.end_row)
|
||||
screen->damaged.start_row = rect.end_row;
|
||||
}
|
||||
if(screen->damaged.end_row >= rect.start_row &&
|
||||
screen->damaged.end_row < rect.end_row) {
|
||||
screen->damaged.end_row -= downward;
|
||||
if(screen->damaged.end_row < rect.start_row)
|
||||
screen->damaged.end_row = rect.start_row;
|
||||
if(screen->damaged.end_row > rect.end_row)
|
||||
screen->damaged.end_row = rect.end_row;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG_LOG2("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n",
|
||||
ARGSrect(screen->damaged), ARGSrect(rect));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
if(screen->callbacks && screen->callbacks->movecursor)
|
||||
return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setpenattr(VTermAttr attr, VTermValue *val, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
switch(attr) {
|
||||
case VTERM_ATTR_BOLD:
|
||||
screen->pen.bold = val->boolean;
|
||||
return 1;
|
||||
case VTERM_ATTR_UNDERLINE:
|
||||
screen->pen.underline = val->number;
|
||||
return 1;
|
||||
case VTERM_ATTR_ITALIC:
|
||||
screen->pen.italic = val->boolean;
|
||||
return 1;
|
||||
case VTERM_ATTR_BLINK:
|
||||
screen->pen.blink = val->boolean;
|
||||
return 1;
|
||||
case VTERM_ATTR_REVERSE:
|
||||
screen->pen.reverse = val->boolean;
|
||||
return 1;
|
||||
case VTERM_ATTR_STRIKE:
|
||||
screen->pen.strike = val->boolean;
|
||||
return 1;
|
||||
case VTERM_ATTR_FONT:
|
||||
screen->pen.font = val->number;
|
||||
return 1;
|
||||
case VTERM_ATTR_FOREGROUND:
|
||||
screen->pen.fg = val->color;
|
||||
return 1;
|
||||
case VTERM_ATTR_BACKGROUND:
|
||||
screen->pen.bg = val->color;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int settermprop(VTermProp prop, VTermValue *val, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
switch(prop) {
|
||||
case VTERM_PROP_ALTSCREEN:
|
||||
if(val->boolean && !screen->buffers[1])
|
||||
return 0;
|
||||
|
||||
screen->buffer = val->boolean ? screen->buffers[1] : screen->buffers[0];
|
||||
/* only send a damage event on disable; because during enable there's an
|
||||
* erase that sends a damage anyway
|
||||
*/
|
||||
if(!val->boolean)
|
||||
damagescreen(screen);
|
||||
break;
|
||||
case VTERM_PROP_REVERSE:
|
||||
screen->global_reverse = val->boolean;
|
||||
damagescreen(screen);
|
||||
break;
|
||||
default:
|
||||
; /* ignore */
|
||||
}
|
||||
|
||||
if(screen->callbacks && screen->callbacks->settermprop)
|
||||
return (*screen->callbacks->settermprop)(prop, val, screen->cbdata);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bell(void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
if(screen->callbacks && screen->callbacks->bell)
|
||||
return (*screen->callbacks->bell)(screen->cbdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resize(int new_rows, int new_cols, VTermPos *delta, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
|
||||
int is_altscreen = (screen->buffers[1] && screen->buffer == screen->buffers[1]);
|
||||
|
||||
int old_rows = screen->rows;
|
||||
int old_cols = screen->cols;
|
||||
int first_blank_row;
|
||||
|
||||
if(!is_altscreen && new_rows < old_rows) {
|
||||
/* Fewer rows - determine if we're going to scroll at all, and if so, push
|
||||
those lines to scrollback */
|
||||
VTermPos pos = { 0, 0 };
|
||||
VTermPos cursor = screen->state->pos;
|
||||
/* Find the first blank row after the cursor. */
|
||||
for(pos.row = old_rows - 1; pos.row >= new_rows; pos.row--)
|
||||
if(!vterm_screen_is_eol(screen, pos) || cursor.row == pos.row)
|
||||
break;
|
||||
|
||||
first_blank_row = pos.row + 1;
|
||||
if(first_blank_row > new_rows) {
|
||||
VTermRect rect = {0,0,0,0};
|
||||
rect.end_row = old_rows;
|
||||
rect.end_col = old_cols;
|
||||
scrollrect(rect, first_blank_row - new_rows, 0, user);
|
||||
vterm_screen_flush_damage(screen);
|
||||
|
||||
delta->row -= first_blank_row - new_rows;
|
||||
}
|
||||
}
|
||||
|
||||
screen->buffers[0] = realloc_buffer(screen, screen->buffers[0], new_rows, new_cols);
|
||||
if(screen->buffers[1])
|
||||
screen->buffers[1] = realloc_buffer(screen, screen->buffers[1], new_rows, new_cols);
|
||||
|
||||
screen->buffer = is_altscreen ? screen->buffers[1] : screen->buffers[0];
|
||||
|
||||
screen->rows = new_rows;
|
||||
screen->cols = new_cols;
|
||||
|
||||
if(screen->sb_buffer)
|
||||
vterm_allocator_free(screen->vt, screen->sb_buffer);
|
||||
|
||||
screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols);
|
||||
|
||||
if(new_cols > old_cols) {
|
||||
VTermRect rect;
|
||||
rect.start_row = 0;
|
||||
rect.end_row = old_rows;
|
||||
rect.start_col = old_cols;
|
||||
rect.end_col = new_cols;
|
||||
damagerect(screen, rect);
|
||||
}
|
||||
|
||||
if(new_rows > old_rows) {
|
||||
if(!is_altscreen && screen->callbacks && screen->callbacks->sb_popline) {
|
||||
int rows = new_rows - old_rows;
|
||||
while(rows) {
|
||||
VTermRect rect = {0,0,0,0};
|
||||
VTermPos pos = { 0, 0 };
|
||||
if(!(screen->callbacks->sb_popline(screen->cols, screen->sb_buffer, screen->cbdata)))
|
||||
break;
|
||||
|
||||
rect.end_row = screen->rows;
|
||||
rect.end_col = screen->cols;
|
||||
scrollrect(rect, -1, 0, user);
|
||||
|
||||
for(pos.col = 0; pos.col < screen->cols; pos.col += screen->sb_buffer[pos.col].width)
|
||||
vterm_screen_set_cell(screen, pos, screen->sb_buffer + pos.col);
|
||||
|
||||
rect.end_row = 1;
|
||||
damagerect(screen, rect);
|
||||
|
||||
vterm_screen_flush_damage(screen);
|
||||
|
||||
rows--;
|
||||
delta->row++;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
VTermRect rect;
|
||||
rect.start_row = old_rows;
|
||||
rect.end_row = new_rows;
|
||||
rect.start_col = 0;
|
||||
rect.end_col = new_cols;
|
||||
damagerect(screen, rect);
|
||||
}
|
||||
}
|
||||
|
||||
if(screen->callbacks && screen->callbacks->resize)
|
||||
return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user)
|
||||
{
|
||||
VTermScreen *screen = user;
|
||||
int col;
|
||||
VTermRect rect;
|
||||
|
||||
if(newinfo->doublewidth != oldinfo->doublewidth ||
|
||||
newinfo->doubleheight != oldinfo->doubleheight) {
|
||||
for(col = 0; col < screen->cols; col++) {
|
||||
ScreenCell *cell = getcell(screen, row, col);
|
||||
cell->pen.dwl = newinfo->doublewidth;
|
||||
cell->pen.dhl = newinfo->doubleheight;
|
||||
}
|
||||
|
||||
rect.start_row = row;
|
||||
rect.end_row = row + 1;
|
||||
rect.start_col = 0;
|
||||
rect.end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols;
|
||||
damagerect(screen, rect);
|
||||
|
||||
if(newinfo->doublewidth) {
|
||||
rect.start_col = screen->cols / 2;
|
||||
rect.end_col = screen->cols;
|
||||
|
||||
erase_internal(rect, 0, user);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static VTermStateCallbacks state_cbs = {
|
||||
&putglyph, /* putglyph */
|
||||
&movecursor, /* movecursor */
|
||||
&scrollrect, /* scrollrect */
|
||||
NULL, /* moverect */
|
||||
&erase, /* erase */
|
||||
NULL, /* initpen */
|
||||
&setpenattr, /* setpenattr */
|
||||
&settermprop, /* settermprop */
|
||||
&bell, /* bell */
|
||||
&resize, /* resize */
|
||||
&setlineinfo /* setlineinfo */
|
||||
};
|
||||
|
||||
static VTermScreen *screen_new(VTerm *vt)
|
||||
{
|
||||
VTermState *state = vterm_obtain_state(vt);
|
||||
VTermScreen *screen;
|
||||
int rows, cols;
|
||||
|
||||
if(!state)
|
||||
return NULL;
|
||||
|
||||
screen = vterm_allocator_malloc(vt, sizeof(VTermScreen));
|
||||
|
||||
vterm_get_size(vt, &rows, &cols);
|
||||
|
||||
screen->vt = vt;
|
||||
screen->state = state;
|
||||
|
||||
screen->damage_merge = VTERM_DAMAGE_CELL;
|
||||
screen->damaged.start_row = -1;
|
||||
screen->pending_scrollrect.start_row = -1;
|
||||
|
||||
screen->rows = rows;
|
||||
screen->cols = cols;
|
||||
|
||||
screen->callbacks = NULL;
|
||||
screen->cbdata = NULL;
|
||||
|
||||
screen->buffers[0] = realloc_buffer(screen, NULL, rows, cols);
|
||||
|
||||
screen->buffer = screen->buffers[0];
|
||||
|
||||
screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols);
|
||||
|
||||
vterm_state_set_callbacks(screen->state, &state_cbs, screen);
|
||||
|
||||
return screen;
|
||||
}
|
||||
|
||||
INTERNAL void vterm_screen_free(VTermScreen *screen)
|
||||
{
|
||||
vterm_allocator_free(screen->vt, screen->buffers[0]);
|
||||
if(screen->buffers[1])
|
||||
vterm_allocator_free(screen->vt, screen->buffers[1]);
|
||||
|
||||
vterm_allocator_free(screen->vt, screen->sb_buffer);
|
||||
|
||||
vterm_allocator_free(screen->vt, screen);
|
||||
}
|
||||
|
||||
void vterm_screen_reset(VTermScreen *screen, int hard)
|
||||
{
|
||||
screen->damaged.start_row = -1;
|
||||
screen->pending_scrollrect.start_row = -1;
|
||||
vterm_state_reset(screen->state, hard);
|
||||
vterm_screen_flush_damage(screen);
|
||||
}
|
||||
|
||||
static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect)
|
||||
{
|
||||
size_t outpos = 0;
|
||||
int padding = 0;
|
||||
int row, col;
|
||||
|
||||
#define PUT(c) \
|
||||
if(utf8) { \
|
||||
size_t thislen = utf8_seqlen(c); \
|
||||
if(buffer && outpos + thislen <= len) \
|
||||
outpos += fill_utf8((c), (char *)buffer + outpos); \
|
||||
else \
|
||||
outpos += thislen; \
|
||||
} \
|
||||
else { \
|
||||
if(buffer && outpos + 1 <= len) \
|
||||
((uint32_t*)buffer)[outpos++] = (c); \
|
||||
else \
|
||||
outpos++; \
|
||||
}
|
||||
|
||||
for(row = rect.start_row; row < rect.end_row; row++) {
|
||||
for(col = rect.start_col; col < rect.end_col; col++) {
|
||||
ScreenCell *cell = getcell(screen, row, col);
|
||||
int i;
|
||||
|
||||
if(cell->chars[0] == 0)
|
||||
/* Erased cell, might need a space */
|
||||
padding++;
|
||||
else if(cell->chars[0] == (uint32_t)-1)
|
||||
/* Gap behind a double-width char, do nothing */
|
||||
;
|
||||
else {
|
||||
while(padding) {
|
||||
PUT(UNICODE_SPACE);
|
||||
padding--;
|
||||
}
|
||||
for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) {
|
||||
PUT(cell->chars[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(row < rect.end_row - 1) {
|
||||
PUT(UNICODE_LINEFEED);
|
||||
padding = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return outpos;
|
||||
}
|
||||
|
||||
size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect)
|
||||
{
|
||||
return _get_chars(screen, 0, chars, len, rect);
|
||||
}
|
||||
|
||||
size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect)
|
||||
{
|
||||
return _get_chars(screen, 1, str, len, rect);
|
||||
}
|
||||
|
||||
/* Copy internal to external representation of a screen cell */
|
||||
int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell)
|
||||
{
|
||||
ScreenCell *intcell = getcell(screen, pos.row, pos.col);
|
||||
int i;
|
||||
|
||||
if(!intcell)
|
||||
return 0;
|
||||
|
||||
for(i = 0; ; i++) {
|
||||
cell->chars[i] = intcell->chars[i];
|
||||
if(!intcell->chars[i])
|
||||
break;
|
||||
}
|
||||
|
||||
cell->attrs.bold = intcell->pen.bold;
|
||||
cell->attrs.underline = intcell->pen.underline;
|
||||
cell->attrs.italic = intcell->pen.italic;
|
||||
cell->attrs.blink = intcell->pen.blink;
|
||||
cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse;
|
||||
cell->attrs.strike = intcell->pen.strike;
|
||||
cell->attrs.font = intcell->pen.font;
|
||||
|
||||
cell->attrs.dwl = intcell->pen.dwl;
|
||||
cell->attrs.dhl = intcell->pen.dhl;
|
||||
|
||||
cell->fg = intcell->pen.fg;
|
||||
cell->bg = intcell->pen.bg;
|
||||
|
||||
if(pos.col < (screen->cols - 1) &&
|
||||
getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1)
|
||||
cell->width = 2;
|
||||
else
|
||||
cell->width = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Copy external to internal representation of a screen cell */
|
||||
/* static because it's only used internally for sb_popline during resize */
|
||||
static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell)
|
||||
{
|
||||
ScreenCell *intcell = getcell(screen, pos.row, pos.col);
|
||||
int i;
|
||||
|
||||
if(!intcell)
|
||||
return 0;
|
||||
|
||||
for(i = 0; ; i++) {
|
||||
intcell->chars[i] = cell->chars[i];
|
||||
if(!cell->chars[i])
|
||||
break;
|
||||
}
|
||||
|
||||
intcell->pen.bold = cell->attrs.bold;
|
||||
intcell->pen.underline = cell->attrs.underline;
|
||||
intcell->pen.italic = cell->attrs.italic;
|
||||
intcell->pen.blink = cell->attrs.blink;
|
||||
intcell->pen.reverse = cell->attrs.reverse ^ screen->global_reverse;
|
||||
intcell->pen.strike = cell->attrs.strike;
|
||||
intcell->pen.font = cell->attrs.font;
|
||||
|
||||
intcell->pen.fg = cell->fg;
|
||||
intcell->pen.bg = cell->bg;
|
||||
|
||||
if(cell->width == 2)
|
||||
getcell(screen, pos.row, pos.col + 1)->chars[0] = (uint32_t)-1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos)
|
||||
{
|
||||
/* This cell is EOL if this and every cell to the right is black */
|
||||
for(; pos.col < screen->cols; pos.col++) {
|
||||
ScreenCell *cell = getcell(screen, pos.row, pos.col);
|
||||
if(cell->chars[0] != 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
VTermScreen *vterm_obtain_screen(VTerm *vt)
|
||||
{
|
||||
VTermScreen *screen;
|
||||
if(vt->screen)
|
||||
return vt->screen;
|
||||
|
||||
screen = screen_new(vt);
|
||||
vt->screen = screen;
|
||||
|
||||
return screen;
|
||||
}
|
||||
|
||||
void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen)
|
||||
{
|
||||
|
||||
if(!screen->buffers[1] && altscreen) {
|
||||
int rows, cols;
|
||||
vterm_get_size(screen->vt, &rows, &cols);
|
||||
|
||||
screen->buffers[1] = realloc_buffer(screen, NULL, rows, cols);
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user)
|
||||
{
|
||||
screen->callbacks = callbacks;
|
||||
screen->cbdata = user;
|
||||
}
|
||||
|
||||
void *vterm_screen_get_cbdata(VTermScreen *screen)
|
||||
{
|
||||
return screen->cbdata;
|
||||
}
|
||||
|
||||
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermParserCallbacks *fallbacks, void *user)
|
||||
{
|
||||
vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user);
|
||||
}
|
||||
|
||||
void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen)
|
||||
{
|
||||
return vterm_state_get_unrecognised_fbdata(screen->state);
|
||||
}
|
||||
|
||||
void vterm_screen_flush_damage(VTermScreen *screen)
|
||||
{
|
||||
if(screen->pending_scrollrect.start_row != -1) {
|
||||
vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward, screen->pending_scroll_rightward,
|
||||
moverect_user, erase_user, screen);
|
||||
|
||||
screen->pending_scrollrect.start_row = -1;
|
||||
}
|
||||
|
||||
if(screen->damaged.start_row != -1) {
|
||||
if(screen->callbacks && screen->callbacks->damage)
|
||||
(*screen->callbacks->damage)(screen->damaged, screen->cbdata);
|
||||
|
||||
screen->damaged.start_row = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size)
|
||||
{
|
||||
vterm_screen_flush_damage(screen);
|
||||
screen->damage_merge = size;
|
||||
}
|
||||
|
||||
static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b)
|
||||
{
|
||||
if((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold))
|
||||
return 1;
|
||||
if((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline))
|
||||
return 1;
|
||||
if((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic))
|
||||
return 1;
|
||||
if((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink))
|
||||
return 1;
|
||||
if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse))
|
||||
return 1;
|
||||
if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike))
|
||||
return 1;
|
||||
if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font))
|
||||
return 1;
|
||||
if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_equal(a->pen.fg, b->pen.fg))
|
||||
return 1;
|
||||
if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_equal(a->pen.bg, b->pen.bg))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs)
|
||||
{
|
||||
int col;
|
||||
|
||||
ScreenCell *target = getcell(screen, pos.row, pos.col);
|
||||
|
||||
/* TODO: bounds check */
|
||||
extent->start_row = pos.row;
|
||||
extent->end_row = pos.row + 1;
|
||||
|
||||
if(extent->start_col < 0)
|
||||
extent->start_col = 0;
|
||||
if(extent->end_col < 0)
|
||||
extent->end_col = screen->cols;
|
||||
|
||||
for(col = pos.col - 1; col >= extent->start_col; col--)
|
||||
if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
|
||||
break;
|
||||
extent->start_col = col + 1;
|
||||
|
||||
for(col = pos.col + 1; col < extent->end_col; col++)
|
||||
if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
|
||||
break;
|
||||
extent->end_col = col - 1;
|
||||
|
||||
return 1;
|
||||
}
|
1851
src/libvterm/src/state.c
Normal file
1851
src/libvterm/src/state.c
Normal file
File diff suppressed because it is too large
Load Diff
331
src/libvterm/src/unicode.c
Normal file
331
src/libvterm/src/unicode.c
Normal file
@ -0,0 +1,331 @@
|
||||
#include "vterm_internal.h"
|
||||
|
||||
/* ### The following from http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
||||
* With modifications:
|
||||
* made functions static
|
||||
* moved 'combining' table to file scope, so other functions can see it
|
||||
* ###################################################################
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is an implementation of wcwidth() and wcswidth() (defined in
|
||||
* IEEE Std 1002.1-2001) for Unicode.
|
||||
*
|
||||
* http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
|
||||
* http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
|
||||
*
|
||||
* In fixed-width output devices, Latin characters all occupy a single
|
||||
* "cell" position of equal width, whereas ideographic CJK characters
|
||||
* occupy two such cells. Interoperability between terminal-line
|
||||
* applications and (teletype-style) character terminals using the
|
||||
* UTF-8 encoding requires agreement on which character should advance
|
||||
* the cursor by how many cell positions. No established formal
|
||||
* standards exist at present on which Unicode character shall occupy
|
||||
* how many cell positions on character terminals. These routines are
|
||||
* a first attempt of defining such behavior based on simple rules
|
||||
* applied to data provided by the Unicode Consortium.
|
||||
*
|
||||
* For some graphical characters, the Unicode standard explicitly
|
||||
* defines a character-cell width via the definition of the East Asian
|
||||
* FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
|
||||
* In all these cases, there is no ambiguity about which width a
|
||||
* terminal shall use. For characters in the East Asian Ambiguous (A)
|
||||
* class, the width choice depends purely on a preference of backward
|
||||
* compatibility with either historic CJK or Western practice.
|
||||
* Choosing single-width for these characters is easy to justify as
|
||||
* the appropriate long-term solution, as the CJK practice of
|
||||
* displaying these characters as double-width comes from historic
|
||||
* implementation simplicity (8-bit encoded characters were displayed
|
||||
* single-width and 16-bit ones double-width, even for Greek,
|
||||
* Cyrillic, etc.) and not any typographic considerations.
|
||||
*
|
||||
* Much less clear is the choice of width for the Not East Asian
|
||||
* (Neutral) class. Existing practice does not dictate a width for any
|
||||
* of these characters. It would nevertheless make sense
|
||||
* typographically to allocate two character cells to characters such
|
||||
* as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
|
||||
* represented adequately with a single-width glyph. The following
|
||||
* routines at present merely assign a single-cell width to all
|
||||
* neutral characters, in the interest of simplicity. This is not
|
||||
* entirely satisfactory and should be reconsidered before
|
||||
* establishing a formal standard in this area. At the moment, the
|
||||
* decision which Not East Asian (Neutral) characters should be
|
||||
* represented by double-width glyphs cannot yet be answered by
|
||||
* applying a simple rule from the Unicode database content. Setting
|
||||
* up a proper standard for the behavior of UTF-8 character terminals
|
||||
* will require a careful analysis not only of each Unicode character,
|
||||
* but also of each presentation form, something the author of these
|
||||
* routines has avoided to do so far.
|
||||
*
|
||||
* http://www.unicode.org/unicode/reports/tr11/
|
||||
*
|
||||
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* for any purpose and without fee is hereby granted. The author
|
||||
* disclaims all warranties with regard to this software.
|
||||
*
|
||||
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
||||
*/
|
||||
|
||||
struct interval {
|
||||
int first;
|
||||
int last;
|
||||
};
|
||||
|
||||
/* sorted list of non-overlapping intervals of non-spacing characters */
|
||||
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
|
||||
static const struct interval combining[] = {
|
||||
{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
|
||||
{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
|
||||
{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
|
||||
{ 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
|
||||
{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
|
||||
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
|
||||
{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
|
||||
{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
|
||||
{ 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
|
||||
{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
|
||||
{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
|
||||
{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
|
||||
{ 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
|
||||
{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
|
||||
{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
|
||||
{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
|
||||
{ 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
|
||||
{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
|
||||
{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
|
||||
{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
|
||||
{ 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
|
||||
{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
|
||||
{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
|
||||
{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
|
||||
{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
|
||||
{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
|
||||
{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
|
||||
{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
|
||||
{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
|
||||
{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
|
||||
{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
|
||||
{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
|
||||
{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
|
||||
{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
|
||||
{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
|
||||
{ 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
|
||||
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
|
||||
{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
|
||||
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
|
||||
{ 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
|
||||
{ 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
|
||||
{ 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
|
||||
{ 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
|
||||
{ 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
|
||||
{ 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
|
||||
{ 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
|
||||
{ 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
|
||||
{ 0xE0100, 0xE01EF }
|
||||
};
|
||||
|
||||
|
||||
/* auxiliary function for binary search in interval table */
|
||||
static int bisearch(uint32_t ucs, const struct interval *table, int max) {
|
||||
int min = 0;
|
||||
int mid;
|
||||
|
||||
if ((int)ucs < table[0].first || (int)ucs > table[max].last)
|
||||
return 0;
|
||||
while (max >= min) {
|
||||
mid = (min + max) / 2;
|
||||
if ((int)ucs > table[mid].last)
|
||||
min = mid + 1;
|
||||
else if ((int)ucs < table[mid].first)
|
||||
max = mid - 1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* The following two functions define the column width of an ISO 10646
|
||||
* character as follows:
|
||||
*
|
||||
* - The null character (U+0000) has a column width of 0.
|
||||
*
|
||||
* - Other C0/C1 control characters and DEL will lead to a return
|
||||
* value of -1.
|
||||
*
|
||||
* - Non-spacing and enclosing combining characters (general
|
||||
* category code Mn or Me in the Unicode database) have a
|
||||
* column width of 0.
|
||||
*
|
||||
* - SOFT HYPHEN (U+00AD) has a column width of 1.
|
||||
*
|
||||
* - Other format characters (general category code Cf in the Unicode
|
||||
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
|
||||
*
|
||||
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
|
||||
* have a column width of 0.
|
||||
*
|
||||
* - Spacing characters in the East Asian Wide (W) or East Asian
|
||||
* Full-width (F) category as defined in Unicode Technical
|
||||
* Report #11 have a column width of 2.
|
||||
*
|
||||
* - All remaining characters (including all printable
|
||||
* ISO 8859-1 and WGL4 characters, Unicode control characters,
|
||||
* etc.) have a column width of 1.
|
||||
*
|
||||
* This implementation assumes that uint32_t characters are encoded
|
||||
* in ISO 10646.
|
||||
*/
|
||||
|
||||
|
||||
static int mk_wcwidth(uint32_t ucs)
|
||||
{
|
||||
/* test for 8-bit control characters */
|
||||
if (ucs == 0)
|
||||
return 0;
|
||||
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
|
||||
return -1;
|
||||
|
||||
/* binary search in table of non-spacing characters */
|
||||
if (bisearch(ucs, combining,
|
||||
sizeof(combining) / sizeof(struct interval) - 1))
|
||||
return 0;
|
||||
|
||||
/* if we arrive here, ucs is not a combining or C0/C1 control character */
|
||||
|
||||
return 1 +
|
||||
(ucs >= 0x1100 &&
|
||||
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
|
||||
ucs == 0x2329 || ucs == 0x232a ||
|
||||
(ucs >= 0x2e80 && ucs <= 0xa4cf &&
|
||||
ucs != 0x303f) || /* CJK ... Yi */
|
||||
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
|
||||
(ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
|
||||
(ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
|
||||
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
|
||||
(ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
|
||||
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
|
||||
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
|
||||
(ucs >= 0x30000 && ucs <= 0x3fffd)));
|
||||
}
|
||||
|
||||
#if 0 /* unused */
|
||||
static int mk_wcswidth(const uint32_t *pwcs, size_t n)
|
||||
{
|
||||
int w, width = 0;
|
||||
|
||||
for (;*pwcs && n-- > 0; pwcs++)
|
||||
if ((w = mk_wcwidth(*pwcs)) < 0)
|
||||
return -1;
|
||||
else
|
||||
width += w;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The following functions are the same as mk_wcwidth() and
|
||||
* mk_wcswidth(), except that spacing characters in the East Asian
|
||||
* Ambiguous (A) category as defined in Unicode Technical Report #11
|
||||
* have a column width of 2. This variant might be useful for users of
|
||||
* CJK legacy encodings who want to migrate to UCS without changing
|
||||
* the traditional terminal character-width behaviour. It is not
|
||||
* otherwise recommended for general use.
|
||||
*/
|
||||
static int mk_wcwidth_cjk(uint32_t ucs)
|
||||
{
|
||||
/* sorted list of non-overlapping intervals of East Asian Ambiguous
|
||||
* characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
|
||||
static const struct interval ambiguous[] = {
|
||||
{ 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
|
||||
{ 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
|
||||
{ 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
|
||||
{ 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
|
||||
{ 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
|
||||
{ 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
|
||||
{ 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
|
||||
{ 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
|
||||
{ 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
|
||||
{ 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
|
||||
{ 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
|
||||
{ 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
|
||||
{ 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
|
||||
{ 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
|
||||
{ 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
|
||||
{ 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
|
||||
{ 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
|
||||
{ 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
|
||||
{ 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
|
||||
{ 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
|
||||
{ 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
|
||||
{ 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
|
||||
{ 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
|
||||
{ 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
|
||||
{ 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
|
||||
{ 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
|
||||
{ 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
|
||||
{ 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
|
||||
{ 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
|
||||
{ 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
|
||||
{ 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
|
||||
{ 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
|
||||
{ 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
|
||||
{ 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
|
||||
{ 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
|
||||
{ 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
|
||||
{ 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
|
||||
{ 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
|
||||
{ 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
|
||||
{ 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
|
||||
{ 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
|
||||
{ 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
|
||||
{ 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
|
||||
{ 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
|
||||
{ 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
|
||||
{ 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
|
||||
{ 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
|
||||
{ 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
|
||||
{ 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
|
||||
{ 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
|
||||
{ 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
|
||||
{ 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
|
||||
};
|
||||
|
||||
/* binary search in table of non-spacing characters */
|
||||
if (bisearch(ucs, ambiguous,
|
||||
sizeof(ambiguous) / sizeof(struct interval) - 1))
|
||||
return 2;
|
||||
|
||||
return mk_wcwidth(ucs);
|
||||
}
|
||||
|
||||
static int mk_wcswidth_cjk(const uint32_t *pwcs, size_t n)
|
||||
{
|
||||
int w, width = 0;
|
||||
|
||||
for (;*pwcs && n-- > 0; pwcs++)
|
||||
if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
|
||||
return -1;
|
||||
else
|
||||
width += w;
|
||||
|
||||
return width;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ################################
|
||||
* ### The rest added by Paul Evans */
|
||||
|
||||
INTERNAL int vterm_unicode_width(uint32_t codepoint)
|
||||
{
|
||||
return mk_wcwidth(codepoint);
|
||||
}
|
||||
|
||||
INTERNAL int vterm_unicode_is_combining(uint32_t codepoint)
|
||||
{
|
||||
return bisearch(codepoint, combining, sizeof(combining) / sizeof(struct interval) - 1);
|
||||
}
|
47
src/libvterm/src/utf8.h
Normal file
47
src/libvterm/src/utf8.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* The following functions copied and adapted from libtermkey
|
||||
*
|
||||
* http://www.leonerd.org.uk/code/libtermkey/
|
||||
*/
|
||||
unsigned int utf8_seqlen(long codepoint);
|
||||
|
||||
#if defined(DEFINE_INLINES) || USE_INLINE
|
||||
INLINE unsigned int utf8_seqlen(long codepoint)
|
||||
{
|
||||
if(codepoint < 0x0000080) return 1;
|
||||
if(codepoint < 0x0000800) return 2;
|
||||
if(codepoint < 0x0010000) return 3;
|
||||
if(codepoint < 0x0200000) return 4;
|
||||
if(codepoint < 0x4000000) return 5;
|
||||
return 6;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Does NOT NUL-terminate the buffer */
|
||||
int fill_utf8(long codepoint, char *str);
|
||||
|
||||
#if defined(DEFINE_INLINES) || USE_INLINE
|
||||
INLINE int fill_utf8(long codepoint, char *str)
|
||||
{
|
||||
int nbytes = utf8_seqlen(codepoint);
|
||||
|
||||
/* This is easier done backwards */
|
||||
int b = nbytes;
|
||||
while(b > 1) {
|
||||
b--;
|
||||
str[b] = 0x80 | (codepoint & 0x3f);
|
||||
codepoint >>= 6;
|
||||
}
|
||||
|
||||
switch(nbytes) {
|
||||
case 1: str[0] = (codepoint & 0x7f); break;
|
||||
case 2: str[0] = 0xc0 | (codepoint & 0x1f); break;
|
||||
case 3: str[0] = 0xe0 | (codepoint & 0x0f); break;
|
||||
case 4: str[0] = 0xf0 | (codepoint & 0x07); break;
|
||||
case 5: str[0] = 0xf8 | (codepoint & 0x03); break;
|
||||
case 6: str[0] = 0xfc | (codepoint & 0x01); break;
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
#endif
|
||||
/* end copy */
|
385
src/libvterm/src/vterm.c
Normal file
385
src/libvterm/src/vterm.c
Normal file
@ -0,0 +1,385 @@
|
||||
#define DEFINE_INLINES
|
||||
|
||||
#include "vterm_internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utf8.h"
|
||||
|
||||
/*****************
|
||||
* API functions *
|
||||
*****************/
|
||||
|
||||
static void *default_malloc(size_t size, void *allocdata UNUSED)
|
||||
{
|
||||
void *ptr = malloc(size);
|
||||
if(ptr)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void default_free(void *ptr, void *allocdata UNUSED)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static VTermAllocatorFunctions default_allocator = {
|
||||
&default_malloc, /* malloc */
|
||||
&default_free /* free */
|
||||
};
|
||||
|
||||
VTerm *vterm_new(int rows, int cols)
|
||||
{
|
||||
return vterm_new_with_allocator(rows, cols, &default_allocator, NULL);
|
||||
}
|
||||
|
||||
VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata)
|
||||
{
|
||||
/* Need to bootstrap using the allocator function directly */
|
||||
VTerm *vt = (*funcs->malloc)(sizeof(VTerm), allocdata);
|
||||
|
||||
vt->allocator = funcs;
|
||||
vt->allocdata = allocdata;
|
||||
|
||||
vt->rows = rows;
|
||||
vt->cols = cols;
|
||||
|
||||
vt->parser_state = NORMAL;
|
||||
|
||||
vt->parser_callbacks = NULL;
|
||||
vt->cbdata = NULL;
|
||||
|
||||
vt->strbuffer_len = 64;
|
||||
vt->strbuffer_cur = 0;
|
||||
vt->strbuffer = vterm_allocator_malloc(vt, vt->strbuffer_len);
|
||||
|
||||
vt->outbuffer_len = 64;
|
||||
vt->outbuffer_cur = 0;
|
||||
vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
|
||||
|
||||
return vt;
|
||||
}
|
||||
|
||||
void vterm_free(VTerm *vt)
|
||||
{
|
||||
if(vt->screen)
|
||||
vterm_screen_free(vt->screen);
|
||||
|
||||
if(vt->state)
|
||||
vterm_state_free(vt->state);
|
||||
|
||||
vterm_allocator_free(vt, vt->strbuffer);
|
||||
vterm_allocator_free(vt, vt->outbuffer);
|
||||
|
||||
vterm_allocator_free(vt, vt);
|
||||
}
|
||||
|
||||
INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size)
|
||||
{
|
||||
return (*vt->allocator->malloc)(size, vt->allocdata);
|
||||
}
|
||||
|
||||
INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
|
||||
{
|
||||
(*vt->allocator->free)(ptr, vt->allocdata);
|
||||
}
|
||||
|
||||
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
|
||||
{
|
||||
if(rowsp)
|
||||
*rowsp = vt->rows;
|
||||
if(colsp)
|
||||
*colsp = vt->cols;
|
||||
}
|
||||
|
||||
void vterm_set_size(VTerm *vt, int rows, int cols)
|
||||
{
|
||||
vt->rows = rows;
|
||||
vt->cols = cols;
|
||||
|
||||
if(vt->parser_callbacks && vt->parser_callbacks->resize)
|
||||
(*vt->parser_callbacks->resize)(rows, cols, vt->cbdata);
|
||||
}
|
||||
|
||||
int vterm_get_utf8(const VTerm *vt)
|
||||
{
|
||||
return vt->mode.utf8;
|
||||
}
|
||||
|
||||
void vterm_set_utf8(VTerm *vt, int is_utf8)
|
||||
{
|
||||
vt->mode.utf8 = is_utf8;
|
||||
}
|
||||
|
||||
INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
|
||||
{
|
||||
if(len > vt->outbuffer_len - vt->outbuffer_cur) {
|
||||
DEBUG_LOG("vterm_push_output(): buffer overflow; truncating output\n");
|
||||
len = vt->outbuffer_len - vt->outbuffer_cur;
|
||||
}
|
||||
|
||||
memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
|
||||
vt->outbuffer_cur += len;
|
||||
}
|
||||
|
||||
static int outbuffer_is_full(VTerm *vt)
|
||||
{
|
||||
return vt->outbuffer_cur >= vt->outbuffer_len - 1;
|
||||
}
|
||||
|
||||
INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
|
||||
{
|
||||
int written;
|
||||
char buffer[1024]; /* 1Kbyte is enough for everybody, right? */
|
||||
|
||||
if(outbuffer_is_full(vt)) {
|
||||
DEBUG_LOG("vterm_push_output(): buffer overflow; truncating output\n");
|
||||
return;
|
||||
}
|
||||
|
||||
written = vsprintf(buffer, format, args);
|
||||
|
||||
if(written >= (int)(vt->outbuffer_len - vt->outbuffer_cur)) {
|
||||
/* output was truncated */
|
||||
written = vt->outbuffer_len - vt->outbuffer_cur;
|
||||
}
|
||||
if (written > 0)
|
||||
{
|
||||
strncpy(vt->outbuffer + vt->outbuffer_cur, buffer, written + 1);
|
||||
vt->outbuffer_cur += written;
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vterm_push_output_vsprintf(vt, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
|
||||
{
|
||||
size_t orig_cur = vt->outbuffer_cur;
|
||||
va_list args;
|
||||
|
||||
if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
|
||||
vterm_push_output_sprintf(vt, ESC_S "%c", ctrl - 0x40);
|
||||
else
|
||||
vterm_push_output_sprintf(vt, "%c", ctrl);
|
||||
|
||||
va_start(args, fmt);
|
||||
vterm_push_output_vsprintf(vt, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if(outbuffer_is_full(vt))
|
||||
vt->outbuffer_cur = orig_cur;
|
||||
}
|
||||
|
||||
INTERNAL void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...)
|
||||
{
|
||||
size_t orig_cur = vt->outbuffer_cur;
|
||||
va_list args;
|
||||
|
||||
if(!vt->mode.ctrl8bit)
|
||||
vterm_push_output_sprintf(vt, ESC_S "%c", C1_DCS - 0x40);
|
||||
else
|
||||
vterm_push_output_sprintf(vt, "%c", C1_DCS);
|
||||
|
||||
va_start(args, fmt);
|
||||
vterm_push_output_vsprintf(vt, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_ST, "");
|
||||
|
||||
if(outbuffer_is_full(vt))
|
||||
vt->outbuffer_cur = orig_cur;
|
||||
}
|
||||
|
||||
size_t vterm_output_get_buffer_size(const VTerm *vt)
|
||||
{
|
||||
return vt->outbuffer_len;
|
||||
}
|
||||
|
||||
size_t vterm_output_get_buffer_current(const VTerm *vt)
|
||||
{
|
||||
return vt->outbuffer_cur;
|
||||
}
|
||||
|
||||
size_t vterm_output_get_buffer_remaining(const VTerm *vt)
|
||||
{
|
||||
return vt->outbuffer_len - vt->outbuffer_cur;
|
||||
}
|
||||
|
||||
size_t vterm_output_read(VTerm *vt, char *buffer, size_t len)
|
||||
{
|
||||
if(len > vt->outbuffer_cur)
|
||||
len = vt->outbuffer_cur;
|
||||
|
||||
memcpy(buffer, vt->outbuffer, len);
|
||||
|
||||
if(len < vt->outbuffer_cur)
|
||||
memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len);
|
||||
|
||||
vt->outbuffer_cur -= len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
|
||||
{
|
||||
vt->parser_callbacks = callbacks;
|
||||
vt->cbdata = user;
|
||||
}
|
||||
|
||||
void *vterm_parser_get_cbdata(VTerm *vt)
|
||||
{
|
||||
return vt->cbdata;
|
||||
}
|
||||
|
||||
VTermValueType vterm_get_attr_type(VTermAttr attr)
|
||||
{
|
||||
switch(attr) {
|
||||
case VTERM_ATTR_BOLD: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_UNDERLINE: return VTERM_VALUETYPE_INT;
|
||||
case VTERM_ATTR_ITALIC: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_BLINK: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_REVERSE: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_STRIKE: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_FONT: return VTERM_VALUETYPE_INT;
|
||||
case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
|
||||
case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
|
||||
}
|
||||
return 0; /* UNREACHABLE */
|
||||
}
|
||||
|
||||
VTermValueType vterm_get_prop_type(VTermProp prop)
|
||||
{
|
||||
switch(prop) {
|
||||
case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_CURSORBLINK: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_ALTSCREEN: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_TITLE: return VTERM_VALUETYPE_STRING;
|
||||
case VTERM_PROP_ICONNAME: return VTERM_VALUETYPE_STRING;
|
||||
case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT;
|
||||
case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT;
|
||||
}
|
||||
return 0; /* UNREACHABLE */
|
||||
}
|
||||
|
||||
void vterm_scroll_rect(VTermRect rect,
|
||||
int downward,
|
||||
int rightward,
|
||||
int (*moverect)(VTermRect src, VTermRect dest, void *user),
|
||||
int (*eraserect)(VTermRect rect, int selective, void *user),
|
||||
void *user)
|
||||
{
|
||||
VTermRect src;
|
||||
VTermRect dest;
|
||||
|
||||
if(abs(downward) >= rect.end_row - rect.start_row ||
|
||||
abs(rightward) >= rect.end_col - rect.start_col) {
|
||||
/* Scroll more than area; just erase the lot */
|
||||
(*eraserect)(rect, 0, user);
|
||||
return;
|
||||
}
|
||||
|
||||
if(rightward >= 0) {
|
||||
/* rect: [XXX................]
|
||||
* src: [----------------]
|
||||
* dest: [----------------]
|
||||
*/
|
||||
dest.start_col = rect.start_col;
|
||||
dest.end_col = rect.end_col - rightward;
|
||||
src.start_col = rect.start_col + rightward;
|
||||
src.end_col = rect.end_col;
|
||||
}
|
||||
else {
|
||||
/* rect: [................XXX]
|
||||
* src: [----------------]
|
||||
* dest: [----------------]
|
||||
*/
|
||||
int leftward = -rightward;
|
||||
dest.start_col = rect.start_col + leftward;
|
||||
dest.end_col = rect.end_col;
|
||||
src.start_col = rect.start_col;
|
||||
src.end_col = rect.end_col - leftward;
|
||||
}
|
||||
|
||||
if(downward >= 0) {
|
||||
dest.start_row = rect.start_row;
|
||||
dest.end_row = rect.end_row - downward;
|
||||
src.start_row = rect.start_row + downward;
|
||||
src.end_row = rect.end_row;
|
||||
}
|
||||
else {
|
||||
int upward = -downward;
|
||||
dest.start_row = rect.start_row + upward;
|
||||
dest.end_row = rect.end_row;
|
||||
src.start_row = rect.start_row;
|
||||
src.end_row = rect.end_row - upward;
|
||||
}
|
||||
|
||||
if(moverect)
|
||||
(*moverect)(dest, src, user);
|
||||
|
||||
if(downward > 0)
|
||||
rect.start_row = rect.end_row - downward;
|
||||
else if(downward < 0)
|
||||
rect.end_row = rect.start_row - downward;
|
||||
|
||||
if(rightward > 0)
|
||||
rect.start_col = rect.end_col - rightward;
|
||||
else if(rightward < 0)
|
||||
rect.end_col = rect.start_col - rightward;
|
||||
|
||||
(*eraserect)(rect, 0, user);
|
||||
}
|
||||
|
||||
void vterm_copy_cells(VTermRect dest,
|
||||
VTermRect src,
|
||||
void (*copycell)(VTermPos dest, VTermPos src, void *user),
|
||||
void *user)
|
||||
{
|
||||
int downward = src.start_row - dest.start_row;
|
||||
int rightward = src.start_col - dest.start_col;
|
||||
|
||||
int init_row, test_row, init_col, test_col;
|
||||
int inc_row, inc_col;
|
||||
|
||||
VTermPos pos;
|
||||
|
||||
if(downward < 0) {
|
||||
init_row = dest.end_row - 1;
|
||||
test_row = dest.start_row - 1;
|
||||
inc_row = -1;
|
||||
}
|
||||
else /* downward >= 0 */ {
|
||||
init_row = dest.start_row;
|
||||
test_row = dest.end_row;
|
||||
inc_row = +1;
|
||||
}
|
||||
|
||||
if(rightward < 0) {
|
||||
init_col = dest.end_col - 1;
|
||||
test_col = dest.start_col - 1;
|
||||
inc_col = -1;
|
||||
}
|
||||
else /* rightward >= 0 */ {
|
||||
init_col = dest.start_col;
|
||||
test_col = dest.end_col;
|
||||
inc_col = +1;
|
||||
}
|
||||
|
||||
for(pos.row = init_row; pos.row != test_row; pos.row += inc_row)
|
||||
for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
|
||||
VTermPos srcpos;
|
||||
srcpos.row = pos.row + downward;
|
||||
srcpos.col = pos.col + rightward;
|
||||
(*copycell)(pos, srcpos, user);
|
||||
}
|
||||
}
|
237
src/libvterm/src/vterm_internal.h
Normal file
237
src/libvterm/src/vterm_internal.h
Normal file
@ -0,0 +1,237 @@
|
||||
#ifndef __VTERM_INTERNAL_H__
|
||||
#define __VTERM_INTERNAL_H__
|
||||
|
||||
#include "vterm.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define INTERNAL __attribute__((visibility("internal")))
|
||||
# define UNUSED __attribute__((unused))
|
||||
#else
|
||||
# define INTERNAL
|
||||
# define UNUSED
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
# define DEBUG_LOG(s) fprintf(stderr, s)
|
||||
# define DEBUG_LOG1(s, a) fprintf(stderr, s, a)
|
||||
# define DEBUG_LOG2(s, a, b) fprintf(stderr, s, a, b)
|
||||
# define DEBUG_LOG3(s, a, b, c) fprintf(stderr, s, a, b, c)
|
||||
#else
|
||||
# define DEBUG_LOG(s)
|
||||
# define DEBUG_LOG1(s, a)
|
||||
# define DEBUG_LOG2(s, a, b)
|
||||
# define DEBUG_LOG3(s, a, b, c)
|
||||
#endif
|
||||
|
||||
#define ESC_S "\x1b"
|
||||
|
||||
typedef struct VTermEncoding VTermEncoding;
|
||||
|
||||
typedef struct {
|
||||
VTermEncoding *enc;
|
||||
|
||||
/* This size should be increased if required by other stateful encodings */
|
||||
char data[4*sizeof(uint32_t)];
|
||||
} VTermEncodingInstance;
|
||||
|
||||
struct VTermPen
|
||||
{
|
||||
VTermColor fg;
|
||||
VTermColor bg;
|
||||
unsigned int bold:1;
|
||||
unsigned int underline:2;
|
||||
unsigned int italic:1;
|
||||
unsigned int blink:1;
|
||||
unsigned int reverse:1;
|
||||
unsigned int strike:1;
|
||||
unsigned int font:4; /* To store 0-9 */
|
||||
};
|
||||
|
||||
int vterm_color_equal(VTermColor a, VTermColor b);
|
||||
|
||||
#if defined(DEFINE_INLINES) || USE_INLINE
|
||||
INLINE int vterm_color_equal(VTermColor a, VTermColor b)
|
||||
{
|
||||
return a.red == b.red && a.green == b.green && a.blue == b.blue;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct VTermState
|
||||
{
|
||||
VTerm *vt;
|
||||
|
||||
const VTermStateCallbacks *callbacks;
|
||||
void *cbdata;
|
||||
|
||||
const VTermParserCallbacks *fallbacks;
|
||||
void *fbdata;
|
||||
|
||||
int rows;
|
||||
int cols;
|
||||
|
||||
/* Current cursor position */
|
||||
VTermPos pos;
|
||||
|
||||
int at_phantom; /* True if we're on the "81st" phantom column to defer a wraparound */
|
||||
|
||||
int scrollregion_top;
|
||||
int scrollregion_bottom; /* -1 means unbounded */
|
||||
#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > -1 ? (state)->scrollregion_bottom : (state)->rows)
|
||||
int scrollregion_left;
|
||||
#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
|
||||
int scrollregion_right; /* -1 means unbounded */
|
||||
#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin && (state)->scrollregion_right > -1 ? (state)->scrollregion_right : (state)->cols)
|
||||
|
||||
/* Bitvector of tab stops */
|
||||
unsigned char *tabstops;
|
||||
|
||||
VTermLineInfo *lineinfo;
|
||||
#define ROWWIDTH(state,row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
|
||||
#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
|
||||
|
||||
/* Mouse state */
|
||||
int mouse_col, mouse_row;
|
||||
int mouse_buttons;
|
||||
int mouse_flags;
|
||||
#define MOUSE_WANT_CLICK 0x01
|
||||
#define MOUSE_WANT_DRAG 0x02
|
||||
#define MOUSE_WANT_MOVE 0x04
|
||||
|
||||
enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT } mouse_protocol;
|
||||
|
||||
/* Last glyph output, for Unicode recombining purposes */
|
||||
uint32_t *combine_chars;
|
||||
size_t combine_chars_size; /* Number of ELEMENTS in the above */
|
||||
int combine_width; /* The width of the glyph above */
|
||||
VTermPos combine_pos; /* Position before movement */
|
||||
|
||||
struct {
|
||||
unsigned int keypad:1;
|
||||
unsigned int cursor:1;
|
||||
unsigned int autowrap:1;
|
||||
unsigned int insert:1;
|
||||
unsigned int newline:1;
|
||||
unsigned int cursor_visible:1;
|
||||
unsigned int cursor_blink:1;
|
||||
unsigned int cursor_shape:2;
|
||||
unsigned int alt_screen:1;
|
||||
unsigned int origin:1;
|
||||
unsigned int screen:1;
|
||||
unsigned int leftrightmargin:1;
|
||||
unsigned int bracketpaste:1;
|
||||
} mode;
|
||||
|
||||
VTermEncodingInstance encoding[4], encoding_utf8;
|
||||
int gl_set, gr_set, gsingle_set;
|
||||
|
||||
struct VTermPen pen;
|
||||
|
||||
VTermColor default_fg;
|
||||
VTermColor default_bg;
|
||||
VTermColor colors[16]; /* Store the 8 ANSI and the 8 ANSI high-brights only */
|
||||
|
||||
int fg_index;
|
||||
int bg_index;
|
||||
int bold_is_highbright;
|
||||
|
||||
unsigned int protected_cell : 1;
|
||||
|
||||
/* Saved state under DEC mode 1048/1049 */
|
||||
struct {
|
||||
VTermPos pos;
|
||||
struct VTermPen pen;
|
||||
|
||||
struct {
|
||||
int cursor_visible:1;
|
||||
int cursor_blink:1;
|
||||
unsigned int cursor_shape:2;
|
||||
} mode;
|
||||
} saved;
|
||||
};
|
||||
|
||||
struct VTerm
|
||||
{
|
||||
VTermAllocatorFunctions *allocator;
|
||||
void *allocdata;
|
||||
|
||||
int rows;
|
||||
int cols;
|
||||
|
||||
struct {
|
||||
unsigned int utf8:1;
|
||||
unsigned int ctrl8bit:1;
|
||||
} mode;
|
||||
|
||||
enum VTermParserState {
|
||||
NORMAL,
|
||||
CSI,
|
||||
OSC,
|
||||
DCS,
|
||||
ESC,
|
||||
ESC_IN_OSC,
|
||||
ESC_IN_DCS
|
||||
} parser_state;
|
||||
const VTermParserCallbacks *parser_callbacks;
|
||||
void *cbdata;
|
||||
|
||||
/* len == malloc()ed size; cur == number of valid bytes */
|
||||
char *strbuffer;
|
||||
size_t strbuffer_len;
|
||||
size_t strbuffer_cur;
|
||||
|
||||
char *outbuffer;
|
||||
size_t outbuffer_len;
|
||||
size_t outbuffer_cur;
|
||||
|
||||
VTermState *state;
|
||||
VTermScreen *screen;
|
||||
};
|
||||
|
||||
struct VTermEncoding {
|
||||
void (*init) (VTermEncoding *enc, void *data);
|
||||
void (*decode)(VTermEncoding *enc, void *data,
|
||||
uint32_t cp[], int *cpi, int cplen,
|
||||
const char bytes[], size_t *pos, size_t len);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ENC_UTF8,
|
||||
ENC_SINGLE_94
|
||||
} VTermEncodingType;
|
||||
|
||||
void *vterm_allocator_malloc(VTerm *vt, size_t size);
|
||||
void vterm_allocator_free(VTerm *vt, void *ptr);
|
||||
|
||||
void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len);
|
||||
void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args);
|
||||
void vterm_push_output_sprintf(VTerm *vt, const char *format, ...);
|
||||
void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...);
|
||||
void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...);
|
||||
|
||||
void vterm_state_free(VTermState *state);
|
||||
|
||||
void vterm_state_newpen(VTermState *state);
|
||||
void vterm_state_resetpen(VTermState *state);
|
||||
void vterm_state_setpen(VTermState *state, const long args[], int argcount);
|
||||
int vterm_state_getpen(VTermState *state, long args[], int argcount);
|
||||
void vterm_state_savepen(VTermState *state, int save);
|
||||
|
||||
enum {
|
||||
C1_SS3 = 0x8f,
|
||||
C1_DCS = 0x90,
|
||||
C1_CSI = 0x9b,
|
||||
C1_ST = 0x9c
|
||||
};
|
||||
|
||||
void vterm_state_push_output_sprintf_CSI(VTermState *vts, const char *format, ...);
|
||||
|
||||
void vterm_screen_free(VTermScreen *screen);
|
||||
|
||||
VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation);
|
||||
|
||||
int vterm_unicode_width(uint32_t codepoint);
|
||||
int vterm_unicode_is_combining(uint32_t codepoint);
|
||||
|
||||
#endif
|
200
src/libvterm/t/02parser.test
Normal file
200
src/libvterm/t/02parser.test
Normal file
@ -0,0 +1,200 @@
|
||||
INIT
|
||||
UTF8 0
|
||||
WANTPARSER
|
||||
|
||||
!Basic text
|
||||
PUSH "hello"
|
||||
text 0x68, 0x65, 0x6c, 0x6c, 0x6f
|
||||
|
||||
!C0
|
||||
PUSH "\x03"
|
||||
control 3
|
||||
|
||||
PUSH "\x1f"
|
||||
control 0x1f
|
||||
|
||||
!C1 8bit
|
||||
PUSH "\x83"
|
||||
control 0x83
|
||||
|
||||
PUSH "\x9f"
|
||||
control 0x9f
|
||||
|
||||
!C1 7bit
|
||||
PUSH "\e\x43"
|
||||
control 0x83
|
||||
|
||||
PUSH "\e\x5f"
|
||||
control 0x9f
|
||||
|
||||
!High bytes
|
||||
PUSH "\xa0\xcc\xfe"
|
||||
text 0xa0, 0xcc, 0xfe
|
||||
|
||||
!Mixed
|
||||
PUSH "1\n2"
|
||||
text 0x31
|
||||
control 10
|
||||
text 0x32
|
||||
|
||||
!Escape
|
||||
PUSH "\e="
|
||||
escape "="
|
||||
|
||||
!Escape 2-byte
|
||||
PUSH "\e(X"
|
||||
escape "(X"
|
||||
|
||||
!Split write Escape
|
||||
PUSH "\e("
|
||||
PUSH "Y"
|
||||
escape "(Y"
|
||||
|
||||
!Escape cancels Escape, starts another
|
||||
PUSH "\e(\e)Z"
|
||||
escape ")Z"
|
||||
|
||||
!CAN cancels Escape, returns to normal mode
|
||||
PUSH "\e(\x{18}AB"
|
||||
text 0x41, 0x42
|
||||
|
||||
!C0 in Escape interrupts and continues
|
||||
PUSH "\e(\nX"
|
||||
control 10
|
||||
escape "(X"
|
||||
|
||||
!CSI 0 args
|
||||
PUSH "\e[a"
|
||||
csi 0x61 *
|
||||
|
||||
!CSI 1 arg
|
||||
PUSH "\e[9b"
|
||||
csi 0x62 9
|
||||
|
||||
!CSI 2 args
|
||||
PUSH "\e[3;4c"
|
||||
csi 0x63 3,4
|
||||
|
||||
!CSI 1 arg 1 sub
|
||||
PUSH "\e[1:2c"
|
||||
csi 0x63 1+,2
|
||||
|
||||
!CSI many digits
|
||||
PUSH "\e[678d"
|
||||
csi 0x64 678
|
||||
|
||||
!CSI leading zero
|
||||
PUSH "\e[007e"
|
||||
csi 0x65 7
|
||||
|
||||
!CSI qmark
|
||||
PUSH "\e[?2;7f"
|
||||
csi 0x66 L=3f 2,7
|
||||
|
||||
!CSI greater
|
||||
PUSH "\e[>c"
|
||||
csi 0x63 L=3e *
|
||||
|
||||
!CSI SP
|
||||
PUSH "\e[12 q"
|
||||
csi 0x71 12 I=20
|
||||
|
||||
!Mixed CSI
|
||||
PUSH "A\e[8mB"
|
||||
text 0x41
|
||||
csi 0x6d 8
|
||||
text 0x42
|
||||
|
||||
!Split write
|
||||
PUSH "\e"
|
||||
PUSH "[a"
|
||||
csi 0x61 *
|
||||
PUSH "foo\e["
|
||||
text 0x66, 0x6f, 0x6f
|
||||
PUSH "4b"
|
||||
csi 0x62 4
|
||||
PUSH "\e[12;"
|
||||
PUSH "3c"
|
||||
csi 0x63 12,3
|
||||
|
||||
!Escape cancels CSI, starts Escape
|
||||
PUSH "\e[123\e9"
|
||||
escape "9"
|
||||
|
||||
!CAN cancels CSI, returns to normal mode
|
||||
PUSH "\e[12\x{18}AB"
|
||||
text 0x41, 0x42
|
||||
|
||||
!C0 in Escape interrupts and continues
|
||||
PUSH "\e[12\n;3X"
|
||||
control 10
|
||||
csi 0x58 12,3
|
||||
|
||||
!OSC BEL
|
||||
PUSH "\e]1;Hello\x07"
|
||||
osc "1;Hello"
|
||||
|
||||
!OSC ST (7bit)
|
||||
PUSH "\e]1;Hello\e\\"
|
||||
osc "1;Hello"
|
||||
|
||||
!OSC ST (8bit)
|
||||
PUSH "\x{9d}1;Hello\x9c"
|
||||
osc "1;Hello"
|
||||
|
||||
!Escape cancels OSC, starts Escape
|
||||
PUSH "\e]Something\e9"
|
||||
escape "9"
|
||||
|
||||
!CAN cancels OSC, returns to normal mode
|
||||
PUSH "\e]12\x{18}AB"
|
||||
text 0x41, 0x42
|
||||
|
||||
!C0 in OSC interrupts and continues
|
||||
PUSH "\e]2;\nBye\x07"
|
||||
control 10
|
||||
osc "2;Bye"
|
||||
|
||||
!DCS BEL
|
||||
PUSH "\ePHello\x07"
|
||||
dcs "Hello"
|
||||
|
||||
!DCS ST (7bit)
|
||||
PUSH "\ePHello\e\\"
|
||||
dcs "Hello"
|
||||
|
||||
!DCS ST (8bit)
|
||||
PUSH "\x{90}Hello\x9c"
|
||||
dcs "Hello"
|
||||
|
||||
!Escape cancels DCS, starts Escape
|
||||
PUSH "\ePSomething\e9"
|
||||
escape "9"
|
||||
|
||||
!CAN cancels DCS, returns to normal mode
|
||||
PUSH "\eP12\x{18}AB"
|
||||
text 0x41, 0x42
|
||||
|
||||
!C0 in OSC interrupts and continues
|
||||
PUSH "\ePBy\ne\x07"
|
||||
control 10
|
||||
dcs "Bye"
|
||||
|
||||
!NUL ignored
|
||||
PUSH "\x{00}"
|
||||
|
||||
!NUL ignored within CSI
|
||||
PUSH "\e[12\x{00}3m"
|
||||
csi 0x6d 123
|
||||
|
||||
!DEL ignored
|
||||
PUSH "\x{7f}"
|
||||
|
||||
!DEL ignored within CSI
|
||||
PUSH "\e[12\x{7f}3m"
|
||||
csi 0x6d 123
|
||||
|
||||
!DEL inside text"
|
||||
PUSH "AB\x{7f}C"
|
||||
text 0x41,0x42
|
||||
text 0x43
|
122
src/libvterm/t/03encoding_utf8.test
Normal file
122
src/libvterm/t/03encoding_utf8.test
Normal file
@ -0,0 +1,122 @@
|
||||
INIT
|
||||
WANTENCODING
|
||||
|
||||
!Low
|
||||
ENCIN "123"
|
||||
encout 0x31,0x32,0x33
|
||||
|
||||
# We want to prove the UTF-8 parser correctly handles all the sequences.
|
||||
# Easy way to do this is to check it does low/high boundary cases, as that
|
||||
# leaves only two for each sequence length
|
||||
#
|
||||
# These ranges are therefore:
|
||||
#
|
||||
# Two bytes:
|
||||
# U+0080 = 000 10000000 => 00010 000000
|
||||
# => 11000010 10000000 = C2 80
|
||||
# U+07FF = 111 11111111 => 11111 111111
|
||||
# => 11011111 10111111 = DF BF
|
||||
#
|
||||
# Three bytes:
|
||||
# U+0800 = 00001000 00000000 => 0000 100000 000000
|
||||
# => 11100000 10100000 10000000 = E0 A0 80
|
||||
# U+FFFD = 11111111 11111101 => 1111 111111 111101
|
||||
# => 11101111 10111111 10111101 = EF BF BD
|
||||
# (We avoid U+FFFE and U+FFFF as they're invalid codepoints)
|
||||
#
|
||||
# Four bytes:
|
||||
# U+10000 = 00001 00000000 00000000 => 000 010000 000000 000000
|
||||
# => 11110000 10010000 10000000 10000000 = F0 90 80 80
|
||||
# U+1FFFFF = 11111 11111111 11111111 => 111 111111 111111 111111
|
||||
# => 11110111 10111111 10111111 10111111 = F7 BF BF BF
|
||||
|
||||
!2 byte
|
||||
ENCIN "\xC2\x80\xDF\xBF"
|
||||
encout 0x0080, 0x07FF
|
||||
|
||||
!3 byte
|
||||
ENCIN "\xE0\xA0\x80\xEF\xBF\xBD"
|
||||
encout 0x0800,0xFFFD
|
||||
|
||||
!4 byte
|
||||
ENCIN "\xF0\x90\x80\x80\xF7\xBF\xBF\xBF"
|
||||
encout 0x10000,0x1fffff
|
||||
|
||||
# Next up, we check some invalid sequences
|
||||
# + Early termination (back to low bytes too soon)
|
||||
# + Early restart (another sequence introduction before the previous one was finished)
|
||||
|
||||
!Early termination
|
||||
ENCIN "\xC2!"
|
||||
encout 0xfffd,0x21
|
||||
|
||||
ENCIN "\xE0!\xE0\xA0!"
|
||||
encout 0xfffd,0x21,0xfffd,0x21
|
||||
|
||||
ENCIN "\xF0!\xF0\x90!\xF0\x90\x80!"
|
||||
encout 0xfffd,0x21,0xfffd,0x21,0xfffd,0x21
|
||||
|
||||
!Early restart
|
||||
ENCIN "\xC2\xC2\x90"
|
||||
encout 0xfffd,0x0090
|
||||
|
||||
ENCIN "\xE0\xC2\x90\xE0\xA0\xC2\x90"
|
||||
encout 0xfffd,0x0090,0xfffd,0x0090
|
||||
|
||||
ENCIN "\xF0\xC2\x90\xF0\x90\xC2\x90\xF0\x90\x80\xC2\x90"
|
||||
encout 0xfffd,0x0090,0xfffd,0x0090,0xfffd,0x0090
|
||||
|
||||
# Test the overlong sequences by giving an overlong encoding of U+0000 and
|
||||
# an encoding of the highest codepoint still too short
|
||||
#
|
||||
# Two bytes:
|
||||
# U+0000 = C0 80
|
||||
# U+007F = 000 01111111 => 00001 111111 =>
|
||||
# => 11000001 10111111 => C1 BF
|
||||
#
|
||||
# Three bytes:
|
||||
# U+0000 = E0 80 80
|
||||
# U+07FF = 00000111 11111111 => 0000 011111 111111
|
||||
# => 11100000 10011111 10111111 = E0 9F BF
|
||||
#
|
||||
# Four bytes:
|
||||
# U+0000 = F0 80 80 80
|
||||
# U+FFFF = 11111111 11111111 => 000 001111 111111 111111
|
||||
# => 11110000 10001111 10111111 10111111 = F0 8F BF BF
|
||||
|
||||
!Overlong
|
||||
ENCIN "\xC0\x80\xC1\xBF"
|
||||
encout 0xfffd,0xfffd
|
||||
|
||||
ENCIN "\xE0\x80\x80\xE0\x9F\xBF"
|
||||
encout 0xfffd,0xfffd
|
||||
|
||||
ENCIN "\xF0\x80\x80\x80\xF0\x8F\xBF\xBF"
|
||||
encout 0xfffd,0xfffd
|
||||
|
||||
# UTF-16 surrogates U+D800 and U+DFFF
|
||||
!UTF-16 Surrogates
|
||||
ENCIN "\xED\xA0\x80\xED\xBF\xBF"
|
||||
encout 0xfffd,0xfffd
|
||||
|
||||
!Split write
|
||||
ENCIN "\xC2"
|
||||
ENCIN "\xA0"
|
||||
encout 0x000A0
|
||||
|
||||
ENCIN "\xE0"
|
||||
ENCIN "\xA0\x80"
|
||||
encout 0x00800
|
||||
ENCIN "\xE0\xA0"
|
||||
ENCIN "\x80"
|
||||
encout 0x00800
|
||||
|
||||
ENCIN "\xF0"
|
||||
ENCIN "\x90\x80\x80"
|
||||
encout 0x10000
|
||||
ENCIN "\xF0\x90"
|
||||
ENCIN "\x80\x80"
|
||||
encout 0x10000
|
||||
ENCIN "\xF0\x90\x80"
|
||||
ENCIN "\x80"
|
||||
encout 0x10000
|
55
src/libvterm/t/10state_putglyph.test
Normal file
55
src/libvterm/t/10state_putglyph.test
Normal file
@ -0,0 +1,55 @@
|
||||
INIT
|
||||
UTF8 1
|
||||
WANTSTATE g
|
||||
|
||||
!Low
|
||||
RESET
|
||||
PUSH "ABC"
|
||||
putglyph 0x41 1 0,0
|
||||
putglyph 0x42 1 0,1
|
||||
putglyph 0x43 1 0,2
|
||||
|
||||
!UTF-8 1 char
|
||||
# U+00C1 = 0xC3 0x81 name: LATIN CAPITAL LETTER A WITH ACUTE
|
||||
# U+00E9 = 0xC3 0xA9 name: LATIN SMALL LETTER E WITH ACUTE
|
||||
RESET
|
||||
PUSH "\xC3\x81\xC3\xA9"
|
||||
putglyph 0xc1 1 0,0
|
||||
putglyph 0xe9 1 0,1
|
||||
|
||||
!UTF-8 wide char
|
||||
# U+FF10 = 0xEF 0xBC 0x90 name: FULLWIDTH DIGIT ZERO
|
||||
RESET
|
||||
PUSH "\xEF\xBC\x90 "
|
||||
putglyph 0xff10 2 0,0
|
||||
putglyph 0x20 1 0,2
|
||||
|
||||
!UTF-8 combining chars
|
||||
# U+0301 = 0xCC 0x81 name: COMBINING ACUTE
|
||||
RESET
|
||||
PUSH "e\xCC\x81Z"
|
||||
putglyph 0x65,0x301 1 0,0
|
||||
putglyph 0x5a 1 0,1
|
||||
|
||||
!Combining across buffers
|
||||
RESET
|
||||
PUSH "e"
|
||||
putglyph 0x65 1 0,0
|
||||
PUSH "\xCC\x81Z"
|
||||
putglyph 0x65,0x301 1 0,0
|
||||
putglyph 0x5a 1 0,1
|
||||
|
||||
RESET
|
||||
PUSH "e"
|
||||
putglyph 0x65 1 0,0
|
||||
PUSH "\xCC\x81"
|
||||
putglyph 0x65,0x301 1 0,0
|
||||
PUSH "\xCC\x82"
|
||||
putglyph 0x65,0x301,0x302 1 0,0
|
||||
|
||||
!DECSCA protected
|
||||
RESET
|
||||
PUSH "A\e[1\"qB\e[2\"qC"
|
||||
putglyph 0x41 1 0,0
|
||||
putglyph 0x42 1 0,1 prot
|
||||
putglyph 0x43 1 0,2
|
224
src/libvterm/t/11state_movecursor.test
Normal file
224
src/libvterm/t/11state_movecursor.test
Normal file
@ -0,0 +1,224 @@
|
||||
INIT
|
||||
UTF8 1
|
||||
WANTSTATE
|
||||
|
||||
!Implicit
|
||||
PUSH "ABC"
|
||||
?cursor = 0,3
|
||||
!Backspace
|
||||
PUSH "\b"
|
||||
?cursor = 0,2
|
||||
!Horizontal Tab
|
||||
PUSH "\t"
|
||||
?cursor = 0,8
|
||||
!Carriage Return
|
||||
PUSH "\r"
|
||||
?cursor = 0,0
|
||||
!Linefeed
|
||||
PUSH "\n"
|
||||
?cursor = 1,0
|
||||
|
||||
!Backspace bounded by lefthand edge
|
||||
PUSH "\e[4;2H"
|
||||
?cursor = 3,1
|
||||
PUSH "\b"
|
||||
?cursor = 3,0
|
||||
PUSH "\b"
|
||||
?cursor = 3,0
|
||||
|
||||
!Backspace cancels phantom
|
||||
PUSH "\e[4;80H"
|
||||
?cursor = 3,79
|
||||
PUSH "X"
|
||||
?cursor = 3,79
|
||||
PUSH "\b"
|
||||
?cursor = 3,78
|
||||
|
||||
!HT bounded by righthand edge
|
||||
PUSH "\e[1;78H"
|
||||
?cursor = 0,77
|
||||
PUSH "\t"
|
||||
?cursor = 0,79
|
||||
PUSH "\t"
|
||||
?cursor = 0,79
|
||||
|
||||
RESET
|
||||
|
||||
!Index
|
||||
PUSH "ABC\eD"
|
||||
?cursor = 1,3
|
||||
!Reverse Index
|
||||
PUSH "\eM"
|
||||
?cursor = 0,3
|
||||
!Newline
|
||||
PUSH "\eE"
|
||||
?cursor = 1,0
|
||||
|
||||
RESET
|
||||
|
||||
!Cursor Forward
|
||||
PUSH "\e[B"
|
||||
?cursor = 1,0
|
||||
PUSH "\e[3B"
|
||||
?cursor = 4,0
|
||||
PUSH "\e[0B"
|
||||
?cursor = 5,0
|
||||
|
||||
!Cursor Down
|
||||
PUSH "\e[C"
|
||||
?cursor = 5,1
|
||||
PUSH "\e[3C"
|
||||
?cursor = 5,4
|
||||
PUSH "\e[0C"
|
||||
?cursor = 5,5
|
||||
|
||||
!Cursor Up
|
||||
PUSH "\e[A"
|
||||
?cursor = 4,5
|
||||
PUSH "\e[3A"
|
||||
?cursor = 1,5
|
||||
PUSH "\e[0A"
|
||||
?cursor = 0,5
|
||||
|
||||
!Cursor Backward
|
||||
PUSH "\e[D"
|
||||
?cursor = 0,4
|
||||
PUSH "\e[3D"
|
||||
?cursor = 0,1
|
||||
PUSH "\e[0D"
|
||||
?cursor = 0,0
|
||||
|
||||
!Cursor Next Line
|
||||
PUSH " "
|
||||
?cursor = 0,3
|
||||
PUSH "\e[E"
|
||||
?cursor = 1,0
|
||||
PUSH " "
|
||||
?cursor = 1,3
|
||||
PUSH "\e[2E"
|
||||
?cursor = 3,0
|
||||
PUSH "\e[0E"
|
||||
?cursor = 4,0
|
||||
|
||||
!Cursor Previous Line
|
||||
PUSH " "
|
||||
?cursor = 4,3
|
||||
PUSH "\e[F"
|
||||
?cursor = 3,0
|
||||
PUSH " "
|
||||
?cursor = 3,3
|
||||
PUSH "\e[2F"
|
||||
?cursor = 1,0
|
||||
PUSH "\e[0F"
|
||||
?cursor = 0,0
|
||||
|
||||
!Cursor Horizonal Absolute
|
||||
PUSH "\n"
|
||||
?cursor = 1,0
|
||||
PUSH "\e[20G"
|
||||
?cursor = 1,19
|
||||
PUSH "\e[G"
|
||||
?cursor = 1,0
|
||||
|
||||
!Cursor Position
|
||||
PUSH "\e[10;5H"
|
||||
?cursor = 9,4
|
||||
PUSH "\e[8H"
|
||||
?cursor = 7,0
|
||||
PUSH "\e[H"
|
||||
?cursor = 0,0
|
||||
|
||||
!Cursor Position cancels phantom
|
||||
PUSH "\e[10;78H"
|
||||
?cursor = 9,77
|
||||
PUSH "ABC"
|
||||
?cursor = 9,79
|
||||
PUSH "\e[10;80H"
|
||||
PUSH "C"
|
||||
?cursor = 9,79
|
||||
PUSH "X"
|
||||
?cursor = 10,1
|
||||
|
||||
RESET
|
||||
|
||||
!Bounds Checking
|
||||
PUSH "\e[A"
|
||||
?cursor = 0,0
|
||||
PUSH "\e[D"
|
||||
?cursor = 0,0
|
||||
PUSH "\e[25;80H"
|
||||
?cursor = 24,79
|
||||
PUSH "\e[B"
|
||||
?cursor = 24,79
|
||||
PUSH "\e[C"
|
||||
?cursor = 24,79
|
||||
PUSH "\e[E"
|
||||
?cursor = 24,0
|
||||
PUSH "\e[H"
|
||||
?cursor = 0,0
|
||||
PUSH "\e[F"
|
||||
?cursor = 0,0
|
||||
PUSH "\e[999G"
|
||||
?cursor = 0,79
|
||||
PUSH "\e[99;99H"
|
||||
?cursor = 24,79
|
||||
|
||||
RESET
|
||||
|
||||
!Horizontal Position Absolute
|
||||
PUSH "\e[5`"
|
||||
?cursor = 0,4
|
||||
|
||||
!Horizontal Position Relative
|
||||
PUSH "\e[3a"
|
||||
?cursor = 0,7
|
||||
|
||||
!Horizontal Position Backward
|
||||
PUSH "\e[3j"
|
||||
?cursor = 0,4
|
||||
|
||||
!Horizontal and Vertical Position
|
||||
PUSH "\e[3;3f"
|
||||
?cursor = 2,2
|
||||
|
||||
!Vertical Position Absolute
|
||||
PUSH "\e[5d"
|
||||
?cursor = 4,2
|
||||
|
||||
!Vertical Position Relative
|
||||
PUSH "\e[2e"
|
||||
?cursor = 6,2
|
||||
|
||||
!Vertical Position Backward
|
||||
PUSH "\e[2k"
|
||||
?cursor = 4,2
|
||||
|
||||
RESET
|
||||
|
||||
!Horizontal Tab
|
||||
PUSH "\t"
|
||||
?cursor = 0,8
|
||||
PUSH " "
|
||||
?cursor = 0,11
|
||||
PUSH "\t"
|
||||
?cursor = 0,16
|
||||
PUSH " "
|
||||
?cursor = 0,23
|
||||
PUSH "\t"
|
||||
?cursor = 0,24
|
||||
PUSH " "
|
||||
?cursor = 0,32
|
||||
PUSH "\t"
|
||||
?cursor = 0,40
|
||||
|
||||
!Cursor Horizontal Tab
|
||||
PUSH "\e[I"
|
||||
?cursor = 0,48
|
||||
PUSH "\e[2I"
|
||||
?cursor = 0,64
|
||||
|
||||
!Cursor Backward Tab
|
||||
PUSH "\e[Z"
|
||||
?cursor = 0,56
|
||||
PUSH "\e[2Z"
|
||||
?cursor = 0,40
|
150
src/libvterm/t/12state_scroll.test
Normal file
150
src/libvterm/t/12state_scroll.test
Normal file
@ -0,0 +1,150 @@
|
||||
INIT
|
||||
UTF8 1
|
||||
WANTSTATE s
|
||||
|
||||
!Linefeed
|
||||
PUSH "\n"x24
|
||||
?cursor = 24,0
|
||||
PUSH "\n"
|
||||
scrollrect 0..25,0..80 => +1,+0
|
||||
?cursor = 24,0
|
||||
|
||||
RESET
|
||||
|
||||
!Index
|
||||
PUSH "\e[25H"
|
||||
PUSH "\eD"
|
||||
scrollrect 0..25,0..80 => +1,+0
|
||||
|
||||
RESET
|
||||
|
||||
!Reverse Index
|
||||
PUSH "\eM"
|
||||
scrollrect 0..25,0..80 => -1,+0
|
||||
|
||||
RESET
|
||||
|
||||
!Linefeed in DECSTBM
|
||||
PUSH "\e[1;10r"
|
||||
?cursor = 0,0
|
||||
PUSH "\n"x9
|
||||
?cursor = 9,0
|
||||
PUSH "\n"
|
||||
scrollrect 0..10,0..80 => +1,+0
|
||||
?cursor = 9,0
|
||||
|
||||
!Linefeed outside DECSTBM
|
||||
PUSH "\e[20H"
|
||||
?cursor = 19,0
|
||||
PUSH "\n"
|
||||
?cursor = 20,0
|
||||
|
||||
!Index in DECSTBM
|
||||
PUSH "\e[10H"
|
||||
PUSH "\e[9;10r"
|
||||
PUSH "\eM"
|
||||
?cursor = 8,0
|
||||
PUSH "\eM"
|
||||
scrollrect 8..10,0..80 => -1,+0
|
||||
|
||||
!Reverse Index in DECSTBM
|
||||
PUSH "\e[25H"
|
||||
?cursor = 24,0
|
||||
PUSH "\n"
|
||||
# no scrollrect
|
||||
?cursor = 24,0
|
||||
|
||||
!Linefeed in DECSTBM+DECSLRM
|
||||
PUSH "\e[?69h"
|
||||
PUSH "\e[3;10r\e[10;40s"
|
||||
PUSH "\e[10;10H\n"
|
||||
scrollrect 2..10,9..40 => +1,+0
|
||||
|
||||
!IND/RI in DECSTBM+DECSLRM
|
||||
PUSH "\eD"
|
||||
scrollrect 2..10,9..40 => +1,+0
|
||||
PUSH "\e[3;10H\eM"
|
||||
scrollrect 2..10,9..40 => -1,+0
|
||||
|
||||
!DECRQSS on DECSTBM
|
||||
PUSH "\eP\$qr\e\\"
|
||||
output "\eP1\$r3;10r\e\\"
|
||||
|
||||
!DECRQSS on DECSLRM
|
||||
PUSH "\eP\$qs\e\\"
|
||||
output "\eP1\$r10;40s\e\\"
|
||||
|
||||
!Setting invalid DECSLRM with !DECVSSM is still rejected
|
||||
PUSH "\e[?69l\e[;0s\e[?69h"
|
||||
|
||||
RESET
|
||||
|
||||
!Scroll Down
|
||||
PUSH "\e[S"
|
||||
scrollrect 0..25,0..80 => +1,+0
|
||||
?cursor = 0,0
|
||||
PUSH "\e[2S"
|
||||
scrollrect 0..25,0..80 => +2,+0
|
||||
?cursor = 0,0
|
||||
PUSH "\e[100S"
|
||||
scrollrect 0..25,0..80 => +25,+0
|
||||
|
||||
!Scroll Up
|
||||
PUSH "\e[T"
|
||||
scrollrect 0..25,0..80 => -1,+0
|
||||
?cursor = 0,0
|
||||
PUSH "\e[2T"
|
||||
scrollrect 0..25,0..80 => -2,+0
|
||||
?cursor = 0,0
|
||||
PUSH "\e[100T"
|
||||
scrollrect 0..25,0..80 => -25,+0
|
||||
|
||||
!SD/SU in DECSTBM
|
||||
PUSH "\e[5;20r"
|
||||
PUSH "\e[S"
|
||||
scrollrect 4..20,0..80 => +1,+0
|
||||
PUSH "\e[T"
|
||||
scrollrect 4..20,0..80 => -1,+0
|
||||
|
||||
RESET
|
||||
|
||||
!SD/SU in DECSTBM+DECSLRM
|
||||
PUSH "\e[?69h"
|
||||
PUSH "\e[3;10r\e[10;40s"
|
||||
?cursor = 0,0
|
||||
PUSH "\e[3;10H"
|
||||
?cursor = 2,9
|
||||
PUSH "\e[S"
|
||||
scrollrect 2..10,9..40 => +1,+0
|
||||
PUSH "\e[?69l"
|
||||
PUSH "\e[S"
|
||||
scrollrect 2..10,0..80 => +1,+0
|
||||
|
||||
!Invalid boundaries
|
||||
RESET
|
||||
|
||||
PUSH "\e[100;105r\eD"
|
||||
PUSH "\e[5;2r\eD"
|
||||
|
||||
RESET
|
||||
WANTSTATE -s+me
|
||||
|
||||
!Scroll Down move+erase emulation
|
||||
PUSH "\e[S"
|
||||
moverect 1..25,0..80 -> 0..24,0..80
|
||||
erase 24..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "\e[2S"
|
||||
moverect 2..25,0..80 -> 0..23,0..80
|
||||
erase 23..25,0..80
|
||||
?cursor = 0,0
|
||||
|
||||
!Scroll Up move+erase emulation
|
||||
PUSH "\e[T"
|
||||
moverect 0..24,0..80 -> 1..25,0..80
|
||||
erase 0..1,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "\e[2T"
|
||||
moverect 0..23,0..80 -> 2..25,0..80
|
||||
erase 0..2,0..80
|
||||
?cursor = 0,0
|
300
src/libvterm/t/13state_edit.test
Normal file
300
src/libvterm/t/13state_edit.test
Normal file
@ -0,0 +1,300 @@
|
||||
INIT
|
||||
UTF8 1
|
||||
WANTSTATE se
|
||||
|
||||
!ICH
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "ACD"
|
||||
PUSH "\e[2D"
|
||||
?cursor = 0,1
|
||||
PUSH "\e[@"
|
||||
scrollrect 0..1,1..80 => +0,-1
|
||||
?cursor = 0,1
|
||||
PUSH "B"
|
||||
?cursor = 0,2
|
||||
PUSH "\e[3@"
|
||||
scrollrect 0..1,2..80 => +0,-3
|
||||
|
||||
!ICH with DECSLRM
|
||||
PUSH "\e[?69h"
|
||||
PUSH "\e[;50s"
|
||||
PUSH "\e[20G\e[@"
|
||||
scrollrect 0..1,19..50 => +0,-1
|
||||
|
||||
!ICH outside DECSLRM
|
||||
PUSH "\e[70G\e[@"
|
||||
# nothing happens
|
||||
|
||||
!DCH
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "ABBC"
|
||||
PUSH "\e[3D"
|
||||
?cursor = 0,1
|
||||
PUSH "\e[P"
|
||||
scrollrect 0..1,1..80 => +0,+1
|
||||
?cursor = 0,1
|
||||
PUSH "\e[3P"
|
||||
scrollrect 0..1,1..80 => +0,+3
|
||||
?cursor = 0,1
|
||||
|
||||
!DCH with DECSLRM
|
||||
PUSH "\e[?69h"
|
||||
PUSH "\e[;50s"
|
||||
PUSH "\e[20G\e[P"
|
||||
scrollrect 0..1,19..50 => +0,+1
|
||||
|
||||
!DCH outside DECSLRM
|
||||
PUSH "\e[70G\e[P"
|
||||
# nothing happens
|
||||
|
||||
!ECH
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "ABC"
|
||||
PUSH "\e[2D"
|
||||
?cursor = 0,1
|
||||
PUSH "\e[X"
|
||||
erase 0..1,1..2
|
||||
?cursor = 0,1
|
||||
PUSH "\e[3X"
|
||||
erase 0..1,1..4
|
||||
?cursor = 0,1
|
||||
# ECH more columns than there are should be bounded
|
||||
PUSH "\e[100X"
|
||||
erase 0..1,1..80
|
||||
|
||||
!IL
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "A\r\nC"
|
||||
?cursor = 1,1
|
||||
PUSH "\e[L"
|
||||
scrollrect 1..25,0..80 => -1,+0
|
||||
# TODO: ECMA-48 says we should move to line home, but neither xterm nor
|
||||
# xfce4-terminal do this
|
||||
?cursor = 1,1
|
||||
PUSH "\rB"
|
||||
?cursor = 1,1
|
||||
PUSH "\e[3L"
|
||||
scrollrect 1..25,0..80 => -3,+0
|
||||
|
||||
!IL with DECSTBM
|
||||
PUSH "\e[5;15r"
|
||||
PUSH "\e[5H\e[L"
|
||||
scrollrect 4..15,0..80 => -1,+0
|
||||
|
||||
!IL outside DECSTBM
|
||||
PUSH "\e[20H\e[L"
|
||||
# nothing happens
|
||||
|
||||
!IL with DECSTBM+DECSLRM
|
||||
PUSH "\e[?69h"
|
||||
PUSH "\e[10;50s"
|
||||
PUSH "\e[5;10H\e[L"
|
||||
scrollrect 4..15,9..50 => -1,+0
|
||||
|
||||
!DL
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "A\r\nB\r\nB\r\nC"
|
||||
?cursor = 3,1
|
||||
PUSH "\e[2H"
|
||||
?cursor = 1,0
|
||||
PUSH "\e[M"
|
||||
scrollrect 1..25,0..80 => +1,+0
|
||||
?cursor = 1,0
|
||||
PUSH "\e[3M"
|
||||
scrollrect 1..25,0..80 => +3,+0
|
||||
?cursor = 1,0
|
||||
|
||||
!DL with DECSTBM
|
||||
PUSH "\e[5;15r"
|
||||
PUSH "\e[5H\e[M"
|
||||
scrollrect 4..15,0..80 => +1,+0
|
||||
|
||||
!DL outside DECSTBM
|
||||
PUSH "\e[20H\e[M"
|
||||
# nothing happens
|
||||
|
||||
!DL with DECSTBM+DECSLRM
|
||||
PUSH "\e[?69h"
|
||||
PUSH "\e[10;50s"
|
||||
PUSH "\e[5;10H\e[M"
|
||||
scrollrect 4..15,9..50 => +1,+0
|
||||
|
||||
!DECIC
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
PUSH "\e[20G\e[5'}"
|
||||
scrollrect 0..25,19..80 => +0,-5
|
||||
|
||||
!DECIC with DECSTBM+DECSLRM
|
||||
PUSH "\e[?69h"
|
||||
PUSH "\e[4;20r\e[20;60s"
|
||||
PUSH "\e[4;20H\e[3'}"
|
||||
scrollrect 3..20,19..60 => +0,-3
|
||||
|
||||
!DECIC outside DECSLRM
|
||||
PUSH "\e[70G\e['}"
|
||||
# nothing happens
|
||||
|
||||
!DECDC
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
PUSH "\e[20G\e[5'~"
|
||||
scrollrect 0..25,19..80 => +0,+5
|
||||
|
||||
!DECDC with DECSTBM+DECSLRM
|
||||
PUSH "\e[?69h"
|
||||
PUSH "\e[4;20r\e[20;60s"
|
||||
PUSH "\e[4;20H\e[3'~"
|
||||
scrollrect 3..20,19..60 => +0,+3
|
||||
|
||||
!DECDC outside DECSLRM
|
||||
PUSH "\e[70G\e['~"
|
||||
# nothing happens
|
||||
|
||||
!EL 0
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "ABCDE"
|
||||
PUSH "\e[3D"
|
||||
?cursor = 0,2
|
||||
PUSH "\e[0K"
|
||||
erase 0..1,2..80
|
||||
?cursor = 0,2
|
||||
|
||||
!EL 1
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "ABCDE"
|
||||
PUSH "\e[3D"
|
||||
?cursor = 0,2
|
||||
PUSH "\e[1K"
|
||||
erase 0..1,0..3
|
||||
?cursor = 0,2
|
||||
|
||||
!EL 2
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "ABCDE"
|
||||
PUSH "\e[3D"
|
||||
?cursor = 0,2
|
||||
PUSH "\e[2K"
|
||||
erase 0..1,0..80
|
||||
?cursor = 0,2
|
||||
|
||||
!SEL
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "\e[11G"
|
||||
?cursor = 0,10
|
||||
PUSH "\e[?0K"
|
||||
erase 0..1,10..80 selective
|
||||
?cursor = 0,10
|
||||
PUSH "\e[?1K"
|
||||
erase 0..1,0..11 selective
|
||||
?cursor = 0,10
|
||||
PUSH "\e[?2K"
|
||||
erase 0..1,0..80 selective
|
||||
?cursor = 0,10
|
||||
|
||||
!ED 0
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "\e[2;2H"
|
||||
?cursor = 1,1
|
||||
PUSH "\e[0J"
|
||||
erase 1..2,1..80
|
||||
erase 2..25,0..80
|
||||
?cursor = 1,1
|
||||
|
||||
!ED 1
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "\e[2;2H"
|
||||
?cursor = 1,1
|
||||
PUSH "\e[1J"
|
||||
erase 0..1,0..80
|
||||
erase 1..2,0..2
|
||||
?cursor = 1,1
|
||||
|
||||
!ED 2
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "\e[2;2H"
|
||||
?cursor = 1,1
|
||||
PUSH "\e[2J"
|
||||
erase 0..25,0..80
|
||||
?cursor = 1,1
|
||||
|
||||
!SED
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
PUSH "\e[5;5H"
|
||||
?cursor = 4,4
|
||||
PUSH "\e[?0J"
|
||||
erase 4..5,4..80 selective
|
||||
erase 5..25,0..80 selective
|
||||
?cursor = 4,4
|
||||
PUSH "\e[?1J"
|
||||
erase 0..4,0..80 selective
|
||||
erase 4..5,0..5 selective
|
||||
?cursor = 4,4
|
||||
PUSH "\e[?2J"
|
||||
erase 0..25,0..80 selective
|
||||
?cursor = 4,4
|
||||
|
||||
!DECRQSS on DECSCA
|
||||
PUSH "\e[2\"q"
|
||||
PUSH "\eP\$q\"q\e\\"
|
||||
output "\eP1\$r2\"q\e\\"
|
||||
|
||||
WANTSTATE -s+m
|
||||
|
||||
!ICH move+erase emuation
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "ACD"
|
||||
PUSH "\e[2D"
|
||||
?cursor = 0,1
|
||||
PUSH "\e[@"
|
||||
moverect 0..1,1..79 -> 0..1,2..80
|
||||
erase 0..1,1..2
|
||||
?cursor = 0,1
|
||||
PUSH "B"
|
||||
?cursor = 0,2
|
||||
PUSH "\e[3@"
|
||||
moverect 0..1,2..77 -> 0..1,5..80
|
||||
erase 0..1,2..5
|
||||
|
||||
!DCH move+erase emulation
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "ABBC"
|
||||
PUSH "\e[3D"
|
||||
?cursor = 0,1
|
||||
PUSH "\e[P"
|
||||
moverect 0..1,2..80 -> 0..1,1..79
|
||||
erase 0..1,79..80
|
||||
?cursor = 0,1
|
||||
PUSH "\e[3P"
|
||||
moverect 0..1,4..80 -> 0..1,1..77
|
||||
erase 0..1,77..80
|
||||
?cursor = 0,1
|
105
src/libvterm/t/14state_encoding.test
Normal file
105
src/libvterm/t/14state_encoding.test
Normal file
@ -0,0 +1,105 @@
|
||||
INIT
|
||||
WANTSTATE g
|
||||
|
||||
!Default
|
||||
RESET
|
||||
PUSH "#"
|
||||
putglyph 0x23 1 0,0
|
||||
|
||||
!Designate G0=UK
|
||||
RESET
|
||||
PUSH "\e(A"
|
||||
PUSH "#"
|
||||
putglyph 0x00a3 1 0,0
|
||||
|
||||
!Designate G0=DEC drawing
|
||||
RESET
|
||||
PUSH "\e(0"
|
||||
PUSH "a"
|
||||
putglyph 0x2592 1 0,0
|
||||
|
||||
!Designate G1 + LS1
|
||||
RESET
|
||||
PUSH "\e)0"
|
||||
PUSH "a"
|
||||
putglyph 0x61 1 0,0
|
||||
PUSH "\x0e"
|
||||
PUSH "a"
|
||||
putglyph 0x2592 1 0,1
|
||||
!LS0
|
||||
PUSH "\x0f"
|
||||
PUSH "a"
|
||||
putglyph 0x61 1 0,2
|
||||
|
||||
!Designate G2 + LS2
|
||||
PUSH "\e*0"
|
||||
PUSH "a"
|
||||
putglyph 0x61 1 0,3
|
||||
PUSH "\en"
|
||||
PUSH "a"
|
||||
putglyph 0x2592 1 0,4
|
||||
PUSH "\x0f"
|
||||
PUSH "a"
|
||||
putglyph 0x61 1 0,5
|
||||
|
||||
!Designate G3 + LS3
|
||||
PUSH "\e+0"
|
||||
PUSH "a"
|
||||
putglyph 0x61 1 0,6
|
||||
PUSH "\eo"
|
||||
PUSH "a"
|
||||
putglyph 0x2592 1 0,7
|
||||
PUSH "\x0f"
|
||||
PUSH "a"
|
||||
putglyph 0x61 1 0,8
|
||||
|
||||
!SS2
|
||||
PUSH "a\x{8e}aa"
|
||||
putglyph 0x61 1 0,9
|
||||
putglyph 0x2592 1 0,10
|
||||
putglyph 0x61 1 0,11
|
||||
|
||||
!SS3
|
||||
PUSH "a\x{8f}aa"
|
||||
putglyph 0x61 1 0,12
|
||||
putglyph 0x2592 1 0,13
|
||||
putglyph 0x61 1 0,14
|
||||
|
||||
!LS1R
|
||||
RESET
|
||||
PUSH "\e~"
|
||||
PUSH "\xe1"
|
||||
putglyph 0x61 1 0,0
|
||||
PUSH "\e)0"
|
||||
PUSH "\xe1"
|
||||
putglyph 0x2592 1 0,1
|
||||
|
||||
!LS2R
|
||||
RESET
|
||||
PUSH "\e}"
|
||||
PUSH "\xe1"
|
||||
putglyph 0x61 1 0,0
|
||||
PUSH "\e*0"
|
||||
PUSH "\xe1"
|
||||
putglyph 0x2592 1 0,1
|
||||
|
||||
!LS3R
|
||||
RESET
|
||||
PUSH "\e|"
|
||||
PUSH "\xe1"
|
||||
putglyph 0x61 1 0,0
|
||||
PUSH "\e+0"
|
||||
PUSH "\xe1"
|
||||
putglyph 0x2592 1 0,1
|
||||
|
||||
UTF8 1
|
||||
|
||||
!Mixed US-ASCII and UTF-8
|
||||
# U+0108 == 0xc4 0x88
|
||||
RESET
|
||||
PUSH "\e(B"
|
||||
PUSH "AB\xc4\x88D"
|
||||
putglyph 0x0041 1 0,0
|
||||
putglyph 0x0042 1 0,1
|
||||
putglyph 0x0108 1 0,2
|
||||
putglyph 0x0044 1 0,3
|
86
src/libvterm/t/15state_mode.test
Normal file
86
src/libvterm/t/15state_mode.test
Normal file
@ -0,0 +1,86 @@
|
||||
INIT
|
||||
UTF8 1
|
||||
WANTSTATE gme
|
||||
|
||||
!Insert/Replace Mode
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "AC\e[DB"
|
||||
putglyph 0x41 1 0,0
|
||||
putglyph 0x43 1 0,1
|
||||
putglyph 0x42 1 0,1
|
||||
PUSH "\e[4h"
|
||||
PUSH "\e[G"
|
||||
PUSH "AC\e[DB"
|
||||
moverect 0..1,0..79 -> 0..1,1..80
|
||||
erase 0..1,0..1
|
||||
putglyph 0x41 1 0,0
|
||||
moverect 0..1,1..79 -> 0..1,2..80
|
||||
erase 0..1,1..2
|
||||
putglyph 0x43 1 0,1
|
||||
moverect 0..1,1..79 -> 0..1,2..80
|
||||
erase 0..1,1..2
|
||||
putglyph 0x42 1 0,1
|
||||
|
||||
!Insert mode only happens once for UTF-8 combining
|
||||
PUSH "e"
|
||||
moverect 0..1,2..79 -> 0..1,3..80
|
||||
erase 0..1,2..3
|
||||
putglyph 0x65 1 0,2
|
||||
PUSH "\xCC\x81"
|
||||
putglyph 0x65,0x301 1 0,2
|
||||
|
||||
!Newline/Linefeed mode
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "\e[5G\n"
|
||||
?cursor = 1,4
|
||||
PUSH "\e[20h"
|
||||
PUSH "\e[5G\n"
|
||||
?cursor = 2,0
|
||||
|
||||
!DEC origin mode
|
||||
RESET
|
||||
erase 0..25,0..80
|
||||
?cursor = 0,0
|
||||
PUSH "\e[5;15r"
|
||||
PUSH "\e[H"
|
||||
?cursor = 0,0
|
||||
PUSH "\e[3;3H"
|
||||
?cursor = 2,2
|
||||
PUSH "\e[?6h"
|
||||
PUSH "\e[H"
|
||||
?cursor = 4,0
|
||||
PUSH "\e[3;3H"
|
||||
?cursor = 6,2
|
||||
|
||||
!DECRQM on DECOM
|
||||
PUSH "\e[?6h"
|
||||
PUSH "\e[?6\$p"
|
||||
output "\e[?6;1\$y"
|
||||
PUSH "\e[?6l"
|
||||
PUSH "\e[?6\$p"
|
||||
output "\e[?6;2\$y"
|
||||
|
||||
!Origin mode with DECSLRM
|
||||
PUSH "\e[?6h"
|
||||
PUSH "\e[?69h"
|
||||
PUSH "\e[20;60s"
|
||||
PUSH "\e[H"
|
||||
?cursor = 4,19
|
||||
|
||||
PUSH "\e[?69l"
|
||||
|
||||
!Origin mode bounds cursor to scrolling region
|
||||
PUSH "\e[H"
|
||||
PUSH "\e[10A"
|
||||
?cursor = 4,0
|
||||
PUSH "\e[20B"
|
||||
?cursor = 14,0
|
||||
|
||||
!Origin mode without scroll region
|
||||
PUSH "\e[?6l"
|
||||
PUSH "\e[r\e[?6h"
|
||||
?cursor = 0,0
|
48
src/libvterm/t/16state_resize.test
Normal file
48
src/libvterm/t/16state_resize.test
Normal file
@ -0,0 +1,48 @@
|
||||
INIT
|
||||
WANTSTATE g
|
||||
|
||||
!Placement
|
||||
RESET
|
||||
PUSH "AB\e[79GCDE"
|
||||
putglyph 0x41 1 0,0
|
||||
putglyph 0x42 1 0,1
|
||||
putglyph 0x43 1 0,78
|
||||
putglyph 0x44 1 0,79
|
||||
putglyph 0x45 1 1,0
|
||||
|
||||
!Resize
|
||||
RESET
|
||||
RESIZE 27,85
|
||||
PUSH "AB\e[79GCDE"
|
||||
putglyph 0x41 1 0,0
|
||||
putglyph 0x42 1 0,1
|
||||
putglyph 0x43 1 0,78
|
||||
putglyph 0x44 1 0,79
|
||||
putglyph 0x45 1 0,80
|
||||
?cursor = 0,81
|
||||
|
||||
!Resize without reset
|
||||
RESIZE 28,90
|
||||
?cursor = 0,81
|
||||
PUSH "FGHI"
|
||||
putglyph 0x46 1 0,81
|
||||
putglyph 0x47 1 0,82
|
||||
putglyph 0x48 1 0,83
|
||||
putglyph 0x49 1 0,84
|
||||
?cursor = 0,85
|
||||
|
||||
!Resize shrink moves cursor
|
||||
RESIZE 25,80
|
||||
?cursor = 0,79
|
||||
|
||||
!Resize grow doesn't cancel phantom
|
||||
RESET
|
||||
PUSH "\e[79GAB"
|
||||
putglyph 0x41 1 0,78
|
||||
putglyph 0x42 1 0,79
|
||||
?cursor = 0,79
|
||||
RESIZE 30,100
|
||||
?cursor = 0,80
|
||||
PUSH "C"
|
||||
putglyph 0x43 1 0,80
|
||||
?cursor = 0,81
|
172
src/libvterm/t/17state_mouse.test
Normal file
172
src/libvterm/t/17state_mouse.test
Normal file
@ -0,0 +1,172 @@
|
||||
INIT
|
||||
WANTSTATE p
|
||||
|
||||
!DECRQM on with mouse off
|
||||
PUSH "\e[?1000\$p"
|
||||
output "\e[?1000;2\$y"
|
||||
PUSH "\e[?1002\$p"
|
||||
output "\e[?1002;2\$y"
|
||||
PUSH "\e[?1003\$p"
|
||||
output "\e[?1003;2\$y"
|
||||
|
||||
!Mouse in simple button report mode
|
||||
RESET
|
||||
settermprop 1 true
|
||||
settermprop 2 true
|
||||
settermprop 7 1
|
||||
PUSH "\e[?1000h"
|
||||
settermprop 8 1
|
||||
|
||||
!Press 1
|
||||
MOUSEMOVE 0,0 0
|
||||
MOUSEBTN d 1 0
|
||||
output "\e[M\x20\x21\x21"
|
||||
|
||||
!Release 1
|
||||
MOUSEBTN u 1 0
|
||||
output "\e[M\x23\x21\x21"
|
||||
|
||||
!Ctrl-Press 1
|
||||
MOUSEBTN d 1 C
|
||||
output "\e[M\x30\x21\x21"
|
||||
MOUSEBTN u 1 C
|
||||
output "\e[M\x33\x21\x21"
|
||||
|
||||
!Button 2
|
||||
MOUSEBTN d 2 0
|
||||
output "\e[M\x21\x21\x21"
|
||||
MOUSEBTN u 2 0
|
||||
output "\e[M\x23\x21\x21"
|
||||
|
||||
!Position
|
||||
MOUSEMOVE 10,20 0
|
||||
MOUSEBTN d 1 0
|
||||
output "\e[M\x20\x35\x2b"
|
||||
|
||||
MOUSEBTN u 1 0
|
||||
output "\e[M\x23\x35\x2b"
|
||||
MOUSEMOVE 10,21 0
|
||||
# no output
|
||||
|
||||
!Wheel events
|
||||
MOUSEBTN d 4 0
|
||||
output "\e[M\x60\x36\x2b"
|
||||
MOUSEBTN d 4 0
|
||||
output "\e[M\x60\x36\x2b"
|
||||
MOUSEBTN d 5 0
|
||||
output "\e[M\x61\x36\x2b"
|
||||
|
||||
!DECRQM on mouse button mode
|
||||
PUSH "\e[?1000\$p"
|
||||
output "\e[?1000;1\$y"
|
||||
PUSH "\e[?1002\$p"
|
||||
output "\e[?1002;2\$y"
|
||||
PUSH "\e[?1003\$p"
|
||||
output "\e[?1003;2\$y"
|
||||
|
||||
!Drag events
|
||||
RESET
|
||||
settermprop 1 true
|
||||
settermprop 2 true
|
||||
settermprop 7 1
|
||||
PUSH "\e[?1002h"
|
||||
settermprop 8 2
|
||||
|
||||
MOUSEMOVE 5,5 0
|
||||
MOUSEBTN d 1 0
|
||||
output "\e[M\x20\x26\x26"
|
||||
MOUSEMOVE 5,6 0
|
||||
output "\e[M\x40\x27\x26"
|
||||
MOUSEMOVE 6,6 0
|
||||
output "\e[M\x40\x27\x27"
|
||||
MOUSEMOVE 6,6 0
|
||||
# no output
|
||||
MOUSEBTN u 1 0
|
||||
output "\e[M\x23\x27\x27"
|
||||
MOUSEMOVE 6,7
|
||||
# no output
|
||||
|
||||
!DECRQM on mouse drag mode
|
||||
PUSH "\e[?1000\$p"
|
||||
output "\e[?1000;2\$y"
|
||||
PUSH "\e[?1002\$p"
|
||||
output "\e[?1002;1\$y"
|
||||
PUSH "\e[?1003\$p"
|
||||
output "\e[?1003;2\$y"
|
||||
|
||||
!Non-drag motion events
|
||||
PUSH "\e[?1003h"
|
||||
settermprop 8 3
|
||||
|
||||
MOUSEMOVE 6,8 0
|
||||
output "\e[M\x43\x29\x27"
|
||||
|
||||
!DECRQM on mouse motion mode
|
||||
PUSH "\e[?1000\$p"
|
||||
output "\e[?1000;2\$y"
|
||||
PUSH "\e[?1002\$p"
|
||||
output "\e[?1002;2\$y"
|
||||
PUSH "\e[?1003\$p"
|
||||
output "\e[?1003;1\$y"
|
||||
|
||||
!Bounds checking
|
||||
MOUSEMOVE 300,300 0
|
||||
output "\e[M\x43\xff\xff"
|
||||
MOUSEBTN d 1 0
|
||||
output "\e[M\x20\xff\xff"
|
||||
MOUSEBTN u 1 0
|
||||
output "\e[M\x23\xff\xff"
|
||||
|
||||
!DECRQM on standard encoding mode
|
||||
PUSH "\e[?1005\$p"
|
||||
output "\e[?1005;2\$y"
|
||||
PUSH "\e[?1006\$p"
|
||||
output "\e[?1006;2\$y"
|
||||
PUSH "\e[?1015\$p"
|
||||
output "\e[?1015;2\$y"
|
||||
|
||||
!UTF-8 extended encoding mode
|
||||
# 300 + 32 + 1 = 333 = U+014d = \xc5\x8d
|
||||
PUSH "\e[?1005h"
|
||||
MOUSEBTN d 1 0
|
||||
output "\e[M\x20\xc5\x8d\xc5\x8d"
|
||||
MOUSEBTN u 1 0
|
||||
output "\e[M\x23\xc5\x8d\xc5\x8d"
|
||||
|
||||
!DECRQM on UTF-8 extended encoding mode
|
||||
PUSH "\e[?1005\$p"
|
||||
output "\e[?1005;1\$y"
|
||||
PUSH "\e[?1006\$p"
|
||||
output "\e[?1006;2\$y"
|
||||
PUSH "\e[?1015\$p"
|
||||
output "\e[?1015;2\$y"
|
||||
|
||||
!SGR extended encoding mode
|
||||
PUSH "\e[?1006h"
|
||||
MOUSEBTN d 1 0
|
||||
output "\e[<0;301;301M"
|
||||
MOUSEBTN u 1 0
|
||||
output "\e[<0;301;301m"
|
||||
|
||||
!DECRQM on SGR extended encoding mode
|
||||
PUSH "\e[?1005\$p"
|
||||
output "\e[?1005;2\$y"
|
||||
PUSH "\e[?1006\$p"
|
||||
output "\e[?1006;1\$y"
|
||||
PUSH "\e[?1015\$p"
|
||||
output "\e[?1015;2\$y"
|
||||
|
||||
!rxvt extended encoding mode
|
||||
PUSH "\e[?1015h"
|
||||
MOUSEBTN d 1 0
|
||||
output "\e[0;301;301M"
|
||||
MOUSEBTN u 1 0
|
||||
output "\e[3;301;301M"
|
||||
|
||||
!DECRQM on rxvt extended encoding mode
|
||||
PUSH "\e[?1005\$p"
|
||||
output "\e[?1005;2\$y"
|
||||
PUSH "\e[?1006\$p"
|
||||
output "\e[?1006;2\$y"
|
||||
PUSH "\e[?1015\$p"
|
||||
output "\e[?1015;1\$y"
|
36
src/libvterm/t/18state_termprops.test
Normal file
36
src/libvterm/t/18state_termprops.test
Normal file
@ -0,0 +1,36 @@
|
||||
INIT
|
||||
WANTSTATE p
|
||||
|
||||
RESET
|
||||
settermprop 1 true
|
||||
settermprop 2 true
|
||||
settermprop 7 1
|
||||
|
||||
!Cursor visibility
|
||||
PUSH "\e[?25h"
|
||||
settermprop 1 true
|
||||
PUSH "\e[?25\$p"
|
||||
output "\e[?25;1\$y"
|
||||
PUSH "\e[?25l"
|
||||
settermprop 1 false
|
||||
PUSH "\e[?25\$p"
|
||||
output "\e[?25;2\$y"
|
||||
|
||||
!Cursor blink
|
||||
PUSH "\e[?12h"
|
||||
settermprop 2 true
|
||||
PUSH "\e[?12\$p"
|
||||
output "\e[?12;1\$y"
|
||||
PUSH "\e[?12l"
|
||||
settermprop 2 false
|
||||
PUSH "\e[?12\$p"
|
||||
output "\e[?12;2\$y"
|
||||
|
||||
!Cursor shape
|
||||
PUSH "\e[3 q"
|
||||
settermprop 2 true
|
||||
settermprop 7 2
|
||||
|
||||
!Title
|
||||
PUSH "\e]2;Here is my title\a"
|
||||
settermprop 4 "Here is my title"
|
69
src/libvterm/t/20state_wrapping.test
Normal file
69
src/libvterm/t/20state_wrapping.test
Normal file
@ -0,0 +1,69 @@
|
||||
INIT
|
||||
UTF8 1
|
||||
WANTSTATE gm
|
||||
|
||||
!79th Column
|
||||
PUSH "\e[75G"
|
||||
PUSH "A"x5
|
||||
putglyph 0x41 1 0,74
|
||||
putglyph 0x41 1 0,75
|
||||
putglyph 0x41 1 0,76
|
||||
putglyph 0x41 1 0,77
|
||||
putglyph 0x41 1 0,78
|
||||
?cursor = 0,79
|
||||
|
||||
!80th Column Phantom
|
||||
PUSH "A"
|
||||
putglyph 0x41 1 0,79
|
||||
?cursor = 0,79
|
||||
|
||||
!Line Wraparound
|
||||
PUSH "B"
|
||||
putglyph 0x42 1 1,0
|
||||
?cursor = 1,1
|
||||
|
||||
!Line Wraparound during combined write
|
||||
PUSH "\e[78G"
|
||||
PUSH "BBBCC"
|
||||
putglyph 0x42 1 1,77
|
||||
putglyph 0x42 1 1,78
|
||||
putglyph 0x42 1 1,79
|
||||
putglyph 0x43 1 2,0
|
||||
putglyph 0x43 1 2,1
|
||||
?cursor = 2,2
|
||||
|
||||
!DEC Auto Wrap Mode
|
||||
RESET
|
||||
PUSH "\e[?7l"
|
||||
PUSH "\e[75G"
|
||||
PUSH "D"x6
|
||||
putglyph 0x44 1 0,74
|
||||
putglyph 0x44 1 0,75
|
||||
putglyph 0x44 1 0,76
|
||||
putglyph 0x44 1 0,77
|
||||
putglyph 0x44 1 0,78
|
||||
putglyph 0x44 1 0,79
|
||||
?cursor = 0,79
|
||||
PUSH "D"
|
||||
putglyph 0x44 1 0,79
|
||||
?cursor = 0,79
|
||||
PUSH "\e[?7h"
|
||||
|
||||
!80th column causes linefeed on wraparound
|
||||
PUSH "\e[25;78HABC"
|
||||
putglyph 0x41 1 24,77
|
||||
putglyph 0x42 1 24,78
|
||||
putglyph 0x43 1 24,79
|
||||
?cursor = 24,79
|
||||
PUSH "D"
|
||||
moverect 1..25,0..80 -> 0..24,0..80
|
||||
putglyph 0x44 1 24,0
|
||||
|
||||
!80th column phantom linefeed phantom cancelled by explicit cursor move
|
||||
PUSH "\e[25;78HABC"
|
||||
putglyph 0x41 1 24,77
|
||||
putglyph 0x42 1 24,78
|
||||
putglyph 0x43 1 24,79
|
||||
?cursor = 24,79
|
||||
PUSH "\e[25;1HD"
|
||||
putglyph 0x44 1 24,0
|
60
src/libvterm/t/21state_tabstops.test
Normal file
60
src/libvterm/t/21state_tabstops.test
Normal file
@ -0,0 +1,60 @@
|
||||
INIT
|
||||
WANTSTATE g
|
||||
|
||||
!Initial
|
||||
RESET
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,8
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,16
|
||||
?cursor = 0,17
|
||||
|
||||
!HTS
|
||||
PUSH "\e[5G\eH"
|
||||
PUSH "\e[G\tX"
|
||||
putglyph 0x58 1 0,4
|
||||
?cursor = 0,5
|
||||
|
||||
!TBC 0
|
||||
PUSH "\e[9G\e[g"
|
||||
PUSH "\e[G\tX\tX"
|
||||
putglyph 0x58 1 0,4
|
||||
putglyph 0x58 1 0,16
|
||||
?cursor = 0,17
|
||||
|
||||
!TBC 3
|
||||
PUSH "\e[3g\e[50G\eH\e[G"
|
||||
?cursor = 0,0
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,49
|
||||
?cursor = 0,50
|
||||
|
||||
!Tabstops after resize
|
||||
RESET
|
||||
RESIZE 30,100
|
||||
# Should be 100/8 = 12 tabstops
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,8
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,16
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,24
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,32
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,40
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,48
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,56
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,64
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,72
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,80
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,88
|
||||
PUSH "\tX"
|
||||
putglyph 0x58 1 0,96
|
||||
?cursor = 0,97
|
64
src/libvterm/t/22state_save.test
Normal file
64
src/libvterm/t/22state_save.test
Normal file
@ -0,0 +1,64 @@
|
||||
INIT
|
||||
WANTSTATE p
|
||||
|
||||
RESET
|
||||
settermprop 1 true
|
||||
settermprop 2 true
|
||||
settermprop 7 1
|
||||
|
||||
!Set up state
|
||||
PUSH "\e[2;2H"
|
||||
?cursor = 1,1
|
||||
PUSH "\e[1m"
|
||||
?pen bold = on
|
||||
|
||||
!Save
|
||||
PUSH "\e[?1048h"
|
||||
|
||||
!Change state
|
||||
PUSH "\e[5;5H"
|
||||
?cursor = 4,4
|
||||
PUSH "\e[4 q"
|
||||
settermprop 2 false
|
||||
settermprop 7 2
|
||||
PUSH "\e[22;4m"
|
||||
?pen bold = off
|
||||
?pen underline = 1
|
||||
|
||||
!Restore
|
||||
PUSH "\e[?1048l"
|
||||
settermprop 1 true
|
||||
settermprop 2 true
|
||||
settermprop 7 1
|
||||
?cursor = 1,1
|
||||
?pen bold = on
|
||||
?pen underline = 0
|
||||
|
||||
!Save/restore using DECSC/DECRC
|
||||
PUSH "\e[2;2H\e7"
|
||||
?cursor = 1,1
|
||||
|
||||
PUSH "\e[5;5H"
|
||||
?cursor = 4,4
|
||||
PUSH "\e8"
|
||||
settermprop 1 true
|
||||
settermprop 2 true
|
||||
settermprop 7 1
|
||||
?cursor = 1,1
|
||||
|
||||
!Save twice, restore twice happens on both edge transitions
|
||||
PUSH "\e[2;10H\e[?1048h\e[6;10H\e[?1048h"
|
||||
PUSH "\e[H"
|
||||
?cursor = 0,0
|
||||
PUSH "\e[?1048l"
|
||||
settermprop 1 true
|
||||
settermprop 2 true
|
||||
settermprop 7 1
|
||||
?cursor = 5,9
|
||||
PUSH "\e[H"
|
||||
?cursor = 0,0
|
||||
PUSH "\e[?1048l"
|
||||
settermprop 1 true
|
||||
settermprop 2 true
|
||||
settermprop 7 1
|
||||
?cursor = 5,9
|
132
src/libvterm/t/25state_input.test
Normal file
132
src/libvterm/t/25state_input.test
Normal file
@ -0,0 +1,132 @@
|
||||
INIT
|
||||
WANTSTATE
|
||||
|
||||
!Unmodified ASCII
|
||||
INCHAR 0 41
|
||||
output "A"
|
||||
INCHAR 0 61
|
||||
output "a"
|
||||
|
||||
!Ctrl modifier on ASCII letters
|
||||
INCHAR C 41
|
||||
output "\e[65;5u"
|
||||
INCHAR C 61
|
||||
output "\x01"
|
||||
|
||||
!Alt modifier on ASCII letters
|
||||
INCHAR A 41
|
||||
output "\eA"
|
||||
INCHAR A 61
|
||||
output "\ea"
|
||||
|
||||
!Ctrl-Alt modifier on ASCII letters
|
||||
INCHAR CA 41
|
||||
output "\e[65;7u"
|
||||
INCHAR CA 61
|
||||
output "\e\x01"
|
||||
|
||||
!Special handling of Ctrl-I
|
||||
INCHAR 0 49
|
||||
output "I"
|
||||
INCHAR 0 69
|
||||
output "i"
|
||||
INCHAR C 49
|
||||
output "\e[73;5u"
|
||||
INCHAR C 69
|
||||
output "\e[105;5u"
|
||||
INCHAR A 49
|
||||
output "\eI"
|
||||
INCHAR A 69
|
||||
output "\ei"
|
||||
INCHAR CA 49
|
||||
output "\e[73;7u"
|
||||
INCHAR CA 69
|
||||
output "\e[105;7u"
|
||||
|
||||
!Special handling of Space
|
||||
INCHAR 0 20
|
||||
output " "
|
||||
INCHAR S 20
|
||||
output "\e[32;2u"
|
||||
INCHAR C 20
|
||||
output "\0"
|
||||
INCHAR SC 20
|
||||
output "\e[32;6u"
|
||||
INCHAR A 20
|
||||
output "\e "
|
||||
INCHAR SA 20
|
||||
output "\e[32;4u"
|
||||
INCHAR CA 20
|
||||
output "\e\0"
|
||||
INCHAR SCA 20
|
||||
output "\e[32;8u"
|
||||
|
||||
!Cursor keys in reset (cursor) mode
|
||||
INKEY 0 Up
|
||||
output "\e[A"
|
||||
INKEY S Up
|
||||
output "\e[1;2A"
|
||||
INKEY C Up
|
||||
output "\e[1;5A"
|
||||
INKEY SC Up
|
||||
output "\e[1;6A"
|
||||
INKEY A Up
|
||||
output "\e[1;3A"
|
||||
INKEY SA Up
|
||||
output "\e[1;4A"
|
||||
INKEY CA Up
|
||||
output "\e[1;7A"
|
||||
INKEY SCA Up
|
||||
output "\e[1;8A"
|
||||
|
||||
!Cursor keys in application mode
|
||||
PUSH "\e[?1h"
|
||||
# Plain "Up" should be SS3 A now
|
||||
INKEY 0 Up
|
||||
output "\eOA"
|
||||
# Modified keys should still use CSI
|
||||
INKEY S Up
|
||||
output "\e[1;2A"
|
||||
INKEY C Up
|
||||
output "\e[1;5A"
|
||||
|
||||
!Shift-Tab should be different
|
||||
INKEY 0 Tab
|
||||
output "\x09"
|
||||
INKEY S Tab
|
||||
output "\e[Z"
|
||||
INKEY C Tab
|
||||
output "\e[9;5u"
|
||||
INKEY A Tab
|
||||
output "\e\x09"
|
||||
INKEY CA Tab
|
||||
output "\e[9;7u"
|
||||
|
||||
!Enter in linefeed mode
|
||||
INKEY 0 Enter
|
||||
output "\x0d"
|
||||
|
||||
!Enter in newline mode
|
||||
PUSH "\e[20h"
|
||||
INKEY 0 Enter
|
||||
output "\x0d\x0a"
|
||||
|
||||
!Keypad in DECKPNM
|
||||
INKEY 0 KP0
|
||||
output "0"
|
||||
|
||||
!Keypad in DECKPAM
|
||||
PUSH "\e="
|
||||
INKEY 0 KP0
|
||||
output "\eOp"
|
||||
|
||||
!Bracketed paste mode off
|
||||
PASTE START
|
||||
PASTE END
|
||||
|
||||
!Bracketed paste mode on
|
||||
PUSH "\e[?2004h"
|
||||
PASTE START
|
||||
output "\e[200~"
|
||||
PASTE END
|
||||
output "\e[201~"
|
62
src/libvterm/t/26state_query.test
Normal file
62
src/libvterm/t/26state_query.test
Normal file
@ -0,0 +1,62 @@
|
||||
INIT
|
||||
WANTSTATE
|
||||
|
||||
!DA
|
||||
RESET
|
||||
PUSH "\e[c"
|
||||
output "\e[?1;2c"
|
||||
|
||||
!DSR
|
||||
RESET
|
||||
PUSH "\e[5n"
|
||||
output "\e[0n"
|
||||
|
||||
!CPR
|
||||
PUSH "\e[6n"
|
||||
output "\e[1;1R"
|
||||
PUSH "\e[10;10H\e[6n"
|
||||
output "\e[10;10R"
|
||||
|
||||
!DECCPR
|
||||
PUSH "\e[?6n"
|
||||
output "\e[?10;10R"
|
||||
|
||||
!DECRQSS on DECSCUSR
|
||||
PUSH "\e[3 q"
|
||||
PUSH "\eP\$q q\e\\"
|
||||
output "\eP1\$r3 q\e\\"
|
||||
|
||||
!DECRQSS on SGR
|
||||
PUSH "\e[1;5;7m"
|
||||
PUSH "\eP\$qm\e\\"
|
||||
output "\eP1\$r1;5;7m\e\\"
|
||||
|
||||
!DECRQSS on SGR ANSI colours
|
||||
PUSH "\e[0;31;42m"
|
||||
PUSH "\eP\$qm\e\\"
|
||||
output "\eP1\$r31;42m\e\\"
|
||||
|
||||
!DECRQSS on SGR ANSI hi-bright colours
|
||||
PUSH "\e[0;93;104m"
|
||||
PUSH "\eP\$qm\e\\"
|
||||
output "\eP1\$r93;104m\e\\"
|
||||
|
||||
!DECRQSS on SGR 256-palette colours
|
||||
PUSH "\e[0;38:5:56;48:5:78m"
|
||||
PUSH "\eP\$qm\e\\"
|
||||
output "\eP1\$r38:5:56;48:5:78m\e\\"
|
||||
|
||||
!DECRQSS on SGR RGB8 colours
|
||||
PUSH "\e[0;38:2:24:68:112;48:2:13:57:101m"
|
||||
PUSH "\eP\$qm\e\\"
|
||||
output "\eP1\$r38:2:24:68:112;48:2:13:57:101m\e\\"
|
||||
|
||||
!S8C1T on DSR
|
||||
PUSH "\e G"
|
||||
PUSH "\e[5n"
|
||||
output "\x{9b}0n"
|
||||
PUSH "\e F"
|
||||
|
||||
!Truncation on attempted buffer overflow
|
||||
PUSH "\e[6n" x 20
|
||||
output "\e[10;10R" x 7
|
32
src/libvterm/t/27state_reset.test
Normal file
32
src/libvterm/t/27state_reset.test
Normal file
@ -0,0 +1,32 @@
|
||||
INIT
|
||||
WANTSTATE
|
||||
|
||||
RESET
|
||||
|
||||
!RIS homes cursor
|
||||
PUSH "\e[5;5H"
|
||||
?cursor = 4,4
|
||||
WANTSTATE +m
|
||||
PUSH "\ec"
|
||||
?cursor = 0,0
|
||||
WANTSTATE -m
|
||||
|
||||
!RIS cancels scrolling region
|
||||
PUSH "\e[5;10r"
|
||||
WANTSTATE +s
|
||||
PUSH "\ec\e[25H\n"
|
||||
scrollrect 0..25,0..80 => +1,+0
|
||||
WANTSTATE -s
|
||||
|
||||
!RIS erases screen
|
||||
PUSH "ABCDE"
|
||||
WANTSTATE +e
|
||||
PUSH "\ec"
|
||||
erase 0..25,0..80
|
||||
WANTSTATE -e
|
||||
|
||||
!RIS clears tabstops
|
||||
PUSH "\e[5G\eH\e[G\t"
|
||||
?cursor = 0,4
|
||||
PUSH "\ec\t"
|
||||
?cursor = 0,8
|
61
src/libvterm/t/28state_dbl_wh.test
Normal file
61
src/libvterm/t/28state_dbl_wh.test
Normal file
@ -0,0 +1,61 @@
|
||||
INIT
|
||||
WANTSTATE g
|
||||
|
||||
!Single Width, Single Height
|
||||
RESET
|
||||
PUSH "\e#5"
|
||||
PUSH "Hello"
|
||||
putglyph 0x48 1 0,0
|
||||
putglyph 0x65 1 0,1
|
||||
putglyph 0x6c 1 0,2
|
||||
putglyph 0x6c 1 0,3
|
||||
putglyph 0x6f 1 0,4
|
||||
|
||||
!Double Width, Single Height
|
||||
RESET
|
||||
PUSH "\e#6"
|
||||
PUSH "Hello"
|
||||
putglyph 0x48 1 0,0 dwl
|
||||
putglyph 0x65 1 0,1 dwl
|
||||
putglyph 0x6c 1 0,2 dwl
|
||||
putglyph 0x6c 1 0,3 dwl
|
||||
putglyph 0x6f 1 0,4 dwl
|
||||
?cursor = 0,5
|
||||
PUSH "\e[40GAB"
|
||||
putglyph 0x41 1 0,39 dwl
|
||||
putglyph 0x42 1 1,0
|
||||
?cursor = 1,1
|
||||
|
||||
!Double Height
|
||||
RESET
|
||||
PUSH "\e#3"
|
||||
PUSH "Hello"
|
||||
putglyph 0x48 1 0,0 dwl dhl-top
|
||||
putglyph 0x65 1 0,1 dwl dhl-top
|
||||
putglyph 0x6c 1 0,2 dwl dhl-top
|
||||
putglyph 0x6c 1 0,3 dwl dhl-top
|
||||
putglyph 0x6f 1 0,4 dwl dhl-top
|
||||
?cursor = 0,5
|
||||
PUSH "\r\n\e#4"
|
||||
PUSH "Hello"
|
||||
putglyph 0x48 1 1,0 dwl dhl-bottom
|
||||
putglyph 0x65 1 1,1 dwl dhl-bottom
|
||||
putglyph 0x6c 1 1,2 dwl dhl-bottom
|
||||
putglyph 0x6c 1 1,3 dwl dhl-bottom
|
||||
putglyph 0x6f 1 1,4 dwl dhl-bottom
|
||||
?cursor = 1,5
|
||||
|
||||
!Double Width scrolling
|
||||
RESET
|
||||
PUSH "\e[20H\e#6ABC"
|
||||
putglyph 0x41 1 19,0 dwl
|
||||
putglyph 0x42 1 19,1 dwl
|
||||
putglyph 0x43 1 19,2 dwl
|
||||
PUSH "\e[25H\n"
|
||||
PUSH "\e[19;4HDE"
|
||||
putglyph 0x44 1 18,3 dwl
|
||||
putglyph 0x45 1 18,4 dwl
|
||||
PUSH "\e[H\eM"
|
||||
PUSH "\e[20;6HFG"
|
||||
putglyph 0x46 1 19,5 dwl
|
||||
putglyph 0x47 1 19,6 dwl
|
19
src/libvterm/t/29state_fallback.test
Normal file
19
src/libvterm/t/29state_fallback.test
Normal file
@ -0,0 +1,19 @@
|
||||
INIT
|
||||
WANTSTATE f
|
||||
RESET
|
||||
|
||||
!Unrecognised control
|
||||
PUSH "\x03"
|
||||
control 03
|
||||
|
||||
!Unrecognised CSI
|
||||
PUSH "\e[?15;2z"
|
||||
csi 0x7a L=3f 15,2
|
||||
|
||||
!Unrecognised OSC
|
||||
PUSH "\e]27;Something\e\\"
|
||||
osc "27;Something"
|
||||
|
||||
!Unrecognised DCS
|
||||
PUSH "\ePz123\e\\"
|
||||
dcs "z123"
|
106
src/libvterm/t/30pen.test
Normal file
106
src/libvterm/t/30pen.test
Normal file
@ -0,0 +1,106 @@
|
||||
INIT
|
||||
UTF8 1
|
||||
WANTSTATE
|
||||
|
||||
!Reset
|
||||
PUSH "\e[m"
|
||||
?pen bold = off
|
||||
?pen underline = 0
|
||||
?pen italic = off
|
||||
?pen blink = off
|
||||
?pen reverse = off
|
||||
?pen font = 0
|
||||
?pen foreground = rgb(240,240,240)
|
||||
?pen background = rgb(0,0,0)
|
||||
|
||||
!Bold
|
||||
PUSH "\e[1m"
|
||||
?pen bold = on
|
||||
PUSH "\e[22m"
|
||||
?pen bold = off
|
||||
PUSH "\e[1m\e[m"
|
||||
?pen bold = off
|
||||
|
||||
!Underline
|
||||
PUSH "\e[4m"
|
||||
?pen underline = 1
|
||||
PUSH "\e[21m"
|
||||
?pen underline = 2
|
||||
PUSH "\e[24m"
|
||||
?pen underline = 0
|
||||
PUSH "\e[4m\e[m"
|
||||
?pen underline = 0
|
||||
|
||||
!Italic
|
||||
PUSH "\e[3m"
|
||||
?pen italic = on
|
||||
PUSH "\e[23m"
|
||||
?pen italic = off
|
||||
PUSH "\e[3m\e[m"
|
||||
?pen italic = off
|
||||
|
||||
!Blink
|
||||
PUSH "\e[5m"
|
||||
?pen blink = on
|
||||
PUSH "\e[25m"
|
||||
?pen blink = off
|
||||
PUSH "\e[5m\e[m"
|
||||
?pen blink = off
|
||||
|
||||
!Reverse
|
||||
PUSH "\e[7m"
|
||||
?pen reverse = on
|
||||
PUSH "\e[27m"
|
||||
?pen reverse = off
|
||||
PUSH "\e[7m\e[m"
|
||||
?pen reverse = off
|
||||
|
||||
!Font Selection
|
||||
PUSH "\e[11m"
|
||||
?pen font = 1
|
||||
PUSH "\e[19m"
|
||||
?pen font = 9
|
||||
PUSH "\e[10m"
|
||||
?pen font = 0
|
||||
PUSH "\e[11m\e[m"
|
||||
?pen font = 0
|
||||
|
||||
!Foreground
|
||||
PUSH "\e[31m"
|
||||
?pen foreground = rgb(224,0,0)
|
||||
PUSH "\e[32m"
|
||||
?pen foreground = rgb(0,224,0)
|
||||
PUSH "\e[34m"
|
||||
?pen foreground = rgb(0,0,224)
|
||||
PUSH "\e[91m"
|
||||
?pen foreground = rgb(255,64,64)
|
||||
PUSH "\e[38:2:10:20:30m"
|
||||
?pen foreground = rgb(10,20,30)
|
||||
PUSH "\e[38:5:1m"
|
||||
?pen foreground = rgb(224,0,0)
|
||||
PUSH "\e[39m"
|
||||
?pen foreground = rgb(240,240,240)
|
||||
|
||||
!Background
|
||||
PUSH "\e[41m"
|
||||
?pen background = rgb(224,0,0)
|
||||
PUSH "\e[42m"
|
||||
?pen background = rgb(0,224,0)
|
||||
PUSH "\e[44m"
|
||||
?pen background = rgb(0,0,224)
|
||||
PUSH "\e[101m"
|
||||
?pen background = rgb(255,64,64)
|
||||
PUSH "\e[48:2:10:20:30m"
|
||||
?pen background = rgb(10,20,30)
|
||||
PUSH "\e[48:5:1m"
|
||||
?pen background = rgb(224,0,0)
|
||||
PUSH "\e[49m"
|
||||
?pen background = rgb(0,0,0)
|
||||
|
||||
!Bold+ANSI colour == highbright
|
||||
PUSH "\e[m\e[1;37m"
|
||||
?pen bold = on
|
||||
?pen foreground = rgb(255,255,255)
|
||||
PUSH "\e[m\e[37;1m"
|
||||
?pen bold = on
|
||||
?pen foreground = rgb(255,255,255)
|
69
src/libvterm/t/40screen_ascii.test
Normal file
69
src/libvterm/t/40screen_ascii.test
Normal file
@ -0,0 +1,69 @@
|
||||
INIT
|
||||
WANTSCREEN c
|
||||
|
||||
!Get
|
||||
RESET
|
||||
PUSH "ABC"
|
||||
movecursor 0,3
|
||||
?screen_chars 0,0,1,3 = 0x41,0x42,0x43
|
||||
?screen_chars 0,0,1,80 = 0x41,0x42,0x43
|
||||
?screen_text 0,0,1,3 = 0x41,0x42,0x43
|
||||
?screen_text 0,0,1,80 = 0x41,0x42,0x43
|
||||
?screen_cell 0,0 = {0x41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
?screen_cell 0,1 = {0x42} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
?screen_cell 0,2 = {0x43} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
?screen_row 0 = "ABC"
|
||||
?screen_eol 0,0 = 0
|
||||
?screen_eol 0,2 = 0
|
||||
?screen_eol 0,3 = 1
|
||||
PUSH "\e[H"
|
||||
movecursor 0,0
|
||||
?screen_chars 0,0,1,80 = 0x41,0x42,0x43
|
||||
?screen_text 0,0,1,80 = 0x41,0x42,0x43
|
||||
PUSH "E"
|
||||
movecursor 0,1
|
||||
?screen_chars 0,0,1,80 = 0x45,0x42,0x43
|
||||
?screen_text 0,0,1,80 = 0x45,0x42,0x43
|
||||
|
||||
WANTSCREEN -c
|
||||
|
||||
!Erase
|
||||
RESET
|
||||
PUSH "ABCDE\e[H\e[K"
|
||||
?screen_chars 0,0,1,80 =
|
||||
?screen_text 0,0,1,80 =
|
||||
|
||||
!Copycell
|
||||
RESET
|
||||
PUSH "ABC\e[H\e[@"
|
||||
PUSH "1"
|
||||
?screen_chars 0,0,1,80 = 0x31,0x41,0x42,0x43
|
||||
|
||||
RESET
|
||||
PUSH "ABC\e[H\e[P"
|
||||
?screen_chars 0,0,1,1 = 0x42
|
||||
?screen_chars 0,1,1,2 = 0x43
|
||||
?screen_chars 0,0,1,80 = 0x42,0x43
|
||||
|
||||
!Space padding
|
||||
RESET
|
||||
PUSH "Hello\e[CWorld"
|
||||
?screen_chars 0,0,1,80 = 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64
|
||||
?screen_text 0,0,1,80 = 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64
|
||||
|
||||
!Linefeed padding
|
||||
RESET
|
||||
PUSH "Hello\r\nWorld"
|
||||
?screen_chars 0,0,2,80 = 0x48,0x65,0x6c,0x6c,0x6f,0x0a,0x57,0x6f,0x72,0x6c,0x64
|
||||
?screen_text 0,0,2,80 = 0x48,0x65,0x6c,0x6c,0x6f,0x0a,0x57,0x6f,0x72,0x6c,0x64
|
||||
|
||||
!Altscreen
|
||||
RESET
|
||||
PUSH "P"
|
||||
?screen_chars 0,0,1,80 = 0x50
|
||||
PUSH "\e[?1049h"
|
||||
?screen_chars 0,0,1,80 =
|
||||
PUSH "\e[2K\e[HA"
|
||||
?screen_chars 0,0,1,80 = 0x41
|
||||
PUSH "\e[?1049l"
|
||||
?screen_chars 0,0,1,80 = 0x50
|
47
src/libvterm/t/41screen_unicode.test
Normal file
47
src/libvterm/t/41screen_unicode.test
Normal file
@ -0,0 +1,47 @@
|
||||
INIT
|
||||
UTF8 1
|
||||
WANTSCREEN
|
||||
|
||||
!Single width UTF-8
|
||||
# U+00C1 = 0xC3 0x81 name: LATIN CAPITAL LETTER A WITH ACUTE
|
||||
# U+00E9 = 0xC3 0xA9 name: LATIN SMALL LETTER E WITH ACUTE
|
||||
RESET
|
||||
PUSH "\xC3\x81\xC3\xA9"
|
||||
?screen_chars 0,0,1,80 = 0xc1,0xe9
|
||||
?screen_text 0,0,1,80 = 0xc3,0x81,0xc3,0xa9
|
||||
?screen_cell 0,0 = {0xc1} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Wide char
|
||||
# U+FF10 = 0xEF 0xBC 0x90 name: FULLWIDTH DIGIT ZERO
|
||||
RESET
|
||||
PUSH "0123\e[H"
|
||||
PUSH "\xEF\xBC\x90"
|
||||
?screen_chars 0,0,1,80 = 0xff10,0x32,0x33
|
||||
?screen_text 0,0,1,80 = 0xef,0xbc,0x90,0x32,0x33
|
||||
?screen_cell 0,0 = {0xff10} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Combining char
|
||||
# U+0301 = 0xCC 0x81 name: COMBINING ACUTE
|
||||
RESET
|
||||
PUSH "0123\e[H"
|
||||
PUSH "e\xCC\x81"
|
||||
?screen_chars 0,0,1,80 = 0x65,0x301,0x31,0x32,0x33
|
||||
?screen_text 0,0,1,80 = 0x65,0xcc,0x81,0x31,0x32,0x33
|
||||
?screen_cell 0,0 = {0x65,0x301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!10 combining accents should not crash
|
||||
RESET
|
||||
PUSH "e\xCC\x81\xCC\x82\xCC\x83\xCC\x84\xCC\x85\xCC\x86\xCC\x87\xCC\x88\xCC\x89\xCC\x8A"
|
||||
?screen_cell 0,0 = {0x65,0x301,0x302,0x303,0x304,0x305} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!40 combining accents in two split writes of 20 should not crash
|
||||
RESET
|
||||
PUSH "e\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81"
|
||||
PUSH "\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81\xCC\x81"
|
||||
?screen_cell 0,0 = {0x65,0x301,0x301,0x301,0x301,0x301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Outputing CJK doublewidth in 80th column should wraparound to next line and not crash"
|
||||
RESET
|
||||
PUSH "\e[80G\xEF\xBC\x90"
|
||||
?screen_cell 0,79 = {} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
?screen_cell 1,0 = {0xff10} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
155
src/libvterm/t/42screen_damage.test
Normal file
155
src/libvterm/t/42screen_damage.test
Normal file
@ -0,0 +1,155 @@
|
||||
INIT
|
||||
WANTSCREEN Db
|
||||
|
||||
!Putglyph
|
||||
RESET
|
||||
damage 0..25,0..80
|
||||
PUSH "123"
|
||||
damage 0..1,0..1 = 0<31>
|
||||
damage 0..1,1..2 = 0<32>
|
||||
damage 0..1,2..3 = 0<33>
|
||||
|
||||
!Erase
|
||||
PUSH "\e[H"
|
||||
PUSH "\e[3X"
|
||||
damage 0..1,0..3
|
||||
|
||||
!Scroll damages entire line in two chunks
|
||||
PUSH "\e[H\e[5@"
|
||||
damage 0..1,5..80
|
||||
damage 0..1,0..5
|
||||
|
||||
!Scroll down damages entire screen in two chunks
|
||||
PUSH "\e[T"
|
||||
damage 1..25,0..80
|
||||
damage 0..1,0..80
|
||||
|
||||
!Altscreen damages entire area
|
||||
PUSH "\e[?1049h"
|
||||
damage 0..25,0..80
|
||||
PUSH "\e[?1049l"
|
||||
damage 0..25,0..80
|
||||
|
||||
WANTSCREEN m
|
||||
|
||||
!Scroll invokes moverect but not damage
|
||||
PUSH "\e[5@"
|
||||
moverect 0..1,0..75 -> 0..1,5..80
|
||||
damage 0..1,0..5
|
||||
|
||||
WANTSCREEN -m
|
||||
|
||||
!Merge to cells
|
||||
RESET
|
||||
damage 0..25,0..80
|
||||
DAMAGEMERGE CELL
|
||||
|
||||
PUSH "A"
|
||||
damage 0..1,0..1 = 0<41>
|
||||
PUSH "B"
|
||||
damage 0..1,1..2 = 0<42>
|
||||
PUSH "C"
|
||||
damage 0..1,2..3 = 0<43>
|
||||
|
||||
!Merge entire rows
|
||||
RESET
|
||||
damage 0..25,0..80
|
||||
DAMAGEMERGE ROW
|
||||
|
||||
PUSH "ABCDE\r\nEFGH"
|
||||
damage 0..1,0..5 = 0<41 42 43 44 45>
|
||||
DAMAGEFLUSH
|
||||
damage 1..2,0..4 = 1<45 46 47 48>
|
||||
PUSH "\e[3;6r\e[6H\eD"
|
||||
damage 2..5,0..80
|
||||
DAMAGEFLUSH
|
||||
damage 5..6,0..80
|
||||
|
||||
!Merge entire screen
|
||||
RESET
|
||||
damage 0..25,0..80
|
||||
DAMAGEMERGE SCREEN
|
||||
|
||||
PUSH "ABCDE\r\nEFGH"
|
||||
DAMAGEFLUSH
|
||||
damage 0..2,0..5 = 0<41 42 43 44 45> 1<45 46 47 48>
|
||||
PUSH "\e[3;6r\e[6H\eD"
|
||||
DAMAGEFLUSH
|
||||
damage 2..6,0..80
|
||||
|
||||
!Merge entire screen with moverect
|
||||
WANTSCREEN m
|
||||
|
||||
RESET
|
||||
damage 0..25,0..80
|
||||
DAMAGEMERGE SCREEN
|
||||
|
||||
PUSH "ABCDE\r\nEFGH"
|
||||
PUSH "\e[3;6r\e[6H\eD"
|
||||
damage 0..2,0..5 = 0<41 42 43 44 45> 1<45 46 47 48>
|
||||
moverect 3..6,0..80 -> 2..5,0..80
|
||||
DAMAGEFLUSH
|
||||
damage 5..6,0..80
|
||||
|
||||
!Merge scroll
|
||||
RESET
|
||||
damage 0..25,0..80
|
||||
DAMAGEMERGE SCROLL
|
||||
|
||||
PUSH "\e[H1\r\n2\r\n3"
|
||||
PUSH "\e[25H\n\n\n"
|
||||
sb_pushline 80 = 31
|
||||
sb_pushline 80 = 32
|
||||
sb_pushline 80 = 33
|
||||
DAMAGEFLUSH
|
||||
moverect 3..25,0..80 -> 0..22,0..80
|
||||
damage 0..25,0..80
|
||||
|
||||
!Merge scroll with damage
|
||||
PUSH "\e[25H"
|
||||
PUSH "ABCDE\r\nEFGH\r\n"
|
||||
sb_pushline 80 =
|
||||
sb_pushline 80 =
|
||||
DAMAGEFLUSH
|
||||
moverect 2..25,0..80 -> 0..23,0..80
|
||||
damage 22..25,0..80 = 22<41 42 43 44 45> 23<45 46 47 48>
|
||||
|
||||
!Merge scroll with damage past region
|
||||
PUSH "\e[3;6r\e[6H1\r\n2\r\n3\r\n4\r\n5"
|
||||
DAMAGEFLUSH
|
||||
damage 2..6,0..80 = 2<32> 3<33> 4<34> 5<35>
|
||||
|
||||
!Damage entirely outside scroll region
|
||||
PUSH "\e[HABC\e[3;6r\e[6H\r\n6"
|
||||
damage 0..1,0..3 = 0<41 42 43>
|
||||
DAMAGEFLUSH
|
||||
moverect 3..6,0..80 -> 2..5,0..80
|
||||
damage 5..6,0..80 = 5<36>
|
||||
|
||||
!Damage overlapping scroll region
|
||||
PUSH "\e[H\e[2J"
|
||||
DAMAGEFLUSH
|
||||
damage 0..25,0..80
|
||||
|
||||
PUSH "\e[HABCD\r\nEFGH\r\nIJKL\e[2;5r\e[5H\r\nMNOP"
|
||||
DAMAGEFLUSH
|
||||
moverect 2..5,0..80 -> 1..4,0..80
|
||||
damage 0..5,0..80 = 0<41 42 43 44> 1<49 4A 4B 4C>
|
||||
## TODO: is this right?
|
||||
|
||||
!Merge scroll*2 with damage
|
||||
RESET
|
||||
damage 0..25,0..80
|
||||
DAMAGEMERGE SCROLL
|
||||
|
||||
PUSH "\e[25H\r\nABCDE\b\b\b\e[2P\r\n"
|
||||
sb_pushline 80 =
|
||||
moverect 1..25,0..80 -> 0..24,0..80
|
||||
damage 24..25,0..80 = 24<41 42 43 44 45>
|
||||
moverect 24..25,4..80 -> 24..25,2..78
|
||||
damage 24..25,78..80
|
||||
sb_pushline 80 =
|
||||
DAMAGEFLUSH
|
||||
moverect 1..25,0..80 -> 0..24,0..80
|
||||
damage 24..25,0..80
|
||||
?screen_chars 23,0,24,5 = 0x41,0x42,0x45
|
90
src/libvterm/t/43screen_resize.test
Normal file
90
src/libvterm/t/43screen_resize.test
Normal file
@ -0,0 +1,90 @@
|
||||
INIT
|
||||
WANTSTATE
|
||||
WANTSCREEN
|
||||
|
||||
!Resize wider preserves cells
|
||||
RESET
|
||||
RESIZE 25,80
|
||||
PUSH "AB\r\nCD"
|
||||
?screen_chars 0,0,1,80 = 0x41,0x42
|
||||
?screen_chars 1,0,2,80 = 0x43,0x44
|
||||
RESIZE 25,100
|
||||
?screen_chars 0,0,1,100 = 0x41,0x42
|
||||
?screen_chars 1,0,2,100 = 0x43,0x44
|
||||
|
||||
!Resize wider allows print in new area
|
||||
RESET
|
||||
RESIZE 25,80
|
||||
PUSH "AB\e[79GCD"
|
||||
?screen_chars 0,0,1,2 = 0x41,0x42
|
||||
?screen_chars 0,78,1,80 = 0x43,0x44
|
||||
RESIZE 25,100
|
||||
?screen_chars 0,0,1,2 = 0x41,0x42
|
||||
?screen_chars 0,78,1,80 = 0x43,0x44
|
||||
PUSH "E"
|
||||
?screen_chars 0,78,1,81 = 0x43,0x44,0x45
|
||||
|
||||
!Resize shorter with blanks just truncates
|
||||
RESET
|
||||
RESIZE 25,80
|
||||
PUSH "Top\e[10HLine 10"
|
||||
?screen_chars 0,0,1,80 = 0x54,0x6f,0x70
|
||||
?screen_chars 9,0,10,80 = 0x4c,0x69,0x6e,0x65,0x20,0x31,0x30
|
||||
?cursor = 9,7
|
||||
RESIZE 20,80
|
||||
?screen_chars 0,0,1,80 = 0x54,0x6f,0x70
|
||||
?screen_chars 9,0,10,80 = 0x4c,0x69,0x6e,0x65,0x20,0x31,0x30
|
||||
?cursor = 9,7
|
||||
|
||||
!Resize shorter with content must scroll
|
||||
RESET
|
||||
RESIZE 25,80
|
||||
PUSH "Top\e[25HLine 25\e[15H"
|
||||
?screen_chars 0,0,1,80 = 0x54,0x6f,0x70
|
||||
?screen_chars 24,0,25,80 = 0x4c,0x69,0x6e,0x65,0x20,0x32,0x35
|
||||
?cursor = 14,0
|
||||
WANTSCREEN b
|
||||
RESIZE 20,80
|
||||
sb_pushline 80 = 54 6F 70
|
||||
sb_pushline 80 =
|
||||
sb_pushline 80 =
|
||||
sb_pushline 80 =
|
||||
sb_pushline 80 =
|
||||
?screen_chars 0,0,1,80 =
|
||||
?screen_chars 19,0,20,80 = 0x4c,0x69,0x6e,0x65,0x20,0x32,0x35
|
||||
?cursor = 9,0
|
||||
|
||||
!Resize shorter does not lose line with cursor
|
||||
# See also https://github.com/neovim/libvterm/commit/1b745d29d45623aa8d22a7b9288c7b0e331c7088
|
||||
RESET
|
||||
WANTSCREEN -b
|
||||
RESIZE 25,80
|
||||
WANTSCREEN b
|
||||
PUSH "\e[24HLine 24\r\nLine 25\r\n"
|
||||
sb_pushline 80 =
|
||||
?screen_chars 23,0,24,10 = 0x4c,0x69,0x6e,0x65,0x20,0x32,0x35
|
||||
?cursor = 24,0
|
||||
RESIZE 24,80
|
||||
sb_pushline 80 =
|
||||
?screen_chars 22,0,23,10 = 0x4c,0x69,0x6e,0x65,0x20,0x32,0x35
|
||||
?cursor = 23,0
|
||||
|
||||
!Resize taller attempts to pop scrollback
|
||||
RESET
|
||||
WANTSCREEN -b
|
||||
RESIZE 25,80
|
||||
PUSH "Line 1\e[25HBottom\e[15H"
|
||||
?screen_chars 0,0,1,80 = 0x4c,0x69,0x6e,0x65,0x20,0x31
|
||||
?screen_chars 24,0,25,80 = 0x42,0x6f,0x74,0x74,0x6f,0x6d
|
||||
?cursor = 14,0
|
||||
WANTSCREEN b
|
||||
RESIZE 30,80
|
||||
sb_popline 80
|
||||
sb_popline 80
|
||||
sb_popline 80
|
||||
sb_popline 80
|
||||
sb_popline 80
|
||||
?screen_chars 0,0,1,80 = 0x41,0x42,0x43,0x44,0x45
|
||||
?screen_chars 5,0,6,80 = 0x4c,0x69,0x6e,0x65,0x20,0x31
|
||||
?screen_chars 29,0,30,80 = 0x42,0x6f,0x74,0x74,0x6f,0x6d
|
||||
?cursor = 19,0
|
55
src/libvterm/t/44screen_pen.test
Normal file
55
src/libvterm/t/44screen_pen.test
Normal file
@ -0,0 +1,55 @@
|
||||
INIT
|
||||
WANTSCREEN
|
||||
|
||||
RESET
|
||||
|
||||
!Plain
|
||||
PUSH "A"
|
||||
?screen_cell 0,0 = {0x41} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Bold
|
||||
PUSH "\e[1mB"
|
||||
?screen_cell 0,1 = {0x42} width=1 attrs={B} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Italic
|
||||
PUSH "\e[3mC"
|
||||
?screen_cell 0,2 = {0x43} width=1 attrs={BI} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Underline
|
||||
PUSH "\e[4mD"
|
||||
?screen_cell 0,3 = {0x44} width=1 attrs={BU1I} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Reset
|
||||
PUSH "\e[mE"
|
||||
?screen_cell 0,4 = {0x45} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Font
|
||||
PUSH "\e[11mF\e[m"
|
||||
?screen_cell 0,5 = {0x46} width=1 attrs={F1} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Foreground
|
||||
PUSH "\e[31mG\e[m"
|
||||
?screen_cell 0,6 = {0x47} width=1 attrs={} fg=rgb(224,0,0) bg=rgb(0,0,0)
|
||||
|
||||
!Background
|
||||
PUSH "\e[42mH\e[m"
|
||||
?screen_cell 0,7 = {0x48} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,224,0)
|
||||
|
||||
!EL sets reverse and colours to end of line
|
||||
PUSH "\e[H\e[7;33;44m\e[K"
|
||||
?screen_cell 0,0 = {} width=1 attrs={R} fg=rgb(224,224,0) bg=rgb(0,0,224)
|
||||
?screen_cell 0,79 = {} width=1 attrs={R} fg=rgb(224,224,0) bg=rgb(0,0,224)
|
||||
|
||||
!DECSCNM xors reverse for entire screen
|
||||
PUSH "\e[?5h"
|
||||
?screen_cell 0,0 = {} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)
|
||||
?screen_cell 0,79 = {} width=1 attrs={} fg=rgb(224,224,0) bg=rgb(0,0,224)
|
||||
?screen_cell 1,0 = {} width=1 attrs={R} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
PUSH "\e[?5\$p"
|
||||
output "\e[?5;1\$y"
|
||||
PUSH "\e[?5l"
|
||||
?screen_cell 0,0 = {} width=1 attrs={R} fg=rgb(224,224,0) bg=rgb(0,0,224)
|
||||
?screen_cell 0,79 = {} width=1 attrs={R} fg=rgb(224,224,0) bg=rgb(0,0,224)
|
||||
?screen_cell 1,0 = {} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
PUSH "\e[?5\$p"
|
||||
output "\e[?5;2\$y"
|
16
src/libvterm/t/45screen_protect.test
Normal file
16
src/libvterm/t/45screen_protect.test
Normal file
@ -0,0 +1,16 @@
|
||||
INIT
|
||||
WANTSCREEN
|
||||
|
||||
!Selective erase
|
||||
RESET
|
||||
PUSH "A\e[1\"qB\e[\"qC"
|
||||
?screen_chars 0,0,1,3 = 0x41,0x42,0x43
|
||||
PUSH "\e[G\e[?J"
|
||||
?screen_chars 0,0,1,3 = 0x20,0x42
|
||||
|
||||
!Non-selective erase
|
||||
RESET
|
||||
PUSH "A\e[1\"qB\e[\"qC"
|
||||
?screen_chars 0,0,1,3 = 0x41,0x42,0x43
|
||||
PUSH "\e[G\e[J"
|
||||
?screen_chars 0,0,1,3 =
|
11
src/libvterm/t/46screen_extent.test
Normal file
11
src/libvterm/t/46screen_extent.test
Normal file
@ -0,0 +1,11 @@
|
||||
INIT
|
||||
WANTSCREEN
|
||||
|
||||
!Bold extent
|
||||
RESET
|
||||
PUSH "AB\e[1mCD\e[mE"
|
||||
?screen_attrs_extent 0,0 = 0,0-1,1
|
||||
?screen_attrs_extent 0,1 = 0,0-1,1
|
||||
?screen_attrs_extent 0,2 = 0,2-1,3
|
||||
?screen_attrs_extent 0,3 = 0,2-1,3
|
||||
?screen_attrs_extent 0,4 = 0,4-1,79
|
32
src/libvterm/t/47screen_dbl_wh.test
Normal file
32
src/libvterm/t/47screen_dbl_wh.test
Normal file
@ -0,0 +1,32 @@
|
||||
INIT
|
||||
WANTSCREEN
|
||||
|
||||
RESET
|
||||
|
||||
!Single Width, Single Height
|
||||
RESET
|
||||
PUSH "\e#5"
|
||||
PUSH "abcde"
|
||||
?screen_cell 0,0 = {0x61} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Double Width, Single Height
|
||||
RESET
|
||||
PUSH "\e#6"
|
||||
PUSH "abcde"
|
||||
?screen_cell 0,0 = {0x61} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Double Height
|
||||
RESET
|
||||
PUSH "\e#3"
|
||||
PUSH "abcde"
|
||||
PUSH "\r\n\e#4"
|
||||
PUSH "abcde"
|
||||
?screen_cell 0,0 = {0x61} width=1 attrs={} dwl dhl-top fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
?screen_cell 1,0 = {0x61} width=1 attrs={} dwl dhl-bottom fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
|
||||
!Late change
|
||||
RESET
|
||||
PUSH "abcde"
|
||||
?screen_cell 0,0 = {0x61} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
|
||||
PUSH "\e#6"
|
||||
?screen_cell 0,0 = {0x61} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0)
|
17
src/libvterm/t/48screen_termprops.test
Normal file
17
src/libvterm/t/48screen_termprops.test
Normal file
@ -0,0 +1,17 @@
|
||||
INIT
|
||||
WANTSCREEN p
|
||||
|
||||
RESET
|
||||
settermprop 1 true
|
||||
settermprop 2 true
|
||||
settermprop 7 1
|
||||
|
||||
!Cursor visibility
|
||||
PUSH "\e[?25h"
|
||||
settermprop 1 true
|
||||
PUSH "\e[?25l"
|
||||
settermprop 1 false
|
||||
|
||||
!Title
|
||||
PUSH "\e]2;Here is my title\a"
|
||||
settermprop 4 "Here is my title"
|
87
src/libvterm/t/90vttest_01-movement-1.test
Normal file
87
src/libvterm/t/90vttest_01-movement-1.test
Normal file
@ -0,0 +1,87 @@
|
||||
INIT
|
||||
WANTSTATE
|
||||
WANTSCREEN
|
||||
|
||||
RESET
|
||||
|
||||
PUSH "\e#8"
|
||||
|
||||
PUSH "\e[9;10H\e[1J"
|
||||
PUSH "\e[18;60H\e[0J\e[1K"
|
||||
PUSH "\e[9;71H\e[0K"
|
||||
|
||||
$SEQ 10 16: PUSH "\e[\#;10H\e[1K\e[\#;71H\e[0K"
|
||||
|
||||
PUSH "\e[17;30H\e[2K"
|
||||
|
||||
$SEQ 1 80: PUSH "\e[24;\#f*\e[1;\#f*"
|
||||
|
||||
PUSH "\e[2;2H"
|
||||
|
||||
$REP 22: PUSH "+\e[1D\eD"
|
||||
|
||||
PUSH "\e[23;79H"
|
||||
$REP 22: PUSH "+\e[1D\eM"
|
||||
|
||||
PUSH "\e[2;1H"
|
||||
$SEQ 2 23: PUSH "*\e[\#;80H*\e[10D\eE"
|
||||
|
||||
PUSH "\e[2;10H\e[42D\e[2C"
|
||||
$REP 76: PUSH "+\e[0C\e[2D\e[1C"
|
||||
|
||||
PUSH "\e[23;70H\e[42C\e[2D"
|
||||
|
||||
$REP 76: PUSH "+\e[1D\e[1C\e[0D\b"
|
||||
|
||||
PUSH "\e[1;1H"
|
||||
PUSH "\e[10A"
|
||||
PUSH "\e[1A"
|
||||
PUSH "\e[0A"
|
||||
PUSH "\e[24;80H"
|
||||
PUSH "\e[10B"
|
||||
PUSH "\e[1B"
|
||||
PUSH "\e[0B"
|
||||
PUSH "\e[10;12H"
|
||||
|
||||
$REP 58: PUSH " "
|
||||
PUSH "\e[1B\e[58D"
|
||||
|
||||
$REP 58: PUSH " "
|
||||
PUSH "\e[1B\e[58D"
|
||||
|
||||
$REP 58: PUSH " "
|
||||
PUSH "\e[1B\e[58D"
|
||||
|
||||
$REP 58: PUSH " "
|
||||
PUSH "\e[1B\e[58D"
|
||||
|
||||
$REP 58: PUSH " "
|
||||
PUSH "\e[1B\e[58D"
|
||||
|
||||
$REP 58: PUSH " "
|
||||
PUSH "\e[1B\e[58D"
|
||||
|
||||
PUSH "\e[5A\e[1CThe screen should be cleared, and have an unbroken bor-"
|
||||
PUSH "\e[12;13Hder of *'s and +'s around the edge, and exactly in the"
|
||||
PUSH "\e[13;13Hmiddle there should be a frame of E's around this text"
|
||||
PUSH "\e[14;13Hwith one (1) free position around it. Push <RETURN>"
|
||||
|
||||
# And the result is...
|
||||
|
||||
!Output
|
||||
?screen_row 0 = "********************************************************************************"
|
||||
?screen_row 1 = "*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*"
|
||||
$SEQ 2 7: ?screen_row \# = "*+ +*"
|
||||
?screen_row 8 = "*+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*"
|
||||
?screen_row 9 = "*+ E E +*"
|
||||
?screen_row 10 = "*+ E The screen should be cleared, and have an unbroken bor- E +*"
|
||||
?screen_row 11 = "*+ E der of *'s and +'s around the edge, and exactly in the E +*"
|
||||
?screen_row 12 = "*+ E middle there should be a frame of E's around this text E +*"
|
||||
?screen_row 13 = "*+ E with one (1) free position around it. Push <RETURN> E +*"
|
||||
?screen_row 14 = "*+ E E +*"
|
||||
?screen_row 15 = "*+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*"
|
||||
$SEQ 16 21: ?screen_row \# = "*+ +*"
|
||||
?screen_row 22 = "*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*"
|
||||
?screen_row 23 = "********************************************************************************"
|
||||
|
||||
?cursor = 13,67
|
40
src/libvterm/t/90vttest_01-movement-2.test
Normal file
40
src/libvterm/t/90vttest_01-movement-2.test
Normal file
@ -0,0 +1,40 @@
|
||||
INIT
|
||||
WANTSTATE
|
||||
WANTSCREEN
|
||||
|
||||
RESET
|
||||
|
||||
PUSH "\e[3;21r"
|
||||
PUSH "\e[?6h"
|
||||
|
||||
PUSH "\e[19;1HA\e[19;80Ha\x0a\e[18;80HaB\e[19;80HB\b b\x0a\e[19;80HC\b\b\t\tc\e[19;2H\bC\x0a\e[19;80H\x0a\e[18;1HD\e[18;80Hd"
|
||||
PUSH "\e[19;1HE\e[19;80He\x0a\e[18;80HeF\e[19;80HF\b f\x0a\e[19;80HG\b\b\t\tg\e[19;2H\bG\x0a\e[19;80H\x0a\e[18;1HH\e[18;80Hh"
|
||||
PUSH "\e[19;1HI\e[19;80Hi\x0a\e[18;80HiJ\e[19;80HJ\b j\x0a\e[19;80HK\b\b\t\tk\e[19;2H\bK\x0a\e[19;80H\x0a\e[18;1HL\e[18;80Hl"
|
||||
PUSH "\e[19;1HM\e[19;80Hm\x0a\e[18;80HmN\e[19;80HN\b n\x0a\e[19;80HO\b\b\t\to\e[19;2H\bO\x0a\e[19;80H\x0a\e[18;1HP\e[18;80Hp"
|
||||
PUSH "\e[19;1HQ\e[19;80Hq\x0a\e[18;80HqR\e[19;80HR\b r\x0a\e[19;80HS\b\b\t\ts\e[19;2H\bS\x0a\e[19;80H\x0a\e[18;1HT\e[18;80Ht"
|
||||
PUSH "\e[19;1HU\e[19;80Hu\x0a\e[18;80HuV\e[19;80HV\b v\x0a\e[19;80HW\b\b\t\tw\e[19;2H\bW\x0a\e[19;80H\x0a\e[18;1HX\e[18;80Hx"
|
||||
PUSH "\e[19;1HY\e[19;80Hy\x0a\e[18;80HyZ\e[19;80HZ\b z\x0a"
|
||||
|
||||
!Output
|
||||
|
||||
?screen_row 2 = "I i"
|
||||
?screen_row 3 = "J j"
|
||||
?screen_row 4 = "K k"
|
||||
?screen_row 5 = "L l"
|
||||
?screen_row 6 = "M m"
|
||||
?screen_row 7 = "N n"
|
||||
?screen_row 8 = "O o"
|
||||
?screen_row 9 = "P p"
|
||||
?screen_row 10 = "Q q"
|
||||
?screen_row 11 = "R r"
|
||||
?screen_row 12 = "S s"
|
||||
?screen_row 13 = "T t"
|
||||
?screen_row 14 = "U u"
|
||||
?screen_row 15 = "V v"
|
||||
?screen_row 16 = "W w"
|
||||
?screen_row 17 = "X x"
|
||||
?screen_row 18 = "Y y"
|
||||
?screen_row 19 = "Z z"
|
||||
?screen_row 20 = ""
|
||||
|
||||
?cursor = 20,79
|
21
src/libvterm/t/90vttest_01-movement-3.test
Normal file
21
src/libvterm/t/90vttest_01-movement-3.test
Normal file
@ -0,0 +1,21 @@
|
||||
# Test of cursor-control characters inside ESC sequences
|
||||
INIT
|
||||
WANTSTATE
|
||||
WANTSCREEN
|
||||
|
||||
RESET
|
||||
|
||||
PUSH "A B C D E F G H I"
|
||||
PUSH "\x0d\x0a"
|
||||
PUSH "A\e[2\bCB\e[2\bCC\e[2\bCD\e[2\bCE\e[2\bCF\e[2\bCG\e[2\bCH\e[2\bCI"
|
||||
PUSH "\x0d\x0a"
|
||||
PUSH "A \e[\x0d2CB\e[\x0d4CC\e[\x0d6CD\e[\x0d8CE\e[\x0d10CF\e[\x0d12CG\e[\x0d14CH\e[\x0d16CI"
|
||||
PUSH "\x0d\x0a"
|
||||
PUSH "A \e[1\x0bAB \e[1\x0bAC \e[1\x0bAD \e[1\x0bAE \e[1\x0bAF \e[1\x0bAG \e[1\x0bAH \e[1\x0bAI \e[1\x0bA"
|
||||
|
||||
!Output
|
||||
|
||||
$SEQ 0 2: ?screen_row \# = "A B C D E F G H I"
|
||||
?screen_row 3 = "A B C D E F G H I "
|
||||
|
||||
?cursor = 3,18
|
36
src/libvterm/t/90vttest_01-movement-4.test
Normal file
36
src/libvterm/t/90vttest_01-movement-4.test
Normal file
@ -0,0 +1,36 @@
|
||||
# Test of leading zeroes in ESC sequences
|
||||
INIT
|
||||
WANTSCREEN
|
||||
|
||||
RESET
|
||||
|
||||
PUSH "\e[00000000004;000000001HT"
|
||||
PUSH "\e[00000000004;000000002Hh"
|
||||
PUSH "\e[00000000004;000000003Hi"
|
||||
PUSH "\e[00000000004;000000004Hs"
|
||||
PUSH "\e[00000000004;000000005H "
|
||||
PUSH "\e[00000000004;000000006Hi"
|
||||
PUSH "\e[00000000004;000000007Hs"
|
||||
PUSH "\e[00000000004;000000008H "
|
||||
PUSH "\e[00000000004;000000009Ha"
|
||||
PUSH "\e[00000000004;0000000010H "
|
||||
PUSH "\e[00000000004;0000000011Hc"
|
||||
PUSH "\e[00000000004;0000000012Ho"
|
||||
PUSH "\e[00000000004;0000000013Hr"
|
||||
PUSH "\e[00000000004;0000000014Hr"
|
||||
PUSH "\e[00000000004;0000000015He"
|
||||
PUSH "\e[00000000004;0000000016Hc"
|
||||
PUSH "\e[00000000004;0000000017Ht"
|
||||
PUSH "\e[00000000004;0000000018H "
|
||||
PUSH "\e[00000000004;0000000019Hs"
|
||||
PUSH "\e[00000000004;0000000020He"
|
||||
PUSH "\e[00000000004;0000000021Hn"
|
||||
PUSH "\e[00000000004;0000000022Ht"
|
||||
PUSH "\e[00000000004;0000000023He"
|
||||
PUSH "\e[00000000004;0000000024Hn"
|
||||
PUSH "\e[00000000004;0000000025Hc"
|
||||
PUSH "\e[00000000004;0000000026He"
|
||||
|
||||
!Output
|
||||
|
||||
?screen_row 3 = "This is a correct sentence"
|
18
src/libvterm/t/90vttest_02-screen-1.test
Normal file
18
src/libvterm/t/90vttest_02-screen-1.test
Normal file
@ -0,0 +1,18 @@
|
||||
# Test of WRAP AROUND mode setting.
|
||||
INIT
|
||||
WANTSCREEN
|
||||
|
||||
RESET
|
||||
|
||||
PUSH "\e[?7h"
|
||||
$REP 170: PUSH "*"
|
||||
|
||||
PUSH "\e[?7l\e[3;1H"
|
||||
$REP 177: PUSH "*"
|
||||
|
||||
PUSH "\e[?7h\e[5;1HOK"
|
||||
|
||||
!Output
|
||||
$SEQ 0 2: ?screen_row \# = "********************************************************************************"
|
||||
?screen_row 3 = ""
|
||||
?screen_row 4 = "OK"
|
29
src/libvterm/t/90vttest_02-screen-2.test
Normal file
29
src/libvterm/t/90vttest_02-screen-2.test
Normal file
@ -0,0 +1,29 @@
|
||||
# TAB setting/resetting
|
||||
INIT
|
||||
WANTSTATE
|
||||
WANTSCREEN
|
||||
|
||||
RESET
|
||||
|
||||
PUSH "\e[2J\e[3g"
|
||||
|
||||
PUSH "\e[1;1H"
|
||||
$REP 26: PUSH "\e[3C\eH"
|
||||
|
||||
PUSH "\e[1;4H"
|
||||
$REP 13: PUSH "\e[0g\e[6C"
|
||||
|
||||
PUSH "\e[1;7H"
|
||||
PUSH "\e[1g\e[2g"
|
||||
|
||||
PUSH "\e[1;1H"
|
||||
$REP 13: PUSH "\t*"
|
||||
|
||||
PUSH "\e[2;2H"
|
||||
$REP 13: PUSH " *"
|
||||
|
||||
!Output
|
||||
?screen_row 0 = " * * * * * * * * * * * * *"
|
||||
?screen_row 1 = " * * * * * * * * * * * * *"
|
||||
|
||||
?cursor = 1,79
|
16
src/libvterm/t/90vttest_02-screen-3.test
Normal file
16
src/libvterm/t/90vttest_02-screen-3.test
Normal file
@ -0,0 +1,16 @@
|
||||
# Origin mode
|
||||
INIT
|
||||
WANTSCREEN
|
||||
|
||||
RESET
|
||||
|
||||
PUSH "\e[?6h"
|
||||
PUSH "\e[23;24r"
|
||||
PUSH "\n"
|
||||
PUSH "Bottom"
|
||||
PUSH "\e[1;1H"
|
||||
PUSH "Above"
|
||||
|
||||
!Output
|
||||
?screen_row 22 = "Above"
|
||||
?screen_row 23 = "Bottom"
|
17
src/libvterm/t/90vttest_02-screen-4.test
Normal file
17
src/libvterm/t/90vttest_02-screen-4.test
Normal file
@ -0,0 +1,17 @@
|
||||
# Origin mode (2)
|
||||
INIT
|
||||
WANTSCREEN
|
||||
|
||||
RESET
|
||||
|
||||
PUSH "\e[?6l"
|
||||
PUSH "\e[23;24r"
|
||||
PUSH "\e[24;1H"
|
||||
PUSH "Bottom"
|
||||
PUSH "\e[1;1H"
|
||||
PUSH "Top"
|
||||
|
||||
!Output
|
||||
?screen_row 23 = "Bottom"
|
||||
?screen_row 0 = "Top"
|
||||
|
13
src/libvterm/t/92lp1640917.test
Normal file
13
src/libvterm/t/92lp1640917.test
Normal file
@ -0,0 +1,13 @@
|
||||
INIT
|
||||
WANTSTATE
|
||||
|
||||
!Mouse reporting should not break by idempotent DECSM 1002
|
||||
PUSH "\e[?1002h"
|
||||
MOUSEMOVE 0,0 0
|
||||
MOUSEBTN d 1 0
|
||||
output "\e[M\x20\x21\x21"
|
||||
MOUSEMOVE 1,0 0
|
||||
output "\e[M\x40\x21\x22"
|
||||
PUSH "\e[?1002h"
|
||||
MOUSEMOVE 2,0 0
|
||||
output "\e[M\x40\x21\x23"
|
929
src/libvterm/t/harness.c
Normal file
929
src/libvterm/t/harness.c
Normal file
@ -0,0 +1,929 @@
|
||||
#include "vterm.h"
|
||||
#include "../src/vterm_internal.h" /* We pull in some internal bits too */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define streq(a,b) (!strcmp(a,b))
|
||||
#define strstartswith(a,b) (!strncmp(a,b,strlen(b)))
|
||||
|
||||
static size_t inplace_hex2bytes(char *s)
|
||||
{
|
||||
char *inpos = s, *outpos = s;
|
||||
|
||||
while(*inpos) {
|
||||
unsigned int ch;
|
||||
sscanf(inpos, "%2x", &ch);
|
||||
*outpos = ch;
|
||||
outpos += 1; inpos += 2;
|
||||
}
|
||||
|
||||
return outpos - s;
|
||||
}
|
||||
|
||||
static VTermModifier strpe_modifiers(char **strp)
|
||||
{
|
||||
VTermModifier state = 0;
|
||||
|
||||
while((*strp)[0]) {
|
||||
switch(((*strp)++)[0]) {
|
||||
case 'S': state |= VTERM_MOD_SHIFT; break;
|
||||
case 'C': state |= VTERM_MOD_CTRL; break;
|
||||
case 'A': state |= VTERM_MOD_ALT; break;
|
||||
default: return state;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static VTermKey strp_key(char *str)
|
||||
{
|
||||
static struct {
|
||||
char *name;
|
||||
VTermKey key;
|
||||
} keys[] = {
|
||||
{ "Up", VTERM_KEY_UP },
|
||||
{ "Tab", VTERM_KEY_TAB },
|
||||
{ "Enter", VTERM_KEY_ENTER },
|
||||
{ "KP0", VTERM_KEY_KP_0 },
|
||||
{ NULL, VTERM_KEY_NONE },
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i = 0; keys[i].name; i++) {
|
||||
if(streq(str, keys[i].name))
|
||||
return keys[i].key;
|
||||
}
|
||||
|
||||
return VTERM_KEY_NONE;
|
||||
}
|
||||
|
||||
static VTerm *vt;
|
||||
static VTermState *state;
|
||||
static VTermScreen *screen;
|
||||
|
||||
static VTermEncodingInstance encoding;
|
||||
|
||||
static int parser_text(const char bytes[], size_t len, void *user)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("text ");
|
||||
for(i = 0; i < len; i++) {
|
||||
unsigned char b = bytes[i];
|
||||
if(b < 0x20 || b == 0x7f || (b >= 0x80 && b < 0xa0))
|
||||
break;
|
||||
printf(i ? ",%x" : "%x", b);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int parser_control(unsigned char control, void *user)
|
||||
{
|
||||
printf("control %02x\n", control);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parser_escape(const char bytes[], size_t len, void *user)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(bytes[0] >= 0x20 && bytes[0] < 0x30) {
|
||||
if(len < 2)
|
||||
return -1;
|
||||
len = 2;
|
||||
}
|
||||
else {
|
||||
len = 1;
|
||||
}
|
||||
|
||||
printf("escape ");
|
||||
for(i = 0; i < len; i++)
|
||||
printf("%02x", bytes[i]);
|
||||
printf("\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
|
||||
{
|
||||
int i;
|
||||
printf("csi %02x", command);
|
||||
|
||||
if(leader && leader[0]) {
|
||||
printf(" L=");
|
||||
for(i = 0; leader[i]; i++)
|
||||
printf("%02x", leader[i]);
|
||||
}
|
||||
|
||||
for(i = 0; i < argcount; i++) {
|
||||
char sep = i ? ',' : ' ';
|
||||
|
||||
if(args[i] == CSI_ARG_MISSING)
|
||||
printf("%c*", sep);
|
||||
else
|
||||
printf("%c%ld%s", sep, CSI_ARG(args[i]), CSI_ARG_HAS_MORE(args[i]) ? "+" : "");
|
||||
}
|
||||
|
||||
if(intermed && intermed[0]) {
|
||||
printf(" I=");
|
||||
for(i = 0; intermed[i]; i++)
|
||||
printf("%02x", intermed[i]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parser_osc(const char *command, size_t cmdlen, void *user)
|
||||
{
|
||||
int i;
|
||||
printf("osc ");
|
||||
for(i = 0; i < cmdlen; i++)
|
||||
printf("%02x", command[i]);
|
||||
printf("\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parser_dcs(const char *command, size_t cmdlen, void *user)
|
||||
{
|
||||
int i;
|
||||
printf("dcs ");
|
||||
for(i = 0; i < cmdlen; i++)
|
||||
printf("%02x", command[i]);
|
||||
printf("\n");
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
/* These callbacks are shared by State and Screen */
|
||||
|
||||
static int want_movecursor = 0;
|
||||
static VTermPos state_pos;
|
||||
static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
|
||||
{
|
||||
state_pos = pos;
|
||||
|
||||
if(want_movecursor)
|
||||
printf("movecursor %d,%d\n", pos.row, pos.col);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int want_scrollrect = 0;
|
||||
static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
|
||||
{
|
||||
if(!want_scrollrect)
|
||||
return 0;
|
||||
|
||||
printf("scrollrect %d..%d,%d..%d => %+d,%+d\n",
|
||||
rect.start_row, rect.end_row, rect.start_col, rect.end_col,
|
||||
downward, rightward);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int want_moverect = 0;
|
||||
static int moverect(VTermRect dest, VTermRect src, void *user)
|
||||
{
|
||||
if(!want_moverect)
|
||||
return 0;
|
||||
|
||||
printf("moverect %d..%d,%d..%d -> %d..%d,%d..%d\n",
|
||||
src.start_row, src.end_row, src.start_col, src.end_col,
|
||||
dest.start_row, dest.end_row, dest.start_col, dest.end_col);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int want_settermprop = 0;
|
||||
static int settermprop(VTermProp prop, VTermValue *val, void *user)
|
||||
{
|
||||
VTermValueType type;
|
||||
if(!want_settermprop)
|
||||
return 1;
|
||||
|
||||
type = vterm_get_prop_type(prop);
|
||||
switch(type) {
|
||||
case VTERM_VALUETYPE_BOOL:
|
||||
printf("settermprop %d %s\n", prop, val->boolean ? "true" : "false");
|
||||
return 1;
|
||||
case VTERM_VALUETYPE_INT:
|
||||
printf("settermprop %d %d\n", prop, val->number);
|
||||
return 1;
|
||||
case VTERM_VALUETYPE_STRING:
|
||||
printf("settermprop %d \"%s\"\n", prop, val->string);
|
||||
return 1;
|
||||
case VTERM_VALUETYPE_COLOR:
|
||||
printf("settermprop %d rgb(%d,%d,%d)\n", prop, val->color.red, val->color.green, val->color.blue);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* These callbacks are for State */
|
||||
|
||||
static int want_state_putglyph = 0;
|
||||
static int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
|
||||
{
|
||||
int i;
|
||||
if(!want_state_putglyph)
|
||||
return 1;
|
||||
|
||||
printf("putglyph ");
|
||||
for(i = 0; info->chars[i]; i++)
|
||||
printf(i ? ",%x" : "%x", info->chars[i]);
|
||||
printf(" %d %d,%d", info->width, pos.row, pos.col);
|
||||
if(info->protected_cell)
|
||||
printf(" prot");
|
||||
if(info->dwl)
|
||||
printf(" dwl");
|
||||
if(info->dhl)
|
||||
printf(" dhl-%s", info->dhl == 1 ? "top" : info->dhl == 2 ? "bottom" : "?" );
|
||||
printf("\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int want_state_erase = 0;
|
||||
static int state_erase(VTermRect rect, int selective, void *user)
|
||||
{
|
||||
if(!want_state_erase)
|
||||
return 1;
|
||||
|
||||
printf("erase %d..%d,%d..%d%s\n",
|
||||
rect.start_row, rect.end_row, rect.start_col, rect.end_col,
|
||||
selective ? " selective" : "");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct {
|
||||
int bold;
|
||||
int underline;
|
||||
int italic;
|
||||
int blink;
|
||||
int reverse;
|
||||
int strike;
|
||||
int font;
|
||||
VTermColor foreground;
|
||||
VTermColor background;
|
||||
} state_pen;
|
||||
static int state_setpenattr(VTermAttr attr, VTermValue *val, void *user)
|
||||
{
|
||||
switch(attr) {
|
||||
case VTERM_ATTR_BOLD:
|
||||
state_pen.bold = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_UNDERLINE:
|
||||
state_pen.underline = val->number;
|
||||
break;
|
||||
case VTERM_ATTR_ITALIC:
|
||||
state_pen.italic = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_BLINK:
|
||||
state_pen.blink = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_REVERSE:
|
||||
state_pen.reverse = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_STRIKE:
|
||||
state_pen.strike = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_FONT:
|
||||
state_pen.font = val->number;
|
||||
break;
|
||||
case VTERM_ATTR_FOREGROUND:
|
||||
state_pen.foreground = val->color;
|
||||
break;
|
||||
case VTERM_ATTR_BACKGROUND:
|
||||
state_pen.background = val->color;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int state_setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
VTermStateCallbacks state_cbs = {
|
||||
state_putglyph, /* putglyph */
|
||||
movecursor, /* movecursor */
|
||||
scrollrect, /* scrollrect */
|
||||
moverect, /* moverect */
|
||||
state_erase, /* erase */
|
||||
NULL, /* initpen */
|
||||
state_setpenattr, /* setpenattr */
|
||||
settermprop, /* settermprop */
|
||||
NULL, /* bell */
|
||||
NULL, /* resize */
|
||||
state_setlineinfo, /* setlineinfo */
|
||||
};
|
||||
|
||||
static int want_screen_damage = 0;
|
||||
static int want_screen_damage_cells = 0;
|
||||
static int screen_damage(VTermRect rect, void *user)
|
||||
{
|
||||
if(!want_screen_damage)
|
||||
return 1;
|
||||
|
||||
printf("damage %d..%d,%d..%d",
|
||||
rect.start_row, rect.end_row, rect.start_col, rect.end_col);
|
||||
|
||||
if(want_screen_damage_cells) {
|
||||
bool equals = false;
|
||||
int row;
|
||||
int col;
|
||||
|
||||
for(row = rect.start_row; row < rect.end_row; row++) {
|
||||
int eol = rect.end_col;
|
||||
while(eol > rect.start_col) {
|
||||
VTermScreenCell cell;
|
||||
VTermPos pos;
|
||||
pos.row = row;
|
||||
pos.col = eol-1;
|
||||
vterm_screen_get_cell(screen, pos, &cell);
|
||||
if(cell.chars[0])
|
||||
break;
|
||||
|
||||
eol--;
|
||||
}
|
||||
|
||||
if(eol == rect.start_col)
|
||||
break;
|
||||
|
||||
if(!equals)
|
||||
printf(" ="), equals = true;
|
||||
|
||||
printf(" %d<", row);
|
||||
for(col = rect.start_col; col < eol; col++) {
|
||||
VTermScreenCell cell;
|
||||
VTermPos pos;
|
||||
pos.row = row;
|
||||
pos.col = col;
|
||||
vterm_screen_get_cell(screen, pos, &cell);
|
||||
printf(col == rect.start_col ? "%02X" : " %02X", cell.chars[0]);
|
||||
}
|
||||
printf(">");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int want_screen_scrollback = 0;
|
||||
static int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user)
|
||||
{
|
||||
int eol;
|
||||
int c;
|
||||
|
||||
if(!want_screen_scrollback)
|
||||
return 1;
|
||||
|
||||
eol = cols;
|
||||
while(eol && !cells[eol-1].chars[0])
|
||||
eol--;
|
||||
|
||||
printf("sb_pushline %d =", cols);
|
||||
for(c = 0; c < eol; c++)
|
||||
printf(" %02X", cells[c].chars[0]);
|
||||
printf("\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int screen_sb_popline(int cols, VTermScreenCell *cells, void *user)
|
||||
{
|
||||
int col;
|
||||
|
||||
if(!want_screen_scrollback)
|
||||
return 0;
|
||||
|
||||
/* All lines of scrollback contain "ABCDE" */
|
||||
for(col = 0; col < cols; col++) {
|
||||
if(col < 5)
|
||||
cells[col].chars[0] = 'A' + col;
|
||||
else
|
||||
cells[col].chars[0] = 0;
|
||||
|
||||
cells[col].width = 1;
|
||||
}
|
||||
|
||||
printf("sb_popline %d\n", cols);
|
||||
return 1;
|
||||
}
|
||||
|
||||
VTermScreenCallbacks screen_cbs = {
|
||||
screen_damage, /* damage */
|
||||
moverect, /* moverect */
|
||||
movecursor, /* movecursor */
|
||||
settermprop, /* settermprop */
|
||||
NULL, /* bell */
|
||||
NULL, /* resize */
|
||||
screen_sb_pushline, /* sb_pushline */
|
||||
screen_sb_popline /* sb_popline */
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char line[1024] = {0};
|
||||
int flag;
|
||||
|
||||
int err;
|
||||
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
while(fgets(line, sizeof line, stdin)) {
|
||||
char *nl;
|
||||
size_t outlen;
|
||||
err = 0;
|
||||
|
||||
if((nl = strchr(line, '\n')))
|
||||
*nl = '\0';
|
||||
|
||||
if(streq(line, "INIT")) {
|
||||
if(!vt)
|
||||
vt = vterm_new(25, 80);
|
||||
}
|
||||
|
||||
else if(streq(line, "WANTPARSER")) {
|
||||
vterm_parser_set_callbacks(vt, &parser_cbs, NULL);
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "WANTSTATE") && (line[9] == '\0' || line[9] == ' ')) {
|
||||
int i = 9;
|
||||
int sense = 1;
|
||||
if(!state) {
|
||||
state = vterm_obtain_state(vt);
|
||||
vterm_state_set_callbacks(state, &state_cbs, NULL);
|
||||
vterm_state_set_bold_highbright(state, 1);
|
||||
vterm_state_reset(state, 1);
|
||||
}
|
||||
|
||||
while(line[i] == ' ')
|
||||
i++;
|
||||
for( ; line[i]; i++)
|
||||
switch(line[i]) {
|
||||
case '+':
|
||||
sense = 1;
|
||||
break;
|
||||
case '-':
|
||||
sense = 0;
|
||||
break;
|
||||
case 'g':
|
||||
want_state_putglyph = sense;
|
||||
break;
|
||||
case 's':
|
||||
want_scrollrect = sense;
|
||||
break;
|
||||
case 'm':
|
||||
want_moverect = sense;
|
||||
break;
|
||||
case 'e':
|
||||
want_state_erase = sense;
|
||||
break;
|
||||
case 'p':
|
||||
want_settermprop = sense;
|
||||
break;
|
||||
case 'f':
|
||||
vterm_state_set_unrecognised_fallbacks(state, sense ? &parser_cbs : NULL, NULL);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unrecognised WANTSTATE flag '%c'\n", line[i]);
|
||||
}
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "WANTSCREEN") && (line[10] == '\0' || line[10] == ' ')) {
|
||||
int i = 10;
|
||||
int sense = 1;
|
||||
if(!screen)
|
||||
screen = vterm_obtain_screen(vt);
|
||||
vterm_screen_enable_altscreen(screen, 1);
|
||||
vterm_screen_set_callbacks(screen, &screen_cbs, NULL);
|
||||
|
||||
while(line[i] == ' ')
|
||||
i++;
|
||||
for( ; line[i]; i++)
|
||||
switch(line[i]) {
|
||||
case '-':
|
||||
sense = 0;
|
||||
break;
|
||||
case 'd':
|
||||
want_screen_damage = sense;
|
||||
break;
|
||||
case 'D':
|
||||
want_screen_damage = sense;
|
||||
want_screen_damage_cells = sense;
|
||||
break;
|
||||
case 'm':
|
||||
want_moverect = sense;
|
||||
break;
|
||||
case 'c':
|
||||
want_movecursor = sense;
|
||||
break;
|
||||
case 'p':
|
||||
want_settermprop = 1;
|
||||
break;
|
||||
case 'b':
|
||||
want_screen_scrollback = sense;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unrecognised WANTSCREEN flag '%c'\n", line[i]);
|
||||
}
|
||||
}
|
||||
|
||||
else if(sscanf(line, "UTF8 %d", &flag)) {
|
||||
vterm_set_utf8(vt, flag);
|
||||
}
|
||||
|
||||
else if(streq(line, "RESET")) {
|
||||
if(state) {
|
||||
vterm_state_reset(state, 1);
|
||||
vterm_state_get_cursorpos(state, &state_pos);
|
||||
}
|
||||
if(screen) {
|
||||
vterm_screen_reset(screen, 1);
|
||||
}
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "RESIZE ")) {
|
||||
int rows, cols;
|
||||
char *linep = line + 7;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
sscanf(linep, "%d, %d", &rows, &cols);
|
||||
vterm_set_size(vt, rows, cols);
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "PUSH ")) {
|
||||
char *bytes = line + 5;
|
||||
size_t len = inplace_hex2bytes(bytes);
|
||||
size_t written = vterm_input_write(vt, bytes, len);
|
||||
if(written < len)
|
||||
fprintf(stderr, "! short write\n");
|
||||
}
|
||||
|
||||
else if(streq(line, "WANTENCODING")) {
|
||||
/* This isn't really external API but it's hard to get this out any
|
||||
* other way
|
||||
*/
|
||||
encoding.enc = vterm_lookup_encoding(ENC_UTF8, 'u');
|
||||
if(encoding.enc->init)
|
||||
(*encoding.enc->init)(encoding.enc, encoding.data);
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "ENCIN ")) {
|
||||
char *bytes = line + 6;
|
||||
size_t len = inplace_hex2bytes(bytes);
|
||||
|
||||
uint32_t cp[1024];
|
||||
int cpi = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
(*encoding.enc->decode)(encoding.enc, encoding.data,
|
||||
cp, &cpi, len, bytes, &pos, len);
|
||||
|
||||
if(cpi > 0) {
|
||||
int i;
|
||||
printf("encout ");
|
||||
for(i = 0; i < cpi; i++) {
|
||||
printf(i ? ",%x" : "%x", cp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "INCHAR ")) {
|
||||
char *linep = line + 7;
|
||||
unsigned int c = 0;
|
||||
VTermModifier mod;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
mod = strpe_modifiers(&linep);
|
||||
sscanf(linep, " %x", &c);
|
||||
|
||||
vterm_keyboard_unichar(vt, c, mod);
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "INKEY ")) {
|
||||
VTermModifier mod;
|
||||
VTermKey key;
|
||||
char *linep = line + 6;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
mod = strpe_modifiers(&linep);
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
key = strp_key(linep);
|
||||
|
||||
vterm_keyboard_key(vt, key, mod);
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "PASTE ")) {
|
||||
char *linep = line + 6;
|
||||
if(streq(linep, "START"))
|
||||
vterm_keyboard_start_paste(vt);
|
||||
else if(streq(linep, "END"))
|
||||
vterm_keyboard_end_paste(vt);
|
||||
else
|
||||
goto abort_line;
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "MOUSEMOVE ")) {
|
||||
char *linep = line + 10;
|
||||
int row, col, len;
|
||||
VTermModifier mod;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
sscanf(linep, "%d,%d%n", &row, &col, &len);
|
||||
linep += len;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
mod = strpe_modifiers(&linep);
|
||||
vterm_mouse_move(vt, row, col, mod);
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "MOUSEBTN ")) {
|
||||
char *linep = line + 9;
|
||||
char press;
|
||||
int button, len;
|
||||
VTermModifier mod;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
sscanf(linep, "%c %d%n", &press, &button, &len);
|
||||
linep += len;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
mod = strpe_modifiers(&linep);
|
||||
vterm_mouse_button(vt, button, (press == 'd' || press == 'D'), mod);
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "DAMAGEMERGE ")) {
|
||||
char *linep = line + 12;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
if(streq(linep, "CELL"))
|
||||
vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_CELL);
|
||||
else if(streq(linep, "ROW"))
|
||||
vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_ROW);
|
||||
else if(streq(linep, "SCREEN"))
|
||||
vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_SCREEN);
|
||||
else if(streq(linep, "SCROLL"))
|
||||
vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_SCROLL);
|
||||
}
|
||||
|
||||
else if(strstartswith(line, "DAMAGEFLUSH")) {
|
||||
vterm_screen_flush_damage(screen);
|
||||
}
|
||||
|
||||
else if(line[0] == '?') {
|
||||
if(streq(line, "?cursor")) {
|
||||
VTermPos pos;
|
||||
vterm_state_get_cursorpos(state, &pos);
|
||||
if(pos.row != state_pos.row)
|
||||
printf("! row mismatch: state=%d,%d event=%d,%d\n",
|
||||
pos.row, pos.col, state_pos.row, state_pos.col);
|
||||
else if(pos.col != state_pos.col)
|
||||
printf("! col mismatch: state=%d,%d event=%d,%d\n",
|
||||
pos.row, pos.col, state_pos.row, state_pos.col);
|
||||
else
|
||||
printf("%d,%d\n", state_pos.row, state_pos.col);
|
||||
}
|
||||
else if(strstartswith(line, "?pen ")) {
|
||||
VTermValue val;
|
||||
char *linep = line + 5;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
|
||||
#define BOOLSTR(v) ((v) ? "on" : "off")
|
||||
|
||||
if(streq(linep, "bold")) {
|
||||
vterm_state_get_penattr(state, VTERM_ATTR_BOLD, &val);
|
||||
if(val.boolean != state_pen.bold)
|
||||
printf("! pen bold mismatch; state=%s, event=%s\n",
|
||||
BOOLSTR(val.boolean), BOOLSTR(state_pen.bold));
|
||||
else
|
||||
printf("%s\n", BOOLSTR(state_pen.bold));
|
||||
}
|
||||
else if(streq(linep, "underline")) {
|
||||
vterm_state_get_penattr(state, VTERM_ATTR_UNDERLINE, &val);
|
||||
if(val.boolean != state_pen.underline)
|
||||
printf("! pen underline mismatch; state=%d, event=%d\n",
|
||||
val.boolean, state_pen.underline);
|
||||
else
|
||||
printf("%d\n", state_pen.underline);
|
||||
}
|
||||
else if(streq(linep, "italic")) {
|
||||
vterm_state_get_penattr(state, VTERM_ATTR_ITALIC, &val);
|
||||
if(val.boolean != state_pen.italic)
|
||||
printf("! pen italic mismatch; state=%s, event=%s\n",
|
||||
BOOLSTR(val.boolean), BOOLSTR(state_pen.italic));
|
||||
else
|
||||
printf("%s\n", BOOLSTR(state_pen.italic));
|
||||
}
|
||||
else if(streq(linep, "blink")) {
|
||||
vterm_state_get_penattr(state, VTERM_ATTR_BLINK, &val);
|
||||
if(val.boolean != state_pen.blink)
|
||||
printf("! pen blink mismatch; state=%s, event=%s\n",
|
||||
BOOLSTR(val.boolean), BOOLSTR(state_pen.blink));
|
||||
else
|
||||
printf("%s\n", BOOLSTR(state_pen.blink));
|
||||
}
|
||||
else if(streq(linep, "reverse")) {
|
||||
vterm_state_get_penattr(state, VTERM_ATTR_REVERSE, &val);
|
||||
if(val.boolean != state_pen.reverse)
|
||||
printf("! pen reverse mismatch; state=%s, event=%s\n",
|
||||
BOOLSTR(val.boolean), BOOLSTR(state_pen.reverse));
|
||||
else
|
||||
printf("%s\n", BOOLSTR(state_pen.reverse));
|
||||
}
|
||||
else if(streq(linep, "font")) {
|
||||
vterm_state_get_penattr(state, VTERM_ATTR_FONT, &val);
|
||||
if(val.boolean != state_pen.font)
|
||||
printf("! pen font mismatch; state=%d, event=%d\n",
|
||||
val.boolean, state_pen.font);
|
||||
else
|
||||
printf("%d\n", state_pen.font);
|
||||
}
|
||||
else if(streq(linep, "foreground")) {
|
||||
printf("rgb(%d,%d,%d)\n", state_pen.foreground.red, state_pen.foreground.green, state_pen.foreground.blue);
|
||||
}
|
||||
else if(streq(linep, "background")) {
|
||||
printf("rgb(%d,%d,%d)\n", state_pen.background.red, state_pen.background.green, state_pen.background.blue);
|
||||
}
|
||||
else
|
||||
printf("?\n");
|
||||
}
|
||||
else if(strstartswith(line, "?screen_chars ")) {
|
||||
char *linep = line + 13;
|
||||
VTermRect rect;
|
||||
size_t len;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
if(sscanf(linep, "%d,%d,%d,%d", &rect.start_row, &rect.start_col, &rect.end_row, &rect.end_col) < 4) {
|
||||
printf("! screen_chars unrecognised input\n");
|
||||
goto abort_line;
|
||||
}
|
||||
len = vterm_screen_get_chars(screen, NULL, 0, rect);
|
||||
if(len == (size_t)-1)
|
||||
printf("! screen_chars error\n");
|
||||
else if(len == 0)
|
||||
printf("\n");
|
||||
else {
|
||||
uint32_t *chars = malloc(sizeof(uint32_t) * len);
|
||||
size_t i;
|
||||
vterm_screen_get_chars(screen, chars, len, rect);
|
||||
for(i = 0; i < len; i++) {
|
||||
printf("0x%02x%s", chars[i], i < len-1 ? "," : "\n");
|
||||
}
|
||||
free(chars);
|
||||
}
|
||||
}
|
||||
else if(strstartswith(line, "?screen_text ")) {
|
||||
char *linep = line + 12;
|
||||
VTermRect rect;
|
||||
size_t len;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
if(sscanf(linep, "%d,%d,%d,%d", &rect.start_row, &rect.start_col, &rect.end_row, &rect.end_col) < 4) {
|
||||
printf("! screen_text unrecognised input\n");
|
||||
goto abort_line;
|
||||
}
|
||||
len = vterm_screen_get_text(screen, NULL, 0, rect);
|
||||
if(len == (size_t)-1)
|
||||
printf("! screen_text error\n");
|
||||
else if(len == 0)
|
||||
printf("\n");
|
||||
else {
|
||||
/* Put an overwrite guard at both ends of the buffer */
|
||||
unsigned char *buffer = malloc(len + 4);
|
||||
unsigned char *text = buffer + 2;
|
||||
text[-2] = 0x55; text[-1] = 0xAA;
|
||||
text[len] = 0x55; text[len+1] = 0xAA;
|
||||
|
||||
vterm_screen_get_text(screen, (char *)text, len, rect);
|
||||
|
||||
if(text[-2] != 0x55 || text[-1] != 0xAA)
|
||||
printf("! screen_get_text buffer overrun left [%02x,%02x]\n", text[-2], text[-1]);
|
||||
else if(text[len] != 0x55 || text[len+1] != 0xAA)
|
||||
printf("! screen_get_text buffer overrun right [%02x,%02x]\n", text[len], text[len+1]);
|
||||
else
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < len; i++) {
|
||||
printf("0x%02x%s", text[i], i < len-1 ? "," : "\n");
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
else if(strstartswith(line, "?screen_cell ")) {
|
||||
char *linep = line + 12;
|
||||
int i;
|
||||
VTermPos pos;
|
||||
VTermScreenCell cell;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) {
|
||||
printf("! screen_cell unrecognised input\n");
|
||||
goto abort_line;
|
||||
}
|
||||
if(!vterm_screen_get_cell(screen, pos, &cell))
|
||||
goto abort_line;
|
||||
printf("{");
|
||||
for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell.chars[i]; i++) {
|
||||
printf("%s0x%x", i ? "," : "", cell.chars[i]);
|
||||
}
|
||||
printf("} width=%d attrs={", cell.width);
|
||||
if(cell.attrs.bold) printf("B");
|
||||
if(cell.attrs.underline) printf("U%d", cell.attrs.underline);
|
||||
if(cell.attrs.italic) printf("I");
|
||||
if(cell.attrs.blink) printf("K");
|
||||
if(cell.attrs.reverse) printf("R");
|
||||
if(cell.attrs.font) printf("F%d", cell.attrs.font);
|
||||
printf("} ");
|
||||
if(cell.attrs.dwl) printf("dwl ");
|
||||
if(cell.attrs.dhl) printf("dhl-%s ", cell.attrs.dhl == 2 ? "bottom" : "top");
|
||||
printf("fg=rgb(%d,%d,%d) ", cell.fg.red, cell.fg.green, cell.fg.blue);
|
||||
printf("bg=rgb(%d,%d,%d)\n", cell.bg.red, cell.bg.green, cell.bg.blue);
|
||||
}
|
||||
else if(strstartswith(line, "?screen_eol ")) {
|
||||
VTermPos pos;
|
||||
char *linep = line + 12;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) {
|
||||
printf("! screen_eol unrecognised input\n");
|
||||
goto abort_line;
|
||||
}
|
||||
printf("%d\n", vterm_screen_is_eol(screen, pos));
|
||||
}
|
||||
else if(strstartswith(line, "?screen_attrs_extent ")) {
|
||||
VTermPos pos;
|
||||
VTermRect rect;
|
||||
char *linep = line + 21;
|
||||
while(linep[0] == ' ')
|
||||
linep++;
|
||||
if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) {
|
||||
printf("! screen_attrs_extent unrecognised input\n");
|
||||
goto abort_line;
|
||||
}
|
||||
rect.start_col = 0;
|
||||
rect.end_col = -1;
|
||||
if(!vterm_screen_get_attrs_extent(screen, &rect, pos, ~0)) {
|
||||
printf("! screen_attrs_extent failed\n");
|
||||
goto abort_line;
|
||||
}
|
||||
printf("%d,%d-%d,%d\n", rect.start_row, rect.start_col, rect.end_row, rect.end_col);
|
||||
}
|
||||
else
|
||||
printf("?\n");
|
||||
|
||||
memset(line, 0, sizeof line);
|
||||
continue;
|
||||
}
|
||||
|
||||
else
|
||||
abort_line: err = 1;
|
||||
|
||||
outlen = vterm_output_get_buffer_current(vt);
|
||||
if(outlen > 0) {
|
||||
int i;
|
||||
char outbuff[1024];
|
||||
vterm_output_read(vt, outbuff, outlen);
|
||||
|
||||
printf("output ");
|
||||
for(i = 0; i < outlen; i++)
|
||||
printf("%x%s", (unsigned char)outbuff[i], i < outlen-1 ? "," : "\n");
|
||||
}
|
||||
|
||||
printf(err ? "?\n" : "DONE\n");
|
||||
}
|
||||
|
||||
vterm_free(vt);
|
||||
|
||||
return 0;
|
||||
}
|
196
src/libvterm/t/run-test.pl
Normal file
196
src/libvterm/t/run-test.pl
Normal file
@ -0,0 +1,196 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Getopt::Long;
|
||||
use IO::Handle;
|
||||
use IPC::Open2 qw( open2 );
|
||||
use POSIX qw( WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG );
|
||||
|
||||
my $VALGRIND = 0;
|
||||
GetOptions(
|
||||
'valgrind|v+' => \$VALGRIND,
|
||||
) or exit 1;
|
||||
|
||||
my ( $hin, $hout, $hpid );
|
||||
{
|
||||
local $ENV{LD_LIBRARY_PATH} = ".libs";
|
||||
my @command = "t/.libs/harness";
|
||||
unshift @command, "valgrind", "--quiet", "--error-exitcode=126" if $VALGRIND;
|
||||
|
||||
$hpid = open2 $hout, $hin, @command or die "Cannot open2 harness - $!";
|
||||
}
|
||||
|
||||
my $exitcode = 0;
|
||||
|
||||
my $command;
|
||||
my @expect;
|
||||
|
||||
sub do_onetest
|
||||
{
|
||||
$hin->print( "$command\n" );
|
||||
undef $command;
|
||||
|
||||
my $fail_printed = 0;
|
||||
|
||||
while( my $outline = <$hout> ) {
|
||||
last if $outline eq "DONE\n" or $outline eq "?\n";
|
||||
|
||||
chomp $outline;
|
||||
|
||||
if( !@expect ) {
|
||||
print "# Test failed\n" unless $fail_printed++;
|
||||
print "# expected nothing more\n" .
|
||||
"# Actual: $outline\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $expectation = shift @expect;
|
||||
|
||||
next if $expectation eq $outline;
|
||||
|
||||
print "# Test failed\n" unless $fail_printed++;
|
||||
print "# Expected: $expectation\n" .
|
||||
"# Actual: $outline\n";
|
||||
}
|
||||
|
||||
if( @expect ) {
|
||||
print "# Test failed\n" unless $fail_printed++;
|
||||
print "# Expected: $_\n" .
|
||||
"# didn't happen\n" for @expect;
|
||||
}
|
||||
|
||||
$exitcode = 1 if $fail_printed;
|
||||
}
|
||||
|
||||
sub do_line
|
||||
{
|
||||
my ( $line ) = @_;
|
||||
|
||||
if( $line =~ m/^!(.*)/ ) {
|
||||
do_onetest if defined $command;
|
||||
print "> $1\n";
|
||||
}
|
||||
|
||||
# Commands have capitals
|
||||
elsif( $line =~ m/^([A-Z]+)/ ) {
|
||||
# Some convenience formatting
|
||||
if( $line =~ m/^(PUSH|ENCIN) (.*)$/ ) {
|
||||
# we're evil
|
||||
my $string = eval($2);
|
||||
$line = "$1 " . unpack "H*", $string;
|
||||
}
|
||||
|
||||
do_onetest if defined $command;
|
||||
|
||||
$command = $line;
|
||||
undef @expect;
|
||||
}
|
||||
# Expectations have lowercase
|
||||
elsif( $line =~ m/^([a-z]+)/ ) {
|
||||
# Convenience formatting
|
||||
if( $line =~ m/^(text|encout) (.*)$/ ) {
|
||||
$line = "$1 " . join ",", map sprintf("%x", $_), eval($2);
|
||||
}
|
||||
elsif( $line =~ m/^(output) (.*)$/ ) {
|
||||
$line = "$1 " . join ",", map sprintf("%x", $_), unpack "C*", eval($2);
|
||||
}
|
||||
elsif( $line =~ m/^control (.*)$/ ) {
|
||||
$line = sprintf "control %02x", eval($1);
|
||||
}
|
||||
elsif( $line =~ m/^csi (\S+) (.*)$/ ) {
|
||||
$line = sprintf "csi %02x %s", eval($1), $2; # TODO
|
||||
}
|
||||
elsif( $line =~ m/^(escape|osc|dcs) (.*)$/ ) {
|
||||
$line = "$1 " . join "", map sprintf("%02x", $_), unpack "C*", eval($2);
|
||||
}
|
||||
elsif( $line =~ m/^putglyph (\S+) (.*)$/ ) {
|
||||
$line = "putglyph " . join( ",", map sprintf("%x", $_), eval($1) ) . " $2";
|
||||
}
|
||||
elsif( $line =~ m/^(?:movecursor|scrollrect|moverect|erase|damage|sb_pushline|sb_popline|settermprop|setmousefunc) / ) {
|
||||
# no conversion
|
||||
}
|
||||
else {
|
||||
warn "Unrecognised test expectation '$line'\n";
|
||||
}
|
||||
|
||||
push @expect, $line;
|
||||
}
|
||||
# ?screen_row assertion is emulated here
|
||||
elsif( $line =~ s/^\?screen_row\s+(\d+)\s*=\s*// ) {
|
||||
my $row = $1;
|
||||
my $row1 = $row + 1;
|
||||
my $want = eval($line);
|
||||
|
||||
do_onetest if defined $command;
|
||||
|
||||
# TODO: may not be 80
|
||||
$hin->print( "\?screen_chars $row,0,$row1,80\n" );
|
||||
my $response = <$hout>;
|
||||
chomp $response;
|
||||
|
||||
$response = pack "C*", map hex, split m/,/, $response;
|
||||
if( $response ne $want ) {
|
||||
print "# Assert ?screen_row $row failed:\n" .
|
||||
"# Expected: $want\n" .
|
||||
"# Actual: $response\n";
|
||||
$exitcode = 1;
|
||||
}
|
||||
}
|
||||
# Assertions start with '?'
|
||||
elsif( $line =~ s/^\?([a-z]+.*?=)\s+// ) {
|
||||
do_onetest if defined $command;
|
||||
|
||||
my ( $assertion ) = $1 =~ m/^(.*)\s+=/;
|
||||
|
||||
$hin->print( "\?$assertion\n" );
|
||||
my $response = <$hout>; defined $response or wait, die "Test harness failed - $?\n";
|
||||
chomp $response;
|
||||
|
||||
if( $response ne $line ) {
|
||||
print "# Assert $assertion failed:\n" .
|
||||
"# Expected: $line\n" .
|
||||
"# Actual: $response\n";
|
||||
$exitcode = 1;
|
||||
}
|
||||
}
|
||||
# Test controls start with '$'
|
||||
elsif( $line =~ s/\$SEQ\s+(\d+)\s+(\d+):\s*// ) {
|
||||
my ( $low, $high ) = ( $1, $2 );
|
||||
foreach my $val ( $low .. $high ) {
|
||||
( my $inner = $line ) =~ s/\\#/$val/g;
|
||||
do_line( $inner );
|
||||
}
|
||||
}
|
||||
elsif( $line =~ s/\$REP\s+(\d+):\s*// ) {
|
||||
my $count = $1;
|
||||
do_line( $line ) for 1 .. $count;
|
||||
}
|
||||
else {
|
||||
die "Unrecognised TEST line $line\n";
|
||||
}
|
||||
}
|
||||
|
||||
open my $test, "<", $ARGV[0] or die "Cannot open test script $ARGV[0] - $!";
|
||||
|
||||
while( my $line = <$test> ) {
|
||||
$line =~ s/^\s+//;
|
||||
next if $line =~ m/^(?:#|$)/;
|
||||
|
||||
chomp $line;
|
||||
do_line( $line );
|
||||
}
|
||||
|
||||
do_onetest if defined $command;
|
||||
|
||||
close $hin;
|
||||
close $hout;
|
||||
|
||||
waitpid $hpid, 0;
|
||||
if( $? ) {
|
||||
printf STDERR "Harness exited %d\n", WEXITSTATUS($?) if WIFEXITED($?);
|
||||
printf STDERR "Harness exit signal %d\n", WTERMSIG($?) if WIFSIGNALED($?);
|
||||
$exitcode = WIFEXITED($?) ? WEXITSTATUS($?) : 125;
|
||||
}
|
||||
|
||||
exit $exitcode;
|
51
src/libvterm/tbl2inc_c.pl
Normal file
51
src/libvterm/tbl2inc_c.pl
Normal file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my ( $encname ) = $ARGV[0] =~ m{/([^/.]+).tbl}
|
||||
or die "Cannot parse encoding name out of $ARGV[0]\n";
|
||||
|
||||
print <<"EOF";
|
||||
static const struct StaticTableEncoding encoding_$encname = {
|
||||
{
|
||||
NULL, /* init */
|
||||
&decode_table /* decode */
|
||||
},
|
||||
{
|
||||
EOF
|
||||
|
||||
my $row = 0;
|
||||
while( <> ) {
|
||||
s/\s*#.*//; # strip comment
|
||||
|
||||
if ($_ =~ m{^\d+/\d+}) {
|
||||
my ($up, $low) = ($_ =~ m{^(\d+)/(\d+)});
|
||||
my $thisrow = $up * 16 + $low;
|
||||
while ($row < $thisrow) {
|
||||
print " 0x0, /* $row */\n";
|
||||
++$row;
|
||||
}
|
||||
}
|
||||
|
||||
s{^(\d+)/(\d+)}{""}e; # Remove 3/1
|
||||
s{ = }{""}e; # Remove " = "
|
||||
s{"(.)"}{sprintf "0x%04x", ord $1}e; # Convert "A" to 0x41
|
||||
s{U\+}{0x}; # Convert U+0041 to 0x0041
|
||||
|
||||
s{$}{, /* $row */}; # append comma and index
|
||||
|
||||
print " $_";
|
||||
|
||||
++$row;
|
||||
}
|
||||
|
||||
while ($row < 128) {
|
||||
print " 0x0, /* $row */\n";
|
||||
++$row;
|
||||
}
|
||||
|
||||
print <<"EOF";
|
||||
}
|
||||
};
|
||||
EOF
|
9
src/libvterm/vterm.pc.in
Normal file
9
src/libvterm/vterm.pc.in
Normal file
@ -0,0 +1,9 @@
|
||||
prefix=@PREFIX@
|
||||
libdir=@LIBDIR@
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: vterm
|
||||
Description: Abstract VT220/Xterm/ECMA-48 emulation library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lvterm
|
||||
Cflags: -I${includedir}
|
28
src/option.c
28
src/option.c
@ -257,6 +257,9 @@
|
||||
# define PV_COCU OPT_WIN(WV_COCU)
|
||||
# define PV_COLE OPT_WIN(WV_COLE)
|
||||
#endif
|
||||
#ifdef FEAT_TERMINAL
|
||||
# define PV_TMS OPT_WIN(WV_TMS)
|
||||
#endif
|
||||
#ifdef FEAT_SIGNS
|
||||
# define PV_SCL OPT_WIN(WV_SCL)
|
||||
#endif
|
||||
@ -2776,6 +2779,15 @@ static struct vimoption options[] =
|
||||
#else
|
||||
(char_u*)NULL, PV_NONE,
|
||||
{(char_u *)FALSE, (char_u *)FALSE}
|
||||
#endif
|
||||
SCRIPTID_INIT},
|
||||
{"termsize", "tms", P_STRING|P_ALLOCED|P_RWIN|P_VI_DEF,
|
||||
#ifdef FEAT_TERMINAL
|
||||
(char_u *)VAR_WIN, PV_TMS,
|
||||
{(char_u *)"", (char_u *)NULL}
|
||||
#else
|
||||
(char_u *)NULL, PV_NONE,
|
||||
{(char_u *)NULL, (char_u *)0L}
|
||||
#endif
|
||||
SCRIPTID_INIT},
|
||||
{"terse", NULL, P_BOOL|P_VI_DEF,
|
||||
@ -10662,8 +10674,11 @@ get_varp(struct vimoption *p)
|
||||
case PV_CRBIND: return (char_u *)&(curwin->w_p_crb);
|
||||
#endif
|
||||
#ifdef FEAT_CONCEAL
|
||||
case PV_COCU: return (char_u *)&(curwin->w_p_cocu);
|
||||
case PV_COLE: return (char_u *)&(curwin->w_p_cole);
|
||||
case PV_COCU: return (char_u *)&(curwin->w_p_cocu);
|
||||
case PV_COLE: return (char_u *)&(curwin->w_p_cole);
|
||||
#endif
|
||||
#ifdef FEAT_TERMINAL
|
||||
case PV_TMS: return (char_u *)&(curwin->w_p_tms);
|
||||
#endif
|
||||
|
||||
case PV_AI: return (char_u *)&(curbuf->b_p_ai);
|
||||
@ -10871,6 +10886,9 @@ copy_winopt(winopt_T *from, winopt_T *to)
|
||||
to->wo_cocu = vim_strsave(from->wo_cocu);
|
||||
to->wo_cole = from->wo_cole;
|
||||
#endif
|
||||
#ifdef FEAT_TERMINAL
|
||||
to->wo_tms = vim_strsave(from->wo_tms);
|
||||
#endif
|
||||
#ifdef FEAT_FOLDING
|
||||
to->wo_fdc = from->wo_fdc;
|
||||
to->wo_fdc_save = from->wo_fdc_save;
|
||||
@ -10937,6 +10955,9 @@ check_winopt(winopt_T *wop UNUSED)
|
||||
#ifdef FEAT_CONCEAL
|
||||
check_string_option(&wop->wo_cocu);
|
||||
#endif
|
||||
#ifdef FEAT_TERMINAL
|
||||
check_string_option(&wop->wo_tms);
|
||||
#endif
|
||||
#ifdef FEAT_LINEBREAK
|
||||
check_string_option(&wop->wo_briopt);
|
||||
#endif
|
||||
@ -10976,6 +10997,9 @@ clear_winopt(winopt_T *wop UNUSED)
|
||||
#ifdef FEAT_CONCEAL
|
||||
clear_string_option(&wop->wo_cocu);
|
||||
#endif
|
||||
#ifdef FEAT_TERMINAL
|
||||
clear_string_option(&wop->wo_tms);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1130,6 +1130,9 @@ enum
|
||||
, WV_COCU
|
||||
, WV_COLE
|
||||
#endif
|
||||
#ifdef FEAT_TERMINAL
|
||||
, WV_TMS
|
||||
#endif
|
||||
#ifdef FEAT_CURSORBIND
|
||||
, WV_CRBIND
|
||||
#endif
|
||||
|
@ -162,6 +162,9 @@ void qsort(void *base, size_t elm_count, size_t elm_size, int (*cmp)(const void
|
||||
# include "syntax.pro"
|
||||
# include "tag.pro"
|
||||
# include "term.pro"
|
||||
# ifdef FEAT_TERMINAL
|
||||
# include "terminal.pro"
|
||||
# endif
|
||||
# if defined(HAVE_TGETENT) && (defined(AMIGA) || defined(VMS))
|
||||
# include "termlib.pro"
|
||||
# endif
|
||||
|
3
src/proto/terminal.pro
Normal file
3
src/proto/terminal.pro
Normal file
@ -0,0 +1,3 @@
|
||||
/* terminal.c */
|
||||
void ex_terminal(exarg_T *eap);
|
||||
/* vim: set ft=c : */
|
@ -68,6 +68,7 @@ typedef struct wininfo_S wininfo_T;
|
||||
typedef struct frame_S frame_T;
|
||||
typedef int scid_T; /* script ID */
|
||||
typedef struct file_buffer buf_T; /* forward declaration */
|
||||
typedef struct terminal_S term_T;
|
||||
|
||||
/*
|
||||
* Reference to a buffer that stores the value of buf_free_count.
|
||||
@ -268,6 +269,10 @@ typedef struct
|
||||
char_u *wo_scl;
|
||||
# define w_p_scl w_onebuf_opt.wo_scl /* 'signcolumn' */
|
||||
#endif
|
||||
#ifdef FEAT_TERMINAL
|
||||
char_u *wo_tms;
|
||||
#define w_p_tms w_onebuf_opt.wo_tms /* 'termsize' */
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_EVAL
|
||||
int wo_scriptID[WV_COUNT]; /* SIDs for window-local options */
|
||||
@ -2351,6 +2356,11 @@ struct file_buffer
|
||||
#endif
|
||||
int b_mapped_ctrl_c; /* modes where CTRL-C is mapped */
|
||||
|
||||
#ifdef FEAT_TERMINAL
|
||||
term_T *b_term; /* When not NULL this buffer is for a terminal
|
||||
* window. */
|
||||
#endif
|
||||
|
||||
}; /* file_buffer */
|
||||
|
||||
|
||||
|
211
src/terminal.c
Normal file
211
src/terminal.c
Normal file
@ -0,0 +1,211 @@
|
||||
/* vi:set ts=8 sts=4 sw=4 noet:
|
||||
*
|
||||
* VIM - Vi IMproved by Bram Moolenaar
|
||||
*
|
||||
* Do ":help uganda" in Vim to read copying and usage conditions.
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Terminal window support, see ":help :terminal".
|
||||
*
|
||||
* For a terminal one VTerm is constructed. This uses libvterm. A copy of
|
||||
* that library is in the libvterm directory.
|
||||
*
|
||||
* The VTerm invokes callbacks when its screen contents changes. The line
|
||||
* range is stored in tl_dirty_row_start and tl_dirty_row_end. Once in a
|
||||
* while, if the window is visible, the screen contents is drawn.
|
||||
*
|
||||
* If the terminal window has keyboard focus, typed keys are converted to the
|
||||
* terminal encoding and writting to the job over a channel.
|
||||
*
|
||||
* If the job produces output, it is written to the VTerm.
|
||||
* This will result in screen updates.
|
||||
*
|
||||
* TODO:
|
||||
* - +terminal in features[] in version.c
|
||||
* - free b_term when closing terminal.
|
||||
* - remove term from first_term list when closing terminal.
|
||||
* - set buffer options to be scratch, hidden, nomodifiable, etc.
|
||||
* - set buffer name to command, add (1) to avoid duplicates.
|
||||
* - command line completion (command name)
|
||||
* - support fixed size when 'termsize' is "rowsXcols".
|
||||
* - support minimal size when 'termsize' is "rows*cols".
|
||||
* - support minimal size when 'termsize' is empty.
|
||||
* - implement ":buf {term-buf-name}"
|
||||
* - implement term_getsize()
|
||||
* - implement term_setsize()
|
||||
* - implement term_sendkeys() send keystrokes to a terminal
|
||||
* - implement term_wait() wait for screen to be updated
|
||||
* - implement term_scrape() inspect terminal screen
|
||||
* - implement term_open() open terminal window
|
||||
* - implement term_getjob()
|
||||
*/
|
||||
|
||||
#include "vim.h"
|
||||
|
||||
#ifdef FEAT_TERMINAL
|
||||
|
||||
#include "libvterm/include/vterm.h"
|
||||
|
||||
/* typedef term_T in structs.h */
|
||||
struct terminal_S {
|
||||
term_T *tl_next;
|
||||
|
||||
VTerm *tl_vterm;
|
||||
job_T *tl_job;
|
||||
|
||||
/* Range of screen rows to update. Zero based. */
|
||||
int tl_dirty_row_start; /* -1 if nothing dirty */
|
||||
int tl_dirty_row_end; /* row below last one to update */
|
||||
|
||||
pos_T tl_cursor;
|
||||
};
|
||||
|
||||
#define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */
|
||||
|
||||
/*
|
||||
* List of all active terminals.
|
||||
*/
|
||||
static term_T *first_term = NULL;
|
||||
|
||||
static int handle_damage(VTermRect rect, void *user);
|
||||
static int handle_moverect(VTermRect dest, VTermRect src, void *user);
|
||||
static int handle_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user);
|
||||
static int handle_resize(int rows, int cols, void *user);
|
||||
|
||||
static VTermScreenCallbacks screen_callbacks = {
|
||||
handle_damage, /* damage */
|
||||
handle_moverect, /* moverect */
|
||||
handle_movecursor, /* movecursor */
|
||||
NULL, /* settermprop */
|
||||
NULL, /* bell */
|
||||
handle_resize, /* resize */
|
||||
NULL, /* sb_pushline */
|
||||
NULL /* sb_popline */
|
||||
};
|
||||
|
||||
/*
|
||||
* ":terminal": open a terminal window and execute a job in it.
|
||||
*/
|
||||
void
|
||||
ex_terminal(exarg_T *eap)
|
||||
{
|
||||
int rows;
|
||||
int cols;
|
||||
exarg_T split_ea;
|
||||
win_T *old_curwin = curwin;
|
||||
typval_T argvars[2];
|
||||
term_T *term;
|
||||
VTerm *vterm;
|
||||
VTermScreen *screen;
|
||||
|
||||
if (check_restricted() || check_secure())
|
||||
return;
|
||||
|
||||
term = (term_T *)alloc_clear(sizeof(term_T));
|
||||
if (term == NULL)
|
||||
return;
|
||||
term->tl_dirty_row_end = MAX_ROW;
|
||||
|
||||
/* Open a new window or tab. */
|
||||
vim_memset(&split_ea, 0, sizeof(split_ea));
|
||||
split_ea.cmdidx = CMD_new;
|
||||
split_ea.cmd = (char_u *)"new";
|
||||
split_ea.arg = (char_u *)"";
|
||||
ex_splitview(&split_ea);
|
||||
if (curwin == old_curwin)
|
||||
{
|
||||
/* split failed */
|
||||
vim_free(term);
|
||||
return;
|
||||
}
|
||||
|
||||
curbuf->b_term = term;
|
||||
term->tl_next = first_term;
|
||||
first_term = term;
|
||||
|
||||
/* TODO: set buffer type, hidden, etc. */
|
||||
|
||||
if (*curwin->w_p_tms != NUL)
|
||||
{
|
||||
char_u *p = vim_strchr(curwin->w_p_tms, 'x') + 1;
|
||||
|
||||
rows = atoi((char *)curwin->w_p_tms);
|
||||
cols = atoi((char *)p);
|
||||
/* TODO: resize window if possible. */
|
||||
}
|
||||
else
|
||||
{
|
||||
rows = curwin->w_height;
|
||||
cols = curwin->w_width;
|
||||
}
|
||||
|
||||
vterm = vterm_new(rows, cols);
|
||||
term->tl_vterm = vterm;
|
||||
screen = vterm_obtain_screen(vterm);
|
||||
vterm_screen_set_callbacks(screen, &screen_callbacks, term);
|
||||
|
||||
argvars[0].v_type = VAR_STRING;
|
||||
argvars[0].vval.v_string = eap->arg;
|
||||
argvars[1].v_type = VAR_UNKNOWN;
|
||||
term->tl_job = job_start(argvars);
|
||||
|
||||
/* TODO: setup channels to/from job */
|
||||
/* Setup pty, see mch_call_shell(). */
|
||||
}
|
||||
|
||||
static int
|
||||
handle_damage(VTermRect rect, void *user)
|
||||
{
|
||||
term_T *term = (term_T *)user;
|
||||
|
||||
term->tl_dirty_row_start = MIN(term->tl_dirty_row_start, rect.start_row);
|
||||
term->tl_dirty_row_end = MAX(term->tl_dirty_row_end, rect.end_row);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_moverect(VTermRect dest, VTermRect src, void *user)
|
||||
{
|
||||
/* TODO */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
|
||||
{
|
||||
/* TODO: handle moving the cursor. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_resize(int rows, int cols, void *user)
|
||||
{
|
||||
/* TODO: handle terminal resize. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* TODO: Use win_del_lines() to make scroll up efficient. */
|
||||
|
||||
/* TODO: function to update the window.
|
||||
* Get the screen contents from vterm with vterm_screen_get_cell().
|
||||
* put in current_ScreenLine and call screen_line().
|
||||
*/
|
||||
|
||||
/* TODO: function to wait for input and send it to the job.
|
||||
* Return when a CTRL-W command is typed that moves to another window.
|
||||
* Convert special keys to vterm keys:
|
||||
* - Write keys to vterm: vterm_keyboard_key()
|
||||
* - read the output (xterm escape sequences): vterm_output_read()
|
||||
* - Write output to channel.
|
||||
*/
|
||||
|
||||
/* TODO: function to read job output from the channel.
|
||||
* write to vterm: vterm_input_write()
|
||||
* This will invoke screen callbacks.
|
||||
* call vterm_screen_flush_damage()
|
||||
*/
|
||||
|
||||
#endif /* FEAT_TERMINAL */
|
@ -764,6 +764,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
693,
|
||||
/**/
|
||||
692,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user