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).
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.
/* 0-based */
st->scroll_top = 0;
st->scroll_bottom = rows - 1;
+ st->scroll_slow = false;
state->mode = 0;
state->escstate = 0;
break;
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;
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;
*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)
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*/;
}
/*
* 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);
}
/* 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;
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--)
/* 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,
* 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];
}
}
at_drawcursor(sim);
+ if (smooth_scroll_offset > 0)
+ smooth_scroll_offset--;
analogtv_reception_update(&sim->reception);
{
const analogtv_reception *rec = &sim->reception;
#ifndef __XSCREENSAVER_APPLE_II__
#define __XSCREENSAVER_APPLE_II__
+#include <stdbool.h>
+#include <stdint.h>
+
#include "analogtv.h"
uint16_t mode;
int blink; /* XXX Still needed? */
int scroll_top, scroll_bottom;
+ bool scroll_slow;
uint32_t lastchar;
} analogterm_state_t;
# 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;
{
/* 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";