Use asciinema/vt instead of libtsm based program

ex-upload
Marcin Kulik 7 years ago
parent 9efbff6046
commit 28519cb455

3
.gitmodules vendored

@ -1,3 +1,6 @@
[submodule "a2png"]
path = a2png
url = https://github.com/asciinema/a2png
[submodule "vt"]
path = vt
url = https://github.com/asciinema/vt.git

@ -13,19 +13,16 @@ RUN apt-get update && \
wget --quiet -O - https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc | apt-key add - && \
apt-get update && \
apt-get install -y \
autoconf \
build-essential \
elixir \
esl-erlang \
git-core \
libfontconfig1 \
libpq-dev \
libtool \
libxml2-dev \
libxslt1-dev \
nginx \
nodejs \
pkg-config \
ruby2.1 \
ruby2.1-dev \
supervisor \
@ -33,7 +30,6 @@ RUN apt-get update && \
tzdata
# Packages required for:
# autoconf, libtool and pkg-config for libtsm
# libfontconfig1 for PhantomJS
# ttf-bitstream-vera for a2png
@ -56,18 +52,6 @@ RUN wget --quiet -O /opt/phantomjs.tar.bz2 https://bitbucket.org/ariya/phantomjs
rm /opt/phantomjs.tar.bz2 && \
ln -sf /opt/phantomjs-$PHANTOMJS_VERSION-linux-x86_64/bin/phantomjs /usr/local/bin
# install libtsm
RUN git clone https://github.com/asciinema/libtsm.git /tmp/libtsm && \
cd /tmp/libtsm && \
git checkout asciinema && \
test -f ./configure || NOCONFIGURE=1 ./autogen.sh && \
./configure --prefix=/usr/local && \
make && \
make install && \
ldconfig && \
rm -rf /tmp/libtsm
# install JDK
RUN wget --quiet -O /opt/jdk-8u131-linux-x64.tar.gz --no-check-certificate --no-cookies --header 'Cookie: oraclelicense=accept-securebackup-cookie' http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u131-linux-x64.tar.gz && \
@ -106,18 +90,19 @@ RUN cd a2png && npm install
COPY a2png /app/a2png
RUN cd a2png && lein cljsbuild once main && lein cljsbuild once page
# build vt
COPY vt/project.clj /app/vt/
RUN cd vt && lein deps
COPY vt /app/vt
RUN cd vt && lein cljsbuild once main
# service URLs
ENV DATABASE_URL "postgresql://postgres@postgres/postgres"
ENV REDIS_URL "redis://redis:6379"
# compile terminal.c
RUN mkdir -p /app/bin
COPY src/Makefile /app/src/
COPY src/terminal.c /app/src/
RUN cd src && make
# add Ruby source files
COPY config/*.rb /app/config/
@ -134,6 +119,7 @@ COPY vendor /app/vendor
COPY config.ru /app/
COPY Rakefile /app/
COPY app /app/app
COPY resources/welcome.json /app/resources/welcome.json
# compile assets with assets pipeline

@ -2,29 +2,27 @@ require 'open3'
class Terminal
BINARY_PATH = (Rails.root + "bin" + "terminal").to_s
SCRIPT_PATH = (Rails.root + "vt" + "main.js").to_s
def initialize(width, height)
@process = Process.new("#{BINARY_PATH} #{width} #{height}")
@process = Process.new("node #{SCRIPT_PATH}")
send_cmd("new", { width: width, height: height })
end
def feed(data)
process.write("d\n#{data.bytesize}\n")
process.write(data)
send_cmd("feed-str", { str: data })
end
def snapshot
process.write("p\n")
lines = Yajl::Parser.new.parse(process.read_line)
def screen
send_cmd("dump-screen")
screen = read_result
lines = screen.fetch("lines")
cursor = screen.fetch("cursor")
Snapshot.build(lines)
end
def cursor
process.write("c\n")
c = Yajl::Parser.new.parse(process.read_line)
Cursor.new(c['x'], c['y'], c['visible'])
{
snapshot: Snapshot.build(lines),
cursor: Cursor.new(cursor['x'], cursor['y'], cursor['visible'])
}
end
def release
@ -33,6 +31,15 @@ class Terminal
private
def send_cmd(cmd, data = {})
json = data.merge({ cmd: cmd }).to_json
process.write("#{json}\n")
end
def read_result()
Yajl::Parser.new.parse(process.read_line).fetch("result")
end
attr_reader :process
class Process

@ -10,13 +10,14 @@ class Film
terminal.feed(data)
end
terminal.snapshot
terminal.screen[:snapshot]
end
def frames
frames = stdout.lazy.map do |delay, data|
terminal.feed(data)
[delay, Frame.new(terminal.snapshot, terminal.cursor)]
screen = terminal.screen
[delay, Frame.new(screen[:snapshot], screen[:cursor])]
end
FrameDiffList.new(frames)

@ -8,12 +8,7 @@ class FakeTerminal
@data << data
end
def snapshot
@data
def screen
{ snapshot: @data, cursor: @data.size }
end
def cursor
@data.size
end
end

2
src/.gitignore vendored

@ -1,2 +0,0 @@
terminal
*.o

@ -1,8 +0,0 @@
all: ../bin/terminal
../bin/terminal: terminal
mkdir -p ../bin
cp terminal ../bin/terminal
terminal: terminal.c
gcc -O3 -o terminal terminal.c -ltsm

@ -1,310 +0,0 @@
#include <stdio.h>
#include <libtsm.h>
#include <string.h>
#define READ_BUF_SIZE 16 * 1024
static int RGB_LEVELS[6] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff };
struct tsm_screen_attr last_attr;
struct tsm_screen_attr *last_attr_ptr = &last_attr;
static int u8_wc_to_utf8(char *dest, uint32_t ch) {
if (ch < 0x80) {
dest[0] = (char)ch;
return 1;
}
if (ch < 0x800) {
dest[0] = (ch >> 6) | 0xC0;
dest[1] = (ch & 0x3F) | 0x80;
return 2;
}
if (ch < 0x10000) {
dest[0] = (ch >> 12) | 0xE0;
dest[1] = ((ch >> 6) & 0x3F) | 0x80;
dest[2] = (ch & 0x3F) | 0x80;
return 3;
}
if (ch < 0x110000) {
dest[0] = (ch >> 18) | 0xF0;
dest[1] = ((ch >> 12) & 0x3F) | 0x80;
dest[2] = ((ch >> 6) & 0x3F) | 0x80;
dest[3] = (ch & 0x3F) | 0x80;
return 4;
}
return 0;
}
void check_err(int err, const char *message) {
if (err) {
printf("%s\n", message);
exit(1);
}
}
void write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data) {}
int prepare_cb(struct tsm_screen *con, void *data) {
int *line = (int *)data;
*line = -1;
printf("[");
return 0;
}
void print_char(const uint32_t *ch) {
char str[5];
int size;
if (*ch == 0) {
printf(" ");
} else if (*ch == '"') {
printf("\\\"");
} else if (*ch == '\\') {
printf("\\\\");
} else {
size = u8_wc_to_utf8(str, *ch);
if (size > 0) {
str[size] = 0;
printf("%s", str);
} else {
printf("?");
}
}
}
int get_rgb_index(int value) {
int i;
for (i=0; i<6; i++) {
if (RGB_LEVELS[i] == value) return i;
}
return -1;
}
int get_rgb_color(int r, int g, int b) {
if (r == g && g == b && (r - 8) % 10 == 0) {
return 232 + (r - 8) / 10;
} else {
return 16 + get_rgb_index(r) * 36 + get_rgb_index(g) * 6 + get_rgb_index(b);
}
}
int get_fg(const struct tsm_screen_attr *attr) {
if (attr->fccode == -1) {
return get_rgb_color(attr->fr, attr->fg, attr->fb);
} else if (attr->fccode >= 0 && attr->fccode < 16) {
return attr->fccode;
} else {
return -1;
}
}
int get_bg(const struct tsm_screen_attr *attr) {
if (attr->bccode == -1) {
return get_rgb_color(attr->br, attr->bg, attr->bb);
} else if (attr->bccode >= 0 && attr->bccode < 16) {
return attr->bccode;
} else {
return -1;
}
}
void print_attr(const struct tsm_screen_attr *attr) {
bool comma_needed = false;
int color_code;
printf("{");
color_code = get_fg(attr);
if (color_code != -1) {
printf("\"fg\":%d", color_code);
comma_needed = true;
}
color_code = get_bg(attr);
if (color_code != -1) {
if (comma_needed) printf(",");
printf("\"bg\":%d", color_code);
comma_needed = true;
}
if (attr->bold) {
if (comma_needed) printf(",");
printf("\"bold\":true");
comma_needed = true;
}
if (attr->underline) {
if (comma_needed) printf(",");
printf("\"underline\":true");
comma_needed = true;
}
if (attr->inverse) {
if (comma_needed) printf(",");
printf("\"inverse\":true");
comma_needed = true;
}
if (attr->blink) {
if (comma_needed) printf(",");
printf("\"blink\":true");
comma_needed = true;
}
printf("}");
}
bool attr_eq(struct tsm_screen_attr *attr1, const struct tsm_screen_attr *attr2) {
return attr1->fccode == attr2->fccode &&
attr1->bccode == attr2->bccode &&
attr1->fr == attr2->fr &&
attr1->fg == attr2->fg &&
attr1->fb == attr2->fb &&
attr1->br == attr2->br &&
attr1->bg == attr2->bg &&
attr1->bb == attr2->bb &&
attr1->bold == attr2->bold &&
attr1->underline == attr2->underline &&
attr1->inverse == attr2->inverse &&
attr1->blink == attr2->blink;
}
void attr_cp(const struct tsm_screen_attr *src, struct tsm_screen_attr *dst) {
memcpy((void *)dst, (const void *)src, sizeof(last_attr));
}
void close_cell() {
printf("\",");
print_attr(last_attr_ptr);
printf("]");
}
void open_cell(const struct tsm_screen_attr *attr) {
printf("[\"");
attr_cp(attr, last_attr_ptr);
}
int draw_cb(struct tsm_screen *con, uint32_t id, const uint32_t *ch, size_t len,
unsigned int width, unsigned int posx, unsigned int posy,
const struct tsm_screen_attr *attr, tsm_age_t age, void *data) {
int *line = (int *)data;
if (((signed int)posy) > *line) {
if (*line >= 0) {
close_cell();
printf("],"); // close line
}
printf("["); // open line
open_cell(attr);
}
*line = posy;
if (width == 0) return 0;
if (!(attr_eq(last_attr_ptr, attr))) {
close_cell();
printf(",");
open_cell(attr);
}
print_char(ch);
return 0;
}
int render_cb(struct tsm_screen *con, void *data) {
int *line = (int *)data;
if (*line >= 0) {
close_cell();
printf("]"); // close line
}
printf("]\n");
return 0;
}
int main(int argc, char *argv[]) {
int err;
int i;
struct tsm_screen *screen;
struct tsm_vte *vte;
int width, height;
char *line = NULL;
size_t size;
unsigned int n;
char *buffer = (char *) malloc(READ_BUF_SIZE);
int line_n, cursor_x, cursor_y;
char *cursor_visible;
unsigned int flags;
int m, read;
width = atoi(argv[1]);
height = atoi(argv[2]);
err = tsm_screen_new(&screen, NULL, NULL);
check_err(err, "can't create screen");
err = tsm_screen_resize(screen, width, height);
check_err(err, "can't resize screen");
err = tsm_vte_new(&vte, screen, write_cb, NULL, NULL, NULL);
check_err(err, "can't create vte");
for (;;) {
if (getline(&line, &size, stdin) == -1) break;
char action = line[0];
switch(action) {
case 'd':
if (getline(&line, &size, stdin) == -1) break;
n = atoi(line);
while (n > 0) {
if (n > READ_BUF_SIZE) {
m = READ_BUF_SIZE;
} else {
m = n;
}
read = fread(buffer, 1, m, stdin);
tsm_vte_input(vte, buffer, read);
n = n - read;
}
break;
case 'p':
//tsm_screen_draw(screen, prepare_cb, draw_cb, render_cb, &line_n);
prepare_cb(screen, &line_n);
tsm_screen_draw(screen, draw_cb, &line_n);
render_cb(screen, &line_n);
break;
case 'c':
cursor_x = tsm_screen_get_cursor_x(screen);
cursor_y = tsm_screen_get_cursor_y(screen);
flags = tsm_screen_get_flags(screen);
if (!(flags & TSM_SCREEN_HIDE_CURSOR)) {
cursor_visible = "true";
} else {
cursor_visible = "false";
}
printf("{\"x\":%d,\"y\":%d,\"visible\":%s}\n", cursor_x, cursor_y, cursor_visible);
break;
}
fflush(stdout);
}
return 0;
}

1
vt

@ -0,0 +1 @@
Subproject commit 5fde1d8cb8306d09034709ae5901d7f7b43630cc
Loading…
Cancel
Save