Initial import of analogterm (phase 2, after forgetting to add)
authorMatthew Mondor <mmondor@pulsar-zone.net>
Thu, 7 Apr 2022 10:24:30 +0000 (10:24 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Thu, 7 Apr 2022 10:24:30 +0000 (10:24 +0000)
14 files changed:
driver/XScreenSaver_ad.h
hacks/Makefile.in
hacks/analogterm-main.c [new file with mode: 0644]
hacks/analogterm.c [new file with mode: 0644]
hacks/analogterm.h [new file with mode: 0644]
hacks/analogtv.c
hacks/images/6x10font.png [new file with mode: 0644]
hacks/images/analogtermfont.png [new file with mode: 0644]
hacks/images/analogtermfont.xbm [new file with mode: 0644]
hacks/images/analogtermgfxfont.png [new file with mode: 0644]
hacks/images/analogtermgfxfont.xbm [new file with mode: 0644]
hacks/images/apple2font.png [new file with mode: 0644]
utils/textclient.c
utils/textclient.h

index 5ca109b..b1eec29 100644 (file)
@@ -13,8 +13,8 @@
 "*dpmsOff:             4:00:00",
 "*grabDesktopImages:   True",
 "*grabVideoFrames:     False",
-"*chooseRandomImages:  True",
-"*imageDirectory:      /Library/Desktop Pictures/",
+"*chooseRandomImages:  False",
+"*imageDirectory:      ",
 "*nice:                        10",
 "*memoryLimit:         0",
 "*lock:                        False",
@@ -54,7 +54,7 @@
 "*installColormap:     True",
 "*programs:                                                                  \
                                maze -root                                  \\n\
-  GL:                          superquadrics -root                         \\n\
+- GL:                          superquadrics -root                         \\n\
                                attraction -root                            \\n\
                                blitspin -root                              \\n\
                                greynetic -root                             \\n\
                                goop -root                                  \\n\
                                grav -root                                  \\n\
                                ifs -root                                   \\n\
-  GL:                          jigsaw -root                                \\n\
+- GL:                          jigsaw -root                                \\n\
                                julia -root                                 \\n\
 -                              kaleidescope -root                          \\n\
-  GL:                          moebius -root                               \\n\
+- GL:                          moebius -root                               \\n\
                                moire -root                                 \\n\
-  GL:                          morph3d -root                               \\n\
+- GL:                          morph3d -root                               \\n\
                                mountain -root                              \\n\
                                munch -root                                 \\n\
                                penrose -root                               \\n\
-  GL:                          pipes -root                                 \\n\
+- GL:                          pipes -root                                 \\n\
                                rd-bomb -root                               \\n\
-  GL:                          rubik -root                                 \\n\
+- GL:                          rubik -root                                 \\n\
 -                              sierpinski -root                            \\n\
                                slip -root                                  \\n\
-  GL:                          sproingies -root                            \\n\
+- GL:                          sproingies -root                            \\n\
                                starfish -root                              \\n\
                                strange -root                               \\n\
                                swirl -root                                 \\n\
                                triangle -root                              \\n\
                                xjack -root                                 \\n\
                                xlyap -root                                 \\n\
-  GL:                          atlantis -root                              \\n\
+- GL:                          atlantis -root                              \\n\
                                bsod -root                                  \\n\
-  GL:                          bubble3d -root                              \\n\
-  GL:                          cage -root                                  \\n\
+- GL:                          bubble3d -root                              \\n\
+- GL:                          cage -root                                  \\n\
 -                              crystal -root                               \\n\
                                cynosure -root                              \\n\
                                discrete -root                              \\n\
                                distort -root                               \\n\
                                epicycle -root                              \\n\
                                flow -root                                  \\n\
-  GL:                          glplanet -root                              \\n\
+- GL:                          glplanet -root                              \\n\
                                interference -root                          \\n\
                                kumppa -root                                \\n\
-  GL:                          lament -root                                \\n\
+- GL:                          lament -root                                \\n\
                                moire2 -root                                \\n\
-  GL:                          sonar -root                                 \\n\
-  GL:                          stairs -root                                \\n\
+- GL:                          sonar -root                                 \\n\
+- GL:                          stairs -root                                \\n\
                                truchet -root                               \\n\
 -                              vidwhacker -root                            \\n\
                                blaster -root                               \\n\
                                compass -root                               \\n\
                                deluxe -root                                \\n\
 -                              demon -root                                 \\n\
-  GL:                          extrusion -root                             \\n\
+- GL:                          extrusion -root                             \\n\
 -                              loop -root                                  \\n\
                                penetrate -root                             \\n\
                                petri -root                                 \\n\
                                phosphor -root                              \\n\
-  GL:                          pulsar -root                                \\n\
+- GL:                          pulsar -root                                \\n\
                                ripples -root                               \\n\
                                shadebobs -root                             \\n\
-  GL:                          sierpinski3d -root                          \\n\
+- GL:                          sierpinski3d -root                          \\n\
                                spotlight -root                             \\n\
                                squiral -root                               \\n\
                                wander -root                                \\n\
 -                              webcollage -root                            \\n\
                                xflame -root                                \\n\
                                xmatrix -root                               \\n\
-  GL:                          gflux -root                                 \\n\
+- GL:                          gflux -root                                 \\n\
 -                              nerverot -root                              \\n\
                                xrayswarm -root                             \\n\
                                xspirograph -root                           \\n\
-  GL:                          circuit -root                               \\n\
-  GL:                          dangerball -root                            \\n\
+- GL:                          circuit -root                               \\n\
+- GL:                          dangerball -root                            \\n\
 - GL:                          dnalogo -root                               \\n\
-  GL:                          engine -root                                \\n\
-  GL:                          flipscreen3d -root                          \\n\
-  GL:                          gltext -root                                \\n\
-  GL:                          menger -root                                \\n\
-  GL:                          molecule -root                              \\n\
+- GL:                          engine -root                                \\n\
+- GL:                          flipscreen3d -root                          \\n\
+- GL:                          gltext -root                                \\n\
+- GL:                          menger -root                                \\n\
+- GL:                          molecule -root                              \\n\
                                rotzoomer -root                             \\n\
                                speedmine -root                             \\n\
-  GL:                          starwars -root                              \\n\
-  GL:                          stonerview -root                            \\n\
+- GL:                          starwars -root                              \\n\
+- GL:                          stonerview -root                            \\n\
                                vermiculate -root                           \\n\
                                whirlwindwarp -root                         \\n\
                                zoom -root                                  \\n\
                                anemone -root                               \\n\
                                apollonian -root                            \\n\
-  GL:                          boxed -root                                 \\n\
-  GL:                          cubenetic -root                             \\n\
-  GL:                          endgame -root                               \\n\
+- GL:                          boxed -root                                 \\n\
+- GL:                          cubenetic -root                             \\n\
+- GL:                          endgame -root                               \\n\
                                euler2d -root                               \\n\
                                fluidballs -root                            \\n\
-  GL:                          flurry -root                                \\n\
+- GL:                          flurry -root                                \\n\
 - GL:                          glblur -root                                \\n\
-  GL:                          glsnake -root                               \\n\
+- GL:                          glsnake -root                               \\n\
                                halftone -root                              \\n\
-  GL:                          juggler3d -root                             \\n\
-  GL:                          lavalite -root                              \\n\
+- GL:                          juggler3d -root                             \\n\
+- GL:                          lavalite -root                              \\n\
 -                              polyominoes -root                           \\n\
-  GL:                          queens -root                                \\n\
+- GL:                          queens -root                                \\n\
 - GL:                          sballs -root                                \\n\
-  GL:                          spheremonics -root                          \\n\
+- GL:                          spheremonics -root                          \\n\
 -                              thornbird -root                             \\n\
                                twang -root                                 \\n\
 - GL:                          antspotlight -root                          \\n\
                                apple2 -root                                \\n\
-  GL:                          atunnel -root                               \\n\
+- GL:                          atunnel -root                               \\n\
                                barcode -root                               \\n\
-  GL:                          blinkbox -root                              \\n\
-  GL:                          blocktube -root                             \\n\
-  GL:                          bouncingcow -root                           \\n\
+- GL:                          blinkbox -root                              \\n\
+- GL:                          blocktube -root                             \\n\
+- GL:                          bouncingcow -root                           \\n\
                                cloudlife -root                             \\n\
-  GL:                          cubestorm -root                             \\n\
+- GL:                          cubestorm -root                             \\n\
                                eruption -root                              \\n\
-  GL:                          flipflop -root                              \\n\
-  GL:                          flyingtoasters -root                        \\n\
+- GL:                          flipflop -root                              \\n\
+- GL:                          flyingtoasters -root                        \\n\
                                fontglide -root                             \\n\
-  GL:                          gleidescope -root                           \\n\
-  GL:                          glknots -root                               \\n\
-  GL:                          glmatrix -root                              \\n\
+- GL:                          gleidescope -root                           \\n\
+- GL:                          glknots -root                               \\n\
+- GL:                          glmatrix -root                              \\n\
 - GL:                          glslideshow -root                           \\n\
-  GL:                          hypertorus -root                            \\n\
+- GL:                          hypertorus -root                            \\n\
 - GL:                          jigglypuff -root                            \\n\
                                metaballs -root                             \\n\
-  GL:                          mirrorblob -root                            \\n\
+- GL:                          mirrorblob -root                            \\n\
                                piecewise -root                             \\n\
-  GL:                          polytopes -root                             \\n\
+- GL:                          polytopes -root                             \\n\
                                pong -root                                  \\n\
                                popsquares -root                            \\n\
-  GL:                          surfaces -root                              \\n\
+- GL:                          surfaces -root                              \\n\
                                xanalogtv -root                             \\n\
                                abstractile -root                           \\n\
                                anemotaxis -root                            \\n\
                                interaggregate -root                        \\n\
                                intermomentary -root                        \\n\
                                memscroller -root                           \\n\
-  GL:                          noof -root                                  \\n\
+- GL:                          noof -root                                  \\n\
                                pacman -root                                \\n\
-  GL:                          pinion -root                                \\n\
-  GL:                          polyhedra -root                             \\n\
+- GL:                          pinion -root                                \\n\
+- GL:                          polyhedra -root                             \\n\
 - GL:                          providence -root                            \\n\
                                substrate -root                             \\n\
                                wormhole -root                              \\n\
 - GL:                          antmaze -root                               \\n\
-  GL:                          boing -root                                 \\n\
+- GL:                          boing -root                                 \\n\
                                boxfit -root                                \\n\
-  GL:                          carousel -root                              \\n\
+- GL:                          carousel -root                              \\n\
                                celtic -root                                \\n\
-  GL:                          crackberg -root                             \\n\
-  GL:                          cube21 -root                                \\n\
+- GL:                          crackberg -root                             \\n\
+- GL:                          cube21 -root                                \\n\
                                fiberlamp -root                             \\n\
-  GL:                          fliptext -root                              \\n\
-  GL:                          glhanoi -root                               \\n\
-  GL:                          tangram -root                               \\n\
-  GL:                          timetunnel -root                            \\n\
-  GL:                          glschool -root                              \\n\
-  GL:                          topblock -root                              \\n\
-  GL:                          cubicgrid -root                             \\n\
+- GL:                          fliptext -root                              \\n\
+- GL:                          glhanoi -root                               \\n\
+- GL:                          tangram -root                               \\n\
+- GL:                          timetunnel -root                            \\n\
+- GL:                          glschool -root                              \\n\
+- GL:                          topblock -root                              \\n\
+- GL:                          cubicgrid -root                             \\n\
                                cwaves -root                                \\n\
-  GL:                          gears -root                                 \\n\
-  GL:                          glcells -root                               \\n\
-  GL:                          lockward -root                              \\n\
+- GL:                          gears -root                                 \\n\
+- GL:                          glcells -root                               \\n\
+- GL:                          lockward -root                              \\n\
                                m6502 -root                                 \\n\
-  GL:                          moebiusgears -root                          \\n\
-  GL:                          voronoi -root                               \\n\
-  GL:                          hypnowheel -root                            \\n\
-  GL:                          klein -root                                 \\n\
+- GL:                          moebiusgears -root                          \\n\
+- GL:                          voronoi -root                               \\n\
+- GL:                          hypnowheel -root                            \\n\
+- GL:                          klein -root                                 \\n\
 -                              lcdscrub -root                              \\n\
-  GL:                          photopile -root                             \\n\
-  GL:                          skytentacles -root                          \\n\
-  GL:                          rubikblocks -root                           \\n\
-  GL:                          companioncube -root                         \\n\
-  GL:                          hilbert -root                               \\n\
-  GL:                          tronbit -root                               \\n\
-  GL:                          geodesic -root                              \\n\
+- GL:                          photopile -root                             \\n\
+- GL:                          skytentacles -root                          \\n\
+- GL:                          rubikblocks -root                           \\n\
+- GL:                          companioncube -root                         \\n\
+- GL:                          hilbert -root                               \\n\
+- GL:                          tronbit -root                               \\n\
+- GL:                          geodesic -root                              \\n\
                                hexadrop -root                              \\n\
-  GL:                          kaleidocycle -root                          \\n\
-  GL:                          quasicrystal -root                          \\n\
-  GL:                          unknownpleasures -root                      \\n\
+- GL:                          kaleidocycle -root                          \\n\
+- GL:                          quasicrystal -root                          \\n\
+- GL:                          unknownpleasures -root                      \\n\
                                binaryring -root                            \\n\
-  GL:                          cityflow -root                              \\n\
-  GL:                          geodesicgears -root                         \\n\
-  GL:                          projectiveplane -root                       \\n\
-  GL:                          romanboy -root                              \\n\
+- GL:                          cityflow -root                              \\n\
+- GL:                          geodesicgears -root                         \\n\
+- GL:                          projectiveplane -root                       \\n\
+- GL:                          romanboy -root                              \\n\
                                tessellimage -root                          \\n\
-  GL:                          winduprobot -root                           \\n\
-  GL:                          splitflap -root                             \\n\
-  GL:                          dymaxionmap -root                           \\n\
-  GL:                          energystream -root                          \\n\
-  GL:                          hydrostat -root                             \\n\
-  GL:                          raverhoop -root                             \\n\
-  GL:                          unicrud -root                               \\n",
+- GL:                          winduprobot -root                           \\n\
+- GL:                          splitflap -root                             \\n\
+- GL:                          dymaxionmap -root                           \\n\
+- GL:                          energystream -root                          \\n\
+- GL:                          hydrostat -root                             \\n\
+- GL:                          raverhoop -root                             \\n\
+- GL:                          unicrud -root                               \\n",
 "XScreenSaver.pointerPollTime:         0:00:05",
 "XScreenSaver.pointerHysteresis:               10",
 "XScreenSaver.initialDelay:            0:00:00",
index ccc0b61..eaf56ec 100644 (file)
@@ -123,6 +123,7 @@ SRCS                = attraction.c blitspin.c bouboule.c braid.c bubbles.c \
                  asm6502.c abstractile.c lcdscrub.c hexadrop.c \
                  tessellimage.c delaunay.c recanim.c binaryring.c \
                  webcollage-cocoa.m webcollage-helper-cocoa.m testx11.c
+SRCS           = apple2.c apple2-main.c analogtv.c analogterm.c analogterm-main.c
 SCRIPTS                = vidwhacker webcollage ljlatest
 
 # Programs that are mentioned in XScreenSaver.ad, and that have XML files,
@@ -163,6 +164,7 @@ OBJS                = attraction.o blitspin.o bouboule.o braid.o bubbles.o \
                  webcollage-helper-cocoa.o m6502.o asm6502.o abstractile.o \
                  lcdscrub.o hexadrop.o tessellimage.o delaunay.o recanim.o \
                  binaryring.o testx11.o
+OBJS           = apple2.o apple2-main.o analogtv.o analogterm.o
 
 EXES           = attraction blitspin bouboule braid decayscreen deco \
                  drift flame galaxy grav greynetic halo \
@@ -186,6 +188,7 @@ EXES                = attraction blitspin bouboule braid decayscreen deco \
                  celtic cwaves m6502 abstractile lcdscrub hexadrop \
                  tessellimage binaryring \
                  @JPEG_EXES@
+EXES           = apple2 analogterm
 JPEG_EXES      = webcollage-helper
 
 RETIRED_EXES   = ant bubbles critical flag forest hyperball hypercube laser \
@@ -208,7 +211,7 @@ THREAD_OBJS = $(UTILS_BIN)/aligned_malloc.o $(UTILS_BIN)/thread_util.o
 HDRS           = screenhack.h screenhackI.h fps.h fpsI.h xlockmore.h \
                  xlockmoreI.h automata.h bubbles.h xpm-pixmap.h \
                  apple2.h analogtv.h pacman.h pacman_ai.h pacman_level.h \
-                 asm6502.h delaunay.h recanim.h
+                 asm6502.h delaunay.h recanim.h analogterm.h
 MEN            = anemone.man apollonian.man attraction.man \
                  blaster.man blitspin.man bouboule.man braid.man bsod.man \
                  bumps.man ccurve.man compass.man coral.man \
@@ -561,6 +564,7 @@ THRO                = $(THREAD_OBJS)
 THRL           = $(THREAD_CFLAGS) $(THREAD_LIBS)
 ATV            = analogtv.o $(SHM) $(THRO)
 APPLE2          = apple2.o $(ATV)
+ANALOGTERM      = analogterm.o $(ATV)
 TEXT            = $(UTILS_BIN)/textclient.o
 
 CC_HACK                = $(CC) $(LDFLAGS)
@@ -690,6 +694,9 @@ bsod:               bsod.o          $(HACK_OBJS) $(GRAB) $(APPLE2) $(XPM)
 apple2:                apple2.o apple2-main.o  $(HACK_OBJS) $(ATV) $(GRAB) $(TEXT)
        $(CC_HACK) -o $@ $@.o   apple2-main.o $(HACK_OBJS) $(ATV) $(GRAB) $(TEXT) $(XPM_LIBS) $(TEXT_LIBS) $(THRL)
 
+analogterm:    analogterm.o analogterm-main.o  $(HACK_OBJS) $(ATV) $(GRAB) $(TEXT)
+       $(CC_HACK) -o $@ $@.o   analogterm-main.o $(HACK_OBJS) $(ATV) $(GRAB) $(TEXT) $(XPM_LIBS) $(TEXT_LIBS) $(THRL)
+
 xanalogtv:     xanalogtv.o     $(HACK_OBJS) $(ATV) $(GRAB) $(XPM)
        $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(ATV) $(GRAB) $(XPM) $(XPM_LIBS) $(HACK_LIBS) $(THRL)
 
@@ -1121,6 +1128,40 @@ apple2.o: $(UTILS_SRC)/usleep.h
 apple2.o: $(UTILS_SRC)/visual.h
 apple2.o: $(UTILS_SRC)/xshm.h
 apple2.o: $(UTILS_SRC)/yarandom.h
+analogterm-main.o: $(srcdir)/analogtv.h
+analogterm-main.o: $(srcdir)/apple2.h
+analogterm-main.o: ../config.h
+analogterm-main.o: $(srcdir)/fps.h
+analogterm-main.o: $(srcdir)/screenhackI.h
+analogterm-main.o: $(srcdir)/screenhack.h
+analogterm-main.o: $(UTILS_SRC)/aligned_malloc.h
+analogterm-main.o: $(UTILS_SRC)/colors.h
+analogterm-main.o: $(UTILS_SRC)/grabscreen.h
+analogterm-main.o: $(UTILS_SRC)/hsv.h
+analogterm-main.o: $(UTILS_SRC)/resources.h
+analogterm-main.o: $(UTILS_SRC)/textclient.h
+analogterm-main.o: $(UTILS_SRC)/thread_util.h
+analogterm-main.o: $(UTILS_SRC)/usleep.h
+analogterm-main.o: $(UTILS_SRC)/utf8wc.h
+analogterm-main.o: $(UTILS_SRC)/visual.h
+analogterm-main.o: $(UTILS_SRC)/xshm.h
+analogterm-main.o: $(UTILS_SRC)/yarandom.h
+analogterm.o: $(srcdir)/analogtv.h
+analogterm.o: $(srcdir)/analogterm.h
+analogterm.o: ../config.h
+analogterm.o: $(srcdir)/fps.h
+analogterm.o: $(srcdir)/images/apple2font.xbm
+analogterm.o: $(srcdir)/screenhackI.h
+analogterm.o: $(UTILS_SRC)/aligned_malloc.h
+analogterm.o: $(UTILS_SRC)/colors.h
+analogterm.o: $(UTILS_SRC)/grabscreen.h
+analogterm.o: $(UTILS_SRC)/hsv.h
+analogterm.o: $(UTILS_SRC)/resources.h
+analogterm.o: $(UTILS_SRC)/thread_util.h
+analogterm.o: $(UTILS_SRC)/usleep.h
+analogterm.o: $(UTILS_SRC)/visual.h
+analogterm.o: $(UTILS_SRC)/xshm.h
+analogterm.o: $(UTILS_SRC)/yarandom.h
 asm6502.o: $(srcdir)/asm6502.h
 asm6502.o: ../config.h
 asm6502.o: $(UTILS_SRC)/yarandom.h
diff --git a/hacks/analogterm-main.c b/hacks/analogterm-main.c
new file mode 100644 (file)
index 0000000..9fee557
--- /dev/null
@@ -0,0 +1,1141 @@
+/* xscreensaver, Copyright (c) 1998-2014 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org>
+ * with additional work by Jamie Zawinski <jwz@jwz.org>
+ * Pty and vt100 emulation by Fredrik Tolf <fredrik@dolda2000.com>
+ *
+ * 2022:
+ * Fixed and improved for more complete xterm emulation by Matthew Mondor:
+ * - 80 columns by 25 lines, lowercase, underline support.
+ * - Scrolling regions, insert/delete lines and characters, vt100 graphics
+ *   alternate character set, report current cursor position, etc.
+ * - A more complete font with sans-serif customizations.
+ * - Sends various PC keys like pgup/pgdn, home/end, arrows.
+ * - Can now run common text editors (vim, nvi, elvis, joe, jed, emacs, nano,
+ *   wordgrinder, etc.), vifm, sc, more/less, tmux/screen, irssi, top,
+ *   systat, lynx, links, hack, angband, matanza, ninvaders, cmatrix...
+ *   For vim :set t_Co=0 is recommended for bold/underline/reverse modes
+ *   instead of colors, more suitable for monochrome.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <math.h>
+#include <ctype.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "screenhack.h"
+#include "analogterm.h"
+#include "textclient.h"
+#include "utf8wc.h"
+
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#define SCREEN_COLS 80
+#define SCREEN_ROWS 25
+
+\f
+
+/* XXX Unused */
+static const char *analogterm_defaults [] = {
+  ".background:                   black",
+  ".foreground:                   white",
+  "*duration:             60",
+  "*program:              xscreensaver-text --cols 80",
+  "*metaSendsESC:         True",
+  "*swapBSDEL:            True",
+  "*fast:                 True",
+# ifdef HAVE_FORKPTY
+  "*usePty:                True",
+#else
+  "*usePty:                False",
+# endif /* !HAVE_FORKPTY */
+
+  ANALOGTV_DEFAULTS
+  0
+};
+
+static XrmOptionDescRec analogterm_options [] = {
+  { "-program",                ".program",             XrmoptionSepArg, 0 },
+  { "-duration",       ".duration",            XrmoptionSepArg, 0 },
+  { "-pty",            ".usePty",              XrmoptionNoArg,  "True"  },
+  { "-pipe",           ".usePty",              XrmoptionNoArg,  "False" },
+  { "-meta",           ".metaSendsESC",        XrmoptionNoArg,  "False" },
+  { "-esc",            ".metaSendsESC",        XrmoptionNoArg,  "True"  },
+  { "-bs",             ".swapBSDEL",           XrmoptionNoArg,  "False" },
+  { "-del",            ".swapBSDEL",           XrmoptionNoArg,  "True"  },
+  { "-fast",           ".fast",                XrmoptionNoArg,  "True"  },
+  ANALOGTV_OPTIONS
+  { 0, 0, 0, 0 }
+};
+
+/*
+  TODO: this should load 10 images at startup time, then cycle through them
+  to avoid the pause while it loads.
+ */
+
+#define NPAR 16
+
+struct terminal_controller_data {
+  Display *dpy;
+  char curword[256];
+  unsigned char lastc;
+  double last_emit_time;
+  text_data *tc;
+
+  int escstate;
+  int csiparam[NPAR];
+  int curparam;
+  int cursor_x, cursor_y;
+  int saved_x,  saved_y;
+  int unicruds; char unicrud[7];
+  unsigned char mode;
+  Bool fast_p;
+};
+
+
+/* The structure of closure linkage throughout this code is so amazingly
+   baroque that I can't get to the 'struct state' from where I need it. */
+static const char *global_program;
+static Bool global_fast_p;
+
+
+static void
+terminal_closegen(struct terminal_controller_data *mine)
+{
+  if (mine->tc) {
+    textclient_close (mine->tc);
+    mine->tc = 0;
+  }
+}
+
+static int
+terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
+{
+  if (!mine || !mine->tc) {
+    return 0;
+  } else {
+    int i, count = 0;
+    for (i = 0; i < n; i++) {
+      int c = textclient_getc (mine->tc);
+      if (c <= 0) break;
+      buf[i] = c;
+      mine->lastc = c;
+      count++;
+    }
+    return count;
+  }
+}
+
+
+static int
+terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
+{
+  struct terminal_controller_data *mine =
+    (struct terminal_controller_data *) data;
+  mine->dpy = dpy;
+  if (event->xany.type == KeyPress && mine->tc) {
+    return textclient_putc (mine->tc, &event->xkey);
+  }
+  return 0;
+}
+
+static void
+unsupported(struct terminal_controller_data *state, int statei, char statec,
+    char function)
+{
+
+         (void)fprintf(stderr, "Unsupported: state=%d '%c' %d, fun='%c' %d",
+               statei, statec, statec, function, function);
+         if (state->curparam != -1) {
+             int i;
+
+             for (i = 0; i <= state->curparam; i++)
+                 (void)fprintf(stderr, ", p%d = %d", i + 1,
+                     state->csiparam[i]);
+         }
+         (void)fprintf(stderr, ".\n");
+}
+
+static void
+at_ascii_printc (analogterm_state_t *st, unsigned char c, unsigned char m,
+                Bool scroll_p)
+{
+
+  if (scroll_p)
+    at_printc(st, c, m);
+  else
+    at_printc_noscroll(st, c, m);
+}
+
+
+/*
+ * XXX xterm is a more functional terminal for modern unix systems based on
+ * it.  Make sure that sequences correspond.
+ * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * Also useful is this minimal vt100 functionality list:
+ * https://raw.githubusercontent.com/cronvel/terminal-kit/master/ext-doc/xterm-control-sequences-doc/ansicode.txt
+ * XXX This uses a duplicate state which is unnecessary.  The goto at the end
+ * applies the internal state to the actual one.
+ * XXX xtermm, the monochrome xterm termcap/terminfo entry, is incomplete and
+ * apparently no longer supported and historical.  While it allows
+ * applications like tmux to work better, it removes DEC Special Graphics
+ * support, underline and even important keys like PgUp/PgDn, ctrl/shift with
+ * arrows, etc.  For this reason analogterm implements pseudocolor, using
+ * inverse text when a non-default background color is selected and dim text
+ * when a non-default foreground color is enabled.  This has some drawbacks
+ * like dialog(1) looking strange on Linux (it seems better on NetBSD) but it
+ * allows things like vim highlighting to partially work by default even
+ * without t_Co=0 and visible tmux copy-paste text selection, decent status
+ * bars for tmux/screen, irssi, etc.  And even when it looks a bit strange,
+ * all text is always readable (even if the selected foreground and background
+ * colors were the same). XXX Revise, it causes trouble in sc.
+ */
+static void
+at_vt100_printc (analogterm_sim_t *sim, struct terminal_controller_data *state,
+                 unsigned char c)
+{
+  analogterm_state_t *st=sim->st;
+  /* XXX Why not simply use constants that allow more efficient literal math?
+   * Presumably for an eventual dynamic window size or other terminal modes
+   * like 132 columns...
+   */
+  int cols = SCREEN_COLS;
+  int rows = SCREEN_ROWS;
+
+  int i, many;
+  int start, end;
+
+  /* Mostly duplicated in phosphor.c */
+
+  switch (state->escstate)
+    {
+    case 0:
+      switch (c)
+        {
+        case 7: /* BEL */
+          /* Dummy case - we don't want the screensaver to beep */
+          /* #### But maybe this should flash the screen? */
+         /* XXX Unimplemented bell */
+          break;
+        case 8: /* BS */
+          if (state->cursor_x > 0)
+            state->cursor_x--;
+          break;
+        case 9: /* HT XXX May be bogus */
+          if (state->cursor_x < cols - 8)
+            {
+              state->cursor_x = (state->cursor_x & ~7) + 8;
+            }
+          else
+            {
+              state->cursor_x = 0;
+              if (state->cursor_y < st->scroll_bottom)
+                state->cursor_y++;
+              else
+                at_scroll (st);
+            }
+          break;
+        case 10: /* LF */ /* FALLTHROUGH */
+        case 11: /* VT */ /* FALLTHROUGH */
+        case 12: /* FF */
+          if (state->cursor_y < st->scroll_bottom)
+            state->cursor_y++;
+          else
+            at_scroll (st);
+          break;
+        case 13: /* CR */
+          state->cursor_x = 0;
+          break;
+        case 14: /* SO */
+        case 15: /* SI */
+          /* Dummy case - there is one and only one font. */
+          break;
+        case 24: /* CAN */
+        case 26: /* SUB */
+          /* Dummy case - these interrupt escape sequences, so
+             they don't do anything in this state */
+          break;
+        case 27: /* ESC */
+          state->escstate = 1;
+          break;
+        case 127: /* DEL */
+          /* Dummy case - this is supposed to be ignored */
+         /* XXX This is the application output, not user input. */
+          break;
+        case 155: /* CSI, reset params state */
+          state->escstate = 2;
+          for(i = 0; i < NPAR; i++)
+            state->csiparam[i] = 0;
+          state->curparam = -1;
+          break;
+        default:
+
+          /* states 102-106 are for UTF-8 decoding */
+
+          if ((c & 0xE0) == 0xC0) {        /* 110xxxxx - 11 bits, 2 bytes */
+            state->unicruds = 1;
+            state->unicrud[0] = c;
+            state->escstate = 102;
+            break;
+          } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx - 16 bits, 3 bytes */
+            state->unicruds = 1;
+            state->unicrud[0] = c;
+            state->escstate = 103;
+            break;
+          } else if ((c & 0xF8) == 0xF0) { /* 11110xxx - 21 bits, 4 bytes */
+            state->unicruds = 1;
+            state->unicrud[0] = c;
+            state->escstate = 104;
+            break;
+          } else if ((c & 0xFC) == 0xF8) { /* 111110xx - 26 bits, 5 bytes */
+            state->unicruds = 1;
+            state->unicrud[0] = c;
+            state->escstate = 105;
+            break;
+          } else if ((c & 0xFE) == 0xFC) { /* 1111110x - 31 bits, 6 bytes */
+            state->unicruds = 1;
+            state->unicrud[0] = c;
+            state->escstate = 106;
+            break;
+          }
+
+        PRINT:
+
+          /* If the cursor is in column 79 and we print a character, then
+             that character shows up in column 79, and the cursor is no longer
+             visible on the screen (it's in "column 80".)  If another character
+             is printed, then that character shows up in column 0, and the
+             cursor moves to column 1.
+
+             This is empirically what xterm and gnome-terminal do, so that must
+             be the right thing.  (In xterm, the cursor vanishes, whereas; in
+             gnome-terminal, the cursor overprints the character in col 79.)
+           */
+          if (state->cursor_x >= cols)
+            {
+              state->cursor_x = 0;
+              if (state->cursor_y >= st->scroll_bottom)
+                at_scroll (st);
+              else
+                state->cursor_y++;
+            }
+
+          at_goto(st, state->cursor_y, state->cursor_x);  /* clips range */
+          at_ascii_printc (st, c, state->mode, False);
+          state->cursor_x++;
+
+          break;
+        }
+      break;
+
+    case 1:
+      switch (c)
+        {
+        case 24: /* CAN */
+        case 26: /* SUB */
+          state->escstate = 0;
+          break;
+        case 'c': /* Reset */
+         /* XXX Make sure we reset everything */
+          at_cls(st);
+         /* 0-based */
+         st->scroll_top = 0;
+         st->scroll_bottom = rows - 1;
+         state->mode = 0;
+          state->escstate = 0;
+          break;
+        case 'D': /* Linefeed */
+          if (state->cursor_y < st->scroll_bottom)
+            state->cursor_y++;
+          else
+            at_scroll (st);
+          state->escstate = 0;
+          break;
+        case 'E': /* Newline */
+          state->cursor_x = 0;
+          state->escstate = 0;
+          break;
+        case 'M': /* Reverse newline, scroll reverse (ri) */
+         /* Scroll back one line when fully at top, used by more/less.
+          * A bit messy as it updates the local state instead of the terminal
+          * one.  Later on real state is updated when escstate is reset to 0.
+          * Takes scrolling range in consideration.
+          */
+          if (state->cursor_y > st->scroll_top)
+            state->cursor_y--;
+         else
+           at_scroll_range(st, 1);
+          state->escstate = 0;
+          break;
+        case '7': /* Save cursor state (sc) */
+          state->saved_x = state->cursor_x;
+          state->saved_y = state->cursor_y;
+          state->escstate = 0;
+          break;
+        case '8': /* Restore cursor state (rc) */
+          state->cursor_x = state->saved_x;
+          state->cursor_y = state->saved_y;
+          state->escstate = 0;
+          break;
+        case '[': /* CSI, reset params and switch to state 2 */
+          state->escstate = 2;
+          for(i = 0; i < NPAR; i++)
+            state->csiparam[i] = 0;
+          state->curparam = -1;
+          break;
+        case '%': /* Select charset */
+          /* @: Select default (ISO 646 / ISO 8859-1)
+             G: Select UTF-8
+             8: Select UTF-8 (obsolete)
+
+             We can just ignore this and always process UTF-8, I think?
+             We must still catch the last byte, though.
+           */
+        case '(':
+/*      case ')': XXX Links uses )0 without apparent exit */
+          /* I don't support different fonts either - see above
+             for SO and SI */
+          state->escstate = 3;
+          break;
+        default:
+          /* XXX Escape sequences not supported:
+           * 
+           * H - Set tab stop
+           * Z - Terminal identification
+           * > - Keypad change
+           * = - Other keypad change
+           * ] - OS command
+           */
+         unsupported(state, 1, '\0', c);
+          state->escstate = 0;
+          break;
+        }
+      break;
+    case 2:
+      switch (c)
+        {
+        case 24: /* CAN */
+        case 26: /* SUB */
+          state->escstate = 0;
+          break;
+       /* Decimal parameter */
+        case '0': case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+          if (state->curparam < NPAR) {
+           if (state->curparam == -1)
+                   state->curparam = 0;
+            state->csiparam[state->curparam] =
+              (state->csiparam[state->curparam] * 10) + (c - '0');
+         }
+          break;
+        case ';': /* Activate new parameter field */
+         if (state->curparam == -1)
+                 state->curparam = 0;
+          state->csiparam[++state->curparam] = 0;
+          break;
+        case '[':
+          state->escstate = 3;
+          break;
+        case '@':
+         /*
+          * Insert character
+          * Shouldn't advance the cursor unless printing in auto-insert mode.
+          * It should advance the rest of the text of the line only.  And
+          * strangely, we must consider [0@ to not perform any action, vs [@
+          * that should move 1 step (ich1) and [n@ that should move n steps
+          * (ich).  The current text modes do not affect the contents of the
+          * newly inserted spaces.
+          */
+         /* XXX Implement auto-insert mode now that it's a function. */
+         at_insert(st, (state->curparam == -1) ? 1 : state->csiparam[0]);
+          state->escstate = 0;
+          break;
+        case 'F': /* Cursor preceding line (cpl) */
+         /* XXX What's the difference between cpl and cuu? */
+          state->cursor_x = 0;
+         /* FALLTHROUGH */
+        case 'A': /* Cursor up (cuu) */
+         if ((i = (state->curparam == -1) ? 1 : state->csiparam[0]) > 0) {
+             if ((state->cursor_y -= i) < 0)
+                 state->cursor_y = 0;
+         }
+          state->escstate = 0;
+          break;
+        case 'E': /* Cursor next line (cnl) */
+          /* XXX What's the difference between this and B (move down)? */
+          state->cursor_x = 0;
+         /* FALLTHROUGH */
+        case 'e': /* FALLTHROUGH */
+        case 'B': /* Cursor down (cud) */
+         if ((i = (state->curparam == -1) ? 1 : state->csiparam[0]) > 0) {
+             if ((state->cursor_y += i) >= rows)
+                 state->cursor_y = rows - 1;
+         }
+          state->escstate = 0;
+          break;
+       case 'b': /* Repeat last printable char <n> times (at least 1) */
+         many = state->csiparam[0];
+         if (many == 0)
+             many = 1;
+         if (st->lastchar != -1 && isprint(st->lastchar)) {
+             for (i = 0; i < many; i++)
+                 at_printc(st, st->lastchar, state->mode);
+             /* XXX Silly double state requires this */
+             state->cursor_x = st->cursx;
+             state->cursor_y = st->cursy;
+         }
+         state->escstate = 0;
+         break;
+        case 'a': /* Cursor forward relative (hpr) */
+         /* XXX Cannot be the same as C */
+         /* FALLTHROUGH */
+        case 'C': /* Cursor forward (cuf) */
+         /* Interestingly param 0 still means 1 for C */
+          if (state->csiparam[0] == 0)
+            state->csiparam[0] = 1;
+          if ((state->cursor_x += state->csiparam[0]) >= cols)
+            state->cursor_x = cols - 1;
+          state->escstate = 0;
+          break;
+       case 'c': /* Query Device Attributes (DA) */
+         {
+             FILE *fh = textclient_pipe(state->tc);
+
+             /* XXX See what to really send here */
+             /* https://vt100.net/docs/vt510-rm/chapter4.html
+              * https://vt100.net/docs/vt220-rm/chapter4.html
+              */
+             /* Keep 15, 18, 21? */
+             (void)fprintf(fh, "\033[?6;15;18;21;44;42;46c");
+             (void)fflush(fh);
+         }
+         state->escstate = 0;
+         break;
+        case 'D': /* Cursor backward (cub) */
+         /* Interestingly param 0 still means 1 for D */
+          if (state->csiparam[0] == 0)
+            state->csiparam[0] = 1;
+          if ((state->cursor_x -= state->csiparam[0]) < 0)
+            state->cursor_x = 0;
+          state->escstate = 0;
+          break;
+        case 'd': /* Line position absolute (vpa) */
+          if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
+            state->cursor_y = rows - 1;
+          state->escstate = 0;
+          break;
+        case '`': /* FALLTHROUGH */
+        case 'G': /* Cursor character absolute (cha) */
+          if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
+            state->cursor_x = cols - 1;
+          state->escstate = 0;
+          break;
+        case 'f': /* FALLTHROUGH */
+        case 'H': /* Cursor address h/v position (cup) */
+         /* XXX Could be optimized considering that sciparams can only be
+          * positive in this implementation.
+          */
+          if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
+            state->cursor_y = rows - 1;
+          if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
+            state->cursor_x = cols - 1;
+          if(state->cursor_y < 0)
+            state->cursor_y = 0;
+          if(state->cursor_x < 0)
+            state->cursor_x = 0;
+          state->escstate = 0;
+          break;
+       case 'h': /* DEC Private Mode Set (DECSET) */
+         switch (state->csiparam[0]) {
+         case 12: /* XXX Start blinking cursor */
+         case 13: /* FALLTHROUGH */
+             break;
+         case 25: /* XXX Show cursor (DECTCEM) */
+             break;
+         default:
+             unsupported(state, 2, '?', 'h');
+             break;
+         }
+          state->escstate = 0;
+         break;
+        case 'J': /* Clear display */
+         /*
+          * Clears part or all of the display without changing cursor.
+          * J or 0J == cursor to bottom (ed), 1J == top left to cursor (?),
+          * 2J == all screen (?).
+          * XXX We wouldn't need to work line by line if we ensured that the
+          * video memory was allocated properly, contiguous.
+          */
+         start = 0;
+         end = rows;
+         if (state->csiparam[0] == 0) {
+             start = state->cursor_y + 1;
+             /* Partial line from cursor */
+             at_clear(st, state->cursor_x, state->cursor_y,
+                 cols - state->cursor_x);
+         } else if (state->csiparam[0] == 1) {
+             end = state->cursor_y;
+             /* Partial line to cursor */
+             at_clear(st, 0, state->cursor_y, state->cursor_x);
+         }
+         /* Full lines */
+         for (i = start; i < end; i++)
+             at_clear(st, 0, i, cols);
+          state->escstate = 0;
+          break;
+        case 'K': /* Clear line */
+          start = 0;
+          end = cols; /* 2K: whole line (?) */
+          if (state->csiparam[0] == 0) /* 0K: cursor to end of line (ce) */
+            start = state->cursor_x;
+          if (state->csiparam[0] == 1) /* 1K: start of line to cursor (el) */
+            end = state->cursor_x;
+         at_clear(st, start, state->cursor_y, end - start);
+          state->escstate = 0;
+          break;
+       case 'l': /* DEC Private Mode Reset (DECRST) */
+         switch (state->csiparam[0]) {
+         case 12: /* XXX Stop blinking cursor */
+         case 13: /* FALLTHROUGH */
+             break;
+         case 25: /* XXX Hide cursor (DECTCEM) */
+             break;
+         default:
+             unsupported(state, 2, '?', 'l');
+             break;
+         }
+          state->escstate = 0;
+         break;
+       case 'L': /* Insert line(s) (il1, il) */
+         /*
+          * [L should insert one line, [0L should insert one, [<n>L should
+          * insert <n> lines.  This is basically backwards scrolling from
+          * cursor point.  Inserted lines are not affected by the current
+          * text mode and are always blank, except for the background color
+          * if not "default".  Interestingly rxvt considers [0L as no
+          * action, but not xterm.
+          * This is basically scrolling down from the cursor y position to
+          * the end of the scroll region.
+          */
+         {
+             int oldtop = st->scroll_top;
+
+             many = (state->csiparam[0] == 0 ? 1 : state->csiparam[0]);
+             if (many > st->scroll_bottom - state->cursor_y)
+                 many = st->scroll_bottom - state->cursor_y;
+             st->scroll_top = state->cursor_y;
+             at_scroll_range(st, many);
+             st->scroll_top = oldtop;
+         }
+          state->escstate = 0;
+         break;
+       case 'M': /* Delete lines (dl, dl1) */
+         /*
+          * The scroll region affects this command.  No argument and 0
+          * are treated like 1.  This basically scrolls up the area of the
+          * screen between the cursor y position and the bottom of the scroll
+          * region.
+          */
+         {
+             int oldtop = st->scroll_top;
+
+             many = (state->csiparam[0] == 0 ? 1 : state->csiparam[0]);
+             if (many > st->scroll_bottom - state->cursor_y)
+                 many = st->scroll_bottom - state->cursor_y;
+             st->scroll_top = state->cursor_y;
+             at_scroll_range(st, -many);
+             st->scroll_top = oldtop;
+         }
+         state->escstate = 0;
+         break;
+        case 'm': /* Set text attributes */
+         /*
+          * XXX We possibly could have better support here, including
+          * italics.  Also review from the code charts.
+          * Perhaps even colors...
+          */
+         if (state->curparam == -1)
+                 state->curparam = 0;
+          for (i = 0; i <= state->curparam; i++)
+            {
+              switch(state->csiparam[i])
+                {
+               /* Reset */
+                case 0:
+                 /* We make sure not to reset TMODE_GFX here */
+                 state->mode &= TMODE_GFX;
+                  break;
+               /* Enable */
+                case 1:
+                 /* Mutually exclusive */
+                 state->mode |= TMODE_BOLD;
+                 state->mode &= ~TMODE_DIM;
+                  break;
+                case 2:
+                 /* Mutually exclusive */
+                 state->mode |= TMODE_DIM;
+                 state->mode &= ~TMODE_BOLD;
+                  break;
+               case 4:
+               case 21: /* FALLTHROUGH (doubly underline) */
+                 state->mode |= TMODE_UNDERLINE;
+                 break;
+                case 5:
+                 state->mode |= TMODE_BLINK;
+                  break;
+                case 7:
+                 state->mode |= TMODE_INVERSE;
+                  break;
+               /* XXX Enable colors, converted to other attributes */
+               /* Foreground color */
+               /*case 30: XXX Ignore black fg color for now. */ 
+               case 31: /* FALLTHROUGH */
+               case 32: /* FALLTHROUGH */
+               case 33: /* FALLTHROUGH */
+               case 34: /* FALLTHROUGH */
+               case 35: /* FALLTHROUGH */
+               case 36: /* FALLTHROUGH */
+               case 37: /* FALLTHROUGH */
+                 state->mode |= TMODE_FGCOLOR;
+                 break;
+               /* Background color */
+               /*case 40: XXX Ignore black bg color for now. */
+               case 41: /* FALLTHROUGH */
+               case 42: /* FALLTHROUGH */
+               case 43: /* FALLTHROUGH */
+               case 44: /* FALLTHROUGH */
+               case 45: /* FALLTHROUGH */
+               case 46: /* FALLTHROUGH */
+               case 47: /* FALLTHROUGH */
+                 state->mode |= TMODE_BGCOLOR;
+                 break;
+               /* Disable */
+                case 22: /* Normal brightness */
+                 /* Mutually exclusive */
+                 state->mode &= ~(TMODE_BOLD | TMODE_DIM);
+                  break;
+               case 24:
+                 state->mode &= ~TMODE_UNDERLINE;
+                 break;
+                case 25:
+                 state->mode &= ~TMODE_BLINK;
+                  break;
+                case 27:
+                 state->mode &= ~TMODE_INVERSE;
+                  break;
+                case 39: /* Default fg color */
+                 state->mode &= ~TMODE_FGCOLOR;
+                 break;
+               case 49: /* Default bg color */
+                 state->mode &= ~TMODE_BGCOLOR;
+                 break;
+                }
+            }
+          state->escstate = 0;
+          break;
+       case 'n': /* Cursor position report */
+         if (state->csiparam[0] == 5) {
+             FILE *fh = textclient_pipe(state->tc);
+
+             (void)fprintf(fh, "\033[0n"); /* Terminal Ok */
+             (void)fflush(fh);
+         } else if (state->csiparam[0] == 6) {
+             /* XXX We shouldn't call the function each time ideally */
+             FILE *fh = textclient_pipe(state->tc);
+
+             (void)fprintf(fh, "\033[%d;%dR",
+                 state->cursor_y + 1, state->cursor_x + 1);
+             (void)fflush(fh);
+         }
+         state->escstate = 0;
+         break;
+       case 'P': /* Delete characters (dch1, dch) */
+         /*
+          * Interestingly, in this case both dhc1 and dch with 0 are
+          * considered equivalent.  dch <n> > 1 delete more than 1.
+          * XXX Could use at_move() and at_clear().
+          */
+         many = (state->csiparam[0] > 1 ? state->csiparam[0] : 1);
+         if (many >= cols - st->cursx) {
+             /* Clear rest of line in this case, like K */
+             many = cols - st->cursx;
+             (void)memset(&st->textlines_char[st->cursy][st->cursx], ' ',
+                 many);
+             (void)memset(&st->textlines_mode[st->cursy][st->cursx],
+                 state->mode & TMODE_BGCOLOR, many);
+         } else {
+             unsigned char *cptr;
+
+             /* Move n chars from right to left */
+             cptr = &st->textlines_char[st->cursy][st->cursx];
+             (void)memmove(cptr, &cptr[many], cols - st->cursx - many);
+             cptr = &st->textlines_mode[st->cursy][st->cursx];
+             (void)memmove(cptr, &cptr[many], cols - st->cursx - many);
+             /* Fill hole at right with emptyness */
+             /* XXX Make sure this is the correct implementation */
+             cptr = &st->textlines_char[st->cursy][cols];
+             (void)memset(&cptr[-many], ' ', many);
+             cptr = &st->textlines_mode[st->cursy][cols];
+             (void)memset(&cptr[-many], state->mode & TMODE_BGCOLOR, many);
+         }
+         state->escstate = 0;
+         break;
+       case 'r': /* Set scroll region (csr) */
+         /*
+          * Requires two arguments, the top and bottom lines, 1-based rather
+          * than 0-based.  If 0, consider like 1.  Like xterm, we then reset
+          * the cursor position to 1, 1 (internally 0, 0).
+          * When a scroll region is set that is smaller than the whole text
+          * area, functions like clear/J still affect the rest, cursor
+          * addressing like H works as usual, but scrolling does not occur
+          * when outside of the boundaries, including for CR/LF.
+          * The limits we store internally are 0-based.
+          */
+         if (state->curparam == 1) {
+             int maxrow = rows - 1;
+
+             st->scroll_top = state->csiparam[0];
+             st->scroll_bottom = state->csiparam[1];
+             if (st->scroll_top > 0)
+                 st->scroll_top--;
+             if (st->scroll_top > maxrow)
+                 st->scroll_top = maxrow;
+             if (st->scroll_bottom > 0)
+                 st->scroll_bottom--;
+             if (st->scroll_bottom > maxrow)
+                 st->scroll_bottom = maxrow;
+             state->cursor_x = state->cursor_y = 0;
+         }
+         state->escstate = 0;
+         break;
+        case 's': /* Save cursor position (sc) */
+          state->saved_x = state->cursor_x;
+          state->saved_y = state->cursor_y;
+          state->escstate = 0;
+          break;
+       case 'S': /* Scroll up (indn) */
+         /*
+          * Scroll <n> lines up. No argument or n=0 is like 1.
+          * Takes the scrolling range in consideration.
+          * Used by nvi for single-line scrolling.
+          */
+         at_scroll_range(st,
+             - (state->csiparam[0] > 1 ? state->csiparam[0] : 1));
+          state->escstate = 0;
+         break;
+       case 'T': /* Scroll down (rin) */
+                 /*
+          * Scroll <n> lines down. No argument is like 1, 0 is no action.
+          * Takes the scrolling range in consideration.
+          * Used by nvi for single-line scrolling.
+          */
+         at_scroll_range(st,
+             (state->curparam == -1 ? 1 : state->csiparam[0]));
+         state->escstate = 0;
+         break;
+        case 'u': /* Restore cursor position (rc) */
+          state->cursor_x = state->saved_x;
+          state->cursor_y = state->saved_y;
+          state->escstate = 0;
+          break;
+       case 'X': /* Erase characters (ech) */
+         /*
+          * Unlike P that deletes collapsing what remains at the right, this
+          * simply overstrike-erases at least one character (no parameter or
+          * 0 are like 1).  The cursor position is not changed.
+          */
+         many = state->csiparam[0];
+         if (many == 0)
+             many = 1;
+         if (many > cols - state->cursor_x)
+             many = cols - state->cursor_x;
+         at_clear(st, state->cursor_x, state->cursor_y, many);
+         state->escstate = 0;
+         break;
+        case '?': /* DEC Private modes, fallsback to 1/[ state */
+         if (state->curparam == -1)
+                 state->curparam = 0;
+          if ((state->curparam != 0) || (state->csiparam[0] != 0))
+            state->escstate = 0;
+          break;
+        default:
+          /* XXX Known unsupported CSIs:
+           *
+           * c - Terminal identification
+           * g - Clear tab stop(s)
+           * h - Set mode (Mainly due to its complexity and lack of good
+           docs)
+           * l - Clear mode
+           * m - Set mode (Phosphor is, per defenition, green on black)
+           * n - Status report
+           * q - Set keyboard LEDs
+           */
+         unsupported(state, 2, '[', c);
+          state->escstate = 0;
+          break;
+        }
+      break;
+    case 3:
+      switch (c) {
+      case '0':
+         /* Switch to DEC Special Graphics character set (smacs) */
+         /* https://en.wikipedia.org/wiki/DEC_Special_Graphics */
+         state->mode |= TMODE_GFX;
+         state->escstate = 0;
+         break;
+      default:
+         /* Switch back to default character set (any other for now) */
+         state->mode &= ~TMODE_GFX;
+         state->escstate = 0;
+         break;
+      }
+      break;
+
+    case 102:
+    case 103:
+    case 104:
+    case 105:
+    case 106:
+      {
+        int total = state->escstate - 100;  /* see what I did there */
+        if (state->unicruds < total) {
+          /* Buffer more bytes of the UTF-8 sequence */
+          state->unicrud[state->unicruds++] = c;
+        }
+
+        if (state->unicruds >= total) {
+          /* Done! Convert it to ASCII and print that. */
+          char *s;
+          state->unicrud[state->unicruds] = 0;
+          s = utf8_to_latin1 ((const char *) state->unicrud, True);
+          state->unicruds = 0;
+          state->escstate = 0;
+          if (s) {
+            c = s[0];
+            free (s); /* XXX "Your code is suboptimal!" */
+            goto PRINT;
+          } else {
+            /* c = 0; */
+          }
+        }
+      }
+      break;
+
+    default:
+      abort();
+    }
+  /* Apply local position to terminal state */
+  at_goto(st, state->cursor_y, state->cursor_x);
+  /* And mode */
+  st->mode = state->mode;
+}
+
+
+/*
+  It's fun to put things like "gdb" as the command. For one, it's
+  amusing how the standard mumble (version, no warranty, it's
+  GNU/Linux dammit) occupies an entire screen on the Apple ][.
+*/
+
+static void
+terminal_controller(analogterm_sim_t *sim, int *stepno, double *next_actiontime)
+{
+  analogterm_state_t *st=sim->st;
+  int c;
+  int i;
+
+  struct terminal_controller_data *mine;
+  if (!sim->controller_data)
+    sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
+  mine=(struct terminal_controller_data *) sim->controller_data;
+  mine->dpy = sim->dpy;
+
+  mine->fast_p           = /*global_fast_p;*/True;
+
+  switch(*stepno) {
+
+  case 0:
+    at_cls(st);
+    at_goto(st,0,0);
+
+    if (! mine->tc) {
+      mine->tc = textclient_open (mine->dpy);
+      textclient_reshape (mine->tc,
+                          SCREEN_COLS, SCREEN_ROWS,
+                          SCREEN_COLS, SCREEN_ROWS,
+                          0);
+    }
+
+    if (! mine->fast_p)
+      *next_actiontime += 1.0;
+    *stepno = 10;
+
+    mine->last_emit_time = sim->curtime;
+    break;
+
+  case 10:
+  case 11:
+    {
+      Bool first_line_p = (*stepno == 10);
+      unsigned char buf[1024];
+      int nr,nwant;
+      double elapsed;
+
+      elapsed=sim->curtime - mine->last_emit_time;
+
+      nwant = elapsed * 25.0;   /* characters per second */
+
+      if (first_line_p) {
+        *stepno = 11;
+        nwant = 1;
+      }
+
+      if (nwant > 80) nwant = 80;
+
+      if (mine->fast_p)
+        nwant = sizeof(buf)-1;
+
+      if (nwant <= 0) break;
+
+      mine->last_emit_time = sim->curtime;
+
+      nr=terminal_read(mine, buf, nwant);
+      for (i=0; i<nr; i++) {
+        c=buf[i];
+
+        if (mine->tc)
+          at_vt100_printc (sim, mine, c);
+        else
+          at_ascii_printc (st, c, 0, True);
+      }
+    }
+    break;
+
+  case A2CONTROLLER_FREE:
+    terminal_closegen(mine);
+    free(mine);
+    mine = 0;
+    return;
+  }
+}
+
+struct state {
+  int duration;
+  Bool random_p;
+  analogterm_sim_t *sim;
+  void (*controller) (analogterm_sim_t *sim, int *stepno, double *next_actiontime);
+};
+
+
+static void *
+analogterm_init (Display *dpy, Window window)
+{
+  struct state *st = (struct state *) calloc (1, sizeof(*st));
+
+  st->duration = 999999; /* "Forever" XXX Make sure it really is or implement -1 */
+  st->controller = terminal_controller;
+
+  global_program = get_string_resource (dpy, "program", "Program");
+  global_fast_p = get_boolean_resource (dpy, "fast", "Boolean");
+
+  /* Kludge for MacOS standalone mode: see OSX/SaverRunner.m. */
+  {
+      /*
+    const char *s = getenv ("XSCREENSAVER_STANDALONE");
+    if (s && *s && strcmp(s, "0"))
+      {
+      */
+      /* XXX Strangely this doesn't seem to get executed, but when we set
+       * XSCREENSAVER_STANDALONE it works.  Must be done elsewhere.
+       */
+        st->controller = terminal_controller;
+        st->random_p   = False;
+        global_program = getenv ("SHELL");
+        global_fast_p  = True;
+       /*
+      }
+      */
+  }
+
+  return st;
+}
+
+static unsigned long
+analogterm_draw (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+
+  if (! st->sim) {
+    st->sim = analogterm_start (dpy, window, st->duration, st->controller);
+  }
+
+  if (! analogterm_one_frame (st->sim)) {
+    st->sim = 0;
+  }
+
+#ifdef HAVE_MOBILE
+  return 0;
+#else
+  /* XXX
+   * 0 provides the fastest interactive response but requires a lot of
+   * CPU time.  Then 1 and over immediately affect performance.
+   * Running cursor effects are also more noticeable at 5000 and lower.
+   * This should be user-configurable.
+   */
+  return /*5000*//*20000*/1; /* Note: use 0 for faster response like for games */
+#endif
+}
+
+static void
+analogterm_reshape (Display *dpy, Window window, void *closure, 
+                 unsigned int w, unsigned int h)
+{
+  struct state *st = (struct state *) closure;
+  if (st->sim)
+    analogtv_reconfigure (st->sim->dec);
+}
+
+static Bool
+analogterm_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+  struct state *st = (struct state *) closure;
+
+  if (st->sim &&
+      st->controller == terminal_controller &&
+      event->xany.type == KeyPress) {
+    terminal_keypress_handler (dpy, event, st->sim->controller_data);
+    return True;
+  }
+
+  return False;
+}
+
+static void
+analogterm_free (Display *dpy, Window window, void *closure)
+{
+  struct state *st = (struct state *) closure;
+  if (st->sim) {
+    st->sim->stepno = A2CONTROLLER_DONE;
+    if (analogterm_one_frame (st->sim))
+      abort();  /* should have freed! */
+  }
+  free (st);
+}
+
+
+XSCREENSAVER_MODULE ("AnalogTerm", analogterm)
diff --git a/hacks/analogterm.c b/hacks/analogterm.c
new file mode 100644 (file)
index 0000000..feeb693
--- /dev/null
@@ -0,0 +1,624 @@
+/* xscreensaver, Copyright (c) 1998-2010 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org>
+ * with additional work by Jamie Zawinski <jwz@jwz.org>
+ * 2022 Adapted to analogterm and more complete xterm emulation by Matthew Mondor.
+ */
+
+#include <math.h>
+#include "screenhackI.h"
+#include "analogterm.h"
+
+#ifdef HAVE_XSHM_EXTENSION
+#include "xshm.h"
+#endif
+
+/*
+ * Implementation notes
+ *
+ * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it
+ * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the
+ * same memory as the text screen. Each could be one of 16 colors. Hires gave
+ * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were
+ * orange or green depending on the setting of the high bit in each byte.
+ *
+ * The graphics modes could also have 4 lines of text at the bottom. This was
+ * fairly unreadable if you had a color monitor.
+ *
+ * Each mode had 2 different screens using different memory space. In hires
+ * mode this was sometimes used for double buffering, but more often the lower
+ * screen was full of code/data and the upper screen was used for display, so
+ * you got random garbage on the screen.
+ *
+ * The text font is based on X's standard 6x10 font, with a few tweaks like
+ * putting a slash across the zero.
+ *
+ * To use this, you'll call apple2(display, window, duration,
+ * controller) where the function controller defines what will happen.
+ * See bsod.c and apple2-main.c for example controllers. The
+ * controller function gets called whenever the machine ready to start
+ * something new. By setting sim->printing or sim->typing, it'll be
+ * busy for some time spitting characters out one at a time. By
+ * setting *next_actiontime+=X.X, it'll pause and just update the screen
+ * for that long before calling the controller function again.
+ *
+ * By setting stepno to A2CONTROLLER_DONE, the loop will end. It will also end
+ * after the time specified by the delay parameter. In either case, it calls
+ * the controller with stepno==A2CONTROLLER_FREE to allow it to release any
+ * memory.
+ *
+ * The void* apple2_sim_t::controller_data is for the use of the controller.
+ * It will be initialize to NULL, and the controller can store its own state
+ * there.
+ *
+ */
+
+void
+at_move(analogterm_state_t *st, int dx, int dy, int sx, int sy, int len)
+{
+
+       (void)memmove(&st->textlines_char[dy][dx],
+           &st->textlines_char[sy][sx], len);
+       (void)memmove(&st->textlines_mode[dy][dx],
+           &st->textlines_mode[sy][sx], len);
+}
+
+void
+at_clear(analogterm_state_t *st, int x, int y, int len)
+{
+
+       (void)memset(&st->textlines_char[y][x], ' ', len);
+       (void)memset(&st->textlines_mode[y][x], st->mode & TMODE_BGCOLOR, len);
+}
+
+/*
+ * Insert space for <many> characters at cursor, scrolling text rightwards.
+ */
+void
+at_insert(analogterm_state_t *st, int many)
+{
+       unsigned char *cptr;
+       int s;
+
+       if (many < 1)
+               return;
+
+       /* Disable cursor */
+       /*st->textlines_mode[st->cursy][st->cursx] &= ~TMODE_BLINK;*/
+
+       if (many > 80 - st->cursx)
+               many = 80 - st->cursx;
+       s = 80 - st->cursx - many;
+       cptr = &st->textlines_char[st->cursy][st->cursx];
+       (void)memmove(&cptr[many], cptr, s);
+       (void)memset(cptr, ' ', many);
+       cptr = &st->textlines_mode[st->cursy][st->cursx];
+       (void)memmove(&cptr[many], cptr, s);
+       (void)memset(cptr, st->mode & TMODE_BGCOLOR, many);
+
+       /* Restore cursor, may not always be necessary */
+       /*st->textlines_mode[st->cursy][st->cursx] |= TMODE_BLINK;*/
+}
+
+static signed char cursorstate = 127;
+static int cursorstate_timer = 1, cursorstate_add = -1;
+void
+at_drawcursor(analogterm_sim_t *sim)
+{
+       analogterm_state_t *st = sim->st;
+       int x, y;
+       signed char *pp;
+
+       /* Line cursor */
+       /*
+       pp = &sim->inp->signal[(ANALOGTV_TOP+3) + (8 * st->cursy) + 7]
+          [(ANALOGTV_PIC_START+100) + (7 * st->cursx)];
+       for (x = 0; x < 8; x++) {
+               *pp++ ^= cursorstate;
+               if (--cursorstate_timer == 0) {
+                       cursorstate_timer = 2;
+                       cursorstate++;
+               }
+       }*/
+
+       /* Block cursor, a bit bizarre for now :) */
+       /* XXX This memory design is very inefficient */
+       /* XXX Should probably use a well defined range instead of cycling
+        * completely. */
+       for (y = 0; y < 8; y++) {
+               pp = &sim->inp->signal[(ANALOGTV_TOP+3) + (8 * st->cursy) + y]
+                  [(ANALOGTV_PIC_START+100) + (7 * st->cursx)];
+               for (x = 0; x < 8; x++) {
+                       *pp++ ^= cursorstate;
+               }
+               if (--cursorstate_timer == 0) {
+                       cursorstate_timer = 2;
+                       if (cursorstate_add > 0) {
+                               if (++cursorstate > 126)
+                                       cursorstate_add = -1;
+                       } else {
+                               if (--cursorstate < 1)
+                                       cursorstate_add = 1;
+                       }
+               }
+       }
+}
+
+void
+at_resetcursor(void)
+{
+
+       cursorstate = 127;
+       cursorstate_add = -1;
+}
+
+/*
+ * Scroll page by offset (may be negative or positive) and takes in
+ * consideration the scroll region/range setting.
+ */
+void
+at_scroll_range(analogterm_state_t *st, int offset)
+{
+       int y;
+
+       /* Nothing to do */
+       if (offset == 0)
+               return;
+
+       /* Disable cursor */
+       /*st->textlines_mode[st->cursy][st->cursx] &= ~TMODE_BLINK;*/
+
+       /* Only one line to clear */
+       if (st->scroll_top == st->scroll_bottom) {
+               at_clear(st, 0, st->cursy, 80);
+               /*goto done;*/
+               return;
+       }
+       /* Nothing to move, clear whole scrolling region */
+       if (abs(offset) > st->scroll_bottom - st->scroll_top) {
+               for (y = st->scroll_top; y < st->scroll_bottom; y++)
+                       at_clear(st, 0, y, 80);
+               /*goto done;*/
+               return;
+       }
+
+       /* General case, move lines and clear blanks */
+       if (offset < 0) {
+               /* Top to bottom moving lines up, blank at bottom */
+               for (y = st->scroll_top; y < st->scroll_bottom; y++)
+                       at_move(st, 0, y, 0, y + -offset, 80);
+               for (y = st->scroll_bottom + 1 + offset; y <= st->scroll_bottom;
+                    y++)
+                       at_clear(st, 0, y, 80);
+       } else {
+               /* Bottom to top moving lines down, blank at top */
+               for (y = st->scroll_bottom - offset; y >= st->scroll_top; y--)
+                       at_move(st, 0, y + offset, 0, y, 80);
+               for (y = st->scroll_top; offset > 0; offset--, y++)
+                       at_clear(st, 0, y, 80);
+       }
+
+/*done:*/
+       /* Restore cursor, may not always be necessary */
+       /*st->textlines_mode[st->cursy][st->cursx] |= TMODE_BLINK;*/
+}
+
+void
+at_scroll(analogterm_state_t *st)
+{
+
+       at_scroll_range(st, -1);
+}
+
+static void
+at_printc_1(analogterm_state_t *st, char c, unsigned char m, int scroll_p)
+{
+  /*st->textlines_mode[st->cursy][st->cursx] &= ~TMODE_BLINK;*/ /* turn off blink */
+  at_resetcursor();
+
+  if (c == '\n')                      /* ^J == NL */
+    {
+      if (st->cursy==st->scroll_bottom)
+        {
+          if (scroll_p)
+            at_scroll(st);
+        }
+      else
+        {
+          st->cursy++;
+        }
+      st->cursx=0;
+    }
+  else if (c == 014)                  /* ^L == CLS, Home */
+    {
+      at_cls(st);
+      at_goto(st,0,0);
+    }
+  else if (c == '\t')                 /* ^I == tab */
+    {
+      at_goto(st, st->cursy, (st->cursx+8)&~7);
+    }
+  else if (c == 010)                  /* ^H == backspace */
+    /* XXX May not behave standardly, to check.  Could use at_clear(). */
+    {
+      st->textlines_char[st->cursy][st->cursx] = ' ';
+      st->textlines_mode[st->cursy][st->cursx] = m & TMODE_BGCOLOR;
+      at_goto(st, st->cursy, st->cursx-1);
+    }
+  else if (c == '\r')                 /* ^M == CR */
+    {
+      st->cursx=0;
+    }
+  else
+    {
+      st->lastchar = c;
+      st->textlines_char[st->cursy][st->cursx] = c;
+      st->textlines_mode[st->cursy][st->cursx] = m;
+      st->cursx++;
+      if (st->cursx==80) {
+        if (st->cursy==st->scroll_bottom) {
+          if (scroll_p)
+            at_scroll(st);
+        } else {
+          st->cursy++;
+        }
+        st->cursx=0;
+      }
+    }
+
+  /*st->textlines_mode[st->cursy][st->cursx] |= TMODE_BLINK;*/ /* turn on blink */
+}
+
+void
+at_printc(analogterm_state_t *st, char c, unsigned char m)
+{
+  at_printc_1(st, c, m, 1);
+}
+
+void
+at_printc_noscroll(analogterm_state_t *st, char c, unsigned char m)
+{
+  at_printc_1(st, c, m, 0);
+}
+
+
+void
+at_prints(analogterm_state_t *st, char *s, unsigned char m)
+{
+  while (*s) at_printc(st, *s++, m);
+}
+
+void
+at_goto(analogterm_state_t *st, int r, int c)
+{
+  if (r > 24) r = 24;
+  if (c > 79) c = 79;
+  /*st->textlines_mode[st->cursy][st->cursx] &= ~TMODE_BLINK;*/ /* turn off blink */
+  st->cursy=r;
+  st->cursx=c;
+  /*st->textlines_mode[st->cursy][st->cursx] |= TMODE_BLINK;*/ /* turn on blink */
+  at_resetcursor();
+}
+
+/* XXX
+ * Why can't a single memset(3) be used?  The video memory may be improperly
+ * allocated.
+ */
+void
+at_cls(analogterm_state_t *st)
+{
+  int i;
+
+  for (i=0; i<25; i++) {
+    memset(st->textlines_char[i], ' ', 80);
+    memset(st->textlines_mode[i], st->mode & TMODE_BGCOLOR, 80);
+  }
+}
+
+/* jwz: since MacOS doesn't have "6x10", I dumped this font to an XBM... */
+
+#include "images/analogtermfont.xbm"
+#include "images/analogtermgfxfont.xbm"
+
+static void
+at_make_font(analogterm_sim_t *sim)
+{
+  Pixmap text_pm = XCreatePixmapFromBitmapData (sim->dpy, sim->window,
+                                                (char *) analogterm_font_bits,
+                                                analogterm_font_width,
+                                                analogterm_font_height,
+                                                1, 0, 1);
+  if (analogterm_font_width != 96*7) abort();
+  if (analogterm_font_height != 8) abort();
+  sim->text_im[0] = XGetImage(sim->dpy, text_pm, 0, 0, 
+                           analogterm_font_width, analogterm_font_height,
+                           ~0L, ZPixmap);
+  XFreePixmap(sim->dpy, text_pm);
+}
+
+static void
+at_make_font_gfx(analogterm_sim_t *sim)
+{
+  Pixmap text_pm = XCreatePixmapFromBitmapData (sim->dpy, sim->window,
+                                                (char *) analogtermgfx_font_bits,
+                                                analogtermgfx_font_width,
+                                                analogtermgfx_font_height,
+                                                1, 0, 1);
+  if (analogtermgfx_font_width != 96*7) abort();
+  if (analogtermgfx_font_height != 8) abort();
+  sim->text_im[1] = XGetImage(sim->dpy, text_pm, 0, 0, 
+                           analogtermgfx_font_width, analogtermgfx_font_height,
+                           ~0L, ZPixmap);
+  XFreePixmap(sim->dpy, text_pm);
+}
+
+analogterm_sim_t *
+analogterm_start(Display *dpy, Window window, int delay,
+             void (*controller)(analogterm_sim_t *sim,
+                                int *stepno,
+                                double *next_actiontime))
+{
+  analogterm_sim_t *sim;
+
+  sim=(analogterm_sim_t *)calloc(1,sizeof(analogterm_sim_t));
+  sim->dpy = dpy;
+  sim->window = window;
+  sim->delay = delay;
+  sim->controller = controller;
+
+  sim->st = (analogterm_state_t *)calloc(1,sizeof(analogterm_state_t));
+  sim->dec = analogtv_allocate(dpy, window);
+  sim->inp = analogtv_input_allocate();
+
+  sim->reception.input = sim->inp;
+  sim->reception.level = 1.0;
+
+  sim->prompt=']';
+
+  sim->typing_rate = 1.0;
+
+  analogtv_set_defaults(sim->dec, "");
+  sim->dec->squish_control=0.05;
+  analogtv_setup_sync(sim->inp, 1, 0);
+
+  /* XXX */
+  sim->st->gr_mode = /*A2_GR_HIRES*/0;
+
+  /* Default scrolling region to full screen, 0-based */
+  sim->st->scroll_top = 0;
+  sim->st->scroll_bottom = 24;         /* XXX Use definition/macro */
+
+  at_make_font(sim);
+  at_make_font_gfx(sim);
+
+  sim->stepno=0;
+  at_goto(sim->st,24,0);
+  sim->st->lastchar = -1;
+
+  sim->next_actiontime=0.0;
+
+  sim->curtime=0.0;
+  sim->next_actiontime=sim->curtime;
+  sim->controller (sim, &sim->stepno, &sim->next_actiontime);
+
+# ifdef GETTIMEOFDAY_TWO_ARGS
+  gettimeofday(&sim->basetime_tv, NULL);
+# else
+  gettimeofday(&sim->basetime_tv);
+# endif
+
+  return sim;
+}
+
+int
+analogterm_one_frame (analogterm_sim_t *sim)
+{
+    double blinkphase;
+    int i;
+    int textrow;
+    analogterm_state_t *st = sim->st;
+
+    if (sim->stepno==A2CONTROLLER_DONE)
+      goto DONE;  /* when caller says we're done, be done, dammit! */
+
+    {
+      struct timeval curtime_tv;
+# ifdef GETTIMEOFDAY_TWO_ARGS
+      struct timezone tzp;
+      gettimeofday(&curtime_tv, &tzp);
+# else
+      gettimeofday(&curtime_tv);
+# endif
+      sim->curtime=(curtime_tv.tv_sec - sim->basetime_tv.tv_sec) +
+        0.000001*(curtime_tv.tv_usec - sim->basetime_tv.tv_usec);
+      if (sim->curtime > sim->dec->powerup)
+        sim->dec->powerup=sim->curtime;
+    }
+
+    blinkphase=sim->curtime/0.2; /* XXX 0.8 */
+
+    /* The blinking rate was controlled by 555 timer with a resistor/capacitor
+       time constant. Because the capacitor was electrolytic, the flash rate
+       varied somewhat between machines. I'm guessing 1.6 seconds/cycle was
+       reasonable. (I soldered a resistor in mine to make it blink faster.) */
+    i=st->blink;
+    st->blink=((int)blinkphase)&1;
+    if (st->blink!=i && !(st->gr_mode&A2_GR_FULL)) {
+      int downcounter=0;
+      /* For every row with blinking text, set the changed flag. This basically
+         works great except with random screen garbage in text mode, when we
+         end up redrawing the whole screen every second */
+      int row, col;
+      for (row=(st->gr_mode ? 20 : 0); row<25; row++) {
+        for (col=0; col<80; col++) {
+          int c = st->textlines_mode[row][col];
+          if ((c & TMODE_BLINK) != 0) {
+            downcounter=4;
+            break;
+          }
+        }
+        if (downcounter>0) {
+          downcounter--;
+        }
+      }
+    }
+
+    if (sim->curtime >= sim->delay)
+      sim->stepno = A2CONTROLLER_DONE;
+
+    if (sim->printing) {
+      int nlcnt=0;
+      while (*sim->printing) {
+        if (*sim->printing=='\001') { /* pause */
+          sim->printing++;
+          break;
+        }
+        else if (*sim->printing=='\n') {
+          at_printc(st,*sim->printing,0);
+          sim->printing++;
+          nlcnt++;
+          if (nlcnt>=2) break;
+        }
+        else {
+          at_printc(st,*sim->printing,0);
+          sim->printing++;
+        }
+      }
+      if (!*sim->printing) sim->printing=NULL;
+    }
+    else if (sim->curtime >= sim->next_actiontime) {
+      if (sim->typing) {
+
+        int c;
+        /* If we're in the midst of typing a string, emit a character with
+           random timing. */
+        c =*sim->typing;
+        if (c==0) {
+          sim->typing=NULL;
+        }
+        else {
+          sim->typing++;
+          at_printc(st, c, 0);
+          if (c=='\r' || c=='\n') {
+            sim->next_actiontime = sim->curtime;
+          }
+          else if (c==010) {
+            sim->next_actiontime = sim->curtime + 0.1;
+          }
+          else {
+            sim->next_actiontime = (sim->curtime +
+                               (((random()%1000)*0.001 + 0.3) *
+                                sim->typing_rate));
+          }
+        }
+      } else {
+        sim->next_actiontime = sim->curtime;
+
+        sim->controller (sim, &sim->stepno, &sim->next_actiontime);
+
+        if (sim->stepno==A2CONTROLLER_DONE) {
+
+        DONE:
+          sim->stepno=A2CONTROLLER_FREE;
+          sim->controller (sim, &sim->stepno, &sim->next_actiontime);
+          /* if stepno is changed, return 1 */
+          if (sim->stepno != A2CONTROLLER_FREE)
+            return 1;
+
+          XClearWindow(sim->dpy, sim->window);
+
+          /* free sim */
+          /* This is from at_make_font */
+          free(sim->text_im[0]->data);
+          sim->text_im[0]->data = 0;
+          XDestroyImage(sim->text_im[0]);
+          free(sim->text_im[1]->data);
+          sim->text_im[1]->data = 0;
+          XDestroyImage(sim->text_im[1]);
+
+          /* And free else */
+          analogtv_release(sim->dec);
+          free(st);
+          free(sim->inp);
+          free(sim);
+
+          return 0;
+        }
+
+      }
+    }
+
+    /* Color burst disabled for pure text mode */
+    analogtv_setup_sync(sim->inp, /* XXX 1 */0, 0);
+    analogtv_setup_frame(sim->dec);
+
+    for (textrow=0; textrow<25; textrow++) {
+      int row;
+      for (row=textrow*8; row<textrow*8+8; row++) {
+        signed char *pp;
+       int col;
+       int lastrow = row == textrow * 8 + 7;
+
+        /* First we generate the pattern that the video circuitry shifts out
+           of memory. It has a 14.something MHz dot clock, equal to 4 times
+           the color burst frequency. So each group of 4 bits defines a color.
+           Each character position, or byte in hires, defines 14 dots, so odd
+           and even bytes have different color spaces. So, pattern[0..600]
+           gets the dots for one scan line. */
+       /* With 25 lines the offset was changed to 3 to avoid loss */
+       pp=&sim->inp->signal[row+ANALOGTV_TOP+/*4*/3][ANALOGTV_PIC_START+100];
+       for (col=0; col<80; col++) {
+            int rev, level;
+            int c = st->textlines_char[textrow][col] & 0xff;
+           unsigned char m = st->textlines_mode[textrow][col] & 0xff;
+           XImage *im = sim->text_im[m & TMODE_GFX ? 1 : 0];
+
+           rev = ((m & (TMODE_INVERSE | TMODE_BGCOLOR)) != 0) ||
+               ((m & TMODE_BLINK) != 0 && st->blink);
+
+           level = TMODE_NORMAL_LEVEL;
+           if ((m & (TMODE_BOLD/* | TMODE_FGCOLOR XXX */)) != 0)
+                   level = TMODE_BOLD_LEVEL;
+           else if ((m & (TMODE_DIM)) != 0)
+                   level = TMODE_DIM_LEVEL;
+
+           /*
+            * Font is 96 7x8 glyphs including embedded spacing.
+            * Virtual resolution is 280x192 but at 80 columns it gives a less
+            * visibly-precise 560 pixels width.
+            * A better font would be one with double-width verticals that
+            * fits into a 9x14 matrix like the IBM 5271 one (very similar to
+            * the IBM 3270 one).  This would however require a 720x350
+            * resolution.
+            */
+            for (i=0; i<7; i++) {
+               unsigned long pix;
+
+               if (c < 32 || c > 126)
+                       c = 32;
+               pix = XGetPixel(im, (c - 32) * 7 + i, row % 8);
+               if ((m & TMODE_UNDERLINE) != 0 && lastrow)
+                       pix = 1;
+               *pp++ = ((pix^rev) ? level : ANALOGTV_BLACK_LEVEL);
+            }
+        }
+      }
+    }
+    at_drawcursor(sim);
+    analogtv_reception_update(&sim->reception);
+    {
+      const analogtv_reception *rec = &sim->reception;
+      analogtv_draw(sim->dec, /*0.03*/0.01, &rec, 1);
+    }
+    return 1;
+}
+
diff --git a/hacks/analogterm.h b/hacks/analogterm.h
new file mode 100644 (file)
index 0000000..a19e4f8
--- /dev/null
@@ -0,0 +1,148 @@
+/* xscreensaver, Copyright (c) 1998-2004 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ *
+ * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org>
+ * with additional work by Jamie Zawinski <jwz@jwz.org>
+ */
+
+#ifndef __XSCREENSAVER_APPLE_II__
+#define __XSCREENSAVER_APPLE_II__
+
+#include "analogtv.h"
+
+
+#define TMODE_INVERSE  (1 << 0)
+#define TMODE_BLINK    (1 << 1)
+#define TMODE_BOLD     (1 << 2)
+#define TMODE_UNDERLINE        (1 << 3)
+#define TMODE_DIM      (1 << 4)
+#define TMODE_GFX      (1 << 5)
+/* We implement these with inverse and dim */
+#define TMODE_FGCOLOR  (1 << 6)
+#define TMODE_BGCOLOR  (1 << 7)
+
+#define TMODE_NORMAL_LEVEL     (ANALOGTV_WHITE_LEVEL - 35)
+#define TMODE_BOLD_LEVEL       (ANALOGTV_WHITE_LEVEL + 25)
+#define TMODE_DIM_LEVEL                (ANALOGTV_WHITE_LEVEL - 60)
+
+#define CHARSETS               2
+
+
+typedef struct analogterm_state {
+  unsigned char hireslines[192][80];
+  unsigned char textlines_char[25][80]; /* XXX Use definition/macro */
+  unsigned char textlines_mode[25][80];
+  int gr_text;
+  enum {
+    A2_GR_FULL=1,
+    A2_GR_LORES=2,
+    A2_GR_HIRES=4
+  } gr_mode;
+  int cursx;
+  int cursy;
+  int mode;
+  int blink; /* XXX Still needed? */
+  int scroll_top, scroll_bottom;
+  int lastchar;
+} analogterm_state_t;
+
+
+typedef struct analogterm_sim_s analogterm_sim_t;
+struct analogterm_sim_s {
+  
+  void *controller_data;
+
+  analogterm_state_t *st;
+
+  analogtv *dec;
+  analogtv_input *inp;
+  analogtv_reception reception;
+
+  const char *typing;
+  char typing_buf[1024];
+  double typing_rate;
+
+  char *printing;
+  char printing_buf[1024];
+
+  char prompt;
+
+  Display *dpy;
+  Window window;
+  XWindowAttributes xgwa;
+  XImage *text_im[CHARSETS];
+
+  struct timeval basetime_tv;
+  double curtime;
+  double delay;
+
+  int stepno;
+  double next_actiontime;
+  void (*controller)(analogterm_sim_t *sim,
+                     int *stepno,
+                     double *next_actiontime);
+
+};
+
+
+enum {
+  A2_HCOLOR_BLACK=0,
+  A2_HCOLOR_GREEN=1,
+  A2_HCOLOR_PURPLE=2,
+  A2_HCOLOR_WHITE=3,
+  A2_HCOLOR_ALTBLACK=4,
+  A2_HCOLOR_RED=5,
+  A2_HCOLOR_BLUE=6,
+  A2_HCOLOR_ALTWHITE=7
+ };
+
+enum {
+  A2CONTROLLER_DONE=-1,
+  A2CONTROLLER_FREE=-2
+};
+
+
+extern analogterm_sim_t * analogterm_start (Display *, Window, int delay,
+                                    void (*)(analogterm_sim_t *, int *stepno,
+                                             double *next_actiontime));
+extern int analogterm_one_frame (analogterm_sim_t *);
+
+void at_move(analogterm_state_t *, int, int, int, int, int);
+void at_clear(analogterm_state_t *, int, int, int);
+void at_insert(analogterm_state_t *, int);
+void at_drawcursor(analogterm_sim_t *);
+void at_resetcursor(void);
+
+void at_poke(analogterm_state_t *st, int addr, int val);
+void at_goto(analogterm_state_t *st, int r, int c);
+void at_cls(analogterm_state_t *st);
+void at_invalidate(analogterm_state_t *st);
+
+void at_add_disk_item(analogterm_state_t *st, char *name, unsigned char *data,
+                      int len, char type);
+void at_scroll_range(analogterm_state_t *st, int offset);
+void at_scroll(analogterm_state_t *st);
+void at_printc(analogterm_state_t *st, char c, unsigned char m);
+void at_printc_noscroll(analogterm_state_t *st, char c, unsigned char m);
+void at_prints(analogterm_state_t *st, char *s, unsigned char m);
+void at_goto(analogterm_state_t *st, int r, int c);
+void at_cls(analogterm_state_t *st);
+void at_clear_hgr(analogterm_state_t *st);
+void at_clear_gr(analogterm_state_t *st);
+void at_invalidate(analogterm_state_t *st);
+void at_poke(analogterm_state_t *st, int addr, int val);
+void at_display_image_loading(analogterm_state_t *st, unsigned char *image,
+                              int lineno);
+void at_init_memory_active(analogterm_sim_t *sim);
+void at_hplot(analogterm_state_t *st, int hcolor, int x, int y);
+void at_hline(analogterm_state_t *st, int hcolor, int x1, int y1, int x2, int y2);
+void at_plot(analogterm_state_t *st, int color, int x, int y);
+
+#endif /* __XSCREENSAVER_APPLE_II__ */
index 28930c5..aa23275 100644 (file)
    - removed unusable hashnoise code
  */
 
