- Implemented smooth scrolling
authorMatthew Mondor <mmondor@pulsar-zone.net>
Thu, 14 Apr 2022 13:20:53 +0000 (13:20 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Thu, 14 Apr 2022 13:20:53 +0000 (13:20 +0000)
- Added code to set tty ERASE using TERM but currently disabled

MATT/TODO.txt
hacks/analogterm-main.c
hacks/analogterm.c
hacks/analogterm.h
utils/textclient.c

index 58e651b..933a6fc 100644 (file)
@@ -9,6 +9,16 @@ BUILD
 TODO
 ====
 
+- Now that upwards smooth scrolling has been implemented:
+  - May be possible to do the same for downwards
+  - May need to support sending XOFF/XON to control speed, if too fast it
+    still jump scrolls.
+  - In tmux/screen, if no title bar or if it's a the top, it works, but then
+    it's a bit strange to see the top bar move and refresh.  Only applying the
+    offset within the scroll range may possibly help.
+  - Works quite nicely for log display with sleepycat(1).  A similar
+    application that delays at the line level, or simulates microdelays using
+    special characters might allow more precise speed tuning.
 - Various latin characters look strange and could be improved.
 - Links appears to enter DEC Special Graphics for some reason and to not exit
   it.  This does not happen under tmux(1).
@@ -53,6 +63,17 @@ TODO
   I may need to use the ncurses source or to decompile a terminfo entry to
   look at it.  Moreover, it's unclear what xterm variant is used for "xterm",
   there are many yet none named exactly that way.  What a mess.
+  infocmp can show compiled definitions.
+  tput kbs | xxd can show the entry.
+  Like tset(1) does, terminfo can also be intialized with setupterm(3) and
+  key_backspace used to get it and then set it via tcsetattr(3).
+  It should be determined if bs/del swap should automatically be activated
+  when 0x7f/^? is kbs/key_backspace rather than 0x08/^H/backspace.
+  The code seems to work for this but requires special OS considerations.
+  On Linux, -lncurses -ltinfo must be linked, on NetBSD, -lterminfo.
+  Unsure that I want to mess with autoconfig very soon with the now stripped
+  configure script.  The two modules needing these extra LIBS are: apple2 and
+  analogterm.
 - Maybe cleanup code checking boundaries, using max or other similar
   macros or inline functions.
 - When blinking and double-underline, the line doesn't blink.
index 7c68611..771c82e 100644 (file)
@@ -388,6 +388,7 @@ at_vt100_printc (analogterm_sim_t *sim, struct terminal_controller_data *state,
          /* 0-based */
          st->scroll_top = 0;
          st->scroll_bottom = rows - 1;
+         st->scroll_slow = false;
          state->mode = 0;
           state->escstate = 0;
           break;
@@ -599,6 +600,10 @@ at_vt100_printc (analogterm_sim_t *sim, struct terminal_controller_data *state,
           break;
        case 'h': /* DEC Private Mode Set (DECSET) */
          switch (state->csiparam[0]) {
+         case 4: /* Smooth (slow) scroll (DECSCLM) */
+              st->scroll_slow = true;
+             state->escstate = 0;
+             break;
          case 12: /* XXX Start blinking cursor */
          case 13: /* FALLTHROUGH */
              break;
@@ -647,6 +652,10 @@ at_vt100_printc (analogterm_sim_t *sim, struct terminal_controller_data *state,
           break;
        case 'l': /* DEC Private Mode Reset (DECRST) */
          switch (state->csiparam[0]) {
+         case 4: /* Fast/jump scroll (DECSCLM) */
+             st->scroll_slow = false;  
+             break;
+             state->escstate = 0;
          case 12: /* XXX Stop blinking cursor */
          case 13: /* FALLTHROUGH */
              break;
index e0e19de..3530d13 100644 (file)
@@ -117,8 +117,9 @@ at_insert(analogterm_state_t *st, int many)
                *mptr = st->mode & TMODE_BGCOLOR;
 }
 
-int cursor_waitnext = 2;
-bool cursor_laststate = false;
+static smooth_scroll_offset = 0;
+static int cursor_waitnext = 2;
+static bool cursor_laststate = false;
 
 void
 at_drawcursor(analogterm_sim_t *sim)
@@ -136,8 +137,10 @@ at_drawcursor(analogterm_sim_t *sim)
        cursor_laststate = st->blink;
 
        for (y = 0; y < 8; y++) {
-               pp = &sim->inp->signal[(ANALOGTV_TOP + 3) + (8 * st->cursy) + y]
-                  [(ANALOGTV_PIC_START + 100) + (7 * st->cursx)];
+               pp = &sim->inp->signal
+                   [(ANALOGTV_TOP + smooth_scroll_offset + 3) +
+                   (8 * st->cursy) + y]
+                   [(ANALOGTV_PIC_START + 100) + (7 * st->cursx)];
                for (x = 0; x < 7; x++)
                        *pp++ ^= 64/*TMODE_NORMAL_LEVEL*/;
        }
@@ -210,22 +213,34 @@ at_resetcursor(void)
 /*
  * Scroll page by offset (may be negative or positive) and takes in
  * consideration the scroll region/range setting.
+ * XXX Now that basic smooth scrolling support was added, it seems that we
+ * should also limit the input speed when it's enabled, possibly via
+ * XON/XOFF...  We'd send XOFF when asked to scroll but that offset is still
+ * not 0, then we'd XON, something similar.  It's also possible that we need a
+ * line-sized buffer for I/O with the application.
  */
 void
 at_scroll_range(analogterm_state_t *st, int offset)
 {
-       int y;
-
-       /* XXX */
-       /*
-       if (st->cursy == 24 && offset == -1)
-               (void)fprintf(stderr, "Smooth scroll possible.\n");
-       */
+       int y, aoffset = abs(offset);
+       bool smooth = false;
 
        /* Nothing to do */
        if (offset == 0)
                return;
 
+       if (st->scroll_slow && aoffset > 1) {
+               /* Slow scroll, loop one at a time */
+               int step = (offset < 0 ? -1 : 1);
+
+               for (y = 0; y < aoffset; y++)
+                       at_scroll_range(st, step);
+               return;
+       }
+
+       if (st->scroll_slow && st->cursy == 24 && offset == -1)
+               smooth = true;
+
        /* Only one line to clear */
        if (st->scroll_top == st->scroll_bottom) {
                at_clear(st, 0, st->cursy, 80);
@@ -233,7 +248,7 @@ at_scroll_range(analogterm_state_t *st, int offset)
        }
 
        /* Nothing to move, clear whole scrolling region */
-       if (abs(offset) > st->scroll_bottom - st->scroll_top) {
+       if (aoffset > st->scroll_bottom - st->scroll_top) {
                for (y = st->scroll_top; y < st->scroll_bottom; y++)
                        at_clear(st, 0, y, 80);
                return;
@@ -247,6 +262,8 @@ at_scroll_range(analogterm_state_t *st, int offset)
                for (y = st->scroll_bottom + 1 + offset; y <= st->scroll_bottom;
                     y++)
                        at_clear(st, 0, y, 80);
+               if (smooth)
+                       smooth_scroll_offset = 8;
        } else {
                /* Bottom to top moving lines down, blank at top */
                for (y = st->scroll_bottom - offset; y >= st->scroll_top; y--)
@@ -538,6 +555,7 @@ analogterm_start(Display *dpy, Window window, int delay,
   /* Default scrolling region to full screen, 0-based */
   sim->st->scroll_top = 0;
   sim->st->scroll_bottom = 24;         /* XXX Use definition/macro */
+  sim->st->scroll_slow = false;
 
   /* Create font */
   sim->font_unknown = at_make_font(sim, 0, analogterm_unknown_bits,
@@ -748,7 +766,8 @@ analogterm_one_frame (analogterm_sim_t *sim)
         * high enough that it would be possible to implement smooth scrolling
         * by sliding a window.
         */
-       pp=&sim->inp->signal[row + ANALOGTV_TOP + 3][ANALOGTV_PIC_START + 100];
+       pp=&sim->inp->signal[row + ANALOGTV_TOP + smooth_scroll_offset + 3]
+           [ANALOGTV_PIC_START + 100];
        for (col = 0; col < 80; col++) {
             int rev, level, olevel, iblink;
             uint32_t c = st->textlines_char[textrow][col];
@@ -812,6 +831,8 @@ analogterm_one_frame (analogterm_sim_t *sim)
       }
     }
     at_drawcursor(sim);
+    if (smooth_scroll_offset > 0)
+           smooth_scroll_offset--;
     analogtv_reception_update(&sim->reception);
     {
       const analogtv_reception *rec = &sim->reception;
index b22ba0a..2644c0d 100644 (file)
@@ -15,6 +15,9 @@
 #ifndef __XSCREENSAVER_APPLE_II__
 #define __XSCREENSAVER_APPLE_II__
 
+#include <stdbool.h>
+#include <stdint.h>
+
 #include "analogtv.h"
 
 
@@ -56,6 +59,7 @@ typedef struct analogterm_state {
   uint16_t mode;
   int blink; /* XXX Still needed? */
   int scroll_top, scroll_bottom;
+  bool scroll_slow;
   uint32_t lastchar;
 } analogterm_state_t;
 
index b581b62..d18edcc 100644 (file)
 # endif
 #endif /* HAVE_FORKPTY */
 
+#ifdef DETECTBS
+/* Terminfo, ncurses on GNU */
+#ifndef __GLIBC__
+#include <curses.h>
+#endif
+#include <term.h>
+#endif
+
+
 #undef DEBUG
 
 extern const char *progname;
@@ -180,26 +189,38 @@ launch_text_generator (text_data *d)
        {
           /* This is the child fork. */
           char *av[10];
-          int i = 0;
+         int i = 0, bschar = 8;
+#ifdef DETECTBS
+         int rval, bschar = -1;
+#endif
          struct termios tios;
+
+         /*
+          * Set terminal to xterm, the terminal we emulate.
+          */
          if (putenv ("TERM=xterm"))
             abort();
 
          /*
-          * XXX Set client-side tty settings
-          * - tset(1) and xterm(1) can set the ERASE character to the
-          *   termcap/terminfo entry in use.  Since we simulate a subset of
-          *   xterm, and that the terminfo definition uses ^H for it, we
-          *   simply set it to that for now.  Like xterm(1) does, we also set
-          *   VEOL2.
+          * Attempt to obtain the erasechar from the terminal definition.
+          * This varies on systems between ^H/0x08/bs and ^?/0x7f/del.
+          * We'll then use this to set the tty's ERASE.
           */
-         if (tcgetattr(STDIN_FILENO, &tios) != -1) {
-                 tios.c_lflag |= ICANON;
-                 tios.c_cc[VERASE] = CTRL('h');
-                 tios.c_cc[VEOL2] = CTRL('@');
-                 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tios) == -1)
-                         abort();
+#ifdef DETECTBS
+         if (setupterm(NULL, STDERR_FILENO, &rval) == -1)
+           abort();
+         if (key_backspace != NULL && key_backspace[1] == '\0')
+                 bschar = key_backspace[0];
+#endif
+         if (bschar != -1) {
+                 if (tcgetattr(STDIN_FILENO, &tios) != -1) {
+                         tios.c_lflag |= ICANON;
+                         tios.c_cc[VERASE] = bschar;
+                         if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tios) == -1)
+                                 abort();
+                 }
          }
+         /* XXX Should we enable bs/del swap if bschar == 127? */
 
          /* Start shell */
           av[i++] = "/bin/sh";