From 547a17be543101b334224e288cf878ef324d6195 Mon Sep 17 00:00:00 2001 From: palomena Date: Mon, 27 Dec 2021 01:48:25 +0100 Subject: [PATCH] Initial commit --- LICENSE | 18 + Makefile | 11 + README.md | 69 + data/SDL_logo.png | Bin 0 -> 20932 bytes data/screenshot.png | Bin 0 -> 84484 bytes data/terminal.png | Bin 0 -> 72405 bytes data/terminal_small.png | Bin 0 -> 25464 bytes libvterm-0.2/.libs/libvterm.a | Bin 0 -> 125582 bytes libvterm-0.2/.libs/libvterm.la | 1 + libvterm-0.2/.libs/libvterm.lai | 41 + libvterm-0.2/.libs/libvterm.so | 1 + libvterm-0.2/.libs/libvterm.so.0 | 1 + libvterm-0.2/.libs/libvterm.so.0.0.0 | Bin 0 -> 99656 bytes libvterm-0.2/CONTRIBUTING | 22 + libvterm-0.2/LICENSE | 23 + libvterm-0.2/Makefile | 145 ++ libvterm-0.2/bin/.libs/unterm | Bin 0 -> 22512 bytes libvterm-0.2/bin/.libs/vterm-ctrl | Bin 0 -> 17976 bytes libvterm-0.2/bin/.libs/vterm-dump | Bin 0 -> 23088 bytes libvterm-0.2/bin/unterm | 210 ++ libvterm-0.2/bin/unterm.c | 280 +++ libvterm-0.2/bin/vterm-ctrl | 210 ++ libvterm-0.2/bin/vterm-ctrl.c | 354 +++ libvterm-0.2/bin/vterm-dump | 210 ++ libvterm-0.2/bin/vterm-dump.c | 242 +++ libvterm-0.2/doc/URLs | 14 + libvterm-0.2/doc/seqs.txt | 278 +++ libvterm-0.2/find-wide-chars.pl | 30 + libvterm-0.2/include/vterm.h | 582 +++++ libvterm-0.2/include/vterm_keycodes.h | 61 + libvterm-0.2/libvterm.la | 41 + libvterm-0.2/src/.libs/encoding.o | Bin 0 -> 6056 bytes libvterm-0.2/src/.libs/keyboard.o | Bin 0 -> 6040 bytes libvterm-0.2/src/.libs/mouse.o | Bin 0 -> 3824 bytes libvterm-0.2/src/.libs/parser.o | Bin 0 -> 7112 bytes libvterm-0.2/src/.libs/pen.o | Bin 0 -> 14696 bytes libvterm-0.2/src/.libs/screen.o | Bin 0 -> 20240 bytes libvterm-0.2/src/.libs/state.o | Bin 0 -> 48056 bytes libvterm-0.2/src/.libs/unicode.o | Bin 0 -> 6344 bytes libvterm-0.2/src/.libs/vterm.o | Bin 0 -> 10432 bytes libvterm-0.2/src/encoding.c | 230 ++ libvterm-0.2/src/encoding.lo | 12 + libvterm-0.2/src/encoding.o | Bin 0 -> 6056 bytes libvterm-0.2/src/encoding/DECdrawing.inc | 36 + libvterm-0.2/src/encoding/DECdrawing.tbl | 31 + libvterm-0.2/src/encoding/uk.inc | 6 + libvterm-0.2/src/encoding/uk.tbl | 1 + libvterm-0.2/src/fullwidth.inc | 111 + libvterm-0.2/src/keyboard.c | 226 ++ libvterm-0.2/src/keyboard.lo | 12 + libvterm-0.2/src/keyboard.o | Bin 0 -> 6040 bytes libvterm-0.2/src/mouse.c | 99 + libvterm-0.2/src/mouse.lo | 12 + libvterm-0.2/src/mouse.o | Bin 0 -> 3824 bytes libvterm-0.2/src/parser.c | 393 ++++ libvterm-0.2/src/parser.lo | 12 + libvterm-0.2/src/parser.o | Bin 0 -> 7112 bytes libvterm-0.2/src/pen.c | 573 +++++ libvterm-0.2/src/pen.lo | 12 + libvterm-0.2/src/pen.o | Bin 0 -> 14648 bytes libvterm-0.2/src/rect.h | 56 + libvterm-0.2/src/screen.c | 947 ++++++++ libvterm-0.2/src/screen.lo | 12 + libvterm-0.2/src/screen.o | Bin 0 -> 20240 bytes libvterm-0.2/src/state.c | 2265 ++++++++++++++++++++ libvterm-0.2/src/state.lo | 12 + libvterm-0.2/src/state.o | Bin 0 -> 48056 bytes libvterm-0.2/src/unicode.c | 337 +++ libvterm-0.2/src/unicode.lo | 12 + libvterm-0.2/src/unicode.o | Bin 0 -> 6344 bytes libvterm-0.2/src/utf8.h | 39 + libvterm-0.2/src/vterm.c | 404 ++++ libvterm-0.2/src/vterm.lo | 12 + libvterm-0.2/src/vterm.o | Bin 0 -> 10432 bytes libvterm-0.2/src/vterm_internal.h | 292 +++ libvterm-0.2/t/02parser.test | 266 +++ libvterm-0.2/t/03encoding_utf8.test | 122 ++ libvterm-0.2/t/10state_putglyph.test | 68 + libvterm-0.2/t/11state_movecursor.test | 224 ++ libvterm-0.2/t/12state_scroll.test | 156 ++ libvterm-0.2/t/13state_edit.test | 300 +++ libvterm-0.2/t/14state_encoding.test | 105 + libvterm-0.2/t/15state_mode.test | 86 + libvterm-0.2/t/16state_resize.test | 48 + libvterm-0.2/t/17state_mouse.test | 181 ++ libvterm-0.2/t/18state_termprops.test | 42 + libvterm-0.2/t/20state_wrapping.test | 69 + libvterm-0.2/t/21state_tabstops.test | 60 + libvterm-0.2/t/22state_save.test | 64 + libvterm-0.2/t/25state_input.test | 155 ++ libvterm-0.2/t/26state_query.test | 58 + libvterm-0.2/t/27state_reset.test | 32 + libvterm-0.2/t/28state_dbl_wh.test | 61 + libvterm-0.2/t/29state_fallback.test | 31 + libvterm-0.2/t/30state_pen.test | 114 + libvterm-0.2/t/31state_rep.test | 128 ++ libvterm-0.2/t/32state_flow.test | 28 + libvterm-0.2/t/40state_selection.test | 65 + libvterm-0.2/t/60screen_ascii.test | 69 + libvterm-0.2/t/61screen_unicode.test | 47 + libvterm-0.2/t/62screen_damage.test | 155 ++ libvterm-0.2/t/63screen_resize.test | 117 + libvterm-0.2/t/64screen_pen.test | 55 + libvterm-0.2/t/65screen_protect.test | 16 + libvterm-0.2/t/66screen_extent.test | 11 + libvterm-0.2/t/67screen_dbl_wh.test | 38 + libvterm-0.2/t/68screen_termprops.test | 17 + libvterm-0.2/t/90vttest_01-movement-1.test | 87 + libvterm-0.2/t/90vttest_01-movement-2.test | 40 + libvterm-0.2/t/90vttest_01-movement-3.test | 21 + libvterm-0.2/t/90vttest_01-movement-4.test | 36 + libvterm-0.2/t/90vttest_02-screen-1.test | 18 + libvterm-0.2/t/90vttest_02-screen-2.test | 29 + libvterm-0.2/t/90vttest_02-screen-3.test | 16 + libvterm-0.2/t/90vttest_02-screen-4.test | 17 + libvterm-0.2/t/92lp1640917.test | 13 + libvterm-0.2/t/harness.c | 1111 ++++++++++ libvterm-0.2/t/run-test.pl | 228 ++ libvterm-0.2/tbl2inc_c.pl | 30 + libvterm-0.2/vterm.pc.in | 8 + src/sdlterm.c | 826 +++++++ 121 files changed, 14466 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 data/SDL_logo.png create mode 100644 data/screenshot.png create mode 100644 data/terminal.png create mode 100644 data/terminal_small.png create mode 100644 libvterm-0.2/.libs/libvterm.a create mode 120000 libvterm-0.2/.libs/libvterm.la create mode 100644 libvterm-0.2/.libs/libvterm.lai create mode 120000 libvterm-0.2/.libs/libvterm.so create mode 120000 libvterm-0.2/.libs/libvterm.so.0 create mode 100755 libvterm-0.2/.libs/libvterm.so.0.0.0 create mode 100644 libvterm-0.2/CONTRIBUTING create mode 100644 libvterm-0.2/LICENSE create mode 100644 libvterm-0.2/Makefile create mode 100755 libvterm-0.2/bin/.libs/unterm create mode 100755 libvterm-0.2/bin/.libs/vterm-ctrl create mode 100755 libvterm-0.2/bin/.libs/vterm-dump create mode 100755 libvterm-0.2/bin/unterm create mode 100644 libvterm-0.2/bin/unterm.c create mode 100755 libvterm-0.2/bin/vterm-ctrl create mode 100644 libvterm-0.2/bin/vterm-ctrl.c create mode 100755 libvterm-0.2/bin/vterm-dump create mode 100644 libvterm-0.2/bin/vterm-dump.c create mode 100644 libvterm-0.2/doc/URLs create mode 100644 libvterm-0.2/doc/seqs.txt create mode 100644 libvterm-0.2/find-wide-chars.pl create mode 100644 libvterm-0.2/include/vterm.h create mode 100644 libvterm-0.2/include/vterm_keycodes.h create mode 100644 libvterm-0.2/libvterm.la create mode 100644 libvterm-0.2/src/.libs/encoding.o create mode 100644 libvterm-0.2/src/.libs/keyboard.o create mode 100644 libvterm-0.2/src/.libs/mouse.o create mode 100644 libvterm-0.2/src/.libs/parser.o create mode 100644 libvterm-0.2/src/.libs/pen.o create mode 100644 libvterm-0.2/src/.libs/screen.o create mode 100644 libvterm-0.2/src/.libs/state.o create mode 100644 libvterm-0.2/src/.libs/unicode.o create mode 100644 libvterm-0.2/src/.libs/vterm.o create mode 100644 libvterm-0.2/src/encoding.c create mode 100644 libvterm-0.2/src/encoding.lo create mode 100644 libvterm-0.2/src/encoding.o create mode 100644 libvterm-0.2/src/encoding/DECdrawing.inc create mode 100644 libvterm-0.2/src/encoding/DECdrawing.tbl create mode 100644 libvterm-0.2/src/encoding/uk.inc create mode 100644 libvterm-0.2/src/encoding/uk.tbl create mode 100644 libvterm-0.2/src/fullwidth.inc create mode 100644 libvterm-0.2/src/keyboard.c create mode 100644 libvterm-0.2/src/keyboard.lo create mode 100644 libvterm-0.2/src/keyboard.o create mode 100644 libvterm-0.2/src/mouse.c create mode 100644 libvterm-0.2/src/mouse.lo create mode 100644 libvterm-0.2/src/mouse.o create mode 100644 libvterm-0.2/src/parser.c create mode 100644 libvterm-0.2/src/parser.lo create mode 100644 libvterm-0.2/src/parser.o create mode 100644 libvterm-0.2/src/pen.c create mode 100644 libvterm-0.2/src/pen.lo create mode 100644 libvterm-0.2/src/pen.o create mode 100644 libvterm-0.2/src/rect.h create mode 100644 libvterm-0.2/src/screen.c create mode 100644 libvterm-0.2/src/screen.lo create mode 100644 libvterm-0.2/src/screen.o create mode 100644 libvterm-0.2/src/state.c create mode 100644 libvterm-0.2/src/state.lo create mode 100644 libvterm-0.2/src/state.o create mode 100644 libvterm-0.2/src/unicode.c create mode 100644 libvterm-0.2/src/unicode.lo create mode 100644 libvterm-0.2/src/unicode.o create mode 100644 libvterm-0.2/src/utf8.h create mode 100644 libvterm-0.2/src/vterm.c create mode 100644 libvterm-0.2/src/vterm.lo create mode 100644 libvterm-0.2/src/vterm.o create mode 100644 libvterm-0.2/src/vterm_internal.h create mode 100644 libvterm-0.2/t/02parser.test create mode 100644 libvterm-0.2/t/03encoding_utf8.test create mode 100644 libvterm-0.2/t/10state_putglyph.test create mode 100644 libvterm-0.2/t/11state_movecursor.test create mode 100644 libvterm-0.2/t/12state_scroll.test create mode 100644 libvterm-0.2/t/13state_edit.test create mode 100644 libvterm-0.2/t/14state_encoding.test create mode 100644 libvterm-0.2/t/15state_mode.test create mode 100644 libvterm-0.2/t/16state_resize.test create mode 100644 libvterm-0.2/t/17state_mouse.test create mode 100644 libvterm-0.2/t/18state_termprops.test create mode 100644 libvterm-0.2/t/20state_wrapping.test create mode 100644 libvterm-0.2/t/21state_tabstops.test create mode 100644 libvterm-0.2/t/22state_save.test create mode 100644 libvterm-0.2/t/25state_input.test create mode 100644 libvterm-0.2/t/26state_query.test create mode 100644 libvterm-0.2/t/27state_reset.test create mode 100644 libvterm-0.2/t/28state_dbl_wh.test create mode 100644 libvterm-0.2/t/29state_fallback.test create mode 100644 libvterm-0.2/t/30state_pen.test create mode 100644 libvterm-0.2/t/31state_rep.test create mode 100644 libvterm-0.2/t/32state_flow.test create mode 100644 libvterm-0.2/t/40state_selection.test create mode 100644 libvterm-0.2/t/60screen_ascii.test create mode 100644 libvterm-0.2/t/61screen_unicode.test create mode 100644 libvterm-0.2/t/62screen_damage.test create mode 100644 libvterm-0.2/t/63screen_resize.test create mode 100644 libvterm-0.2/t/64screen_pen.test create mode 100644 libvterm-0.2/t/65screen_protect.test create mode 100644 libvterm-0.2/t/66screen_extent.test create mode 100644 libvterm-0.2/t/67screen_dbl_wh.test create mode 100644 libvterm-0.2/t/68screen_termprops.test create mode 100644 libvterm-0.2/t/90vttest_01-movement-1.test create mode 100644 libvterm-0.2/t/90vttest_01-movement-2.test create mode 100644 libvterm-0.2/t/90vttest_01-movement-3.test create mode 100644 libvterm-0.2/t/90vttest_01-movement-4.test create mode 100644 libvterm-0.2/t/90vttest_02-screen-1.test create mode 100644 libvterm-0.2/t/90vttest_02-screen-2.test create mode 100644 libvterm-0.2/t/90vttest_02-screen-3.test create mode 100644 libvterm-0.2/t/90vttest_02-screen-4.test create mode 100644 libvterm-0.2/t/92lp1640917.test create mode 100644 libvterm-0.2/t/harness.c create mode 100755 libvterm-0.2/t/run-test.pl create mode 100644 libvterm-0.2/tbl2inc_c.pl create mode 100644 libvterm-0.2/vterm.pc.in create mode 100644 src/sdlterm.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cf609f8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2020 Niklas Benfer + +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. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dfcb3cb --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +INCLUDE_PATHS := -I/usr/include/SDL2 -Ilibvterm-0.2/include +LIBRARY_PATHS := -Llibvterm-0.2/.libs +LINK := -lSDL2 -lsdlfox -lvterm -lutil +CFLAGS := ${INCLUDE_PATHS} ${LIBRARY_PATHS} ${LINK} + +all: + mkdir -p ./build + cc ./src/*.c -o./build/sdlterm ${CFLAGS} + +clean: + rm -rv ./build \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ad22afe --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# sdlterm + +![SDL logo](data/SDL_logo.png) ![logo](data/terminal_small.png) + +### About + +SDL2 Terminal Emulator based on [libsdl2](http://www.libsdl.org), [libvterm](http://www.leonerd.org.uk/code/libvterm/) and [SDL_fox](https://github.com/palomena/SDL_fox). +Should be compatible with all POSIX compliant systems and probably also win32. + +![Screenshot](data/screenshot.png) + +Originally developed as a standalone terminal emulator, meaning all of the +vt100 and xterm behind the scenes management had been implemented from scratch. +Turned out ok-ish, but a few edge cases kept failing. Did not want to +go through 3000+ loc of cryptic csi sequence parsing and vt100 handling. +Heard of libvterm and never looked back since. Don't reimplement the wheel. + +Single source file, less than 1000 loc, kept as simple and minimal as possible. + +![Screenshot](data/terminal.png) + +### Features +- Clipboard handling (Copy+Paste) +- Visual terminal bell +- Blinking cursor +- Colors +- Font zoom on mousewheel/touchpad event +- Runtime selectable renderer backend (software, opengl, etc) +- window resize triggers buffer and child process resize +- Fast, due to SDL_fox prerendered font rendering +- Easily hackable by playing around with the accessible sourcecode +- Probably runs on a posix compliant toaster (if SDL supports it) + +### Missing features / Future improvements +- Scrollback buffer +- Performance + - damage triggers full screen redraw, where partial update would be sufficient + +### Build + +1. Install libsdl2, libsdlfox, make +2. `git clone [REPOSITORY URL]` +3. cd ./sdlterm +4. `make` +5. A *wild* sdlterm application should appear in the ./build directory + +### License + +``` +MIT License + +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. +``` \ No newline at end of file diff --git a/data/SDL_logo.png b/data/SDL_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b5098197cee29d6907221d2039c43a1f51025a74 GIT binary patch literal 20932 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 literal 0 HcmV?d00001 diff --git a/data/screenshot.png b/data/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..8f9207a5bfd6e85caa32eccd097090941b1165e0 GIT binary patch literal 84484 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

aN&MiMLZ*&VEAHQq~xgwvQo{pM=0FMuU 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 literal 0 HcmV?d00001 diff --git a/data/terminal.png b/data/terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..e7c7f2ee48d80892c89496e92e6b29ddec212e9f GIT binary patch literal 72405 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 literal 0 HcmV?d00001 diff --git a/data/terminal_small.png b/data/terminal_small.png new file mode 100644 index 0000000000000000000000000000000000000000..85497e9170e1a7f0cd0732c4ba1edcd12a15335c GIT binary patch literal 25464 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

G&GPV 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 a##gd_fCRJ1JW@uU46I}PxJJt^>VE+9NNXkl literal 0 HcmV?d00001 diff --git a/libvterm-0.2/.libs/libvterm.a b/libvterm-0.2/.libs/libvterm.a new file mode 100644 index 0000000000000000000000000000000000000000..ce54a3f5e6be3ffa3f420190aaae01294d212172 GIT binary patch literal 125582 zcmeFa4SZZxnLmE>QW{ESA}>;<$`lI}fs#UnRtii@2L`s4h|SU!G)>c#R?;RUGi`C3 zCOE0hbqI|wthnM@6?eO?yVezJMJ#EpB(hx%*u|{BDp~Bh8K)}|x)P+?|M&Zx=iIq- zGHI*ge*U|kz3t?l`#sNj&U4P9HP^MYHnh~X zHY987>lz!E*VW(Ms>JGb7rmCRs7ux<4w;e-wQC#JH#bb12-ghHl}etBaV{;)S|yGF=(~MQ3AgZfR<^xg;Lp!mxQsj{+hoo>WU~ zQ%iGGP(%S=wBDu=JP(kaP)+}GWHm^;gfRqR> z1WK+#y=-i%uS+)B_7yzWt`Vowhl(;!5dxW0t*bu}6j9AzfCpMOL)QN_{N7}PE%h+IuC=*k_1fghB0)~*Q(K>Gu}wJ4Qv=qv25LuzU_zFsR<5M_ z&!={*XML)rCEs4g`nNQ!fxgHWj8e2T)UD8TFeWy=+T^{>Hdl$U>4Y&f_NK%$R4qkS~QQSxSghpJk za=Lro`|Sc+zh*GuFCJLv$M-Mt7x&t**#|+5hss0-|Kq)hl7YxhuPgp?N88KiMLtAd zy4Kx|znj)KFoKM?xi@(sTmu=eJ5iFz)C~BO5)UQ1&)Uq0;V}pPTvmwGB_}6D_A_8AfnpVVhEDPK z((PAQ2vl9cNYX>spIQKW-POm0jJYC7j%;2UuqjWEo)X#eC{pY92I=mez~pDjOPLpE z(~WF6HYDL=73tCT_Iv5>i&ZdZa*gV7?XiiT*IpO)S`v7Hu>Iz+*Sx&fd0zXhu-Ejw z*RitpH-^0?_v9w^(X6E4W zig&<&(XY%-D>a3;ZW)U3x%Kanu)jkcGUan#L9zA4%E46%wNKhl_M4`PJSVZd@cYIR zabrP|Yb=Z}TVrB*L1KCN_l;$u8w*>k^^L;{g?IZhU`a z>#eUIJ7((f2{jzTlZ`61$nM??Iv|oCKboT=EA-9O9Gx}L6@Rs(?bY*`@2|Ee--DX> zYG?0;+f3}^>N;w%S4E47z{Qimwq%mfo<S3kfncN0^M4 zI@(@>`rZW+xS&n{0WOI760~X8=fqv}5+oW;)IXC*?=8=i_duy%71^00db~C*S~5}p zy+nEsLUsA2CoExR&r2uDo-=~%iEO>-7)sb?k>7UMAL)t@_r;GwY%jMY6F&+CyJx68 zftnZS+_~|?skayLitH>eof98Uy|IX20=hakJsm$QgUa-Qcq&kI7&|L6Rll><*+;CUa#@6_p@HyOXz;Wr7t9Z1Gk@S6_zY4}Y6O(lMl z@jDB@dH6l{R?qt;ejeP9=Pf(m^FEItU&rr% z+C{nl@A_}fftzl);T_Q#3mcZNu3HSA?8uc*3WR@JOoRo+=DR>=o$ z^GY^UmYh3zVnq*>*HqyA@w$|M=AT~qq0$>(cgkHLz$5+U5y$m>IsRDxvLBa|g8p~X z6W8PXWBp6NE)3|ukvOimQdwI6vbU7j@DGbGhTgne|I!Z`0o2dEkyW|(ON?vBUS&mR zWFE$5XkbOIGH`OQ$pA`2d?asxy+LvNEP0}V%e)EdD!i3>m?1b9nmM@V@k;e)U&44# zS5}gzz^+$%OK0!ZO8=D7OCAH`Rpy;)9HGDOg}Wq#SC$&jdlZlAv&Gv4pXfymnaB9- zRpv35#$ILK>&AjW%DmIY;9G#40g3*T!%kdM2j~bU>Gd9#39tyo)shuh^21t>xGh1U zPitLk{p!_0=-QM58Zyb1$h_~u_zf#s>eh1=BG2ybz-^Zg+=Y3F_i0u@T+A+(;!%uw zc>V1-`0KT(NpIT~oZh7l9wmq?;o#_EA*F`+yefy6;~J~ZsNAjRvwGn_GS54+_sAu8j!dROK46A z57)wkgGW`Kr6HVrY&tDZ8?S9&Gc=rwN0kq~!%2-fxJ}nG2e;|!aBv&%HV3!u#hclB z?;6x<+>r7I2T^5!@Npmba~M7b@=d7`vQ!m5uPQZr)kWEEt|Oh3C0H~lvNWBjiCLz@0(_=T@%_yO+g@GBO7?Dd8)96wpQ z@v~hPi+`CCRyhfd8n(gTcf(aI{$Y*3N-3Fs_L6S=cLQVmmJUw2dQ?Hquz=o4%gf%A zA-8zwpIsV0jL~hwS)7YI#KO9Sem49rK7=kSzlA#CD3<;yXBdhNO33t6PulRd{uhgz zL7G*mj;g?{PV58R@XS-U?f~*uF+yCQHn-rfn|>BkvG6@A|Hp!m#^HvyZ_Q7BjeoYj z23R)fWY_qo*v=0k#6*mkcRF%Y*X>6W={77HPuxY>ayKXLYa5lN;)%OhsJS@;U;qmW zrZjhv0kAAQaTkj&_XY!CF?ixGw$a>#T~vp<^prjv4ZTRfa#P=M8UqllI&pOzYh=^Y zYmRa;ZWxQ>{$4JT)c50YC{fa%eyXFZGO?#^_>^?p(USIOAt=_vKLO@kQnD`sg2kCJ z3*uneF6)LvU6r|d6UTY!{@wt))nJiy|4@KkW3Y+o{*eH?$Y5pZ{?PzC(_kg({-YM_ zfmq))??W`%e(I24Gn8&S6it?=8-{+HZX5DaS7zde65TZ~Cj4ia9slxMn;nVzfkgM> z7s)u`e~*4hs+Yxn6%Q=dB;Urr>u<*2_piSJe^<9o!Qa~TSL5%Ut!LwJG)d%+)CE|_ zlJdOTE@;g}mZpbFHupvz?2YW~WzL@q(q-$Aw}(EkPK$qnG+1~d!@cCy66QBpvbzi} zEKFu<4pF%Ie!u^;zI@pIDM$xVC7{Tgi%YO_1Vfq+ohMJFcsf0*yY@W?H8RgRqN+K`qLv@>HtwVe;`r9byzIimF5@j5}C<(T6`q4 zu(Sec%YFtSmBG42bRetN-+)rNjLIxMIytiCOfpme!krY~+%3v$-Fyj(_LClcePqj1 za9H4Ed1c~9(xVlTEgwbL6TkdHwJJ%SFZIT}5*k}7Sq`bCC{PEI%UGVW2F~0cH_TZG zBV02bGmtZpogI%L>(WD&oxRDZiB3wbp!~Bx0ja(Nk_aat!r6>4QGzwv(a7$B6IY__ zCnY|i7A8e2P$IJ!E#=9C|NVTu$=0$A+0HHr#gSHQ;b z@6YGu@f$|=JIp*vHp_;rIeHRV@jziz&1i-i?7Q>*k$lR=2$|!{mf?g`cvx?abVi_q zv(G~F6r(61MGT(M#$EBz>_5O+@Mx{iUU+|9BTW+G_Qr`&}XTZ6P@Wi^g1;DyFRIqNA zn{}TPa!-Mq7spZ@cQ&hg7Vh_``wHA!)O{uHt?HhQds5xh94U2k9YscLwoO6_a(S<987sQpJ`P0Ww@_V_e|W^s{3->o76oC_r>bI7Wbw29q+ne z#WfeKNU@q+(0W0=_a1K+p5NoWZjyH{9`T>gbeTWq;dl%~epgggZSt(yoT!ZE&f6IcIO9+Is*xD&6iml~RS8)wRHZ)cm` zg?9W+YRWK(9S#@5Le+_PL_dZ6;br?8yQcfBfydyFXnC{WV0l{l1zH}K59>FtC5n@t zeTJn^IrQg|7}q8TpJrh1BMyGP;-6BS{Mip#{yz)RQ=M4+IzHf{H=R~zeAvP7)O79) z;q1Sv@#D339DLBhFEZrb&lPv`VKM<+l!yE;<^!&`DDKkVF|GDGP-{H`+Kj*b( z9Quzs^f96Kyhp~N-|x`JmHsE=(63hgV&&(!gx8-d?xy!29D0sLc=e7$Z+uNXVI1!c zwL|(5?42_o^4_G{=xUEMPxBb8p6D)}L9h#n{ z4!%n3$DI!T18pbE9Q;b9U*X_~6<_7x7ic+g{Km`5c~tRc0|&URAGX{q{e>!j)S=f$ zFXrHN`n=4+ZT@sPxb4TbIk@e|dL7*MV}IZ~kpH&V8d1p4Dx=w`squn*>vKG`dIfZx zL$Qcs0b(4ZIsx0R_2o3c3~TGXt)CYvu4E=K{zj>6am`^thpuSIO(*97=e0dIP1msP0USyg@zxrgnGB~TljW3ilslwi6*WdbCb91Op*<6%fDFM z3^Jtg+jstxft_9?JbAih56Bx~09+MLf}Ka{@Wt<1#tbvIAT#QVDw_;g}u)c1L3nzwk9&Fj7i@*2>OAIa1l#5;)Y=JPUbhcb($Zp`;zk?JLzaQJrWXPBa4 z2X(NJ8zdL4<6swF;T22p1~A7<8*kHX_*>S#r~?b)$+wEs)f@1dBt2ZR8@1H)_x#{z z{>Z7a5!|odkQY3o&z<0lmR2=78d_g_`d5bS-#Xejv zEZVh0<3trd>2*Lm=Ko`^0akH9VAt@b{5T(=4QOMf1$@ja~m=de_R@Mp*x zqI!+Fk@Szb#>X*2c;955O29Sm-(`)DYapbza|OvG<@H*u*>Nmu=XuIjZVSf2nGUY0 zb?E>`$^=-8;_G#=DrEu8GlP}!7S!~#qY!x~yGmAMHO!pWAMf}+#Q0s;v`*E*DQ}z> z*sCMC0$!gmUW3 zpN-$@%kk#Q%}Tgg8gAX8Uxm(3S6V{7yWx!)mi%P-M04e22wRk? zp2EjPnGT6zcbmwViOm4vBq8ihT$-Ek4X`p3i|Qo?ZC=WICKd|_)4dC^eUF5T?KpV; zv1dY(a)>|3d^muhgNTC@K&B28P{e{$`j{iqUO=#Y%|wO0Mt;pGhS~?ZmP{DRPb2fw zzaU5F3p35(OstaWy)1++BF6Ykj$#i`D9ZWA1{`UzK?pVsV3)w}H&WvvK0*c;U=Pgo z@`w8lM=wCCdruuOsTsyzr(po719Fxh<@RcN=!sKt==D{&59}Gj>1qSE3kZh8nOHPE zIw`WH0$t^aH*GOQz9L0Bzk|jR#=s3pk97WhA&%rn4O!O^1T^u+5}6dzR5R-LAqTP_ zC=4QORy!EWj_lu6h%d?{W)xvx&m0?0bZ=OQyTo*}-=Dx_-QPR^*wfj`g-nF;t16vM zV``#yGdF)?2)q!%8abFfToj^H0I#KC*c@p$LnYkY&c zR~>#v!vNDHCwR7?zz|u@$Kt}~B0ag+6>OCv^^;PSy!=GD8m$Pqm+D2m+-28eK-n)f zXu`VF)7bJlS{~VQ1{8E|5-4OKkUJGL2ooRa$&NxpqriKCj9P0NFOWrKXKV^;Ew*rd zytgaFMWx+f_{y5&eC!C<=qCO(J`CfeCSL>g~pNhWG{&t>=nH z5Xf?Sss!A!JmQDQCdxXAO~DqbMxf`dZ(~Twxk-`EeoO~~=kj$>#X%m8m&Wg7$_~vW zxu*+Wzm`|dH7qq{;H6a zET215(>vlX#GZ`*q69m#u_iJhkzO-YVtCn%&7EoUleo>|eDb#(M8|(()r07+S@#YK zvwpS_BbFXLJ+k#92(8b?3_-ttODwW;F|wwnKk-o4qSA%gHF+9?KDn@Tp@iy-$*V*% zNc6>)Lc6xBl$<&sw#y?Q+G|{MBU{+ZNLQvK5~K`<%dCzZ^J7clhs3+dr1vjM$71A>xYnxM2^ zfS`*8DzYEI&=zTVN9r9##LIv}VWzJmaq1qI!ISY9u3`Sm+8k=w5>u!8eIpNJ6=_YB zOfNGdTZU+bvm2l<7PKVP2M)%V3^d)TFCROGU6@+HGo*mOiUGLcG9xJ983hG=X?81U zAmF*lEAjXGb(i7qru7`sK-d{UkzXp&n7zFHrqx`I2uWtErEU2M!|?KTOTcRG`ga;u zsBy9on<888kSNAPt?Z#F zyzCr9JTZAzN82yFNarQy`QgasX%d)g>#BdzpL$$`ymFj89YK>KYiRrFM%#CO6hXA; zr~TL1qTiz-P>4a2+@k+9Xpkp>{wd1T8fASYH|jLY~*E4WO$FAklom$SA%G z{VgU2ZM~?}7^1ukJ$&qGnJx^XgB$cx6^ux7GXtw(W*n2i@;@gOxi|FW&g{umju}p+ z(w`0>Yz=T{l=II>JcxJcgp7OI zeUSJy!xB}d2JRl_i9TXZA>+TieH|o}iR~By(Q9kQ1q@%q>!jZ(84HNj6eJpq< z@woVU-e=j6Z1}Q=PbGt?fDWoZ*PjCaMqw}hN7bM20|NhQdu)1Y?|Qld#n&&eqkuHMG{(!MGz%tiuKa&yU|FC(Mh#5dqcO`ewVg_Ybs6 z3^D7qJeb@Fe)gTl-K_h#cw^nL`1*11pK)-6%hJoR5zjARad7hH zMSA}JRLe(i0sbBZY|XLs?^B%Xl)NmyTya}REWT24`|i`??^k?^VH5DN^_lITm!*G| z4qQ?Bo5!0mCgU~T+yeRm#clpsK9^~{TvO*|@jWaYTvg^4;KwxIXFK?MjummuF}HyJ z-!;SLIrwztA9L{gsQYj&G`E1y14^H8@Q*6K$ie?Y@oESEOT}%yu<89P#o2y&S^P1r zH+LF1z{~lf4%agIo2Rc(e1(HgR(zF%S1R7<;Abh`?BG)rPdfNi#Wy&3g;vas4nAG! zI~;t5;++nDoAT*$@G7Oh-@)6IzQ@7$D*kZ?FHt^G2iHe0=HRy9SmxkX?hXgH{l+#2 zxBW)1gIhg4xtFV|6ASx-l6@DCf_F4KFXQHay-QX#5jh}F!$|>v-vQq zD)iavHPYy1xdF&PKb8otJR2iRlxi)zA{ zeomd-^z$7f(`5B4oN~=(XNhZuxdnf1`tAKD_*i#91jn5kK8(@r(o&xhWBmu+km;vNx8ZI17mJ%gHdOMj=k;s+R2M7~H#~W|bqA0a)B5)X zK7=kOIpgk^9a8yMhQiY6hR0_E@-F~SV!!OH*;mb;1G?z%uwQo9+w(%i#F>8wh_(1p z7&Q20*Wx2xHF&dim{YkY&0G(2BE(bMJAOnV&QFAL8BXycD#PT`!Y2yV9kuuBxqzOX4>OBVy{{NwuYbP^Uz8 za`7H>)xP1kVll$LZ?G|QI?K88hPUz-Y3Fu8OmGnm-?^msRAblmd!8?~mLtkb7s86g@i^e!6hi&p>?1o(wmdfXfuGL7 z|I@8}1Bs_c6MJ5{o&sGwknsPYI9Oi83;jL*uUTkou|L*if4j}DP0e&2L&mkQOx(9- zWEd_f<1yAxq{^W1i+G^I&|~&y1ja5~Dp42^*Xn|`jK1L`m^_3FK6o=7mDZ8MJL{TZ zfAPp7A67)#j+7=(!`}(XMcI#l6)IC{dj+D%c9B0TRk|0YixofAVtJ~NC(41k0-Wg{ zV#I&Wh^c*Yx5_fkSxUm!vhDgFU^0D-*^=DF0Shu4G7Hb^z$8RtUSN+&g3nbP*o!~| zPUwqzu8o@q^K#PcL#402m4s(CQnGj$dtvcTTi&kPW>sL~C0KFO&DE5Rd0sP;Nr}xA zyekd%&DMt+=kLyi{S342&3_OV_BiD7L)vx-@~R5OJ&X;@d|!^8uai=fkwe3Y2~}vb zcn>M=OWA()oquE}i(%9h!wd4(tY$Q`SW=uh!)yY$W2~Hp(Ht=P#t7tijDL3X4<%Y< zUcMpLc_aYpJ;pz~`G*8IS%U5!QV6r|E^-rRe(Qm?eRHWXyqxZ>4q69O!maK!@@%Ak zH@MPBGc4akHhnL&iMvvCA5t0F*)toF@D-;xm~tZ-P?78qH@Jr>UvVjI;d$IrAEtcS zrDUUEYI|IsFSwMpC|A0a&$*Or`Q$mzrF_DrJVhzba4FkeO3qfva~O?KQ}lMprID^4 zI`X;1fd?tel_$HN%tu*nevect=2RaFa)bM(h6kTIz{*~UMQQa5Q+iVp(?X9wgMb0r0E?l!)qK%WXt~JE>U@i$QFB( zOEe)wWJ~-Om*|ubkuB^CT%w6qAX~^~c?v1yp@^lhg?o`wWP&0M3i7tV%NAcyXjj9_ z7MdNdNb7*|jgR0{A#?U7C+$KBd)W-jS^UtC^dc2_5Ah}g$UqI$v64A^vHJnuxn&M% zif=K{lEb8_%N66`j=@Xk?3I_#q$^Xb44Cc?n=FD94w0=yX`srS4Yd z2Zb`KLgi%V$_|Ex&-9gpQDU3$dc(~Y69cL+x|TEipt&$4pH(A5^bWS9^w2Bm{!*WB zOh>x{VtA5HJj<;o5i1*#qjIs~YM-6Qsw=2apK8HFjrjC56oQcXcj#C}glNXGQx7&P-B2Gs+8&3@JO+5bXS z?Ta4)9S#OjyLX@U9o8GLz-E8}j6TfJ!bhQ`+lKFMe+O?~r0c^7C`e>WJDn`GF=Hw6 zll5b;!Z%m$Ya8Y`C9-+32?1gHI70KXH(FqVc9%n7IB0tiY-_Pp$}S)a(}Q8Z8l4?tA;Nas%jFjg{eVr0t?%rj4+A@QWx6QVH96at7w0_#0PP&gIH z5ZIi9WI-c+2?AudD6c@?3RhmMp%2l}@SxW`;_l!Nqv zcar?uE_sJbjyfvjmy!JAARni+(!-O|L*|<`gZ|U$e}mGOu08$y$f-{Sy*w*dL8mad z+ja_PFap{G2#e3+&|E)J=00--;a@_XD+r$-h7A@D!`@5seJ*)^gr(%R4$ma{gD!b~ z=%(bhq~j!C>5}J1WlE0JO6CsW-E#J7mpnf-Q*v9 z3bVkD+5aA)X$N%t4mdr0XJJsAdo2t)30*ki_HeosMpchui<}Rehs8Q`5V0dk{VT|> z!H1jfKZXv>e7Gst&0%M>{$ea%U}xp)Q5A+mJ#6Y6vC7E(X8~N!z6HlZv*(!F8+b_P zd%7u4fU%OU__2<*V-we3hYe0xh5-MGkG+MmPk3wwZrzhhdAgJ-I8=}Ve7H=u3AyV+FvJM83OCqA?Y^bN$E2L&w#okjW@ro8 zaZ~aX;vQGFf!$C^9SOf`Tng@gKSuF?DID{8$1K`>5a4Sk%o8XD2s6hNQ4BMsUcTn) z@b`=>-T)Tp>Ljy%*W*lf>c1`9zO{B29-HA!#W}3EX zr)0`cP%@lUFo^b%XrDuffotfV|K#v&Opjr>SP6`-HU#h|!I_2tK?0>0n!ueF+0p<` zoMN76MuYAws86~aF|6+dvKN8}M6d**WsQKOtdJzUsu9pUXOe2!^bVH?OA!IhH(i?i znnpnL3700nq+w}t>q7GO4(oD8;JZ|PgM#H$DDdU}Zk@Pu*ka#|aI3MxEC>rbivp0% zURT1g62DL4w;w+)=7%r7*W(_pY4}}=-_`gnz;7{rOYy76Z!LcJ;n{(hK z_kW(Hd2qbFpE1(dy_J8S-#a-^!=4}Ze{bHO@5^~@;vXBn^j;yD3TPv7T=V6Re&=gr zKK#St>v^xB^#$R}js!MoA6Nfxhr^fd4)`Bbe-5sK@TE0@|55e7vA}Lq~Nfe~tWYAPV8`b|G3;h2+i2q^r=hupZ@MXL5=~w@+75MK6{100H0{%M! z{zuiHdMn_+R=j0z>{JZJcwNpvR{rMyV4ZTRMV_>Jnnq#*pQ zLHLK&pJ(TT`2RE?{&Dr6!T#U!FMDeRDVVbXvev*9J9K2U?u9fk!^xub@^rv#9ip!>B2yWuf>E0x%LnOfU zTG)Ge1$@pZDe>;sctr;Qn~Z$n9_GK+y_M3ofb)C{>EC<`9&m|Hz&)n;k61Bqi7vo> ziSpUXfiAAEnVZL%Bl#~>{?hj0*QNM+#ih*y|7*q1zf*zT54PFT3+zH zdhLpa4cOpax3+aPws?c2)x(Ftny-?-SIqYG^LZ;8R@S8&lR}besU^AfVyNO!b6sOY zGTBf_=(RQ^Ay-{8*%ETrE^or#^1K(fo`)Y12_I__k}?-VRt8_*)VQK{)#|%eEpNe}WPxl&(q;0kfJ#Kv5@Ib1 zY*V6|d2Bj(OE1^o(G!=&@k&~*Ck-6nc=s&Vqp0t^NKX}I@iPhFa`Et<{V@GLho1cx zuOE+t|7slkN2*V#=H7^(mFHsJL(E`IFXtb;-aZb#L~-(Y6MmM@tJ)qdA3MgF$jETH zxb0Vn^S4O(&L%M~i`OYW$G`#pS=GPu9Q-0K3gL>$-#nl9D8A6a@6tW02?zg*@?YfO zf2H)*4*s;_R^M6q%XJUvQiuL(#qV_RYZPDR;MXg@!ohD)e3gUWqkaUTgI}lkb_c&l@h>@eO7R^Iey`$>I(VDnyBz#J#UFR@ z&5HLrxUcv=2k%zA-@#u}e7}Qtone3h2k%wrxdSr@Ou?M%fZ_epW@*6DL&P~H!B`>a9{E14&JT!3kL76o16Q zKcV<`2mh4fUvluzD89qNKd<f2h4R_w;4doP@8G{u ze7}SLTJZq~|E=N&9K2NJ8FcVd6hG+TlN29v@Y^`p#C6EQ&(iVbu!GN4{IG*BQ+&k1 zyA(g-;5!r_b?`yOk2?5o6c>dJd9B_)PvtKs5W?pxPK#k-{7%IuJNSCVqlP@-ua92L z!R_-h2Vbtw9S%OE&)Xc_uFLc~xLubSa&WsY<2m_b*JYv(Zr5dE4sO?FmN~dxm+5eD zyKb`0!R`7;uY=q5ks$}S>mzPC+VzpBTQ0gj5_537UbD=>?Yc>agWGkJZ4PeNO?n;N zuA2-wxLu#JOLp|;&#tFLb=}P3c0J{P%bCkR({`fO($nI#kK>-XtiI5YtkM8nmZGyl zLr&2<$%tz)H?J&3-@yT zV)|@vyDl=Wn6Ah1#-#%{{nr7bOjY=Wuc|kjrx{@oZY#gN-xR{aq)Rn?7^B;Uv-q8g zImz^Fcv@NtUsisL-3~{w^p9R)D3&T?rk_itHoPr=%Hzh(kUdfU#o0@Ze=^(*I~*7}x)@UhDi*I(hv_{o2xr1DRK z=)IUYW3clP3BEgyece2gg$)InN^DIKt6rnBVP`Z`U7km(%Y~-80$a$HsJg;HlS9yC zyA9oL#TUk)8_9Tc_io}rrjbk~!`V2BP4dv zYwX8QNa|X{JfDxLOAJE3Cg)PzR0oD8&CDx%io%@I(=Al_GSf0*nYB&a#cGqQ7H(Q9 zEH3Lo^HV4_lM0s8CJiCS4NRmkZ;ME+hxxq4m`F{A7>V1tJJ3oIW?E%ZqNxSbcJ7F^ zwdVNI;IwahyB;688OH`jk72bH0GoMkjsVzu0f3DwkS#?HP| za0~XIMeY7G2qfn@(^!Qz+bj8@_vz_1M|mt6=a8?Q&*=pWdlAah#X#H>UgkxS!(*)tNrXD|?ehuyJZM zk-23a_3Mfh6CMy)qE|AFJt>QOy6K4LxOgis=rScMd# zMpZ|?%;P!(D1L0|gi;cFlV#k>CuS%63rog)0R|feCCCthM7})Wgpo`f#ujHijwlJj z5mI}G@m+ebG&c|Bhv92_oZG2o$TwD`B zwDE61Hd1&1RP6eWBzPzpTPU&j8mB?YOl+*;qeg4do!|gQB>f+d68mK3TSjln-VPXK z1G2f4r(a?5Im<0Y_$m*quBG>u%pKS?%}CWZED3a}p9X4TnojeOP#$t%S6R-Vp77sw zY4# zdM?5<8Hh1&dzxNNAQxZ7)v{ZVY}g#A6Xan2IR%G(tD!ae zv1_##1+@!QyO;x+nthqYy?7ciLB5SwJiz6!l$X*ocr;06E9Ui}?N6`^=LT(Kbv0AL zNcoxi`e*#fJ$!&9kj814dfs+IJ9~*SOi;B_pMzSOASzke^_BOa+z`7=$#ZX|ym0R= z5c+fo-RJR5oSF;lXYUpt-S{-@C!<2+FU}13sFv}4K28nP>|^|(DS`QKR*-&d8J=v9 zqYbB@FY%w=^Ka!i4md6S{0lhqQq5{CMBD`l-CD!jo}mf;p2*Gx)6zrFo!Z~EARy`; zF2xa0Qqh>Wl3cGOiwEGr_Jsx`rF0LZmj$1oRf>M+vFzY<`5|a`TLSYC7WtcK7R`>j z7Z|_AAus4~hzAqt`C7pI1F7V|Q5F)*Dpoyr@5Y^mQ>-#?lv`}e3wI@Rj=|4g@@{-( zYZ=T@-0Z)y$p79V|A$aGs8u^qyHKNQw&BqIc2v-Z5dI<2CerAQUH4T0k$g4>u@0WR zJrm!7Vy{MR#O>xx&EupH=~8TiAg;E7mL|~lFYJrUB7g4eOwC~unA6+WHvyy4lhWl`AuJ(G!B-e}VhZkBP1}ZGeq=ao{T;k?XfqWx z4c-N3SU-#{1YNu#2;aN=XXmy7_%@yQrkCU&Q%-p|Ki&_Id7-qB4U z!KUxvOzMy-feA(lJZF^vX=Eofm<)|+-8jEuy4{1&K9T!r6Pr4ZQc8MaGv>yRwqY7` z0QboKXTiTPud1Z93-U@z8wU-LS11zpH7w*Y`F;t~U&t#ume&aKebh(8!O87*$#S5%ok~bPu^5$DO)0U-TS7nlkU5X*x)*lH%J&z2k0&;&p z5N1+j^KQ7rqk1}8H+r*o1y6@CXS~4#ntnUkMieFI?|B>nIi5UJ zfX^skkEam(86oysY=%6LlHE7-vTQ38k8WIjj?(85zn@Pof=+a?FJB%?CSJ zEJMG=5J#M*nzWm0BGeoWfx4`FG7h>c`74Fw)gf}rynFI@z&Rh}<-#CuE)rzVP^RA?p~8E zP*7s`0hMeSHImK%wgssI!6dD8%W^tih%Ke`34g&V%Opj^O+fmFuZC95@zV}Nb`@%g z^_ogAIcjg}G+19Heo~iX^5vroEPx6=#ke^{Mc0Lzsur41y-^RmpkIngOJe8ZjEOF_ z%#gY$$3&FN#GR3^>e4+0QXhM!pvk;Dao^!C=78VU<)=FRnhw8vBYMJxe(xe59}+!w ztZVZI`~|lznVlUlu4+!EiPdx#eD4B7woJT(s{@GjBD0x=nL#hqk^VWBFWPEtE8BmvV$JiC7>ftOAI1UTi~HH_3FKEwX2FCZGpiGQ=Z$blHk)oF z()soPJ;M5w z_n_5JPGew&q1S{>|76t94onrMvMXcV+fI`gB^dTcVfl;0ekxg^u;c#iwjWy%g-IP~ z=>-9aIROc62%3_h^CBI4t}q=>K`Nd0SfpZi6{g}jNRbM9Wy!1o`?NhYJAs%oI|<3a zlaT>$vj73!?5M)L@kKL}+&%w>lafqsdpNmowl+W{Ltq1hiy<@sB8ws0!GDJ|d}!mi zK27(lQ4J-trr>b$TR8Q8kQ&Z3CcahK&Y_@LtH~_h1_1S;8I`D5eV_%QDUsd1CH+1a zab`cTsfKr>TDJZJW(+xul25wAFuhi_Y|o;h$G6Fkujol+-a8s}z8q-ysbLH+hcJpA1gF7JH_R~wRTX<(S=hlnwu}oqx};!U6JdRm zG*}0cg#idPvT3Oa6>BDoHz|u|bN510A`N48i&S;Q6(&RiFhW_-%(nw#K5PVEFBzio zM_E}K#RAuSe}vNvgz6inr6d(WQgMldd$3omTNVk&Aqm5M6X8gDf?*RZZ5{Tj7cmCR zf&6Opp-guBA1&FE7RdJ(Xb74vSvWGYN_GsH7(W>p7|G-+;S57rJwH|dHArq8b!nz%)T@2lA7Vl_c10#_}fMDs2N4eX;4#@=O+-v*|Q#XaqId%;@2&76yH>N*fAajgt)) zpvXwXTB17+krmeH`~|H2h`-w0{pwZYw{DO*t(y#*4TD&PFzlB75<%vGcc8Z_k#(aW z#!q6yy^x(u6r|-_<+IF?k0q;StVJ16r6L1my@}ai?cS*^J+*z8&(2ZJPQx!&0dsu$ zzyfMP=NUC4A-nw4qtL+Hp@Fwyfw*UpzxW~hGC@u4Wa2$&k=Y)M%CJ!H#}8+pLe)UA z8QadgOci+ad1+jOh-#3d=G3WaY~9FyN^23;0Y;BSI`7A$ta_!}9`%y9JrPC9hF`#% zI==12<>_?Wb}uqhz4ZqX;>CQW5Wp(rb=jv;op|>r{3zDM;C|7?_tG&4ceP>%U8b+Rx~_z;}U#CWH%92q{UD ztl*oCd_uqrPsI{qh5%SDwZfIyB$=thgm#Qj_&!VhIjfn0G^XQx6Y?&BR4U*7_XEhR1%>=r3B*NuawA4lcd!$jZHrg zm$Kf!0^>X8P0&WGq>cJmu=bH;zkd-Xs0FXA2T&}it9*&fRqZzP;k`HWtFi9L!!gOvdou%oD_5A7?L9b@sx+mQdJEZ!nm>V9F!2mw+Ugy?`b( zdjXxyUP5#_dm){jy?{z)FCnU6_A*>Bdl?MPULdB-UP7UC_F{OO*$a5f>?K5HW-myt znZ1CI%w8-7-ux6=Fsy#F&R*WE88WSRnDVj4gi?W7kE9|*b&AiCGlVWR&}Hp|FJc{SVK-nJQ76hR{|s24Rq=4u1qE^tk4@X6SeQdv`@ ztEAYX7V1~-3b%Gezy-C7&32SZ8n5S2J-ysi#3g+PzC9X`fVsFlWp8YAoiE}`R4+ygSVohg2Zeybz_z%Lxdg{ZIf@a^!f3< zgbisrUMPVoyIxYxBm6Z=Q+lHzv3^l7m|B1(6jw9 zw*!?%dOm<&XU(fAR0jendu~jc!eAIJO$iM*(HJ$v)shTkLMVjjPK20RG=!O%2Ni>T zFWc( zjJE`_$w0YOG@Pks0{}T*m>-mdm7}>Td4GA7-X@+iBpzt!#Uof6M@xpHiecb@q@JPs zAQ=a3-znf6l_9h zUTByVMsul!!s)-w=dZbu zdl3E$LHL8}|5pX!KPlm(z>cavcaR3*Z@RxMU0$}FOqhl!bSbaB{A1%N{Z=4DO#Lqn z^C-J0kYSnnFDeLsQxLvG{p$++^JTP6{ojcf9YOqMHgv4EPVJ|4Rk( z{UVUh9^Bxo=YW6de+1$AmW9_6{t41=>|aFz#nk`ei6Q?)x~y#K#DK>NrIGW7mPgsK zK!#5BzqdffWZ?gZ`d?Gve^ubWPyIU}LS9B=1de%hNd0dv@Lw3nSI%wixc(pWDvza-%Q$O-uGQ~&Yw)FJi1B+TDb z5vfDv+{KSe%n{i1EtcrTf4cgQrw10Q|69n!hBu#Amhdaozc=h(`pqDHo$5a$>|dXc z{}J_nV?q9%73AMO^}o2FeoPPQ$07B9TbO@?;ZLa8MOJ@I8);lyC=Hz?ld=QGBYVW{ zv-B;%E>!$yxbu=e1@|iaNdILDjBAktSw~m8b+j8F*<%IrL!}Wrmw%>-P8(^8Xn`g)2^OJG#m&d_R z1;1i)zGWPI);M@<9Q-}NX{&Er)X3jqZeAmCq$@hj0Mr+}+Q;GZ5v56@|D_pH!D!Fn_ZEM3ljWFW3a&=>4Edj5-v7xS|zM-+vgS9%j*EKda)z>ahtz6mA z;;pD#Q+HPbS*g=V=QXF2cQxMIyvl1`UfY~%UDddHZ3Dhs-?XN-rD?rKar4oyg<-z{ zNwtD&LrYz2gOQD%<}vTXENE(Mlvo+CKGo9N6gF~53x+gV*xb_8>@BD8Fg3XP0}WPc z^F%7jwR-K!CePUQt6$!#`CAKfh8}Gvw$`p#ElI7tY0<3<<}a$f^~M|D9bZzrWd4Fh z@mjU#CwWqrgx$wA;Y1R?uCQU-D%;IIEF?2AIxV*8UwyrU$wJXRBoFYe+9XAmo&GHp> z$+|*@43b)lTx_~)?dsNs6}2nfObD{)_+6EqB?% zDsBWbAGfKd$livvP7a^YkY!N~ZLV#U{D-jDugO<2I-*k1Ed6Pdo(= zxQJ85S^Pd7hY`Olgiq0~o%qZU{^<}N=Cg1zh;Whqa{Mg+cbrbt;8nO={FV?N=CdJ$ zhv~l&!o&3SYCF(P*Ed3Vn9nzLJZJM$wi-aLzH#uI&ZhK6{&0SteNG;q38zih&2~Ho zEQ~)H!e@o(8#z(NMft<{gCRT|Z@rESUHbb&c$ofP9siM@eXo`CGa)=o|5ymWGDP1R z#RD$#3DZ9iC2H#D`M6vDe-R~W=x5_@@x4)^W7C_B3Opts%5*&BruU8G;C~vzuLesS z@A42Hu0QALfPwtO>HT5|kA?VrH-s+;;cwRQ2l<5S+h2$9aJ*g2L|mj_h@X}7v2pOJ zY@E1Ae-nO|o)|CIXa24u0WQ4-`1e#_R2c67|Ec1W9sE_rD;@kyWvtVgfX^k0+xBGT zvGY-_&H?=foj*k#{s*)jPIvHAbU>-uAMly0IP;d5jkj5GmIW`1e?f89NnRHJk>Ya< z9N-a^bDo1=r+Cc4*C@Ww!8a?OaPUtnt}sT*MkYI-w&i7UJ5ROcVsSgKjVdFH+j+3f z2aDTztxd1R?YuVT@Uin+E5D_;^Ve#J-p*g6irakFM=$2!c7CzU!R@+BhlAU7mu(Ji z*IjxY+^)L}Ik;VSk&K6*mD8@fM48YKZtHW*!R`9WG6%QoD;*AQ*H^YVxLsfAb#PlB zh8*0kuZZ9wIqmw&bS5Z-+x3-&4sO?1Ryep_U$OaT^T*b!M;v-v5BE8^t+$69+}1Q*Y`pOIk zxAXOcgWLJ~DhIdg99<4>=kePe+|J|s9o)|2haKF`<12LBXyv!__!$mv=kW;#xAXN? z4sO?Px*Xij*S9;kU4QC#a64Zgc5pji{{wB-wKO!=&EyPaCR{Tyv&MXPW_{C|H5|*& zT-%gvn0eDXYc9j|wEk|7G<7o@R$(Sxx2D0=i$C-)s~4NL4$}j6F?GFF`+w`9YQR2@ zYs+rc^@b#><<2<-!`gnu_SY9GuGQNF!ryu)UhCn;&;FM@f8X%q*|J-q@h`Fw>s^x* zpd33Biv@^rblqU?{VFii&no1mpJXI)kT@!NhHPPwLV5`k-mxdnf1{@eSV@UiZI2#%PB4`Xz@wD%~c>Ny|}!aEhy%5Mo9 z;V72=88;e=Ra#(7|7rNy@V5R@9ye}=jOjqFSK~h&7}w_9@SMiFWe3Q-kio59;};Bn z-1KwUSuA|dEhfaD+kkp^!^^_e%%;nudY#CY-t0M5Z@)@tPu`Z^uCM2Xh>0_XwMn(5 zhr_&f?~O4F!PBNg++K?PEV?}}va_QTf@2dB{`>rLC$?_=_SAtc{i+}i-*C5#HQ-|o zVt};!EYHyX=6JNSZ;RVV&)Q7kOo*34A+X5>`-*s&7yIkD<3)C?apRLb=Zzm>|K8#Q ziEeCElHEM>j}2_@4Yn8sd-iCtf_u|oP62zG#6(}}^#-XD1lHq{ILC9?OL99AcGAtE zTy#qGkVUMY7+H*P3#>0rwLm~p;xsGn$ht=m*z69K@2Ce`cM#cC*eoYfVlPo3sFnv3 zfn7Z~uEisfHG}vBtDMBgMkZP_NZ}CdfNnR6A|m7j3n6bsDYCON{rt;amBv^XErhgB zCLK=QaGTM76UIo-9psXgNK%w}%`JACw*bVQsFZD=_UH)r8;;FigozLDrhVsN7ohGW zH5)LQCD@21IpbH)Fi&UU2D=H|qKCY0wt3wbOXi&$0%u1Dow!JjGau5R)m@p#J9cPG zGhQ9Ciz*1!(Hr)~9xofPucIUE)7Q}hAF>rT8F>ty4nQ#@rNQPaBoezqKiH-8D5!Mr z#O~^e*h!z*yJxhFC&?&vkw6Qo>BA#Y51oj=9Ai02(q$&T>ZNkWiI?4cvHS#n@});4 zOC+vfiHT$##wjgKN(B3O4J3t@=mfIW!+lm5V!@dFnRT)fAB z3TlkPZrVuO1>s6F2;H*^N5T^IvWF4JUqL&MnUuN#5~9yEmhy21jmLdANEv}~xv;kir=dN-UT#8Dvm0=&^;Ph%VA z5*vcS6E=ti!6uq_$8cUT%!*sGZ+Fbx6tlVq4p4qoa*M?__lm7ZDC8J+qvp6}pURor z`v)SMA4XOiWFAj?h#!NTs!ll;NP2U)6`OcFzQEFowDf_=j~v4ohD&NoPl2)(zBohW!#nJuw6by}5gbWs^( zz#saAIyF&(R>ww+pEVI9zKA1evmAwxrwvm;*rlyMT!Rg<)e8t$z)d*SfIdV(_18mz ztf=O|0na)NO?-@L_V@hYc^^X^?#Bg39;H1*U5<7&fRT=LY?)iC(L3(>H8lJ)_%iyW z__40i{P_W%U^sb(GQK+Y@__&JG`1c$O8=>@%HNmu`SD};lDVE@&;yF55Ye5?!(k2< zD+WfaqCjkYqf;se95dz)z>F-i#mhAr;@P~7seT}})ej-&j7Q392vS=O>9jFjnvbc# z@NWKaBMd-iIk@66s7Qb0`1|Z^n=MrP}o7-#-6OX%O zYt?dlFd?@gSVDvy1@nMDj{O|1Kl<@bz=m{h0v00Br|-Ji-#h=CtgASZ)M`*eF+~Y7<l#XPCO0BATIMHMTWJ=;JpvI0r>R%EGh5}5~SYz1OVLPeW&_HLMI7)8PM#Ef#c zm>j^YDK-T&F4XVIf=u9`EyocYgqnU*ypV_6L64ueC?kimgVfQCon-EO|J%7!=6@6X zn4Im2?&gXhh)G8G;tr_99mh3=K7eO0SDua!@PtsWjxl-Q)=qBa-12idSPmI5!+8*y9&PU4 z7L)hrBu|0eN{j}fKxmhewkkJhha^JwinS+?0LQvb~#`W7-#Yy%9MZ5$M~jW zZhU{+l;TNDWsV=|M@Ctjib`#UP?Gu{rrD6~&@L3xb?JbkmtFi%VOB5u&sf$Fg9WfZ zvR`JJ2XfSZ$a{?93_HsCx*}$LdJ^@| z`r;Z>h5Y#anfU!BIGP#XkF@N+CVpV!E|UVEM>Y}k8MPmrUbA1S2MidY8vhA16RQar zW{SBfZ(b%EM;=(Fn^y$Sv;EDh^r1L^uKz&41PQPP zCV~X`n`xdak@=d5!aL^eN( zv|Eczi?JNVn!*e!(%>G{3=3N9LCtz|>b*y8IpIit3GFq3O&&rSe(w0(uO5#(jRV*M zLR$P#<6@h?4INXDZCS5L6=^?@IS>?ZISM371hs=oIR~p_g_RKllSTd^Q>Uq1BKKc} z0X&Y(qQ8vynr_cb!w$dzh-&17e=yPAFa*oN^WQhWHaPZL^eD_ha^ESeA=nW-H9pi!c`4huY?VP@d2A>> zJTX055!q6IQqNlowv++DZ(v`I?x4UhJ&Sby zP${%^XZxII_OA@p*TGQ${(IT&1rQJ~`=COM=Rw4i+_jnTkpW{A<B@k%Fy2edrx3|UBP47?iR z-<%2yrUCKSh4IQgJoRjhJi$g248lIh42l-U;Wg(3*2HR3aKx5zcs)P;&2H zEFy&~(G~x3?3i7rNFT075%xnL_j0O(@VitYbF2Y7cj@71dh~sfEq{Z7?TIxnY>D)i zCe=Rcy$Aw^Ftr22a)#OHjY+8Ho04bC(hnp<9#>CEKmVJq>M1!|t_Yx~N(m{a%fU&$ zQPWBZ#mWQlr;g7J@bHd!eCoXNfw@vQy4$L{kOM2Ubu8v=a>~ z>w|^S=%I{@f%8r6rn*T4*$oLn38Sn-Ncvvk~*4 z06!Wdz)8HC6xJphzLyl?a%;)J3`N)^4ESexkbS8E7aEYu4fwAhjS(R@!F+HrdS<|U z@B*63kvA05pP2!M!e~? z%c%NM9J2^_U)!MrP|T6ejS$Q}Y<C7G^8I%G$ zFK&mRHdqhqJK1l;gQ!+4yx}U~wHdU`+%Mpk9(tACFP#h#s7_8m^mW5LecixL)L%dq zTu63)0aEauqQJ_y36F0bEIz^z;SO*X%%EjoH#XH)Qj>C#R8syta>=F~p(N#PzXFf% zhCJXDj}`RbYvY1!uyw~|g47*xwhf_<+y`oFbnC5|l&qraa1~6wK=)SuoMzO_XVj=H z@$&5H>_P;VmC=i|G9}M}?ZUsm+@X=gT2+Lagfcl~+BaTsn01TYpJMcWpA36s zoRWJ7K0D?mC(76{H{lzoK9UU@-0we5Se@S8cUTtf{I-WMzVGpC?#C3Z!+alr3+7$0 zJ8hQEF(w*R1*CIFSUyB?aGdp!m%J5XKZHRYlr;P1hjc)XM;)I>&KKE8BU6U!khAL} z8VU&0ewrE49h!z76Kp%YWD5v73|r2rJI1ix1zM#c+tG_6pV-^^OsYy0BFrrQ;Q53w zb}WkC%})Jvu$I z<&$_i3AHjDOOIX|*>d+uQD57PtcrBLj1{%)T!cV&_MqBbfI8EjxR!sD=M`XB#^6hq zfC!yT3CaX}S%6|Opl^5pCRDj*_HM&&5Mxk3zKyUz^Dx`IJ{k;$2JGRlpkD#P zg&zb(k;}#dI%RGF2R0sjDR&+Hbpe`D*trScwL=|%C+8+YU9bSH;jv(EJ``=}WaKGsf=bnEbGKg&op=4Brlkt-`{lUoyM>Kv}r;0~Z zyh#mU&D^X`qEHJmX6En0W?#DP{*q1Hp$To=Tj4ZsHjV;3Xwf6s-W5cTtQ!d)M3$hD zazsyK$;@E6#pV^%M6-fi*r+)JKm)WpJ}iAfB5!`VeI{A({yNZ~k=>*!& zXst}mmoo9~_}teynZ=MGuY0ILFE}8}dsJqkFI9PBvEz}xI2S80+}wsWZ+wn4g@y0< zV6hp*_mEg)i1`tIlJZ|NHFzz%4XZ8>aQUG~tY7{Ca_4gR^~JX_jvzHQQUg%uO7z2w z9z&es(cdc3^OFvU{{L(5T;QWB&i;RL;U?G(ii(1|AZQSb0iuEg3CQYNgGPlGEj0mjCaW zdFJfdC%cH!*Z=?he0n~4GH1VYznqylbLP@c-XA5x8SvWnj$<3$RH$^onVKdJbqHXL zID}uvi-12W0*+J@5jaQ@pwaS}>38=5Ny?z zSs0`7ljSWfQr_t6AeA!zExN4HOnfS9{i&?^ukHA$oinES{OV0#cs>^=oiG_Xk;6Fq zSrsy($Kg147~eS!ImMapx_df08ZlCG{9cs3ROrD^nK)mz|B>^S?tdH@Q(mu68yUtdGq{tGjTjyrCVCp2cM zRTDq<(dpo5^0v@J%`=3kJu&Y$^bth7-%6wXz{-W)-2_m??Wygwj=@Q74aY;SQI6 zt>9^m&ZuH83l70p{S^DAl&TGAz7kp|dYWA8?!@{^f@n2q$gsK%Zvn2FrA&Ep5g)42 zag)Zf_LrGTts~aC=%p;9*eSfO#2q;6NRAlN({-md6v7c*P~#1^b#xAhS#o7?^ti^Z zsJ9@FGL2}eD67*-?9*vFBf`rK8&)2*_WQqig(S3V;3?Z z)bZd6u|nyp$rD^1Z0dY7o%8{!zDtEEJ<8;&87X)V+Y89{k$$oK{!*iNc;-h(#;Brd zylwvu*W-?W<;HZ~Rg)$-$6VDD>Ww}LM_F|A6{bwOeS}Y5&N9=(b~@R91*U=+FAvT@ z;i3^tdxcp?i*SI4{p6FSf9ZR3kRDji?IJ z5xm||7&V0}il*WvuOm!9NDHcETX;35g^xiy$Y8`R|QByQ0Q)^-x(JFc# z(FCWG0zb6|K7t&nq}BMamSx;jj+Ilp3-jzeTqvjWhK#weTWSjr*j~(+Ej+jTgV$$b zAsQBGkdtN;mOv3>w?R52_@L^JB@f`%ryoH;+HduGDVHETh;**cX3_^X%5c zC79bFG9~QEF!R^^{KP2hXURY5*^j+mD%bZrAq8TJ$=qROdsjXxV^OZ&PW-1g&~Q z?w+7Nt#)igVNfH79rxlc6wU~R$A-dbq44NXI5iX=843>%h0hL!hlawZhr*|Z!Y7Bq zDWUL*q3{Wj36&Ir=qlz)lXmu38=Mlyb*ZXE`7@>cRflT40lEm-3(!#<^aAus{XE&L z)S)2_QRS$odC~i8RDfa(xlU|db>0`5(Jm^dV!3#l4lI>d4!0L=lv+4X)ekKi!WSOe zJ)wt3Uu>2A?)ZQ+KS&D;)NxX%_U#Iunz<()a2NTFhNuM!fi5fxuBz-leR)5% zPPl1mcrt#DH$Sfor{U*h{@I=!J{vzT!^E}Gh;sNO{Fs6t7t#+sty3=|GR|hLa>2+= ztF`0pQ^Qx`XP$z$7oH+|Gh+2bbz2qc@27u}o{$>scw7 z&Ecx~1vuTTONCx_*HTx_9f~Yx{NFifrkmqW2XO^m%C8sUbO+eq3?gLnh}H}2Y{1TP2>i8`n=4!+0$Zz z6;y#FYd_UJi3Uw}v=62Okr7h;8>yItcZEGfA`$B-)s|FpOm?k#6wX#xwj`Dh#wynx z?YfY(V&$k>^<0frtk6v=zgg*?pz<5K>X$BXt(1@2d?GCEPYlzim@k>Tk?d2uZ0W%H zpW|3}Y|%Nwg)q?EzWp7mFwBe9MJjBgZR`yhTTB!Ve$uJkmu zGt4G~eG^apY?ye`i*9vOBG#PiNGDOGlcKZ95gk>;ke@A-pQBO1(tLwE|4I>}{TW;} zztM3s#Y^vBhF^9m;=*uMU>Cx)Uq2g7O34m%cyD_T_M5!@u!YTw(61gz#b zOPj<4_JTG^^&O6g#B@$=;Oy+c-@MO5e>LQYci7(@4%&q{JMgFMz=zp^_p$?FZ{S^3 zkLqn5$YcFXCDW~=_=-~POoe4p5Anj!F#uQDV)=()snL$M!TmjZbmbBV(KE?w>8f{m zX&LHw9g9#a4>@;lYDI01Mr=kK!qJB3rwco`Ww>g#A(7PGI!Z3wVa`Xjxm)*&zEf|| zhZ6i(b~FOOfmUiC>8NKF)t%e%o@}+hq=(m~9^UD!_9vmI`*nMc;=-M_sh`9)rQFK5 zO;u@AT}W0{0ipMZE(~@JP@$uBJ!)9%>s=xl3Id6y6C8;Xicvh zpMsQo*usf7>M$!Bqt;>EcBqpA6WEqY#U!dBU6Z2F`oW|G)mh;*UZvKH>R-L6-2y;g zb3404nP87y8M-llqZ^OY z@+{iwPOS#tueaer3(*$Fq$nLG+PEdKo`Q#iuhoVNF#WRfE8%736}xkBa1Di?o1ePd zaqn!9cCa>5ps8O=K$%_hdijPZ-w_Lr9-go2PE1jBt(l1orO>Nh1Cm+A7xAK*t!m0D zBZ{{|DVV=H9GhvnYUW9n$Q~eM+?4i1AF}7joDs|QT z1!>;#V<2x`T{Us zv0%-M6jI8D)o=*kMyNOMIpiM|tq%)TXg(uBOzAN|t4_oY=&-)(9ZlOQM`tS2gpC z-i!D?6+6w)(~F_3>YZpNE8C{hn<+l0;CmadQrRBDT_j05ei(7ue$S-WIxO5`SCJ3& z9%gT_vA6_%(b`um5^X!f)t^{ntf+)&jT?u(n=xCwE95vNV~sp}(b(1e+7wDQ-3lzzuYn>TOpyS!iJSNA?u zdY}5WdY>-6=al$Tu}9kH1uC}$$~Pmy>kaW2J62RRE>O_ZsgIJt3CFpB?rPj zd+*jJhR4S+sa_4<)tl%^ZQ4OEvKSBX>eg@LFgFQtTzfsL%J6FZlHq+vGrZd#Tj1U~ zTHxL}df-f8%5|fI#Ldak$rY8z>QO*i1hg(jqb7Q`xN4>%n|M7?$G>!S)K0rOV$-SW zooE=QxAsNO;;lyQbNZ&ro-Jsx&G&WXY?Mc+Q+WDEcq}cUcACWs>{c33g6q8h0`dpz zt6#>hb&1tjkEBZwf_Atv6_zJ?wp9Eoe3oB8$Q=5^oQ71@e`TBTO3|DsnR^~%w1r;KqPTjgq>x0rp3XKmJd z6ysT-p;Ncg&dE{=t$$bFCffA~A@;sx4QeIXs8<^`-$`H;4W$Q0l?G}vdfFNY;a+Wd zO=E`oM=;Av5BoU=psHhAUUY$GH3Z8y2R&O6UJ4w1GjnH6UNQBC;5nagN$#s zjnm5BCh}5u0d$Y_+nb_>A5km0Rl5db=s%`X4*>C}F(vamtXef{$!>j=-b|<^B2BfU zr?SBNmwKbyJeZ1XRGbA*S_0+tp4}4pyoX!!krB7kQCci*z>B95{b>&->*;0uW}$u* z`&=*Hce0n0dot|KtsHThd;h~Irn^sYk8aqUW}n4iu-n7jyD``c5!RelVPc2sJF=yR zdFL8uS9vF+@}&^y8h-ENs3QkPA=eW$pGK{5Zc)uecg0SwgJ@N{gd`0W(hmG!2P!7}{)Ohf9*G3ycl+3<}1I5!sFBO&g2P8U~ARmYT`mndpxNNfVFuH0q$=crKFk360L^El&n0tJls zNhEW~6TC=91~LJ#q~w4ZTp%O%Jky~MN4Bq_-Dd;zaz^incx2VKLH5SB`svpu0_d32 z=BxheU>F8xb)shL%lGZLPto_f%A{}3FoYjmTE<6!no~fUy0nA`E3M`%Ow>`yOWVoQ zP`6_*V3=0UrSR~R3AP<5ybsxd((ciVL?Fj)yFS?Ax?8#Ka7(-Hjtp*5rH73u)R>m4 z&UCb(d4&p@#&*v=K;b=#jUWuPZ zc>FBgC@1Y`>w{gQo>O~y%p1A-U;SF<1=J7iO^(Bs30K{K7b$V=?J7R*$JE z5;3lJ%i!7*fl!-iA6pl}em$nKlSz>gm3H-L9Of#JvPeA|s6?sQQ<3+y+mA-V|1l$Z z%ZW#-Wu!fFn4qDj7L*{4B)u^%Kr(F( zlyAb58p12<1~|J}y#Z0Z?D>HnSy;+LUE$L)h@>iEy_@(so=td*7(k;-7@1C5^&Wbv zdZbIwa&|qNoZ{Nv3O8FE0}@oexBn3T(8fguB*^3Ds?7);U0CF=1~chn(I2VIH+$NM z>)@GhOxQDEAJIrfzB#CzP+z(9qryaeW1^7oZ*m>qnD#|`+c6QAFl>jh%>aE!3F&c7 z`ZlwVH`5K#S0i-O5K$e(UU11d*otAl4I>dK_4RK9UQLo*Yu4a5Iop1c>a9;f>vc00 zq*J!mCMpf=c8zZQ6qV6+HzmNpWEiMTQkuP^G!voeHkwg7Nzm!7bm-GgGXAwCM<RJNGlxnTJ{Tzn=~IcF zlWTsAcNcnBqc2~r>Qjkcd%igkqKwxCu8O{8501(_%FcsGLh@HXSF2LRmBhXHJ_t~Ry9N3Ok78n_qo0ECaKr#TE60dt4>KV> z=0p5H*MCyrKPezmV9LnCNh1qKtQ7b^KaY|EQ^rmjS72SHQVxk%U|}ZG$g=b$_@~(Q zN3r`Kt^c@EWR)I1*rvlw znKc66XpjB6%DUFNXxyZc1@JBH{4%ScWNF#4+ddAHS6+VkrS8<}zPStYm$=8Kr;SS+ zd%?w{_|82pecagev18LM`cO<)!Y$WZ2`f?(h79P_H-vu0P%sj@Xy%$V^&2U7Bwl`E z@5J|ELhEG`$MqQfv-LOG`g4fmQb!Wn`cK>X6whOo|68{H z9M-3Pk8OX2(pK@SV13oUx846Q+x|w@rwJ^!{lpeqzmfH6Pc~aWsc!-q7y)#E^?yXe zKF0lP)+ODYytY?$@1(VTU~nk3k}3Ug&A|71+gy@2iQFOm5v)%WFKzu>R4|nO^e@ma zWPO^&Y1>~Jv0uabYog*&rQ$*UZ({v6>KdAOB>f@Tgl`w?FLJt{I8S*%_7Ac?O{R$0 z-)YA$nK-WBkU87^-F7*m`FnI#563@Sf7Ni~|8&;>HW@PdiPziy7qb4sDE&gEPx(~C z`fv7f*uSbSX>D?KucViH+3sv%b4#PrsmM;JJ*@vDr#ngCR}N4*9AfNNpcw^E#pe$p~KPxrI^vz+#C)bSwyyIB8Dr+(t?w)=xA4RK+chlu+#BK9Y+ezsFT z@k-nN>@Tptg7vGT%G@fu%xz%(sZRGRBK|jgf&PBhALg__Ct|5HBs$ztZ4(&f6LIrf zwMk%SGp9C7=LBuzx2ime!T@QW+^CZe> zT&j+TdLHwasm{iw>UO9Tn6IY-fJ@csP^T~->{ZzMwpOffGB0MnkomWm?`N*+Zs-*< z|M@lApsK5JyNJ0vM{`w2XMXgx1%xk`-!D{B~m?v=n4W|P_D1B}hoca*N z6J~1%RDH{CC8<-!`3EbsRa%0Bb8syh?N2~?2ywmOEE!1_7s6<#P=2@&_TXHY9sDJ@ zufE4P7~Q@bhyQmR-Ve`nEO+Ru$Vqr;Z{X)z^(ocprTxV{@gNuafww>n0l@;Wd_>@PBiwpHlp|2oU{V4SP?k9m~s*oGK8|EG#G^Gx;S2q?9bC|7FFBmHNsS z-r}=L@=MEnNWJ``qPh76i^>p6c09LW0rb++t=ucKubnnEJNMcvuDHQ7Gk50HY1y7! z&N)~sF39&6m*y^3w=zRZd`lP=6&EimFUj>SDJU+aBy#5byi%Vj3K0htT%NG{<7ex6=<;di0v<7l75^!BOkz77M zn<^ri$nK^QlxL=S?bcma50Z&Q@n~tGL7nT&{jJOHe?Z#^XDL2;~;c#Y3In!$SEI zb>AcVo84%?wIcGn|GW?EG9WyLtz9of@ z=ZdPYJhVe}p?b4u93c7l%I^hyOMX?})<(a6gN*GaUZb#^E#hy@bruJB$hE5BuT=F5=GgdDX!$ zhMqAWc5r8Wo3U>iUF6Os5M!RUlA}xV%efOE`3VCf<|p9RxKqjRA5<*p*6=_a{)FIT zw6oT8aroBLv;hjwM2Gty#^D$6dx&pm2`$yC4Wrt zJqg;}FXHeghH8EC*I8~~JTt;4;nt+5pWl;=zX#drgV_8H%qbl*Att_+am=raV}9Lv(e4+KgSdVWhi?aG2!FgY?j&t_7GMce~1{(kD<&UT@HE}7QmoxJy4 z%}u_{`4P z-j^UQ!~1c6g@8XZ;i+VI3I%_d&2urd-FcLG zkud)l^Af?IWbPOIel8#@1mD2CLhy~us|2rSUL*Jp_P18>7n!dW{3Yfg!Czs1zuGV&(z=AoxGookqbwVcsP8 z=gfBr{sz0>BKSe(dj#*z?z9SiBEKi>6MP`^{eqvu{D9!6Gj9|8Oy&m#AI7|0@R7_9 z2|kK>r{EVbKP>oI=3Rnc#JpSZiOeyCtS(bOOlF=e_*CY71@|x?Aox|xQv{#Ee30PR zF{e4ibeZtXVm?&x8=1QW|1R?pg6A_&72L;sjNl8IrwhKAd4}LinbY??XZXvRX9~WO zd6wXn%%=-}8*{JVYnW#X9$=m$cpdYZg5Se@w&3fT&k_6~=6QlQ@N-uv_`_^|f#8oa zFB1GQ<|TqZ$=omaQ_NQg{tM<6fhHVD3l z%{L0(%DhSNeav?WZn?F;ErOrPe2?I7GH(^!)K~iiH}%zi!A*U2KyXuEwF!PI*S7}+ zH}zG!;HJJhB)G-t-zj)9^TUFh`l?HCQ(tupZt5#5NvA7mlbHG{S#VQd^%dOIR|5n$ z^;L@Cro5T&B*xzYPPY-leD(+(zEr`}M`}Js@FF&!E_f~T48b=spCI@y=9z+jz&uNE zFNbHk;MvT*g6A;L7JMf29KmNZpDFkp<}{y)E|Z>l%;yMR$UIN*vzZqPUc`KX;3dq9 z1oty95qt%6zu*^cpLKrf*)kwCirvA4+=hpdAs0s=g`v@S;JtS zy9_qpDa=n`epv8K=3Rm}u>0MD?`N()u)u%gZx{3AWECu%XS4f#1s&KW-?C^ z=4Ue>BzWow?cZR*^Vs}Q!3&wY1z*5?gy2QYQw1+!K1OgqbF(hQ#A^lf3}L>4`2@kM zm}d%J!#qoHQ~yjCd@Y+d-_`B#jL_*;BFwjp(cCZiNc!l6YlYzFcr>pN{5-XM4Z^G3nvGj9@n5%XPw7c*}Wyo~uC!Iv{{75rA_`vhOje81q;%nt~D zC-XMJgUk;KzK(gj;J=@w%jY4%A7t~Lg8zW|VZndIyi4%mlePQZg8!7woB1#1xvOUL z$-T5a!{6umF?|KMMrm^c1fR=1MetqB2MK=e`P!Yqg4Z%1D)>K{y9J*+TDvns@F$t4 z3jP4sCu0QfWS%bg46bJ~1iv?3`#VAKJLsc5u1vwrbC)H!dG4kQzG|Fy$1Avb?y?0p z&s~n-^)z__*G$1HGBlqpxOwj82yULcJi*O#S19;`i?sU-1UJuJk>D$7k|VAX!Oe5$ z7u-B|D+D*sU4`KHd9?dgf}7{AM({s#cxnY7cZqgqt>E`F4+-8fL7Tr{@bAzhM_d~O ze~tM@!N*>z&DRTl2G=K>1mDGcv*0gyv^!e__hxFoRq)4|KO^`6t~VM4U(38v@F$oz z3I3)>`@2i}OXQ$xvm>(AW2h6(!Z)M&sc&{nieT(PIn0o#a=4L*f;R~717UugF zYIo)cK5DM!d4k`}yio9N<_iQL&AdqP3z?S){yXM=!5?A1Lhxq`bU4j?s6_lE8Oia| zy0n%~bF&_^FKZZX)?=m!Zq`K(7JU6k4cvm8b%?2goArj=%-G>E>kYYDvAJ1qm?hjX z>kYkvoArin<|ZBZrW<76{;ATB)!A<#W5!|fXZ5Q0E+wCjfyUn`YRKZO-_X=*-?Jf}9tlO;>+^pM` z<2L4bk>ep|-L4#GFwaqn=ntFcC_`}b9L*NoJVzCRo9Aew;AXvKqu^#8=mEi(=c(dh zbql_p`A|`xn{|sU7m?kd#bX%yV7mpmZ2SufcwxLGebRMf*} zy`+^^wsJ8oL`=iVF0C#wccs3f{4}*7F-@(qN}F3&W~C`wTC9osLAQj{N{iEIL0*xS zhDDQ$eOR5CwxrnaOS|%#Sr=gORKX&)?OTvLuQY$LPe<{%{<~qD9(j(Q?N%1weZ0U% z>V~Ta8xFx8WqFoO^iAvSTJ7Sc+q9P+*){EuXR&zMKM)$R>`!=3+hAvm^&aWR3uM!ZFLbLbT`P`z zO)k#JQ0(IIeB;hLVYMgwio}FSjwE2kO5~UArB%ywjEQ6^oQ3opV9oLd{urPG)XLki zhc$2Wj;%A>f)>tODjkY_hSk{)FOnVNbhblQGW3=eHxLV#W7l_>N@xw3BSx|HK5dgr zM?%D~)*pJ<`8D9_4qp({7f7P;+Z>#M75rVn=1a%l_2OY?XJ?wu;k2W6!fXzq$8~dqZCs*Db z`ybGQwIeCmN%fAm^^aU+vg?kQ^p8>J;73iP{&Bwg@pJuSHOkkrQ(^hFP4M$X+)Tk! z=~@%g+DD#%c0bTZ-o(u+@}!H&kxy|`1mgM^lhf(uCXfpaDW)4Q*)k;W2*|}C@6rvq z{3p5@L1vC5X!7}yL~Geeir^*%H=koA>vB$3eee!4#NP1PO?!@0fiRo?ry}3madt;@ zdo9ulxr+ldtd)IKf`)M-S62cKU#rH+O4=L}a-b?HbizXbd){Je<84TD+XKon%7Y&u zQ6hOzisS!awU3&u5IzA0;NfZT(6)D!fc+Q?ga3U2TM`wpw~&-Q0=5dFwF71%6kVtu zhP{`54X2KJq2efpRHKf&143i{0mwaQXtqIKVA%n6Cxpf*UW7EWP@|s&Nr9XKxf*ga zgx*m}-hdp0{24+gUi}5~SBMLBBFR9Obg18i`~PyHK5HIAL zkQtC!kZ(a6SzdrjvYTZO)K?(yLq32^MdNijWCqL2P)S~g?1Q|)G8T;#Ne1Ln$d!<5 zAWyM81NC{xuON+(7RW1*!7j@>9dZ%NB~VEwvMhzV3Q`5Bf!qPP6B2?v2zdz72-(eY z0V=BUAbF4i$oC+{kXFctkn_{Awh!_U(3wwCnQ)&kY149kO2@EWCG+;$P||OdlRgM zkTS^akTsAXdc19Aw` z$?_3YlFvsXtssNWMSTPDLT-SpX1Nb4$-^vkzwRFcnGdYy+pIOGIKe@H4zHdK;pSZ;ti3-WE2^-v##Y-M=?D#?)1*rybd!!iph zNmUy17V-ngM-W=b`8!A_!}1Q2XZ~65HcT90{QIe z1S=VnoJn3|x%=-47Rf!3??bGABv^@%6v!k<7Q_q5fm{d4gZLoUM+uf2aslKb$R&^p z$STNvkb20=kk=r;g|tCBA%|JIpk9WF)mbdlpmsx!Kqmh)!MYl94I~FLA94%JdZ-UT zHnDsL^;&EQRt0$xvWKM=>g$l-v4o)>f*gi?3TgW|!3sl8{3O9z3MqpugWLl7G2~Im z!KZ1M=LGe<1?sY>&QBp#$ z79;~%PKHV{gymeQw2$6sma$OBLng3HfjSK`oy80FYRC+h>!IENnFpBnRAxHz{dC2Q5`=FBinZ+7z!2|U5AW4uukiL-qEH0=d z16fXiN^%CvnNZ!3b67?~C80GDV<2fP8Bi~QWI`rGye!#JNv>zfgPIR1gv^J053&fd z1X2vS9a0Us19B&1JH!=V-|rfi@EohFp#IF$Yoxse;fxBKJaQT)H0e zFys-)W00pI{{?v#Li61kA=@BLkR6cSkQT^qSYCy?7xF&jkB|>pdZB&n4e7`74X7@N zX{U$cp5!c+ET|;YSn{CeLkb{1$b886AVrYHkYbj}XfsKcK~_VoHndA!XrIy6kib`{ zE~qI`Q_#r$`}P>8de7d+H}MztKDwF2aZz1j>nF{y_dK4XbP+#N%|5`1dZXK+KozV% z!Kq&s*#~zc>&LU-_yzV4us(`P#Q&SM`>fSNp{V>%?vYC##@f4e4LI%LX|58~V|4Y0 z&>o@YQh9+NgP5D&Di6TUWHB@(o3>O{)|bjCRx3s0eb&ggn zY!Rq7z)LHz=3>XS{L+F2*5XCE%M0|bcj~sxaj$N33%<9=%ExYb^UI6NX`?@fF{L-J zyr{^zZC&GX*#9xV1IqK4*<0 zS%RB#ktet*7gd6ra#1h1$^Rz7P5!hAZt~|VeB0sAI$zu88x?Xl{?pSXOJWunUHP03 z`j<9q?n%X~Eb0r(%7Qqr!SsOQ%2WUG-UOBW;m6Dx%pBFb;=&u%=V2pJN~9*xcnlPB z^Q+p&YmxHbS(eDB1|WA1H3xM^Yg zPDc0A?6;Z8@EiMG!U2>1249bxSoybt^KUa7r2Heh#=glv3Xk-gtTYXz|A+?%nCfvt z(msaddgL9DRf};Qy4GpM{x9QCZpyqT!*2fWkZ;@6H`2b^F4F!2;&!LIBbUC{|IIg$ zvFRD($0;Mn{tcvQL?jSd>NX3#6qou2VjnP!ac>&NwvUa)s4|Xn>%viMIDxDkT3y@8 zs;v)Tj|1zI4)rmnIFGQoolH6oLd3Xtpu7ub-(qn4dF6^e-j7Y%?xf?3OrRWLpmPUj z>{o;7!4xPscX0O$6OeW|+m-@G-%r%xgrrHgFrd#u4bD2mC!r$Lft*34-<;)Eo|4h# zs{M42OWAC$YEwV#pPkV^P4Q*JeG!B#nBbo&oSW)rFhsiQ)^X6HI)?dndTiK^+v!9w zL_cHO8O%C%>gZHf@vTZHO$^S{k(Y?S{>{nBC{-9yzoU$V>|i+qOzwq@=gu=D^;wpxzefa9N8=4;xzA#X~FnlT({4gr4z8bg6hoM&Oi!E zYP+RQ#Rj81Zx1-m&{YW+C_fy)VV7iU)?vGdbaD|v5>mOKB%P!Ku73x!)Oq9QIT#-~ zPN~!eE5R9^IGdACnso*jKJlT;IASq!)GfjR^ZFdwE;@I8hwIsCgR9$cwrlOQ!J&vj zm6{Y^mBZ?sPAZ)I6Awv<3a7CkPYPC+s+0x9X%yMY4(i`ZOIgM_x#T>u=T$tlQ;)QU zi?#ESq8zNHLQZEtk81Hw^t3O}W)pVV305f+bpsN)qJVR|y@A(MCBTN2394NZb}AAG z5y4@fc7aGYAKxM;{%+-G13|3P<&mlcsv2|!gAc8`E`WhrZ9tzU>o{oAcyQYx$}4YR zTPi-7c>}+2D`kD2QtB2!$quY3QA*tkD0I@RYm}$THEKr4HELG9Yt(nQx<>h$T%#7Z zx<-|^xkgoXHm{iglhjT&ugSz+I{8rNL1|k1-B*5r^9SVO0!d+XMdX>YJjc^zD% z148K@Ra@S6NadLc==#CRiLV&KKxgfm5pclutWs*4R^I5h$XN+lM0SN zm5s5($^}n5*CSWM$)QLg7g<|JS^MQnoypTcYwa3Ul$upIDNCxhQ4Zh>{SN&2U!CXI zpmxxC?nYFa*ZhBw=dNlR?x@;EX^teW#t$w8_Os;g=dDvC2;n|_gHokS-My;x;2|1a zrwX3oPe+%=Q@w$msJ)^du%ng33y-xN_6bE5MLyXY6)W{w9BqznHmGuzFm{u`ree1~ zs@6xFvCO5L0o_nwYK#6^Yr&gHn_*XlPQ$QWcBvDLrY@>(M1t%9H7h&VjB--7W{&1Z z`|Gf&n;%$d*X@shRJ?1b-U+Fa@oT?U2hlgp;&(NNqtd$tPqWhoc4Rl@=_%u84Mt0y` z)b8kaG@&tW3CwB?%xJ)g;#;!=Gd2f3jSmG{$9oz_wFYPH3eIR6_435l%F}|L28A04 z?+MOmAzX0|&UQy@;%UiNr$En0QKKQ=z{ltrpqJ4>A5W?)8>|(>VFu?v2R(a6dl~|s z1HoA>a1m3gJndis&!KQCet*0llctnbH))Y^GD}v;*l5g{@f@nIY*g+w1w5@m&#uv) zMjW{voQ0#B8_7}|Sip0T+-oHF&1_728#~AVaAXESvV*dV{0YyZ9@ z_)@%>1w99KBF{pOwb3n(OrKSct|!vdgY?8X;imf;Br`Olj&k?FL{De=cICn$LU3vS zL{C@wBO%kNt3H&X-z;e!oc{uEP*uK|mdEdy7a>IG^Evt}FHj&=&45?i#8nY1UC+}X z0#C|{V+G!lgYmB+BMU72ZM1M^>i^_bv~aE(HgGS_|6Xyadzr7a3`erK7cVSZobNAK zFv&f#(4D$Ge~G_rlzZ4@^$S)?^GjEbqG1%AabxT+T=GTsFO$}f@w*23!$UYnJwJ6C z88qJ}DkI+>GKu4&@pN1Nrii{t;ZaU~eQcyUce8>xuCpk|jr&Qb+uueuDqTKD)#xWa zs5}@7y++ojo~CVoQN;fPtUu1Fzc`}b&H9s^`pY8vLn&OiXk6WPKe5>MKa=&T&ui=7 z5V1dp_2b5$^=W+3wqF#nzmfITppG5?=WYKRSwG)t-|_9}0PE9K4cq<1O}71R*8c;k z#{Hx>?DQYXgLXkuZT&m!GafTpUk#%f{hUbn=X`;F1?$uJuI+w)#QsLsZ*uA<{>l!2 zBkR{h>94l+53s(PYHh-oxYX9~X8oT;#sA?*{CUvu)F}Odwtgn-FOTxSR7ZeVjEkK5 zpZs=4W2Me-VbeGaR7Q!L-|CrVy;$GcMncc|tu2Mkk`n(Lma4iOCiU%?IY;jjSaJGg z9J@0Pr|C(t%s&^0&jhD)(DI?urJhZwH$#ZuMG%*&lb}-G(l@o~Tvk+F19mrYnBPnp ziff}*tcRe+3eOX9IMrd~zMtJ!brslN;&A^b%vBu)*2erj=BjQ2`;hr8E|aQG0{a*9 z#mr57>0C{6{{iNz4uV;lFGJxm=TNG;2b|{0#ERDyY<>-WAi$Nc6)QiEd73{%?*EC+ ztGWqx==@CL>s0>$^IY(ouDNmC*~I3*$KME59R>3{i6dVBqz3^P5AmyW7>S?2Mbz*k zZ2oNKsxE@PVMt#x?_u6Vj^P>)9xELtvH4!~5d)X1lb~M9JiwfneOLv|k1$tt5X_e| zfAmCh1@mAKKDzE;PIGJNQgsg0?}Nt*&%<%}V{y1XEDK+V3w`tQ%ZvQx#5VI&owsIw z*vGKNIsFZ%uSL#7ql5eOfm^uCUB1x2AU9%PYuLYyuNpfJe#6;rKHn0oO0$oG=TA2Xmg%r%e?Um#oY!@D&xAgXIbq z_zD)~a+et=+7lF}#u> zE<6?%RV9X-=au<~{{OJ&rk6jKsRO+UX)aMRB> z6OKrm#FRHTHxP!KetwqVrl0>6<_OZR5cZZ2yx&9ArVwA}>_Io;PFc*&JnrJ#OmTbD5iV89&ukD*QE^i=+8zQT<77epUMy z!!Efg?bBLEx=cHwfA#!LZ5Kr`%NSra!~-~{9;f(IA4A6fYB2Jj%A50|wUu-o)QbJz z*f-z`+{E&~iTyYIRq~&@X2!lr{~N%_e-lpJs>{z2Pc?v5lm14f`q|t&jc$|)C+r)u zOWD4Re?8luEi%T0-`Fq0O|1C04$~$|*kOu4H8sY*i9dy1`b{>wc<$m>avT?RhsX_S jpZYo?h#**p#UA`jI4;v_U>y5dBa}1Nty;1FOZ)#9l@&U} literal 0 HcmV?d00001 diff --git a/libvterm-0.2/.libs/libvterm.la b/libvterm-0.2/.libs/libvterm.la new file mode 120000 index 0000000..67d6e0d --- /dev/null +++ b/libvterm-0.2/.libs/libvterm.la @@ -0,0 +1 @@ +../libvterm.la \ No newline at end of file diff --git a/libvterm-0.2/.libs/libvterm.lai b/libvterm-0.2/.libs/libvterm.lai new file mode 100644 index 0000000..53cec2f --- /dev/null +++ b/libvterm-0.2/.libs/libvterm.lai @@ -0,0 +1,41 @@ +# libvterm.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libvterm.so.0' + +# Names of this library. +library_names='libvterm.so.0.0.0 libvterm.so.0 libvterm.so' + +# The name of the static archive. +old_library='libvterm.a' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs='' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libvterm. +current=0 +age=0 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/usr/local/lib' diff --git a/libvterm-0.2/.libs/libvterm.so b/libvterm-0.2/.libs/libvterm.so new file mode 120000 index 0000000..6a3f714 --- /dev/null +++ b/libvterm-0.2/.libs/libvterm.so @@ -0,0 +1 @@ +libvterm.so.0.0.0 \ No newline at end of file diff --git a/libvterm-0.2/.libs/libvterm.so.0 b/libvterm-0.2/.libs/libvterm.so.0 new file mode 120000 index 0000000..6a3f714 --- /dev/null +++ b/libvterm-0.2/.libs/libvterm.so.0 @@ -0,0 +1 @@ +libvterm.so.0.0.0 \ No newline at end of file diff --git a/libvterm-0.2/.libs/libvterm.so.0.0.0 b/libvterm-0.2/.libs/libvterm.so.0.0.0 new file mode 100755 index 0000000000000000000000000000000000000000..52a2f8b0519c4211fec0245a1bd848096eb0d9bb GIT binary patch literal 99656 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 F{{=e!Z$SV6 literal 0 HcmV?d00001 diff --git a/libvterm-0.2/CONTRIBUTING b/libvterm-0.2/CONTRIBUTING new file mode 100644 index 0000000..2100d1e --- /dev/null +++ b/libvterm-0.2/CONTRIBUTING @@ -0,0 +1,22 @@ +How to Contribute +----------------- + +The main resources for this library are: + + Launchpad + https://launchpad.net/libvterm + + Freenode: + ##tty or #tickit on irc.freenode.net + + Email: + Paul "LeoNerd" Evans + + +Bug reports and feature requests can be sent to any of the above resources. + +New features, bug patches, etc.. should in the first instance be discussed via +any of the resources listed above, before starting work on the actual code. +There may be future plans or development already in-progress that could be +affected so it is better to discuss the ideas first before starting work +actually writing any code. diff --git a/libvterm-0.2/LICENSE b/libvterm-0.2/LICENSE new file mode 100644 index 0000000..0d05163 --- /dev/null +++ b/libvterm-0.2/LICENSE @@ -0,0 +1,23 @@ + + +The MIT License + +Copyright (c) 2008 Paul Evans + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/libvterm-0.2/Makefile b/libvterm-0.2/Makefile new file mode 100644 index 0000000..e3c1c39 --- /dev/null +++ b/libvterm-0.2/Makefile @@ -0,0 +1,145 @@ +ifeq ($(shell uname),Darwin) + LIBTOOL ?= glibtool +else + LIBTOOL ?= libtool +endif + +ifneq ($(VERBOSE),1) + LIBTOOL +=--quiet +endif + +override CFLAGS +=-Wall -Iinclude -std=c99 -Wpedantic + +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=2 + +VERSION_CURRENT=0 +VERSION_REVISION=0 +VERSION_AGE=0 + +VERSION=$(VERSION_MAJOR).$(VERSION_MINOR) + +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/fullwidth.inc: + @perl find-wide-chars.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,@INCDIR@,$(INCDIR)," -e "s,@LIBDIR@,$(LIBDIR)," -e "s,@VERSION@,$(VERSION)," $(DESTDIR)$(LIBDIR)/pkgconfig/vterm.pc + +install-lib: $(LIBRARY) + install -d $(DESTDIR)$(LIBDIR) + $(LIBTOOL) --mode=install install $(LIBRARY) $(DESTDIR)$(LIBDIR)/$(LIBRARY) + $(LIBTOOL) --mode=finish $(DESTDIR)$(LIBDIR) + +install-bin: $(BINFILES) + install -d $(DESTDIR)$(BINDIR) + $(LIBTOOL) --mode=install install $(BINFILES) $(DESTDIR)$(BINDIR)/ + +# DIST CUT + +DISTDIR=libvterm-$(VERSION) + +distdir: $(INCFILES) + mkdir __distdir + cp LICENSE CONTRIBUTING __distdir + mkdir __distdir/src + cp src/*.c src/*.h src/*.inc __distdir/src + mkdir __distdir/src/encoding + cp src/encoding/*.inc __distdir/src/encoding + mkdir __distdir/include + cp include/*.h __distdir/include + mkdir __distdir/bin + cp bin/*.c __distdir/bin + mkdir __distdir/t + cp t/*.test t/harness.c t/run-test.pl __distdir/t + sed "s,@VERSION@,$(VERSION)," __distdir/vterm.pc.in + sed "/^# DIST CUT/Q" __distdir/Makefile + mv __distdir $(DISTDIR) + +TARBALL=$(DISTDIR).tar.gz + +dist: distdir + tar -czf $(TARBALL) $(DISTDIR) + rm -rf $(DISTDIR) + +dist+bzr: + $(MAKE) dist VERSION=$(VERSION)+bzr`bzr revno` + +distdir+bzr: + $(MAKE) distdir VERSION=$(VERSION)+bzr`bzr revno` diff --git a/libvterm-0.2/bin/.libs/unterm b/libvterm-0.2/bin/.libs/unterm new file mode 100755 index 0000000000000000000000000000000000000000..8c06ca6521621ddd1f379bc3c9a1c50f05a6e2e3 GIT binary patch literal 22512 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 H?fU-#En)J~ literal 0 HcmV?d00001 diff --git a/libvterm-0.2/bin/.libs/vterm-ctrl b/libvterm-0.2/bin/.libs/vterm-ctrl new file mode 100755 index 0000000000000000000000000000000000000000..7b36d8f9361ee36daee02a0e8f76b1988115a287 GIT binary patch literal 17976 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 Ac>n+a literal 0 HcmV?d00001 diff --git a/libvterm-0.2/bin/.libs/vterm-dump b/libvterm-0.2/bin/.libs/vterm-dump new file mode 100755 index 0000000000000000000000000000000000000000..a44ff1c62b275fc43f75d8180af98bbe66ddcf7a GIT binary patch literal 23088 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 Jbp}IK{|mc$O|Jj| literal 0 HcmV?d00001 diff --git a/libvterm-0.2/bin/unterm b/libvterm-0.2/bin/unterm new file mode 100755 index 0000000..3232a48 --- /dev/null +++ b/libvterm-0.2/bin/unterm @@ -0,0 +1,210 @@ +#! /bin/bash + +# bin/unterm - temporary wrapper script for .libs/unterm +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# The bin/unterm program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command="" + +# This environment variable determines our operation mode. +if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then + # install mode needs the following variables: + generated_by_libtool_version='2.4.6' + notinst_deplibs=' /home/rt044200/Downloads/libvterm-0.2/libvterm.la' +else + # When we are sourced in execute mode, $file and $ECHO are already set. + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + file="$0" + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + ECHO="printf %s\\n" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ that is used only on +# windows platforms, and (c) all begin with the string --lt- +# (application programs are unlikely to have options that match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's /usr/bin/libtool value, followed by no. +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=$0 + shift + for lt_opt + do + case "$lt_opt" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%/[^/]*$%%'` + test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. + lt_dump_F=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%^.*/%%'` + cat "$lt_dump_D/$lt_dump_F" + exit 0 + ;; + --lt-*) + $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n "$lt_option_debug"; then + echo "unterm:bin/unterm:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-15" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + $ECHO "unterm:bin/unterm:$LINENO: newargv[$lt_dump_args_N]: $lt_arg" + lt_dump_args_N=`expr $lt_dump_args_N + 1` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ + + if test -n "$lt_option_debug"; then + $ECHO "unterm:bin/unterm:$LINENO: newargv[0]: $progdir/$program" 1>&2 + func_lt_dump_args ${1+"$@"} 1>&2 + fi + exec "$progdir/$program" ${1+"$@"} + + $ECHO "$0: cannot exec $program $*" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from $@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case " $* " in + *\ --lt-*) + for lt_wr_arg + do + case $lt_wr_arg in + --lt-*) ;; + *) set x "$@" "$lt_wr_arg"; shift;; + esac + shift + done ;; + esac + func_exec_program_core ${1+"$@"} +} + + # Parse options + func_parse_lt_options "$0" ${1+"$@"} + + # Find the directory that this script lives in. + thisdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + test "x$thisdir" = "x$file" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'` + while test -n "$file"; do + destdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + + # If there was a directory component, then change thisdir. + if test "x$destdir" != "x$file"; then + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; + *) thisdir="$thisdir/$destdir" ;; + esac + fi + + file=`$ECHO "$file" | /bin/sed 's%^.*/%%'` + file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no + if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then + # special case for '.' + if test "$thisdir" = "."; then + thisdir=`pwd` + fi + # remove .libs from thisdir + case "$thisdir" in + *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /bin/sed 's%[\\/][^\\/]*$%%'` ;; + .libs ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=`cd "$thisdir" && pwd` + test -n "$absdir" && thisdir="$absdir" + + program='unterm' + progdir="$thisdir/.libs" + + + if test -f "$progdir/$program"; then + # Add our own library path to LD_LIBRARY_PATH + LD_LIBRARY_PATH="/home/rt044200/Downloads/libvterm-0.2/.libs:$LD_LIBRARY_PATH" + + # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH + # The second colon is a workaround for a bug in BeOS R4 sed + LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /bin/sed 's/::*$//'` + + export LD_LIBRARY_PATH + + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + # Run the actual program with our arguments. + func_exec_program ${1+"$@"} + fi + else + # The program doesn't exist. + $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2 + $ECHO "This script is just a wrapper for $program." 1>&2 + $ECHO "See the libtool documentation for more information." 1>&2 + exit 1 + fi +fi diff --git a/libvterm-0.2/bin/unterm.c b/libvterm-0.2/bin/unterm.c new file mode 100644 index 0000000..6074b5e --- /dev/null +++ b/libvterm-0.2/bin/unterm.c @@ -0,0 +1,280 @@ +#include +#include + +#include +#include +#include +#include + +#include "vterm.h" + +#include "../src/utf8.h" // fill_utf8 + +/* + * unterm [OPTIONS] SCRIPTFILE + * + * Interprets terminal sequences in SCRIPTFILE and outputs the final state of + * the terminal buffer at the end. + * + * OPTIONS: + * -f FORMAT -- set the output format: ["plain" | "sgr"] + * -l LINES, + * -c COLS -- set the size of the emulated terminal + */ + +#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 dump_cell_color(const VTermColor *col, int sgri, int sgr[], int fg) +{ + /* Reset the color if the given color is the default color */ + if (fg && VTERM_COLOR_IS_DEFAULT_FG(col)) { + sgr[sgri++] = 39; + return sgri; + } + if (!fg && VTERM_COLOR_IS_DEFAULT_BG(col)) { + sgr[sgri++] = 49; + return sgri; + } + + /* Decide whether to send an indexed color or an RGB color */ + if (VTERM_COLOR_IS_INDEXED(col)) { + const uint8_t idx = col->indexed.idx; + if (idx < 8) { + sgr[sgri++] = (idx + (fg ? 30 : 40)); + } + else if (idx < 16) { + sgr[sgri++] = (idx - 8 + (fg ? 90 : 100)); + } + else { + sgr[sgri++] = (fg ? 38 : 48); + sgr[sgri++] = 5; + sgr[sgri++] = idx; + } + } + else if (VTERM_COLOR_IS_RGB(col)) { + sgr[sgri++] = (fg ? 38 : 48); + sgr[sgri++] = 2; + sgr[sgri++] = col->rgb.red; + sgr[sgri++] = col->rgb.green; + sgr[sgri++] = col->rgb.blue; + } + return sgri; +} + +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 5 entries + int sgr[7 + 2*5]; 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.conceal && cell->attrs.conceal) + sgr[sgri++] = 8; + if(prevcell->attrs.conceal && !cell->attrs.conceal) + sgr[sgri++] = 28; + + 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(!vterm_color_is_equal(&prevcell->fg, &cell->fg)) { + sgri = dump_cell_color(&cell->fg, sgri, sgr, 1); + } + + if(!vterm_color_is_equal(&prevcell->bg, &cell->bg)) { + sgri = dump_cell_color(&cell->bg, sgri, sgr, 0); + } + + if(!sgri) + break; + + printf("\x1b["); + for(int i = 0; i < sgri; i++) + printf(!i ? "%d" : + CSI_ARG_HAS_MORE(sgr[i]) ? ":%d" : + ";%d", + CSI_ARG(sgr[i])); + printf("m"); + } + break; + } + + for(int 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.conceal || prevcell->attrs.font) + printf("\x1b[m"); + break; + } + + printf("\n"); +} + +void dump_row(int row) +{ + VTermPos pos = { .row = row, .col = 0 }; + VTermScreenCell prevcell = { 0 }; + 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 = { 0 }; + vterm_state_get_default_colors(vterm_obtain_state(vt), &prevcell.fg, &prevcell.bg); + + for(int 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 = { + .sb_pushline = &screen_sb_pushline, + .resize = &screen_resize, +}; + +int main(int argc, char *argv[]) +{ + rows = 25; + cols = 80; + + int opt; + 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; + } + } + + const char *file = argv[optind++]; + int 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); + + int len; + char buffer[1024]; + while((len = read(fd, buffer, sizeof(buffer))) > 0) { + vterm_input_write(vt, buffer, len); + } + + for(int row = 0; row < rows; row++) { + dump_row(row); + } + + close(fd); + + vterm_free(vt); + + return 0; +} diff --git a/libvterm-0.2/bin/vterm-ctrl b/libvterm-0.2/bin/vterm-ctrl new file mode 100755 index 0000000..a975af4 --- /dev/null +++ b/libvterm-0.2/bin/vterm-ctrl @@ -0,0 +1,210 @@ +#! /bin/bash + +# bin/vterm-ctrl - temporary wrapper script for .libs/vterm-ctrl +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# The bin/vterm-ctrl program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command="" + +# This environment variable determines our operation mode. +if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then + # install mode needs the following variables: + generated_by_libtool_version='2.4.6' + notinst_deplibs=' /home/rt044200/Downloads/libvterm-0.2/libvterm.la' +else + # When we are sourced in execute mode, $file and $ECHO are already set. + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + file="$0" + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + ECHO="printf %s\\n" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ that is used only on +# windows platforms, and (c) all begin with the string --lt- +# (application programs are unlikely to have options that match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's /usr/bin/libtool value, followed by no. +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=$0 + shift + for lt_opt + do + case "$lt_opt" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%/[^/]*$%%'` + test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. + lt_dump_F=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%^.*/%%'` + cat "$lt_dump_D/$lt_dump_F" + exit 0 + ;; + --lt-*) + $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n "$lt_option_debug"; then + echo "vterm-ctrl:bin/vterm-ctrl:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-15" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + $ECHO "vterm-ctrl:bin/vterm-ctrl:$LINENO: newargv[$lt_dump_args_N]: $lt_arg" + lt_dump_args_N=`expr $lt_dump_args_N + 1` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ + + if test -n "$lt_option_debug"; then + $ECHO "vterm-ctrl:bin/vterm-ctrl:$LINENO: newargv[0]: $progdir/$program" 1>&2 + func_lt_dump_args ${1+"$@"} 1>&2 + fi + exec "$progdir/$program" ${1+"$@"} + + $ECHO "$0: cannot exec $program $*" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from $@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case " $* " in + *\ --lt-*) + for lt_wr_arg + do + case $lt_wr_arg in + --lt-*) ;; + *) set x "$@" "$lt_wr_arg"; shift;; + esac + shift + done ;; + esac + func_exec_program_core ${1+"$@"} +} + + # Parse options + func_parse_lt_options "$0" ${1+"$@"} + + # Find the directory that this script lives in. + thisdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + test "x$thisdir" = "x$file" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'` + while test -n "$file"; do + destdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + + # If there was a directory component, then change thisdir. + if test "x$destdir" != "x$file"; then + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; + *) thisdir="$thisdir/$destdir" ;; + esac + fi + + file=`$ECHO "$file" | /bin/sed 's%^.*/%%'` + file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no + if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then + # special case for '.' + if test "$thisdir" = "."; then + thisdir=`pwd` + fi + # remove .libs from thisdir + case "$thisdir" in + *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /bin/sed 's%[\\/][^\\/]*$%%'` ;; + .libs ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=`cd "$thisdir" && pwd` + test -n "$absdir" && thisdir="$absdir" + + program='vterm-ctrl' + progdir="$thisdir/.libs" + + + if test -f "$progdir/$program"; then + # Add our own library path to LD_LIBRARY_PATH + LD_LIBRARY_PATH="/home/rt044200/Downloads/libvterm-0.2/.libs:$LD_LIBRARY_PATH" + + # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH + # The second colon is a workaround for a bug in BeOS R4 sed + LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /bin/sed 's/::*$//'` + + export LD_LIBRARY_PATH + + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + # Run the actual program with our arguments. + func_exec_program ${1+"$@"} + fi + else + # The program doesn't exist. + $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2 + $ECHO "This script is just a wrapper for $program." 1>&2 + $ECHO "See the libtool documentation for more information." 1>&2 + exit 1 + fi +fi diff --git a/libvterm-0.2/bin/vterm-ctrl.c b/libvterm-0.2/bin/vterm-ctrl.c new file mode 100644 index 0000000..8f8be7e --- /dev/null +++ b/libvterm-0.2/bin/vterm-ctrl.c @@ -0,0 +1,354 @@ +#define _XOPEN_SOURCE 600 /* strdup */ + +#include +#include +#include +#include +#define streq(a,b) (strcmp(a,b)==0) + +#include + +static char *getvalue(int *argip, int argc, char *argv[]) +{ + if(*argip >= argc) { + fprintf(stderr, "Expected an option value\n"); + exit(1); + } + + return argv[(*argip)++]; +} + +static int getchoice(int *argip, int argc, char *argv[], const char *options[]) +{ + const char *arg = getvalue(argip, argc, argv); + + int value = -1; + while(options[++value]) + if(streq(arg, options[value])) + return value; + + fprintf(stderr, "Unrecognised option value %s\n", arg); + exit(1); +} + +typedef enum { + OFF, + ON, + QUERY, +} BoolQuery; + +static BoolQuery getboolq(int *argip, int argc, char *argv[]) +{ + return getchoice(argip, argc, argv, (const char *[]){"off", "on", "query", NULL}); +} + +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]", + "reportfocus [off|on|query]", + "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(unsigned char c1) +{ + unsigned char 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() +{ + await_c1(0x9B); // CSI + + /* TODO: This really should be a more robust CSI parser + */ + char csi[32]; + int i = 0; + for(; i < sizeof(csi)-1; i++) { + char c = csi[i] = getchar(); + if(c >= 0x40 && c <= 0x7e) + break; + } + csi[++i] = 0; + + // TODO: returns longer than 32? + + return strdup(csi); +} + +static char *read_dcs() +{ + await_c1(0x90); + + char dcs[32]; + bool in_esc = false; + int i = 0; + for(; 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(dcs); +} + +static void usage(int exitcode) +{ + fprintf(stderr, "Control a libvterm-based terminal\n" + "\n" + "Options:\n"); + + for(char **p = helptext; *p; p++) + fprintf(stderr, " %s\n", *p); + + exit(exitcode); +} + +static bool query_dec_mode(int mode) +{ + printf("\x1b[?%d$p", mode); + + char *s = NULL; + do { + if(s) + free(s); + s = read_csi(); + + /* expect "?" mode ";" value "$y" */ + + int reply_mode, reply_value; + char reply_cmd; + /* 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) +{ + printf("\x1bP$q%s\x1b\\", cmd); + + char *s = NULL; + do { + 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; + + int num; + 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")) { + switch(getchoice(&argi, argc, argv, (const char *[]){"off", "on", NULL})) { + case 0: + printf("\x1b F"); break; + case 1: + printf("\x1b G"); break; + } + } + else if(streq(arg, "keypad")) { + switch(getchoice(&argi, argc, argv, (const char *[]){"app", "num", NULL})) { + 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 + int shape = getchoice(&argi, argc, argv, (const char *[]){"block", "under", "bar", "query", NULL}); + 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")) { + switch(getchoice(&argi, argc, argv, (const char *[]){"off", "click", "clickdrag", "motion", NULL})) { + 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, "reportfocus")) { + do_dec_mode(1004, getboolq(&argi, argc, argv), "reportfocus"); + } + else if(streq(arg, "altscreen")) { + do_dec_mode(1049, getboolq(&argi, argc, argv), "altscreen"); + } + else if(streq(arg, "bracketpaste")) { + do_dec_mode(2004, getboolq(&argi, argc, argv), "bracketpaste"); + } + else if(streq(arg, "icontitle")) { + printf("\x1b]0;%s\a", getvalue(&argi, argc, argv)); + } + else if(streq(arg, "icon")) { + printf("\x1b]1;%s\a", getvalue(&argi, argc, argv)); + } + else if(streq(arg, "title")) { + printf("\x1b]2;%s\a", getvalue(&argi, argc, argv)); + } + else { + fprintf(stderr, "Unrecognised command %s\n", arg); + exit(1); + } + } + + return 0; +} diff --git a/libvterm-0.2/bin/vterm-dump b/libvterm-0.2/bin/vterm-dump new file mode 100755 index 0000000..67cf175 --- /dev/null +++ b/libvterm-0.2/bin/vterm-dump @@ -0,0 +1,210 @@ +#! /bin/bash + +# bin/vterm-dump - temporary wrapper script for .libs/vterm-dump +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# The bin/vterm-dump program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command="" + +# This environment variable determines our operation mode. +if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then + # install mode needs the following variables: + generated_by_libtool_version='2.4.6' + notinst_deplibs=' /home/rt044200/Downloads/libvterm-0.2/libvterm.la' +else + # When we are sourced in execute mode, $file and $ECHO are already set. + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + file="$0" + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + ECHO="printf %s\\n" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ that is used only on +# windows platforms, and (c) all begin with the string --lt- +# (application programs are unlikely to have options that match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's /usr/bin/libtool value, followed by no. +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=$0 + shift + for lt_opt + do + case "$lt_opt" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%/[^/]*$%%'` + test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. + lt_dump_F=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%^.*/%%'` + cat "$lt_dump_D/$lt_dump_F" + exit 0 + ;; + --lt-*) + $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n "$lt_option_debug"; then + echo "vterm-dump:bin/vterm-dump:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-15" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + $ECHO "vterm-dump:bin/vterm-dump:$LINENO: newargv[$lt_dump_args_N]: $lt_arg" + lt_dump_args_N=`expr $lt_dump_args_N + 1` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ + + if test -n "$lt_option_debug"; then + $ECHO "vterm-dump:bin/vterm-dump:$LINENO: newargv[0]: $progdir/$program" 1>&2 + func_lt_dump_args ${1+"$@"} 1>&2 + fi + exec "$progdir/$program" ${1+"$@"} + + $ECHO "$0: cannot exec $program $*" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from $@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case " $* " in + *\ --lt-*) + for lt_wr_arg + do + case $lt_wr_arg in + --lt-*) ;; + *) set x "$@" "$lt_wr_arg"; shift;; + esac + shift + done ;; + esac + func_exec_program_core ${1+"$@"} +} + + # Parse options + func_parse_lt_options "$0" ${1+"$@"} + + # Find the directory that this script lives in. + thisdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + test "x$thisdir" = "x$file" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'` + while test -n "$file"; do + destdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + + # If there was a directory component, then change thisdir. + if test "x$destdir" != "x$file"; then + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; + *) thisdir="$thisdir/$destdir" ;; + esac + fi + + file=`$ECHO "$file" | /bin/sed 's%^.*/%%'` + file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no + if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then + # special case for '.' + if test "$thisdir" = "."; then + thisdir=`pwd` + fi + # remove .libs from thisdir + case "$thisdir" in + *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /bin/sed 's%[\\/][^\\/]*$%%'` ;; + .libs ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=`cd "$thisdir" && pwd` + test -n "$absdir" && thisdir="$absdir" + + program='vterm-dump' + progdir="$thisdir/.libs" + + + if test -f "$progdir/$program"; then + # Add our own library path to LD_LIBRARY_PATH + LD_LIBRARY_PATH="/home/rt044200/Downloads/libvterm-0.2/.libs:$LD_LIBRARY_PATH" + + # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH + # The second colon is a workaround for a bug in BeOS R4 sed + LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /bin/sed 's/::*$//'` + + export LD_LIBRARY_PATH + + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + # Run the actual program with our arguments. + func_exec_program ${1+"$@"} + fi + else + # The program doesn't exist. + $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2 + $ECHO "This script is just a wrapper for $program." 1>&2 + $ECHO "See the libtool documentation for more information." 1>&2 + exit 1 + fi +fi diff --git a/libvterm-0.2/bin/vterm-dump.c b/libvterm-0.2/bin/vterm-dump.c new file mode 100644 index 0000000..997d3e3 --- /dev/null +++ b/libvterm-0.2/bin/vterm-dump.c @@ -0,0 +1,242 @@ +// Require getopt(3) +#define _XOPEN_SOURCE + +#include +#include +#define streq(a,b) (strcmp(a,b)==0) + +#include +#include +#include +#include +#include + +#include "vterm.h" + +static const char *special_begin = "{"; +static const char *special_end = "}"; + +static int parser_text(const char bytes[], size_t len, void *user) +{ + unsigned char *b = (unsigned char *)bytes; + + int i; + for(i = 0; i < len; /* none */) { + if(b[i] < 0x20) // C0 + break; + else if(b[i] < 0x80) // ASCII + i++; + else if(b[i] < 0xa0) // C1 + break; + else if(b[i] < 0xc0) // UTF-8 continuation + break; + else if(b[i] < 0xe0) { // UTF-8 2-byte + // 2-byte UTF-8 + if(len < i+2) break; + i += 2; + } + else if(b[i] < 0xf0) { // UTF-8 3-byte + if(len < i+3) break; + i += 3; + } + else if(b[i] < 0xf8) { // UTF-8 4-byte + if(len < i+4) break; + i += 4; + } + else // otherwise invalid + break; + } + + printf("%.*s", i, b); + return i; +} + +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ +static const char *name_c0[] = { + "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "LS0", "LS1", + "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US", +}; +static const char *name_c1[] = { + NULL, NULL, "BPH", "NBH", NULL, "NEL", "SSA", "ESA", "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3", + "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA", "SOS", NULL, "SCI", "CSI", "ST", "OSC", "PM", "APC", +}; + +static int parser_control(unsigned char control, void *user) +{ + if(control < 0x20) + printf("%s%s%s", special_begin, name_c0[control], special_end); + else if(control >= 0x80 && control < 0xa0 && name_c1[control - 0x80]) + printf("%s%s%s", special_begin, name_c1[control - 0x80], special_end); + else + printf("%sCONTROL 0x%02x%s", special_begin, control, special_end); + + if(control == 0x0a) + printf("\n"); + return 1; +} + +static int parser_escape(const char bytes[], size_t len, void *user) +{ + if(bytes[0] >= 0x20 && bytes[0] < 0x30) { + if(len < 2) + return -1; + len = 2; + } + else { + len = 1; + } + + printf("%sESC %.*s%s", special_begin, (int)len, bytes, special_end); + + return len; +} + +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ +static const char *name_csi_plain[] = { + "ICH", "CUU", "CUD", "CUF", "CUB", "CNL", "CPL", "CHA", "CUP", "CHT", "ED", "EL", "IL", "DL", "EF", "EA", + "DCH", "SSE", "CPR", "SU", "SD", "NP", "PP", "CTC", "ECH", "CVT", "CBT", "SRS", "PTX", "SDS", "SIMD",NULL, + "HPA", "HPR", "REP", "DA", "VPA", "VPR", "HVP", "TBC", "SM", "MC", "HPB", "VPB", "RM", "SGR", "DSR", "DAQ", +}; + +/*0 4 8 B */ +static const int newline_csi_plain[] = { + 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, +}; + +static int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user) +{ + const char *name = NULL; + if(!leader && !intermed && command < 0x70) + name = name_csi_plain[command - 0x40]; + else if(leader && streq(leader, "?") && !intermed) { + /* DEC */ + switch(command) { + case 'h': name = "DECSM"; break; + case 'l': name = "DECRM"; break; + } + if(name) + leader = NULL; + } + + if(!leader && !intermed && command < 0x70 && newline_csi_plain[command - 0x40]) + printf("\n"); + + if(name) + printf("%s%s", special_begin, name); + else + printf("%sCSI", special_begin); + + if(leader && leader[0]) + printf(" %s", leader); + + for(int 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(int command, VTermStringFragment frag, void *user) +{ + if(frag.initial) { + if(command == -1) + printf("%sOSC ", special_begin); + else + printf("%sOSC %d;", special_begin, command); + } + + printf("%.*s", (int)frag.len, frag.str); + + if(frag.final) + printf("%s", special_end); + + return 1; +} + +static int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user) +{ + if(frag.initial) + printf("%sDCS %.*s", special_begin, (int)commandlen, command); + + printf("%.*s", (int)frag.len, frag.str); + + if(frag.final) + printf("%s", special_end); + + return 1; +} + +static VTermParserCallbacks parser_cbs = { + .text = &parser_text, + .control = &parser_control, + .escape = &parser_escape, + .csi = &parser_csi, + .osc = &parser_osc, + .dcs = &parser_dcs, +}; + +int main(int argc, char *argv[]) +{ + int use_colour = isatty(1); + + int opt; + while((opt = getopt(argc, argv, "c")) != -1) { + switch(opt) { + case 'c': use_colour = 1; break; + } + } + + const char *file = argv[optind++]; + + int fd; + 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 */ + VTerm *vt = vterm_new(25, 80); + vterm_set_utf8(vt, 1); + vterm_parser_set_callbacks(vt, &parser_cbs, NULL); + + int len; + char buffer[1024]; + while((len = read(fd, buffer, sizeof(buffer))) > 0) { + vterm_input_write(vt, buffer, len); + } + + printf("\n"); + + close(fd); + vterm_free(vt); + + return 0; +} diff --git a/libvterm-0.2/doc/URLs b/libvterm-0.2/doc/URLs new file mode 100644 index 0000000..8380f5c --- /dev/null +++ b/libvterm-0.2/doc/URLs @@ -0,0 +1,14 @@ +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/ + +Summary of ANSI standards for ASCII terminals + http://www.inwap.com/pdp10/ansicode.txt diff --git a/libvterm-0.2/doc/seqs.txt b/libvterm-0.2/doc/seqs.txt new file mode 100644 index 0000000..7f21367 --- /dev/null +++ b/libvterm-0.2/doc/seqs.txt @@ -0,0 +1,278 @@ +Sequences documented in parens are implicit ones from parser.c, which move +between states. + +1 = VT100 +2 = VT220 +3 = VT320 +x = xterm + + C0 controls + +123 0x00 = NUL +123x 0x07 = BEL +123x 0x08 = BS +123x 0x09 = HT +123x 0x0A = LF +123x 0x0B = VT +123x 0x0C = FF +123x 0x0D = CR +123x 0x0E = LS1 +123x 0x0F = LS0 + (0x18 = CAN) + (0x1A = SUB) + (0x1B = ESC) + +123 0x7f = DEL (ignored) + + C1 controls + +123x 0x84 = IND +123x 0x85 = NEL +123x 0x88 = HTS +123x 0x8D = RI + 23x 0x8E = SS2 + 23x 0x8F = SS3 + (0x90 = DCS) + (0x98 = SOS) + (0x9B = CSI) + (0x9C = ST) + (0x9D = OSC) + (0x9E = PM) + (0x9F = APC) + + Escape sequences + - excluding sequences that are C1 aliases + +123x ESC ( = SCS, select character set G0 +123x ESC ) = SCS, select character set G1 + 23x ESC * = SCS, select character set G2 + 23x ESC + = SCS, select character set G3 +123x ESC 7 = DECSC - save cursor +123x ESC 8 = DECRC - restore cursor +123x ESC # 3 = DECDHL, double-height line (top half) +123x ESC # 4 = DECDHL, double-height line (bottom half) +123x ESC # 5 = DECSWL, single-width single-height line +123x ESC # 6 = DECDWL, double-width single-height line +123x ESC # 8 = DECALN +123 ESC < = Ignored (used by VT100 to exit VT52 mode) +123x ESC = = DECKPAM, keypad application mode +123x ESC > = DECKPNM, keypad numeric mode + 23x ESC Sp F = S7C1T + 23x ESC Sp G = S8C1T + (ESC P = DCS) + (ESC X = SOS) + (ESC [ = CSI) + (ESC \ = ST) + (ESC ] = OSC) + (ESC ^ = PM) + (ESC _ = APC) +123x ESC c = RIS, reset initial state + 3x ESC n = LS2 + 3x ESC o = LS3 + 3x ESC | = LS3R + 3x ESC } = LS2R + 3x ESC ~ = LS1R + + DCSes + + 3x DCS $ q ST = DECRQSS + 3x m = Request SGR + x Sp q = Request DECSCUSR + 3x " q = Request DECSCA + 3x r = Request DECSTBM + x s = Request DECSLRM + + CSIs + 23x CSI @ = ICH +123x CSI A = CUU +123x CSI B = CUD +123x CSI C = CUF +123x CSI D = CUB + x CSI E = CNL + x CSI F = CPL + x CSI G = CHA +123x CSI H = CUP + x CSI I = CHT +123x CSI J = ED + 23x CSI ? J = DECSED, selective erase in display +123x CSI K = EL + 23x CSI ? K = DECSEL, selective erase in line + 23x CSI L = IL + 23x CSI M = DL + 23x CSI P = DCH + x CSI S = SU + x CSI T = SD + 23x CSI X = ECH + x CSI Z = CBT + x CSI ` = HPA + x CSI a = HPR + x CSI b = REP +123x CSI c = DA, device attributes +123 0 = DA + 23x CSI > c = DECSDA + 23 0 = SDA + x CSI d = VPA + x CSI e = VPR +123x CSI f = HVP +123x CSI g = TBC +123x CSI h = SM, Set mode +123x CSI ? h = DECSM, DEC set mode + CSI j = HPB + CSI k = VPB +123x CSI l = RM, Reset mode +123x CSI ? l = DECRM, DEC reset mode +123x CSI m = SGR, Set Graphic Rendition +123x CSI n = DSR, Device Status Report + 23x 5 = operating status + 23x 6 = CPR = cursor position + 23x CSI ? n = DECDSR; behaves as DSR but uses CSI ? instead of CSI to respond + 23x CSI ! p = DECSTR, soft terminal reset + 3x CSI ? $ p = DECRQM, request private mode + x 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 + 23x CSI " q = DECSCA, select character attributes +123x CSI r = DECSTBM + x CSI s = DECSLRM + x CSI ' } = DECIC + x CSI ' ~ = DECDC + + OSCs + + x OSC 0; = Set icon name and title + x OSC 1; = Set icon name + x OSC 2; = Set title + x OSC 52; = Selection management + + Standard modes + + 23x SM 4 = IRM +123x SM 20 = NLM, linefeed/newline + + DEC modes + +123x DECSM 1 = DECCKM, cursor keys +123x DECSM 5 = DECSCNM, screen +123x DECSM 6 = DECOM, origin +123x DECSM 7 = DECAWM, autowrap + x DECSM 12 = Cursor blink + 23x DECSM 25 = DECTCEM, text cursor enable + x DECSM 69 = DECVSSM, vertical screen split + x DECSM 1000 = Mouse click/release tracking + x DECSM 1002 = Mouse click/release/drag tracking + x DECSM 1003 = Mouse all movements tracking + x DECSM 1004 = Focus in/out reporting + x DECSM 1005 = Mouse protocol extended (UTF-8) - not recommended + x DECSM 1006 = Mouse protocol SGR + x DECSM 1015 = Mouse protocol rxvt + x DECSM 1047 = Altscreen + x DECSM 1048 = Save cursor + x DECSM 1049 = 1047 + 1048 + x DECSM 2004 = Bracketed paste + + Graphic Renditions + +123x SGR 0 = Reset +123x SGR 1 = Bold on + x SGR 3 = Italic on +123x SGR 4 = Underline single + SGR 4:x = Underline style +123x SGR 5 = Blink on +123x SGR 7 = Reverse on + x SGR 8 = Conceal on + x SGR 9 = Strikethrough on + SGR 10-19 = Select font + x SGR 21 = Underline double + 23x SGR 22 = Bold off + x SGR 23 = Italic off + 23x SGR 24 = Underline off + 23x SGR 25 = Blink off + 23x SGR 27 = Reverse off + x SGR 28 = Conceal off + x SGR 29 = Strikethrough off + x SGR 30-37 = Foreground ANSI + x SGR 38 = Foreground alternative palette + x SGR 39 = Foreground default + x SGR 40-47 = Background ANSI + x SGR 48 = Background alternative palette + x SGR 49 = Background default + x SGR 90-97 = Foreground ANSI high-intensity + x 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. + +123x 0x05 = ENQ + 3 0x11 = DC1 (XON) + 3 0x13 = DC3 (XOFF) + x ESC % @ = Select default character set + x ESC % G = Select UTF-8 character set + x ESC 6 = DECBI, Back Index +12 ESC Z = DECID, identify terminal + x DCS + Q = XTGETXRES, Request resource values + DCS $ q = [DECRQSS] + 3x " p = Request DECSCL + x t = Request DECSLPP + x $ | = Request DECSCPP + x * | = Request DECSLNS + 3 $ } = Request DECSASD + 3 $ ~ = Request DECSSDT + x DCS + p = XTSETTCAP, set termcap/terminfo data + x DCS + q = XTGETTCAP, request termcap/terminfo + 23 DCS { = DECDLD, down-line-loadable character set + 23x DCS | = DECUDK, user-defined key + x CSI Sp @ = Shift left columns + x CSI Sp A = Shift right columns + x CSI # P = XTPUSHCOLORS, push current dynamic colours to stack + x CSI # Q = XTPOPCOLORS, pop dynamic colours from stack + x CSI # R = XTREPORTCOLORS, report current entry on palette stack + x CSI ? S = XTSMGRAPHICS, set/request graphics attribute + x CSI > T = XTRMTITLE, reset title mode features + 23x CSI i = DEC printer control + x CSI > m = XTMODKEYS, set key modifier options + x CSI > n = (XTMODKEYS), reset key modifier options + x CSI $ p = DECRQM, request ANSI mode + 23x CSI " p = DECSCL, set compatibility level + x CSI > p = XTSMPOINTER, set resource value pointer mode +1 x CSI q = DECLL, load LEDs + x CSI ? r = XTRESTORE, restore DEC private mode values + x CSI $ r = DECCARA, change attributes in rectangular area + x CSI > s = XTSHIFTESCAPE, set/reset shift-escape options + x CSI ? s = XTSAVE, save DEC private mode values + x CSI t = XTWINOPS, window operations + x CSI > t = XTSMTITLE, set title mode features + x CSI $ t = DECRARA, reset attributes in rectangular area + 3 CSI $ u = DECRQTSR, request terminal state report + 3 1 = terminal state report + 3 CSI & u = DECRQUPSS, request user-preferred supplemental set + x CSI $ v = DECCRA, copy rectangular area + 3x CSI $ w = DECRQPSR, request presentation state report + 3x 1 = cursor information report + 3x 2 = tab stop report + x CSI ' w = DECEFR, enable filter rectangle +1 x CSI x = DECREQTPARM, request terminal parameters + x CSI * x = DECSACE, select attribute change extent + x CSI $ x = DECFRA, fill rectangular area +123 CSI y = DECTST, invoke confidence test + x CSI $ z = DECERA, erase rectangular area + x CSI # { = XTPUSHSGR, push video attributes onto stack + x CSI $ { = DECSERA, selective erase in rectangular area + x CSI # | = XTREPORTSGR, report selected graphic rendition + x CSI $ | = DECSCPP, select columns per page + x CSI # } = XTPOPSGR, pop video attributes from stack + 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 diff --git a/libvterm-0.2/find-wide-chars.pl b/libvterm-0.2/find-wide-chars.pl new file mode 100644 index 0000000..f7f2205 --- /dev/null +++ b/libvterm-0.2/find-wide-chars.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +STDOUT->autoflush(1); + +sub iswide +{ + my ( $cp ) = @_; + return chr($cp) =~ m/\p{East_Asian_Width=Wide}|\p{East_Asian_Width=Fullwidth}/; +} + +my ( $start, $end ); +foreach my $cp ( 0 .. 0x1FFFF ) { + iswide($cp) or next; + + if( defined $end and $end == $cp-1 ) { + # extend the range + $end = $cp; + next; + } + + # start a new range + printf " { %#04x, %#04x },\n", $start, $end if defined $start; + + $start = $end = $cp; +} + +printf " { %#04x, %#04x },\n", $start, $end if defined $start; diff --git a/libvterm-0.2/include/vterm.h b/libvterm-0.2/include/vterm.h new file mode 100644 index 0000000..327f62c --- /dev/null +++ b/libvterm-0.2/include/vterm.h @@ -0,0 +1,582 @@ +#ifndef __VTERM_H__ +#define __VTERM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "vterm_keycodes.h" + +#define VTERM_VERSION_MAJOR 0 +#define VTERM_VERSION_MINOR 2 + +#define VTERM_CHECK_VERSION \ + vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR) + +typedef struct VTerm VTerm; +typedef struct VTermState VTermState; +typedef struct VTermScreen VTermScreen; + +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 */ +static inline int vterm_pos_cmp(VTermPos a, VTermPos b) +{ + return (a.row == b.row) ? a.col - b.col : a.row - b.row; +} + +typedef struct { + int start_row; + int end_row; + int start_col; + int end_col; +} VTermRect; + +/* true if the rect contains the point */ +static 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; +} + +/* move a rect */ +static 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; +} + +/** + * Bit-field describing the content of the tagged union `VTermColor`. + */ +typedef enum { + /** + * If the lower bit of `type` is not set, the colour is 24-bit RGB. + */ + VTERM_COLOR_RGB = 0x00, + + /** + * The colour is an index into a palette of 256 colours. + */ + VTERM_COLOR_INDEXED = 0x01, + + /** + * Mask that can be used to extract the RGB/Indexed bit. + */ + VTERM_COLOR_TYPE_MASK = 0x01, + + /** + * If set, indicates that this colour should be the default foreground + * color, i.e. there was no SGR request for another colour. When + * rendering this colour it is possible to ignore "idx" and just use a + * colour that is not in the palette. + */ + VTERM_COLOR_DEFAULT_FG = 0x02, + + /** + * If set, indicates that this colour should be the default background + * color, i.e. there was no SGR request for another colour. A common + * option when rendering this colour is to not render a background at + * all, for example by rendering the window transparently at this spot. + */ + VTERM_COLOR_DEFAULT_BG = 0x04, + + /** + * Mask that can be used to extract the default foreground/background bit. + */ + VTERM_COLOR_DEFAULT_MASK = 0x06 +} VTermColorType; + +/** + * Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the + * given VTermColor instance is an indexed colour. + */ +#define VTERM_COLOR_IS_INDEXED(col) \ + (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED) + +/** + * Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that + * the given VTermColor instance is an rgb colour. + */ +#define VTERM_COLOR_IS_RGB(col) \ + (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB) + +/** + * Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating + * that the given VTermColor instance corresponds to the default foreground + * color. + */ +#define VTERM_COLOR_IS_DEFAULT_FG(col) \ + (!!((col)->type & VTERM_COLOR_DEFAULT_FG)) + +/** + * Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating + * that the given VTermColor instance corresponds to the default background + * color. + */ +#define VTERM_COLOR_IS_DEFAULT_BG(col) \ + (!!((col)->type & VTERM_COLOR_DEFAULT_BG)) + +/** + * Tagged union storing either an RGB color or an index into a colour palette. + * In order to convert indexed colours to RGB, you may use the + * vterm_state_convert_color_to_rgb() or vterm_screen_convert_color_to_rgb() + * functions which lookup the RGB colour from the palette maintained by a + * VTermState or VTermScreen instance. + */ +typedef union { + /** + * Tag indicating which union member is actually valid. This variable + * coincides with the `type` member of the `rgb` and the `indexed` struct + * in memory. Please use the `VTERM_COLOR_IS_*` test macros to check whether + * a particular type flag is set. + */ + uint8_t type; + + /** + * Valid if `VTERM_COLOR_IS_RGB(type)` is true. Holds the RGB colour values. + */ + struct { + /** + * Same as the top-level `type` member stored in VTermColor. + */ + uint8_t type; + + /** + * The actual 8-bit red, green, blue colour values. + */ + uint8_t red, green, blue; + } rgb; + + /** + * If `VTERM_COLOR_IS_INDEXED(type)` is true, this member holds the index into + * the colour palette. + */ + struct { + /** + * Same as the top-level `type` member stored in VTermColor. + */ + uint8_t type; + + /** + * Index into the colour map. + */ + uint8_t idx; + } indexed; +} VTermColor; + +/** + * Constructs a new VTermColor instance representing the given RGB values. + */ +static inline void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green, + uint8_t blue) +{ + col->type = VTERM_COLOR_RGB; + col->rgb.red = red; + col->rgb.green = green; + col->rgb.blue = blue; +} + +/** + * Construct a new VTermColor instance representing an indexed color with the + * given index. + */ +static inline void vterm_color_indexed(VTermColor *col, uint8_t idx) +{ + col->type = VTERM_COLOR_INDEXED; + col->indexed.idx = idx; +} + +/** + * Compares two colours. Returns true if the colors are equal, false otherwise. + */ +int vterm_color_is_equal(const VTermColor *a, const VTermColor *b); + +typedef enum { + /* VTERM_VALUETYPE_NONE = 0 */ + VTERM_VALUETYPE_BOOL = 1, + VTERM_VALUETYPE_INT, + VTERM_VALUETYPE_STRING, + VTERM_VALUETYPE_COLOR, + + VTERM_N_VALUETYPES +} VTermValueType; + +typedef struct { + const char *str; + size_t len : 30; + bool initial : 1; + bool final : 1; +} VTermStringFragment; + +typedef union { + int boolean; + int number; + VTermStringFragment 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_CONCEAL, // bool: 8, 28 + 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 + + VTERM_N_ATTRS +} 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 + + VTERM_N_PROPS +} VTermProp; + +enum { + VTERM_PROP_CURSORSHAPE_BLOCK = 1, + VTERM_PROP_CURSORSHAPE_UNDERLINE, + VTERM_PROP_CURSORSHAPE_BAR_LEFT, + + VTERM_N_PROP_CURSORSHAPES +}; + +enum { + VTERM_PROP_MOUSE_NONE = 0, + VTERM_PROP_MOUSE_CLICK, + VTERM_PROP_MOUSE_DRAG, + VTERM_PROP_MOUSE_MOVE, + + VTERM_N_PROP_MOUSES +}; + +typedef enum { + VTERM_SELECTION_CLIPBOARD = (1<<0), + VTERM_SELECTION_PRIMARY = (1<<1), + VTERM_SELECTION_SECONDARY = (1<<2), + VTERM_SELECTION_SELECT = (1<<3), + VTERM_SELECTION_CUT0 = (1<<4), /* also CUT1 .. CUT7 by bitshifting */ +} VTermSelectionMask; + +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) */ + unsigned int continuation:1; /* Line is a flow continuation of the previous */ +} VTermLineInfo; + +/* Copies of VTermState fields that the 'resize' callback might have reason to + * edit. 'resize' callback gets total control of these fields and may + * free-and-reallocate them if required. They will be copied back from the + * struct after the callback has returned. + */ +typedef struct { + VTermPos pos; /* current cursor position */ +} VTermStateFields; + +typedef struct { + /* libvterm relies on this 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; + +void vterm_check_version(int major, int minor); + +VTerm *vterm_new(int rows, int cols); +VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata); +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); + +/* Setting output callback will override the buffer logic */ +typedef void VTermOutputCallback(const char *s, size_t len, void *user); +void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user); + +/* These buffer functions only work if output callback is NOT set + * These are deprecated and will be removed in a later version */ +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); + +/* This too */ +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 (1U<<31) +#define CSI_ARG_MASK (~(1U<<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)(int command, VTermStringFragment frag, void *user); + int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user); + int (*apc)(VTermStringFragment frag, void *user); + int (*pm)(VTermStringFragment frag, void *user); + int (*sos)(VTermStringFragment frag, 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, VTermStateFields *fields, void *user); + int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user); +} VTermStateCallbacks; + +typedef struct { + int (*control)(unsigned char control, void *user); + int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user); + int (*osc)(int command, VTermStringFragment frag, void *user); + int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user); + int (*apc)(VTermStringFragment frag, void *user); + int (*pm)(VTermStringFragment frag, void *user); + int (*sos)(VTermStringFragment frag, void *user); +} VTermStateFallbacks; + +typedef struct { + int (*set)(VTermSelectionMask mask, VTermStringFragment frag, void *user); + int (*query)(VTermSelectionMask mask, void *user); +} VTermSelectionCallbacks; + +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); + +void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *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); +void vterm_state_focus_in(VTermState *state); +void vterm_state_focus_out(VTermState *state); +const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row); + +/** + * Makes sure that the given color `col` is indeed an RGB colour. After this + * function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other + * flags stored in `col->type` will have been reset. + * + * @param state is the VTermState instance from which the colour palette should + * be extracted. + * @param col is a pointer at the VTermColor instance that should be converted + * to an RGB colour. + */ +void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col); + +void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user, + char *buffer, size_t buflen); + +void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag); + +// ------------ +// 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 conceal : 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; + +enum { + VTERM_UNDERLINE_OFF, + VTERM_UNDERLINE_SINGLE, + VTERM_UNDERLINE_DOUBLE, + VTERM_UNDERLINE_CURLY, +}; + +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; + +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); + +void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user); +void *vterm_screen_get_cbdata(VTermScreen *screen); + +void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *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 */ + + VTERM_N_DAMAGES +} 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, + VTERM_ATTR_CONCEAL_MASK = 1 << 9, + + VTERM_ALL_ATTRS_MASK = (1 << 10) - 1 +} 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); + +/** + * Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state` + * instance. + */ +void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col); + +// --------- +// 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 diff --git a/libvterm-0.2/include/vterm_keycodes.h b/libvterm-0.2/include/vterm_keycodes.h new file mode 100644 index 0000000..661759f --- /dev/null +++ b/libvterm-0.2/include/vterm_keycodes.h @@ -0,0 +1,61 @@ +#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, + + VTERM_ALL_MODS_MASK = 0x07 +} 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 + VTERM_N_KEYS = VTERM_KEY_MAX +} VTermKey; + +#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0+(n)) + +#endif diff --git a/libvterm-0.2/libvterm.la b/libvterm-0.2/libvterm.la new file mode 100644 index 0000000..1eff93d --- /dev/null +++ b/libvterm-0.2/libvterm.la @@ -0,0 +1,41 @@ +# libvterm.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libvterm.so.0' + +# Names of this library. +library_names='libvterm.so.0.0.0 libvterm.so.0 libvterm.so' + +# The name of the static archive. +old_library='libvterm.a' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs='' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libvterm. +current=0 +age=0 +revision=0 + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/usr/local/lib' diff --git a/libvterm-0.2/src/.libs/encoding.o b/libvterm-0.2/src/.libs/encoding.o new file mode 100644 index 0000000000000000000000000000000000000000..3b00ed210444fd76a942333f654158c9618d49e4 GIT binary patch literal 6056 zcmeHLYiv|S6rO!R3lz2>7zik<6s!uHmLLIxu-IO?CZG+nkr119DGM}xVY}NFW7#Gx z(3?%esxje*M5BN7kI`rf;h_s8HZjCB{((P+#>AwX#h{khCN0+Q%$;-h_I7z#jR^@S z%*>hZyk^eKoVz8iZGXw_atS7v*etR$MujMTB+CaS8x#w~d=b)(Uv#5mNH=1GdO{o2 z4Q&+TXus5vT+mB=vhwmIT_bc&H+Bq^8`_oa#*P%*rr(D)CWU$e|Fo3u8u0ar9&Mzv zV`RDS7)gn1twVpLEkXej;%F+m3fw?K^y)4>5gIUN>nHWzg-0nc1lQoDU)-LMle=kM z^7UyWhMT;a8%W>s$bfNQ_hSf6;_j5sxPTO40pI$Le+7;g5qtLj7LooCPNh*W|Ki|G z;}Y>kMZSb8@+nBy;FejFV9{GKA=M}=a?8G>yDc}~uF;vk<6mR9M#|cE?+n_0!t177 zEFvChm>87y!The#c>FN&{lR?7Or~B5UYN)eA|AGjc3C1Q#$UFJHf2T2MZDB5D$0r` zJnoFnqs+9TP`u3j7Ye*Iq4!pxpr9HV^zJ0GQCr-)2oqeijoKC8iC4!aCd_+$&k6^7 za-)ix@0--hPAtjL#xqpNCQB2c@zQ}FZLG6nY&qq9EFRs1n>W^-YTs!_zhkYVDmEso zm|Sq~Eznysw-E2b8@%V*QQ>7}pS;TK9&V*Cn2DaOkOjFE>NP*=U6;HGZxTg)y{|7u=-uA5tde@o4|>-{SoIk0d!8_*XZT*} zSzx8dcjBoDoG_=z=(u70)}syeYvWk9$m~jJ<0!Bf2faFOo>g- z7j0IN5Cv#+(PpE4fo*(=Rs? z8?ECRe7Vt59wDlq6yg(@oJM=lh}Qj({^tyAD=T~6UtAv9TN`flmzD(921?hge_C<= zwIypyOG-;i#6q*v>-1(iQ=^kI5ncx)NP$tc+6ckcjf^oU`n!0goA$e zqGP$|CogG`{0$O<&Qr&7_i5>X7ocR$fn0gT z@AzFil{`8BA`|#+h6k2|Jn-bC0swBCADJ6qk1Cw!GAGK|BW5UW^57Bp3&BBn=J1}y z3zeL{B@}m_awT&LY>U!oJsvBR{`X2><`@`}N6a!ATVDy@Wy1^IrtnRL`_-KB4vFUp zzo{lD9+O85XjLq_ ze}mW;0T?lPR+ubl3AfbL)>_t8F$E~gXn1dZBr7e~%Jwyf+Yly;#p*1+MYKjE%?(xc zO-*&NLscA+mUoISAc)dMr#Oz^JVc+}0iUmqn)LZyffcJAxSv3f?!fVhkrZ*@eueWu z{;8Ey2jZK}xUO(o0})CC?$q(<7eR5oWU&YGKiTjlw65F>Lbl|+N zY6s5y>U7{7_ag_+??nV!4maRdV~jSN<3Z+{BlY1xG}0awfqmg(^5?6?g#B4_IN~Zd(g*W2 z(m9jQ>DsS{E#EP)7IA#~uG)#;Ib)?@a_#@5+W)6Yk@ioY4(I;MfKhzs`Ulni4?$)p zzkLFJGg_|r!TF}5JwCZ~#;13gD}J?7EOBhuZ@RVm*TCh9KcwQ9C{5ZweI=do>wr;w z)&naksS0E<18O8)A|9hQUJ6uqM%mjj;@OUI`p+H0?6rhsY=4F(HsbX&)D0%r{ueJW zHQJRT?Vsu-+w=X;6`M>BsiHcn7EITPz5z~q%Bl1201^i&An24iSiN)qbWFMIlWP6% KS(YZ_wEr6;wbM!f literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/.libs/keyboard.o b/libvterm-0.2/src/.libs/keyboard.o new file mode 100644 index 0000000000000000000000000000000000000000..c31f1ea29403ccae069e6812dfc73a920eca07db GIT binary patch literal 6040 zcmb_fZ){sv6~E6;o2;aDwsugdKutx~?oUjdjy2m@ahke$MJX+7qE%gx<2a6zHUDZq zC+#3jw%3vxD_|y}F>MkzjqD%uv0ny*ZhmaByz!ULjN63deCg4pX2yJ1lU;*$=s>?L9oh)q{FQ}Oj* z$qwuf{nW`FGo8wtnJ|?%?%2!A%js|_6E3C0OU2LILrVr!cK*xh>zVM98JnYO-ZbXy zn^TwT%xe>OHro?R#^fy;=w{#a>*So;nln2t5fbJMIu)5JkNE?0=3z-0^UJa~J3pDe z4riC^iZQ2;-S%9r%pG?)pFNi^bI&?lwLN#Y%S~UEyA&x{vC9fO<|E< zTiUXWIPWLfRkc?R>962mvX-E z=X|p{to$xA?H;4GU3htn!@ub&j7dogF02Ex&3`WCtOi0erjhAfz~EZabLl_uTEF`y zHK3u&hH|{cuxwUE&1lXGyJgKowFfv<#cCBZxSO~`zGww!zKE_@+*DXd;k$U2^6-_3 z1>_cVP0fVNVUst?)x?$qvTa0cfel zTPZ3H*g$rv);GN!4w<0A-Iw3uSJ>BBi)>2!$1XkSo4!dv5%=pW6aL69)%d1gqqgbX zy>gw>%1G^zdE@MWwNWXUauiMuSlx){3Aybyh-6_0wUJ&kQ~PLTe6v$mXzA>H-Nl^c zce;~NTNVGp2V|NpR%MF@Z3yac6UF{XbZ8pB*>K7C?)<%zsPKmDPg++_ATx7}cFJ$e z^zT>lrVv34vCkfN^|mt0@6rrbk2fI4Yh*WQv?j?5|0Xw%|GAa5+<0RYevQS$$bzb6 z=hA&F#dlVBHA>r%o4wbPzPmEBHG?d^AY-Hj&vP;7yXd1cxNwV3p7ksYScFJPr}5=* zsqhZLY;jArSj}NA@69@Sf#5VQck*LQGViAh`3(7{V_F``YZEg2e{w?I-CJ;TZDljc zY4HGKioBR~;a!Yzfwj2sAs0UE!mW(y!W0+ifp3M;C;XW7K!A@(v|@wfN!deRNpR7g zU?uR^T=;nxKFL`5^if^m2VM9f7vAo|_*78#h?~Nna^anXAvN?g(lg39Nab0^O;jcr z@24`!_(>`)#yB}rjGv-%jPU^~#~C+Md5-bZRGw%27?m$Eew@lPjGv%#kZ}W*LyQAd z4m17&l_QL|Pksx0>Or-9%bZjYHSKN27_38i2A%UUVdH} z=jx1IwbeCQO6*UQ4i6-XSIf4#Z+Tj`ZuWGMg`@itc#?nNRn{+-^n{9`?91lzmFG-wk7u3mX49hZf({_&v!)5P|%dBRPyBG3HjmI4edEe#7-S1)r z`zk)#pmz7WmwB}g%Ky_9>|fOEai2qmD%iiK*@vXH5LYVL|5mdPv;7AZ><9RKQRByV z3FI5h>+^j_v&XjxBv--S(K>m8K2Ey!A#~vI`~?*fd{03XUvjDf_Yp+#b?ir-&5Fk_ zG>G~hP<)7al}CzK_dti!tnB|8p3fdezxz`_z#)y*Z6vl6TaUd zYMe{Vk2-XjSNWmhrtBZ#@%uGGzF(u*$%&<5aLjoaX^WGfc=#7;z`*GNkG$YV1@w+hK6ZF8R-u{&O(FY@!=;OyZ22SfnsP?bA+5&Pqm)Rh5 zp)%uuw{duqOmT;pKT=zt|3Ttl(nyb6{>V0 a`5>*)CahKE9k*WlpYizn;LIg@`~L#%u2Z%E literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/.libs/mouse.o b/libvterm-0.2/src/.libs/mouse.o new file mode 100644 index 0000000000000000000000000000000000000000..ff819ba9f5af282ac11e5c2976e063a5cdbdf115 GIT binary patch literal 3824 zcmbtXZ)j6j6u&R8ZR)=^Q}+iH8ktVVW@u!H|8R-+*|*Wmq6YWDk)<(3S@UPfdzuYn zBG1k~9vS_%!S-RWFZ(dqw^8=Nwyvz1$jCMk2SoyA5*J~02xhEXcFukGKGSDfVLNHw zJ?D4+-E;1}@1{9v;E+QQ2q%H;CyykK5|Ui?i1$&}N4m%w5|s5{Wj$M$wQN0?b55Xd z?KSyAw%%|O0)PX7T@+|gKty1x3G|>v8v@UmKtlwe9f9_V4jA70mL|~dxgkka*446X zNHES;wML_%OLar4>(WAPvld$rP^Fb$)vp-Rf*dJ=SeAvNb~QidlBcuP4lTPNrW}N{lv#{IOhknnFUUL9=P)oPGs^jk#(OGe?hBoU*XPCf2PL z%NDlR#GG1j-omz;n5Y%&7PijB1g*Hhv2mEz_q=Q$lzmfIuj|2*9=O)0OT|7tFf$#! z<^ac=Mm`TS)#_sR#jl|8$XuGIjHCNvA7d`v0%mkdS*g_pMY&r7=|Z-63mnfoXC=y9 z$j)$?GFrAoR8U^WwKAlcLUNNK6$>&vudkA>6$agyT{ehczwg(J`j4z-mp=h}SeywM zNEIJ2Gg>pcF%uE^Sq ztM>Y$b}SR~iQ&e_t^LxA$3#~vda$eq=Ag`~Y~;7$8o*h>b{oFrZmF!gOkUY7Rk|;J z2{lm}oCcF7SJ4L*)8?F3cEI*oWlEXRDYeT|w1FbU6k1sTo!64UN!@5kXz84wXMde} z;#ZrmhIabO(4_}PaL$k(6p}qgwqgY6-tg;x((UyP6oC7xdS{{0s0l5~h0`%N2FJZs z!5IjyWh>XUUN05#{8J}OwtIyBKRSJEoriE%gMd;4`j~57VkfJaF2UiUv%hnIPUiL zdUklc+jqXmIQNd;9o}BAw-<7(XL*G8j}Rf_5;m-Mc8)_AHv*1dq%c1GU9Jxt2iJ5s zPJo4}+lM&NLws2JKP>$T#DRM8?%;ayB?lT{>~p9D`W+u!@A%E~{}gecXK_`zUR*0s z1FCuafu?QbK3q|cli2b`Bt(QyD-t1+omK$gXo^F70~lU4h;y0tggJ|ZUP??dhaP7C zCQcZL_Q+&mHqhV}BofU2IEe(VXdAr@vde~{cwh+}=e-u@KgL=?*95Q>!_Tr-(6s=J z#1d*c>WL6l8Qv92N8cTZ#>w#Mk&zGrB%vxvRS8ie66zl~e84{tI(+ERQR!IdnE$|l z6e4GoXet&;s_9c9W}Qx^PREtuP((?MFf|PTVa`Ms0Yj>yB;q)YtM~xIErwlyqetlm zfi%R}o${5CV>I|(uT(hD5M)lAQE7mLI949D@#LE`TTj{6wS`^RyQHbYcSP2u) zP&!RK6ip>eB$9~5qH!>YCzPnC|Bc}Gv=WZI!>ppGLc^(WEXs2KpTP~z*h2hq+)O?u zd}w=Ruy$Lf8-T+#_gRR^)Y*d1GaP^Jqc7hFe7#;^INN5-nvfrCdAY)#AKxz+^S}D< z1YeAa`=*&nEdTyy5O}`7IqYSaj{|J1vRC96Ovd^#q`iKOkNNp|1C~-3TaaxWU?Z=e zkNtpizS(rAeL-#h+!pUf^tac)i}_D*LpJjHbNDzgt=4~hrD>62hFCwgg!}XLZmAA literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/.libs/parser.o b/libvterm-0.2/src/.libs/parser.o new file mode 100644 index 0000000000000000000000000000000000000000..32817174e1dcd82a838cb55beddec54363a768c2 GIT binary patch literal 7112 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 Dsf&&> literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/.libs/screen.o b/libvterm-0.2/src/.libs/screen.o new file mode 100644 index 0000000000000000000000000000000000000000..60bc0cd8a7672fea990e0b94601fde2c1e150c4d GIT binary patch literal 20240 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 BQT6}; literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/.libs/state.o b/libvterm-0.2/src/.libs/state.o new file mode 100644 index 0000000000000000000000000000000000000000..25bdbabf420df74430458bcef1e810fd981afad1 GIT binary patch literal 48056 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

`0dEU2hbl2nrgZ5yX0 zgDE>Y9;)mpV^crM$YYT14h%tomAEnzQmd#uTqWkVy@jt057WjO#fKV8Y@QvC&x2#N zGI|w?8I_peMKHVuLuia@bQ16E#EX>}r)!rGTRv?!!vZmy` z5FtxcGu88reT43of+tR*W73Yb*r!SC8WEvUo(R2aBBaOLBgVw0+O}MW$e5Ronfm3T ze5%7!^-G32d;x4yb@+PUrW}AXt;0W5#pn2$)!|)8srZ-Rf-43USsq1VGju5a&6E{o zWNZMZWH266cAt@U7+4y+rj#A2RD(`qw@lj6V4HOYq@6yqqWrr$Wv9cEpK^B|+eQ41 z;be~QNt7++=1=fkmw52J6#to*k4o_um~-P28}NkfE5=Q8GfZjTJRz)dU5#o6I1phc zsSqS+gU7UCo)k~K&%@1;9g0fgI0^=rKzp=rg*Ddyamb`bmRSTxZDT!OcO+!|2 zj2b&8Mtuv#My)3g=i`>0c~8B?_8c}{GB(@qQKZGkp-p(fDB z4G_;Ay8|bY9W>6`Y6b5?u(x6akJgv^=B+%S$19J|eUnt!NJdQ=Zbizj^RX)siu<{G zM0flOjOd`Y*$S?OK(kg$bL!?1YC9pzGNd+nmG9GCEiZ>kRDLJ2_`wuU_7$FztExGqy7B_W4nC&0&Qb^=WMX9LYrKB z8J2^TvE!z9!`P%~(?nzJC?Z-vcQRy;zsndq&LR4cH3{^A3C7rQl9WAU6@mVkWRwFMp?AQa#aI%3`#Qazh`!ZgyBiV3rvTxI8@OB2x z%78zdywSJm!84McQ;($bwY-iMwfIc9KypS=?53d1Y_#Xl-{9qG49gg_$>Iv33(rBG z;4BL$76ZBmdVfKin7)>KwYokG6`a6!vV}>_0(R(^QoViD=MD|1hgSzN(g-d5z$vm^ z?Jbj0%`ITS!Gm3iTb0Lpft$QAa}z#Q2%WS5t)$ZypxHeZ>~XqgKh}>B#)_RJTITe) zYtL`|6y4?XC=0M~BmH-z3<8FOC{7pY*9!%Gygnaa_BLoQFF`!iW`fk7_jYKu7jA%AVJu%pPT~QoUGW z)2oxn)UpldNrpR00hm64pYdx$(qe$Bym*EmmN-xgBfXG=spxHyW%? zf~w7OXAs{*WW#|I*qGcgGjO8uhmi_w%WlW2%M-Nxkipll{Q#+RJ?y#z+ew`p8{?@4 z)aEMh2grL2aWZ>t&u;wYuxaUFrC`MxLSVW5#AC2u^8vR4c{ghVFJTOEIt|5%Sdj*j<~VJ=i=Lkfwd52(RF1%wj7 zZO_a?1ME!ulxd_gUZ+~NaS6Jb5#tI~O{s=|iR#)a9l*1V@6tXU-`~CSe>T3<+@{(w z-6T<5pG7T+H|fBgZCrz|=(vu~5ZCkuBU6t36Kr7z)3;2B%m$nh*$^?OiV#9`WRHqai7HBHlW*+d0hgXu@Ucd3R8bgb1_n2N zom}t$o~3$c24cRBP3P$(K(9$lhT(q90(>nGG3CQWSXzRe75|XNvh|mlO0u};qOY=C z&8je6$+~bB&rb>IbEn|SH=})0X6;#G^Z)5waL&1Blx3+BG-}-nI-p`MfMhv7<|*BJ zC*fn?!;t=FK3$WzFoMe;$Z>5~3A=D$koIIy|ymo%KsJeYXfHy~^ZknIiZI*DJ~O@Yqa#zopeX)cI>uYgB11-g9q<_N*&ll^M1B zTBZrkF;}^)x{SPIhk@MPT}VZ+ULIKpL0Z97iph(3k5ItN zezIn{g>GnWXe=L8L=UWs99Exu(4dVD-bsIb9m}8-ru>cPQZx6+$KvH0jPkXiGx$@7|`N84H(+%xj2@z+sed_%bee174wuTa-wnCwHyA!#CHpUEa{@#PioFb`!?0W%^6lmwS=)xZGs?3!)?AC9lQ=2C&831o2U|cBA0DaxRwGUOoUZx@ zNSZc@MCc1MO3wt1#*48R4;OC6WJz}ltL{c~gO?)pbA27ZAEini?x*8DCwOVRAAmf^ z=@L2w8m99jWJu6uCzsNh6Qa@hZY?jpro+Q7!y#;^E42}fHOtwmx0$MhMy@>41UhZy zQMsn^Acesx_tOvh)YjM|bgdqXwX-Q|ik8pTmN-VVf$Ag1D8=*xKeYumL57>yncwJC z%0krdLY~z_L+Csr-F_vfF2|=?3lG>`#>p1yZC~V`*|>;?OB!h*X6>4mn0R;=jlH7^ zU(4YThN+Ku?k^1lX)|{OqRI;?e@E# zcl%GDW&eieL+d>IEW1PNl37NvPqEW)#RXo}%O!tB9acd*!^MKsOw=uvv?xXu78UZX ztay1=JiZn~gBsEyNk!uC+heSKK}T%LKPF;gvBMLopFEF9ZRb`Ar=rx!@Ovno)C-@e zD>Hzv$@S4!GJN}*+A!*9R)Y_9qh%{@y_M083A*)$+&xEat;V;ZFsSvF_$OhCCQ74; z>CwcrXyV#vqBxqE5=~5sCN7UACPot%M-%UgCf*rM6h;&0MHAuaa4Cyemu^iJ9D_%Av0BXqIA zbX0C@!QB!p(ZHOq6R+6aN8^S9w?>S^(o`#{qIJr3AJtcT&)klPU6X3wPPt6sDlwbm zLJ7w+QgIUJ>z<()@DTY&ooN>ooFpyrx$&0O6@1jdI})XU*DE-a zVN82;Ep+3sh4$!ryx>p;c|^U7_Nc6at`dh&*f`nQsjegATEky#=O9M#+i_riCt~$< zxZh999RH?^Oy`R3=It=5^3(ZZ^B2h+e^^^?=Myr?24BlJ!Evm^*HH|+aPlEv%f}UY z$k#!~z{AP<;9~sob>7a zs&IsRFaV?X6;71Gr4H$(+2^L)e@42`PqR-nra3N1x2*J7s(X_r#seka#u_Pq24U=A zx+}UR!%0Q+6%+Io=-a6$H%U4HCx;?PcWg;f`eUz@j-<>TMSqk@{h}*jTKU z?o|5rsBCD7g$j)$97bt<5H}_~?=3IuF;OR4Aj-AbIU%}^O);1~R|iCAXKsRSp>u%g z&PZo>^A0$m$K&1X+|UAg#YICp4~n*5UnZmDy=mMo(63Qy+LZ#Gb-rg~d`~ETi*!T# zf4-K>rqb`6xNp??}Zw%oPq1NkuHa zRy$J3F~`^X6x4>BdU8V(aLaZ7fG#AxxH+nBJ%@3N6}EYyk1E@9RDQ!&z3BqiOZlkf zbK$5jH$hu5Z!vdM*{8m;r2`jymSY{?rE`P}q0>FE@2_<3t-HCYdpL;e;fMD3k5*sG z=%%uAC>-da^7j_QfOzJI0xD)WO$PTSp1N+Bc+x~S9LvR>a~iWrJc z59Q~zXkh8Q!KU9(gy{PWzLx*iaWlnBf4>ZGZYhFeDh%_T0|--}t{XA+v%rblkNpDf zWA<2l!sZ3oyM><5H)0b3mm1!!B~ihi*OJuU#RkUUXr>mpe1Y>@`&+Qzh=0TO6~Ff= zbPMqU=l?Enez(B+#R4Z`JFlvCRDafiJl1t8pNt%0D!!srdsE@zv`W0{O)S7Q^;p46 zan)#l|AcdK*<7`EPL%2--=zy5u<07=M?9BMn@)JY-qefM9G%$0euSeR0Z8J@ZKb}J z=a5Ls)IrGw6V7~ujj7|i+N5UCCvt*!#Pc7#3@hc8%k?ZZ6T8hA-BuS+}3 z>8ydT!cLFt4xK{7CRgg;GfOF5%Ur2uEtND45Jry}!r-d`Ds;Htp_jGZ`38HQpfM_> z^?=pyzVPRe)D;Y}#Xz-CTiQj~3R3#;2`Bzghq=+1b`RsR6N3~O=ec4kCX^~}N7`Db z(3T1G6KPcfSnN{wMfKY#>bC$eR&Bi$QmVrZPD2n-D`I&sQko;Z!(F*(s@rmz<2>>O zZH2hdPVa4n=72rE%0Qp88$xwj=cf--soUUv`ZGM}A^HUMFH9o_TTg`fTDsjz=k>oafe0k@+7N<3`43B}OLVV|u(!GCtXeoX z2jfYV{;H})%v8<1(R_~0#rV<;RZc8rg}cyAHuaa$%oN)x*l*)oD*K`^rIIB6xs>X( zX&o+Zu~u3)z&y;ndHkEo{`vNNQjIl^6}G%gN4q%?^AHHEE5ZZo%))ZMCoHLJzv?1w z3>OQ6SK`mc_t1eVTv5#po}0L&J1~IMvl8dSKuv3+0EW>pL^;5S(+<4q_6Q$n!pSze z`5QpWj7%2H;5{i5hP#v&T2Iy6_WEmKh#2{NMye9{?xspUy~^J=|8#zPR{-2gFMhha zhHC=-=m*gkF6>*N$As7}K$n0^J-H#e0yv<<)&5?D4$Xkx9KbOB-5L6oJad6jU6gS9 z6&e%k7w%~2yP*o;`*;(ynfeveiy}yorh)l-;{u;z^4^;F_AxY1n_)X|sIKKZhytSV z5p?O;G@(n#4&A&^m(Df5sW(9WoHs6D$GO-bFGQ2<6 z43*~00{8P%1@7mk4x9;0NOw9&-12grTxp4{sshr&(YESt;VxfG8M2A*1I7QQo1+2x z94bCK74AaEFt4`&Ig441`p)SqDtmUJ$2R-xN^P1)XjAz3M`Ai%q4vt+26itkC_y`a ze*yV}`_%{W)w|lu@u^(x^Dgt9 z;)SZ$P0YsCfL<7y?yvMnXFyfn91GuSM`h2>yVD`bbrf_9hYXQrwBb zC%ABsY$5?Zbi#J}bB74K@;9{t3Nf<+3Kb?HA>t~lN7jggmpxLu8B?%R>J{D@YmNcC$F!}zh7MgWLEttpxJgzD9(C;Qk_G&7-| zh%8YIoTzd@MndU*9#HB-#eR6wxlGM}=dC)3S_le@@W zk{SuPLnD!FJWOVrE5SAH3v8(n(E5tD)s>CRg(wH=8`P-ydT67C5GC_n#Ph|cuM@s~ zw;}H7-y0~hp2BuVB(x^Fuu>&;RbifKOQK4|FJZgZc&M?lPfbn+{ zNe+2}iDYUa6989Aj+(^<5^>L&PW(dZ^ELb}e%KGHDyu#lLY`o*$?lgzDsqM3iO9xC;+t4sRxPr|0|MKOHz#kmVPni#VR5e*MnEkjMoZrwM6Nq zwc)f$iPlW5NK|j`sQQaKYnI>PFO8_9v;@z>&=KvyD@T%Mj4P2$JDt!D)YK@ZtXmYa=vBz{I-P`y~_=ROVtZh29h9A}InzT1_gg>SmrClTV zjfmjAfcl9&AP{8`|2*$DjWE^dvHYMrEWec4W2L-|5Hx zPTsoNs@w&!nnHGMy6m7BAqPEcuIy6TQjty194cYhFj5L>tHjO8)-Pi2LUT3R`RY|$ zCHn69PA5tkuM1o=?PX6O9d2e(b{Z5kpfu6hRk88icwV%!QA5OFXpliQ`?P=WOx?iuX zRs0&+UXAZv{r~B@-^TWIfW>v6+vD2rWqbN2n`@s}kV68KfsV5M=V{r;=)bcg?}_}) zqr#)}HjjbeL|Elh`r%oK{d!j}&(0$~WIvhh>ENYn|ELOvvY&Sv`)anQQ=G2*M^f%v z*uFI_9?dEqFHx{kbcY2ju=Z+tXo+l>06>e)+`l{5Q$D z`d@U*5uM+oCp-y%T>A}^jQ{i4{yq{i_PO`C{#Ud8sx^0=Shp0_za zJSy*{QLdg{EcZ}aI@P-AbcpRg@70s{zmx(>hZAi7LRvUtN`Dd9I7(kU8|jaUM1xf0FGtdF^u_clA%8G{l3?Jf!q5Ou3)I_6xlBxp%nk7oW!cdbSUz zmAMUWncK?tWnTS_DgQf9W8cU2S9skoNx4rN|GoCP8(Qh7CNRn;;^tlTNnn>Vr#_7y)feG+ z3jUEiUDu&U^+EWhBNN02*=yCefX!xJO>HS2)u-S$fPW-kqZXDeOZ6pS_cJe~0OL`8 z2!5#zAo*p?Ro?;jG3I-jt3CtlUzxwbT=f-TJD7hRH;m~~eFT1Y;vebXa4u2HQhft{ z=^Q)p$T*^wrTPT?K8JtAui=4!>H|msPoBkvM8B%PJ@`xV>n_r8F5CT>`5wXJ%>Tq( z^#Nc*%x|0k10L1(<97`5iS&0~tgxlpd>H9m5b>?5LR*$<>%lK!o<|vtN44?zy^8rk zYP0dEwjIASm~Wv1fJe3I_?^pqf~~N1pZ>Al&%BQLD&`+#-p5?E-LR`>{2&K>Cg3}m+c+O(2K()85 zE6?J>%9xWRd@47CstHaHeoxoP|%y!s!Y`COwO@@TK4s-%oS+ zYjiZMhAi?=WZ_?8J?oZg1>8(n&oSS^d=u+`h51BIXVuTc?zhaRF<1RN_=PAp6wXJ5 z{7mM)YOT18_20?7T<~hECyeTk$*v)|ELbzG%Bl|qAE)=*8A2uBlmPHB_ywtF5(aSFNd8QBzZGJ+QWJ zU1e3>>g52H%R?(x)U34{YJxzumBD4p8-jK9R#k1yvbAYsRl|r^?jwKWf{ zs#{}0g;GcUty;69&Z=8e8LU|!q~EH#H9-W1>?x4-YwPMOm)8lfYE3Xrpdq++)tU#a zhGh?%ctTT6L)EhS8s*XIx@v8xuBob2FKcTa3e_|OD_uZdH>|RjBYAErt*l#9V};h} z7c$p1R8=3Qw0K+)m057v#h>~e(G1#--bHH*48wvdbGx> zU$(ZP2C28Kws!flss|eoN>*N3wGwvIO03E|7Tk4P*@DWu-uJ$H1B)sbmEE==P{}z5 zXLVJ}f^};vS1Y5;(E6G+jB4xZ9t_o2)~u%xiTstK}G zeW+n&WnCzUf?e5AkF*Z1sH_UEtxZ=_6O=k0(5PXJc9TKd@<)O-4H>xc1v!gqwRTO_ z>Ux%{TOM3SrJCxRaj3IYRTDQ`y~(ZOk|A)Fx@y*=fOy5TD?)@StCpiuFB{x3D>4RyT&IhTRJEe63I%$VDMUIiG(-lZ`AX{CdMv~5Db*Rn(onOe+EcHn z`C5v8h#oX=7Tp}h|EEqp=c4_khjG2#4N0bcMB_omPHJp3lu z8To{Vd*j=U@21g1dTz!)BTt`_qet@Fc@QA^Ip?S3XTWImH1Yg{iUk=Bce3!O1)r+b zTF++TyD!oL6rPzL{lCk?uj6@%lz+p+Z}I5)49{0c-pgNSZsJuAdwTLN(Z7;^Mesv8 zTJD=!_|p@$J^AY`w=Z0l;u=7&zaaQ7a){@vS@?`AwY||_#Pw=k z7G5KGKg&Oqg_li9*FT=+?`2NuFdP3&e4Db!cVv<8xGG(LEh)tF*)04y!OfKUr7ZkW zD)e|v`q-?0GjkdK-9nzmmGpFFk>5*&50BB`J4(OzWZ|z-V&NgRwB}&a?LG8@$LP7A z{k>lB4>NBNdk5eL~*H^4)@uXWlFL#mw{Xg;G3He}K6O=VdH^r{G1* z!&&s~^>9D@G3k)Ehnp7`Fs7%zAzoZkL)-~9?Jej|(~eXjaYihJc> z^l-2IYfH2|rBAU(&mWkRzwxb}@ObO{dLh4o<^MH{{BJ$nn{J<~N%wa%>v>x6X69YYDLmeCi#d{d zi2L!+q}$h5D(tpZVZfR}3Q^2<4WxEpl2&78^}Jx0$1%>9a{_=C*n30}wC7W^sZW?jhW{}yvI zzB9a+IX8!Hc*b3#6?1dq@-6SyoU6ObKf`>f2u~C1sTTYTEYHQz)$n2!rpJV* zg84+j?`K{l_(zye7JM1=V!>;ePZfL>^Af>VGcOhVA?CE7=M8^|`E0=-VeS{aiTOOi zA7gF{-pYJ|;12V0!P}WH68sa)7Yn|H`4YiD!+fdWom}s#1^)ueuN3?#=Cy)!je>uRd9&cpF>eukKl3)hUtqpj@TJV7f}4DQQt-npzg6%bGT$cn zkC|^5{AbK}2>uJ^I|ct0^Id|+neP_-H_V?A{P)Z|1^*NCy@J2NJSO;?%nt~Df_abN zKV^PM@Po{I1>eWxlOuv#m+E@kC-`m5j|$$xykGDY6Sbb>f*)l00l~vpX!#R@_e|1! zQ1A%LpA@`t%@FM26;FFmz5WJXqx!_ZoFA}_j`C`FKnbY^4yy;-pRhJ4ro8_wo_cLE9 zxLJp-72Ia|dchYk4+>t+e7)d{m^TW(n0d3{OPIF^zLa^J;ML4G3%-(hRPb8nPYPbo ze5>F==Gz2c&wRV!jm&oleh%}Uf}8TQOYm7NzgzGFlXN+FM(~%oz3mkI5X+lr|UNS}Vse;$Ce2L&~%u5B|!F-0`2bj+m{FluAg4-OPd4exs zZVO({e1YJLn3oH_nE4{XmoTUEO!S!aT*`cj;ML5R3Vu2BYQbxnuN1tVd9C0<=JkTF zXC4&1k@m>f6K6zeDg{EWcCm-OP6h{tWZof_E~1M)1ANI|Yw1-z)e5<}ty0m>&@Q5c3|v zdzl{+{0Q@2!TXpW5&S6gKEeB$9~JyK^M1jfWqw@nsmuoiZ@-diTj~yjsduF;KPcp9 zFh42yZ01S9J6Zpb;C;;11`GT*{wA5{=c{13d;#k(5WJkZxnp7UEMi_Luyg}GmF z)Bem8d^5|NeRVfHlXbe)3;CX@ng<1+LYq!_)(d`RK=VeyuVUUT__fSi1izkno8UJx z-z@me%%g(OV*aGybD3`y{C4Ks1iyp%cELZue23t7G2bcpz07wBejoGQf`6F#GlEw# z?-aa>`Ch>vU>+0vLFNYpuVdaLcmwl8g0ExVEBMEl9}#>b^FG1D%#RAbiFv=^5$4AQ z?_fS4_)li(@_9n=PqX}>;GboFQt;0+PYOP1j@CaU_?KDUoPRO(F3j@zqqIB2kMa4K z0>Q1TwcI$tmoqOE`~dUug1_e)t!IMZZOkVM{wL-|f-k>T>zOS0SD6c*U?+n2|PMh|4W(#iWonLTM@8$`<;Rdb87Tnak1%jJ;S1$N=I=p~q zk>HJ`nlBdI)Vn2un|ilYa8vK91z-7It$(H9rry;G{su(m^)VmhJf6d`(6a0powVus_Kgm2Qc+U(i|D@m_rbCW+whI1z=Gz3Hev6jh zF8C$fp6n3(0P~%KKOfL~b_s6J)_k|%|H1qj!N+mC(JA<5=6eNynt4p{R|4AK1A_mQ zd5_?)GCw5vi+-)ASMZPDruh-U4|96<2|kI_?Wo}IVcswJN!D{*@M$bRAovi=pAdZc ztvZ~8g0EnHQt;0*PYT}4d`R$7bG3ep&&!y0{$}RpJe}dIm@gLc1=U*562Y%tuK7~I zKgzsX@FC_a1;3Vgt>D))uNVA>%!7h|iTQfL_f+X{n)6V(c%^?+I9}S8?$T*)?qe3P zh2iEtW})EbUgQM9w@lHXNN{rxu~=|(-;lc*H$3LPAvY^7H}?(wLXWv`XbW!c8x}D) z>A#e)EBuPV(#tA zbp}(93dMNX)T2_tO+8vHxT!~tf}47@O>lExa2}!+}vZ`Cb+L!hi9+g=Dy@n!OeZiA;Hak$%&#JHuoj1X$_C8ri+Nv zaI;JQt~92#HMPs8sSAnI)Ge!N%NrW3X^O6`(?q?Hk?^#&b<^lVUad6^mnK)&;CAA) zHFda!dB>d<*WvP1)r0K1W@Y7ywaZr5=qR4m-y*oCD$mh#b@|!!`brlm12+#Yyc8zN za=%Noq4#n9o8+t8^esJdYx*J6zr2SzS0C*Ne{Wvp8q*BYf137?&cD|^4U~`Pk)hwk?B2`BEMv;eG literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/.libs/unicode.o b/libvterm-0.2/src/.libs/unicode.o new file mode 100644 index 0000000000000000000000000000000000000000..ef50231d42d557560dea7a002ebe4cf4cb619c0f GIT binary patch literal 6344 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 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, void *data_) +{ + struct UTF8DecoderData *data = data_; + + data->bytes_remaining = 0; + data->bytes_total = 0; +} + +static void decode_utf8(VTermEncoding *enc, 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 = &init_utf8, + .decode = &decode_utf8, +}; + +static void decode_usascii(VTermEncoding *enc, void *data, + 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 = { + .decode = &decode_usascii, +}; + +struct StaticTableEncoding { + const VTermEncoding enc; + const uint32_t chars[128]; +}; + +static void decode_table(VTermEncoding *enc, void *data, + 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) +{ + for(int i = 0; encodings[i].designation; i++) + if(encodings[i].type == type && encodings[i].designation == designation) + return encodings[i].enc; + return NULL; +} diff --git a/libvterm-0.2/src/encoding.lo b/libvterm-0.2/src/encoding.lo new file mode 100644 index 0000000..bd24c64 --- /dev/null +++ b/libvterm-0.2/src/encoding.lo @@ -0,0 +1,12 @@ +# src/encoding.lo - a libtool object file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/encoding.o' + +# Name of the non-PIC object +non_pic_object='encoding.o' + diff --git a/libvterm-0.2/src/encoding.o b/libvterm-0.2/src/encoding.o new file mode 100644 index 0000000000000000000000000000000000000000..3b00ed210444fd76a942333f654158c9618d49e4 GIT binary patch literal 6056 zcmeHLYiv|S6rO!R3lz2>7zik<6s!uHmLLIxu-IO?CZG+nkr119DGM}xVY}NFW7#Gx z(3?%esxje*M5BN7kI`rf;h_s8HZjCB{((P+#>AwX#h{khCN0+Q%$;-h_I7z#jR^@S z%*>hZyk^eKoVz8iZGXw_atS7v*etR$MujMTB+CaS8x#w~d=b)(Uv#5mNH=1GdO{o2 z4Q&+TXus5vT+mB=vhwmIT_bc&H+Bq^8`_oa#*P%*rr(D)CWU$e|Fo3u8u0ar9&Mzv zV`RDS7)gn1twVpLEkXej;%F+m3fw?K^y)4>5gIUN>nHWzg-0nc1lQoDU)-LMle=kM z^7UyWhMT;a8%W>s$bfNQ_hSf6;_j5sxPTO40pI$Le+7;g5qtLj7LooCPNh*W|Ki|G z;}Y>kMZSb8@+nBy;FejFV9{GKA=M}=a?8G>yDc}~uF;vk<6mR9M#|cE?+n_0!t177 zEFvChm>87y!The#c>FN&{lR?7Or~B5UYN)eA|AGjc3C1Q#$UFJHf2T2MZDB5D$0r` zJnoFnqs+9TP`u3j7Ye*Iq4!pxpr9HV^zJ0GQCr-)2oqeijoKC8iC4!aCd_+$&k6^7 za-)ix@0--hPAtjL#xqpNCQB2c@zQ}FZLG6nY&qq9EFRs1n>W^-YTs!_zhkYVDmEso zm|Sq~Eznysw-E2b8@%V*QQ>7}pS;TK9&V*Cn2DaOkOjFE>NP*=U6;HGZxTg)y{|7u=-uA5tde@o4|>-{SoIk0d!8_*XZT*} zSzx8dcjBoDoG_=z=(u70)}syeYvWk9$m~jJ<0!Bf2faFOo>g- z7j0IN5Cv#+(PpE4fo*(=Rs? z8?ECRe7Vt59wDlq6yg(@oJM=lh}Qj({^tyAD=T~6UtAv9TN`flmzD(921?hge_C<= zwIypyOG-;i#6q*v>-1(iQ=^kI5ncx)NP$tc+6ckcjf^oU`n!0goA$e zqGP$|CogG`{0$O<&Qr&7_i5>X7ocR$fn0gT z@AzFil{`8BA`|#+h6k2|Jn-bC0swBCADJ6qk1Cw!GAGK|BW5UW^57Bp3&BBn=J1}y z3zeL{B@}m_awT&LY>U!oJsvBR{`X2><`@`}N6a!ATVDy@Wy1^IrtnRL`_-KB4vFUp zzo{lD9+O85XjLq_ ze}mW;0T?lPR+ubl3AfbL)>_t8F$E~gXn1dZBr7e~%Jwyf+Yly;#p*1+MYKjE%?(xc zO-*&NLscA+mUoISAc)dMr#Oz^JVc+}0iUmqn)LZyffcJAxSv3f?!fVhkrZ*@eueWu z{;8Ey2jZK}xUO(o0})CC?$q(<7eR5oWU&YGKiTjlw65F>Lbl|+N zY6s5y>U7{7_ag_+??nV!4maRdV~jSN<3Z+{BlY1xG}0awfqmg(^5?6?g#B4_IN~Zd(g*W2 z(m9jQ>DsS{E#EP)7IA#~uG)#;Ib)?@a_#@5+W)6Yk@ioY4(I;MfKhzs`Ulni4?$)p zzkLFJGg_|r!TF}5JwCZ~#;13gD}J?7EOBhuZ@RVm*TCh9KcwQ9C{5ZweI=do>wr;w z)&naksS0E<18O8)A|9hQUJ6uqM%mjj;@OUI`p+H0?6rhsY=4F(HsbX&)D0%r{ueJW zHQJRT?Vsu-+w=X;6`M>BsiHcn7EITPz5z~q%Bl1201^i&An24iSiN)qbWFMIlWP6% KS(YZ_wEr6;wbM!f literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/encoding/DECdrawing.inc b/libvterm-0.2/src/encoding/DECdrawing.inc new file mode 100644 index 0000000..47093ed --- /dev/null +++ b/libvterm-0.2/src/encoding/DECdrawing.inc @@ -0,0 +1,36 @@ +static const struct StaticTableEncoding encoding_DECdrawing = { + { .decode = &decode_table }, + { + [0x60] = 0x25C6, + [0x61] = 0x2592, + [0x62] = 0x2409, + [0x63] = 0x240C, + [0x64] = 0x240D, + [0x65] = 0x240A, + [0x66] = 0x00B0, + [0x67] = 0x00B1, + [0x68] = 0x2424, + [0x69] = 0x240B, + [0x6a] = 0x2518, + [0x6b] = 0x2510, + [0x6c] = 0x250C, + [0x6d] = 0x2514, + [0x6e] = 0x253C, + [0x6f] = 0x23BA, + [0x70] = 0x23BB, + [0x71] = 0x2500, + [0x72] = 0x23BC, + [0x73] = 0x23BD, + [0x74] = 0x251C, + [0x75] = 0x2524, + [0x76] = 0x2534, + [0x77] = 0x252C, + [0x78] = 0x2502, + [0x79] = 0x2A7D, + [0x7a] = 0x2A7E, + [0x7b] = 0x03C0, + [0x7c] = 0x2260, + [0x7d] = 0x00A3, + [0x7e] = 0x00B7, + } +}; diff --git a/libvterm-0.2/src/encoding/DECdrawing.tbl b/libvterm-0.2/src/encoding/DECdrawing.tbl new file mode 100644 index 0000000..6e19c50 --- /dev/null +++ b/libvterm-0.2/src/encoding/DECdrawing.tbl @@ -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 diff --git a/libvterm-0.2/src/encoding/uk.inc b/libvterm-0.2/src/encoding/uk.inc new file mode 100644 index 0000000..da1445d --- /dev/null +++ b/libvterm-0.2/src/encoding/uk.inc @@ -0,0 +1,6 @@ +static const struct StaticTableEncoding encoding_uk = { + { .decode = &decode_table }, + { + [0x23] = 0x00a3, + } +}; diff --git a/libvterm-0.2/src/encoding/uk.tbl b/libvterm-0.2/src/encoding/uk.tbl new file mode 100644 index 0000000..b27b1a2 --- /dev/null +++ b/libvterm-0.2/src/encoding/uk.tbl @@ -0,0 +1 @@ +2/3 = "£" diff --git a/libvterm-0.2/src/fullwidth.inc b/libvterm-0.2/src/fullwidth.inc new file mode 100644 index 0000000..a703529 --- /dev/null +++ b/libvterm-0.2/src/fullwidth.inc @@ -0,0 +1,111 @@ + { 0x1100, 0x115f }, + { 0x231a, 0x231b }, + { 0x2329, 0x232a }, + { 0x23e9, 0x23ec }, + { 0x23f0, 0x23f0 }, + { 0x23f3, 0x23f3 }, + { 0x25fd, 0x25fe }, + { 0x2614, 0x2615 }, + { 0x2648, 0x2653 }, + { 0x267f, 0x267f }, + { 0x2693, 0x2693 }, + { 0x26a1, 0x26a1 }, + { 0x26aa, 0x26ab }, + { 0x26bd, 0x26be }, + { 0x26c4, 0x26c5 }, + { 0x26ce, 0x26ce }, + { 0x26d4, 0x26d4 }, + { 0x26ea, 0x26ea }, + { 0x26f2, 0x26f3 }, + { 0x26f5, 0x26f5 }, + { 0x26fa, 0x26fa }, + { 0x26fd, 0x26fd }, + { 0x2705, 0x2705 }, + { 0x270a, 0x270b }, + { 0x2728, 0x2728 }, + { 0x274c, 0x274c }, + { 0x274e, 0x274e }, + { 0x2753, 0x2755 }, + { 0x2757, 0x2757 }, + { 0x2795, 0x2797 }, + { 0x27b0, 0x27b0 }, + { 0x27bf, 0x27bf }, + { 0x2b1b, 0x2b1c }, + { 0x2b50, 0x2b50 }, + { 0x2b55, 0x2b55 }, + { 0x2e80, 0x2e99 }, + { 0x2e9b, 0x2ef3 }, + { 0x2f00, 0x2fd5 }, + { 0x2ff0, 0x2ffb }, + { 0x3000, 0x303e }, + { 0x3041, 0x3096 }, + { 0x3099, 0x30ff }, + { 0x3105, 0x312f }, + { 0x3131, 0x318e }, + { 0x3190, 0x31ba }, + { 0x31c0, 0x31e3 }, + { 0x31f0, 0x321e }, + { 0x3220, 0x3247 }, + { 0x3250, 0x4dbf }, + { 0x4e00, 0xa48c }, + { 0xa490, 0xa4c6 }, + { 0xa960, 0xa97c }, + { 0xac00, 0xd7a3 }, + { 0xf900, 0xfaff }, + { 0xfe10, 0xfe19 }, + { 0xfe30, 0xfe52 }, + { 0xfe54, 0xfe66 }, + { 0xfe68, 0xfe6b }, + { 0xff01, 0xff60 }, + { 0xffe0, 0xffe6 }, + { 0x16fe0, 0x16fe3 }, + { 0x17000, 0x187f7 }, + { 0x18800, 0x18af2 }, + { 0x1b000, 0x1b11e }, + { 0x1b150, 0x1b152 }, + { 0x1b164, 0x1b167 }, + { 0x1b170, 0x1b2fb }, + { 0x1f004, 0x1f004 }, + { 0x1f0cf, 0x1f0cf }, + { 0x1f18e, 0x1f18e }, + { 0x1f191, 0x1f19a }, + { 0x1f200, 0x1f202 }, + { 0x1f210, 0x1f23b }, + { 0x1f240, 0x1f248 }, + { 0x1f250, 0x1f251 }, + { 0x1f260, 0x1f265 }, + { 0x1f300, 0x1f320 }, + { 0x1f32d, 0x1f335 }, + { 0x1f337, 0x1f37c }, + { 0x1f37e, 0x1f393 }, + { 0x1f3a0, 0x1f3ca }, + { 0x1f3cf, 0x1f3d3 }, + { 0x1f3e0, 0x1f3f0 }, + { 0x1f3f4, 0x1f3f4 }, + { 0x1f3f8, 0x1f43e }, + { 0x1f440, 0x1f440 }, + { 0x1f442, 0x1f4fc }, + { 0x1f4ff, 0x1f53d }, + { 0x1f54b, 0x1f54e }, + { 0x1f550, 0x1f567 }, + { 0x1f57a, 0x1f57a }, + { 0x1f595, 0x1f596 }, + { 0x1f5a4, 0x1f5a4 }, + { 0x1f5fb, 0x1f64f }, + { 0x1f680, 0x1f6c5 }, + { 0x1f6cc, 0x1f6cc }, + { 0x1f6d0, 0x1f6d2 }, + { 0x1f6d5, 0x1f6d5 }, + { 0x1f6eb, 0x1f6ec }, + { 0x1f6f4, 0x1f6fa }, + { 0x1f7e0, 0x1f7eb }, + { 0x1f90d, 0x1f971 }, + { 0x1f973, 0x1f976 }, + { 0x1f97a, 0x1f9a2 }, + { 0x1f9a5, 0x1f9aa }, + { 0x1f9ae, 0x1f9ca }, + { 0x1f9cd, 0x1f9ff }, + { 0x1fa70, 0x1fa73 }, + { 0x1fa78, 0x1fa7a }, + { 0x1fa80, 0x1fa82 }, + { 0x1fa90, 0x1fa95 }, diff --git a/libvterm-0.2/src/keyboard.c b/libvterm-0.2/src/keyboard.c new file mode 100644 index 0000000..d31c8be --- /dev/null +++ b/libvterm-0.2/src/keyboard.c @@ -0,0 +1,226 @@ +#include "vterm_internal.h" + +#include + +#include "utf8.h" + +void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod) +{ + /* 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; + } + + int needs_CSIu; + 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 }, // NONE + + { KEYCODE_ENTER, '\r' }, // ENTER + { KEYCODE_TAB, '\t' }, // TAB + { KEYCODE_LITERAL, '\x7f' }, // BACKSPACE == ASCII DEL + { KEYCODE_LITERAL, '\x1b' }, // ESCAPE + + { KEYCODE_CSI_CURSOR, 'A' }, // UP + { KEYCODE_CSI_CURSOR, 'B' }, // DOWN + { KEYCODE_CSI_CURSOR, 'D' }, // LEFT + { KEYCODE_CSI_CURSOR, 'C' }, // RIGHT + + { KEYCODE_CSINUM, '~', 2 }, // INS + { KEYCODE_CSINUM, '~', 3 }, // DEL + { KEYCODE_CSI_CURSOR, 'H' }, // HOME + { KEYCODE_CSI_CURSOR, 'F' }, // END + { KEYCODE_CSINUM, '~', 5 }, // PAGEUP + { KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN +}; + +static keycodes_s keycodes_fn[] = { + { KEYCODE_NONE }, // F0 - shouldn't happen + { KEYCODE_SS3, 'P' }, // F1 + { KEYCODE_SS3, 'Q' }, // F2 + { KEYCODE_SS3, 'R' }, // F3 + { KEYCODE_SS3, 'S' }, // 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) +{ + if(key == VTERM_KEY_NONE) + return; + + keycodes_s k; + 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~"); +} diff --git a/libvterm-0.2/src/keyboard.lo b/libvterm-0.2/src/keyboard.lo new file mode 100644 index 0000000..276da65 --- /dev/null +++ b/libvterm-0.2/src/keyboard.lo @@ -0,0 +1,12 @@ +# src/keyboard.lo - a libtool object file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/keyboard.o' + +# Name of the non-PIC object +non_pic_object='keyboard.o' + diff --git a/libvterm-0.2/src/keyboard.o b/libvterm-0.2/src/keyboard.o new file mode 100644 index 0000000000000000000000000000000000000000..c31f1ea29403ccae069e6812dfc73a920eca07db GIT binary patch literal 6040 zcmb_fZ){sv6~E6;o2;aDwsugdKutx~?oUjdjy2m@ahke$MJX+7qE%gx<2a6zHUDZq zC+#3jw%3vxD_|y}F>MkzjqD%uv0ny*ZhmaByz!ULjN63deCg4pX2yJ1lU;*$=s>?L9oh)q{FQ}Oj* z$qwuf{nW`FGo8wtnJ|?%?%2!A%js|_6E3C0OU2LILrVr!cK*xh>zVM98JnYO-ZbXy zn^TwT%xe>OHro?R#^fy;=w{#a>*So;nln2t5fbJMIu)5JkNE?0=3z-0^UJa~J3pDe z4riC^iZQ2;-S%9r%pG?)pFNi^bI&?lwLN#Y%S~UEyA&x{vC9fO<|E< zTiUXWIPWLfRkc?R>962mvX-E z=X|p{to$xA?H;4GU3htn!@ub&j7dogF02Ex&3`WCtOi0erjhAfz~EZabLl_uTEF`y zHK3u&hH|{cuxwUE&1lXGyJgKowFfv<#cCBZxSO~`zGww!zKE_@+*DXd;k$U2^6-_3 z1>_cVP0fVNVUst?)x?$qvTa0cfel zTPZ3H*g$rv);GN!4w<0A-Iw3uSJ>BBi)>2!$1XkSo4!dv5%=pW6aL69)%d1gqqgbX zy>gw>%1G^zdE@MWwNWXUauiMuSlx){3Aybyh-6_0wUJ&kQ~PLTe6v$mXzA>H-Nl^c zce;~NTNVGp2V|NpR%MF@Z3yac6UF{XbZ8pB*>K7C?)<%zsPKmDPg++_ATx7}cFJ$e z^zT>lrVv34vCkfN^|mt0@6rrbk2fI4Yh*WQv?j?5|0Xw%|GAa5+<0RYevQS$$bzb6 z=hA&F#dlVBHA>r%o4wbPzPmEBHG?d^AY-Hj&vP;7yXd1cxNwV3p7ksYScFJPr}5=* zsqhZLY;jArSj}NA@69@Sf#5VQck*LQGViAh`3(7{V_F``YZEg2e{w?I-CJ;TZDljc zY4HGKioBR~;a!Yzfwj2sAs0UE!mW(y!W0+ifp3M;C;XW7K!A@(v|@wfN!deRNpR7g zU?uR^T=;nxKFL`5^if^m2VM9f7vAo|_*78#h?~Nna^anXAvN?g(lg39Nab0^O;jcr z@24`!_(>`)#yB}rjGv-%jPU^~#~C+Md5-bZRGw%27?m$Eew@lPjGv%#kZ}W*LyQAd z4m17&l_QL|Pksx0>Or-9%bZjYHSKN27_38i2A%UUVdH} z=jx1IwbeCQO6*UQ4i6-XSIf4#Z+Tj`ZuWGMg`@itc#?nNRn{+-^n{9`?91lzmFG-wk7u3mX49hZf({_&v!)5P|%dBRPyBG3HjmI4edEe#7-S1)r z`zk)#pmz7WmwB}g%Ky_9>|fOEai2qmD%iiK*@vXH5LYVL|5mdPv;7AZ><9RKQRByV z3FI5h>+^j_v&XjxBv--S(K>m8K2Ey!A#~vI`~?*fd{03XUvjDf_Yp+#b?ir-&5Fk_ zG>G~hP<)7al}CzK_dti!tnB|8p3fdezxz`_z#)y*Z6vl6TaUd zYMe{Vk2-XjSNWmhrtBZ#@%uGGzF(u*$%&<5aLjoaX^WGfc=#7;z`*GNkG$YV1@w+hK6ZF8R-u{&O(FY@!=;OyZ22SfnsP?bA+5&Pqm)Rh5 zp)%uuw{duqOmT;pKT=zt|3Ttl(nyb6{>V0 a`5>*)CahKE9k*WlpYizn;LIg@`~L#%u2Z%E literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/mouse.c b/libvterm-0.2/src/mouse.c new file mode 100644 index 0000000..bd713f8 --- /dev/null +++ b/libvterm-0.2/src/mouse.c @@ -0,0 +1,99 @@ +#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(!state->mouse_flags) + 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); + } +} diff --git a/libvterm-0.2/src/mouse.lo b/libvterm-0.2/src/mouse.lo new file mode 100644 index 0000000..af1c38f --- /dev/null +++ b/libvterm-0.2/src/mouse.lo @@ -0,0 +1,12 @@ +# src/mouse.lo - a libtool object file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/mouse.o' + +# Name of the non-PIC object +non_pic_object='mouse.o' + diff --git a/libvterm-0.2/src/mouse.o b/libvterm-0.2/src/mouse.o new file mode 100644 index 0000000000000000000000000000000000000000..ff819ba9f5af282ac11e5c2976e063a5cdbdf115 GIT binary patch literal 3824 zcmbtXZ)j6j6u&R8ZR)=^Q}+iH8ktVVW@u!H|8R-+*|*Wmq6YWDk)<(3S@UPfdzuYn zBG1k~9vS_%!S-RWFZ(dqw^8=Nwyvz1$jCMk2SoyA5*J~02xhEXcFukGKGSDfVLNHw zJ?D4+-E;1}@1{9v;E+QQ2q%H;CyykK5|Ui?i1$&}N4m%w5|s5{Wj$M$wQN0?b55Xd z?KSyAw%%|O0)PX7T@+|gKty1x3G|>v8v@UmKtlwe9f9_V4jA70mL|~dxgkka*446X zNHES;wML_%OLar4>(WAPvld$rP^Fb$)vp-Rf*dJ=SeAvNb~QidlBcuP4lTPNrW}N{lv#{IOhknnFUUL9=P)oPGs^jk#(OGe?hBoU*XPCf2PL z%NDlR#GG1j-omz;n5Y%&7PijB1g*Hhv2mEz_q=Q$lzmfIuj|2*9=O)0OT|7tFf$#! z<^ac=Mm`TS)#_sR#jl|8$XuGIjHCNvA7d`v0%mkdS*g_pMY&r7=|Z-63mnfoXC=y9 z$j)$?GFrAoR8U^WwKAlcLUNNK6$>&vudkA>6$agyT{ehczwg(J`j4z-mp=h}SeywM zNEIJ2Gg>pcF%uE^Sq ztM>Y$b}SR~iQ&e_t^LxA$3#~vda$eq=Ag`~Y~;7$8o*h>b{oFrZmF!gOkUY7Rk|;J z2{lm}oCcF7SJ4L*)8?F3cEI*oWlEXRDYeT|w1FbU6k1sTo!64UN!@5kXz84wXMde} z;#ZrmhIabO(4_}PaL$k(6p}qgwqgY6-tg;x((UyP6oC7xdS{{0s0l5~h0`%N2FJZs z!5IjyWh>XUUN05#{8J}OwtIyBKRSJEoriE%gMd;4`j~57VkfJaF2UiUv%hnIPUiL zdUklc+jqXmIQNd;9o}BAw-<7(XL*G8j}Rf_5;m-Mc8)_AHv*1dq%c1GU9Jxt2iJ5s zPJo4}+lM&NLws2JKP>$T#DRM8?%;ayB?lT{>~p9D`W+u!@A%E~{}gecXK_`zUR*0s z1FCuafu?QbK3q|cli2b`Bt(QyD-t1+omK$gXo^F70~lU4h;y0tggJ|ZUP??dhaP7C zCQcZL_Q+&mHqhV}BofU2IEe(VXdAr@vde~{cwh+}=e-u@KgL=?*95Q>!_Tr-(6s=J z#1d*c>WL6l8Qv92N8cTZ#>w#Mk&zGrB%vxvRS8ie66zl~e84{tI(+ERQR!IdnE$|l z6e4GoXet&;s_9c9W}Qx^PREtuP((?MFf|PTVa`Ms0Yj>yB;q)YtM~xIErwlyqetlm zfi%R}o${5CV>I|(uT(hD5M)lAQE7mLI949D@#LE`TTj{6wS`^RyQHbYcSP2u) zP&!RK6ip>eB$9~5qH!>YCzPnC|Bc}Gv=WZI!>ppGLc^(WEXs2KpTP~z*h2hq+)O?u zd}w=Ruy$Lf8-T+#_gRR^)Y*d1GaP^Jqc7hFe7#;^INN5-nvfrCdAY)#AKxz+^S}D< z1YeAa`=*&nEdTyy5O}`7IqYSaj{|J1vRC96Ovd^#q`iKOkNNp|1C~-3TaaxWU?Z=e zkNtpizS(rAeL-#h+!pUf^tac)i}_D*LpJjHbNDzgt=4~hrD>62hFCwgg!}XLZmAA literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/parser.c b/libvterm-0.2/src/parser.c new file mode 100644 index 0000000..0459a89 --- /dev/null +++ b/libvterm-0.2/src/parser.c @@ -0,0 +1,393 @@ +#include "vterm_internal.h" + +#include +#include + +#undef DEBUG_PARSER + +static bool is_intermed(unsigned char c) +{ + return c >= 0x20 && c <= 0x2f; +} + +static void do_control(VTerm *vt, unsigned char control) +{ + if(vt->parser.callbacks && vt->parser.callbacks->control) + if((*vt->parser.callbacks->control)(control, vt->parser.cbdata)) + return; + + DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control); +} + +static void do_csi(VTerm *vt, char command) +{ +#ifdef DEBUG_PARSER + printf("Parsed CSI args as:\n", arglen, args); + printf(" leader: %s\n", vt->parser.v.csi.leader); + for(int argi = 0; argi < vt->parser.v.csi.argi; argi++) { + printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi])); + if(!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi])) + printf("\n"); + printf(" intermed: %s\n", vt->parser.intermed); + } +#endif + + if(vt->parser.callbacks && vt->parser.callbacks->csi) + if((*vt->parser.callbacks->csi)( + vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL, + vt->parser.v.csi.args, + vt->parser.v.csi.argi, + vt->parser.intermedlen ? vt->parser.intermed : NULL, + command, + vt->parser.cbdata)) + return; + + DEBUG_LOG("libvterm: Unhandled CSI %c\n", command); +} + +static void do_escape(VTerm *vt, char command) +{ + char seq[INTERMED_MAX+1]; + + size_t len = vt->parser.intermedlen; + strncpy(seq, vt->parser.intermed, len); + seq[len++] = command; + seq[len] = 0; + + if(vt->parser.callbacks && vt->parser.callbacks->escape) + if((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata)) + return; + + DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command); +} + +static void string_fragment(VTerm *vt, const char *str, size_t len, bool final) +{ + VTermStringFragment frag = { + .str = str, + .len = len, + .initial = vt->parser.string_initial, + .final = final, + }; + + switch(vt->parser.state) { + case OSC: + if(vt->parser.callbacks && vt->parser.callbacks->osc) + (*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata); + break; + + case DCS: + if(vt->parser.callbacks && vt->parser.callbacks->dcs) + (*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, vt->parser.v.dcs.commandlen, frag, vt->parser.cbdata); + break; + + case APC: + if(vt->parser.callbacks && vt->parser.callbacks->apc) + (*vt->parser.callbacks->apc)(frag, vt->parser.cbdata); + break; + + case PM: + if(vt->parser.callbacks && vt->parser.callbacks->pm) + (*vt->parser.callbacks->pm)(frag, vt->parser.cbdata); + break; + + case SOS: + if(vt->parser.callbacks && vt->parser.callbacks->sos) + (*vt->parser.callbacks->sos)(frag, vt->parser.cbdata); + break; + + case NORMAL: + case CSI_LEADER: + case CSI_ARGS: + case CSI_INTERMED: + case OSC_COMMAND: + case DCS_COMMAND: + break; + } + + vt->parser.string_initial = false; +} + +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: + case CSI_LEADER: + case CSI_ARGS: + case CSI_INTERMED: + case OSC_COMMAND: + case DCS_COMMAND: + string_start = NULL; + break; + case OSC: + case DCS: + case APC: + case PM: + case SOS: + string_start = bytes; + break; + } + +#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0) +#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL) + +#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND) + + for( ; pos < len; pos++) { + unsigned char c = bytes[pos]; + bool c1_allowed = !vt->mode.utf8; + + if(c == 0x00 || c == 0x7f) { // NUL, DEL + if(IS_STRING_STATE()) { + string_fragment(vt, string_start, bytes + pos - string_start, false); + string_start = bytes + pos + 1; + } + continue; + } + if(c == 0x18 || c == 0x1a) { // CAN, SUB + vt->parser.in_esc = false; + ENTER_NORMAL_STATE(); + continue; + } + else if(c == 0x1b) { // ESC + vt->parser.intermedlen = 0; + if(!IS_STRING_STATE()) + vt->parser.state = NORMAL; + vt->parser.in_esc = true; + continue; + } + else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state + IS_STRING_STATE()) { + // fallthrough + } + else if(c < 0x20) { // other C0 + if(vt->parser.state == SOS) + continue; // All other C0s permitted in SOS + + if(IS_STRING_STATE()) + string_fragment(vt, string_start, bytes + pos - string_start, false); + do_control(vt, c); + if(IS_STRING_STATE()) + string_start = bytes + pos + 1; + continue; + } + // else fallthrough + + size_t string_len = bytes + pos - string_start; + + if(vt->parser.in_esc) { + // Hoist an ESC letter into a C1 if we're not in a string mode + // Always accept ESC \ == ST even in string mode + if(!vt->parser.intermedlen && + c >= 0x40 && c < 0x60 && + ((!IS_STRING_STATE() || c == 0x5c))) { + c += 0x40; + c1_allowed = true; + if(string_len) + string_len -= 1; + vt->parser.in_esc = false; + } + else { + string_start = NULL; + vt->parser.state = NORMAL; + } + } + + switch(vt->parser.state) { + case CSI_LEADER: + /* Extract leader bytes 0x3c to 0x3f */ + if(c >= 0x3c && c <= 0x3f) { + if(vt->parser.v.csi.leaderlen < CSI_LEADER_MAX-1) + vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = c; + break; + } + + /* else fallthrough */ + vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0; + + vt->parser.v.csi.argi = 0; + vt->parser.v.csi.args[0] = CSI_ARG_MISSING; + vt->parser.state = CSI_ARGS; + + /* fallthrough */ + case CSI_ARGS: + /* Numerical value of argument */ + if(c >= '0' && c <= '9') { + if(vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING) + vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0; + vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10; + vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0'; + break; + } + if(c == ':') { + vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE; + c = ';'; + } + if(c == ';') { + vt->parser.v.csi.argi++; + vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING; + break; + } + + /* else fallthrough */ + vt->parser.v.csi.argi++; + vt->parser.intermedlen = 0; + vt->parser.state = CSI_INTERMED; + case CSI_INTERMED: + if(is_intermed(c)) { + if(vt->parser.intermedlen < INTERMED_MAX-1) + vt->parser.intermed[vt->parser.intermedlen++] = c; + break; + } + else if(c == 0x1b) { + /* ESC in CSI cancels */ + } + else if(c >= 0x40 && c <= 0x7e) { + vt->parser.intermed[vt->parser.intermedlen] = 0; + do_csi(vt, c); + } + /* else was invalid CSI */ + + ENTER_NORMAL_STATE(); + break; + + case OSC_COMMAND: + /* Numerical value of command */ + if(c >= '0' && c <= '9') { + if(vt->parser.v.osc.command == -1) + vt->parser.v.osc.command = 0; + else + vt->parser.v.osc.command *= 10; + vt->parser.v.osc.command += c - '0'; + break; + } + if(c == ';') { + vt->parser.state = OSC; + string_start = bytes + pos + 1; + break; + } + + /* else fallthrough */ + string_start = bytes + pos; + string_len = 0; + vt->parser.state = OSC; + goto string_state; + + case DCS_COMMAND: + if(vt->parser.v.dcs.commandlen < CSI_LEADER_MAX) + vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = c; + + if(c >= 0x40 && c<= 0x7e) { + string_start = bytes + pos + 1; + vt->parser.state = DCS; + } + break; + +string_state: + case OSC: + case DCS: + case APC: + case PM: + case SOS: + if(c == 0x07 || (c1_allowed && c == 0x9c)) { + string_fragment(vt, string_start, string_len, true); + ENTER_NORMAL_STATE(); + } + break; + + case NORMAL: + if(vt->parser.in_esc) { + if(is_intermed(c)) { + if(vt->parser.intermedlen < INTERMED_MAX-1) + vt->parser.intermed[vt->parser.intermedlen++] = c; + } + else if(c >= 0x30 && c < 0x7f) { + do_escape(vt, c); + vt->parser.in_esc = 0; + ENTER_NORMAL_STATE(); + } + else { + DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c); + } + break; + } + if(c1_allowed && c >= 0x80 && c < 0xa0) { + switch(c) { + case 0x90: // DCS + vt->parser.string_initial = true; + vt->parser.v.dcs.commandlen = 0; + ENTER_STATE(DCS_COMMAND); + break; + case 0x98: // SOS + vt->parser.string_initial = true; + ENTER_STATE(SOS); + string_start = bytes + pos + 1; + string_len = 0; + break; + case 0x9b: // CSI + vt->parser.v.csi.leaderlen = 0; + ENTER_STATE(CSI_LEADER); + break; + case 0x9d: // OSC + vt->parser.v.osc.command = -1; + vt->parser.string_initial = true; + string_start = bytes + pos + 1; + ENTER_STATE(OSC_COMMAND); + break; + case 0x9e: // PM + vt->parser.string_initial = true; + ENTER_STATE(PM); + string_start = bytes + pos + 1; + string_len = 0; + break; + case 0x9f: // APC + vt->parser.string_initial = true; + ENTER_STATE(APC); + string_start = bytes + pos + 1; + string_len = 0; + break; + default: + do_control(vt, c); + break; + } + } + else { + size_t eaten = 0; + if(vt->parser.callbacks && vt->parser.callbacks->text) + eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata); + + if(!eaten) { + DEBUG_LOG("libvterm: Text callback did not consume any input\n"); + /* force it to make progress */ + eaten = 1; + } + + pos += (eaten - 1); // we'll ++ it again in a moment + } + break; + } + } + + if(string_start) { + size_t string_len = bytes + pos - string_start; + if(vt->parser.in_esc) + string_len -= 1; + string_fragment(vt, string_start, string_len, false); + } + + return len; +} + +void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user) +{ + vt->parser.callbacks = callbacks; + vt->parser.cbdata = user; +} + +void *vterm_parser_get_cbdata(VTerm *vt) +{ + return vt->parser.cbdata; +} diff --git a/libvterm-0.2/src/parser.lo b/libvterm-0.2/src/parser.lo new file mode 100644 index 0000000..3f8b4d8 --- /dev/null +++ b/libvterm-0.2/src/parser.lo @@ -0,0 +1,12 @@ +# src/parser.lo - a libtool object file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/parser.o' + +# Name of the non-PIC object +non_pic_object='parser.o' + diff --git a/libvterm-0.2/src/parser.o b/libvterm-0.2/src/parser.o new file mode 100644 index 0000000000000000000000000000000000000000..32817174e1dcd82a838cb55beddec54363a768c2 GIT binary patch literal 7112 zcmd5=du&tJ8NbehJW9A3&@LNDHCozbf`o)JMuC+Zh~cgZkah9KRv;wCK-RpJTqhJY zC7T1uotM=s)r2+dw=MUM_jdsSg=NhwbyDY6o z9Ofwwx4=3N9p)9&rhxe7O=vMA{){={OPcoY!MY$dniDM^)AncdN&l_!*5af&;ZHIX zJc(Im)4VI9jZBz`aKxx2e`{S>Ghw7J!Xj!$4!Kk|W)q6F3#J`{J^pz+&aKA4w@3mW z%Vx8C*~6Ye93F$Uv9bjBWAKnPrj z7~XKETpuef&@?+{POQJk0x(fbuSih=Vm0wFgFJ~W1z;iPwm$j>mYy4fG`Oyh{wyDd z`(92qo`fu}*Dxa?_~g$}z7nbP`7BIKr-D)LNc`vX@#ctGvyAuvMQp*GIM;+X)ATtz zZrV3Zd!{jaGqpNj2oroySSNjq81)SQV?GXl>bXLhPEv)H_m*kTk~25*>6wflD_F$X z$?$LFTij!83<$OMxG)Grs3~vD8=Kan>56E&Jen?vri-F!Pn4)< z*;3M?&VFyfE<*G|F|5lebIzB6*_6pRTS3KxYr+ew!kHp{Y;75;Q8_3Ui6UJF8u0iQ zqp3_Oark3EGNvb?Pio`7yCg!NG^(&_M`QXcv2ov>k>R_VwF=J*p(BolBK^{}e13B? zvTD!pW#o(VT7eyW<9;^FRL~A4<(>rTPn`fegjO-p9}hjwHMdlU|M0KzmTDT!8D6qk z+Me3SIW~ko&1Y-atn|5P=pi=~^8?b}^gvpwgtL}}Dv5^02(h8k2ooM18g|ffeKcO; zie%9sO0g58m3(~bUN(!$#G+@vLv^d<1Ln073GjNkBuq{ER=mm_zemA^O zZwBI0v*$g|%5Afo6Q!y1sOIoxS7?_cV#0;>pb#+mH)57es{&?ycXw0#Lk*13Xyt1v zZgYDSr^sIlMye2Mmt~kUoSlUAPzbH$1{U$g_p{m2m?aWi!xDU>NJ^=35?qs);4P__ zKm!9eSWn=&cd!=E7l-IdgY8h`q;fYZ_g76kYOo;q=B%HbfcOi3k=o4Iy7i*KEv}B{c z0YaD_V{^QOVIw6;t&20=K?9Wda7MrIsOUR!QZ@nwN52&07QCpBJV+@m2leCdN#cB09!Ve{r5_REeHgA*PGiQbdSqU(Kui=*wUcso(UE zrKz7597v+$lA8K!pn+f5rWs1Q$1X*!PFugSbA7&ISV#J9+Bixd&^xUm{nJc zTp6jq5FL|hpeMRsh|DQ?)4>gFQx=n)t|fxGg>)U1WjtjI0XZ&m0>{2X@&F6dY%k@r zQhAipAH=#Y|Hxbaw`?{VOO^mW7hm6tc%gPx{P$Z>>y{Cp-4VwKOi8TqDg-r~bx zIEF=yE6V$*!`Ykcb739_+`JYlg_-j#nh`&4^B~5~3@_kI!Iw!t5>2m*rYo0Rr^3Dr zB|T&A_D$(u|KOj9d3bnHvkdq4W|tWsD^1P@VdrTAynf(fmoYD~%YFyvA#_Sy(D12; z%Ng!N{spEg;zvJ9F%d(2Q?UDARD2&21KGsCz>dF8a{oWu!TtMpd25=29i8nx-iG?R zZFLQ`J3b|P-fi{U8tNMw>Y459g$JJV&uE@=m7Wc&OG=|i*GAyQ z!e#%NARL8%nN|o%YNM)E;F7~)59|?v%Q1^NuuTFV#hV(7HQsbxAUSnpFdB<7-rMja z{I5D^P-EY)eBgQ(YLxM%tx%)DFKvYy9fbL9ABL6r5E~s`0Y3?RE$p;Jh**w+ z^$YyrHRzzR+~PffAJHEWILj#!^tJa71pDg(TIWDpXO9)^?+%{QPW840dV8$?-Y(W1 z=%iP0Akf|y)CR2n&Ym-Er~BK_bO(DZt?gj*k$sKLZAT6q_=2yst+jDqv#*V)dIEjV zYl9>ag8M>N+faX}6%+_p+cpri+5+ueT^;R#=LYhp&d^lHsdlTK%nXQpkh?m5_#$KN zIa>Q@^rE#P@u3y)Khfm~P14gkp!U}l@aqrpc}dSM_kez}0?w(qeB$|{$d7n2y@Y>7 z0CMFJM!ypRUyVLBiFXKGt`Ui!7Pzca691~etGF0Pj$F@FAE-(Cd(=VmvS$wO6I-)d z!4m?P@so0Dh2AX+{WK*9jaSv2{H%!kc0TRcGbFaiP6dBg1Z90`QE-ot<5h4m)C>ie=f+6|mv)aRxI8z0sNnM4h$*#X28ORHmLvaVxbK&xYDe=kP@z1^%x*Y)&T!McNo{j~!Y zO0A#@o~1J09TYbGFE1~c$px{U9Z`npJuA)~IU(GYLu~_kMfe#G!aECV`THjVr|m<$ zrD81W8|9C1^(OH>MXz9!;g&;f3wk8;zvbTnUeZtbbGNHP{}nfgKKbfybWShle{>zE ziVDNXf4YUJ{yznb^vn8+E^FKAutcljFXu_L)a3i1tIsCH*MNANq|5VZ?zs&z~x{2jnB|SAQF7#8mpp>ZmBvZ{jAk7f# a?>Y@!O>sy22 + +/** + * Structure used to store RGB triples without the additional metadata stored in + * VTermColor. + */ +typedef struct { + uint8_t red, green, blue; +} VTermRGB; + +static const VTermRGB 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 void lookup_default_colour_ansi(long idx, VTermColor *col) +{ + if (idx >= 0 && idx < 16) { + vterm_color_rgb( + col, + ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue); + } +} + +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; + + vterm_color_rgb(col, ramp6[index/6/6 % 6], + ramp6[index/6 % 6], + ramp6[index % 6]); + + return true; + } + else if(index >= 232 && index < 256) { + // 24 greyscales + index -= 232; + + vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]); + + return true; + } + + return false; +} + +static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col) +{ + switch(palette) { + case 2: // RGB mode - 3 args contain colour values directly + if(argcount < 3) + return argcount; + + vterm_color_rgb(col, CSI_ARG(args[0]), CSI_ARG(args[1]), CSI_ARG(args[2])); + + return 3; + + case 5: // XTerm 256-colour mode + if (!argcount || CSI_ARG_IS_MISSING(args[0])) { + return argcount ? 1 : 0; + } + + vterm_color_indexed(col, args[0]); + + return argcount ? 1 : 0; + + default: + DEBUG_LOG("Unrecognised colour palette %d\n", palette); + return 0; + } +} + +// Some conveniences + +static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, 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 = { .boolean = boolean }; + setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val); +} + +static void setpenattr_int(VTermState *state, VTermAttr attr, int number) +{ + VTermValue val = { .number = number }; + setpenattr(state, attr, VTERM_VALUETYPE_INT, &val); +} + +static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color) +{ + VTermValue 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; + + vterm_color_indexed(colp, col); + + setpenattr_col(state, attr, *colp); +} + +INTERNAL void vterm_state_newpen(VTermState *state) +{ + // 90% grey so that pure white is brighter + vterm_color_rgb(&state->default_fg, 240, 240, 240); + vterm_color_rgb(&state->default_bg, 0, 0, 0); + vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg); + + for(int col = 0; col < 16; col++) + lookup_default_colour_ansi(col, &state->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.conceal = 0; setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0); + state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0); + state->pen.font = 0; setpenattr_int( state, VTERM_ATTR_FONT, 0); + + 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_CONCEAL, state->pen.conceal); + 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); + } +} + +int vterm_color_is_equal(const VTermColor *a, const VTermColor *b) +{ + /* First make sure that the two colours are of the same type (RGB/Indexed) */ + if (a->type != b->type) { + return false; + } + + /* Depending on the type inspect the corresponding members */ + if (VTERM_COLOR_IS_INDEXED(a)) { + return a->indexed.idx == b->indexed.idx; + } + else if (VTERM_COLOR_IS_RGB(a)) { + return (a->rgb.red == b->rgb.red) + && (a->rgb.green == b->rgb.green) + && (a->rgb.blue == b->rgb.blue); + } + + return 0; +} + +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) +{ + /* Copy the given colors */ + state->default_fg = *default_fg; + state->default_bg = *default_bg; + + /* Make sure the correct type flags are set */ + state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK) + | VTERM_COLOR_DEFAULT_FG; + state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK) + | VTERM_COLOR_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_convert_color_to_rgb(const VTermState *state, VTermColor *col) +{ + if (VTERM_COLOR_IS_INDEXED(col)) { /* Convert indexed colors to RGB */ + lookup_colour_palette(state, col->indexed.idx, col); + } + col->type &= VTERM_COLOR_TYPE_MASK; /* Reset any metadata but the type */ +} + +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 + const VTermColor *fg = &state->pen.fg; + state->pen.bold = 1; + setpenattr_bool(state, VTERM_ATTR_BOLD, 1); + if(!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8 && state->bold_is_highbright) + set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (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 + state->pen.underline = VTERM_UNDERLINE_SINGLE; + if(CSI_ARG_HAS_MORE(args[argi])) { + argi++; + switch(CSI_ARG(args[argi])) { + case 0: + state->pen.underline = 0; + break; + case 1: + state->pen.underline = VTERM_UNDERLINE_SINGLE; + break; + case 2: + state->pen.underline = VTERM_UNDERLINE_DOUBLE; + break; + case 3: + state->pen.underline = VTERM_UNDERLINE_CURLY; + break; + } + } + setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline); + 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 8: // Conceal on + state->pen.conceal = 1; + setpenattr_bool(state, VTERM_ATTR_CONCEAL, 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 = VTERM_UNDERLINE_DOUBLE; + setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline); + 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 28: // Conceal off (Reveal) + state->pen.conceal = 0; + setpenattr_bool(state, VTERM_ATTR_CONCEAL, 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; + 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 + if(argcount - argi < 1) + return; + argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg); + setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg); + break; + + case 39: // Foreground colour default + 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; + set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value); + break; + + case 48: // Background colour alternative palette + if(argcount - argi < 1) + return; + argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg); + setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg); + break; + + case 49: // Default background + 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; + 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; + set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value); + break; + + default: + done = 0; + break; + } + + if(!done) + DEBUG_LOG("libvterm: Unhandled CSI SGR %ld\n", arg); + + while(CSI_ARG_HAS_MORE(args[argi++])); + } +} + +static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg) +{ + /* Do nothing if the given color is the default color */ + if (( fg && VTERM_COLOR_IS_DEFAULT_FG(col)) || + (!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) { + return argi; + } + + /* Decide whether to send an indexed color or an RGB color */ + if (VTERM_COLOR_IS_INDEXED(col)) { + const uint8_t idx = col->indexed.idx; + if (idx < 8) { + args[argi++] = (idx + (fg ? 30 : 40)); + } + else if (idx < 16) { + args[argi++] = (idx - 8 + (fg ? 90 : 100)); + } + else { + args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48); + args[argi++] = CSI_ARG_FLAG_MORE | 5; + args[argi++] = idx; + } + } + else if (VTERM_COLOR_IS_RGB(col)) { + args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48); + args[argi++] = CSI_ARG_FLAG_MORE | 2; + args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red; + args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green; + args[argi++] = col->rgb.blue; + } + return argi; +} + +INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount) +{ + int argi = 0; + + if(state->pen.bold) + args[argi++] = 1; + + if(state->pen.italic) + args[argi++] = 3; + + if(state->pen.underline == VTERM_UNDERLINE_SINGLE) + args[argi++] = 4; + if(state->pen.underline == VTERM_UNDERLINE_CURLY) + args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3; + + if(state->pen.blink) + args[argi++] = 5; + + if(state->pen.reverse) + args[argi++] = 7; + + if(state->pen.conceal) + args[argi++] = 8; + + if(state->pen.strike) + args[argi++] = 9; + + if(state->pen.font) + args[argi++] = 10 + state->pen.font; + + if(state->pen.underline == VTERM_UNDERLINE_DOUBLE) + args[argi++] = 21; + + argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true); + + argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false); + + 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_CONCEAL: + val->boolean = state->pen.conceal; + 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; + + case VTERM_N_ATTRS: + return 0; + } + + return 0; +} diff --git a/libvterm-0.2/src/pen.lo b/libvterm-0.2/src/pen.lo new file mode 100644 index 0000000..1e07d2b --- /dev/null +++ b/libvterm-0.2/src/pen.lo @@ -0,0 +1,12 @@ +# src/pen.lo - a libtool object file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/pen.o' + +# Name of the non-PIC object +non_pic_object='pen.o' + diff --git a/libvterm-0.2/src/pen.o b/libvterm-0.2/src/pen.o new file mode 100644 index 0000000000000000000000000000000000000000..8f4e124a8ec5381e9a692c57879e1787ebda3c14 GIT binary patch literal 14648 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 ZYl$!}>rX$=#?!C5MeDE85BDzh{~Ja7hIjw~ literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/rect.h b/libvterm-0.2/src/rect.h new file mode 100644 index 0000000..2114f24 --- /dev/null +++ b/libvterm-0.2/src/rect.h @@ -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; +} diff --git a/libvterm-0.2/src/screen.c b/libvterm-0.2/src/screen.c new file mode 100644 index 0000000..38b9b52 --- /dev/null +++ b/libvterm-0.2/src/screen.c @@ -0,0 +1,947 @@ +#include "vterm_internal.h" + +#include +#include + +#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 conceal : 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; + +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 inline void clearcell(const VTermScreen *screen, ScreenCell *cell) +{ + cell->chars[0] = 0; + cell->pen = screen->pen; +} + +static inline 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 *alloc_buffer(VTermScreen *screen, int rows, int cols) +{ + ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * rows * cols); + + for(int row = 0; row < rows; row++) { + for(int col = 0; col < cols; col++) { + clearcell(screen, &new_buffer[row * cols + col]); + } + } + + 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_LOG("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 = { + .start_row = 0, + .end_row = screen->rows, + .start_col = 0, + .end_col = screen->cols, + }; + + damagerect(screen, rect); +} + +static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) +{ + VTermScreen *screen = user; + ScreenCell *cell = getcell(screen, pos.row, pos.col); + + if(!cell) + return 0; + + int i; + 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(int col = 1; col < info->width; col++) + getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1; + + VTermRect rect = { + .start_row = pos.row, + .end_row = pos.row+1, + .start_col = pos.col, + .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 void sb_pushline_from_row(VTermScreen *screen, int row) +{ + VTermPos pos = { .row = 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); +} + +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[BUFIDX_PRIMARY]) { // not altscreen + for(int row = 0; row < src.start_row; row++) + sb_pushline_from_row(screen, row); + } + + int cols = src.end_col - src.start_col; + int downward = src.start_row - dest.start_row; + + int init_row, test_row, inc_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(int 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; + + for(int row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) { + const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row); + + for(int 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, 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_LOG("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_CONCEAL: + screen->pen.conceal = 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; + + case VTERM_N_ATTRS: + return 0; + } + + 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[BUFIDX_ALTSCREEN]) + return 0; + + screen->buffer = val->boolean ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY]; + /* 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 void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, bool active, VTermStateFields *statefields) +{ + int old_rows = screen->rows; + int old_cols = screen->cols; + + ScreenCell *old_buffer = screen->buffers[bufidx]; + ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols); + + int old_row = old_rows - 1; + int new_row = new_rows - 1; + + while(new_row >= 0 && old_row >= 0) { + int col; + for(col = 0; col < old_cols && col < new_cols; col++) + new_buffer[new_row * new_cols + col] = old_buffer[old_row * old_cols + col]; + for( ; col < new_cols; col++) + clearcell(screen, &new_buffer[new_row * new_cols + col]); + + old_row--; + new_row--; + + if(new_row < 0 && old_row >= 0 && + new_buffer[(new_rows - 1) * new_cols].chars[0] == 0 && + (!active || statefields->pos.row < (new_rows - 1))) { + int moverows = new_rows - 1; + memmove(&new_buffer[1 * new_cols], &new_buffer[0], moverows * new_cols * sizeof(ScreenCell)); + + new_row++; + } + } + + if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) { + /* Push spare lines to scrollback buffer */ + for(int row = 0; row <= old_row; row++) + sb_pushline_from_row(screen, row); + if(active) + statefields->pos.row -= (old_row + 1); + } + if(new_row >= 0 && bufidx == BUFIDX_PRIMARY && + screen->callbacks && screen->callbacks->sb_popline) { + /* Try to backfill rows by popping scrollback buffer */ + while(new_row >= 0) { + if(!(screen->callbacks->sb_popline(old_cols, screen->sb_buffer, screen->cbdata))) + break; + + VTermPos pos = { .row = new_row }; + for(pos.col = 0; pos.col < old_cols && pos.col < new_cols; pos.col += screen->sb_buffer[pos.col].width) { + VTermScreenCell *src = &screen->sb_buffer[pos.col]; + ScreenCell *dst = &new_buffer[pos.row * new_cols + pos.col]; + + for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) { + dst->chars[i] = src->chars[i]; + if(!src->chars[i]) + break; + } + + dst->pen.bold = src->attrs.bold; + dst->pen.underline = src->attrs.underline; + dst->pen.italic = src->attrs.italic; + dst->pen.blink = src->attrs.blink; + dst->pen.reverse = src->attrs.reverse ^ screen->global_reverse; + dst->pen.conceal = src->attrs.conceal; + dst->pen.strike = src->attrs.strike; + dst->pen.font = src->attrs.font; + + dst->pen.fg = src->fg; + dst->pen.bg = src->bg; + + if(src->width == 2 && pos.col < (new_cols-1)) + (dst + 1)->chars[0] = (uint32_t) -1; + } + for( ; pos.col < new_cols; pos.col++) + clearcell(screen, &new_buffer[pos.row * new_cols + pos.col]); + new_row--; + + if(active) + statefields->pos.row++; + } + } + if(new_row >= 0) { + /* Scroll new rows back up to the top and fill in blanks at the bottom */ + int moverows = new_rows - new_row - 1; + memmove(&new_buffer[0], &new_buffer[(new_row + 1) * new_cols], moverows * new_cols * sizeof(ScreenCell)); + + for(new_row = moverows; new_row < new_rows; new_row++) + for(int col = 0; col < new_cols; col++) + clearcell(screen, &new_buffer[new_row * new_cols + col]); + } + + vterm_allocator_free(screen->vt, old_buffer); + screen->buffers[bufidx] = new_buffer; + + return; + + /* REFLOW TODO: + * Handle delta. Probably needs to be a full cursorpos that we edit + */ +} + +static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *user) +{ + VTermScreen *screen = user; + + int altscreen_active = (screen->buffers[BUFIDX_ALTSCREEN] && screen->buffer == screen->buffers[BUFIDX_ALTSCREEN]); + + int old_cols = screen->cols; + + if(new_cols > old_cols) { + /* Ensure that ->sb_buffer is large enough for a new or and old row */ + if(screen->sb_buffer) + vterm_allocator_free(screen->vt, screen->sb_buffer); + + screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols); + } + + resize_buffer(screen, 0, new_rows, new_cols, !altscreen_active, fields); + if(screen->buffers[BUFIDX_ALTSCREEN]) + resize_buffer(screen, 1, new_rows, new_cols, altscreen_active, fields); + + screen->buffer = altscreen_active ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY]; + + screen->rows = new_rows; + screen->cols = new_cols; + + if(new_cols <= old_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); + } + + /* TODO: Maaaaybe we can optimise this if there's no reflow happening */ + damagescreen(screen); + + 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; + + if(newinfo->doublewidth != oldinfo->doublewidth || + newinfo->doubleheight != oldinfo->doubleheight) { + for(int col = 0; col < screen->cols; col++) { + ScreenCell *cell = getcell(screen, row, col); + cell->pen.dwl = newinfo->doublewidth; + cell->pen.dhl = newinfo->doubleheight; + } + + VTermRect rect = { + .start_row = row, + .end_row = row + 1, + .start_col = 0, + .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, + .erase = &erase, + .setpenattr = &setpenattr, + .settermprop = &settermprop, + .bell = &bell, + .resize = &resize, + .setlineinfo = &setlineinfo, +}; + +static VTermScreen *screen_new(VTerm *vt) +{ + VTermState *state = vterm_obtain_state(vt); + if(!state) + return NULL; + + VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen)); + int rows, cols; + + 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[BUFIDX_PRIMARY] = alloc_buffer(screen, rows, cols); + + screen->buffer = screen->buffers[BUFIDX_PRIMARY]; + + 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[BUFIDX_PRIMARY]); + if(screen->buffers[BUFIDX_ALTSCREEN]) + vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_ALTSCREEN]); + + 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; + +#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(int row = rect.start_row; row < rect.end_row; row++) { + for(int col = rect.start_col; col < rect.end_col; col++) { + ScreenCell *cell = getcell(screen, row, col); + + 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(int 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); + if(!intcell) + return 0; + + for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; 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.conceal = intcell->pen.conceal; + 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; +} + +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) +{ + if(vt->screen) + return vt->screen; + + VTermScreen *screen = screen_new(vt); + vt->screen = screen; + + return screen; +} + +void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen) +{ + if(!screen->buffers[BUFIDX_ALTSCREEN] && altscreen) { + int rows, cols; + vterm_get_size(screen->vt, &rows, &cols); + + screen->buffers[BUFIDX_ALTSCREEN] = alloc_buffer(screen, 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 VTermStateFallbacks *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_CONCEAL_MASK) && (a->pen.conceal != b->pen.conceal)) + 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_is_equal(&a->pen.fg, &b->pen.fg)) + return 1; + if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_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) +{ + 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; + + int col; + + 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; +} + +void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col) +{ + vterm_state_convert_color_to_rgb(screen->state, col); +} diff --git a/libvterm-0.2/src/screen.lo b/libvterm-0.2/src/screen.lo new file mode 100644 index 0000000..d6e94a8 --- /dev/null +++ b/libvterm-0.2/src/screen.lo @@ -0,0 +1,12 @@ +# src/screen.lo - a libtool object file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/screen.o' + +# Name of the non-PIC object +non_pic_object='screen.o' + diff --git a/libvterm-0.2/src/screen.o b/libvterm-0.2/src/screen.o new file mode 100644 index 0000000000000000000000000000000000000000..60bc0cd8a7672fea990e0b94601fde2c1e150c4d GIT binary patch literal 20240 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 BQT6}; literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/state.c b/libvterm-0.2/src/state.c new file mode 100644 index 0000000..d5f3a30 --- /dev/null +++ b/libvterm-0.2/src/state.c @@ -0,0 +1,2265 @@ +#include "vterm_internal.h" + +#include +#include + +#define strneq(a,b,n) (strncmp(a,b,n)==0) + +#if defined(DEBUG) && DEBUG > 1 +# define DEBUG_GLYPH_COMBINE +#endif + +/* Some convenient wrappers to make callback functions easier */ + +static void putglyph(VTermState *state, const uint32_t chars[], int width, VTermPos pos) +{ + VTermGlyphInfo info = { + .chars = chars, + .width = width, + .protected_cell = state->protected_cell, + .dwl = state->lineinfo[pos.row].doublewidth, + .dhl = state->lineinfo[pos.row].doubleheight, + }; + + if(state->callbacks && state->callbacks->putglyph) + if((*state->callbacks->putglyph)(&info, pos, state->cbdata)) + return; + + DEBUG_LOG("libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row); +} + +static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom) +{ + if(state->pos.col == oldpos->col && state->pos.row == oldpos->row) + return; + + if(cancel_phantom) + state->at_phantom = 0; + + if(state->callbacks && state->callbacks->movecursor) + if((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible, state->cbdata)) + return; +} + +static void erase(VTermState *state, VTermRect rect, int selective) +{ + if(rect.end_col == state->cols) { + /* If we're erasing the final cells of any lines, cancel the continuation + * marker on the subsequent line + */ + for(int row = rect.start_row + 1; row < rect.end_row + 1 && row < state->rows; row++) + state->lineinfo[row].continuation = 0; + } + + if(state->callbacks && state->callbacks->erase) + if((*state->callbacks->erase)(rect, selective, state->cbdata)) + return; +} + +static VTermState *vterm_state_new(VTerm *vt) +{ + VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState)); + + state->vt = vt; + + state->rows = vt->rows; + state->cols = vt->cols; + + state->mouse_col = 0; + state->mouse_row = 0; + state->mouse_buttons = 0; + + state->mouse_protocol = MOUSE_X10; + + state->callbacks = NULL; + state->cbdata = NULL; + + state->selection.callbacks = NULL; + state->selection.user = NULL; + state->selection.buffer = NULL; + + vterm_state_newpen(state); + + state->bold_is_highbright = 0; + + state->combine_chars_size = 16; + state->combine_chars = vterm_allocator_malloc(state->vt, state->combine_chars_size * sizeof(state->combine_chars[0])); + + state->tabstops = vterm_allocator_malloc(state->vt, (state->cols + 7) / 8); + + state->lineinfos[BUFIDX_PRIMARY] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo)); + /* TODO: Make an 'enable' function */ + state->lineinfos[BUFIDX_ALTSCREEN] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo)); + state->lineinfo = state->lineinfos[BUFIDX_PRIMARY]; + + state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u'); + if(*state->encoding_utf8.enc->init) + (*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data); + + return state; +} + +INTERNAL void vterm_state_free(VTermState *state) +{ + vterm_allocator_free(state->vt, state->tabstops); + vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_PRIMARY]); + if(state->lineinfos[BUFIDX_ALTSCREEN]) + vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_ALTSCREEN]); + vterm_allocator_free(state->vt, state->combine_chars); + vterm_allocator_free(state->vt, state); +} + +static void scroll(VTermState *state, VTermRect rect, int downward, int rightward) +{ + if(!downward && !rightward) + return; + + int rows = rect.end_row - rect.start_row; + if(downward > rows) + downward = rows; + else if(downward < -rows) + downward = -rows; + + int cols = rect.end_col - rect.start_col; + if(rightward > cols) + rightward = cols; + else if(rightward < -cols) + rightward = -cols; + + // Update lineinfo if full line + if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) { + int height = rect.end_row - rect.start_row - abs(downward); + + if(downward > 0) { + memmove(state->lineinfo + rect.start_row, + state->lineinfo + rect.start_row + downward, + height * sizeof(state->lineinfo[0])); + for(int row = rect.end_row - downward; row < rect.end_row; row++) + state->lineinfo[row] = (VTermLineInfo){ 0 }; + } + else { + memmove(state->lineinfo + rect.start_row - downward, + state->lineinfo + rect.start_row, + height * sizeof(state->lineinfo[0])); + for(int row = rect.start_row; row < rect.start_row - downward; row++) + state->lineinfo[row] = (VTermLineInfo){ 0 }; + } + } + + if(state->callbacks && state->callbacks->scrollrect) + if((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata)) + return; + + if(state->callbacks) + vterm_scroll_rect(rect, downward, rightward, + state->callbacks->moverect, state->callbacks->erase, state->cbdata); +} + +static void linefeed(VTermState *state) +{ + if(state->pos.row == SCROLLREGION_BOTTOM(state) - 1) { + VTermRect rect = { + .start_row = state->scrollregion_top, + .end_row = SCROLLREGION_BOTTOM(state), + .start_col = SCROLLREGION_LEFT(state), + .end_col = SCROLLREGION_RIGHT(state), + }; + + scroll(state, rect, 1, 0); + } + else if(state->pos.row < state->rows-1) + state->pos.row++; +} + +static void grow_combine_buffer(VTermState *state) +{ + size_t new_size = state->combine_chars_size * 2; + uint32_t *new_chars = vterm_allocator_malloc(state->vt, new_size * sizeof(new_chars[0])); + + memcpy(new_chars, state->combine_chars, state->combine_chars_size * sizeof(new_chars[0])); + + vterm_allocator_free(state->vt, state->combine_chars); + + state->combine_chars = new_chars; + state->combine_chars_size = new_size; +} + +static void set_col_tabstop(VTermState *state, int col) +{ + unsigned char mask = 1 << (col & 7); + state->tabstops[col >> 3] |= mask; +} + +static void clear_col_tabstop(VTermState *state, int col) +{ + unsigned char mask = 1 << (col & 7); + state->tabstops[col >> 3] &= ~mask; +} + +static int is_col_tabstop(VTermState *state, int col) +{ + unsigned char mask = 1 << (col & 7); + return state->tabstops[col >> 3] & mask; +} + +static int is_cursor_in_scrollregion(const VTermState *state) +{ + if(state->pos.row < state->scrollregion_top || + state->pos.row >= SCROLLREGION_BOTTOM(state)) + return 0; + if(state->pos.col < SCROLLREGION_LEFT(state) || + state->pos.col >= SCROLLREGION_RIGHT(state)) + return 0; + + return 1; +} + +static void tab(VTermState *state, int count, int direction) +{ + while(count > 0) { + if(direction > 0) { + if(state->pos.col >= THISROWWIDTH(state)-1) + return; + + state->pos.col++; + } + else if(direction < 0) { + if(state->pos.col < 1) + return; + + state->pos.col--; + } + + if(is_col_tabstop(state, state->pos.col)) + count--; + } +} + +#define NO_FORCE 0 +#define FORCE 1 + +#define DWL_OFF 0 +#define DWL_ON 1 + +#define DHL_OFF 0 +#define DHL_TOP 1 +#define DHL_BOTTOM 2 + +static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl) +{ + VTermLineInfo info = state->lineinfo[row]; + + if(dwl == DWL_OFF) + info.doublewidth = DWL_OFF; + else if(dwl == DWL_ON) + info.doublewidth = DWL_ON; + // else -1 to ignore + + if(dhl == DHL_OFF) + info.doubleheight = DHL_OFF; + else if(dhl == DHL_TOP) + info.doubleheight = DHL_TOP; + else if(dhl == DHL_BOTTOM) + info.doubleheight = DHL_BOTTOM; + + if((state->callbacks && + state->callbacks->setlineinfo && + (*state->callbacks->setlineinfo)(row, &info, state->lineinfo + row, state->cbdata)) + || force) + state->lineinfo[row] = info; +} + +static int on_text(const char bytes[], size_t len, void *user) +{ + VTermState *state = user; + + VTermPos oldpos = state->pos; + + // We'll have at most len codepoints + uint32_t codepoints[len]; + int npoints = 0; + size_t eaten = 0; + + VTermEncodingInstance *encoding = + state->gsingle_set ? &state->encoding[state->gsingle_set] : + !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set] : + state->vt->mode.utf8 ? &state->encoding_utf8 : + &state->encoding[state->gr_set]; + + (*encoding->enc->decode)(encoding->enc, encoding->data, + codepoints, &npoints, state->gsingle_set ? 1 : len, + bytes, &eaten, len); + + /* There's a chance an encoding (e.g. UTF-8) hasn't found enough bytes yet + * for even a single codepoint + */ + if(!npoints) + return eaten; + + if(state->gsingle_set && npoints) + state->gsingle_set = 0; + + int i = 0; + + /* This is a combining char. that needs to be merged with the previous + * glyph output */ + if(vterm_unicode_is_combining(codepoints[i])) { + /* See if the cursor has moved since */ + if(state->pos.row == state->combine_pos.row && state->pos.col == state->combine_pos.col + state->combine_width) { +#ifdef DEBUG_GLYPH_COMBINE + int printpos; + printf("DEBUG: COMBINING SPLIT GLYPH of chars {"); + for(printpos = 0; state->combine_chars[printpos]; printpos++) + printf("U+%04x ", state->combine_chars[printpos]); + printf("} + {"); +#endif + + /* Find where we need to append these combining chars */ + int saved_i = 0; + while(state->combine_chars[saved_i]) + saved_i++; + + /* Add extra ones */ + while(i < npoints && vterm_unicode_is_combining(codepoints[i])) { + if(saved_i >= state->combine_chars_size) + grow_combine_buffer(state); + state->combine_chars[saved_i++] = codepoints[i++]; + } + if(saved_i >= state->combine_chars_size) + grow_combine_buffer(state); + state->combine_chars[saved_i] = 0; + +#ifdef DEBUG_GLYPH_COMBINE + for(; state->combine_chars[printpos]; printpos++) + printf("U+%04x ", state->combine_chars[printpos]); + printf("}\n"); +#endif + + /* Now render it */ + putglyph(state, state->combine_chars, state->combine_width, state->combine_pos); + } + else { + DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n"); + } + } + + for(; i < npoints; i++) { + // Try to find combining characters following this + int glyph_starts = i; + int glyph_ends; + for(glyph_ends = i + 1; glyph_ends < npoints; glyph_ends++) + if(!vterm_unicode_is_combining(codepoints[glyph_ends])) + break; + + int width = 0; + + uint32_t chars[glyph_ends - glyph_starts + 1]; + + for( ; i < glyph_ends; i++) { + chars[i - glyph_starts] = codepoints[i]; + int this_width = vterm_unicode_width(codepoints[i]); +#ifdef DEBUG + if(this_width < 0) { + fprintf(stderr, "Text with negative-width codepoint U+%04x\n", codepoints[i]); + abort(); + } +#endif + width += this_width; + } + + chars[glyph_ends - glyph_starts] = 0; + i--; + +#ifdef DEBUG_GLYPH_COMBINE + int printpos; + printf("DEBUG: COMBINED GLYPH of %d chars {", glyph_ends - glyph_starts); + for(printpos = 0; printpos < glyph_ends - glyph_starts; printpos++) + printf("U+%04x ", chars[printpos]); + printf("}, onscreen width %d\n", width); +#endif + + if(state->at_phantom || state->pos.col + width > THISROWWIDTH(state)) { + linefeed(state); + state->pos.col = 0; + state->at_phantom = 0; + state->lineinfo[state->pos.row].continuation = 1; + } + + if(state->mode.insert) { + /* TODO: This will be a little inefficient for large bodies of text, as + * it'll have to 'ICH' effectively before every glyph. We should scan + * ahead and ICH as many times as required + */ + VTermRect rect = { + .start_row = state->pos.row, + .end_row = state->pos.row + 1, + .start_col = state->pos.col, + .end_col = THISROWWIDTH(state), + }; + scroll(state, rect, 0, -1); + } + + putglyph(state, chars, width, state->pos); + + if(i == npoints - 1) { + /* End of the buffer. Save the chars in case we have to combine with + * more on the next call */ + int save_i; + for(save_i = 0; chars[save_i]; save_i++) { + if(save_i >= state->combine_chars_size) + grow_combine_buffer(state); + state->combine_chars[save_i] = chars[save_i]; + } + if(save_i >= state->combine_chars_size) + grow_combine_buffer(state); + state->combine_chars[save_i] = 0; + state->combine_width = width; + state->combine_pos = state->pos; + } + + if(state->pos.col + width >= THISROWWIDTH(state)) { + if(state->mode.autowrap) + state->at_phantom = 1; + } + else { + state->pos.col += width; + } + } + + updatecursor(state, &oldpos, 0); + +#ifdef DEBUG + if(state->pos.row < 0 || state->pos.row >= state->rows || + state->pos.col < 0 || state->pos.col >= state->cols) { + fprintf(stderr, "Position out of bounds after text: (%d,%d)\n", + state->pos.row, state->pos.col); + abort(); + } +#endif + + return eaten; +} + +static int on_control(unsigned char control, void *user) +{ + VTermState *state = user; + + VTermPos oldpos = state->pos; + + switch(control) { + case 0x07: // BEL - ECMA-48 8.3.3 + if(state->callbacks && state->callbacks->bell) + (*state->callbacks->bell)(state->cbdata); + break; + + case 0x08: // BS - ECMA-48 8.3.5 + if(state->pos.col > 0) + state->pos.col--; + break; + + case 0x09: // HT - ECMA-48 8.3.60 + tab(state, 1, +1); + break; + + case 0x0a: // LF - ECMA-48 8.3.74 + case 0x0b: // VT + case 0x0c: // FF + linefeed(state); + if(state->mode.newline) + state->pos.col = 0; + break; + + case 0x0d: // CR - ECMA-48 8.3.15 + state->pos.col = 0; + break; + + case 0x0e: // LS1 - ECMA-48 8.3.76 + state->gl_set = 1; + break; + + case 0x0f: // LS0 - ECMA-48 8.3.75 + state->gl_set = 0; + break; + + case 0x84: // IND - DEPRECATED but implemented for completeness + linefeed(state); + break; + + case 0x85: // NEL - ECMA-48 8.3.86 + linefeed(state); + state->pos.col = 0; + break; + + case 0x88: // HTS - ECMA-48 8.3.62 + set_col_tabstop(state, state->pos.col); + break; + + case 0x8d: // RI - ECMA-48 8.3.104 + if(state->pos.row == state->scrollregion_top) { + VTermRect rect = { + .start_row = state->scrollregion_top, + .end_row = SCROLLREGION_BOTTOM(state), + .start_col = SCROLLREGION_LEFT(state), + .end_col = SCROLLREGION_RIGHT(state), + }; + + scroll(state, rect, -1, 0); + } + else if(state->pos.row > 0) + state->pos.row--; + break; + + case 0x8e: // SS2 - ECMA-48 8.3.141 + state->gsingle_set = 2; + break; + + case 0x8f: // SS3 - ECMA-48 8.3.142 + state->gsingle_set = 3; + break; + + default: + if(state->fallbacks && state->fallbacks->control) + if((*state->fallbacks->control)(control, state->fbdata)) + return 1; + + return 0; + } + + updatecursor(state, &oldpos, 1); + +#ifdef DEBUG + if(state->pos.row < 0 || state->pos.row >= state->rows || + state->pos.col < 0 || state->pos.col >= state->cols) { + fprintf(stderr, "Position out of bounds after Ctrl %02x: (%d,%d)\n", + control, state->pos.row, state->pos.col); + abort(); + } +#endif + + return 1; +} + +static int settermprop_bool(VTermState *state, VTermProp prop, int v) +{ + VTermValue val = { .boolean = v }; + return vterm_state_set_termprop(state, prop, &val); +} + +static int settermprop_int(VTermState *state, VTermProp prop, int v) +{ + VTermValue val = { .number = v }; + return vterm_state_set_termprop(state, prop, &val); +} + +static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag) +{ + VTermValue val = { .string = frag }; + return vterm_state_set_termprop(state, prop, &val); +} + +static void savecursor(VTermState *state, int save) +{ + if(save) { + state->saved.pos = state->pos; + state->saved.mode.cursor_visible = state->mode.cursor_visible; + state->saved.mode.cursor_blink = state->mode.cursor_blink; + state->saved.mode.cursor_shape = state->mode.cursor_shape; + + vterm_state_savepen(state, 1); + } + else { + VTermPos oldpos = state->pos; + + state->pos = state->saved.pos; + + settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, state->saved.mode.cursor_visible); + settermprop_bool(state, VTERM_PROP_CURSORBLINK, state->saved.mode.cursor_blink); + settermprop_int (state, VTERM_PROP_CURSORSHAPE, state->saved.mode.cursor_shape); + + vterm_state_savepen(state, 0); + + updatecursor(state, &oldpos, 1); + } +} + +static int on_escape(const char *bytes, size_t len, void *user) +{ + VTermState *state = user; + + /* Easier to decode this from the first byte, even though the final + * byte terminates it + */ + switch(bytes[0]) { + case ' ': + if(len != 2) + return 0; + + switch(bytes[1]) { + case 'F': // S7C1T + state->vt->mode.ctrl8bit = 0; + break; + + case 'G': // S8C1T + state->vt->mode.ctrl8bit = 1; + break; + + default: + return 0; + } + return 2; + + case '#': + if(len != 2) + return 0; + + switch(bytes[1]) { + case '3': // DECDHL top + if(state->mode.leftrightmargin) + break; + set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_TOP); + break; + + case '4': // DECDHL bottom + if(state->mode.leftrightmargin) + break; + set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_BOTTOM); + break; + + case '5': // DECSWL + if(state->mode.leftrightmargin) + break; + set_lineinfo(state, state->pos.row, NO_FORCE, DWL_OFF, DHL_OFF); + break; + + case '6': // DECDWL + if(state->mode.leftrightmargin) + break; + set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_OFF); + break; + + case '8': // DECALN + { + VTermPos pos; + uint32_t E[] = { 'E', 0 }; + for(pos.row = 0; pos.row < state->rows; pos.row++) + for(pos.col = 0; pos.col < ROWWIDTH(state, pos.row); pos.col++) + putglyph(state, E, 1, pos); + break; + } + + default: + return 0; + } + return 2; + + case '(': case ')': case '*': case '+': // SCS + if(len != 2) + return 0; + + { + int setnum = bytes[0] - 0x28; + VTermEncoding *newenc = vterm_lookup_encoding(ENC_SINGLE_94, bytes[1]); + + if(newenc) { + state->encoding[setnum].enc = newenc; + + if(newenc->init) + (*newenc->init)(newenc, state->encoding[setnum].data); + } + } + + return 2; + + case '7': // DECSC + savecursor(state, 1); + return 1; + + case '8': // DECRC + savecursor(state, 0); + return 1; + + case '<': // Ignored by VT100. Used in VT52 mode to switch up to VT100 + return 1; + + case '=': // DECKPAM + state->mode.keypad = 1; + return 1; + + case '>': // DECKPNM + state->mode.keypad = 0; + return 1; + + case 'c': // RIS - ECMA-48 8.3.105 + { + VTermPos oldpos = state->pos; + vterm_state_reset(state, 1); + if(state->callbacks && state->callbacks->movecursor) + (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible, state->cbdata); + return 1; + } + + case 'n': // LS2 - ECMA-48 8.3.78 + state->gl_set = 2; + return 1; + + case 'o': // LS3 - ECMA-48 8.3.80 + state->gl_set = 3; + return 1; + + case '~': // LS1R - ECMA-48 8.3.77 + state->gr_set = 1; + return 1; + + case '}': // LS2R - ECMA-48 8.3.79 + state->gr_set = 2; + return 1; + + case '|': // LS3R - ECMA-48 8.3.81 + state->gr_set = 3; + return 1; + + default: + return 0; + } +} + +static void set_mode(VTermState *state, int num, int val) +{ + switch(num) { + case 4: // IRM - ECMA-48 7.2.10 + state->mode.insert = val; + break; + + case 20: // LNM - ANSI X3.4-1977 + state->mode.newline = val; + break; + + default: + DEBUG_LOG("libvterm: Unknown mode %d\n", num); + return; + } +} + +static void set_dec_mode(VTermState *state, int num, int val) +{ + switch(num) { + case 1: + state->mode.cursor = val; + break; + + case 5: // DECSCNM - screen mode + settermprop_bool(state, VTERM_PROP_REVERSE, val); + break; + + case 6: // DECOM - origin mode + { + VTermPos oldpos = state->pos; + state->mode.origin = val; + state->pos.row = state->mode.origin ? state->scrollregion_top : 0; + state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0; + updatecursor(state, &oldpos, 1); + } + break; + + case 7: + state->mode.autowrap = val; + break; + + case 12: + settermprop_bool(state, VTERM_PROP_CURSORBLINK, val); + break; + + case 25: + settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, val); + break; + + case 69: // DECVSSM - vertical split screen mode + // DECLRMM - left/right margin mode + state->mode.leftrightmargin = val; + if(val) { + // Setting DECVSSM must clear doublewidth/doubleheight state of every line + for(int row = 0; row < state->rows; row++) + set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF); + } + + break; + + case 1000: + case 1002: + case 1003: + settermprop_int(state, VTERM_PROP_MOUSE, + !val ? VTERM_PROP_MOUSE_NONE : + (num == 1000) ? VTERM_PROP_MOUSE_CLICK : + (num == 1002) ? VTERM_PROP_MOUSE_DRAG : + VTERM_PROP_MOUSE_MOVE); + break; + + case 1004: + state->mode.report_focus = val; + break; + + case 1005: + state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10; + break; + + case 1006: + state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10; + break; + + case 1015: + state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10; + break; + + case 1047: + settermprop_bool(state, VTERM_PROP_ALTSCREEN, val); + break; + + case 1048: + savecursor(state, val); + break; + + case 1049: + settermprop_bool(state, VTERM_PROP_ALTSCREEN, val); + savecursor(state, val); + break; + + case 2004: + state->mode.bracketpaste = val; + break; + + default: + DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num); + return; + } +} + +static void request_dec_mode(VTermState *state, int num) +{ + int reply; + + switch(num) { + case 1: + reply = state->mode.cursor; + break; + + case 5: + reply = state->mode.screen; + break; + + case 6: + reply = state->mode.origin; + break; + + case 7: + reply = state->mode.autowrap; + break; + + case 12: + reply = state->mode.cursor_blink; + break; + + case 25: + reply = state->mode.cursor_visible; + break; + + case 69: + reply = state->mode.leftrightmargin; + break; + + case 1000: + reply = state->mouse_flags == MOUSE_WANT_CLICK; + break; + + case 1002: + reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_DRAG); + break; + + case 1003: + reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE); + break; + + case 1004: + reply = state->mode.report_focus; + break; + + case 1005: + reply = state->mouse_protocol == MOUSE_UTF8; + break; + + case 1006: + reply = state->mouse_protocol == MOUSE_SGR; + break; + + case 1015: + reply = state->mouse_protocol == MOUSE_RXVT; + break; + + case 1047: + reply = state->mode.alt_screen; + break; + + case 2004: + reply = state->mode.bracketpaste; + break; + + default: + vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0); + return; + } + + vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2); +} + +static int on_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user) +{ + VTermState *state = user; + int leader_byte = 0; + int intermed_byte = 0; + int cancel_phantom = 1; + + if(leader && leader[0]) { + if(leader[1]) // longer than 1 char + return 0; + + switch(leader[0]) { + case '?': + case '>': + leader_byte = leader[0]; + break; + default: + return 0; + } + } + + if(intermed && intermed[0]) { + if(intermed[1]) // longer than 1 char + return 0; + + switch(intermed[0]) { + case ' ': + case '"': + case '$': + case '\'': + intermed_byte = intermed[0]; + break; + default: + return 0; + } + } + + VTermPos oldpos = state->pos; + + // Some temporaries for later code + int count, val; + int row, col; + VTermRect rect; + int selective; + +#define LBOUND(v,min) if((v) < (min)) (v) = (min) +#define UBOUND(v,max) if((v) > (max)) (v) = (max) + +#define LEADER(l,b) ((l << 8) | b) +#define INTERMED(i,b) ((i << 16) | b) + + switch(intermed_byte << 16 | leader_byte << 8 | command) { + case 0x40: // ICH - ECMA-48 8.3.64 + count = CSI_ARG_COUNT(args[0]); + + if(!is_cursor_in_scrollregion(state)) + break; + + rect.start_row = state->pos.row; + rect.end_row = state->pos.row + 1; + rect.start_col = state->pos.col; + if(state->mode.leftrightmargin) + rect.end_col = SCROLLREGION_RIGHT(state); + else + rect.end_col = THISROWWIDTH(state); + + scroll(state, rect, 0, -count); + + break; + + case 0x41: // CUU - ECMA-48 8.3.22 + count = CSI_ARG_COUNT(args[0]); + state->pos.row -= count; + state->at_phantom = 0; + break; + + case 0x42: // CUD - ECMA-48 8.3.19 + count = CSI_ARG_COUNT(args[0]); + state->pos.row += count; + state->at_phantom = 0; + break; + + case 0x43: // CUF - ECMA-48 8.3.20 + count = CSI_ARG_COUNT(args[0]); + state->pos.col += count; + state->at_phantom = 0; + break; + + case 0x44: // CUB - ECMA-48 8.3.18 + count = CSI_ARG_COUNT(args[0]); + state->pos.col -= count; + state->at_phantom = 0; + break; + + case 0x45: // CNL - ECMA-48 8.3.12 + count = CSI_ARG_COUNT(args[0]); + state->pos.col = 0; + state->pos.row += count; + state->at_phantom = 0; + break; + + case 0x46: // CPL - ECMA-48 8.3.13 + count = CSI_ARG_COUNT(args[0]); + state->pos.col = 0; + state->pos.row -= count; + state->at_phantom = 0; + break; + + case 0x47: // CHA - ECMA-48 8.3.9 + val = CSI_ARG_OR(args[0], 1); + state->pos.col = val-1; + state->at_phantom = 0; + break; + + case 0x48: // CUP - ECMA-48 8.3.21 + row = CSI_ARG_OR(args[0], 1); + col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]); + // zero-based + state->pos.row = row-1; + state->pos.col = col-1; + if(state->mode.origin) { + state->pos.row += state->scrollregion_top; + state->pos.col += SCROLLREGION_LEFT(state); + } + state->at_phantom = 0; + break; + + case 0x49: // CHT - ECMA-48 8.3.10 + count = CSI_ARG_COUNT(args[0]); + tab(state, count, +1); + break; + + case 0x4a: // ED - ECMA-48 8.3.39 + case LEADER('?', 0x4a): // DECSED - Selective Erase in Display + selective = (leader_byte == '?'); + switch(CSI_ARG(args[0])) { + case CSI_ARG_MISSING: + case 0: + rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1; + rect.start_col = state->pos.col; rect.end_col = state->cols; + if(rect.end_col > rect.start_col) + erase(state, rect, selective); + + rect.start_row = state->pos.row + 1; rect.end_row = state->rows; + rect.start_col = 0; + for(int row = rect.start_row; row < rect.end_row; row++) + set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF); + if(rect.end_row > rect.start_row) + erase(state, rect, selective); + break; + + case 1: + rect.start_row = 0; rect.end_row = state->pos.row; + rect.start_col = 0; rect.end_col = state->cols; + for(int row = rect.start_row; row < rect.end_row; row++) + set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF); + if(rect.end_col > rect.start_col) + erase(state, rect, selective); + + rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1; + rect.end_col = state->pos.col + 1; + if(rect.end_row > rect.start_row) + erase(state, rect, selective); + break; + + case 2: + rect.start_row = 0; rect.end_row = state->rows; + rect.start_col = 0; rect.end_col = state->cols; + for(int row = rect.start_row; row < rect.end_row; row++) + set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF); + erase(state, rect, selective); + break; + } + break; + + case 0x4b: // EL - ECMA-48 8.3.41 + case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line + selective = (leader_byte == '?'); + rect.start_row = state->pos.row; + rect.end_row = state->pos.row + 1; + + switch(CSI_ARG(args[0])) { + case CSI_ARG_MISSING: + case 0: + rect.start_col = state->pos.col; rect.end_col = THISROWWIDTH(state); break; + case 1: + rect.start_col = 0; rect.end_col = state->pos.col + 1; break; + case 2: + rect.start_col = 0; rect.end_col = THISROWWIDTH(state); break; + default: + return 0; + } + + if(rect.end_col > rect.start_col) + erase(state, rect, selective); + + break; + + case 0x4c: // IL - ECMA-48 8.3.67 + count = CSI_ARG_COUNT(args[0]); + + if(!is_cursor_in_scrollregion(state)) + break; + + rect.start_row = state->pos.row; + rect.end_row = SCROLLREGION_BOTTOM(state); + rect.start_col = SCROLLREGION_LEFT(state); + rect.end_col = SCROLLREGION_RIGHT(state); + + scroll(state, rect, -count, 0); + + break; + + case 0x4d: // DL - ECMA-48 8.3.32 + count = CSI_ARG_COUNT(args[0]); + + if(!is_cursor_in_scrollregion(state)) + break; + + rect.start_row = state->pos.row; + rect.end_row = SCROLLREGION_BOTTOM(state); + rect.start_col = SCROLLREGION_LEFT(state); + rect.end_col = SCROLLREGION_RIGHT(state); + + scroll(state, rect, count, 0); + + break; + + case 0x50: // DCH - ECMA-48 8.3.26 + count = CSI_ARG_COUNT(args[0]); + + if(!is_cursor_in_scrollregion(state)) + break; + + rect.start_row = state->pos.row; + rect.end_row = state->pos.row + 1; + rect.start_col = state->pos.col; + if(state->mode.leftrightmargin) + rect.end_col = SCROLLREGION_RIGHT(state); + else + rect.end_col = THISROWWIDTH(state); + + scroll(state, rect, 0, count); + + break; + + case 0x53: // SU - ECMA-48 8.3.147 + count = CSI_ARG_COUNT(args[0]); + + rect.start_row = state->scrollregion_top; + rect.end_row = SCROLLREGION_BOTTOM(state); + rect.start_col = SCROLLREGION_LEFT(state); + rect.end_col = SCROLLREGION_RIGHT(state); + + scroll(state, rect, count, 0); + + break; + + case 0x54: // SD - ECMA-48 8.3.113 + count = CSI_ARG_COUNT(args[0]); + + rect.start_row = state->scrollregion_top; + rect.end_row = SCROLLREGION_BOTTOM(state); + rect.start_col = SCROLLREGION_LEFT(state); + rect.end_col = SCROLLREGION_RIGHT(state); + + scroll(state, rect, -count, 0); + + break; + + case 0x58: // ECH - ECMA-48 8.3.38 + count = CSI_ARG_COUNT(args[0]); + + rect.start_row = state->pos.row; + rect.end_row = state->pos.row + 1; + rect.start_col = state->pos.col; + rect.end_col = state->pos.col + count; + UBOUND(rect.end_col, THISROWWIDTH(state)); + + erase(state, rect, 0); + break; + + case 0x5a: // CBT - ECMA-48 8.3.7 + count = CSI_ARG_COUNT(args[0]); + tab(state, count, -1); + break; + + case 0x60: // HPA - ECMA-48 8.3.57 + col = CSI_ARG_OR(args[0], 1); + state->pos.col = col-1; + state->at_phantom = 0; + break; + + case 0x61: // HPR - ECMA-48 8.3.59 + count = CSI_ARG_COUNT(args[0]); + state->pos.col += count; + state->at_phantom = 0; + break; + + case 0x62: { // REP - ECMA-48 8.3.103 + const int row_width = THISROWWIDTH(state); + count = CSI_ARG_COUNT(args[0]); + col = state->pos.col + count; + UBOUND(col, row_width); + while (state->pos.col < col) { + putglyph(state, state->combine_chars, state->combine_width, state->pos); + state->pos.col += state->combine_width; + } + if (state->pos.col + state->combine_width >= row_width) { + if (state->mode.autowrap) { + state->at_phantom = 1; + cancel_phantom = 0; + } + } + break; + } + + case 0x63: // DA - ECMA-48 8.3.24 + val = CSI_ARG_OR(args[0], 0); + if(val == 0) + // DEC VT100 response + vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?1;2c"); + break; + + case LEADER('>', 0x63): // DEC secondary Device Attributes + vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0); + break; + + case 0x64: // VPA - ECMA-48 8.3.158 + row = CSI_ARG_OR(args[0], 1); + state->pos.row = row-1; + if(state->mode.origin) + state->pos.row += state->scrollregion_top; + state->at_phantom = 0; + break; + + case 0x65: // VPR - ECMA-48 8.3.160 + count = CSI_ARG_COUNT(args[0]); + state->pos.row += count; + state->at_phantom = 0; + break; + + case 0x66: // HVP - ECMA-48 8.3.63 + row = CSI_ARG_OR(args[0], 1); + col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]); + // zero-based + state->pos.row = row-1; + state->pos.col = col-1; + if(state->mode.origin) { + state->pos.row += state->scrollregion_top; + state->pos.col += SCROLLREGION_LEFT(state); + } + state->at_phantom = 0; + break; + + case 0x67: // TBC - ECMA-48 8.3.154 + val = CSI_ARG_OR(args[0], 0); + + switch(val) { + case 0: + clear_col_tabstop(state, state->pos.col); + break; + case 3: + case 5: + for(col = 0; col < state->cols; col++) + clear_col_tabstop(state, col); + break; + case 1: + case 2: + case 4: + break; + /* TODO: 1, 2 and 4 aren't meaningful yet without line tab stops */ + default: + return 0; + } + break; + + case 0x68: // SM - ECMA-48 8.3.125 + if(!CSI_ARG_IS_MISSING(args[0])) + set_mode(state, CSI_ARG(args[0]), 1); + break; + + case LEADER('?', 0x68): // DEC private mode set + if(!CSI_ARG_IS_MISSING(args[0])) + set_dec_mode(state, CSI_ARG(args[0]), 1); + break; + + case 0x6a: // HPB - ECMA-48 8.3.58 + count = CSI_ARG_COUNT(args[0]); + state->pos.col -= count; + state->at_phantom = 0; + break; + + case 0x6b: // VPB - ECMA-48 8.3.159 + count = CSI_ARG_COUNT(args[0]); + state->pos.row -= count; + state->at_phantom = 0; + break; + + case 0x6c: // RM - ECMA-48 8.3.106 + if(!CSI_ARG_IS_MISSING(args[0])) + set_mode(state, CSI_ARG(args[0]), 0); + break; + + case LEADER('?', 0x6c): // DEC private mode reset + if(!CSI_ARG_IS_MISSING(args[0])) + set_dec_mode(state, CSI_ARG(args[0]), 0); + break; + + case 0x6d: // SGR - ECMA-48 8.3.117 + vterm_state_setpen(state, args, argcount); + break; + + case 0x6e: // DSR - ECMA-48 8.3.35 + case LEADER('?', 0x6e): // DECDSR + val = CSI_ARG_OR(args[0], 0); + + { + char *qmark = (leader_byte == '?') ? "?" : ""; + + switch(val) { + case 0: case 1: case 2: case 3: case 4: + // ignore - these are replies + break; + case 5: + vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s0n", qmark); + break; + case 6: // CPR - cursor position report + vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1, state->pos.col + 1); + break; + } + } + break; + + + case LEADER('!', 0x70): // DECSTR - DEC soft terminal reset + vterm_state_reset(state, 0); + break; + + case LEADER('?', INTERMED('$', 0x70)): + request_dec_mode(state, CSI_ARG(args[0])); + break; + + case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape + val = CSI_ARG_OR(args[0], 1); + + switch(val) { + case 0: case 1: + settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); + settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK); + break; + case 2: + settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0); + settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK); + break; + case 3: + settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); + settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE); + break; + case 4: + settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0); + settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE); + break; + case 5: + settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); + settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT); + break; + case 6: + settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0); + settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT); + break; + } + + break; + + case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute + val = CSI_ARG_OR(args[0], 0); + + switch(val) { + case 0: case 2: + state->protected_cell = 0; + break; + case 1: + state->protected_cell = 1; + break; + } + + break; + + case 0x72: // DECSTBM - DEC custom + state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1; + state->scrollregion_bottom = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]); + LBOUND(state->scrollregion_top, 0); + UBOUND(state->scrollregion_top, state->rows); + LBOUND(state->scrollregion_bottom, -1); + if(state->scrollregion_top == 0 && state->scrollregion_bottom == state->rows) + state->scrollregion_bottom = -1; + else + UBOUND(state->scrollregion_bottom, state->rows); + + if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) { + // Invalid + state->scrollregion_top = 0; + state->scrollregion_bottom = -1; + } + + // Setting the scrolling region restores the cursor to the home position + state->pos.row = 0; + state->pos.col = 0; + if(state->mode.origin) { + state->pos.row += state->scrollregion_top; + state->pos.col += SCROLLREGION_LEFT(state); + } + + break; + + case 0x73: // DECSLRM - DEC custom + // Always allow setting these margins, just they won't take effect without DECVSSM + state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1; + state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]); + LBOUND(state->scrollregion_left, 0); + UBOUND(state->scrollregion_left, state->cols); + LBOUND(state->scrollregion_right, -1); + if(state->scrollregion_left == 0 && state->scrollregion_right == state->cols) + state->scrollregion_right = -1; + else + UBOUND(state->scrollregion_right, state->cols); + + if(state->scrollregion_right > -1 && + state->scrollregion_right <= state->scrollregion_left) { + // Invalid + state->scrollregion_left = 0; + state->scrollregion_right = -1; + } + + // Setting the scrolling region restores the cursor to the home position + state->pos.row = 0; + state->pos.col = 0; + if(state->mode.origin) { + state->pos.row += state->scrollregion_top; + state->pos.col += SCROLLREGION_LEFT(state); + } + + break; + + case INTERMED('\'', 0x7D): // DECIC + count = CSI_ARG_COUNT(args[0]); + + if(!is_cursor_in_scrollregion(state)) + break; + + rect.start_row = state->scrollregion_top; + rect.end_row = SCROLLREGION_BOTTOM(state); + rect.start_col = state->pos.col; + rect.end_col = SCROLLREGION_RIGHT(state); + + scroll(state, rect, 0, -count); + + break; + + case INTERMED('\'', 0x7E): // DECDC + count = CSI_ARG_COUNT(args[0]); + + if(!is_cursor_in_scrollregion(state)) + break; + + rect.start_row = state->scrollregion_top; + rect.end_row = SCROLLREGION_BOTTOM(state); + rect.start_col = state->pos.col; + rect.end_col = SCROLLREGION_RIGHT(state); + + scroll(state, rect, 0, count); + + break; + + default: + if(state->fallbacks && state->fallbacks->csi) + if((*state->fallbacks->csi)(leader, args, argcount, intermed, command, state->fbdata)) + return 1; + + return 0; + } + + if(state->mode.origin) { + LBOUND(state->pos.row, state->scrollregion_top); + UBOUND(state->pos.row, SCROLLREGION_BOTTOM(state)-1); + LBOUND(state->pos.col, SCROLLREGION_LEFT(state)); + UBOUND(state->pos.col, SCROLLREGION_RIGHT(state)-1); + } + else { + LBOUND(state->pos.row, 0); + UBOUND(state->pos.row, state->rows-1); + LBOUND(state->pos.col, 0); + UBOUND(state->pos.col, THISROWWIDTH(state)-1); + } + + updatecursor(state, &oldpos, cancel_phantom); + +#ifdef DEBUG + if(state->pos.row < 0 || state->pos.row >= state->rows || + state->pos.col < 0 || state->pos.col >= state->cols) { + fprintf(stderr, "Position out of bounds after CSI %c: (%d,%d)\n", + command, state->pos.row, state->pos.col); + abort(); + } + + if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) { + fprintf(stderr, "Scroll region height out of bounds after CSI %c: %d <= %d\n", + command, SCROLLREGION_BOTTOM(state), state->scrollregion_top); + abort(); + } + + if(SCROLLREGION_RIGHT(state) <= SCROLLREGION_LEFT(state)) { + fprintf(stderr, "Scroll region width out of bounds after CSI %c: %d <= %d\n", + command, SCROLLREGION_RIGHT(state), SCROLLREGION_LEFT(state)); + abort(); + } +#endif + + return 1; +} + +static char base64_one(uint8_t b) +{ + if(b < 26) + return 'A' + b; + else if(b < 52) + return 'a' + b - 26; + else if(b < 62) + return '0' + b - 52; + else if(b == 62) + return '+'; + else if(b == 63) + return '/'; + return 0; +} + +static uint8_t unbase64one(char c) +{ + if(c >= 'A' && c <= 'Z') + return c - 'A'; + else if(c >= 'a' && c <= 'z') + return c - 'a' + 26; + else if(c >= '0' && c <= '9') + return c - '0' + 52; + else if(c == '+') + return 62; + else if(c == '/') + return 63; + + return 0xFF; +} + +static void osc_selection(VTermState *state, VTermStringFragment frag) +{ + if(frag.initial) { + state->tmp.selection.mask = 0; + state->tmp.selection.state = SELECTION_INITIAL; + } + + while(!state->tmp.selection.state && frag.len) { + /* Parse selection parameter */ + switch(frag.str[0]) { + case 'c': + state->tmp.selection.mask |= VTERM_SELECTION_CLIPBOARD; + break; + case 'p': + state->tmp.selection.mask |= VTERM_SELECTION_PRIMARY; + break; + case 'q': + state->tmp.selection.mask |= VTERM_SELECTION_SECONDARY; + break; + case 's': + state->tmp.selection.mask |= VTERM_SELECTION_SELECT; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + state->tmp.selection.mask |= (VTERM_SELECTION_CUT0 << (frag.str[0] - '0')); + break; + + case ';': + state->tmp.selection.state = SELECTION_SELECTED; + if(!state->tmp.selection.mask) + state->tmp.selection.mask = VTERM_SELECTION_SELECT|VTERM_SELECTION_CUT0; + break; + } + + frag.str++; + frag.len--; + } + + if(!frag.len) + return; + + if(state->tmp.selection.state == SELECTION_SELECTED) { + if(frag.str[0] == '?') { + state->tmp.selection.state = SELECTION_QUERY; + } + else { + state->tmp.selection.state = SELECTION_SET_INITIAL; + state->tmp.selection.recvpartial = 0; + } + } + + if(state->tmp.selection.state == SELECTION_QUERY) { + if(state->selection.callbacks->query) + (*state->selection.callbacks->query)(state->tmp.selection.mask, state->selection.user); + return; + } + + if(state->selection.callbacks->set) { + size_t bufcur = 0; + char *buffer = state->selection.buffer; + + uint32_t x = 0; /* Current decoding value */ + int n = 0; /* Number of sextets consumed */ + + if(state->tmp.selection.recvpartial) { + n = state->tmp.selection.recvpartial >> 24; + x = state->tmp.selection.recvpartial & 0x03FFFF; /* could be up to 18 bits of state in here */ + + state->tmp.selection.recvpartial = 0; + } + + while((state->selection.buflen - bufcur) >= 3 && frag.len) { + if(frag.str[0] == '=') { + if(n == 2) { + buffer[0] = (x >> 4) & 0xFF; + buffer += 1, bufcur += 1; + } + if(n == 3) { + buffer[0] = (x >> 10) & 0xFF; + buffer[1] = (x >> 2) & 0xFF; + buffer += 2, bufcur += 2; + } + + while(frag.len && frag.str[0] == '=') + frag.str++, frag.len--; + + n = 0; + } + else { + uint8_t b = unbase64one(frag.str[0]); + if(b == 0xFF) { + DEBUG_LOG("base64decode bad input %02X\n", (uint8_t)frag.str[0]); + } + else { + x = (x << 6) | b; + n++; + } + frag.str++, frag.len--; + + if(n == 4) { + buffer[0] = (x >> 16) & 0xFF; + buffer[1] = (x >> 8) & 0xFF; + buffer[2] = (x >> 0) & 0xFF; + + buffer += 3, bufcur += 3; + x = 0; + n = 0; + } + } + + if(!frag.len || (state->selection.buflen - bufcur) < 3) { + if(bufcur) { + (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){ + .str = state->selection.buffer, + .len = bufcur, + .initial = state->tmp.selection.state == SELECTION_SET_INITIAL, + .final = frag.final, + }, state->selection.user); + state->tmp.selection.state = SELECTION_SET; + } + + buffer = state->selection.buffer; + bufcur = 0; + } + } + + if(n) + state->tmp.selection.recvpartial = (n << 24) | x; + } +} + +static int on_osc(int command, VTermStringFragment frag, void *user) +{ + VTermState *state = user; + + switch(command) { + case 0: + settermprop_string(state, VTERM_PROP_ICONNAME, frag); + settermprop_string(state, VTERM_PROP_TITLE, frag); + return 1; + + case 1: + settermprop_string(state, VTERM_PROP_ICONNAME, frag); + return 1; + + case 2: + settermprop_string(state, VTERM_PROP_TITLE, frag); + return 1; + + case 52: + if(state->selection.callbacks) + osc_selection(state, frag); + + return 1; + + default: + if(state->fallbacks && state->fallbacks->osc) + if((*state->fallbacks->osc)(command, frag, state->fbdata)) + return 1; + } + + return 0; +} + +static void request_status_string(VTermState *state, VTermStringFragment frag) +{ + VTerm *vt = state->vt; + + char *tmp = state->tmp.decrqss; + + if(frag.initial) + tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; + + int i = 0; + while(i < sizeof(state->tmp.decrqss)-1 && tmp[i]) + i++; + while(i < sizeof(state->tmp.decrqss)-1 && frag.len--) + tmp[i++] = (frag.str++)[0]; + tmp[i] = 0; + + if(!frag.final) + return; + + switch(tmp[0] | tmp[1]<<8 | tmp[2]<<16) { + case 'm': { + // Query SGR + long args[20]; + int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0])); + size_t cur = 0; + + cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, + vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ... + if(cur >= vt->tmpbuffer_len) + return; + + for(int argi = 0; argi < argc; argi++) { + cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, + argi == argc - 1 ? "%ld" : + CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" : + "%ld;", + CSI_ARG(args[argi])); + if(cur >= vt->tmpbuffer_len) + return; + } + + cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, + vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST + if(cur >= vt->tmpbuffer_len) + return; + + vterm_push_output_bytes(vt, vt->tmpbuffer, cur); + return; + } + + case 'r': + // Query DECSTBM + vterm_push_output_sprintf_str(vt, C1_DCS, true, + "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state)); + return; + + case 's': + // Query DECSLRM + vterm_push_output_sprintf_str(vt, C1_DCS, true, + "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state)); + return; + + case ' '|('q'<<8): { + // Query DECSCUSR + int reply; + switch(state->mode.cursor_shape) { + case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break; + case VTERM_PROP_CURSORSHAPE_UNDERLINE: reply = 4; break; + case VTERM_PROP_CURSORSHAPE_BAR_LEFT: reply = 6; break; + } + if(state->mode.cursor_blink) + reply--; + vterm_push_output_sprintf_str(vt, C1_DCS, true, + "1$r%d q", reply); + return; + } + + case '\"'|('q'<<8): + // Query DECSCA + vterm_push_output_sprintf_str(vt, C1_DCS, true, + "1$r%d\"q", state->protected_cell ? 1 : 2); + return; + } + + vterm_push_output_sprintf_str(state->vt, C1_DCS, true, "0$r%s", tmp); +} + +static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user) +{ + VTermState *state = user; + + if(commandlen == 2 && strneq(command, "$q", 2)) { + request_status_string(state, frag); + return 1; + } + else if(state->fallbacks && state->fallbacks->dcs) + if((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata)) + return 1; + + DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command); + return 0; +} + +static int on_apc(VTermStringFragment frag, void *user) +{ + VTermState *state = user; + + if(state->fallbacks && state->fallbacks->apc) + if((*state->fallbacks->apc)(frag, state->fbdata)) + return 1; + + /* No DEBUG_LOG because all APCs are unhandled */ + return 0; +} + +static int on_pm(VTermStringFragment frag, void *user) +{ + VTermState *state = user; + + if(state->fallbacks && state->fallbacks->pm) + if((*state->fallbacks->pm)(frag, state->fbdata)) + return 1; + + /* No DEBUG_LOG because all PMs are unhandled */ + return 0; +} + +static int on_sos(VTermStringFragment frag, void *user) +{ + VTermState *state = user; + + if(state->fallbacks && state->fallbacks->sos) + if((*state->fallbacks->sos)(frag, state->fbdata)) + return 1; + + /* No DEBUG_LOG because all SOSs are unhandled */ + return 0; +} + +static int on_resize(int rows, int cols, void *user) +{ + VTermState *state = user; + VTermPos oldpos = state->pos; + + if(cols != state->cols) { + unsigned char *newtabstops = vterm_allocator_malloc(state->vt, (cols + 7) / 8); + + /* TODO: This can all be done much more efficiently bytewise */ + int col; + for(col = 0; col < state->cols && col < cols; col++) { + unsigned char mask = 1 << (col & 7); + if(state->tabstops[col >> 3] & mask) + newtabstops[col >> 3] |= mask; + else + newtabstops[col >> 3] &= ~mask; + } + + for( ; col < cols; col++) { + unsigned char mask = 1 << (col & 7); + if(col % 8 == 0) + newtabstops[col >> 3] |= mask; + else + newtabstops[col >> 3] &= ~mask; + } + + vterm_allocator_free(state->vt, state->tabstops); + state->tabstops = newtabstops; + } + + if(rows != state->rows) { + for(int bufidx = BUFIDX_PRIMARY; bufidx <= BUFIDX_ALTSCREEN; bufidx++) { + VTermLineInfo *oldlineinfo = state->lineinfos[bufidx]; + if(!oldlineinfo) + continue; + + VTermLineInfo *newlineinfo = vterm_allocator_malloc(state->vt, rows * sizeof(VTermLineInfo)); + + int row; + for(row = 0; row < state->rows && row < rows; row++) { + newlineinfo[row] = oldlineinfo[row]; + } + + for( ; row < rows; row++) { + newlineinfo[row] = (VTermLineInfo){ + .doublewidth = 0, + }; + } + + vterm_allocator_free(state->vt, state->lineinfos[bufidx]); + state->lineinfos[bufidx] = newlineinfo; + } + + state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY]; + } + + state->rows = rows; + state->cols = cols; + + if(state->scrollregion_bottom > -1) + UBOUND(state->scrollregion_bottom, state->rows); + if(state->scrollregion_right > -1) + UBOUND(state->scrollregion_right, state->cols); + + VTermStateFields fields = { + .pos = state->pos, + }; + + if(state->callbacks && state->callbacks->resize) + (*state->callbacks->resize)(rows, cols, &fields, state->cbdata); + + state->pos = fields.pos; + + if(state->at_phantom && state->pos.col < cols-1) { + state->at_phantom = 0; + state->pos.col++; + } + + if(state->pos.row < 0) + state->pos.row = 0; + if(state->pos.row >= rows) + state->pos.row = rows - 1; + if(state->pos.col < 0) + state->pos.col = 0; + if(state->pos.col >= cols) + state->pos.col = cols - 1; + + updatecursor(state, &oldpos, 1); + + return 1; +} + +static const VTermParserCallbacks parser_callbacks = { + .text = on_text, + .control = on_control, + .escape = on_escape, + .csi = on_csi, + .osc = on_osc, + .dcs = on_dcs, + .apc = on_apc, + .pm = on_pm, + .sos = on_sos, + .resize = on_resize, +}; + +VTermState *vterm_obtain_state(VTerm *vt) +{ + if(vt->state) + return vt->state; + + VTermState *state = vterm_state_new(vt); + vt->state = state; + + vterm_parser_set_callbacks(vt, &parser_callbacks, state); + + return state; +} + +void vterm_state_reset(VTermState *state, int hard) +{ + state->scrollregion_top = 0; + state->scrollregion_bottom = -1; + state->scrollregion_left = 0; + state->scrollregion_right = -1; + + state->mode.keypad = 0; + state->mode.cursor = 0; + state->mode.autowrap = 1; + state->mode.insert = 0; + state->mode.newline = 0; + state->mode.alt_screen = 0; + state->mode.origin = 0; + state->mode.leftrightmargin = 0; + state->mode.bracketpaste = 0; + state->mode.report_focus = 0; + + state->mouse_flags = 0; + + state->vt->mode.ctrl8bit = 0; + + for(int col = 0; col < state->cols; col++) + if(col % 8 == 0) + set_col_tabstop(state, col); + else + clear_col_tabstop(state, col); + + for(int row = 0; row < state->rows; row++) + set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF); + + if(state->callbacks && state->callbacks->initpen) + (*state->callbacks->initpen)(state->cbdata); + + vterm_state_resetpen(state); + + VTermEncoding *default_enc = state->vt->mode.utf8 ? + vterm_lookup_encoding(ENC_UTF8, 'u') : + vterm_lookup_encoding(ENC_SINGLE_94, 'B'); + + for(int i = 0; i < 4; i++) { + state->encoding[i].enc = default_enc; + if(default_enc->init) + (*default_enc->init)(default_enc, state->encoding[i].data); + } + + state->gl_set = 0; + state->gr_set = 1; + state->gsingle_set = 0; + + state->protected_cell = 0; + + // Initialise the props + settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, 1); + settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); + settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK); + + if(hard) { + state->pos.row = 0; + state->pos.col = 0; + state->at_phantom = 0; + + VTermRect rect = { 0, state->rows, 0, state->cols }; + erase(state, rect, 0); + } +} + +void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos) +{ + *cursorpos = state->pos; +} + +void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user) +{ + if(callbacks) { + state->callbacks = callbacks; + state->cbdata = user; + + if(state->callbacks && state->callbacks->initpen) + (*state->callbacks->initpen)(state->cbdata); + } + else { + state->callbacks = NULL; + state->cbdata = NULL; + } +} + +void *vterm_state_get_cbdata(VTermState *state) +{ + return state->cbdata; +} + +void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user) +{ + if(fallbacks) { + state->fallbacks = fallbacks; + state->fbdata = user; + } + else { + state->fallbacks = NULL; + state->fbdata = NULL; + } +} + +void *vterm_state_get_unrecognised_fbdata(VTermState *state) +{ + return state->fbdata; +} + +int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val) +{ + /* Only store the new value of the property if usercode said it was happy. + * This is especially important for altscreen switching */ + if(state->callbacks && state->callbacks->settermprop) + if(!(*state->callbacks->settermprop)(prop, val, state->cbdata)) + return 0; + + switch(prop) { + case VTERM_PROP_TITLE: + case VTERM_PROP_ICONNAME: + // we don't store these, just transparently pass through + return 1; + case VTERM_PROP_CURSORVISIBLE: + state->mode.cursor_visible = val->boolean; + return 1; + case VTERM_PROP_CURSORBLINK: + state->mode.cursor_blink = val->boolean; + return 1; + case VTERM_PROP_CURSORSHAPE: + state->mode.cursor_shape = val->number; + return 1; + case VTERM_PROP_REVERSE: + state->mode.screen = val->boolean; + return 1; + case VTERM_PROP_ALTSCREEN: + state->mode.alt_screen = val->boolean; + state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY]; + if(state->mode.alt_screen) { + VTermRect rect = { + .start_row = 0, + .start_col = 0, + .end_row = state->rows, + .end_col = state->cols, + }; + erase(state, rect, 0); + } + return 1; + case VTERM_PROP_MOUSE: + state->mouse_flags = 0; + if(val->number) + state->mouse_flags |= MOUSE_WANT_CLICK; + if(val->number == VTERM_PROP_MOUSE_DRAG) + state->mouse_flags |= MOUSE_WANT_DRAG; + if(val->number == VTERM_PROP_MOUSE_MOVE) + state->mouse_flags |= MOUSE_WANT_MOVE; + return 1; + + case VTERM_N_PROPS: + return 0; + } + + return 0; +} + +void vterm_state_focus_in(VTermState *state) +{ + if(state->mode.report_focus) + vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "I"); +} + +void vterm_state_focus_out(VTermState *state) +{ + if(state->mode.report_focus) + vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "O"); +} + +const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row) +{ + return state->lineinfo + row; +} + +void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user, + char *buffer, size_t buflen) +{ + if(buflen && !buffer) + buffer = vterm_allocator_malloc(state->vt, buflen); + + state->selection.callbacks = callbacks; + state->selection.user = user; + state->selection.buffer = buffer; + state->selection.buflen = buflen; +} + +void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag) +{ + VTerm *vt = state->vt; + + if(frag.initial) { + /* TODO: support sending more than one mask bit */ + const static char selection_chars[] = "cpqs"; + int idx; + for(idx = 0; idx < 4; idx++) + if(mask & (1 << idx)) + break; + + vterm_push_output_sprintf_str(vt, C1_OSC, false, "52;%c;", selection_chars[idx]); + + state->tmp.selection.sendpartial = 0; + } + + if(frag.len) { + size_t bufcur = 0; + char *buffer = state->selection.buffer; + + uint32_t x = 0; + int n = 0; + + if(state->tmp.selection.sendpartial) { + n = state->tmp.selection.sendpartial >> 24; + x = state->tmp.selection.sendpartial & 0xFFFFFF; + + state->tmp.selection.sendpartial = 0; + } + + while((state->selection.buflen - bufcur) >= 4 && frag.len) { + x = (x << 8) | frag.str[0]; + n++; + frag.str++, frag.len--; + + if(n == 3) { + buffer[0] = base64_one((x >> 18) & 0x3F); + buffer[1] = base64_one((x >> 12) & 0x3F); + buffer[2] = base64_one((x >> 6) & 0x3F); + buffer[3] = base64_one((x >> 0) & 0x3F); + + buffer += 4, bufcur += 4; + x = 0; + n = 0; + } + + if(!frag.len || (state->selection.buflen - bufcur) < 4) { + if(bufcur) + vterm_push_output_bytes(vt, state->selection.buffer, bufcur); + + buffer = state->selection.buffer; + bufcur = 0; + } + } + + if(n) + state->tmp.selection.sendpartial = (n << 24) | x; + } + + if(frag.final) { + if(state->tmp.selection.sendpartial) { + int n = state->tmp.selection.sendpartial >> 24; + uint32_t x = state->tmp.selection.sendpartial & 0xFFFFFF; + char *buffer = state->selection.buffer; + + /* n is either 1 or 2 now */ + x <<= (n == 1) ? 16 : 8; + + buffer[0] = base64_one((x >> 18) & 0x3F); + buffer[1] = base64_one((x >> 12) & 0x3F); + buffer[2] = (n == 1) ? '=' : base64_one((x >> 6) & 0x3F); + buffer[3] = '='; + + vterm_push_output_sprintf_str(vt, 0, true, "%.*s", 4, buffer); + } + else + vterm_push_output_sprintf_str(vt, 0, true, ""); + } +} diff --git a/libvterm-0.2/src/state.lo b/libvterm-0.2/src/state.lo new file mode 100644 index 0000000..ba5562b --- /dev/null +++ b/libvterm-0.2/src/state.lo @@ -0,0 +1,12 @@ +# src/state.lo - a libtool object file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/state.o' + +# Name of the non-PIC object +non_pic_object='state.o' + diff --git a/libvterm-0.2/src/state.o b/libvterm-0.2/src/state.o new file mode 100644 index 0000000000000000000000000000000000000000..25bdbabf420df74430458bcef1e810fd981afad1 GIT binary patch literal 48056 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

`0dEU2hbl2nrgZ5yX0 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%0D!!srdsE@zv`W0{O)S7Q^;p46 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 literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/unicode.c b/libvterm-0.2/src/unicode.c new file mode 100644 index 0000000..0d1b5ff --- /dev/null +++ b/libvterm-0.2/src/unicode.c @@ -0,0 +1,337 @@ +#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 (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (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))); +} + + +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; +} + +// ################################ +// ### The rest added by Paul Evans + +static const struct interval fullwidth[] = { +#include "fullwidth.inc" +}; + +INTERNAL int vterm_unicode_width(uint32_t codepoint) +{ + if(bisearch(codepoint, fullwidth, sizeof(fullwidth) / sizeof(fullwidth[0]) - 1)) + return 2; + + return mk_wcwidth(codepoint); +} + +INTERNAL int vterm_unicode_is_combining(uint32_t codepoint) +{ + return bisearch(codepoint, combining, sizeof(combining) / sizeof(struct interval) - 1); +} diff --git a/libvterm-0.2/src/unicode.lo b/libvterm-0.2/src/unicode.lo new file mode 100644 index 0000000..8249063 --- /dev/null +++ b/libvterm-0.2/src/unicode.lo @@ -0,0 +1,12 @@ +# src/unicode.lo - a libtool object file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/unicode.o' + +# Name of the non-PIC object +non_pic_object='unicode.o' + diff --git a/libvterm-0.2/src/unicode.o b/libvterm-0.2/src/unicode.o new file mode 100644 index 0000000000000000000000000000000000000000..ef50231d42d557560dea7a002ebe4cf4cb619c0f GIT binary patch literal 6344 zcmb{1dsr299>?+Da0F6B@RDhoowU?0o`AgMB}I%pPlh2%?WGNlh=|A~4xssnqNvHT z7HyBU);`Z_m)*3swYH13xMF#&C^Bo?-P%~)io~8wyO3gM-`|<;25OpH6PVf9`V`85xW&YaK=2|m0^+@T!+SC^RZ~SZf>~iaVtEo1*3(G2sw@R(Gyf(9)wXCL^mvPi?QPvv>Q8 zSLDe3)kn^&scf&VY&YfI?E0spSef~YE8_TETG4xXW&7{wzOdWY5Ywl9SzMdi{?b}% z{a?1ZlQQ%;#Pm)%w+YHnSIX8r{E8EPS(e?>#y__+$QrrF2sV!Zc^*E6|LndSJ5GUpI7?hr}#~^xZ1m)IltiTR0V22(3e&E0!W^Dg|2Uc{( zf&HCR$#**Qz?O2f9S7!qp))V4jr8013Y+@D$zx10s=FAo8f#F4dn1k6hV5zx`7(B5 z7xpL$I3Lq+J4)2MV4wBk6duEs=Qf|^YFk)~#l zvoISuSgz_xu>(8Ns1A~cV0B}=n4l(;DVPdBrXdX_xC6_v0`+PKxeI%+7mey5c?edF zV*)8AsmbINq^Vm-u|hSFqESukZcH+6fFCy^4VkzF^=dmQcB|LPMjXT;Opay0NK>zp zVm}VxU6t6w7%>v#Fcs7Bw0f3&5r08F_M#E};*9B!QECh+#;QBWrKrM6tj1la!vk24 zdhAvsxcMa@NsT7QV66HH`BP+bUko9ux1-ku#`HlFMj`_|P9k+`+}S+^$c;>Sh@a zA`yk|h{LrQhe;~$Ez9I1h!t3cT5QH2k+R=1*TXVBY+iC1jMc+@j&OSNMQta=fGdp|h;)@niYgyti-&L==4xZ!!BL!q zVaz9DGVX_52O&fzvMLgBH|p>b8gK|lF(`@a08^2U?P+Xpx-q|cl=ZP2*_(`60`sIX z^0HvQW&6A$#B881d)6vki) zmf}8az^ix@Z=)GU@ug}bCw|HHRSMaT5U%^mGSe^}>Bz%kwT`?WkE)a8&0kxl3cK*S zY9jaJ9o0%6!IwCJ=5H+1ir8;0a|eQ`z+ya%$FLbs;Z-yugpw1ii$z$BDy%>q*1?@N z(hv-}?GGfk4YOH2MT#wW7SH1)$jj$;>{d;rkW1w~{0sla$Et-qj3f9AN7XU%IKBaw zmy!2>!KGxAe_Dv1sy8VHsG;OAT&WVtYjCZaM5f?+LPc9LR`+DE>IM%6@$_wY}ApgttUy&lF1k6;s?#tyuKeXzPX z;>nvaL&baf&7wE@q96KWATELzgK;T_AweaP*I<+yPl}1Ej1`B@ zDIQbXNzrSFBl}XGnP|l{o{#AWq6+um*I0)Q*oa543D4lqcpfjH9^24>9oUV%cul=Q zzJ-JM2%oD+o{v%Js(O%daG%oyX^TNBnG`81n+zZaxyZwOEI=WO)pa~GMFp0@H1k|) zBc{YgMLfqE&aw$oXUbsxj^&;-OIDD+x5+}L zfP8fHzpbPH5~(v?Ds+<` zU{+2s+bYU0%Hubw$0estMw7oq*i*Hh$Bw$5s)71_-1-tdud~AH?smMLwy&GCE6HJX z_qxaqtGm}lRao7w z#y2NW7BIfq!JzS#7CSSX%);CvmK7D3<@%;h&m0jf3*;2Af^^HBmo>LEP?)Q`{h!0j z7UWkQekCSTlPRF%;LJJex@}4RDQo}AlVzsU)K71$*;?aIBldpWUqU~vb9=YzVY{*4 zd32RCBK!_?6-#BCKej*rEVB3C`C5`4Hs}VM`5Q{-$=i8`_E^X_k~?ecD48| 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; +} +/* end copy */ diff --git a/libvterm-0.2/src/vterm.c b/libvterm-0.2/src/vterm.c new file mode 100644 index 0000000..bbc55ae --- /dev/null +++ b/libvterm-0.2/src/vterm.c @@ -0,0 +1,404 @@ +#include "vterm_internal.h" + +#include +#include +#include +#include + +/***************** + * API functions * + *****************/ + +static void *default_malloc(size_t size, void *allocdata) +{ + void *ptr = malloc(size); + if(ptr) + memset(ptr, 0, size); + return ptr; +} + +static void default_free(void *ptr, void *allocdata) +{ + free(ptr); +} + +static VTermAllocatorFunctions default_allocator = { + .malloc = &default_malloc, + .free = &default_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->parser.cbdata = NULL; + + vt->outfunc = NULL; + vt->outdata = NULL; + + vt->outbuffer_len = 64; + vt->outbuffer_cur = 0; + vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len); + + vt->tmpbuffer_len = 64; + vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_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->outbuffer); + vterm_allocator_free(vt, vt->tmpbuffer); + + 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->parser.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; +} + +void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user) +{ + vt->outfunc = func; + vt->outdata = user; +} + +INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len) +{ + if(vt->outfunc) { + (vt->outfunc)(bytes, len, vt->outdata); + return; + } + + if(len > vt->outbuffer_len - vt->outbuffer_cur) + return; + + memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len); + vt->outbuffer_cur += len; +} + +INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args) +{ + size_t len = vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len, + format, args); + + vterm_push_output_bytes(vt, vt->tmpbuffer, len); +} + +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 cur; + + if(ctrl >= 0x80 && !vt->mode.ctrl8bit) + cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len, + ESC_S "%c", ctrl - 0x40); + else + cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len, + "%c", ctrl); + + if(cur >= vt->tmpbuffer_len) + return; + + va_list args; + va_start(args, fmt); + cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, + fmt, args); + va_end(args); + + if(cur >= vt->tmpbuffer_len) + return; + + vterm_push_output_bytes(vt, vt->tmpbuffer, cur); +} + +INTERNAL void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...) +{ + size_t cur = 0; + + if(ctrl) { + if(ctrl >= 0x80 && !vt->mode.ctrl8bit) + cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len, + ESC_S "%c", ctrl - 0x40); + else + cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len, + "%c", ctrl); + + if(cur >= vt->tmpbuffer_len) + return; + } + + va_list args; + va_start(args, fmt); + cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, + fmt, args); + va_end(args); + + if(cur >= vt->tmpbuffer_len) + return; + + if(term) { + cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, + vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST + + if(cur >= vt->tmpbuffer_len) + return; + } + + vterm_push_output_bytes(vt, vt->tmpbuffer, 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; +} + +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_CONCEAL: 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; + + case VTERM_N_ATTRS: return 0; + } + 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; + + case VTERM_N_PROPS: return 0; + } + 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; + + 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; + } + + VTermPos pos; + 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 = { pos.row + downward, pos.col + rightward }; + (*copycell)(pos, srcpos, user); + } +} + +void vterm_check_version(int major, int minor) +{ + if(major != VTERM_VERSION_MAJOR) { + fprintf(stderr, "libvterm major version mismatch; %d (wants) != %d (library)\n", + major, VTERM_VERSION_MAJOR); + exit(1); + } + + if(minor > VTERM_VERSION_MINOR) { + fprintf(stderr, "libvterm minor version mismatch; %d (wants) > %d (library)\n", + minor, VTERM_VERSION_MINOR); + exit(1); + } + + // Happy +} diff --git a/libvterm-0.2/src/vterm.lo b/libvterm-0.2/src/vterm.lo new file mode 100644 index 0000000..95141aa --- /dev/null +++ b/libvterm-0.2/src/vterm.lo @@ -0,0 +1,12 @@ +# src/vterm.lo - a libtool object file +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/vterm.o' + +# Name of the non-PIC object +non_pic_object='vterm.o' + diff --git a/libvterm-0.2/src/vterm.o b/libvterm-0.2/src/vterm.o new file mode 100644 index 0000000000000000000000000000000000000000..e5db877bb06d2277706ecb2f27b5a8cb9c656ae4 GIT binary patch literal 10432 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 Ns4*Uro%gHD{~Lx64eS5_ literal 0 HcmV?d00001 diff --git a/libvterm-0.2/src/vterm_internal.h b/libvterm-0.2/src/vterm_internal.h new file mode 100644 index 0000000..2d77107 --- /dev/null +++ b/libvterm-0.2/src/vterm_internal.h @@ -0,0 +1,292 @@ +#ifndef __VTERM_INTERNAL_H__ +#define __VTERM_INTERNAL_H__ + +#include "vterm.h" + +#include + +#if defined(__GNUC__) +# define INTERNAL __attribute__((visibility("internal"))) +#else +# define INTERNAL +#endif + +#ifdef DEBUG +# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__) +#else +# define DEBUG_LOG(...) +#endif + +#define ESC_S "\x1b" + +#define INTERMED_MAX 16 + +#define CSI_ARGS_MAX 16 +#define CSI_LEADER_MAX 16 + +#define BUFIDX_PRIMARY 0 +#define BUFIDX_ALTSCREEN 1 + +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 conceal:1; + unsigned int strike:1; + unsigned int font:4; /* To store 0-9 */ +}; + +struct VTermState +{ + VTerm *vt; + + const VTermStateCallbacks *callbacks; + void *cbdata; + + const VTermStateFallbacks *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; + + /* Primary and Altscreen; lineinfos[1] is lazily allocated as needed */ + VTermLineInfo *lineinfos[2]; + + /* lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen */ + 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; + unsigned int report_focus: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 bold_is_highbright; + + unsigned int protected_cell : 1; + + /* Saved state under DEC mode 1048/1049 */ + struct { + VTermPos pos; + struct VTermPen pen; + + struct { + unsigned int cursor_visible:1; + unsigned int cursor_blink:1; + unsigned int cursor_shape:2; + } mode; + } saved; + + /* Temporary state for DECRQSS parsing */ + union { + char decrqss[4]; + struct { + uint16_t mask; + enum { + SELECTION_INITIAL, + SELECTION_SELECTED, + SELECTION_QUERY, + SELECTION_SET_INITIAL, + SELECTION_SET, + } state : 8; + uint32_t recvpartial; + uint32_t sendpartial; + } selection; + } tmp; + + struct { + const VTermSelectionCallbacks *callbacks; + void *user; + char *buffer; + size_t buflen; + } selection; +}; + +struct VTerm +{ + VTermAllocatorFunctions *allocator; + void *allocdata; + + int rows; + int cols; + + struct { + unsigned int utf8:1; + unsigned int ctrl8bit:1; + } mode; + + struct { + enum VTermParserState { + NORMAL, + CSI_LEADER, + CSI_ARGS, + CSI_INTERMED, + DCS_COMMAND, + /* below here are the "string states" */ + OSC_COMMAND, + OSC, + DCS, + APC, + PM, + SOS, + } state; + + bool in_esc : 1; + + int intermedlen; + char intermed[INTERMED_MAX]; + + union { + struct { + int leaderlen; + char leader[CSI_LEADER_MAX]; + + int argi; + long args[CSI_ARGS_MAX]; + } csi; + struct { + int command; + } osc; + struct { + int commandlen; + char command[CSI_LEADER_MAX]; + } dcs; + } v; + + const VTermParserCallbacks *callbacks; + void *cbdata; + + bool string_initial; + } parser; + + /* len == malloc()ed size; cur == number of valid bytes */ + + VTermOutputCallback *outfunc; + void *outdata; + + char *outbuffer; + size_t outbuffer_len; + size_t outbuffer_cur; + + char *tmpbuffer; + size_t tmpbuffer_len; + + 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_str(VTerm *vt, unsigned char ctrl, bool term, 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, + C1_OSC = 0x9d, +}; + +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 diff --git a/libvterm-0.2/t/02parser.test b/libvterm-0.2/t/02parser.test new file mode 100644 index 0000000..2cc51dc --- /dev/null +++ b/libvterm-0.2/t/02parser.test @@ -0,0 +1,266 @@ +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 "\x99" + control 0x99 + +!C1 7bit +PUSH "\e\x43" + control 0x83 + +PUSH "\e\x59" + control 0x99 + +!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"] + +!OSC in parts +PUSH "\e]52;abc" + osc [52 "abc" +PUSH "def" + osc "def" +PUSH "ghi\e\\" + osc "ghi"] + +!OSC BEL without semicolon +PUSH "\e]1234\x07" + osc [1234 ] + +!OSC ST without semicolon +PUSH "\e]1234\e\\" + osc [1234 ] + +!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" + osc [2 "" + control 10 + osc "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"] + +!Split write of 7bit ST +PUSH "\ePABC\e" + dcs ["ABC" +PUSH "\\" + dcs ] + +!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" + dcs ["By" + control 10 + dcs "e"] + +!APC BEL +PUSH "\e_Hello\x07" + apc ["Hello"] + +!APC ST (7bit) +PUSH "\e_Hello\e\\" + apc ["Hello"] + +!APC ST (8bit) +PUSH "\x{9f}Hello\x9c" + apc ["Hello"] + +!PM BEL +PUSH "\e^Hello\x07" + pm ["Hello"] + +!PM ST (7bit) +PUSH "\e^Hello\e\\" + pm ["Hello"] + +!PM ST (8bit) +PUSH "\x{9e}Hello\x9c" + pm ["Hello"] + +!SOS BEL +PUSH "\eXHello\x07" + sos ["Hello"] + +!SOS ST (7bit) +PUSH "\eXHello\e\\" + sos ["Hello"] + +!SOS ST (8bit) +PUSH "\x{98}Hello\x9c" + sos ["Hello"] + +!SOS can contain any C0 or C1 code +PUSH "\eXABC\x01DEF\e\\" + sos ["ABC\x01DEF"] +PUSH "\eXABC\x99DEF\e\\" + sos ["ABC\x{99}DEF"] + +!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 diff --git a/libvterm-0.2/t/03encoding_utf8.test b/libvterm-0.2/t/03encoding_utf8.test new file mode 100644 index 0000000..7ee16ac --- /dev/null +++ b/libvterm-0.2/t/03encoding_utf8.test @@ -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 diff --git a/libvterm-0.2/t/10state_putglyph.test b/libvterm-0.2/t/10state_putglyph.test new file mode 100644 index 0000000..bae0423 --- /dev/null +++ b/libvterm-0.2/t/10state_putglyph.test @@ -0,0 +1,68 @@ +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 split writes +RESET +PUSH "\xC3" +PUSH "\x81" + putglyph 0xc1 1 0,0 + +!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 emoji wide char +# U+1F600 = 0xF0 0x9F 0x98 0x80 name: GRINNING FACE +RESET +PUSH "\xF0\x9F\x98\x80 " + putglyph 0x1f600 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 diff --git a/libvterm-0.2/t/11state_movecursor.test b/libvterm-0.2/t/11state_movecursor.test new file mode 100644 index 0000000..c1d72b2 --- /dev/null +++ b/libvterm-0.2/t/11state_movecursor.test @@ -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 diff --git a/libvterm-0.2/t/12state_scroll.test b/libvterm-0.2/t/12state_scroll.test new file mode 100644 index 0000000..c1f2791 --- /dev/null +++ b/libvterm-0.2/t/12state_scroll.test @@ -0,0 +1,156 @@ +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[9;10r" +PUSH "\e[10H" +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 + +!DECSTBM resets cursor position +PUSH "\e[5;5H" + ?cursor = 4,4 +PUSH "\e[r" + ?cursor = 0,0 diff --git a/libvterm-0.2/t/13state_edit.test b/libvterm-0.2/t/13state_edit.test new file mode 100644 index 0000000..b435655 --- /dev/null +++ b/libvterm-0.2/t/13state_edit.test @@ -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 diff --git a/libvterm-0.2/t/14state_encoding.test b/libvterm-0.2/t/14state_encoding.test new file mode 100644 index 0000000..b1f5d69 --- /dev/null +++ b/libvterm-0.2/t/14state_encoding.test @@ -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 diff --git a/libvterm-0.2/t/15state_mode.test b/libvterm-0.2/t/15state_mode.test new file mode 100644 index 0000000..b7917ad --- /dev/null +++ b/libvterm-0.2/t/15state_mode.test @@ -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 diff --git a/libvterm-0.2/t/16state_resize.test b/libvterm-0.2/t/16state_resize.test new file mode 100644 index 0000000..42c77c7 --- /dev/null +++ b/libvterm-0.2/t/16state_resize.test @@ -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 diff --git a/libvterm-0.2/t/17state_mouse.test b/libvterm-0.2/t/17state_mouse.test new file mode 100644 index 0000000..e5ba29b --- /dev/null +++ b/libvterm-0.2/t/17state_mouse.test @@ -0,0 +1,181 @@ +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" + +!Mouse disabled reports nothing +RESET + settermprop 1 true + settermprop 2 true + settermprop 7 1 +MOUSEMOVE 0,0 0 +MOUSEBTN d 1 0 +MOUSEBTN u 1 0 diff --git a/libvterm-0.2/t/18state_termprops.test b/libvterm-0.2/t/18state_termprops.test new file mode 100644 index 0000000..83c333f --- /dev/null +++ b/libvterm-0.2/t/18state_termprops.test @@ -0,0 +1,42 @@ +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"] + +!Title split write +PUSH "\e]2;Here is" + settermprop 4 ["Here is" +PUSH " another title\a" + settermprop 4 " another title"] diff --git a/libvterm-0.2/t/20state_wrapping.test b/libvterm-0.2/t/20state_wrapping.test new file mode 100644 index 0000000..606fa06 --- /dev/null +++ b/libvterm-0.2/t/20state_wrapping.test @@ -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 diff --git a/libvterm-0.2/t/21state_tabstops.test b/libvterm-0.2/t/21state_tabstops.test new file mode 100644 index 0000000..df4a589 --- /dev/null +++ b/libvterm-0.2/t/21state_tabstops.test @@ -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 diff --git a/libvterm-0.2/t/22state_save.test b/libvterm-0.2/t/22state_save.test new file mode 100644 index 0000000..81e9226 --- /dev/null +++ b/libvterm-0.2/t/22state_save.test @@ -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 diff --git a/libvterm-0.2/t/25state_input.test b/libvterm-0.2/t/25state_input.test new file mode 100644 index 0000000..4eb4c6a --- /dev/null +++ b/libvterm-0.2/t/25state_input.test @@ -0,0 +1,155 @@ +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" + +!Unmodified F1 is SS3 P +INKEY 0 F1 + output "\eOP" + +!Modified F1 is CSI P +INKEY S F1 + output "\e[1;2P" +INKEY A F1 + output "\e[1;3P" +INKEY C F1 + output "\e[1;5P" + +!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~" + +!Focus reporting disabled +FOCUS IN +FOCUS OUT + +!Focus reporting enabled +PUSH "\e[?1004h" +FOCUS IN + output "\e[I" +FOCUS OUT + output "\e[O" diff --git a/libvterm-0.2/t/26state_query.test b/libvterm-0.2/t/26state_query.test new file mode 100644 index 0000000..7c85042 --- /dev/null +++ b/libvterm-0.2/t/26state_query.test @@ -0,0 +1,58 @@ +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" diff --git a/libvterm-0.2/t/27state_reset.test b/libvterm-0.2/t/27state_reset.test new file mode 100644 index 0000000..254f994 --- /dev/null +++ b/libvterm-0.2/t/27state_reset.test @@ -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 diff --git a/libvterm-0.2/t/28state_dbl_wh.test b/libvterm-0.2/t/28state_dbl_wh.test new file mode 100644 index 0000000..596194d --- /dev/null +++ b/libvterm-0.2/t/28state_dbl_wh.test @@ -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 diff --git a/libvterm-0.2/t/29state_fallback.test b/libvterm-0.2/t/29state_fallback.test new file mode 100644 index 0000000..4ab2e18 --- /dev/null +++ b/libvterm-0.2/t/29state_fallback.test @@ -0,0 +1,31 @@ +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"] + +!Unrecognised APC +PUSH "\e_z123\e\\" + apc ["z123"] + +!Unrecognised PM +PUSH "\e^z123\e\\" + pm ["z123"] + +!Unrecognised SOS +PUSH "\eXz123\e\\" + sos ["z123"] diff --git a/libvterm-0.2/t/30state_pen.test b/libvterm-0.2/t/30state_pen.test new file mode 100644 index 0000000..915baec --- /dev/null +++ b/libvterm-0.2/t/30state_pen.test @@ -0,0 +1,114 @@ +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,is_default_fg) + ?pen background = rgb(0,0,0,is_default_bg) + +!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[4:0m" + ?pen underline = 0 +PUSH "\e[4:1m" + ?pen underline = 1 +PUSH "\e[4:2m" + ?pen underline = 2 +PUSH "\e[4:3m" + ?pen underline = 3 +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 = idx(1) +PUSH "\e[32m" + ?pen foreground = idx(2) +PUSH "\e[34m" + ?pen foreground = idx(4) +PUSH "\e[91m" + ?pen foreground = idx(9) +PUSH "\e[38:2:10:20:30m" + ?pen foreground = rgb(10,20,30) +PUSH "\e[38:5:1m" + ?pen foreground = idx(1) +PUSH "\e[39m" + ?pen foreground = rgb(240,240,240,is_default_fg) + +!Background +PUSH "\e[41m" + ?pen background = idx(1) +PUSH "\e[42m" + ?pen background = idx(2) +PUSH "\e[44m" + ?pen background = idx(4) +PUSH "\e[101m" + ?pen background = idx(9) +PUSH "\e[48:2:10:20:30m" + ?pen background = rgb(10,20,30) +PUSH "\e[48:5:1m" + ?pen background = idx(1) +PUSH "\e[49m" + ?pen background = rgb(0,0,0,is_default_bg) + +!Bold+ANSI colour == highbright +PUSH "\e[m\e[1;37m" + ?pen bold = on + ?pen foreground = idx(15) +PUSH "\e[m\e[37;1m" + ?pen bold = on + ?pen foreground = idx(15) diff --git a/libvterm-0.2/t/31state_rep.test b/libvterm-0.2/t/31state_rep.test new file mode 100644 index 0000000..f820e67 --- /dev/null +++ b/libvterm-0.2/t/31state_rep.test @@ -0,0 +1,128 @@ +INIT +UTF8 1 +WANTSTATE g + +!REP no argument +RESET +PUSH "a\e[b" + putglyph 0x61 1 0,0 + putglyph 0x61 1 0,1 + +!REP zero (zero should be interpreted as one) +RESET +PUSH "a\e[0b" + putglyph 0x61 1 0,0 + putglyph 0x61 1 0,1 + +!REP lowercase a times two +RESET +PUSH "a\e[2b" + putglyph 0x61 1 0,0 + putglyph 0x61 1 0,1 + putglyph 0x61 1 0,2 + +!REP with UTF-8 1 char +# U+00E9 = 0xC3 0xA9 name: LATIN SMALL LETTER E WITH ACUTE +RESET +PUSH "\xC3\xA9\e[b" + putglyph 0xe9 1 0,0 + putglyph 0xe9 1 0,1 + +!REP with UTF-8 wide char +# U+00E9 = 0xC3 0xA9 name: LATIN SMALL LETTER E WITH ACUTE +RESET +PUSH "\xEF\xBC\x90\e[b" + putglyph 0xff10 2 0,0 + putglyph 0xff10 2 0,2 + +!REP with UTF-8 combining character +RESET +PUSH "e\xCC\x81\e[b" + putglyph 0x65,0x301 1 0,0 + putglyph 0x65,0x301 1 0,1 + +!REP till end of line +RESET +PUSH "a\e[1000bb" + putglyph 0x61 1 0,0 + putglyph 0x61 1 0,1 + putglyph 0x61 1 0,2 + putglyph 0x61 1 0,3 + putglyph 0x61 1 0,4 + putglyph 0x61 1 0,5 + putglyph 0x61 1 0,6 + putglyph 0x61 1 0,7 + putglyph 0x61 1 0,8 + putglyph 0x61 1 0,9 + putglyph 0x61 1 0,10 + putglyph 0x61 1 0,11 + putglyph 0x61 1 0,12 + putglyph 0x61 1 0,13 + putglyph 0x61 1 0,14 + putglyph 0x61 1 0,15 + putglyph 0x61 1 0,16 + putglyph 0x61 1 0,17 + putglyph 0x61 1 0,18 + putglyph 0x61 1 0,19 + putglyph 0x61 1 0,20 + putglyph 0x61 1 0,21 + putglyph 0x61 1 0,22 + putglyph 0x61 1 0,23 + putglyph 0x61 1 0,24 + putglyph 0x61 1 0,25 + putglyph 0x61 1 0,26 + putglyph 0x61 1 0,27 + putglyph 0x61 1 0,28 + putglyph 0x61 1 0,29 + putglyph 0x61 1 0,30 + putglyph 0x61 1 0,31 + putglyph 0x61 1 0,32 + putglyph 0x61 1 0,33 + putglyph 0x61 1 0,34 + putglyph 0x61 1 0,35 + putglyph 0x61 1 0,36 + putglyph 0x61 1 0,37 + putglyph 0x61 1 0,38 + putglyph 0x61 1 0,39 + putglyph 0x61 1 0,40 + putglyph 0x61 1 0,41 + putglyph 0x61 1 0,42 + putglyph 0x61 1 0,43 + putglyph 0x61 1 0,44 + putglyph 0x61 1 0,45 + putglyph 0x61 1 0,46 + putglyph 0x61 1 0,47 + putglyph 0x61 1 0,48 + putglyph 0x61 1 0,49 + putglyph 0x61 1 0,50 + putglyph 0x61 1 0,51 + putglyph 0x61 1 0,52 + putglyph 0x61 1 0,53 + putglyph 0x61 1 0,54 + putglyph 0x61 1 0,55 + putglyph 0x61 1 0,56 + putglyph 0x61 1 0,57 + putglyph 0x61 1 0,58 + putglyph 0x61 1 0,59 + putglyph 0x61 1 0,60 + putglyph 0x61 1 0,61 + putglyph 0x61 1 0,62 + putglyph 0x61 1 0,63 + putglyph 0x61 1 0,64 + putglyph 0x61 1 0,65 + putglyph 0x61 1 0,66 + putglyph 0x61 1 0,67 + putglyph 0x61 1 0,68 + putglyph 0x61 1 0,69 + putglyph 0x61 1 0,70 + putglyph 0x61 1 0,71 + putglyph 0x61 1 0,72 + putglyph 0x61 1 0,73 + putglyph 0x61 1 0,74 + putglyph 0x61 1 0,75 + putglyph 0x61 1 0,76 + putglyph 0x61 1 0,77 + putglyph 0x61 1 0,78 + putglyph 0x61 1 0,79 + putglyph 0x62 1 1,0 + diff --git a/libvterm-0.2/t/32state_flow.test b/libvterm-0.2/t/32state_flow.test new file mode 100644 index 0000000..84a13df --- /dev/null +++ b/libvterm-0.2/t/32state_flow.test @@ -0,0 +1,28 @@ +INIT +WANTSTATE + +# Many of these test cases inspired by +# https://blueprints.launchpad.net/libvterm/+spec/reflow-cases + +!Spillover text marks continuation on second line +RESET +PUSH "A"x100 +PUSH "\r\n" + ?lineinfo 0 = + ?lineinfo 1 = cont + +!CRLF in column 80 does not mark continuation +RESET +PUSH "B"x80 +PUSH "\r\n" +PUSH "B"x20 +PUSH "\r\n" + ?lineinfo 0 = + ?lineinfo 1 = + +!EL cancels continuation of following line +RESET +PUSH "D"x100 + ?lineinfo 1 = cont +PUSH "\eM\e[79G\e[K" + ?lineinfo 1 = diff --git a/libvterm-0.2/t/40state_selection.test b/libvterm-0.2/t/40state_selection.test new file mode 100644 index 0000000..ccc3d92 --- /dev/null +++ b/libvterm-0.2/t/40state_selection.test @@ -0,0 +1,65 @@ +INIT +UTF8 1 +WANTSTATE + +!Set clipboard; final chunk len 4 +PUSH "\e]52;c;SGVsbG8s\e\\" + selection-set mask=0001 ["Hello,"] + +!Set clipboard; final chunk len 3 +PUSH "\e]52;c;SGVsbG8sIHc=\e\\" + selection-set mask=0001 ["Hello, w"] + +!Set clipboard; final chunk len 2 +PUSH "\e]52;c;SGVsbG8sIHdvcmxkCg==\e\\" + selection-set mask=0001 ["Hello, world\n"] + +!Set clipboard; split between chunks +PUSH "\e]52;c;SGVs" + selection-set mask=0001 ["Hel" +PUSH "bG8s\e\\" + selection-set mask=0001 "lo,"] + +!Set clipboard; split within chunk +PUSH "\e]52;c;SGVsbG" + selection-set mask=0001 ["Hel" +PUSH "8s\e\\" + selection-set mask=0001 "lo,"] + +!Query clipboard +PUSH "\e]52;c;?\e\\" + selection-query mask=0001 + +!Send clipboard; final chunk len 4 +SELECTION 1 ["Hello,"] + output "\e]52;c;" + output "SGVsbG8s" + output "\e\\" + +!Send clipboard; final chunk len 3 +SELECTION 1 ["Hello, w"] + output "\e]52;c;" + output "SGVsbG8s" + output "IHc=\e\\" + +!Send clipboard; final chunk len 2 +SELECTION 1 ["Hello, world\n"] + output "\e]52;c;" + output "SGVsbG8sIHdvcmxk" + output "Cg==\e\\" + +!Send clipboard; split between chunks +SELECTION 1 ["Hel" + output "\e]52;c;" + output "SGVs" +SELECTION 1 "lo,"] + output "bG8s" + output "\e\\" + +!Send clipboard; split within chunk +SELECTION 1 ["Hello" + output "\e]52;c;" + output "SGVs" +SELECTION 1 ","] + output "bG8s" + output "\e\\" diff --git a/libvterm-0.2/t/60screen_ascii.test b/libvterm-0.2/t/60screen_ascii.test new file mode 100644 index 0000000..e679b98 --- /dev/null +++ b/libvterm-0.2/t/60screen_ascii.test @@ -0,0 +1,69 @@ +INIT +WANTSCREEN ac + +!Get +RESET +PUSH "ABC" + movecursor 0,3 + ?screen_chars 0,0,1,3 = "ABC" + ?screen_chars 0,0,1,80 = "ABC" + ?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 = "ABC" + ?screen_text 0,0,1,80 = 0x41,0x42,0x43 +PUSH "E" + movecursor 0,1 + ?screen_chars 0,0,1,80 = "EBC" + ?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 = "1ABC" + +RESET +PUSH "ABC\e[H\e[P" + ?screen_chars 0,0,1,1 = "B" + ?screen_chars 0,1,1,2 = "C" + ?screen_chars 0,0,1,80 = "BC" + +!Space padding +RESET +PUSH "Hello\e[CWorld" + ?screen_chars 0,0,1,80 = "Hello World" + ?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 = "Hello\nWorld" + ?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 = "P" +PUSH "\e[?1049h" + ?screen_chars 0,0,1,80 = +PUSH "\e[2K\e[HA" + ?screen_chars 0,0,1,80 = "A" +PUSH "\e[?1049l" + ?screen_chars 0,0,1,80 = "P" diff --git a/libvterm-0.2/t/61screen_unicode.test b/libvterm-0.2/t/61screen_unicode.test new file mode 100644 index 0000000..79dcb68 --- /dev/null +++ b/libvterm-0.2/t/61screen_unicode.test @@ -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) diff --git a/libvterm-0.2/t/62screen_damage.test b/libvterm-0.2/t/62screen_damage.test new file mode 100644 index 0000000..32cac2d --- /dev/null +++ b/libvterm-0.2/t/62screen_damage.test @@ -0,0 +1,155 @@ +INIT +WANTSCREEN aDb + +!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 = "ABE" diff --git a/libvterm-0.2/t/63screen_resize.test b/libvterm-0.2/t/63screen_resize.test new file mode 100644 index 0000000..87b88d6 --- /dev/null +++ b/libvterm-0.2/t/63screen_resize.test @@ -0,0 +1,117 @@ +INIT +WANTSTATE +WANTSCREEN + +!Resize wider preserves cells +RESET +RESIZE 25,80 +PUSH "AB\r\nCD" + ?screen_chars 0,0,1,80 = "AB" + ?screen_chars 1,0,2,80 = "CD" +RESIZE 25,100 + ?screen_chars 0,0,1,100 = "AB" + ?screen_chars 1,0,2,100 = "CD" + +!Resize wider allows print in new area +RESET +RESIZE 25,80 +PUSH "AB\e[79GCD" + ?screen_chars 0,0,1,2 = "AB" + ?screen_chars 0,78,1,80 = "CD" +RESIZE 25,100 + ?screen_chars 0,0,1,2 = "AB" + ?screen_chars 0,78,1,80 = "CD" +PUSH "E" + ?screen_chars 0,78,1,81 = "CDE" + +!Resize shorter with blanks just truncates +RESET +RESIZE 25,80 +PUSH "Top\e[10HLine 10" + ?screen_chars 0,0,1,80 = "Top" + ?screen_chars 9,0,10,80 = "Line 10" + ?cursor = 9,7 +RESIZE 20,80 + ?screen_chars 0,0,1,80 = "Top" + ?screen_chars 9,0,10,80 = "Line 10" + ?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 = "Top" + ?screen_chars 24,0,25,80 = "Line 25" + ?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 = "Line 25" + ?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 = "Line 25" + ?cursor = 24,0 +RESIZE 24,80 + sb_pushline 80 = + ?screen_chars 22,0,23,10 = "Line 25" + ?cursor = 23,0 + +!Resize shorter does not send the cursor to a negative row +# See also https://github.com/vim/vim/pull/6141 +RESET +WANTSCREEN -b +RESIZE 25,80 +WANTSCREEN b +PUSH "\e[24HLine 24\r\nLine 25\e[H" + ?cursor = 0,0 +RESIZE 20,80 + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + sb_pushline 80 = + ?cursor = 0,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 = "Line 1" + ?screen_chars 24,0,25,80 = "Bottom" + ?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 = "ABCDE" + ?screen_chars 5,0,6,80 = "Line 1" + ?screen_chars 29,0,30,80 = "Bottom" + ?cursor = 19,0 +WANTSCREEN -b + +!Resize can operate on altscreen +RESET +WANTSCREEN a +RESIZE 25,80 +PUSH "Main screen\e[?1049h\e[HAlt screen" +RESIZE 30,80 + ?screen_chars 0,0,1,3 = "Alt" +PUSH "\e[?1049l" + ?screen_chars 0,0,1,3 = "Mai" diff --git a/libvterm-0.2/t/64screen_pen.test b/libvterm-0.2/t/64screen_pen.test new file mode 100644 index 0000000..f1ee639 --- /dev/null +++ b/libvterm-0.2/t/64screen_pen.test @@ -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" diff --git a/libvterm-0.2/t/65screen_protect.test b/libvterm-0.2/t/65screen_protect.test new file mode 100644 index 0000000..718f853 --- /dev/null +++ b/libvterm-0.2/t/65screen_protect.test @@ -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 = diff --git a/libvterm-0.2/t/66screen_extent.test b/libvterm-0.2/t/66screen_extent.test new file mode 100644 index 0000000..a126cec --- /dev/null +++ b/libvterm-0.2/t/66screen_extent.test @@ -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 diff --git a/libvterm-0.2/t/67screen_dbl_wh.test b/libvterm-0.2/t/67screen_dbl_wh.test new file mode 100644 index 0000000..9c81e83 --- /dev/null +++ b/libvterm-0.2/t/67screen_dbl_wh.test @@ -0,0 +1,38 @@ +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) + +!DWL doesn't spill over on scroll +RESET +PUSH "\e[25H\e#6Final\r\n" + ?screen_cell 23,0 = {0x46} width=1 attrs={} dwl fg=rgb(240,240,240) bg=rgb(0,0,0) + ?screen_cell 24,0 = {} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0) diff --git a/libvterm-0.2/t/68screen_termprops.test b/libvterm-0.2/t/68screen_termprops.test new file mode 100644 index 0000000..bba6660 --- /dev/null +++ b/libvterm-0.2/t/68screen_termprops.test @@ -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"] diff --git a/libvterm-0.2/t/90vttest_01-movement-1.test b/libvterm-0.2/t/90vttest_01-movement-1.test new file mode 100644 index 0000000..c1a8cb9 --- /dev/null +++ b/libvterm-0.2/t/90vttest_01-movement-1.test @@ -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 " + +# 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 E +*" + ?screen_row 14 = "*+ E E +*" + ?screen_row 15 = "*+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*" +$SEQ 16 21: ?screen_row \# = "*+ +*" + ?screen_row 22 = "*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*" + ?screen_row 23 = "********************************************************************************" + +?cursor = 13,67 diff --git a/libvterm-0.2/t/90vttest_01-movement-2.test b/libvterm-0.2/t/90vttest_01-movement-2.test new file mode 100644 index 0000000..3a515e3 --- /dev/null +++ b/libvterm-0.2/t/90vttest_01-movement-2.test @@ -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 diff --git a/libvterm-0.2/t/90vttest_01-movement-3.test b/libvterm-0.2/t/90vttest_01-movement-3.test new file mode 100644 index 0000000..f9a99bf --- /dev/null +++ b/libvterm-0.2/t/90vttest_01-movement-3.test @@ -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 diff --git a/libvterm-0.2/t/90vttest_01-movement-4.test b/libvterm-0.2/t/90vttest_01-movement-4.test new file mode 100644 index 0000000..0dab3c7 --- /dev/null +++ b/libvterm-0.2/t/90vttest_01-movement-4.test @@ -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" diff --git a/libvterm-0.2/t/90vttest_02-screen-1.test b/libvterm-0.2/t/90vttest_02-screen-1.test new file mode 100644 index 0000000..003d56f --- /dev/null +++ b/libvterm-0.2/t/90vttest_02-screen-1.test @@ -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" diff --git a/libvterm-0.2/t/90vttest_02-screen-2.test b/libvterm-0.2/t/90vttest_02-screen-2.test new file mode 100644 index 0000000..1c3a6a7 --- /dev/null +++ b/libvterm-0.2/t/90vttest_02-screen-2.test @@ -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 diff --git a/libvterm-0.2/t/90vttest_02-screen-3.test b/libvterm-0.2/t/90vttest_02-screen-3.test new file mode 100644 index 0000000..8cdf8df --- /dev/null +++ b/libvterm-0.2/t/90vttest_02-screen-3.test @@ -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" diff --git a/libvterm-0.2/t/90vttest_02-screen-4.test b/libvterm-0.2/t/90vttest_02-screen-4.test new file mode 100644 index 0000000..44d51f1 --- /dev/null +++ b/libvterm-0.2/t/90vttest_02-screen-4.test @@ -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" + diff --git a/libvterm-0.2/t/92lp1640917.test b/libvterm-0.2/t/92lp1640917.test new file mode 100644 index 0000000..70de439 --- /dev/null +++ b/libvterm-0.2/t/92lp1640917.test @@ -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" diff --git a/libvterm-0.2/t/harness.c b/libvterm-0.2/t/harness.c new file mode 100644 index 0000000..1cda50b --- /dev/null +++ b/libvterm-0.2/t/harness.c @@ -0,0 +1,1111 @@ +#include "vterm.h" +#include "../src/vterm_internal.h" // We pull in some internal bits too + +#include +#include + +#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; + if(sscanf(inpos, "%2x", &ch) < 1) + break; + *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 }, + { "F1", VTERM_KEY_FUNCTION(1) }, + { NULL, VTERM_KEY_NONE }, + }; + + for(int i = 0; keys[i].name; i++) { + if(streq(str, keys[i].name)) + return keys[i].key; + } + + return VTERM_KEY_NONE; +} + +static void print_color(const VTermColor *col) +{ + if (VTERM_COLOR_IS_RGB(col)) { + printf("rgb(%d,%d,%d", col->rgb.red, col->rgb.green, col->rgb.blue); + } + else if (VTERM_COLOR_IS_INDEXED(col)) { + printf("idx(%d", col->indexed.idx); + } + else { + printf("invalid(%d", col->type); + } + if (VTERM_COLOR_IS_DEFAULT_FG(col)) { + printf(",is_default_fg"); + } + if (VTERM_COLOR_IS_DEFAULT_BG(col)) { + printf(",is_default_bg"); + } + printf(")"); +} + +static VTerm *vt; +static VTermState *state; +static VTermScreen *screen; + +static VTermEncodingInstance encoding; + +static void term_output(const char *s, size_t len, void *user) +{ + printf("output "); + for(int i = 0; i < len; i++) + printf("%x%s", (unsigned char)s[i], i < len-1 ? "," : "\n"); +} + +static void printhex(const char *s, size_t len) +{ + while(len--) + printf("%02x", (uint8_t)(s++)[0]); +} + +static int parser_text(const char bytes[], size_t len, void *user) +{ + printf("text "); + int i; + 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) +{ + if(bytes[0] >= 0x20 && bytes[0] < 0x30) { + if(len < 2) + return -1; + len = 2; + } + else { + len = 1; + } + + printf("escape "); + printhex(bytes, len); + printf("\n"); + + return len; +} + +static int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user) +{ + printf("csi %02x", command); + + if(leader && leader[0]) { + printf(" L="); + for(int i = 0; leader[i]; i++) + printf("%02x", leader[i]); + } + + for(int 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(int i = 0; intermed[i]; i++) + printf("%02x", intermed[i]); + } + + printf("\n"); + + return 1; +} + +static int parser_osc(int command, VTermStringFragment frag, void *user) +{ + printf("osc "); + + if(frag.initial) { + if(command == -1) + printf("["); + else + printf("[%d;", command); + } + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user) +{ + printf("dcs "); + + if(frag.initial) { + printf("["); + for(int i = 0; i < commandlen; i++) + printf("%02x", command[i]); + } + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static int parser_apc(VTermStringFragment frag, void *user) +{ + printf("apc "); + + if(frag.initial) + printf("["); + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static int parser_pm(VTermStringFragment frag, void *user) +{ + printf("pm "); + + if(frag.initial) + printf("["); + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static int parser_sos(VTermStringFragment frag, void *user) +{ + printf("sos "); + + if(frag.initial) + printf("["); + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static VTermParserCallbacks parser_cbs = { + .text = parser_text, + .control = parser_control, + .escape = parser_escape, + .csi = parser_csi, + .osc = parser_osc, + .dcs = parser_dcs, + .apc = parser_apc, + .pm = parser_pm, + .sos = parser_sos, +}; + +static VTermStateFallbacks fallbacks = { + .control = parser_control, + .csi = parser_csi, + .osc = parser_osc, + .dcs = parser_dcs, + .apc = parser_apc, + .pm = parser_pm, + .sos = parser_sos, +}; + +/* 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) +{ + if(!want_settermprop) + return 1; + + VTermValueType 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\"%.*s\"%s\n", prop, + val->string.initial ? "[" : "", val->string.len, val->string.str, val->string.final ? "]" : ""); + return 1; + case VTERM_VALUETYPE_COLOR: + printf("settermprop %d ", prop); + print_color(&val->color); + printf("\n"); + return 1; + + case VTERM_N_VALUETYPES: + return 0; + } + + return 0; +} + +/* These callbacks are for State */ + +static int want_state_putglyph = 0; +static int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) +{ + if(!want_state_putglyph) + return 1; + + printf("putglyph "); + for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && 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 conceal; + 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_CONCEAL: + state_pen.conceal = 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; + + case VTERM_N_ATTRS: + return 0; + } + + return 1; +} + +static int state_setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user) +{ + return 1; +} + +VTermStateCallbacks state_cbs = { + .putglyph = state_putglyph, + .movecursor = movecursor, + .scrollrect = scrollrect, + .moverect = moverect, + .erase = state_erase, + .setpenattr = state_setpenattr, + .settermprop = settermprop, + .setlineinfo = state_setlineinfo, +}; + +static int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user) +{ + printf("selection-set mask=%04X ", mask); + if(frag.initial) + printf("["); + printhex(frag.str, frag.len); + if(frag.final) + printf("]"); + printf("\n"); + + return 1; +} + +static int selection_query(VTermSelectionMask mask, void *user) +{ + printf("selection-query mask=%04X\n", mask); + + return 1; +} + +VTermSelectionCallbacks selection_cbs = { + .set = selection_set, + .query = selection_query, +}; + +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; + + for(int row = rect.start_row; row < rect.end_row; row++) { + int eol = rect.end_col; + while(eol > rect.start_col) { + VTermScreenCell cell; + vterm_screen_get_cell(screen, (VTermPos) { .row = row, .col = eol-1 }, &cell); + if(cell.chars[0]) + break; + + eol--; + } + + if(eol == rect.start_col) + break; + + if(!equals) + printf(" ="), equals = true; + + printf(" %d<", row); + for(int col = rect.start_col; col < eol; col++) { + VTermScreenCell cell; + vterm_screen_get_cell(screen, (VTermPos) { .row = row, .col = col }, &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) +{ + if(!want_screen_scrollback) + return 1; + + int eol = cols; + while(eol && !cells[eol-1].chars[0]) + eol--; + + printf("sb_pushline %d =", cols); + for(int 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) +{ + if(!want_screen_scrollback) + return 0; + + // All lines of scrollback contain "ABCDE" + for(int 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 = { + .damage = screen_damage, + .moverect = moverect, + .movecursor = movecursor, + .settermprop = settermprop, + .sb_pushline = screen_sb_pushline, + .sb_popline = screen_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)) { + err = 0; + + char *nl; + if((nl = strchr(line, '\n'))) + *nl = '\0'; + + if(streq(line, "INIT")) { + if(!vt) + vt = vterm_new(25, 80); + + vterm_output_set_callback(vt, term_output, NULL); + } + + else if(streq(line, "WANTPARSER")) { + vterm_parser_set_callbacks(vt, &parser_cbs, NULL); + } + + else if(strstartswith(line, "WANTSTATE") && (line[9] == '\0' || line[9] == ' ')) { + if(!state) { + state = vterm_obtain_state(vt); + vterm_state_set_callbacks(state, &state_cbs, NULL); + vterm_state_set_selection_callbacks(state, &selection_cbs, NULL, NULL, 1024); + vterm_state_set_bold_highbright(state, 1); + vterm_state_reset(state, 1); + } + + int i = 9; + int sense = 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 ? &fallbacks : NULL, NULL); + break; + default: + fprintf(stderr, "Unrecognised WANTSTATE flag '%c'\n", line[i]); + } + } + + else if(strstartswith(line, "WANTSCREEN") && (line[10] == '\0' || line[10] == ' ')) { + if(!screen) + screen = vterm_obtain_screen(vt); + vterm_screen_set_callbacks(screen, &screen_cbs, NULL); + + int i = 10; + int sense = 1; + while(line[i] == ' ') + i++; + for( ; line[i]; i++) + switch(line[i]) { + case '-': + sense = 0; + break; + case 'a': + vterm_screen_enable_altscreen(screen, 1); + 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[len]; + int cpi = 0; + size_t pos = 0; + + (*encoding.enc->decode)(encoding.enc, encoding.data, + cp, &cpi, len, bytes, &pos, len); + + if(cpi > 0) { + printf("encout "); + for(int 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; + while(linep[0] == ' ') + linep++; + VTermModifier mod = strpe_modifiers(&linep); + sscanf(linep, " %x", &c); + + vterm_keyboard_unichar(vt, c, mod); + } + + else if(strstartswith(line, "INKEY ")) { + char *linep = line + 6; + while(linep[0] == ' ') + linep++; + VTermModifier mod = strpe_modifiers(&linep); + while(linep[0] == ' ') + linep++; + VTermKey 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, "FOCUS ")) { + char *linep = line + 6; + if(streq(linep, "IN")) + vterm_state_focus_in(state); + else if(streq(linep, "OUT")) + vterm_state_focus_out(state); + else + goto abort_line; + } + + else if(strstartswith(line, "MOUSEMOVE ")) { + char *linep = line + 10; + int row, col, len; + while(linep[0] == ' ') + linep++; + sscanf(linep, "%d,%d%n", &row, &col, &len); + linep += len; + while(linep[0] == ' ') + linep++; + VTermModifier 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; + while(linep[0] == ' ') + linep++; + sscanf(linep, "%c %d%n", &press, &button, &len); + linep += len; + while(linep[0] == ' ') + linep++; + VTermModifier mod = strpe_modifiers(&linep); + vterm_mouse_button(vt, button, (press == 'd' || press == 'D'), mod); + } + + else if(strstartswith(line, "SELECTION ")) { + char *linep = line + 10; + unsigned int mask; + int len; + VTermStringFragment frag = { 0 }; + sscanf(linep, "%x%n", &mask, &len); + linep += len; + while(linep[0] == ' ') + linep++; + if(linep[0] == '[') { + frag.initial = true; + linep++; + while(linep[0] == ' ') + linep++; + } + frag.len = inplace_hex2bytes(linep); + frag.str = linep; + linep += frag.len * 2; + while(linep[0] == ' ') + linep++; + if(linep[0] == ']') { + frag.final = true; + } + vterm_state_send_selection(state, mask, frag); + } + + 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 ")) { + char *linep = line + 5; + while(linep[0] == ' ') + linep++; + + VTermValue val; +#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")) { + print_color(&state_pen.foreground); + printf("\n"); + } + else if(streq(linep, "background")) { + print_color(&state_pen.background); + printf("\n"); + } + else + printf("?\n"); + } + else if(strstartswith(line, "?lineinfo ")) { + char *linep = line + 10; + int row; + const VTermLineInfo *info; + while(linep[0] == ' ') + linep++; + if(sscanf(linep, "%d", &row) < 1) { + printf("! lineinfo unrecognised input\n"); + goto abort_line; + } + info = vterm_state_get_lineinfo(state, row); + if(info->doublewidth) + printf("dwl "); + if(info->doubleheight) + printf("dhl "); + if(info->continuation) + printf("cont "); + 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); + vterm_screen_get_chars(screen, chars, len, rect); + for(size_t 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 + for(size_t 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; + VTermPos pos; + while(linep[0] == ' ') + linep++; + if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) { + printf("! screen_cell unrecognised input\n"); + goto abort_line; + } + VTermScreenCell cell; + if(!vterm_screen_get_cell(screen, pos, &cell)) + goto abort_line; + printf("{"); + for(int 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="); + vterm_screen_convert_color_to_rgb(screen, &cell.fg); + print_color(&cell.fg); + printf(" bg="); + vterm_screen_convert_color_to_rgb(screen, &cell.bg); + print_color(&cell.bg); + printf("\n"); + } + else if(strstartswith(line, "?screen_eol ")) { + char *linep = line + 12; + while(linep[0] == ' ') + linep++; + VTermPos pos; + 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 ")) { + char *linep = line + 21; + while(linep[0] == ' ') + linep++; + VTermPos pos; + if(sscanf(linep, "%d,%d\n", &pos.row, &pos.col) < 2) { + printf("! screen_attrs_extent unrecognised input\n"); + goto abort_line; + } + VTermRect rect = { + .start_col = 0, + .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; + + size_t outlen = vterm_output_get_buffer_current(vt); + if(outlen > 0) { + char outbuff[outlen]; + vterm_output_read(vt, outbuff, outlen); + + term_output(outbuff, outlen, NULL); + } + + printf(err ? "?\n" : "DONE\n"); + } + + vterm_free(vt); + + return 0; +} diff --git a/libvterm-0.2/t/run-test.pl b/libvterm-0.2/t/run-test.pl new file mode 100755 index 0000000..730eef3 --- /dev/null +++ b/libvterm-0.2/t/run-test.pl @@ -0,0 +1,228 @@ +#!/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; +my $EXECUTABLE = "t/.libs/harness"; +GetOptions( + 'valgrind|v+' => \$VALGRIND, + 'executable|e=s' => \$EXECUTABLE, + 'fail-early|F' => \(my $FAIL_EARLY), +) or exit 1; + +my ( $hin, $hout, $hpid ); +{ + local $ENV{LD_LIBRARY_PATH} = ".libs"; + my @command = $EXECUTABLE; + 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; + +my $linenum = 0; + +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 "# line $linenum: 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 "# line $linenum: Test failed\n" unless $fail_printed++; + print "# Expected: $expectation\n" . + "# Actual: $outline\n"; + } + + if( @expect ) { + print "# line $linenum: Test failed\n" unless $fail_printed++; + print "# Expected: $_\n" . + "# didn't happen\n" for @expect; + } + + $exitcode = 1 if $fail_printed; + exit $exitcode if $exitcode and $FAIL_EARLY; +} + +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; + } + elsif( $line =~ m/^(SELECTION \d+) +(\[?)(.*?)(\]?)$/ ) { + # we're evil + my $string = eval($3); + $line = "$1 $2 " . unpack( "H*", $string ) . " $4"; + } + + 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/^(osc) (\[\d+)? *(.*?)(\]?)$/ ) { + my ( $cmd, $initial, $data, $final ) = ( $1, $2, $3, $4 ); + $initial //= ""; + $initial .= ";" if $initial =~ m/\d+/; + + $line = "$cmd $initial" . join( "", map sprintf("%02x", $_), unpack "C*", length $data ? eval($data) : "" ) . "$final"; + } + elsif( $line =~ m/^(escape|dcs|apc|pm|sos) (\[?)(.*?)(\]?)$/ ) { + $line = "$1 $2" . join( "", map sprintf("%02x", $_), unpack "C*", length $3 ? eval($3) : "" ) . "$4"; + } + 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|selection-query) / ) { + # no conversion + } + elsif( $line =~ m/^(selection-set) (.*?) (\[?)(.*?)(\]?)$/ ) { + $line = "$1 $2 $3" . join( "", map sprintf("%02x", $_), unpack "C*", eval($4) ) . "$5"; + } + 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 "# line $linenum: Assert ?screen_row $row failed:\n" . + "# Expected: $want\n" . + "# Actual: $response\n"; + $exitcode = 1; + exit $exitcode if $exitcode and $FAIL_EARLY; + } + } + # Assertions start with '?' + elsif( $line =~ s/^\?([a-z]+.*?=)\s*// ) { + do_onetest if defined $command; + + my ( $assertion ) = $1 =~ m/^(.*)\s+=/; + my $expectation = $line; + + $hin->print( "\?$assertion\n" ); + my $response = <$hout>; defined $response or wait, die "Test harness failed - $?\n"; + chomp $response; $response =~ s/^\s+|\s+$//g; + + # Some convenience formatting + if( $assertion =~ m/^screen_chars/ and $expectation =~ m/^"/ ) { + $expectation = join ",", map sprintf("0x%02x", ord $_), split m//, eval($expectation); + } + + if( $response ne $expectation ) { + print "# line $linenum: Assert $assertion failed:\n" . + "# Expected: $expectation\n" . + "# Actual: $response\n"; + $exitcode = 1; + exit $exitcode if $exitcode and $FAIL_EARLY; + } + } + # 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> ) { + $linenum++; + $line =~ s/^\s+//; + chomp $line; + + next if $line =~ m/^(?:#|$)/; + last if $line eq "__END__"; + + 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; diff --git a/libvterm-0.2/tbl2inc_c.pl b/libvterm-0.2/tbl2inc_c.pl new file mode 100644 index 0000000..38c9963 --- /dev/null +++ b/libvterm-0.2/tbl2inc_c.pl @@ -0,0 +1,30 @@ +#!/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 = { + { .decode = &decode_table }, + { +EOF + +while( <> ) { + s/\s*#.*//; # strip comment + + s{^(\d+)/(\d+)}{sprintf "[0x%02x]", $1*16 + $2}e; # Convert 3/1 to [0x31] + s{"(.)"}{sprintf "0x%04x", ord $1}e; # Convert "A" to 0x41 + s{U\+}{0x}; # Convert U+0041 to 0x0041 + + s{$}{,}; # append comma + + print " $_"; +} + +print <<"EOF"; + } +}; +EOF diff --git a/libvterm-0.2/vterm.pc.in b/libvterm-0.2/vterm.pc.in new file mode 100644 index 0000000..8d7e59a --- /dev/null +++ b/libvterm-0.2/vterm.pc.in @@ -0,0 +1,8 @@ +libdir=@LIBDIR@ +includedir=@INCDIR@ + +Name: vterm +Description: Abstract VT220/Xterm/ECMA-48 emulation library +Version: @VERSION@ +Libs: -L${libdir} -lvterm +Cflags: -I${includedir} diff --git a/src/sdlterm.c b/src/sdlterm.c new file mode 100644 index 0000000..1d5aa66 --- /dev/null +++ b/src/sdlterm.c @@ -0,0 +1,826 @@ +/****************************************************************************** + * sdlterm + ****************************************************************************** + * SDL2 Terminal Emulator + * Based on libsdl2, libsdlfox, libvterm + * Written in C99 + * Copyright (C) 2020 Niklas Benfer + * License: MIT License + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*****************************************************************************/ + +#define lengthof(f) (sizeof(f)/sizeof(f[0])) +#define PROGNAME "sdlterm version 0.1" +#define COPYRIGHT "Copyright (c) 2020 Niklas Benfer " + +/*****************************************************************************/ + +typedef struct { + const char *exec; + char **args; + const char *fontpattern; + const char *boldfontpattern; + const char *renderer; + const char *windowflags[5]; + int nWindowFlags; + int fontsize; + int width; + int height; + int rows; + int columns; +} TERM_Config; + +typedef struct { + SDL_Window *window; + SDL_Renderer *renderer; + SDL_Cursor *pointer; + SDL_Surface *icon; + const Uint8 *keys; + struct { + const FOX_FontMetrics *metrics; + FOX_Font *regular; + FOX_Font *bold; + } font; + VTerm *vterm; + VTermScreen *screen; + VTermState *termstate; + struct { + SDL_Point position; + bool visible; + bool active; + Uint32 ticks; + } cursor; + struct { + SDL_Rect rect; + bool clicked; + } mouse; + struct { + Uint32 ticks; + bool active; + } bell; + Uint32 ticks; + bool dirty; + pid_t child; + int childfd; + TERM_Config cfg; +} TERM_State; + +/*****************************************************************************/ + +static int childState = 0; + +static const char help[] = { + PROGNAME"\n" + COPYRIGHT"\n" + "sdlterm usage:\n" + "\tsdlterm [option...]* [child options...]*\n" + "Options:\n" + " -h\tDisplay help text\n" + " -v\tDisplay program version\n" + " -x\tSet window width in pixels\n" + " -y\tSet window height in pixels\n" + " -f\tSet regular font via path (fontconfig pattern not yet supported)\n" + " -b\tSet bold font via path (fontconfig pattern not yet supported)\n" + " -s\tSet fontsize\n" + " -r\tSet SDL renderer backend\n" + " -l\tList available SDL renderer backends\n" + " -w\tSet SDL window flags\n" + " -e\tSet child process executable path\n" +}; + +static const char options[] = "hvlx:y:f:b:s:r:w:e:"; + +static const char version[] = { + PROGNAME"\n" + COPYRIGHT +}; + +static char clipboardbuffer[1024]; + +static Uint16 pixels[16*16] = { + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0aab, 0x0789, 0x0bcc, 0x0eee, 0x09aa, 0x099a, 0x0ddd, + 0x0fff, 0x0eee, 0x0899, 0x0fff, 0x0fff, 0x1fff, 0x0dde, 0x0dee, + 0x0fff, 0xabbc, 0xf779, 0x8cdd, 0x3fff, 0x9bbc, 0xaaab, 0x6fff, + 0x0fff, 0x3fff, 0xbaab, 0x0fff, 0x0fff, 0x6689, 0x6fff, 0x0dee, + 0xe678, 0xf134, 0x8abb, 0xf235, 0xf678, 0xf013, 0xf568, 0xf001, + 0xd889, 0x7abc, 0xf001, 0x0fff, 0x0fff, 0x0bcc, 0x9124, 0x5fff, + 0xf124, 0xf356, 0x3eee, 0x0fff, 0x7bbc, 0xf124, 0x0789, 0x2fff, + 0xf002, 0xd789, 0xf024, 0x0fff, 0x0fff, 0x0002, 0x0134, 0xd79a, + 0x1fff, 0xf023, 0xf000, 0xf124, 0xc99a, 0xf024, 0x0567, 0x0fff, + 0xf002, 0xe678, 0xf013, 0x0fff, 0x0ddd, 0x0fff, 0x0fff, 0xb689, + 0x8abb, 0x0fff, 0x0fff, 0xf001, 0xf235, 0xf013, 0x0fff, 0xd789, + 0xf002, 0x9899, 0xf001, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0xe789, + 0xf023, 0xf000, 0xf001, 0xe456, 0x8bcc, 0xf013, 0xf002, 0xf012, + 0x1767, 0x5aaa, 0xf013, 0xf001, 0xf000, 0x0fff, 0x7fff, 0xf124, + 0x0fff, 0x089a, 0x0578, 0x0fff, 0x089a, 0x0013, 0x0245, 0x0eff, + 0x0223, 0x0dde, 0x0135, 0x0789, 0x0ddd, 0xbbbc, 0xf346, 0x0467, + 0x0fff, 0x4eee, 0x3ddd, 0x0edd, 0x0dee, 0x0fff, 0x0fff, 0x0dee, + 0x0def, 0x08ab, 0x0fff, 0x7fff, 0xfabc, 0xf356, 0x0457, 0x0467, + 0x0fff, 0x0bcd, 0x4bde, 0x9bcc, 0x8dee, 0x8eff, 0x8fff, 0x9fff, + 0xadee, 0xeccd, 0xf689, 0xc357, 0x2356, 0x0356, 0x0467, 0x0467, + 0x0fff, 0x0ccd, 0x0bdd, 0x0cdd, 0x0aaa, 0x2234, 0x4135, 0x4346, + 0x5356, 0x2246, 0x0346, 0x0356, 0x0467, 0x0356, 0x0467, 0x0467, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff +}; + +/*****************************************************************************/ + +static Uint32 TERM_GetWindowFlags(TERM_Config *cfg); +static int TERM_GetRendererIndex(TERM_Config *cfg); +static void TERM_ListRenderBackends(void); +static int TERM_ParseArgs(TERM_Config *cfg, int argc, char **argv); +static int TERM_InitializeTerminal(TERM_State *state, TERM_Config *cfg); +static void TERM_DeinitializeTerminal(TERM_State *state); +static int TERM_HandleEvents(TERM_State *state); +static void TERM_Update(TERM_State *state); +static void TERM_RenderScreen(TERM_State *state); +static void TERM_RenderCell(TERM_State *state, int x, int y); +static void TERM_Resize(TERM_State *state, int width, int height); +static void TERM_SignalHandler(int signum); +static void swap(int *a, int *b); + +/*****************************************************************************/ + +int damage(VTermRect rect, void *user) { + return 1; +} +int moverect(VTermRect dest, VTermRect src, void *user) { + return 1; +} +int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) { + TERM_State *state = (TERM_State*)user; + state->cursor.position.x = pos.col; + state->cursor.position.y = pos.row; + if(visible == 0) { + state->cursor.active = false; + } else state->cursor.active = true; + return 1; +} +int settermprop(VTermProp prop, VTermValue *val, void *user) { + return 1; +} +int bell(void *user) { + TERM_State *state = (TERM_State*)user; + state->bell.active = true; + state->bell.ticks = state->ticks; + return 1; +} + +int sb_pushline(int cols, const VTermScreenCell *cells, void *user) { + return 1; +} +int sb_popline(int cols, VTermScreenCell *cells, void *user) { + return 1; +} + +VTermScreenCallbacks callbacks = { + .movecursor = movecursor, + .sb_pushline = sb_pushline, + .bell = bell +}; + +int main(int argc, char *argv[]) { + TERM_State state; + TERM_Config cfg = { + .exec = "/bin/bash", + .args = NULL, + .fontpattern = "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", + .boldfontpattern = "/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf", + .renderer = NULL, + .windowflags = {NULL}, + .nWindowFlags = 0, + .fontsize = 16, + .width = 800, + .height = 600, + .rows = 0, + .columns = 0 + }; + + if(TERM_ParseArgs(&cfg, argc, argv)) return -1; + if(TERM_InitializeTerminal(&state, &cfg)) return -1; + while(!TERM_HandleEvents(&state)) { + TERM_Update(&state); + SDL_Delay(10); + } + + TERM_DeinitializeTerminal(&state); + return 0; +} + +/*****************************************************************************/ + +Uint32 TERM_GetWindowFlags(TERM_Config *cfg) { + Uint32 flags = 0; + + static const char *names[] = { + "FULLSCREEN", "BORDERLESS", "RESIZABLE", "ONTOP", "MAXIMIZED" + }; + + static const SDL_WindowFlags values[] = { + SDL_WINDOW_FULLSCREEN, + SDL_WINDOW_BORDERLESS, + SDL_WINDOW_RESIZABLE, + SDL_WINDOW_ALWAYS_ON_TOP, + SDL_WINDOW_MAXIMIZED + }; + + for(int i = 0; i < cfg->nWindowFlags; i++) { + for(int k = 0; k < lengthof(values); k++) { + if(!strcasecmp(cfg->windowflags[i], names[k])) { + flags |= values[k]; + break; + } + } + } + + return flags; +} + +/*****************************************************************************/ + +int TERM_GetRendererIndex(TERM_Config *cfg) { + int renderer_index = -1; + if(cfg->renderer != NULL) { + int num = SDL_GetNumRenderDrivers(); + for(int i = 0; i < num; i++) { + SDL_RendererInfo info; + SDL_GetRenderDriverInfo(i, &info); + if(!strcmp(cfg->renderer, info.name)) { + renderer_index = i; + break; + } + } + } + + return renderer_index; +} + +/*****************************************************************************/ + +void TERM_ListRenderBackends(void) { + int num = SDL_GetNumRenderDrivers(); + puts("Available SDL renderer backends:"); + for(int i = 0; i < num; i++) { + SDL_RendererInfo info; + SDL_GetRenderDriverInfo(i, &info); + printf("%d: %s\n", i, info.name); + } +} + +/*****************************************************************************/ + +extern char* optarg; +extern int optind; + +int TERM_ParseArgs(TERM_Config *cfg, int argc, char **argv) { + int option; + int status = 0; + + while((option = getopt(argc, argv, options)) != -1) { + switch (option) { + case 'h': + puts(help); + status = 1; + break; + case 'v': + puts(version); + status = 1; + break; + case 'x': + if(optarg != NULL) cfg->width = strtol(optarg, NULL, 10); + break; + case 'y': + if(optarg != NULL) cfg->height = strtol(optarg, NULL, 10); + break; + case 'f': + if(optarg != NULL) cfg->fontpattern = optarg; + break; + case 'b': + if(optarg != NULL) cfg->boldfontpattern = optarg; + break; + case 'r': + if(optarg != NULL) cfg->renderer = optarg; + break; + case 'w': + if(optarg != NULL) { + cfg->windowflags[cfg->nWindowFlags++] = optarg; + } + break; + case 'e': + if(optarg != NULL) cfg->exec = optarg; + break; + case 'l': + TERM_ListRenderBackends(); + status = 1; + break; + default: + status = 1; + break; + } + } + if (optind < argc) { + cfg->args = &argv[optind]; + } + + return status; +} + +/*****************************************************************************/ + +int TERM_InitializeTerminal(TERM_State *state, TERM_Config *cfg) { + if(SDL_Init(SDL_INIT_VIDEO)) { + return -1; + } + + if(FOX_Init() != FOX_INITIALIZED) { + return -1; + } + + state->window = SDL_CreateWindow(PROGNAME, SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, cfg->width, cfg->height, + TERM_GetWindowFlags(cfg)); + if(state->window == NULL) { + return -1; + } + + state->renderer = SDL_CreateRenderer(state->window, + TERM_GetRendererIndex(cfg), 0); + if(state->renderer == NULL) { + SDL_DestroyWindow(state->window); + return -1; + } + + state->keys = SDL_GetKeyboardState(NULL); + SDL_StartTextInput(); + + state->font.regular = FOX_OpenFont(state->renderer, + cfg->fontpattern, cfg->fontsize); + if(state->font.regular == NULL) return -1; + + state->font.bold = FOX_OpenFont(state->renderer, + cfg->boldfontpattern, cfg->fontsize); + if(state->font.bold == NULL) return -1; + + state->pointer = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR); + if(state->pointer) SDL_SetCursor(state->pointer); + + state->icon = SDL_CreateRGBSurfaceFrom(pixels,16,16,16,16*2, + 0x0f00,0x00f0,0x000f,0xf000); + if(state->icon) SDL_SetWindowIcon(state->window, state->icon); + + state->font.metrics = FOX_QueryFontMetrics(state->font.regular); + state->ticks = SDL_GetTicks(); + state->cursor.visible = true; + state->cursor.active = true; + state->cursor.ticks = 0; + state->bell.active = false; + state->bell.ticks = 0; + + state->mouse.rect = (SDL_Rect){0}; + state->mouse.clicked = false; + + state->vterm = vterm_new(cfg->rows, cfg->columns); + vterm_set_utf8(state->vterm, 1); + state->screen = vterm_obtain_screen(state->vterm); + vterm_screen_reset(state->screen, 1); + state->termstate = vterm_obtain_state(state->vterm); + vterm_screen_set_callbacks(state->screen, &callbacks, state); + + state->cfg = *cfg; + + state->child = forkpty(&state->childfd, NULL, NULL, NULL); + if(state->child < 0) return -1; + else if(state->child == 0) { + execvp(cfg->exec, cfg->args); + exit(0); + } else { + struct sigaction action = {0}; + action.sa_handler = TERM_SignalHandler; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaction(SIGCHLD, &action, NULL); + childState = 1; + } + + TERM_Resize(state, cfg->width, cfg->height); + state->dirty = true; + return 0; +} + +/*****************************************************************************/ + +void TERM_DeinitializeTerminal(TERM_State *state) { + pid_t wpid; + int wstatus; + kill(state->child, SIGKILL); + do { + wpid = waitpid(state->child, &wstatus, WUNTRACED | WCONTINUED); + if(wpid == -1) break; + } while(!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); + state->child = wpid; + vterm_free(state->vterm); + FOX_CloseFont(state->font.bold); + FOX_CloseFont(state->font.regular); + SDL_FreeSurface(state->icon); + SDL_FreeCursor(state->pointer); + SDL_DestroyRenderer(state->renderer); + SDL_DestroyWindow(state->window); + FOX_Exit(); + SDL_Quit(); +} + +/*****************************************************************************/ + +static void TERM_HandleWindowEvent(TERM_State *state, SDL_Event *event) { + switch(event->window.event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + TERM_Resize(state, event->window.data1, event->window.data2); + break; + } +} + +static void TERM_HandleKeyEvent(TERM_State *state, SDL_Event *event) { + const char *cmd = NULL; + + if(state->keys[SDL_SCANCODE_LCTRL]) { + int mod = SDL_toupper(event->key.keysym.sym); + if(mod >= 'A' && mod <= 'Z') { + char ch = mod - 'A' + 1; + write(state->childfd, &ch, sizeof(ch)); + return; + } + } + + switch(event->key.keysym.sym) { + + case SDLK_ESCAPE: + cmd = "\033"; + break; + + case SDLK_LEFT: + cmd = "\033[D"; + break; + + case SDLK_RIGHT: + cmd = "\033[C"; + break; + + case SDLK_UP: + cmd = "\033[A"; + break; + + case SDLK_DOWN: + cmd = "\033[B"; + break; + + case SDLK_PAGEDOWN: + cmd = "\033[6~"; + break; + + case SDLK_PAGEUP: + cmd = "\033[5~"; + break; + + case SDLK_RETURN: + cmd = "\r"; + break; + + case SDLK_INSERT: + cmd = "\033[2~"; + break; + + case SDLK_DELETE: + cmd = "\033[3~"; + break; + + case SDLK_BACKSPACE: + cmd = "\b"; + break; + + case SDLK_TAB: + cmd = "\t"; + break; + + case SDLK_F1: + cmd = "\033OP"; + break; + + case SDLK_F2: + cmd = "\033OQ"; + break; + + case SDLK_F3: + cmd = "\033OR"; + break; + + case SDLK_F4: + cmd = "\033OS"; + break; + + case SDLK_F5: + cmd = "\033[15~"; + break; + + case SDLK_F6: + cmd = "\033[17~"; + break; + + case SDLK_F7: + cmd = "\033[18~"; + break; + + case SDLK_F8: + cmd = "\033[19~"; + break; + + case SDLK_F9: + cmd = "\033[20~"; + break; + + case SDLK_F10: + cmd = "\033[21~"; + break; + + case SDLK_F11: + cmd = "\033[23~"; + break; + + case SDLK_F12: + cmd = "\033[24~"; + break; + + } + + if(cmd) { + write(state->childfd, cmd, SDL_strlen(cmd)); + } +} + +static void TERM_HandleChildEvents(TERM_State *state) { + fd_set rfds; + struct timeval tv = {0}; + + FD_ZERO(&rfds); + FD_SET(state->childfd, &rfds); + + tv.tv_sec = 0; + tv.tv_usec = 50000; + + if(select(state->childfd+1, &rfds, NULL, NULL, &tv) > 0) { + char line[256]; + int n; + if((n = read(state->childfd, line, sizeof(line))) > 0) { + vterm_input_write(state->vterm, line, n); + state->dirty = true; + //vterm_screen_flush_damage(state->screen); + } + } +} + +int TERM_HandleEvents(TERM_State *state) { + SDL_Event event; + int status = 0; + + if(childState == 0) { + SDL_Event event; + event.type = SDL_QUIT; + SDL_PushEvent(&event); + } + + TERM_HandleChildEvents(state); + + while(SDL_PollEvent(&event)) switch(event.type) { + + case SDL_QUIT: + status = 1; + break; + + case SDL_WINDOWEVENT: + TERM_HandleWindowEvent(state, &event); + break; + + case SDL_KEYDOWN: + TERM_HandleKeyEvent(state, &event); + break; + + case SDL_TEXTINPUT: + write(state->childfd, event.edit.text, SDL_strlen(event.edit.text)); + break; + + case SDL_MOUSEMOTION: + if(state->mouse.clicked) { + state->mouse.rect.w = event.motion.x - state->mouse.rect.x; + state->mouse.rect.h = event.motion.y - state->mouse.rect.y; + } + break; + + case SDL_MOUSEBUTTONDOWN: + if(state->mouse.clicked) { + + } else if(event.button.button == SDL_BUTTON_LEFT) { + state->mouse.clicked = true; + SDL_GetMouseState(&state->mouse.rect.x, &state->mouse.rect.y); + state->mouse.rect.w = 0; + state->mouse.rect.h = 0; + } + break; + + case SDL_MOUSEBUTTONUP: + if(event.button.button == SDL_BUTTON_RIGHT) { + char *clipboard = SDL_GetClipboardText(); + write(state->childfd, clipboard, SDL_strlen(clipboard)); + SDL_free(clipboard); + } else if(event.button.button == SDL_BUTTON_LEFT) { + VTermRect rect = { + .start_col = state->mouse.rect.x / state->font.metrics->max_advance, + .start_row = state->mouse.rect.y / state->font.metrics->height, + .end_col = (state->mouse.rect.x + state->mouse.rect.w) / state->font.metrics->max_advance, + .end_row = (state->mouse.rect.y + state->mouse.rect.h) / state->font.metrics->height + }; + if(rect.start_col > rect.end_col) swap(&rect.start_col, &rect.end_col); + if(rect.start_row > rect.end_row) swap(&rect.start_row, &rect.end_row); + size_t n = vterm_screen_get_text(state->screen, clipboardbuffer, + sizeof(clipboardbuffer), rect); + if(n >= sizeof(clipboardbuffer)) n = sizeof(clipboardbuffer) - 1; + clipboardbuffer[n] = '\0'; + SDL_SetClipboardText(clipboardbuffer); + state->mouse.clicked = false; + } + break; + + case SDL_MOUSEWHEEL: { + int size = state->cfg.fontsize; + size += event.wheel.y; + FOX_CloseFont(state->font.regular); + FOX_CloseFont(state->font.bold); + state->font.regular = FOX_OpenFont(state->renderer, + state->cfg.fontpattern, size); + if(state->font.regular == NULL) return -1; + + state->font.bold = FOX_OpenFont(state->renderer, + state->cfg.boldfontpattern, size); + if(state->font.bold == NULL) return -1; + state->font.metrics = FOX_QueryFontMetrics(state->font.regular); + TERM_Resize(state, state->cfg.width, state->cfg.height); + state->cfg.fontsize = size; + break; + } + + } + + return status; +} + +/*****************************************************************************/ + +void TERM_Update(TERM_State *state) { + state->ticks = SDL_GetTicks(); + + if(state->dirty) { + SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, 255); + SDL_RenderClear(state->renderer); + TERM_RenderScreen(state); + } + + if(state->ticks > (state->cursor.ticks + 250)) { + state->cursor.ticks = state->ticks; + state->cursor.visible = !state->cursor.visible; + state->dirty = true; + } + + if(state->bell.active && (state->ticks > (state->bell.ticks + 250))) { + state->bell.active = false; + } + + if(state->mouse.clicked) { + SDL_RenderDrawRect(state->renderer, &state->mouse.rect); + } + + SDL_RenderPresent(state->renderer); +} + +/*****************************************************************************/ + +static void TERM_RenderCursor(TERM_State *state) { + if(state->cursor.active && state->cursor.visible) { + SDL_Rect rect = { + state->cursor.position.x * state->font.metrics->max_advance, + 4 + state->cursor.position.y * state->font.metrics->height, + 4, + state->font.metrics->height + }; + SDL_RenderFillRect(state->renderer, &rect); + } +} + +void TERM_RenderScreen(TERM_State *state) { + SDL_SetRenderDrawColor(state->renderer, 255, 255, 255, 255); + for(unsigned y = 0; y < state->cfg.rows; y++) { + for(unsigned x = 0; x < state->cfg.columns; x++) { + TERM_RenderCell(state, x, y); + } + } + + TERM_RenderCursor(state); + state->dirty = false; + + if(state->bell.active) { + SDL_Rect rect = {0, 0, state->cfg.width, state->cfg.height}; + SDL_RenderDrawRect(state->renderer, &rect); + } +} + +/*****************************************************************************/ + +void TERM_RenderCell(TERM_State *state, int x, int y) { + FOX_Font *font = state->font.regular; + VTermScreenCell cell; + VTermPos pos = {.row = y, .col = x}; + SDL_Point cursor = { + x * state->font.metrics->max_advance, + y * state->font.metrics->height + }; + + vterm_screen_get_cell(state->screen, pos, &cell); + Uint32 ch = cell.chars[0]; + if(ch == 0) return; + + vterm_state_convert_color_to_rgb(state->termstate, &cell.fg); + vterm_state_convert_color_to_rgb(state->termstate, &cell.bg); + SDL_Color color = {cell.fg.rgb.red, cell.fg.rgb.green, cell.fg.rgb.blue, 255}; + if(cell.attrs.reverse) { + SDL_Rect rect = {cursor.x, cursor.y+4, + state->font.metrics->max_advance, + state->font.metrics->height + }; + SDL_SetRenderDrawColor(state->renderer, color.r, color.g, color.b, color.a); + color.r = ~color.r; + color.g = ~color.g; + color.b = ~color.b; + SDL_RenderFillRect(state->renderer, &rect); + } + + if(cell.attrs.bold) font = state->font.bold; + else if(cell.attrs.italic); + + SDL_SetRenderDrawColor(state->renderer, color.r, color.g, color.b, color.a); + FOX_RenderChar(font, ch, 0, &cursor); +} + +/*****************************************************************************/ + +void TERM_Resize(TERM_State *state, int width, int height) { + int cols = width / (state->font.metrics->max_advance); + int rows = height / state->font.metrics->height; + state->cfg.width = width; + state->cfg.height = height; + if(rows != state->cfg.rows || cols != state->cfg.columns) { + state->cfg.rows = rows; + state->cfg.columns = cols; + vterm_set_size(state->vterm, state->cfg.rows, state->cfg.columns); + + struct winsize ws = {0}; + ws.ws_col = state->cfg.columns; + ws.ws_row = state->cfg.rows; + ioctl(state->childfd, TIOCSWINSZ, &ws); + } +} + +/*****************************************************************************/ + +void TERM_SignalHandler(int signum) { + childState = 0; +} + +/*****************************************************************************/ + +void swap(int *a, int *b) { + int tmp = *a; + *a = *b; + *b = tmp; +} + +/*****************************************************************************/ + +/*****************************************************************************/ \ No newline at end of file