struct beeptime {
struct beeptime *next;
- long ticks;
+ unsigned long ticks;
};
static struct beeptime *beephead, *beeptail;
int nbeeps;
#define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
#define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
+static bufchain inbuf; /* terminal input buffer */
static pos curs; /* cursor */
static pos savecurs; /* saved cursor position */
static int marg_t, marg_b; /* scroll margins */
static int save_cset, save_csattr; /* saved with cursor position */
static int save_utf; /* saved with cursor position */
static int rvideo; /* global reverse video flag */
-static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
+static unsigned long rvbell_startpoint;/* 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 */
big_cursor = 0;
save_attr = curr_attr = ATTR_DEFAULT;
term_editing = term_echoing = FALSE;
- ldisc_send(NULL, 0); /* cause ldisc to notice changes */
+ ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
app_cursor_keys = cfg.app_cursor;
app_keypad_keys = cfg.app_keypad;
use_bce = cfg.bce;
Context ctx;
ctx = get_ctx();
if (ctx) {
- if (seen_disp_event)
- update_sbar();
+ int need_sbar_update = seen_disp_event;
if ((seen_key_event && (cfg.scroll_on_key)) ||
(seen_disp_event && (cfg.scroll_on_disp))) {
disptop = 0; /* return to main screen */
seen_disp_event = seen_key_event = 0;
+ need_sbar_update = TRUE;
}
+ if (need_sbar_update)
+ update_sbar();
do_paint(ctx, TRUE);
sys_cursor(curs.x, curs.y - disptop);
free_ctx(ctx);
update_sbar();
term_update();
+ back->size();
}
/*
static void scroll(int topline, int botline, int lines, int sb)
{
unsigned long *line, *line2;
- int i;
+ int i, seltop;
if (topline != 0 || alt_which != 0)
sb = FALSE;
}
addpos234(scrollback, line, sblen);
line = line2;
+
+ /*
+ * If the user is currently looking at part of the
+ * scrollback, and they haven't enabled any options
+ * that are going to reset the scrollback as a
+ * result of this movement, then the chances are
+ * they'd like to keep looking at the same line. So
+ * we move their viewpoint at the same rate as the
+ * scroll, at least until their viewpoint hits the
+ * top end of the scrollback buffer, at which point
+ * we don't have the choice any more.
+ *
+ * Thanks to Jan Holmen Holsten for the idea and
+ * initial implementation.
+ */
+ if (disptop > -savelines && disptop < 0)
+ disptop--;
}
line = resizeline(line, cols);
for (i = 0; i < cols; i++)
line[cols + 1] = 0;
addpos234(screen, line, botline);
- if (selstart.y >= topline && selstart.y <= botline) {
+ /*
+ * If the selection endpoints move into the scrollback,
+ * we keep them moving until they hit the top. However,
+ * of course, if the line _hasn't_ moved into the
+ * scrollback then we don't do this, and cut them off
+ * at the top of the scroll region.
+ *
+ * This applies to selstart and selend (for an existing
+ * selection), and also selanchor (for one being
+ * selected as we speak).
+ */
+ seltop = sb ? -savelines : topline;
+
+ if (selstart.y >= seltop && selstart.y <= botline) {
selstart.y--;
- if (selstart.y < topline) {
- selstart.y = topline;
+ if (selstart.y < seltop) {
+ selstart.y = seltop;
selstart.x = 0;
}
}
- if (selend.y >= topline && selend.y <= botline) {
+ if (selend.y >= seltop && selend.y <= botline) {
selend.y--;
- if (selend.y < topline) {
- selend.y = topline;
+ if (selend.y < seltop) {
+ selend.y = seltop;
selend.x = 0;
}
}
+ if (selanchor.y >= seltop && selanchor.y <= botline) {
+ selanchor.y--;
+ if (selanchor.y < seltop) {
+ selanchor.y = seltop;
+ selanchor.x = 0;
+ }
+ }
lines--;
}
*/
static void toggle_mode(int mode, int query, int state)
{
- long ticks;
+ unsigned long ticks;
if (query)
switch (mode) {
break;
case 3: /* 80/132 columns */
deselect();
- request_resize(state ? 132 : 80, rows, 1);
+ request_resize(state ? 132 : 80, rows);
reset_132 = state;
break;
case 5: /* reverse video */
* 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 */
+ /* turn off a previous vbell to avoid inconsistencies */
+ if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
+ in_vbell = FALSE;
+ if (rvideo && !state && /* we're turning it off... */
+ (ticks - rvbell_startpoint) < VBELL_TIMEOUT) { /* ...soon */
+ /* If there's no vbell timeout already, or this one lasts
+ * longer, replace vbell_timeout with ours. */
+ if (!in_vbell ||
+ (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
+ vbell_startpoint = rvbell_startpoint;
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;
+ rvbell_startpoint = ticks;
}
rvideo = state;
seen_disp_event = TRUE;
break;
case 10: /* set local edit mode */
term_editing = state;
- ldisc_send(NULL, 0); /* cause ldisc to notice changes */
+ ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
break;
case 25: /* enable/disable cursor */
compatibility2(OTHER, VT220);
break;
case 12: /* set echo mode */
term_echoing = !state;
- ldisc_send(NULL, 0); /* cause ldisc to notice changes */
+ ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
break;
case 20: /* Return sends ... */
cr_lf_return = state;
*/
void term_out(void)
{
- int c, inbuf_reap;
+ int c, unget;
+ unsigned char localbuf[256], *chars;
+ int nchars = 0;
+
+ unget = -1;
+
+ while (nchars > 0 || bufchain_size(&inbuf) > 0) {
+ if (unget == -1) {
+ if (nchars == 0) {
+ void *ret;
+ bufchain_prefix(&inbuf, &ret, &nchars);
+ if (nchars > sizeof(localbuf))
+ nchars = sizeof(localbuf);
+ memcpy(localbuf, ret, nchars);
+ bufchain_consume(&inbuf, nchars);
+ chars = localbuf;
+ assert(chars != NULL);
+ }
+ c = *chars++;
+ nchars--;
- /*
- * Optionally log the session traffic to a file. Useful for
- * debugging and possibly also useful for actual logging.
- */
- if (cfg.logtype == LGTYP_DEBUG)
- for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
- logtraffic((unsigned char) inbuf[inbuf_reap], LGTYP_DEBUG);
+ /*
+ * Optionally log the session traffic to a file. Useful for
+ * debugging and possibly also useful for actual logging.
+ */
+ if (cfg.logtype == LGTYP_DEBUG)
+ logtraffic((unsigned char) c, LGTYP_DEBUG);
+ } else {
+ c = unget;
+ unget = -1;
}
- for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
- c = inbuf[inbuf_reap];
-
/* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
* be able to display 8-bit characters, but I'll let that go 'cause
* of i18n.
case 4:
case 5:
if ((c & 0xC0) != 0x80) {
- inbuf_reap--;
+ unget = c;
c = UCSERR;
utf_state = 0;
break;
} else
*d++ = *s;
}
- lpage_send(CP_ACP, abuf, d - abuf);
+ lpage_send(CP_ACP, abuf, d - abuf, 0);
}
break;
case '\007':
{
struct beeptime *newbeep;
- long ticks;
+ unsigned long ticks;
ticks = GetTickCount();
}
if (cfg.bellovl && beep_overloaded &&
- ticks - lastbeep >= cfg.bellovl_s) {
+ ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
/*
* If we're currently overloaded and the
* last beep was more than s seconds ago,
beep(cfg.beep);
if (cfg.beep == BELL_VISUAL) {
in_vbell = TRUE;
- vbell_timeout = ticks + VBELL_TIMEOUT;
+ vbell_startpoint = ticks;
term_update();
}
}
width = wcwidth((wchar_t) c);
switch (width) {
case 2:
- if (curs.x + 1 != cols) {
- *cpos++ = c | ATTR_WIDE | curr_attr;
- *cpos++ = UCSWIDE | curr_attr;
- curs.x++;
- break;
+ *cpos++ = c | curr_attr;
+ if (++curs.x == cols) {
+ *cpos |= LATTR_WRAPPED;
+ if (curs.y == marg_b)
+ scroll(marg_t, marg_b, 1, TRUE);
+ else if (curs.y < rows - 1)
+ curs.y++;
+ curs.x = 0;
+ fix_cpos;
}
+ *cpos++ = UCSWIDE | curr_attr;
+ break;
case 1:
*cpos++ = c | curr_attr;
break;
break;
case 'Z': /* terminal type query */
compatibility(VT100);
- ldisc_send(id_string, strlen(id_string));
+ ldisc_send(id_string, strlen(id_string), 0);
break;
case 'c': /* restore power-on settings */
compatibility(VT100);
power_on();
if (reset_132) {
- request_resize(80, rows, 1);
+ request_resize(80, rows);
reset_132 = 0;
}
fix_cpos;
compatibility(OTHER);
/* this reports xterm version 136 so that VIM can
use the drag messages from the mouse reporting */
- ldisc_send("\033[>0;136;0c", 11);
+ ldisc_send("\033[>0;136;0c", 11, 0);
break;
case 'a': /* move right N cols */
compatibility(ANSI);
case 'c': /* terminal type query */
compatibility(VT100);
/* This is the response for a VT102 */
- ldisc_send(id_string, strlen(id_string));
+ ldisc_send(id_string, strlen(id_string), 0);
break;
case 'n': /* cursor position query */
if (esc_args[0] == 6) {
char buf[32];
sprintf(buf, "\033[%d;%dR", curs.y + 1,
curs.x + 1);
- ldisc_send(buf, strlen(buf));
+ ldisc_send(buf, strlen(buf), 0);
} else if (esc_args[0] == 5) {
- ldisc_send("\033[0n", 4);
+ ldisc_send("\033[0n", 4, 0);
}
break;
case 'h': /* toggle modes to high */
compatibility(VT340TEXT);
if (esc_nargs <= 1
&& (esc_args[0] < 1 || esc_args[0] >= 24)) {
- request_resize(cols, def(esc_args[0], 24), 0);
+ request_resize(cols, def(esc_args[0], 24));
deselect();
}
break;
*/
compatibility(VT420);
if (esc_nargs == 1 && esc_args[0] > 0) {
- request_resize(cols,
- def(esc_args[0], cfg.height),
- 0);
+ request_resize(cols, def(esc_args[0], cfg.height));
deselect();
}
break;
*/
compatibility(VT340TEXT);
if (esc_nargs <= 1) {
- request_resize(def(esc_args[0], cfg.width),
- rows, 0);
+ request_resize(def(esc_args[0], cfg.width), rows);
deselect();
}
break;
if (i == 0 || i == 1) {
strcpy(buf, "\033[2;1;1;112;112;1;0x");
buf[2] += i;
- ldisc_send(buf, 20);
+ ldisc_send(buf, 20, 0);
}
}
break;
*/
if (!has_compat(VT420) && has_compat(VT100)) {
if (reset_132)
- request_resize(132, 24, 1);
+ request_resize(132, 24);
else
- request_resize(80, 24, 1);
+ request_resize(80, 24);
}
#endif
break;
termstate = VT52_Y1;
break;
case 'Z':
- ldisc_send("\033/Z", 3);
+ ldisc_send("\033/Z", 3, 0);
break;
case '=':
app_keypad_keys = TRUE;
check_selection(curs, cursplus);
}
}
- inbuf_head = 0;
}
#if 0
pos scrpos;
char ch[1024];
long cursor_background = ERASE_CHAR;
- long ticks;
+ unsigned long ticks;
/*
* Check the visual bell state.
*/
if (in_vbell) {
ticks = GetTickCount();
- if (ticks - vbell_timeout >= 0)
- in_vbell = FALSE;
- }
+ if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
+ in_vbell = FALSE;
+ }
rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
if (dispcurs && (curstype != cursor ||
dispcurs !=
disptext + our_curs_y * (cols + 1) + curs.x)) {
- if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
+ if (dispcurs > disptext &&
+ (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
dispcurs[-1] |= ATTR_INVALID;
- if ((*dispcurs & ATTR_WIDE))
+ if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
dispcurs[1] |= ATTR_INVALID;
*dispcurs |= ATTR_INVALID;
curstype = 0;
}
tattr |= (tchar & CSET_MASK);
tchar &= CHAR_MASK;
+ if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+ tattr |= ATTR_WIDE;
/* Video reversing things */
tattr = (tattr ^ rv
tattr &= ~ATTR_BLINK;
}
+ /*
+ * Check the font we'll _probably_ be using to see if
+ * the character is wide when we don't want it to be.
+ */
+ if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
+ if ((tattr & ATTR_WIDE) == 0 &&
+ CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
+ tattr |= ATTR_NARROW;
+ } else if (disptext[idx]&ATTR_NARROW)
+ tattr |= ATTR_NARROW;
+
/* Cursor here ? Save the 'background' */
if (i == our_curs_y && j == curs.x) {
cursor_background = tattr | tchar;
/*
* Paint the window in response to a WM_PAINT message.
*/
-void term_paint(Context ctx, int l, int t, int r, int b)
+void term_paint(Context ctx, int left, int top, int right, int bottom)
{
- int i, j, left, top, right, bottom;
+ int i, j;
+ if (left < 0) left = 0;
+ if (top < 0) top = 0;
+ if (right >= cols) right = cols-1;
+ if (bottom >= rows) bottom = rows-1;
- left = l / font_width;
- right = (r - 1) / font_width;
- top = t / font_height;
- bottom = (b - 1) / font_height;
for (i = top; i <= bottom && i < rows; i++) {
if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
for (j = left; j <= right && j < cols; j++)
/* This should happen soon enough, also for some reason it sometimes
* fails to actually do anything when re-sizing ... painting the wrong
* window perhaps ?
- do_paint (ctx, FALSE);
*/
+ if (alt_pressed)
+ do_paint (ctx, FALSE);
}
/*
break;
}
+ /* For DBCS font's I can't do anything usefull. Even this will sometimes
+ * fail as there's such a thing as a double width space. :-(
+ */
+ if (dbcs_screenfont && font_codepage == line_codepage)
+ return (uc != ' ');
+
if (uc < 0x80)
return wordness[uc];
/* Assume a small paste will be OK in one go. */
if (paste_len < 256) {
- luni_send(paste_buffer, paste_len);
+ luni_send(paste_buffer, paste_len, 0);
if (paste_buffer)
sfree(paste_buffer);
paste_buffer = 0;
c = x + 33;
sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
- ldisc_send(abuf, 6);
+ ldisc_send(abuf, 6, 0);
return;
}
if (paste_buffer[paste_pos + n++] == '\r')
break;
}
- luni_send(paste_buffer + paste_pos, n);
+ luni_send(paste_buffer + paste_pos, n, 0);
paste_pos += n;
if (paste_pos < paste_len) {
*/
int from_backend(int is_stderr, char *data, int len)
{
- while (len--) {
- if (inbuf_head >= INBUF_SIZE)
- term_out();
- inbuf[inbuf_head++] = *data++;
- }
+ bufchain_add(&inbuf, data, len);
/*
- * We process all stdout/stderr data immediately we receive it,
- * and don't return until it's all gone. Therefore, there's no
- * reason at all to return anything other than zero from this
- * function.
- *
+ * term_out() always completely empties inbuf. Therefore,
+ * there's no reason at all to return anything other than zero
+ * from this function, because there _can't_ be a question of
+ * the remote side needing to wait until term_out() has cleared
+ * a backlog.
+ *
* This is a slightly suboptimal way to deal with SSH2 - in
* principle, the window mechanism would allow us to continue
* to accept data on forwarded ports and X connections even
* portability. So we manage stdout buffering the old SSH1 way:
* if the terminal processing goes slowly, the whole SSH
* connection stops accepting data until it's ready.
- *
+ *
* In practice, I can't imagine this causing serious trouble.
*/
return 0;