#include <ctype.h>
#include <time.h>
+#include <assert.h>
#include "putty.h"
#include "tree234.h"
static tree234 *scrollback; /* lines scrolled off top of screen */
static tree234 *screen; /* lines on primary screen */
static tree234 *alt_screen; /* lines on alternate screen */
-static int disptop; /* distance scrolled back (0 or negative) */
+static int disptop; /* distance scrolled back (0 or -ve) */
static unsigned long *cpos; /* cursor position (convenience) */
static unsigned long *disptext; /* buffer of text on real screen */
static unsigned long *wanttext; /* buffer of text we want on screen */
-#define VBELL_TIMEOUT 100 /* millisecond duration of visual bell */
+#define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
struct beeptime {
struct beeptime *next;
static unsigned char *selspace; /* buffer for building selections in */
#define TSIZE (sizeof(unsigned long))
-#define lineptr(x) ( (unsigned long *) \
- ((x) >= 0 ? index234(screen, x) : \
- index234(scrollback, (x)+count234(scrollback)) ) )
-#define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
+#define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
static unsigned long curr_attr, save_attr;
static unsigned long erase_char = ERASE_CHAR;
static int cset; /* 0 or 1: which char set */
static int save_cset, save_csattr; /* saved with cursor position */
static int rvideo; /* global reverse video flag */
+static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
static int cursor_on; /* cursor enabled flag */
static int reset_132; /* Flag ESC c resets to 80 cols */
static int use_bce; /* Use Background coloured erase */
static void logtraffic(unsigned char c, int logmode);
/*
+ * Retrieve a line of the screen or of the scrollback, according to
+ * whether the y coordinate is non-negative or negative
+ * (respectively).
+ */
+unsigned long *lineptr(int y, int lineno) {
+ unsigned long *line, lineattrs;
+ tree234 *whichtree;
+ int i, treeindex, oldlen;
+
+ if (y >= 0) {
+ whichtree = screen;
+ treeindex = y;
+ } else {
+ whichtree = scrollback;
+ treeindex = y + count234(scrollback);
+ }
+ line = index234(whichtree, treeindex);
+
+ /* We assume that we don't screw up and retrieve something out of range. */
+ assert(line != NULL);
+
+ if (line[0] != cols) {
+ /*
+ * This line is the wrong length, which probably means it
+ * hasn't been accessed since a resize. Resize it now.
+ */
+ oldlen = line[0];
+ lineattrs = line[oldlen+1];
+ delpos234(whichtree, treeindex);
+ line = srealloc(line, TSIZE * (2+cols));
+ line[0] = cols;
+ for (i = oldlen; i < cols; i++)
+ line[i+1] = ERASE_CHAR;
+ line[cols+1] = lineattrs & LATTR_MODE;
+ addpos234(whichtree, line, treeindex);
+ }
+
+ return line+1;
+}
+#define lineptr(x) lineptr(x,__LINE__)
+/*
* Set up power-on settings for the terminal.
*/
static void power_on(void) {
tree234 *newsb, *newscreen, *newalt;
unsigned long *newdisp, *newwant, *oldline, *line;
int i, j, ccols;
- int posn, oldposn, furthest_back, oldsbsize;
+ int sblen;
int save_alt_which = alt_which;
if (newrows == rows && newcols == cols && newsavelines == savelines)
alt_t = marg_t = 0;
alt_b = marg_b = newrows - 1;
- newsb = newtree234(NULL);
- newscreen = newtree234(NULL);
- ccols = (cols < newcols ? cols : newcols);
- oldsbsize = scrollback ? count234(scrollback) : 0;
- furthest_back = newrows - rows - oldsbsize;
- if (furthest_back > 0)
- furthest_back = 0;
- if (furthest_back < -newsavelines)
- furthest_back = -newsavelines;
- for (posn = newrows; posn-- > furthest_back ;) {
- oldposn = posn - newrows + rows;
- if (rows == -1 || oldposn < -oldsbsize) {
- line = smalloc(TSIZE * (newcols + 1));
- for (j = 0; j < newcols; j++)
- line[j] = erase_char;
- line[newcols] = 0;
- } else {
- oldline = (oldposn >= 0 ?
- delpos234(screen, count234(screen)-1) :
- delpos234(scrollback, count234(scrollback)-1));
- if (newcols != cols) {
- line = smalloc(TSIZE * (newcols + 1));
- for (j = 0; j < ccols; j++)
- line[j] = oldline[j];
- for (j = ccols; j < newcols; j++)
- line[j] = erase_char;
- line[newcols] = oldline[cols] & LATTR_MODE;
- sfree(oldline);
+ if (rows == -1) {
+ scrollback = newtree234(NULL);
+ screen = newtree234(NULL);
+ rows = 0;
+ }
+
+ /*
+ * Resize the screen and scrollback. We only need to shift
+ * lines around within our data structures, because lineptr()
+ * will take care of resizing each individual line if
+ * necessary. So:
+ *
+ * - If the new screen and the old screen differ in length, we
+ * must shunt some lines in from the scrollback or out to
+ * the scrollback.
+ *
+ * - If doing that fails to provide us with enough material to
+ * fill the new screen (i.e. the number of rows needed in
+ * the new screen exceeds the total number in the previous
+ * screen+scrollback), we must invent some blank lines to
+ * cover the gap.
+ *
+ * - Then, if the new scrollback length is less than the
+ * amount of scrollback we actually have, we must throw some
+ * away.
+ */
+ sblen = count234(scrollback);
+ if (newrows > rows) {
+ for (i = rows; i < newrows; i++) {
+ if (sblen > 0) {
+ line = delpos234(scrollback, --sblen);
} else {
- line = oldline;
+ line = smalloc(TSIZE * (newcols+2));
+ line[0] = newcols;
+ for (j = 0; j <= newcols; j++)
+ line[j+1] = ERASE_CHAR;
}
+ addpos234(screen, line, 0);
+ }
+ } else if (newrows < rows) {
+ for (i = newrows; i < rows; i++) {
+ line = delpos234(screen, 0);
+ addpos234(scrollback, line, sblen++);
}
- if (posn >= 0)
- addpos234(newscreen, line, 0);
- else
- addpos234(newsb, line, 0);
}
+ assert(count234(screen) == newrows);
+ while (sblen > newsavelines) {
+ line = delpos234(scrollback, 0);
+ sfree(line);
+ sblen--;
+ }
+ assert(count234(scrollback) <= newsavelines);
disptop = 0;
- if (scrollback) freetree234(scrollback);
- if (screen) freetree234(screen);
- scrollback = newsb;
- screen = newscreen;
newdisp = smalloc (newrows*(newcols+1)*TSIZE);
for (i=0; i<newrows*(newcols+1); i++)
newalt = newtree234(NULL);
for (i=0; i<newrows; i++) {
- line = smalloc(TSIZE * (newcols+1));
+ line = smalloc(TSIZE * (newcols+2));
+ line[0] = newcols;
for (j = 0; j <= newcols; j++)
- line[j] = erase_char;
+ line[j+1] = erase_char;
addpos234(newalt, line, i);
}
if (alt_screen) {
while (lines < 0) {
line = delpos234(screen, botline);
for (i = 0; i < cols; i++)
- line[i] = erase_char;
- line[cols] = 0;
+ line[i+1] = erase_char;
+ line[cols+1] = 0;
addpos234(screen, line, topline);
if (selstart.y >= topline && selstart.y <= botline) {
} else {
while (lines > 0) {
line = delpos234(screen, topline);
- if (sb) {
+ if (sb && savelines > 0) {
int sblen = count234(scrollback);
/*
* We must add this line to the scrollback. We'll
* replace it, or allocate a new one if the
* scrollback isn't full.
*/
- if (sblen == savelines)
+ if (sblen == savelines) {
sblen--, line2 = delpos234(scrollback, 0);
- else
- line2 = smalloc(TSIZE * (cols+1));
+ } else {
+ line2 = smalloc(TSIZE * (cols+2));
+ line2[0] = cols;
+ }
addpos234(scrollback, line, sblen);
line = line2;
}
for (i = 0; i < cols; i++)
- line[i] = erase_char;
- line[cols] = 0;
+ line[i+1] = erase_char;
+ line[cols+1] = 0;
addpos234(screen, line, botline);
if (selstart.y >= topline && selstart.y <= botline) {
ldata[start.x] &= ~ATTR_WRAPPED;
else
ldata[start.x] = erase_char;
- if (incpos(start))
+ if (incpos(start) && start.y < rows)
ldata = lineptr(start.y);
}
}
* whether the mode is a DEC private one or a normal one.)
*/
static void toggle_mode (int mode, int query, int state) {
+ long ticks;
if (query) switch (mode) {
case 1: /* application cursor keys */
app_cursor_keys = state;
reset_132 = state;
break;
case 5: /* reverse video */
+ /*
+ * Toggle reverse video. If we receive an OFF within the
+ * visual bell timeout period after an ON, we trigger an
+ * effective visual bell, so that ESC[?5hESC[?5l will
+ * always be an actually _visible_ visual bell.
+ */
+ ticks = GetTickCount();
+ if (rvideo && !state && /* we're turning it off */
+ ticks < rvbell_timeout) { /* and it's not long since it was turned on */
+ in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
+ if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
+ vbell_timeout = rvbell_timeout; /* vbell end is at least then */
+ } else if (!rvideo && state) {
+ /* This is an ON, so we notice the time and save it. */
+ rvbell_timeout = ticks + VBELL_TIMEOUT;
+ }
rvideo = state;
seen_disp_event = TRUE;
if (state) term_update();
* t seconds ago.
*/
while (beephead &&
- beephead->ticks < ticks - cfg.bellovl_t*1000) {
+ beephead->ticks < ticks - cfg.bellovl_t) {
struct beeptime *tmp = beephead;
beephead = tmp->next;
sfree(tmp);
}
if (cfg.bellovl && beep_overloaded &&
- ticks-lastbeep >= cfg.bellovl_s * 1000) {
+ ticks-lastbeep >= cfg.bellovl_s) {
/*
* If we're currently overloaded and the
* last beep was more than s seconds ago,