term->rows = term->cols = -1;
power_on(term);
term->beephead = term->beeptail = NULL;
+#ifdef OPTIMISE_SCROLL
+ term->scrollhead = term->scrolltail = NULL;
+#endif /* OPTIMISE_SCROLL */
term->nbeeps = 0;
term->lastbeep = FALSE;
term->beep_overloaded = FALSE;
} else {
line = smalloc(TSIZE * (newcols + 2));
line[0] = newcols;
- for (j = 0; j <= newcols; j++)
+ for (j = 0; j < newcols; j++)
line[j + 1] = ERASE_CHAR;
+ line[newcols + 1] = LATTR_NORM;
}
addpos234(term->screen, line, 0);
}
for (i = 0; i < newrows; i++) {
line = smalloc(TSIZE * (newcols + 2));
line[0] = newcols;
- for (j = 0; j <= newcols; j++)
+ for (j = 0; j < newcols; j++)
line[j + 1] = term->erase_char;
+ line[newcols + 1] = LATTR_NORM;
addpos234(newalt, line, i);
}
if (term->alt_screen) {
line = resizeline(line, term->cols);
for (i = 0; i < term->cols; i++)
line[i + 1] = term->erase_char;
- line[term->cols + 1] = 0;
+ line[term->cols + 1] = LATTR_NORM;
addpos234(term->screen, line, botline);
/*
#ifdef OPTIMISE_SCROLL
/*
+ * Add a scroll of a region on the screen into the pending scroll list.
+ * `lines' is +ve for scrolling forward, -ve for backward.
+ *
+ * If the scroll is on the same area as the last scroll in the list,
+ * merge them.
+ */
+static void save_scroll(Terminal *term, int topline, int botline, int lines)
+{
+ struct scrollregion *newscroll;
+ if (term->scrolltail &&
+ term->scrolltail->topline == topline &&
+ term->scrolltail->botline == botline) {
+ term->scrolltail->lines += lines;
+ } else {
+ newscroll = smalloc(sizeof(struct scrollregion));
+ newscroll->topline = topline;
+ newscroll->botline = botline;
+ newscroll->lines = lines;
+ newscroll->next = NULL;
+
+ if (!term->scrollhead)
+ term->scrollhead = newscroll;
+ else
+ term->scrolltail->next = newscroll;
+ term->scrolltail = newscroll;
+ }
+}
+
+/*
* Scroll the physical display, and our conception of it in disptext.
*/
static void scroll_display(Terminal *term, int topline, int botline, int lines)
for (i = 0; i < distance; i++)
start[i] |= ATTR_INVALID;
}
- do_scroll(term->frontend, topline, botline, lines);
+ save_scroll(term, topline, botline, lines);
}
#endif /* OPTIMISE_SCROLL */
ldata = lineptr(start.y);
while (poslt(start, end)) {
- if (start.x == term->cols && !erase_lattr)
- ldata[start.x] &= ~(LATTR_WRAPPED | LATTR_WRAPPED2);
- else
+ if (start.x == term->cols) {
+ if (!erase_lattr)
+ ldata[start.x] &= ~(LATTR_WRAPPED | LATTR_WRAPPED2);
+ else
+ ldata[start.x] = LATTR_NORM;
+ } else {
ldata[start.x] = term->erase_char;
+ }
if (incpos(start) && start.y < term->rows)
ldata = lineptr(start.y);
}
term->curr_attr |=
(term->esc_args[i] - 30)<<ATTR_FGSHIFT;
break;
+ case 90:
+ case 91:
+ case 92:
+ case 93:
+ case 94:
+ case 95:
+ case 96:
+ case 97:
+ /* xterm-style bright foreground */
+ term->curr_attr &= ~ATTR_FGMASK;
+ term->curr_attr |=
+ ((term->esc_args[i] - 90 + 16)
+ << ATTR_FGSHIFT);
+ break;
case 39: /* default-foreground */
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |= ATTR_DEFFG;
term->curr_attr |=
(term->esc_args[i] - 40)<<ATTR_BGSHIFT;
break;
+ case 100:
+ case 101:
+ case 102:
+ case 103:
+ case 104:
+ case 105:
+ case 106:
+ case 107:
+ /* xterm-style bright background */
+ term->curr_attr &= ~ATTR_BGMASK;
+ term->curr_attr |=
+ ((term->esc_args[i] - 100 + 16)
+ << ATTR_BGSHIFT);
+ break;
case 49: /* default-background */
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |= ATTR_DEFBG;
char ch[1024];
long cursor_background = ERASE_CHAR;
unsigned long ticks;
+#ifdef OPTIMISE_SCROLL
+ struct scrollregion *sr;
+#endif /* OPTIMISE_SCROLL */
/*
* Check the visual bell state.
}
term->dispcurs = NULL;
+#ifdef OPTIMISE_SCROLL
+ /* Do scrolls */
+ sr = term->scrollhead;
+ while (sr) {
+ struct scrollregion *next = sr->next;
+ do_scroll(ctx, sr->topline, sr->botline, sr->lines);
+ sfree(sr);
+ sr = next;
+ }
+ term->scrollhead = term->scrolltail = NULL;
+#endif /* OPTIMISE_SCROLL */
+
/* The normal screen data */
for (i = 0; i < term->rows; i++) {
unsigned long *ldata;
/* 'Real' blinking ? */
if (term->blink_is_real && (tattr & ATTR_BLINK)) {
if (term->has_focus && term->tblinker) {
- tchar = ' ';
- tattr &= ~CSET_MASK;
- tattr |= ATTR_ACP;
+ tchar = term->ucsdata->unitab_line[(unsigned char)' '];
}
tattr &= ~ATTR_BLINK;
}
term_update(term);
}
+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->cfg.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(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->cfg.funky_type == FUNKY_VT400 ||
+ ((term->cfg.funky_type == FUNKY_LINUX ||
+ term->cfg.funky_type == FUNKY_TILDE) &&
+ term->app_keypad_keys && !term->cfg.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->cfg.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->cfg.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->cfg.bksp_is_delete ? 0x7F : 0x08);
+ else if (modifiers == PKM_SHIFT)
+ /* We do the opposite of what is configured */
+ *p++ = (term->cfg.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->cfg.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->cfg.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->cfg.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: 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: 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->cfg.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->cfg.funky_type == FUNKY_LINUX && keysym <= PK_F5) {
+ p += sprintf((char *) p, "\x1B[[%c", 'A' + keysym - PK_F1);
+ goto done;
+ }
+ if (term->cfg.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: break; /* else gcc warns `enum value not used' */
+ }
+ if (term->vt52_mode)
+ p += sprintf((char *) p, "\x1B%c", xkey);
+ else {
+ int app_flg = (term->app_cursor_keys && !term->cfg.no_applic_c);
+
+ /* Useful mapping of Ctrl-arrows */
+ if (modifiers == PKM_CONTROL)
+ app_flg = !app_flg;
+
+ if (app_flg)
+ p += sprintf((char *) p, "\x1BO%c", xkey);
+ else
+ p += sprintf((char *) p, "\x1B[%c", xkey);
+ }
+ 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)
/*
* from_backend(), to get data from the backend for the terminal.
*/
-int from_backend(void *vterm, int is_stderr, char *data, int len)
+int from_backend(void *vterm, int is_stderr, const char *data, int len)
{
Terminal *term = (Terminal *)vterm;