diff options
54 files changed, 1971 insertions, 0 deletions
| diff --git a/examples b/examples new file mode 120000 index 0000000..28de457 --- /dev/null +++ b/examples @@ -0,0 +1 @@ +share/hackvr/examples
\ No newline at end of file diff --git a/share/hackvr/examples/anonet_map.sh b/share/hackvr/examples/anonet_map.sh new file mode 100755 index 0000000..a16a196 --- /dev/null +++ b/share/hackvr/examples/anonet_map.sh @@ -0,0 +1,12 @@ +#!/bin/bash +cat <(printf "%s move 0 0 -270\n" "$USER") \ +    <(wget http://hacking.allowed.org/cgi-bin/map_hackvr.cgi -qO- 2>/dev/null) \ +    /dev/stdin \ + | slowcat 0 \ + | hackvr "$USER" | tee -a /dev/stderr \ + | grep --line-buffered action \ + | stdbuf -oL cut '-d ' -f3 \ + | xargs -r -L1 printf "whois://hacking.allowed.org/AS%s\n" \ + | tee /dev/stderr \ + | xargs -r -L1 urlstart + diff --git a/share/hackvr/examples/chess/board.sh b/share/hackvr/examples/chess/board.sh new file mode 100755 index 0000000..f574065 --- /dev/null +++ b/share/hackvr/examples/chess/board.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +width=8 +height=8 +cols="a b c d e f g h" + +for x in $(seq 1 $width);do + for y in $(seq 1 $height);do +  color=$[((($y % 2) + ($x % 2))%2)+16] +### board is on the wall. draw in x and y. +#  printf "%s%s addshape %s 4 %s %s 0 %s %s 0 %s %s 0 %s %s 0\n" $(printf "%s\n" "$cols" | cut '-d ' -f$x) $y $color $x $y $[x+1] $y $[x+1] $[y+1] $x $[y+1] +### board is on floor. draw in x and z. +  printf "%s%s addshape %s 4 %s 0 %s %s 0 %s %s 0 %s %s 0 %s\n" $(printf "%s\n" "$cols" | cut '-d ' -f$x) $y $color $x $y $[x+1] $y $[x+1] $[y+1] $x $[y+1] + done +done + +row8="rook knight bishop queen king bishop knight rook" +row1="$row8" + +color="19 19 0 0 0 0 20 20" + +#draw pawns. triangles. +for x in $(seq $width);do + for y in 2 7;do +  locat="$(printf "%s\n" "$cols" | cut '-d ' "-f$x")$y" +  cat "./pieces/pawn" | sed 's/^[^ ][^ ]* addshape [0-9][0-9]* /piece_'"${locat}"' addshape '"$(printf "%s\n" "$color" | cut '-d ' "-f$y")"' /g' +### board is on wall. use x and y. +#  printf "piece_%s move %s %s 0\n" "$locat" "$x" "$y" +### board is on floor. use x and z. +  printf "piece_%s move %s 0 %s\n" "$locat" "$x" "$y" + done + for y in 1 8;do +  locat="$(printf "%s\n" "$cols" | cut '-d ' "-f$x")$y" +  cat "./pieces/$(printf "%s\n" "$row1" | cut '-d ' "-f$x")" | sed 's/^[^ ][^ ]* addshape [0-9][0-9]* /piece_'"${locat}"' addshape '"$(printf "%s\n" "$color" | cut '-d ' "-f$y")"' /g' +### board is on wall. use x and y. +#  printf "piece_%s move %s %s 0\n" "$locat" "$x" "$y" +### board is on floor. use x and z. +  printf "piece_%s move %s 0 %s\n" "$locat" "$x" "$y" + done +done diff --git a/share/hackvr/examples/chess/camera.pos b/share/hackvr/examples/chess/camera.pos new file mode 100644 index 0000000..32433c2 --- /dev/null +++ b/share/hackvr/examples/chess/camera.pos @@ -0,0 +1,2 @@ +epoch move 5 5 -6 +derp set global.zoom 60 diff --git a/share/hackvr/examples/chess/gnuchess-to-hackvr.sh b/share/hackvr/examples/chess/gnuchess-to-hackvr.sh new file mode 100755 index 0000000..0b94a87 --- /dev/null +++ b/share/hackvr/examples/chess/gnuchess-to-hackvr.sh @@ -0,0 +1,31 @@ +#!/bin/sh +tee /dev/stderr | while read first second third;do +  if [ "_$first" = "_feature" ];then +    printf "# just gnuchess saying what features it has: %s %s %s\n" "$first" "$second" "$third" >&2 +  elif printf "%s\n" "$first" | grep '^[0-9][0-9]*\.$' >/dev/null 2>&1;then +    #we made a successful move. +    ### how to tell hackvr to move the piece now? $second should contain what the move was. +    printf "# Hey! Hackvr! do this move: %s\n" "$second" >&2 +    if [ "_$second" = "_..." ];then #computer/other person made a move. +      move=$third +    else +      move=$second +    fi +      from=$(printf "%s\n" "$move" | fold -w1 | head -n2 | tr -d '\n') +      to=$(printf "%s\n" "$move" | fold -w1 | tail -n+3 | head -n2 | tr -d '\n') +      x=$(echo "a b c d e f g h" | tr ' ' '\n' | grep -n "$(printf "%s\n" "$to" | fold -w1 | head -n1)" | cut -d: -f1) +      y=$(printf "%s\n" "$to" | fold -w1 | tail -n1) +      magic=$(printf "%s\n" "$move" | fold -w1 | tail -n+5 | tr -d '\n') +### if the board is on the wall we need to use x and y +#    printf "piece_%s move %s %s 0\n" "$from" "$x" "$y" | tee /dev/stderr +### the board is on the floor atm. use x and z +    printf "piece_%s move %s 0 %s\n" "$from" "$x" "$y" | tee /dev/stderr + +    printf "#delete the old piece_%s group\n" +    printf "epoch deletegroup piece_%s\n" "$to" +    printf "epoch renamegroup piece_%s piece_%s\n" "$from" "$to" | tee /dev/stderr +  else +    #something else. +    printf "oops. something else happened. let's see: %s %s %s\n" "$first" "$second" "$third" >&2 +  fi +done diff --git a/share/hackvr/examples/chess/hackvr-to-gnuchess.sh b/share/hackvr/examples/chess/hackvr-to-gnuchess.sh new file mode 100755 index 0000000..94cd1ab --- /dev/null +++ b/share/hackvr/examples/chess/hackvr-to-gnuchess.sh @@ -0,0 +1,18 @@ +#!/bin/sh +part1="" +echo "sleeping 3 seconds. might have gnuchess less likely to not do anything." >&2 +sleep 3 +echo "protover 2" +while read derp command target extra;do +  if [ "_$command" = "_action" ];then +    if printf "%s\n" "$target" | grep -v "^piece_" 2>&1 > /dev/null;then #we are ignoring clicks on pieces in favor of squares atm. when full 3d probably want pieces. +      if [ "_$part1" = "_" ];then +        part1=$target +      else +        #we have two parts. let's tell gnuchess! +        printf "%s%s\n" $part1 $target +        part1="" +      fi +    fi +  fi +done diff --git a/share/hackvr/examples/chess/pieces/bishop b/share/hackvr/examples/chess/pieces/bishop new file mode 100644 index 0000000..db6f144 --- /dev/null +++ b/share/hackvr/examples/chess/pieces/bishop @@ -0,0 +1,2 @@ +rook addshape 4 4  .4 .1 0   .6 .1 0   .6 .9 0   .4 .9 0 +rook addshape 4 4  .3 .6 0   .7 .6 0   .7 .8 0   .3 .8 0 diff --git a/share/hackvr/examples/chess/pieces/king b/share/hackvr/examples/chess/pieces/king new file mode 100644 index 0000000..8ca79ef --- /dev/null +++ b/share/hackvr/examples/chess/pieces/king @@ -0,0 +1,2 @@ +king addshape 4 4  .1 .7 0  .3 .9 0  .9 .3 0  .7 .1 0 +king addshape 4 4  .3 .1 0  .9 .7 0  .7 .9 0  .1 .3 0 diff --git a/share/hackvr/examples/chess/pieces/knight b/share/hackvr/examples/chess/pieces/knight new file mode 100644 index 0000000..adc7f53 --- /dev/null +++ b/share/hackvr/examples/chess/pieces/knight @@ -0,0 +1,2 @@ +knight addshape 4 4   .1 .1 0  .3 .1 0  .3 .9 0  .1 .9 0 +knight addshape 4 4   .1 .1 0  .9 .1 0  .9 .3 0  .1 .3 0 diff --git a/share/hackvr/examples/chess/pieces/pawn b/share/hackvr/examples/chess/pieces/pawn new file mode 100644 index 0000000..d7d2d29 --- /dev/null +++ b/share/hackvr/examples/chess/pieces/pawn @@ -0,0 +1 @@ +pawn addshape 4 3   .2 .1 0   .8 .1 0   .5 .5 0 diff --git a/share/hackvr/examples/chess/pieces/queen b/share/hackvr/examples/chess/pieces/queen new file mode 100644 index 0000000..3ebb4ab --- /dev/null +++ b/share/hackvr/examples/chess/pieces/queen @@ -0,0 +1 @@ +pawn addshape 4 3   .2 .1 0   .8 .1 0   .5 .9 0 diff --git a/share/hackvr/examples/chess/pieces/rook b/share/hackvr/examples/chess/pieces/rook new file mode 100644 index 0000000..2550eb5 --- /dev/null +++ b/share/hackvr/examples/chess/pieces/rook @@ -0,0 +1 @@ +rook addshape 4 4  .4 .1 0   .6 .1 0   .6 .9 0   .4 .9 0 diff --git a/share/hackvr/examples/chess/run b/share/hackvr/examples/chess/run new file mode 100755 index 0000000..4f0cdce --- /dev/null +++ b/share/hackvr/examples/chess/run @@ -0,0 +1,4 @@ +#!/bin/bash +mknod p p +stdbuf -oL cat camera.pos <(./board.sh) <(cat p | ./gnuchess-to-hackvr.sh) | hackvr "$USER" | ./hackvr-to-gnuchess.sh | gnuchess -x > p +rm p diff --git a/share/hackvr/examples/draw/draw.sh b/share/hackvr/examples/draw/draw.sh new file mode 100755 index 0000000..506d9ff --- /dev/null +++ b/share/hackvr/examples/draw/draw.sh @@ -0,0 +1,36 @@ +#!/bin/bash +echo "$USER move 0 0 -100" + +for y in $(seq 1 1 48);do + for x in $(seq 1 1 48);do +  echo ${x}_${y} addshape 2 4 $x $y 0 $[x] $[y+1] 0 $[x+1] $[y+1] 0 $[x+1] $[y] 0 + done +done + +### color pallete +### 0-8 are ansi colors. +16 to make them not affected by "lighting" +for c in $(seq 0 8);do +  echo color_${c} addshape $[c+16] 4 -2 ${c+2} 0 -1 ${c+2} 0 -1 $[c+3] 0 -2 $[c+3] 0 +done + +echo "derp" >&2 + +color=3 +echo color_${color} addshape $[color+16] 4 -2 0 0 -2 1 0 -1 1 0 -1 0 0 + +while read source command target;do +  printf "target: %s command: %s\n" "$target" "$command" >&2 +  if [ "$command" = "action" ];then +    if echo "$target" | grep "^color_" 2>&1 >/dev/null;then +      color=$(echo $target | cut -d_ -f2) +      echo deletegroup $target +      echo color_${color}_current addshape $[color+16] 4 -2 -2 0 -2 -1 0 -1 -1 0 -1 -2 0 +    else  +      echo "derp" >&2 +      x=$(echo $target | cut -d_ -f1) +      y=$(echo $target | cut -d_ -f2) +      echo $target deletegroup $target +      echo $target addshape $[color + 16] 4 $x $y 0 $x $[y+1] 0 $[x+1] $[y+1] 0 $[x+1] $y 0 | tee /dev/stderr +    fi +  fi +done diff --git a/share/hackvr/examples/draw/run b/share/hackvr/examples/draw/run new file mode 100755 index 0000000..f14b415 --- /dev/null +++ b/share/hackvr/examples/draw/run @@ -0,0 +1,3 @@ +#!/bin/sh +mknod p p +cat p | tee /dev/stderr | ./draw.sh | hackvr $USER > p diff --git a/share/hackvr/examples/editor/editor.sh b/share/hackvr/examples/editor/editor.sh new file mode 100755 index 0000000..e8ae65e --- /dev/null +++ b/share/hackvr/examples/editor/editor.sh @@ -0,0 +1,6 @@ +#!/bin/sh +cat p | hackvr $USER \ +  | stdbuf -oL tr -s ' ' \ +  | grep --line-buffered "^[^ ][^ ]* action " \ +  | stdbuf -oL cut '-d ' -f3 \ +  | xargs -L1 printf "epoch control %s\n" > p diff --git a/share/hackvr/examples/fib.sh b/share/hackvr/examples/fib.sh new file mode 100644 index 0000000..9a9cf5c --- /dev/null +++ b/share/hackvr/examples/fib.sh @@ -0,0 +1,12 @@ +#!/bin/sh +s=1 1 2  3  5  8  13 +x=0 0 1  0 -5 -5  +y=0 1 0 -3 -3  0  + +for i in 1 1 2 3 5 8 13 21 34 55 89;do +  x=0  +  y=0 +  w=i +  h=i +  addshape 2 4 x y 0 x+w y 0 x+w y+h 0 x y+h 0 +done diff --git a/share/hackvr/examples/filebrowser/action_to_target.sh b/share/hackvr/examples/filebrowser/action_to_target.sh new file mode 100755 index 0000000..2abbe58 --- /dev/null +++ b/share/hackvr/examples/filebrowser/action_to_target.sh @@ -0,0 +1,11 @@ +#!/bin/sh +#this script gets the action lines that hackvr outputs and turns them into as normal of a string as possible +#for the backend script to use. +#so hex encoded strings need to be unencoded before they leave here. +#the hex encoded strings don't have a newline at the end. we need to add one. see xargs printf "%s0a" +grep --line-buffered ^USER \ +  | stdbuf -oL cut '-d ' -f2- \ +  | grep --line-buffered ^action \ +  | stdbuf -oL cut '-d ' -f2- \ +  | xargs -L1 printf "%s0a\n" \ +  | stdbuf -oL xxd -r -p diff --git a/share/hackvr/examples/filebrowser/backend-filebrowser.sh b/share/hackvr/examples/filebrowser/backend-filebrowser.sh new file mode 100755 index 0000000..789b601 --- /dev/null +++ b/share/hackvr/examples/filebrowser/backend-filebrowser.sh @@ -0,0 +1,18 @@ +#!/bin/bash +#give a list of files. +#wait for a selection on stdin +#cd or start that file +#repeat. +while true;do +#not sure why this needs to be printed to show up every time. +  echo .. +  find . -maxdepth 1 #| tr ' ' '\n' #wut? no? +  read -r selection +  if [ -f "$selection" ];then +    xdg-open "$selection" #good enough? +  fi +  if [ -d "$selection" ];then +    cd "$selection" +  fi +  echo +done diff --git a/share/hackvr/examples/filebrowser/backend-gopher.sh b/share/hackvr/examples/filebrowser/backend-gopher.sh new file mode 100755 index 0000000..ab360da --- /dev/null +++ b/share/hackvr/examples/filebrowser/backend-gopher.sh @@ -0,0 +1,22 @@ +#!/bin/bash +#give a list of files. +#wait for a selection on stdin +#cd or start that file +#repeat. +server=$1 +${server:="gopher.hacking.allowed.org"} +#gopher.hacking.allowed.org +port=70 +selection=/ +while true;do +#not sure why this needs to be printed to show up every time. +  printf "/\n" +  printf '%s\n' "$selection" | ncat "$server" "$port" | tee /dev/stderr | grep -v ^i | cut -f2 | tr -d '\r' +### need to ask user for input in the form of a pop-up window if the selected target was of type 7 +  read -r selection +  if [ "_" = "_$selection" ];then +   echo "looks like there's nothing here. exiting." >&2 +   exit 1 +  fi +  echo +done diff --git a/share/hackvr/examples/filebrowser/backend-ps.sh b/share/hackvr/examples/filebrowser/backend-ps.sh new file mode 100755 index 0000000..b06ff99 --- /dev/null +++ b/share/hackvr/examples/filebrowser/backend-ps.sh @@ -0,0 +1,12 @@ +#!/bin/bash +#give a list of files. +#wait for a selection on stdin +#cd or start that file +#repeat. +while true;do +#not sure why this needs to be printed to show up every time. +  ps | tail -n+2 +  read -r selection +  echo $selection +  echo +done diff --git a/share/hackvr/examples/filebrowser/camera.pos b/share/hackvr/examples/filebrowser/camera.pos new file mode 100644 index 0000000..718923c --- /dev/null +++ b/share/hackvr/examples/filebrowser/camera.pos @@ -0,0 +1,2 @@ +USER move 0 0 0 +USER rotate 0 0 0 diff --git a/share/hackvr/examples/filebrowser/frontend-hackvr.sh b/share/hackvr/examples/filebrowser/frontend-hackvr.sh new file mode 100755 index 0000000..2b262a9 --- /dev/null +++ b/share/hackvr/examples/filebrowser/frontend-hackvr.sh @@ -0,0 +1,6 @@ +#!/bin/bash +if [ "_$1" = "_" ];then + echo "I need an argument for what my backend is." >&2 + exit 1 +fi +cat camera.pos <(stdbuf -oL $1 < p | ./list_to_cubes.sh) | hackvr USER | ./action_to_target.sh > p diff --git a/share/hackvr/examples/filebrowser/frontend-zenity.sh b/share/hackvr/examples/filebrowser/frontend-zenity.sh new file mode 100755 index 0000000..cb1fa9d --- /dev/null +++ b/share/hackvr/examples/filebrowser/frontend-zenity.sh @@ -0,0 +1,6 @@ +#!/bin/bash +if [ "_$1" = "_" ];then + echo "I need a backend script passed as first argument." >&2 + exit 1 +fi +stdbuf -oL $1 < p | stdbuf -oL tr '\n' ' ' | sed -u 's/$/_/' | stdbuf -oL tr '_' '\n' | xargs -L1 zenity --list --column file > p diff --git a/share/hackvr/examples/filebrowser/list_to_cubes.sh b/share/hackvr/examples/filebrowser/list_to_cubes.sh new file mode 100755 index 0000000..be438b8 --- /dev/null +++ b/share/hackvr/examples/filebrowser/list_to_cubes.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# blank lines deleteallexcept epoch +# expected input format: +# +# line1 +# line2 +# line3 +# +# line1 +# line2 +j=1 +r=200 +while read -r line;do + if [ "_$line" != '_' ];then +#  ../tools/obj2hackvr.pl "$line" ../meshes/cube.obj +  #turn line into hex to prevent stupid shit +  hexline="$(printf "%s" "$line" | xxd -p | tr -d '\n')" +  printf "%s addshape 18 3 8 %d %d 0 %d %d 0 %d %d\n" "$hexline" "$j" "$r" "$[$j+8]" "$r" "$j" "$r" +  cd .. +  printf "%s\n" "$line" | tee /dev/stderr | makelabel.sh "$hexline" 15 $j $r +  cd filebrowser +  #printf "%s move 0 %d %d 0 0 0 0 0 0\n" "$hexline" "$i" "$r" +  printf "%s rotate 0 %d 0\n" "$hexline" "$[i * 3]" +  #somehow printf '%s\n' "$line" | ./testfont.sh and make its output belong to same group as $line.. sed? +  i=$[i+10] +  j=$[j+4] + else +  cat camera.pos +  printf "USER deleteallexcept USER\n" #deleteallexcept doesn't have gr deletions yet I think. +  j=0 +  i=0 + fi +done diff --git a/share/hackvr/examples/filebrowser/run b/share/hackvr/examples/filebrowser/run new file mode 100755 index 0000000..8829124 --- /dev/null +++ b/share/hackvr/examples/filebrowser/run @@ -0,0 +1,3 @@ +#!/bin/bash +cd "$(dirname "$0")" +./frontend-hackvr.sh ./backend-gopher.sh $1 diff --git a/share/hackvr/examples/grid_floor.sh b/share/hackvr/examples/grid_floor.sh new file mode 100755 index 0000000..459c0e4 --- /dev/null +++ b/share/hackvr/examples/grid_floor.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cat <(for i in $(seq 1 32);do +        for j in $(seq 1 32);do +          echo floor addshape 2 4 $i 0 $j $i 0 $[j+1] $[i+1] 0 $[j+1] $[i+1] 0 $j +#          echo floor addshape 2 4 $i 0 $j $[i+1] 0 $j $[i+1] 0 $[j+1] $i 0 $[j+1] +        done +      done) \ +    <(echo -e "floor scaleup 8\nepoch export floor\nepoch quit\n") \ + | hackvr_headless $USER \ + | sed "s/\.0*//g" diff --git a/share/hackvr/examples/hackvr_term/Makefile b/share/hackvr/examples/hackvr_term/Makefile new file mode 100644 index 0000000..fb33227 --- /dev/null +++ b/share/hackvr/examples/hackvr_term/Makefile @@ -0,0 +1,19 @@ +.PHONY: all clean install uninstall + +all: hackvr_term + +hackvr_term.o: CFLAGS=-pedantic -Wall +libtmt/tmt.o: CFLAGS=-pedantic + +hackvr_term: hackvr_term.o libtmt/tmt.o + +clean: +	rm *.o +	rm */*.o +	rm hackvr_term + +install: +	install hackvr_term $(PREFIX)/bin/hackvr_term + +uninstall: +	[ -e $(PREFIX)/bin/hackvr_term ] && rm -i $(PREFIX)/bin/hackvr_term || echo "nothing to uninstall" diff --git a/share/hackvr/examples/hackvr_term/camera.pos b/share/hackvr/examples/hackvr_term/camera.pos new file mode 100644 index 0000000..df832fd --- /dev/null +++ b/share/hackvr/examples/hackvr_term/camera.pos @@ -0,0 +1,4 @@ +USER move 250 -140 -300 +#these two lines are for when I want a screenshot of the terminal rotated so I can take pretty screeshots +#USER move 315 -125 -285 +#USER rotate 0 -5 0 diff --git a/share/hackvr/examples/hackvr_term/hackvr_data_decode.sh b/share/hackvr/examples/hackvr_term/hackvr_data_decode.sh new file mode 100755 index 0000000..0ac04f2 --- /dev/null +++ b/share/hackvr/examples/hackvr_term/hackvr_data_decode.sh @@ -0,0 +1,2 @@ +#!/bin/sh +grep --line-buffered '^[^ ]* data ' | stdbuf -oL tr -s ' ' | stdbuf -oL cut '-d ' -f3- | stdbuf -o0 xxd -r -p diff --git a/share/hackvr/examples/hackvr_term/hackvr_term b/share/hackvr/examples/hackvr_term/hackvr_termBinary files differ new file mode 100755 index 0000000..d2fb6d2 --- /dev/null +++ b/share/hackvr/examples/hackvr_term/hackvr_term diff --git a/share/hackvr/examples/hackvr_term/hackvr_term.c b/share/hackvr/examples/hackvr_term/hackvr_term.c new file mode 100644 index 0000000..71f2492 --- /dev/null +++ b/share/hackvr/examples/hackvr_term/hackvr_term.c @@ -0,0 +1,184 @@ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> //PATH_MAX +#include "libtmt/tmt.h" + +//these numbers include the space between characters +//apple401 font is this wide: +//#define FONTW 14 +//#define FONTH 16 + +//epoch font is this wide: +#define FONTW 6 +#define FONTH 10 + +//////////////////////////////////// hey. you can do some config here. either preload glyphs or re-read font file each time. +//comment out the next line if you want what is probably the slow version. might be safer and use less memory though. +//maybe just put the font file in ram. linux caches that crap anyway, right? +//testing showed preloading was at least 10x faster. +#define PRELOAD_GLYPHS +//only 8-bit characters atm. +//and we'll need to malloc the amount of lines each character will have. +//and malloc each line in that glyph... +//or just set it statically. + +#ifdef PRELOAD_GLYPHS + +#define MAXCHARACTER 256 +#define MAXLINESPERGLYPH 16 +#define MAXCHARSPERGLYPHLINE 256 +char glyphs[MAXCHARACTER][MAXLINESPERGLYPH][MAXCHARSPERGLYPHLINE]; + +char font_file[PATH_MAX]; + +void preload_glyphs() { +  char line[MAXCHARSPERGLYPHLINE];//tmp storage +  FILE *fp; +  int charN; +  int i; +  for(i=0;i<MAXCHARACTER;i++) { +   glyphs[i][0][0]=0;//zero out the first byte of the first line of every glyph. we'll mark the end if we actually read something +  } +  if((fp=fopen(font_file,"r")) == NULL) { +    fprintf(stderr,"hackvr_term: failed to open font file: %s\n",font_file); +    exit(1); +  } +  for(i=0;fgets(line,sizeof(line)-1,fp) != 0;i++) { +    if(charN != strtol(line,0,16)) { +     i=0; +     charN=strtol(line,0,16); +    } +    strcpy(glyphs[charN][i],line); +    glyphs[charN][i+1][0]=0;//mark the end here. if we do it inside the conditional it won't mark the last one. +  } +  fclose(fp); +} + +void hackvr_draw_character(int c,int r,const TMTCHAR *ch) { +  char *line; +  int i,ret=0; +  printf("term_%02d_%02d addshape %d 4  -1 -3 0  5 -3 0  5 7 0  -1 7 0\n",c,r,ch->a.bg+15); +  for(i=0,line=glyphs[ch->c][0];line[0];i++,line=glyphs[ch->c][i]) { +    ret=1; +    printf("term_%02d_%02d addshape %d %s",c,r,ch->a.fg == -1 ? 17 : ch->a.fg + 15,line+strlen("XX addshape X")); +  } +  if(ret) {//if we drew something we should place it somewhere. +    printf("term_%02d_%02d move %d %d 0\n",c,r,c*FONTW,-r*FONTH); +  } +} + +#else + +void hackvr_draw_character(int c,int r,const TMTCHAR *ch) {//this is slow but allows you to change the font between each character... if you really wanted that. + int i; + FILE *fp; + char str[16]; + char line[256];//whatever + if(ch->c < 128) { +  snprintf(str,sizeof(str)-1,"%02lx",ch->c); + } else { +  snprintf(str,sizeof(str)-1,"%08lx",ch->c); + } + if((fp=fopen(font_file,"r")) == NULL ) { +  fprintf(stderr,"# fail to open font\n"); +  exit(1); + } + printf("term_%02d_%02d addshape %d 4  -1 -3 0  5 -3 0  5 7 0  -1 7 0\n",c,r,ch->a.bg+15); + for(i=0;fgets(line,sizeof(line)-1,fp) != 0;i++) { +  if(!strncmp(str,line,2)) { +   printf("term_%02d_%02d addshape %d %s",c,r,ch->a.fg==-1?2:ch->a.fg+15,line+strlen("XX addshape X ")); +  } + } + if(i) printf("term_%02d_%02d move %d %d 0\n",c,r,c*FONTW,-r*FONTH); + fclose(fp); +} + +#endif + +void callback(tmt_msg_t m,TMT *vt, const void *a,void *vt_old) { + static int cr_old=0; + static int cc_old=0; + const TMTSCREEN *s_old=tmt_screen(vt_old); + const TMTSCREEN *s=tmt_screen(vt); + const TMTPOINT *c=tmt_cursor(vt); + const TMTCHAR *ch; + switch(m) { +  case TMT_MSG_BELL: +   printf("term set global.beep\n"); +   break; +  case TMT_MSG_UPDATE: +   for (size_t r = 0; r < s->nline; r++){ +    if (s->lines[r]->dirty){ +     for (size_t c = 0; c < s->ncol; c++){ +      if(memcmp(&(s->lines[r]->chars[c]),&(s_old->lines[r]->chars[c]),sizeof(TMTCHAR))) { +       ch=&(s->lines[r]->chars[c]); +       printf("term_%02d_%02d deletegroup term_%02d_%02d\n",c,r,c,r); +       hackvr_draw_character(c,r,ch); +       memcpy(&(s_old->lines[r]->chars[c]),&(s->lines[r]->chars[c]),sizeof(TMTCHAR)); +      } +     } +    } +   } +   break; +  case TMT_MSG_ANSWER://what does this do? +   fprintf(stderr,"ANSWER: %s\n",(const char *)a); +   break; +  case TMT_MSG_MOVED: +   printf("cursor move %d %d 0\n", (c->c - cc_old) * 5, - (c->r - cr_old) * 9);//calculate relative movement needed based on previous and current positions. +   cr_old=c->r; +   cc_old=c->c; +   break; +  case TMT_MSG_CURSOR: +   if(!strcmp(a,"t")) { +    printf("cursor deletegroup cursor\n");//just delete it and redraw the cursor. +    printf("cursor addshape 2 4  0 -2 0  4 -2 0  4 6 0  0 6 0\n");//let's pretend this is how cursors should be. +    printf("cursor move %d %d 0\n",c->c * 5,-c->r * 9); +   } +   if(!strcmp(a,"f")) { +    printf("cursor deletegroup cursor\n"); +    cr_old=0; +    cc_old=0; +   } +   break; +  default: +   fprintf(stderr,"unhandled message type: %d\n",m); + } + return; +} + +int main(int argc,char *argv[]) { + char in[16]; + if(argc < 3) return fprintf(stderr,"usage: ./hackvr_term rows cols\n"),1; + int r=atoi(argv[2]); + int ret=0; + int c=atoi(argv[1]); + if(!getenv("PREFIX")) { +  fprintf(stderr,"hackvr_term: PREFIX is not set. I dunno where to find my fonts.\n"); +  return 1; + } + snprintf(font_file,sizeof(font_file)-1,"%s/share/hackvr/font/default.hackvr",getenv("PREFIX")); +#ifdef PRELOAD_GLYPHS + printf("# hackvr_term: preloading glyphs\n"); + preload_glyphs();//will exit on error + printf("# hackvr_term: done.\n"); +#endif + setbuf(stdin,0); + setbuf(stdout,0); + TMT *vt_old = tmt_open(r,c,NULL,NULL,NULL); + if(!vt_old) return fprintf(stderr,"failed to open tmt's virtual terminal for storage"),1; + TMT *vt = tmt_open(r,c,callback,vt_old,NULL); + if(!vt) return fprintf(stderr,"failed to open tmt's virtual terminal"),1; + //read from stdin and write to terminal. + while((ret=read(0,in,16)) != 0) {//16 bytes at a time work? +  if(ret == -1) { +   if(errno == EAGAIN) continue; +   break; +  } +  tmt_write(vt,in,ret); + } + tmt_close(vt); + return 0; +} diff --git a/share/hackvr/examples/hackvr_term/libtmt/README.rst b/share/hackvr/examples/hackvr_term/libtmt/README.rst new file mode 100644 index 0000000..f3819df --- /dev/null +++ b/share/hackvr/examples/hackvr_term/libtmt/README.rst @@ -0,0 +1,637 @@ + +============================================ +libtmt - a simple terminal emulation library +============================================ + +libtmt is the Tiny Mock Terminal Library.  It provides emulation of a classic +smart text terminal, by maintaining an in-memory screen image.  Sending text +and command sequences to libtmt causes it to update this in-memory image, +which can then be examined and rendered however the user sees fit. + +The imagined primary goal for libtmt is to for terminal emulators and +multiplexers; it provides the terminal emulation layer for the `mtm`_ +terminal multiplexer, for example. Other uses include screen-scraping and +automated test harnesses. + +libtmt is similar in purpose to `libtsm`_, but considerably smaller (500 +lines versus 6500 lines). libtmt is also, in this author's humble opinion, +considerably easier to use. + +.. _`mtm`: https://github.com/deadpixi/mtm +.. _`libtsm`: https://www.freedesktop.org/wiki/Software/kmscon/libtsm/ + +Major Features and Advantages +============================= + +Works Out-of-the-Box +    libtmt emulates a well-known terminal type (`ansi`), the definition of +    which has been in the terminfo database since at least 1995.  There's no +    need to install a custom terminfo entry.  There's no claiming to be an +    xterm but only emulating a small subset of its features. Any program +    using terminfo works automatically: this includes vim, emacs, mc, +    cmus, nano, nethack, ... + +Portable +    Written in pure C99. +    Optionally, the POSIX-mandated `wcwidth` function can be used, which +    provides minimal support for combining characters. + +Small +    Less than 500 lines of C, including comments and whitespace. + +Free +    Released under a BSD-style license, free for commercial and +    non-commerical use, with no restrictions on source code release or +    redistribution. + +Simple +    Only 8 functions to learn, and really you can get by with 6! + +International +    libtmt internally uses wide characters exclusively, and uses your C +    library's multibyte encoding functions. +    This means that the library automatically supports any encoding that +    your operating system does. + +How to Use libtmt +================= + +libtmt is a single C file and a single header.  Just include these files +in your project and you should be good to go. + +By default, libtmt uses only ISO standard C99 features, +but see `Compile-Time Options`_ below. + +Example Code +------------ + +Below is a simple program fragment giving the flavor of libtmt. +Note that another good example is the `mtm`_ terminal multiplexer: + +.. _`mtm`: https://github.com/deadpixi/mtm + +.. code:: c + +    #include <stdio.h> +    #include <stdlib.h> +    #include "tmt.h" + +    /* Forward declaration of a callback. +     * libtmt will call this function when the terminal's state changes. +     */ +    void callback(tmt_msg_t m, TMT *vt, const void *a, void *p); + +    int +    main(void) +    { +        /* Open a virtual terminal with 2 lines and 10 columns. +         * The first NULL is just a pointer that will be provided to the +         * callback; it can be anything. The second NULL specifies that +         * we want to use the default Alternate Character Set; this +         * could be a pointer to a wide string that has the desired +         * characters to be displayed when in ACS mode. +         */ +        TMT *vt = tmt_open(2, 10, callback, NULL, NULL); +        if (!vt) +            return perror("could not allocate terminal"), EXIT_FAILURE; + +        /* Write some text to the terminal, using escape sequences to +         * use a bold rendition. +         * +         * The final argument is the length of the input; 0 means that +         * libtmt will determine the length dynamically using strlen. +         */ +        tmt_write(vt, "\033[1mhello, world (in bold!)\033[0m", 0); + +        /* Writing input to the virtual terminal can (and in this case, did) +         * call the callback letting us know the screen was updated. See the +         * callback below to see how that works. +         */ +        tmt_close(vt); +        return EXIT_SUCCESS; +    } + +    void +    callback(tmt_msg_t m, TMT *vt, const void *a, void *p) +    { +        /* grab a pointer to the virtual screen */ +        const TMTSCREEN *s = tmt_screen(vt); +        const TMTPOINT *c = tmt_cursor(vt); + +        switch (m){ +            case TMT_MSG_BELL: +                /* the terminal is requesting that we ring the bell/flash the +                 * screen/do whatever ^G is supposed to do; a is NULL +                 */ +                printf("bing!\n"); +                break; + +            case TMT_MSG_UPDATE: +                /* the screen image changed; a is a pointer to the TMTSCREEN */ +                for (size_t r = 0; r < s->nline; r++){ +                    if (s->lines[r]->dirty){ +                        for (size_t c = 0; c < s->ncol; c++){ +                            printf("contents of %zd,%zd: %lc (%s bold)\n", r, c, +                                   s->lines[r]->chars[c].c, +                                   s->lines[r]->chars[c].a.bold? "is" : "is not"); +                        } +                    } +                } + +                /* let tmt know we've redrawn the screen */ +                tmt_clean(vt); +                break; + +            case TMT_MSG_ANSWER: +                /* the terminal has a response to give to the program; a is a +                 * pointer to a string */ +                printf("terminal answered %s\n", (const char *)a); +                break; + +            case TMT_MSG_MOVED: +                /* the cursor moved; a is a pointer to the cursor's TMTPOINT */ +                printf("cursor is now at %zd,%zd\n", c->r, c->c); +                break; +        } +    } + +Data Types and Enumerations +--------------------------- + +.. code:: c + +    /* an opaque structure */ +    typedef struct TMT TMT; + +    /* possible messages sent to the callback */ +    typedef enum{ +        TMT_MSG_MOVED,  /* the cursor changed position       */ +        TMT_MSG_UPDATE, /* the screen image changed          */ +        TMT_MSG_ANSWER, /* the terminal responded to a query */ +        TMT_MSG_BELL    /* the terminal bell was rung        */ +    } tmt_msg_T; + +    /* a callback for the library +     * m is one of the message constants above +     * vt is a pointer to the vt structure +     * r is NULL for TMT_MSG_BELL +     *   is a pointer to the cursor's TMTPOINT for TMT_MSG_MOVED +     *   is a pointer to the terminal's TMTSCREEN for TMT_MSG_UPDATE +     *   is a pointer to a string for TMT_MSG_ANSWER +     * p is whatever was passed to tmt_open (see below). +     */ +    typedef void (*TMTCALLBACK)(tmt_msg_t m, struct TMT *vt, +                                const void *r, void *p); + +    /* color definitions */ +    typedef enum{ +        TMT_COLOR_BLACK, +        TMT_COLOR_RED, +        TMT_COLOR_GREEN, +        TMT_COLOR_YELLOW, +        TMT_COLOR_BLUE, +        TMT_COLOR_MAGENTA, +        TMT_COLOR_CYAN, +        TMT_COLOR_WHITE, +        TMT_COLOR_DEFAULT /* whatever the host terminal wants it to mean */ +    } tmt_color_t; + +    /* graphical rendition */ +    typedef struct TMTATTRS TMTATTRS; +    struct TMTATTRS{ +        bool bold;      /* character is bold             */ +        bool dim;       /* character is half-bright      */ +        bool underline; /* character is underlined       */ +        bool blink;     /* character is blinking         */ +        bool reverse;   /* character is in reverse video */ +        bool invisible; /* character is invisible        */ +        tmt_color_t fg; /* character foreground color    */ +        tmt_color_t bg; /* character background color    */ +    }; + +    /* characters */ +    typedef struct TMTCHAR TMTCHAR; +    struct TMTCHAR{ +        wchar_t  c; /* the character */ +        TMTATTRS a; /* its rendition */ +    }; + +    /* a position on the screen; upper left corner is 0,0 */ +    typedef struct TMTPOINT TMTPOINT; +    struct TMTPOINT{ +        size_t r; /* row    */ +        size_t c; /* column */ +    }; + +    /* a line of characters on the screen; +     * every line is always as wide as the screen +     */ +    typedef struct TMTLINE TMTLINE; +    struct TMTLINE{ +        bool dirty;     /* line has changed since it was last drawn */ +        TMTCHAR chars;  /* the contents of the line                 */ +    }; + +    /* a virtual terminal screen image */ +    typedef struct TMTSCREEN TMTSCREEN; +    struct TMTSCREEN{ +        size_t nline;    /* number of rows          */ +        size_t ncol;     /* number of columns       */ +        TMTLINE **lines; /* the lines on the screen */ +    }; + +Functions +--------- + +`TMT *tmt_open(size_t nrows, size_t ncols, TMTCALLBACK cb, VOID *p, const wchar *acs);` +    Creates a new virtual terminal, with `nrows` rows and `ncols` columns. +    The callback `cb` will be called on updates, and passed `p` as a final +    argument. See the definition of `tmt_msg_t` above for possible values +    of each argument to the callback. + +    Terminals must have a size of at least two rows and two columns. + +    `acs` specifies the characters to use when in Alternate Character Set +    (ACS) mode. The default string (used if `NULL` is specified) is:: + +         L"><^v#+:o##+++++~---_++++|<>*!fo" + +    See `Alternate Character Set`_ for more information. + +    Note that the callback must be ready to be called immediately, as +    it will be called after initialization of the terminal is done, but +    before the call to `tmt_open` returns. + +`void tmt_close(TMT *vt)` +    Close and free all resources associated with `vt`. + +`bool tmt_resize(TMT *vt, size_t nrows, size_t ncols)` +    Resize the virtual terminal to have `nrows` rows and `ncols` columns. +    The contents of the area in common between the two sizes will be preserved. + +    Terminals must have a size of at least two rows and two columns. + +    If this function returns false, the resize failed (only possible in +    out-of-memory conditions or invalid sizes). If this happens, the terminal +    is trashed and the only valid operation is the close the terminal. + +`void tmt_write(TMT *vt, const char *s, size_t n);` +    Write the provided string to the terminal, interpreting any escape +    sequences contained threin, and update the screen image. The last +    argument is the length of the input. If set to 0, the length is +    determined using `strlen`. + +    The terminal's callback function may be invoked one or more times before +    a call to this function returns. + +    The string is converted internally to a wide-character string using the +    system's current multibyte encoding. Each terminal maintains a private +    multibyte decoding state, and correctly handles mulitbyte characters that +    span multiple calls to this function (that is, the final byte(s) of `s` +    may be a partial mulitbyte character to be completed on the next call). + +`const TMTSCREEN *tmt_screen(const TMT *vt);` +    Returns a pointer to the terminal's screen image. + +`const TMTPOINT *tmt_cursor(cosnt TMT *vt);` +    Returns a pointer to the terminal's cursor position. + +`void tmt_clean(TMT *vt);` +    Call this after receiving a `TMT_MSG_UPDATE` or `TMT_MSG_MOVED` callback +    to let the library know that the program has handled all reported changes +    to the screen image. + +`void tmt_reset(TMT *vt);` +    Resets the virtual terminal to its default state (colors, multibyte +    decoding state, rendition, etc). + +Special Keys +------------ + +To send special keys to a program that is using libtmt for its display, +write one of the `TMT_KEY_*` strings to that program's standard input +(*not* to libtmt; it makes no sense to send any of these constants to +libtmt itself). + +The following macros are defined, and are all constant strings: + +- TMT_KEY_UP +- TMT_KEY_DOWN +- TMT_KEY_RIGHT +- TMT_KEY_LEFT +- TMT_KEY_HOME +- TMT_KEY_END +- TMT_KEY_INSERT +- TMT_KEY_BACKSPACE +- TMT_KEY_ESCAPE +- TMT_KEY_BACK_TAB +- TMT_KEY_PAGE_UP +- TMT_KEY_PAGE_DOWN +- TMT_KEY_F1 through TMT_KEY_F10 + +Note also that the classic PC console sent the enter key as +a carriage return, not a linefeed. Many programs don't care, +but some do. + +Compile-Time Options +-------------------- + +There are two preprocessor macros that affect libtmt: + +`TMT_INVALID_CHAR` +    Define this to a wide-character. This character will be added to +    the virtual display when an invalid multibyte character sequence +    is encountered. + +    By default (if you don't define it as something else before compiling), +    this is `((wchar_t)0xfffd)`, which is the codepoint for the Unicode +    'REPLACEMENT CHARACTER'. Note that your system might not use Unicode, +    and its wide-character type might not be able to store a constant as +    large as `0xfffd`, in which case you'll want to use an alternative. + +`TMT_HAS_WCWIDTH` +    By default, libtmt uses only standard C99 features.  If you define +    TMT_HAS_WCWIDTH before compiling, libtmt will use the POSIX `wcwidth` +    function to detect combining characters. + +    Note that combining characters are still not handled particularly +    well, regardless of whether this was defined. Also note that what +    your C library's `wcwidth` considers a combining character and what +    the written language in question considers one could be different. + +Alternate Character Set +----------------------- + +The terminal can be switched to and from its "Alternate Character Set" (ACS) +using escape sequences. The ACS traditionally contained box-drawing and other +semigraphic characters. + +The characters in the ACS are configurable at runtime, by passing a wide string +to `tmt_open`. The default if none is provided (i.e. the argument is `NULL`) +uses ASCII characters to approximate the traditional characters. + +The string passed to `tmt_open` must be 31 characters long. The characters, +and their default ASCII-safe values, are in order: + +- RIGHT ARROW ">" +- LEFT ARROW "<" +- UP ARROW "^" +- DOWN ARROW "v" +- BLOCK "#" +- DIAMOND "+" +- CHECKERBOARD "#" +- DEGREE "o" +- PLUS/MINUS "+" +- BOARD ":" +- LOWER RIGHT CORNER "+" +- UPPER RIGHT CORNER "+" +- UPPER LEFT CORNER "+" +- LOWER LEFT CORNER "+" +- CROSS "+" +- SCAN LINE 1 "~" +- SCAN LINE 3 "-" +- HORIZONTAL LINE "-" +- SCAN LINE 7 "-" +- SCAN LINE 9 "_" +- LEFT TEE "+" +- RIGHT TEE "+" +- BOTTOM TEE "+" +- TOP TEE "+" +- VERTICAL LINE "|" +- LESS THAN OR EQUAL "<" +- GREATER THAN OR EQUAL ">" +- PI "*" +- NOT EQUAL "!" +- POUND STERLING "f" +- BULLET "o" + +If your system's wide character type's character set corresponds to the +Universal Character Set (UCS/Unicode), the following wide string is a +good option to use:: + +    L"→←↑↓■◆▒°±▒┘┐┌└┼⎺───⎽├┤┴┬│≤≥π≠£•" + +**Note that multibyte decoding is disabled in ACS mode.** The traditional +implementations of the "ansi" terminal type (i.e. IBM PCs and compatibles) +had no concept of multibyte encodings and used the character codes +outside the ASCII range for various special semigraphic characters. +(Technically they had an entire alternate character set as well via the +code page mechanism, but that's beyond the scope of this explanation.) + +The end result is that the terminfo definition of "ansi" sends characters +with the high bit set when in ACS mode. This breaks several multibyte +encoding schemes (including, most importantly, UTF-8). + +As a result, libtmt does not attempt to decode multibyte characters in +ACS mode, since that would break the multibyte encoding, the semigraphic +characters, or both. + +In general this isn't a problem, since programs explicitly switch to and +from ACS mode using escape sequences. + +When in ACS mode, bytes that are not special members of the alternate +character set (that is, bytes not mapped to the string provided to +`tmt_open`) are passed unchanged to the terminal. + +Supported Input and Escape Sequences +==================================== + +Internally libtmt uses your C library's/compiler's idea of a wide character +for all characters, so you should be able to use whatever characters you want +when writing to the virtual terminal (but see `Alternate Character Set`_). + +The following escape sequences are recognized and will be processed +specially. + +In the descriptions below, "ESC" means a literal escape character and "Ps" +means zero or more decimal numeric arguments separated by semicolons. +In descriptions "P1", "P2", etc, refer to the first parameter, second +parameter, and so on.  If a required parameter is omitted, it defaults +to the smallest meaningful value (zero if the command accepts zero as +an argument, one otherwise).  Any number of parameters may be passed, +but any after the first eight are ignored. + +Unless explicitly stated below, cursor motions past the edges of the screen +are ignored and do not result in scrolling.  When characters are moved, +the spaces left behind are filled with blanks and any characters moved +off the edges of the screen are lost. + +======================  ====================================================================== +Sequence                Action +======================  ====================================================================== +0x07 (Bell)             Callback with TMT_MSG_BELL +0x08 (Backspace)        Cursor left one cell +0x09 (Tab)              Cursor to next tab stop or end of line +0x0a (Carriage Return)  Cursor to first cell on this line +0x0d (Linefeed)         Cursor to same column one line down, scroll if needed +ESC H                   Set a tabstop in this column +ESC 7                   Save cursor position and current graphical state +ESC 8                   Restore saved cursor position and current graphical state +ESC c                   Reset terminal to default state +ESC [ Ps A              Cursor up P1 rows +ESC [ Ps B              Cursor down P1 rows +ESC [ Ps C              Cursor right P1 columns +ESC [ Ps D              Cursor left P1 columns +ESC [ Ps E              Cursor to first column of line P1 rows down from current +ESC [ Ps F              Cursor to first column of line P1 rows up from current +ESC [ Ps G              Cursor to column P1 +ESC [ Ps d              Cursor to row P1 +ESC [ Ps H              Cursor to row P1, column P2 +ESC [ Ps f              Alias for ESC [ Ps H +ESC [ Ps I              Cursor to next tab stop +ESC [ Ps J              Clear screen +                        P1 == 0: from cursor to end of screen +                        P1 == 1: from beginning of screen to cursor +                        P1 == 2: entire screen +ESC [ Ps K              Clear line +                        P1 == 0: from cursor to end of line +                        P1 == 1: from beginning of line to cursor +                        P1 == 2: entire line +ESC [ Ps L              Insert P1 lines at cursor, scrolling lines below down +ESC [ Ps M              Delete P1 lines at cursor, scrolling lines below up +ESC [ Ps P              Delete P1 characters at cursor, moving characters to the right over +ESC [ Ps S              Scroll screen up P1 lines +ESC [ Ps T              Scroll screen down P1 lines +ESC [ Ps X              Erase P1 characters at cursor (overwrite with spaces) +ESC [ Ps Z              Go to previous tab stop +ESC [ Ps b              Repeat previous character P1 times +ESC [ Ps c              Callback with TMT_MSG_ANSWER "\033[?6c" +ESC [ Ps g              If P1 == 3, clear all tabstops +ESC [ Ps h              If P1 == 25, show the cursor (if it was hidden) +ESC [ Ps m              Change graphical rendition state; see below +ESC [ Ps l              If P1 == 25, hide the cursor +ESC [ Ps n              If P1 == 6, callback with TMT_MSG_ANSWER "\033[%d;%dR" +                        with cursor row, column +ESC [ Ps s              Alias for ESC 7 +ESC [ Ps u              Alias for ESC 8 +ESC [ Ps @              Insert P1 blank spaces at cursor, moving characters to the right over +======================  ====================================================================== + +For the `ESC [ Ps m` escape sequence above ("Set Graphic Rendition"), +up to eight parameters may be passed; the results are cumulative: + +==============   ================================================= +Rendition Code   Meaning +==============   ================================================= +0                Reset all graphic rendition attributes to default +1                Bold +2                Dim (half bright) +4                Underline +5                Blink +7                Reverse video +8                Invisible +10               Leave ACS mode +11               Enter ACS mode +22               Bold off +23               Dim (half bright) off +24               Underline off +25               Blink off +27               Reverse video off +28               Invisible off +30               Foreground black +31               Foreground red +32               Foreground green +33               Foreground yellow +34               Foreground blue +35               Foreground magenta +36               Foreground cyan +37               Foreground white +39               Foreground default color +40               Background black +41               Background red +42               Background green +43               Background yellow +44               Background blue +45               Background magenta +46               Background cyan +47               Background white +49               Background default color +==============   ================================================= + +Other escape sequences are recognized but ignored.  This includes escape +sequences for switching out codesets (officially, all code sets are defined +as equivalent in libtmt), and the various "Media Copy" escape sequences +used to print output on paper (officially, there is no printer attached +to libtmt). + +Additionally, "?" characters are stripped out of escape sequence parameter +lists for compatibility purposes. + +Known Issues +============ + +- Combining characters are "handled" by ignoring them +  (when compiled with `TMT_HAS_WCWIDTH`) or by printing them separately. +- Double-width characters are rendered as single-width invalid +  characters. +- The documentation and error messages are available only in English. + +Frequently Asked Questions +========================== + +What programs work with libtmt? +------------------------------- + +Pretty much all of them.  Any program that doesn't assume what terminal +it's running under should work without problem; this includes any program +that uses the terminfo, termcap, or (pd|n)?curses libraries.  Any program +that assumes it's running under some specific terminal might fail if its +assumption is wrong, and not just under libtmt. + +I've tested quite a few applications in libtmt and they've worked flawlessly: +vim, GNU emacs, nano, cmus, mc (Midnight Commander), and others just work +with no changes. + +What programs don't work with libtmt? +------------------------------------- + +Breakage with libtmt is of two kinds: breakage due to assuming a terminal +type, and reduced functionality. + +In all my testing, I only found one program that didn't work correctly by +default with libtmt: recent versions of Debian's `apt`_ assume a terminal +with definable scrolling regions to draw a fancy progress bar during +package installation.  Using apt in its default configuration in libtmt will +result in a corrupted display (that can be fixed by clearing the screen). + +.. _`apt`: https://wiki.debian.org/Apt + +In my honest opinion, this is a bug in apt: it shouldn't assume the type +of terminal it's running in. + +The second kind of breakage is when not all of a program's features are +available.  The biggest missing feature here is mouse support: libtmt +doesn't, and probably never will, support mouse tracking.  I know of many +programs that *can* use mouse tracking in a terminal, but I don't know +of any that *require* it.  Most (if not all?) programs of this kind would +still be completely usable in libtmt. + +License +------- + +Copyright (c) 2017 Rob King +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright +  notice, this list of conditions and the following disclaimer. +- 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. +- Neither the name of the copyright holder nor the +  names of contributors may be used to endorse or promote products +  derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 AUTHORS, +COPYRIGHT HOLDERS, 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. diff --git a/share/hackvr/examples/hackvr_term/libtmt/tmt.c b/share/hackvr/examples/hackvr_term/libtmt/tmt.c new file mode 100644 index 0000000..26c122e --- /dev/null +++ b/share/hackvr/examples/hackvr_term/libtmt/tmt.c @@ -0,0 +1,500 @@ +/* Copyright (c) 2017 Rob King + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *   * Redistributions of source code must retain the above copyright + *     notice, this list of conditions and the following disclaimer. + *   * 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. + *   * Neither the name of the copyright holder nor the + *     names of contributors may be used to endorse or promote products + *     derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER 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 AUTHORS, + * COPYRIGHT HOLDERS, 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. + */ +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "tmt.h" + +#define BUF_MAX 100 +#define PAR_MAX 8 +#define TAB 8 +#define MAX(x, y) (((size_t)(x) > (size_t)(y)) ? (size_t)(x) : (size_t)(y)) +#define MIN(x, y) (((size_t)(x) < (size_t)(y)) ? (size_t)(x) : (size_t)(y)) +#define CLINE(vt) (vt)->screen.lines[MIN((vt)->curs.r, (vt)->screen.nline - 1)] + +#define P0(x) (vt->pars[x]) +#define P1(x) (vt->pars[x]? vt->pars[x] : 1) +#define CB(vt, m, a) ((vt)->cb? (vt)->cb(m, vt, a, (vt)->p) : (void)0) +#define INESC ((vt)->state) + +#define COMMON_VARS             \ +    TMTSCREEN *s = &vt->screen; \ +    TMTPOINT *c = &vt->curs;    \ +    TMTLINE *l = CLINE(vt);     \ +    TMTCHAR *t = vt->tabs->chars + +#define HANDLER(name) static void name (TMT *vt) { COMMON_VARS;  + +struct TMT{ +    TMTPOINT curs, oldcurs; +    TMTATTRS attrs, oldattrs; + +    bool dirty, acs, ignored; +    TMTSCREEN screen; +    TMTLINE *tabs; + +    TMTCALLBACK cb; +    void *p; +    const wchar_t *acschars; + +    mbstate_t ms; +    size_t nmb; +    char mb[BUF_MAX + 1]; + +    size_t pars[PAR_MAX];    +    size_t npar; +    size_t arg; +    enum {S_NUL, S_ESC, S_ARG} state; +}; + +static TMTATTRS defattrs = {.fg = TMT_COLOR_DEFAULT, .bg = TMT_COLOR_DEFAULT}; +static void writecharatcurs(TMT *vt, wchar_t w); + +static wchar_t +tacs(const TMT *vt, unsigned char c) +{ +    /* The terminfo alternate character set for ANSI. */ +    static unsigned char map[] = {0020U, 0021U, 0030U, 0031U, 0333U, 0004U, +                                  0261U, 0370U, 0361U, 0260U, 0331U, 0277U, +                                  0332U, 0300U, 0305U, 0176U, 0304U, 0304U, +                                  0304U, 0137U, 0303U, 0264U, 0301U, 0302U, +                                  0263U, 0363U, 0362U, 0343U, 0330U, 0234U, +                                  0376U}; +    for (size_t i = 0; i < sizeof(map); i++) if (map[i] == c) +        return vt->acschars[i]; +    return (wchar_t)c; +} + +static void +dirtylines(TMT *vt, size_t s, size_t e) +{ +    vt->dirty = true; +    for (size_t i = s; i < e; i++) +        vt->screen.lines[i]->dirty = true; +} + +static void +clearline(TMT *vt, TMTLINE *l, size_t s, size_t e) +{ +    vt->dirty = l->dirty = true; +    for (size_t i = s; i < e && i < vt->screen.ncol; i++){ +        l->chars[i].a = defattrs; +        l->chars[i].c = L' '; +    } +} + +static void +clearlines(TMT *vt, size_t r, size_t n) +{ +    for (size_t i = r; i < r + n && i < vt->screen.nline; i++) +        clearline(vt, vt->screen.lines[i], 0, vt->screen.ncol); +} + +static void +scrup(TMT *vt, size_t r, size_t n) +{ +    n = MIN(n, vt->screen.nline - 1 - r); + +    if (n){ +        TMTLINE *buf[n]; + +        memcpy(buf, vt->screen.lines + r, n * sizeof(TMTLINE *)); +        memmove(vt->screen.lines + r, vt->screen.lines + r + n, +                (vt->screen.nline - n - r) * sizeof(TMTLINE *)); +        memcpy(vt->screen.lines + (vt->screen.nline - n), +               buf, n * sizeof(TMTLINE *)); + +        clearlines(vt, vt->screen.nline - n, n); +        dirtylines(vt, r, vt->screen.nline); +    } +} + +static void +scrdn(TMT *vt, size_t r, size_t n) +{ +    n = MIN(n, vt->screen.nline - 1 - r); + +    if (n){ +        TMTLINE *buf[n]; + +        memcpy(buf, vt->screen.lines + (vt->screen.nline - n), +               n * sizeof(TMTLINE *)); +        memmove(vt->screen.lines + r + n, vt->screen.lines + r, +                (vt->screen.nline - n - r) * sizeof(TMTLINE *)); +        memcpy(vt->screen.lines + r, buf, n * sizeof(TMTLINE *)); +     +        clearlines(vt, r, n); +        dirtylines(vt, r, vt->screen.nline); +    } +} + +HANDLER(ed) +    size_t b = 0; +    size_t e = s->nline; + +    switch (P0(0)){ +        case 0: b = c->r + 1; clearline(vt, l, c->c, vt->screen.ncol); break; +        case 1: e = c->r - 1; clearline(vt, l, 0, c->c);               break; +        case 2:  /* use defaults */                                    break; +        default: /* do nothing   */                                    return; +    } + +    clearlines(vt, b, e - b); +} + +HANDLER(ich) +    size_t n = P1(0); /* XXX use MAX */ +    if (n > s->ncol - c->c - 1) n = s->ncol - c->c - 1; + +    memmove(l->chars + c->c + n, l->chars + c->c, +            MIN(s->ncol - 1 - c->c, +            (s->ncol - c->c - n - 1)) * sizeof(TMTCHAR)); +    clearline(vt, l, c->c, n); +} + +HANDLER(dch) +    size_t n = P1(0); /* XXX use MAX */ +    if (n > s->ncol - c->c) n = s->ncol - c->c; + +    memmove(l->chars + c->c, l->chars + c->c + n, +            (s->ncol - c->c - n) * sizeof(TMTCHAR)); + +    clearline(vt, l, s->ncol - c->c - n, s->ncol); +} + +HANDLER(el) +    switch (P0(0)){ +        case 0: clearline(vt, l, c->c, vt->screen.ncol);         break; +        case 1: clearline(vt, l, 0, MIN(c->c + 1, s->ncol - 1)); break; +        case 2: clearline(vt, l, 0, vt->screen.ncol);            break; +    } +} + +HANDLER(sgr) +    #define FGBG(c) *(P0(i) < 40? &vt->attrs.fg : &vt->attrs.bg) = c +    for (size_t i = 0; i < vt->npar; i++) switch (P0(i)){ +        case  0: vt->attrs                    = defattrs;   break; +        case  1: case 22: vt->attrs.bold      = P0(0) < 20; break; +        case  2: case 23: vt->attrs.dim       = P0(0) < 20; break; +        case  4: case 24: vt->attrs.underline = P0(0) < 20; break; +        case  5: case 25: vt->attrs.blink     = P0(0) < 20; break; +        case  7: case 27: vt->attrs.reverse   = P0(0) < 20; break; +        case  8: case 28: vt->attrs.invisible = P0(0) < 20; break; +        case 10: case 11: vt->acs             = P0(0) > 10; break; +        case 30: case 40: FGBG(TMT_COLOR_BLACK);            break; +        case 31: case 41: FGBG(TMT_COLOR_RED);              break; +        case 32: case 42: FGBG(TMT_COLOR_GREEN);            break; +        case 33: case 43: FGBG(TMT_COLOR_YELLOW);           break; +        case 34: case 44: FGBG(TMT_COLOR_BLUE);             break; +        case 35: case 45: FGBG(TMT_COLOR_MAGENTA);          break; +        case 36: case 46: FGBG(TMT_COLOR_CYAN);             break; +        case 37: case 47: FGBG(TMT_COLOR_WHITE);            break; +        case 39: case 49: FGBG(TMT_COLOR_DEFAULT);          break; +    } +} + +HANDLER(rep) +    if (!c->c) return; +    wchar_t r = l->chars[c->c - 1].c; +    for (size_t i = 0; i < P1(0); i++) +        writecharatcurs(vt, r); +} + +HANDLER(dsr) +    char r[BUF_MAX + 1] = {0}; +    snprintf(r, BUF_MAX, "\033[%zd;%zdR", c->r, c->c); +    CB(vt, TMT_MSG_ANSWER, (const char *)r); +} + +HANDLER(resetparser) +    memset(vt->pars, 0, sizeof(vt->pars)); +    vt->state = vt->npar = vt->arg = vt->ignored = (bool)0; +} + +HANDLER(consumearg) +    if (vt->npar < PAR_MAX) +        vt->pars[vt->npar++] = vt->arg; +    vt->arg = 0; +} + +HANDLER(fixcursor) +    c->r = MIN(c->r, s->nline - 1); +    c->c = MIN(c->c, s->ncol - 1); +} + +static bool +handlechar(TMT *vt, char i) +{ +    COMMON_VARS; + +    char cs[] = {i, 0}; +    #define ON(S, C, A) if (vt->state == (S) && strchr(C, i)){ A; return true;} +    #define DO(S, C, A) ON(S, C, consumearg(vt); if (!vt->ignored) {A;} \ +                                 fixcursor(vt); resetparser(vt);); + +    DO(S_NUL, "\x07",       CB(vt, TMT_MSG_BELL, NULL)) +    DO(S_NUL, "\x08",       if (c->c) c->c--) +    DO(S_NUL, "\x09",       while (++c->c < s->ncol - 1 && t[c->c].c != L'*')) +    DO(S_NUL, "\x0a",       c->r < s->nline - 1? (void)c->r++ : scrup(vt, 0, 1)) +    DO(S_NUL, "\x0d",       c->c = 0) +    ON(S_NUL, "\x1b",       vt->state = S_ESC) +    ON(S_ESC, "\x1b",       vt->state = S_ESC) +    DO(S_ESC, "H",          t[c->c].c = L'*') +    DO(S_ESC, "7",          vt->oldcurs = vt->curs; vt->oldattrs = vt->attrs) +    DO(S_ESC, "8",          vt->curs = vt->oldcurs; vt->attrs = vt->oldattrs) +    ON(S_ESC, "+*()",       vt->ignored = true; vt->state = S_ARG) +    DO(S_ESC, "c",          tmt_reset(vt)) +    ON(S_ESC, "[",          vt->state = S_ARG) +    ON(S_ARG, "\x1b",       vt->state = S_ESC) +    ON(S_ARG, ";",          consumearg(vt)) +    ON(S_ARG, "?",          (void)0) +    ON(S_ARG, "0123456789", vt->arg = vt->arg * 10 + atoi(cs)) +    DO(S_ARG, "A",          c->r = MAX(c->r - P1(0), 0)) +    DO(S_ARG, "B",          c->r = MIN(c->r + P1(0), s->nline - 1)) +    DO(S_ARG, "C",          c->c = MIN(c->c + P1(0), s->ncol - 1)) +    DO(S_ARG, "D",          c->c = MIN(c->c - P1(0), c->c)) +    DO(S_ARG, "E",          c->c = 0; c->r = MIN(c->r + P1(0), s->nline - 1)) +    DO(S_ARG, "F",          c->c = 0; c->r = MAX(c->r - P1(0), 0)) +    DO(S_ARG, "G",          c->c = MIN(P1(0) - 1, s->ncol - 1)) +    DO(S_ARG, "d",          c->r = MIN(P1(0) - 1, s->nline - 1)) +    DO(S_ARG, "Hf",         c->r = P1(0) - 1; c->c = P1(1) - 1) +    DO(S_ARG, "I",          while (++c->c < s->ncol - 1 && t[c->c].c != L'*')) +    DO(S_ARG, "J",          ed(vt)) +    DO(S_ARG, "K",          el(vt)) +    DO(S_ARG, "L",          scrdn(vt, c->r, P1(0))) +    DO(S_ARG, "M",          scrup(vt, c->r, P1(0))) +    DO(S_ARG, "P",          dch(vt)) +    DO(S_ARG, "S",          scrup(vt, 0, P1(0))) +    DO(S_ARG, "T",          scrdn(vt, 0, P1(0))) +    DO(S_ARG, "X",          clearline(vt, l, c->c, P1(0))) +    DO(S_ARG, "Z",          while (c->c && t[--c->c].c != L'*')) +    DO(S_ARG, "b",          rep(vt)); +    DO(S_ARG, "c",          CB(vt, TMT_MSG_ANSWER, "\033[?6c")) +    DO(S_ARG, "g",          if (P0(0) == 3) clearline(vt, vt->tabs, 0, s->ncol)) +    DO(S_ARG, "m",          sgr(vt)) +    DO(S_ARG, "n",          if (P0(0) == 6) dsr(vt)) +    DO(S_ARG, "h",          if (P0(0) == 25) CB(vt, TMT_MSG_CURSOR, "t")) +    DO(S_ARG, "i",          (void)0) +    DO(S_ARG, "l",          if (P0(0) == 25) CB(vt, TMT_MSG_CURSOR, "f")) +    DO(S_ARG, "s",          vt->oldcurs = vt->curs; vt->oldattrs = vt->attrs) +    DO(S_ARG, "u",          vt->curs = vt->oldcurs; vt->attrs = vt->oldattrs) +    DO(S_ARG, "@",          ich(vt)) + +    return resetparser(vt), false; +} + +static void +notify(TMT *vt, bool update, bool moved) +{ +    if (update) CB(vt, TMT_MSG_UPDATE, &vt->screen); +    if (moved) CB(vt, TMT_MSG_MOVED, &vt->curs); +} + +static TMTLINE * +allocline(TMT *vt, TMTLINE *o, size_t n, size_t pc) +{ +    TMTLINE *l = realloc(o, sizeof(TMTLINE) + n * sizeof(TMTCHAR)); +    if (!l) return NULL; + +    clearline(vt, l, pc, n); +    return l; +} + +static void +freelines(TMT *vt, size_t s, size_t n, bool screen) +{ +    for (size_t i = s; vt->screen.lines && i < s + n; i++){ +        free(vt->screen.lines[i]); +        vt->screen.lines[i] = NULL; +    } +    if (screen) free(vt->screen.lines); +} + +TMT * +tmt_open(size_t nline, size_t ncol, TMTCALLBACK cb, void *p, +         const wchar_t *acs) +{ +    TMT *vt = calloc(1, sizeof(TMT)); +    if (!nline || !ncol || !vt) return free(vt), NULL; + +    /* ASCII-safe defaults for box-drawing characters. */ +    vt->acschars = acs? acs : L"><^v#+:o##+++++~---_++++|<>*!fo"; +    vt->cb = cb; +    vt->p = p; + +    if (!tmt_resize(vt, nline, ncol)) return tmt_close(vt), NULL; +    return vt; +} + +void +tmt_close(TMT *vt) +{ +    free(vt->tabs); +    freelines(vt, 0, vt->screen.nline, true); +    free(vt); +} + +bool +tmt_resize(TMT *vt, size_t nline, size_t ncol) +{ +    if (nline < 2 || ncol < 2) return false; +    if (nline < vt->screen.nline) +        freelines(vt, nline, vt->screen.nline - nline, false); + +    TMTLINE **l = realloc(vt->screen.lines, nline * sizeof(TMTLINE *)); +    if (!l) return false; + +    size_t pc = vt->screen.ncol; +    vt->screen.lines = l; +    vt->screen.ncol = ncol; +    for (size_t i = 0; i < nline; i++){ +        TMTLINE *nl = NULL; +        if (i >= vt->screen.nline) +            nl = vt->screen.lines[i] = allocline(vt, NULL, ncol, 0); +        else +            nl = allocline(vt, vt->screen.lines[i], ncol, pc); + +        if (!nl) return false; +        vt->screen.lines[i] = nl; +    } +    vt->screen.nline = nline; + +    vt->tabs = allocline(vt, vt->tabs, ncol, 0); +    if (!vt->tabs) return free(l), false; +    vt->tabs->chars[0].c = vt->tabs->chars[ncol - 1].c = L'*'; +    for (size_t i = 0; i < ncol; i++) if (i % TAB == 0) +        vt->tabs->chars[i].c = L'*'; + +    fixcursor(vt); +    dirtylines(vt, 0, nline); +    notify(vt, true, true); +    return true; +} + +static void +writecharatcurs(TMT *vt, wchar_t w) +{ +    COMMON_VARS; + +    #ifdef TMT_HAS_WCWIDTH +    extern int wcwidth(wchar_t c); +    if (wcwidth(w) > 1)  w = TMT_INVALID_CHAR; +    if (wcwidth(w) < 0) return; +    #endif + +    CLINE(vt)->chars[vt->curs.c].c = w; +    CLINE(vt)->chars[vt->curs.c].a = vt->attrs; +    CLINE(vt)->dirty = vt->dirty = true; + +    if (c->c < s->ncol - 1) +        c->c++; +    else{ +        c->c = 0; +        c->r++; +    } + +    if (c->r >= s->nline){ +        c->r = s->nline - 1; +        scrup(vt, 0, 1); +    } +} + +static inline size_t +testmbchar(TMT *vt) +{ +    mbstate_t ts = vt->ms; +    return vt->nmb? mbrtowc(NULL, vt->mb, vt->nmb, &ts) : (size_t)-2; +} + +static inline wchar_t +getmbchar(TMT *vt) +{ +    wchar_t c = 0; +    size_t n = mbrtowc(&c, vt->mb, vt->nmb, &vt->ms); +    vt->nmb = 0; +    return (n == (size_t)-1 || n == (size_t)-2)? TMT_INVALID_CHAR : c; +} + +void +tmt_write(TMT *vt, const char *s, size_t n) +{ +    TMTPOINT oc = vt->curs; +    n = n? n : strlen(s); + +    for (size_t p = 0; p < n; p++){ +        if (handlechar(vt, s[p])) +            continue; +        else if (vt->acs) +            writecharatcurs(vt, tacs(vt, (unsigned char)s[p])); +        else if (vt->nmb >= BUF_MAX) +            writecharatcurs(vt, getmbchar(vt)); +        else{ +            switch (testmbchar(vt)){ +                case (size_t)-1: writecharatcurs(vt, getmbchar(vt)); break; +                case (size_t)-2: vt->mb[vt->nmb++] = s[p];           break; +            } + +            if (testmbchar(vt) <= MB_LEN_MAX) +                writecharatcurs(vt, getmbchar(vt)); +        } +    } + +    notify(vt, vt->dirty, memcmp(&oc, &vt->curs, sizeof(oc)) != 0); +} + +const TMTSCREEN * +tmt_screen(const TMT *vt) +{ +    return &vt->screen; +} + +const TMTPOINT * +tmt_cursor(const TMT *vt) +{ +    return &vt->curs; +} + +void +tmt_clean(TMT *vt) +{ +    for (size_t i = 0; i < vt->screen.nline; i++) +        vt->dirty = vt->screen.lines[i]->dirty = false; +} + +void +tmt_reset(TMT *vt) +{ +    vt->curs.r = vt->curs.c = vt->oldcurs.r = vt->oldcurs.c = vt->acs = (bool)0; +    resetparser(vt); +    vt->attrs = vt->oldattrs = defattrs; +    memset(&vt->ms, 0, sizeof(vt->ms)); +    clearlines(vt, 0, vt->screen.nline); +    CB(vt, TMT_MSG_CURSOR, "t"); +    notify(vt, true, true); +} diff --git a/share/hackvr/examples/hackvr_term/libtmt/tmt.h b/share/hackvr/examples/hackvr_term/libtmt/tmt.h new file mode 100644 index 0000000..ae0ddbb --- /dev/null +++ b/share/hackvr/examples/hackvr_term/libtmt/tmt.h @@ -0,0 +1,140 @@ +/* Copyright (c) 2017 Rob King + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *   * Redistributions of source code must retain the above copyright + *     notice, this list of conditions and the following disclaimer. + *   * 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. + *   * Neither the name of the copyright holder nor the + *     names of contributors may be used to endorse or promote products + *     derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER 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 AUTHORS, + * COPYRIGHT HOLDERS, 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. + */ + +#ifndef TMT_H +#define TMT_H + +#include <stdbool.h> +#include <stddef.h> +#include <wchar.h> + +/**** INVALID WIDE CHARACTER */ +#ifndef TMT_INVALID_CHAR +#define TMT_INVALID_CHAR ((wchar_t)0xfffd) +#endif + +/**** INPUT SEQUENCES */ +#define TMT_KEY_UP             "\033[A" +#define TMT_KEY_DOWN           "\033[B" +#define TMT_KEY_RIGHT          "\033[C" +#define TMT_KEY_LEFT           "\033[D" +#define TMT_KEY_HOME           "\033[H" +#define TMT_KEY_END            "\033[Y" +#define TMT_KEY_INSERT         "\033[L" +#define TMT_KEY_BACKSPACE      "\x08" +#define TMT_KEY_ESCAPE         "\x1b" +#define TMT_KEY_BACK_TAB       "\033[Z" +#define TMT_KEY_PAGE_UP        "\033[V" +#define TMT_KEY_PAGE_DOWN      "\033[U" +#define TMT_KEY_F1             "\033OP" +#define TMT_KEY_F2             "\033OQ" +#define TMT_KEY_F3             "\033OR" +#define TMT_KEY_F4             "\033OS" +#define TMT_KEY_F5             "\033OT" +#define TMT_KEY_F6             "\033OU" +#define TMT_KEY_F7             "\033OV" +#define TMT_KEY_F8             "\033OW" +#define TMT_KEY_F9             "\033OX" +#define TMT_KEY_F10            "\033OY" + +/**** BASIC DATA STRUCTURES */ +typedef struct TMT TMT; + +typedef enum{ +    TMT_COLOR_DEFAULT = -1, +    TMT_COLOR_BLACK = 1, +    TMT_COLOR_RED, +    TMT_COLOR_GREEN, +    TMT_COLOR_YELLOW, +    TMT_COLOR_BLUE, +    TMT_COLOR_MAGENTA, +    TMT_COLOR_CYAN, +    TMT_COLOR_WHITE, +    TMT_COLOR_MAX +} tmt_color_t; + +typedef struct TMTATTRS TMTATTRS; +struct TMTATTRS{ +    bool bold; +    bool dim; +    bool underline; +    bool blink; +    bool reverse; +    bool invisible; +    tmt_color_t fg; +    tmt_color_t bg; +}; + +typedef struct TMTCHAR TMTCHAR; +struct TMTCHAR{ +    wchar_t c; +    TMTATTRS a; +}; + +typedef struct TMTPOINT TMTPOINT; +struct TMTPOINT{ +    size_t r; +    size_t c; +}; + +typedef struct TMTLINE TMTLINE; +struct TMTLINE{ +    bool dirty; +    TMTCHAR chars[]; +}; + +typedef struct TMTSCREEN TMTSCREEN; +struct TMTSCREEN{ +    size_t nline; +    size_t ncol; + +    TMTLINE **lines; +}; + +/**** CALLBACK SUPPORT */ +typedef enum{ +    TMT_MSG_MOVED, +    TMT_MSG_UPDATE, +    TMT_MSG_ANSWER, +    TMT_MSG_BELL, +    TMT_MSG_CURSOR +} tmt_msg_t; + +typedef void (*TMTCALLBACK)(tmt_msg_t m, struct TMT *v, const void *r, void *p); + +/**** PUBLIC FUNCTIONS */ +TMT *tmt_open(size_t nline, size_t ncol, TMTCALLBACK cb, void *p, +              const wchar_t *acs); +void tmt_close(TMT *vt); +bool tmt_resize(TMT *vt, size_t nline, size_t ncol); +void tmt_write(TMT *vt, const char *s, size_t n); +const TMTSCREEN *tmt_screen(const TMT *vt); +const TMTPOINT *tmt_cursor(const TMT *vt); +void tmt_clean(TMT *vt); +void tmt_reset(TMT *vt); + +#endif diff --git a/share/hackvr/examples/hackvr_term/pty b/share/hackvr/examples/hackvr_term/ptyBinary files differ new file mode 100755 index 0000000..a1900b1 --- /dev/null +++ b/share/hackvr/examples/hackvr_term/pty diff --git a/share/hackvr/examples/hackvr_term/pty.c b/share/hackvr/examples/hackvr_term/pty.c new file mode 100644 index 0000000..1011e38 --- /dev/null +++ b/share/hackvr/examples/hackvr_term/pty.c @@ -0,0 +1,62 @@ +#include <stdio.h> +#include <fcntl.h> +#include <sys/wait.h> + +int main(int argc,char *argv[]) { +  char *pts; +  char in[256];//I dunno. +  int r; +  int pid; +  int master,slave; +  master=open("/dev/ptmx",O_RDWR); +  if(master == -1) return 1; +  pts=ptsname(master); +//  printf("%s\n",pts); +//  system("ls -l /dev/pts/*"); +  grantpt(master); +  unlockpt(master); +//  system("ls -l /dev/pts/*"); +  if(pts == NULL) return 2; +  slave=open(pts,O_RDWR); +  if(slave == -1) { +   perror("open"); +   return 3; +  } +  argv++; +  fcntl(master,F_SETFL,O_NONBLOCK); +//  fcntl(slave,F_SETFL,O_NONBLOCK); +  fcntl(0,F_SETFL,O_NONBLOCK); +  fcntl(1,F_SETFL,O_NONBLOCK); +  fcntl(2,F_SETFL,O_NONBLOCK); +  switch(pid=fork()) { +    case -1: +      return 4;//fork failed +    case 0://child +      setsid(); +      close(master); +      dup2(slave,0); +      dup2(slave,1); +      dup2(slave,2); +      execv(argv[0],argv);//execute arguments. +      return 5;//exec failed +    default: +      break; +  } +  for(;;) { +    if(waitpid(-1,0,WNOHANG) > 0) { +      return 0;//fuck if I know. +      //we got a dead child. let's bail. +    } +    switch(r=read(0,&in,1)) { +     case 0: return 6;//EOF +     case -1: break;//EAGAIN probably. +     default: write(master,in,r); +    } +    switch(r=read(master,&in,1)) { +     case 0: return 7;//EOF +     case -1: break;//EAGAIN probably +     default: write(1,in,r); +    } +    usleep(100);//kek +  } +} diff --git a/share/hackvr/examples/hackvr_term/run b/share/hackvr/examples/hackvr_term/run new file mode 100755 index 0000000..cf39f77 --- /dev/null +++ b/share/hackvr/examples/hackvr_term/run @@ -0,0 +1,9 @@ +#!/bin/bash +mknod p p +cat <(cat camera.pos | sed 's/USER/'"$USER"'/g') \ +    <(./hackvr_data_decode.sh <p \ +       | python -c 'import pty;pty.spawn("/bin/bash")' \ +       | ./hackvr_term 80 25 \ +       | sed -u 's/addshape -1/addshape 2/g') \ +  | hackvr $USER | tee /dev/stderr > p +rm p diff --git a/share/hackvr/examples/hackvr_term/run.pty b/share/hackvr/examples/hackvr_term/run.pty new file mode 100755 index 0000000..73e24cb --- /dev/null +++ b/share/hackvr/examples/hackvr_term/run.pty @@ -0,0 +1,9 @@ +#!/bin/bash +mknod p p +cat <(cat camera.pos | sed 's/USER/'"$USER"'/g') \ +    <(./hackvr_data_decode.sh <p \ +       | ./pty /bin/bash \ +       | ./hackvr_term 80 25 \ +       | sed -u 's/addshape -1/addshape 2/g') \ +  | hackvr $USER | tee /dev/stderr > p +rm p diff --git a/share/hackvr/examples/hackvrnet/connect.sh b/share/hackvr/examples/hackvrnet/connect.sh new file mode 100755 index 0000000..fe66a1b --- /dev/null +++ b/share/hackvr/examples/hackvrnet/connect.sh @@ -0,0 +1,3 @@ +#!/bin/bash +password=hackvr +ncat 21.41.41.1 31337 -c 'hackvr "$USER"' diff --git a/share/hackvr/examples/starfield b/share/hackvr/examples/starfieldBinary files differ new file mode 100755 index 0000000..b3b4778 --- /dev/null +++ b/share/hackvr/examples/starfield diff --git a/share/hackvr/examples/starfield.c b/share/hackvr/examples/starfield.c new file mode 100644 index 0000000..d367bb3 --- /dev/null +++ b/share/hackvr/examples/starfield.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +int main(int argc,char *argv[]) { + +  int i,x,y,z; +  for(i=0;i<100;i++) { +    x=(rand()%1000)-500; +    y=(rand()%1000)-500; +    z=(rand()%1000)-500; +    printf("star%d addshape %d 1 %d %d %d %d %d %d\n",i,(rand()%7)+17,x,y,z,x+rand()%10,y,z); +  }  +} diff --git a/share/hackvr/examples/tictactoe/board b/share/hackvr/examples/tictactoe/board new file mode 100644 index 0000000..607ebf3 --- /dev/null +++ b/share/hackvr/examples/tictactoe/board @@ -0,0 +1,13 @@ +_reset addshape 2 3 -4 6 4 -5 6 4 -4 5 4 + +topleft addshape 2 4 -3 13 4 -1 13 4 -1 11 4 -3 11 4 +topcenter addshape 2 4 -1 13 4 1 13 4 1 11 4 -1 11 4 +topright addshape 2 4 1 13 4 3 13 4 3 11 4 1 11 4 + +middleleft addshape 2 4 -3 11 4 -1 11 4 -1 9 4 -3 9 4 +middlecenter addshape 2 4 -1 11 4 1 11 4 1 9 4 -1 9 4 +middleright addshape 2 4 1 11 4 3 11 4 3 9 4 1 9 4 + +bottomleft addshape 2 4 -3 9 4 -1 9 4 -1 7 4 -3 7 4 +bottomcenter addshape 2 4 -1 9 4 1 9 4 1 7 4 -1 7 4 +bottomright addshape 2 4 1 9 4 3 9 4 3 7 4 1 7 4 diff --git a/share/hackvr/examples/tictactoe/board_orig b/share/hackvr/examples/tictactoe/board_orig new file mode 100644 index 0000000..2d8a0e5 --- /dev/null +++ b/share/hackvr/examples/tictactoe/board_orig @@ -0,0 +1,13 @@ +_reset addshape 2 3 -4 -4 4 -5 -4 4 -4 -5 4 + +topleft addshape 2 4 -3 3 4 -1 3 4 -1 1 4 -3 1 4 +topcenter addshape 2 4 -1 3 4 1 3 4 1 1 4 -1 1 4 +topright addshape 2 4 1 3 4 3 3 4 3 1 4 1 1 4 + +middleleft addshape 2 4 -3 1 4 -1 1 4 -1 -1 4 -3 -1 4 +middlecenter addshape 2 4 -1 1 4 1 1 4 1 -1 4 -1 -1 4 +middleright addshape 2 4 1 1 4 3 1 4 3 -1 4 1 -1 4 + +bottomleft addshape 2 4 -3 -1 4 -1 -1 4 -1 -3 4 -3 -3 4 +bottomcenter addshape 2 4 -1 -1 4 1 -1 4 1 -3 4 -1 -3 4 +bottomright addshape 2 4 1 -1 4 3 -1 4 3 -3 4 1 -3 4 diff --git a/share/hackvr/examples/tictactoe/camera.pos b/share/hackvr/examples/tictactoe/camera.pos new file mode 100644 index 0000000..ef8e9da --- /dev/null +++ b/share/hackvr/examples/tictactoe/camera.pos @@ -0,0 +1,5 @@ +junk set global.zoom 45 +junk set camera.p.z -6 +junk set camera.p.y -1 +junk set camera.p.x 0 + diff --git a/share/hackvr/examples/tictactoe/game.sh b/share/hackvr/examples/tictactoe/game.sh new file mode 100755 index 0000000..63776f4 --- /dev/null +++ b/share/hackvr/examples/tictactoe/game.sh @@ -0,0 +1,30 @@ +#!/bin/bash +turn=$( expr $RANDOM % 2) +cat camera.pos | sed 's/^junk/$USER/g' +echo "go. player: $turn" >&2 +stdbuf -oL uniq \ +  | grep --line-buffered action \ +  | stdbuf -oL cut '-d ' -f1,3 | while read user group;do +    if grep "_reset" <<<$group >/dev/null;then +     printf "%s deleteallexcept .\n" "$user" +     cat board +     continue +    elif grep "_" <<<$group >/dev/null;then +#     xmessage "CUT IT OUT" +     echo CUT IT OUT > /dev/stderr +     continue +    else +     turn=$(expr \( $turn + 1 \) % 2) +     #printf "# turn: %d\n" "$turn" > /dev/stderr +     printf "$user deletegroup %s\n" "$group" +     #printf "$user deletegroup %s\n" "$group" > /dev/stderr +#need to get the first point of the group clicked and translate the new shape by that much +     translatex="$(grep "$group" board | grep -v '^#' | tr -s ' ' | cut '-d ' -f5)" +     translatey="$(grep "$group" board | grep -v '^#' | tr -s ' ' | cut '-d ' -f6)" +     cat "marker$turn" +     printf "%s move %s %s 2\n" "_marker" "$(expr "$translatex")" "$(expr "$translatey" - 2 )" +     #printf "%s move %s %s 2\n" "_marker" "$(expr "$translatex")" "$(expr "$translatey" - 2 )" >/dev/stderr +     grep "$group" board | sed "s/$group/_marker2/g" #this is to make it still show the square around it. +     printf "%s renamegroup _marker _marker%s%s\n" "$user" "$(date +%s)" "$RANDOM" +    fi +  done diff --git a/share/hackvr/examples/tictactoe/listen.sh b/share/hackvr/examples/tictactoe/listen.sh new file mode 100755 index 0000000..0318cd2 --- /dev/null +++ b/share/hackvr/examples/tictactoe/listen.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +#mabe not. might be buggier. +#cat board p1out | ncat -lp 1050 > p1in & +#cat board p2out | ncat -lp 1051 > p2in & +echo listening in port 1050 for player 1 and 1051 for player 2 + +mknod pin p +mknod p1out p +mknod p2out p + +cat board p1out | ncat -lp 1050 > pin & +cat board p2out | ncat -lp 1051 > pin & + +cat pin | ./game.sh | tee p1out p2out + +rm pin +rm p1out +rm p2out diff --git a/share/hackvr/examples/tictactoe/marker0 b/share/hackvr/examples/tictactoe/marker0 new file mode 100644 index 0000000..5bc0c5c --- /dev/null +++ b/share/hackvr/examples/tictactoe/marker0 @@ -0,0 +1,2 @@ +_marker addshape 1 2 .5 .5 2 1.5 1.5 2 +_marker addshape 1 2 1.5 .5 2 .5 1.5 2 diff --git a/share/hackvr/examples/tictactoe/marker1 b/share/hackvr/examples/tictactoe/marker1 new file mode 100644 index 0000000..e7aa5a7 --- /dev/null +++ b/share/hackvr/examples/tictactoe/marker1 @@ -0,0 +1 @@ +_marker addshape 3 4 1.5 1.5 2 .5 1.5 2 .5 .5 2 1.5 .5 2 diff --git a/share/hackvr/examples/tictactoe/run b/share/hackvr/examples/tictactoe/run new file mode 100755 index 0000000..c60fc43 --- /dev/null +++ b/share/hackvr/examples/tictactoe/run @@ -0,0 +1,6 @@ +#!/bin/bash +turn=$( expr $RANDOM % 2) +echo "go. player: $turn" +mknod p p +stdbuf -oL cat board p | hackvr | ./game.sh > p +rm p diff --git a/share/hackvr/examples/wget-log b/share/hackvr/examples/wget-log new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/share/hackvr/examples/wget-log diff --git a/share/hackvr/examples/wget-log.1 b/share/hackvr/examples/wget-log.1 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/share/hackvr/examples/wget-log.1 diff --git a/share/hackvr/examples/wget-log.2 b/share/hackvr/examples/wget-log.2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/share/hackvr/examples/wget-log.2 diff --git a/share/hackvr/examples/xcmd.sh b/share/hackvr/examples/xcmd.sh new file mode 100755 index 0000000..df6279a --- /dev/null +++ b/share/hackvr/examples/xcmd.sh @@ -0,0 +1,4 @@ +#!/bin/sh +exec $* | xclip -i | xclip -o | xmessage -buttons ok:0,pipe:1 -file - || exit 0 +#ask for command +xclip -o | eval $(zenity --entry) | 
