From 999d0c02a7991674fd4baf35f3f37190e7e898ed Mon Sep 17 00:00:00 2001 From: Matthew Mondor Date: Thu, 7 Apr 2022 10:24:30 +0000 Subject: [PATCH] Initial import of analogterm (phase 2, after forgetting to add) --- driver/XScreenSaver_ad.h | 188 +++--- hacks/Makefile.in | 43 +- hacks/analogterm-main.c | 1141 ++++++++++++++++++++++++++++++++++++ hacks/analogterm.c | 624 ++++++++++++++++++++ hacks/analogterm.h | 148 +++++ hacks/analogtv.c | 118 +++- hacks/images/6x10font.png | Bin 0 -> 1431 bytes hacks/images/analogtermfont.png | Bin 0 -> 607 bytes hacks/images/analogtermfont.xbm | 60 ++ hacks/images/analogtermgfxfont.png | Bin 0 -> 620 bytes hacks/images/analogtermgfxfont.xbm | 60 ++ hacks/images/apple2font.png | Bin 0 -> 600 bytes utils/textclient.c | 305 +++++++++- utils/textclient.h | 1 + 14 files changed, 2583 insertions(+), 105 deletions(-) create mode 100644 hacks/analogterm-main.c create mode 100644 hacks/analogterm.c create mode 100644 hacks/analogterm.h create mode 100644 hacks/images/6x10font.png create mode 100644 hacks/images/analogtermfont.png create mode 100644 hacks/images/analogtermfont.xbm create mode 100644 hacks/images/analogtermgfxfont.png create mode 100644 hacks/images/analogtermgfxfont.xbm create mode 100644 hacks/images/apple2font.png diff --git a/driver/XScreenSaver_ad.h b/driver/XScreenSaver_ad.h index 5ca109b..b1eec29 100644 --- a/driver/XScreenSaver_ad.h +++ b/driver/XScreenSaver_ad.h @@ -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\ @@ -81,44 +81,44 @@ 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\ @@ -127,85 +127,85 @@ 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\ @@ -215,62 +215,62 @@ 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", diff --git a/hacks/Makefile.in b/hacks/Makefile.in index ccc0b61..eaf56ec 100644 --- a/hacks/Makefile.in +++ b/hacks/Makefile.in @@ -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 index 0000000..9fee557 --- /dev/null +++ b/hacks/analogterm-main.c @@ -0,0 +1,1141 @@ +/* xscreensaver, Copyright (c) 1998-2014 Jamie Zawinski + * + * 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 + * with additional work by Jamie Zawinski + * Pty and vt100 emulation by Fredrik Tolf + * + * 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 +#include + +#ifdef HAVE_UNISTD_H +# include +#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 + + + +/* 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 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, [L should + * insert 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 > 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 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 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; itc) + 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 index 0000000..feeb693 --- /dev/null +++ b/hacks/analogterm.c @@ -0,0 +1,624 @@ +/* xscreensaver, Copyright (c) 1998-2010 Jamie Zawinski + * + * 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 + * with additional work by Jamie Zawinski + * 2022 Adapted to analogterm and more complete xterm emulation by Matthew Mondor. + */ + +#include +#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 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; rowinp->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 index 0000000..a19e4f8 --- /dev/null +++ b/hacks/analogterm.h @@ -0,0 +1,148 @@ +/* xscreensaver, Copyright (c) 1998-2004 Jamie Zawinski + * + * 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 + * with additional work by Jamie Zawinski + */ + +#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__ */ diff --git a/hacks/analogtv.c b/hacks/analogtv.c index 28930c5..aa23275 100644 --- a/hacks/analogtv.c +++ b/hacks/analogtv.c @@ -64,6 +64,12 @@ - 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; i65535) intensity=65535; it->red_values[i]=((intensity>>it->red_invprec)<red_shift); it->green_values[i]=((intensity>>it->green_invprec)<green_shift); it->blue_values[i]=((intensity>>it->blue_invprec)<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; heightleveltable[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; i004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00002 zVoOIv00G^K6QlqD00(qQO+^RR10NPG8iI-x+yDRspGibPR7l6AmCsKbM-;~`Re}pO z*Qx?@=snG;I3jraA8=_vs*<=glB1^a54FV$aqU%vR3ED1TR9-v#~ctL5^6v!-Yv?( zs+1h6Dl(DT9nn>b5*G$T%oxVAuWxptf{H?w%wgZ|&b*oTneV(e0dVKRZyIoRAFbdF z2)s?BZ{-bD@3{(4R5W}c7||vWYFJs2rtI=MR5e440hlD4lRwR<@Qe@~ygp6#N$eo;TZ5Kzy!M7G`Px`>HVgpr!O_JS}C9}b1HTyoches3qRCY!pTpC!u<(L zPX`BD9GHtk4seC7wjZ+aP4Qs=`RwrAr$5~}+aQa=G7c1Qi3V;{(uN&?Qt4oiGfHkK zhpk(a9bF;Og#5gjy$4VshAF5+$;~o;Q5kNtUXp!H4BjZ`pdJ8wPG_`EFZqD)904kd zyDtG(#=d0KrnncPTBNqhgsz&J#w~q5ZY@$vCzj46*_JJhy3JjjTLSKb3Bo-Em~&rU zAf7>3hJcy`*oG=03jB^iBxOXELg?yv(=v0SJ>GC?DtsC8lqY@6QG*NQ?w zFMM)^QAR&hPAX`_+GQ2zzW)AUoe>&EK(0q=`5Vgh+o8x#mM^Gru}BF5W_Du|&_%%N zye-^qfT`luCARUP^vvFU={)$c7*^#TbQ@wzj;t*v&Ne`Te3@5ZWGIPbl$eBljzPrB zoHK7ooFJf!0oFe992Pvkng_q*NOQ2aSsC)+pUA#ZD*zpHdTRqFhqO~iR2j@VXxx=7 zKDWF2b~L7w(!keW@XEBA1((gLz2eaZnI!AHdB@79LlHLioD##8qV30P{iD_l*)p0j zYbF*K5?-})u6DkF_R67TJGpMky7!-Nu=UxBY6)rq#I z;7SbKoh*CMDHVJn6=ZiUGi}-kSS2~Yq=)ae_#+jM?_as5l*b*E@V8Av!1)T=hDc?$ zU__YapR&3DyC=Dm#TAOl_ul2ve-w;K2R4I#WmSTxf~X0qQ&Z76n5v(HwwHtEu!VrX=(Y<6x}{M; zwxLS}6R03qj}d-a%efb%plz&Yc>~lS!5ox;dlE26qRajs#I0jM52Ee^{;SUiWHYg6 zGglISU=hwH;|;<*|dxl2lOUpwJ8Pbv1!G*p5tO>iT|n@c=HUqVEorxqt#Bv5G<`!0SV~ z<3?Y~@sFsiqCHq-@?Md#WY%TrZ@z-Jyi{yB6~6%Gr!QSMM@0bdLA(m&t#Mkx(@g+B zQR>dvdv@Y|)i+q&lG2(-ed_n=DZM&eMX8-^IC3HntbYx+4WjbwdWNBu305UKzHZ3qS lEi*AxGBP?eH99gjEig4YFfgmK^V0wT002ovPDHLkV1kV*ed7QC literal 0 HcmV?d00001 diff --git a/hacks/images/analogtermfont.png b/hacks/images/analogtermfont.png new file mode 100644 index 0000000000000000000000000000000000000000..e5ce90035ebc69350df875c6f0d06ac8ce185ebb GIT binary patch literal 607 zcmV-l0-*hgP)vDss2^BP=c;B>#n#h-obRu?E52lOwx=knJv=onB#K5%8{4 zm~#iQSgx}PIk}q|!d7~2CW$6bne05@WUsLHHIL!J=Y8MzI~%C(q(1?)LMof7&3fnA zr>u_{L!L>m(YZTj{o)k-JlXRa+rD5y?dlX!Sw#K^xe^xtEpIl*4T2uscmQg05F?iC zwU!~2EJTY&uy@7jd##u%*xbf4Z@x3AlC;7}*187Td+ zqYVprciJ_BpfDA5cX7`_EkQ>Iva|+a&)aN3o~T!H-p-SxcCET1TH!fC?NJwy|GHG4 z#xPL(H{&*B2^zHUCfG8EAu?~SC?RsIDR!Y%+ z2p%DM8j<@eocqVJDote(|Eu{~i14{ofrN4Eu?dSaEr1_iDlePL6g7(PMuw+umkY>wOb7%LM tEw6Ypr1bxwhLY`M;%diU3#;4ghK4>4@{87u$*002ovPDHLkV1k^T5sv@> literal 0 HcmV?d00001 diff --git a/hacks/images/analogtermfont.xbm b/hacks/images/analogtermfont.xbm new file mode 100644 index 0000000..deaff75 --- /dev/null +++ b/hacks/images/analogtermfont.xbm @@ -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 index 0000000000000000000000000000000000000000..19d000f86598960e387d4fcaa3b5c8b7a19ccae8 GIT binary patch literal 620 zcmV-y0+aoTP)gaZYWy}8pIFEZJAzP(k!)@i1gnIHdo{}PI3_ya%+q@^P< zS8lELqIE|u$} zovX_rhFczD#$L+Afs8q*QN?UCJ$Nex6Dj+iYiR3JP@X59<5a=6ynM zUC~Ftp-iX>gxqaO#T?!lbyQ%dszBS5J2|Qe-P=kstuVT=3UUHwk#y!a>K$LM;t`>G zrvu1O&X-3X1j4U+w;}4HLJhC%dG+S-^HXuGCU@hmZPvi*OA%^4!6zHnyGFIwSgN>l zB~PD2J@dXK{TJaF^Q31c`-VkQ83=E`Zb=51jXLmpHcuMoN#S11Z*uy6e^0pR4l!C> zes>L_;h;+a@a%%l5abtkyQ(ND>TUZz(o9`M!IDfkgi4-Y<#s;Pm?bHre{(BN;L%5x zXbfqlf%I!002C;m8aax3S51@ZP(MU!KSRZ%#Hg=~W#nsWA3oUB{sU@%et*Fl@aMzY zDYcgv(hY)a*{h@g0000LqiJ#!!Mvv!wUw6QUeBtR|yOZRx=nF#0%!^3bX-AFeQ1ryD%``?Gj!B zTGYaskN6e+og6x+tytZ&qgqAetvdiw`SA5 zPkbjfJXJdO#;1+%$Qj38feouy)N8W}3;y9y8!o@9Qs_JC;k&JQ zR_>DnuRI=R#`-+ClCYlTtkxnM(LDWpPqve8JLY(s*{Mq>nm<@QXHnU%M*^$G9Iupr zle^T*!pfceP+&@gt3#f;Ih#(8MCy|;0jH0ht-(3x^RDF9HeAW_`S|CT)4nf?wZXxw zKV$^2{(U8D*Yu5Tf1cQWU;ASA{;>b%?)D3JM2eh!yDkG5{Hi6c5hW>!C8<`)MX5lF z!N|bKNY}tf*T^8m(8$Wz)XKzM*TB@uz+l0)$LS~ #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)) { diff --git a/utils/textclient.h b/utils/textclient.h index 0d9aca3..232b7b8 100644 --- a/utils/textclient.h +++ b/utils/textclient.h @@ -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) -- 2.9.0