@@ 3,7 3,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <pthread.h>
+#include <termios.h>
 
 #include "HsFFI.h"
 #include <espeak-ng/espeak_ng.h>
@@ 56,19 56,16 @@ int read_mono(SNDFILE *fd, SF_INFO *info, short *out) {
     return i;
 }
 
-pthread_mutex_t marker_mutex = PTHREAD_MUTEX_INITIALIZER;
-int paragraph_no, section_no;
-int tablerow, tablecol, tableno, in_table;
+int paragraph_no = 0, section_no = 0;
+int tablerow = 0, tablecol = 0, tableno = 0, in_table = 0;
 int capture_marks(short *wav, int numsamples, espeak_EVENT *events) {
     while (events->type != 0) {
         if (events->type == espeakEVENT_MARK) {
-            pthread_mutex_lock(&marker_mutex);
             in_table = 0;
             if (sscanf(events->id.name, "-rhaps-paragraph%i", ¶graph_no) == 1) {}
             else if (sscanf(events->id.name, "-rhaps-section%i", §ion_no) == 1) {}
             else if (sscanf(events->id.name, "-rhaps-tablecell%i:%ix%i",
                     &tableno, &tablerow, &tablecol) == 3) {in_table = 1;}
-            pthread_mutex_unlock(&marker_mutex);
         }
         events++;
     }
@@ 124,13 121,37 @@ void speak(char *ssml) {
     int flags = espeakCHARS_AUTO | espeakPHONEMES | espeakENDPAUSE | espeakCHARS_UTF8 | espeakSSML;
     espeak_Synth(ssml, strlen(ssml)+1, 0, POS_CHARACTER, 0, flags, NULL, NULL);
 }
+int read_keyboard = 0;
 int speak_finalize() {
+    while (read_keyboard) {
+        char c = getc(stdin);
+        if (c == '\033') {
+            espeak_Cancel();
+            c = getc(stdin);
+            if (c == 0 || c == -1 || c == '\033' || c == 'q') goto close; // skip [
+            switch (getc(stdin)) {
+            case 'A':
+                printf("🠕");
+                break;
+            case 'B':
+                printf("🠗");
+                break;
+            case 'C':
+                printf("➔");
+                break;
+            case 'D':
+                printf("🠔");
+                break;
+            }
+        }
+    }
     espeak_ng_STATUS result = espeak_ng_Synchronize();
     if (result != ENS_OK) {
         espeak_ng_PrintStatusCodeMessage(result, stderr, context);
         return 4;
     }
 
+close:
     if (path_wav != NULL) sf_close(fd_wav);
     espeak_ng_Terminate();
     return 0;
@@ 153,6 174,7 @@ FILE *parse_opt_file() {
     return ret;
 }
 
+struct termios stored_settings;
 int main(int argc, char **argv) {
     int speak_err = 0;
     hs_init(&argc, &argv);
@@ 161,10 183,11 @@ int main(int argc, char **argv) {
     FILE *fd_ssml = NULL;
     FILE *fd_links = NULL;
     int use_espeak = 0;
+    tcgetattr(0, &stored_settings);
 
     int c;
     opterr = 0;
-    while ((c = getopt(argc, argv, "xs::l::w::h")) != -1) {
+    while ((c = getopt(argc, argv, "xs::l::kw::h")) != -1) {
         switch (c) {
         case 'x':
             mimes = "text/xml application/xml application/xhtml+xml text/html text/plain";
@@ 175,6 198,16 @@ int main(int argc, char **argv) {
         case 'l':
             fd_links = parse_opt_file();
             break;
+        case 'k':
+            read_keyboard = 1;
+            // Read input character by character, not line by line.
+            struct termios new_settings = stored_settings;
+            new_settings.c_lflag &= (~ICANON);
+            new_settings.c_lflag &= (~ECHO);
+            new_settings.c_cc[VTIME] = 1;
+            new_settings.c_cc[VMIN] = 0;
+            tcsetattr(0, TCSANOW, &new_settings);
+            break;
         case 'w':
             use_espeak = 1;
             path_wav = optarg;
@@ 220,6 253,7 @@ int main(int argc, char **argv) {
     c_freePage(referer);
     c_freeSession(session);
     hs_exit();
+    tcsetattr(0, TCSANOW, &stored_settings);
 
     return speak_err;
 }
 
@@ 5,6 5,7 @@ datalist, template {speak: never}
     counter-reset: -rhaps-table, -rhaps-section, -rhaps-paragraph
 }
 [lang] {-rhaps-lang: attr(lang)} /* Pass this info through styletree into output */
+:target {-rhaps-marker: "main"}
 
 /** Forms **/
 button, select, textarea, input, output {speak: never} /* Leave to special form entry mode */