#define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 )
-char *EMPTY_WINDOW_TITLE = "";
+const char *EMPTY_WINDOW_TITLE = "";
const char sco2ansicolour[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
static void resizeline(Terminal *, termline *, int);
static termline *lineptr(Terminal *, int, int, int);
static void unlineptr(termline *);
+static void check_line_size(Terminal *, termline *);
static void do_paint(Terminal *, Context, int);
static void erase_lots(Terminal *, int, int, int);
static int find_last_nonempty_line(Terminal *, tree234 *);
}
assert(line != NULL);
- resizeline(term, line, term->cols);
- /* FIXME: should we sort the compressed scrollback out here? */
+ /*
+ * Here we resize lines to _at least_ the right length, but we
+ * don't truncate them. Truncation is done as a side effect of
+ * modifying the line.
+ *
+ * The point of this policy is to try to arrange that resizing the
+ * terminal window repeatedly - e.g. successive steps in an X11
+ * opaque window-resize drag, or resizing as a side effect of
+ * retiling by tiling WMs such as xmonad - does not throw away
+ * data gratuitously. Specifically, we want a sequence of resize
+ * operations with no terminal output between them to have the
+ * same effect as a single resize to the ultimate terminal size,
+ * and also (for the case in which xmonad narrows a window that's
+ * scrolling things) we want scrolling up new text at the bottom
+ * of a narrowed window to avoid truncating lines further up when
+ * the window is re-widened.
+ */
+ if (term->cols > line->cols)
+ resizeline(term, line, term->cols);
return line;
}
#define lineptr(x) (lineptr)(term,x,__LINE__,FALSE)
#define scrlineptr(x) (lineptr)(term,x,__LINE__,TRUE)
+/*
+ * Coerce a termline to the terminal's current width. Unlike the
+ * optional resize in lineptr() above, this is potentially destructive
+ * of text, since it can shrink as well as grow the line.
+ *
+ * We call this whenever a termline is actually going to be modified.
+ * Helpfully, putting a single call to this function in check_boundary
+ * deals with _nearly_ all such cases, leaving only a few things like
+ * bulk erase and ESC#8 to handle separately.
+ */
+static void check_line_size(Terminal *term, termline *line)
+{
+ if (term->cols != line->cols) /* trivial optimisation */
+ resizeline(term, line, term->cols);
+}
+
static void term_schedule_tblink(Terminal *term);
static void term_schedule_cblink(Terminal *term);
-static void term_timer(void *ctx, long now)
+static void term_timer(void *ctx, unsigned long now)
{
Terminal *term = (Terminal *)ctx;
int update = FALSE;
- if (term->tblink_pending && now - term->next_tblink >= 0) {
+ if (term->tblink_pending && now == term->next_tblink) {
term->tblinker = !term->tblinker;
term->tblink_pending = FALSE;
term_schedule_tblink(term);
update = TRUE;
}
- if (term->cblink_pending && now - term->next_cblink >= 0) {
+ if (term->cblink_pending && now == term->next_cblink) {
term->cblinker = !term->cblinker;
term->cblink_pending = FALSE;
term_schedule_cblink(term);
update = TRUE;
}
- if (term->in_vbell && now - term->vbell_end >= 0) {
+ if (term->in_vbell && now == term->vbell_end) {
term->in_vbell = FALSE;
update = TRUE;
}
if (update ||
- (term->window_update_pending && now - term->next_update >= 0))
+ (term->window_update_pending && now == term->next_update))
term_update(term);
}
term->alt_which = 0;
term_print_finish(term);
term->xterm_mouse = 0;
+ term->xterm_extended_mouse = 0;
+ term->urxvt_extended_mouse = 0;
set_raw_mouse_mode(term->frontend, FALSE);
+ term->bracketed_paste = FALSE;
{
int i;
for (i = 0; i < 256; i++)
{
power_on(term, clear);
if (term->ldisc) /* cause ldisc to notice changes */
- ldisc_send(term->ldisc, NULL, 0, 0);
+ ldisc_echoedit_update(term->ldisc);
term->disptop = 0;
deselect(term);
term_update(term);
void term_clrsb(Terminal *term)
{
unsigned char *line;
+ int i;
+
+ /*
+ * Scroll forward to the current screen, if we were back in the
+ * scrollback somewhere until now.
+ */
term->disptop = 0;
+
+ /*
+ * Clear the actual scrollback.
+ */
while ((line = delpos234(term->scrollback, 0)) != NULL) {
sfree(line); /* this is compressed data, not a termline */
}
+
+ /*
+ * When clearing the scrollback, we also truncate any termlines on
+ * the current screen which have remembered data from a previous
+ * larger window size. Rationale: clearing the scrollback is
+ * sometimes done to protect privacy, so the user intention is
+ * specifically that we should not retain evidence of what
+ * previously happened in the terminal, and that ought to include
+ * evidence to the right as well as evidence above.
+ */
+ for (i = 0; i < term->rows; i++)
+ check_line_size(term, scrlineptr(i));
+
+ /*
+ * There are now no lines of real scrollback which can be pulled
+ * back into the screen by a resize, and no lines of the alternate
+ * screen which should be displayed as if part of the scrollback.
+ */
term->tempsblines = 0;
term->alt_sblines = 0;
+
+ /*
+ * Update the scrollbar to reflect the new state of the world.
+ */
update_sbar(term);
}
term->cblink_pending = term->tblink_pending = FALSE;
term->paste_buffer = NULL;
term->paste_len = 0;
- term->last_paste = 0;
bufchain_init(&term->inbuf);
bufchain_init(&term->printer_buf);
term->printing = term->only_printing = FALSE;
for (i = 0; i < term->bidi_cache_size; i++) {
sfree(term->pre_bidi_cache[i].chars);
sfree(term->post_bidi_cache[i].chars);
+ sfree(term->post_bidi_cache[i].forward);
+ sfree(term->post_bidi_cache[i].backward);
}
sfree(term->pre_bidi_cache);
sfree(term->post_bidi_cache);
+ sfree(term->tabs);
+
expire_timer_context(term);
conf_free(term->conf);
while (term->rows > newrows) {
if (term->curs.y < term->rows - 1) {
/* delete bottom row, unless it contains the cursor */
- sfree(delpos234(term->screen, term->rows - 1));
+ line = delpos234(term->screen, term->rows - 1);
+ freeline(line);
} else {
/* push top row to scrollback */
line = delpos234(term->screen, 0);
static void scroll(Terminal *term, int topline, int botline, int lines, int sb)
{
termline *line;
- int i, seltop;
+ int i, seltop, scrollwinsize;
#ifdef OPTIMISE_SCROLL
int olddisptop, shift;
#endif /* OPTIMISE_SCROLL */
olddisptop = term->disptop;
shift = lines;
#endif /* OPTIMISE_SCROLL */
+
+ scrollwinsize = botline - topline + 1;
+
if (lines < 0) {
- while (lines < 0) {
+ lines = -lines;
+ if (lines > scrollwinsize)
+ lines = scrollwinsize;
+ while (lines-- > 0) {
line = delpos234(term->screen, botline);
resizeline(term, line, term->cols);
for (i = 0; i < term->cols; i++)
term->selend.x = 0;
}
}
-
- lines++;
}
} else {
- while (lines > 0) {
+ if (lines > scrollwinsize)
+ lines = scrollwinsize;
+ while (lines-- > 0) {
line = delpos234(term->screen, topline);
#ifdef TERM_CC_DIAGS
cc_check(line);
}
}
}
-
- lines--;
}
}
#ifdef OPTIMISE_SCROLL
return;
ldata = scrlineptr(y);
+ check_line_size(term, ldata);
if (x == term->cols) {
ldata->lattr &= ~LATTR_WRAPPED2;
} else {
} else {
termline *ldata = scrlineptr(start.y);
while (poslt(start, end)) {
+ check_line_size(term, ldata);
if (start.x == term->cols) {
if (!erase_lattr)
ldata->lattr &= ~(LATTR_WRAPPED | LATTR_WRAPPED2);
{
int dir = (n < 0 ? -1 : +1);
int m, j;
- pos cursplus;
+ pos eol;
termline *ldata;
n = (n < 0 ? -n : n);
if (n > term->cols - term->curs.x)
n = term->cols - term->curs.x;
m = term->cols - term->curs.x - n;
- cursplus.y = term->curs.y;
- cursplus.x = term->curs.x + n;
- check_selection(term, term->curs, cursplus);
+
+ /*
+ * We must de-highlight the selection if it overlaps any part of
+ * the region affected by this operation, i.e. the region from the
+ * current cursor position to end-of-line, _unless_ the entirety
+ * of the selection is going to be moved to the left or right by
+ * this operation but otherwise unchanged, in which case we can
+ * simply move the highlight with the text.
+ */
+ eol.y = term->curs.y;
+ eol.x = term->cols;
+ if (poslt(term->curs, term->selend) && poslt(term->selstart, eol)) {
+ pos okstart = term->curs;
+ pos okend = eol;
+ if (dir > 0) {
+ /* Insertion: n characters at EOL will be splatted. */
+ okend.x -= n;
+ } else {
+ /* Deletion: n characters at cursor position will be splatted. */
+ okstart.x += n;
+ }
+ if (posle(okstart, term->selstart) && posle(term->selend, okend)) {
+ /* Selection is contained entirely in the interval
+ * [okstart,okend), so we need only adjust the selection
+ * bounds. */
+ term->selstart.x += dir * n;
+ term->selend.x += dir * n;
+ assert(term->selstart.x >= term->curs.x);
+ assert(term->selstart.x < term->cols);
+ assert(term->selend.x > term->curs.x);
+ assert(term->selend.x <= term->cols);
+ } else {
+ /* Selection is not wholly contained in that interval, so
+ * we must unhighlight it. */
+ deselect(term);
+ }
+ }
+
check_boundary(term, term->curs.x, term->curs.y);
if (dir < 0)
check_boundary(term, term->curs.x + n, term->curs.y);
case 10: /* DECEDM: set local edit mode */
term->term_editing = state;
if (term->ldisc) /* cause ldisc to notice changes */
- ldisc_send(term->ldisc, NULL, 0, 0);
+ ldisc_echoedit_update(term->ldisc);
break;
case 25: /* DECTCEM: enable/disable cursor */
compatibility2(OTHER, VT220);
compatibility(OTHER);
deselect(term);
swap_screen(term, term->no_alt_screen ? 0 : state, FALSE, FALSE);
- term->disptop = 0;
+ if (term->scroll_on_disp)
+ term->disptop = 0;
break;
case 1000: /* xterm mouse 1 (normal) */
term->xterm_mouse = state ? 1 : 0;
term->xterm_mouse = state ? 2 : 0;
set_raw_mouse_mode(term->frontend, state);
break;
+ case 1006: /* xterm extended mouse */
+ term->xterm_extended_mouse = state ? 1 : 0;
+ break;
+ case 1015: /* urxvt extended mouse */
+ term->urxvt_extended_mouse = state ? 1 : 0;
+ break;
case 1047: /* alternate screen */
compatibility(OTHER);
deselect(term);
swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, TRUE);
- term->disptop = 0;
+ if (term->scroll_on_disp)
+ term->disptop = 0;
break;
case 1048: /* save/restore cursor */
if (!term->no_alt_screen)
swap_screen(term, term->no_alt_screen ? 0 : state, TRUE, FALSE);
if (!state && !term->no_alt_screen)
save_cursor(term, state);
- term->disptop = 0;
+ if (term->scroll_on_disp)
+ term->disptop = 0;
+ break;
+ case 2004: /* xterm bracketed paste */
+ term->bracketed_paste = state ? TRUE : FALSE;
break;
} else
switch (mode) {
case 12: /* SRM: set echo mode */
term->term_echoing = !state;
if (term->ldisc) /* cause ldisc to notice changes */
- ldisc_send(term->ldisc, NULL, 0, 0);
+ ldisc_echoedit_update(term->ldisc);
break;
case 20: /* LNM: Return sends ... */
term->cr_lf_return = state;
term->curs.x = 0;
term->wrapnext = FALSE;
seen_disp_event(term);
- term->paste_hold = 0;
if (term->crhaslf) {
if (term->curs.y == term->marg_b)
if (has_compat(SCOANSI)) {
move(term, 0, 0, 0);
erase_lots(term, FALSE, FALSE, TRUE);
- term->disptop = 0;
+ if (term->scroll_on_disp)
+ term->disptop = 0;
term->wrapnext = FALSE;
seen_disp_event(term);
break;
term->curs.x = 0;
term->wrapnext = FALSE;
seen_disp_event(term);
- term->paste_hold = 0;
if (term->logctx)
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
break;
compatibility(VT100);
power_on(term, TRUE);
if (term->ldisc) /* cause ldisc to notice changes */
- ldisc_send(term->ldisc, NULL, 0, 0);
+ ldisc_echoedit_update(term->ldisc);
if (term->reset_132) {
if (!term->no_remote_resize)
request_resize(term->frontend, 80, term->rows);
term->reset_132 = 0;
}
- term->disptop = 0;
+ if (term->scroll_on_disp)
+ term->disptop = 0;
seen_disp_event(term);
break;
case 'H': /* HTS: set a tab */
for (i = 0; i < term->rows; i++) {
ldata = scrlineptr(i);
+ check_line_size(term, ldata);
for (j = 0; j < term->cols; j++) {
copy_termchar(ldata, j,
&term->basic_erase_char);
}
ldata->lattr = LATTR_NORM;
}
- term->disptop = 0;
+ if (term->scroll_on_disp)
+ term->disptop = 0;
seen_disp_event(term);
scrtop.x = scrtop.y = 0;
scrbot.x = 0;
compatibility(VT100);
{
int nlattr;
+ termline *ldata;
switch (ANSI(c, term->esc_query)) {
case ANSI('3', '#'): /* DECDHL: 2*height, top */
nlattr = LATTR_WIDE;
break;
}
- scrlineptr(term->curs.y)->lattr = nlattr;
+ ldata = scrlineptr(term->curs.y);
+ check_line_size(term, ldata);
+ ldata->lattr = nlattr;
}
break;
/* GZD4: G0 designate 94-set */
erase_lots(term, FALSE, !!(i & 2), !!(i & 1));
}
}
- term->disptop = 0;
+ if (term->scroll_on_disp)
+ term->disptop = 0;
seen_disp_event(term);
break;
case 'K': /* EL: erase line or parts of it */
switch (term->esc_args[0]) {
int x, y, len;
- char buf[80], *p;
+ char buf[80];
+ const char *p;
case 1:
set_iconic(term->frontend, FALSE);
break;
case 13:
if (term->ldisc) {
get_window_pos(term->frontend, &x, &y);
- len = sprintf(buf, "\033[3;%d;%dt", x, y);
+ len = sprintf(buf, "\033[3;%u;%ut",
+ (unsigned)x,
+ (unsigned)y);
ldisc_send(term->ldisc, buf, len, 0);
}
break;
break;
case 'J':
erase_lots(term, FALSE, FALSE, TRUE);
- term->disptop = 0;
+ if (term->scroll_on_disp)
+ term->disptop = 0;
break;
case 'K':
erase_lots(term, TRUE, FALSE, TRUE);
/* compatibility(ATARI) */
move(term, 0, 0, 0);
erase_lots(term, FALSE, FALSE, TRUE);
- term->disptop = 0;
+ if (term->scroll_on_disp)
+ term->disptop = 0;
break;
case 'L':
/* compatibility(ATARI) */
case 'd':
/* compatibility(ATARI) */
erase_lots(term, FALSE, TRUE, FALSE);
- term->disptop = 0;
+ if (term->scroll_on_disp)
+ term->disptop = 0;
break;
case 'e':
/* compatibility(ATARI) */
break_run = ((tattr ^ attr) & term->attr_mask) != 0;
+#ifdef USES_VTLINE_HACK
/* Special hack for VT100 Linedraw glyphs */
if ((tchar >= 0x23BA && tchar <= 0x23BD) ||
(j > 0 && (newline[j-1].chr >= 0x23BA &&
newline[j-1].chr <= 0x23BD)))
break_run = TRUE;
+#endif
/*
* Separate out sequences of characters that have the
else
break;
} else {
- if (ldata->lattr & LATTR_WRAPPED) {
+ if (p.y+1 < term->rows &&
+ (ldata->lattr & LATTR_WRAPPED)) {
termline *ldata2;
ldata2 = lineptr(p.y+1);
if (wordtype(term, UCSGET(ldata2->chars, 0))
}
}
+static void term_paste_callback(void *vterm)
+{
+ Terminal *term = (Terminal *)vterm;
+
+ if (term->paste_len == 0)
+ return;
+
+ while (term->paste_pos < term->paste_len) {
+ int n = 0;
+ while (n + term->paste_pos < term->paste_len) {
+ if (term->paste_buffer[term->paste_pos + n++] == '\015')
+ break;
+ }
+ if (term->ldisc)
+ luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, 0);
+ term->paste_pos += n;
+
+ if (term->paste_pos < term->paste_len) {
+ queue_toplevel_callback(term_paste_callback, term);
+ return;
+ }
+ }
+ sfree(term->paste_buffer);
+ term->paste_buffer = NULL;
+ term->paste_len = 0;
+}
+
void term_do_paste(Terminal *term)
{
wchar_t *data;
if (term->paste_buffer)
sfree(term->paste_buffer);
- term->paste_pos = term->paste_hold = term->paste_len = 0;
- term->paste_buffer = snewn(len, wchar_t);
+ term->paste_pos = term->paste_len = 0;
+ term->paste_buffer = snewn(len + 12, wchar_t);
+
+ if (term->bracketed_paste) {
+ memcpy(term->paste_buffer, L"\033[200~", 6 * sizeof(wchar_t));
+ term->paste_len += 6;
+ }
p = q = data;
while (p < data + len) {
q = p;
}
+ if (term->bracketed_paste) {
+ memcpy(term->paste_buffer + term->paste_len,
+ L"\033[201~", 6 * sizeof(wchar_t));
+ term->paste_len += 6;
+ }
+
/* Assume a small paste will be OK in one go. */
if (term->paste_len < 256) {
if (term->ldisc)
if (term->paste_buffer)
sfree(term->paste_buffer);
term->paste_buffer = 0;
- term->paste_pos = term->paste_hold = term->paste_len = 0;
+ term->paste_pos = term->paste_len = 0;
}
}
get_clip(term->frontend, NULL, NULL);
+
+ queue_toplevel_callback(term_paste_callback, term);
}
void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
*/
if (raw_mouse &&
(term->selstate != ABOUT_TO) && (term->selstate != DRAGGING)) {
- int encstate = 0, r, c;
- char abuf[16];
+ int encstate = 0, r, c, wheel;
+ char abuf[32];
+ int len = 0;
if (term->ldisc) {
switch (braw) {
case MBT_LEFT:
- encstate = 0x20; /* left button down */
+ encstate = 0x00; /* left button down */
+ wheel = FALSE;
break;
case MBT_MIDDLE:
- encstate = 0x21;
+ encstate = 0x01;
+ wheel = FALSE;
break;
case MBT_RIGHT:
- encstate = 0x22;
+ encstate = 0x02;
+ wheel = FALSE;
break;
case MBT_WHEEL_UP:
- encstate = 0x60;
+ encstate = 0x40;
+ wheel = TRUE;
break;
case MBT_WHEEL_DOWN:
- encstate = 0x61;
+ encstate = 0x41;
+ wheel = TRUE;
break;
- default: break; /* placate gcc warning about enum use */
+ default:
+ return;
}
- switch (a) {
+ if (wheel) {
+ /* For mouse wheel buttons, we only ever expect to see
+ * MA_CLICK actions, and we don't try to keep track of
+ * the buttons being 'pressed' (since without matching
+ * click/release pairs that's pointless). */
+ if (a != MA_CLICK)
+ return;
+ } else switch (a) {
case MA_DRAG:
if (term->xterm_mouse == 1)
return;
encstate += 0x20;
break;
case MA_RELEASE:
- encstate = 0x23;
+ /* If multiple extensions are enabled, the xterm 1006 is used, so it's okay to check for only that */
+ if (!term->xterm_extended_mouse)
+ encstate = 0x03;
term->mouse_is_down = 0;
break;
case MA_CLICK:
return;
term->mouse_is_down = braw;
break;
- default: break; /* placate gcc warning about enum use */
+ default:
+ return;
}
if (shift)
encstate += 0x04;
if (ctrl)
encstate += 0x10;
- r = y + 33;
- c = x + 33;
-
- sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
- ldisc_send(term->ldisc, abuf, 6, 0);
+ r = y + 1;
+ c = x + 1;
+
+ /* Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. */
+ if (term->xterm_extended_mouse) {
+ len = sprintf(abuf, "\033[<%d;%d;%d%c", encstate, c, r, a == MA_RELEASE ? 'm' : 'M');
+ } else if (term->urxvt_extended_mouse) {
+ len = sprintf(abuf, "\033[%d;%d;%dM", encstate + 32, c, r);
+ } else if (c <= 223 && r <= 223) {
+ len = sprintf(abuf, "\033[M%c%c%c", encstate + 32, c + 32, r + 32);
+ }
+ if (len > 0)
+ ldisc_send(term->ldisc, abuf, len, 0);
}
return;
}
sel_spread(term);
} else if ((bcooked == MBT_SELECT && a == MA_DRAG) ||
(bcooked == MBT_EXTEND && a != MA_RELEASE)) {
+ if (term->selstate == NO_SELECTION || term->selstate == SELECTED) {
+ /*
+ * This can happen if a front end has passed us a MA_DRAG
+ * without a prior MA_CLICK. OS X GTK does so, for
+ * example, if the initial button press was eaten by the
+ * WM when it activated the window in the first place. The
+ * nicest thing to do in this situation is to ignore
+ * further drags, and wait for the user to click in the
+ * window again properly if they want to select.
+ */
+ return;
+ }
if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint))
return;
if (bcooked == MBT_EXTEND && a != MA_DRAG &&
request_paste(term->frontend);
}
+ /*
+ * Since terminal output is suppressed during drag-selects, we
+ * should make sure to write any pending output if one has just
+ * finished.
+ */
+ if (term->selstate != DRAGGING)
+ term_out(term);
term_update(term);
}
return p - buf;
}
-void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
- unsigned int modifiers, unsigned int flags)
-{
- char output[10];
- char *p = output;
- int prependesc = FALSE;
-#if 0
- int i;
-
- fprintf(stderr, "keysym = %d, %d chars:", keysym, tlen);
- for (i = 0; i < tlen; i++)
- fprintf(stderr, " %04x", (unsigned)text[i]);
- fprintf(stderr, "\n");
-#endif
-
- /* XXX Num Lock */
- if ((flags & PKF_REPEAT) && term->repeat_off)
- return;
-
- /* Currently, Meta always just prefixes everything with ESC. */
- if (modifiers & PKM_META)
- prependesc = TRUE;
- modifiers &= ~PKM_META;
-
- /*
- * Alt is only used for Alt+keypad, which isn't supported yet, so
- * ignore it.
- */
- modifiers &= ~PKM_ALT;
-
- /* Standard local function keys */
- switch (modifiers & (PKM_SHIFT | PKM_CONTROL)) {
- case PKM_SHIFT:
- if (keysym == PK_PAGEUP)
- /* scroll up one page */;
- if (keysym == PK_PAGEDOWN)
- /* scroll down on page */;
- if (keysym == PK_INSERT)
- term_do_paste(term);
- break;
- case PKM_CONTROL:
- if (keysym == PK_PAGEUP)
- /* scroll up one line */;
- if (keysym == PK_PAGEDOWN)
- /* scroll down one line */;
- /* Control-Numlock for app-keypad mode switch */
- if (keysym == PK_PF1)
- term->app_keypad_keys ^= 1;
- break;
- }
-
- if (modifiers & PKM_ALT) {
- /* Alt+F4 (close) */
- /* Alt+Return (full screen) */
- /* Alt+Space (system menu) */
- }
-
- if (keysym == PK_NULL && (modifiers & PKM_CONTROL) && tlen == 1 &&
- text[0] >= 0x20 && text[0] <= 0x7e) {
- /* ASCII chars + Control */
- if ((text[0] >= 0x40 && text[0] <= 0x5f) ||
- (text[0] >= 0x61 && text[0] <= 0x7a))
- text[0] &= 0x1f;
- else {
- /*
- * Control-2 should return ^@ (0x00), Control-6 should return
- * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since
- * the DOS keyboard handling did it, and we have nothing better
- * to do with the key combo in question, we'll also map
- * Control-Backquote to ^\ (0x1C).
- */
- switch (text[0]) {
- case ' ': text[0] = 0x00; break;
- case '-': text[0] = 0x1f; break;
- case '/': text[0] = 0x1f; break;
- case '2': text[0] = 0x00; break;
- case '3': text[0] = 0x1b; break;
- case '4': text[0] = 0x1c; break;
- case '5': text[0] = 0x1d; break;
- case '6': text[0] = 0x1e; break;
- case '7': text[0] = 0x1f; break;
- case '8': text[0] = 0x7f; break;
- case '`': text[0] = 0x1c; break;
- }
- }
- }
-
- /* Nethack keypad */
- if (term->nethack_keypad) {
- char c = 0;
- switch (keysym) {
- case PK_KP1: c = 'b'; break;
- case PK_KP2: c = 'j'; break;
- case PK_KP3: c = 'n'; break;
- case PK_KP4: c = 'h'; break;
- case PK_KP5: c = '.'; break;
- case PK_KP6: c = 'l'; break;
- case PK_KP7: c = 'y'; break;
- case PK_KP8: c = 'k'; break;
- case PK_KP9: c = 'u'; break;
- default: break; /* else gcc warns `enum value not used' */
- }
- if (c != 0) {
- if (c != '.') {
- if (modifiers & PKM_CONTROL)
- c &= 0x1f;
- else if (modifiers & PKM_SHIFT)
- c = toupper((unsigned char)c);
- }
- *p++ = c;
- goto done;
- }
- }
-
- /* Numeric Keypad */
- if (PK_ISKEYPAD(keysym)) {
- int xkey = 0;
-
- /*
- * In VT400 mode, PFn always emits an escape sequence. In
- * Linux and tilde modes, this only happens in app keypad mode.
- */
- if (term->funky_type == FUNKY_VT400 ||
- ((term->funky_type == FUNKY_LINUX ||
- term->funky_type == FUNKY_TILDE) &&
- term->app_keypad_keys && !term->no_applic_k)) {
- switch (keysym) {
- case PK_PF1: xkey = 'P'; break;
- case PK_PF2: xkey = 'Q'; break;
- case PK_PF3: xkey = 'R'; break;
- case PK_PF4: xkey = 'S'; break;
- default: break; /* else gcc warns `enum value not used' */
- }
- }
- if (term->app_keypad_keys && !term->no_applic_k) {
- switch (keysym) {
- case PK_KP0: xkey = 'p'; break;
- case PK_KP1: xkey = 'q'; break;
- case PK_KP2: xkey = 'r'; break;
- case PK_KP3: xkey = 's'; break;
- case PK_KP4: xkey = 't'; break;
- case PK_KP5: xkey = 'u'; break;
- case PK_KP6: xkey = 'v'; break;
- case PK_KP7: xkey = 'w'; break;
- case PK_KP8: xkey = 'x'; break;
- case PK_KP9: xkey = 'y'; break;
- case PK_KPDECIMAL: xkey = 'n'; break;
- case PK_KPENTER: xkey = 'M'; break;
- default: break; /* else gcc warns `enum value not used' */
- }
- if (term->funky_type == FUNKY_XTERM && tlen > 0) {
- /*
- * xterm can't see the layout of the keypad, so it has
- * to rely on the X keysyms returned by the keys.
- * Hence, we look at the strings here, not the PuTTY
- * keysyms (which describe the layout).
- */
- switch (text[0]) {
- case '+':
- if (modifiers & PKM_SHIFT)
- xkey = 'l';
- else
- xkey = 'k';
- break;
- case '/': xkey = 'o'; break;
- case '*': xkey = 'j'; break;
- case '-': xkey = 'm'; break;
- }
- } else {
- /*
- * In all other modes, we try to retain the layout of
- * the DEC keypad in application mode.
- */
- switch (keysym) {
- case PK_KPBIGPLUS:
- /* This key covers the '-' and ',' keys on a VT220 */
- if (modifiers & PKM_SHIFT)
- xkey = 'm'; /* VT220 '-' */
- else
- xkey = 'l'; /* VT220 ',' */
- break;
- case PK_KPMINUS: xkey = 'm'; break;
- case PK_KPCOMMA: xkey = 'l'; break;
- default: break; /* else gcc warns `enum value not used' */
- }
- }
- }
- if (xkey) {
- if (term->vt52_mode) {
- if (xkey >= 'P' && xkey <= 'S')
- p += sprintf((char *) p, "\x1B%c", xkey);
- else
- p += sprintf((char *) p, "\x1B?%c", xkey);
- } else
- p += sprintf((char *) p, "\x1BO%c", xkey);
- goto done;
- }
- /* Not in application mode -- treat the number pad as arrow keys? */
- if ((flags & PKF_NUMLOCK) == 0) {
- switch (keysym) {
- case PK_KP0: keysym = PK_INSERT; break;
- case PK_KP1: keysym = PK_END; break;
- case PK_KP2: keysym = PK_DOWN; break;
- case PK_KP3: keysym = PK_PAGEDOWN; break;
- case PK_KP4: keysym = PK_LEFT; break;
- case PK_KP5: keysym = PK_REST; break;
- case PK_KP6: keysym = PK_RIGHT; break;
- case PK_KP7: keysym = PK_HOME; break;
- case PK_KP8: keysym = PK_UP; break;
- case PK_KP9: keysym = PK_PAGEUP; break;
- default: break; /* else gcc warns `enum value not used' */
- }
- }
- }
-
- /* Miscellaneous keys */
- switch (keysym) {
- case PK_ESCAPE:
- *p++ = 0x1b;
- goto done;
- case PK_BACKSPACE:
- if (modifiers == 0)
- *p++ = (term->bksp_is_delete ? 0x7F : 0x08);
- else if (modifiers == PKM_SHIFT)
- /* We do the opposite of what is configured */
- *p++ = (term->bksp_is_delete ? 0x08 : 0x7F);
- else break;
- goto done;
- case PK_TAB:
- if (modifiers == 0)
- *p++ = 0x09;
- else if (modifiers == PKM_SHIFT)
- *p++ = 0x1B, *p++ = '[', *p++ = 'Z';
- else break;
- goto done;
- /* XXX window.c has ctrl+shift+space sending 0xa0 */
- case PK_PAUSE:
- if (modifiers == PKM_CONTROL)
- *p++ = 26;
- else break;
- goto done;
- case PK_RETURN:
- case PK_KPENTER: /* Odd keypad modes handled above */
- if (modifiers == 0) {
- *p++ = 0x0d;
- if (term->cr_lf_return)
- *p++ = 0x0a;
- goto done;
- }
- default: break; /* else gcc warns `enum value not used' */
- }
-
- /* SCO function keys and editing keys */
- if (term->funky_type == FUNKY_SCO) {
- if (PK_ISFKEY(keysym) && keysym <= PK_F12) {
- static char const codes[] =
- "MNOPQRSTUVWX" "YZabcdefghij" "klmnopqrstuv" "wxyz@[\\]^_`{";
- int index = keysym - PK_F1;
-
- if (modifiers & PKM_SHIFT) index += 12;
- if (modifiers & PKM_CONTROL) index += 24;
- p += sprintf((char *) p, "\x1B[%c", codes[index]);
- goto done;
- }
- if (PK_ISEDITING(keysym)) {
- int xkey = 0;
-
- switch (keysym) {
- case PK_DELETE: *p++ = 0x7f; goto done;
- case PK_HOME: xkey = 'H'; break;
- case PK_INSERT: xkey = 'L'; break;
- case PK_END: xkey = 'F'; break;
- case PK_PAGEUP: xkey = 'I'; break;
- case PK_PAGEDOWN: xkey = 'G'; break;
- default: break; /* else gcc warns `enum value not used' */
- }
- p += sprintf((char *) p, "\x1B[%c", xkey);
- }
- }
-
- if (PK_ISEDITING(keysym) && (modifiers & PKM_SHIFT) == 0) {
- int code;
-
- if (term->funky_type == FUNKY_XTERM) {
- /* Xterm shuffles these keys, apparently. */
- switch (keysym) {
- case PK_HOME: keysym = PK_INSERT; break;
- case PK_INSERT: keysym = PK_HOME; break;
- case PK_DELETE: keysym = PK_END; break;
- case PK_END: keysym = PK_PAGEUP; break;
- case PK_PAGEUP: keysym = PK_DELETE; break;
- case PK_PAGEDOWN: keysym = PK_PAGEDOWN; break;
- default: break; /* else gcc warns `enum value not used' */
- }
- }
-
- /* RXVT Home/End */
- if (term->rxvt_homeend &&
- (keysym == PK_HOME || keysym == PK_END)) {
- p += sprintf((char *) p, keysym == PK_HOME ? "\x1B[H" : "\x1BOw");
- goto done;
- }
-
- if (term->vt52_mode) {
- int xkey;
-
- /*
- * A real VT52 doesn't have these, and a VT220 doesn't
- * send anything for them in VT52 mode.
- */
- switch (keysym) {
- case PK_HOME: xkey = 'H'; break;
- case PK_INSERT: xkey = 'L'; break;
- case PK_DELETE: xkey = 'M'; break;
- case PK_END: xkey = 'E'; break;
- case PK_PAGEUP: xkey = 'I'; break;
- case PK_PAGEDOWN: xkey = 'G'; break;
- default: xkey=0; break; /* else gcc warns `enum value not used'*/
- }
- p += sprintf((char *) p, "\x1B%c", xkey);
- goto done;
- }
-
- switch (keysym) {
- case PK_HOME: code = 1; break;
- case PK_INSERT: code = 2; break;
- case PK_DELETE: code = 3; break;
- case PK_END: code = 4; break;
- case PK_PAGEUP: code = 5; break;
- case PK_PAGEDOWN: code = 6; break;
- default: code = 0; break; /* else gcc warns `enum value not used' */
- }
- p += sprintf((char *) p, "\x1B[%d~", code);
- goto done;
- }
-
- if (PK_ISFKEY(keysym)) {
- /* Map Shift+F1-F10 to F11-F20 */
- if (keysym >= PK_F1 && keysym <= PK_F10 && (modifiers & PKM_SHIFT))
- keysym += 10;
- if ((term->vt52_mode || term->funky_type == FUNKY_VT100P) &&
- keysym <= PK_F14) {
- /* XXX This overrides the XTERM/VT52 mode below */
- int offt = 0;
- if (keysym >= PK_F6) offt++;
- if (keysym >= PK_F12) offt++;
- p += sprintf((char *) p, term->vt52_mode ? "\x1B%c" : "\x1BO%c",
- 'P' + keysym - PK_F1 - offt);
- goto done;
- }
- if (term->funky_type == FUNKY_LINUX && keysym <= PK_F5) {
- p += sprintf((char *) p, "\x1B[[%c", 'A' + keysym - PK_F1);
- goto done;
- }
- if (term->funky_type == FUNKY_XTERM && keysym <= PK_F4) {
- if (term->vt52_mode)
- p += sprintf((char *) p, "\x1B%c", 'P' + keysym - PK_F1);
- else
- p += sprintf((char *) p, "\x1BO%c", 'P' + keysym - PK_F1);
- goto done;
- }
- p += sprintf((char *) p, "\x1B[%d~", 11 + keysym - PK_F1);
- goto done;
- }
-
- if (PK_ISCURSOR(keysym)) {
- int xkey;
-
- switch (keysym) {
- case PK_UP: xkey = 'A'; break;
- case PK_DOWN: xkey = 'B'; break;
- case PK_RIGHT: xkey = 'C'; break;
- case PK_LEFT: xkey = 'D'; break;
- case PK_REST: xkey = 'G'; break; /* centre key on number pad */
- default: xkey = 0; break; /* else gcc warns `enum value not used' */
- }
- p += format_arrow_key(p, term, xkey, modifiers == PKM_CONTROL);
- goto done;
- }
-
- done:
- if (p > output || tlen > 0) {
- /*
- * Interrupt an ongoing paste. I'm not sure
- * this is sensible, but for the moment it's
- * preferable to having to faff about buffering
- * things.
- */
- term_nopaste(term);
-
- /*
- * We need not bother about stdin backlogs
- * here, because in GUI PuTTY we can't do
- * anything about it anyway; there's no means
- * of asking Windows to hold off on KEYDOWN
- * messages. We _have_ to buffer everything
- * we're sent.
- */
- term_seen_key_event(term);
-
- if (prependesc) {
-#if 0
- fprintf(stderr, "sending ESC\n");
-#endif
- ldisc_send(term->ldisc, "\x1b", 1, 1);
- }
-
- if (p > output) {
-#if 0
- fprintf(stderr, "sending %d bytes:", p - output);
- for (i = 0; i < p - output; i++)
- fprintf(stderr, " %02x", output[i]);
- fprintf(stderr, "\n");
-#endif
- ldisc_send(term->ldisc, output, p - output, 1);
- } else if (tlen > 0) {
-#if 0
- fprintf(stderr, "sending %d unichars:", tlen);
- for (i = 0; i < tlen; i++)
- fprintf(stderr, " %04x", (unsigned) text[i]);
- fprintf(stderr, "\n");
-#endif
- luni_send(term->ldisc, text, tlen, 1);
- }
- }
-}
-
void term_nopaste(Terminal *term)
{
if (term->paste_len == 0)
term->paste_len = 0;
}
-int term_paste_pending(Terminal *term)
-{
- return term->paste_len != 0;
-}
-
-void term_paste(Terminal *term)
-{
- long now, paste_diff;
-
- if (term->paste_len == 0)
- return;
-
- /* Don't wait forever to paste */
- if (term->paste_hold) {
- now = GETTICKCOUNT();
- paste_diff = now - term->last_paste;
- if (paste_diff >= 0 && paste_diff < 450)
- return;
- }
- term->paste_hold = 0;
-
- while (term->paste_pos < term->paste_len) {
- int n = 0;
- while (n + term->paste_pos < term->paste_len) {
- if (term->paste_buffer[term->paste_pos + n++] == '\015')
- break;
- }
- if (term->ldisc)
- luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, 0);
- term->paste_pos += n;
-
- if (term->paste_pos < term->paste_len) {
- term->paste_hold = 1;
- return;
- }
- }
- sfree(term->paste_buffer);
- term->paste_buffer = NULL;
- term->paste_len = 0;
-}
-
static void deselect(Terminal *term)
{
term->selstate = NO_SELECTION;
{
deselect(term);
term_update(term);
+
+ /*
+ * Since terminal output is suppressed during drag-selects, we
+ * should make sure to write any pending output if one has just
+ * finished.
+ */
+ if (term->selstate != DRAGGING)
+ term_out(term);
}
int term_ldisc(Terminal *term, int option)
*/
char *term_get_ttymode(Terminal *term, const char *mode)
{
- char *val = NULL;
+ const char *val = NULL;
if (strcmp(mode, "ERASE") == 0) {
val = term->bksp_is_delete ? "^?" : "^H";
}
* input.
*/
int term_get_userpass_input(Terminal *term, prompts_t *p,
- unsigned char *in, int inlen)
+ const unsigned char *in, int inlen)
{
struct term_userpass_state *s = (struct term_userpass_state *)p->data;
if (!s) {