+/*
+ * 2022 Adapted to analogterm by Matthew Mondor.
+ * Altered brightness and sharpness and added optional green, amber, gray,
+ * cyan and blue monochrome modes.
+ */
+
 #ifdef HAVE_JWXYZ
 # include "jwxyz.h"
 #else /* !HAVE_JWXYZ */
@@ -113,9 +119,25 @@ do { \
 #define FASTRND_C 12345
 #define FASTRND (fastrnd = fastrnd*FASTRND_A+FASTRND_C)
 
+
+/*
+ * XXX This is very interesting and has an effect.
+ * This affects the levelmult.
+ * Also see analogtv_levelmult().
+ * The first value affects the level of dark/odd scanlines.
+ * The last value affects the level of bright/even scanlines.
+ * I have no idea yet what the second value does.  Maybe it has to do with
+ * colors?  Initial tests don't suggest so.
+ */
+/*static const double levelfac[3]={-7.5, 5.5, 24.5};  Original */
+static const double levelfac[3]={-3.75, 5.5, 24.5};
+/*static const double levelfac[3]={18.0, 5.5, 40.0};*/
+
+
 static void analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
                                  int start, int end, struct analogtv_yiq_s *it_yiq);
 
+/* This seems to be used for scaling */
 static float puramp(const analogtv *it, float tc, float start, float over)
 {
   float pt=it->powerup-start;
@@ -212,8 +234,11 @@ analogtv_set_defaults(analogtv *it, char *prefix)
   it->hashnoise_on=0;
   it->hashnoise_enable=1;
 
+  /* XXX Original
   it->horiz_desync=frand(10.0)-5.0;
-  it->squeezebottom=frand(5.0)-1.0;
+  it->squeezebottom=frand(5.0)-1.0; */
+  it->horiz_desync=frand(6.0)-3.0;
+  it->squeezebottom=frand(3.0)-1.0;
 
 #ifdef DEBUG
   printf("analogtv: prefix=%s\n",prefix);
@@ -564,12 +589,20 @@ analogtv_allocate(Display *dpy, Window window)
     }
 
     for (i=0; i<ANALOGTV_CV_MAX; i++) {
-      int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
+      /*ORIG int intensity=pow(i/256.0, 0.8)*65535.0;*/ /* gamma correction */
+      /*int intensity=(i / 4096.0)*1565535.0;*/ /* gamma correction */
+      /*int intensity = i * 448.0; linear */
+      /* XXX Custom modification after altering levelmult */
+      /*int intensity=pow(i/256.0, 0.9)*65535.0; Decent */
+      /*int intensity=pow(i/192.0, 1.5)*65535.0; too far*/
+      /*int intensity=pow(i/384.0, 0.9)*65535.0; Interesting, dark */
+      /*int intensity=pow(i/512.0, 0.7)*65535.0; Interesting, blurry */
+      int intensity=pow(i/256.0, 0.8)*65535.0;
       if (intensity>65535) intensity=65535;
       it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
       it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
       it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
-    }
+     }
 
   }
 
