Migrate from switch to native Python module.
This commit is contained in:
parent
102ecca8ec
commit
42e7bdd22b
|
@ -1,4 +1,6 @@
|
||||||
*.o
|
*.o
|
||||||
*.so
|
*.so
|
||||||
parser
|
parser
|
||||||
|
*.egg-info/
|
||||||
|
.venv/
|
||||||
|
.envrc
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
include src/*.h
|
33
Makefile
33
Makefile
|
@ -2,30 +2,6 @@
|
||||||
## Makefile for vt100
|
## Makefile for vt100
|
||||||
##
|
##
|
||||||
## Made by julien palard
|
## Made by julien palard
|
||||||
## Login <vt100@mandark.fr>
|
|
||||||
##
|
|
||||||
## 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.
|
|
||||||
##
|
##
|
||||||
|
|
||||||
NAME = vt100
|
NAME = vt100
|
||||||
|
@ -54,20 +30,13 @@ $(NAME): $(OBJ)
|
||||||
test: $(OBJ_TEST)
|
test: $(OBJ_TEST)
|
||||||
$(CC) $(OBJ_TEST) -L . -l$(NAME) -o test
|
$(CC) $(OBJ_TEST) -L . -l$(NAME) -o test
|
||||||
|
|
||||||
python_module:
|
|
||||||
swig -python -threads *.i
|
|
||||||
|
|
||||||
all:
|
all:
|
||||||
@make $(NAME)
|
@make $(NAME)
|
||||||
|
|
||||||
.c.o:
|
.c.o:
|
||||||
$(CC) -D $(DEFINE) -c $(CFLAGS) $< -o $(<:.c=.o)
|
$(CC) -D $(DEFINE) -c $(CFLAGS) $< -o $(<:.c=.o)
|
||||||
|
|
||||||
clean_python_module:
|
clean:
|
||||||
$(RM) *.pyc *.so hl_vt100_wrap.c hl_vt100.py
|
|
||||||
$(RM) -r build
|
|
||||||
|
|
||||||
clean: clean_python_module
|
|
||||||
$(RM) $(LINKERNAME) test src/*~ *~ src/\#*\# src/*.o \#*\# *.o *core
|
$(RM) $(LINKERNAME) test src/*~ *~ src/\#*\# src/*.o \#*\# *.o *core
|
||||||
|
|
||||||
re: clean all
|
re: clean all
|
||||||
|
|
125
README.md
125
README.md
|
@ -10,63 +10,91 @@ want with it, like interfacing over TCP, HTTP, automatically testing
|
||||||
your implementation `malloc` against `top` while running `top` in the
|
your implementation `malloc` against `top` while running `top` in the
|
||||||
headless terminal, whatever pleases you.
|
headless terminal, whatever pleases you.
|
||||||
|
|
||||||
For copyright information, please see the file COPYRIGHT in this
|
For copyright information, please see the file LICENSE in this
|
||||||
directory or in the files of the source tree.
|
directory or in the files of the source tree.
|
||||||
|
|
||||||
|
|
||||||
# INSTALL
|
# INSTALL
|
||||||
|
|
||||||
## Python module
|
## Python module
|
||||||
|
|
||||||
You'll need `swig`, so `apt-get install swig` or whatever works for you.
|
pip install hl-vt100
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
$ make python_module && su -c 'python setup.py install'
|
## Python module from source
|
||||||
|
|
||||||
# Usage using the python wrapper (same methods in C)
|
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()
|
||||||
```
|
```
|
||||||
>>> import hl_vt100
|
|
||||||
>>> import threading
|
|
||||||
>>> class Top:
|
|
||||||
... def __init__(self):
|
|
||||||
... self.vt100 = hl_vt100.vt100_headless()
|
|
||||||
... self.vt100.fork('top', ['top'])
|
|
||||||
...
|
|
||||||
... def __call__(self):
|
|
||||||
... self.vt100.main_loop()
|
|
||||||
...
|
|
||||||
... def __str__(self):
|
|
||||||
... return "\n".join(self.vt100.getlines())
|
|
||||||
...
|
|
||||||
>>> top = Top()
|
|
||||||
>>> thread = threading.Thread(target=top)
|
|
||||||
>>> thread.start()
|
|
||||||
>>> print(top)
|
|
||||||
top - 09:42:43 up 332 days, 23:42, 14 users, load average: 3.08, 3.41, 3.52
|
|
||||||
KiB Mem: 16443168 total, 15865172 used, 577996 free, 1141216 buffers
|
|
||||||
KiB Swap: 999420 total, 669960 used, 329460 free. 6097952 cached Mem
|
|
||||||
|
|
||||||
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
# Usage using the C library
|
||||||
24237 nobody 20 0 1041352 348040 39116 R 78.8 2.1 142:18.54 redacted
|
|
||||||
24078 nobody 20 0 1152024 382592 38948 R 25.8 2.3 355:44.85 for
|
```c
|
||||||
24892 nobody 20 0 1182408 434560 40276 S 21.1 2.6 394:06.48 privacy
|
#include <stdio.h>
|
||||||
24472 nobody 20 0 1153196 380712 39000 S 20.3 2.3 410:51.64 ayah
|
#include <stdlib.h>
|
||||||
23617 nobody 20 0 170048 100072 6376 S 19.5 0.6 8:36.66 python
|
#include "hl_vt100.h"
|
||||||
23646 nobody 20 0 1095252 367128 39916 R 17.9 2.2 353:48.65 you
|
|
||||||
23505 nobody 20 0 1159452 377948 38920 S 17.2 2.3 364:29.40 wont
|
|
||||||
24757 nobody 20 0 943848 216084 40028 R 15.6 1.3 231:10.38 know
|
void changed(struct vt100_headless *vt100)
|
||||||
23894 nobody 20 0 885740 166600 38724 S 13.3 1.0 195:48.99 what
|
{
|
||||||
24689 nobody 20 0 901152 183240 39780 S 13.3 1.1 192:50.13 eats
|
const char **lines;
|
||||||
24820 nobody 20 0 937712 232392 39496 S 13.3 1.4 215:25.32 my_cpu
|
|
||||||
3117 nobody 20 0 402568 351740 5912 S 3.1 2.1 0:03.72 python
|
lines = vt100_headless_getlines(vt100);
|
||||||
3122 mandark 20 0 27052 3040 1184 R 3.1 0.0 0:00.34 top
|
for (unsigned int y = 0; y < vt100->term->height; ++y)
|
||||||
23796 nobody 20 0 1069940 347252 39156 S 3.1 2.1 190:37.78 probably
|
{
|
||||||
23503 nobody 20 0 903592 268248 38360 S 1.6 1.6 75:54.32 somethign
|
write(1, "|", 1);
|
||||||
5169 root 20 0 0 0 0 S 0.8 0.0 45:05.66 kworker/0:1
|
write(1, lines[y], vt100->term->width);
|
||||||
15324 root 20 0 63848 9768 1628 S 0.8 0.1 437:02.99 supervisord
|
write(1, "|\n", 2);
|
||||||
16382 root 20 0 0 0 0 S 0.8 0.0 11:29.88 kworker/2:0
|
}
|
||||||
18278 root 20 0 0 0 0 S 0.8 0.0 184:02.23 kworker/1:0
|
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
|
# Code overview
|
||||||
|
@ -126,7 +154,7 @@ lw_terminal_parser, lw_terminal_vt100, and hl_vt100 are three modules used to em
|
||||||
`lw_terminal_parser` parses terminal escape sequences, calling callbacks
|
`lw_terminal_parser` parses terminal escape sequences, calling callbacks
|
||||||
when a sequence is sucessfully parsed, read `example/parse.c`.
|
when a sequence is sucessfully parsed, read `example/parse.c`.
|
||||||
|
|
||||||
Provides :
|
Provides:
|
||||||
|
|
||||||
* `struct lw_terminal *lw_terminal_parser_init(void);`
|
* `struct lw_terminal *lw_terminal_parser_init(void);`
|
||||||
* `void lw_terminal_parser_destroy(struct lw_terminal* this);`
|
* `void lw_terminal_parser_destroy(struct lw_terminal* this);`
|
||||||
|
@ -140,7 +168,7 @@ Provides :
|
||||||
Hooks into a `lw_terminal_parser` and keep an in-memory state of the
|
Hooks into a `lw_terminal_parser` and keep an in-memory state of the
|
||||||
screen of a vt100.
|
screen of a vt100.
|
||||||
|
|
||||||
Provides :
|
Provides:
|
||||||
|
|
||||||
* `struct lw_terminal_vt100 *lw_terminal_vt100_init(void *user_data, void (*unimplemented)(struct lw_terminal* term_emul, char *seq, char chr));`
|
* `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);`
|
* `char lw_terminal_vt100_get(struct lw_terminal_vt100 *vt100, unsigned int x, unsigned int y);`
|
||||||
|
@ -154,7 +182,8 @@ Provides :
|
||||||
Forks a program, plug its io to a pseudo terminal and emulate a vt100
|
Forks a program, plug its io to a pseudo terminal and emulate a vt100
|
||||||
using `lw_terminal_vt100`.
|
using `lw_terminal_vt100`.
|
||||||
|
|
||||||
Provides :
|
Provides:
|
||||||
|
|
||||||
* `void vt100_headless_fork(struct vt100_headless *this, const char *progname, char *const argv[]);`
|
* `void vt100_headless_fork(struct vt100_headless *this, const char *progname, char *const argv[]);`
|
||||||
* `struct vt100_headless *vt100_headless_init(void);`
|
* `struct vt100_headless *vt100_headless_init(void);`
|
||||||
* `const char **vt100_headless_getlines(struct vt100_headless *this);`
|
* `const char **vt100_headless_getlines(struct vt100_headless *this);`
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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()
|
85
hl_vt100.i
85
hl_vt100.i
|
@ -1,85 +0,0 @@
|
||||||
%module hl_vt100
|
|
||||||
/*
|
|
||||||
* 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 "src/lw_terminal_vt100.h"
|
|
||||||
#include "src/hl_vt100.h"
|
|
||||||
%}
|
|
||||||
|
|
||||||
%typemap(in) char ** {
|
|
||||||
/* Check if is a list */
|
|
||||||
if (PyList_Check($input)) {
|
|
||||||
int size = PyList_Size($input);
|
|
||||||
int i = 0;
|
|
||||||
$1 = (char **) malloc((size+1)*sizeof(char *));
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
PyObject *o = PyList_GetItem($input,i);
|
|
||||||
if (PyString_Check(o))
|
|
||||||
$1[i] = PyString_AsString(PyList_GetItem($input,i));
|
|
||||||
else {
|
|
||||||
PyErr_SetString(PyExc_TypeError,"list must contain strings");
|
|
||||||
free($1);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$1[i] = 0;
|
|
||||||
} else {
|
|
||||||
PyErr_SetString(PyExc_TypeError,"not a list");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
%typemap(out) char ** {
|
|
||||||
/* Check if is a list */
|
|
||||||
int i;
|
|
||||||
|
|
||||||
$result = PyList_New(0);
|
|
||||||
for (i = 0; i < arg1->term->height; i++)
|
|
||||||
PyList_Append($result, PyString_FromStringAndSize($1[i], arg1->term->width));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// This cleans up the char ** array we malloc'd before the function call
|
|
||||||
%typemap(freearg) char ** {
|
|
||||||
free((char *) $1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct vt100_headless
|
|
||||||
{
|
|
||||||
void (*changed)(struct vt100_headless *this);
|
|
||||||
%immutable;
|
|
||||||
int master;
|
|
||||||
struct termios backup;
|
|
||||||
struct lw_terminal_vt100 *term;
|
|
||||||
%extend {
|
|
||||||
vt100_headless();
|
|
||||||
~vt100_headless();
|
|
||||||
void fork(const char *progname, char **argv);
|
|
||||||
char **getlines();
|
|
||||||
int main_loop();
|
|
||||||
void stop();
|
|
||||||
}
|
|
||||||
};
|
|
20
run_tests.sh
20
run_tests.sh
|
@ -25,27 +25,15 @@
|
||||||
|
|
||||||
if [ "$1" = python ]
|
if [ "$1" = python ]
|
||||||
then
|
then
|
||||||
if ! which swig > /dev/null
|
python -m pip install .
|
||||||
then
|
|
||||||
echo "You should install swig !"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
make python_module
|
|
||||||
if [ $? != 0 ]
|
|
||||||
then
|
|
||||||
echo "Failed to build python module"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
cat <<EOF | python
|
cat <<EOF | python
|
||||||
import hl_vt100
|
import hl_vt100
|
||||||
import time
|
|
||||||
import sys
|
|
||||||
|
|
||||||
print "Starting python test..."
|
print("Starting python test...")
|
||||||
vt100 = hl_vt100.vt100_headless()
|
vt100 = hl_vt100.vt100_headless()
|
||||||
vt100.fork('/usr/bin/top', ['/usr/bin/top', '-n', '1'])
|
vt100.fork('/usr/bin/top', ['/usr/bin/top', '-n', '1'])
|
||||||
vt100.main_loop()
|
vt100.main_loop()
|
||||||
[sys.stdout.write(line + "\n") for line in vt100.getlines()]
|
print(*vt100.getlines(), sep="\n")
|
||||||
EOF
|
EOF
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
@ -55,7 +43,7 @@ make clean
|
||||||
if [ "$1" = c ]
|
if [ "$1" = c ]
|
||||||
then
|
then
|
||||||
make && make test
|
make && make test
|
||||||
LD_LIBRARY_PATH=. ./test /usr/bin/top
|
LD_LIBRARY_PATH=. ./test /usr/bin/top -n 1
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -7,8 +7,10 @@ setup.py file for hl_vt100
|
||||||
from distutils.core import setup, Extension
|
from distutils.core import setup, Extension
|
||||||
|
|
||||||
|
|
||||||
hl_vt100_module = Extension('_hl_vt100',
|
hl_vt100_module = Extension('hl_vt100',
|
||||||
sources=['hl_vt100_wrap.c',
|
include_dirs=['src'],
|
||||||
|
define_macros=[('NDEBUG', '1')],
|
||||||
|
sources=['src/vt100_module.c',
|
||||||
'src/hl_vt100.c',
|
'src/hl_vt100.c',
|
||||||
'src/lw_terminal_parser.c',
|
'src/lw_terminal_parser.c',
|
||||||
'src/lw_terminal_vt100.c'])
|
'src/lw_terminal_vt100.c'])
|
||||||
|
@ -20,6 +22,7 @@ setup(name='hl_vt100',
|
||||||
author_email='julien@palard.fr',
|
author_email='julien@palard.fr',
|
||||||
description="""Headless vt100 emulator""",
|
description="""Headless vt100 emulator""",
|
||||||
ext_modules=[hl_vt100_module],
|
ext_modules=[hl_vt100_module],
|
||||||
|
include_package_data=True,
|
||||||
py_modules=["hl_vt100"],
|
py_modules=["hl_vt100"],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Programming Language :: C",
|
"Programming Language :: C",
|
||||||
|
|
|
@ -0,0 +1,281 @@
|
||||||
|
/* 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_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 char **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++)
|
||||||
|
PyList_Append(result, PyUnicode_FromStringAndSize(lines[i], self->obj->term->width));
|
||||||
|
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},
|
||||||
|
{"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;
|
||||||
|
}
|
Loading…
Reference in New Issue