Rewrite vtv-player in C. - vtv-tools - virtual terminal video tools HTML git clone git://bitreich.org/vtv-tools git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/vtv-tools DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- DIR commit 3995532c578c1894a08817a4b6c463bcb892243f DIR parent 97eae58b832d521bc35a23b2bec42e8ac957b233 HTML Author: Troels Henriksen <athas@sigkill.dk> Date: Sun, 17 Sep 2023 15:00:16 +0200 Rewrite vtv-player in C. This is much more efficient when the files are large. Diffstat: M .gitignore | 1 + M Makefile | 8 +++++--- D bin/vtv-player | 39 ------------------------------- M man/vtv-player.1 | 2 ++ A src/vtv-player.c | 79 +++++++++++++++++++++++++++++++ M src/vtv-viewer.c | 8 -------- M src/vtv.h | 28 ++++++++++++++++++++-------- 7 files changed, 107 insertions(+), 58 deletions(-) --- DIR diff --git a/.gitignore b/.gitignore @@ -1,2 +1,3 @@ bin/vtv-from-ff bin/vtv-viewer +bin/vtv-player DIR diff --git a/Makefile b/Makefile @@ -4,9 +4,10 @@ MANPREFIX ?= ${PREFIX}/share/man CFLAGS?=-O -Wall -Wextra -pedantic CC?=cc -all: bin/vtv-from-ff bin/vtv-viewer +all: bin/vtv-from-ff bin/vtv-viewer bin/vtv-player bin/%: src/%.c src/vtv.h + @mkdir -p bin $(CC) -o $@ $< $(CFLAGS) install: all @@ -18,6 +19,7 @@ install: all @echo \# Installing manpages to ${MANPREFIX}/man1/ install -D -m 644 man/* ${MANPREFIX}/man1/ -.PHONY: all install +.PHONY: all install clean -clean: rm bin/vtv-from-ff bin/vtv-viewer +clean: + rm -f bin/vtv-from-ff bin/vtv-viewer bin/vtv-player DIR diff --git a/bin/vtv-player b/bin/vtv-player @@ -1,39 +0,0 @@ -#!/bin/sh -# -# Play a single vtv file in an infinite loop. -# -# Copyright 2023 Troels Henriksen <athas@sigkill.dk> -# -# See LICENSE file for licensing information. - -trap 'clear; tput reset; exit 0' SIGINT - -fps=20 - -if [ "$1" = "-r" ]; then - fps=$2 - shift; shift; -fi - -if [ $# -ne 1 ]; then - echo "Usage: $0 FILE" >&2 - exit 1 -fi - -vtv="$1" -frametime=$(echo "scale =2; 1 / $fps" | bc) -framelines=25 - -tput civis -clear -i=0 -nframes=$(echo "$(wc -l < "${vtv}")" / "$framelines" | bc) -while true; do - tput cup 0 0 - tail -n +$(echo "(1+${i} % ${nframes} * ${framelines})" | bc) "$vtv" | head -n $framelines - i=$(($i + 1)) - - userinput="" - sleep $frametime -done - DIR diff --git a/man/vtv-player.1 b/man/vtv-player.1 @@ -22,6 +22,8 @@ Plays a VTV file in the terminal. Loops until manually terminated. .Bl -tag -width Ds .It Fl r Ar fps Show this many frames per second. Defaults to 20. +.It Fl h Ar lines +The number of lines in a frame. Defaults to 25. .El . .Sh FORMAT DIR diff --git a/src/vtv-player.c b/src/vtv-player.c @@ -0,0 +1,79 @@ +// Copyright 2023 Troels Henriksen <athas@sigkill.dk> +// +// See LICENSE file for licensing information. + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <signal.h> + +#include "vtv.h" + +void hide_cursor() { printf("\033[?25l"); } +void show_cursor() { printf("\033[?25h"); } +void move(int x, int y) { printf("\033[%d;%dH", y, x); } +void home() { printf("\033[;H"); } +void clear_screen() { printf("\033[2J"); } +void clear_line() { printf("\033[2K"); } +void def() { printf("\033[0m"); } +void reset() { printf("\033c"); } + +void sigint(int unused) { + (void)unused; + reset(); + exit(0); +} + +int main(int argc, char* argv[]) { + int fps = 20; + int frame_lines = 25; + const char *vtv_file; + struct vtv* vtv; + + while (1) { + switch (getopt(argc, argv, "r:h:")) { + case 'r': + fps = atoi(optarg); + break; + case 'h': + frame_lines = atoi(optarg); + break; + case -1: + if (optind == argc-1) { + vtv_file = argv[optind]; + goto done; + } + // fallthrough + default: + fprintf(stderr, "Usage: %s [-r INT] FILE\n", argv[0]); + exit(1); + } + } + done: + vtv = vtv_read_from_file(vtv_file); + if (vtv == NULL) { + fprintf(stderr, "%s: cannot read %s: %s\n", + argv[0], vtv_file, strerror(errno)); + exit(1); + } + + int num_frames = vtv->num_lines / frame_lines; + + hide_cursor(); + clear_screen(); + + int frame = 0; + signal(SIGINT, sigint); + + while (1) { + useconds_t nap = 1000000.0 / fps; + frame = (frame+1) % num_frames; + home(); + vtv_show_frame(vtv, stdout, frame, frame_lines, + "\033[0m MISSING LINE"); + usleep(nap); + } + def(); +} DIR diff --git a/src/vtv-viewer.c b/src/vtv-viewer.c @@ -41,14 +41,6 @@ void clear_screen() { printf("\033[2J"); } void clear_line() { printf("\033[2K"); } void def() { printf("\033[0m"); } -void fg_rgb(uint8_t r, uint8_t g, uint8_t b) { - printf("\033[38;2;%d;%d;%dm", r, g, b); -} - -void bg_rgb(uint8_t r, uint8_t g, uint8_t b) { - printf("\033[48;2;%d;%d;%dm", r, g, b); -} - struct { int frame; int lines_per_frame; DIR diff --git a/src/vtv.h b/src/vtv.h @@ -15,7 +15,7 @@ struct vtv { char** lines; }; -static void vtv_free(struct vtv* vtv) { +void vtv_free(struct vtv* vtv) { for (int i = 0; i < vtv->num_lines; i++) { free(vtv->lines[i]); } @@ -24,7 +24,7 @@ static void vtv_free(struct vtv* vtv) { } // Returns nonzero on error. -static int vtv_read_lines(FILE* f, char*** lines_out, int *num_lines_out) { +int vtv_read_lines(FILE* f, char*** lines_out, int *num_lines_out) { size_t n, num_lines = 0, capacity = 10; char** lines = calloc(capacity, sizeof(char*)); ssize_t len; @@ -47,11 +47,11 @@ static int vtv_read_lines(FILE* f, char*** lines_out, int *num_lines_out) { // Show the given frame on the provided file. If there are not enough // lines, show the provided line instead of the missing ones. -static void vtv_show_frame(struct vtv* vtv, - FILE* f, - int frame, - int lines_per_frame, - const char *missing_line) { +void vtv_show_frame(struct vtv* vtv, + FILE* f, + int frame, + int lines_per_frame, + const char *missing_line) { for (int i = 0; i < lines_per_frame; i++) { int j = frame*lines_per_frame + i; if (j < vtv->num_lines) { @@ -65,7 +65,7 @@ static void vtv_show_frame(struct vtv* vtv, } // Returns NULL on error. -static struct vtv* vtv_read(FILE *f) { +struct vtv* vtv_read(FILE *f) { struct vtv* vtv = malloc(sizeof(struct vtv)); if (vtv_read_lines(f, &vtv->lines, &vtv->num_lines) == 0) { return vtv; @@ -74,3 +74,15 @@ static struct vtv* vtv_read(FILE *f) { return NULL; } } + +// Returns NULL on error. +struct vtv* vtv_read_from_file(const char *fname) { + FILE *f = fopen(fname, "r"); + if (f == NULL) { + return NULL; + } else { + struct vtv* vtv = vtv_read(f); + fclose(f); + return vtv; + } +}