mirror of
https://github.com/zoriya/vim.git
synced 2025-12-06 07:16:15 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
292eff0c5a | ||
|
|
f1d13478e3 | ||
|
|
5a15b6aa0a | ||
|
|
cae24be4a8 | ||
|
|
163095f088 | ||
|
|
faf29d7f91 | ||
|
|
c577d813b7 | ||
|
|
11e79bb04e | ||
|
|
0ea5070d79 | ||
|
|
710b4a1646 | ||
|
|
c4f833808a | ||
|
|
8858498516 | ||
|
|
cce1cf12eb | ||
|
|
e5ae108ab8 | ||
|
|
a34293ae0a | ||
|
|
e4f25e4a8d | ||
|
|
da5116da45 | ||
|
|
a83fe75ca7 | ||
|
|
c2226845eb | ||
|
|
a693d0584b | ||
|
|
9c4fefffb6 | ||
|
|
86f100dc09 | ||
|
|
9f5f7bf4d5 | ||
|
|
e6bf655bc4 | ||
|
|
28b238225a | ||
|
|
2e147caa14 | ||
|
|
0b2eef24bc | ||
|
|
18d90b95c4 | ||
|
|
07ecfa64a1 | ||
|
|
41cc038ff8 | ||
|
|
8eeeba8c02 | ||
|
|
1814183b86 | ||
|
|
182a17b1e8 | ||
|
|
774e5a9673 | ||
|
|
5d7be4f0fa | ||
|
|
ea20de8146 | ||
|
|
cc0750dc6e |
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 \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*eval.txt* For Vim version 8.0. Last change: 2017 Jun 23
|
||||
*eval.txt* For Vim version 8.0. Last change: 2017 Jul 08
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -4189,14 +4189,14 @@ getchar([expr]) *getchar()*
|
||||
not consumed. Return zero if no character available.
|
||||
|
||||
Without [expr] and when [expr] is 0 a whole character or
|
||||
special key is returned. If it is an 8-bit character, the
|
||||
special key is returned. If it is a single character, the
|
||||
result is a number. Use nr2char() to convert it to a String.
|
||||
Otherwise a String is returned with the encoded character.
|
||||
For a special key it's a sequence of bytes starting with 0x80
|
||||
(decimal: 128). This is the same value as the string
|
||||
"\<Key>", e.g., "\<Left>". The returned value is also a
|
||||
String when a modifier (shift, control, alt) was used that is
|
||||
not included in the character.
|
||||
For a special key it's a String with a sequence of bytes
|
||||
starting with 0x80 (decimal: 128). This is the same value as
|
||||
the String "\<Key>", e.g., "\<Left>". The returned value is
|
||||
also a String when a modifier (shift, control, alt) was used
|
||||
that is not included in the character.
|
||||
|
||||
When [expr] is 0 and Esc is typed, there will be a short delay
|
||||
while Vim waits to see if this is the start of an escape
|
||||
@@ -7008,7 +7008,8 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()*
|
||||
title quickfix list title text
|
||||
Unsupported keys in {what} are ignored.
|
||||
If the "nr" item is not present, then the current quickfix list
|
||||
is modified.
|
||||
is modified. When creating a new quickfix list, "nr" can be
|
||||
set to a value one greater than the quickfix stack size.
|
||||
|
||||
Examples: >
|
||||
:call setqflist([], 'r', {'title': 'My search'})
|
||||
@@ -7663,12 +7664,21 @@ synconcealed({lnum}, {col}) *synconcealed()*
|
||||
is 1, the second item contains the text which will be
|
||||
displayed in place of the concealed text, depending on the
|
||||
current setting of 'conceallevel' and 'listchars'.
|
||||
3. The third and final item in the list is a unique number
|
||||
representing the specific syntax region matched. This
|
||||
allows detection of the beginning of a new concealable
|
||||
region if there are two consecutive regions with the same
|
||||
replacement character. For an example use see
|
||||
$VIMRUNTIME/syntax/2html.vim .
|
||||
3. The third and final item in the list is a number
|
||||
representing the specific syntax region matched in the
|
||||
line. When the character is not concealed the value is
|
||||
zero. This allows detection of the beginning of a new
|
||||
concealable region if there are two consecutive regions
|
||||
with the same replacement character. For an example, if
|
||||
the text is "123456" and both "23" and "45" are concealed
|
||||
and replace by the character "X", then:
|
||||
call returns ~
|
||||
synconcealed(lnum, 1) [0, '', 0]
|
||||
synconcealed(lnum, 2) [1, 'X', 1]
|
||||
synconcealed(lnum, 3) [1, 'X', 1]
|
||||
synconcealed(lnum, 4) [1, 'X', 2]
|
||||
synconcealed(lnum, 5) [1, 'X', 2]
|
||||
synconcealed(lnum, 6) [0, '', 0]
|
||||
|
||||
|
||||
synstack({lnum}, {col}) *synstack()*
|
||||
@@ -7933,8 +7943,19 @@ test_override({name}, {val}) *test_override()*
|
||||
name effect when {val} is non-zero ~
|
||||
redraw disable the redrawing() function
|
||||
char_avail disable the char_avail() function
|
||||
starting reset the "starting" variable, see below
|
||||
ALL clear all overrides ({val} is not used)
|
||||
|
||||
"starting" is to be used when a test should behave like
|
||||
startup was done. Since the tests are run by sourcing a
|
||||
script the "starting" variable is non-zero. This is usually a
|
||||
good thing (tests run faster), but sometimes changes behavior
|
||||
in a way that the test doesn't work properly.
|
||||
When using: >
|
||||
call test_override('starting', 1)
|
||||
< The value of "starting" is saved. It is restored by: >
|
||||
call test_override('starting', 0)
|
||||
|
||||
test_settime({expr}) *test_settime()*
|
||||
Set the time Vim uses internally. Currently only used for
|
||||
timestamps in the history, as they are used in viminfo, and
|
||||
@@ -7996,6 +8017,10 @@ timer_start({time}, {callback} [, {options}])
|
||||
"repeat" Number of times to repeat calling the
|
||||
callback. -1 means forever. When not present
|
||||
the callback will be called once.
|
||||
If the timer causes an error three times in a
|
||||
row the repeat is cancelled. This avoids that
|
||||
Vim becomes unusable because of all the error
|
||||
messages.
|
||||
|
||||
Example: >
|
||||
func MyHandler(timer)
|
||||
|
||||
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:
|
||||
91
src/Makefile
91
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); \
|
||||
@@ -2072,25 +2092,23 @@ test1 \
|
||||
test_autoformat_join \
|
||||
test_changelist \
|
||||
test_close_count \
|
||||
test_comparators \
|
||||
test_erasebackword \
|
||||
test_eval \
|
||||
test_fixeol \
|
||||
test_getcwd \
|
||||
test_insertcount \
|
||||
test_listchars \
|
||||
test_search_mbyte \
|
||||
test_wordcount \
|
||||
test3 test4 test5 test6 test7 test8 test9 \
|
||||
test11 test12 test14 test15 test17 test18 test19 \
|
||||
test20 test21 test22 test23 test24 test25 test26 test27 test28 test29 \
|
||||
test20 test21 test22 test25 test27 test28 test29 \
|
||||
test30 test31 test32 test33 test34 test36 test37 test38 test39 \
|
||||
test40 test41 test42 test43 test44 test45 test48 test49 \
|
||||
test50 test51 test52 test53 test54 test55 test56 test57 test59 \
|
||||
test60 test64 test66 test67 test68 test69 \
|
||||
test70 test72 test73 test74 test75 test77 test78 test79 \
|
||||
test80 test82 test83 test84 test85 test86 test87 test88 \
|
||||
test90 test91 test94 test95 test97 test98 test99 \
|
||||
test60 test64 test66 test68 test69 \
|
||||
test70 test72 test73 test74 test77 test78 test79 \
|
||||
test80 test83 test84 test85 test86 test87 test88 \
|
||||
test91 test94 test95 test98 test99 \
|
||||
test100 test101 test103 test104 test107 test108:
|
||||
cd testdir; rm -f $@.out; $(MAKE) -f Makefile $@.out VIMPROG=../$(VIMTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE)
|
||||
|
||||
@@ -2114,6 +2132,7 @@ test_arglist \
|
||||
test_clientserver \
|
||||
test_cmdline \
|
||||
test_command_count \
|
||||
test_comparators \
|
||||
test_crypt \
|
||||
test_cscope \
|
||||
test_cursor_func \
|
||||
@@ -2122,9 +2141,12 @@ test_arglist \
|
||||
test_digraph \
|
||||
test_display \
|
||||
test_edit \
|
||||
test_escaped_glob \
|
||||
test_ex_undo \
|
||||
test_ex_z \
|
||||
test_exec_while_if \
|
||||
test_execute_func \
|
||||
test_exists_autocmd \
|
||||
test_expand \
|
||||
test_expand_dllpath \
|
||||
test_expr \
|
||||
@@ -2143,6 +2165,7 @@ test_arglist \
|
||||
test_fold \
|
||||
test_functions \
|
||||
test_ga \
|
||||
test_getcwd \
|
||||
test_gf \
|
||||
test_glob2regpat \
|
||||
test_global \
|
||||
@@ -2171,6 +2194,7 @@ test_arglist \
|
||||
test_lua \
|
||||
test_makeencoding \
|
||||
test_man \
|
||||
test_maparg \
|
||||
test_mapping \
|
||||
test_marks \
|
||||
test_match \
|
||||
@@ -2189,6 +2213,7 @@ test_arglist \
|
||||
test_partial \
|
||||
test_paste \
|
||||
test_perl \
|
||||
test_plus_arg_edit \
|
||||
test_popup \
|
||||
test_profile \
|
||||
test_put \
|
||||
@@ -2199,6 +2224,7 @@ test_arglist \
|
||||
test_quickfix \
|
||||
test_quotestar \
|
||||
test_recover \
|
||||
test_regex_char_classes \
|
||||
test_regexp_latin \
|
||||
test_regexp_utf8 \
|
||||
test_reltime \
|
||||
@@ -2207,6 +2233,7 @@ test_arglist \
|
||||
test_search \
|
||||
test_searchpos \
|
||||
test_set \
|
||||
test_sha256 \
|
||||
test_signs \
|
||||
test_smartindent \
|
||||
test_sort \
|
||||
@@ -2233,6 +2260,7 @@ test_arglist \
|
||||
test_unlet \
|
||||
test_usercommands \
|
||||
test_utf8 \
|
||||
test_utf8_comparisons \
|
||||
test_viminfo \
|
||||
test_vimscript \
|
||||
test_visual \
|
||||
@@ -2788,7 +2816,7 @@ SHADOWDIR = shadow
|
||||
|
||||
shadow: runtime pixmaps
|
||||
$(MKDIR_P) $(SHADOWDIR)
|
||||
cd $(SHADOWDIR); ln -s ../*.[chm] ../*.in ../*.sh ../*.xs ../*.xbm ../gui_gtk_res.xml ../toolcheck ../proto ../vimtutor ../gvimtutor ../install-sh .
|
||||
cd $(SHADOWDIR); ln -s ../*.[chm] ../*.in ../*.sh ../*.xs ../*.xbm ../gui_gtk_res.xml ../toolcheck ../proto ../libvterm ../vimtutor ../gvimtutor ../install-sh .
|
||||
mkdir $(SHADOWDIR)/auto
|
||||
cd $(SHADOWDIR)/auto; ln -s ../../auto/configure .
|
||||
$(MKDIR_P) $(SHADOWDIR)/po
|
||||
@@ -3226,6 +3254,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
|
||||
|
||||
@@ -3253,6 +3284,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
|
||||
###
|
||||
@@ -3397,7 +3456,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 \
|
||||
@@ -3501,7 +3560,7 @@ objects/option.o: option.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h
|
||||
objects/os_unix.o: os_unix.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 if_mzsch.h os_unixx.h
|
||||
globals.h farsi.h arabic.h os_unixx.h
|
||||
objects/pathdef.o: auto/pathdef.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 \
|
||||
@@ -3550,6 +3609,10 @@ objects/term.o: term.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
|
||||
objects/terminal.o: terminal.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
|
||||
objects/ui.o: ui.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 \
|
||||
@@ -3659,7 +3722,7 @@ objects/json_test.o: json_test.c main.c vim.h auto/config.h feature.h os_unix.h
|
||||
objects/kword_test.o: kword_test.c main.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 charset.c mbyte.c
|
||||
proto.h globals.h farsi.h arabic.h charset.c
|
||||
objects/memfile_test.o: memfile_test.c main.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 \
|
||||
@@ -3679,7 +3742,7 @@ objects/if_lua.o: if_lua.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h
|
||||
objects/if_mzsch.o: if_mzsch.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 if_mzsch.h mzscheme_base.c
|
||||
proto.h globals.h farsi.h arabic.h if_mzsch.h
|
||||
objects/if_perl.o: auto/if_perl.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 \
|
||||
|
||||
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.], ,
|
||||
|
||||
@@ -7329,7 +7329,9 @@ oneleft(void)
|
||||
#ifdef FEAT_VIRTUALEDIT
|
||||
if (virtual_active())
|
||||
{
|
||||
# ifdef FEAT_LINEBREAK
|
||||
int width;
|
||||
# endif
|
||||
int v = getviscol();
|
||||
|
||||
if (v == 0)
|
||||
|
||||
@@ -3191,7 +3191,11 @@ f_feedkeys(typval_T *argvars, typval_T *rettv UNUSED)
|
||||
ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE),
|
||||
insert ? 0 : typebuf.tb_len, !typed, FALSE);
|
||||
vim_free(keys_esc);
|
||||
if (vgetc_busy || timer_busy)
|
||||
if (vgetc_busy
|
||||
#ifdef FEAT_TIMERS
|
||||
|| timer_busy
|
||||
#endif
|
||||
)
|
||||
typebuf_was_filled = TRUE;
|
||||
if (execute)
|
||||
{
|
||||
@@ -5866,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
|
||||
@@ -12394,6 +12401,7 @@ f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
|
||||
{
|
||||
char_u *name = (char_u *)"";
|
||||
int val;
|
||||
static int save_starting = -1;
|
||||
|
||||
if (argvars[0].v_type != VAR_STRING
|
||||
|| (argvars[1].v_type) != VAR_NUMBER)
|
||||
@@ -12407,10 +12415,29 @@ f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
|
||||
disable_redraw_for_testing = val;
|
||||
else if (STRCMP(name, (char_u *)"char_avail") == 0)
|
||||
disable_char_avail_for_testing = val;
|
||||
else if (STRCMP(name, (char_u *)"starting") == 0)
|
||||
{
|
||||
if (val)
|
||||
{
|
||||
if (save_starting < 0)
|
||||
save_starting = starting;
|
||||
starting = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
starting = save_starting;
|
||||
save_starting = -1;
|
||||
}
|
||||
}
|
||||
else if (STRCMP(name, (char_u *)"ALL") == 0)
|
||||
{
|
||||
disable_char_avail_for_testing = FALSE;
|
||||
disable_redraw_for_testing = FALSE;
|
||||
if (save_starting >= 0)
|
||||
{
|
||||
starting = save_starting;
|
||||
save_starting = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
EMSG2(_(e_invarg2), name);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3968,8 +3968,8 @@ do_ecmd(
|
||||
* <VN> We could instead free the synblock
|
||||
* and re-attach to buffer, perhaps.
|
||||
*/
|
||||
if (curwin->w_buffer != NULL
|
||||
&& curwin->w_s == &(curwin->w_buffer->b_s))
|
||||
if (curwin->w_buffer == NULL
|
||||
|| curwin->w_s == &(curwin->w_buffer->b_s))
|
||||
curwin->w_s = &(buf->b_s);
|
||||
#endif
|
||||
curwin->w_buffer = buf;
|
||||
@@ -6835,7 +6835,11 @@ fix_help_buffer(void)
|
||||
#ifdef FEAT_AUTOCMD
|
||||
/* Set filetype to "help" if still needed. */
|
||||
if (STRCMP(curbuf->b_p_ft, "help") != 0)
|
||||
{
|
||||
++curbuf_lock;
|
||||
set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL);
|
||||
--curbuf_lock;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_SYN_HL
|
||||
|
||||
@@ -51,7 +51,9 @@
|
||||
#define BUFUNL 0x20000L /* accepts unlisted buffer too */
|
||||
#define ARGOPT 0x40000L /* allow "++opt=val" argument */
|
||||
#define SBOXOK 0x80000L /* allowed in the sandbox */
|
||||
#define CMDWIN 0x100000L /* allowed in cmdline window */
|
||||
#define CMDWIN 0x100000L /* allowed in cmdline window; when missing
|
||||
* disallows editing another buffer when
|
||||
* curbuf_lock is set */
|
||||
#define MODIFY 0x200000L /* forbidden in non-'modifiable' buffer */
|
||||
#define EXFLAGS 0x400000L /* allow flags after count in argument */
|
||||
#define FILES (XFILE | EXTRA) /* multiple extra files allowed */
|
||||
@@ -1176,7 +1178,7 @@ EX(CMD_registers, "registers", ex_display,
|
||||
EXTRA|NOTRLCOM|TRLBAR|CMDWIN,
|
||||
ADDR_LINES),
|
||||
EX(CMD_resize, "resize", ex_resize,
|
||||
RANGE|NOTADR|TRLBAR|WORD1,
|
||||
RANGE|NOTADR|TRLBAR|WORD1|CMDWIN,
|
||||
ADDR_LINES),
|
||||
EX(CMD_retab, "retab", ex_retab,
|
||||
TRLBAR|RANGE|WHOLEFOLD|DFLALL|BANG|WORD1|CMDWIN|MODIFY,
|
||||
@@ -1481,6 +1483,9 @@ EX(CMD_tclfile, "tclfile", ex_tclfile,
|
||||
EX(CMD_tearoff, "tearoff", ex_tearoff,
|
||||
NEEDARG|EXTRA|TRLBAR|NOTRLCOM|CMDWIN,
|
||||
ADDR_LINES),
|
||||
EX(CMD_terminal, "terminal", ex_terminal,
|
||||
RANGE|NOTADR|EXTRA|TRLBAR|CMDWIN,
|
||||
ADDR_OTHER),
|
||||
EX(CMD_tfirst, "tfirst", ex_tag,
|
||||
RANGE|NOTADR|BANG|TRLBAR|ZEROR,
|
||||
ADDR_LINES),
|
||||
@@ -1620,7 +1625,7 @@ EX(CMD_winsize, "winsize", ex_winsize,
|
||||
EXTRA|NEEDARG|TRLBAR,
|
||||
ADDR_LINES),
|
||||
EX(CMD_wincmd, "wincmd", ex_wincmd,
|
||||
NEEDARG|WORD1|RANGE|NOTADR,
|
||||
NEEDARG|WORD1|RANGE|NOTADR|CMDWIN,
|
||||
ADDR_WINDOWS),
|
||||
EX(CMD_windo, "windo", ex_listdo,
|
||||
NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL,
|
||||
|
||||
@@ -1183,6 +1183,7 @@ timer_callback(timer_T *timer)
|
||||
/*
|
||||
* Call timers that are due.
|
||||
* Return the time in msec until the next timer is due.
|
||||
* Returns -1 if there are no pending timers.
|
||||
*/
|
||||
long
|
||||
check_due_timer(void)
|
||||
@@ -1196,7 +1197,13 @@ check_due_timer(void)
|
||||
long current_id = last_timer_id;
|
||||
# ifdef WIN3264
|
||||
LARGE_INTEGER fr;
|
||||
# endif
|
||||
|
||||
/* Don't run any timers while exiting or dealing with an error. */
|
||||
if (exiting || aborting())
|
||||
return next_due;
|
||||
|
||||
# ifdef WIN3264
|
||||
QueryPerformanceFrequency(&fr);
|
||||
# endif
|
||||
profile_start(&now);
|
||||
@@ -1211,9 +1218,13 @@ check_due_timer(void)
|
||||
{
|
||||
int save_timer_busy = timer_busy;
|
||||
int save_vgetc_busy = vgetc_busy;
|
||||
int did_emsg_save = did_emsg;
|
||||
int called_emsg_save = called_emsg;
|
||||
int did_throw_save = did_throw;
|
||||
|
||||
timer_busy = timer_busy > 0 || vgetc_busy > 0;
|
||||
vgetc_busy = 0;
|
||||
called_emsg = FALSE;
|
||||
timer->tr_firing = TRUE;
|
||||
timer_callback(timer);
|
||||
timer->tr_firing = FALSE;
|
||||
@@ -1221,10 +1232,19 @@ check_due_timer(void)
|
||||
did_one = TRUE;
|
||||
timer_busy = save_timer_busy;
|
||||
vgetc_busy = save_vgetc_busy;
|
||||
if (called_emsg)
|
||||
{
|
||||
++timer->tr_emsg_count;
|
||||
if (!did_throw_save && did_throw && current_exception != NULL)
|
||||
discard_current_exception();
|
||||
}
|
||||
did_emsg = did_emsg_save;
|
||||
called_emsg = called_emsg_save;
|
||||
|
||||
/* Only fire the timer again if it repeats and stop_timer() wasn't
|
||||
* called while inside the callback (tr_id == -1). */
|
||||
if (timer->tr_repeat != 0 && timer->tr_id != -1)
|
||||
if (timer->tr_repeat != 0 && timer->tr_id != -1
|
||||
&& timer->tr_emsg_count < 3)
|
||||
{
|
||||
profile_setlimit(timer->tr_interval, &timer->tr_due);
|
||||
this_due = GET_TIMEDIFF(timer, now);
|
||||
@@ -3285,19 +3305,6 @@ source_callback(char_u *fname, void *cookie UNUSED)
|
||||
(void)do_source(fname, FALSE, DOSO_NONE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Source the file "name" from all directories in 'runtimepath'.
|
||||
* "name" can contain wildcards.
|
||||
* When "flags" has DIP_ALL: source all files, otherwise only the first one.
|
||||
*
|
||||
* return FAIL when no file could be sourced, OK otherwise.
|
||||
*/
|
||||
int
|
||||
source_runtime(char_u *name, int flags)
|
||||
{
|
||||
return do_in_runtimepath(name, flags, source_callback, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the file "name" in all directories in "path" and invoke
|
||||
* "callback(fname, cookie)".
|
||||
@@ -3435,18 +3442,19 @@ do_in_path(
|
||||
}
|
||||
|
||||
/*
|
||||
* Find "name" in 'runtimepath'. When found, invoke the callback function for
|
||||
* Find "name" in "path". When found, invoke the callback function for
|
||||
* it: callback(fname, "cookie")
|
||||
* When "flags" has DIP_ALL repeat for all matches, otherwise only the first
|
||||
* one is used.
|
||||
* Returns OK when at least one match found, FAIL otherwise.
|
||||
*
|
||||
* If "name" is NULL calls callback for each entry in runtimepath. Cookie is
|
||||
* If "name" is NULL calls callback for each entry in "path". Cookie is
|
||||
* passed by reference in this case, setting it to NULL indicates that callback
|
||||
* has done its job.
|
||||
*/
|
||||
int
|
||||
do_in_runtimepath(
|
||||
static int
|
||||
do_in_path_and_pp(
|
||||
char_u *path,
|
||||
char_u *name,
|
||||
int flags,
|
||||
void (*callback)(char_u *fname, void *ck),
|
||||
@@ -3459,7 +3467,7 @@ do_in_runtimepath(
|
||||
char *opt_dir = "pack/*/opt/*/%s";
|
||||
|
||||
if ((flags & DIP_NORTP) == 0)
|
||||
done = do_in_path(p_rtp, name, flags, callback, cookie);
|
||||
done = do_in_path(path, name, flags, callback, cookie);
|
||||
|
||||
if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START))
|
||||
{
|
||||
@@ -3486,6 +3494,42 @@ do_in_runtimepath(
|
||||
return done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Just like do_in_path_and_pp(), using 'runtimepath' for "path".
|
||||
*/
|
||||
int
|
||||
do_in_runtimepath(
|
||||
char_u *name,
|
||||
int flags,
|
||||
void (*callback)(char_u *fname, void *ck),
|
||||
void *cookie)
|
||||
{
|
||||
return do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
|
||||
}
|
||||
|
||||
/*
|
||||
* Source the file "name" from all directories in 'runtimepath'.
|
||||
* "name" can contain wildcards.
|
||||
* When "flags" has DIP_ALL: source all files, otherwise only the first one.
|
||||
*
|
||||
* return FAIL when no file could be sourced, OK otherwise.
|
||||
*/
|
||||
int
|
||||
source_runtime(char_u *name, int flags)
|
||||
{
|
||||
return source_in_path(p_rtp, name, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Just like source_runtime(), but use "path" instead of 'runtimepath'.
|
||||
*/
|
||||
int
|
||||
source_in_path(char_u *path, char_u *name, int flags)
|
||||
{
|
||||
return do_in_path_and_pp(path, name, flags, source_callback, NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Expand wildcards in "pat" and invoke do_source() for each match.
|
||||
*/
|
||||
|
||||
@@ -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[].
|
||||
@@ -7268,8 +7271,11 @@ ex_quit(exarg_T *eap)
|
||||
apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf);
|
||||
/* Refuse to quit when locked or when the buffer in the last window is
|
||||
* being closed (can only happen in autocommands). */
|
||||
if (curbuf_locked() || (wp->w_buffer->b_nwindows == 1
|
||||
&& wp->w_buffer->b_locked > 0))
|
||||
if (curbuf_locked()
|
||||
# ifdef FEAT_WINDOWS
|
||||
|| !win_valid(wp)
|
||||
# endif
|
||||
|| (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0))
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -640,8 +640,11 @@ discard_exception(except_T *excp, int was_finished)
|
||||
void
|
||||
discard_current_exception(void)
|
||||
{
|
||||
discard_exception(current_exception, FALSE);
|
||||
current_exception = NULL;
|
||||
if (current_exception != NULL)
|
||||
{
|
||||
discard_exception(current_exception, FALSE);
|
||||
current_exception = NULL;
|
||||
}
|
||||
did_throw = FALSE;
|
||||
need_rethrow = FALSE;
|
||||
}
|
||||
@@ -1978,7 +1981,10 @@ enter_cleanup(cleanup_T *csp)
|
||||
* there is an extra instance for every call of do_cmdline(), anyway.
|
||||
*/
|
||||
if (did_throw || need_rethrow)
|
||||
{
|
||||
csp->exception = current_exception;
|
||||
current_exception = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
csp->exception = NULL;
|
||||
|
||||
@@ -1492,7 +1492,7 @@ getcmdline(
|
||||
if (c != NUL)
|
||||
{
|
||||
if (c == firstc || vim_strchr((char_u *)(
|
||||
p_magic ? "\\^$.*[" : "\\^$"), c)
|
||||
p_magic ? "\\~^$.*[" : "\\^$"), c)
|
||||
!= NULL)
|
||||
{
|
||||
/* put a backslash before special
|
||||
@@ -1708,6 +1708,14 @@ getcmdline(
|
||||
search_start = t;
|
||||
(void)decl(&search_start);
|
||||
}
|
||||
else if (c == Ctrl_G && firstc == '?')
|
||||
{
|
||||
/* move just after the current match, so that
|
||||
* when nv_search finishes the cursor will be
|
||||
* put back on the match */
|
||||
search_start = t;
|
||||
(void)incl(&search_start);
|
||||
}
|
||||
if (LT_POS(t, search_start) && c == Ctrl_G)
|
||||
{
|
||||
/* wrap around */
|
||||
@@ -6878,6 +6886,8 @@ open_cmdwin(void)
|
||||
# ifdef FEAT_AUTOCMD
|
||||
/* Do execute autocommands for setting the filetype (load syntax). */
|
||||
unblock_autocmds();
|
||||
/* But don't allow switching to another buffer. */
|
||||
++curbuf_lock;
|
||||
# endif
|
||||
|
||||
/* Showing the prompt may have set need_wait_return, reset it. */
|
||||
@@ -6893,6 +6903,9 @@ open_cmdwin(void)
|
||||
}
|
||||
set_option_value((char_u *)"ft", 0L, (char_u *)"vim", OPT_LOCAL);
|
||||
}
|
||||
# ifdef FEAT_AUTOCMD
|
||||
--curbuf_lock;
|
||||
# endif
|
||||
|
||||
/* Reset 'textwidth' after setting 'filetype' (the Vim filetype plugin
|
||||
* sets 'textwidth' to 78). */
|
||||
@@ -7029,7 +7042,13 @@ open_cmdwin(void)
|
||||
else
|
||||
ccline.cmdbuff = vim_strsave(ml_get_curline());
|
||||
if (ccline.cmdbuff == NULL)
|
||||
{
|
||||
ccline.cmdbuff = vim_strsave((char_u *)"");
|
||||
ccline.cmdlen = 0;
|
||||
ccline.cmdbufflen = 1;
|
||||
ccline.cmdpos = 0;
|
||||
cmdwin_result = Ctrl_C;
|
||||
}
|
||||
else
|
||||
{
|
||||
ccline.cmdlen = (int)STRLEN(ccline.cmdbuff);
|
||||
|
||||
@@ -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.
|
||||
|
||||
778
src/fileio.c
778
src/fileio.c
@@ -3166,6 +3166,7 @@ buf_write(
|
||||
int device = FALSE; /* writing to a device */
|
||||
stat_T st_old;
|
||||
int prev_got_int = got_int;
|
||||
int checking_conversion;
|
||||
int file_readonly = FALSE; /* overwritten file is read-only */
|
||||
static char *err_readonly = "is read-only (cannot override: \"W\" in 'cpoptions')";
|
||||
#if defined(UNIX) /*XXX fix me sometime? */
|
||||
@@ -4344,433 +4345,491 @@ buf_write(
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Open the file "wfname" for writing.
|
||||
* We may try to open the file twice: If we can't write to the
|
||||
* file and forceit is TRUE we delete the existing file and try to create
|
||||
* a new one. If this still fails we may have lost the original file!
|
||||
* (this may happen when the user reached his quotum for number of files).
|
||||
* Appending will fail if the file does not exist and forceit is FALSE.
|
||||
* If conversion is taking place, we may first pretend to write and check
|
||||
* for conversion errors. Then loop again to write for real.
|
||||
* When not doing conversion this writes for real right away.
|
||||
*/
|
||||
while ((fd = mch_open((char *)wfname, O_WRONLY | O_EXTRA | (append
|
||||
? (forceit ? (O_APPEND | O_CREAT) : O_APPEND)
|
||||
: (O_CREAT | O_TRUNC))
|
||||
, perm < 0 ? 0666 : (perm & 0777))) < 0)
|
||||
for (checking_conversion = TRUE; ; checking_conversion = FALSE)
|
||||
{
|
||||
/*
|
||||
* A forced write will try to create a new file if the old one is
|
||||
* still readonly. This may also happen when the directory is
|
||||
* read-only. In that case the mch_remove() will fail.
|
||||
* There is no need to check conversion when:
|
||||
* - there is no conversion
|
||||
* - we make a backup file, that can be restored in case of conversion
|
||||
* failure.
|
||||
*/
|
||||
if (errmsg == NULL)
|
||||
{
|
||||
#ifdef UNIX
|
||||
stat_T st;
|
||||
|
||||
/* Don't delete the file when it's a hard or symbolic link. */
|
||||
if ((!newfile && st_old.st_nlink > 1)
|
||||
|| (mch_lstat((char *)fname, &st) == 0
|
||||
&& (st.st_dev != st_old.st_dev
|
||||
|| st.st_ino != st_old.st_ino)))
|
||||
errmsg = (char_u *)_("E166: Can't open linked file for writing");
|
||||
else
|
||||
#ifdef FEAT_MBYTE
|
||||
if (!converted || dobackup)
|
||||
#endif
|
||||
checking_conversion = FALSE;
|
||||
|
||||
if (checking_conversion)
|
||||
{
|
||||
/* Make sure we don't write anything. */
|
||||
fd = -1;
|
||||
write_info.bw_fd = fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Open the file "wfname" for writing.
|
||||
* We may try to open the file twice: If we can't write to the file
|
||||
* and forceit is TRUE we delete the existing file and try to
|
||||
* create a new one. If this still fails we may have lost the
|
||||
* original file! (this may happen when the user reached his
|
||||
* quotum for number of files).
|
||||
* Appending will fail if the file does not exist and forceit is
|
||||
* FALSE.
|
||||
*/
|
||||
while ((fd = mch_open((char *)wfname, O_WRONLY | O_EXTRA | (append
|
||||
? (forceit ? (O_APPEND | O_CREAT) : O_APPEND)
|
||||
: (O_CREAT | O_TRUNC))
|
||||
, perm < 0 ? 0666 : (perm & 0777))) < 0)
|
||||
{
|
||||
errmsg = (char_u *)_("E212: Can't open file for writing");
|
||||
if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL
|
||||
&& perm >= 0)
|
||||
/*
|
||||
* A forced write will try to create a new file if the old one
|
||||
* is still readonly. This may also happen when the directory
|
||||
* is read-only. In that case the mch_remove() will fail.
|
||||
*/
|
||||
if (errmsg == NULL)
|
||||
{
|
||||
#ifdef UNIX
|
||||
/* we write to the file, thus it should be marked
|
||||
writable after all */
|
||||
if (!(perm & 0200))
|
||||
made_writable = TRUE;
|
||||
perm |= 0200;
|
||||
if (st_old.st_uid != getuid() || st_old.st_gid != getgid())
|
||||
perm &= 0777;
|
||||
stat_T st;
|
||||
|
||||
/* Don't delete the file when it's a hard or symbolic link.
|
||||
*/
|
||||
if ((!newfile && st_old.st_nlink > 1)
|
||||
|| (mch_lstat((char *)fname, &st) == 0
|
||||
&& (st.st_dev != st_old.st_dev
|
||||
|| st.st_ino != st_old.st_ino)))
|
||||
errmsg = (char_u *)_("E166: Can't open linked file for writing");
|
||||
else
|
||||
#endif
|
||||
if (!append) /* don't remove when appending */
|
||||
mch_remove(wfname);
|
||||
continue;
|
||||
{
|
||||
errmsg = (char_u *)_("E212: Can't open file for writing");
|
||||
if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL
|
||||
&& perm >= 0)
|
||||
{
|
||||
#ifdef UNIX
|
||||
/* we write to the file, thus it should be marked
|
||||
writable after all */
|
||||
if (!(perm & 0200))
|
||||
made_writable = TRUE;
|
||||
perm |= 0200;
|
||||
if (st_old.st_uid != getuid()
|
||||
|| st_old.st_gid != getgid())
|
||||
perm &= 0777;
|
||||
#endif
|
||||
if (!append) /* don't remove when appending */
|
||||
mch_remove(wfname);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
restore_backup:
|
||||
{
|
||||
stat_T st;
|
||||
|
||||
/*
|
||||
* If we failed to open the file, we don't need a backup. Throw it
|
||||
* away. If we moved or removed the original file try to put the
|
||||
* backup in its place.
|
||||
*/
|
||||
if (backup != NULL && wfname == fname)
|
||||
{
|
||||
if (backup_copy)
|
||||
{
|
||||
stat_T st;
|
||||
|
||||
/*
|
||||
* There is a small chance that we removed the original,
|
||||
* try to move the copy in its place.
|
||||
* This may not work if the vim_rename() fails.
|
||||
* In that case we leave the copy around.
|
||||
* If we failed to open the file, we don't need a backup.
|
||||
* Throw it away. If we moved or removed the original file
|
||||
* try to put the backup in its place.
|
||||
*/
|
||||
/* If file does not exist, put the copy in its place */
|
||||
if (mch_stat((char *)fname, &st) < 0)
|
||||
vim_rename(backup, fname);
|
||||
/* if original file does exist throw away the copy */
|
||||
if (mch_stat((char *)fname, &st) >= 0)
|
||||
mch_remove(backup);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* try to put the original file back */
|
||||
vim_rename(backup, fname);
|
||||
}
|
||||
}
|
||||
if (backup != NULL && wfname == fname)
|
||||
{
|
||||
if (backup_copy)
|
||||
{
|
||||
/*
|
||||
* There is a small chance that we removed the
|
||||
* original, try to move the copy in its place.
|
||||
* This may not work if the vim_rename() fails.
|
||||
* In that case we leave the copy around.
|
||||
*/
|
||||
/* If file does not exist, put the copy in its
|
||||
* place */
|
||||
if (mch_stat((char *)fname, &st) < 0)
|
||||
vim_rename(backup, fname);
|
||||
/* if original file does exist throw away the copy
|
||||
*/
|
||||
if (mch_stat((char *)fname, &st) >= 0)
|
||||
mch_remove(backup);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* try to put the original file back */
|
||||
vim_rename(backup, fname);
|
||||
}
|
||||
}
|
||||
|
||||
/* if original file no longer exists give an extra warning */
|
||||
if (!newfile && mch_stat((char *)fname, &st) < 0)
|
||||
end = 0;
|
||||
}
|
||||
/* if original file no longer exists give an extra warning
|
||||
*/
|
||||
if (!newfile && mch_stat((char *)fname, &st) < 0)
|
||||
end = 0;
|
||||
}
|
||||
|
||||
#ifdef FEAT_MBYTE
|
||||
if (wfname != fname)
|
||||
vim_free(wfname);
|
||||
if (wfname != fname)
|
||||
vim_free(wfname);
|
||||
#endif
|
||||
goto fail;
|
||||
}
|
||||
errmsg = NULL;
|
||||
goto fail;
|
||||
}
|
||||
write_info.bw_fd = fd;
|
||||
|
||||
#if defined(MACOS_CLASSIC) || defined(WIN3264)
|
||||
/* TODO: Is it need for MACOS_X? (Dany) */
|
||||
/*
|
||||
* On macintosh copy the original files attributes (i.e. the backup)
|
||||
* This is done in order to preserve the resource fork and the
|
||||
* Finder attribute (label, comments, custom icons, file creator)
|
||||
*/
|
||||
if (backup != NULL && overwriting && !append)
|
||||
{
|
||||
if (backup_copy)
|
||||
(void)mch_copy_file_attribute(wfname, backup);
|
||||
else
|
||||
(void)mch_copy_file_attribute(backup, wfname);
|
||||
}
|
||||
/* TODO: Is it need for MACOS_X? (Dany) */
|
||||
/*
|
||||
* On macintosh copy the original files attributes (i.e. the backup)
|
||||
* This is done in order to preserve the resource fork and the
|
||||
* Finder attribute (label, comments, custom icons, file creator)
|
||||
*/
|
||||
if (backup != NULL && overwriting && !append)
|
||||
{
|
||||
if (backup_copy)
|
||||
(void)mch_copy_file_attribute(wfname, backup);
|
||||
else
|
||||
(void)mch_copy_file_attribute(backup, wfname);
|
||||
}
|
||||
|
||||
if (!overwriting && !append)
|
||||
{
|
||||
if (buf->b_ffname != NULL)
|
||||
(void)mch_copy_file_attribute(buf->b_ffname, wfname);
|
||||
/* Should copy resource fork */
|
||||
}
|
||||
if (!overwriting && !append)
|
||||
{
|
||||
if (buf->b_ffname != NULL)
|
||||
(void)mch_copy_file_attribute(buf->b_ffname, wfname);
|
||||
/* Should copy resource fork */
|
||||
}
|
||||
#endif
|
||||
|
||||
write_info.bw_fd = fd;
|
||||
|
||||
#ifdef FEAT_CRYPT
|
||||
if (*buf->b_p_key != NUL && !filtering)
|
||||
{
|
||||
char_u *header;
|
||||
int header_len;
|
||||
|
||||
buf->b_cryptstate = crypt_create_for_writing(crypt_get_method_nr(buf),
|
||||
buf->b_p_key, &header, &header_len);
|
||||
if (buf->b_cryptstate == NULL || header == NULL)
|
||||
end = 0;
|
||||
else
|
||||
{
|
||||
/* Write magic number, so that Vim knows how this file is
|
||||
* encrypted when reading it back. */
|
||||
write_info.bw_buf = header;
|
||||
write_info.bw_len = header_len;
|
||||
write_info.bw_flags = FIO_NOCONVERT;
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
end = 0;
|
||||
wb_flags |= FIO_ENCRYPTED;
|
||||
vim_free(header);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
write_info.bw_buf = buffer;
|
||||
nchars = 0;
|
||||
|
||||
/* use "++bin", "++nobin" or 'binary' */
|
||||
if (eap != NULL && eap->force_bin != 0)
|
||||
write_bin = (eap->force_bin == FORCE_BIN);
|
||||
else
|
||||
write_bin = buf->b_p_bin;
|
||||
|
||||
#ifdef FEAT_MBYTE
|
||||
/*
|
||||
* The BOM is written just after the encryption magic number.
|
||||
* Skip it when appending and the file already existed, the BOM only makes
|
||||
* sense at the start of the file.
|
||||
*/
|
||||
if (buf->b_p_bomb && !write_bin && (!append || perm < 0))
|
||||
{
|
||||
write_info.bw_len = make_bom(buffer, fenc);
|
||||
if (write_info.bw_len > 0)
|
||||
{
|
||||
/* don't convert, do encryption */
|
||||
write_info.bw_flags = FIO_NOCONVERT | wb_flags;
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
end = 0;
|
||||
else
|
||||
nchars += write_info.bw_len;
|
||||
}
|
||||
}
|
||||
write_info.bw_start_lnum = start;
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_PERSISTENT_UNDO
|
||||
write_undo_file = (buf->b_p_udf && overwriting && !append
|
||||
&& !filtering && reset_changed);
|
||||
if (write_undo_file)
|
||||
/* Prepare for computing the hash value of the text. */
|
||||
sha256_start(&sha_ctx);
|
||||
#endif
|
||||
|
||||
write_info.bw_len = bufsize;
|
||||
#ifdef HAS_BW_FLAGS
|
||||
write_info.bw_flags = wb_flags;
|
||||
#endif
|
||||
fileformat = get_fileformat_force(buf, eap);
|
||||
s = buffer;
|
||||
len = 0;
|
||||
for (lnum = start; lnum <= end; ++lnum)
|
||||
{
|
||||
/*
|
||||
* The next while loop is done once for each character written.
|
||||
* Keep it fast!
|
||||
*/
|
||||
ptr = ml_get_buf(buf, lnum, FALSE) - 1;
|
||||
#ifdef FEAT_PERSISTENT_UNDO
|
||||
if (write_undo_file)
|
||||
sha256_update(&sha_ctx, ptr + 1, (UINT32_T)(STRLEN(ptr + 1) + 1));
|
||||
#endif
|
||||
while ((c = *++ptr) != NUL)
|
||||
{
|
||||
if (c == NL)
|
||||
*s = NUL; /* replace newlines with NULs */
|
||||
else if (c == CAR && fileformat == EOL_MAC)
|
||||
*s = NL; /* Mac: replace CRs with NLs */
|
||||
else
|
||||
*s = c;
|
||||
++s;
|
||||
if (++len != bufsize)
|
||||
continue;
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
if (*buf->b_p_key != NUL && !filtering)
|
||||
{
|
||||
end = 0; /* write error: break loop */
|
||||
break;
|
||||
}
|
||||
nchars += bufsize;
|
||||
s = buffer;
|
||||
len = 0;
|
||||
#ifdef FEAT_MBYTE
|
||||
write_info.bw_start_lnum = lnum;
|
||||
#endif
|
||||
}
|
||||
/* write failed or last line has no EOL: stop here */
|
||||
if (end == 0
|
||||
|| (lnum == end
|
||||
&& (write_bin || !buf->b_p_fixeol)
|
||||
&& (lnum == buf->b_no_eol_lnum
|
||||
|| (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol))))
|
||||
{
|
||||
++lnum; /* written the line, count it */
|
||||
no_eol = TRUE;
|
||||
break;
|
||||
}
|
||||
if (fileformat == EOL_UNIX)
|
||||
*s++ = NL;
|
||||
else
|
||||
{
|
||||
*s++ = CAR; /* EOL_MAC or EOL_DOS: write CR */
|
||||
if (fileformat == EOL_DOS) /* write CR-NL */
|
||||
{
|
||||
if (++len == bufsize)
|
||||
char_u *header;
|
||||
int header_len;
|
||||
|
||||
buf->b_cryptstate = crypt_create_for_writing(
|
||||
crypt_get_method_nr(buf),
|
||||
buf->b_p_key, &header, &header_len);
|
||||
if (buf->b_cryptstate == NULL || header == NULL)
|
||||
end = 0;
|
||||
else
|
||||
{
|
||||
/* Write magic number, so that Vim knows how this file is
|
||||
* encrypted when reading it back. */
|
||||
write_info.bw_buf = header;
|
||||
write_info.bw_len = header_len;
|
||||
write_info.bw_flags = FIO_NOCONVERT;
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
{
|
||||
end = 0; /* write error: break loop */
|
||||
break;
|
||||
}
|
||||
nchars += bufsize;
|
||||
s = buffer;
|
||||
len = 0;
|
||||
end = 0;
|
||||
wb_flags |= FIO_ENCRYPTED;
|
||||
vim_free(header);
|
||||
}
|
||||
*s++ = NL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (++len == bufsize && end)
|
||||
{
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
{
|
||||
end = 0; /* write error: break loop */
|
||||
break;
|
||||
}
|
||||
nchars += bufsize;
|
||||
s = buffer;
|
||||
len = 0;
|
||||
errmsg = NULL;
|
||||
|
||||
ui_breakcheck();
|
||||
if (got_int)
|
||||
{
|
||||
end = 0; /* Interrupted, break loop */
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef VMS
|
||||
write_info.bw_buf = buffer;
|
||||
nchars = 0;
|
||||
|
||||
/* use "++bin", "++nobin" or 'binary' */
|
||||
if (eap != NULL && eap->force_bin != 0)
|
||||
write_bin = (eap->force_bin == FORCE_BIN);
|
||||
else
|
||||
write_bin = buf->b_p_bin;
|
||||
|
||||
#ifdef FEAT_MBYTE
|
||||
/*
|
||||
* On VMS there is a problem: newlines get added when writing blocks
|
||||
* at a time. Fix it by writing a line at a time.
|
||||
* This is much slower!
|
||||
* Explanation: VAX/DECC RTL insists that records in some RMS
|
||||
* structures end with a newline (carriage return) character, and if
|
||||
* they don't it adds one.
|
||||
* With other RMS structures it works perfect without this fix.
|
||||
* The BOM is written just after the encryption magic number.
|
||||
* Skip it when appending and the file already existed, the BOM only
|
||||
* makes sense at the start of the file.
|
||||
*/
|
||||
if (buf->b_fab_rfm == FAB$C_VFC
|
||||
|| ((buf->b_fab_rat & (FAB$M_FTN | FAB$M_CR)) != 0))
|
||||
if (buf->b_p_bomb && !write_bin && (!append || perm < 0))
|
||||
{
|
||||
int b2write;
|
||||
|
||||
buf->b_fab_mrs = (buf->b_fab_mrs == 0
|
||||
? MIN(4096, bufsize)
|
||||
: MIN(buf->b_fab_mrs, bufsize));
|
||||
|
||||
b2write = len;
|
||||
while (b2write > 0)
|
||||
write_info.bw_len = make_bom(buffer, fenc);
|
||||
if (write_info.bw_len > 0)
|
||||
{
|
||||
write_info.bw_len = MIN(b2write, buf->b_fab_mrs);
|
||||
/* don't convert, do encryption */
|
||||
write_info.bw_flags = FIO_NOCONVERT | wb_flags;
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
end = 0;
|
||||
else
|
||||
nchars += write_info.bw_len;
|
||||
}
|
||||
}
|
||||
write_info.bw_start_lnum = start;
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_PERSISTENT_UNDO
|
||||
write_undo_file = (buf->b_p_udf
|
||||
&& overwriting
|
||||
&& !append
|
||||
&& !filtering
|
||||
&& reset_changed
|
||||
&& !checking_conversion);
|
||||
if (write_undo_file)
|
||||
/* Prepare for computing the hash value of the text. */
|
||||
sha256_start(&sha_ctx);
|
||||
#endif
|
||||
|
||||
write_info.bw_len = bufsize;
|
||||
#ifdef HAS_BW_FLAGS
|
||||
write_info.bw_flags = wb_flags;
|
||||
#endif
|
||||
fileformat = get_fileformat_force(buf, eap);
|
||||
s = buffer;
|
||||
len = 0;
|
||||
for (lnum = start; lnum <= end; ++lnum)
|
||||
{
|
||||
/*
|
||||
* The next while loop is done once for each character written.
|
||||
* Keep it fast!
|
||||
*/
|
||||
ptr = ml_get_buf(buf, lnum, FALSE) - 1;
|
||||
#ifdef FEAT_PERSISTENT_UNDO
|
||||
if (write_undo_file)
|
||||
sha256_update(&sha_ctx, ptr + 1,
|
||||
(UINT32_T)(STRLEN(ptr + 1) + 1));
|
||||
#endif
|
||||
while ((c = *++ptr) != NUL)
|
||||
{
|
||||
if (c == NL)
|
||||
*s = NUL; /* replace newlines with NULs */
|
||||
else if (c == CAR && fileformat == EOL_MAC)
|
||||
*s = NL; /* Mac: replace CRs with NLs */
|
||||
else
|
||||
*s = c;
|
||||
++s;
|
||||
if (++len != bufsize)
|
||||
continue;
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
{
|
||||
end = 0;
|
||||
end = 0; /* write error: break loop */
|
||||
break;
|
||||
}
|
||||
b2write -= MIN(b2write, buf->b_fab_mrs);
|
||||
}
|
||||
write_info.bw_len = bufsize;
|
||||
nchars += len;
|
||||
s = buffer;
|
||||
len = 0;
|
||||
}
|
||||
nchars += bufsize;
|
||||
s = buffer;
|
||||
len = 0;
|
||||
#ifdef FEAT_MBYTE
|
||||
write_info.bw_start_lnum = lnum;
|
||||
#endif
|
||||
}
|
||||
if (len > 0 && end > 0)
|
||||
{
|
||||
write_info.bw_len = len;
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
end = 0; /* write error */
|
||||
nchars += len;
|
||||
}
|
||||
/* write failed or last line has no EOL: stop here */
|
||||
if (end == 0
|
||||
|| (lnum == end
|
||||
&& (write_bin || !buf->b_p_fixeol)
|
||||
&& (lnum == buf->b_no_eol_lnum
|
||||
|| (lnum == buf->b_ml.ml_line_count
|
||||
&& !buf->b_p_eol))))
|
||||
{
|
||||
++lnum; /* written the line, count it */
|
||||
no_eol = TRUE;
|
||||
break;
|
||||
}
|
||||
if (fileformat == EOL_UNIX)
|
||||
*s++ = NL;
|
||||
else
|
||||
{
|
||||
*s++ = CAR; /* EOL_MAC or EOL_DOS: write CR */
|
||||
if (fileformat == EOL_DOS) /* write CR-NL */
|
||||
{
|
||||
if (++len == bufsize)
|
||||
{
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
{
|
||||
end = 0; /* write error: break loop */
|
||||
break;
|
||||
}
|
||||
nchars += bufsize;
|
||||
s = buffer;
|
||||
len = 0;
|
||||
}
|
||||
*s++ = NL;
|
||||
}
|
||||
}
|
||||
if (++len == bufsize && end)
|
||||
{
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
{
|
||||
end = 0; /* write error: break loop */
|
||||
break;
|
||||
}
|
||||
nchars += bufsize;
|
||||
s = buffer;
|
||||
len = 0;
|
||||
|
||||
ui_breakcheck();
|
||||
if (got_int)
|
||||
{
|
||||
end = 0; /* Interrupted, break loop */
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef VMS
|
||||
/*
|
||||
* On VMS there is a problem: newlines get added when writing
|
||||
* blocks at a time. Fix it by writing a line at a time.
|
||||
* This is much slower!
|
||||
* Explanation: VAX/DECC RTL insists that records in some RMS
|
||||
* structures end with a newline (carriage return) character, and
|
||||
* if they don't it adds one.
|
||||
* With other RMS structures it works perfect without this fix.
|
||||
*/
|
||||
if (buf->b_fab_rfm == FAB$C_VFC
|
||||
|| ((buf->b_fab_rat & (FAB$M_FTN | FAB$M_CR)) != 0))
|
||||
{
|
||||
int b2write;
|
||||
|
||||
buf->b_fab_mrs = (buf->b_fab_mrs == 0
|
||||
? MIN(4096, bufsize)
|
||||
: MIN(buf->b_fab_mrs, bufsize));
|
||||
|
||||
b2write = len;
|
||||
while (b2write > 0)
|
||||
{
|
||||
write_info.bw_len = MIN(b2write, buf->b_fab_mrs);
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
{
|
||||
end = 0;
|
||||
break;
|
||||
}
|
||||
b2write -= MIN(b2write, buf->b_fab_mrs);
|
||||
}
|
||||
write_info.bw_len = bufsize;
|
||||
nchars += len;
|
||||
s = buffer;
|
||||
len = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (len > 0 && end > 0)
|
||||
{
|
||||
write_info.bw_len = len;
|
||||
if (buf_write_bytes(&write_info) == FAIL)
|
||||
end = 0; /* write error */
|
||||
nchars += len;
|
||||
}
|
||||
|
||||
/* Stop when writing done or an error was encountered. */
|
||||
if (!checking_conversion || end == 0)
|
||||
break;
|
||||
|
||||
/* If no error happened until now, writing should be ok, so loop to
|
||||
* really write the buffer. */
|
||||
}
|
||||
|
||||
#if defined(UNIX) && defined(HAVE_FSYNC)
|
||||
/* On many journalling file systems there is a bug that causes both the
|
||||
* original and the backup file to be lost when halting the system right
|
||||
* after writing the file. That's because only the meta-data is
|
||||
* journalled. Syncing the file slows down the system, but assures it has
|
||||
* been written to disk and we don't lose it.
|
||||
* For a device do try the fsync() but don't complain if it does not work
|
||||
* (could be a pipe).
|
||||
* If the 'fsync' option is FALSE, don't fsync(). Useful for laptops. */
|
||||
if (p_fs && fsync(fd) != 0 && !device)
|
||||
/* If we started writing, finish writing. Also when an error was
|
||||
* encountered. */
|
||||
if (!checking_conversion)
|
||||
{
|
||||
errmsg = (char_u *)_("E667: Fsync failed");
|
||||
end = 0;
|
||||
}
|
||||
#if defined(UNIX) && defined(HAVE_FSYNC)
|
||||
/*
|
||||
* On many journalling file systems there is a bug that causes both the
|
||||
* original and the backup file to be lost when halting the system
|
||||
* right after writing the file. That's because only the meta-data is
|
||||
* journalled. Syncing the file slows down the system, but assures it
|
||||
* has been written to disk and we don't lose it.
|
||||
* For a device do try the fsync() but don't complain if it does not
|
||||
* work (could be a pipe).
|
||||
* If the 'fsync' option is FALSE, don't fsync(). Useful for laptops.
|
||||
*/
|
||||
if (p_fs && fsync(fd) != 0 && !device)
|
||||
{
|
||||
errmsg = (char_u *)_("E667: Fsync failed");
|
||||
end = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
|
||||
/* Probably need to set the security context. */
|
||||
if (!backup_copy)
|
||||
mch_copy_sec(backup, wfname);
|
||||
/* Probably need to set the security context. */
|
||||
if (!backup_copy)
|
||||
mch_copy_sec(backup, wfname);
|
||||
#endif
|
||||
|
||||
#ifdef UNIX
|
||||
/* When creating a new file, set its owner/group to that of the original
|
||||
* file. Get the new device and inode number. */
|
||||
if (backup != NULL && !backup_copy)
|
||||
{
|
||||
# ifdef HAVE_FCHOWN
|
||||
stat_T st;
|
||||
|
||||
/* don't change the owner when it's already OK, some systems remove
|
||||
* permission or ACL stuff */
|
||||
if (mch_stat((char *)wfname, &st) < 0
|
||||
|| st.st_uid != st_old.st_uid
|
||||
|| st.st_gid != st_old.st_gid)
|
||||
/* When creating a new file, set its owner/group to that of the
|
||||
* original file. Get the new device and inode number. */
|
||||
if (backup != NULL && !backup_copy)
|
||||
{
|
||||
ignored = fchown(fd, st_old.st_uid, st_old.st_gid);
|
||||
if (perm >= 0) /* set permission again, may have changed */
|
||||
(void)mch_setperm(wfname, perm);
|
||||
}
|
||||
# ifdef HAVE_FCHOWN
|
||||
stat_T st;
|
||||
|
||||
/* don't change the owner when it's already OK, some systems remove
|
||||
* permission or ACL stuff */
|
||||
if (mch_stat((char *)wfname, &st) < 0
|
||||
|| st.st_uid != st_old.st_uid
|
||||
|| st.st_gid != st_old.st_gid)
|
||||
{
|
||||
ignored = fchown(fd, st_old.st_uid, st_old.st_gid);
|
||||
if (perm >= 0) /* set permission again, may have changed */
|
||||
(void)mch_setperm(wfname, perm);
|
||||
}
|
||||
# endif
|
||||
buf_setino(buf);
|
||||
}
|
||||
else if (!buf->b_dev_valid)
|
||||
/* Set the inode when creating a new file. */
|
||||
buf_setino(buf);
|
||||
buf_setino(buf);
|
||||
}
|
||||
else if (!buf->b_dev_valid)
|
||||
/* Set the inode when creating a new file. */
|
||||
buf_setino(buf);
|
||||
#endif
|
||||
|
||||
if (close(fd) != 0)
|
||||
{
|
||||
errmsg = (char_u *)_("E512: Close failed");
|
||||
end = 0;
|
||||
}
|
||||
if (close(fd) != 0)
|
||||
{
|
||||
errmsg = (char_u *)_("E512: Close failed");
|
||||
end = 0;
|
||||
}
|
||||
|
||||
#ifdef UNIX
|
||||
if (made_writable)
|
||||
perm &= ~0200; /* reset 'w' bit for security reasons */
|
||||
if (made_writable)
|
||||
perm &= ~0200; /* reset 'w' bit for security reasons */
|
||||
#endif
|
||||
if (perm >= 0) /* set perm. of new file same as old file */
|
||||
(void)mch_setperm(wfname, perm);
|
||||
if (perm >= 0) /* set perm. of new file same as old file */
|
||||
(void)mch_setperm(wfname, perm);
|
||||
#ifdef HAVE_ACL
|
||||
/*
|
||||
* Probably need to set the ACL before changing the user (can't set the
|
||||
* ACL on a file the user doesn't own).
|
||||
* On Solaris, with ZFS and the aclmode property set to "discard" (the
|
||||
* default), chmod() discards all part of a file's ACL that don't represent
|
||||
* the mode of the file. It's non-trivial for us to discover whether we're
|
||||
* in that situation, so we simply always re-set the ACL.
|
||||
*/
|
||||
/*
|
||||
* Probably need to set the ACL before changing the user (can't set the
|
||||
* ACL on a file the user doesn't own).
|
||||
* On Solaris, with ZFS and the aclmode property set to "discard" (the
|
||||
* default), chmod() discards all part of a file's ACL that don't
|
||||
* represent the mode of the file. It's non-trivial for us to discover
|
||||
* whether we're in that situation, so we simply always re-set the ACL.
|
||||
*/
|
||||
# ifndef HAVE_SOLARIS_ZFS_ACL
|
||||
if (!backup_copy)
|
||||
if (!backup_copy)
|
||||
# endif
|
||||
mch_set_acl(wfname, acl);
|
||||
mch_set_acl(wfname, acl);
|
||||
#endif
|
||||
#ifdef FEAT_CRYPT
|
||||
if (buf->b_cryptstate != NULL)
|
||||
{
|
||||
crypt_free_state(buf->b_cryptstate);
|
||||
buf->b_cryptstate = NULL;
|
||||
}
|
||||
if (buf->b_cryptstate != NULL)
|
||||
{
|
||||
crypt_free_state(buf->b_cryptstate);
|
||||
buf->b_cryptstate = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FEAT_MBYTE) && defined(FEAT_EVAL)
|
||||
if (wfname != fname)
|
||||
{
|
||||
/*
|
||||
* The file was written to a temp file, now it needs to be converted
|
||||
* with 'charconvert' to (overwrite) the output file.
|
||||
*/
|
||||
if (end != 0)
|
||||
if (wfname != fname)
|
||||
{
|
||||
if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc,
|
||||
wfname, fname) == FAIL)
|
||||
/*
|
||||
* The file was written to a temp file, now it needs to be
|
||||
* converted with 'charconvert' to (overwrite) the output file.
|
||||
*/
|
||||
if (end != 0)
|
||||
{
|
||||
write_info.bw_conv_error = TRUE;
|
||||
end = 0;
|
||||
if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc,
|
||||
fenc, wfname, fname) == FAIL)
|
||||
{
|
||||
write_info.bw_conv_error = TRUE;
|
||||
end = 0;
|
||||
}
|
||||
}
|
||||
mch_remove(wfname);
|
||||
vim_free(wfname);
|
||||
}
|
||||
mch_remove(wfname);
|
||||
vim_free(wfname);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (end == 0)
|
||||
{
|
||||
/*
|
||||
* Error encountered.
|
||||
*/
|
||||
if (errmsg == NULL)
|
||||
{
|
||||
#ifdef FEAT_MBYTE
|
||||
@@ -5690,6 +5749,10 @@ buf_write_bytes(struct bw_info *ip)
|
||||
}
|
||||
#endif /* FEAT_MBYTE */
|
||||
|
||||
if (ip->bw_fd < 0)
|
||||
/* Only checking conversion, which is OK if we get here. */
|
||||
return OK;
|
||||
|
||||
#ifdef FEAT_CRYPT
|
||||
if (flags & FIO_ENCRYPTED)
|
||||
{
|
||||
@@ -8800,7 +8863,7 @@ do_doautocmd(
|
||||
/*
|
||||
* Loop over the events.
|
||||
*/
|
||||
while (*arg && !VIM_ISWHITE(*arg))
|
||||
while (*arg && !ends_excmd(*arg) && !VIM_ISWHITE(*arg))
|
||||
if (apply_autocmds_group(event_name2nr(arg, &arg),
|
||||
fname, NULL, TRUE, group, curbuf, NULL))
|
||||
nothing_done = FALSE;
|
||||
@@ -9322,7 +9385,8 @@ apply_autocmds_group(
|
||||
* Quickly return if there are no autocommands for this event or
|
||||
* autocommands are blocked.
|
||||
*/
|
||||
if (first_autopat[(int)event] == NULL || autocmd_blocked > 0)
|
||||
if (event == NUM_EVENTS || first_autopat[(int)event] == NULL
|
||||
|| autocmd_blocked > 0)
|
||||
goto BYPASS_AU;
|
||||
|
||||
/*
|
||||
@@ -9395,7 +9459,7 @@ apply_autocmds_group(
|
||||
{
|
||||
if (event == EVENT_COLORSCHEME || event == EVENT_OPTIONSET)
|
||||
autocmd_fname = NULL;
|
||||
else if (fname != NULL && *fname != NUL)
|
||||
else if (fname != NULL && !ends_excmd(*fname))
|
||||
autocmd_fname = fname;
|
||||
else if (buf != NULL)
|
||||
autocmd_fname = buf->b_ffname;
|
||||
|
||||
@@ -779,6 +779,7 @@ get_exceptions(void)
|
||||
|
||||
static int initialised = 0;
|
||||
#define PYINITIALISED initialised
|
||||
static int python_end_called = FALSE;
|
||||
|
||||
#define DESTRUCTOR_FINISH(self) self->ob_type->tp_free((PyObject*)self);
|
||||
|
||||
@@ -878,6 +879,7 @@ python_end(void)
|
||||
if (recurse != 0)
|
||||
return;
|
||||
|
||||
python_end_called = TRUE;
|
||||
++recurse;
|
||||
|
||||
#ifdef DYNAMIC_PYTHON
|
||||
@@ -1040,6 +1042,8 @@ DoPyCommand(const char *cmd, rangeinitializer init_range, runner run, void *arg)
|
||||
}
|
||||
++recursive;
|
||||
#endif
|
||||
if (python_end_called)
|
||||
return;
|
||||
|
||||
#if defined(MACOS) && !defined(MACOS_X_UNIX)
|
||||
GetPort(&oldPort);
|
||||
@@ -1568,7 +1572,7 @@ do_pyeval (char_u *str, typval_T *rettv)
|
||||
(rangeinitializer) init_range_eval,
|
||||
(runner) run_eval,
|
||||
(void *) rettv);
|
||||
switch(rettv->v_type)
|
||||
switch (rettv->v_type)
|
||||
{
|
||||
case VAR_DICT: ++rettv->vval.v_dict->dv_refcount; break;
|
||||
case VAR_LIST: ++rettv->vval.v_list->lv_refcount; break;
|
||||
|
||||
@@ -733,8 +733,8 @@ get_py3_exceptions(void)
|
||||
#endif /* DYNAMIC_PYTHON3 */
|
||||
|
||||
static int py3initialised = 0;
|
||||
|
||||
#define PYINITIALISED py3initialised
|
||||
static int python_end_called = FALSE;
|
||||
|
||||
#define DESTRUCTOR_FINISH(self) Py_TYPE(self)->tp_free((PyObject*)self)
|
||||
|
||||
@@ -817,6 +817,7 @@ python3_end(void)
|
||||
if (recurse != 0)
|
||||
return;
|
||||
|
||||
python_end_called = TRUE;
|
||||
++recurse;
|
||||
|
||||
#ifdef DYNAMIC_PYTHON3
|
||||
@@ -938,6 +939,9 @@ DoPyCommand(const char *cmd, rangeinitializer init_range, runner run, void *arg)
|
||||
PyObject *cmdbytes;
|
||||
PyGILState_STATE pygilstate;
|
||||
|
||||
if (python_end_called)
|
||||
goto theend;
|
||||
|
||||
#if defined(MACOS) && !defined(MACOS_X_UNIX)
|
||||
GetPort(&oldPort);
|
||||
/* Check if the Python library is available */
|
||||
|
||||
195
src/keymap.h
195
src/keymap.h
@@ -112,7 +112,7 @@
|
||||
|
||||
/* Used for the sgr mouse. */
|
||||
#define KS_SGR_MOUSE 237
|
||||
#define KS_SGR_MOUSE_RELEASE 236 /* Release */
|
||||
#define KS_SGR_MOUSE_RELEASE 236
|
||||
|
||||
/*
|
||||
* Filler used after KS_SPECIAL and others
|
||||
@@ -140,6 +140,8 @@
|
||||
|
||||
/*
|
||||
* Codes for keys that do not have a termcap name.
|
||||
* The numbers are fixed to make sure that recorded key sequences remain valid.
|
||||
* Add new entries at the end, not halfway.
|
||||
*
|
||||
* K_SPECIAL KS_EXTRA KE_xxx
|
||||
*/
|
||||
@@ -147,125 +149,126 @@ enum key_extra
|
||||
{
|
||||
KE_NAME = 3 /* name of this terminal entry */
|
||||
|
||||
, KE_S_UP /* shift-up */
|
||||
, KE_S_DOWN /* shift-down */
|
||||
, KE_S_UP = 4 /* shift-up */
|
||||
, KE_S_DOWN = 5 /* shift-down */
|
||||
|
||||
, KE_S_F1 /* shifted function keys */
|
||||
, KE_S_F2
|
||||
, KE_S_F3
|
||||
, KE_S_F4
|
||||
, KE_S_F5
|
||||
, KE_S_F6
|
||||
, KE_S_F7
|
||||
, KE_S_F8
|
||||
, KE_S_F9
|
||||
, KE_S_F10
|
||||
, KE_S_F1 = 6 /* shifted function keys */
|
||||
, KE_S_F2 = 7
|
||||
, KE_S_F3 = 8
|
||||
, KE_S_F4 = 9
|
||||
, KE_S_F5 = 10
|
||||
, KE_S_F6 = 11
|
||||
, KE_S_F7 = 12
|
||||
, KE_S_F8 = 13
|
||||
, KE_S_F9 = 14
|
||||
, KE_S_F10 = 15
|
||||
|
||||
, KE_S_F11
|
||||
, KE_S_F12
|
||||
, KE_S_F13
|
||||
, KE_S_F14
|
||||
, KE_S_F15
|
||||
, KE_S_F16
|
||||
, KE_S_F17
|
||||
, KE_S_F18
|
||||
, KE_S_F19
|
||||
, KE_S_F20
|
||||
, KE_S_F11 = 16
|
||||
, KE_S_F12 = 17
|
||||
, KE_S_F13 = 18
|
||||
, KE_S_F14 = 19
|
||||
, KE_S_F15 = 20
|
||||
, KE_S_F16 = 21
|
||||
, KE_S_F17 = 22
|
||||
, KE_S_F18 = 23
|
||||
, KE_S_F19 = 24
|
||||
, KE_S_F20 = 25
|
||||
|
||||
, KE_S_F21
|
||||
, KE_S_F22
|
||||
, KE_S_F23
|
||||
, KE_S_F24
|
||||
, KE_S_F25
|
||||
, KE_S_F26
|
||||
, KE_S_F27
|
||||
, KE_S_F28
|
||||
, KE_S_F29
|
||||
, KE_S_F30
|
||||
, KE_S_F21 = 26
|
||||
, KE_S_F22 = 27
|
||||
, KE_S_F23 = 28
|
||||
, KE_S_F24 = 29
|
||||
, KE_S_F25 = 30
|
||||
, KE_S_F26 = 31
|
||||
, KE_S_F27 = 32
|
||||
, KE_S_F28 = 33
|
||||
, KE_S_F29 = 34
|
||||
, KE_S_F30 = 35
|
||||
|
||||
, KE_S_F31
|
||||
, KE_S_F32
|
||||
, KE_S_F33
|
||||
, KE_S_F34
|
||||
, KE_S_F35
|
||||
, KE_S_F36
|
||||
, KE_S_F37
|
||||
, KE_S_F31 = 36
|
||||
, KE_S_F32 = 37
|
||||
, KE_S_F33 = 38
|
||||
, KE_S_F34 = 39
|
||||
, KE_S_F35 = 40
|
||||
, KE_S_F36 = 41
|
||||
, KE_S_F37 = 42
|
||||
|
||||
, KE_MOUSE /* mouse event start */
|
||||
, KE_MOUSE = 43 /* mouse event start */
|
||||
|
||||
/*
|
||||
* Symbols for pseudo keys which are translated from the real key symbols
|
||||
* above.
|
||||
*/
|
||||
, KE_LEFTMOUSE /* Left mouse button click */
|
||||
, KE_LEFTDRAG /* Drag with left mouse button down */
|
||||
, KE_LEFTRELEASE /* Left mouse button release */
|
||||
, KE_MIDDLEMOUSE /* Middle mouse button click */
|
||||
, KE_MIDDLEDRAG /* Drag with middle mouse button down */
|
||||
, KE_MIDDLERELEASE /* Middle mouse button release */
|
||||
, KE_RIGHTMOUSE /* Right mouse button click */
|
||||
, KE_RIGHTDRAG /* Drag with right mouse button down */
|
||||
, KE_RIGHTRELEASE /* Right mouse button release */
|
||||
, KE_LEFTMOUSE = 44 /* Left mouse button click */
|
||||
, KE_LEFTDRAG = 45 /* Drag with left mouse button down */
|
||||
, KE_LEFTRELEASE = 46 /* Left mouse button release */
|
||||
, KE_MIDDLEMOUSE = 47 /* Middle mouse button click */
|
||||
, KE_MIDDLEDRAG = 48 /* Drag with middle mouse button down */
|
||||
, KE_MIDDLERELEASE = 49 /* Middle mouse button release */
|
||||
, KE_RIGHTMOUSE = 50 /* Right mouse button click */
|
||||
, KE_RIGHTDRAG = 51 /* Drag with right mouse button down */
|
||||
, KE_RIGHTRELEASE = 52 /* Right mouse button release */
|
||||
|
||||
, KE_IGNORE /* Ignored mouse drag/release */
|
||||
, KE_IGNORE = 53 /* Ignored mouse drag/release */
|
||||
|
||||
, KE_TAB /* unshifted TAB key */
|
||||
, KE_S_TAB_OLD /* shifted TAB key (no longer used) */
|
||||
, KE_TAB = 54 /* unshifted TAB key */
|
||||
, KE_S_TAB_OLD = 55 /* shifted TAB key (no longer used) */
|
||||
|
||||
, KE_XF1 /* extra vt100 function keys for xterm */
|
||||
, KE_XF2
|
||||
, KE_XF3
|
||||
, KE_XF4
|
||||
, KE_XEND /* extra (vt100) end key for xterm */
|
||||
, KE_ZEND /* extra (vt100) end key for xterm */
|
||||
, KE_XHOME /* extra (vt100) home key for xterm */
|
||||
, KE_ZHOME /* extra (vt100) home key for xterm */
|
||||
, KE_XUP /* extra vt100 cursor keys for xterm */
|
||||
, KE_XDOWN
|
||||
, KE_XLEFT
|
||||
, KE_XRIGHT
|
||||
, KE_SNIFF_UNUSED = 56 /* obsolete */
|
||||
, KE_XF1 = 57 /* extra vt100 function keys for xterm */
|
||||
, KE_XF2 = 58
|
||||
, KE_XF3 = 59
|
||||
, KE_XF4 = 60
|
||||
, KE_XEND = 61 /* extra (vt100) end key for xterm */
|
||||
, KE_ZEND = 62 /* extra (vt100) end key for xterm */
|
||||
, KE_XHOME = 63 /* extra (vt100) home key for xterm */
|
||||
, KE_ZHOME = 64 /* extra (vt100) home key for xterm */
|
||||
, KE_XUP = 65 /* extra vt100 cursor keys for xterm */
|
||||
, KE_XDOWN = 66
|
||||
, KE_XLEFT = 67
|
||||
, KE_XRIGHT = 68
|
||||
|
||||
, KE_LEFTMOUSE_NM /* non-mappable Left mouse button click */
|
||||
, KE_LEFTRELEASE_NM /* non-mappable left mouse button release */
|
||||
, KE_LEFTMOUSE_NM = 69 /* non-mappable Left mouse button click */
|
||||
, KE_LEFTRELEASE_NM = 70 /* non-mappable left mouse button release */
|
||||
|
||||
, KE_S_XF1 /* extra vt100 shifted function keys for xterm */
|
||||
, KE_S_XF2
|
||||
, KE_S_XF3
|
||||
, KE_S_XF4
|
||||
, KE_S_XF1 = 71 /* vt100 shifted function keys for xterm */
|
||||
, KE_S_XF2 = 72
|
||||
, KE_S_XF3 = 73
|
||||
, KE_S_XF4 = 74
|
||||
|
||||
/* NOTE: The scroll wheel events are inverted: i.e. UP is the same as
|
||||
* moving the actual scroll wheel down, LEFT is the same as moving the
|
||||
* scroll wheel right. */
|
||||
, KE_MOUSEDOWN /* scroll wheel pseudo-button Down */
|
||||
, KE_MOUSEUP /* scroll wheel pseudo-button Up */
|
||||
, KE_MOUSELEFT /* scroll wheel pseudo-button Left */
|
||||
, KE_MOUSERIGHT /* scroll wheel pseudo-button Right */
|
||||
, KE_MOUSEDOWN = 75 /* scroll wheel pseudo-button Down */
|
||||
, KE_MOUSEUP = 76 /* scroll wheel pseudo-button Up */
|
||||
, KE_MOUSELEFT = 77 /* scroll wheel pseudo-button Left */
|
||||
, KE_MOUSERIGHT = 78 /* scroll wheel pseudo-button Right */
|
||||
|
||||
, KE_KINS /* keypad Insert key */
|
||||
, KE_KDEL /* keypad Delete key */
|
||||
, KE_KINS = 79 /* keypad Insert key */
|
||||
, KE_KDEL = 80 /* keypad Delete key */
|
||||
|
||||
, KE_CSI /* CSI typed directly */
|
||||
, KE_SNR /* <SNR> */
|
||||
, KE_PLUG /* <Plug> */
|
||||
, KE_CMDWIN /* open command-line window from Command-line Mode */
|
||||
, KE_CSI = 81 /* CSI typed directly */
|
||||
, KE_SNR = 82 /* <SNR> */
|
||||
, KE_PLUG = 83 /* <Plug> */
|
||||
, KE_CMDWIN = 84 /* open command-line window from Command-line Mode */
|
||||
|
||||
, KE_C_LEFT /* control-left */
|
||||
, KE_C_RIGHT /* control-right */
|
||||
, KE_C_HOME /* control-home */
|
||||
, KE_C_END /* control-end */
|
||||
, KE_C_LEFT = 85 /* control-left */
|
||||
, KE_C_RIGHT = 86 /* control-right */
|
||||
, KE_C_HOME = 87 /* control-home */
|
||||
, KE_C_END = 88 /* control-end */
|
||||
|
||||
, KE_X1MOUSE /* X1/X2 mouse-buttons */
|
||||
, KE_X1DRAG
|
||||
, KE_X1RELEASE
|
||||
, KE_X2MOUSE
|
||||
, KE_X2DRAG
|
||||
, KE_X2RELEASE
|
||||
, KE_X1MOUSE = 89 /* X1/X2 mouse-buttons */
|
||||
, KE_X1DRAG = 90
|
||||
, KE_X1RELEASE = 91
|
||||
, KE_X2MOUSE = 92
|
||||
, KE_X2DRAG = 93
|
||||
, KE_X2RELEASE = 94
|
||||
|
||||
, KE_DROP /* DnD data is available */
|
||||
, KE_CURSORHOLD /* CursorHold event */
|
||||
, KE_NOP /* doesn't do something */
|
||||
, KE_FOCUSGAINED /* focus gained */
|
||||
, KE_FOCUSLOST /* focus lost */
|
||||
, KE_DROP = 95 /* DnD data is available */
|
||||
, KE_CURSORHOLD = 96 /* CursorHold event */
|
||||
, KE_NOP = 97 /* doesn't do something */
|
||||
, KE_FOCUSGAINED = 98 /* focus gained */
|
||||
, KE_FOCUSLOST = 99 /* focus lost */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
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
|
||||
17
src/libvterm/.gitignore
vendored
Normal file
17
src/libvterm/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
*~
|
||||
*.swp
|
||||
|
||||
tags
|
||||
src/*.o
|
||||
src/*.lo
|
||||
|
||||
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;
|
||||
}
|
||||
136
src/libvterm/src/encoding/DECdrawing.inc
Normal file
136
src/libvterm/src/encoding/DECdrawing.inc
Normal file
@@ -0,0 +1,136 @@
|
||||
static const struct StaticTableEncoding encoding_DECdrawing = {
|
||||
{
|
||||
NULL, /* init */
|
||||
&decode_table /* decode */
|
||||
},
|
||||
{
|
||||
0x0, /* 0 */
|
||||
0x0, /* 1 */
|
||||
0x0, /* 2 */
|
||||
0x0, /* 3 */
|
||||
0x0, /* 4 */
|
||||
0x0, /* 5 */
|
||||
0x0, /* 6 */
|
||||
0x0, /* 7 */
|
||||
0x0, /* 8 */
|
||||
0x0, /* 9 */
|
||||
0x0, /* 10 */
|
||||
0x0, /* 11 */
|
||||
0x0, /* 12 */
|
||||
0x0, /* 13 */
|
||||
0x0, /* 14 */
|
||||
0x0, /* 15 */
|
||||
0x0, /* 16 */
|
||||
0x0, /* 17 */
|
||||
0x0, /* 18 */
|
||||
0x0, /* 19 */
|
||||
0x0, /* 20 */
|
||||
0x0, /* 21 */
|
||||
0x0, /* 22 */
|
||||
0x0, /* 23 */
|
||||
0x0, /* 24 */
|
||||
0x0, /* 25 */
|
||||
0x0, /* 26 */
|
||||
0x0, /* 27 */
|
||||
0x0, /* 28 */
|
||||
0x0, /* 29 */
|
||||
0x0, /* 30 */
|
||||
0x0, /* 31 */
|
||||
0x0, /* 32 */
|
||||
0x0, /* 33 */
|
||||
0x0, /* 34 */
|
||||
0x0, /* 35 */
|
||||
0x0, /* 36 */
|
||||
0x0, /* 37 */
|
||||
0x0, /* 38 */
|
||||
0x0, /* 39 */
|
||||
0x0, /* 40 */
|
||||
0x0, /* 41 */
|
||||
0x0, /* 42 */
|
||||
0x0, /* 43 */
|
||||
0x0, /* 44 */
|
||||
0x0, /* 45 */
|
||||
0x0, /* 46 */
|
||||
0x0, /* 47 */
|
||||
0x0, /* 48 */
|
||||
0x0, /* 49 */
|
||||
0x0, /* 50 */
|
||||
0x0, /* 51 */
|
||||
0x0, /* 52 */
|
||||
0x0, /* 53 */
|
||||
0x0, /* 54 */
|
||||
0x0, /* 55 */
|
||||
0x0, /* 56 */
|
||||
0x0, /* 57 */
|
||||
0x0, /* 58 */
|
||||
0x0, /* 59 */
|
||||
0x0, /* 60 */
|
||||
0x0, /* 61 */
|
||||
0x0, /* 62 */
|
||||
0x0, /* 63 */
|
||||
0x0, /* 64 */
|
||||
0x0, /* 65 */
|
||||
0x0, /* 66 */
|
||||
0x0, /* 67 */
|
||||
0x0, /* 68 */
|
||||
0x0, /* 69 */
|
||||
0x0, /* 70 */
|
||||
0x0, /* 71 */
|
||||
0x0, /* 72 */
|
||||
0x0, /* 73 */
|
||||
0x0, /* 74 */
|
||||
0x0, /* 75 */
|
||||
0x0, /* 76 */
|
||||
0x0, /* 77 */
|
||||
0x0, /* 78 */
|
||||
0x0, /* 79 */
|
||||
0x0, /* 80 */
|
||||
0x0, /* 81 */
|
||||
0x0, /* 82 */
|
||||
0x0, /* 83 */
|
||||
0x0, /* 84 */
|
||||
0x0, /* 85 */
|
||||
0x0, /* 86 */
|
||||
0x0, /* 87 */
|
||||
0x0, /* 88 */
|
||||
0x0, /* 89 */
|
||||
0x0, /* 90 */
|
||||
0x0, /* 91 */
|
||||
0x0, /* 92 */
|
||||
0x0, /* 93 */
|
||||
0x0, /* 94 */
|
||||
0x0, /* 95 */
|
||||
0x25C6, /* 96 */
|
||||
0x2592, /* 97 */
|
||||
0x2409, /* 98 */
|
||||
0x240C, /* 99 */
|
||||
0x240D, /* 100 */
|
||||
0x240A, /* 101 */
|
||||
0x00B0, /* 102 */
|
||||
0x00B1, /* 103 */
|
||||
0x2424, /* 104 */
|
||||
0x240B, /* 105 */
|
||||
0x2518, /* 106 */
|
||||
0x2510, /* 107 */
|
||||
0x250C, /* 108 */
|
||||
0x2514, /* 109 */
|
||||
0x253C, /* 110 */
|
||||
0x23BA, /* 111 */
|
||||
0x23BB, /* 112 */
|
||||
0x2500, /* 113 */
|
||||
0x23BC, /* 114 */
|
||||
0x23BD, /* 115 */
|
||||
0x251C, /* 116 */
|
||||
0x2524, /* 117 */
|
||||
0x2534, /* 118 */
|
||||
0x252C, /* 119 */
|
||||
0x2502, /* 120 */
|
||||
0x2A7D, /* 121 */
|
||||
0x2A7E, /* 122 */
|
||||
0x03C0, /* 123 */
|
||||
0x2260, /* 124 */
|
||||
0x00A3, /* 125 */
|
||||
0x00B7, /* 126 */
|
||||
0x0, /* 127 */
|
||||
}
|
||||
};
|
||||
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
|
||||
136
src/libvterm/src/encoding/uk.inc
Normal file
136
src/libvterm/src/encoding/uk.inc
Normal file
@@ -0,0 +1,136 @@
|
||||
static const struct StaticTableEncoding encoding_uk = {
|
||||
{
|
||||
NULL, /* init */
|
||||
&decode_table /* decode */
|
||||
},
|
||||
{
|
||||
0x0, /* 0 */
|
||||
0x0, /* 1 */
|
||||
0x0, /* 2 */
|
||||
0x0, /* 3 */
|
||||
0x0, /* 4 */
|
||||
0x0, /* 5 */
|
||||
0x0, /* 6 */
|
||||
0x0, /* 7 */
|
||||
0x0, /* 8 */
|
||||
0x0, /* 9 */
|
||||
0x0, /* 10 */
|
||||
0x0, /* 11 */
|
||||
0x0, /* 12 */
|
||||
0x0, /* 13 */
|
||||
0x0, /* 14 */
|
||||
0x0, /* 15 */
|
||||
0x0, /* 16 */
|
||||
0x0, /* 17 */
|
||||
0x0, /* 18 */
|
||||
0x0, /* 19 */
|
||||
0x0, /* 20 */
|
||||
0x0, /* 21 */
|
||||
0x0, /* 22 */
|
||||
0x0, /* 23 */
|
||||
0x0, /* 24 */
|
||||
0x0, /* 25 */
|
||||
0x0, /* 26 */
|
||||
0x0, /* 27 */
|
||||
0x0, /* 28 */
|
||||
0x0, /* 29 */
|
||||
0x0, /* 30 */
|
||||
0x0, /* 31 */
|
||||
0x0, /* 32 */
|
||||
0x0, /* 33 */
|
||||
0x0, /* 34 */
|
||||
0x00a3, /* 35 */
|
||||
0x0, /* 36 */
|
||||
0x0, /* 37 */
|
||||
0x0, /* 38 */
|
||||
0x0, /* 39 */
|
||||
0x0, /* 40 */
|
||||
0x0, /* 41 */
|
||||
0x0, /* 42 */
|
||||
0x0, /* 43 */
|
||||
0x0, /* 44 */
|
||||
0x0, /* 45 */
|
||||
0x0, /* 46 */
|
||||
0x0, /* 47 */
|
||||
0x0, /* 48 */
|
||||
0x0, /* 49 */
|
||||
0x0, /* 50 */
|
||||
0x0, /* 51 */
|
||||
0x0, /* 52 */
|
||||
0x0, /* 53 */
|
||||
0x0, /* 54 */
|
||||
0x0, /* 55 */
|
||||
0x0, /* 56 */
|
||||
0x0, /* 57 */
|
||||
0x0, /* 58 */
|
||||
0x0, /* 59 */
|
||||
0x0, /* 60 */
|
||||
0x0, /* 61 */
|
||||
0x0, /* 62 */
|
||||
0x0, /* 63 */
|
||||
0x0, /* 64 */
|
||||
0x0, /* 65 */
|
||||
0x0, /* 66 */
|
||||
0x0, /* 67 */
|
||||
0x0, /* 68 */
|
||||
0x0, /* 69 */
|
||||
0x0, /* 70 */
|
||||
0x0, /* 71 */
|
||||
0x0, /* 72 */
|
||||
0x0, /* 73 */
|
||||
0x0, /* 74 */
|
||||
0x0, /* 75 */
|
||||
0x0, /* 76 */
|
||||
0x0, /* 77 */
|
||||
0x0, /* 78 */
|
||||
0x0, /* 79 */
|
||||
0x0, /* 80 */
|
||||
0x0, /* 81 */
|
||||
0x0, /* 82 */
|
||||
0x0, /* 83 */
|
||||
0x0, /* 84 */
|
||||
0x0, /* 85 */
|
||||
0x0, /* 86 */
|
||||
0x0, /* 87 */
|
||||
0x0, /* 88 */
|
||||
0x0, /* 89 */
|
||||
0x0, /* 90 */
|
||||
0x0, /* 91 */
|
||||
0x0, /* 92 */
|
||||
0x0, /* 93 */
|
||||
0x0, /* 94 */
|
||||
0x0, /* 95 */
|
||||
0x0, /* 96 */
|
||||
0x0, /* 97 */
|
||||
0x0, /* 98 */
|
||||
0x0, /* 99 */
|
||||
0x0, /* 100 */
|
||||
0x0, /* 101 */
|
||||
0x0, /* 102 */
|
||||
0x0, /* 103 */
|
||||
0x0, /* 104 */
|
||||
0x0, /* 105 */
|
||||
0x0, /* 106 */
|
||||
0x0, /* 107 */
|
||||
0x0, /* 108 */
|
||||
0x0, /* 109 */
|
||||
0x0, /* 110 */
|
||||
0x0, /* 111 */
|
||||
0x0, /* 112 */
|
||||
0x0, /* 113 */
|
||||
0x0, /* 114 */
|
||||
0x0, /* 115 */
|
||||
0x0, /* 116 */
|
||||
0x0, /* 117 */
|
||||
0x0, /* 118 */
|
||||
0x0, /* 119 */
|
||||
0x0, /* 120 */
|
||||
0x0, /* 121 */
|
||||
0x0, /* 122 */
|
||||
0x0, /* 123 */
|
||||
0x0, /* 124 */
|
||||
0x0, /* 125 */
|
||||
0x0, /* 126 */
|
||||
0x0, /* 127 */
|
||||
}
|
||||
};
|
||||
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}
|
||||
16
src/main.c
16
src/main.c
@@ -449,18 +449,28 @@ vim_main2(void)
|
||||
*/
|
||||
if (p_lpl)
|
||||
{
|
||||
char_u *rtp_copy = NULL;
|
||||
|
||||
/* First add all package directories to 'runtimepath', so that their
|
||||
* autoload directories can be found. Only if not done already with a
|
||||
* :packloadall command. */
|
||||
* :packloadall command.
|
||||
* Make a copy of 'runtimepath', so that source_runtime does not use
|
||||
* the pack directories. */
|
||||
if (!did_source_packages)
|
||||
{
|
||||
rtp_copy = vim_strsave(p_rtp);
|
||||
add_pack_start_dirs();
|
||||
}
|
||||
|
||||
source_in_path(rtp_copy == NULL ? p_rtp : rtp_copy,
|
||||
# ifdef VMS /* Somehow VMS doesn't handle the "**". */
|
||||
source_runtime((char_u *)"plugin/*.vim", DIP_ALL | DIP_NOAFTER);
|
||||
(char_u *)"plugin/*.vim",
|
||||
# else
|
||||
source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_NOAFTER);
|
||||
(char_u *)"plugin/**/*.vim",
|
||||
# endif
|
||||
DIP_ALL | DIP_NOAFTER);
|
||||
TIME_MSG("loading plugins");
|
||||
vim_free(rtp_copy);
|
||||
|
||||
/* Only source "start" packages if not done already with a :packloadall
|
||||
* command. */
|
||||
|
||||
32
src/misc1.c
32
src/misc1.c
@@ -3685,16 +3685,30 @@ vim_beep(
|
||||
{
|
||||
if (!((bo_flags & val) || (bo_flags & BO_ALL)))
|
||||
{
|
||||
if (p_vb
|
||||
#ifdef FEAT_GUI
|
||||
/* While the GUI is starting up the termcap is set for the
|
||||
* GUI but the output still goes to a terminal. */
|
||||
&& !(gui.in_use && gui.starting)
|
||||
#ifdef ELAPSED_FUNC
|
||||
static int did_init = FALSE;
|
||||
static ELAPSED_TYPE start_tv;
|
||||
|
||||
/* Only beep once per half a second, otherwise a sequence of beeps
|
||||
* would freeze Vim. */
|
||||
if (!did_init || ELAPSED_FUNC(start_tv) > 500)
|
||||
{
|
||||
did_init = TRUE;
|
||||
ELAPSED_INIT(start_tv);
|
||||
#endif
|
||||
if (p_vb
|
||||
#ifdef FEAT_GUI
|
||||
/* While the GUI is starting up the termcap is set for
|
||||
* the GUI but the output still goes to a terminal. */
|
||||
&& !(gui.in_use && gui.starting)
|
||||
#endif
|
||||
)
|
||||
out_str_cf(T_VB);
|
||||
else
|
||||
out_char(BELL);
|
||||
#ifdef ELAPSED_FUNC
|
||||
}
|
||||
#endif
|
||||
)
|
||||
out_str(T_VB);
|
||||
else
|
||||
out_char(BELL);
|
||||
}
|
||||
|
||||
/* When 'verbose' is set and we are sourcing a script or executing a
|
||||
|
||||
@@ -1636,7 +1636,9 @@ shift_delete_registers()
|
||||
free_yank_all(); /* free register nine */
|
||||
for (n = 9; n > 1; --n)
|
||||
y_regs[n] = y_regs[n - 1];
|
||||
y_previous = y_current = &y_regs[1];
|
||||
y_current = &y_regs[1];
|
||||
if (!y_append)
|
||||
y_previous = y_current;
|
||||
y_regs[1].y_array = NULL; /* set register one to empty */
|
||||
}
|
||||
|
||||
|
||||
109
src/option.c
109
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,
|
||||
@@ -4294,6 +4306,32 @@ set_title_defaults(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
|
||||
static void
|
||||
trigger_optionsset_string(
|
||||
int opt_idx,
|
||||
int opt_flags,
|
||||
char_u *oldval,
|
||||
char_u *newval)
|
||||
{
|
||||
if (oldval != NULL && newval != NULL)
|
||||
{
|
||||
char_u buf_type[7];
|
||||
|
||||
sprintf((char *)buf_type, "%s",
|
||||
(opt_flags & OPT_LOCAL) ? "local" : "global");
|
||||
set_vim_var_string(VV_OPTION_OLD, oldval, -1);
|
||||
set_vim_var_string(VV_OPTION_NEW, newval, -1);
|
||||
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
|
||||
apply_autocmds(EVENT_OPTIONSET,
|
||||
(char_u *)options[opt_idx].fullname, NULL, FALSE, NULL);
|
||||
reset_v_option_vars();
|
||||
}
|
||||
vim_free(oldval);
|
||||
vim_free(newval);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Parse 'arg' for option settings.
|
||||
*
|
||||
@@ -4763,6 +4801,7 @@ do_set(
|
||||
char_u *origval = NULL;
|
||||
#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
|
||||
char_u *saved_origval = NULL;
|
||||
char_u *saved_newval = NULL;
|
||||
#endif
|
||||
unsigned newlen;
|
||||
int comma;
|
||||
@@ -5114,14 +5153,21 @@ do_set(
|
||||
# ifdef FEAT_CRYPT
|
||||
&& options[opt_idx].indir != PV_KEY
|
||||
# endif
|
||||
&& origval != NULL)
|
||||
&& origval != NULL && newval != NULL)
|
||||
{
|
||||
/* origval may be freed by
|
||||
* did_set_string_option(), make a copy. */
|
||||
saved_origval = vim_strsave(origval);
|
||||
/* newval (and varp) may become invalid if the
|
||||
* buffer is closed by autocommands. */
|
||||
saved_newval = vim_strsave(newval);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Handle side effects, and set the global value for
|
||||
* ":set" on local options. */
|
||||
* ":set" on local options. Note: when setting 'syntax'
|
||||
* or 'filetype' autocommands may be triggered that can
|
||||
* cause havoc. */
|
||||
errmsg = did_set_string_option(opt_idx, (char_u **)varp,
|
||||
new_value_alloced, oldval, errbuf, opt_flags);
|
||||
|
||||
@@ -5130,28 +5176,14 @@ do_set(
|
||||
{
|
||||
#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
|
||||
vim_free(saved_origval);
|
||||
vim_free(saved_newval);
|
||||
#endif
|
||||
goto skip;
|
||||
}
|
||||
#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
|
||||
if (saved_origval != NULL)
|
||||
{
|
||||
char_u buf_type[7];
|
||||
|
||||
sprintf((char *)buf_type, "%s",
|
||||
(opt_flags & OPT_LOCAL) ? "local" : "global");
|
||||
set_vim_var_string(VV_OPTION_NEW,
|
||||
*(char_u **)varp, -1);
|
||||
set_vim_var_string(VV_OPTION_OLD, saved_origval, -1);
|
||||
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
|
||||
apply_autocmds(EVENT_OPTIONSET,
|
||||
(char_u *)options[opt_idx].fullname,
|
||||
NULL, FALSE, NULL);
|
||||
reset_v_option_vars();
|
||||
vim_free(saved_origval);
|
||||
}
|
||||
trigger_optionsset_string(opt_idx, opt_flags,
|
||||
saved_origval, saved_newval);
|
||||
#endif
|
||||
|
||||
}
|
||||
else /* key code option */
|
||||
{
|
||||
@@ -5922,6 +5954,7 @@ set_string_option(
|
||||
char_u *oldval;
|
||||
#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
|
||||
char_u *saved_oldval = NULL;
|
||||
char_u *saved_newval = NULL;
|
||||
#endif
|
||||
char_u *r = NULL;
|
||||
|
||||
@@ -5945,26 +5978,19 @@ set_string_option(
|
||||
&& options[opt_idx].indir != PV_KEY
|
||||
# endif
|
||||
)
|
||||
{
|
||||
saved_oldval = vim_strsave(oldval);
|
||||
saved_newval = vim_strsave(s);
|
||||
}
|
||||
#endif
|
||||
if ((r = did_set_string_option(opt_idx, varp, TRUE, oldval, NULL,
|
||||
opt_flags)) == NULL)
|
||||
did_set_option(opt_idx, opt_flags, TRUE);
|
||||
|
||||
/* call autocommand after handling side effects */
|
||||
#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
|
||||
if (saved_oldval != NULL)
|
||||
{
|
||||
char_u buf_type[7];
|
||||
sprintf((char *)buf_type, "%s",
|
||||
(opt_flags & OPT_LOCAL) ? "local" : "global");
|
||||
set_vim_var_string(VV_OPTION_NEW, *varp, -1);
|
||||
set_vim_var_string(VV_OPTION_OLD, saved_oldval, -1);
|
||||
set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
|
||||
apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, FALSE, NULL);
|
||||
reset_v_option_vars();
|
||||
vim_free(saved_oldval);
|
||||
}
|
||||
/* call autocommand after handling side effects */
|
||||
trigger_optionsset_string(opt_idx, opt_flags,
|
||||
saved_oldval, saved_newval);
|
||||
#endif
|
||||
}
|
||||
return r;
|
||||
@@ -7544,6 +7570,9 @@ did_set_string_option(
|
||||
did_filetype = TRUE;
|
||||
apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft,
|
||||
curbuf->b_fname, TRUE, curbuf);
|
||||
/* Just in case the old "curbuf" is now invalid. */
|
||||
if (varp != &(curbuf->b_p_ft))
|
||||
varp = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -10648,8 +10677,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);
|
||||
@@ -10857,6 +10889,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;
|
||||
@@ -10923,6 +10958,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
|
||||
@@ -10962,6 +11000,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
|
||||
|
||||
@@ -69,9 +69,10 @@ void ex_argdelete(exarg_T *eap);
|
||||
void ex_listdo(exarg_T *eap);
|
||||
void ex_compiler(exarg_T *eap);
|
||||
void ex_runtime(exarg_T *eap);
|
||||
int source_runtime(char_u *name, int flags);
|
||||
int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
|
||||
int do_in_runtimepath(char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
|
||||
int source_runtime(char_u *name, int flags);
|
||||
int source_in_path(char_u *path, char_u *name, int flags);
|
||||
void add_pack_start_dirs(void);
|
||||
void load_start_packages(void);
|
||||
void ex_packloadall(exarg_T *eap);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user