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) )
+/* Product-order comparisons for rectangular block selection. */
+#define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
+#define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
+
+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 */
static enum {
NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
} selstate;
+static enum {
+ LEXICOGRAPHIC, RECTANGULAR
+} seltype;
static enum {
SM_CHAR, SM_WORD, SM_LINE
} selmode;
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);
* selection), and also selanchor (for one being
* selected as we speak).
*/
- seltop = sb ? -savelines : 0;
+ seltop = sb ? -savelines : topline;
if (selstart.y >= seltop && selstart.y <= botline) {
selstart.y--;
*/
static void toggle_mode(int mode, int query, int state)
{
- long ticks;
+ unsigned long ticks;
if (query)
switch (mode) {
* 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();
}
}
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);
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 */
* illegal values (eg first arg 1..9) for window changing
* and reports.
*/
- compatibility(VT340TEXT);
if (esc_nargs <= 1
&& (esc_args[0] < 1 || esc_args[0] >= 24)) {
+ compatibility(VT340TEXT);
request_resize(cols, def(esc_args[0], 24));
deselect();
+ } else if (esc_nargs >= 1 &&
+ esc_args[0] >= 1 &&
+ esc_args[0] < 24) {
+ compatibility(OTHER);
+
+ switch (esc_args[0]) {
+ int x, y, len;
+ char buf[80], *p;
+ case 1:
+ set_iconic(FALSE);
+ break;
+ case 2:
+ set_iconic(TRUE);
+ break;
+ case 3:
+ if (esc_nargs >= 3) {
+ move_window(def(esc_args[1], 0),
+ def(esc_args[2], 0));
+ }
+ break;
+ case 4:
+ /* We should resize the window to a given
+ * size in pixels here, but currently our
+ * resizing code isn't healthy enough to
+ * manage it. */
+ break;
+ case 5:
+ set_zorder(TRUE); /* move to top */
+ break;
+ case 6:
+ set_zorder(FALSE); /* move to bottom */
+ break;
+ case 7:
+ refresh_window();
+ break;
+ case 8:
+ if (esc_nargs >= 3) {
+ request_resize(def(esc_args[1], cfg.width),
+ def(esc_args[2], cfg.height));
+ }
+ break;
+ case 9:
+ if (esc_nargs >= 2)
+ set_zoomed(esc_args[1] ? TRUE : FALSE);
+ break;
+ case 11:
+ ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
+ 4, 0);
+ break;
+ case 13:
+ get_window_pos(&x, &y);
+ len = sprintf(buf, "\033[3;%d;%dt", x, y);
+ ldisc_send(buf, len, 0);
+ break;
+ case 14:
+ get_window_pixels(&x, &y);
+ len = sprintf(buf, "\033[4;%d;%dt", x, y);
+ ldisc_send(buf, len, 0);
+ break;
+ case 18:
+ len = sprintf(buf, "\033[8;%d;%dt",
+ cols, rows);
+ ldisc_send(buf, len, 0);
+ break;
+ case 19:
+ /*
+ * Hmmm. Strictly speaking we
+ * should return `the size of the
+ * screen in characters', but
+ * that's not easy: (a) window
+ * furniture being what it is it's
+ * hard to compute, and (b) in
+ * resize-font mode maximising the
+ * window wouldn't change the
+ * number of characters. *shrug*. I
+ * think we'll ignore it for the
+ * moment and see if anyone
+ * complains, and then ask them
+ * what they would like it to do.
+ */
+ break;
+ case 20:
+ p = get_window_title(TRUE);
+ len = strlen(p);
+ ldisc_send("\033]L", 3, 0);
+ ldisc_send(p, len, 0);
+ ldisc_send("\033\\", 2, 0);
+ break;
+ case 21:
+ p = get_window_title(FALSE);
+ len = strlen(p);
+ ldisc_send("\033]l", 3, 0);
+ ldisc_send(p, len, 0);
+ ldisc_send("\033\\", 2, 0);
+ break;
+ }
}
break;
case 'S':
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;
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);
for (i = 0; i < rows; i++) {
unsigned long *ldata;
int lattr;
- int idx, dirty_line, dirty_run;
+ int idx, dirty_line, dirty_run, selected;
unsigned long attr = 0;
int updated_line = 0;
int start = 0;
tattr |= ATTR_WIDE;
/* Video reversing things */
+ if (seltype == LEXICOGRAPHIC)
+ selected = posle(selstart, scrpos) && poslt(scrpos, selend);
+ else
+ selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
tattr = (tattr ^ rv
- ^ (posle(selstart, scrpos) &&
- poslt(scrpos, selend) ? ATTR_REVERSE : 0));
+ ^ (selected ? ATTR_REVERSE : 0));
/* 'Real' blinking ? */
if (blink_is_real && (tattr & ATTR_BLINK)) {
term_update();
}
-static void clipme(pos top, pos bottom)
+static void clipme(pos top, pos bottom, int rect)
{
wchar_t *workbuf;
wchar_t *wbptr; /* where next char goes within workbuf */
+ int old_top_x;
int wblen = 0; /* workbuf len */
int buflen; /* amount of memory allocated to workbuf */
buflen = 5120; /* Default size */
workbuf = smalloc(buflen * sizeof(wchar_t));
wbptr = workbuf; /* start filling here */
+ old_top_x = top.x; /* needed for rect==1 */
while (poslt(top, bottom)) {
int nl = FALSE;
unsigned long *ldata = lineptr(top.y);
pos nlpos;
+ /*
+ * nlpos will point at the maximum position on this line we
+ * should copy up to. So we start it at the end of the
+ * line...
+ */
nlpos.y = top.y;
nlpos.x = cols;
+ /*
+ * ... move it backwards if there's unused space at the end
+ * of the line (and also set `nl' if this is the case,
+ * because in normal selection mode this means we need a
+ * newline at the end)...
+ */
if (!(ldata[cols] & LATTR_WRAPPED)) {
while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
(DIRECT_CHAR(ldata[nlpos.x - 1]) &&
if (poslt(nlpos, bottom))
nl = TRUE;
}
+
+ /*
+ * ... and then clip it to the terminal x coordinate if
+ * we're doing rectangular selection. (In this case we
+ * still did the above, so that copying e.g. the right-hand
+ * column from a table doesn't fill with spaces on the
+ * right.)
+ */
+ if (rect) {
+ if (nlpos.x > bottom.x)
+ nlpos.x = bottom.x;
+ nl = (top.y < bottom.y);
+ }
+
while (poslt(top, bottom) && poslt(top, nlpos)) {
#if 0
char cbuf[16], *p;
}
}
top.y++;
- top.x = 0;
+ top.x = rect ? old_top_x : 0;
}
wblen++;
*wbptr++ = 0;
pos top;
top.y = -count234(scrollback);
top.x = 0;
- clipme(top, curs);
+ clipme(top, curs, 0);
}
/*
{
unsigned long *ldata;
short wvalue;
+ int topy = -count234(scrollback);
ldata = lineptr(p.y);
*/
wvalue = wordtype(ldata[p.x]);
if (dir == +1) {
- while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
- p.x++;
+ while (1) {
+ if (p.x < cols-1) {
+ if (wordtype(ldata[p.x + 1]) == wvalue)
+ p.x++;
+ else
+ break;
+ } else {
+ if (ldata[cols] & LATTR_WRAPPED) {
+ unsigned long *ldata2;
+ ldata2 = lineptr(p.y+1);
+ if (wordtype(ldata2[0]) == wvalue) {
+ p.x = 0;
+ p.y++;
+ ldata = ldata2;
+ } else
+ break;
+ } else
+ break;
+ }
+ }
} else {
- while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
- p.x--;
+ while (1) {
+ if (p.x > 0) {
+ if (wordtype(ldata[p.x - 1]) == wvalue)
+ p.x--;
+ else
+ break;
+ } else {
+ unsigned long *ldata2;
+ if (p.y <= topy)
+ break;
+ ldata2 = lineptr(p.y-1);
+ if ((ldata2[cols] & LATTR_WRAPPED) &&
+ wordtype(ldata2[cols-1]) == wvalue) {
+ p.x = cols-1;
+ p.y--;
+ ldata = ldata2;
+ } else
+ break;
+ }
+ }
}
break;
case SM_LINE:
static void sel_spread(void)
{
- selstart = sel_spread_half(selstart, -1);
- decpos(selend);
- selend = sel_spread_half(selend, +1);
- incpos(selend);
+ if (seltype == LEXICOGRAPHIC) {
+ selstart = sel_spread_half(selstart, -1);
+ decpos(selend);
+ selend = sel_spread_half(selend, +1);
+ incpos(selend);
+ }
}
void term_do_paste(void)
/* 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;
}
void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
- int shift, int ctrl)
+ int shift, int ctrl, int alt)
{
pos selpoint;
unsigned long *ldata;
int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
+ int default_seltype;
if (y < 0) {
y = 0;
c = x + 33;
sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
- ldisc_send(abuf, 6);
+ ldisc_send(abuf, 6, 0);
return;
}
b = translate_button(b);
+ /*
+ * Set the selection type (rectangular or normal) at the start
+ * of a selection attempt, from the state of Alt.
+ */
+ if (!alt ^ !cfg.rect_select)
+ default_seltype = RECTANGULAR;
+ else
+ default_seltype = LEXICOGRAPHIC;
+
+ if (selstate == NO_SELECTION) {
+ seltype = default_seltype;
+ }
+
if (b == MBT_SELECT && a == MA_CLICK) {
deselect();
selstate = ABOUT_TO;
+ seltype = default_seltype;
selanchor = selpoint;
selmode = SM_CHAR;
} else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
return;
if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
- if (posdiff(selpoint, selstart) <
- posdiff(selend, selstart) / 2) {
- selanchor = selend;
- decpos(selanchor);
+ if (seltype == LEXICOGRAPHIC) {
+ /*
+ * For normal selection, we extend by moving
+ * whichever end of the current selection is closer
+ * to the mouse.
+ */
+ if (posdiff(selpoint, selstart) <
+ posdiff(selend, selstart) / 2) {
+ selanchor = selend;
+ decpos(selanchor);
+ } else {
+ selanchor = selstart;
+ }
} else {
- selanchor = selstart;
+ /*
+ * For rectangular selection, we have a choice of
+ * _four_ places to put selanchor and selpoint: the
+ * four corners of the selection.
+ */
+ if (2*selpoint.x < selstart.x + selend.x)
+ selanchor.x = selend.x-1;
+ else
+ selanchor.x = selstart.x;
+
+ if (2*selpoint.y < selstart.y + selend.y)
+ selanchor.y = selend.y;
+ else
+ selanchor.y = selstart.y;
}
selstate = DRAGGING;
}
if (selstate != ABOUT_TO && selstate != DRAGGING)
selanchor = selpoint;
selstate = DRAGGING;
- if (poslt(selpoint, selanchor)) {
- selstart = selpoint;
- selend = selanchor;
- incpos(selend);
+ if (seltype == LEXICOGRAPHIC) {
+ /*
+ * For normal selection, we set (selstart,selend) to
+ * (selpoint,selanchor) in some order.
+ */
+ if (poslt(selpoint, selanchor)) {
+ selstart = selpoint;
+ selend = selanchor;
+ incpos(selend);
+ } else {
+ selstart = selanchor;
+ selend = selpoint;
+ incpos(selend);
+ }
} else {
- selstart = selanchor;
- selend = selpoint;
- incpos(selend);
+ /*
+ * For rectangular selection, we may need to
+ * interchange x and y coordinates (if the user has
+ * dragged in the -x and +y directions, or vice versa).
+ */
+ selstart.x = min(selanchor.x, selpoint.x);
+ selend.x = 1+max(selanchor.x, selpoint.x);
+ selstart.y = min(selanchor.y, selpoint.y);
+ selend.y = max(selanchor.y, selpoint.y);
}
sel_spread();
} else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
* We've completed a selection. We now transfer the
* data to the clipboard.
*/
- clipme(selstart, selend);
+ clipme(selstart, selend, (seltype == RECTANGULAR));
selstate = SELECTED;
} else
selstate = NO_SELECTION;
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;