Compare commits
12 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bfe2d7ca88 | |||
| 7734d1c4d9 | |||
| c6a43d0b41 | |||
| 348da63f70 | |||
| 636e79cd37 | |||
| 2e5fb48327 | |||
| 9abcfdcafa | |||
| bf3494ec6f | |||
| 117f3ccd53 | |||
| 0ce5d49ac8 | |||
| c4867953cd | |||
| c4dba51f7f |
23 changed files with 2986 additions and 168 deletions
|
|
@ -13,7 +13,13 @@ pico_sdk_init()
|
|||
|
||||
project(pico-cr100)
|
||||
|
||||
add_executable(cr100 chargen.c vt.c)
|
||||
add_executable(cr100
|
||||
chargen.c
|
||||
hl-vt100/src/lw_terminal_parser.c
|
||||
hl-vt100/src/lw_terminal_vt100.c
|
||||
)
|
||||
|
||||
target_include_directories(cr100 PRIVATE hl-vt100/src)
|
||||
|
||||
pico_enable_stdio_usb(cr100 1)
|
||||
|
||||
|
|
|
|||
186
chargen.c
186
chargen.c
|
|
@ -6,7 +6,7 @@
|
|||
#include "pico/multicore.h"
|
||||
#include "vga_660x480_60.pio.h"
|
||||
|
||||
#include "vt.h"
|
||||
#include "lw_terminal_vt100.h"
|
||||
|
||||
int pixels_sm;
|
||||
|
||||
|
|
@ -21,6 +21,8 @@ int pixels_sm;
|
|||
#define CHAR_Y (9)
|
||||
#define FB_HEIGHT_PIXEL (FB_HEIGHT_CHAR * CHAR_Y)
|
||||
|
||||
struct lw_terminal_vt100 *vt100;
|
||||
|
||||
void __not_in_flash_func(scan_convert)(const uint32_t * restrict cptr32, const uint16_t * restrict cgptr, const uint16_t * restrict shade) {
|
||||
#define READ_CHARDATA \
|
||||
(ch = *cptr32++)
|
||||
|
|
@ -80,65 +82,8 @@ int cx, cy, attr = 0x300;
|
|||
_Static_assert(FB_WIDTH_CHAR % 6 == 0);
|
||||
uint32_t chardata32[FB_WIDTH_CHAR * FB_HEIGHT_CHAR / 2];
|
||||
|
||||
int readchar(int cx, int cy) {
|
||||
uint16_t *chardata = (void*)chardata32;
|
||||
int i = cx + cy * FB_WIDTH_CHAR;
|
||||
return chardata[i] & 0xff;
|
||||
}
|
||||
int readattr(int cx, int cy) {
|
||||
uint16_t *chardata = (void*)chardata32;
|
||||
int i = cx + cy * FB_WIDTH_CHAR;
|
||||
return chardata[i] & 0xff00;
|
||||
}
|
||||
|
||||
void setchar(int cx, int cy, int ch) {
|
||||
uint16_t *chardata = (void*)chardata32;
|
||||
int i = cx + cy * FB_WIDTH_CHAR;
|
||||
chardata[i] = (chardata[i] & 0xff00) | ch;
|
||||
}
|
||||
void setattr(int cx, int cy, int attr) {
|
||||
uint16_t *chardata = (void*)chardata32;
|
||||
int i = cx + cy * FB_WIDTH_CHAR;
|
||||
chardata[i] = (chardata[i] & 0xff) | attr;
|
||||
}
|
||||
|
||||
void scroll_terminal() {
|
||||
|
||||
memmove(chardata32, chardata32 + FB_WIDTH_CHAR / 2, FB_WIDTH_CHAR * (FB_HEIGHT_CHAR - 1) * 2);
|
||||
uint32_t mask = attr | (attr << 16);
|
||||
for(size_t i=0; i<FB_WIDTH_CHAR / 2; i++) {
|
||||
chardata32[(FB_HEIGHT_CHAR-1) * FB_WIDTH_CHAR / 2 + i] = mask;
|
||||
}
|
||||
}
|
||||
void increase_y() {
|
||||
cy = (cy + 1);
|
||||
if (cy == FB_HEIGHT_CHAR) {
|
||||
scroll_terminal();
|
||||
cy = FB_HEIGHT_CHAR - 1;
|
||||
}
|
||||
}
|
||||
|
||||
int writefn(void *cookie, const char *data, int n) {
|
||||
uint16_t *chardata = cookie;
|
||||
for(; n; data++, n--) {
|
||||
switch(*data) {
|
||||
case '\r':
|
||||
cx = 0;
|
||||
break;
|
||||
case '\n':
|
||||
increase_y();
|
||||
break;
|
||||
default:
|
||||
if(*data >= 32) {
|
||||
if(cx == FB_WIDTH_CHAR) {
|
||||
cx = 0;
|
||||
increase_y();
|
||||
}
|
||||
chardata[cx + cy * FB_WIDTH_CHAR] = *data | attr;
|
||||
cx ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
lw_terminal_vt100_read_buf(vt100, data, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
|
@ -241,141 +186,48 @@ void __not_in_flash_func(core1_entry)() {
|
|||
#define BG_ATTR(x) ((x) << 11)
|
||||
#define FG_ATTR(x) ((x) << 8)
|
||||
|
||||
int saved_attr;
|
||||
void show_cursor() {
|
||||
int xx = cx == FB_WIDTH_CHAR ? FB_WIDTH_CHAR - 1 : cx;
|
||||
saved_attr = readattr(xx, cy);
|
||||
setattr(xx, cy, saved_attr ^ BG_ATTR(7));
|
||||
}
|
||||
|
||||
void hide_cursor() {
|
||||
int xx = cx == FB_WIDTH_CHAR ? FB_WIDTH_CHAR - 1 : cx;
|
||||
setattr(xx, cy, saved_attr);
|
||||
}
|
||||
|
||||
#define MAKE_ATTR(fg, bg) ((fg) ^ (((bg) * 9) & 073))
|
||||
|
||||
esc_state vt_st;
|
||||
#define MAKE_ATTR(fg, bg) (((fg) ^ (((bg) * 9) & 073)) << 8)
|
||||
|
||||
void invert_screen() {
|
||||
for(size_t i=0; i<count_of(chardata32); i++) { chardata32[i] ^= 0x18001800; }
|
||||
}
|
||||
|
||||
void clear_eol() {
|
||||
uint16_t *chardata = (void*)chardata32;
|
||||
for(int x = cx; x < FB_WIDTH_CHAR; x++) {
|
||||
chardata[cx + cy * FB_WIDTH_CHAR] = 32 | attr;
|
||||
}
|
||||
}
|
||||
|
||||
void clear_screen() {
|
||||
uint32_t x = (32 | attr) | ((32 | attr) << 16);
|
||||
for(size_t i=0; i<count_of(chardata32); i++) { chardata32[i] = x; }
|
||||
}
|
||||
|
||||
void cursor_left() {
|
||||
if(cx > 0) cx -= 1;
|
||||
}
|
||||
|
||||
void cursor_position(esc_state *st) {
|
||||
// param 1 is row (cy), 1-bsaed
|
||||
if(st->esc_param[1] > 0 && st->esc_param[1] < FB_WIDTH_CHAR) {
|
||||
cx = st->esc_param[1] - 1;
|
||||
}
|
||||
// param 1 is column (cx), 1-bsaed
|
||||
if(st->esc_param[0] > 0 && st->esc_param[0] < FB_HEIGHT_CHAR) {
|
||||
cy = st->esc_param[0] - 1;
|
||||
}
|
||||
}
|
||||
|
||||
int map_one(int i) {
|
||||
return (i > 0) + (i > 6);
|
||||
}
|
||||
|
||||
void char_attr(esc_state *st) {
|
||||
int new_fg = 2;
|
||||
int new_bg = 0;
|
||||
|
||||
for(int i= 0; i<count_of(st->esc_param); i++) {
|
||||
int p = st->esc_param[i];
|
||||
if (30 <= p && p <= 37) new_fg = map_one(p - 30);
|
||||
if (90 <= p && p <= 97) new_fg = map_one(p - 90);
|
||||
if (40 <= p && p <= 47) new_fg = map_one(p - 40);
|
||||
if (100 <= p && p <= 107) new_fg = map_one(p - 100);
|
||||
lw_cell_t char_attr(void *user_data, const struct lw_parsed_attr *attr) {
|
||||
int fg = map_one(attr->fg);
|
||||
int bg = map_one(attr->bg);
|
||||
if(attr->bold) fg = 3;
|
||||
if(attr->blink) fg ^= 4;
|
||||
if(attr->inverse) {
|
||||
return MAKE_ATTR(bg, fg);
|
||||
}
|
||||
attr = MAKE_ATTR(new_fg, new_bg) << 8;
|
||||
return MAKE_ATTR(fg, bg);
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
#if !STANDALONE
|
||||
set_sys_clock_khz(vga_660x480_60_sys_clock_khz, false);
|
||||
stdio_init_all();
|
||||
#endif
|
||||
|
||||
vt100 = lw_terminal_vt100_init(NULL, NULL, NULL, FB_WIDTH_CHAR, FB_HEIGHT_CHAR);
|
||||
multicore_launch_core1(core1_entry);
|
||||
|
||||
scrnprintf(
|
||||
"(line 0)\r\n"
|
||||
"CR100 terminal demo...\r\n"
|
||||
);
|
||||
|
||||
for(int bg = 0; bg < 8; bg++) {
|
||||
for(int fg = 0; fg < 8; fg++) {
|
||||
attr = MAKE_ATTR(fg, bg) << 8;
|
||||
scrnprintf(" %o%o ", bg, fg);
|
||||
attr = 0x300;
|
||||
scrnprintf(" ");
|
||||
}
|
||||
scrnprintf("\r\n");
|
||||
}
|
||||
|
||||
multicore_launch_core1(core1_entry);
|
||||
attr = 0x300;
|
||||
show_cursor();
|
||||
while (true) {
|
||||
int c = getchar();
|
||||
if (c == EOF) { continue; }
|
||||
|
||||
vt_action action = vt_process_code(&vt_st, c);
|
||||
|
||||
if (action == NO_OUTPUT) { continue; }
|
||||
|
||||
hide_cursor();
|
||||
switch(action) {
|
||||
case NO_OUTPUT:
|
||||
__builtin_unreachable();
|
||||
|
||||
case PRINTABLE:
|
||||
scrnprintf("%c", c);
|
||||
if(0 && c == '\r')
|
||||
scrnprintf("\n");
|
||||
break;
|
||||
|
||||
case BELL:
|
||||
invert_screen();
|
||||
sleep_ms(100);
|
||||
invert_screen();
|
||||
break;
|
||||
|
||||
case CLEAR_EOL:
|
||||
clear_eol();
|
||||
break;
|
||||
|
||||
case CLEAR_SCREEN:
|
||||
clear_screen();
|
||||
break;
|
||||
|
||||
case CURSOR_LEFT:
|
||||
cursor_left();
|
||||
break;
|
||||
|
||||
case CURSOR_POSITION:
|
||||
cursor_position(&vt_st);
|
||||
break;
|
||||
|
||||
case CHAR_ATTR:
|
||||
char_attr(&vt_st);
|
||||
break;
|
||||
}
|
||||
show_cursor();
|
||||
char buf[2] = { c, 0 };
|
||||
lw_terminal_vt100_read_str(vt100, buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
7
hl-vt100/.gitignore
vendored
Normal file
7
hl-vt100/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
*.o
|
||||
*.so
|
||||
parser
|
||||
*.egg-info/
|
||||
.venv/
|
||||
.envrc
|
||||
test
|
||||
24
hl-vt100/LICENSE
Normal file
24
hl-vt100/LICENSE
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
vt100-emulator is distributed under the following terms:
|
||||
|
||||
Copyright (c) 2016 Palard Julien. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
1
hl-vt100/MANIFEST.in
Normal file
1
hl-vt100/MANIFEST.in
Normal file
|
|
@ -0,0 +1 @@
|
|||
include src/*.h
|
||||
45
hl-vt100/Makefile
Normal file
45
hl-vt100/Makefile
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
##
|
||||
## Makefile for vt100
|
||||
##
|
||||
## Made by julien palard
|
||||
##
|
||||
|
||||
NAME = vt100
|
||||
VERSION = 0
|
||||
MINOR = 0
|
||||
RELEASE = 0
|
||||
|
||||
LINKERNAME = lib$(NAME).so
|
||||
SONAME = $(LINKERNAME).$(VERSION)
|
||||
REALNAME = $(SONAME).$(MINOR).$(RELEASE)
|
||||
|
||||
SRC = src/lw_terminal_parser.c src/lw_terminal_vt100.c src/hl_vt100.c
|
||||
SRC_TEST = src/test.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
OBJ_TEST = $(SRC_TEST:.c=.o)
|
||||
CC = gcc
|
||||
INCLUDE = src
|
||||
DEFINE = _GNU_SOURCE
|
||||
CFLAGS = -DNDEBUG -g3 -Wextra -Wstrict-prototypes -Wall -std=gnu17 -fPIC -I$(INCLUDE)
|
||||
LIB = -lutil
|
||||
RM = rm -f
|
||||
|
||||
$(NAME): $(OBJ)
|
||||
$(CC) --shared $(OBJ) $(LIB) -o $(LINKERNAME)
|
||||
|
||||
test: $(OBJ_TEST)
|
||||
$(CC) $(OBJ_TEST) -L . -l$(NAME) -o test
|
||||
|
||||
all:
|
||||
@make $(NAME)
|
||||
|
||||
.c.o:
|
||||
$(CC) -D $(DEFINE) -c $(CFLAGS) $< -o $(<:.c=.o)
|
||||
|
||||
clean:
|
||||
$(RM) $(LINKERNAME) test src/*~ *~ src/\#*\# src/*.o \#*\# *.o *core
|
||||
|
||||
re: clean all
|
||||
|
||||
check-syntax:
|
||||
gcc -Isrc -Wall -Wextra -Wfallthrough -std=gnu17 -o /dev/null -S ${CHK_SOURCES}
|
||||
189
hl-vt100/README.md
Normal file
189
hl-vt100/README.md
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
# vt100 emulator
|
||||
|
||||
`vt100-emulator` is a headless
|
||||
[vt100](https://fr.wikipedia.org/wiki/VT100) emulator, a bit like any
|
||||
terminal you may use daily (like urxvt, xterm, ...) but those you're
|
||||
using are NOT headless, they have a graphical interface to interact
|
||||
with you, human). Here, `vt100-emulator` is only the underlying a `C`
|
||||
and `Python` API to an actual emulator, so you can do everything you
|
||||
want with it, like interfacing over TCP, HTTP, automatically testing
|
||||
your implementation `malloc` against `top` while running `top` in the
|
||||
headless terminal, whatever pleases you.
|
||||
|
||||
For copyright information, please see the file LICENSE in this
|
||||
directory or in the files of the source tree.
|
||||
|
||||
|
||||
# INSTALL
|
||||
|
||||
## Python module
|
||||
|
||||
pip install hl-vt100
|
||||
|
||||
|
||||
## Python module from source
|
||||
|
||||
The simpliest way is just to run `pip install .` from within the repo,
|
||||
but if you want build artifacts, you can build one in an isolated
|
||||
environment using:
|
||||
|
||||
pip install build
|
||||
python -m build
|
||||
|
||||
Or just create an `sdist` the quick way:
|
||||
n
|
||||
python setup.py sdist
|
||||
|
||||
In both case it will provide a build artifact in the `dist/` directory
|
||||
that you can also `pip install`.
|
||||
|
||||
|
||||
# Usage using the Python wrapper (same methods in C)
|
||||
|
||||
```python
|
||||
import hl_vt100
|
||||
|
||||
|
||||
def dump(vt100):
|
||||
print("╭" + "─" * vt100.width + "╮")
|
||||
for line in vt100.getlines():
|
||||
print(f"│{line:{vt100.width}}│")
|
||||
print("╰" + "─" * vt100.width + "╯")
|
||||
|
||||
|
||||
def main():
|
||||
vt100 = hl_vt100.vt100_headless()
|
||||
vt100.changed_callback = lambda: dump(vt100)
|
||||
vt100.fork('top', ['top'])
|
||||
vt100.main_loop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
# Usage using the C library
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "hl_vt100.h"
|
||||
|
||||
|
||||
void changed(struct vt100_headless *vt100)
|
||||
{
|
||||
const char **lines;
|
||||
|
||||
lines = vt100_headless_getlines(vt100);
|
||||
for (unsigned int y = 0; y < vt100->term->height; ++y)
|
||||
{
|
||||
write(1, "|", 1);
|
||||
write(1, lines[y], vt100->term->width);
|
||||
write(1, "|\n", 2);
|
||||
}
|
||||
write(1, "\n", 1);
|
||||
}
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
struct vt100_headless *vt100;
|
||||
char *argv[] = {"top", NULL};
|
||||
|
||||
vt100 = new_vt100_headless();
|
||||
vt100_headless_fork(vt100, argv[0], argv);
|
||||
vt100->changed = changed;
|
||||
vt100_headless_main_loop(vt100);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
# Code overview
|
||||
|
||||
lw_terminal_parser, lw_terminal_vt100, and hl_vt100 are three modules used to emulate a vt100 terminal:
|
||||
|
||||
```
|
||||
-------------
|
||||
| |
|
||||
| Your Code |
|
||||
| |
|
||||
-------------
|
||||
| ^
|
||||
vt100 = vt100_headless_init() | |
|
||||
vt100->changed = changed; | | hl_vt100 raises 'changed'
|
||||
vt100_headless_fork(vt100, ... | | when the screen has changed.
|
||||
| | You get the content of the screen
|
||||
| | calling vt100_headless_getlines.
|
||||
V |
|
||||
------------- -------------
|
||||
Read from PTY master and write | | | PTY | |
|
||||
to lw_terminal_vt100_read_str | | hl_vt100 |<------------>| Program |
|
||||
V | |Master Slave| |
|
||||
------------- -------------
|
||||
| |^ hl_vt100 gets lw_terminal_vt100's
|
||||
| || lines by calling
|
||||
| || lw_terminal_vt100_getlines
|
||||
| ||
|
||||
| ||
|
||||
V V|
|
||||
----------------------
|
||||
Got data from | | | Recieve data from callbacks
|
||||
lw_terminal_vt100_read_str | | lw_terminal_vt100 | And store an in-memory
|
||||
give it to | | | state of the vt100 terminal
|
||||
lw_terminal_parser_read_strV ----------------------
|
||||
| ^
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| | Callbacks
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
V |
|
||||
----------------------
|
||||
Got data from | |
|
||||
lw_terminal_pasrser_read_str | lw_terminal_parser |
|
||||
parses, and call callbacks | |
|
||||
----------------------
|
||||
```
|
||||
|
||||
## lw_terminal_parser
|
||||
|
||||
`lw_terminal_parser` parses terminal escape sequences, calling callbacks
|
||||
when a sequence is sucessfully parsed, read `example/parse.c`.
|
||||
|
||||
Provides:
|
||||
|
||||
* `struct lw_terminal *lw_terminal_parser_init(void);`
|
||||
* `void lw_terminal_parser_destroy(struct lw_terminal* this);`
|
||||
* `void lw_terminal_parser_default_unimplemented(struct lw_terminal* this, char *seq, char chr);`
|
||||
* `void lw_terminal_parser_read(struct lw_terminal *this, char c);`
|
||||
* `void lw_terminal_parser_read_str(struct lw_terminal *this, char *c);`
|
||||
|
||||
|
||||
## lw_terminal_vt100
|
||||
|
||||
Hooks into a `lw_terminal_parser` and keep an in-memory state of the
|
||||
screen of a vt100.
|
||||
|
||||
Provides:
|
||||
|
||||
* `struct lw_terminal_vt100 *lw_terminal_vt100_init(void *user_data, void (*unimplemented)(struct lw_terminal* term_emul, char *seq, char chr));`
|
||||
* `char lw_terminal_vt100_get(struct lw_terminal_vt100 *vt100, unsigned int x, unsigned int y);`
|
||||
* `const char **lw_terminal_vt100_getlines(struct lw_terminal_vt100 *vt100);`
|
||||
* `void lw_terminal_vt100_destroy(struct lw_terminal_vt100 *this);`
|
||||
* `void lw_terminal_vt100_read_str(struct lw_terminal_vt100 *this, char *buffer);`
|
||||
|
||||
|
||||
## hl_vt100
|
||||
|
||||
Forks a program, plug its io to a pseudo terminal and emulate a vt100
|
||||
using `lw_terminal_vt100`.
|
||||
|
||||
Provides:
|
||||
|
||||
* `void vt100_headless_fork(struct vt100_headless *this, const char *progname, char *const argv[]);`
|
||||
* `struct vt100_headless *vt100_headless_init(void);`
|
||||
* `const char **vt100_headless_getlines(struct vt100_headless *this);`
|
||||
79
hl-vt100/doc/vt100_emulator.3
Normal file
79
hl-vt100/doc/vt100_emulator.3
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH lw_terminal_parser 3 2011-09-27
|
||||
.SH NAME
|
||||
lw_terminal_parser_init, lw_terminal_read, lw_terminal_parser_read_str, lw_terminal_destroy \- LW Terminal Parser
|
||||
.SH SYNOPSIS
|
||||
.B #include <lw_terminal_parser.h>
|
||||
.sp
|
||||
.BI "struct lw_terminal *lw_terminal_parser_init(void);"
|
||||
.br
|
||||
.BI "void lw_terminal_read(struct lw_terminal *" this ", char " c ");"
|
||||
.br
|
||||
.BI "void lw_terminal_parser_read_str(struct lw_terminal *" this " , char *" c ");"
|
||||
.br
|
||||
.BI "void lw_terminal_parser_read_buf(struct lw_terminal *" this " , char *" c ", size_t " n);"
|
||||
.br
|
||||
.BI "void lw_terminal_destroy(struct lw_terminal* " this ");"
|
||||
.SH DESCRIPTION
|
||||
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
|
||||
.BR lw_terminal_parser_init ()
|
||||
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
|
||||
.BR lw_terminal_parser_read_buf()
|
||||
.BR lw_terminal_parser_read_str()
|
||||
or
|
||||
.BR lw_terminal_read()
|
||||
to make the terminal parse them.
|
||||
Finally to free the struct terminal, call
|
||||
.BR lw_terminal_destroy().
|
||||
.PP
|
||||
lw_terminal->callback is a structure for you to hook into escape sequences.
|
||||
This struct is broke into substructures for each type of sequencec : esc, csi, hash, and scs.
|
||||
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.
|
||||
.PP
|
||||
Here is simple an example on how to hook a callback into a terminal emulator :
|
||||
.nf
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "../src/lw_terminal_parser.h"
|
||||
|
||||
static void vt100_write(struct lw_terminal *term_emul __attribute__((unused)),
|
||||
char c)
|
||||
{
|
||||
printf("Got a char : %c\\n", c);
|
||||
}
|
||||
|
||||
static void csi_f(struct lw_terminal *term_emul)
|
||||
{
|
||||
printf("\\\\033[...f with %d parameters\\n", term_emul->argc);
|
||||
}
|
||||
|
||||
static void csi_K(struct lw_terminal *term_emul)
|
||||
{
|
||||
printf("\\\\033[...K with %d parameters\\n", term_emul->argc);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct lw_terminal *lw_terminal;
|
||||
|
||||
lw_terminal = lw_terminal_parser_init();
|
||||
if (lw_terminal == NULL)
|
||||
return EXIT_FAILURE;
|
||||
lw_terminal->write = vt100_write;
|
||||
lw_terminal->callbacks.csi.f = csi_f;
|
||||
lw_terminal->callbacks.csi.K = csi_K;
|
||||
lw_terminal_parser_read_str(lw_terminal, "\\033[2KHello world !\\033[f");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
.fi
|
||||
|
||||
.br
|
||||
.SH "AUTHOR"
|
||||
lw_terminal_parser was written by Julien Palard.
|
||||
.PP
|
||||
This manual page was written by Julien Palard <julien@palard.fr>,
|
||||
for the Debian project (and may be used by others).
|
||||
33
hl-vt100/example/Makefile
Normal file
33
hl-vt100/example/Makefile
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
##
|
||||
## Makefile for lw_vt100 examples
|
||||
##
|
||||
## Made by julien palard <vt100@mandark.fr>
|
||||
##
|
||||
|
||||
NAME = parser
|
||||
|
||||
SRC = parser.c ../src/lw_terminal_parser.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
CC = gcc
|
||||
INCLUDE = ../src
|
||||
DEFINE = _GNU_SOURCE
|
||||
CFLAGS = -g3 -Wextra -Wstrict-prototypes -Wall -ansi -pedantic -I$(INCLUDE)
|
||||
LIB = -lutil
|
||||
RM = rm -f
|
||||
|
||||
$(NAME): $(OBJ)
|
||||
$(CC) $(OBJ) $(LIB) -o $(NAME)
|
||||
|
||||
all:
|
||||
@make $(NAME)
|
||||
|
||||
.c.o:
|
||||
$(CC) -D $(DEFINE) -c $(CFLAGS) $< -o $(<:.c=.o)
|
||||
|
||||
clean:
|
||||
$(RM) $(NAME) *~ \#*\# *.o *core
|
||||
|
||||
re: clean all
|
||||
|
||||
check-syntax:
|
||||
gcc -Isrc -Wall -Wextra -ansi -pedantic -o /dev/null -S ${CHK_SOURCES}
|
||||
33
hl-vt100/example/parser.c
Normal file
33
hl-vt100/example/parser.c
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "../src/lw_terminal_parser.h"
|
||||
|
||||
static void vt100_write(struct lw_terminal *term_emul __attribute__((unused)),
|
||||
char c)
|
||||
{
|
||||
printf("Got a char : %c\n", c);
|
||||
}
|
||||
|
||||
static void csi_f(struct lw_terminal *term_emul)
|
||||
{
|
||||
printf("\\033[...f with %d parameters\n", term_emul->argc);
|
||||
}
|
||||
|
||||
static void csi_K(struct lw_terminal *term_emul)
|
||||
{
|
||||
printf("\\033[...K with %d parameters\n", term_emul->argc);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct lw_terminal *lw_terminal;
|
||||
|
||||
lw_terminal = lw_terminal_parser_init();
|
||||
if (lw_terminal == NULL)
|
||||
return EXIT_FAILURE;
|
||||
lw_terminal->write = vt100_write;
|
||||
lw_terminal->callbacks.csi.f = csi_f;
|
||||
lw_terminal->callbacks.csi.K = csi_K;
|
||||
lw_terminal_parser_read_str(lw_terminal, "\033[2KHello world !\033[f");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
30
hl-vt100/example/top.c
Normal file
30
hl-vt100/example/top.c
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "hl_vt100.h"
|
||||
|
||||
|
||||
void changed(struct vt100_headless *vt100)
|
||||
{
|
||||
const char **lines;
|
||||
|
||||
lines = vt100_headless_getlines(vt100);
|
||||
for (unsigned int y = 0; y < vt100->term->height; ++y)
|
||||
{
|
||||
write(1, "|", 1);
|
||||
write(1, lines[y], vt100->term->width);
|
||||
write(1, "|\n", 2);
|
||||
}
|
||||
write(1, "\n", 1);
|
||||
}
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
struct vt100_headless *vt100;
|
||||
char *argv[] = {"top", NULL};
|
||||
|
||||
vt100 = new_vt100_headless();
|
||||
vt100_headless_fork(vt100, argv[0], argv);
|
||||
vt100->changed = changed;
|
||||
vt100_headless_main_loop(vt100);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
19
hl-vt100/example/top.py
Normal file
19
hl-vt100/example/top.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import hl_vt100
|
||||
|
||||
|
||||
def dump(vt100):
|
||||
print("╭" + "─" * vt100.width + "╮")
|
||||
for line in vt100.getlines():
|
||||
print(f"│{line:{vt100.width}}│")
|
||||
print("╰" + "─" * vt100.width + "╯")
|
||||
|
||||
|
||||
def main():
|
||||
vt100 = hl_vt100.vt100_headless()
|
||||
vt100.changed_callback = lambda: dump(vt100)
|
||||
vt100.fork('top', ['top'])
|
||||
vt100.main_loop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
44
hl-vt100/run_tests.sh
Executable file
44
hl-vt100/run_tests.sh
Executable file
|
|
@ -0,0 +1,44 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2016 Julien Palard.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
set -eo pipefail
|
||||
|
||||
if [ "$1" = python ]
|
||||
then
|
||||
python -m pip install .
|
||||
python vt_test.py
|
||||
exit
|
||||
fi
|
||||
|
||||
make clean
|
||||
|
||||
if [ "$1" = c ]
|
||||
then
|
||||
make && make test
|
||||
LD_LIBRARY_PATH=. ./test /usr/bin/top -n 1
|
||||
exit
|
||||
fi
|
||||
|
||||
bash $0 python
|
||||
bash $0 c
|
||||
39
hl-vt100/setup.py
Normal file
39
hl-vt100/setup.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
setup.py file for hl_vt100
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import setup, Extension
|
||||
|
||||
|
||||
hl_vt100_module = Extension('hl_vt100',
|
||||
include_dirs=['src'],
|
||||
define_macros=[('NDEBUG', '1')],
|
||||
sources=['src/vt100_module.c',
|
||||
'src/hl_vt100.c',
|
||||
'src/lw_terminal_parser.c',
|
||||
'src/lw_terminal_vt100.c'])
|
||||
|
||||
setup(name='hl_vt100',
|
||||
version='0.2',
|
||||
url='https://github.com/JulienPalard/vt100-emulator',
|
||||
author="Julien Palard",
|
||||
author_email='julien@palard.fr',
|
||||
description="""Headless vt100 emulator""",
|
||||
long_description=(Path(__file__).parent / "README.md").read_text(encoding="UTF-8"),
|
||||
long_description_content_type="text/markdown",
|
||||
ext_modules=[hl_vt100_module],
|
||||
include_package_data=True,
|
||||
py_modules=["hl_vt100"],
|
||||
classifiers=[
|
||||
"Programming Language :: C",
|
||||
"Programming Language :: Python",
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Operating System :: OS Independent",
|
||||
"Topic :: System :: Emulators",
|
||||
"Topic :: Terminals :: Terminal Emulators/X Terminals"
|
||||
])
|
||||
171
hl-vt100/src/hl_vt100.c
Normal file
171
hl-vt100/src/hl_vt100.c
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Julien Palard.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define _XOPEN_SOURCE
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <pty.h>
|
||||
#include <stdlib.h>
|
||||
#include "hl_vt100.h"
|
||||
|
||||
struct vt100_headless *new_vt100_headless(void)
|
||||
{
|
||||
return calloc(1, sizeof(struct vt100_headless));
|
||||
}
|
||||
|
||||
void delete_vt100_headless(struct vt100_headless *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
static void set_non_canonical(struct vt100_headless *this, int fd)
|
||||
{
|
||||
struct termios termios;
|
||||
|
||||
ioctl(fd, TCGETS, &this->backup);
|
||||
ioctl(fd, TCGETS, &termios);
|
||||
termios.c_iflag |= ICANON;
|
||||
termios.c_cc[VMIN] = 1;
|
||||
termios.c_cc[VTIME] = 0;
|
||||
ioctl(fd, TCSETS, &termios);
|
||||
}
|
||||
|
||||
static void restore_termios(struct vt100_headless *this, int fd)
|
||||
{
|
||||
ioctl(fd, TCSETS, &this->backup);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
static void strdump(char *str)
|
||||
{
|
||||
while (*str != '\0')
|
||||
{
|
||||
if (*str >= ' ' && *str <= '~')
|
||||
fprintf(stderr, "%c", *str);
|
||||
else
|
||||
fprintf(stderr, "\\0%o", *str);
|
||||
str += 1;
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
void vt100_headless_stop(struct vt100_headless *this)
|
||||
{
|
||||
this->should_quit = 1;
|
||||
}
|
||||
|
||||
int vt100_headless_main_loop(struct vt100_headless *this)
|
||||
{
|
||||
char buffer[4096];
|
||||
fd_set rfds;
|
||||
int retval;
|
||||
ssize_t read_size;
|
||||
|
||||
while (!this->should_quit)
|
||||
{
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(this->master, &rfds);
|
||||
FD_SET(0, &rfds);
|
||||
retval = select(this->master + 1, &rfds, NULL, NULL, NULL);
|
||||
if (retval == -1)
|
||||
{
|
||||
perror("select()");
|
||||
}
|
||||
if (FD_ISSET(0, &rfds))
|
||||
{
|
||||
read_size = read(0, &buffer, 4096);
|
||||
if (read_size == -1)
|
||||
{
|
||||
perror("read");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
buffer[read_size] = '\0';
|
||||
write(this->master, buffer, read_size);
|
||||
}
|
||||
if (FD_ISSET(this->master, &rfds))
|
||||
{
|
||||
read_size = read(this->master, &buffer, 4096);
|
||||
if (read_size == -1)
|
||||
{
|
||||
if (errno == EIO) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
perror("read");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
buffer[read_size] = '\0';
|
||||
#ifndef NDEBUG
|
||||
strdump(buffer);
|
||||
#endif
|
||||
lw_terminal_vt100_read_str(this->term, buffer);
|
||||
if (this->changed != NULL)
|
||||
this->changed(this);
|
||||
}
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void master_write(void *user_data, void *buffer, size_t len)
|
||||
{
|
||||
struct vt100_headless *this;
|
||||
|
||||
this = (struct vt100_headless*)user_data;
|
||||
write(this->master, buffer, len);
|
||||
}
|
||||
|
||||
const lw_cell_t **vt100_headless_getlines(struct vt100_headless *this)
|
||||
{
|
||||
return lw_terminal_vt100_getlines(this->term);
|
||||
}
|
||||
|
||||
void vt100_headless_fork(struct vt100_headless *this,
|
||||
const char *progname,
|
||||
char **argv)
|
||||
{
|
||||
int child;
|
||||
struct winsize winsize;
|
||||
|
||||
set_non_canonical(this, 0);
|
||||
winsize.ws_row = 24;
|
||||
winsize.ws_col = 80;
|
||||
child = forkpty(&this->master, NULL, NULL, NULL);
|
||||
if (child == CHILD)
|
||||
{
|
||||
setsid();
|
||||
putenv("TERM=vt100");
|
||||
execvp(progname, argv);
|
||||
return ;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->term = lw_terminal_vt100_init(this, lw_terminal_parser_default_unimplemented, NULL, winsize.ws_col, winsize.ws_row);
|
||||
this->term->master_write = master_write;
|
||||
ioctl(this->master, TIOCSWINSZ, &winsize);
|
||||
}
|
||||
restore_termios(this, 0);
|
||||
}
|
||||
54
hl-vt100/src/hl_vt100.h
Normal file
54
hl-vt100/src/hl_vt100.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Julien Palard.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __VT100_HEADLESS_H__
|
||||
#define __VT100_HEADLESS_H__
|
||||
|
||||
#define CHILD 0
|
||||
|
||||
#include <utmp.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include "lw_terminal_vt100.h"
|
||||
|
||||
struct vt100_headless
|
||||
{
|
||||
int master;
|
||||
struct termios backup;
|
||||
struct lw_terminal_vt100 *term;
|
||||
int should_quit;
|
||||
void (*changed)(struct vt100_headless *this);
|
||||
};
|
||||
|
||||
|
||||
void vt100_headless_fork(struct vt100_headless *this, const char *progname, char **argv);
|
||||
int vt100_headless_main_loop(struct vt100_headless *this);
|
||||
void delete_vt100_headless(struct vt100_headless *this);
|
||||
struct vt100_headless *new_vt100_headless(void);
|
||||
const lw_cell_t **vt100_headless_getlines(struct vt100_headless *this);
|
||||
void vt100_headless_stop(struct vt100_headless *this);
|
||||
|
||||
#endif
|
||||
234
hl-vt100/src/lw_terminal_parser.c
Normal file
234
hl-vt100/src/lw_terminal_parser.c
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Julien Palard.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef NDEBUG
|
||||
# include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "lw_terminal_parser.h"
|
||||
|
||||
static void lw_terminal_parser_push(struct lw_terminal *this, char c)
|
||||
{
|
||||
if (this->stack_ptr >= TERM_STACK_SIZE)
|
||||
return ;
|
||||
this->stack[this->stack_ptr++] = c;
|
||||
}
|
||||
|
||||
static void lw_terminal_parser_parse_params(struct lw_terminal *this)
|
||||
{
|
||||
unsigned int i;
|
||||
int got_something;
|
||||
|
||||
got_something = 0;
|
||||
this->argc = 0;
|
||||
this->argv[0] = 0;
|
||||
for (i = 0; i < this->stack_ptr; ++i)
|
||||
{
|
||||
if (this->stack[i] >= '0' && this->stack[i] <= '9')
|
||||
{
|
||||
got_something = 1;
|
||||
this->argv[this->argc] = this->argv[this->argc] * 10
|
||||
+ this->stack[i] - '0';
|
||||
}
|
||||
else if (this->stack[i] == ';')
|
||||
{
|
||||
got_something = 0;
|
||||
this->argc += 1;
|
||||
this->argv[this->argc] = 0;
|
||||
}
|
||||
}
|
||||
this->argc += got_something;
|
||||
}
|
||||
|
||||
static void lw_terminal_parser_call_CSI(struct lw_terminal *this, char c)
|
||||
{
|
||||
lw_terminal_parser_parse_params(this);
|
||||
if (((term_action *)&this->callbacks.csi)[c - '0'] == NULL)
|
||||
{
|
||||
if (this->unimplemented != NULL)
|
||||
this->unimplemented(this, "CSI", c);
|
||||
goto leave;
|
||||
}
|
||||
((term_action *)&this->callbacks.csi)[c - '0'](this);
|
||||
leave:
|
||||
this->state = INIT;
|
||||
this->flag = '\0';
|
||||
this->stack_ptr = 0;
|
||||
this->argc = 0;
|
||||
}
|
||||
|
||||
static void lw_terminal_parser_call_ESC(struct lw_terminal *this, char c)
|
||||
{
|
||||
if (((term_action *)&this->callbacks.esc)[c - '0'] == NULL)
|
||||
{
|
||||
if (this->unimplemented != NULL)
|
||||
this->unimplemented(this, "ESC", c);
|
||||
goto leave;
|
||||
}
|
||||
((term_action *)&this->callbacks.esc)[c - '0'](this);
|
||||
leave:
|
||||
this->state = INIT;
|
||||
this->stack_ptr = 0;
|
||||
this->argc = 0;
|
||||
}
|
||||
|
||||
static void lw_terminal_parser_call_HASH(struct lw_terminal *this, char c)
|
||||
{
|
||||
if (((term_action *)&this->callbacks.hash)[c - '0'] == NULL)
|
||||
{
|
||||
if (this->unimplemented != NULL)
|
||||
this->unimplemented(this, "HASH", c);
|
||||
goto leave;
|
||||
}
|
||||
((term_action *)&this->callbacks.hash)[c - '0'](this);
|
||||
leave:
|
||||
this->state = INIT;
|
||||
this->stack_ptr = 0;
|
||||
this->argc = 0;
|
||||
}
|
||||
|
||||
static void lw_terminal_parser_call_GSET(struct lw_terminal *this, char c)
|
||||
{
|
||||
if (c < '0' || c > 'B'
|
||||
|| ((term_action *)&this->callbacks.scs)[c - '0'] == NULL)
|
||||
{
|
||||
if (this->unimplemented != NULL)
|
||||
this->unimplemented(this, "GSET", c);
|
||||
goto leave;
|
||||
}
|
||||
((term_action *)&this->callbacks.scs)[c - '0'](this);
|
||||
leave:
|
||||
this->state = INIT;
|
||||
this->stack_ptr = 0;
|
||||
this->argc = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** INIT
|
||||
** \_ ESC "\033"
|
||||
** | \_ CSI "\033["
|
||||
** | | \_ c == '?' : term->flag = '?'
|
||||
** | | \_ c == ';' || (c >= '0' && c <= '9') : term_push
|
||||
** | | \_ else : term_call_CSI()
|
||||
** | \_ HASH "\033#"
|
||||
** | | \_ term_call_hash()
|
||||
** | \_ G0SET "\033("
|
||||
** | | \_ term_call_GSET()
|
||||
** | \_ G1SET "\033)"
|
||||
** | | \_ term_call_GSET()
|
||||
** \_ term->write()
|
||||
*/
|
||||
void lw_terminal_parser_read(struct lw_terminal *this, char c)
|
||||
{
|
||||
if (this->state == INIT)
|
||||
{
|
||||
if (c == '\033')
|
||||
this->state = ESC;
|
||||
else
|
||||
this->write(this, c);
|
||||
}
|
||||
else if (this->state == ESC)
|
||||
{
|
||||
if (c == '[')
|
||||
this->state = CSI;
|
||||
else if (c == '#')
|
||||
this->state = HASH;
|
||||
else if (c == '(')
|
||||
this->state = G0SET;
|
||||
else if (c == ')')
|
||||
this->state = G1SET;
|
||||
else if (c >= '0' && c <= 'z')
|
||||
lw_terminal_parser_call_ESC(this, c);
|
||||
else this->write(this, c);
|
||||
}
|
||||
else if (this->state == HASH)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
lw_terminal_parser_call_HASH(this, c);
|
||||
else
|
||||
this->write(this, c);
|
||||
}
|
||||
else if (this->state == G0SET || this->state == G1SET)
|
||||
{
|
||||
lw_terminal_parser_call_GSET(this, c);
|
||||
}
|
||||
else if (this->state == CSI)
|
||||
{
|
||||
if (c == '?')
|
||||
this->flag = '?';
|
||||
else if (c == ';' || (c >= '0' && c <= '9'))
|
||||
lw_terminal_parser_push(this, c);
|
||||
else if (c >= '?' && c <= 'z')
|
||||
lw_terminal_parser_call_CSI(this, c);
|
||||
else
|
||||
this->write(this, c);
|
||||
}
|
||||
}
|
||||
|
||||
void lw_terminal_parser_read_buf(struct lw_terminal *this, const char *c, size_t n)
|
||||
{
|
||||
while (n--)
|
||||
lw_terminal_parser_read(this, *c++);
|
||||
}
|
||||
|
||||
void lw_terminal_parser_read_str(struct lw_terminal *this, const char *c)
|
||||
{
|
||||
while (*c)
|
||||
lw_terminal_parser_read(this, *c++);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void lw_terminal_parser_default_unimplemented(struct lw_terminal* this, char *seq, char chr)
|
||||
{
|
||||
unsigned int argc;
|
||||
|
||||
fprintf(stderr, "WARNING: UNIMPLEMENTED %s (", seq);
|
||||
for (argc = 0; argc < this->argc; ++argc)
|
||||
{
|
||||
fprintf(stderr, "%d", this->argv[argc]);
|
||||
if (argc != this->argc - 1)
|
||||
fprintf(stderr, ", ");
|
||||
}
|
||||
fprintf(stderr, ")%o\n", chr);
|
||||
}
|
||||
#else
|
||||
void lw_terminal_parser_default_unimplemented(struct lw_terminal* this, char *seq, char chr)
|
||||
{
|
||||
this = this;
|
||||
seq = seq;
|
||||
chr = chr;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct lw_terminal *lw_terminal_parser_init(void)
|
||||
{
|
||||
return calloc(1, sizeof(struct lw_terminal));
|
||||
}
|
||||
|
||||
void lw_terminal_parser_destroy(struct lw_terminal* this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
228
hl-vt100/src/lw_terminal_parser.h
Normal file
228
hl-vt100/src/lw_terminal_parser.h
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Julien Palard.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __TERMINAL_H__
|
||||
#define __TERMINAL_H__
|
||||
|
||||
/*
|
||||
**
|
||||
** Introduction
|
||||
** ============
|
||||
**
|
||||
** terminal.c is a basic level that parses escape sequences to call
|
||||
** appropriated callbacks permiting you to implement a specific terminal.
|
||||
**
|
||||
** Callbacks
|
||||
** =========
|
||||
**
|
||||
** terminal.c maps sequences to callbacks in this way :
|
||||
** \033... maps to terminal->callbacks->esc
|
||||
** \033[... maps to terminal->callbacks->csi
|
||||
** \033#... maps to terminal->callbacks->hash
|
||||
** and \033( and \033) maps to terminal->callbacks->scs
|
||||
**
|
||||
** In 'callbacks', esc, csi, hash and scs are structs ascii_callbacks
|
||||
** where you can bind your callbacks.
|
||||
**
|
||||
** Typically when terminal parses \033[42;43m
|
||||
** it calls terminal->callbacks->csi->m(terminal);
|
||||
**
|
||||
** Parameters (here 42;43) are stored in terminal->argc and terminal->argv
|
||||
** argv is an array of integers of length argc.
|
||||
**
|
||||
** Public members
|
||||
** ==============
|
||||
**
|
||||
** void *user_data :
|
||||
** A (void *) where your implementation can store whatever you want
|
||||
** to get it back on your callabks.
|
||||
**
|
||||
** void (*write)(struct terminal *, char c) :
|
||||
** Hook for your implementation to recieve chars that are not
|
||||
** escape sequences
|
||||
**
|
||||
** struct term_callbacks callbacks :
|
||||
** Hooks for your callbacks to recieve escape sequences
|
||||
**
|
||||
** enum term_state state :
|
||||
** During a callback, typically a scs, you can read here if it's a
|
||||
** G1SET or a G0SET
|
||||
**
|
||||
** unsigned int argc :
|
||||
** For your callbacks, to know how many parameters are available
|
||||
** in argv.
|
||||
**
|
||||
** unsigned int argv[TERM_STACK_SIZE] :
|
||||
** For your callbacks, parameters of escape sequences are accessible
|
||||
** here.
|
||||
** \033[42;43m will have 2 in argc and argv[0] = 42, argv[1] = 43
|
||||
**
|
||||
** char flag;
|
||||
** Optinal constructor flag present before parameters, like in :
|
||||
** \033[?1049h -> The flag will be '?'
|
||||
** Otherwise the flag is set to '\0'
|
||||
**
|
||||
** void (*unimplemented)(struct terminal*, char *seq, char chr) :
|
||||
** Can be NULL, you can hook here to know where the terminal parses an
|
||||
** escape sequence on which you have not registered a callback.
|
||||
**
|
||||
** Exemple
|
||||
** =======
|
||||
**
|
||||
** See terminal_vt100.c for a working exemple.
|
||||
**
|
||||
*/
|
||||
|
||||
#define TERM_STACK_SIZE 1024
|
||||
|
||||
enum term_state
|
||||
{
|
||||
INIT,
|
||||
ESC,
|
||||
HASH,
|
||||
G0SET,
|
||||
G1SET,
|
||||
CSI
|
||||
};
|
||||
|
||||
struct lw_terminal;
|
||||
|
||||
typedef void (*term_action)(struct lw_terminal *emul);
|
||||
|
||||
struct ascii_callbacks
|
||||
{
|
||||
term_action n0;
|
||||
term_action n1;
|
||||
term_action n2;
|
||||
term_action n3;
|
||||
term_action n4;
|
||||
term_action n5;
|
||||
term_action n6;
|
||||
term_action n7;
|
||||
term_action n8;
|
||||
term_action n9;
|
||||
|
||||
term_action h3A;
|
||||
term_action h3B;
|
||||
term_action h3C;
|
||||
term_action h3D;
|
||||
term_action h3E;
|
||||
term_action h3F;
|
||||
term_action h40;
|
||||
|
||||
term_action A;
|
||||
term_action B;
|
||||
term_action C;
|
||||
term_action D;
|
||||
term_action E;
|
||||
term_action F;
|
||||
term_action G;
|
||||
term_action H;
|
||||
term_action I;
|
||||
term_action J;
|
||||
term_action K;
|
||||
term_action L;
|
||||
term_action M;
|
||||
term_action N;
|
||||
term_action O;
|
||||
term_action P;
|
||||
term_action Q;
|
||||
term_action R;
|
||||
term_action S;
|
||||
term_action T;
|
||||
term_action U;
|
||||
term_action V;
|
||||
term_action W;
|
||||
term_action X;
|
||||
term_action Y;
|
||||
term_action Z;
|
||||
|
||||
term_action h5B;
|
||||
term_action h5C;
|
||||
term_action h5D;
|
||||
term_action h5E;
|
||||
term_action h5F;
|
||||
term_action h60;
|
||||
|
||||
term_action a;
|
||||
term_action b;
|
||||
term_action c;
|
||||
term_action d;
|
||||
term_action e;
|
||||
term_action f;
|
||||
term_action g;
|
||||
term_action h;
|
||||
term_action i;
|
||||
term_action j;
|
||||
term_action k;
|
||||
term_action l;
|
||||
term_action m;
|
||||
term_action n;
|
||||
term_action o;
|
||||
term_action p;
|
||||
term_action q;
|
||||
term_action r;
|
||||
term_action s;
|
||||
term_action t;
|
||||
term_action u;
|
||||
term_action v;
|
||||
term_action w;
|
||||
term_action x;
|
||||
term_action y;
|
||||
term_action z;
|
||||
};
|
||||
|
||||
struct term_callbacks
|
||||
{
|
||||
struct ascii_callbacks esc;
|
||||
struct ascii_callbacks csi;
|
||||
struct ascii_callbacks hash;
|
||||
struct ascii_callbacks scs;
|
||||
};
|
||||
|
||||
struct lw_terminal
|
||||
{
|
||||
unsigned int cursor_pos_x;
|
||||
unsigned int cursor_pos_y;
|
||||
enum term_state state;
|
||||
unsigned int argc;
|
||||
unsigned int argv[TERM_STACK_SIZE];
|
||||
void (*write)(struct lw_terminal *, char c);
|
||||
char stack[TERM_STACK_SIZE];
|
||||
unsigned int stack_ptr;
|
||||
struct term_callbacks callbacks;
|
||||
char flag;
|
||||
void *user_data;
|
||||
void (*unimplemented)(struct lw_terminal*,
|
||||
char *seq, char chr);
|
||||
};
|
||||
|
||||
struct lw_terminal *lw_terminal_parser_init(void);
|
||||
void lw_terminal_parser_default_unimplemented(struct lw_terminal* this, char *seq, char chr);
|
||||
void lw_terminal_parser_read(struct lw_terminal *this, char c);
|
||||
void lw_terminal_parser_read_str(struct lw_terminal *this, const char *c);
|
||||
void lw_terminal_parser_read_buf(struct lw_terminal *this, const char *c, size_t n);
|
||||
void lw_terminal_parser_destroy(struct lw_terminal* this);
|
||||
#endif
|
||||
1203
hl-vt100/src/lw_terminal_vt100.c
Normal file
1203
hl-vt100/src/lw_terminal_vt100.c
Normal file
File diff suppressed because it is too large
Load diff
124
hl-vt100/src/lw_terminal_vt100.h
Normal file
124
hl-vt100/src/lw_terminal_vt100.h
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Julien Palard.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __LW_TERMINAL_VT100_H__
|
||||
#define __LW_TERMINAL_VT100_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lw_terminal_parser.h"
|
||||
|
||||
/*
|
||||
* Source : http://vt100.net/docs/vt100-ug/chapter3.html
|
||||
http://vt100.net/docs/tp83/appendixb.html
|
||||
* It's a vt100 implementation, that implements ANSI control function.
|
||||
*/
|
||||
|
||||
#define SCROLLBACK 3
|
||||
|
||||
#define MASK_LNM 1
|
||||
#define MASK_DECCKM 2
|
||||
#define MASK_DECANM 4
|
||||
#define MASK_DECCOLM 8
|
||||
#define MASK_DECSCLM 16
|
||||
#define MASK_DECSCNM 32
|
||||
#define MASK_DECOM 64
|
||||
#define MASK_DECAWM 128
|
||||
#define MASK_DECARM 256
|
||||
#define MASK_DECINLM 512
|
||||
|
||||
#define LNM 20
|
||||
#define DECCKM 1
|
||||
#define DECANM 2
|
||||
#define DECCOLM 3
|
||||
#define DECSCLM 4
|
||||
#define DECSCNM 5
|
||||
#define DECOM 6
|
||||
#define DECAWM 7
|
||||
#define DECARM 8
|
||||
#define DECINLM 9
|
||||
|
||||
|
||||
#define SET_MODE(vt100, mode) ((vt100)->modes |= get_mode_mask(mode))
|
||||
#define UNSET_MODE(vt100, mode) ((vt100)->modes &= ~get_mode_mask(mode))
|
||||
#define MODE_IS_SET(vt100, mode) ((vt100)->modes & get_mode_mask(mode))
|
||||
|
||||
typedef uint16_t lw_cell_t;
|
||||
|
||||
struct lw_parsed_attr {
|
||||
uint8_t fg, bg;
|
||||
bool blink, bold, inverse;
|
||||
};
|
||||
|
||||
#define LW_DEFAULT_ATTR ((struct lw_parsed_attr) { 7, 0, false, false, false })
|
||||
|
||||
/*
|
||||
** frozen_screen is the frozen part of the screen
|
||||
** when margins are set.
|
||||
** The top of the frozen_screen holds the top margin
|
||||
** while the bottom holds the bottom margin.
|
||||
*/
|
||||
struct lw_terminal_vt100
|
||||
{
|
||||
struct lw_terminal *lw_terminal;
|
||||
int ustate, ubits;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
unsigned int saved_x;
|
||||
unsigned int saved_y;
|
||||
unsigned int margin_top;
|
||||
unsigned int margin_bottom;
|
||||
unsigned int top_line; /* Line at the top of the display */
|
||||
lw_cell_t *ascreen;
|
||||
lw_cell_t *afrozen_screen;
|
||||
char *tabulations;
|
||||
bool unicode;
|
||||
unsigned int selected_charset;
|
||||
unsigned int modes;
|
||||
struct lw_parsed_attr parsed_attr;
|
||||
lw_cell_t attr, saved_cell;
|
||||
const lw_cell_t *alines[80];
|
||||
void (*master_write)(void *user_data, void *buffer, size_t len);
|
||||
lw_cell_t (*encode_attr)(void *user_data, const struct lw_parsed_attr *attr);
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
struct lw_terminal_vt100 *lw_terminal_vt100_init(void *user_data,
|
||||
void (*unimplemented)(struct lw_terminal* term_emul,
|
||||
char *seq, char chr),
|
||||
lw_cell_t (*encode_attr)(void *user_data,
|
||||
const struct lw_parsed_attr *attr),
|
||||
unsigned int width, unsigned int height);
|
||||
char lw_terminal_vt100_get(struct lw_terminal_vt100 *vt100, unsigned int x, unsigned int y);
|
||||
const lw_cell_t *lw_terminal_vt100_getline(struct lw_terminal_vt100 *vt100, unsigned y);
|
||||
const lw_cell_t **lw_terminal_vt100_getlines(struct lw_terminal_vt100 *vt100);
|
||||
void lw_terminal_vt100_destroy(struct lw_terminal_vt100 *this);
|
||||
void lw_terminal_vt100_read_str(struct lw_terminal_vt100 *this, const char *buffer);
|
||||
void lw_terminal_vt100_read_buf(struct lw_terminal_vt100 *this, const char *buffer, size_t n);
|
||||
|
||||
#endif
|
||||
65
hl-vt100/src/test.c
Normal file
65
hl-vt100/src/test.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Julien Palard.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
#include <string.h>
|
||||
#include <pty.h>
|
||||
#include <stdio.h>
|
||||
#include "hl_vt100.h"
|
||||
|
||||
void disp(struct vt100_headless *vt100)
|
||||
{
|
||||
unsigned int x, y;
|
||||
const lw_cell_t **lines;
|
||||
|
||||
lines = vt100_headless_getlines(vt100);
|
||||
write(1, "\n", 1);
|
||||
for (y = 0; y < vt100->term->height; ++y)
|
||||
{
|
||||
write(1, "|", 1);
|
||||
for (x = 0; x < vt100->term->width; x++) {
|
||||
write(1, &lines[y][x], 1);
|
||||
}
|
||||
write(1, "|\n", 2);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
struct vt100_headless *vt100_headless;
|
||||
|
||||
if (ac == 1)
|
||||
{
|
||||
puts("Usage: test PROGNAME");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
vt100_headless = new_vt100_headless();
|
||||
vt100_headless->changed = disp;
|
||||
vt100_headless_fork(vt100_headless, av[1], (av + 1));
|
||||
vt100_headless_main_loop(vt100_headless);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
325
hl-vt100/src/vt100_module.c
Normal file
325
hl-vt100/src/vt100_module.c
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
/* This is a Python wrapper of the vt100 headless library */
|
||||
|
||||
/*
|
||||
|
||||
occurrences of 'xx' should be changed to something reasonable for your
|
||||
module. If your module is named foo your sourcefile should be named
|
||||
foomodule.c.
|
||||
|
||||
You will probably want to delete all references to 'x_attr' and add
|
||||
your own types of attributes instead. Maybe you want to name your
|
||||
local variables other than 'self'. If your object type is needed in
|
||||
other files, you'll have to create a file "foobarobject.h"; see
|
||||
floatobject.h for an example. */
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
|
||||
#include "lw_terminal_vt100.h"
|
||||
#include "hl_vt100.h"
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
struct vt100_headless *obj;
|
||||
PyObject *changed_callback;
|
||||
} VT100Object;
|
||||
|
||||
static PyTypeObject VT100_Type;
|
||||
|
||||
#define VT100Object_Check(v) Py_IS_TYPE(v, &VT100_Type)
|
||||
|
||||
|
||||
VT100Object **allocated;
|
||||
size_t allocated_size;
|
||||
|
||||
/* VT100 methods */
|
||||
|
||||
PyDoc_STRVAR(vt100_headless_fork_doc,
|
||||
"fork(progname, argv)\n\
|
||||
\n\
|
||||
Fork a process in a new PTY handled by an headless VT100 emulator.");
|
||||
|
||||
static PyObject *
|
||||
VT100_fork(VT100Object *self, PyObject *args)
|
||||
{
|
||||
char *progname;
|
||||
PyObject *pyargv;
|
||||
const char **argv;
|
||||
int argc;
|
||||
int i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sO:fork", &progname, &pyargv))
|
||||
return NULL;
|
||||
if (!PyList_Check(pyargv))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "not a list");
|
||||
return NULL;
|
||||
}
|
||||
argc = PyList_Size(pyargv);
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
PyObject *o = PyList_GetItem(pyargv, i);
|
||||
if (!PyUnicode_Check(o))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "argv list must contain strings");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
argv = PyMem_Calloc(argc + 1, sizeof(char *));
|
||||
for (i = 0; i < argc; i++)
|
||||
argv[i] = PyUnicode_AsUTF8(PyList_GetItem(pyargv, i));
|
||||
argv[i] = NULL;
|
||||
vt100_headless_fork(self->obj, progname, (char **)argv);
|
||||
PyMem_Free(argv);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(vt100_headless_getattrlines_doc,
|
||||
"getattrlines()\n\
|
||||
\n\
|
||||
Get a list of lines (with attributes) as currently seen by the emulator.");
|
||||
|
||||
static PyObject *
|
||||
VT100_getattrlines(VT100Object *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
const lw_cell_t **lines;
|
||||
PyObject *result;
|
||||
|
||||
lines = vt100_headless_getlines(self->obj);
|
||||
result = PyList_New(0);
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
for (unsigned int i = 0; i < self->obj->term->height; i++) {
|
||||
PyObject *row = PyList_New(0);
|
||||
if (row == NULL) {
|
||||
Py_XDECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
PyList_Append(result, row);
|
||||
for (unsigned int j = 0; j < self->obj->term->width; j++) {
|
||||
PyObject *value = PyLong_FromLong(lines[i][j]);
|
||||
if (!value) {
|
||||
Py_XDECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
PyList_Append(row, value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(vt100_headless_getlines_doc,
|
||||
"getlines()\n\
|
||||
\n\
|
||||
Get a list of lines as currently seen by the emulator.");
|
||||
|
||||
static PyObject *
|
||||
VT100_getlines(VT100Object *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
const lw_cell_t **lines;
|
||||
PyObject *result;
|
||||
|
||||
lines = vt100_headless_getlines(self->obj);
|
||||
result = PyList_New(0);
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
for (unsigned int i = 0; i < self->obj->term->height; i++) {
|
||||
PyObject *row = PyUnicode_New(self->obj->term->width, 255);
|
||||
if (row == NULL) {
|
||||
Py_XDECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
for (unsigned int j = 0; j < self->obj->term->width; j++) {
|
||||
PyUnicode_WriteChar(row, j, lines[i][j] & 0xff);
|
||||
}
|
||||
PyList_Append(result, row);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(vt100_headless_main_loop_doc,
|
||||
"main_loop()\n\
|
||||
\n\
|
||||
Enter the emulator main loop.");
|
||||
|
||||
static PyObject *
|
||||
VT100_main_loop(VT100Object *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
vt100_headless_main_loop(self->obj);
|
||||
if (PyErr_Occurred())
|
||||
return NULL;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(vt100_headless_stop_doc,
|
||||
"stop()\n\
|
||||
\n\
|
||||
Stop emulator main loop.");
|
||||
|
||||
static PyObject *
|
||||
VT100_stop(VT100Object *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
vt100_headless_stop(self->obj);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
vt100_add_to_allocated(VT100Object *obj)
|
||||
{
|
||||
for (size_t i = 0; i < allocated_size; i++)
|
||||
{
|
||||
if (allocated[i] == NULL)
|
||||
{
|
||||
allocated[i] = obj;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* Out of allocated memory, realloc. */
|
||||
allocated_size *= 2;
|
||||
allocated = PyMem_Realloc(allocated, allocated_size * sizeof(VT100Object*));
|
||||
if (allocated == NULL)
|
||||
{
|
||||
PyErr_SetString(PyExc_MemoryError, "cannot allocate vt100 emulator");
|
||||
return -1;
|
||||
}
|
||||
return vt100_add_to_allocated(obj);
|
||||
}
|
||||
|
||||
static int
|
||||
vt100_del_from_allocated(VT100Object *obj)
|
||||
{
|
||||
for (size_t i = 0; i < allocated_size; i++)
|
||||
{
|
||||
if (allocated[i] == obj)
|
||||
{
|
||||
allocated[i] = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
VT100Object *
|
||||
vt100_find_in_allocated(struct vt100_headless *obj)
|
||||
{
|
||||
for (size_t i = 0; i < allocated_size; i++)
|
||||
if (allocated[i]->obj == obj)
|
||||
return allocated[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
hl_vt100_changed_cb(struct vt100_headless *this)
|
||||
{
|
||||
VT100Object *obj;
|
||||
PyObject *result;
|
||||
|
||||
obj = vt100_find_in_allocated(this);
|
||||
if (obj->changed_callback != NULL && obj->changed_callback != Py_None)
|
||||
{
|
||||
result = PyObject_CallNoArgs(obj->changed_callback);
|
||||
if (result == NULL)
|
||||
this->should_quit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
VT100_init(VT100Object *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
self->obj = new_vt100_headless();
|
||||
vt100_add_to_allocated(self);
|
||||
self->obj->changed = hl_vt100_changed_cb;
|
||||
if (self->obj == NULL)
|
||||
{
|
||||
PyErr_SetString(PyExc_MemoryError, "cannot allocate vt100 emulator");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
VT100_getwidth(VT100Object *self, void *closure)
|
||||
{
|
||||
return PyLong_FromUnsignedLong(self->obj->term->width);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
VT100_getheight(VT100Object *self, void *closure)
|
||||
{
|
||||
return PyLong_FromUnsignedLong(self->obj->term->height);
|
||||
}
|
||||
|
||||
static void
|
||||
VT100_dealloc(VT100Object *self)
|
||||
{
|
||||
vt100_del_from_allocated(self);
|
||||
Py_XDECREF(self->changed_callback);
|
||||
delete_vt100_headless(self->obj);
|
||||
Py_TYPE(self)->tp_free((PyObject *)self);
|
||||
}
|
||||
|
||||
static PyMethodDef VT100_methods[] = {
|
||||
{"fork", (PyCFunction)VT100_fork, METH_VARARGS, vt100_headless_fork_doc},
|
||||
{"getlines", (PyCFunction)VT100_getlines, METH_NOARGS, vt100_headless_getlines_doc},
|
||||
{"getattrlines", (PyCFunction)VT100_getattrlines, METH_NOARGS, vt100_headless_getattrlines_doc},
|
||||
{"main_loop", (PyCFunction)VT100_main_loop, METH_NOARGS, vt100_headless_main_loop_doc},
|
||||
{"stop", (PyCFunction)VT100_stop, METH_NOARGS, vt100_headless_stop_doc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
||||
static PyMemberDef VT100_members[] = {
|
||||
{"changed_callback", T_OBJECT_EX, offsetof(VT100Object, changed_callback), 0,
|
||||
"Changed Callback"},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyGetSetDef VT100_getsetters[] = {
|
||||
{"width", (getter) VT100_getwidth, NULL, "Terminal width", NULL},
|
||||
{"height", (getter) VT100_getheight, NULL, "Terminal height", NULL},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyTypeObject VT100_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "hl_vt100.vt100_headless",
|
||||
.tp_basicsize = sizeof(VT100Object),
|
||||
.tp_dealloc = (destructor)VT100_dealloc,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||
.tp_methods = VT100_methods,
|
||||
.tp_init = (initproc)VT100_init,
|
||||
.tp_new = PyType_GenericNew,
|
||||
.tp_members = VT100_members,
|
||||
.tp_getset = VT100_getsetters,
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(module_doc,
|
||||
"Headless VT100 Terminal Emulator.");
|
||||
|
||||
static struct PyModuleDef hl_vt100_module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
.m_name = "hl_vt100",
|
||||
.m_doc = module_doc,
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_hl_vt100(void)
|
||||
{
|
||||
PyObject *m;
|
||||
|
||||
m = PyModule_Create(&hl_vt100_module);
|
||||
allocated = PyMem_Calloc(4096, sizeof(VT100Object*));
|
||||
allocated_size = 4096;
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
if (PyModule_AddType(m, &VT100_Type) < 0) {
|
||||
Py_DECREF(m);
|
||||
return NULL;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
13
hl-vt100/vt_test.py
Normal file
13
hl-vt100/vt_test.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import hl_vt100
|
||||
|
||||
print("Starting python test...")
|
||||
vt100 = hl_vt100.vt100_headless()
|
||||
vt100.fork("/usr/bin/top", ["/usr/bin/top", "-n", "1"])
|
||||
vt100.main_loop()
|
||||
print(*vt100.getlines(), sep="\n")
|
||||
|
||||
|
||||
print()
|
||||
for a in vt100.getattrlines():
|
||||
s = " ".join(f"{c >> 8:02x}" for c in a[:20])
|
||||
print(s)
|
||||
Loading…
Reference in a new issue