1999-01-08 16:02:13 +03:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2000-10-24 14:47:49 +04:00
|
|
|
#include <ctype.h>
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2001-01-07 21:24:59 +03:00
|
|
|
#include <time.h>
|
2001-04-28 19:32:25 +04:00
|
|
|
#include <assert.h>
|
1999-01-08 16:02:13 +03:00
|
|
|
#include "putty.h"
|
2002-10-22 20:11:33 +04:00
|
|
|
#include "terminal.h"
|
|
|
|
|
|
|
|
#define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
|
|
|
|
#define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
|
|
|
|
#define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
|
|
|
|
#define posdiff(p1,p2) ( ((p1).y - (p2).y) * (term->cols+1) + (p1).x - (p2).x )
|
|
|
|
|
|
|
|
/* 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 )
|
|
|
|
|
|
|
|
#define incpos(p) ( (p).x == term->cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
|
|
|
|
#define decpos(p) ( (p).x == 0 ? ((p).x = term->cols, (p).y--, 1) : ((p).x--, 0) )
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2001-05-10 12:34:20 +04:00
|
|
|
#define VT52_PLUS
|
|
|
|
|
2001-05-06 18:35:20 +04:00
|
|
|
#define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
|
|
|
|
#define CL_VT100 0x0002 /* VT100 */
|
|
|
|
#define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
|
|
|
|
#define CL_VT102 0x0008 /* VT102 */
|
|
|
|
#define CL_VT220 0x0010 /* VT220 */
|
|
|
|
#define CL_VT320 0x0020 /* VT320 */
|
|
|
|
#define CL_VT420 0x0040 /* VT420 */
|
|
|
|
#define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
|
|
|
|
#define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
|
|
|
|
#define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
|
|
|
|
#define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
|
|
|
|
#define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
|
2000-03-17 13:39:05 +03:00
|
|
|
|
2000-07-26 16:13:51 +04:00
|
|
|
#define TM_VT100 (CL_ANSIMIN|CL_VT100)
|
|
|
|
#define TM_VT100AVO (TM_VT100|CL_VT100AVO)
|
|
|
|
#define TM_VT102 (TM_VT100AVO|CL_VT102)
|
|
|
|
#define TM_VT220 (TM_VT102|CL_VT220)
|
|
|
|
#define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
|
|
|
|
#define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
|
|
|
|
|
|
|
|
#define TM_PUTTY (0xFFFF)
|
2000-03-17 13:39:05 +03:00
|
|
|
|
|
|
|
#define compatibility(x) \
|
2002-10-22 20:11:33 +04:00
|
|
|
if ( ((CL_##x)&term->compatibility_level) == 0 ) { \
|
|
|
|
term->termstate=TOPLEVEL; \
|
2000-03-17 13:39:05 +03:00
|
|
|
break; \
|
|
|
|
}
|
2000-07-26 16:13:51 +04:00
|
|
|
#define compatibility2(x,y) \
|
2002-10-22 20:11:33 +04:00
|
|
|
if ( ((CL_##x|CL_##y)&term->compatibility_level) == 0 ) { \
|
|
|
|
term->termstate=TOPLEVEL; \
|
2000-07-26 16:13:51 +04:00
|
|
|
break; \
|
|
|
|
}
|
2000-03-17 13:39:05 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
#define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 )
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2001-05-10 12:34:20 +04:00
|
|
|
#define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
|
2002-10-22 20:11:33 +04:00
|
|
|
const wchar_t sel_nl[] = SEL_NL;
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2003-01-02 19:20:29 +03:00
|
|
|
/*
|
|
|
|
* Fetch the character at a particular position in a line array,
|
|
|
|
* for purposes of `wordtype'. The reason this isn't just a simple
|
|
|
|
* array reference is that if the character we find is UCSWIDE,
|
|
|
|
* then we must look one space further to the left.
|
|
|
|
*/
|
|
|
|
#define UCSGET(a, x) \
|
|
|
|
( (x)>0 && ((a)[(x)] & (CHAR_MASK | CSET_MASK)) == UCSWIDE ? \
|
|
|
|
(a)[(x)-1] : (a)[(x)] )
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Internal prototypes.
|
|
|
|
*/
|
2002-12-29 17:47:01 +03:00
|
|
|
static unsigned long *resizeline(unsigned long *, int);
|
|
|
|
static unsigned long *lineptr(Terminal *, int, int);
|
2002-10-22 20:11:33 +04:00
|
|
|
static void do_paint(Terminal *, Context, int);
|
|
|
|
static void erase_lots(Terminal *, int, int, int);
|
|
|
|
static void swap_screen(Terminal *, int, int, int);
|
|
|
|
static void update_sbar(Terminal *);
|
|
|
|
static void deselect(Terminal *);
|
|
|
|
static void term_print_finish(Terminal *);
|
2002-11-23 16:07:29 +03:00
|
|
|
#ifdef OPTIMISE_SCROLL
|
|
|
|
static void scroll_display(Terminal *, int, int, int);
|
|
|
|
#endif /* OPTIMISE_SCROLL */
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2001-05-17 12:53:13 +04:00
|
|
|
/*
|
|
|
|
* Resize a line to make it `cols' columns wide.
|
|
|
|
*/
|
2002-12-29 17:47:01 +03:00
|
|
|
static unsigned long *resizeline(unsigned long *line, int cols)
|
2001-05-17 12:53:13 +04:00
|
|
|
{
|
|
|
|
int i, oldlen;
|
|
|
|
unsigned long lineattrs;
|
|
|
|
|
|
|
|
if (line[0] != (unsigned long)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];
|
2003-03-29 19:14:26 +03:00
|
|
|
line = sresize(line, 2 + cols, TTYPE);
|
2001-05-17 12:53:13 +04:00
|
|
|
line[0] = cols;
|
|
|
|
for (i = oldlen; i < cols; i++)
|
|
|
|
line[i + 1] = ERASE_CHAR;
|
|
|
|
line[cols + 1] = lineattrs & LATTR_MODE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
2003-03-06 15:51:12 +03:00
|
|
|
/*
|
|
|
|
* Get the number of lines in the scrollback.
|
|
|
|
*/
|
|
|
|
static int sblines(Terminal *term)
|
|
|
|
{
|
|
|
|
int sblines = count234(term->scrollback);
|
|
|
|
if (term->cfg.erase_to_scrollback &&
|
|
|
|
term->alt_which && term->alt_screen) {
|
|
|
|
sblines += term->alt_sblines;
|
|
|
|
}
|
|
|
|
return sblines;
|
|
|
|
}
|
|
|
|
|
2001-04-28 19:32:25 +04:00
|
|
|
/*
|
|
|
|
* Retrieve a line of the screen or of the scrollback, according to
|
|
|
|
* whether the y coordinate is non-negative or negative
|
|
|
|
* (respectively).
|
|
|
|
*/
|
2002-12-29 17:47:01 +03:00
|
|
|
static unsigned long *lineptr(Terminal *term, int y, int lineno)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-05-17 12:53:13 +04:00
|
|
|
unsigned long *line, *newline;
|
2001-04-28 19:32:25 +04:00
|
|
|
tree234 *whichtree;
|
2001-05-17 12:53:13 +04:00
|
|
|
int treeindex;
|
2001-04-28 19:32:25 +04:00
|
|
|
|
|
|
|
if (y >= 0) {
|
2002-10-22 20:11:33 +04:00
|
|
|
whichtree = term->screen;
|
2001-04-28 19:32:25 +04:00
|
|
|
treeindex = y;
|
|
|
|
} else {
|
2003-03-06 15:51:12 +03:00
|
|
|
int altlines = 0;
|
|
|
|
if (term->cfg.erase_to_scrollback &&
|
|
|
|
term->alt_which && term->alt_screen) {
|
|
|
|
altlines = term->alt_sblines;
|
|
|
|
}
|
|
|
|
if (y < -altlines) {
|
|
|
|
whichtree = term->scrollback;
|
|
|
|
treeindex = y + altlines + count234(term->scrollback);
|
|
|
|
} else {
|
|
|
|
whichtree = term->alt_screen;
|
|
|
|
treeindex = y + term->alt_sblines;
|
|
|
|
/* treeindex = y + count234(term->alt_screen); */
|
|
|
|
}
|
2001-04-28 19:32:25 +04:00
|
|
|
}
|
|
|
|
line = index234(whichtree, treeindex);
|
|
|
|
|
|
|
|
/* We assume that we don't screw up and retrieve something out of range. */
|
|
|
|
assert(line != NULL);
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
newline = resizeline(line, term->cols);
|
2001-05-17 12:53:13 +04:00
|
|
|
if (newline != line) {
|
2001-04-28 19:32:25 +04:00
|
|
|
delpos234(whichtree, treeindex);
|
2001-05-17 12:53:13 +04:00
|
|
|
addpos234(whichtree, newline, treeindex);
|
2001-05-21 17:33:12 +04:00
|
|
|
line = newline;
|
2001-04-28 19:32:25 +04:00
|
|
|
}
|
|
|
|
|
2001-05-06 18:35:20 +04:00
|
|
|
return line + 1;
|
2001-04-28 19:32:25 +04:00
|
|
|
}
|
2001-05-06 18:35:20 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
#define lineptr(x) lineptr(term,x,__LINE__)
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Set up power-on settings for the terminal.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void power_on(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.x = term->curs.y = 0;
|
|
|
|
term->alt_x = term->alt_y = 0;
|
|
|
|
term->savecurs.x = term->savecurs.y = 0;
|
|
|
|
term->alt_t = term->marg_t = 0;
|
|
|
|
if (term->rows != -1)
|
|
|
|
term->alt_b = term->marg_b = term->rows - 1;
|
1999-01-08 16:02:13 +03:00
|
|
|
else
|
2002-10-22 20:11:33 +04:00
|
|
|
term->alt_b = term->marg_b = 0;
|
|
|
|
if (term->cols != -1) {
|
1999-01-08 16:02:13 +03:00
|
|
|
int i;
|
2002-10-22 20:11:33 +04:00
|
|
|
for (i = 0; i < term->cols; i++)
|
|
|
|
term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2003-01-12 17:30:02 +03:00
|
|
|
term->alt_om = term->dec_om = term->cfg.dec_om;
|
2002-10-23 16:41:35 +04:00
|
|
|
term->alt_ins = term->insert = FALSE;
|
|
|
|
term->alt_wnext = term->wrapnext = term->save_wnext = FALSE;
|
2003-01-12 17:30:02 +03:00
|
|
|
term->alt_wrap = term->wrap = term->cfg.wrap_mode;
|
2002-10-23 16:41:35 +04:00
|
|
|
term->alt_cset = term->cset = term->save_cset = 0;
|
|
|
|
term->alt_utf = term->utf = term->save_utf = 0;
|
|
|
|
term->utf_state = 0;
|
|
|
|
term->alt_sco_acs = term->sco_acs = term->save_sco_acs = 0;
|
|
|
|
term->cset_attr[0] = term->cset_attr[1] = term->save_csattr = ATTR_ASCII;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->rvideo = 0;
|
|
|
|
term->in_vbell = FALSE;
|
|
|
|
term->cursor_on = 1;
|
|
|
|
term->big_cursor = 0;
|
|
|
|
term->save_attr = term->curr_attr = ATTR_DEFAULT;
|
|
|
|
term->term_editing = term->term_echoing = FALSE;
|
2003-01-12 17:30:02 +03:00
|
|
|
term->app_cursor_keys = term->cfg.app_cursor;
|
|
|
|
term->app_keypad_keys = term->cfg.app_keypad;
|
|
|
|
term->use_bce = term->cfg.bce;
|
|
|
|
term->blink_is_real = term->cfg.blinktext;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->erase_char = ERASE_CHAR;
|
|
|
|
term->alt_which = 0;
|
|
|
|
term_print_finish(term);
|
1999-01-08 16:02:13 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 256; i++)
|
2003-01-12 17:30:02 +03:00
|
|
|
term->wordness[i] = term->cfg.wordness[i];
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->screen) {
|
|
|
|
swap_screen(term, 1, FALSE, FALSE);
|
|
|
|
erase_lots(term, FALSE, TRUE, TRUE);
|
|
|
|
swap_screen(term, 0, FALSE, FALSE);
|
|
|
|
erase_lots(term, FALSE, TRUE, TRUE);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Force a screen update.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_update(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
1999-01-08 16:02:13 +03:00
|
|
|
Context ctx;
|
2002-10-26 16:58:13 +04:00
|
|
|
ctx = get_ctx(term->frontend);
|
1999-01-08 16:02:13 +03:00
|
|
|
if (ctx) {
|
2002-10-22 20:11:33 +04:00
|
|
|
int need_sbar_update = term->seen_disp_event;
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->seen_disp_event && term->cfg.scroll_on_disp) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0; /* return to main screen */
|
|
|
|
term->seen_disp_event = 0;
|
2001-09-18 23:04:21 +04:00
|
|
|
need_sbar_update = TRUE;
|
1999-11-30 13:52:07 +03:00
|
|
|
}
|
2001-09-18 23:04:21 +04:00
|
|
|
if (need_sbar_update)
|
2002-10-22 20:11:33 +04:00
|
|
|
update_sbar(term);
|
|
|
|
do_paint(term, ctx, TRUE);
|
2002-10-26 16:58:13 +04:00
|
|
|
sys_cursor(term->frontend, term->curs.x, term->curs.y - term->disptop);
|
2001-05-06 18:35:20 +04:00
|
|
|
free_ctx(ctx);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-10-20 17:23:30 +04:00
|
|
|
/*
|
|
|
|
* Called from front end when a keypress occurs, to trigger
|
|
|
|
* anything magical that needs to happen in that situation.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_seen_key_event(Terminal *term)
|
2002-10-20 17:23:30 +04:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* On any keypress, clear the bell overload mechanism
|
|
|
|
* completely, on the grounds that large numbers of
|
|
|
|
* beeps coming from deliberate key action are likely
|
|
|
|
* to be intended (e.g. beeps from filename completion
|
|
|
|
* blocking repeatedly).
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
term->beep_overloaded = FALSE;
|
|
|
|
while (term->beephead) {
|
|
|
|
struct beeptime *tmp = term->beephead;
|
|
|
|
term->beephead = tmp->next;
|
2002-10-20 17:23:30 +04:00
|
|
|
sfree(tmp);
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->beeptail = NULL;
|
|
|
|
term->nbeeps = 0;
|
2002-10-20 17:23:30 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the scrollback on keypress, if we're doing that.
|
|
|
|
*/
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->cfg.scroll_on_key) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0; /* return to main screen */
|
|
|
|
term->seen_disp_event = 1;
|
2002-10-20 17:23:30 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Same as power_on(), but an external function.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_pwron(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
power_on(term);
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) /* cause ldisc to notice changes */
|
|
|
|
ldisc_send(term->ldisc, NULL, 0, 0);
|
1999-01-08 16:02:13 +03:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0;
|
|
|
|
deselect(term);
|
|
|
|
term_update(term);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2002-03-07 02:04:20 +03:00
|
|
|
/*
|
|
|
|
* When the user reconfigures us, we need to check the forbidden-
|
2002-03-09 20:59:15 +03:00
|
|
|
* alternate-screen config option, disable raw mouse mode if the
|
|
|
|
* user has disabled mouse reporting, and abandon a print job if
|
|
|
|
* the user has disabled printing.
|
2002-03-07 02:04:20 +03:00
|
|
|
*/
|
2003-01-12 17:30:02 +03:00
|
|
|
void term_reconfig(Terminal *term, Config *cfg)
|
2002-03-07 02:04:20 +03:00
|
|
|
{
|
2003-01-12 17:30:02 +03:00
|
|
|
/*
|
|
|
|
* Before adopting the new config, check all those terminal
|
|
|
|
* settings which control power-on defaults; and if they've
|
|
|
|
* changed, we will modify the current state as well as the
|
|
|
|
* default one. The full list is: Auto wrap mode, DEC Origin
|
|
|
|
* Mode, BCE, blinking text, character classes.
|
|
|
|
*/
|
|
|
|
int reset_wrap, reset_decom, reset_bce, reset_blink, reset_charclass;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
reset_wrap = (term->cfg.wrap_mode != cfg->wrap_mode);
|
|
|
|
reset_decom = (term->cfg.dec_om != cfg->dec_om);
|
|
|
|
reset_bce = (term->cfg.bce != cfg->bce);
|
|
|
|
reset_blink = (term->cfg.blinktext != cfg->blinktext);
|
|
|
|
reset_charclass = 0;
|
|
|
|
for (i = 0; i < lenof(term->cfg.wordness); i++)
|
|
|
|
if (term->cfg.wordness[i] != cfg->wordness[i])
|
|
|
|
reset_charclass = 1;
|
|
|
|
|
|
|
|
term->cfg = *cfg; /* STRUCTURE COPY */
|
|
|
|
|
|
|
|
if (reset_wrap)
|
|
|
|
term->alt_wrap = term->wrap = term->cfg.wrap_mode;
|
|
|
|
if (reset_decom)
|
|
|
|
term->alt_om = term->dec_om = term->cfg.dec_om;
|
2003-04-12 13:05:28 +04:00
|
|
|
if (reset_bce) {
|
2003-01-12 17:30:02 +03:00
|
|
|
term->use_bce = term->cfg.bce;
|
2003-04-12 13:05:28 +04:00
|
|
|
if (term->use_bce)
|
|
|
|
term->erase_char = (' ' | ATTR_ASCII |
|
|
|
|
(term->curr_attr &
|
|
|
|
(ATTR_FGMASK | ATTR_BGMASK)));
|
|
|
|
else
|
|
|
|
term->erase_char = ERASE_CHAR;
|
|
|
|
}
|
2003-01-12 17:30:02 +03:00
|
|
|
if (reset_blink)
|
|
|
|
term->blink_is_real = term->cfg.blinktext;
|
|
|
|
if (reset_charclass)
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
term->wordness[i] = term->cfg.wordness[i];
|
|
|
|
|
|
|
|
if (term->cfg.no_alt_screen)
|
2002-10-22 20:11:33 +04:00
|
|
|
swap_screen(term, 0, FALSE, FALSE);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->cfg.no_mouse_rep) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->xterm_mouse = 0;
|
2002-10-26 16:58:13 +04:00
|
|
|
set_raw_mouse_mode(term->frontend, 0);
|
2002-03-09 14:47:39 +03:00
|
|
|
}
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->cfg.no_remote_charset) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[0] = term->cset_attr[1] = ATTR_ASCII;
|
|
|
|
term->sco_acs = term->alt_sco_acs = 0;
|
|
|
|
term->utf = 0;
|
2002-03-07 02:04:20 +03:00
|
|
|
}
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!*term->cfg.printer) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term_print_finish(term);
|
2002-03-09 20:59:15 +03:00
|
|
|
}
|
2002-03-07 02:04:20 +03:00
|
|
|
}
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Clear the scrollback.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_clrsb(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-04-17 01:25:13 +04:00
|
|
|
unsigned long *line;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0;
|
|
|
|
while ((line = delpos234(term->scrollback, 0)) != NULL) {
|
2001-04-17 01:25:13 +04:00
|
|
|
sfree(line);
|
|
|
|
}
|
2003-03-07 21:18:38 +03:00
|
|
|
term->tempsblines = 0;
|
2003-03-06 15:51:12 +03:00
|
|
|
term->alt_sblines = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
update_sbar(term);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialise the terminal.
|
|
|
|
*/
|
2003-01-14 21:28:23 +03:00
|
|
|
Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
|
|
|
|
void *frontend)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
Terminal *term;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a new Terminal structure and initialise the fields
|
|
|
|
* that need it.
|
|
|
|
*/
|
2003-03-29 19:14:26 +03:00
|
|
|
term = snew(Terminal);
|
2002-10-26 16:58:13 +04:00
|
|
|
term->frontend = frontend;
|
2003-01-14 21:28:23 +03:00
|
|
|
term->ucsdata = ucsdata;
|
2003-01-12 17:30:02 +03:00
|
|
|
term->cfg = *mycfg; /* STRUCTURE COPY */
|
2002-10-26 16:58:13 +04:00
|
|
|
term->logctx = NULL;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->compatibility_level = TM_PUTTY;
|
|
|
|
strcpy(term->id_string, "\033[?6c");
|
|
|
|
term->last_blink = term->last_tblink = 0;
|
|
|
|
term->paste_buffer = NULL;
|
2003-01-08 12:15:56 +03:00
|
|
|
term->paste_len = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->last_paste = 0;
|
|
|
|
bufchain_init(&term->inbuf);
|
|
|
|
bufchain_init(&term->printer_buf);
|
2002-10-23 16:41:35 +04:00
|
|
|
term->printing = term->only_printing = FALSE;
|
|
|
|
term->print_job = NULL;
|
|
|
|
term->vt52_mode = FALSE;
|
|
|
|
term->cr_lf_return = FALSE;
|
|
|
|
term->seen_disp_event = FALSE;
|
2002-10-26 14:16:19 +04:00
|
|
|
term->xterm_mouse = term->mouse_is_down = FALSE;
|
2002-10-23 16:41:35 +04:00
|
|
|
term->reset_132 = FALSE;
|
|
|
|
term->blinker = term->tblinker = 0;
|
|
|
|
term->has_focus = 1;
|
|
|
|
term->repeat_off = FALSE;
|
|
|
|
term->termstate = TOPLEVEL;
|
|
|
|
term->selstate = NO_SELECTION;
|
2002-10-28 20:30:47 +03:00
|
|
|
term->curstype = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
|
|
|
|
term->screen = term->alt_screen = term->scrollback = NULL;
|
2003-03-07 21:18:38 +03:00
|
|
|
term->tempsblines = 0;
|
2003-03-06 15:51:12 +03:00
|
|
|
term->alt_sblines = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0;
|
|
|
|
term->disptext = term->dispcurs = NULL;
|
|
|
|
term->tabs = NULL;
|
|
|
|
deselect(term);
|
|
|
|
term->rows = term->cols = -1;
|
|
|
|
power_on(term);
|
|
|
|
term->beephead = term->beeptail = NULL;
|
2003-02-01 15:26:33 +03:00
|
|
|
#ifdef OPTIMISE_SCROLL
|
|
|
|
term->scrollhead = term->scrolltail = NULL;
|
|
|
|
#endif /* OPTIMISE_SCROLL */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->nbeeps = 0;
|
|
|
|
term->lastbeep = FALSE;
|
|
|
|
term->beep_overloaded = FALSE;
|
2002-11-29 03:32:03 +03:00
|
|
|
term->attr_mask = 0xffffffff;
|
2002-10-25 15:30:33 +04:00
|
|
|
term->resize_fn = NULL;
|
|
|
|
term->resize_ctx = NULL;
|
2003-03-29 21:30:14 +03:00
|
|
|
term->in_term_out = FALSE;
|
2002-10-22 20:11:33 +04:00
|
|
|
|
|
|
|
return term;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2003-01-16 02:30:21 +03:00
|
|
|
void term_free(Terminal *term)
|
|
|
|
{
|
|
|
|
unsigned long *line;
|
|
|
|
struct beeptime *beep;
|
|
|
|
|
|
|
|
while ((line = delpos234(term->scrollback, 0)) != NULL)
|
|
|
|
sfree(line);
|
|
|
|
freetree234(term->scrollback);
|
|
|
|
while ((line = delpos234(term->screen, 0)) != NULL)
|
|
|
|
sfree(line);
|
|
|
|
freetree234(term->screen);
|
|
|
|
while ((line = delpos234(term->alt_screen, 0)) != NULL)
|
|
|
|
sfree(line);
|
|
|
|
freetree234(term->alt_screen);
|
|
|
|
sfree(term->disptext);
|
|
|
|
while (term->beephead) {
|
|
|
|
beep = term->beephead;
|
|
|
|
term->beephead = beep->next;
|
|
|
|
sfree(beep);
|
|
|
|
}
|
|
|
|
bufchain_clear(&term->inbuf);
|
|
|
|
if(term->print_job)
|
|
|
|
printer_finish_job(term->print_job);
|
|
|
|
bufchain_clear(&term->printer_buf);
|
|
|
|
sfree(term->paste_buffer);
|
|
|
|
sfree(term);
|
|
|
|
}
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Set up the terminal for a given size.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-05-13 18:02:28 +04:00
|
|
|
tree234 *newalt;
|
|
|
|
unsigned long *newdisp, *line;
|
|
|
|
int i, j;
|
2001-04-28 19:32:25 +04:00
|
|
|
int sblen;
|
2002-10-22 20:11:33 +04:00
|
|
|
int save_alt_which = term->alt_which;
|
2000-03-11 17:10:10 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (newrows == term->rows && newcols == term->cols &&
|
|
|
|
newsavelines == term->savelines)
|
1999-01-08 16:02:13 +03:00
|
|
|
return; /* nothing to do */
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
deselect(term);
|
|
|
|
swap_screen(term, 0, FALSE, FALSE);
|
2000-03-11 17:10:10 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
term->alt_t = term->marg_t = 0;
|
|
|
|
term->alt_b = term->marg_b = newrows - 1;
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->rows == -1) {
|
|
|
|
term->scrollback = newtree234(NULL);
|
|
|
|
term->screen = newtree234(NULL);
|
2003-03-07 21:18:38 +03:00
|
|
|
term->tempsblines = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->rows = 0;
|
2001-04-28 19:32:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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:
|
|
|
|
*
|
2003-03-07 21:18:38 +03:00
|
|
|
* - If the new screen is longer, we shunt lines in from temporary
|
|
|
|
* scrollback if possible, otherwise we add new blank lines at
|
|
|
|
* the bottom.
|
|
|
|
*
|
|
|
|
* - If the new screen is shorter, we remove any blank lines at
|
|
|
|
* the bottom if possible, otherwise shunt lines above the cursor
|
|
|
|
* to scrollback if possible, otherwise delete lines below the
|
|
|
|
* cursor.
|
2001-04-28 19:32:25 +04:00
|
|
|
*
|
|
|
|
* - Then, if the new scrollback length is less than the
|
|
|
|
* amount of scrollback we actually have, we must throw some
|
|
|
|
* away.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
sblen = count234(term->scrollback);
|
2001-05-03 14:10:53 +04:00
|
|
|
/* Do this loop to expand the screen if newrows > rows */
|
2003-03-07 21:18:38 +03:00
|
|
|
assert(term->rows == count234(term->screen));
|
|
|
|
while (term->rows < newrows) {
|
|
|
|
if (term->tempsblines > 0) {
|
|
|
|
/* Insert a line from the scrollback at the top of the screen. */
|
|
|
|
assert(sblen >= term->tempsblines);
|
2002-10-22 20:11:33 +04:00
|
|
|
line = delpos234(term->scrollback, --sblen);
|
2003-03-07 21:18:38 +03:00
|
|
|
term->tempsblines -= 1;
|
|
|
|
addpos234(term->screen, line, 0);
|
|
|
|
term->curs.y += 1;
|
|
|
|
term->savecurs.y += 1;
|
2001-05-06 18:35:20 +04:00
|
|
|
} else {
|
2003-03-07 21:18:38 +03:00
|
|
|
/* Add a new blank line at the bottom of the screen. */
|
2003-03-29 19:14:26 +03:00
|
|
|
line = snewn(newcols + 2, TTYPE);
|
2001-05-06 18:35:20 +04:00
|
|
|
line[0] = newcols;
|
2003-01-28 02:03:31 +03:00
|
|
|
for (j = 0; j < newcols; j++)
|
2001-05-06 18:35:20 +04:00
|
|
|
line[j + 1] = ERASE_CHAR;
|
2003-01-30 16:39:24 +03:00
|
|
|
line[newcols + 1] = LATTR_NORM;
|
2003-03-07 21:18:38 +03:00
|
|
|
addpos234(term->screen, line, count234(term->screen));
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
2003-03-07 21:18:38 +03:00
|
|
|
term->rows += 1;
|
2001-05-03 14:10:53 +04:00
|
|
|
}
|
|
|
|
/* Do this loop to shrink the screen if newrows < rows */
|
2003-03-07 21:18:38 +03:00
|
|
|
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));
|
|
|
|
} else {
|
|
|
|
/* push top row to scrollback */
|
|
|
|
line = delpos234(term->screen, 0);
|
|
|
|
addpos234(term->scrollback, line, sblen++);
|
|
|
|
term->tempsblines += 1;
|
|
|
|
term->curs.y -= 1;
|
|
|
|
term->savecurs.y -= 1;
|
|
|
|
}
|
|
|
|
term->rows -= 1;
|
2001-04-28 19:32:25 +04:00
|
|
|
}
|
2003-03-07 21:18:38 +03:00
|
|
|
assert(term->rows == newrows);
|
2002-10-22 20:11:33 +04:00
|
|
|
assert(count234(term->screen) == newrows);
|
2003-03-07 21:18:38 +03:00
|
|
|
|
|
|
|
/* Delete any excess lines from the scrollback. */
|
2001-04-28 19:32:25 +04:00
|
|
|
while (sblen > newsavelines) {
|
2002-10-22 20:11:33 +04:00
|
|
|
line = delpos234(term->scrollback, 0);
|
2001-04-28 19:32:25 +04:00
|
|
|
sfree(line);
|
|
|
|
sblen--;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
2003-03-07 21:18:38 +03:00
|
|
|
if (sblen < term->tempsblines)
|
|
|
|
term->tempsblines = sblen;
|
2002-10-22 20:11:33 +04:00
|
|
|
assert(count234(term->scrollback) <= newsavelines);
|
2003-03-07 21:18:38 +03:00
|
|
|
assert(count234(term->scrollback) >= term->tempsblines);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0;
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2003-03-07 21:18:38 +03:00
|
|
|
/* Make a new displayed text buffer. */
|
2003-03-29 19:14:26 +03:00
|
|
|
newdisp = snewn(newrows * (newcols + 1), TTYPE);
|
2001-05-06 18:35:20 +04:00
|
|
|
for (i = 0; i < newrows * (newcols + 1); i++)
|
1999-01-08 16:02:13 +03:00
|
|
|
newdisp[i] = ATTR_INVALID;
|
2002-10-22 20:11:33 +04:00
|
|
|
sfree(term->disptext);
|
|
|
|
term->disptext = newdisp;
|
|
|
|
term->dispcurs = NULL;
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2003-03-07 21:18:38 +03:00
|
|
|
/* Make a new alternate screen. */
|
2001-04-17 01:25:13 +04:00
|
|
|
newalt = newtree234(NULL);
|
2001-05-06 18:35:20 +04:00
|
|
|
for (i = 0; i < newrows; i++) {
|
2003-03-29 19:14:26 +03:00
|
|
|
line = snewn(newcols + 2, TTYPE);
|
2001-04-28 19:32:25 +04:00
|
|
|
line[0] = newcols;
|
2003-01-28 02:03:31 +03:00
|
|
|
for (j = 0; j < newcols; j++)
|
2002-10-22 20:11:33 +04:00
|
|
|
line[j + 1] = term->erase_char;
|
2003-01-28 02:03:31 +03:00
|
|
|
line[newcols + 1] = LATTR_NORM;
|
2001-04-17 01:25:13 +04:00
|
|
|
addpos234(newalt, line, i);
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->alt_screen) {
|
|
|
|
while (NULL != (line = delpos234(term->alt_screen, 0)))
|
2001-04-17 01:25:13 +04:00
|
|
|
sfree(line);
|
2002-10-22 20:11:33 +04:00
|
|
|
freetree234(term->alt_screen);
|
2001-04-17 01:25:13 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->alt_screen = newalt;
|
2003-03-06 15:51:12 +03:00
|
|
|
term->alt_sblines = 0;
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2003-03-29 19:14:26 +03:00
|
|
|
term->tabs = sresize(term->tabs, newcols, unsigned char);
|
1999-01-08 16:02:13 +03:00
|
|
|
{
|
|
|
|
int i;
|
2002-10-22 20:11:33 +04:00
|
|
|
for (i = (term->cols > 0 ? term->cols : 0); i < newcols; i++)
|
|
|
|
term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2003-03-07 21:18:38 +03:00
|
|
|
/* Check that the cursor positions are still valid. */
|
|
|
|
if (term->savecurs.y < 0)
|
|
|
|
term->savecurs.y = 0;
|
|
|
|
if (term->savecurs.y >= newrows)
|
|
|
|
term->savecurs.y = newrows - 1;
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.y < 0)
|
|
|
|
term->curs.y = 0;
|
|
|
|
if (term->curs.y >= newrows)
|
|
|
|
term->curs.y = newrows - 1;
|
|
|
|
if (term->curs.x >= newcols)
|
|
|
|
term->curs.x = newcols - 1;
|
|
|
|
term->alt_x = term->alt_y = 0;
|
|
|
|
term->wrapnext = term->alt_wnext = FALSE;
|
|
|
|
|
|
|
|
term->rows = newrows;
|
|
|
|
term->cols = newcols;
|
|
|
|
term->savelines = newsavelines;
|
1999-01-08 16:02:13 +03:00
|
|
|
fix_cpos;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
swap_screen(term, save_alt_which, FALSE, FALSE);
|
2000-03-11 17:10:10 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
update_sbar(term);
|
|
|
|
term_update(term);
|
2002-10-25 15:30:33 +04:00
|
|
|
if (term->resize_fn)
|
|
|
|
term->resize_fn(term->resize_ctx, term->cols, term->rows);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hand a function and context pointer to the terminal which it can
|
|
|
|
* use to notify a back end of resizes.
|
|
|
|
*/
|
|
|
|
void term_provide_resize_fn(Terminal *term,
|
|
|
|
void (*resize_fn)(void *, int, int),
|
|
|
|
void *resize_ctx)
|
|
|
|
{
|
|
|
|
term->resize_fn = resize_fn;
|
|
|
|
term->resize_ctx = resize_ctx;
|
|
|
|
if (term->cols > 0 && term->rows > 0)
|
|
|
|
resize_fn(resize_ctx, term->cols, term->rows);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2003-03-06 15:51:12 +03:00
|
|
|
/* Find the bottom line on the screen that has any content.
|
|
|
|
* If only the top line has content, returns 0.
|
|
|
|
* If no lines have content, return -1.
|
|
|
|
*/
|
|
|
|
static int find_last_nonempty_line(Terminal * term, tree234 * screen)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = count234(screen) - 1; i >= 0; i--) {
|
|
|
|
unsigned long *line = index234(screen, i);
|
|
|
|
int j;
|
|
|
|
int cols = line[0];
|
|
|
|
for (j = 0; j < cols; j++) {
|
|
|
|
if (line[j + 1] != term->erase_char) break;
|
|
|
|
}
|
|
|
|
if (j != cols) break;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
2002-10-22 14:31:23 +04:00
|
|
|
* Swap screens. If `reset' is TRUE and we have been asked to
|
|
|
|
* switch to the alternate screen, we must bring most of its
|
|
|
|
* configuration from the main screen and erase the contents of the
|
|
|
|
* alternate screen completely. (This is even true if we're already
|
|
|
|
* on it! Blame xterm.)
|
1999-01-08 16:02:13 +03:00
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void swap_screen(Terminal *term, int which, int reset, int keep_cur_pos)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
1999-01-08 16:02:13 +03:00
|
|
|
int t;
|
2001-04-17 01:25:13 +04:00
|
|
|
tree234 *ttr;
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2002-10-22 14:31:23 +04:00
|
|
|
if (!which)
|
|
|
|
reset = FALSE; /* do no weird resetting if which==0 */
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (which != term->alt_which) {
|
|
|
|
term->alt_which = which;
|
2002-10-22 14:31:23 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
ttr = term->alt_screen;
|
|
|
|
term->alt_screen = term->screen;
|
|
|
|
term->screen = ttr;
|
2003-03-06 15:51:12 +03:00
|
|
|
term->alt_sblines = find_last_nonempty_line(term, term->alt_screen) + 1;
|
2002-10-22 20:11:33 +04:00
|
|
|
t = term->curs.x;
|
2002-10-22 14:31:23 +04:00
|
|
|
if (!reset && !keep_cur_pos)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.x = term->alt_x;
|
|
|
|
term->alt_x = t;
|
|
|
|
t = term->curs.y;
|
2002-10-22 14:31:23 +04:00
|
|
|
if (!reset && !keep_cur_pos)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.y = term->alt_y;
|
|
|
|
term->alt_y = t;
|
|
|
|
t = term->marg_t;
|
|
|
|
if (!reset) term->marg_t = term->alt_t;
|
|
|
|
term->alt_t = t;
|
|
|
|
t = term->marg_b;
|
|
|
|
if (!reset) term->marg_b = term->alt_b;
|
|
|
|
term->alt_b = t;
|
|
|
|
t = term->dec_om;
|
|
|
|
if (!reset) term->dec_om = term->alt_om;
|
|
|
|
term->alt_om = t;
|
|
|
|
t = term->wrap;
|
|
|
|
if (!reset) term->wrap = term->alt_wrap;
|
|
|
|
term->alt_wrap = t;
|
|
|
|
t = term->wrapnext;
|
|
|
|
if (!reset) term->wrapnext = term->alt_wnext;
|
|
|
|
term->alt_wnext = t;
|
|
|
|
t = term->insert;
|
|
|
|
if (!reset) term->insert = term->alt_ins;
|
|
|
|
term->alt_ins = t;
|
|
|
|
t = term->cset;
|
|
|
|
if (!reset) term->cset = term->alt_cset;
|
|
|
|
term->alt_cset = t;
|
|
|
|
t = term->utf;
|
|
|
|
if (!reset) term->utf = term->alt_utf;
|
|
|
|
term->alt_utf = t;
|
|
|
|
t = term->sco_acs;
|
|
|
|
if (!reset) term->sco_acs = term->alt_sco_acs;
|
|
|
|
term->alt_sco_acs = t;
|
2002-10-22 14:31:23 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (reset && term->screen) {
|
2002-10-22 14:31:23 +04:00
|
|
|
/*
|
|
|
|
* Yes, this _is_ supposed to honour background-colour-erase.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
erase_lots(term, FALSE, TRUE, TRUE);
|
2002-10-22 14:31:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This might not be possible if we're called during
|
|
|
|
* initialisation.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->screen)
|
2002-10-22 14:31:23 +04:00
|
|
|
fix_cpos;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the scroll bar.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void update_sbar(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2003-03-06 15:51:12 +03:00
|
|
|
int nscroll = sblines(term);
|
2002-10-26 16:58:13 +04:00
|
|
|
set_sbar(term->frontend, nscroll + term->rows,
|
|
|
|
nscroll + term->disptop, term->rows);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether the region bounded by the two pointers intersects
|
|
|
|
* the scroll region, and de-select the on-screen selection if so.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void check_selection(Terminal *term, pos from, pos to)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
if (poslt(from, term->selend) && poslt(term->selstart, to))
|
|
|
|
deselect(term);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scroll the screen. (`lines' is +ve for scrolling forward, -ve
|
|
|
|
* for backward.) `sb' is TRUE if the scrolling is permitted to
|
|
|
|
* affect the scrollback buffer.
|
2001-04-17 13:25:52 +04:00
|
|
|
*
|
|
|
|
* NB this function invalidates all pointers into lines of the
|
|
|
|
* screen data structures. In particular, you MUST call fix_cpos
|
|
|
|
* after calling scroll() and before doing anything else that
|
|
|
|
* uses the cpos shortcut pointer.
|
1999-01-08 16:02:13 +03:00
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void scroll(Terminal *term, int topline, int botline, int lines, int sb)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-04-17 01:25:13 +04:00
|
|
|
unsigned long *line, *line2;
|
2002-11-24 00:42:46 +03:00
|
|
|
int i, seltop, olddisptop, shift;
|
2000-07-26 16:13:51 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (topline != 0 || term->alt_which != 0)
|
2001-04-17 01:25:13 +04:00
|
|
|
sb = FALSE;
|
|
|
|
|
2002-11-24 00:42:46 +03:00
|
|
|
olddisptop = term->disptop;
|
|
|
|
shift = lines;
|
2001-04-17 01:25:13 +04:00
|
|
|
if (lines < 0) {
|
|
|
|
while (lines < 0) {
|
2002-10-22 20:11:33 +04:00
|
|
|
line = delpos234(term->screen, botline);
|
|
|
|
line = resizeline(line, term->cols);
|
|
|
|
for (i = 0; i < term->cols; i++)
|
|
|
|
line[i + 1] = term->erase_char;
|
|
|
|
line[term->cols + 1] = 0;
|
|
|
|
addpos234(term->screen, line, topline);
|
|
|
|
|
|
|
|
if (term->selstart.y >= topline && term->selstart.y <= botline) {
|
|
|
|
term->selstart.y++;
|
|
|
|
if (term->selstart.y > botline) {
|
2003-03-29 16:54:11 +03:00
|
|
|
term->selstart.y = botline + 1;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selstart.x = 0;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->selend.y >= topline && term->selend.y <= botline) {
|
|
|
|
term->selend.y++;
|
|
|
|
if (term->selend.y > botline) {
|
2003-03-29 16:54:11 +03:00
|
|
|
term->selend.y = botline + 1;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selend.x = 0;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-04-17 01:25:13 +04:00
|
|
|
lines++;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
} else {
|
2001-04-17 01:25:13 +04:00
|
|
|
while (lines > 0) {
|
2002-10-22 20:11:33 +04:00
|
|
|
line = delpos234(term->screen, topline);
|
|
|
|
if (sb && term->savelines > 0) {
|
2003-03-07 21:18:38 +03:00
|
|
|
int sblen = count234(term->scrollback);
|
2001-04-17 01:25:13 +04:00
|
|
|
/*
|
|
|
|
* We must add this line to the scrollback. We'll
|
|
|
|
* remove a line from the top of the scrollback to
|
|
|
|
* replace it, or allocate a new one if the
|
|
|
|
* scrollback isn't full.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (sblen == term->savelines) {
|
|
|
|
sblen--, line2 = delpos234(term->scrollback, 0);
|
2001-04-28 19:32:25 +04:00
|
|
|
} else {
|
2003-03-29 19:14:26 +03:00
|
|
|
line2 = snewn(term->cols + 2, TTYPE);
|
2002-10-22 20:11:33 +04:00
|
|
|
line2[0] = term->cols;
|
2003-03-07 21:18:38 +03:00
|
|
|
term->tempsblines += 1;
|
2001-04-28 19:32:25 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
addpos234(term->scrollback, line, sblen);
|
2001-04-17 01:25:13 +04:00
|
|
|
line = line2;
|
2001-09-13 00:38:40 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->disptop > -term->savelines && term->disptop < 0)
|
|
|
|
term->disptop--;
|
2001-04-17 01:25:13 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
line = resizeline(line, term->cols);
|
|
|
|
for (i = 0; i < term->cols; i++)
|
|
|
|
line[i + 1] = term->erase_char;
|
2003-01-28 02:03:31 +03:00
|
|
|
line[term->cols + 1] = LATTR_NORM;
|
2002-10-22 20:11:33 +04:00
|
|
|
addpos234(term->screen, line, botline);
|
2001-04-17 01:25:13 +04:00
|
|
|
|
2001-09-13 00:38:40 +04:00
|
|
|
/*
|
|
|
|
* 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.
|
2001-09-18 22:59:37 +04:00
|
|
|
*
|
|
|
|
* This applies to selstart and selend (for an existing
|
|
|
|
* selection), and also selanchor (for one being
|
|
|
|
* selected as we speak).
|
2001-09-13 00:38:40 +04:00
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
seltop = sb ? -term->savelines : topline;
|
|
|
|
|
|
|
|
if (term->selstart.y >= seltop &&
|
|
|
|
term->selstart.y <= botline) {
|
|
|
|
term->selstart.y--;
|
|
|
|
if (term->selstart.y < seltop) {
|
|
|
|
term->selstart.y = seltop;
|
|
|
|
term->selstart.x = 0;
|
2001-04-17 01:25:13 +04:00
|
|
|
}
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->selend.y >= seltop && term->selend.y <= botline) {
|
|
|
|
term->selend.y--;
|
|
|
|
if (term->selend.y < seltop) {
|
|
|
|
term->selend.y = seltop;
|
|
|
|
term->selend.x = 0;
|
2001-04-17 01:25:13 +04:00
|
|
|
}
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->selanchor.y >= seltop && term->selanchor.y <= botline) {
|
|
|
|
term->selanchor.y--;
|
|
|
|
if (term->selanchor.y < seltop) {
|
|
|
|
term->selanchor.y = seltop;
|
|
|
|
term->selanchor.x = 0;
|
2001-09-18 22:59:37 +04:00
|
|
|
}
|
|
|
|
}
|
2001-04-17 01:25:13 +04:00
|
|
|
|
|
|
|
lines--;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2002-11-24 00:42:46 +03:00
|
|
|
#ifdef OPTIMISE_SCROLL
|
|
|
|
shift += term->disptop - olddisptop;
|
|
|
|
if (shift < term->rows && shift > -term->rows && shift != 0)
|
|
|
|
scroll_display(term, topline, botline, shift);
|
|
|
|
#endif /* OPTIMISE_SCROLL */
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2002-11-23 16:07:29 +03:00
|
|
|
#ifdef OPTIMISE_SCROLL
|
2003-02-01 15:26:33 +03:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2003-02-01 18:42:01 +03:00
|
|
|
static void save_scroll(Terminal *term, int topline, int botline, int lines)
|
2003-02-01 15:26:33 +03:00
|
|
|
{
|
|
|
|
struct scrollregion *newscroll;
|
|
|
|
if (term->scrolltail &&
|
|
|
|
term->scrolltail->topline == topline &&
|
|
|
|
term->scrolltail->botline == botline) {
|
|
|
|
term->scrolltail->lines += lines;
|
|
|
|
} else {
|
2003-03-29 19:14:26 +03:00
|
|
|
newscroll = snew(struct scrollregion);
|
2003-02-01 15:26:33 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-11-23 16:07:29 +03:00
|
|
|
/*
|
|
|
|
* Scroll the physical display, and our conception of it in disptext.
|
|
|
|
*/
|
|
|
|
static void scroll_display(Terminal *term, int topline, int botline, int lines)
|
|
|
|
{
|
|
|
|
unsigned long *start, *end;
|
|
|
|
int distance, size, i;
|
|
|
|
|
|
|
|
start = term->disptext + topline * (term->cols + 1);
|
|
|
|
end = term->disptext + (botline + 1) * (term->cols + 1);
|
|
|
|
distance = (lines > 0 ? lines : -lines) * (term->cols + 1);
|
|
|
|
size = end - start - distance;
|
|
|
|
if (lines > 0) {
|
|
|
|
memmove(start, start + distance, size * TSIZE);
|
2002-11-24 00:42:46 +03:00
|
|
|
if (term->dispcurs >= start + distance &&
|
|
|
|
term->dispcurs <= start + distance + size)
|
|
|
|
term->dispcurs -= distance;
|
2002-11-23 16:07:29 +03:00
|
|
|
for (i = 0; i < distance; i++)
|
|
|
|
(start + size)[i] |= ATTR_INVALID;
|
|
|
|
} else {
|
|
|
|
memmove(start + distance, start, size * TSIZE);
|
2002-11-24 00:42:46 +03:00
|
|
|
if (term->dispcurs >= start && term->dispcurs <= start + size)
|
|
|
|
term->dispcurs += distance;
|
2002-11-23 16:07:29 +03:00
|
|
|
for (i = 0; i < distance; i++)
|
|
|
|
start[i] |= ATTR_INVALID;
|
|
|
|
}
|
2003-02-01 15:26:33 +03:00
|
|
|
save_scroll(term, topline, botline, lines);
|
2002-11-23 16:07:29 +03:00
|
|
|
}
|
|
|
|
#endif /* OPTIMISE_SCROLL */
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Move the cursor to a given position, clipping at boundaries. We
|
|
|
|
* may or may not want to clip at the scroll margin: marg_clip is 0
|
|
|
|
* not to, 1 to disallow _passing_ the margins, and 2 to disallow
|
|
|
|
* even _being_ outside the margins.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void move(Terminal *term, int x, int y, int marg_clip)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
1999-01-08 16:02:13 +03:00
|
|
|
if (x < 0)
|
|
|
|
x = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
if (x >= term->cols)
|
|
|
|
x = term->cols - 1;
|
1999-01-08 16:02:13 +03:00
|
|
|
if (marg_clip) {
|
2002-10-22 20:11:33 +04:00
|
|
|
if ((term->curs.y >= term->marg_t || marg_clip == 2) &&
|
|
|
|
y < term->marg_t)
|
|
|
|
y = term->marg_t;
|
|
|
|
if ((term->curs.y <= term->marg_b || marg_clip == 2) &&
|
|
|
|
y > term->marg_b)
|
|
|
|
y = term->marg_b;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
if (y < 0)
|
|
|
|
y = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
if (y >= term->rows)
|
|
|
|
y = term->rows - 1;
|
|
|
|
term->curs.x = x;
|
|
|
|
term->curs.y = y;
|
1999-01-08 16:02:13 +03:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrapnext = FALSE;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save or restore the cursor and SGR mode.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void save_cursor(Terminal *term, int save)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
1999-01-08 16:02:13 +03:00
|
|
|
if (save) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->savecurs = term->curs;
|
|
|
|
term->save_attr = term->curr_attr;
|
|
|
|
term->save_cset = term->cset;
|
|
|
|
term->save_utf = term->utf;
|
|
|
|
term->save_wnext = term->wrapnext;
|
|
|
|
term->save_csattr = term->cset_attr[term->cset];
|
|
|
|
term->save_sco_acs = term->sco_acs;
|
1999-01-08 16:02:13 +03:00
|
|
|
} else {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs = term->savecurs;
|
2000-07-26 16:13:51 +04:00
|
|
|
/* Make sure the window hasn't shrunk since the save */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.x >= term->cols)
|
|
|
|
term->curs.x = term->cols - 1;
|
|
|
|
if (term->curs.y >= term->rows)
|
|
|
|
term->curs.y = term->rows - 1;
|
|
|
|
|
|
|
|
term->curr_attr = term->save_attr;
|
|
|
|
term->cset = term->save_cset;
|
|
|
|
term->utf = term->save_utf;
|
|
|
|
term->wrapnext = term->save_wnext;
|
2002-03-02 20:37:48 +03:00
|
|
|
/*
|
|
|
|
* wrapnext might reset to False if the x position is no
|
|
|
|
* longer at the rightmost edge.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->wrapnext && term->curs.x < term->cols-1)
|
|
|
|
term->wrapnext = FALSE;
|
|
|
|
term->cset_attr[term->cset] = term->save_csattr;
|
|
|
|
term->sco_acs = term->save_sco_acs;
|
1999-01-08 16:02:13 +03:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->use_bce)
|
|
|
|
term->erase_char = (' ' | ATTR_ASCII |
|
|
|
|
(term->curr_attr &
|
|
|
|
(ATTR_FGMASK | ATTR_BGMASK)));
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-01-02 19:20:29 +03:00
|
|
|
/*
|
|
|
|
* This function is called before doing _anything_ which affects
|
|
|
|
* only part of a line of text. It is used to mark the boundary
|
|
|
|
* between two character positions, and it indicates that some sort
|
|
|
|
* of effect is going to happen on only one side of that boundary.
|
|
|
|
*
|
|
|
|
* The effect of this function is to check whether a CJK
|
|
|
|
* double-width character is straddling the boundary, and to remove
|
|
|
|
* it and replace it with two spaces if so. (Of course, one or
|
|
|
|
* other of those spaces is then likely to be replaced with
|
|
|
|
* something else again, as a result of whatever happens next.)
|
|
|
|
*
|
|
|
|
* Also, if the boundary is at the right-hand _edge_ of the screen,
|
|
|
|
* it implies something deliberate is being done to the rightmost
|
|
|
|
* column position; hence we must clear LATTR_WRAPPED2.
|
|
|
|
*
|
|
|
|
* The input to the function is the coordinates of the _second_
|
|
|
|
* character of the pair.
|
|
|
|
*/
|
|
|
|
static void check_boundary(Terminal *term, int x, int y)
|
|
|
|
{
|
|
|
|
unsigned long *ldata;
|
|
|
|
|
|
|
|
/* Validate input coordinates, just in case. */
|
|
|
|
if (x == 0 || x > term->cols)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ldata = lineptr(y);
|
|
|
|
if (x == term->cols) {
|
|
|
|
ldata[x] &= ~LATTR_WRAPPED2;
|
|
|
|
} else {
|
|
|
|
if ((ldata[x] & (CHAR_MASK | CSET_MASK)) == UCSWIDE) {
|
|
|
|
ldata[x-1] = ldata[x] =
|
|
|
|
(ldata[x-1] &~ (CHAR_MASK | CSET_MASK)) | ATTR_ASCII | ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Erase a large portion of the screen: the whole screen, or the
|
|
|
|
* whole line, or parts thereof.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void erase_lots(Terminal *term,
|
|
|
|
int line_only, int from_begin, int to_end)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-04-17 12:24:29 +04:00
|
|
|
pos start, end;
|
2001-04-17 01:25:13 +04:00
|
|
|
int erase_lattr;
|
2003-03-07 21:18:38 +03:00
|
|
|
int erasing_lines_from_top = 0;
|
1999-01-08 16:02:13 +03:00
|
|
|
|
|
|
|
if (line_only) {
|
2002-10-22 20:11:33 +04:00
|
|
|
start.y = term->curs.y;
|
2001-04-17 01:25:13 +04:00
|
|
|
start.x = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
end.y = term->curs.y + 1;
|
2001-04-17 01:25:13 +04:00
|
|
|
end.x = 0;
|
|
|
|
erase_lattr = FALSE;
|
1999-01-08 16:02:13 +03:00
|
|
|
} else {
|
2001-04-17 01:25:13 +04:00
|
|
|
start.y = 0;
|
|
|
|
start.x = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
end.y = term->rows;
|
2001-04-17 01:25:13 +04:00
|
|
|
end.x = 0;
|
|
|
|
erase_lattr = TRUE;
|
|
|
|
}
|
|
|
|
if (!from_begin) {
|
2002-10-22 20:11:33 +04:00
|
|
|
start = term->curs;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2001-04-17 01:25:13 +04:00
|
|
|
if (!to_end) {
|
2002-10-22 20:11:33 +04:00
|
|
|
end = term->curs;
|
2001-05-19 15:32:38 +04:00
|
|
|
incpos(end);
|
2001-04-17 01:25:13 +04:00
|
|
|
}
|
2003-01-02 19:20:29 +03:00
|
|
|
if (!from_begin || !to_end)
|
|
|
|
check_boundary(term, term->curs.x, term->curs.y);
|
2002-10-22 20:11:33 +04:00
|
|
|
check_selection(term, start, end);
|
2000-03-17 13:39:05 +03:00
|
|
|
|
|
|
|
/* Clear screen also forces a full window redraw, just in case. */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (start.y == 0 && start.x == 0 && end.y == term->rows)
|
|
|
|
term_invalidate(term);
|
2000-03-17 13:39:05 +03:00
|
|
|
|
2003-03-07 21:18:38 +03:00
|
|
|
/* Lines scrolled away shouldn't be brought back on if the terminal
|
|
|
|
* resizes. */
|
|
|
|
if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr)
|
|
|
|
erasing_lines_from_top = 1;
|
|
|
|
|
|
|
|
if (term->cfg.erase_to_scrollback && erasing_lines_from_top) {
|
2003-03-06 15:51:12 +03:00
|
|
|
/* If it's a whole number of lines, starting at the top, and
|
|
|
|
* we're fully erasing them, erase by scrolling and keep the
|
|
|
|
* lines in the scrollback. */
|
|
|
|
int scrolllines = end.y;
|
|
|
|
if (end.y == term->rows) {
|
|
|
|
/* Shrink until we find a non-empty row.*/
|
|
|
|
scrolllines = find_last_nonempty_line(term, term->screen) + 1;
|
|
|
|
}
|
|
|
|
if (scrolllines > 0)
|
|
|
|
scroll(term, 0, scrolllines - 1, scrolllines, TRUE);
|
|
|
|
fix_cpos;
|
|
|
|
} else {
|
|
|
|
unsigned long *ldata = lineptr(start.y);
|
|
|
|
while (poslt(start, end)) {
|
|
|
|
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);
|
|
|
|
}
|
2001-04-17 01:25:13 +04:00
|
|
|
}
|
2003-03-07 21:18:38 +03:00
|
|
|
|
|
|
|
/* After an erase of lines from the top of the screen, we shouldn't
|
|
|
|
* bring the lines back again if the terminal enlarges (since the user or
|
|
|
|
* application has explictly thrown them away). */
|
|
|
|
if (erasing_lines_from_top && !(term->alt_which))
|
|
|
|
term->tempsblines = 0;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert or delete characters within the current line. n is +ve if
|
|
|
|
* insertion is desired, and -ve for deletion.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void insch(Terminal *term, int n)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
1999-01-08 16:02:13 +03:00
|
|
|
int dir = (n < 0 ? -1 : +1);
|
|
|
|
int m;
|
2001-04-17 01:25:13 +04:00
|
|
|
pos cursplus;
|
|
|
|
unsigned long *ldata;
|
1999-01-08 16:02:13 +03:00
|
|
|
|
|
|
|
n = (n < 0 ? -n : n);
|
2002-10-22 20:11:33 +04:00
|
|
|
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);
|
2003-01-02 19:20:29 +03:00
|
|
|
check_boundary(term, term->curs.x, term->curs.y);
|
|
|
|
if (dir < 0)
|
|
|
|
check_boundary(term, term->curs.x + n, term->curs.y);
|
2002-10-22 20:11:33 +04:00
|
|
|
ldata = lineptr(term->curs.y);
|
1999-01-08 16:02:13 +03:00
|
|
|
if (dir < 0) {
|
2002-10-22 20:11:33 +04:00
|
|
|
memmove(ldata + term->curs.x, ldata + term->curs.x + n, m * TSIZE);
|
1999-01-08 16:02:13 +03:00
|
|
|
while (n--)
|
2002-10-22 20:11:33 +04:00
|
|
|
ldata[term->curs.x + m++] = term->erase_char;
|
1999-01-08 16:02:13 +03:00
|
|
|
} else {
|
2002-10-22 20:11:33 +04:00
|
|
|
memmove(ldata + term->curs.x + n, ldata + term->curs.x, m * TSIZE);
|
1999-01-08 16:02:13 +03:00
|
|
|
while (n--)
|
2002-10-22 20:11:33 +04:00
|
|
|
ldata[term->curs.x + n] = term->erase_char;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Toggle terminal mode `mode' to state `state'. (`query' indicates
|
|
|
|
* whether the mode is a DEC private one or a normal one.)
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void toggle_mode(Terminal *term, int mode, int query, int state)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-10-23 23:51:23 +04:00
|
|
|
unsigned long ticks;
|
2001-05-06 18:20:41 +04:00
|
|
|
|
2001-05-06 18:35:20 +04:00
|
|
|
if (query)
|
|
|
|
switch (mode) {
|
|
|
|
case 1: /* application cursor keys */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->app_cursor_keys = state;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 2: /* VT52 mode */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->vt52_mode = !state;
|
|
|
|
if (term->vt52_mode) {
|
|
|
|
term->blink_is_real = FALSE;
|
|
|
|
term->vt52_bold = FALSE;
|
2001-05-10 12:34:20 +04:00
|
|
|
} else {
|
2003-01-12 17:30:02 +03:00
|
|
|
term->blink_is_real = term->cfg.blinktext;
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 3: /* 80/132 columns */
|
2002-10-22 20:11:33 +04:00
|
|
|
deselect(term);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_resize)
|
2002-10-26 16:58:13 +04:00
|
|
|
request_resize(term->frontend, state ? 132 : 80, term->rows);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->reset_132 = state;
|
2003-03-27 03:22:59 +03:00
|
|
|
term->alt_t = term->marg_t = 0;
|
|
|
|
term->alt_b = term->marg_b = term->rows - 1;
|
|
|
|
move(term, 0, 0, 0);
|
|
|
|
erase_lots(term, FALSE, TRUE, TRUE);
|
2001-05-06 18:35:20 +04:00
|
|
|
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.
|
|
|
|
*/
|
2002-10-07 20:45:23 +04:00
|
|
|
ticks = GETTICKCOUNT();
|
2001-10-23 23:51:23 +04:00
|
|
|
/* turn off a previous vbell to avoid inconsistencies */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
|
|
|
|
term->in_vbell = FALSE;
|
|
|
|
if (term->rvideo && !state && /* we're turning it off... */
|
|
|
|
(ticks - term->rvbell_startpoint) < VBELL_TIMEOUT) {/*...soon*/
|
2001-10-23 23:51:23 +04:00
|
|
|
/* If there's no vbell timeout already, or this one lasts
|
|
|
|
* longer, replace vbell_timeout with ours. */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (!term->in_vbell ||
|
|
|
|
(term->rvbell_startpoint - term->vbell_startpoint <
|
|
|
|
VBELL_TIMEOUT))
|
|
|
|
term->vbell_startpoint = term->rvbell_startpoint;
|
|
|
|
term->in_vbell = TRUE; /* may clear rvideo but set in_vbell */
|
|
|
|
} else if (!term->rvideo && state) {
|
2001-05-06 18:35:20 +04:00
|
|
|
/* This is an ON, so we notice the time and save it. */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->rvbell_startpoint = ticks;
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->rvideo = state;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
if (state)
|
2002-10-22 20:11:33 +04:00
|
|
|
term_update(term);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 6: /* DEC origin mode */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->dec_om = state;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 7: /* auto wrap */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrap = state;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 8: /* auto key repeat */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->repeat_off = !state;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 10: /* set local edit mode */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->term_editing = state;
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) /* cause ldisc to notice changes */
|
|
|
|
ldisc_send(term->ldisc, NULL, 0, 0);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 25: /* enable/disable cursor */
|
|
|
|
compatibility2(OTHER, VT220);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cursor_on = state;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 47: /* alternate screen */
|
|
|
|
compatibility(OTHER);
|
2002-10-22 20:11:33 +04:00
|
|
|
deselect(term);
|
2003-01-12 17:30:02 +03:00
|
|
|
swap_screen(term, term->cfg.no_alt_screen ? 0 : state, FALSE, FALSE);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 1000: /* xterm mouse 1 */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->xterm_mouse = state ? 1 : 0;
|
2002-10-26 16:58:13 +04:00
|
|
|
set_raw_mouse_mode(term->frontend, state);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 1002: /* xterm mouse 2 */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->xterm_mouse = state ? 2 : 0;
|
2002-10-26 16:58:13 +04:00
|
|
|
set_raw_mouse_mode(term->frontend, state);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2002-10-22 14:31:23 +04:00
|
|
|
case 1047: /* alternate screen */
|
|
|
|
compatibility(OTHER);
|
2002-10-22 20:11:33 +04:00
|
|
|
deselect(term);
|
2003-01-12 17:30:02 +03:00
|
|
|
swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, TRUE);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0;
|
2002-10-22 14:31:23 +04:00
|
|
|
break;
|
|
|
|
case 1048: /* save/restore cursor */
|
2003-05-27 13:43:14 +04:00
|
|
|
if (!term->cfg.no_alt_screen)
|
|
|
|
save_cursor(term, state);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (!state) term->seen_disp_event = TRUE;
|
2002-10-22 14:31:23 +04:00
|
|
|
break;
|
|
|
|
case 1049: /* cursor & alternate screen */
|
2003-05-27 13:43:14 +04:00
|
|
|
if (state && !term->cfg.no_alt_screen)
|
2002-10-22 20:11:33 +04:00
|
|
|
save_cursor(term, state);
|
|
|
|
if (!state) term->seen_disp_event = TRUE;
|
2002-10-22 14:31:23 +04:00
|
|
|
compatibility(OTHER);
|
2002-10-22 20:11:33 +04:00
|
|
|
deselect(term);
|
2003-01-12 17:30:02 +03:00
|
|
|
swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, FALSE);
|
2003-05-27 13:43:14 +04:00
|
|
|
if (!state && !term->cfg.no_alt_screen)
|
2002-10-22 20:11:33 +04:00
|
|
|
save_cursor(term, state);
|
|
|
|
term->disptop = 0;
|
2002-10-22 14:31:23 +04:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
} else
|
|
|
|
switch (mode) {
|
|
|
|
case 4: /* set insert mode */
|
|
|
|
compatibility(VT102);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->insert = state;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 12: /* set echo mode */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->term_echoing = !state;
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) /* cause ldisc to notice changes */
|
|
|
|
ldisc_send(term->ldisc, NULL, 0, 0);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 20: /* Return sends ... */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cr_lf_return = state;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
case 34: /* Make cursor BIG */
|
|
|
|
compatibility2(OTHER, VT220);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->big_cursor = !state;
|
2001-04-28 18:39:16 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process an OSC sequence: set window title or icon name.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void do_osc(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->osc_w) {
|
|
|
|
while (term->osc_strlen--)
|
|
|
|
term->wordness[(unsigned char)
|
|
|
|
term->osc_string[term->osc_strlen]] = term->esc_args[0];
|
1999-01-08 16:02:13 +03:00
|
|
|
} else {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->osc_string[term->osc_strlen] = '\0';
|
|
|
|
switch (term->esc_args[0]) {
|
1999-01-08 16:02:13 +03:00
|
|
|
case 0:
|
|
|
|
case 1:
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_wintitle)
|
2002-10-26 16:58:13 +04:00
|
|
|
set_icon(term->frontend, term->osc_string);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_args[0] == 1)
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
|
|
|
/* fall through: parameter 0 means set both */
|
|
|
|
case 2:
|
|
|
|
case 21:
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_wintitle)
|
2002-10-26 16:58:13 +04:00
|
|
|
set_title(term->frontend, term->osc_string);
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-03-09 20:59:15 +03:00
|
|
|
/*
|
|
|
|
* ANSI printing routines.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void term_print_setup(Terminal *term)
|
2002-03-09 20:59:15 +03:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
bufchain_clear(&term->printer_buf);
|
2003-01-12 17:30:02 +03:00
|
|
|
term->print_job = printer_start_job(term->cfg.printer);
|
2002-03-09 20:59:15 +03:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
static void term_print_flush(Terminal *term)
|
2002-03-09 20:59:15 +03:00
|
|
|
{
|
|
|
|
void *data;
|
|
|
|
int len;
|
|
|
|
int size;
|
2002-10-22 20:11:33 +04:00
|
|
|
while ((size = bufchain_size(&term->printer_buf)) > 5) {
|
|
|
|
bufchain_prefix(&term->printer_buf, &data, &len);
|
2002-03-09 20:59:15 +03:00
|
|
|
if (len > size-5)
|
|
|
|
len = size-5;
|
2002-10-22 20:11:33 +04:00
|
|
|
printer_job_data(term->print_job, data, len);
|
|
|
|
bufchain_consume(&term->printer_buf, len);
|
2002-03-09 20:59:15 +03:00
|
|
|
}
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
static void term_print_finish(Terminal *term)
|
2002-03-09 20:59:15 +03:00
|
|
|
{
|
|
|
|
void *data;
|
|
|
|
int len, size;
|
|
|
|
char c;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (!term->printing && !term->only_printing)
|
2002-10-09 22:09:42 +04:00
|
|
|
return; /* we need do nothing */
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
term_print_flush(term);
|
|
|
|
while ((size = bufchain_size(&term->printer_buf)) > 0) {
|
|
|
|
bufchain_prefix(&term->printer_buf, &data, &len);
|
2002-03-09 20:59:15 +03:00
|
|
|
c = *(char *)data;
|
|
|
|
if (c == '\033' || c == '\233') {
|
2002-10-22 20:11:33 +04:00
|
|
|
bufchain_consume(&term->printer_buf, size);
|
2002-03-09 20:59:15 +03:00
|
|
|
break;
|
|
|
|
} else {
|
2002-10-22 20:11:33 +04:00
|
|
|
printer_job_data(term->print_job, &c, 1);
|
|
|
|
bufchain_consume(&term->printer_buf, 1);
|
2002-03-09 20:59:15 +03:00
|
|
|
}
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
printer_finish_job(term->print_job);
|
|
|
|
term->print_job = NULL;
|
|
|
|
term->printing = term->only_printing = FALSE;
|
2002-03-09 20:59:15 +03:00
|
|
|
}
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Remove everything currently in `inbuf' and stick it up on the
|
|
|
|
* in-memory display. There's a big state machine in here to
|
|
|
|
* process escape sequences...
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_out(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-09-18 23:41:07 +04:00
|
|
|
int c, unget;
|
|
|
|
unsigned char localbuf[256], *chars;
|
|
|
|
int nchars = 0;
|
|
|
|
|
|
|
|
unget = -1;
|
|
|
|
|
2002-10-09 22:09:42 +04:00
|
|
|
chars = NULL; /* placate compiler warnings */
|
2002-10-22 20:11:33 +04:00
|
|
|
while (nchars > 0 || bufchain_size(&term->inbuf) > 0) {
|
2001-09-18 23:41:07 +04:00
|
|
|
if (unget == -1) {
|
|
|
|
if (nchars == 0) {
|
|
|
|
void *ret;
|
2002-10-22 20:11:33 +04:00
|
|
|
bufchain_prefix(&term->inbuf, &ret, &nchars);
|
2001-09-18 23:41:07 +04:00
|
|
|
if (nchars > sizeof(localbuf))
|
|
|
|
nchars = sizeof(localbuf);
|
|
|
|
memcpy(localbuf, ret, nchars);
|
2002-10-22 20:11:33 +04:00
|
|
|
bufchain_consume(&term->inbuf, nchars);
|
2001-09-18 23:41:07 +04:00
|
|
|
chars = localbuf;
|
|
|
|
assert(chars != NULL);
|
|
|
|
}
|
|
|
|
c = *chars++;
|
|
|
|
nchars--;
|
2000-07-26 16:13:51 +04:00
|
|
|
|
2001-09-18 23:41:07 +04:00
|
|
|
/*
|
|
|
|
* Optionally log the session traffic to a file. Useful for
|
|
|
|
* debugging and possibly also useful for actual logging.
|
|
|
|
*/
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->cfg.logtype == LGTYP_DEBUG && term->logctx)
|
2002-10-26 16:58:13 +04:00
|
|
|
logtraffic(term->logctx, (unsigned char) c, LGTYP_DEBUG);
|
2001-09-18 23:41:07 +04:00
|
|
|
} else {
|
|
|
|
c = unget;
|
|
|
|
unget = -1;
|
2001-09-08 02:51:52 +04:00
|
|
|
}
|
|
|
|
|
2000-03-17 13:39:05 +03:00
|
|
|
/* 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.
|
|
|
|
*/
|
2001-05-10 12:34:20 +04:00
|
|
|
|
2002-03-09 20:59:15 +03:00
|
|
|
/*
|
|
|
|
* If we're printing, add the character to the printer
|
|
|
|
* buffer.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->printing) {
|
|
|
|
bufchain_add(&term->printer_buf, &c, 1);
|
2002-03-09 20:59:15 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we're in print-only mode, we use a much simpler
|
|
|
|
* state machine designed only to recognise the ESC[4i
|
|
|
|
* termination sequence.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->only_printing) {
|
2002-03-09 20:59:15 +03:00
|
|
|
if (c == '\033')
|
2002-10-22 20:11:33 +04:00
|
|
|
term->print_state = 1;
|
2002-03-09 20:59:15 +03:00
|
|
|
else if (c == (unsigned char)'\233')
|
2002-10-22 20:11:33 +04:00
|
|
|
term->print_state = 2;
|
|
|
|
else if (c == '[' && term->print_state == 1)
|
|
|
|
term->print_state = 2;
|
|
|
|
else if (c == '4' && term->print_state == 2)
|
|
|
|
term->print_state = 3;
|
|
|
|
else if (c == 'i' && term->print_state == 3)
|
|
|
|
term->print_state = 4;
|
2002-03-09 20:59:15 +03:00
|
|
|
else
|
2002-10-22 20:11:33 +04:00
|
|
|
term->print_state = 0;
|
|
|
|
if (term->print_state == 4) {
|
|
|
|
term_print_finish(term);
|
2002-03-09 20:59:15 +03:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-05-10 12:34:20 +04:00
|
|
|
/* First see about all those translations. */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->termstate == TOPLEVEL) {
|
|
|
|
if (in_utf(term))
|
|
|
|
switch (term->utf_state) {
|
2001-05-10 12:34:20 +04:00
|
|
|
case 0:
|
|
|
|
if (c < 0x80) {
|
2001-05-19 19:21:05 +04:00
|
|
|
/* UTF-8 must be stateless so we ignore iso2022. */
|
2003-01-14 21:28:23 +03:00
|
|
|
if (term->ucsdata->unitab_ctrl[c] != 0xFF)
|
|
|
|
c = term->ucsdata->unitab_ctrl[c];
|
2001-05-19 19:21:05 +04:00
|
|
|
else c = ((unsigned char)c) | ATTR_ASCII;
|
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
} else if ((c & 0xe0) == 0xc0) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->utf_size = term->utf_state = 1;
|
|
|
|
term->utf_char = (c & 0x1f);
|
2001-05-10 12:34:20 +04:00
|
|
|
} else if ((c & 0xf0) == 0xe0) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->utf_size = term->utf_state = 2;
|
|
|
|
term->utf_char = (c & 0x0f);
|
2001-05-10 12:34:20 +04:00
|
|
|
} else if ((c & 0xf8) == 0xf0) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->utf_size = term->utf_state = 3;
|
|
|
|
term->utf_char = (c & 0x07);
|
2001-05-10 12:34:20 +04:00
|
|
|
} else if ((c & 0xfc) == 0xf8) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->utf_size = term->utf_state = 4;
|
|
|
|
term->utf_char = (c & 0x03);
|
2001-05-10 12:34:20 +04:00
|
|
|
} else if ((c & 0xfe) == 0xfc) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->utf_size = term->utf_state = 5;
|
|
|
|
term->utf_char = (c & 0x01);
|
2001-05-10 12:34:20 +04:00
|
|
|
} else {
|
|
|
|
c = UCSERR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
case 5:
|
|
|
|
if ((c & 0xC0) != 0x80) {
|
2001-09-18 23:41:07 +04:00
|
|
|
unget = c;
|
2001-09-08 02:51:52 +04:00
|
|
|
c = UCSERR;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->utf_state = 0;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->utf_char = (term->utf_char << 6) | (c & 0x3f);
|
|
|
|
if (--term->utf_state)
|
2001-05-10 12:34:20 +04:00
|
|
|
continue;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
c = term->utf_char;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
/* Is somebody trying to be evil! */
|
|
|
|
if (c < 0x80 ||
|
2002-10-22 20:11:33 +04:00
|
|
|
(c < 0x800 && term->utf_size >= 2) ||
|
|
|
|
(c < 0x10000 && term->utf_size >= 3) ||
|
|
|
|
(c < 0x200000 && term->utf_size >= 4) ||
|
|
|
|
(c < 0x4000000 && term->utf_size >= 5))
|
2001-05-10 12:34:20 +04:00
|
|
|
c = UCSERR;
|
|
|
|
|
|
|
|
/* Unicode line separator and paragraph separator are CR-LF */
|
|
|
|
if (c == 0x2028 || c == 0x2029)
|
|
|
|
c = 0x85;
|
|
|
|
|
|
|
|
/* High controls are probably a Baaad idea too. */
|
|
|
|
if (c < 0xA0)
|
|
|
|
c = 0xFFFD;
|
|
|
|
|
|
|
|
/* The UTF-16 surrogates are not nice either. */
|
|
|
|
/* The standard give the option of decoding these:
|
|
|
|
* I don't want to! */
|
|
|
|
if (c >= 0xD800 && c < 0xE000)
|
|
|
|
c = UCSERR;
|
|
|
|
|
|
|
|
/* ISO 10646 characters now limited to UTF-16 range. */
|
|
|
|
if (c > 0x10FFFF)
|
|
|
|
c = UCSERR;
|
|
|
|
|
|
|
|
/* This is currently a TagPhobic application.. */
|
|
|
|
if (c >= 0xE0000 && c <= 0xE007F)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* U+FEFF is best seen as a null. */
|
|
|
|
if (c == 0xFEFF)
|
|
|
|
continue;
|
|
|
|
/* But U+FFFE is an error. */
|
|
|
|
if (c == 0xFFFE || c == 0xFFFF)
|
|
|
|
c = UCSERR;
|
|
|
|
|
|
|
|
/* Oops this is a 16bit implementation */
|
|
|
|
if (c >= 0x10000)
|
|
|
|
c = 0xFFFD;
|
|
|
|
break;
|
2001-05-19 18:12:41 +04:00
|
|
|
}
|
|
|
|
/* Are we in the nasty ACS mode? Note: no sco in utf mode. */
|
2002-10-22 20:11:33 +04:00
|
|
|
else if(term->sco_acs &&
|
2002-11-19 02:49:30 +03:00
|
|
|
(c!='\033' && c!='\012' && c!='\015' && c!='\b'))
|
2001-05-19 18:12:41 +04:00
|
|
|
{
|
2002-12-31 14:03:23 +03:00
|
|
|
if (term->sco_acs == 2) c |= 0x80;
|
2001-05-19 18:12:41 +04:00
|
|
|
c |= ATTR_SCOACS;
|
2001-05-10 12:34:20 +04:00
|
|
|
} else {
|
2002-10-22 20:11:33 +04:00
|
|
|
switch (term->cset_attr[term->cset]) {
|
2001-05-10 12:34:20 +04:00
|
|
|
/*
|
|
|
|
* Linedraw characters are different from 'ESC ( B'
|
|
|
|
* only for a small range. For ones outside that
|
|
|
|
* range, make sure we use the same font as well as
|
|
|
|
* the same encoding.
|
|
|
|
*/
|
|
|
|
case ATTR_LINEDRW:
|
2003-01-14 21:28:23 +03:00
|
|
|
if (term->ucsdata->unitab_ctrl[c] != 0xFF)
|
|
|
|
c = term->ucsdata->unitab_ctrl[c];
|
2001-05-10 12:34:20 +04:00
|
|
|
else
|
|
|
|
c = ((unsigned char) c) | ATTR_LINEDRW;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ATTR_GBCHR:
|
|
|
|
/* If UK-ASCII, make the '#' a LineDraw Pound */
|
|
|
|
if (c == '#') {
|
|
|
|
c = '}' | ATTR_LINEDRW;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*FALLTHROUGH*/ case ATTR_ASCII:
|
2003-01-14 21:28:23 +03:00
|
|
|
if (term->ucsdata->unitab_ctrl[c] != 0xFF)
|
|
|
|
c = term->ucsdata->unitab_ctrl[c];
|
2001-05-10 12:34:20 +04:00
|
|
|
else
|
|
|
|
c = ((unsigned char) c) | ATTR_ASCII;
|
|
|
|
break;
|
2001-05-19 18:12:41 +04:00
|
|
|
case ATTR_SCOACS:
|
|
|
|
if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
|
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* How about C1 controls ? */
|
2002-10-22 20:11:33 +04:00
|
|
|
if ((c & -32) == 0x80 && term->termstate < DO_CTRLS &&
|
|
|
|
!term->vt52_mode && has_compat(VT220)) {
|
|
|
|
term->termstate = SEEN_ESC;
|
|
|
|
term->esc_query = FALSE;
|
2001-05-10 12:34:20 +04:00
|
|
|
c = '@' + (c & 0x1F);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Or the GL control. */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (c == '\177' && term->termstate < DO_CTRLS && has_compat(OTHER)) {
|
|
|
|
if (term->curs.x && !term->wrapnext)
|
|
|
|
term->curs.x--;
|
|
|
|
term->wrapnext = FALSE;
|
2001-05-10 12:34:20 +04:00
|
|
|
fix_cpos;
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_dbackspace) /* destructive bksp might be disabled */
|
2002-10-22 20:11:33 +04:00
|
|
|
*term->cpos = (' ' | term->curr_attr | ATTR_ASCII);
|
2001-05-10 12:34:20 +04:00
|
|
|
} else
|
|
|
|
/* Or normal C0 controls. */
|
2002-10-22 20:11:33 +04:00
|
|
|
if ((c & -32) == 0 && term->termstate < DO_CTRLS) {
|
1999-01-08 16:02:13 +03:00
|
|
|
switch (c) {
|
|
|
|
case '\005': /* terminal type query */
|
2000-03-17 13:39:05 +03:00
|
|
|
/* Strictly speaking this is VT100 but a VT100 defaults to
|
|
|
|
* no response. Other terminals respond at their option.
|
|
|
|
*
|
|
|
|
* Don't put a CR in the default string as this tends to
|
|
|
|
* upset some weird software.
|
|
|
|
*
|
|
|
|
* An xterm returns "xterm" (5 characters)
|
|
|
|
*/
|
2001-05-06 18:35:20 +04:00
|
|
|
compatibility(ANSIMIN);
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) {
|
2001-04-09 16:43:46 +04:00
|
|
|
char abuf[256], *s, *d;
|
2001-05-06 18:35:20 +04:00
|
|
|
int state = 0;
|
2003-01-12 17:30:02 +03:00
|
|
|
for (s = term->cfg.answerback, d = abuf; *s; s++) {
|
2001-05-06 18:35:20 +04:00
|
|
|
if (state) {
|
|
|
|
if (*s >= 'a' && *s <= 'z')
|
|
|
|
*d++ = (*s - ('a' - 1));
|
|
|
|
else if ((*s >= '@' && *s <= '_') ||
|
|
|
|
*s == '?' || (*s & 0x80))
|
|
|
|
*d++ = ('@' ^ *s);
|
|
|
|
else if (*s == '~')
|
|
|
|
*d++ = '^';
|
|
|
|
state = 0;
|
|
|
|
} else if (*s == '^') {
|
|
|
|
state = 1;
|
|
|
|
} else
|
2001-05-10 12:34:20 +04:00
|
|
|
*d++ = *s;
|
2001-04-09 16:43:46 +04:00
|
|
|
}
|
2002-10-26 14:16:19 +04:00
|
|
|
lpage_send(term->ldisc, DEFAULT_CODEPAGE,
|
|
|
|
abuf, d - abuf, 0);
|
2001-04-09 16:43:46 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
|
|
|
case '\007':
|
2001-04-13 14:52:36 +04:00
|
|
|
{
|
|
|
|
struct beeptime *newbeep;
|
2001-10-23 23:51:23 +04:00
|
|
|
unsigned long ticks;
|
2001-04-13 14:52:36 +04:00
|
|
|
|
2002-10-07 20:45:23 +04:00
|
|
|
ticks = GETTICKCOUNT();
|
2001-04-13 14:52:36 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (!term->beep_overloaded) {
|
2003-03-29 19:14:26 +03:00
|
|
|
newbeep = snew(struct beeptime);
|
2001-04-13 14:52:36 +04:00
|
|
|
newbeep->ticks = ticks;
|
|
|
|
newbeep->next = NULL;
|
2002-10-22 20:11:33 +04:00
|
|
|
if (!term->beephead)
|
|
|
|
term->beephead = newbeep;
|
2001-04-13 14:52:36 +04:00
|
|
|
else
|
2002-10-22 20:11:33 +04:00
|
|
|
term->beeptail->next = newbeep;
|
|
|
|
term->beeptail = newbeep;
|
|
|
|
term->nbeeps++;
|
2001-04-13 14:52:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Throw out any beeps that happened more than
|
|
|
|
* t seconds ago.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
while (term->beephead &&
|
2003-01-12 17:30:02 +03:00
|
|
|
term->beephead->ticks < ticks - term->cfg.bellovl_t) {
|
2002-10-22 20:11:33 +04:00
|
|
|
struct beeptime *tmp = term->beephead;
|
|
|
|
term->beephead = tmp->next;
|
2001-04-13 14:52:36 +04:00
|
|
|
sfree(tmp);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (!term->beephead)
|
|
|
|
term->beeptail = NULL;
|
|
|
|
term->nbeeps--;
|
2001-04-13 14:52:36 +04:00
|
|
|
}
|
|
|
|
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->cfg.bellovl && term->beep_overloaded &&
|
|
|
|
ticks - term->lastbeep >= (unsigned)term->cfg.bellovl_s) {
|
2001-04-13 14:52:36 +04:00
|
|
|
/*
|
|
|
|
* If we're currently overloaded and the
|
|
|
|
* last beep was more than s seconds ago,
|
|
|
|
* leave overload mode.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
term->beep_overloaded = FALSE;
|
2003-01-12 17:30:02 +03:00
|
|
|
} else if (term->cfg.bellovl && !term->beep_overloaded &&
|
|
|
|
term->nbeeps >= term->cfg.bellovl_n) {
|
2001-04-13 14:52:36 +04:00
|
|
|
/*
|
|
|
|
* Now, if we have n or more beeps
|
|
|
|
* remaining in the queue, go into overload
|
|
|
|
* mode.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
term->beep_overloaded = TRUE;
|
2001-04-13 14:52:36 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->lastbeep = ticks;
|
2001-04-13 14:52:36 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform an actual beep if we're not overloaded.
|
|
|
|
*/
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.bellovl || !term->beep_overloaded) {
|
|
|
|
beep(term->frontend, term->cfg.beep);
|
|
|
|
if (term->cfg.beep == BELL_VISUAL) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->in_vbell = TRUE;
|
|
|
|
term->vbell_startpoint = ticks;
|
|
|
|
term_update(term);
|
2002-10-29 00:58:07 +03:00
|
|
|
}
|
2001-04-13 14:52:36 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0;
|
2001-04-13 14:52:36 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
|
|
|
case '\b':
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.x == 0 &&
|
|
|
|
(term->curs.y == 0 || term->wrap == 0))
|
|
|
|
/* do nothing */ ;
|
|
|
|
else if (term->curs.x == 0 && term->curs.y > 0)
|
|
|
|
term->curs.x = term->cols - 1, term->curs.y--;
|
|
|
|
else if (term->wrapnext)
|
|
|
|
term->wrapnext = FALSE;
|
1999-01-08 16:02:13 +03:00
|
|
|
else
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.x--;
|
1999-01-08 16:02:13 +03:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->seen_disp_event = TRUE;
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
|
|
|
case '\016':
|
2001-05-06 18:35:20 +04:00
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset = 1;
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
|
|
|
case '\017':
|
2001-05-06 18:35:20 +04:00
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset = 0;
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
|
|
|
case '\033':
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->vt52_mode)
|
|
|
|
term->termstate = VT52_ESC;
|
2000-03-17 13:39:05 +03:00
|
|
|
else {
|
|
|
|
compatibility(ANSIMIN);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = SEEN_ESC;
|
|
|
|
term->esc_query = FALSE;
|
2000-03-17 13:39:05 +03:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
2002-11-19 02:49:30 +03:00
|
|
|
case '\015':
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.x = 0;
|
|
|
|
term->wrapnext = FALSE;
|
1999-01-08 16:02:13 +03:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->seen_disp_event = TRUE;
|
|
|
|
term->paste_hold = 0;
|
2002-10-26 16:58:13 +04:00
|
|
|
if (term->logctx)
|
|
|
|
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
|
|
|
case '\014':
|
2001-05-09 17:35:08 +04:00
|
|
|
if (has_compat(SCOANSI)) {
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, 0, 0, 0);
|
|
|
|
erase_lots(term, FALSE, FALSE, TRUE);
|
|
|
|
term->disptop = 0;
|
|
|
|
term->wrapnext = FALSE;
|
|
|
|
term->seen_disp_event = 1;
|
2001-05-09 17:35:08 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '\013':
|
2001-05-06 18:35:20 +04:00
|
|
|
compatibility(VT100);
|
2002-11-19 02:49:30 +03:00
|
|
|
case '\012':
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.y == term->marg_b)
|
|
|
|
scroll(term, term->marg_t, term->marg_b, 1, TRUE);
|
|
|
|
else if (term->curs.y < term->rows - 1)
|
|
|
|
term->curs.y++;
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->cfg.lfhascr)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.x = 0;
|
1999-01-08 16:02:13 +03:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrapnext = FALSE;
|
|
|
|
term->seen_disp_event = 1;
|
|
|
|
term->paste_hold = 0;
|
2002-10-26 16:58:13 +04:00
|
|
|
if (term->logctx)
|
|
|
|
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
pos old_curs = term->curs;
|
|
|
|
unsigned long *ldata = lineptr(term->curs.y);
|
2000-07-26 16:13:51 +04:00
|
|
|
|
|
|
|
do {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.x++;
|
|
|
|
} while (term->curs.x < term->cols - 1 &&
|
|
|
|
!term->tabs[term->curs.x]);
|
2000-07-26 16:13:51 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if ((ldata[term->cols] & LATTR_MODE) != LATTR_NORM) {
|
|
|
|
if (term->curs.x >= term->cols / 2)
|
|
|
|
term->curs.x = term->cols / 2 - 1;
|
2001-05-06 18:35:20 +04:00
|
|
|
} else {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.x >= term->cols)
|
|
|
|
term->curs.x = term->cols - 1;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
check_selection(term, old_curs, term->curs);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->seen_disp_event = TRUE;
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
1999-11-30 13:52:07 +03:00
|
|
|
}
|
2001-05-06 18:35:20 +04:00
|
|
|
} else
|
2002-10-22 20:11:33 +04:00
|
|
|
switch (term->termstate) {
|
2001-05-06 18:35:20 +04:00
|
|
|
case TOPLEVEL:
|
2002-10-22 20:11:33 +04:00
|
|
|
/* Only graphic characters get this far;
|
|
|
|
* ctrls are stripped above */
|
|
|
|
if (term->wrapnext && term->wrap) {
|
|
|
|
term->cpos[1] |= LATTR_WRAPPED;
|
|
|
|
if (term->curs.y == term->marg_b)
|
|
|
|
scroll(term, term->marg_t, term->marg_b, 1, TRUE);
|
|
|
|
else if (term->curs.y < term->rows - 1)
|
|
|
|
term->curs.y++;
|
|
|
|
term->curs.x = 0;
|
2001-05-06 18:35:20 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrapnext = FALSE;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->insert)
|
|
|
|
insch(term, 1);
|
|
|
|
if (term->selstate != NO_SELECTION) {
|
|
|
|
pos cursplus = term->curs;
|
2001-05-06 18:35:20 +04:00
|
|
|
incpos(cursplus);
|
2002-10-22 20:11:33 +04:00
|
|
|
check_selection(term, term->curs, cursplus);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2002-10-26 16:58:13 +04:00
|
|
|
if (((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0) &&
|
|
|
|
term->logctx)
|
|
|
|
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
|
2001-05-10 12:34:20 +04:00
|
|
|
{
|
|
|
|
int width = 0;
|
|
|
|
if (DIRECT_CHAR(c))
|
|
|
|
width = 1;
|
|
|
|
if (!width)
|
|
|
|
width = wcwidth((wchar_t) c);
|
|
|
|
switch (width) {
|
|
|
|
case 2:
|
2003-01-02 19:20:29 +03:00
|
|
|
/*
|
|
|
|
* If we're about to display a double-width
|
|
|
|
* character starting in the rightmost
|
|
|
|
* column, then we do something special
|
|
|
|
* instead. We must print a space in the
|
|
|
|
* last column of the screen, then wrap;
|
|
|
|
* and we also set LATTR_WRAPPED2 which
|
|
|
|
* instructs subsequent cut-and-pasting not
|
|
|
|
* only to splice this line to the one
|
|
|
|
* after it, but to ignore the space in the
|
|
|
|
* last character position as well.
|
|
|
|
* (Because what was actually output to the
|
|
|
|
* terminal was presumably just a sequence
|
|
|
|
* of CJK characters, and we don't want a
|
|
|
|
* space to be pasted in the middle of
|
|
|
|
* those just because they had the
|
|
|
|
* misfortune to start in the wrong parity
|
|
|
|
* column. xterm concurs.)
|
|
|
|
*/
|
|
|
|
check_boundary(term, term->curs.x, term->curs.y);
|
|
|
|
check_boundary(term, term->curs.x+2, term->curs.y);
|
|
|
|
if (term->curs.x == term->cols-1) {
|
|
|
|
*term->cpos++ = ATTR_ASCII | ' ' | term->curr_attr;
|
|
|
|
*term->cpos |= LATTR_WRAPPED | LATTR_WRAPPED2;
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.y == term->marg_b)
|
|
|
|
scroll(term, term->marg_t, term->marg_b,
|
|
|
|
1, TRUE);
|
|
|
|
else if (term->curs.y < term->rows - 1)
|
|
|
|
term->curs.y++;
|
|
|
|
term->curs.x = 0;
|
2001-09-15 19:54:24 +04:00
|
|
|
fix_cpos;
|
2003-01-02 19:20:29 +03:00
|
|
|
/* Now we must check_boundary again, of course. */
|
|
|
|
check_boundary(term, term->curs.x, term->curs.y);
|
|
|
|
check_boundary(term, term->curs.x+2, term->curs.y);
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
2003-01-02 19:20:29 +03:00
|
|
|
*term->cpos++ = c | term->curr_attr;
|
2002-10-22 20:11:33 +04:00
|
|
|
*term->cpos++ = UCSWIDE | term->curr_attr;
|
2003-01-02 19:20:29 +03:00
|
|
|
term->curs.x++;
|
2001-09-15 19:54:24 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
case 1:
|
2003-01-02 19:20:29 +03:00
|
|
|
check_boundary(term, term->curs.x, term->curs.y);
|
|
|
|
check_boundary(term, term->curs.x+1, term->curs.y);
|
2002-10-22 20:11:33 +04:00
|
|
|
*term->cpos++ = c | term->curr_attr;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
default:
|
|
|
|
continue;
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.x++;
|
|
|
|
if (term->curs.x == term->cols) {
|
|
|
|
term->cpos--;
|
|
|
|
term->curs.x--;
|
|
|
|
term->wrapnext = TRUE;
|
|
|
|
if (term->wrap && term->vt52_mode) {
|
|
|
|
term->cpos[1] |= LATTR_WRAPPED;
|
|
|
|
if (term->curs.y == term->marg_b)
|
|
|
|
scroll(term, term->marg_t, term->marg_b, 1, TRUE);
|
|
|
|
else if (term->curs.y < term->rows - 1)
|
|
|
|
term->curs.y++;
|
|
|
|
term->curs.x = 0;
|
2001-05-10 12:34:20 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrapnext = FALSE;
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
2000-03-17 13:39:05 +03:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->seen_disp_event = 1;
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
|
|
|
|
case OSC_MAYBE_ST:
|
|
|
|
/*
|
|
|
|
* This state is virtually identical to SEEN_ESC, with the
|
|
|
|
* exception that we have an OSC sequence in the pipeline,
|
|
|
|
* and _if_ we see a backslash, we process it.
|
|
|
|
*/
|
|
|
|
if (c == '\\') {
|
2002-10-22 20:11:33 +04:00
|
|
|
do_osc(term);
|
|
|
|
term->termstate = TOPLEVEL;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2000-03-17 13:39:05 +03:00
|
|
|
}
|
2001-05-06 18:35:20 +04:00
|
|
|
/* else fall through */
|
|
|
|
case SEEN_ESC:
|
2001-05-10 12:34:20 +04:00
|
|
|
if (c >= ' ' && c <= '/') {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_query)
|
|
|
|
term->esc_query = -1;
|
2001-05-10 12:34:20 +04:00
|
|
|
else
|
2002-10-22 20:11:33 +04:00
|
|
|
term->esc_query = c;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = TOPLEVEL;
|
|
|
|
switch (ANSI(c, term->esc_query)) {
|
2001-05-06 18:35:20 +04:00
|
|
|
case '[': /* enter CSI mode */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = SEEN_CSI;
|
|
|
|
term->esc_nargs = 1;
|
|
|
|
term->esc_args[0] = ARG_DEFAULT;
|
|
|
|
term->esc_query = FALSE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case ']': /* xterm escape sequences */
|
|
|
|
/* Compatibility is nasty here, xterm, linux, decterm yuk! */
|
|
|
|
compatibility(OTHER);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = SEEN_OSC;
|
|
|
|
term->esc_args[0] = 0;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case '7': /* save cursor */
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
save_cursor(term, TRUE);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case '8': /* restore cursor */
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
save_cursor(term, FALSE);
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case '=':
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->app_keypad_keys = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case '>':
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->app_keypad_keys = FALSE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'D': /* exactly equivalent to LF */
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.y == term->marg_b)
|
|
|
|
scroll(term, term->marg_t, term->marg_b, 1, TRUE);
|
|
|
|
else if (term->curs.y < term->rows - 1)
|
|
|
|
term->curs.y++;
|
2001-05-06 18:35:20 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrapnext = FALSE;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'E': /* exactly equivalent to CR-LF */
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.x = 0;
|
|
|
|
if (term->curs.y == term->marg_b)
|
|
|
|
scroll(term, term->marg_t, term->marg_b, 1, TRUE);
|
|
|
|
else if (term->curs.y < term->rows - 1)
|
|
|
|
term->curs.y++;
|
2001-05-06 18:35:20 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrapnext = FALSE;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'M': /* reverse index - backwards LF */
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.y == term->marg_t)
|
|
|
|
scroll(term, term->marg_t, term->marg_b, -1, TRUE);
|
|
|
|
else if (term->curs.y > 0)
|
|
|
|
term->curs.y--;
|
2001-05-06 18:35:20 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrapnext = FALSE;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'Z': /* terminal type query */
|
|
|
|
compatibility(VT100);
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc)
|
|
|
|
ldisc_send(term->ldisc, term->id_string,
|
|
|
|
strlen(term->id_string), 0);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'c': /* restore power-on settings */
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
power_on(term);
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) /* cause ldisc to notice changes */
|
|
|
|
ldisc_send(term->ldisc, NULL, 0, 0);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->reset_132) {
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_resize)
|
2002-10-26 16:58:13 +04:00
|
|
|
request_resize(term->frontend, 80, term->rows);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->reset_132 = 0;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2001-05-06 18:35:20 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'H': /* set a tab */
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->tabs[term->curs.x] = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
|
|
|
|
compatibility(VT100);
|
|
|
|
{
|
|
|
|
unsigned long *ldata;
|
|
|
|
int i, j;
|
|
|
|
pos scrtop, scrbot;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
for (i = 0; i < term->rows; i++) {
|
2001-05-10 12:34:20 +04:00
|
|
|
ldata = lineptr(i);
|
2002-10-22 20:11:33 +04:00
|
|
|
for (j = 0; j < term->cols; j++)
|
2001-05-10 12:34:20 +04:00
|
|
|
ldata[j] = ATTR_DEFAULT | 'E';
|
2002-10-22 20:11:33 +04:00
|
|
|
ldata[term->cols] = 0;
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-10 12:34:20 +04:00
|
|
|
scrtop.x = scrtop.y = 0;
|
|
|
|
scrbot.x = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
scrbot.y = term->rows;
|
|
|
|
check_selection(term, scrtop, scrbot);
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ANSI('3', '#'):
|
|
|
|
case ANSI('4', '#'):
|
|
|
|
case ANSI('5', '#'):
|
|
|
|
case ANSI('6', '#'):
|
|
|
|
compatibility(VT100);
|
|
|
|
{
|
|
|
|
unsigned long nlattr;
|
|
|
|
unsigned long *ldata;
|
2002-10-22 20:11:33 +04:00
|
|
|
switch (ANSI(c, term->esc_query)) {
|
2001-05-10 12:34:20 +04:00
|
|
|
case ANSI('3', '#'):
|
|
|
|
nlattr = LATTR_TOP;
|
|
|
|
break;
|
|
|
|
case ANSI('4', '#'):
|
|
|
|
nlattr = LATTR_BOT;
|
|
|
|
break;
|
|
|
|
case ANSI('5', '#'):
|
|
|
|
nlattr = LATTR_NORM;
|
|
|
|
break;
|
2001-05-13 18:02:28 +04:00
|
|
|
default: /* spiritually case ANSI('6', '#'): */
|
2001-05-10 12:34:20 +04:00
|
|
|
nlattr = LATTR_WIDE;
|
|
|
|
break;
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
ldata = lineptr(term->curs.y);
|
|
|
|
ldata[term->cols] &= ~LATTR_MODE;
|
|
|
|
ldata[term->cols] |= nlattr;
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ANSI('A', '('):
|
|
|
|
compatibility(VT100);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_charset)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[0] = ATTR_GBCHR;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case ANSI('B', '('):
|
|
|
|
compatibility(VT100);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_charset)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[0] = ATTR_ASCII;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case ANSI('0', '('):
|
|
|
|
compatibility(VT100);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_charset)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[0] = ATTR_LINEDRW;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
2001-05-19 18:12:41 +04:00
|
|
|
case ANSI('U', '('):
|
|
|
|
compatibility(OTHER);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_charset)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[0] = ATTR_SCOACS;
|
2001-05-19 18:12:41 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
case ANSI('A', ')'):
|
|
|
|
compatibility(VT100);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_charset)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[1] = ATTR_GBCHR;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case ANSI('B', ')'):
|
|
|
|
compatibility(VT100);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_charset)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[1] = ATTR_ASCII;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case ANSI('0', ')'):
|
|
|
|
compatibility(VT100);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_charset)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[1] = ATTR_LINEDRW;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
2001-05-19 18:12:41 +04:00
|
|
|
case ANSI('U', ')'):
|
|
|
|
compatibility(OTHER);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_charset)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[1] = ATTR_SCOACS;
|
2001-05-19 18:12:41 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
case ANSI('8', '%'): /* Old Linux code */
|
|
|
|
case ANSI('G', '%'):
|
|
|
|
compatibility(OTHER);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_charset)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->utf = 1;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case ANSI('@', '%'):
|
|
|
|
compatibility(OTHER);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_charset)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->utf = 0;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case SEEN_CSI:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = TOPLEVEL; /* default */
|
2001-05-06 18:35:20 +04:00
|
|
|
if (isdigit(c)) {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs <= ARGS_MAX) {
|
|
|
|
if (term->esc_args[term->esc_nargs - 1] == ARG_DEFAULT)
|
|
|
|
term->esc_args[term->esc_nargs - 1] = 0;
|
|
|
|
term->esc_args[term->esc_nargs - 1] =
|
|
|
|
10 * term->esc_args[term->esc_nargs - 1] + c - '0';
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = SEEN_CSI;
|
2001-05-06 18:35:20 +04:00
|
|
|
} else if (c == ';') {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (++term->esc_nargs <= ARGS_MAX)
|
|
|
|
term->esc_args[term->esc_nargs - 1] = ARG_DEFAULT;
|
|
|
|
term->termstate = SEEN_CSI;
|
2001-05-06 18:35:20 +04:00
|
|
|
} else if (c < '@') {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_query)
|
|
|
|
term->esc_query = -1;
|
2001-05-06 18:35:20 +04:00
|
|
|
else if (c == '?')
|
2002-10-22 20:11:33 +04:00
|
|
|
term->esc_query = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
else
|
2002-10-22 20:11:33 +04:00
|
|
|
term->esc_query = c;
|
|
|
|
term->termstate = SEEN_CSI;
|
2001-05-06 18:35:20 +04:00
|
|
|
} else
|
2002-10-22 20:11:33 +04:00
|
|
|
switch (ANSI(c, term->esc_query)) {
|
2001-05-06 18:35:20 +04:00
|
|
|
case 'A': /* move up N lines */
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, term->curs.x,
|
|
|
|
term->curs.y - def(term->esc_args[0], 1), 1);
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'e': /* move down N lines */
|
|
|
|
compatibility(ANSI);
|
2001-05-19 15:34:20 +04:00
|
|
|
/* FALLTHROUGH */
|
2001-05-06 18:35:20 +04:00
|
|
|
case 'B':
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, term->curs.x,
|
|
|
|
term->curs.y + def(term->esc_args[0], 1), 1);
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case ANSI('c', '>'): /* report xterm version */
|
|
|
|
compatibility(OTHER);
|
|
|
|
/* this reports xterm version 136 so that VIM can
|
|
|
|
use the drag messages from the mouse reporting */
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc)
|
|
|
|
ldisc_send(term->ldisc, "\033[>0;136;0c", 11, 0);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2001-05-19 15:34:20 +04:00
|
|
|
case 'a': /* move right N cols */
|
|
|
|
compatibility(ANSI);
|
|
|
|
/* FALLTHROUGH */
|
2001-05-06 18:35:20 +04:00
|
|
|
case 'C':
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, term->curs.x + def(term->esc_args[0], 1),
|
|
|
|
term->curs.y, 1);
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'D': /* move left N cols */
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, term->curs.x - def(term->esc_args[0], 1),
|
|
|
|
term->curs.y, 1);
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'E': /* move down N lines and CR */
|
|
|
|
compatibility(ANSI);
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, 0,
|
|
|
|
term->curs.y + def(term->esc_args[0], 1), 1);
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'F': /* move up N lines and CR */
|
|
|
|
compatibility(ANSI);
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, 0,
|
|
|
|
term->curs.y - def(term->esc_args[0], 1), 1);
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'G':
|
|
|
|
case '`': /* set horizontal posn */
|
|
|
|
compatibility(ANSI);
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, def(term->esc_args[0], 1) - 1,
|
|
|
|
term->curs.y, 0);
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'd': /* set vertical posn */
|
|
|
|
compatibility(ANSI);
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, term->curs.x,
|
|
|
|
((term->dec_om ? term->marg_t : 0) +
|
|
|
|
def(term->esc_args[0], 1) - 1),
|
|
|
|
(term->dec_om ? 2 : 0));
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'H':
|
|
|
|
case 'f': /* set horz and vert posns at once */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs < 2)
|
|
|
|
term->esc_args[1] = ARG_DEFAULT;
|
|
|
|
move(term, def(term->esc_args[1], 1) - 1,
|
|
|
|
((term->dec_om ? term->marg_t : 0) +
|
|
|
|
def(term->esc_args[0], 1) - 1),
|
|
|
|
(term->dec_om ? 2 : 0));
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'J': /* erase screen or parts of it */
|
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
unsigned int i = def(term->esc_args[0], 0) + 1;
|
2001-05-06 18:35:20 +04:00
|
|
|
if (i > 3)
|
|
|
|
i = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
erase_lots(term, FALSE, !!(i & 2), !!(i & 1));
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptop = 0;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'K': /* erase line or parts of it */
|
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
unsigned int i = def(term->esc_args[0], 0) + 1;
|
2001-05-06 18:35:20 +04:00
|
|
|
if (i > 3)
|
|
|
|
i = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
erase_lots(term, TRUE, !!(i & 2), !!(i & 1));
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'L': /* insert lines */
|
|
|
|
compatibility(VT102);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.y <= term->marg_b)
|
|
|
|
scroll(term, term->curs.y, term->marg_b,
|
|
|
|
-def(term->esc_args[0], 1), FALSE);
|
2001-05-06 18:35:20 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'M': /* delete lines */
|
|
|
|
compatibility(VT102);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.y <= term->marg_b)
|
|
|
|
scroll(term, term->curs.y, term->marg_b,
|
|
|
|
def(term->esc_args[0], 1),
|
2001-05-06 18:35:20 +04:00
|
|
|
TRUE);
|
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case '@': /* insert chars */
|
|
|
|
/* XXX VTTEST says this is vt220, vt510 manual says vt102 */
|
|
|
|
compatibility(VT102);
|
2002-10-22 20:11:33 +04:00
|
|
|
insch(term, def(term->esc_args[0], 1));
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'P': /* delete chars */
|
|
|
|
compatibility(VT102);
|
2002-10-22 20:11:33 +04:00
|
|
|
insch(term, -def(term->esc_args[0], 1));
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'c': /* terminal type query */
|
|
|
|
compatibility(VT100);
|
|
|
|
/* This is the response for a VT102 */
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc)
|
|
|
|
ldisc_send(term->ldisc, term->id_string,
|
|
|
|
strlen(term->id_string), 0);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'n': /* cursor position query */
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) {
|
|
|
|
if (term->esc_args[0] == 6) {
|
|
|
|
char buf[32];
|
|
|
|
sprintf(buf, "\033[%d;%dR", term->curs.y + 1,
|
|
|
|
term->curs.x + 1);
|
|
|
|
ldisc_send(term->ldisc, buf, strlen(buf), 0);
|
|
|
|
} else if (term->esc_args[0] == 5) {
|
|
|
|
ldisc_send(term->ldisc, "\033[0n", 4, 0);
|
|
|
|
}
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'h': /* toggle modes to high */
|
|
|
|
case ANSI_QUE('h'):
|
|
|
|
compatibility(VT100);
|
|
|
|
{
|
|
|
|
int i;
|
2002-10-22 20:11:33 +04:00
|
|
|
for (i = 0; i < term->esc_nargs; i++)
|
|
|
|
toggle_mode(term, term->esc_args[i],
|
|
|
|
term->esc_query, TRUE);
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
break;
|
2002-03-09 20:59:15 +03:00
|
|
|
case 'i':
|
|
|
|
case ANSI_QUE('i'):
|
|
|
|
compatibility(VT100);
|
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs != 1) break;
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->esc_args[0] == 5 && *term->cfg.printer) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->printing = TRUE;
|
|
|
|
term->only_printing = !term->esc_query;
|
|
|
|
term->print_state = 0;
|
|
|
|
term_print_setup(term);
|
|
|
|
} else if (term->esc_args[0] == 4 &&
|
|
|
|
term->printing) {
|
|
|
|
term_print_finish(term);
|
2002-03-09 20:59:15 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case 'l': /* toggle modes to low */
|
|
|
|
case ANSI_QUE('l'):
|
|
|
|
compatibility(VT100);
|
|
|
|
{
|
|
|
|
int i;
|
2002-10-22 20:11:33 +04:00
|
|
|
for (i = 0; i < term->esc_nargs; i++)
|
|
|
|
toggle_mode(term, term->esc_args[i],
|
|
|
|
term->esc_query, FALSE);
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'g': /* clear tabs */
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs == 1) {
|
|
|
|
if (term->esc_args[0] == 0) {
|
|
|
|
term->tabs[term->curs.x] = FALSE;
|
|
|
|
} else if (term->esc_args[0] == 3) {
|
2001-05-06 18:35:20 +04:00
|
|
|
int i;
|
2002-10-22 20:11:33 +04:00
|
|
|
for (i = 0; i < term->cols; i++)
|
|
|
|
term->tabs[i] = FALSE;
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'r': /* set scroll margins */
|
|
|
|
compatibility(VT100);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs <= 2) {
|
2001-05-06 18:35:20 +04:00
|
|
|
int top, bot;
|
2002-10-22 20:11:33 +04:00
|
|
|
top = def(term->esc_args[0], 1) - 1;
|
|
|
|
bot = (term->esc_nargs <= 1
|
|
|
|
|| term->esc_args[1] == 0 ?
|
|
|
|
term->rows :
|
|
|
|
def(term->esc_args[1], term->rows)) - 1;
|
|
|
|
if (bot >= term->rows)
|
|
|
|
bot = term->rows - 1;
|
2001-05-06 18:35:20 +04:00
|
|
|
/* VTTEST Bug 9 - if region is less than 2 lines
|
|
|
|
* don't change region.
|
|
|
|
*/
|
|
|
|
if (bot - top > 0) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->marg_t = top;
|
|
|
|
term->marg_b = bot;
|
|
|
|
term->curs.x = 0;
|
2001-05-06 18:35:20 +04:00
|
|
|
/*
|
|
|
|
* I used to think the cursor should be
|
|
|
|
* placed at the top of the newly marginned
|
|
|
|
* area. Apparently not: VMS TPU falls over
|
|
|
|
* if so.
|
|
|
|
*
|
2002-10-22 20:11:33 +04:00
|
|
|
* Well actually it should for
|
|
|
|
* Origin mode - RDB
|
2001-05-06 18:35:20 +04:00
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.y = (term->dec_om ?
|
|
|
|
term->marg_t : 0);
|
2001-05-06 18:35:20 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'm': /* set graphics rendition */
|
|
|
|
{
|
|
|
|
/*
|
2002-10-22 20:11:33 +04:00
|
|
|
* A VT100 without the AVO only had one
|
|
|
|
* attribute, either underline or
|
|
|
|
* reverse video depending on the
|
|
|
|
* cursor type, this was selected by
|
|
|
|
* CSI 7m.
|
2001-05-06 18:35:20 +04:00
|
|
|
*
|
|
|
|
* case 2:
|
2002-10-22 20:11:33 +04:00
|
|
|
* This is sometimes DIM, eg on the
|
|
|
|
* GIGI and Linux
|
2001-05-06 18:35:20 +04:00
|
|
|
* case 8:
|
2001-05-10 12:34:20 +04:00
|
|
|
* This is sometimes INVIS various ANSI.
|
2001-05-06 18:35:20 +04:00
|
|
|
* case 21:
|
|
|
|
* This like 22 disables BOLD, DIM and INVIS
|
|
|
|
*
|
2002-10-22 20:11:33 +04:00
|
|
|
* The ANSI colours appear on any
|
|
|
|
* terminal that has colour (obviously)
|
|
|
|
* but the interaction between sgr0 and
|
|
|
|
* the colours varies but is usually
|
|
|
|
* related to the background colour
|
|
|
|
* erase item. The interaction between
|
|
|
|
* colour attributes and the mono ones
|
|
|
|
* is also very implementation
|
|
|
|
* dependent.
|
2001-05-06 18:35:20 +04:00
|
|
|
*
|
2002-10-22 20:11:33 +04:00
|
|
|
* The 39 and 49 attributes are likely
|
|
|
|
* to be unimplemented.
|
2001-05-06 18:35:20 +04:00
|
|
|
*/
|
|
|
|
int i;
|
2002-10-22 20:11:33 +04:00
|
|
|
for (i = 0; i < term->esc_nargs; i++) {
|
|
|
|
switch (def(term->esc_args[i], 0)) {
|
2001-05-06 18:35:20 +04:00
|
|
|
case 0: /* restore defaults */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr = ATTR_DEFAULT;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 1: /* enable bold */
|
|
|
|
compatibility(VT100AVO);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr |= ATTR_BOLD;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 21: /* (enable double underline) */
|
|
|
|
compatibility(OTHER);
|
|
|
|
case 4: /* enable underline */
|
|
|
|
compatibility(VT100AVO);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr |= ATTR_UNDER;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 5: /* enable blink */
|
|
|
|
compatibility(VT100AVO);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr |= ATTR_BLINK;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 7: /* enable reverse video */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr |= ATTR_REVERSE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2001-05-19 18:12:41 +04:00
|
|
|
case 10: /* SCO acs off */
|
|
|
|
compatibility(SCOANSI);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->cfg.no_remote_charset) break;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->sco_acs = 0; break;
|
2001-05-19 18:12:41 +04:00
|
|
|
case 11: /* SCO acs on */
|
|
|
|
compatibility(SCOANSI);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->cfg.no_remote_charset) break;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->sco_acs = 1; break;
|
2002-12-31 14:03:23 +03:00
|
|
|
case 12: /* SCO acs on, |0x80 */
|
2001-05-19 18:12:41 +04:00
|
|
|
compatibility(SCOANSI);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->cfg.no_remote_charset) break;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->sco_acs = 2; break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case 22: /* disable bold */
|
|
|
|
compatibility2(OTHER, VT220);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr &= ~ATTR_BOLD;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 24: /* disable underline */
|
|
|
|
compatibility2(OTHER, VT220);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr &= ~ATTR_UNDER;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 25: /* disable blink */
|
|
|
|
compatibility2(OTHER, VT220);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr &= ~ATTR_BLINK;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 27: /* disable reverse video */
|
|
|
|
compatibility2(OTHER, VT220);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr &= ~ATTR_REVERSE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 30:
|
|
|
|
case 31:
|
|
|
|
case 32:
|
|
|
|
case 33:
|
|
|
|
case 34:
|
|
|
|
case 35:
|
|
|
|
case 36:
|
|
|
|
case 37:
|
|
|
|
/* foreground */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr &= ~ATTR_FGMASK;
|
|
|
|
term->curr_attr |=
|
|
|
|
(term->esc_args[i] - 30)<<ATTR_FGSHIFT;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2003-01-28 02:03:31 +03:00
|
|
|
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;
|
2001-05-06 18:35:20 +04:00
|
|
|
case 39: /* default-foreground */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr &= ~ATTR_FGMASK;
|
|
|
|
term->curr_attr |= ATTR_DEFFG;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 40:
|
|
|
|
case 41:
|
|
|
|
case 42:
|
|
|
|
case 43:
|
|
|
|
case 44:
|
|
|
|
case 45:
|
|
|
|
case 46:
|
|
|
|
case 47:
|
|
|
|
/* background */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr &= ~ATTR_BGMASK;
|
|
|
|
term->curr_attr |=
|
|
|
|
(term->esc_args[i] - 40)<<ATTR_BGSHIFT;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2003-01-28 02:03:31 +03:00
|
|
|
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;
|
2001-05-06 18:35:20 +04:00
|
|
|
case 49: /* default-background */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr &= ~ATTR_BGMASK;
|
|
|
|
term->curr_attr |= ATTR_DEFBG;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->use_bce)
|
|
|
|
term->erase_char = (' ' | ATTR_ASCII |
|
|
|
|
(term->curr_attr &
|
|
|
|
(ATTR_FGMASK |
|
|
|
|
ATTR_BGMASK)));
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 's': /* save cursor */
|
2002-10-22 20:11:33 +04:00
|
|
|
save_cursor(term, TRUE);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'u': /* restore cursor */
|
2002-10-22 20:11:33 +04:00
|
|
|
save_cursor(term, FALSE);
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 't': /* set page size - ie window height */
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
2001-05-06 18:35:20 +04:00
|
|
|
* VT340/VT420 sequence DECSLPP, DEC only allows values
|
|
|
|
* 24/25/36/48/72/144 other emulators (eg dtterm) use
|
|
|
|
* illegal values (eg first arg 1..9) for window changing
|
|
|
|
* and reports.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs <= 1
|
|
|
|
&& (term->esc_args[0] < 1 ||
|
|
|
|
term->esc_args[0] >= 24)) {
|
2001-11-25 18:21:25 +03:00
|
|
|
compatibility(VT340TEXT);
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_resize)
|
2002-10-26 16:58:13 +04:00
|
|
|
request_resize(term->frontend, term->cols,
|
2002-10-22 20:11:33 +04:00
|
|
|
def(term->esc_args[0], 24));
|
|
|
|
deselect(term);
|
|
|
|
} else if (term->esc_nargs >= 1 &&
|
|
|
|
term->esc_args[0] >= 1 &&
|
|
|
|
term->esc_args[0] < 24) {
|
2001-11-25 18:21:25 +03:00
|
|
|
compatibility(OTHER);
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
switch (term->esc_args[0]) {
|
2001-11-25 18:21:25 +03:00
|
|
|
int x, y, len;
|
|
|
|
char buf[80], *p;
|
|
|
|
case 1:
|
2002-10-26 16:58:13 +04:00
|
|
|
set_iconic(term->frontend, FALSE);
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
case 2:
|
2002-10-26 16:58:13 +04:00
|
|
|
set_iconic(term->frontend, TRUE);
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
case 3:
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs >= 3) {
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_resize)
|
2002-10-26 16:58:13 +04:00
|
|
|
move_window(term->frontend,
|
|
|
|
def(term->esc_args[1], 0),
|
2002-10-22 20:11:33 +04:00
|
|
|
def(term->esc_args[2], 0));
|
2001-11-25 18:21:25 +03:00
|
|
|
}
|
|
|
|
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:
|
2002-10-26 16:58:13 +04:00
|
|
|
/* move to top */
|
|
|
|
set_zorder(term->frontend, TRUE);
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
case 6:
|
2002-10-26 16:58:13 +04:00
|
|
|
/* move to bottom */
|
|
|
|
set_zorder(term->frontend, FALSE);
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
case 7:
|
2002-10-26 16:58:13 +04:00
|
|
|
refresh_window(term->frontend);
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
case 8:
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs >= 3) {
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_resize)
|
2002-10-26 16:58:13 +04:00
|
|
|
request_resize(term->frontend,
|
2003-01-12 17:30:02 +03:00
|
|
|
def(term->esc_args[2], term->cfg.width),
|
|
|
|
def(term->esc_args[1], term->cfg.height));
|
2001-11-25 18:21:25 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 9:
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs >= 2)
|
2002-10-26 16:58:13 +04:00
|
|
|
set_zoomed(term->frontend,
|
|
|
|
term->esc_args[1] ?
|
2002-10-22 20:11:33 +04:00
|
|
|
TRUE : FALSE);
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
case 11:
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc)
|
|
|
|
ldisc_send(term->ldisc,
|
2002-10-26 16:58:13 +04:00
|
|
|
is_iconic(term->frontend) ?
|
|
|
|
"\033[1t" : "\033[2t", 4, 0);
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
case 13:
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) {
|
2002-10-26 16:58:13 +04:00
|
|
|
get_window_pos(term->frontend, &x, &y);
|
2002-10-26 14:16:19 +04:00
|
|
|
len = sprintf(buf, "\033[3;%d;%dt", x, y);
|
|
|
|
ldisc_send(term->ldisc, buf, len, 0);
|
|
|
|
}
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
case 14:
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) {
|
2002-10-26 16:58:13 +04:00
|
|
|
get_window_pixels(term->frontend, &x, &y);
|
2002-10-26 14:16:19 +04:00
|
|
|
len = sprintf(buf, "\033[4;%d;%dt", x, y);
|
|
|
|
ldisc_send(term->ldisc, buf, len, 0);
|
|
|
|
}
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
case 18:
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) {
|
|
|
|
len = sprintf(buf, "\033[8;%d;%dt",
|
|
|
|
term->rows, term->cols);
|
|
|
|
ldisc_send(term->ldisc, buf, len, 0);
|
|
|
|
}
|
2001-11-25 18:21:25 +03:00
|
|
|
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:
|
2003-04-12 12:59:06 +04:00
|
|
|
if (term->ldisc &&
|
|
|
|
!term->cfg.no_remote_qtitle) {
|
2002-10-26 16:58:13 +04:00
|
|
|
p = get_window_title(term->frontend, TRUE);
|
2002-10-26 14:16:19 +04:00
|
|
|
len = strlen(p);
|
|
|
|
ldisc_send(term->ldisc, "\033]L", 3, 0);
|
|
|
|
ldisc_send(term->ldisc, p, len, 0);
|
|
|
|
ldisc_send(term->ldisc, "\033\\", 2, 0);
|
|
|
|
}
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
case 21:
|
2003-04-12 12:59:06 +04:00
|
|
|
if (term->ldisc &&
|
|
|
|
!term->cfg.no_remote_qtitle) {
|
2002-10-26 16:58:13 +04:00
|
|
|
p = get_window_title(term->frontend,FALSE);
|
2002-10-26 14:16:19 +04:00
|
|
|
len = strlen(p);
|
|
|
|
ldisc_send(term->ldisc, "\033]l", 3, 0);
|
|
|
|
ldisc_send(term->ldisc, p, len, 0);
|
|
|
|
ldisc_send(term->ldisc, "\033\\", 2, 0);
|
|
|
|
}
|
2001-11-25 18:21:25 +03:00
|
|
|
break;
|
|
|
|
}
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
break;
|
2001-05-09 17:35:08 +04:00
|
|
|
case 'S':
|
|
|
|
compatibility(SCOANSI);
|
2002-10-22 20:11:33 +04:00
|
|
|
scroll(term, term->marg_t, term->marg_b,
|
|
|
|
def(term->esc_args[0], 1), TRUE);
|
2001-05-09 17:35:08 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrapnext = FALSE;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-09 17:35:08 +04:00
|
|
|
break;
|
|
|
|
case 'T':
|
|
|
|
compatibility(SCOANSI);
|
2002-10-22 20:11:33 +04:00
|
|
|
scroll(term, term->marg_t, term->marg_b,
|
|
|
|
-def(term->esc_args[0], 1), TRUE);
|
2001-05-09 17:35:08 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrapnext = FALSE;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-09 17:35:08 +04:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case ANSI('|', '*'):
|
|
|
|
/* VT420 sequence DECSNLS
|
|
|
|
* Set number of lines on screen
|
|
|
|
* VT420 uses VGA like hardware and can support any size in
|
|
|
|
* reasonable range (24..49 AIUI) with no default specified.
|
|
|
|
*/
|
|
|
|
compatibility(VT420);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs == 1 && term->esc_args[0] > 0) {
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_resize)
|
2002-10-26 16:58:13 +04:00
|
|
|
request_resize(term->frontend, term->cols,
|
2002-10-22 20:11:33 +04:00
|
|
|
def(term->esc_args[0],
|
2003-01-12 17:30:02 +03:00
|
|
|
term->cfg.height));
|
2002-10-22 20:11:33 +04:00
|
|
|
deselect(term);
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ANSI('|', '$'):
|
|
|
|
/* VT340/VT420 sequence DECSCPP
|
|
|
|
* Set number of columns per page
|
|
|
|
* Docs imply range is only 80 or 132, but I'll allow any.
|
|
|
|
*/
|
|
|
|
compatibility(VT340TEXT);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_nargs <= 1) {
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_resize)
|
2002-10-26 16:58:13 +04:00
|
|
|
request_resize(term->frontend,
|
|
|
|
def(term->esc_args[0],
|
2003-01-12 17:30:02 +03:00
|
|
|
term->cfg.width), term->rows);
|
2002-10-22 20:11:33 +04:00
|
|
|
deselect(term);
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'X': /* write N spaces w/o moving cursor */
|
|
|
|
/* XXX VTTEST says this is vt220, vt510 manual says vt100 */
|
|
|
|
compatibility(ANSIMIN);
|
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
int n = def(term->esc_args[0], 1);
|
2001-05-06 18:35:20 +04:00
|
|
|
pos cursplus;
|
2002-10-22 20:11:33 +04:00
|
|
|
unsigned long *p = term->cpos;
|
|
|
|
if (n > term->cols - term->curs.x)
|
|
|
|
n = term->cols - term->curs.x;
|
|
|
|
cursplus = term->curs;
|
2001-05-06 18:35:20 +04:00
|
|
|
cursplus.x += n;
|
2003-01-02 19:20:29 +03:00
|
|
|
check_boundary(term, term->curs.x, term->curs.y);
|
|
|
|
check_boundary(term, term->curs.x+n, term->curs.y);
|
2002-10-22 20:11:33 +04:00
|
|
|
check_selection(term, term->curs, cursplus);
|
2001-05-06 18:35:20 +04:00
|
|
|
while (n--)
|
2002-10-22 20:11:33 +04:00
|
|
|
*p++ = term->erase_char;
|
|
|
|
term->seen_disp_event = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'x': /* report terminal characteristics */
|
|
|
|
compatibility(VT100);
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) {
|
2001-05-06 18:35:20 +04:00
|
|
|
char buf[32];
|
2002-10-22 20:11:33 +04:00
|
|
|
int i = def(term->esc_args[0], 0);
|
2001-05-06 18:35:20 +04:00
|
|
|
if (i == 0 || i == 1) {
|
|
|
|
strcpy(buf, "\033[2;1;1;112;112;1;0x");
|
|
|
|
buf[2] += i;
|
2002-10-26 14:16:19 +04:00
|
|
|
ldisc_send(term->ldisc, buf, 20, 0);
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2001-06-23 20:59:00 +04:00
|
|
|
case 'Z': /* BackTab for xterm */
|
|
|
|
compatibility(OTHER);
|
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
int i = def(term->esc_args[0], 1);
|
|
|
|
pos old_curs = term->curs;
|
2001-06-23 20:59:00 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
for(;i>0 && term->curs.x>0; i--) {
|
2001-06-23 20:59:00 +04:00
|
|
|
do {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curs.x--;
|
|
|
|
} while (term->curs.x >0 &&
|
|
|
|
!term->tabs[term->curs.x]);
|
2001-06-23 20:59:00 +04:00
|
|
|
}
|
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
check_selection(term, old_curs, term->curs);
|
2001-06-23 20:59:00 +04:00
|
|
|
}
|
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case ANSI('L', '='):
|
|
|
|
compatibility(OTHER);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->use_bce = (term->esc_args[0] <= 0);
|
|
|
|
term->erase_char = ERASE_CHAR;
|
|
|
|
if (term->use_bce)
|
|
|
|
term->erase_char = (' ' | ATTR_ASCII |
|
|
|
|
(term->curr_attr &
|
|
|
|
(ATTR_FGMASK | ATTR_BGMASK)));
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case ANSI('E', '='):
|
|
|
|
compatibility(OTHER);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->blink_is_real = (term->esc_args[0] >= 1);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case ANSI('p', '"'):
|
2002-10-22 20:11:33 +04:00
|
|
|
/*
|
|
|
|
* Allow the host to make this emulator a
|
|
|
|
* 'perfect' VT102. This first appeared in
|
|
|
|
* the VT220, but we do need to get back to
|
|
|
|
* PuTTY mode so I won't check it.
|
2000-03-17 13:39:05 +03:00
|
|
|
*
|
2001-05-10 12:34:20 +04:00
|
|
|
* The arg in 40..42,50 are a PuTTY extension.
|
2001-05-06 18:35:20 +04:00
|
|
|
* The 2nd arg, 8bit vs 7bit is not checked.
|
|
|
|
*
|
2002-10-22 20:11:33 +04:00
|
|
|
* Setting VT102 mode should also change
|
|
|
|
* the Fkeys to generate PF* codes as a
|
|
|
|
* real VT102 has no Fkeys. The VT220 does
|
|
|
|
* this, F11..F13 become ESC,BS,LF other
|
|
|
|
* Fkeys send nothing.
|
2001-05-06 18:35:20 +04:00
|
|
|
*
|
|
|
|
* Note ESC c will NOT change this!
|
1999-01-08 16:02:13 +03:00
|
|
|
*/
|
2001-05-06 18:35:20 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
switch (term->esc_args[0]) {
|
2001-05-06 18:35:20 +04:00
|
|
|
case 61:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->compatibility_level &= ~TM_VTXXX;
|
|
|
|
term->compatibility_level |= TM_VT102;
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case 62:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->compatibility_level &= ~TM_VTXXX;
|
|
|
|
term->compatibility_level |= TM_VT220;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_args[0] > 60 &&
|
|
|
|
term->esc_args[0] < 70)
|
|
|
|
term->compatibility_level |= TM_VTXXX;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 40:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->compatibility_level &= TM_VTXXX;
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case 41:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->compatibility_level = TM_PUTTY;
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case 42:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->compatibility_level = TM_SCOANSI;
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
|
|
|
|
case ARG_DEFAULT:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->compatibility_level = TM_PUTTY;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 50:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Change the response to CSI c */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_args[0] == 50) {
|
2001-05-06 18:35:20 +04:00
|
|
|
int i;
|
|
|
|
char lbuf[64];
|
2002-10-22 20:11:33 +04:00
|
|
|
strcpy(term->id_string, "\033[?");
|
|
|
|
for (i = 1; i < term->esc_nargs; i++) {
|
2001-05-06 18:35:20 +04:00
|
|
|
if (i != 1)
|
2002-10-22 20:11:33 +04:00
|
|
|
strcat(term->id_string, ";");
|
|
|
|
sprintf(lbuf, "%d", term->esc_args[i]);
|
|
|
|
strcat(term->id_string, lbuf);
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
strcat(term->id_string, "c");
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
/* Is this a good idea ?
|
|
|
|
* Well we should do a soft reset at this point ...
|
|
|
|
*/
|
|
|
|
if (!has_compat(VT420) && has_compat(VT100)) {
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.no_remote_resize) {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->reset_132)
|
2002-03-07 02:04:20 +03:00
|
|
|
request_resize(132, 24);
|
|
|
|
else
|
|
|
|
request_resize(80, 24);
|
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2001-05-06 18:35:20 +04:00
|
|
|
#endif
|
|
|
|
break;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case SEEN_OSC:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->osc_w = FALSE;
|
2001-05-06 18:35:20 +04:00
|
|
|
switch (c) {
|
|
|
|
case 'P': /* Linux palette sequence */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = SEEN_OSC_P;
|
|
|
|
term->osc_strlen = 0;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'R': /* Linux palette reset */
|
2002-10-26 16:58:13 +04:00
|
|
|
palette_reset(term->frontend);
|
2002-10-22 20:11:33 +04:00
|
|
|
term_invalidate(term);
|
|
|
|
term->termstate = TOPLEVEL;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'W': /* word-set */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = SEEN_OSC_W;
|
|
|
|
term->osc_w = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
|
|
|
case '8':
|
|
|
|
case '9':
|
2002-10-22 20:11:33 +04:00
|
|
|
term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
/*
|
|
|
|
* Grotty hack to support xterm and DECterm title
|
|
|
|
* sequences concurrently.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->esc_args[0] == 2) {
|
|
|
|
term->esc_args[0] = 1;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* else fall through */
|
|
|
|
default:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = OSC_STRING;
|
|
|
|
term->osc_strlen = 0;
|
2000-03-17 13:39:05 +03:00
|
|
|
}
|
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case OSC_STRING:
|
|
|
|
/*
|
|
|
|
* This OSC stuff is EVIL. It takes just one character to get into
|
|
|
|
* sysline mode and it's not initially obvious how to get out.
|
|
|
|
* So I've added CR and LF as string aborts.
|
|
|
|
* This shouldn't effect compatibility as I believe embedded
|
|
|
|
* control characters are supposed to be interpreted (maybe?)
|
|
|
|
* and they don't display anything useful anyway.
|
|
|
|
*
|
|
|
|
* -- RDB
|
2000-03-17 13:39:05 +03:00
|
|
|
*/
|
2002-11-19 02:49:30 +03:00
|
|
|
if (c == '\012' || c == '\015') {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = TOPLEVEL;
|
2001-05-06 18:35:20 +04:00
|
|
|
} else if (c == 0234 || c == '\007') {
|
|
|
|
/*
|
|
|
|
* These characters terminate the string; ST and BEL
|
|
|
|
* terminate the sequence and trigger instant
|
|
|
|
* processing of it, whereas ESC goes back to SEEN_ESC
|
|
|
|
* mode unless it is followed by \, in which case it is
|
|
|
|
* synonymous with ST in the first place.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
do_osc(term);
|
|
|
|
term->termstate = TOPLEVEL;
|
2001-05-06 18:35:20 +04:00
|
|
|
} else if (c == '\033')
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = OSC_MAYBE_ST;
|
|
|
|
else if (term->osc_strlen < OSC_STR_MAX)
|
|
|
|
term->osc_string[term->osc_strlen++] = c;
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case SEEN_OSC_P:
|
1999-01-08 16:02:13 +03:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
int max = (term->osc_strlen == 0 ? 21 : 16);
|
2001-05-06 18:35:20 +04:00
|
|
|
int val;
|
|
|
|
if (c >= '0' && c <= '9')
|
|
|
|
val = c - '0';
|
|
|
|
else if (c >= 'A' && c <= 'A' + max - 10)
|
|
|
|
val = c - 'A' + 10;
|
|
|
|
else if (c >= 'a' && c <= 'a' + max - 10)
|
|
|
|
val = c - 'a' + 10;
|
2001-05-13 18:02:28 +04:00
|
|
|
else {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = TOPLEVEL;
|
2001-05-13 18:02:28 +04:00
|
|
|
break;
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->osc_string[term->osc_strlen++] = val;
|
|
|
|
if (term->osc_strlen >= 7) {
|
2002-10-26 16:58:13 +04:00
|
|
|
palette_set(term->frontend, term->osc_string[0],
|
2002-10-22 20:11:33 +04:00
|
|
|
term->osc_string[1] * 16 + term->osc_string[2],
|
|
|
|
term->osc_string[3] * 16 + term->osc_string[4],
|
|
|
|
term->osc_string[5] * 16 + term->osc_string[6]);
|
|
|
|
term_invalidate(term);
|
|
|
|
term->termstate = TOPLEVEL;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case SEEN_OSC_W:
|
|
|
|
switch (c) {
|
|
|
|
case '0':
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
|
|
|
case '8':
|
|
|
|
case '9':
|
2002-10-22 20:11:33 +04:00
|
|
|
term->esc_args[0] = 10 * term->esc_args[0] + c - '0';
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
default:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = OSC_STRING;
|
|
|
|
term->osc_strlen = 0;
|
2000-10-09 16:53:32 +04:00
|
|
|
}
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case VT52_ESC:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = TOPLEVEL;
|
|
|
|
term->seen_disp_event = TRUE;
|
2000-07-26 16:13:51 +04:00
|
|
|
switch (c) {
|
2001-05-06 18:35:20 +04:00
|
|
|
case 'A':
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, term->curs.x, term->curs.y - 1, 1);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'B':
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, term->curs.x, term->curs.y + 1, 1);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'C':
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, term->curs.x + 1, term->curs.y, 1);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'D':
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, term->curs.x - 1, term->curs.y, 1);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
/*
|
|
|
|
* From the VT100 Manual
|
|
|
|
* NOTE: The special graphics characters in the VT100
|
|
|
|
* are different from those in the VT52
|
|
|
|
*
|
|
|
|
* From VT102 manual:
|
|
|
|
* 137 _ Blank - Same
|
|
|
|
* 140 ` Reserved - Humm.
|
|
|
|
* 141 a Solid rectangle - Similar
|
|
|
|
* 142 b 1/ - Top half of fraction for the
|
|
|
|
* 143 c 3/ - subscript numbers below.
|
|
|
|
* 144 d 5/
|
|
|
|
* 145 e 7/
|
|
|
|
* 146 f Degrees - Same
|
|
|
|
* 147 g Plus or minus - Same
|
|
|
|
* 150 h Right arrow
|
|
|
|
* 151 i Ellipsis (dots)
|
|
|
|
* 152 j Divide by
|
|
|
|
* 153 k Down arrow
|
|
|
|
* 154 l Bar at scan 0
|
|
|
|
* 155 m Bar at scan 1
|
|
|
|
* 156 n Bar at scan 2
|
|
|
|
* 157 o Bar at scan 3 - Similar
|
|
|
|
* 160 p Bar at scan 4 - Similar
|
|
|
|
* 161 q Bar at scan 5 - Similar
|
|
|
|
* 162 r Bar at scan 6 - Same
|
|
|
|
* 163 s Bar at scan 7 - Similar
|
|
|
|
* 164 t Subscript 0
|
|
|
|
* 165 u Subscript 1
|
|
|
|
* 166 v Subscript 2
|
|
|
|
* 167 w Subscript 3
|
|
|
|
* 170 x Subscript 4
|
|
|
|
* 171 y Subscript 5
|
|
|
|
* 172 z Subscript 6
|
|
|
|
* 173 { Subscript 7
|
|
|
|
* 174 | Subscript 8
|
|
|
|
* 175 } Subscript 9
|
|
|
|
* 176 ~ Paragraph
|
|
|
|
*
|
|
|
|
*/
|
2001-05-06 18:35:20 +04:00
|
|
|
case 'F':
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[term->cset = 0] = ATTR_LINEDRW;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'G':
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cset_attr[term->cset = 0] = ATTR_ASCII;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'H':
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, 0, 0, 0);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'I':
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.y == 0)
|
|
|
|
scroll(term, 0, term->rows - 1, -1, TRUE);
|
|
|
|
else if (term->curs.y > 0)
|
|
|
|
term->curs.y--;
|
2001-05-06 18:35:20 +04:00
|
|
|
fix_cpos;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrapnext = FALSE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'J':
|
2002-10-22 20:11:33 +04:00
|
|
|
erase_lots(term, FALSE, FALSE, TRUE);
|
|
|
|
term->disptop = 0;
|
2000-07-26 16:13:51 +04:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case 'K':
|
2002-10-22 20:11:33 +04:00
|
|
|
erase_lots(term, TRUE, FALSE, TRUE);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
#if 0
|
2001-05-06 18:35:20 +04:00
|
|
|
case 'V':
|
|
|
|
/* XXX Print cursor line */
|
|
|
|
break;
|
|
|
|
case 'W':
|
|
|
|
/* XXX Start controller mode */
|
|
|
|
break;
|
|
|
|
case 'X':
|
|
|
|
/* XXX Stop controller mode */
|
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
#endif
|
2001-05-06 18:35:20 +04:00
|
|
|
case 'Y':
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = VT52_Y1;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case 'Z':
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc)
|
|
|
|
ldisc_send(term->ldisc, "\033/Z", 3, 0);
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case '=':
|
2002-10-22 20:11:33 +04:00
|
|
|
term->app_keypad_keys = TRUE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case '>':
|
2002-10-22 20:11:33 +04:00
|
|
|
term->app_keypad_keys = FALSE;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
|
|
|
case '<':
|
|
|
|
/* XXX This should switch to VT100 mode not current or default
|
|
|
|
* VT mode. But this will only have effect in a VT220+
|
|
|
|
* emulation.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
term->vt52_mode = FALSE;
|
2003-01-12 17:30:02 +03:00
|
|
|
term->blink_is_real = term->cfg.blinktext;
|
2001-05-06 18:35:20 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
#if 0
|
2001-05-06 18:35:20 +04:00
|
|
|
case '^':
|
|
|
|
/* XXX Enter auto print mode */
|
|
|
|
break;
|
|
|
|
case '_':
|
|
|
|
/* XXX Exit auto print mode */
|
|
|
|
break;
|
|
|
|
case ']':
|
|
|
|
/* XXX Print screen */
|
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef VT52_PLUS
|
|
|
|
case 'E':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
move(term, 0, 0, 0);
|
|
|
|
erase_lots(term, FALSE, FALSE, TRUE);
|
|
|
|
term->disptop = 0;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.y <= term->marg_b)
|
|
|
|
scroll(term, term->curs.y, term->marg_b, -1, FALSE);
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->curs.y <= term->marg_b)
|
|
|
|
scroll(term, term->curs.y, term->marg_b, 1, TRUE);
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = VT52_FG;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = VT52_BG;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
erase_lots(term, FALSE, TRUE, FALSE);
|
|
|
|
term->disptop = 0;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'e':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cursor_on = TRUE;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->cursor_on = FALSE;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
/* case 'j': Save cursor position - broken on ST */
|
|
|
|
/* case 'k': Restore cursor position */
|
|
|
|
case 'l':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
erase_lots(term, TRUE, TRUE, TRUE);
|
|
|
|
term->curs.x = 0;
|
|
|
|
term->wrapnext = FALSE;
|
2001-05-10 12:34:20 +04:00
|
|
|
fix_cpos;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
erase_lots(term, TRUE, TRUE, FALSE);
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr |= ATTR_REVERSE;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'q':
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr &= ~ATTR_REVERSE;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'v': /* wrap Autowrap on - Wyse style */
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrap = 1;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'w': /* Autowrap off */
|
|
|
|
/* compatibility(ATARI) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->wrap = 0;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'R':
|
|
|
|
/* compatibility(OTHER) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->vt52_bold = FALSE;
|
|
|
|
term->curr_attr = ATTR_DEFAULT;
|
|
|
|
if (term->use_bce)
|
|
|
|
term->erase_char = (' ' | ATTR_ASCII |
|
|
|
|
(term->curr_attr &
|
|
|
|
(ATTR_FGMASK | ATTR_BGMASK)));
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
/* compatibility(VI50) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr |= ATTR_UNDER;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'W':
|
|
|
|
/* compatibility(VI50) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr &= ~ATTR_UNDER;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'U':
|
|
|
|
/* compatibility(VI50) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->vt52_bold = TRUE;
|
|
|
|
term->curr_attr |= ATTR_BOLD;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case 'T':
|
|
|
|
/* compatibility(VI50) */
|
2002-10-22 20:11:33 +04:00
|
|
|
term->vt52_bold = FALSE;
|
|
|
|
term->curr_attr &= ~ATTR_BOLD;
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
#endif
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
2000-03-17 13:39:05 +03:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case VT52_Y1:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = VT52_Y2;
|
|
|
|
move(term, term->curs.x, c - ' ', 0);
|
2000-03-17 13:39:05 +03:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
case VT52_Y2:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = TOPLEVEL;
|
|
|
|
move(term, c - ' ', term->curs.y, 0);
|
2000-03-17 13:39:05 +03:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
#ifdef VT52_PLUS
|
|
|
|
case VT52_FG:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = TOPLEVEL;
|
|
|
|
term->curr_attr &= ~ATTR_FGMASK;
|
|
|
|
term->curr_attr &= ~ATTR_BOLD;
|
|
|
|
term->curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
|
|
|
|
if ((c & 0x8) || term->vt52_bold)
|
|
|
|
term->curr_attr |= ATTR_BOLD;
|
|
|
|
|
|
|
|
if (term->use_bce)
|
|
|
|
term->erase_char = (' ' | ATTR_ASCII |
|
|
|
|
(term->curr_attr &
|
|
|
|
(ATTR_FGMASK | ATTR_BGMASK)));
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case VT52_BG:
|
2002-10-22 20:11:33 +04:00
|
|
|
term->termstate = TOPLEVEL;
|
|
|
|
term->curr_attr &= ~ATTR_BGMASK;
|
|
|
|
term->curr_attr &= ~ATTR_BLINK;
|
|
|
|
term->curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
/* Note: bold background */
|
|
|
|
if (c & 0x8)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curr_attr |= ATTR_BLINK;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->use_bce)
|
|
|
|
term->erase_char = (' ' | ATTR_ASCII |
|
|
|
|
(term->curr_attr &
|
|
|
|
(ATTR_FGMASK | ATTR_BGMASK)));
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
#endif
|
2001-05-13 18:02:28 +04:00
|
|
|
default: break; /* placate gcc warning about enum use */
|
2000-03-17 13:39:05 +03:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->selstate != NO_SELECTION) {
|
|
|
|
pos cursplus = term->curs;
|
2001-04-17 01:25:13 +04:00
|
|
|
incpos(cursplus);
|
2002-10-22 20:11:33 +04:00
|
|
|
check_selection(term, term->curs, cursplus);
|
2001-04-17 01:25:13 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2002-03-09 20:59:15 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
term_print_flush(term);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2001-05-10 12:34:20 +04:00
|
|
|
#if 0
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Compare two lines to determine whether they are sufficiently
|
|
|
|
* alike to scroll-optimise one to the other. Return the degree of
|
|
|
|
* similarity.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static int linecmp(Terminal *term, unsigned long *a, unsigned long *b)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
1999-01-08 16:02:13 +03:00
|
|
|
int i, n;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
for (i = n = 0; i < term->cols; i++)
|
1999-01-08 16:02:13 +03:00
|
|
|
n += (*a++ == *b++);
|
|
|
|
return n;
|
|
|
|
}
|
2001-05-10 12:34:20 +04:00
|
|
|
#endif
|
1999-01-08 16:02:13 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a context, update the window. Out of paranoia, we don't
|
|
|
|
* allow WM_PAINT responses to do scrolling optimisations.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static void do_paint(Terminal *term, Context ctx, int may_optimise)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2003-01-02 19:20:29 +03:00
|
|
|
int i, j, our_curs_y, our_curs_x;
|
2001-05-10 12:34:20 +04:00
|
|
|
unsigned long rv, cursor;
|
2001-04-17 01:25:13 +04:00
|
|
|
pos scrpos;
|
1999-01-08 16:02:13 +03:00
|
|
|
char ch[1024];
|
2001-05-10 12:34:20 +04:00
|
|
|
long cursor_background = ERASE_CHAR;
|
2001-10-23 23:51:23 +04:00
|
|
|
unsigned long ticks;
|
2003-02-01 15:26:33 +03:00
|
|
|
#ifdef OPTIMISE_SCROLL
|
|
|
|
struct scrollregion *sr;
|
|
|
|
#endif /* OPTIMISE_SCROLL */
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2001-04-13 14:52:36 +04:00
|
|
|
/*
|
|
|
|
* Check the visual bell state.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->in_vbell) {
|
2002-10-07 20:45:23 +04:00
|
|
|
ticks = GETTICKCOUNT();
|
2002-10-22 20:11:33 +04:00
|
|
|
if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
|
|
|
|
term->in_vbell = FALSE;
|
2001-10-23 23:51:23 +04:00
|
|
|
}
|
2001-04-13 14:52:36 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);
|
2001-05-10 12:34:20 +04:00
|
|
|
|
2001-04-13 14:52:36 +04:00
|
|
|
/* Depends on:
|
|
|
|
* screen array, disptop, scrtop,
|
|
|
|
* selection, rv,
|
|
|
|
* cfg.blinkpc, blink_is_real, tblinker,
|
2001-05-10 12:34:20 +04:00
|
|
|
* curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
|
2001-04-13 14:52:36 +04:00
|
|
|
*/
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
/* Has the cursor position or type changed ? */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->cursor_on) {
|
|
|
|
if (term->has_focus) {
|
2003-01-12 17:30:02 +03:00
|
|
|
if (term->blinker || !term->cfg.blink_cur)
|
2001-05-10 12:34:20 +04:00
|
|
|
cursor = TATTR_ACTCURS;
|
2001-05-06 18:35:20 +04:00
|
|
|
else
|
|
|
|
cursor = 0;
|
|
|
|
} else
|
2001-05-10 12:34:20 +04:00
|
|
|
cursor = TATTR_PASCURS;
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->wrapnext)
|
2001-05-10 12:34:20 +04:00
|
|
|
cursor |= TATTR_RIGHTCURS;
|
2001-05-06 18:35:20 +04:00
|
|
|
} else
|
2001-04-13 14:52:36 +04:00
|
|
|
cursor = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
our_curs_y = term->curs.y - term->disptop;
|
2003-01-02 19:20:29 +03:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Adjust the cursor position in the case where it's
|
|
|
|
* resting on the right-hand half of a CJK wide character.
|
|
|
|
* xterm's behaviour here, which seems adequate to me, is
|
|
|
|
* to display the cursor covering the _whole_ character,
|
|
|
|
* exactly as if it were one space to the left.
|
|
|
|
*/
|
|
|
|
unsigned long *ldata = lineptr(term->curs.y);
|
|
|
|
our_curs_x = term->curs.x;
|
|
|
|
if (our_curs_x > 0 &&
|
|
|
|
(ldata[our_curs_x] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
|
|
|
|
our_curs_x--;
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
|
|
|
|
if (term->dispcurs && (term->curstype != cursor ||
|
|
|
|
term->dispcurs !=
|
|
|
|
term->disptext + our_curs_y * (term->cols + 1) +
|
2003-01-02 19:20:29 +03:00
|
|
|
our_curs_x)) {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->dispcurs > term->disptext &&
|
2003-01-02 19:20:29 +03:00
|
|
|
(*term->dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
|
2002-10-22 20:11:33 +04:00
|
|
|
term->dispcurs[-1] |= ATTR_INVALID;
|
|
|
|
if ( (term->dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
|
|
|
|
term->dispcurs[1] |= ATTR_INVALID;
|
|
|
|
*term->dispcurs |= ATTR_INVALID;
|
|
|
|
term->curstype = 0;
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->dispcurs = NULL;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
2003-02-01 15:26:33 +03:00
|
|
|
#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 */
|
|
|
|
|
2001-05-10 12:34:20 +04:00
|
|
|
/* The normal screen data */
|
2002-10-22 20:11:33 +04:00
|
|
|
for (i = 0; i < term->rows; i++) {
|
2001-04-17 01:25:13 +04:00
|
|
|
unsigned long *ldata;
|
|
|
|
int lattr;
|
2001-10-31 21:50:09 +03:00
|
|
|
int idx, dirty_line, dirty_run, selected;
|
2001-05-10 12:34:20 +04:00
|
|
|
unsigned long attr = 0;
|
|
|
|
int updated_line = 0;
|
|
|
|
int start = 0;
|
|
|
|
int ccount = 0;
|
|
|
|
int last_run_dirty = 0;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
scrpos.y = i + term->disptop;
|
2001-04-17 01:25:13 +04:00
|
|
|
ldata = lineptr(scrpos.y);
|
2002-10-22 20:11:33 +04:00
|
|
|
lattr = (ldata[term->cols] & LATTR_MODE);
|
2001-05-10 12:34:20 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
idx = i * (term->cols + 1);
|
|
|
|
dirty_run = dirty_line = (ldata[term->cols] !=
|
|
|
|
term->disptext[idx + term->cols]);
|
|
|
|
term->disptext[idx + term->cols] = ldata[term->cols];
|
2001-05-10 12:34:20 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
for (j = 0; j < term->cols; j++, idx++) {
|
2001-05-10 12:34:20 +04:00
|
|
|
unsigned long tattr, tchar;
|
|
|
|
unsigned long *d = ldata + j;
|
|
|
|
int break_run;
|
2001-04-17 01:25:13 +04:00
|
|
|
scrpos.x = j;
|
2001-05-06 18:35:20 +04:00
|
|
|
|
2001-05-10 12:34:20 +04:00
|
|
|
tchar = (*d & (CHAR_MASK | CSET_MASK));
|
|
|
|
tattr = (*d & (ATTR_MASK ^ CSET_MASK));
|
|
|
|
switch (tchar & CSET_MASK) {
|
|
|
|
case ATTR_ASCII:
|
2003-01-14 21:28:23 +03:00
|
|
|
tchar = term->ucsdata->unitab_line[tchar & 0xFF];
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case ATTR_LINEDRW:
|
2003-01-14 21:28:23 +03:00
|
|
|
tchar = term->ucsdata->unitab_xterm[tchar & 0xFF];
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
2001-05-19 18:12:41 +04:00
|
|
|
case ATTR_SCOACS:
|
2003-01-14 21:28:23 +03:00
|
|
|
tchar = term->ucsdata->unitab_scoacs[tchar&0xFF];
|
2001-05-19 18:12:41 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
|
|
|
tattr |= (tchar & CSET_MASK);
|
|
|
|
tchar &= CHAR_MASK;
|
2001-09-15 19:54:24 +04:00
|
|
|
if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
|
|
|
|
tattr |= ATTR_WIDE;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
/* Video reversing things */
|
2002-10-23 16:41:35 +04:00
|
|
|
if (term->selstate == DRAGGING || term->selstate == SELECTED) {
|
|
|
|
if (term->seltype == LEXICOGRAPHIC)
|
|
|
|
selected = (posle(term->selstart, scrpos) &&
|
|
|
|
poslt(scrpos, term->selend));
|
|
|
|
else
|
|
|
|
selected = (posPle(term->selstart, scrpos) &&
|
|
|
|
posPlt(scrpos, term->selend));
|
|
|
|
} else
|
|
|
|
selected = FALSE;
|
2001-05-10 12:34:20 +04:00
|
|
|
tattr = (tattr ^ rv
|
2001-10-31 21:50:09 +03:00
|
|
|
^ (selected ? ATTR_REVERSE : 0));
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
/* 'Real' blinking ? */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->blink_is_real && (tattr & ATTR_BLINK)) {
|
|
|
|
if (term->has_focus && term->tblinker) {
|
2003-01-27 18:44:48 +03:00
|
|
|
tchar = term->ucsdata->unitab_line[(unsigned char)' '];
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
2001-05-10 12:34:20 +04:00
|
|
|
tattr &= ~ATTR_BLINK;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2001-09-15 19:54:24 +04:00
|
|
|
/*
|
|
|
|
* Check the font we'll _probably_ be using to see if
|
|
|
|
* the character is wide when we don't want it to be.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if ((tchar | tattr) != (term->disptext[idx]& ~ATTR_NARROW)) {
|
2001-09-15 19:54:24 +04:00
|
|
|
if ((tattr & ATTR_WIDE) == 0 &&
|
2002-11-10 00:46:21 +03:00
|
|
|
char_width(ctx, (tchar | tattr) & 0xFFFF) == 2)
|
2001-09-15 19:54:24 +04:00
|
|
|
tattr |= ATTR_NARROW;
|
2002-10-22 20:11:33 +04:00
|
|
|
} else if (term->disptext[idx]&ATTR_NARROW)
|
2001-09-15 19:54:24 +04:00
|
|
|
tattr |= ATTR_NARROW;
|
|
|
|
|
2001-05-10 12:34:20 +04:00
|
|
|
/* Cursor here ? Save the 'background' */
|
2003-01-02 19:20:29 +03:00
|
|
|
if (i == our_curs_y && j == our_curs_x) {
|
2001-05-10 12:34:20 +04:00
|
|
|
cursor_background = tattr | tchar;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->dispcurs = term->disptext + idx;
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if ((term->disptext[idx] ^ tattr) & ATTR_WIDE)
|
2001-05-10 12:34:20 +04:00
|
|
|
dirty_line = TRUE;
|
|
|
|
|
2002-11-29 03:32:03 +03:00
|
|
|
break_run = (((tattr ^ attr) & term->attr_mask) ||
|
|
|
|
j - start >= sizeof(ch));
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
/* Special hack for VT100 Linedraw glyphs */
|
|
|
|
if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
|
|
|
|
&& tchar <= 0xBD) break_run = TRUE;
|
|
|
|
|
2003-01-14 21:28:23 +03:00
|
|
|
if (!term->ucsdata->dbcs_screenfont && !dirty_line) {
|
2002-10-22 20:11:33 +04:00
|
|
|
if ((tchar | tattr) == term->disptext[idx])
|
2001-05-10 12:34:20 +04:00
|
|
|
break_run = TRUE;
|
|
|
|
else if (!dirty_run && ccount == 1)
|
|
|
|
break_run = TRUE;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
if (break_run) {
|
|
|
|
if ((dirty_run || last_run_dirty) && ccount > 0) {
|
|
|
|
do_text(ctx, start, i, ch, ccount, attr, lattr);
|
|
|
|
updated_line = 1;
|
|
|
|
}
|
|
|
|
start = j;
|
|
|
|
ccount = 0;
|
|
|
|
attr = tattr;
|
2003-01-14 21:28:23 +03:00
|
|
|
if (term->ucsdata->dbcs_screenfont)
|
2001-05-10 12:34:20 +04:00
|
|
|
last_run_dirty = dirty_run;
|
|
|
|
dirty_run = dirty_line;
|
|
|
|
}
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if ((tchar | tattr) != term->disptext[idx])
|
2001-05-10 12:34:20 +04:00
|
|
|
dirty_run = TRUE;
|
|
|
|
ch[ccount++] = (char) tchar;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptext[idx] = tchar | tattr;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
/* If it's a wide char step along to the next one. */
|
|
|
|
if (tattr & ATTR_WIDE) {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (++j < term->cols) {
|
2001-05-10 12:34:20 +04:00
|
|
|
idx++;
|
|
|
|
d++;
|
2003-01-02 19:20:29 +03:00
|
|
|
/*
|
|
|
|
* By construction above, the cursor should not
|
|
|
|
* be on the right-hand half of this character.
|
|
|
|
* Ever.
|
|
|
|
*/
|
|
|
|
assert(!(i == our_curs_y && j == our_curs_x));
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->disptext[idx] != *d)
|
2001-05-10 12:34:20 +04:00
|
|
|
dirty_run = TRUE;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->disptext[idx] = *d;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
}
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
|
|
|
if (dirty_run && ccount > 0) {
|
|
|
|
do_text(ctx, start, i, ch, ccount, attr, lattr);
|
|
|
|
updated_line = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cursor on this line ? (and changed) */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (i == our_curs_y && (term->curstype != cursor || updated_line)) {
|
2001-05-10 12:34:20 +04:00
|
|
|
ch[0] = (char) (cursor_background & CHAR_MASK);
|
|
|
|
attr = (cursor_background & ATTR_MASK) | cursor;
|
2003-01-02 19:20:29 +03:00
|
|
|
do_cursor(ctx, our_curs_x, i, ch, 1, attr, lattr);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->curstype = cursor;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-03-17 13:39:05 +03:00
|
|
|
/*
|
|
|
|
* Flick the switch that says if blinking things should be shown or hidden.
|
|
|
|
*/
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_blink(Terminal *term, int flg)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2000-03-17 13:39:05 +03:00
|
|
|
long now, blink_diff;
|
|
|
|
|
2002-10-07 20:45:23 +04:00
|
|
|
now = GETTICKCOUNT();
|
2002-10-22 20:11:33 +04:00
|
|
|
blink_diff = now - term->last_tblink;
|
2000-07-26 16:13:51 +04:00
|
|
|
|
2002-10-15 20:40:22 +04:00
|
|
|
/* Make sure the text blinks no more than 2Hz; we'll use 0.45 s period. */
|
|
|
|
if (blink_diff < 0 || blink_diff > (TICKSPERSEC * 9 / 20)) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->last_tblink = now;
|
|
|
|
term->tblinker = !term->tblinker;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
|
|
|
|
2000-03-17 13:39:05 +03:00
|
|
|
if (flg) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->blinker = 1;
|
|
|
|
term->last_blink = now;
|
2000-03-17 13:39:05 +03:00
|
|
|
return;
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
2000-03-17 13:39:05 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
blink_diff = now - term->last_blink;
|
2000-03-17 13:39:05 +03:00
|
|
|
|
2002-10-07 20:45:23 +04:00
|
|
|
/* Make sure the cursor blinks no faster than system blink rate */
|
|
|
|
if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
|
2001-05-06 18:35:20 +04:00
|
|
|
return;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
term->last_blink = now;
|
|
|
|
term->blinker = !term->blinker;
|
2000-03-17 13:39:05 +03:00
|
|
|
}
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Invalidate the whole screen so it will be repainted in full.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_invalidate(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
1999-01-08 16:02:13 +03:00
|
|
|
int i;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
for (i = 0; i < term->rows * (term->cols + 1); i++)
|
|
|
|
term->disptext[i] = ATTR_INVALID;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Paint the window in response to a WM_PAINT message.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_paint(Terminal *term, Context ctx,
|
2002-10-26 18:06:52 +04:00
|
|
|
int left, int top, int right, int bottom, int immediately)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-09-15 19:54:24 +04:00
|
|
|
int i, j;
|
|
|
|
if (left < 0) left = 0;
|
|
|
|
if (top < 0) top = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
if (right >= term->cols) right = term->cols-1;
|
|
|
|
if (bottom >= term->rows) bottom = term->rows-1;
|
|
|
|
|
|
|
|
for (i = top; i <= bottom && i < term->rows; i++) {
|
|
|
|
if ((term->disptext[i * (term->cols + 1) + term->cols] &
|
|
|
|
LATTR_MODE) == LATTR_NORM)
|
|
|
|
for (j = left; j <= right && j < term->cols; j++)
|
|
|
|
term->disptext[i * (term->cols + 1) + j] = ATTR_INVALID;
|
2000-07-26 16:13:51 +04:00
|
|
|
else
|
2002-10-22 20:11:33 +04:00
|
|
|
for (j = left / 2; j <= right / 2 + 1 && j < term->cols; j++)
|
|
|
|
term->disptext[i * (term->cols + 1) + j] = ATTR_INVALID;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2000-03-17 13:39:05 +03:00
|
|
|
/* This should happen soon enough, also for some reason it sometimes
|
|
|
|
* fails to actually do anything when re-sizing ... painting the wrong
|
|
|
|
* window perhaps ?
|
2001-05-06 18:35:20 +04:00
|
|
|
*/
|
2002-10-26 18:06:52 +04:00
|
|
|
if (immediately)
|
2002-10-22 20:11:33 +04:00
|
|
|
do_paint (term, ctx, FALSE);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attempt to scroll the scrollback. The second parameter gives the
|
|
|
|
* position we want to scroll to; the first is +1 to denote that
|
|
|
|
* this position is relative to the beginning of the scrollback, -1
|
|
|
|
* to denote it is relative to the end, and 0 to denote that it is
|
|
|
|
* relative to the current position.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_scroll(Terminal *term, int rel, int where)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2003-03-06 15:51:12 +03:00
|
|
|
int sbtop = -sblines(term);
|
2002-11-23 16:07:29 +03:00
|
|
|
#ifdef OPTIMISE_SCROLL
|
|
|
|
int olddisptop = term->disptop;
|
|
|
|
int shift;
|
|
|
|
#endif /* OPTIMISE_SCROLL */
|
2002-10-22 20:11:33 +04:00
|
|
|
|
|
|
|
term->disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : term->disptop) + where;
|
|
|
|
if (term->disptop < sbtop)
|
|
|
|
term->disptop = sbtop;
|
|
|
|
if (term->disptop > 0)
|
|
|
|
term->disptop = 0;
|
|
|
|
update_sbar(term);
|
2002-11-23 16:07:29 +03:00
|
|
|
#ifdef OPTIMISE_SCROLL
|
|
|
|
shift = (term->disptop - olddisptop);
|
|
|
|
if (shift < term->rows && shift > -term->rows)
|
|
|
|
scroll_display(term, 0, term->rows - 1, shift);
|
|
|
|
#endif /* OPTIMISE_SCROLL */
|
2002-10-22 20:11:33 +04:00
|
|
|
term_update(term);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2003-04-11 22:10:13 +04:00
|
|
|
static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-05-10 12:34:20 +04:00
|
|
|
wchar_t *workbuf;
|
|
|
|
wchar_t *wbptr; /* where next char goes within workbuf */
|
2001-10-31 21:50:09 +03:00
|
|
|
int old_top_x;
|
2001-05-06 18:35:20 +04:00
|
|
|
int wblen = 0; /* workbuf len */
|
|
|
|
int buflen; /* amount of memory allocated to workbuf */
|
2000-11-21 22:28:25 +03:00
|
|
|
|
2001-05-10 12:34:20 +04:00
|
|
|
buflen = 5120; /* Default size */
|
2003-03-29 19:14:26 +03:00
|
|
|
workbuf = snewn(buflen, wchar_t);
|
2001-05-10 12:34:20 +04:00
|
|
|
wbptr = workbuf; /* start filling here */
|
2001-10-31 21:50:09 +03:00
|
|
|
old_top_x = top.x; /* needed for rect==1 */
|
2000-11-21 22:28:25 +03:00
|
|
|
|
2001-04-17 01:25:13 +04:00
|
|
|
while (poslt(top, bottom)) {
|
2000-11-21 22:28:25 +03:00
|
|
|
int nl = FALSE;
|
2001-04-17 01:25:13 +04:00
|
|
|
unsigned long *ldata = lineptr(top.y);
|
2001-04-17 12:24:29 +04:00
|
|
|
pos nlpos;
|
2001-04-17 01:25:13 +04:00
|
|
|
|
2001-10-31 21:50:09 +03:00
|
|
|
/*
|
|
|
|
* 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...
|
|
|
|
*/
|
2001-04-17 01:25:13 +04:00
|
|
|
nlpos.y = top.y;
|
2002-10-22 20:11:33 +04:00
|
|
|
nlpos.x = term->cols;
|
2000-11-21 22:28:25 +03:00
|
|
|
|
2001-10-31 21:50:09 +03:00
|
|
|
/*
|
|
|
|
* ... 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)...
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (!(ldata[term->cols] & LATTR_WRAPPED)) {
|
2001-05-10 12:34:20 +04:00
|
|
|
while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
|
|
|
|
(DIRECT_CHAR(ldata[nlpos.x - 1]) &&
|
|
|
|
(ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
|
|
|
|
&& poslt(top, nlpos))
|
|
|
|
decpos(nlpos);
|
2001-04-17 01:25:13 +04:00
|
|
|
if (poslt(nlpos, bottom))
|
2000-11-21 22:28:25 +03:00
|
|
|
nl = TRUE;
|
2003-01-02 19:20:29 +03:00
|
|
|
} else if (ldata[term->cols] & LATTR_WRAPPED2) {
|
|
|
|
/* Ignore the last char on the line in a WRAPPED2 line. */
|
|
|
|
decpos(nlpos);
|
2000-11-21 22:28:25 +03:00
|
|
|
}
|
2001-10-31 21:50:09 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ... 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);
|
|
|
|
}
|
|
|
|
|
2001-04-17 01:25:13 +04:00
|
|
|
while (poslt(top, bottom) && poslt(top, nlpos)) {
|
2001-05-10 12:34:20 +04:00
|
|
|
#if 0
|
|
|
|
char cbuf[16], *p;
|
|
|
|
sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
|
|
|
|
#else
|
|
|
|
wchar_t cbuf[16], *p;
|
|
|
|
int uc = (ldata[top.x] & 0xFFFF);
|
|
|
|
int set, c;
|
|
|
|
|
|
|
|
if (uc == UCSWIDE) {
|
|
|
|
top.x++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (uc & CSET_MASK) {
|
|
|
|
case ATTR_LINEDRW:
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!term->cfg.rawcnp) {
|
2003-01-14 21:28:23 +03:00
|
|
|
uc = term->ucsdata->unitab_xterm[uc & 0xFF];
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
2001-05-06 18:35:20 +04:00
|
|
|
}
|
2001-05-10 12:34:20 +04:00
|
|
|
case ATTR_ASCII:
|
2003-01-14 21:28:23 +03:00
|
|
|
uc = term->ucsdata->unitab_line[uc & 0xFF];
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
2001-05-19 18:12:41 +04:00
|
|
|
case ATTR_SCOACS:
|
2003-01-14 21:28:23 +03:00
|
|
|
uc = term->ucsdata->unitab_scoacs[uc&0xFF];
|
2001-05-19 18:12:41 +04:00
|
|
|
break;
|
2001-04-09 15:59:35 +04:00
|
|
|
}
|
2001-05-10 12:34:20 +04:00
|
|
|
switch (uc & CSET_MASK) {
|
|
|
|
case ATTR_ACP:
|
2003-01-14 21:28:23 +03:00
|
|
|
uc = term->ucsdata->unitab_font[uc & 0xFF];
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case ATTR_OEMCP:
|
2003-01-14 21:28:23 +03:00
|
|
|
uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
set = (uc & CSET_MASK);
|
|
|
|
c = (uc & CHAR_MASK);
|
|
|
|
cbuf[0] = uc;
|
|
|
|
cbuf[1] = 0;
|
|
|
|
|
|
|
|
if (DIRECT_FONT(uc)) {
|
|
|
|
if (c >= ' ' && c != 0x7F) {
|
2002-11-17 04:56:01 +03:00
|
|
|
char buf[4];
|
2001-05-10 12:34:20 +04:00
|
|
|
WCHAR wbuf[4];
|
|
|
|
int rv;
|
2003-01-14 21:28:23 +03:00
|
|
|
if (is_dbcs_leadbyte(term->ucsdata->font_codepage, (BYTE) c)) {
|
2001-05-10 12:34:20 +04:00
|
|
|
buf[0] = c;
|
2002-12-18 14:51:09 +03:00
|
|
|
buf[1] = (char) (0xFF & ldata[top.x + 1]);
|
2003-01-14 21:28:23 +03:00
|
|
|
rv = mb_to_wc(term->ucsdata->font_codepage, 0, buf, 2, wbuf, 4);
|
2001-05-10 12:34:20 +04:00
|
|
|
top.x++;
|
|
|
|
} else {
|
|
|
|
buf[0] = c;
|
2003-01-14 21:28:23 +03:00
|
|
|
rv = mb_to_wc(term->ucsdata->font_codepage, 0, buf, 1, wbuf, 4);
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
2000-11-21 22:28:25 +03:00
|
|
|
|
2001-05-10 12:34:20 +04:00
|
|
|
if (rv > 0) {
|
|
|
|
memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
|
|
|
|
cbuf[rv] = 0;
|
2001-04-09 15:59:35 +04:00
|
|
|
}
|
|
|
|
}
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (p = cbuf; *p; p++) {
|
|
|
|
/* Enough overhead for trailing NL and nul */
|
|
|
|
if (wblen >= buflen - 16) {
|
2003-03-29 19:14:26 +03:00
|
|
|
buflen += 100;
|
|
|
|
workbuf = sresize(workbuf, buflen, wchar_t);
|
2001-05-10 12:34:20 +04:00
|
|
|
wbptr = workbuf + wblen;
|
|
|
|
}
|
|
|
|
wblen++;
|
|
|
|
*wbptr++ = *p;
|
2000-11-21 22:28:25 +03:00
|
|
|
}
|
2001-04-17 01:25:13 +04:00
|
|
|
top.x++;
|
2000-11-21 22:28:25 +03:00
|
|
|
}
|
|
|
|
if (nl) {
|
|
|
|
int i;
|
2001-05-10 12:34:20 +04:00
|
|
|
for (i = 0; i < sel_nl_sz; i++) {
|
2001-05-06 18:35:20 +04:00
|
|
|
wblen++;
|
2000-11-21 22:28:25 +03:00
|
|
|
*wbptr++ = sel_nl[i];
|
|
|
|
}
|
|
|
|
}
|
2001-04-17 01:25:13 +04:00
|
|
|
top.y++;
|
2001-10-31 21:50:09 +03:00
|
|
|
top.x = rect ? old_top_x : 0;
|
2000-11-21 22:28:25 +03:00
|
|
|
}
|
2002-10-13 15:24:25 +04:00
|
|
|
#if SELECTION_NUL_TERMINATED
|
2001-05-10 12:34:20 +04:00
|
|
|
wblen++;
|
|
|
|
*wbptr++ = 0;
|
2002-10-13 15:24:25 +04:00
|
|
|
#endif
|
2003-04-11 22:10:13 +04:00
|
|
|
write_clip(term->frontend, workbuf, wblen, desel); /* transfer to clipbd */
|
2001-05-06 18:35:20 +04:00
|
|
|
if (buflen > 0) /* indicates we allocated this buffer */
|
2000-11-21 22:28:25 +03:00
|
|
|
sfree(workbuf);
|
|
|
|
}
|
2001-05-10 12:34:20 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_copyall(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-04-17 01:25:13 +04:00
|
|
|
pos top;
|
2003-03-06 15:51:12 +03:00
|
|
|
top.y = -sblines(term);
|
2001-04-17 01:25:13 +04:00
|
|
|
top.x = 0;
|
2003-04-11 22:10:13 +04:00
|
|
|
clipme(term, top, term->curs, 0, TRUE);
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2002-10-22 20:11:33 +04:00
|
|
|
* The wordness array is mainly for deciding the disposition of the
|
|
|
|
* US-ASCII characters.
|
2001-05-10 12:34:20 +04:00
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static int wordtype(Terminal *term, int uc)
|
2001-05-10 12:34:20 +04:00
|
|
|
{
|
2002-10-26 14:16:19 +04:00
|
|
|
struct ucsword {
|
2001-05-10 12:34:20 +04:00
|
|
|
int start, end, ctype;
|
2002-10-26 14:16:19 +04:00
|
|
|
};
|
|
|
|
static const struct ucsword ucs_words[] = {
|
2001-05-10 12:34:20 +04:00
|
|
|
{
|
|
|
|
128, 160, 0}, {
|
|
|
|
161, 191, 1}, {
|
|
|
|
215, 215, 1}, {
|
|
|
|
247, 247, 1}, {
|
|
|
|
0x037e, 0x037e, 1}, /* Greek question mark */
|
|
|
|
{
|
|
|
|
0x0387, 0x0387, 1}, /* Greek ano teleia */
|
|
|
|
{
|
|
|
|
0x055a, 0x055f, 1}, /* Armenian punctuation */
|
|
|
|
{
|
|
|
|
0x0589, 0x0589, 1}, /* Armenian full stop */
|
|
|
|
{
|
|
|
|
0x0700, 0x070d, 1}, /* Syriac punctuation */
|
|
|
|
{
|
|
|
|
0x104a, 0x104f, 1}, /* Myanmar punctuation */
|
|
|
|
{
|
|
|
|
0x10fb, 0x10fb, 1}, /* Georgian punctuation */
|
|
|
|
{
|
|
|
|
0x1361, 0x1368, 1}, /* Ethiopic punctuation */
|
|
|
|
{
|
|
|
|
0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
|
|
|
|
{
|
|
|
|
0x17d4, 0x17dc, 1}, /* Khmer punctuation */
|
|
|
|
{
|
|
|
|
0x1800, 0x180a, 1}, /* Mongolian punctuation */
|
|
|
|
{
|
|
|
|
0x2000, 0x200a, 0}, /* Various spaces */
|
|
|
|
{
|
|
|
|
0x2070, 0x207f, 2}, /* superscript */
|
|
|
|
{
|
|
|
|
0x2080, 0x208f, 2}, /* subscript */
|
|
|
|
{
|
|
|
|
0x200b, 0x27ff, 1}, /* punctuation and symbols */
|
|
|
|
{
|
|
|
|
0x3000, 0x3000, 0}, /* ideographic space */
|
|
|
|
{
|
|
|
|
0x3001, 0x3020, 1}, /* ideographic punctuation */
|
|
|
|
{
|
|
|
|
0x303f, 0x309f, 3}, /* Hiragana */
|
|
|
|
{
|
|
|
|
0x30a0, 0x30ff, 3}, /* Katakana */
|
|
|
|
{
|
|
|
|
0x3300, 0x9fff, 3}, /* CJK Ideographs */
|
|
|
|
{
|
|
|
|
0xac00, 0xd7a3, 3}, /* Hangul Syllables */
|
|
|
|
{
|
|
|
|
0xf900, 0xfaff, 3}, /* CJK Ideographs */
|
|
|
|
{
|
|
|
|
0xfe30, 0xfe6b, 1}, /* punctuation forms */
|
|
|
|
{
|
|
|
|
0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
|
|
|
|
{
|
|
|
|
0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
|
|
|
|
{
|
|
|
|
0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
|
|
|
|
{
|
|
|
|
0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
|
|
|
|
{
|
|
|
|
0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
|
|
|
|
{
|
|
|
|
0, 0, 0}
|
|
|
|
};
|
2002-10-26 14:16:19 +04:00
|
|
|
const struct ucsword *wptr;
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
uc &= (CSET_MASK | CHAR_MASK);
|
|
|
|
|
|
|
|
switch (uc & CSET_MASK) {
|
|
|
|
case ATTR_LINEDRW:
|
2003-01-14 21:28:23 +03:00
|
|
|
uc = term->ucsdata->unitab_xterm[uc & 0xFF];
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case ATTR_ASCII:
|
2003-01-14 21:28:23 +03:00
|
|
|
uc = term->ucsdata->unitab_line[uc & 0xFF];
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
2001-05-19 18:12:41 +04:00
|
|
|
case ATTR_SCOACS:
|
2003-01-14 21:28:23 +03:00
|
|
|
uc = term->ucsdata->unitab_scoacs[uc&0xFF];
|
2001-05-19 18:12:41 +04:00
|
|
|
break;
|
2001-05-10 12:34:20 +04:00
|
|
|
}
|
|
|
|
switch (uc & CSET_MASK) {
|
|
|
|
case ATTR_ACP:
|
2003-01-14 21:28:23 +03:00
|
|
|
uc = term->ucsdata->unitab_font[uc & 0xFF];
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
case ATTR_OEMCP:
|
2003-01-14 21:28:23 +03:00
|
|
|
uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
|
2001-05-10 12:34:20 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2001-09-15 19:54:24 +04:00
|
|
|
/* 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. :-(
|
|
|
|
*/
|
2003-01-14 21:28:23 +03:00
|
|
|
if (term->ucsdata->dbcs_screenfont &&
|
|
|
|
term->ucsdata->font_codepage == term->ucsdata->line_codepage)
|
2001-09-15 19:54:24 +04:00
|
|
|
return (uc != ' ');
|
|
|
|
|
2001-05-10 12:34:20 +04:00
|
|
|
if (uc < 0x80)
|
2002-10-22 20:11:33 +04:00
|
|
|
return term->wordness[uc];
|
2001-05-10 12:34:20 +04:00
|
|
|
|
|
|
|
for (wptr = ucs_words; wptr->start; wptr++) {
|
|
|
|
if (uc >= wptr->start && uc <= wptr->end)
|
|
|
|
return wptr->ctype;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 2;
|
2000-11-21 22:28:25 +03:00
|
|
|
}
|
|
|
|
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* Spread the selection outwards according to the selection mode.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
static pos sel_spread_half(Terminal *term, pos p, int dir)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-04-17 01:25:13 +04:00
|
|
|
unsigned long *ldata;
|
1999-01-08 16:02:13 +03:00
|
|
|
short wvalue;
|
2003-03-06 15:51:12 +03:00
|
|
|
int topy = -sblines(term);
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2001-04-17 01:25:13 +04:00
|
|
|
ldata = lineptr(p.y);
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
switch (term->selmode) {
|
1999-01-08 16:02:13 +03:00
|
|
|
case SM_CHAR:
|
|
|
|
/*
|
|
|
|
* In this mode, every character is a separate unit, except
|
|
|
|
* for runs of spaces at the end of a non-wrapping line.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (!(ldata[term->cols] & LATTR_WRAPPED)) {
|
|
|
|
unsigned long *q = ldata + term->cols;
|
2001-04-17 01:25:13 +04:00
|
|
|
while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
|
1999-01-08 16:02:13 +03:00
|
|
|
q--;
|
2002-10-22 20:11:33 +04:00
|
|
|
if (q == ldata + term->cols)
|
1999-01-08 16:02:13 +03:00
|
|
|
q--;
|
2001-05-06 18:35:20 +04:00
|
|
|
if (p.x >= q - ldata)
|
2002-10-22 20:11:33 +04:00
|
|
|
p.x = (dir == -1 ? q - ldata : term->cols - 1);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SM_WORD:
|
|
|
|
/*
|
|
|
|
* In this mode, the units are maximal runs of characters
|
|
|
|
* whose `wordness' has the same value.
|
|
|
|
*/
|
2003-01-02 19:20:29 +03:00
|
|
|
wvalue = wordtype(term, UCSGET(ldata, p.x));
|
1999-01-08 16:02:13 +03:00
|
|
|
if (dir == +1) {
|
2001-10-31 01:02:15 +03:00
|
|
|
while (1) {
|
2003-01-02 19:20:29 +03:00
|
|
|
int maxcols = (ldata[term->cols] & LATTR_WRAPPED2 ?
|
|
|
|
term->cols-1 : term->cols);
|
|
|
|
if (p.x < maxcols-1) {
|
|
|
|
if (wordtype(term, UCSGET(ldata, p.x + 1)) == wvalue)
|
2001-10-31 01:02:15 +03:00
|
|
|
p.x++;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
} else {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (ldata[term->cols] & LATTR_WRAPPED) {
|
2001-10-31 01:02:15 +03:00
|
|
|
unsigned long *ldata2;
|
|
|
|
ldata2 = lineptr(p.y+1);
|
2003-01-02 19:20:29 +03:00
|
|
|
if (wordtype(term, UCSGET(ldata2, 0)) == wvalue) {
|
2001-10-31 01:02:15 +03:00
|
|
|
p.x = 0;
|
|
|
|
p.y++;
|
|
|
|
ldata = ldata2;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
} else {
|
2001-10-31 01:02:15 +03:00
|
|
|
while (1) {
|
|
|
|
if (p.x > 0) {
|
2003-01-02 19:20:29 +03:00
|
|
|
if (wordtype(term, UCSGET(ldata, p.x - 1)) == wvalue)
|
2001-10-31 01:02:15 +03:00
|
|
|
p.x--;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
unsigned long *ldata2;
|
2003-01-02 19:20:29 +03:00
|
|
|
int maxcols;
|
2001-10-31 01:02:15 +03:00
|
|
|
if (p.y <= topy)
|
|
|
|
break;
|
|
|
|
ldata2 = lineptr(p.y-1);
|
2003-01-02 19:20:29 +03:00
|
|
|
maxcols = (ldata2[term->cols] & LATTR_WRAPPED2 ?
|
|
|
|
term->cols-1 : term->cols);
|
|
|
|
if (ldata2[term->cols] & LATTR_WRAPPED) {
|
|
|
|
if (wordtype(term, UCSGET(ldata2, maxcols-1))
|
|
|
|
== wvalue) {
|
|
|
|
p.x = maxcols-1;
|
|
|
|
p.y--;
|
|
|
|
ldata = ldata2;
|
|
|
|
} else
|
|
|
|
break;
|
2001-10-31 01:02:15 +03:00
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SM_LINE:
|
|
|
|
/*
|
|
|
|
* In this mode, every line is a unit.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
p.x = (dir == -1 ? 0 : term->cols - 1);
|
1999-01-08 16:02:13 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
static void sel_spread(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->seltype == LEXICOGRAPHIC) {
|
|
|
|
term->selstart = sel_spread_half(term, term->selstart, -1);
|
|
|
|
decpos(term->selend);
|
|
|
|
term->selend = sel_spread_half(term, term->selend, +1);
|
|
|
|
incpos(term->selend);
|
2001-10-31 21:50:09 +03:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_do_paste(Terminal *term)
|
2001-05-17 14:06:54 +04:00
|
|
|
{
|
|
|
|
wchar_t *data;
|
|
|
|
int len;
|
|
|
|
|
2002-10-26 16:58:13 +04:00
|
|
|
get_clip(term->frontend, &data, &len);
|
2002-10-20 17:23:30 +04:00
|
|
|
if (data && len > 0) {
|
2001-05-17 14:06:54 +04:00
|
|
|
wchar_t *p, *q;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
term_seen_key_event(term); /* pasted data counts */
|
2002-10-20 17:23:30 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->paste_buffer)
|
|
|
|
sfree(term->paste_buffer);
|
|
|
|
term->paste_pos = term->paste_hold = term->paste_len = 0;
|
2003-03-29 19:14:26 +03:00
|
|
|
term->paste_buffer = snewn(len, wchar_t);
|
2001-05-17 14:06:54 +04:00
|
|
|
|
|
|
|
p = q = data;
|
|
|
|
while (p < data + len) {
|
|
|
|
while (p < data + len &&
|
|
|
|
!(p <= data + len - sel_nl_sz &&
|
|
|
|
!memcmp(p, sel_nl, sizeof(sel_nl))))
|
|
|
|
p++;
|
|
|
|
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < p - q; i++) {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->paste_buffer[term->paste_len++] = q[i];
|
2001-05-17 14:06:54 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p <= data + len - sel_nl_sz &&
|
|
|
|
!memcmp(p, sel_nl, sizeof(sel_nl))) {
|
2002-11-19 02:49:30 +03:00
|
|
|
term->paste_buffer[term->paste_len++] = '\015';
|
2001-05-17 14:06:54 +04:00
|
|
|
p += sel_nl_sz;
|
|
|
|
}
|
|
|
|
q = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assume a small paste will be OK in one go. */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->paste_len < 256) {
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc)
|
|
|
|
luni_send(term->ldisc, term->paste_buffer, term->paste_len, 0);
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->paste_buffer)
|
|
|
|
sfree(term->paste_buffer);
|
|
|
|
term->paste_buffer = 0;
|
|
|
|
term->paste_pos = term->paste_hold = term->paste_len = 0;
|
2001-05-17 14:06:54 +04:00
|
|
|
}
|
|
|
|
}
|
2002-10-26 16:58:13 +04:00
|
|
|
get_clip(term->frontend, NULL, NULL);
|
2001-05-17 14:06:54 +04:00
|
|
|
}
|
|
|
|
|
2003-01-25 19:16:45 +03:00
|
|
|
void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
|
|
|
|
Mouse_Action a, int x, int y, int shift, int ctrl, int alt)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2001-04-17 01:25:13 +04:00
|
|
|
pos selpoint;
|
|
|
|
unsigned long *ldata;
|
2002-10-22 20:11:33 +04:00
|
|
|
int raw_mouse = (term->xterm_mouse &&
|
2003-01-12 17:30:02 +03:00
|
|
|
!term->cfg.no_mouse_rep &&
|
|
|
|
!(term->cfg.mouse_override && shift));
|
2001-10-31 21:50:09 +03:00
|
|
|
int default_seltype;
|
2001-05-06 18:35:20 +04:00
|
|
|
|
2001-09-08 16:37:48 +04:00
|
|
|
if (y < 0) {
|
2001-05-06 18:35:20 +04:00
|
|
|
y = 0;
|
2001-09-08 16:37:48 +04:00
|
|
|
if (a == MA_DRAG && !raw_mouse)
|
2002-10-22 20:11:33 +04:00
|
|
|
term_scroll(term, 0, -1);
|
2001-09-08 16:37:48 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (y >= term->rows) {
|
|
|
|
y = term->rows - 1;
|
2001-09-08 16:37:48 +04:00
|
|
|
if (a == MA_DRAG && !raw_mouse)
|
2002-10-22 20:11:33 +04:00
|
|
|
term_scroll(term, 0, +1);
|
2001-09-08 16:37:48 +04:00
|
|
|
}
|
2001-05-06 18:35:20 +04:00
|
|
|
if (x < 0) {
|
|
|
|
if (y > 0) {
|
2002-10-22 20:11:33 +04:00
|
|
|
x = term->cols - 1;
|
2001-05-06 18:35:20 +04:00
|
|
|
y--;
|
|
|
|
} else
|
|
|
|
x = 0;
|
1999-01-22 12:36:21 +03:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (x >= term->cols)
|
|
|
|
x = term->cols - 1;
|
1999-01-08 16:10:19 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
selpoint.y = y + term->disptop;
|
2001-04-17 01:25:13 +04:00
|
|
|
selpoint.x = x;
|
|
|
|
ldata = lineptr(selpoint.y);
|
2002-10-22 20:11:33 +04:00
|
|
|
if ((ldata[term->cols] & LATTR_MODE) != LATTR_NORM)
|
2001-04-17 01:25:13 +04:00
|
|
|
selpoint.x /= 2;
|
1999-01-08 16:02:13 +03:00
|
|
|
|
2001-09-08 16:37:48 +04:00
|
|
|
if (raw_mouse) {
|
2001-05-06 18:20:41 +04:00
|
|
|
int encstate = 0, r, c;
|
|
|
|
char abuf[16];
|
|
|
|
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc) {
|
|
|
|
|
2003-01-25 19:16:45 +03:00
|
|
|
switch (braw) {
|
2002-10-26 14:16:19 +04:00
|
|
|
case MBT_LEFT:
|
|
|
|
encstate = 0x20; /* left button down */
|
|
|
|
break;
|
|
|
|
case MBT_MIDDLE:
|
|
|
|
encstate = 0x21;
|
|
|
|
break;
|
|
|
|
case MBT_RIGHT:
|
|
|
|
encstate = 0x22;
|
|
|
|
break;
|
|
|
|
case MBT_WHEEL_UP:
|
|
|
|
encstate = 0x60;
|
|
|
|
break;
|
|
|
|
case MBT_WHEEL_DOWN:
|
|
|
|
encstate = 0x61;
|
|
|
|
break;
|
|
|
|
default: break; /* placate gcc warning about enum use */
|
|
|
|
}
|
|
|
|
switch (a) {
|
|
|
|
case MA_DRAG:
|
|
|
|
if (term->xterm_mouse == 1)
|
|
|
|
return;
|
|
|
|
encstate += 0x20;
|
|
|
|
break;
|
|
|
|
case MA_RELEASE:
|
|
|
|
encstate = 0x23;
|
|
|
|
term->mouse_is_down = 0;
|
|
|
|
break;
|
|
|
|
case MA_CLICK:
|
2003-01-25 19:16:45 +03:00
|
|
|
if (term->mouse_is_down == braw)
|
2002-10-26 14:16:19 +04:00
|
|
|
return;
|
2003-01-25 19:16:45 +03:00
|
|
|
term->mouse_is_down = braw;
|
2002-10-26 14:16:19 +04:00
|
|
|
break;
|
|
|
|
default: break; /* placate gcc warning about enum use */
|
|
|
|
}
|
|
|
|
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);
|
2001-05-06 18:20:41 +04:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2001-10-31 21:50:09 +03:00
|
|
|
/*
|
|
|
|
* Set the selection type (rectangular or normal) at the start
|
|
|
|
* of a selection attempt, from the state of Alt.
|
|
|
|
*/
|
2003-01-12 17:30:02 +03:00
|
|
|
if (!alt ^ !term->cfg.rect_select)
|
2001-10-31 21:50:09 +03:00
|
|
|
default_seltype = RECTANGULAR;
|
|
|
|
else
|
|
|
|
default_seltype = LEXICOGRAPHIC;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->selstate == NO_SELECTION) {
|
|
|
|
term->seltype = default_seltype;
|
2001-10-31 21:50:09 +03:00
|
|
|
}
|
|
|
|
|
2003-01-25 19:16:45 +03:00
|
|
|
if (bcooked == MBT_SELECT && a == MA_CLICK) {
|
2002-10-22 20:11:33 +04:00
|
|
|
deselect(term);
|
|
|
|
term->selstate = ABOUT_TO;
|
|
|
|
term->seltype = default_seltype;
|
|
|
|
term->selanchor = selpoint;
|
|
|
|
term->selmode = SM_CHAR;
|
2003-01-25 19:16:45 +03:00
|
|
|
} else if (bcooked == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
|
2002-10-22 20:11:33 +04:00
|
|
|
deselect(term);
|
|
|
|
term->selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
|
|
|
|
term->selstate = DRAGGING;
|
|
|
|
term->selstart = term->selanchor = selpoint;
|
|
|
|
term->selend = term->selstart;
|
|
|
|
incpos(term->selend);
|
|
|
|
sel_spread(term);
|
2003-01-25 19:16:45 +03:00
|
|
|
} else if ((bcooked == MBT_SELECT && a == MA_DRAG) ||
|
|
|
|
(bcooked == MBT_EXTEND && a != MA_RELEASE)) {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint))
|
1999-01-08 16:02:13 +03:00
|
|
|
return;
|
2003-01-25 19:16:45 +03:00
|
|
|
if (bcooked == MBT_EXTEND && a != MA_DRAG &&
|
|
|
|
term->selstate == SELECTED) {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->seltype == LEXICOGRAPHIC) {
|
2001-10-31 21:50:09 +03:00
|
|
|
/*
|
|
|
|
* For normal selection, we extend by moving
|
|
|
|
* whichever end of the current selection is closer
|
|
|
|
* to the mouse.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (posdiff(selpoint, term->selstart) <
|
|
|
|
posdiff(term->selend, term->selstart) / 2) {
|
|
|
|
term->selanchor = term->selend;
|
|
|
|
decpos(term->selanchor);
|
2001-10-31 21:50:09 +03:00
|
|
|
} else {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selanchor = term->selstart;
|
2001-10-31 21:50:09 +03:00
|
|
|
}
|
2001-04-17 01:25:13 +04:00
|
|
|
} else {
|
2001-10-31 21:50:09 +03:00
|
|
|
/*
|
|
|
|
* For rectangular selection, we have a choice of
|
|
|
|
* _four_ places to put selanchor and selpoint: the
|
|
|
|
* four corners of the selection.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (2*selpoint.x < term->selstart.x + term->selend.x)
|
|
|
|
term->selanchor.x = term->selend.x-1;
|
2001-10-31 21:50:09 +03:00
|
|
|
else
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selanchor.x = term->selstart.x;
|
2001-10-31 21:50:09 +03:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (2*selpoint.y < term->selstart.y + term->selend.y)
|
|
|
|
term->selanchor.y = term->selend.y;
|
2001-10-31 21:50:09 +03:00
|
|
|
else
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selanchor.y = term->selstart.y;
|
2001-04-17 01:25:13 +04:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selstate = DRAGGING;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->selstate != ABOUT_TO && term->selstate != DRAGGING)
|
|
|
|
term->selanchor = selpoint;
|
|
|
|
term->selstate = DRAGGING;
|
|
|
|
if (term->seltype == LEXICOGRAPHIC) {
|
2001-10-31 21:50:09 +03:00
|
|
|
/*
|
|
|
|
* For normal selection, we set (selstart,selend) to
|
|
|
|
* (selpoint,selanchor) in some order.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
if (poslt(selpoint, term->selanchor)) {
|
|
|
|
term->selstart = selpoint;
|
|
|
|
term->selend = term->selanchor;
|
|
|
|
incpos(term->selend);
|
2001-10-31 21:50:09 +03:00
|
|
|
} else {
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selstart = term->selanchor;
|
|
|
|
term->selend = selpoint;
|
|
|
|
incpos(term->selend);
|
2001-10-31 21:50:09 +03:00
|
|
|
}
|
1999-01-08 16:02:13 +03:00
|
|
|
} else {
|
2001-10-31 21:50:09 +03:00
|
|
|
/*
|
|
|
|
* 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).
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selstart.x = min(term->selanchor.x, selpoint.x);
|
|
|
|
term->selend.x = 1+max(term->selanchor.x, selpoint.x);
|
|
|
|
term->selstart.y = min(term->selanchor.y, selpoint.y);
|
|
|
|
term->selend.y = max(term->selanchor.y, selpoint.y);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
sel_spread(term);
|
2003-01-25 19:16:45 +03:00
|
|
|
} else if ((bcooked == MBT_SELECT || bcooked == MBT_EXTEND) &&
|
|
|
|
a == MA_RELEASE) {
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->selstate == DRAGGING) {
|
1999-01-08 16:02:13 +03:00
|
|
|
/*
|
|
|
|
* We've completed a selection. We now transfer the
|
|
|
|
* data to the clipboard.
|
|
|
|
*/
|
2002-10-22 20:11:33 +04:00
|
|
|
clipme(term, term->selstart, term->selend,
|
2003-04-11 22:10:13 +04:00
|
|
|
(term->seltype == RECTANGULAR), FALSE);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selstate = SELECTED;
|
1999-01-08 16:02:13 +03:00
|
|
|
} else
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selstate = NO_SELECTION;
|
2003-01-25 19:16:45 +03:00
|
|
|
} else if (bcooked == MBT_PASTE
|
2002-10-13 15:24:25 +04:00
|
|
|
&& (a == MA_CLICK
|
|
|
|
#if MULTICLICK_ONLY_EVENT
|
|
|
|
|| a == MA_2CLK || a == MA_3CLK
|
|
|
|
#endif
|
|
|
|
)) {
|
2002-10-26 16:58:13 +04:00
|
|
|
request_paste(term->frontend);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
term_update(term);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2003-01-27 03:39:01 +03:00
|
|
|
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;
|
2003-01-28 03:17:17 +03:00
|
|
|
#if 0
|
2003-01-27 03:39:01 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
fprintf(stderr, "keysym = %d, %d chars:", keysym, tlen);
|
|
|
|
for (i = 0; i < tlen; i++)
|
2003-02-01 15:28:43 +03:00
|
|
|
fprintf(stderr, " %04x", (unsigned)text[i]);
|
2003-01-27 03:39:01 +03:00
|
|
|
fprintf(stderr, "\n");
|
2003-01-28 03:17:17 +03:00
|
|
|
#endif
|
2003-01-27 03:39:01 +03:00
|
|
|
|
|
|
|
/* 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 */
|
2003-02-01 15:28:43 +03:00
|
|
|
if ((text[0] >= 0x40 && text[0] <= 0x5f) ||
|
|
|
|
(text[0] >= 0x61 && text[0] <= 0x7a))
|
2003-01-27 03:39:01 +03:00
|
|
|
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;
|
2003-02-01 15:28:43 +03:00
|
|
|
default: break; /* else gcc warns `enum value not used' */
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
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;
|
2003-02-01 15:28:43 +03:00
|
|
|
default: break; /* else gcc warns `enum value not used' */
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
2003-02-01 15:28:43 +03:00
|
|
|
default: break; /* else gcc warns `enum value not used' */
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
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;
|
2003-02-01 15:28:43 +03:00
|
|
|
default: break; /* else gcc warns `enum value not used' */
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
2003-02-01 15:28:43 +03:00
|
|
|
default: break; /* else gcc warns `enum value not used' */
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
2003-02-01 15:28:43 +03:00
|
|
|
default: break; /* else gcc warns `enum value not used' */
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
2003-02-01 15:28:43 +03:00
|
|
|
default: break; /* else gcc warns `enum value not used' */
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
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;
|
2003-02-01 15:28:43 +03:00
|
|
|
default: break; /* else gcc warns `enum value not used' */
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
2003-04-23 17:48:09 +04:00
|
|
|
default: xkey=0; break; /* else gcc warns `enum value not used'*/
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
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;
|
2003-04-23 17:48:09 +04:00
|
|
|
default: code = 0; break; /* else gcc warns `enum value not used' */
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
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 */
|
2003-05-10 12:37:54 +04:00
|
|
|
default: xkey = 0; break; /* else gcc warns `enum value not used' */
|
2003-01-27 03:39:01 +03:00
|
|
|
}
|
|
|
|
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) {
|
2003-01-28 03:17:17 +03:00
|
|
|
#if 0
|
2003-01-27 03:39:01 +03:00
|
|
|
fprintf(stderr, "sending ESC\n");
|
2003-01-28 03:17:17 +03:00
|
|
|
#endif
|
2003-01-27 03:39:01 +03:00
|
|
|
ldisc_send(term->ldisc, "\x1b", 1, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p > output) {
|
2003-01-28 03:17:17 +03:00
|
|
|
#if 0
|
2003-01-27 03:39:01 +03:00
|
|
|
fprintf(stderr, "sending %d bytes:", p - output);
|
|
|
|
for (i = 0; i < p - output; i++)
|
|
|
|
fprintf(stderr, " %02x", output[i]);
|
|
|
|
fprintf(stderr, "\n");
|
2003-01-28 03:17:17 +03:00
|
|
|
#endif
|
2003-01-27 03:39:01 +03:00
|
|
|
ldisc_send(term->ldisc, output, p - output, 1);
|
|
|
|
} else if (tlen > 0) {
|
2003-01-28 03:17:17 +03:00
|
|
|
#if 0
|
2003-01-27 03:39:01 +03:00
|
|
|
fprintf(stderr, "sending %d unichars:", tlen);
|
|
|
|
for (i = 0; i < tlen; i++)
|
2003-02-01 15:28:43 +03:00
|
|
|
fprintf(stderr, " %04x", (unsigned) text[i]);
|
2003-01-27 03:39:01 +03:00
|
|
|
fprintf(stderr, "\n");
|
2003-01-28 03:17:17 +03:00
|
|
|
#endif
|
2003-01-27 03:39:01 +03:00
|
|
|
luni_send(term->ldisc, text, tlen, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_nopaste(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->paste_len == 0)
|
2001-05-06 18:35:20 +04:00
|
|
|
return;
|
2002-10-22 20:11:33 +04:00
|
|
|
sfree(term->paste_buffer);
|
2002-10-23 16:41:35 +04:00
|
|
|
term->paste_buffer = NULL;
|
2002-10-22 20:11:33 +04:00
|
|
|
term->paste_len = 0;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
int term_paste_pending(Terminal *term)
|
2002-10-15 03:32:00 +04:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
return term->paste_len != 0;
|
2002-10-15 03:32:00 +04:00
|
|
|
}
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_paste(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2000-07-26 16:13:51 +04:00
|
|
|
long now, paste_diff;
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->paste_len == 0)
|
2001-05-06 18:35:20 +04:00
|
|
|
return;
|
2000-07-26 16:13:51 +04:00
|
|
|
|
|
|
|
/* Don't wait forever to paste */
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->paste_hold) {
|
2002-10-07 20:45:23 +04:00
|
|
|
now = GETTICKCOUNT();
|
2002-10-22 20:11:33 +04:00
|
|
|
paste_diff = now - term->last_paste;
|
2001-05-06 18:35:20 +04:00
|
|
|
if (paste_diff >= 0 && paste_diff < 450)
|
2000-07-26 16:13:51 +04:00
|
|
|
return;
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
term->paste_hold = 0;
|
2000-07-26 16:13:51 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
while (term->paste_pos < term->paste_len) {
|
2000-10-23 14:32:37 +04:00
|
|
|
int n = 0;
|
2002-10-22 20:11:33 +04:00
|
|
|
while (n + term->paste_pos < term->paste_len) {
|
2002-11-19 02:49:30 +03:00
|
|
|
if (term->paste_buffer[term->paste_pos + n++] == '\015')
|
2000-10-23 14:32:37 +04:00
|
|
|
break;
|
|
|
|
}
|
2002-10-26 14:16:19 +04:00
|
|
|
if (term->ldisc)
|
|
|
|
luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, 0);
|
2002-10-22 20:11:33 +04:00
|
|
|
term->paste_pos += n;
|
2000-07-26 16:13:51 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
if (term->paste_pos < term->paste_len) {
|
|
|
|
term->paste_hold = 1;
|
2000-07-26 16:13:51 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2002-10-22 20:11:33 +04:00
|
|
|
sfree(term->paste_buffer);
|
|
|
|
term->paste_buffer = NULL;
|
|
|
|
term->paste_len = 0;
|
2000-07-26 16:13:51 +04:00
|
|
|
}
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
static void deselect(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
term->selstate = NO_SELECTION;
|
|
|
|
term->selstart.x = term->selstart.y = term->selend.x = term->selend.y = 0;
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
void term_deselect(Terminal *term)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2002-10-22 20:11:33 +04:00
|
|
|
deselect(term);
|
|
|
|
term_update(term);
|
1999-01-08 16:02:13 +03:00
|
|
|
}
|
2000-10-20 17:51:46 +04:00
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
int term_ldisc(Terminal *term, int option)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
|
|
|
if (option == LD_ECHO)
|
2002-10-22 20:11:33 +04:00
|
|
|
return term->term_echoing;
|
2001-05-06 18:35:20 +04:00
|
|
|
if (option == LD_EDIT)
|
2002-10-22 20:11:33 +04:00
|
|
|
return term->term_editing;
|
2001-01-24 17:08:20 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
Rationalisation of the system of frontend handles. Most modular bits
of PuTTY (terminal, backend, logctx etc) take a `void *' handle
passed to them from the frontend, and used as a context for all
their callbacks. Most of these point at the frontend structure
itself (on platforms where this is meaningful), except that the
handle passed to the backend has always pointed at the terminal
because from_backend() was implemented in terminal.c. This has
finally bitten Unix PuTTY, because both backend and logctx have
been passing their respective and very different frontend handles to
logevent(), so I've fixed it.
from_backend() is now a function supplied by the _frontend_ itself,
in all cases, and the frontend handle passed to backends must be the
same as that passed to everything else. What was from_backend() in
terminal.c is now called term_data(), and the typical implementation
of from_backend() in a GUI frontend will just extract the terminal
handle from the frontend structure and delegate to that.
This appears to work on Unix and Windows, but has most likely broken
the Mac build.
[originally from svn r3100]
2003-04-11 22:36:27 +04:00
|
|
|
int term_data(Terminal *term, int is_stderr, const char *data, int len)
|
2001-05-06 18:35:20 +04:00
|
|
|
{
|
2002-03-01 16:17:45 +03:00
|
|
|
assert(len > 0);
|
|
|
|
|
2002-10-22 20:11:33 +04:00
|
|
|
bufchain_add(&term->inbuf, data, len);
|
2001-08-25 21:09:23 +04:00
|
|
|
|
2003-03-29 21:30:14 +03:00
|
|
|
if (!term->in_term_out) {
|
|
|
|
term->in_term_out = TRUE;
|
|
|
|
term_blink(term, 1);
|
|
|
|
term_out(term);
|
|
|
|
term->in_term_out = FALSE;
|
|
|
|
}
|
|
|
|
|
2001-08-25 21:09:23 +04:00
|
|
|
/*
|
2001-09-18 23:41:07 +04:00
|
|
|
* 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.
|
|
|
|
*
|
2001-08-25 21:09:23 +04:00
|
|
|
* 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
|
|
|
|
* while the terminal processing was going slowly - but we
|
|
|
|
* can't do the 100% right thing without moving the terminal
|
|
|
|
* processing into a separate thread, and that might hurt
|
|
|
|
* 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.
|
2001-09-18 23:41:07 +04:00
|
|
|
*
|
2001-08-25 21:09:23 +04:00
|
|
|
* In practice, I can't imagine this causing serious trouble.
|
|
|
|
*/
|
|
|
|
return 0;
|
2000-10-20 17:51:46 +04:00
|
|
|
}
|
2002-10-26 16:58:13 +04:00
|
|
|
|
|
|
|
void term_provide_logctx(Terminal *term, void *logctx)
|
|
|
|
{
|
|
|
|
term->logctx = logctx;
|
|
|
|
}
|