From bf16b7b8593d4013adbd03838af5a2fb3bce34ee Mon Sep 17 00:00:00 2001
From: epoch <epoch@hacking.allowed.org>
Date: Wed, 6 Feb 2019 00:53:08 -0600
Subject: I forgot to add the examples dir somehow. they're back. tictactoe
 works again too.

---
 examples                                           |   1 +
 share/hackvr/examples/anonet_map.sh                |  12 +
 share/hackvr/examples/chess/board.sh               |  40 ++
 share/hackvr/examples/chess/camera.pos             |   2 +
 share/hackvr/examples/chess/gnuchess-to-hackvr.sh  |  31 +
 share/hackvr/examples/chess/hackvr-to-gnuchess.sh  |  18 +
 share/hackvr/examples/chess/pieces/bishop          |   2 +
 share/hackvr/examples/chess/pieces/king            |   2 +
 share/hackvr/examples/chess/pieces/knight          |   2 +
 share/hackvr/examples/chess/pieces/pawn            |   1 +
 share/hackvr/examples/chess/pieces/queen           |   1 +
 share/hackvr/examples/chess/pieces/rook            |   1 +
 share/hackvr/examples/chess/run                    |   4 +
 share/hackvr/examples/draw/draw.sh                 |  36 ++
 share/hackvr/examples/draw/run                     |   3 +
 share/hackvr/examples/editor/editor.sh             |   6 +
 share/hackvr/examples/fib.sh                       |  12 +
 .../examples/filebrowser/action_to_target.sh       |  11 +
 .../examples/filebrowser/backend-filebrowser.sh    |  18 +
 .../hackvr/examples/filebrowser/backend-gopher.sh  |  22 +
 share/hackvr/examples/filebrowser/backend-ps.sh    |  12 +
 share/hackvr/examples/filebrowser/camera.pos       |   2 +
 .../hackvr/examples/filebrowser/frontend-hackvr.sh |   6 +
 .../hackvr/examples/filebrowser/frontend-zenity.sh |   6 +
 share/hackvr/examples/filebrowser/list_to_cubes.sh |  33 ++
 share/hackvr/examples/filebrowser/run              |   3 +
 share/hackvr/examples/grid_floor.sh                |  10 +
 share/hackvr/examples/hackvr_term/Makefile         |  19 +
 share/hackvr/examples/hackvr_term/camera.pos       |   4 +
 .../examples/hackvr_term/hackvr_data_decode.sh     |   2 +
 share/hackvr/examples/hackvr_term/hackvr_term      | Bin 0 -> 30508 bytes
 share/hackvr/examples/hackvr_term/hackvr_term.c    | 184 ++++++
 .../hackvr/examples/hackvr_term/libtmt/README.rst  | 637 +++++++++++++++++++++
 share/hackvr/examples/hackvr_term/libtmt/tmt.c     | 500 ++++++++++++++++
 share/hackvr/examples/hackvr_term/libtmt/tmt.h     | 140 +++++
 share/hackvr/examples/hackvr_term/pty              | Bin 0 -> 7776 bytes
 share/hackvr/examples/hackvr_term/pty.c            |  62 ++
 share/hackvr/examples/hackvr_term/run              |   9 +
 share/hackvr/examples/hackvr_term/run.pty          |   9 +
 share/hackvr/examples/hackvrnet/connect.sh         |   3 +
 share/hackvr/examples/starfield                    | Bin 0 -> 7296 bytes
 share/hackvr/examples/starfield.c                  |  12 +
 share/hackvr/examples/tictactoe/board              |  13 +
 share/hackvr/examples/tictactoe/board_orig         |  13 +
 share/hackvr/examples/tictactoe/camera.pos         |   5 +
 share/hackvr/examples/tictactoe/game.sh            |  30 +
 share/hackvr/examples/tictactoe/listen.sh          |  19 +
 share/hackvr/examples/tictactoe/marker0            |   2 +
 share/hackvr/examples/tictactoe/marker1            |   1 +
 share/hackvr/examples/tictactoe/run                |   6 +
 share/hackvr/examples/wget-log                     |   0
 share/hackvr/examples/wget-log.1                   |   0
 share/hackvr/examples/wget-log.2                   |   0
 share/hackvr/examples/xcmd.sh                      |   4 +
 54 files changed, 1971 insertions(+)
 create mode 120000 examples
 create mode 100755 share/hackvr/examples/anonet_map.sh
 create mode 100755 share/hackvr/examples/chess/board.sh
 create mode 100644 share/hackvr/examples/chess/camera.pos
 create mode 100755 share/hackvr/examples/chess/gnuchess-to-hackvr.sh
 create mode 100755 share/hackvr/examples/chess/hackvr-to-gnuchess.sh
 create mode 100644 share/hackvr/examples/chess/pieces/bishop
 create mode 100644 share/hackvr/examples/chess/pieces/king
 create mode 100644 share/hackvr/examples/chess/pieces/knight
 create mode 100644 share/hackvr/examples/chess/pieces/pawn
 create mode 100644 share/hackvr/examples/chess/pieces/queen
 create mode 100644 share/hackvr/examples/chess/pieces/rook
 create mode 100755 share/hackvr/examples/chess/run
 create mode 100755 share/hackvr/examples/draw/draw.sh
 create mode 100755 share/hackvr/examples/draw/run
 create mode 100755 share/hackvr/examples/editor/editor.sh
 create mode 100644 share/hackvr/examples/fib.sh
 create mode 100755 share/hackvr/examples/filebrowser/action_to_target.sh
 create mode 100755 share/hackvr/examples/filebrowser/backend-filebrowser.sh
 create mode 100755 share/hackvr/examples/filebrowser/backend-gopher.sh
 create mode 100755 share/hackvr/examples/filebrowser/backend-ps.sh
 create mode 100644 share/hackvr/examples/filebrowser/camera.pos
 create mode 100755 share/hackvr/examples/filebrowser/frontend-hackvr.sh
 create mode 100755 share/hackvr/examples/filebrowser/frontend-zenity.sh
 create mode 100755 share/hackvr/examples/filebrowser/list_to_cubes.sh
 create mode 100755 share/hackvr/examples/filebrowser/run
 create mode 100755 share/hackvr/examples/grid_floor.sh
 create mode 100644 share/hackvr/examples/hackvr_term/Makefile
 create mode 100644 share/hackvr/examples/hackvr_term/camera.pos
 create mode 100755 share/hackvr/examples/hackvr_term/hackvr_data_decode.sh
 create mode 100755 share/hackvr/examples/hackvr_term/hackvr_term
 create mode 100644 share/hackvr/examples/hackvr_term/hackvr_term.c
 create mode 100644 share/hackvr/examples/hackvr_term/libtmt/README.rst
 create mode 100644 share/hackvr/examples/hackvr_term/libtmt/tmt.c
 create mode 100644 share/hackvr/examples/hackvr_term/libtmt/tmt.h
 create mode 100755 share/hackvr/examples/hackvr_term/pty
 create mode 100644 share/hackvr/examples/hackvr_term/pty.c
 create mode 100755 share/hackvr/examples/hackvr_term/run
 create mode 100755 share/hackvr/examples/hackvr_term/run.pty
 create mode 100755 share/hackvr/examples/hackvrnet/connect.sh
 create mode 100755 share/hackvr/examples/starfield
 create mode 100644 share/hackvr/examples/starfield.c
 create mode 100644 share/hackvr/examples/tictactoe/board
 create mode 100644 share/hackvr/examples/tictactoe/board_orig
 create mode 100644 share/hackvr/examples/tictactoe/camera.pos
 create mode 100755 share/hackvr/examples/tictactoe/game.sh
 create mode 100755 share/hackvr/examples/tictactoe/listen.sh
 create mode 100644 share/hackvr/examples/tictactoe/marker0
 create mode 100644 share/hackvr/examples/tictactoe/marker1
 create mode 100755 share/hackvr/examples/tictactoe/run
 create mode 100644 share/hackvr/examples/wget-log
 create mode 100644 share/hackvr/examples/wget-log.1
 create mode 100644 share/hackvr/examples/wget-log.2
 create mode 100755 share/hackvr/examples/xcmd.sh

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_term
new file mode 100755
index 0000000..d2fb6d2
Binary files /dev/null and b/share/hackvr/examples/hackvr_term/hackvr_term differ
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/pty
new file mode 100755
index 0000000..a1900b1
Binary files /dev/null and b/share/hackvr/examples/hackvr_term/pty differ
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/starfield
new file mode 100755
index 0000000..b3b4778
Binary files /dev/null and b/share/hackvr/examples/starfield differ
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
diff --git a/share/hackvr/examples/wget-log.1 b/share/hackvr/examples/wget-log.1
new file mode 100644
index 0000000..e69de29
diff --git a/share/hackvr/examples/wget-log.2 b/share/hackvr/examples/wget-log.2
new file mode 100644
index 0000000..e69de29
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)
-- 
cgit v1.2.3