@@ -782,8 +815,13 @@ analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
   int phasecorr=(signal-it->rx_signal)&3;
   struct analogtv_yiq_s *yiq;
   int colormode;
-  float agclevel=it->agclevel;
-  float brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
+  float agclevel=it->agclevel; /* XXX Doubling this adds echo. */
+  /* XXX Interesting.  It seems that brightadd increases brightness without
+   * causing the destructive echo.  However brightness_control does...
+   * Also, it affects the background so much that the text may actually be
+   * more readable when decreasing this to 50...
+   */
+  float brightadd=it->brightness_control* /*100.0*/50.0 - ANALOGTV_BLACK_LEVEL;
   float delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
   float multiq2[4];
 
@@ -844,14 +882,26 @@ analogtv_ntsc_to_yiq(const analogtv *it, int lineno, const float *signal,
        mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
        Delay about 2 */
 
+    /*
+     * XXX Very interesting to tweak
+     * In particular, increasing 8.0 for [3] increases sharpness.
+     */
+    /* Original
     dp[0] = sp[0] * 0.0469904257251935f * agclevel;
     dp[8] = (+1.0f*(dp[6]+dp[0])
              +4.0f*(dp[5]+dp[1])
              +7.0f*(dp[4]+dp[2])
              +8.0f*(dp[3])
              -0.0176648f*dp[12]
+             -0.4860288f*dp[10]); */
+    dp[0] = sp[0] * 0.0469904257251935f * agclevel;
+    dp[8] = (+1.0f*(dp[6]+dp[0])
+             +4.0f*(dp[5]+dp[1])
+             +9.0f*(dp[4]+dp[2])
+             +16.0f*(dp[3])
+             -0.0176648f*dp[12]
              -0.4860288f*dp[10]);
-    yiq->y = dp[8] + brightadd;
+     yiq->y = dp[8] + brightadd;
   }
 
   if (colormode) {
@@ -1114,7 +1164,8 @@ analogtv_sync(analogtv *it)
 static double
 analogtv_levelmult(const analogtv *it, int level)
 {
-  static const double levelfac[3]={-7.5, 5.5, 24.5};
+
+       /* XXX Can be tweaked to 50.0 */
   return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
 }
 
@@ -1154,7 +1205,6 @@ static void
 analogtv_setup_levels(analogtv *it, double avgheight)
 {
   int i,height;
-  static const double levelfac[3]={-7.5, 5.5, 24.5};
 
   for (height=0; height<avgheight+2.0 && height<=ANALOGTV_MAX_LINEHEIGHT; height++) {
 
@@ -1174,6 +1224,7 @@ analogtv_setup_levels(analogtv *it, double avgheight)
     }
 
     for (i=0; i<height; i++) {
+           /* XXX can be tweaked to 50.0 */
       it->leveltable[height][i].value = 
         (40.0 + levelfac[it->leveltable[height][i].index]*puramp(it, 3.0, 6.0, 1.0)) / 256.0;
     }
@@ -1405,6 +1456,17 @@ analogtv_blast_imagerow(const analogtv *it,
 
     int level=it->leveltable[lineheight][line].index;
     float levelmult=it->leveltable[lineheight][line].value;
+    /*
+     * XXX Custom modification:
+     * Signal oscillates between 0.126953 and 0.251953.
+     * Intensify visible scanlines and reduce invisible ones.
+     */
+    /*
+    if (levelmult < 0.126954)
+           levelmult *= 0.8;
+    else
+           levelmult *= 1.2;
+    */
 
     /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
        why standard graphics sw has to be fast, or else people will have to
@@ -1430,10 +1492,45 @@ analogtv_blast_imagerow(const analogtv *it,
         unsigned int *pixelptr=(unsigned int *)rowdata;
         unsigned int pix;
 
+/* XXX Make this command-line and/or sequence configurable */
+#define AMBERCRT
         for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
+#ifdef AMBERCRT
+          int ntscri=rpf[0]*levelmult;
+          int ntscgi=rpf[1]*levelmult*0.6;
+          int ntscbi=rpf[2]*0;
+#endif
+#ifdef ORANGECRT
+          int ntscri=rpf[0]*levelmult*1.1;
+          int ntscgi=rpf[1]*levelmult*0.5;
+          int ntscbi=rpf[2]*0;
+#endif
+#ifdef GREENCRT
+          int ntscri=rpf[0]*levelmult*0.20; /* XXX */
+          int ntscgi=rpf[1]*levelmult;
+          int ntscbi=rpf[2]*levelmult*0.45;
+#endif
+#ifdef CYANCRT
+          int ntscri=rpf[0]*levelmult*0;
+          int ntscgi=rpf[1]*levelmult;
+          int ntscbi=rpf[2]*levelmult;
+#endif
+#ifdef BLUECRT
+          int ntscri=rpf[0]*levelmult*0.2;
+          int ntscgi=rpf[1]*levelmult*0.2;
+          int ntscbi=rpf[2]*levelmult*2;
+#endif
+#ifdef REDCRT
+          int ntscri=rpf[0]*levelmult;
+          int ntscgi=rpf[1]*levelmult*0.2;
+          int ntscbi=rpf[2]*levelmult*0.4;
+#endif
+#ifdef WHITECRT
+         /* XXX Brighness can be amplified here but can still cause blur */
           int ntscri=rpf[0]*levelmult;
           int ntscgi=rpf[1]*levelmult;
           int ntscbi=rpf[2]*levelmult;
+#endif
           if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
           if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
           if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
@@ -1441,6 +1538,8 @@ analogtv_blast_imagerow(const analogtv *it,
                  it->green_values[ntscgi] |
                  it->blue_values[ntscbi]);
           pixelptr[0] = pix;
+         /* XXX Commenting the following is interesting but results it lower
+          * brightness.  This is used as part of image scaling. */
           if (xrepl>=2) {
             pixelptr[1] = pix;
             if (xrepl>=3) pixelptr[2] = pix;
@@ -1681,6 +1780,7 @@ static void analogtv_thread_draw_lines(void *thread_raw)
 
       pixbright=it->contrast_control * puramp(it, 1.0f, 0.0f, 1.0f)
         / (0.5f+0.5f*it->puheight) * 1024.0f/100.0f;
+      /* pixbright *= 2; XXX This affects brightness */
       pixmultinc=pixrate;
       i=scanstart_i; rrp=rgb_start;
       while (i<0 && rrp!=rgb_end) {
@@ -2027,7 +2127,7 @@ analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
 
   img_w=pic_im->width;
   img_h=pic_im->height;
-  
+
   for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
     double phase=90.0-90.0*i;
     double ampl=1.0;
diff --git a/hacks/images/6x10font.png b/hacks/images/6x10font.png
new file mode 100644 (file)
index 0000000..21e72f3
Binary files /dev/null and b/hacks/images/6x10font.png differ
diff --git a/hacks/images/analogtermfont.png b/hacks/images/analogtermfont.png
new file mode 100644 (file)
index 0000000..e5ce900
Binary files /dev/null and b/hacks/images/analogtermfont.png differ
diff --git a/hacks/images/analogtermfont.xbm b/hacks/images/analogtermfont.xbm
new file mode 100644 (file)
index 0000000..deaff75
--- /dev/null
@@ -0,0 +1,60 @@
+#define analogterm_font_width 672
+#define analogterm_font_height 8
+static char analogterm_font_bits[] = {
+  0x00, 0x04, 0x85, 0x82, 0x30, 0x10, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 
+  0x00, 0x80, 0x1C, 0x04, 0xC7, 0x07, 0xF1, 0x61, 0x7C, 0x1C, 0x0E, 0x00, 
+  0x00, 0x01, 0x10, 0x38, 0x1C, 0x84, 0x87, 0xE3, 0xF1, 0xF9, 0x78, 0x22, 
+  0x0E, 0x48, 0x24, 0x10, 0x89, 0x38, 0x1E, 0x8E, 0x87, 0xE3, 0x13, 0x89, 
+  0x44, 0x22, 0x91, 0x8F, 0x17, 0xF0, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 
+  0x02, 0x60, 0x00, 0x02, 0x04, 0x44, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x70, 0xB0, 0x00, 
+  0x00, 0x04, 0x85, 0xC2, 0x33, 0x29, 0x10, 0x04, 0x88, 0x0A, 0x01, 0x00, 
+  0x00, 0x40, 0x22, 0x86, 0x08, 0x84, 0x11, 0x10, 0x40, 0x22, 0x11, 0x00, 
+  0x80, 0x00, 0x20, 0x44, 0x22, 0x8A, 0x48, 0x24, 0x12, 0x08, 0x04, 0x22, 
+  0x04, 0x48, 0x22, 0xB0, 0x89, 0x44, 0x22, 0x91, 0x48, 0x84, 0x10, 0x89, 
+  0x44, 0x22, 0x11, 0x88, 0x20, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 
+  0x02, 0x90, 0x00, 0x02, 0x00, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0xC0, 0x68, 0x54, 
+  0x00, 0x04, 0xC5, 0xA7, 0x80, 0x28, 0x10, 0x02, 0x10, 0x07, 0x01, 0x00, 
+  0x00, 0x20, 0x2A, 0x04, 0x08, 0x42, 0xF1, 0x08, 0x20, 0x22, 0x11, 0x02, 
+  0x41, 0xF0, 0x41, 0x20, 0x2A, 0x91, 0x48, 0x20, 0x12, 0x08, 0x04, 0x22, 
+  0x04, 0x48, 0x21, 0x50, 0x99, 0x44, 0x22, 0x91, 0x48, 0x80, 0x10, 0x89, 
+  0x44, 0x14, 0x0A, 0x84, 0x40, 0x80, 0x20, 0x00, 0x10, 0x8E, 0x87, 0xC7, 
+  0xE3, 0x10, 0x38, 0x1E, 0x04, 0x46, 0x84, 0xB0, 0x79, 0x38, 0x1E, 0x1E, 
+  0x8F, 0xE7, 0x13, 0x89, 0x44, 0x22, 0x91, 0x8F, 0x81, 0xC0, 0x00, 0x28, 
+  0x00, 0x04, 0x80, 0xC2, 0x41, 0x10, 0x00, 0x02, 0xD0, 0xDF, 0x07, 0xF0, 
+  0x01, 0x10, 0x2A, 0x04, 0x06, 0x23, 0x01, 0x79, 0x10, 0x1C, 0x1E, 0x00, 
+  0x20, 0x00, 0x80, 0x10, 0x3A, 0x91, 0x47, 0x20, 0xF2, 0x78, 0x64, 0x3E, 
+  0x04, 0xC8, 0x20, 0x50, 0xA9, 0x44, 0x1E, 0x91, 0x87, 0x83, 0x10, 0x89, 
+  0x54, 0x08, 0x04, 0x82, 0x80, 0x80, 0x50, 0x00, 0x00, 0x90, 0x48, 0x20, 
+  0x12, 0x79, 0x44, 0x22, 0x04, 0x44, 0x82, 0x50, 0x89, 0x44, 0x22, 0x91, 
+  0x40, 0x80, 0x10, 0x89, 0x44, 0x14, 0x11, 0xC4, 0x80, 0x80, 0x01, 0x54, 
+  0x00, 0x04, 0xC0, 0x87, 0x22, 0xA8, 0x00, 0x02, 0x10, 0x07, 0x01, 0x00, 
+  0x00, 0x08, 0x2A, 0x04, 0x01, 0xE4, 0x03, 0x89, 0x08, 0x22, 0x10, 0x00, 
+  0x40, 0xF0, 0x41, 0x10, 0x1A, 0x9F, 0x48, 0x20, 0x12, 0x08, 0x44, 0x22, 
+  0x04, 0x48, 0x21, 0x10, 0xC9, 0x44, 0x02, 0x95, 0x02, 0x84, 0x10, 0x89, 
+  0x54, 0x14, 0x04, 0x81, 0x00, 0x81, 0x88, 0x00, 0x00, 0x9E, 0x48, 0x20, 
+  0xF2, 0x11, 0x44, 0x22, 0x04, 0xC4, 0x81, 0x50, 0x89, 0x44, 0x22, 0x91, 
+  0x80, 0x83, 0x10, 0x89, 0x54, 0x08, 0x11, 0x82, 0x81, 0xC0, 0x00, 0x28, 
+  0x00, 0x00, 0x80, 0xE2, 0x91, 0x49, 0x00, 0x04, 0x88, 0x0A, 0x81, 0x00, 
+  0x00, 0x04, 0x22, 0x84, 0x40, 0x04, 0x11, 0x89, 0x08, 0x22, 0x08, 0x02, 
+  0x81, 0x00, 0x20, 0x00, 0x02, 0x91, 0x48, 0x24, 0x12, 0x08, 0x44, 0x22, 
+  0x84, 0x48, 0x22, 0x10, 0x89, 0x44, 0x02, 0x89, 0x44, 0x84, 0x10, 0x51, 
+  0x6C, 0x22, 0x84, 0x80, 0x00, 0x82, 0x00, 0x00, 0x00, 0x91, 0x48, 0x20, 
+  0x12, 0x10, 0x78, 0x22, 0x04, 0x44, 0x82, 0x50, 0x89, 0x44, 0x1E, 0x9E, 
+  0x00, 0x84, 0x10, 0x51, 0x54, 0x14, 0x1E, 0x81, 0x81, 0xC0, 0x00, 0x54, 
+  0x00, 0x04, 0x80, 0x82, 0x80, 0xB1, 0x00, 0x08, 0x04, 0x00, 0x40, 0x00, 
+  0x20, 0x02, 0x1C, 0x84, 0x8F, 0x03, 0xE1, 0x70, 0x08, 0x1C, 0x06, 0x80, 
+  0x00, 0x01, 0x10, 0x10, 0x3C, 0x91, 0x87, 0xE3, 0xF1, 0x09, 0x78, 0x22, 
+  0x0E, 0x47, 0xE4, 0x13, 0x89, 0x38, 0x02, 0x96, 0x88, 0x83, 0xE0, 0x20, 
+  0x44, 0x22, 0x84, 0x8F, 0x07, 0xF4, 0x00, 0x00, 0x00, 0x9E, 0x87, 0xC7, 
+  0xE3, 0x11, 0x40, 0x22, 0x84, 0x44, 0x84, 0x11, 0x89, 0x38, 0x02, 0x90, 
+  0xC0, 0x83, 0xE1, 0x21, 0x6C, 0x22, 0x90, 0x0F, 0x87, 0x70, 0x00, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x00, 0x38, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x80, 0x00, 0x00, 0x00, 
+  };
diff --git a/hacks/images/analogtermgfxfont.png b/hacks/images/analogtermgfxfont.png
new file mode 100644 (file)
index 0000000..19d000f
Binary files /dev/null and b/hacks/images/analogtermgfxfont.png differ
diff --git a/hacks/images/analogtermgfxfont.xbm b/hacks/images/analogtermgfxfont.xbm
new file mode 100644 (file)
index 0000000..f36dec1
--- /dev/null
@@ -0,0 +1,60 @@
+#define analogtermgfx_font_width 672
+#define analogtermgfx_font_height 8
+static char analogtermgfx_font_bits[] = {
+  0x00, 0x04, 0x85, 0x82, 0x30, 0x10, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 
+  0x00, 0x80, 0x1C, 0x04, 0xC7, 0x07, 0xF1, 0x61, 0x7C, 0x1C, 0x0E, 0x00, 
+  0x00, 0x01, 0x10, 0x38, 0x1C, 0x84, 0x87, 0xE3, 0xF1, 0xF9, 0x78, 0x22, 
+  0x0E, 0x48, 0x24, 0x10, 0x89, 0x38, 0x1E, 0x8E, 0x87, 0xE3, 0x13, 0x89, 
+  0x44, 0x22, 0x91, 0x8F, 0x17, 0xF0, 0x00, 0x00, 0x00, 0x55, 0xE1, 0x60, 
+  0x08, 0x00, 0x00, 0x85, 0x02, 0x02, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 
+  0x00, 0x80, 0x40, 0x20, 0x00, 0x08, 0x18, 0x01, 0x00, 0xC1, 0x01, 0x00, 
+  0x00, 0x04, 0x85, 0xC2, 0x33, 0x29, 0x10, 0x04, 0x88, 0x0A, 0x01, 0x00, 
+  0x00, 0x40, 0x22, 0x86, 0x08, 0x84, 0x11, 0x10, 0x40, 0x22, 0x11, 0x00, 
+  0x80, 0x00, 0x20, 0x44, 0x22, 0x8A, 0x48, 0x24, 0x12, 0x08, 0x04, 0x22, 
+  0x04, 0x48, 0x22, 0xB0, 0x89, 0x44, 0x22, 0x91, 0x48, 0x84, 0x10, 0x89, 
+  0x44, 0x22, 0x11, 0x88, 0x20, 0x80, 0x00, 0x00, 0x88, 0x6A, 0x21, 0x10, 
+  0x08, 0x20, 0x10, 0x85, 0x02, 0x02, 0x00, 0x40, 0x20, 0xFE, 0x00, 0x00, 
+  0x00, 0x80, 0x40, 0x20, 0x00, 0x08, 0x06, 0x06, 0x00, 0x21, 0x02, 0x54, 
+  0x00, 0x04, 0xC5, 0xA7, 0x80, 0x28, 0x10, 0x02, 0x10, 0x07, 0x01, 0x00, 
+  0x00, 0x20, 0x2A, 0x04, 0x08, 0x42, 0xF1, 0x08, 0x20, 0x22, 0x11, 0x02, 
+  0x41, 0xF0, 0x41, 0x20, 0x2A, 0x91, 0x48, 0x20, 0x12, 0x08, 0x04, 0x22, 
+  0x04, 0x48, 0x21, 0x50, 0x99, 0x44, 0x22, 0x91, 0x48, 0x80, 0x10, 0x89, 
+  0x44, 0x14, 0x0A, 0x84, 0x40, 0x80, 0x20, 0x00, 0x1C, 0xD5, 0x61, 0x10, 
+  0x08, 0x50, 0x10, 0x87, 0x02, 0x02, 0x00, 0x40, 0x20, 0x00, 0x7F, 0x00, 
+  0x00, 0x80, 0x40, 0x20, 0x00, 0x08, 0x01, 0xC8, 0xE7, 0x23, 0x00, 0x28, 
+  0x00, 0x04, 0x80, 0xC2, 0x41, 0x10, 0x00, 0x02, 0xD0, 0xDF, 0x07, 0xF0, 
+  0x01, 0x10, 0x2A, 0x04, 0x06, 0x23, 0x01, 0x79, 0x10, 0x1C, 0x1E, 0x00, 
+  0x20, 0x00, 0x80, 0x10, 0x3A, 0x91, 0x47, 0x20, 0xF2, 0x78, 0x64, 0x3E, 
+  0x04, 0xC8, 0x20, 0x50, 0xA9, 0x44, 0x1E, 0x91, 0x87, 0x83, 0x10, 0x89, 
+  0x54, 0x08, 0x04, 0x82, 0x80, 0x80, 0x50, 0x00, 0xBE, 0x6A, 0x3D, 0x1E, 
+  0x8F, 0x23, 0x7C, 0x15, 0xF9, 0xE3, 0x81, 0xC7, 0xFF, 0x01, 0x80, 0x3F, 
+  0x00, 0x80, 0x7F, 0xFC, 0xFF, 0x08, 0x06, 0xA6, 0x82, 0x70, 0x00, 0x54, 
+  0x00, 0x04, 0xC0, 0x87, 0x22, 0xA8, 0x00, 0x02, 0x10, 0x07, 0x01, 0x00, 
+  0x00, 0x08, 0x2A, 0x04, 0x01, 0xE4, 0x03, 0x89, 0x08, 0x22, 0x10, 0x00, 
+  0x40, 0xF0, 0x41, 0x10, 0x1A, 0x9F, 0x48, 0x20, 0x12, 0x08, 0x44, 0x22, 
+  0x04, 0x48, 0x21, 0x10, 0xC9, 0x44, 0x02, 0x95, 0x02, 0x84, 0x10, 0x89, 
+  0x54, 0x14, 0x04, 0x81, 0x00, 0x81, 0x88, 0x00, 0x1C, 0x55, 0x29, 0x62, 
+  0xBD, 0x00, 0x10, 0x15, 0x11, 0x00, 0x81, 0x00, 0x20, 0x00, 0x00, 0xC0, 
+  0x1F, 0x80, 0x40, 0x00, 0x10, 0x08, 0x98, 0x81, 0xE2, 0x23, 0x20, 0x28, 
+  0x00, 0x00, 0x80, 0xE2, 0x91, 0x49, 0x00, 0x04, 0x88, 0x0A, 0x81, 0x00, 
+  0x00, 0x04, 0x22, 0x84, 0x40, 0x04, 0x11, 0x89, 0x08, 0x22, 0x08, 0x02, 
+  0x81, 0x00, 0x20, 0x00, 0x02, 0x91, 0x48, 0x24, 0x12, 0x08, 0x44, 0x22, 
+  0x84, 0x48, 0x22, 0x10, 0x89, 0x44, 0x02, 0x89, 0x44, 0x84, 0x10, 0x51, 
+  0x6C, 0x22, 0x84, 0x80, 0x00, 0x82, 0x00, 0x00, 0x88, 0x2A, 0x08, 0x06, 
+  0x83, 0x01, 0x10, 0x10, 0x10, 0x00, 0x81, 0x00, 0x20, 0x00, 0x00, 0x00, 
+  0xE0, 0x8F, 0x40, 0x00, 0x10, 0x08, 0x00, 0x80, 0x42, 0x20, 0x00, 0x54, 
+  0x00, 0x04, 0x80, 0x82, 0x80, 0xB1, 0x00, 0x08, 0x04, 0x00, 0x40, 0x00, 
+  0x20, 0x02, 0x1C, 0x84, 0x8F, 0x03, 0xE1, 0x70, 0x08, 0x1C, 0x06, 0x80, 
+  0x00, 0x01, 0x10, 0x10, 0x3C, 0x91, 0x87, 0xE3, 0xF1, 0x09, 0x78, 0x22, 
+  0x0E, 0x47, 0xE4, 0x13, 0x89, 0x38, 0x02, 0x96, 0x88, 0x83, 0xE0, 0x20, 
+  0x44, 0x22, 0x84, 0x8F, 0x07, 0xF4, 0x00, 0x00, 0x00, 0x15, 0x08, 0x02, 
+  0x85, 0x00, 0x7C, 0x10, 0x10, 0x00, 0x81, 0x00, 0x20, 0x00, 0x00, 0x00, 
+  0x00, 0x80, 0x40, 0x00, 0x10, 0x08, 0x9F, 0x4F, 0x44, 0xD0, 0x03, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x80, 0x2A, 0x08, 0x02, 
+  0x85, 0x00, 0x00, 0x70, 0x10, 0x00, 0x81, 0x00, 0x20, 0x00, 0x00, 0x00, 
+  0x00, 0x80, 0x40, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+  };
diff --git a/hacks/images/apple2font.png b/hacks/images/apple2font.png
new file mode 100644 (file)
index 0000000..a96cee7
Binary files /dev/null and b/hacks/images/apple2font.png differ
index f0d7fe8..cce25c8 100644 (file)
  * swapBSDEL: bool     Swap Backspace and Delete in pty-mode.
  *
  * On iOS and Android, textclient-mobile.c is used instead.
+ *
+ * Adapted for more complete xterm emulation by Matthew Mondor.
  */
 
+#include <stdbool.h>
 #include "utils.h"
 
 #if !defined(USE_IPHONE) && !defined(HAVE_ANDROID)  /* whole file */
@@ -178,7 +181,7 @@ launch_text_generator (text_data *d)
           /* This is the child fork. */
           char *av[10];
           int i = 0;
-         if (putenv ("TERM=vt100"))
+         if (putenv ("TERM=xterm"))
             abort();
           av[i++] = "/bin/sh";
           av[i++] = "-c";
@@ -191,9 +194,36 @@ launch_text_generator (text_data *d)
        }
       else
        {
+         struct termios tios;
+
           /* This is the parent fork. */
           if (d->pipe) abort();
          d->pipe = fdopen (fd, "r+");
+
+         /*
+          * XXX Set tty atributes
+          * - If we don't IXON, as with the original implementation, ctrl-s
+          *   causes a freeze where ctrl-q cannot resume.  With IXON this
+          *   gets ignored.  Ideally we'd want user-controlled xon/xoff to
+          *   work properly.
+          *   It may also be possible to set ERASE here to match BS/DEL
+          *   settings.
+          */
+         if (tcgetattr(fd, &tios) != -1) {
+                 tios.c_iflag &= ~(BRKINT | INPCK | ISTRIP | IXON);
+                 tios.c_iflag |= ICRNL;
+                 tios.c_oflag |= ONLCR;
+                 tios.c_cflag &= ~CS8;
+                 tios.c_cflag |= CS7;
+                 tios.c_lflag &= ~(ICANON | IEXTEN);
+                 tios.c_lflag |= ECHO | ECHONL | ECHOK;
+                 /*tios.c_lflag |= ISIG; If we want to get SIGINT */
+                 if (tcsetattr(fd, TCSAFLUSH, &tios) == -1)
+                         abort();
+         }
+         /* XXX Disable stdio buffering */
+         /*(void)setvbuf(d->pipe, NULL, _IONBF, 0);*/
+
           if (d->pipe_id) abort();
          d->pipe_id =
            XtAppAddInput (app, fileno (d->pipe),
@@ -382,12 +412,18 @@ textclient_open (Display *dpy)
 # ifdef HAVE_FORKPTY
   /* Kludge for MacOS standalone mode: see OSX/SaverRunner.m. */
   {
+         /* XXX Code duplicated in analogterm-main and this one seems to have
+          * precedence for some reason */
+         /*
     const char *s = getenv ("XSCREENSAVER_STANDALONE");
     if (s && *s && strcmp(s, "0"))
       {
+      */
         d->pty_p = 1;
         d->program = strdup (getenv ("SHELL"));
+       /*
       }
+      */
   }
 # endif
 
@@ -532,19 +568,286 @@ meta_modifier (text_data *d)
   return d->meta_mask;
 }
 
+FILE *
+textclient_pipe(text_data *d)
+{
+
+       return d->pipe;
+}
+
+/* Returns the most appropriate string depending on state */
+/* XXX Interestingly unless using XK_Control_R only, even if it actually
+ * corresponds to left-ctrl, there is confusion with shift+ctrl. */
+static const char *
+kv(unsigned int state, const char *norm, const char *shift,
+    const char *ctrl, const char *shiftctrl)
+{
+       bool ctrl_p = (state & XK_Control_R) != 0;
+       bool shift_p = (state & (XK_Shift_L | XK_Shift_R)) != 0;
+
+       if (shiftctrl && ctrl_p && shift_p)
+               return shiftctrl;
+       if (shift && shift_p)
+               return shift;
+       if (ctrl && ctrl_p)
+               return ctrl;
+       return norm;
+}
 
 Bool
 textclient_putc (text_data *d, XKeyEvent *k)
 {
   KeySym keysym;
   unsigned char c = 0;
+  const char *send = NULL;
+
   XLookupString (k, (char *) &c, 1, &keysym, &d->compose);
+
+  /* Keys to be sent as vt100 sequences */
+  /* XXX Update this comment */
+  /* XXX Ideally we should initially build a table at application setup using
+   * the terminfo database and taking TERM in consideration.  Since xterm and
+   * rxvt attempt to be vt100 compatible and that this terminal's early
+   * implementation also targetted vt100, let's just issue these directly.
+   * Moreover, at least on modern unix, ncurse's terminfo took over and does
+   * not support well non-xterm terminals other than allowing a type of
+   * automatic functionality degradation and support for others.  Thus "vt100"
+   * is an overly limited TERM on those systems, especially if the application
+   * software also uses current ncurses or terminfo.  For this reason, we
+   * should really only care about xterm support.  Afterall, it is the most
+   * actively supported terminal today, with tmux/screen and most third party
+   * terminal applications based on it.  Home/End/PgUp/PgDn etc are only
+   * really expected to properly work with xterm/rxvt and close modern variants.
+   * Querying the terminfo database for many terminals will reveal that those
+   * keys are generally not defined otherwise.  Unless using a non-terminfo
+   * based terminal as well as a non-terminfo based application specialized
+   * for a particular terminal other than xterm, support will be suboptimal.
+   * It may be worth it for a vt52 terminal dedicated to run CP/M-80
+   * applications.
+   * Moreover, terminfo definitions for vt100 add ridiculous delays for
+   * historical reasons, causing a lot of NUL byte padding and slowing down
+   * software like more/less.  Thus it may not be worth it to use terminfo for
+   * now, or to use vt100 traditional codes.
+   */
+        switch (keysym) {
+       case XK_Insert:
+               send = kv(k->state,
+                   "\033[@", "\033[2;2~", "\033[2;5~", "\033[2;6~");
+               break;
+       case XK_Delete:
+               send = kv(k->state,
+                   "\033[3~", "\033[3;2~", "\033[3;5~", "\033[3;6~");
+               break;
+       case XK_Home:
+               send = kv(k->state,
+                   "\033OH", "\033[1;2H", "\033[1;5H", "\033[1;6H");
+               break;
+       case XK_Left:
+               send = kv(k->state,
+                   "\033OD", "\033[1;2D", "\033[1;5D", "\033[1;6D");
+               break;
+       case XK_Up:
+               send = kv(k->state,
+                   "\033OA", "\033[1;2A", "\033[1;5A", "\033[1;6A");
+               break;
+       case XK_Right:
+               send = kv(k->state,
+                   "\033OC", "\033[1;2C", "\033[1;5C", "\033[1;6C");
+               break;
+       case XK_Down:
+               send = kv(k->state,
+                   "\033OB", "\033[1;2B", "\033[1;5B", "\033[1;6B");
+               break;
+       case XK_Page_Up:
+               send = kv(k->state,
+                   "\033[5~", "\033[5;2~", "\033[5;5~", "\033[5;6~");
+               break;
+       case XK_Page_Down:
+               send = kv(k->state,
+                   "\033[6~", "\033[6;2~", "\033[6;5~", "\033[6;6~");
+               break;
+       case XK_End:
+               send = kv(k->state,
+                   "\033OF", "\033[1;2F", "\033[1;5F", "\033[1;6F");
+               break;
+       case XK_Begin:
+               send = kv(k->state,
+                   "\033[H", "\033[1;2H", "\033[1;5H", "\033[1;6H");
+               break;
+       case XK_Tab:
+               send = kv(k->state,
+                   "\011", "\033[Z", "\011", "\033[Z");
+               break;
+       case XK_ISO_Left_Tab:
+               /*
+                * BackTab (back_tab/cbt, key_btab/kcbt)
+                * Probably redundant with above XK_Tab handling.
+                */
+               send = "\033[Z";
+               break;
+
+       /* The following is to be able to send the NUL character. */
+       case XK_2:
+       case XK_at: /* FALLTHROUGH */
+               send = kv(k->state,
+                   "2", "@", "\0", "\0");
+               break;
+       case XK_space:
+       case XK_KP_Space: /* FALLTHROUGH */
+               send = kv(k->state,
+                   " ", " ", "\0", "\0");
+               break;
+       }
+       if (send != NULL)
+               goto send;
+
+       switch (keysym) {
+       case XK_F1:
+               send = kv(k->state,
+                   "\033OP", "\033[1;2P", "\033[1;5P", "\033[1;6P");
+               break;
+       case XK_F2:
+               send = kv(k->state,
+                   "\033OQ", "\033[1;2Q", "\033[1;5Q", "\033[1;6Q");
+               break;
+       case XK_F3:
+               send = kv(k->state,
+                   "\033OR", "\033[1;2R", "\033[1;5R", "\033[1;6R");
+               break;
+       case XK_F4:
+               send = kv(k->state,
+                   "\033OS", "\033[1;2S", "\033[1;5S", "\033[1;6S");
+               break;
+       case XK_F5:
+               send = kv(k->state,
+                   "\033[15~", "\033[15;2~", "\033[15;5~", "\033[15;6~");
+               break;
+       case XK_F6:
+               send = kv(k->state,
+                   "\033[17~", "\033[17;2~", "\033[17;5~", "\033[17;6~");
+               break;
+       case XK_F7:
+               send = kv(k->state,
+                   "\033[18~", "\033[18;2~", "\033[18;5~", "\033[18;6~");
+               break;
+       case XK_F8:
+               send = kv(k->state,
+                   "\033[19~", "\033[19;2~", "\033[19;5~", "\033[19;6~");
+               break;
+       case XK_F9:
+               send = kv(k->state,
+                   "\033[20~", "\033[20;2~", "\033[20;5~", "\033[20;6~");
+               break;
+       case XK_F10:
+               send = kv(k->state,
+                   "\033[21~", "\033[21;2~", "\033[21;5~", "\033[21;6~");
+               break;
+       case XK_F11:
+               send = kv(k->state,
+                   "\033[23~", "\033[23;2~", "\033[23;5~", "\033[23;6~");
+               break;
+       case XK_F12:
+               send = kv(k->state,
+                   "\033[24~", "\033[24;2~", "\033[24;5~", "\033[24;6~");
+               break;
+       }
+       if (send != NULL)
+               goto send;
+
+       /* Numpad.  For these, shift and ctrl-shift result in the n */
+       switch (keysym) {
+       case XK_KP_Insert:
+               send = kv(k->state,
+                   "\033[@", "0", "\033[2;5~", "0");
+               break;
+       case XK_KP_Delete:
+               send = kv(k->state,
+                   "\033[3~", ".", "\033[3;5~", ".");
+               break;
+       case XK_KP_Left:
+               send = kv(k->state,
+                   "\033OD", "4", "\033[1;5D", "4");
+               break;
+       case XK_KP_Up:
+               send = kv(k->state,
+                   "\033OA", "8", "\033[1;5A", "8");
+               break;
+       case XK_KP_Right:
+               send = kv(k->state,
+                   "\033OC", "6", "\033[1;5C", "6");
+               break;
+       case XK_KP_Down:
+               send = kv(k->state,
+                   "\033OB", "2", "\033[1;5B", "2");
+               break;
+       case XK_KP_Page_Up:
+               send = kv(k->state,
+                   "\033[5~", "9", "\033[5;5~", "9");
+               break;
+       case XK_KP_Page_Down:
+               send = kv(k->state,
+                   "\033[6~", "3", "\033[6;5~", "3");
+               break;
+       case XK_KP_End:
+               send = kv(k->state,
+                   "\033OF", "1", "\033[1;5F", "1");
+               break;
+       case XK_KP_Begin:
+       case XK_KP_Home: /* FALLTHROUGH */
+               send = kv(k->state,
+                   "\033[H", "7", "\033[1;5H", "7");
+               break;
+       }
+       if (send != NULL)
+               goto send;
+       /* I don't have these but X11 does, only for 1-4 */
+       switch (keysym) {
+       case XK_KP_F1:
+               send = kv(k->state,
+                   "\033OP", "\033[1;2P", "\033[1;5P", "\033[1;6P");
+               break;
+       case XK_KP_F2:
+               send = kv(k->state,
+                   "\033OQ", "\033[1;2Q", "\033[1;5Q", "\033[1;6Q");
+               break;
+       case XK_KP_F3:
+               send = kv(k->state,
+                   "\033OR", "\033[1;2R", "\033[1;5R", "\033[1;6R");
+               break;
+       case XK_KP_F4:
+               send = kv(k->state,
+                   "\033OS", "\033[1;2S", "\033[1;5S", "\033[1;6S");
+               break;
+       }
+
+send:
+       if (send != NULL) {
+               int len = strlen(send);
+
+               /* Handle the NUL special case */
+               if (len == 0)
+                       (void)fwrite("\0", 1, 1, d->pipe);
+               else
+                       (void)fwrite(send, len, 1, d->pipe);
+               (void)fflush(d->pipe);
+               return True;
+       }
+
   if (c != 0 && d->pipe)
     {
+      /* XXX May need to swap above in X11 key handling */
       if (!d->swap_bs_del_p) ;
       else if (c == 127) c = 8;
       else if (c == 8)   c = 127;
 
+      /* Issue terminal code for the delete key */
+      /* XXX Now redundant with XP_Delete above */
+      if (c == 127) {
+             (void)fprintf(d->pipe, "\033[3~");
+             (void)fflush(d->pipe);
+             return True;
+      }
+
       /* If meta was held down, send ESC, or turn on the high bit. */
       if (k->state & meta_modifier (d))
         {
index 0d9aca3..232b7b8 100644 (file)
@@ -27,6 +27,7 @@ extern void textclient_reshape (text_data *,
                                 int char_w, int char_h,
                                 int max_lines);
 extern int textclient_getc (text_data *);
+extern FILE *textclient_pipe(text_data *);
 extern Bool textclient_putc (text_data *, XKeyEvent *);
 
 # if defined(USE_IPHONE) || defined(HAVE_ANDROID)