├── .gitignore ├── README.md ├── example ├── Makefile └── parser.c ├── setup.py ├── COPYRIGHT ├── run_tests.sh ├── src ├── hl_vt100.h ├── test.c ├── lw_terminal_vt100.h ├── hl_vt100.c ├── lw_terminal_parser.h ├── lw_terminal_parser.c └── lw_terminal_vt100.c ├── Makefile ├── hl_vt100.i └── doc └── vt100_emulator.3 /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | parser 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vt100 emulator 2 | 3 | My headless VT100 emulator moved to another forge: 4 | 5 | https://git.afpy.org/mdk/hl-vt100 6 | 7 | Because I'm not happy with hosting open-source software on a 8 | close-source forge. 9 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | ## 2 | ## Makefile for lw_vt100 examples 3 | ## 4 | ## Made by julien palard 5 | ## 6 | 7 | NAME = parser 8 | 9 | SRC = parser.c ../src/lw_terminal_parser.c 10 | OBJ = $(SRC:.c=.o) 11 | CC = gcc 12 | INCLUDE = ../src 13 | DEFINE = _GNU_SOURCE 14 | CFLAGS = -g3 -Wextra -Wstrict-prototypes -Wall -ansi -pedantic -I$(INCLUDE) 15 | LIB = -lutil 16 | RM = rm -f 17 | 18 | $(NAME): $(OBJ) 19 | $(CC) $(OBJ) $(LIB) -o $(NAME) 20 | 21 | all: 22 | @make $(NAME) 23 | 24 | .c.o: 25 | $(CC) -D $(DEFINE) -c $(CFLAGS) $< -o $(<:.c=.o) 26 | 27 | clean: 28 | $(RM) $(NAME) *~ \#*\# *.o *core 29 | 30 | re: clean all 31 | 32 | check-syntax: 33 | gcc -Isrc -Wall -Wextra -ansi -pedantic -o /dev/null -S ${CHK_SOURCES} 34 | -------------------------------------------------------------------------------- /example/parser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../src/lw_terminal_parser.h" 4 | 5 | static void vt100_write(struct lw_terminal *term_emul __attribute__((unused)), 6 | char c) 7 | { 8 | printf("Got a char : %c\n", c); 9 | } 10 | 11 | static void csi_f(struct lw_terminal *term_emul) 12 | { 13 | printf("\\033[...f with %d parameters\n", term_emul->argc); 14 | } 15 | 16 | static void csi_K(struct lw_terminal *term_emul) 17 | { 18 | printf("\\033[...K with %d parameters\n", term_emul->argc); 19 | } 20 | 21 | int main(void) 22 | { 23 | struct lw_terminal *lw_terminal; 24 | 25 | lw_terminal = lw_terminal_parser_init(); 26 | if (lw_terminal == NULL) 27 | return EXIT_FAILURE; 28 | lw_terminal->write = vt100_write; 29 | lw_terminal->callbacks.csi.f = csi_f; 30 | lw_terminal->callbacks.csi.K = csi_K; 31 | lw_terminal_parser_read_str(lw_terminal, "\033[2KHello world !\033[f"); 32 | return EXIT_SUCCESS; 33 | } 34 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | setup.py file for hl_vt100 5 | """ 6 | 7 | from distutils.core import setup, Extension 8 | 9 | 10 | hl_vt100_module = Extension('_hl_vt100', 11 | sources=['hl_vt100_wrap.c', 12 | 'src/hl_vt100.c', 13 | 'src/lw_terminal_parser.c', 14 | 'src/lw_terminal_vt100.c']) 15 | 16 | setup(name='hl_vt100', 17 | version='0.1', 18 | url='https://github.com/JulienPalard/vt100-emulator', 19 | author="Julien Palard", 20 | author_email='julien@palard.fr', 21 | description="""Headless vt100 emulator""", 22 | ext_modules=[hl_vt100_module], 23 | py_modules=["hl_vt100"], 24 | classifiers=[ 25 | "Programming Language :: C", 26 | "Programming Language :: Python", 27 | "Development Status :: 5 - Production/Stable", 28 | "License :: OSI Approved :: BSD License", 29 | "Operating System :: OS Independent", 30 | "Topic :: System :: Emulators", 31 | "Topic :: Terminals :: Terminal Emulators/X Terminals" 32 | ]) 33 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | vt100-emulator is distributed under the following terms: 2 | 3 | Copyright (c) 2016 Palard Julien. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (c) 2016 Julien Palard. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 1. Redistributions of source code must retain the above copyright 10 | # notice, this list of conditions and the following disclaimer. 11 | # 2. Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | if [ "$1" = python ] 27 | then 28 | if ! which swig > /dev/null 29 | then 30 | echo "You should install swig !" 31 | exit 1 32 | fi 33 | make python_module 34 | if [ $? != 0 ] 35 | then 36 | echo "Failed to build python module" 37 | exit 1 38 | fi 39 | cat < 32 | #include 33 | #include 34 | #include 35 | #include "lw_terminal_vt100.h" 36 | 37 | struct vt100_headless 38 | { 39 | int master; 40 | struct termios backup; 41 | struct lw_terminal_vt100 *term; 42 | int should_quit; 43 | void (*changed)(struct vt100_headless *this); 44 | }; 45 | 46 | 47 | void vt100_headless_fork(struct vt100_headless *this, const char *progname, char **argv); 48 | int vt100_headless_main_loop(struct vt100_headless *this); 49 | void delete_vt100_headless(struct vt100_headless *this); 50 | struct vt100_headless *new_vt100_headless(void); 51 | const char **vt100_headless_getlines(struct vt100_headless *this); 52 | void vt100_headless_stop(struct vt100_headless *this); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "hl_vt100.h" 33 | 34 | void disp(struct vt100_headless *vt100) 35 | { 36 | unsigned int y; 37 | const char **lines; 38 | 39 | lines = vt100_headless_getlines(vt100); 40 | write(1, "\n", 1); 41 | for (y = 0; y < vt100->term->height; ++y) 42 | { 43 | write(1, "|", 1); 44 | write(1, lines[y], vt100->term->width); 45 | write(1, "|\n", 2); 46 | } 47 | } 48 | 49 | int main(int ac, char **av) 50 | { 51 | struct vt100_headless *vt100_headless; 52 | 53 | if (ac == 1) 54 | { 55 | puts("Usage: test PROGNAME"); 56 | return EXIT_FAILURE; 57 | } 58 | vt100_headless = new_vt100_headless(); 59 | vt100_headless->changed = disp; 60 | vt100_headless_fork(vt100_headless, av[1], (av + 1)); 61 | vt100_headless_main_loop(vt100_headless); 62 | return EXIT_SUCCESS; 63 | } 64 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## 2 | ## Makefile for vt100 3 | ## 4 | ## Made by julien palard 5 | ## Login 6 | ## 7 | ## Copyright (c) 2016 Julien Palard. 8 | ## All rights reserved. 9 | ## 10 | ## Redistribution and use in source and binary forms, with or without 11 | ## modification, are permitted provided that the following conditions 12 | ## are met: 13 | ## 1. Redistributions of source code must retain the above copyright 14 | ## notice, this list of conditions and the following disclaimer. 15 | ## 2. Redistributions in binary form must reproduce the above copyright 16 | ## notice, this list of conditions and the following disclaimer in the 17 | ## documentation and/or other materials provided with the distribution. 18 | ## 19 | ## THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 | ## IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 | ## OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | ## IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | ## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | ## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | ## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | ## THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | ## 30 | 31 | NAME = vt100 32 | VERSION = 0 33 | MINOR = 0 34 | RELEASE = 0 35 | 36 | LINKERNAME = lib$(NAME).so 37 | SONAME = $(LINKERNAME).$(VERSION) 38 | REALNAME = $(SONAME).$(MINOR).$(RELEASE) 39 | 40 | SRC = src/lw_terminal_parser.c src/lw_terminal_vt100.c src/hl_vt100.c 41 | SRC_TEST = src/test.c 42 | OBJ = $(SRC:.c=.o) 43 | OBJ_TEST = $(SRC_TEST:.c=.o) 44 | CC = gcc 45 | INCLUDE = src 46 | DEFINE = _GNU_SOURCE 47 | CFLAGS = -DNDEBUG -g3 -Wextra -Wstrict-prototypes -Wall -ansi -pedantic -fPIC -I$(INCLUDE) 48 | LIB = -lutil 49 | RM = rm -f 50 | 51 | $(NAME): $(OBJ) 52 | $(CC) --shared $(OBJ) $(LIB) -o $(LINKERNAME) 53 | 54 | test: $(OBJ_TEST) 55 | $(CC) $(OBJ_TEST) -L . -l$(NAME) -o test 56 | 57 | python_module: 58 | swig -python -threads *.i 59 | 60 | all: 61 | @make $(NAME) 62 | 63 | .c.o: 64 | $(CC) -D $(DEFINE) -c $(CFLAGS) $< -o $(<:.c=.o) 65 | 66 | clean_python_module: 67 | $(RM) *.pyc *.so hl_vt100_wrap.c hl_vt100.py 68 | $(RM) -r build 69 | 70 | clean: clean_python_module 71 | $(RM) $(LINKERNAME) test src/*~ *~ src/\#*\# src/*.o \#*\# *.o *core 72 | 73 | re: clean all 74 | 75 | check-syntax: 76 | gcc -Isrc -Wall -Wextra -ansi -pedantic -o /dev/null -S ${CHK_SOURCES} 77 | -------------------------------------------------------------------------------- /hl_vt100.i: -------------------------------------------------------------------------------- 1 | %module hl_vt100 2 | /* 3 | * Copyright (c) 2016 Julien Palard. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | %{ 28 | #include "src/lw_terminal_vt100.h" 29 | #include "src/hl_vt100.h" 30 | %} 31 | 32 | %typemap(in) char ** { 33 | /* Check if is a list */ 34 | if (PyList_Check($input)) { 35 | int size = PyList_Size($input); 36 | int i = 0; 37 | $1 = (char **) malloc((size+1)*sizeof(char *)); 38 | for (i = 0; i < size; i++) { 39 | PyObject *o = PyList_GetItem($input,i); 40 | if (PyString_Check(o)) 41 | $1[i] = PyString_AsString(PyList_GetItem($input,i)); 42 | else { 43 | PyErr_SetString(PyExc_TypeError,"list must contain strings"); 44 | free($1); 45 | return NULL; 46 | } 47 | } 48 | $1[i] = 0; 49 | } else { 50 | PyErr_SetString(PyExc_TypeError,"not a list"); 51 | return NULL; 52 | } 53 | } 54 | 55 | %typemap(out) char ** { 56 | /* Check if is a list */ 57 | int i; 58 | 59 | $result = PyList_New(0); 60 | for (i = 0; i < arg1->term->height; i++) 61 | PyList_Append($result, PyString_FromStringAndSize($1[i], arg1->term->width)); 62 | } 63 | 64 | 65 | // This cleans up the char ** array we malloc'd before the function call 66 | %typemap(freearg) char ** { 67 | free((char *) $1); 68 | } 69 | 70 | struct vt100_headless 71 | { 72 | void (*changed)(struct vt100_headless *this); 73 | %immutable; 74 | int master; 75 | struct termios backup; 76 | struct lw_terminal_vt100 *term; 77 | %extend { 78 | vt100_headless(); 79 | ~vt100_headless(); 80 | void fork(const char *progname, char **argv); 81 | char **getlines(); 82 | int main_loop(); 83 | void stop(); 84 | } 85 | }; 86 | -------------------------------------------------------------------------------- /doc/vt100_emulator.3: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" First parameter, NAME, should be all caps 3 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection 4 | .\" other parameters are allowed: see man(7), man(1) 5 | .TH lw_terminal_parser 3 2011-09-27 6 | .SH NAME 7 | lw_terminal_parser_init, lw_terminal_read, lw_terminal_parser_read_str, lw_terminal_destroy \- LW Terminal Parser 8 | .SH SYNOPSIS 9 | .B #include 10 | .sp 11 | .BI "struct lw_terminal *lw_terminal_parser_init(void);" 12 | .br 13 | .BI "void lw_terminal_read(struct lw_terminal *" this ", char " c ");" 14 | .br 15 | .BI "void lw_terminal_parser_read_str(struct lw_terminal *" this " , char *" c ");" 16 | .br 17 | .BI "void lw_terminal_destroy(struct lw_terminal* " this ");" 18 | .SH DESCRIPTION 19 | lw_terminal_parser is a library to parse escape sequences commonly sent to terminals. The functions in lw_terminal_parser allows you to create, send data, and destroy a terminal parser. The function 20 | .BR lw_terminal_parser_init () 21 | allocates and prepare a new struct lw_terminal for you. Once a lw_terminal initialized you should hook your callbacks for escape sequences and write in lw_terminal->callbacks and lw_terminal->write. The you should call 22 | .BR lw_terminal_parser_read_str() 23 | or 24 | .BR lw_terminal_read() 25 | to make the terminal parse them. 26 | Finally to free the struct terminal, call 27 | .BR lw_terminal_destroy(). 28 | .PP 29 | lw_terminal->callback is a structure for you to hook into escape sequences. 30 | This struct is broke into substructures for each type of sequencec : esc, csi, hash, and scs. 31 | Each substructure is a struct ascii_callback that have one member for each ascii character, in order, starting from 0x30, '0'. Members from '9' to '9' are named "n0" to "n9", letters just have their name, and others characters are of the form hXX where XX is their hexadecimal notation. 32 | .PP 33 | Here is simple an example on how to hook a callback into a terminal emulator : 34 | .nf 35 | 36 | #include 37 | #include 38 | #include "../src/lw_terminal_parser.h" 39 | 40 | static void vt100_write(struct lw_terminal *term_emul __attribute__((unused)), 41 | char c) 42 | { 43 | printf("Got a char : %c\\n", c); 44 | } 45 | 46 | static void csi_f(struct lw_terminal *term_emul) 47 | { 48 | printf("\\\\033[...f with %d parameters\\n", term_emul->argc); 49 | } 50 | 51 | static void csi_K(struct lw_terminal *term_emul) 52 | { 53 | printf("\\\\033[...K with %d parameters\\n", term_emul->argc); 54 | } 55 | 56 | int main(void) 57 | { 58 | struct lw_terminal *lw_terminal; 59 | 60 | lw_terminal = lw_terminal_parser_init(); 61 | if (lw_terminal == NULL) 62 | return EXIT_FAILURE; 63 | lw_terminal->write = vt100_write; 64 | lw_terminal->callbacks.csi.f = csi_f; 65 | lw_terminal->callbacks.csi.K = csi_K; 66 | lw_terminal_parser_read_str(lw_terminal, "\\033[2KHello world !\\033[f"); 67 | return EXIT_SUCCESS; 68 | } 69 | .fi 70 | 71 | .br 72 | .SH "AUTHOR" 73 | lw_terminal_parser was written by Julien Palard. 74 | .PP 75 | This manual page was written by Julien Palard , 76 | for the Debian project (and may be used by others). 77 | -------------------------------------------------------------------------------- /src/lw_terminal_vt100.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __LW_TERMINAL_VT100_H__ 27 | #define __LW_TERMINAL_VT100_H__ 28 | 29 | #include 30 | #include "lw_terminal_parser.h" 31 | 32 | /* 33 | * Source : http://vt100.net/docs/vt100-ug/chapter3.html 34 | http://vt100.net/docs/tp83/appendixb.html 35 | * It's a vt100 implementation, that implements ANSI control function. 36 | */ 37 | 38 | #define SCROLLBACK 3 39 | 40 | #define MASK_LNM 1 41 | #define MASK_DECCKM 2 42 | #define MASK_DECANM 4 43 | #define MASK_DECCOLM 8 44 | #define MASK_DECSCLM 16 45 | #define MASK_DECSCNM 32 46 | #define MASK_DECOM 64 47 | #define MASK_DECAWM 128 48 | #define MASK_DECARM 256 49 | #define MASK_DECINLM 512 50 | 51 | #define LNM 20 52 | #define DECCKM 1 53 | #define DECANM 2 54 | #define DECCOLM 3 55 | #define DECSCLM 4 56 | #define DECSCNM 5 57 | #define DECOM 6 58 | #define DECAWM 7 59 | #define DECARM 8 60 | #define DECINLM 9 61 | 62 | 63 | #define SET_MODE(vt100, mode) ((vt100)->modes |= get_mode_mask(mode)) 64 | #define UNSET_MODE(vt100, mode) ((vt100)->modes &= ~get_mode_mask(mode)) 65 | #define MODE_IS_SET(vt100, mode) ((vt100)->modes & get_mode_mask(mode)) 66 | 67 | /* 68 | ** frozen_screen is the frozen part of the screen 69 | ** when margins are set. 70 | ** The top of the frozen_screen holds the top margin 71 | ** while the bottom holds the bottom margin. 72 | */ 73 | struct lw_terminal_vt100 74 | { 75 | struct lw_terminal *lw_terminal; 76 | unsigned int width; 77 | unsigned int height; 78 | unsigned int x; 79 | unsigned int y; 80 | unsigned int saved_x; 81 | unsigned int saved_y; 82 | unsigned int margin_top; 83 | unsigned int margin_bottom; 84 | unsigned int top_line; /* Line at the top of the display */ 85 | char *screen; 86 | char *frozen_screen; 87 | char *tabulations; 88 | unsigned int selected_charset; 89 | unsigned int modes; 90 | char *lines[80]; 91 | void (*master_write)(void *user_data, void *buffer, size_t len); 92 | void *user_data; 93 | pthread_mutex_t mutex; 94 | }; 95 | 96 | struct lw_terminal_vt100 *lw_terminal_vt100_init(void *user_data, 97 | void (*unimplemented)(struct lw_terminal* term_emul, 98 | char *seq, char chr)); 99 | char lw_terminal_vt100_get(struct lw_terminal_vt100 *vt100, unsigned int x, unsigned int y); 100 | const char **lw_terminal_vt100_getlines(struct lw_terminal_vt100 *vt100); 101 | void lw_terminal_vt100_destroy(struct lw_terminal_vt100 *this); 102 | void lw_terminal_vt100_read_str(struct lw_terminal_vt100 *this, char *buffer); 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /src/hl_vt100.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #define _XOPEN_SOURCE 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "hl_vt100.h" 33 | 34 | struct vt100_headless *new_vt100_headless(void) 35 | { 36 | return calloc(1, sizeof(struct vt100_headless)); 37 | } 38 | 39 | void delete_vt100_headless(struct vt100_headless *this) 40 | { 41 | free(this); 42 | } 43 | 44 | static void set_non_canonical(struct vt100_headless *this, int fd) 45 | { 46 | struct termios termios; 47 | 48 | ioctl(fd, TCGETS, &this->backup); 49 | ioctl(fd, TCGETS, &termios); 50 | termios.c_iflag |= ICANON; 51 | termios.c_cc[VMIN] = 1; 52 | termios.c_cc[VTIME] = 0; 53 | ioctl(fd, TCSETS, &termios); 54 | } 55 | 56 | static void restore_termios(struct vt100_headless *this, int fd) 57 | { 58 | ioctl(fd, TCSETS, &this->backup); 59 | } 60 | 61 | #ifndef NDEBUG 62 | static void strdump(char *str) 63 | { 64 | while (*str != '\0') 65 | { 66 | if (*str >= ' ' && *str <= '~') 67 | fprintf(stderr, "%c", *str); 68 | else 69 | fprintf(stderr, "\\0%o", *str); 70 | str += 1; 71 | } 72 | fprintf(stderr, "\n"); 73 | } 74 | #endif 75 | 76 | void vt100_headless_stop(struct vt100_headless *this) 77 | { 78 | this->should_quit = 1; 79 | } 80 | 81 | int vt100_headless_main_loop(struct vt100_headless *this) 82 | { 83 | char buffer[4096]; 84 | fd_set rfds; 85 | int retval; 86 | ssize_t read_size; 87 | 88 | while (!this->should_quit) 89 | { 90 | FD_ZERO(&rfds); 91 | FD_SET(this->master, &rfds); 92 | FD_SET(0, &rfds); 93 | retval = select(this->master + 1, &rfds, NULL, NULL, NULL); 94 | if (retval == -1) 95 | { 96 | perror("select()"); 97 | } 98 | if (FD_ISSET(0, &rfds)) 99 | { 100 | read_size = read(0, &buffer, 4096); 101 | if (read_size == -1) 102 | { 103 | perror("read"); 104 | return EXIT_FAILURE; 105 | } 106 | buffer[read_size] = '\0'; 107 | write(this->master, buffer, read_size); 108 | } 109 | if (FD_ISSET(this->master, &rfds)) 110 | { 111 | read_size = read(this->master, &buffer, 4096); 112 | if (read_size == -1) 113 | { 114 | perror("read"); 115 | return EXIT_FAILURE; 116 | } 117 | buffer[read_size] = '\0'; 118 | #ifndef NDEBUG 119 | strdump(buffer); 120 | #endif 121 | lw_terminal_vt100_read_str(this->term, buffer); 122 | if (this->changed != NULL) 123 | this->changed(this); 124 | } 125 | } 126 | return EXIT_SUCCESS; 127 | } 128 | 129 | void master_write(void *user_data, void *buffer, size_t len) 130 | { 131 | struct vt100_headless *this; 132 | 133 | this = (struct vt100_headless*)user_data; 134 | write(this->master, buffer, len); 135 | } 136 | 137 | const char **vt100_headless_getlines(struct vt100_headless *this) 138 | { 139 | return lw_terminal_vt100_getlines(this->term); 140 | } 141 | 142 | void vt100_headless_fork(struct vt100_headless *this, 143 | const char *progname, 144 | char **argv) 145 | { 146 | int child; 147 | struct winsize winsize; 148 | 149 | set_non_canonical(this, 0); 150 | winsize.ws_row = 24; 151 | winsize.ws_col = 80; 152 | child = forkpty(&this->master, NULL, NULL, NULL); 153 | if (child == CHILD) 154 | { 155 | setsid(); 156 | putenv("TERM=vt100"); 157 | execvp(progname, argv); 158 | return ; 159 | } 160 | else 161 | { 162 | this->term = lw_terminal_vt100_init(this, lw_terminal_parser_default_unimplemented); 163 | this->term->master_write = master_write; 164 | ioctl(this->master, TIOCSWINSZ, &winsize); 165 | } 166 | restore_termios(this, 0); 167 | } 168 | -------------------------------------------------------------------------------- /src/lw_terminal_parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef __TERMINAL_H__ 27 | #define __TERMINAL_H__ 28 | 29 | /* 30 | ** 31 | ** Introduction 32 | ** ============ 33 | ** 34 | ** terminal.c is a basic level that parses escape sequences to call 35 | ** appropriated callbacks permiting you to implement a specific terminal. 36 | ** 37 | ** Callbacks 38 | ** ========= 39 | ** 40 | ** terminal.c maps sequences to callbacks in this way : 41 | ** \033... maps to terminal->callbacks->esc 42 | ** \033[... maps to terminal->callbacks->csi 43 | ** \033#... maps to terminal->callbacks->hash 44 | ** and \033( and \033) maps to terminal->callbacks->scs 45 | ** 46 | ** In 'callbacks', esc, csi, hash and scs are structs ascii_callbacks 47 | ** where you can bind your callbacks. 48 | ** 49 | ** Typically when terminal parses \033[42;43m 50 | ** it calls terminal->callbacks->csi->m(terminal); 51 | ** 52 | ** Parameters (here 42;43) are stored in terminal->argc and terminal->argv 53 | ** argv is an array of integers of length argc. 54 | ** 55 | ** Public members 56 | ** ============== 57 | ** 58 | ** void *user_data : 59 | ** A (void *) where your implementation can store whatever you want 60 | ** to get it back on your callabks. 61 | ** 62 | ** void (*write)(struct terminal *, char c) : 63 | ** Hook for your implementation to recieve chars that are not 64 | ** escape sequences 65 | ** 66 | ** struct term_callbacks callbacks : 67 | ** Hooks for your callbacks to recieve escape sequences 68 | ** 69 | ** enum term_state state : 70 | ** During a callback, typically a scs, you can read here if it's a 71 | ** G1SET or a G0SET 72 | ** 73 | ** unsigned int argc : 74 | ** For your callbacks, to know how many parameters are available 75 | ** in argv. 76 | ** 77 | ** unsigned int argv[TERM_STACK_SIZE] : 78 | ** For your callbacks, parameters of escape sequences are accessible 79 | ** here. 80 | ** \033[42;43m will have 2 in argc and argv[0] = 42, argv[1] = 43 81 | ** 82 | ** char flag; 83 | ** Optinal constructor flag present before parameters, like in : 84 | ** \033[?1049h -> The flag will be '?' 85 | ** Otherwise the flag is set to '\0' 86 | ** 87 | ** void (*unimplemented)(struct terminal*, char *seq, char chr) : 88 | ** Can be NULL, you can hook here to know where the terminal parses an 89 | ** escape sequence on which you have not registered a callback. 90 | ** 91 | ** Exemple 92 | ** ======= 93 | ** 94 | ** See terminal_vt100.c for a working exemple. 95 | ** 96 | */ 97 | 98 | #define TERM_STACK_SIZE 1024 99 | 100 | enum term_state 101 | { 102 | INIT, 103 | ESC, 104 | HASH, 105 | G0SET, 106 | G1SET, 107 | CSI 108 | }; 109 | 110 | struct lw_terminal; 111 | 112 | typedef void (*term_action)(struct lw_terminal *emul); 113 | 114 | struct ascii_callbacks 115 | { 116 | term_action n0; 117 | term_action n1; 118 | term_action n2; 119 | term_action n3; 120 | term_action n4; 121 | term_action n5; 122 | term_action n6; 123 | term_action n7; 124 | term_action n8; 125 | term_action n9; 126 | 127 | term_action h3A; 128 | term_action h3B; 129 | term_action h3C; 130 | term_action h3D; 131 | term_action h3E; 132 | term_action h3F; 133 | term_action h40; 134 | 135 | term_action A; 136 | term_action B; 137 | term_action C; 138 | term_action D; 139 | term_action E; 140 | term_action F; 141 | term_action G; 142 | term_action H; 143 | term_action I; 144 | term_action J; 145 | term_action K; 146 | term_action L; 147 | term_action M; 148 | term_action N; 149 | term_action O; 150 | term_action P; 151 | term_action Q; 152 | term_action R; 153 | term_action S; 154 | term_action T; 155 | term_action U; 156 | term_action V; 157 | term_action W; 158 | term_action X; 159 | term_action Y; 160 | term_action Z; 161 | 162 | term_action h5B; 163 | term_action h5C; 164 | term_action h5D; 165 | term_action h5E; 166 | term_action h5F; 167 | term_action h60; 168 | 169 | term_action a; 170 | term_action b; 171 | term_action c; 172 | term_action d; 173 | term_action e; 174 | term_action f; 175 | term_action g; 176 | term_action h; 177 | term_action i; 178 | term_action j; 179 | term_action k; 180 | term_action l; 181 | term_action m; 182 | term_action n; 183 | term_action o; 184 | term_action p; 185 | term_action q; 186 | term_action r; 187 | term_action s; 188 | term_action t; 189 | term_action u; 190 | term_action v; 191 | term_action w; 192 | term_action x; 193 | term_action y; 194 | term_action z; 195 | }; 196 | 197 | struct term_callbacks 198 | { 199 | struct ascii_callbacks esc; 200 | struct ascii_callbacks csi; 201 | struct ascii_callbacks hash; 202 | struct ascii_callbacks scs; 203 | }; 204 | 205 | struct lw_terminal 206 | { 207 | unsigned int cursor_pos_x; 208 | unsigned int cursor_pos_y; 209 | enum term_state state; 210 | unsigned int argc; 211 | unsigned int argv[TERM_STACK_SIZE]; 212 | void (*write)(struct lw_terminal *, char c); 213 | char stack[TERM_STACK_SIZE]; 214 | unsigned int stack_ptr; 215 | struct term_callbacks callbacks; 216 | char flag; 217 | void *user_data; 218 | void (*unimplemented)(struct lw_terminal*, 219 | char *seq, char chr); 220 | }; 221 | 222 | struct lw_terminal *lw_terminal_parser_init(void); 223 | void lw_terminal_parser_default_unimplemented(struct lw_terminal* this, char *seq, char chr); 224 | void lw_terminal_parser_read(struct lw_terminal *this, char c); 225 | void lw_terminal_parser_read_str(struct lw_terminal *this, char *c); 226 | void lw_terminal_parser_destroy(struct lw_terminal* this); 227 | #endif 228 | -------------------------------------------------------------------------------- /src/lw_terminal_parser.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include 27 | #ifndef NDEBUG 28 | # include 29 | #endif 30 | 31 | #include "lw_terminal_parser.h" 32 | 33 | static void lw_terminal_parser_push(struct lw_terminal *this, char c) 34 | { 35 | if (this->stack_ptr >= TERM_STACK_SIZE) 36 | return ; 37 | this->stack[this->stack_ptr++] = c; 38 | } 39 | 40 | static void lw_terminal_parser_parse_params(struct lw_terminal *this) 41 | { 42 | unsigned int i; 43 | int got_something; 44 | 45 | got_something = 0; 46 | this->argc = 0; 47 | this->argv[0] = 0; 48 | for (i = 0; i < this->stack_ptr; ++i) 49 | { 50 | if (this->stack[i] >= '0' && this->stack[i] <= '9') 51 | { 52 | got_something = 1; 53 | this->argv[this->argc] = this->argv[this->argc] * 10 54 | + this->stack[i] - '0'; 55 | } 56 | else if (this->stack[i] == ';') 57 | { 58 | got_something = 0; 59 | this->argc += 1; 60 | this->argv[this->argc] = 0; 61 | } 62 | } 63 | this->argc += got_something; 64 | } 65 | 66 | static void lw_terminal_parser_call_CSI(struct lw_terminal *this, char c) 67 | { 68 | lw_terminal_parser_parse_params(this); 69 | if (((term_action *)&this->callbacks.csi)[c - '0'] == NULL) 70 | { 71 | if (this->unimplemented != NULL) 72 | this->unimplemented(this, "CSI", c); 73 | goto leave; 74 | } 75 | ((term_action *)&this->callbacks.csi)[c - '0'](this); 76 | leave: 77 | this->state = INIT; 78 | this->flag = '\0'; 79 | this->stack_ptr = 0; 80 | this->argc = 0; 81 | } 82 | 83 | static void lw_terminal_parser_call_ESC(struct lw_terminal *this, char c) 84 | { 85 | if (((term_action *)&this->callbacks.esc)[c - '0'] == NULL) 86 | { 87 | if (this->unimplemented != NULL) 88 | this->unimplemented(this, "ESC", c); 89 | goto leave; 90 | } 91 | ((term_action *)&this->callbacks.esc)[c - '0'](this); 92 | leave: 93 | this->state = INIT; 94 | this->stack_ptr = 0; 95 | this->argc = 0; 96 | } 97 | 98 | static void lw_terminal_parser_call_HASH(struct lw_terminal *this, char c) 99 | { 100 | if (((term_action *)&this->callbacks.hash)[c - '0'] == NULL) 101 | { 102 | if (this->unimplemented != NULL) 103 | this->unimplemented(this, "HASH", c); 104 | goto leave; 105 | } 106 | ((term_action *)&this->callbacks.hash)[c - '0'](this); 107 | leave: 108 | this->state = INIT; 109 | this->stack_ptr = 0; 110 | this->argc = 0; 111 | } 112 | 113 | static void lw_terminal_parser_call_GSET(struct lw_terminal *this, char c) 114 | { 115 | if (c < '0' || c > 'B' 116 | || ((term_action *)&this->callbacks.scs)[c - '0'] == NULL) 117 | { 118 | if (this->unimplemented != NULL) 119 | this->unimplemented(this, "GSET", c); 120 | goto leave; 121 | } 122 | ((term_action *)&this->callbacks.scs)[c - '0'](this); 123 | leave: 124 | this->state = INIT; 125 | this->stack_ptr = 0; 126 | this->argc = 0; 127 | } 128 | 129 | /* 130 | ** INIT 131 | ** \_ ESC "\033" 132 | ** | \_ CSI "\033[" 133 | ** | | \_ c == '?' : term->flag = '?' 134 | ** | | \_ c == ';' || (c >= '0' && c <= '9') : term_push 135 | ** | | \_ else : term_call_CSI() 136 | ** | \_ HASH "\033#" 137 | ** | | \_ term_call_hash() 138 | ** | \_ G0SET "\033(" 139 | ** | | \_ term_call_GSET() 140 | ** | \_ G1SET "\033)" 141 | ** | | \_ term_call_GSET() 142 | ** \_ term->write() 143 | */ 144 | void lw_terminal_parser_read(struct lw_terminal *this, char c) 145 | { 146 | if (this->state == INIT) 147 | { 148 | if (c == '\033') 149 | this->state = ESC; 150 | else 151 | this->write(this, c); 152 | } 153 | else if (this->state == ESC) 154 | { 155 | if (c == '[') 156 | this->state = CSI; 157 | else if (c == '#') 158 | this->state = HASH; 159 | else if (c == '(') 160 | this->state = G0SET; 161 | else if (c == ')') 162 | this->state = G1SET; 163 | else if (c >= '0' && c <= 'z') 164 | lw_terminal_parser_call_ESC(this, c); 165 | else this->write(this, c); 166 | } 167 | else if (this->state == HASH) 168 | { 169 | if (c >= '0' && c <= '9') 170 | lw_terminal_parser_call_HASH(this, c); 171 | else 172 | this->write(this, c); 173 | } 174 | else if (this->state == G0SET || this->state == G1SET) 175 | { 176 | lw_terminal_parser_call_GSET(this, c); 177 | } 178 | else if (this->state == CSI) 179 | { 180 | if (c == '?') 181 | this->flag = '?'; 182 | else if (c == ';' || (c >= '0' && c <= '9')) 183 | lw_terminal_parser_push(this, c); 184 | else if (c >= '?' && c <= 'z') 185 | lw_terminal_parser_call_CSI(this, c); 186 | else 187 | this->write(this, c); 188 | } 189 | } 190 | 191 | void lw_terminal_parser_read_str(struct lw_terminal *this, char *c) 192 | { 193 | while (*c) 194 | lw_terminal_parser_read(this, *c++); 195 | } 196 | 197 | #ifndef NDEBUG 198 | void lw_terminal_parser_default_unimplemented(struct lw_terminal* this, char *seq, char chr) 199 | { 200 | unsigned int argc; 201 | 202 | fprintf(stderr, "WARNING: UNIMPLEMENTED %s (", seq); 203 | for (argc = 0; argc < this->argc; ++argc) 204 | { 205 | fprintf(stderr, "%d", this->argv[argc]); 206 | if (argc != this->argc - 1) 207 | fprintf(stderr, ", "); 208 | } 209 | fprintf(stderr, ")%o\n", chr); 210 | } 211 | #else 212 | void lw_terminal_parser_default_unimplemented(struct lw_terminal* this, char *seq, char chr) 213 | { 214 | this = this; 215 | seq = seq; 216 | chr = chr; 217 | } 218 | #endif 219 | 220 | struct lw_terminal *lw_terminal_parser_init(void) 221 | { 222 | return calloc(1, sizeof(struct lw_terminal)); 223 | } 224 | 225 | void lw_terminal_parser_destroy(struct lw_terminal* this) 226 | { 227 | free(this); 228 | } 229 | -------------------------------------------------------------------------------- /src/lw_terminal_vt100.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Julien Palard. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "lw_terminal_vt100.h" 30 | 31 | static unsigned int get_mode_mask(unsigned int mode) 32 | { 33 | switch (mode) 34 | { 35 | case LNM : return MASK_LNM; 36 | case DECCKM : return MASK_DECCKM; 37 | case DECANM : return MASK_DECANM; 38 | case DECCOLM : return MASK_DECCOLM; 39 | case DECSCLM : return MASK_DECSCLM; 40 | case DECSCNM : return MASK_DECSCNM; 41 | case DECOM : return MASK_DECOM; 42 | case DECAWM : return MASK_DECAWM; 43 | case DECARM : return MASK_DECARM; 44 | case DECINLM : return MASK_DECINLM; 45 | default: return 0; 46 | } 47 | } 48 | 49 | /* 50 | Modes 51 | ===== 52 | 53 | The following is a list of VT100 modes which may be changed with set 54 | mode (SM) and reset mode (RM) controls. 55 | 56 | ANSI Specified Modes 57 | -------------------- 58 | 59 | Parameter Mode Mnemonic Mode Function 60 | 0 Error (ignored) 61 | 20 LNM Line feed new line mode 62 | 63 | 64 | DEC Private Modes 65 | ================= 66 | If the first character in the parameter string is ? (077), the 67 | parameters are interpreted as DEC private parameters according to the 68 | following: 69 | 70 | Parameter Mode Mnemonic Mode Function 71 | 0 Error (ignored) 72 | 1 DECCKM Cursor key 73 | 2 DECANM ANSI/VT52 74 | 3 DECCOLM Column 75 | 4 DECSCLM Scrolling 76 | 5 DECSCNM Screen 77 | 6 DECOM Origin 78 | 7 DECAWM Auto wrap 79 | 8 DECARM Auto repeating 80 | 9 DECINLM Interlace 81 | 82 | LNM – Line Feed/New Line Mode 83 | ----------------------------- 84 | This is a parameter applicable to set mode (SM) and reset mode (RM) 85 | control sequences. The reset state causes the interpretation of the 86 | line feed (LF), defined in ANSI Standard X3.4-1977, to imply only 87 | vertical movement of the active position and causes the RETURN key 88 | (CR) to send the single code CR. The set state causes the LF to imply 89 | movement to the first position of the following line and causes the 90 | RETURN key to send the two codes (CR, LF). This is the New Line (NL) 91 | option. 92 | 93 | This mode does not affect the index (IND), or next line (NEL) format 94 | effectors. 95 | 96 | DECCKM – Cursor Keys Mode (DEC Private) 97 | --------------------------------------- 98 | This is a private parameter applicable to set mode (SM) and reset mode 99 | (RM) control sequences. This mode is only effective when the terminal 100 | is in keypad application mode (see DECKPAM) and the ANSI/VT52 mode 101 | (DECANM) is set (see DECANM). Under these conditions, if the cursor 102 | key mode is reset, the four cursor function keys will send ANSI cursor 103 | control commands. If cursor key mode is set, the four cursor function 104 | keys will send application functions. 105 | 106 | DECANM – ANSI/VT52 Mode (DEC Private) 107 | ------------------------------------- 108 | This is a private parameter applicable to set mode (SM) and reset mode 109 | (RM) control sequences. The reset state causes only VT52 compatible 110 | escape sequences to be interpreted and executed. The set state causes 111 | only ANSI "compatible" escape and control sequences to be interpreted 112 | and executed. 113 | 114 | DECCOLM – Column Mode (DEC Private) 115 | ----------------------------------- 116 | This is a private parameter applicable to set mode (SM) and reset mode 117 | (RM) control sequences. The reset state causes a maximum of 80 columns 118 | on the screen. The set state causes a maximum of 132 columns on the 119 | screen. 120 | 121 | DECSCLM – Scrolling Mode (DEC Private) 122 | -------------------------------------- 123 | This is a private parameter applicable to set mode (SM) and reset mode 124 | (RM) control sequences. The reset state causes scrolls to "jump" 125 | instantaneously. The set state causes scrolls to be "smooth" at a 126 | maximum rate of six lines per second. 127 | 128 | DECSCNM – Screen Mode (DEC Private) 129 | ----------------------------------- 130 | This is a private parameter applicable to set mode (SM) and reset mode 131 | (RM) control sequences. The reset state causes the screen to be black 132 | with white characters. The set state causes the screen to be white 133 | with black characters. 134 | 135 | DECOM – Origin Mode (DEC Private) 136 | --------------------------------- 137 | This is a private parameter applicable to set mode (SM) and reset mode 138 | (RM) control sequences. The reset state causes the origin to be at the 139 | upper-left character position on the screen. Line and column numbers 140 | are, therefore, independent of current margin settings. The cursor may 141 | be positioned outside the margins with a cursor position (CUP) or 142 | horizontal and vertical position (HVP) control. 143 | 144 | The set state causes the origin to be at the upper-left character 145 | position within the margins. Line and column numbers are therefore 146 | relative to the current margin settings. The cursor is not allowed to 147 | be positioned outside the margins. 148 | 149 | The cursor is moved to the new home position when this mode is set or 150 | reset. 151 | 152 | Lines and columns are numbered consecutively, with the origin being 153 | line 1, column 1. 154 | 155 | DECAWM – Autowrap Mode (DEC Private) 156 | ------------------------------------ 157 | This is a private parameter applicable to set mode (SM) and reset mode 158 | (RM) control sequences. The reset state causes any displayable 159 | characters received when the cursor is at the right margin to replace 160 | any previous characters there. The set state causes these characters 161 | to advance to the start of the next line, doing a scroll up if 162 | required and permitted. 163 | 164 | DECARM – Auto Repeat Mode (DEC Private) 165 | --------------------------------------- 166 | This is a private parameter applicable to set mode (SM) and reset mode 167 | (RM) control sequences. The reset state causes no keyboard keys to 168 | auto-repeat. The set state causes certain keyboard keys to auto-repeat. 169 | 170 | DECINLM – Interlace Mode (DEC Private) 171 | -------------------------------------- 172 | This is a private parameter applicable to set mode (SM) and reset mode 173 | (RM) control sequences. The reset state (non-interlace) causes the video 174 | processor to display 240 scan lines per frame. The set state (interlace) 175 | causes the video processor to display 480 scan lines per frame. There is 176 | no increase in character resolution. 177 | */ 178 | 179 | #define SCREEN_PTR(vt100, x, y) \ 180 | ((vt100->top_line * vt100->width + x + vt100->width * y) \ 181 | % (vt100->width * SCROLLBACK * vt100->height)) 182 | 183 | #define FROZEN_SCREEN_PTR(vt100, x, y) \ 184 | ((x + vt100->width * y) \ 185 | % (vt100->width * SCROLLBACK * vt100->height)) 186 | 187 | static void set(struct lw_terminal_vt100 *headless_term, 188 | unsigned int x, unsigned int y, 189 | char c) 190 | { 191 | if (y < headless_term->margin_top || y > headless_term->margin_bottom) 192 | headless_term->frozen_screen[FROZEN_SCREEN_PTR(headless_term, x, y)] = c; 193 | else 194 | headless_term->screen[SCREEN_PTR(headless_term, x, y)] = c; 195 | } 196 | 197 | 198 | char lw_terminal_vt100_get(struct lw_terminal_vt100 *vt100, unsigned int x, unsigned int y) 199 | { 200 | if (y < vt100->margin_top || y > vt100->margin_bottom) 201 | return vt100->frozen_screen[FROZEN_SCREEN_PTR(vt100, x, y)]; 202 | else 203 | return vt100->screen[SCREEN_PTR(vt100, x, y)]; 204 | } 205 | 206 | static void froze_line(struct lw_terminal_vt100 *vt100, unsigned int y) 207 | { 208 | memcpy(vt100->frozen_screen + vt100->width * y, 209 | vt100->screen + SCREEN_PTR(vt100, 0, y), 210 | vt100->width); 211 | } 212 | 213 | static void unfroze_line(struct lw_terminal_vt100 *vt100, unsigned int y) 214 | { 215 | memcpy(vt100->screen + SCREEN_PTR(vt100, 0, y), 216 | vt100->frozen_screen + vt100->width * y, 217 | vt100->width); 218 | } 219 | 220 | static void blank_screen(struct lw_terminal_vt100 *lw_terminal_vt100) 221 | { 222 | unsigned int x; 223 | unsigned int y; 224 | 225 | for (x = 0; x < lw_terminal_vt100->width; ++x) 226 | for (y = 0; y < lw_terminal_vt100->height; ++y) 227 | set(lw_terminal_vt100, x, y, ' '); 228 | } 229 | 230 | /* 231 | DECSC – Save Cursor (DEC Private) 232 | 233 | ESC 7 234 | 235 | This sequence causes the cursor position, graphic rendition, and 236 | character set to be saved. (See DECRC). 237 | */ 238 | static void DECSC(struct lw_terminal *term_emul) 239 | { 240 | /*TODO: Save graphic rendition and charset.*/ 241 | struct lw_terminal_vt100 *vt100; 242 | 243 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 244 | vt100->saved_x = vt100->x; 245 | vt100->saved_y = vt100->y; 246 | } 247 | 248 | /* 249 | RM – Reset Mode 250 | 251 | ESC [ Ps ; Ps ; . . . ; Ps l 252 | 253 | Resets one or more VT100 modes as specified by each selective 254 | parameter in the parameter string. Each mode to be reset is specified 255 | by a separate parameter. [See Set Mode (SM) control sequence]. (See 256 | Modes following this section). 257 | 258 | */ 259 | static void RM(struct lw_terminal *term_emul) 260 | { 261 | struct lw_terminal_vt100 *vt100; 262 | unsigned int mode; 263 | 264 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 265 | if (term_emul->argc > 0) 266 | { 267 | mode = term_emul->argv[0]; 268 | if (mode == DECCOLM) 269 | { 270 | vt100->width = 80; 271 | vt100->x = vt100->y = 0; 272 | blank_screen(vt100); 273 | } 274 | UNSET_MODE(vt100, mode); 275 | } 276 | } 277 | 278 | /* 279 | CUP – Cursor Position 280 | 281 | ESC [ Pn ; Pn H default value: 1 282 | 283 | The CUP sequence moves the active position to the position specified 284 | by the parameters. This sequence has two parameter values, the first 285 | specifying the line position and the second specifying the column 286 | position. A parameter value of zero or one for the first or second 287 | parameter moves the active position to the first line or column in the 288 | display, respectively. The default condition with no parameters 289 | present is equivalent to a cursor to home action. In the VT100, this 290 | control behaves identically with its format effector counterpart, 291 | HVP. Editor Function 292 | 293 | The numbering of lines depends on the state of the Origin Mode 294 | (DECOM). 295 | */ 296 | static void CUP(struct lw_terminal *term_emul) 297 | { 298 | struct lw_terminal_vt100 *vt100; 299 | int arg0; 300 | int arg1; 301 | 302 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 303 | arg0 = 0; 304 | arg1 = 0; 305 | if (term_emul->argc > 0) 306 | arg0 = term_emul->argv[0] - 1; 307 | if (term_emul->argc > 1) 308 | arg1 = term_emul->argv[1] - 1; 309 | if (arg0 < 0) 310 | arg0 = 0; 311 | if (arg1 < 0) 312 | arg1 = 0; 313 | 314 | if (MODE_IS_SET(vt100, DECOM)) 315 | { 316 | arg0 += vt100->margin_top; 317 | if ((unsigned int)arg0 > vt100->margin_bottom) 318 | arg0 = vt100->margin_bottom; 319 | } 320 | vt100->y = arg0; 321 | vt100->x = arg1; 322 | } 323 | 324 | /* 325 | SM – Set Mode 326 | 327 | ESC [ Ps ; . . . ; Ps h 328 | 329 | Causes one or more modes to be set within the VT100 as specified by 330 | each selective parameter in the parameter string. Each mode to be set 331 | is specified by a separate parameter. A mode is considered set until 332 | it is reset by a reset mode (RM) control sequence. 333 | 334 | */ 335 | static void SM(struct lw_terminal *term_emul) 336 | { 337 | struct lw_terminal_vt100 *vt100; 338 | unsigned int mode; 339 | unsigned int saved_argc; 340 | 341 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 342 | if (term_emul->argc > 0) 343 | { 344 | mode = term_emul->argv[0]; 345 | SET_MODE(vt100, mode); 346 | if (mode == DECANM) 347 | { 348 | /* TODO: Support vt52 mode */ 349 | return ; 350 | } 351 | if (mode == DECCOLM) 352 | { 353 | vt100->width = 132; 354 | vt100->x = vt100->y = 0; 355 | blank_screen(vt100); 356 | } 357 | if (mode == DECOM) 358 | { 359 | saved_argc = term_emul->argc; 360 | term_emul->argc = 0; 361 | CUP(term_emul); 362 | term_emul->argc = saved_argc; 363 | } 364 | } 365 | } 366 | 367 | /* 368 | DECSTBM – Set Top and Bottom Margins (DEC Private) 369 | 370 | ESC [ Pn; Pn r 371 | 372 | This sequence sets the top and bottom margins to define the scrolling 373 | region. The first parameter is the line number of the first line in 374 | the scrolling region; the second parameter is the line number of the 375 | bottom line in the scrolling region. Default is the entire screen (no 376 | margins). The minimum size of the scrolling region allowed is two 377 | lines, i.e., the top margin must be less than the bottom margin. The 378 | cursor is placed in the home position (see Origin Mode DECOM). 379 | 380 | */ 381 | static void DECSTBM(struct lw_terminal *term_emul) 382 | { 383 | unsigned int margin_top; 384 | unsigned int margin_bottom; 385 | struct lw_terminal_vt100 *vt100; 386 | unsigned int line; 387 | 388 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 389 | 390 | if (term_emul->argc == 2) 391 | { 392 | margin_top = term_emul->argv[0] - 1; 393 | margin_bottom = term_emul->argv[1] - 1; 394 | if (margin_bottom >= vt100->height) 395 | return ; 396 | if (margin_bottom - margin_top <= 0) 397 | return ; 398 | } 399 | else 400 | { 401 | margin_top = 0; 402 | margin_bottom = vt100->height - 1; 403 | } 404 | for (line = vt100->margin_top; line < margin_top; ++line) 405 | froze_line(vt100, line); 406 | for (line = vt100->margin_bottom; line < margin_bottom; ++line) 407 | unfroze_line(vt100, line); 408 | for (line = margin_top; line < vt100->margin_top; ++line) 409 | unfroze_line(vt100, line); 410 | for (line = margin_bottom; line < vt100->margin_bottom; ++line) 411 | froze_line(vt100, line); 412 | vt100->margin_bottom = margin_bottom; 413 | vt100->margin_top = margin_top; 414 | term_emul->argc = 0; 415 | CUP(term_emul); 416 | } 417 | 418 | /* 419 | SGR – Select Graphic Rendition 420 | 421 | ESC [ Ps ; . . . ; Ps m 422 | 423 | Invoke the graphic rendition specified by the parameter(s). All 424 | following characters transmitted to the VT100 are rendered according 425 | to the parameter(s) until the next occurrence of SGR. Format Effector 426 | 427 | Parameter Parameter Meaning 428 | 0 Attributes off 429 | 1 Bold or increased intensity 430 | 4 Underscore 431 | 5 Blink 432 | 7 Negative (reverse) image 433 | 434 | All other parameter values are ignored. 435 | 436 | With the Advanced Video Option, only one type of character attribute 437 | is possible as determined by the cursor selection; in that case 438 | specifying either the underscore or the reverse attribute will 439 | activate the currently selected attribute. (See cursor selection in 440 | Chapter 1). 441 | */ 442 | static void SGR(struct lw_terminal *term_emul) 443 | { 444 | term_emul = term_emul; 445 | /* Just ignore them for now, we are rendering pure text only */ 446 | } 447 | 448 | /* 449 | DA – Device Attributes 450 | 451 | ESC [ Pn c 452 | 453 | 454 | The host requests the VT100 to send a device attributes (DA) control 455 | sequence to identify itself by sending the DA control sequence with 456 | either no parameter or a parameter of 0. Response to the request 457 | described above (VT100 to host) is generated by the VT100 as a DA 458 | control sequence with the numeric parameters as follows: 459 | 460 | Option Present Sequence Sent 461 | No options ESC [?1;0c 462 | Processor option (STP) ESC [?1;1c 463 | Advanced video option (AVO) ESC [?1;2c 464 | AVO and STP ESC [?1;3c 465 | Graphics option (GPO) ESC [?1;4c 466 | GPO and STP ESC [?1;5c 467 | GPO and AVO ESC [?1;6c 468 | GPO, STP and AVO ESC [?1;7c 469 | 470 | */ 471 | static void DA(struct lw_terminal *term_emul) 472 | { 473 | struct lw_terminal_vt100 *vt100; 474 | 475 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 476 | vt100->master_write(vt100->user_data, "\033[?1;0c", 7); 477 | } 478 | 479 | /* 480 | DECRC – Restore Cursor (DEC Private) 481 | 482 | ESC 8 483 | 484 | This sequence causes the previously saved cursor position, graphic 485 | rendition, and character set to be restored. 486 | */ 487 | static void DECRC(struct lw_terminal *term_emul) 488 | { 489 | /*TODO Save graphic rendition and charset */ 490 | struct lw_terminal_vt100 *vt100; 491 | 492 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 493 | vt100->x = vt100->saved_x; 494 | vt100->y = vt100->saved_y; 495 | } 496 | 497 | /* 498 | DECALN – Screen Alignment Display (DEC Private) 499 | 500 | ESC # 8 501 | 502 | This command fills the entire screen area with uppercase Es for screen 503 | focus and alignment. This command is used by DEC manufacturing and 504 | Field Service personnel. 505 | */ 506 | static void DECALN(struct lw_terminal *term_emul) 507 | { 508 | struct lw_terminal_vt100 *vt100; 509 | unsigned int x; 510 | unsigned int y; 511 | 512 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 513 | for (x = 0; x < vt100->width; ++x) 514 | for (y = 0; y < vt100->height; ++y) 515 | set(vt100, x, y, 'E'); 516 | } 517 | 518 | /* 519 | IND – Index 520 | 521 | ESC D 522 | 523 | This sequence causes the active position to move downward one line 524 | without changing the column position. If the active position is at the 525 | bottom margin, a scroll up is performed. Format Effector 526 | */ 527 | static void IND(struct lw_terminal *term_emul) 528 | { 529 | struct lw_terminal_vt100 *vt100; 530 | unsigned int x; 531 | 532 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 533 | if (vt100->y >= vt100->margin_bottom) 534 | { 535 | /* SCROLL */ 536 | vt100->top_line = (vt100->top_line + 1) % (vt100->height * SCROLLBACK); 537 | for (x = 0; x < vt100->width; ++x) 538 | set(vt100, x, vt100->margin_bottom, ' '); 539 | 540 | } 541 | else 542 | { 543 | /* Do not scroll, just move downward on the current display space */ 544 | vt100->y += 1; 545 | } 546 | } 547 | /* 548 | RI – Reverse Index 549 | 550 | ESC M 551 | 552 | Move the active position to the same horizontal position on the 553 | preceding line. If the active position is at the top margin, a scroll 554 | down is performed. Format Effector 555 | */ 556 | static void RI(struct lw_terminal *term_emul) 557 | { 558 | struct lw_terminal_vt100 *vt100; 559 | 560 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 561 | if (vt100->y == 0) 562 | { 563 | /* SCROLL */ 564 | vt100->top_line = (vt100->top_line - 1) % (vt100->height * SCROLLBACK); 565 | } 566 | else 567 | { 568 | /* Do not scroll, just move upward on the current display space */ 569 | vt100->y -= 1; 570 | } 571 | } 572 | 573 | /* 574 | NEL – Next Line 575 | 576 | ESC E 577 | 578 | This sequence causes the active position to move to the first position 579 | on the next line downward. If the active position is at the bottom 580 | margin, a scroll up is performed. Format Effector 581 | */ 582 | static void NEL(struct lw_terminal *term_emul) 583 | { 584 | struct lw_terminal_vt100 *vt100; 585 | unsigned int x; 586 | 587 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 588 | if (vt100->y >= vt100->margin_bottom) 589 | { 590 | /* SCROLL */ 591 | vt100->top_line = (vt100->top_line + 1) % (vt100->height * SCROLLBACK); 592 | for (x = 0; x < vt100->width; ++x) 593 | set(vt100, x, vt100->margin_bottom, ' '); 594 | } 595 | else 596 | { 597 | /* Do not scroll, just move downward on the current display space */ 598 | vt100->y += 1; 599 | } 600 | vt100->x = 0; 601 | } 602 | 603 | /* 604 | CUU – Cursor Up – Host to VT100 and VT100 to Host 605 | 606 | ESC [ Pn A default value: 1 607 | 608 | Moves the active position upward without altering the column 609 | position. The number of lines moved is determined by the parameter. A 610 | parameter value of zero or one moves the active position one line 611 | upward. A parameter value of n moves the active position n lines 612 | upward. If an attempt is made to move the cursor above the top margin, 613 | the cursor stops at the top margin. Editor Function 614 | */ 615 | static void CUU(struct lw_terminal *term_emul) 616 | { 617 | struct lw_terminal_vt100 *vt100; 618 | unsigned int arg0; 619 | 620 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 621 | arg0 = 1; 622 | if (term_emul->argc > 0) 623 | arg0 = term_emul->argv[0]; 624 | if (arg0 == 0) 625 | arg0 = 1; 626 | if (arg0 <= vt100->y) 627 | vt100->y -= arg0; 628 | else 629 | vt100->y = 0; 630 | } 631 | 632 | /* 633 | CUD – Cursor Down – Host to VT100 and VT100 to Host 634 | 635 | ESC [ Pn B default value: 1 636 | 637 | The CUD sequence moves the active position downward without altering 638 | the column position. The number of lines moved is determined by the 639 | parameter. If the parameter value is zero or one, the active position 640 | is moved one line downward. If the parameter value is n, the active 641 | position is moved n lines downward. In an attempt is made to move the 642 | cursor below the bottom margin, the cursor stops at the bottom 643 | margin. Editor Function 644 | */ 645 | static void CUD(struct lw_terminal *term_emul) 646 | { 647 | struct lw_terminal_vt100 *vt100; 648 | unsigned int arg0; 649 | 650 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 651 | arg0 = 1; 652 | if (term_emul->argc > 0) 653 | arg0 = term_emul->argv[0]; 654 | if (arg0 == 0) 655 | arg0 = 1; 656 | vt100->y += arg0; 657 | if (vt100->y >= vt100->height) 658 | vt100->y = vt100->height - 1; 659 | } 660 | 661 | /* 662 | CUF – Cursor Forward – Host to VT100 and VT100 to Host 663 | 664 | ESC [ Pn C default value: 1 665 | 666 | The CUF sequence moves the active position to the right. The distance 667 | moved is determined by the parameter. A parameter value of zero or one 668 | moves the active position one position to the right. A parameter value 669 | of n moves the active position n positions to the right. If an attempt 670 | is made to move the cursor to the right of the right margin, the 671 | cursor stops at the right margin. Editor Function 672 | */ 673 | static void CUF(struct lw_terminal *term_emul) 674 | { 675 | struct lw_terminal_vt100 *vt100; 676 | unsigned int arg0; 677 | 678 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 679 | arg0 = 1; 680 | if (term_emul->argc > 0) 681 | arg0 = term_emul->argv[0]; 682 | if (arg0 == 0) 683 | arg0 = 1; 684 | vt100->x += arg0; 685 | if (vt100->x >= vt100->width) 686 | vt100->x = vt100->width - 1; 687 | } 688 | 689 | /* 690 | CUB – Cursor Backward – Host to VT100 and VT100 to Host 691 | 692 | ESC [ Pn D default value: 1 693 | 694 | The CUB sequence moves the active position to the left. The distance 695 | moved is determined by the parameter. If the parameter value is zero 696 | or one, the active position is moved one position to the left. If the 697 | parameter value is n, the active position is moved n positions to the 698 | left. If an attempt is made to move the cursor to the left of the left 699 | margin, the cursor stops at the left margin. Editor Function 700 | */ 701 | static void CUB(struct lw_terminal *term_emul) 702 | { 703 | struct lw_terminal_vt100 *vt100; 704 | unsigned int arg0; 705 | 706 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 707 | arg0 = 1; 708 | if (term_emul->argc > 0) 709 | arg0 = term_emul->argv[0]; 710 | if (arg0 == 0) 711 | arg0 = 1; 712 | if (arg0 < vt100->x) 713 | vt100->x -= arg0; 714 | else 715 | vt100->x = 0; 716 | } 717 | 718 | /* 719 | ED – Erase In Display 720 | 721 | ESC [ Ps J default value: 0 722 | 723 | This sequence erases some or all of the characters in the display 724 | according to the parameter. Any complete line erased by this sequence 725 | will return that line to single width mode. Editor Function 726 | 727 | Parameter Parameter Meaning 728 | 0 Erase from the active position to the end of the screen, 729 | inclusive (default) 730 | 1 Erase from start of the screen to the active position, inclusive 731 | 2 Erase all of the display – all lines are erased, changed to 732 | single-width, and the cursor does not move. 733 | */ 734 | static void ED(struct lw_terminal *term_emul) 735 | { 736 | struct lw_terminal_vt100 *vt100; 737 | unsigned int arg0; 738 | unsigned int x; 739 | unsigned int y; 740 | 741 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 742 | arg0 = 0; 743 | if (term_emul->argc > 0) 744 | arg0 = term_emul->argv[0]; 745 | if (arg0 == 0) 746 | { 747 | for (x = vt100->x; x < vt100->width; ++x) 748 | set(vt100, x, vt100->y, ' '); 749 | for (x = 0 ; x < vt100->width; ++x) 750 | for (y = vt100->y + 1; y < vt100->height; ++y) 751 | set(vt100, x, y, ' '); 752 | } 753 | else if (arg0 == 1) 754 | { 755 | for (x = 0 ; x < vt100->width; ++x) 756 | for (y = 0; y < vt100->y; ++y) 757 | set(vt100, x, y, ' '); 758 | for (x = 0; x <= vt100->x; ++x) 759 | set(vt100, x, vt100->y, ' '); 760 | } 761 | else if (arg0 == 2) 762 | { 763 | for (x = 0 ; x < vt100->width; ++x) 764 | for (y = 0; y < vt100->height; ++y) 765 | set(vt100, x, y, ' '); 766 | } 767 | } 768 | 769 | /* 770 | EL – Erase In Line 771 | 772 | ESC [ Ps K default value: 0 773 | 774 | Erases some or all characters in the active line according to the 775 | parameter. Editor Function 776 | 777 | Parameter Parameter Meaning 778 | 0 Erase from the active position to the end of the line, inclusive 779 | (default) 780 | 1 Erase from the start of the screen to the active position, inclusive 781 | 2 Erase all of the line, inclusive 782 | */ 783 | static void EL(struct lw_terminal *term_emul) 784 | { 785 | struct lw_terminal_vt100 *vt100; 786 | unsigned int arg0; 787 | unsigned int x; 788 | 789 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 790 | arg0 = 0; 791 | if (term_emul->argc > 0) 792 | arg0 = term_emul->argv[0]; 793 | if (arg0 == 0) 794 | { 795 | for (x = vt100->x; x < vt100->width; ++x) 796 | set(vt100, x, vt100->y, ' '); 797 | } 798 | else if (arg0 == 1) 799 | { 800 | for (x = 0; x <= vt100->x; ++x) 801 | set(vt100, x, vt100->y, ' '); 802 | } 803 | else if (arg0 == 2) 804 | { 805 | for (x = 0; x < vt100->width; ++x) 806 | set(vt100, x, vt100->y, ' '); 807 | } 808 | } 809 | 810 | /* 811 | HVP – Horizontal and Vertical Position 812 | 813 | ESC [ Pn ; Pn f default value: 1 814 | 815 | Moves the active position to the position specified by the 816 | parameters. This sequence has two parameter values, the first 817 | specifying the line position and the second specifying the column. A 818 | parameter value of either zero or one causes the active position to 819 | move to the first line or column in the display, respectively. The 820 | default condition with no parameters present moves the active position 821 | to the home position. In the VT100, this control behaves identically 822 | with its editor function counterpart, CUP. The numbering of lines and 823 | columns depends on the reset or set state of the origin mode 824 | (DECOM). Format Effector 825 | */ 826 | static void HVP(struct lw_terminal *term_emul) 827 | { 828 | CUP(term_emul); 829 | } 830 | 831 | static void TBC(struct lw_terminal *term_emul) 832 | { 833 | struct lw_terminal_vt100 *vt100; 834 | unsigned int i; 835 | 836 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 837 | if (term_emul->argc == 0 || term_emul->argv[0] == 0) 838 | { 839 | vt100->tabulations[vt100->x] = '-'; 840 | } 841 | else if (term_emul->argc == 1 && term_emul->argv[0] == 3) 842 | { 843 | for (i = 0; i < 132; ++i) 844 | vt100->tabulations[i] = '-'; 845 | } 846 | } 847 | 848 | static void HTS(struct lw_terminal *term_emul) 849 | { 850 | struct lw_terminal_vt100 *vt100; 851 | 852 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 853 | vt100->tabulations[vt100->x] = '|'; 854 | } 855 | 856 | static void vt100_write(struct lw_terminal *term_emul, char c) 857 | { 858 | struct lw_terminal_vt100 *vt100; 859 | 860 | vt100 = (struct lw_terminal_vt100 *)term_emul->user_data; 861 | if (c == '\r') 862 | { 863 | vt100->x = 0; 864 | return ; 865 | } 866 | if (c == '\n' || c == '\013' || c == '\014') 867 | { 868 | if (MODE_IS_SET(vt100, LNM)) 869 | NEL(term_emul); 870 | else 871 | IND(term_emul); 872 | return ; 873 | } 874 | if (c == '\010' && vt100->x > 0) 875 | { 876 | if (vt100->x == vt100->width) 877 | vt100->x -= 1; 878 | vt100->x -= 1; 879 | return ; 880 | } 881 | if (c == '\t') 882 | { 883 | do 884 | { 885 | set(vt100, vt100->x, vt100->y, ' '); 886 | vt100->x += 1; 887 | } while (vt100->x < vt100->width && vt100->tabulations[vt100->x] == '-'); 888 | return ; 889 | } 890 | if (c == '\016') 891 | { 892 | vt100->selected_charset = 0; 893 | return ; 894 | } 895 | if (c == '\017') 896 | { 897 | vt100->selected_charset = 1; 898 | return ; 899 | } 900 | if (vt100->x == vt100->width) 901 | { 902 | if (MODE_IS_SET(vt100, DECAWM)) 903 | NEL(term_emul); 904 | else 905 | vt100->x -= 1; 906 | } 907 | set(vt100, vt100->x, vt100->y, c); 908 | vt100->x += 1; 909 | } 910 | 911 | const char **lw_terminal_vt100_getlines(struct lw_terminal_vt100 *vt100) 912 | { 913 | unsigned int y; 914 | 915 | pthread_mutex_lock(&vt100->mutex); 916 | for (y = 0; y < vt100->height; ++y) 917 | if (y < vt100->margin_top || y > vt100->margin_bottom) 918 | vt100->lines[y] = vt100->frozen_screen + FROZEN_SCREEN_PTR(vt100, 0, y); 919 | else 920 | vt100->lines[y] = vt100->screen + SCREEN_PTR(vt100, 0, y); 921 | pthread_mutex_unlock(&vt100->mutex); 922 | return (const char **)vt100->lines; 923 | } 924 | 925 | struct lw_terminal_vt100 *lw_terminal_vt100_init(void *user_data, 926 | void (*unimplemented)(struct lw_terminal* term_emul, char *seq, char chr)) 927 | { 928 | struct lw_terminal_vt100 *this; 929 | 930 | this = calloc(1, sizeof(*this)); 931 | if (this == NULL) 932 | return NULL; 933 | this->user_data = user_data; 934 | this->height = 24; 935 | this->width = 80; 936 | this->screen = malloc(132 * SCROLLBACK * this->height); 937 | if (this->screen == NULL) 938 | goto free_this; 939 | memset(this->screen, ' ', 132 * SCROLLBACK * this->height); 940 | this->frozen_screen = malloc(132 * this->height); 941 | if (this->frozen_screen == NULL) 942 | goto free_screen; 943 | memset(this->frozen_screen, ' ', 132 * this->height); 944 | this->tabulations = malloc(132); 945 | if (this->tabulations == NULL) 946 | goto free_frozen_screen; 947 | if (this->tabulations == NULL) 948 | return NULL; 949 | this->margin_top = 0; 950 | this->margin_bottom = this->height - 1; 951 | this->selected_charset = 0; 952 | this->x = 0; 953 | this->y = 0; 954 | this->modes = MASK_DECANM; 955 | this->top_line = 0; 956 | this->lw_terminal = lw_terminal_parser_init(); 957 | if (this->lw_terminal == NULL) 958 | goto free_tabulations; 959 | this->lw_terminal->user_data = this; 960 | this->lw_terminal->write = vt100_write; 961 | this->lw_terminal->callbacks.csi.f = HVP; 962 | this->lw_terminal->callbacks.csi.K = EL; 963 | this->lw_terminal->callbacks.csi.c = DA; 964 | this->lw_terminal->callbacks.csi.h = SM; 965 | this->lw_terminal->callbacks.csi.l = RM; 966 | this->lw_terminal->callbacks.csi.J = ED; 967 | this->lw_terminal->callbacks.csi.H = CUP; 968 | this->lw_terminal->callbacks.csi.C = CUF; 969 | this->lw_terminal->callbacks.csi.B = CUD; 970 | this->lw_terminal->callbacks.csi.r = DECSTBM; 971 | this->lw_terminal->callbacks.csi.m = SGR; 972 | this->lw_terminal->callbacks.csi.A = CUU; 973 | this->lw_terminal->callbacks.csi.g = TBC; 974 | this->lw_terminal->callbacks.esc.H = HTS; 975 | this->lw_terminal->callbacks.csi.D = CUB; 976 | this->lw_terminal->callbacks.esc.E = NEL; 977 | this->lw_terminal->callbacks.esc.D = IND; 978 | this->lw_terminal->callbacks.esc.M = RI; 979 | this->lw_terminal->callbacks.esc.n8 = DECRC; 980 | this->lw_terminal->callbacks.esc.n7 = DECSC; 981 | this->lw_terminal->callbacks.hash.n8 = DECALN; 982 | this->lw_terminal->unimplemented = unimplemented; 983 | return this; 984 | free_tabulations: 985 | free(this->tabulations); 986 | free_frozen_screen: 987 | free(this->frozen_screen); 988 | free_screen: 989 | free(this->screen); 990 | free_this: 991 | free(this); 992 | return NULL; 993 | } 994 | 995 | void lw_terminal_vt100_read_str(struct lw_terminal_vt100 *this, char *buffer) 996 | { 997 | pthread_mutex_lock(&this->mutex); 998 | lw_terminal_parser_read_str(this->lw_terminal, buffer); 999 | pthread_mutex_unlock(&this->mutex); 1000 | } 1001 | 1002 | void lw_terminal_vt100_destroy(struct lw_terminal_vt100 *this) 1003 | { 1004 | lw_terminal_parser_destroy(this->lw_terminal); 1005 | free(this->screen); 1006 | free(this->frozen_screen); 1007 | free(this); 1008 | } 1009 | --------------------------------------------------------------------------------