"*dpmsOff: 4:00:00",
"*grabDesktopImages: True",
"*grabVideoFrames: False",
-"*chooseRandomImages: True",
-"*imageDirectory: /Library/Desktop Pictures/",
+"*chooseRandomImages: False",
+"*imageDirectory: ",
"*nice: 10",
"*memoryLimit: 0",
"*lock: False",
"*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",
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,
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 \
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 \
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 \
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)
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)
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
--- /dev/null
+/* 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)
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+/* 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__ */
- 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 */
#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;
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);
}
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);
- }
+ }
}
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];
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) {
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;
}
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++) {
}
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;
}
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
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;
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;
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) {
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;
--- /dev/null
+#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,
+ };
--- /dev/null
+#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,
+ };
* 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 */
/* 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";
}
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),
# 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
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))
{
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)