From e40f58de8a63079728d9e182ec28f69dbdcf59b0 Mon Sep 17 00:00:00 2001 From: Matthew Mondor Date: Thu, 14 Apr 2022 13:20:53 +0000 Subject: [PATCH] - Implemented smooth scrolling - Added code to set tty ERASE using TERM but currently disabled --- MATT/TODO.txt | 21 +++++++++++++++++++++ hacks/analogterm-main.c | 9 +++++++++ hacks/analogterm.c | 47 ++++++++++++++++++++++++++++++++++------------- hacks/analogterm.h | 4 ++++ utils/textclient.c | 47 ++++++++++++++++++++++++++++++++++------------- 5 files changed, 102 insertions(+), 26 deletions(-) diff --git a/MATT/TODO.txt b/MATT/TODO.txt index 58e651b..933a6fc 100644 --- a/MATT/TODO.txt +++ b/MATT/TODO.txt @@ -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. diff --git a/hacks/analogterm-main.c b/hacks/analogterm-main.c index 7c68611..771c82e 100644 --- a/hacks/analogterm-main.c +++ b/hacks/analogterm-main.c @@ -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; diff --git a/hacks/analogterm.c b/hacks/analogterm.c index e0e19de..3530d13 100644 --- a/hacks/analogterm.c +++ b/hacks/analogterm.c @@ -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; diff --git a/hacks/analogterm.h b/hacks/analogterm.h index b22ba0a..2644c0d 100644 --- a/hacks/analogterm.h +++ b/hacks/analogterm.h @@ -15,6 +15,9 @@ #ifndef __XSCREENSAVER_APPLE_II__ #define __XSCREENSAVER_APPLE_II__ +#include +#include + #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; diff --git a/utils/textclient.c b/utils/textclient.c index b581b62..d18edcc 100644 --- a/utils/textclient.c +++ b/utils/textclient.c @@ -60,6 +60,15 @@ # endif #endif /* HAVE_FORKPTY */ +#ifdef DETECTBS +/* Terminfo, ncurses on GNU */ +#ifndef __GLIBC__ +#include +#endif +#include +#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"; -- 2.9.0