27856896b0a4be6ffbff6cba3de36fa8a595623e
[u/mdw/putty] / terminal.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4
5 #include <time.h>
6 #include <assert.h>
7 #include "putty.h"
8 #include "tree234.h"
9
10 #define VT52_PLUS
11
12 #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
13 #define CL_VT100 0x0002 /* VT100 */
14 #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
15 #define CL_VT102 0x0008 /* VT102 */
16 #define CL_VT220 0x0010 /* VT220 */
17 #define CL_VT320 0x0020 /* VT320 */
18 #define CL_VT420 0x0040 /* VT420 */
19 #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
20 #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
21 #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
22 #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
23 #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
24
25 #define TM_VT100 (CL_ANSIMIN|CL_VT100)
26 #define TM_VT100AVO (TM_VT100|CL_VT100AVO)
27 #define TM_VT102 (TM_VT100AVO|CL_VT102)
28 #define TM_VT220 (TM_VT102|CL_VT220)
29 #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
30 #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
31
32 #define TM_PUTTY (0xFFFF)
33
34 #define compatibility(x) \
35 if ( ((CL_##x)&compatibility_level) == 0 ) { \
36 termstate=TOPLEVEL; \
37 break; \
38 }
39 #define compatibility2(x,y) \
40 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
41 termstate=TOPLEVEL; \
42 break; \
43 }
44
45 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
46
47 static int compatibility_level = TM_PUTTY;
48
49 static tree234 *scrollback; /* lines scrolled off top of screen */
50 static tree234 *screen; /* lines on primary screen */
51 static tree234 *alt_screen; /* lines on alternate screen */
52 static int disptop; /* distance scrolled back (0 or -ve) */
53
54 static unsigned long *cpos; /* cursor position (convenience) */
55
56 static unsigned long *disptext; /* buffer of text on real screen */
57 static unsigned long *dispcurs; /* location of cursor on real screen */
58 static unsigned long curstype; /* type of cursor on real screen */
59
60 #define VBELL_TIMEOUT (TICKSPERSEC/10) /* visual bell lasts 1/10 sec */
61
62 struct beeptime {
63 struct beeptime *next;
64 unsigned long ticks;
65 };
66 static struct beeptime *beephead, *beeptail;
67 int nbeeps;
68 int beep_overloaded;
69 long lastbeep;
70
71 #define TSIZE (sizeof(unsigned long))
72 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
73
74 static unsigned long curr_attr, save_attr;
75 static unsigned long erase_char = ERASE_CHAR;
76
77 typedef struct {
78 int y, x;
79 } pos;
80 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
81 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
82 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
83 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
84 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
85 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
86
87 /* Product-order comparisons for rectangular block selection. */
88 #define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
89 #define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
90
91 static bufchain inbuf; /* terminal input buffer */
92 static pos curs; /* cursor */
93 static pos savecurs; /* saved cursor position */
94 static int marg_t, marg_b; /* scroll margins */
95 static int dec_om; /* DEC origin mode flag */
96 static int wrap, wrapnext; /* wrap flags */
97 static int insert; /* insert-mode flag */
98 static int cset; /* 0 or 1: which char set */
99 static int save_cset, save_csattr; /* saved with cursor position */
100 static int save_utf, save_wnext; /* saved with cursor position */
101 static int rvideo; /* global reverse video flag */
102 static unsigned long rvbell_startpoint;/* for ESC[?5hESC[?5l vbell */
103 static int cursor_on; /* cursor enabled flag */
104 static int reset_132; /* Flag ESC c resets to 80 cols */
105 static int use_bce; /* Use Background coloured erase */
106 static int blinker; /* When blinking is the cursor on ? */
107 static int tblinker; /* When the blinking text is on */
108 static int blink_is_real; /* Actually blink blinking text */
109 static int term_echoing; /* Does terminal want local echo? */
110 static int term_editing; /* Does terminal want local edit? */
111 static int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */
112 static int vt52_bold; /* Force bold on non-bold colours */
113 static int utf_state; /* Is there a pending UTF-8 character */
114 static int utf_char; /* and what is it so far. */
115 static int utf_size; /* The size of the UTF character. */
116 static int printing, only_printing; /* Are we doing ANSI printing? */
117 static int print_state; /* state of print-end-sequence scan */
118 static bufchain printer_buf; /* buffered data for printer */
119 static printer_job *print_job;
120
121 static int xterm_mouse; /* send mouse messages to app */
122
123 static unsigned long cset_attr[2];
124
125 /*
126 * Saved settings on the alternate screen.
127 */
128 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
129 static int alt_t, alt_b;
130 static int alt_which;
131
132 #define ARGS_MAX 32 /* max # of esc sequence arguments */
133 #define ARG_DEFAULT 0 /* if an arg isn't specified */
134 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
135 static int esc_args[ARGS_MAX];
136 static int esc_nargs;
137 static int esc_query;
138 #define ANSI(x,y) ((x)+((y)<<8))
139 #define ANSI_QUE(x) ANSI(x,TRUE)
140
141 #define OSC_STR_MAX 2048
142 static int osc_strlen;
143 static char osc_string[OSC_STR_MAX + 1];
144 static int osc_w;
145
146 static char id_string[1024] = "\033[?6c";
147
148 static unsigned char *tabs;
149
150 static enum {
151 TOPLEVEL,
152 SEEN_ESC,
153 SEEN_CSI,
154 SEEN_OSC,
155 SEEN_OSC_W,
156
157 DO_CTRLS,
158
159 SEEN_OSC_P,
160 OSC_STRING, OSC_MAYBE_ST,
161 VT52_ESC,
162 VT52_Y1,
163 VT52_Y2,
164 VT52_FG,
165 VT52_BG
166 } termstate;
167
168 static enum {
169 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
170 } selstate;
171 static enum {
172 LEXICOGRAPHIC, RECTANGULAR
173 } seltype;
174 static enum {
175 SM_CHAR, SM_WORD, SM_LINE
176 } selmode;
177 static pos selstart, selend, selanchor;
178
179 static short wordness[256] = {
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
181 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
182 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
183 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
184 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
185 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
186 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
187 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
188 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
189 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
190 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
191 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
192 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
193 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
194 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
195 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
196 };
197
198 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
199 static wchar_t sel_nl[] = SEL_NL;
200 static wchar_t *paste_buffer = 0;
201 static int paste_len, paste_pos, paste_hold;
202
203 /*
204 * Internal prototypes.
205 */
206 static void do_paint(Context, int);
207 static void erase_lots(int, int, int);
208 static void swap_screen(int, int, int);
209 static void update_sbar(void);
210 static void deselect(void);
211 static void term_print_finish(void);
212
213 /*
214 * Resize a line to make it `cols' columns wide.
215 */
216 unsigned long *resizeline(unsigned long *line, int cols)
217 {
218 int i, oldlen;
219 unsigned long lineattrs;
220
221 if (line[0] != (unsigned long)cols) {
222 /*
223 * This line is the wrong length, which probably means it
224 * hasn't been accessed since a resize. Resize it now.
225 */
226 oldlen = line[0];
227 lineattrs = line[oldlen + 1];
228 line = srealloc(line, TSIZE * (2 + cols));
229 line[0] = cols;
230 for (i = oldlen; i < cols; i++)
231 line[i + 1] = ERASE_CHAR;
232 line[cols + 1] = lineattrs & LATTR_MODE;
233 }
234
235 return line;
236 }
237
238 /*
239 * Retrieve a line of the screen or of the scrollback, according to
240 * whether the y coordinate is non-negative or negative
241 * (respectively).
242 */
243 unsigned long *lineptr(int y, int lineno)
244 {
245 unsigned long *line, *newline;
246 tree234 *whichtree;
247 int treeindex;
248
249 if (y >= 0) {
250 whichtree = screen;
251 treeindex = y;
252 } else {
253 whichtree = scrollback;
254 treeindex = y + count234(scrollback);
255 }
256 line = index234(whichtree, treeindex);
257
258 /* We assume that we don't screw up and retrieve something out of range. */
259 assert(line != NULL);
260
261 newline = resizeline(line, cols);
262 if (newline != line) {
263 delpos234(whichtree, treeindex);
264 addpos234(whichtree, newline, treeindex);
265 line = newline;
266 }
267
268 return line + 1;
269 }
270
271 #define lineptr(x) lineptr(x,__LINE__)
272 /*
273 * Set up power-on settings for the terminal.
274 */
275 static void power_on(void)
276 {
277 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
278 alt_t = marg_t = 0;
279 if (rows != -1)
280 alt_b = marg_b = rows - 1;
281 else
282 alt_b = marg_b = 0;
283 if (cols != -1) {
284 int i;
285 for (i = 0; i < cols; i++)
286 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
287 }
288 alt_om = dec_om = cfg.dec_om;
289 alt_wnext = wrapnext = alt_ins = insert = FALSE;
290 alt_wrap = wrap = cfg.wrap_mode;
291 alt_cset = cset = 0;
292 alt_utf = utf = 0;
293 alt_sco_acs = sco_acs = 0;
294 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
295 rvideo = 0;
296 in_vbell = FALSE;
297 cursor_on = 1;
298 big_cursor = 0;
299 save_attr = curr_attr = ATTR_DEFAULT;
300 term_editing = term_echoing = FALSE;
301 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
302 app_cursor_keys = cfg.app_cursor;
303 app_keypad_keys = cfg.app_keypad;
304 use_bce = cfg.bce;
305 blink_is_real = cfg.blinktext;
306 erase_char = ERASE_CHAR;
307 alt_which = 0;
308 term_print_finish();
309 {
310 int i;
311 for (i = 0; i < 256; i++)
312 wordness[i] = cfg.wordness[i];
313 }
314 if (screen) {
315 swap_screen(1, FALSE, FALSE);
316 erase_lots(FALSE, TRUE, TRUE);
317 swap_screen(0, FALSE, FALSE);
318 erase_lots(FALSE, TRUE, TRUE);
319 }
320 }
321
322 /*
323 * Force a screen update.
324 */
325 void term_update(void)
326 {
327 Context ctx;
328 ctx = get_ctx();
329 if (ctx) {
330 int need_sbar_update = seen_disp_event;
331 if (seen_disp_event && cfg.scroll_on_disp) {
332 disptop = 0; /* return to main screen */
333 seen_disp_event = 0;
334 need_sbar_update = TRUE;
335 }
336 if (need_sbar_update)
337 update_sbar();
338 do_paint(ctx, TRUE);
339 sys_cursor(curs.x, curs.y - disptop);
340 free_ctx(ctx);
341 }
342 }
343
344 /*
345 * Called from front end when a keypress occurs, to trigger
346 * anything magical that needs to happen in that situation.
347 */
348 void term_seen_key_event(void)
349 {
350 /*
351 * On any keypress, clear the bell overload mechanism
352 * completely, on the grounds that large numbers of
353 * beeps coming from deliberate key action are likely
354 * to be intended (e.g. beeps from filename completion
355 * blocking repeatedly).
356 */
357 beep_overloaded = FALSE;
358 while (beephead) {
359 struct beeptime *tmp = beephead;
360 beephead = tmp->next;
361 sfree(tmp);
362 }
363 beeptail = NULL;
364 nbeeps = 0;
365
366 /*
367 * Reset the scrollback on keypress, if we're doing that.
368 */
369 if (cfg.scroll_on_key) {
370 disptop = 0; /* return to main screen */
371 seen_disp_event = 1;
372 }
373 }
374
375 /*
376 * Same as power_on(), but an external function.
377 */
378 void term_pwron(void)
379 {
380 power_on();
381 fix_cpos;
382 disptop = 0;
383 deselect();
384 term_update();
385 }
386
387 /*
388 * When the user reconfigures us, we need to check the forbidden-
389 * alternate-screen config option, disable raw mouse mode if the
390 * user has disabled mouse reporting, and abandon a print job if
391 * the user has disabled printing.
392 */
393 void term_reconfig(void)
394 {
395 if (cfg.no_alt_screen)
396 swap_screen(0, FALSE, FALSE);
397 if (cfg.no_mouse_rep) {
398 xterm_mouse = 0;
399 set_raw_mouse_mode(0);
400 }
401 if (cfg.no_remote_charset) {
402 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
403 sco_acs = alt_sco_acs = 0;
404 utf = 0;
405 }
406 if (!*cfg.printer) {
407 term_print_finish();
408 }
409 }
410
411 /*
412 * Clear the scrollback.
413 */
414 void term_clrsb(void)
415 {
416 unsigned long *line;
417 disptop = 0;
418 while ((line = delpos234(scrollback, 0)) != NULL) {
419 sfree(line);
420 }
421 update_sbar();
422 }
423
424 /*
425 * Initialise the terminal.
426 */
427 void term_init(void)
428 {
429 screen = alt_screen = scrollback = NULL;
430 disptop = 0;
431 disptext = dispcurs = NULL;
432 tabs = NULL;
433 deselect();
434 rows = cols = -1;
435 power_on();
436 beephead = beeptail = NULL;
437 nbeeps = 0;
438 lastbeep = FALSE;
439 beep_overloaded = FALSE;
440 }
441
442 /*
443 * Set up the terminal for a given size.
444 */
445 void term_size(int newrows, int newcols, int newsavelines)
446 {
447 tree234 *newalt;
448 unsigned long *newdisp, *line;
449 int i, j;
450 int sblen;
451 int save_alt_which = alt_which;
452
453 if (newrows == rows && newcols == cols && newsavelines == savelines)
454 return; /* nothing to do */
455
456 deselect();
457 swap_screen(0, FALSE, FALSE);
458
459 alt_t = marg_t = 0;
460 alt_b = marg_b = newrows - 1;
461
462 if (rows == -1) {
463 scrollback = newtree234(NULL);
464 screen = newtree234(NULL);
465 rows = 0;
466 }
467
468 /*
469 * Resize the screen and scrollback. We only need to shift
470 * lines around within our data structures, because lineptr()
471 * will take care of resizing each individual line if
472 * necessary. So:
473 *
474 * - If the new screen and the old screen differ in length, we
475 * must shunt some lines in from the scrollback or out to
476 * the scrollback.
477 *
478 * - If doing that fails to provide us with enough material to
479 * fill the new screen (i.e. the number of rows needed in
480 * the new screen exceeds the total number in the previous
481 * screen+scrollback), we must invent some blank lines to
482 * cover the gap.
483 *
484 * - Then, if the new scrollback length is less than the
485 * amount of scrollback we actually have, we must throw some
486 * away.
487 */
488 sblen = count234(scrollback);
489 /* Do this loop to expand the screen if newrows > rows */
490 for (i = rows; i < newrows; i++) {
491 if (sblen > 0) {
492 line = delpos234(scrollback, --sblen);
493 } else {
494 line = smalloc(TSIZE * (newcols + 2));
495 line[0] = newcols;
496 for (j = 0; j <= newcols; j++)
497 line[j + 1] = ERASE_CHAR;
498 }
499 addpos234(screen, line, 0);
500 }
501 /* Do this loop to shrink the screen if newrows < rows */
502 for (i = newrows; i < rows; i++) {
503 line = delpos234(screen, 0);
504 addpos234(scrollback, line, sblen++);
505 }
506 assert(count234(screen) == newrows);
507 while (sblen > newsavelines) {
508 line = delpos234(scrollback, 0);
509 sfree(line);
510 sblen--;
511 }
512 assert(count234(scrollback) <= newsavelines);
513 disptop = 0;
514
515 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
516 for (i = 0; i < newrows * (newcols + 1); i++)
517 newdisp[i] = ATTR_INVALID;
518 sfree(disptext);
519 disptext = newdisp;
520 dispcurs = NULL;
521
522 newalt = newtree234(NULL);
523 for (i = 0; i < newrows; i++) {
524 line = smalloc(TSIZE * (newcols + 2));
525 line[0] = newcols;
526 for (j = 0; j <= newcols; j++)
527 line[j + 1] = erase_char;
528 addpos234(newalt, line, i);
529 }
530 if (alt_screen) {
531 while (NULL != (line = delpos234(alt_screen, 0)))
532 sfree(line);
533 freetree234(alt_screen);
534 }
535 alt_screen = newalt;
536
537 tabs = srealloc(tabs, newcols * sizeof(*tabs));
538 {
539 int i;
540 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
541 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
542 }
543
544 if (rows > 0)
545 curs.y += newrows - rows;
546 if (curs.y < 0)
547 curs.y = 0;
548 if (curs.y >= newrows)
549 curs.y = newrows - 1;
550 if (curs.x >= newcols)
551 curs.x = newcols - 1;
552 alt_x = alt_y = 0;
553 wrapnext = alt_wnext = FALSE;
554
555 rows = newrows;
556 cols = newcols;
557 savelines = newsavelines;
558 fix_cpos;
559
560 swap_screen(save_alt_which, FALSE, FALSE);
561
562 update_sbar();
563 term_update();
564 back->size();
565 }
566
567 /*
568 * Swap screens. If `reset' is TRUE and we have been asked to
569 * switch to the alternate screen, we must bring most of its
570 * configuration from the main screen and erase the contents of the
571 * alternate screen completely. (This is even true if we're already
572 * on it! Blame xterm.)
573 */
574 static void swap_screen(int which, int reset, int keep_cur_pos)
575 {
576 int t;
577 tree234 *ttr;
578
579 if (!which)
580 reset = FALSE; /* do no weird resetting if which==0 */
581
582 if (which != alt_which) {
583 alt_which = which;
584
585 ttr = alt_screen;
586 alt_screen = screen;
587 screen = ttr;
588 t = curs.x;
589 if (!reset && !keep_cur_pos)
590 curs.x = alt_x;
591 alt_x = t;
592 t = curs.y;
593 if (!reset && !keep_cur_pos)
594 curs.y = alt_y;
595 alt_y = t;
596 t = marg_t;
597 if (!reset) marg_t = alt_t;
598 alt_t = t;
599 t = marg_b;
600 if (!reset) marg_b = alt_b;
601 alt_b = t;
602 t = dec_om;
603 if (!reset) dec_om = alt_om;
604 alt_om = t;
605 t = wrap;
606 if (!reset) wrap = alt_wrap;
607 alt_wrap = t;
608 t = wrapnext;
609 if (!reset) wrapnext = alt_wnext;
610 alt_wnext = t;
611 t = insert;
612 if (!reset) insert = alt_ins;
613 alt_ins = t;
614 t = cset;
615 if (!reset) cset = alt_cset;
616 alt_cset = t;
617 t = utf;
618 if (!reset) utf = alt_utf;
619 alt_utf = t;
620 t = sco_acs;
621 if (!reset) sco_acs = alt_sco_acs;
622 alt_sco_acs = t;
623 }
624
625 if (reset && screen) {
626 /*
627 * Yes, this _is_ supposed to honour background-colour-erase.
628 */
629 erase_lots(FALSE, TRUE, TRUE);
630 }
631
632 /*
633 * This might not be possible if we're called during
634 * initialisation.
635 */
636 if (screen)
637 fix_cpos;
638 }
639
640 /*
641 * Update the scroll bar.
642 */
643 static void update_sbar(void)
644 {
645 int nscroll;
646
647 nscroll = count234(scrollback);
648
649 set_sbar(nscroll + rows, nscroll + disptop, rows);
650 }
651
652 /*
653 * Check whether the region bounded by the two pointers intersects
654 * the scroll region, and de-select the on-screen selection if so.
655 */
656 static void check_selection(pos from, pos to)
657 {
658 if (poslt(from, selend) && poslt(selstart, to))
659 deselect();
660 }
661
662 /*
663 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
664 * for backward.) `sb' is TRUE if the scrolling is permitted to
665 * affect the scrollback buffer.
666 *
667 * NB this function invalidates all pointers into lines of the
668 * screen data structures. In particular, you MUST call fix_cpos
669 * after calling scroll() and before doing anything else that
670 * uses the cpos shortcut pointer.
671 */
672 static void scroll(int topline, int botline, int lines, int sb)
673 {
674 unsigned long *line, *line2;
675 int i, seltop;
676
677 if (topline != 0 || alt_which != 0)
678 sb = FALSE;
679
680 if (lines < 0) {
681 while (lines < 0) {
682 line = delpos234(screen, botline);
683 line = resizeline(line, cols);
684 for (i = 0; i < cols; i++)
685 line[i + 1] = erase_char;
686 line[cols + 1] = 0;
687 addpos234(screen, line, topline);
688
689 if (selstart.y >= topline && selstart.y <= botline) {
690 selstart.y++;
691 if (selstart.y > botline) {
692 selstart.y = botline;
693 selstart.x = 0;
694 }
695 }
696 if (selend.y >= topline && selend.y <= botline) {
697 selend.y++;
698 if (selend.y > botline) {
699 selend.y = botline;
700 selend.x = 0;
701 }
702 }
703
704 lines++;
705 }
706 } else {
707 while (lines > 0) {
708 line = delpos234(screen, topline);
709 if (sb && savelines > 0) {
710 int sblen = count234(scrollback);
711 /*
712 * We must add this line to the scrollback. We'll
713 * remove a line from the top of the scrollback to
714 * replace it, or allocate a new one if the
715 * scrollback isn't full.
716 */
717 if (sblen == savelines) {
718 sblen--, line2 = delpos234(scrollback, 0);
719 } else {
720 line2 = smalloc(TSIZE * (cols + 2));
721 line2[0] = cols;
722 }
723 addpos234(scrollback, line, sblen);
724 line = line2;
725
726 /*
727 * If the user is currently looking at part of the
728 * scrollback, and they haven't enabled any options
729 * that are going to reset the scrollback as a
730 * result of this movement, then the chances are
731 * they'd like to keep looking at the same line. So
732 * we move their viewpoint at the same rate as the
733 * scroll, at least until their viewpoint hits the
734 * top end of the scrollback buffer, at which point
735 * we don't have the choice any more.
736 *
737 * Thanks to Jan Holmen Holsten for the idea and
738 * initial implementation.
739 */
740 if (disptop > -savelines && disptop < 0)
741 disptop--;
742 }
743 line = resizeline(line, cols);
744 for (i = 0; i < cols; i++)
745 line[i + 1] = erase_char;
746 line[cols + 1] = 0;
747 addpos234(screen, line, botline);
748
749 /*
750 * If the selection endpoints move into the scrollback,
751 * we keep them moving until they hit the top. However,
752 * of course, if the line _hasn't_ moved into the
753 * scrollback then we don't do this, and cut them off
754 * at the top of the scroll region.
755 *
756 * This applies to selstart and selend (for an existing
757 * selection), and also selanchor (for one being
758 * selected as we speak).
759 */
760 seltop = sb ? -savelines : topline;
761
762 if (selstart.y >= seltop && selstart.y <= botline) {
763 selstart.y--;
764 if (selstart.y < seltop) {
765 selstart.y = seltop;
766 selstart.x = 0;
767 }
768 }
769 if (selend.y >= seltop && selend.y <= botline) {
770 selend.y--;
771 if (selend.y < seltop) {
772 selend.y = seltop;
773 selend.x = 0;
774 }
775 }
776 if (selanchor.y >= seltop && selanchor.y <= botline) {
777 selanchor.y--;
778 if (selanchor.y < seltop) {
779 selanchor.y = seltop;
780 selanchor.x = 0;
781 }
782 }
783
784 lines--;
785 }
786 }
787 }
788
789 /*
790 * Move the cursor to a given position, clipping at boundaries. We
791 * may or may not want to clip at the scroll margin: marg_clip is 0
792 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
793 * even _being_ outside the margins.
794 */
795 static void move(int x, int y, int marg_clip)
796 {
797 if (x < 0)
798 x = 0;
799 if (x >= cols)
800 x = cols - 1;
801 if (marg_clip) {
802 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
803 y = marg_t;
804 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
805 y = marg_b;
806 }
807 if (y < 0)
808 y = 0;
809 if (y >= rows)
810 y = rows - 1;
811 curs.x = x;
812 curs.y = y;
813 fix_cpos;
814 wrapnext = FALSE;
815 }
816
817 /*
818 * Save or restore the cursor and SGR mode.
819 */
820 static void save_cursor(int save)
821 {
822 if (save) {
823 savecurs = curs;
824 save_attr = curr_attr;
825 save_cset = cset;
826 save_utf = utf;
827 save_wnext = wrapnext;
828 save_csattr = cset_attr[cset];
829 save_sco_acs = sco_acs;
830 } else {
831 curs = savecurs;
832 /* Make sure the window hasn't shrunk since the save */
833 if (curs.x >= cols)
834 curs.x = cols - 1;
835 if (curs.y >= rows)
836 curs.y = rows - 1;
837
838 curr_attr = save_attr;
839 cset = save_cset;
840 utf = save_utf;
841 wrapnext = save_wnext;
842 /*
843 * wrapnext might reset to False if the x position is no
844 * longer at the rightmost edge.
845 */
846 if (wrapnext && curs.x < cols-1)
847 wrapnext = FALSE;
848 cset_attr[cset] = save_csattr;
849 sco_acs = save_sco_acs;
850 fix_cpos;
851 if (use_bce)
852 erase_char = (' ' | ATTR_ASCII |
853 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
854 }
855 }
856
857 /*
858 * Erase a large portion of the screen: the whole screen, or the
859 * whole line, or parts thereof.
860 */
861 static void erase_lots(int line_only, int from_begin, int to_end)
862 {
863 pos start, end;
864 int erase_lattr;
865 unsigned long *ldata;
866
867 if (line_only) {
868 start.y = curs.y;
869 start.x = 0;
870 end.y = curs.y + 1;
871 end.x = 0;
872 erase_lattr = FALSE;
873 } else {
874 start.y = 0;
875 start.x = 0;
876 end.y = rows;
877 end.x = 0;
878 erase_lattr = TRUE;
879 }
880 if (!from_begin) {
881 start = curs;
882 }
883 if (!to_end) {
884 end = curs;
885 incpos(end);
886 }
887 check_selection(start, end);
888
889 /* Clear screen also forces a full window redraw, just in case. */
890 if (start.y == 0 && start.x == 0 && end.y == rows)
891 term_invalidate();
892
893 ldata = lineptr(start.y);
894 while (poslt(start, end)) {
895 if (start.x == cols && !erase_lattr)
896 ldata[start.x] &= ~LATTR_WRAPPED;
897 else
898 ldata[start.x] = erase_char;
899 if (incpos(start) && start.y < rows)
900 ldata = lineptr(start.y);
901 }
902 }
903
904 /*
905 * Insert or delete characters within the current line. n is +ve if
906 * insertion is desired, and -ve for deletion.
907 */
908 static void insch(int n)
909 {
910 int dir = (n < 0 ? -1 : +1);
911 int m;
912 pos cursplus;
913 unsigned long *ldata;
914
915 n = (n < 0 ? -n : n);
916 if (n > cols - curs.x)
917 n = cols - curs.x;
918 m = cols - curs.x - n;
919 cursplus.y = curs.y;
920 cursplus.x = curs.x + n;
921 check_selection(curs, cursplus);
922 ldata = lineptr(curs.y);
923 if (dir < 0) {
924 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
925 while (n--)
926 ldata[curs.x + m++] = erase_char;
927 } else {
928 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
929 while (n--)
930 ldata[curs.x + n] = erase_char;
931 }
932 }
933
934 /*
935 * Toggle terminal mode `mode' to state `state'. (`query' indicates
936 * whether the mode is a DEC private one or a normal one.)
937 */
938 static void toggle_mode(int mode, int query, int state)
939 {
940 unsigned long ticks;
941
942 if (query)
943 switch (mode) {
944 case 1: /* application cursor keys */
945 app_cursor_keys = state;
946 break;
947 case 2: /* VT52 mode */
948 vt52_mode = !state;
949 if (vt52_mode) {
950 blink_is_real = FALSE;
951 vt52_bold = FALSE;
952 } else {
953 blink_is_real = cfg.blinktext;
954 }
955 break;
956 case 3: /* 80/132 columns */
957 deselect();
958 if (!cfg.no_remote_resize)
959 request_resize(state ? 132 : 80, rows);
960 reset_132 = state;
961 break;
962 case 5: /* reverse video */
963 /*
964 * Toggle reverse video. If we receive an OFF within the
965 * visual bell timeout period after an ON, we trigger an
966 * effective visual bell, so that ESC[?5hESC[?5l will
967 * always be an actually _visible_ visual bell.
968 */
969 ticks = GETTICKCOUNT();
970 /* turn off a previous vbell to avoid inconsistencies */
971 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
972 in_vbell = FALSE;
973 if (rvideo && !state && /* we're turning it off... */
974 (ticks - rvbell_startpoint) < VBELL_TIMEOUT) { /* ...soon */
975 /* If there's no vbell timeout already, or this one lasts
976 * longer, replace vbell_timeout with ours. */
977 if (!in_vbell ||
978 (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
979 vbell_startpoint = rvbell_startpoint;
980 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
981 } else if (!rvideo && state) {
982 /* This is an ON, so we notice the time and save it. */
983 rvbell_startpoint = ticks;
984 }
985 rvideo = state;
986 seen_disp_event = TRUE;
987 if (state)
988 term_update();
989 break;
990 case 6: /* DEC origin mode */
991 dec_om = state;
992 break;
993 case 7: /* auto wrap */
994 wrap = state;
995 break;
996 case 8: /* auto key repeat */
997 repeat_off = !state;
998 break;
999 case 10: /* set local edit mode */
1000 term_editing = state;
1001 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
1002 break;
1003 case 25: /* enable/disable cursor */
1004 compatibility2(OTHER, VT220);
1005 cursor_on = state;
1006 seen_disp_event = TRUE;
1007 break;
1008 case 47: /* alternate screen */
1009 compatibility(OTHER);
1010 deselect();
1011 swap_screen(cfg.no_alt_screen ? 0 : state, FALSE, FALSE);
1012 disptop = 0;
1013 break;
1014 case 1000: /* xterm mouse 1 */
1015 xterm_mouse = state ? 1 : 0;
1016 set_raw_mouse_mode(state);
1017 break;
1018 case 1002: /* xterm mouse 2 */
1019 xterm_mouse = state ? 2 : 0;
1020 set_raw_mouse_mode(state);
1021 break;
1022 case 1047: /* alternate screen */
1023 compatibility(OTHER);
1024 deselect();
1025 swap_screen(cfg.no_alt_screen ? 0 : state, TRUE, TRUE);
1026 disptop = 0;
1027 break;
1028 case 1048: /* save/restore cursor */
1029 save_cursor(state);
1030 if (!state) seen_disp_event = TRUE;
1031 break;
1032 case 1049: /* cursor & alternate screen */
1033 if (state)
1034 save_cursor(state);
1035 if (!state) seen_disp_event = TRUE;
1036 compatibility(OTHER);
1037 deselect();
1038 swap_screen(cfg.no_alt_screen ? 0 : state, TRUE, FALSE);
1039 if (!state)
1040 save_cursor(state);
1041 disptop = 0;
1042 break;
1043 } else
1044 switch (mode) {
1045 case 4: /* set insert mode */
1046 compatibility(VT102);
1047 insert = state;
1048 break;
1049 case 12: /* set echo mode */
1050 term_echoing = !state;
1051 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
1052 break;
1053 case 20: /* Return sends ... */
1054 cr_lf_return = state;
1055 break;
1056 case 34: /* Make cursor BIG */
1057 compatibility2(OTHER, VT220);
1058 big_cursor = !state;
1059 }
1060 }
1061
1062 /*
1063 * Process an OSC sequence: set window title or icon name.
1064 */
1065 static void do_osc(void)
1066 {
1067 if (osc_w) {
1068 while (osc_strlen--)
1069 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
1070 } else {
1071 osc_string[osc_strlen] = '\0';
1072 switch (esc_args[0]) {
1073 case 0:
1074 case 1:
1075 if (!cfg.no_remote_wintitle)
1076 set_icon(osc_string);
1077 if (esc_args[0] == 1)
1078 break;
1079 /* fall through: parameter 0 means set both */
1080 case 2:
1081 case 21:
1082 if (!cfg.no_remote_wintitle)
1083 set_title(osc_string);
1084 break;
1085 }
1086 }
1087 }
1088
1089 /*
1090 * ANSI printing routines.
1091 */
1092 static void term_print_setup(void)
1093 {
1094 bufchain_clear(&printer_buf);
1095 print_job = printer_start_job(cfg.printer);
1096 }
1097 static void term_print_flush(void)
1098 {
1099 void *data;
1100 int len;
1101 int size;
1102 while ((size = bufchain_size(&printer_buf)) > 5) {
1103 bufchain_prefix(&printer_buf, &data, &len);
1104 if (len > size-5)
1105 len = size-5;
1106 printer_job_data(print_job, data, len);
1107 bufchain_consume(&printer_buf, len);
1108 }
1109 }
1110 static void term_print_finish(void)
1111 {
1112 void *data;
1113 int len, size;
1114 char c;
1115
1116 if (!printing && !only_printing)
1117 return; /* we need do nothing */
1118
1119 term_print_flush();
1120 while ((size = bufchain_size(&printer_buf)) > 0) {
1121 bufchain_prefix(&printer_buf, &data, &len);
1122 c = *(char *)data;
1123 if (c == '\033' || c == '\233') {
1124 bufchain_consume(&printer_buf, size);
1125 break;
1126 } else {
1127 printer_job_data(print_job, &c, 1);
1128 bufchain_consume(&printer_buf, 1);
1129 }
1130 }
1131 printer_finish_job(print_job);
1132 print_job = NULL;
1133 printing = only_printing = FALSE;
1134 }
1135
1136 /*
1137 * Remove everything currently in `inbuf' and stick it up on the
1138 * in-memory display. There's a big state machine in here to
1139 * process escape sequences...
1140 */
1141 void term_out(void)
1142 {
1143 int c, unget;
1144 unsigned char localbuf[256], *chars;
1145 int nchars = 0;
1146
1147 unget = -1;
1148
1149 chars = NULL; /* placate compiler warnings */
1150 while (nchars > 0 || bufchain_size(&inbuf) > 0) {
1151 if (unget == -1) {
1152 if (nchars == 0) {
1153 void *ret;
1154 bufchain_prefix(&inbuf, &ret, &nchars);
1155 if (nchars > sizeof(localbuf))
1156 nchars = sizeof(localbuf);
1157 memcpy(localbuf, ret, nchars);
1158 bufchain_consume(&inbuf, nchars);
1159 chars = localbuf;
1160 assert(chars != NULL);
1161 }
1162 c = *chars++;
1163 nchars--;
1164
1165 /*
1166 * Optionally log the session traffic to a file. Useful for
1167 * debugging and possibly also useful for actual logging.
1168 */
1169 if (cfg.logtype == LGTYP_DEBUG)
1170 logtraffic((unsigned char) c, LGTYP_DEBUG);
1171 } else {
1172 c = unget;
1173 unget = -1;
1174 }
1175
1176 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1177 * be able to display 8-bit characters, but I'll let that go 'cause
1178 * of i18n.
1179 */
1180
1181 /*
1182 * If we're printing, add the character to the printer
1183 * buffer.
1184 */
1185 if (printing) {
1186 bufchain_add(&printer_buf, &c, 1);
1187
1188 /*
1189 * If we're in print-only mode, we use a much simpler
1190 * state machine designed only to recognise the ESC[4i
1191 * termination sequence.
1192 */
1193 if (only_printing) {
1194 if (c == '\033')
1195 print_state = 1;
1196 else if (c == (unsigned char)'\233')
1197 print_state = 2;
1198 else if (c == '[' && print_state == 1)
1199 print_state = 2;
1200 else if (c == '4' && print_state == 2)
1201 print_state = 3;
1202 else if (c == 'i' && print_state == 3)
1203 print_state = 4;
1204 else
1205 print_state = 0;
1206 if (print_state == 4) {
1207 term_print_finish();
1208 }
1209 continue;
1210 }
1211 }
1212
1213 /* First see about all those translations. */
1214 if (termstate == TOPLEVEL) {
1215 if (in_utf)
1216 switch (utf_state) {
1217 case 0:
1218 if (c < 0x80) {
1219 /* UTF-8 must be stateless so we ignore iso2022. */
1220 if (unitab_ctrl[c] != 0xFF)
1221 c = unitab_ctrl[c];
1222 else c = ((unsigned char)c) | ATTR_ASCII;
1223 break;
1224 } else if ((c & 0xe0) == 0xc0) {
1225 utf_size = utf_state = 1;
1226 utf_char = (c & 0x1f);
1227 } else if ((c & 0xf0) == 0xe0) {
1228 utf_size = utf_state = 2;
1229 utf_char = (c & 0x0f);
1230 } else if ((c & 0xf8) == 0xf0) {
1231 utf_size = utf_state = 3;
1232 utf_char = (c & 0x07);
1233 } else if ((c & 0xfc) == 0xf8) {
1234 utf_size = utf_state = 4;
1235 utf_char = (c & 0x03);
1236 } else if ((c & 0xfe) == 0xfc) {
1237 utf_size = utf_state = 5;
1238 utf_char = (c & 0x01);
1239 } else {
1240 c = UCSERR;
1241 break;
1242 }
1243 continue;
1244 case 1:
1245 case 2:
1246 case 3:
1247 case 4:
1248 case 5:
1249 if ((c & 0xC0) != 0x80) {
1250 unget = c;
1251 c = UCSERR;
1252 utf_state = 0;
1253 break;
1254 }
1255 utf_char = (utf_char << 6) | (c & 0x3f);
1256 if (--utf_state)
1257 continue;
1258
1259 c = utf_char;
1260
1261 /* Is somebody trying to be evil! */
1262 if (c < 0x80 ||
1263 (c < 0x800 && utf_size >= 2) ||
1264 (c < 0x10000 && utf_size >= 3) ||
1265 (c < 0x200000 && utf_size >= 4) ||
1266 (c < 0x4000000 && utf_size >= 5))
1267 c = UCSERR;
1268
1269 /* Unicode line separator and paragraph separator are CR-LF */
1270 if (c == 0x2028 || c == 0x2029)
1271 c = 0x85;
1272
1273 /* High controls are probably a Baaad idea too. */
1274 if (c < 0xA0)
1275 c = 0xFFFD;
1276
1277 /* The UTF-16 surrogates are not nice either. */
1278 /* The standard give the option of decoding these:
1279 * I don't want to! */
1280 if (c >= 0xD800 && c < 0xE000)
1281 c = UCSERR;
1282
1283 /* ISO 10646 characters now limited to UTF-16 range. */
1284 if (c > 0x10FFFF)
1285 c = UCSERR;
1286
1287 /* This is currently a TagPhobic application.. */
1288 if (c >= 0xE0000 && c <= 0xE007F)
1289 continue;
1290
1291 /* U+FEFF is best seen as a null. */
1292 if (c == 0xFEFF)
1293 continue;
1294 /* But U+FFFE is an error. */
1295 if (c == 0xFFFE || c == 0xFFFF)
1296 c = UCSERR;
1297
1298 /* Oops this is a 16bit implementation */
1299 if (c >= 0x10000)
1300 c = 0xFFFD;
1301 break;
1302 }
1303 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1304 else if(sco_acs &&
1305 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1306 {
1307 if (sco_acs == 2) c ^= 0x80;
1308 c |= ATTR_SCOACS;
1309 } else {
1310 switch (cset_attr[cset]) {
1311 /*
1312 * Linedraw characters are different from 'ESC ( B'
1313 * only for a small range. For ones outside that
1314 * range, make sure we use the same font as well as
1315 * the same encoding.
1316 */
1317 case ATTR_LINEDRW:
1318 if (unitab_ctrl[c] != 0xFF)
1319 c = unitab_ctrl[c];
1320 else
1321 c = ((unsigned char) c) | ATTR_LINEDRW;
1322 break;
1323
1324 case ATTR_GBCHR:
1325 /* If UK-ASCII, make the '#' a LineDraw Pound */
1326 if (c == '#') {
1327 c = '}' | ATTR_LINEDRW;
1328 break;
1329 }
1330 /*FALLTHROUGH*/ case ATTR_ASCII:
1331 if (unitab_ctrl[c] != 0xFF)
1332 c = unitab_ctrl[c];
1333 else
1334 c = ((unsigned char) c) | ATTR_ASCII;
1335 break;
1336 case ATTR_SCOACS:
1337 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1338 break;
1339 }
1340 }
1341 }
1342
1343 /* How about C1 controls ? */
1344 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1345 has_compat(VT220)) {
1346 termstate = SEEN_ESC;
1347 esc_query = FALSE;
1348 c = '@' + (c & 0x1F);
1349 }
1350
1351 /* Or the GL control. */
1352 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1353 if (curs.x && !wrapnext)
1354 curs.x--;
1355 wrapnext = FALSE;
1356 fix_cpos;
1357 if (!cfg.no_dbackspace) /* destructive bksp might be disabled */
1358 *cpos = (' ' | curr_attr | ATTR_ASCII);
1359 } else
1360 /* Or normal C0 controls. */
1361 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1362 switch (c) {
1363 case '\005': /* terminal type query */
1364 /* Strictly speaking this is VT100 but a VT100 defaults to
1365 * no response. Other terminals respond at their option.
1366 *
1367 * Don't put a CR in the default string as this tends to
1368 * upset some weird software.
1369 *
1370 * An xterm returns "xterm" (5 characters)
1371 */
1372 compatibility(ANSIMIN);
1373 {
1374 char abuf[256], *s, *d;
1375 int state = 0;
1376 for (s = cfg.answerback, d = abuf; *s; s++) {
1377 if (state) {
1378 if (*s >= 'a' && *s <= 'z')
1379 *d++ = (*s - ('a' - 1));
1380 else if ((*s >= '@' && *s <= '_') ||
1381 *s == '?' || (*s & 0x80))
1382 *d++ = ('@' ^ *s);
1383 else if (*s == '~')
1384 *d++ = '^';
1385 state = 0;
1386 } else if (*s == '^') {
1387 state = 1;
1388 } else
1389 *d++ = *s;
1390 }
1391 lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
1392 }
1393 break;
1394 case '\007':
1395 {
1396 struct beeptime *newbeep;
1397 unsigned long ticks;
1398
1399 ticks = GETTICKCOUNT();
1400
1401 if (!beep_overloaded) {
1402 newbeep = smalloc(sizeof(struct beeptime));
1403 newbeep->ticks = ticks;
1404 newbeep->next = NULL;
1405 if (!beephead)
1406 beephead = newbeep;
1407 else
1408 beeptail->next = newbeep;
1409 beeptail = newbeep;
1410 nbeeps++;
1411 }
1412
1413 /*
1414 * Throw out any beeps that happened more than
1415 * t seconds ago.
1416 */
1417 while (beephead &&
1418 beephead->ticks < ticks - cfg.bellovl_t) {
1419 struct beeptime *tmp = beephead;
1420 beephead = tmp->next;
1421 sfree(tmp);
1422 if (!beephead)
1423 beeptail = NULL;
1424 nbeeps--;
1425 }
1426
1427 if (cfg.bellovl && beep_overloaded &&
1428 ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
1429 /*
1430 * If we're currently overloaded and the
1431 * last beep was more than s seconds ago,
1432 * leave overload mode.
1433 */
1434 beep_overloaded = FALSE;
1435 } else if (cfg.bellovl && !beep_overloaded &&
1436 nbeeps >= cfg.bellovl_n) {
1437 /*
1438 * Now, if we have n or more beeps
1439 * remaining in the queue, go into overload
1440 * mode.
1441 */
1442 beep_overloaded = TRUE;
1443 }
1444 lastbeep = ticks;
1445
1446 /*
1447 * Perform an actual beep if we're not overloaded.
1448 */
1449 if (!cfg.bellovl || !beep_overloaded) {
1450 if (cfg.beep == BELL_VISUAL) {
1451 in_vbell = TRUE;
1452 vbell_startpoint = ticks;
1453 term_update();
1454 } else
1455 beep(cfg.beep);
1456 }
1457 disptop = 0;
1458 }
1459 break;
1460 case '\b':
1461 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1462 else if (curs.x == 0 && curs.y > 0)
1463 curs.x = cols - 1, curs.y--;
1464 else if (wrapnext)
1465 wrapnext = FALSE;
1466 else
1467 curs.x--;
1468 fix_cpos;
1469 seen_disp_event = TRUE;
1470 break;
1471 case '\016':
1472 compatibility(VT100);
1473 cset = 1;
1474 break;
1475 case '\017':
1476 compatibility(VT100);
1477 cset = 0;
1478 break;
1479 case '\033':
1480 if (vt52_mode)
1481 termstate = VT52_ESC;
1482 else {
1483 compatibility(ANSIMIN);
1484 termstate = SEEN_ESC;
1485 esc_query = FALSE;
1486 }
1487 break;
1488 case '\r':
1489 curs.x = 0;
1490 wrapnext = FALSE;
1491 fix_cpos;
1492 seen_disp_event = TRUE;
1493 paste_hold = 0;
1494 logtraffic((unsigned char) c, LGTYP_ASCII);
1495 break;
1496 case '\014':
1497 if (has_compat(SCOANSI)) {
1498 move(0, 0, 0);
1499 erase_lots(FALSE, FALSE, TRUE);
1500 disptop = 0;
1501 wrapnext = FALSE;
1502 seen_disp_event = 1;
1503 break;
1504 }
1505 case '\013':
1506 compatibility(VT100);
1507 case '\n':
1508 if (curs.y == marg_b)
1509 scroll(marg_t, marg_b, 1, TRUE);
1510 else if (curs.y < rows - 1)
1511 curs.y++;
1512 if (cfg.lfhascr)
1513 curs.x = 0;
1514 fix_cpos;
1515 wrapnext = FALSE;
1516 seen_disp_event = 1;
1517 paste_hold = 0;
1518 logtraffic((unsigned char) c, LGTYP_ASCII);
1519 break;
1520 case '\t':
1521 {
1522 pos old_curs = curs;
1523 unsigned long *ldata = lineptr(curs.y);
1524
1525 do {
1526 curs.x++;
1527 } while (curs.x < cols - 1 && !tabs[curs.x]);
1528
1529 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1530 if (curs.x >= cols / 2)
1531 curs.x = cols / 2 - 1;
1532 } else {
1533 if (curs.x >= cols)
1534 curs.x = cols - 1;
1535 }
1536
1537 fix_cpos;
1538 check_selection(old_curs, curs);
1539 }
1540 seen_disp_event = TRUE;
1541 break;
1542 }
1543 } else
1544 switch (termstate) {
1545 case TOPLEVEL:
1546 /* Only graphic characters get this far, ctrls are stripped above */
1547 if (wrapnext && wrap) {
1548 cpos[1] |= LATTR_WRAPPED;
1549 if (curs.y == marg_b)
1550 scroll(marg_t, marg_b, 1, TRUE);
1551 else if (curs.y < rows - 1)
1552 curs.y++;
1553 curs.x = 0;
1554 fix_cpos;
1555 wrapnext = FALSE;
1556 }
1557 if (insert)
1558 insch(1);
1559 if (selstate != NO_SELECTION) {
1560 pos cursplus = curs;
1561 incpos(cursplus);
1562 check_selection(curs, cursplus);
1563 }
1564 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1565 logtraffic((unsigned char) c, LGTYP_ASCII);
1566 {
1567 extern int wcwidth(wchar_t ucs);
1568 int width = 0;
1569 if (DIRECT_CHAR(c))
1570 width = 1;
1571 if (!width)
1572 width = wcwidth((wchar_t) c);
1573 switch (width) {
1574 case 2:
1575 *cpos++ = c | curr_attr;
1576 if (++curs.x == cols) {
1577 *cpos |= LATTR_WRAPPED;
1578 if (curs.y == marg_b)
1579 scroll(marg_t, marg_b, 1, TRUE);
1580 else if (curs.y < rows - 1)
1581 curs.y++;
1582 curs.x = 0;
1583 fix_cpos;
1584 }
1585 *cpos++ = UCSWIDE | curr_attr;
1586 break;
1587 case 1:
1588 *cpos++ = c | curr_attr;
1589 break;
1590 default:
1591 continue;
1592 }
1593 }
1594 curs.x++;
1595 if (curs.x == cols) {
1596 cpos--;
1597 curs.x--;
1598 wrapnext = TRUE;
1599 if (wrap && vt52_mode) {
1600 cpos[1] |= LATTR_WRAPPED;
1601 if (curs.y == marg_b)
1602 scroll(marg_t, marg_b, 1, TRUE);
1603 else if (curs.y < rows - 1)
1604 curs.y++;
1605 curs.x = 0;
1606 fix_cpos;
1607 wrapnext = FALSE;
1608 }
1609 }
1610 seen_disp_event = 1;
1611 break;
1612
1613 case OSC_MAYBE_ST:
1614 /*
1615 * This state is virtually identical to SEEN_ESC, with the
1616 * exception that we have an OSC sequence in the pipeline,
1617 * and _if_ we see a backslash, we process it.
1618 */
1619 if (c == '\\') {
1620 do_osc();
1621 termstate = TOPLEVEL;
1622 break;
1623 }
1624 /* else fall through */
1625 case SEEN_ESC:
1626 if (c >= ' ' && c <= '/') {
1627 if (esc_query)
1628 esc_query = -1;
1629 else
1630 esc_query = c;
1631 break;
1632 }
1633 termstate = TOPLEVEL;
1634 switch (ANSI(c, esc_query)) {
1635 case '[': /* enter CSI mode */
1636 termstate = SEEN_CSI;
1637 esc_nargs = 1;
1638 esc_args[0] = ARG_DEFAULT;
1639 esc_query = FALSE;
1640 break;
1641 case ']': /* xterm escape sequences */
1642 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1643 compatibility(OTHER);
1644 termstate = SEEN_OSC;
1645 esc_args[0] = 0;
1646 break;
1647 case '7': /* save cursor */
1648 compatibility(VT100);
1649 save_cursor(TRUE);
1650 break;
1651 case '8': /* restore cursor */
1652 compatibility(VT100);
1653 save_cursor(FALSE);
1654 seen_disp_event = TRUE;
1655 break;
1656 case '=':
1657 compatibility(VT100);
1658 app_keypad_keys = TRUE;
1659 break;
1660 case '>':
1661 compatibility(VT100);
1662 app_keypad_keys = FALSE;
1663 break;
1664 case 'D': /* exactly equivalent to LF */
1665 compatibility(VT100);
1666 if (curs.y == marg_b)
1667 scroll(marg_t, marg_b, 1, TRUE);
1668 else if (curs.y < rows - 1)
1669 curs.y++;
1670 fix_cpos;
1671 wrapnext = FALSE;
1672 seen_disp_event = TRUE;
1673 break;
1674 case 'E': /* exactly equivalent to CR-LF */
1675 compatibility(VT100);
1676 curs.x = 0;
1677 if (curs.y == marg_b)
1678 scroll(marg_t, marg_b, 1, TRUE);
1679 else if (curs.y < rows - 1)
1680 curs.y++;
1681 fix_cpos;
1682 wrapnext = FALSE;
1683 seen_disp_event = TRUE;
1684 break;
1685 case 'M': /* reverse index - backwards LF */
1686 compatibility(VT100);
1687 if (curs.y == marg_t)
1688 scroll(marg_t, marg_b, -1, TRUE);
1689 else if (curs.y > 0)
1690 curs.y--;
1691 fix_cpos;
1692 wrapnext = FALSE;
1693 seen_disp_event = TRUE;
1694 break;
1695 case 'Z': /* terminal type query */
1696 compatibility(VT100);
1697 ldisc_send(id_string, strlen(id_string), 0);
1698 break;
1699 case 'c': /* restore power-on settings */
1700 compatibility(VT100);
1701 power_on();
1702 if (reset_132) {
1703 if (!cfg.no_remote_resize)
1704 request_resize(80, rows);
1705 reset_132 = 0;
1706 }
1707 fix_cpos;
1708 disptop = 0;
1709 seen_disp_event = TRUE;
1710 break;
1711 case 'H': /* set a tab */
1712 compatibility(VT100);
1713 tabs[curs.x] = TRUE;
1714 break;
1715
1716 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1717 compatibility(VT100);
1718 {
1719 unsigned long *ldata;
1720 int i, j;
1721 pos scrtop, scrbot;
1722
1723 for (i = 0; i < rows; i++) {
1724 ldata = lineptr(i);
1725 for (j = 0; j < cols; j++)
1726 ldata[j] = ATTR_DEFAULT | 'E';
1727 ldata[cols] = 0;
1728 }
1729 disptop = 0;
1730 seen_disp_event = TRUE;
1731 scrtop.x = scrtop.y = 0;
1732 scrbot.x = 0;
1733 scrbot.y = rows;
1734 check_selection(scrtop, scrbot);
1735 }
1736 break;
1737
1738 case ANSI('3', '#'):
1739 case ANSI('4', '#'):
1740 case ANSI('5', '#'):
1741 case ANSI('6', '#'):
1742 compatibility(VT100);
1743 {
1744 unsigned long nlattr;
1745 unsigned long *ldata;
1746 switch (ANSI(c, esc_query)) {
1747 case ANSI('3', '#'):
1748 nlattr = LATTR_TOP;
1749 break;
1750 case ANSI('4', '#'):
1751 nlattr = LATTR_BOT;
1752 break;
1753 case ANSI('5', '#'):
1754 nlattr = LATTR_NORM;
1755 break;
1756 default: /* spiritually case ANSI('6', '#'): */
1757 nlattr = LATTR_WIDE;
1758 break;
1759 }
1760 ldata = lineptr(curs.y);
1761 ldata[cols] &= ~LATTR_MODE;
1762 ldata[cols] |= nlattr;
1763 }
1764 break;
1765
1766 case ANSI('A', '('):
1767 compatibility(VT100);
1768 if (!cfg.no_remote_charset)
1769 cset_attr[0] = ATTR_GBCHR;
1770 break;
1771 case ANSI('B', '('):
1772 compatibility(VT100);
1773 if (!cfg.no_remote_charset)
1774 cset_attr[0] = ATTR_ASCII;
1775 break;
1776 case ANSI('0', '('):
1777 compatibility(VT100);
1778 if (!cfg.no_remote_charset)
1779 cset_attr[0] = ATTR_LINEDRW;
1780 break;
1781 case ANSI('U', '('):
1782 compatibility(OTHER);
1783 if (!cfg.no_remote_charset)
1784 cset_attr[0] = ATTR_SCOACS;
1785 break;
1786
1787 case ANSI('A', ')'):
1788 compatibility(VT100);
1789 if (!cfg.no_remote_charset)
1790 cset_attr[1] = ATTR_GBCHR;
1791 break;
1792 case ANSI('B', ')'):
1793 compatibility(VT100);
1794 if (!cfg.no_remote_charset)
1795 cset_attr[1] = ATTR_ASCII;
1796 break;
1797 case ANSI('0', ')'):
1798 compatibility(VT100);
1799 if (!cfg.no_remote_charset)
1800 cset_attr[1] = ATTR_LINEDRW;
1801 break;
1802 case ANSI('U', ')'):
1803 compatibility(OTHER);
1804 if (!cfg.no_remote_charset)
1805 cset_attr[1] = ATTR_SCOACS;
1806 break;
1807
1808 case ANSI('8', '%'): /* Old Linux code */
1809 case ANSI('G', '%'):
1810 compatibility(OTHER);
1811 if (!cfg.no_remote_charset)
1812 utf = 1;
1813 break;
1814 case ANSI('@', '%'):
1815 compatibility(OTHER);
1816 if (!cfg.no_remote_charset)
1817 utf = 0;
1818 break;
1819 }
1820 break;
1821 case SEEN_CSI:
1822 termstate = TOPLEVEL; /* default */
1823 if (isdigit(c)) {
1824 if (esc_nargs <= ARGS_MAX) {
1825 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1826 esc_args[esc_nargs - 1] = 0;
1827 esc_args[esc_nargs - 1] =
1828 10 * esc_args[esc_nargs - 1] + c - '0';
1829 }
1830 termstate = SEEN_CSI;
1831 } else if (c == ';') {
1832 if (++esc_nargs <= ARGS_MAX)
1833 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1834 termstate = SEEN_CSI;
1835 } else if (c < '@') {
1836 if (esc_query)
1837 esc_query = -1;
1838 else if (c == '?')
1839 esc_query = TRUE;
1840 else
1841 esc_query = c;
1842 termstate = SEEN_CSI;
1843 } else
1844 switch (ANSI(c, esc_query)) {
1845 case 'A': /* move up N lines */
1846 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1847 seen_disp_event = TRUE;
1848 break;
1849 case 'e': /* move down N lines */
1850 compatibility(ANSI);
1851 /* FALLTHROUGH */
1852 case 'B':
1853 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1854 seen_disp_event = TRUE;
1855 break;
1856 case ANSI('c', '>'): /* report xterm version */
1857 compatibility(OTHER);
1858 /* this reports xterm version 136 so that VIM can
1859 use the drag messages from the mouse reporting */
1860 ldisc_send("\033[>0;136;0c", 11, 0);
1861 break;
1862 case 'a': /* move right N cols */
1863 compatibility(ANSI);
1864 /* FALLTHROUGH */
1865 case 'C':
1866 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1867 seen_disp_event = TRUE;
1868 break;
1869 case 'D': /* move left N cols */
1870 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1871 seen_disp_event = TRUE;
1872 break;
1873 case 'E': /* move down N lines and CR */
1874 compatibility(ANSI);
1875 move(0, curs.y + def(esc_args[0], 1), 1);
1876 seen_disp_event = TRUE;
1877 break;
1878 case 'F': /* move up N lines and CR */
1879 compatibility(ANSI);
1880 move(0, curs.y - def(esc_args[0], 1), 1);
1881 seen_disp_event = TRUE;
1882 break;
1883 case 'G':
1884 case '`': /* set horizontal posn */
1885 compatibility(ANSI);
1886 move(def(esc_args[0], 1) - 1, curs.y, 0);
1887 seen_disp_event = TRUE;
1888 break;
1889 case 'd': /* set vertical posn */
1890 compatibility(ANSI);
1891 move(curs.x,
1892 (dec_om ? marg_t : 0) + def(esc_args[0],
1893 1) - 1,
1894 (dec_om ? 2 : 0));
1895 seen_disp_event = TRUE;
1896 break;
1897 case 'H':
1898 case 'f': /* set horz and vert posns at once */
1899 if (esc_nargs < 2)
1900 esc_args[1] = ARG_DEFAULT;
1901 move(def(esc_args[1], 1) - 1,
1902 (dec_om ? marg_t : 0) + def(esc_args[0],
1903 1) - 1,
1904 (dec_om ? 2 : 0));
1905 seen_disp_event = TRUE;
1906 break;
1907 case 'J': /* erase screen or parts of it */
1908 {
1909 unsigned int i = def(esc_args[0], 0) + 1;
1910 if (i > 3)
1911 i = 0;
1912 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1913 }
1914 disptop = 0;
1915 seen_disp_event = TRUE;
1916 break;
1917 case 'K': /* erase line or parts of it */
1918 {
1919 unsigned int i = def(esc_args[0], 0) + 1;
1920 if (i > 3)
1921 i = 0;
1922 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1923 }
1924 seen_disp_event = TRUE;
1925 break;
1926 case 'L': /* insert lines */
1927 compatibility(VT102);
1928 if (curs.y <= marg_b)
1929 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1930 FALSE);
1931 fix_cpos;
1932 seen_disp_event = TRUE;
1933 break;
1934 case 'M': /* delete lines */
1935 compatibility(VT102);
1936 if (curs.y <= marg_b)
1937 scroll(curs.y, marg_b, def(esc_args[0], 1),
1938 TRUE);
1939 fix_cpos;
1940 seen_disp_event = TRUE;
1941 break;
1942 case '@': /* insert chars */
1943 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1944 compatibility(VT102);
1945 insch(def(esc_args[0], 1));
1946 seen_disp_event = TRUE;
1947 break;
1948 case 'P': /* delete chars */
1949 compatibility(VT102);
1950 insch(-def(esc_args[0], 1));
1951 seen_disp_event = TRUE;
1952 break;
1953 case 'c': /* terminal type query */
1954 compatibility(VT100);
1955 /* This is the response for a VT102 */
1956 ldisc_send(id_string, strlen(id_string), 0);
1957 break;
1958 case 'n': /* cursor position query */
1959 if (esc_args[0] == 6) {
1960 char buf[32];
1961 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1962 curs.x + 1);
1963 ldisc_send(buf, strlen(buf), 0);
1964 } else if (esc_args[0] == 5) {
1965 ldisc_send("\033[0n", 4, 0);
1966 }
1967 break;
1968 case 'h': /* toggle modes to high */
1969 case ANSI_QUE('h'):
1970 compatibility(VT100);
1971 {
1972 int i;
1973 for (i = 0; i < esc_nargs; i++)
1974 toggle_mode(esc_args[i], esc_query, TRUE);
1975 }
1976 break;
1977 case 'i':
1978 case ANSI_QUE('i'):
1979 compatibility(VT100);
1980 {
1981 if (esc_nargs != 1) break;
1982 if (esc_args[0] == 5 && *cfg.printer) {
1983 printing = TRUE;
1984 only_printing = !esc_query;
1985 print_state = 0;
1986 term_print_setup();
1987 } else if (esc_args[0] == 4 && printing) {
1988 term_print_finish();
1989 }
1990 }
1991 break;
1992 case 'l': /* toggle modes to low */
1993 case ANSI_QUE('l'):
1994 compatibility(VT100);
1995 {
1996 int i;
1997 for (i = 0; i < esc_nargs; i++)
1998 toggle_mode(esc_args[i], esc_query, FALSE);
1999 }
2000 break;
2001 case 'g': /* clear tabs */
2002 compatibility(VT100);
2003 if (esc_nargs == 1) {
2004 if (esc_args[0] == 0) {
2005 tabs[curs.x] = FALSE;
2006 } else if (esc_args[0] == 3) {
2007 int i;
2008 for (i = 0; i < cols; i++)
2009 tabs[i] = FALSE;
2010 }
2011 }
2012 break;
2013 case 'r': /* set scroll margins */
2014 compatibility(VT100);
2015 if (esc_nargs <= 2) {
2016 int top, bot;
2017 top = def(esc_args[0], 1) - 1;
2018 bot = (esc_nargs <= 1
2019 || esc_args[1] ==
2020 0 ? rows : def(esc_args[1], rows)) - 1;
2021 if (bot >= rows)
2022 bot = rows - 1;
2023 /* VTTEST Bug 9 - if region is less than 2 lines
2024 * don't change region.
2025 */
2026 if (bot - top > 0) {
2027 marg_t = top;
2028 marg_b = bot;
2029 curs.x = 0;
2030 /*
2031 * I used to think the cursor should be
2032 * placed at the top of the newly marginned
2033 * area. Apparently not: VMS TPU falls over
2034 * if so.
2035 *
2036 * Well actually it should for Origin mode - RDB
2037 */
2038 curs.y = (dec_om ? marg_t : 0);
2039 fix_cpos;
2040 seen_disp_event = TRUE;
2041 }
2042 }
2043 break;
2044 case 'm': /* set graphics rendition */
2045 {
2046 /*
2047 * A VT100 without the AVO only had one attribute, either
2048 * underline or reverse video depending on the cursor type,
2049 * this was selected by CSI 7m.
2050 *
2051 * case 2:
2052 * This is sometimes DIM, eg on the GIGI and Linux
2053 * case 8:
2054 * This is sometimes INVIS various ANSI.
2055 * case 21:
2056 * This like 22 disables BOLD, DIM and INVIS
2057 *
2058 * The ANSI colours appear on any terminal that has colour
2059 * (obviously) but the interaction between sgr0 and the
2060 * colours varies but is usually related to the background
2061 * colour erase item.
2062 * The interaction between colour attributes and the mono
2063 * ones is also very implementation dependent.
2064 *
2065 * The 39 and 49 attributes are likely to be unimplemented.
2066 */
2067 int i;
2068 for (i = 0; i < esc_nargs; i++) {
2069 switch (def(esc_args[i], 0)) {
2070 case 0: /* restore defaults */
2071 curr_attr = ATTR_DEFAULT;
2072 break;
2073 case 1: /* enable bold */
2074 compatibility(VT100AVO);
2075 curr_attr |= ATTR_BOLD;
2076 break;
2077 case 21: /* (enable double underline) */
2078 compatibility(OTHER);
2079 case 4: /* enable underline */
2080 compatibility(VT100AVO);
2081 curr_attr |= ATTR_UNDER;
2082 break;
2083 case 5: /* enable blink */
2084 compatibility(VT100AVO);
2085 curr_attr |= ATTR_BLINK;
2086 break;
2087 case 7: /* enable reverse video */
2088 curr_attr |= ATTR_REVERSE;
2089 break;
2090 case 10: /* SCO acs off */
2091 compatibility(SCOANSI);
2092 if (cfg.no_remote_charset) break;
2093 sco_acs = 0; break;
2094 case 11: /* SCO acs on */
2095 compatibility(SCOANSI);
2096 if (cfg.no_remote_charset) break;
2097 sco_acs = 1; break;
2098 case 12: /* SCO acs on flipped */
2099 compatibility(SCOANSI);
2100 if (cfg.no_remote_charset) break;
2101 sco_acs = 2; break;
2102 case 22: /* disable bold */
2103 compatibility2(OTHER, VT220);
2104 curr_attr &= ~ATTR_BOLD;
2105 break;
2106 case 24: /* disable underline */
2107 compatibility2(OTHER, VT220);
2108 curr_attr &= ~ATTR_UNDER;
2109 break;
2110 case 25: /* disable blink */
2111 compatibility2(OTHER, VT220);
2112 curr_attr &= ~ATTR_BLINK;
2113 break;
2114 case 27: /* disable reverse video */
2115 compatibility2(OTHER, VT220);
2116 curr_attr &= ~ATTR_REVERSE;
2117 break;
2118 case 30:
2119 case 31:
2120 case 32:
2121 case 33:
2122 case 34:
2123 case 35:
2124 case 36:
2125 case 37:
2126 /* foreground */
2127 curr_attr &= ~ATTR_FGMASK;
2128 curr_attr |=
2129 (esc_args[i] - 30) << ATTR_FGSHIFT;
2130 break;
2131 case 39: /* default-foreground */
2132 curr_attr &= ~ATTR_FGMASK;
2133 curr_attr |= ATTR_DEFFG;
2134 break;
2135 case 40:
2136 case 41:
2137 case 42:
2138 case 43:
2139 case 44:
2140 case 45:
2141 case 46:
2142 case 47:
2143 /* background */
2144 curr_attr &= ~ATTR_BGMASK;
2145 curr_attr |=
2146 (esc_args[i] - 40) << ATTR_BGSHIFT;
2147 break;
2148 case 49: /* default-background */
2149 curr_attr &= ~ATTR_BGMASK;
2150 curr_attr |= ATTR_DEFBG;
2151 break;
2152 }
2153 }
2154 if (use_bce)
2155 erase_char = (' ' | ATTR_ASCII |
2156 (curr_attr &
2157 (ATTR_FGMASK | ATTR_BGMASK)));
2158 }
2159 break;
2160 case 's': /* save cursor */
2161 save_cursor(TRUE);
2162 break;
2163 case 'u': /* restore cursor */
2164 save_cursor(FALSE);
2165 seen_disp_event = TRUE;
2166 break;
2167 case 't': /* set page size - ie window height */
2168 /*
2169 * VT340/VT420 sequence DECSLPP, DEC only allows values
2170 * 24/25/36/48/72/144 other emulators (eg dtterm) use
2171 * illegal values (eg first arg 1..9) for window changing
2172 * and reports.
2173 */
2174 if (esc_nargs <= 1
2175 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
2176 compatibility(VT340TEXT);
2177 if (!cfg.no_remote_resize)
2178 request_resize(cols, def(esc_args[0], 24));
2179 deselect();
2180 } else if (esc_nargs >= 1 &&
2181 esc_args[0] >= 1 &&
2182 esc_args[0] < 24) {
2183 compatibility(OTHER);
2184
2185 switch (esc_args[0]) {
2186 int x, y, len;
2187 char buf[80], *p;
2188 case 1:
2189 set_iconic(FALSE);
2190 break;
2191 case 2:
2192 set_iconic(TRUE);
2193 break;
2194 case 3:
2195 if (esc_nargs >= 3) {
2196 if (!cfg.no_remote_resize)
2197 move_window(def(esc_args[1], 0),
2198 def(esc_args[2], 0));
2199 }
2200 break;
2201 case 4:
2202 /* We should resize the window to a given
2203 * size in pixels here, but currently our
2204 * resizing code isn't healthy enough to
2205 * manage it. */
2206 break;
2207 case 5:
2208 set_zorder(TRUE); /* move to top */
2209 break;
2210 case 6:
2211 set_zorder(FALSE); /* move to bottom */
2212 break;
2213 case 7:
2214 refresh_window();
2215 break;
2216 case 8:
2217 if (esc_nargs >= 3) {
2218 if (!cfg.no_remote_resize)
2219 request_resize(def(esc_args[2], cfg.width),
2220 def(esc_args[1], cfg.height));
2221 }
2222 break;
2223 case 9:
2224 if (esc_nargs >= 2)
2225 set_zoomed(esc_args[1] ? TRUE : FALSE);
2226 break;
2227 case 11:
2228 ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
2229 4, 0);
2230 break;
2231 case 13:
2232 get_window_pos(&x, &y);
2233 len = sprintf(buf, "\033[3;%d;%dt", x, y);
2234 ldisc_send(buf, len, 0);
2235 break;
2236 case 14:
2237 get_window_pixels(&x, &y);
2238 len = sprintf(buf, "\033[4;%d;%dt", x, y);
2239 ldisc_send(buf, len, 0);
2240 break;
2241 case 18:
2242 len = sprintf(buf, "\033[8;%d;%dt",
2243 rows, cols);
2244 ldisc_send(buf, len, 0);
2245 break;
2246 case 19:
2247 /*
2248 * Hmmm. Strictly speaking we
2249 * should return `the size of the
2250 * screen in characters', but
2251 * that's not easy: (a) window
2252 * furniture being what it is it's
2253 * hard to compute, and (b) in
2254 * resize-font mode maximising the
2255 * window wouldn't change the
2256 * number of characters. *shrug*. I
2257 * think we'll ignore it for the
2258 * moment and see if anyone
2259 * complains, and then ask them
2260 * what they would like it to do.
2261 */
2262 break;
2263 case 20:
2264 p = get_window_title(TRUE);
2265 len = strlen(p);
2266 ldisc_send("\033]L", 3, 0);
2267 ldisc_send(p, len, 0);
2268 ldisc_send("\033\\", 2, 0);
2269 break;
2270 case 21:
2271 p = get_window_title(FALSE);
2272 len = strlen(p);
2273 ldisc_send("\033]l", 3, 0);
2274 ldisc_send(p, len, 0);
2275 ldisc_send("\033\\", 2, 0);
2276 break;
2277 }
2278 }
2279 break;
2280 case 'S':
2281 compatibility(SCOANSI);
2282 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
2283 fix_cpos;
2284 wrapnext = FALSE;
2285 seen_disp_event = TRUE;
2286 break;
2287 case 'T':
2288 compatibility(SCOANSI);
2289 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
2290 fix_cpos;
2291 wrapnext = FALSE;
2292 seen_disp_event = TRUE;
2293 break;
2294 case ANSI('|', '*'):
2295 /* VT420 sequence DECSNLS
2296 * Set number of lines on screen
2297 * VT420 uses VGA like hardware and can support any size in
2298 * reasonable range (24..49 AIUI) with no default specified.
2299 */
2300 compatibility(VT420);
2301 if (esc_nargs == 1 && esc_args[0] > 0) {
2302 if (!cfg.no_remote_resize)
2303 request_resize(cols, def(esc_args[0], cfg.height));
2304 deselect();
2305 }
2306 break;
2307 case ANSI('|', '$'):
2308 /* VT340/VT420 sequence DECSCPP
2309 * Set number of columns per page
2310 * Docs imply range is only 80 or 132, but I'll allow any.
2311 */
2312 compatibility(VT340TEXT);
2313 if (esc_nargs <= 1) {
2314 if (!cfg.no_remote_resize)
2315 request_resize(def(esc_args[0], cfg.width), rows);
2316 deselect();
2317 }
2318 break;
2319 case 'X': /* write N spaces w/o moving cursor */
2320 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2321 compatibility(ANSIMIN);
2322 {
2323 int n = def(esc_args[0], 1);
2324 pos cursplus;
2325 unsigned long *p = cpos;
2326 if (n > cols - curs.x)
2327 n = cols - curs.x;
2328 cursplus = curs;
2329 cursplus.x += n;
2330 check_selection(curs, cursplus);
2331 while (n--)
2332 *p++ = erase_char;
2333 seen_disp_event = TRUE;
2334 }
2335 break;
2336 case 'x': /* report terminal characteristics */
2337 compatibility(VT100);
2338 {
2339 char buf[32];
2340 int i = def(esc_args[0], 0);
2341 if (i == 0 || i == 1) {
2342 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2343 buf[2] += i;
2344 ldisc_send(buf, 20, 0);
2345 }
2346 }
2347 break;
2348 case 'Z': /* BackTab for xterm */
2349 compatibility(OTHER);
2350 {
2351 int i = def(esc_args[0], 1);
2352 pos old_curs = curs;
2353
2354 for(;i>0 && curs.x>0; i--) {
2355 do {
2356 curs.x--;
2357 } while (curs.x >0 && !tabs[curs.x]);
2358 }
2359 fix_cpos;
2360 check_selection(old_curs, curs);
2361 }
2362 break;
2363 case ANSI('L', '='):
2364 compatibility(OTHER);
2365 use_bce = (esc_args[0] <= 0);
2366 erase_char = ERASE_CHAR;
2367 if (use_bce)
2368 erase_char = (' ' | ATTR_ASCII |
2369 (curr_attr &
2370 (ATTR_FGMASK | ATTR_BGMASK)));
2371 break;
2372 case ANSI('E', '='):
2373 compatibility(OTHER);
2374 blink_is_real = (esc_args[0] >= 1);
2375 break;
2376 case ANSI('p', '"'):
2377 /* Allow the host to make this emulator a 'perfect' VT102.
2378 * This first appeared in the VT220, but we do need to get
2379 * back to PuTTY mode so I won't check it.
2380 *
2381 * The arg in 40..42,50 are a PuTTY extension.
2382 * The 2nd arg, 8bit vs 7bit is not checked.
2383 *
2384 * Setting VT102 mode should also change the Fkeys to
2385 * generate PF* codes as a real VT102 has no Fkeys.
2386 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2387 * send nothing.
2388 *
2389 * Note ESC c will NOT change this!
2390 */
2391
2392 switch (esc_args[0]) {
2393 case 61:
2394 compatibility_level &= ~TM_VTXXX;
2395 compatibility_level |= TM_VT102;
2396 break;
2397 case 62:
2398 compatibility_level &= ~TM_VTXXX;
2399 compatibility_level |= TM_VT220;
2400 break;
2401
2402 default:
2403 if (esc_args[0] > 60 && esc_args[0] < 70)
2404 compatibility_level |= TM_VTXXX;
2405 break;
2406
2407 case 40:
2408 compatibility_level &= TM_VTXXX;
2409 break;
2410 case 41:
2411 compatibility_level = TM_PUTTY;
2412 break;
2413 case 42:
2414 compatibility_level = TM_SCOANSI;
2415 break;
2416
2417 case ARG_DEFAULT:
2418 compatibility_level = TM_PUTTY;
2419 break;
2420 case 50:
2421 break;
2422 }
2423
2424 /* Change the response to CSI c */
2425 if (esc_args[0] == 50) {
2426 int i;
2427 char lbuf[64];
2428 strcpy(id_string, "\033[?");
2429 for (i = 1; i < esc_nargs; i++) {
2430 if (i != 1)
2431 strcat(id_string, ";");
2432 sprintf(lbuf, "%d", esc_args[i]);
2433 strcat(id_string, lbuf);
2434 }
2435 strcat(id_string, "c");
2436 }
2437 #if 0
2438 /* Is this a good idea ?
2439 * Well we should do a soft reset at this point ...
2440 */
2441 if (!has_compat(VT420) && has_compat(VT100)) {
2442 if (!cfg.no_remote_resize) {
2443 if (reset_132)
2444 request_resize(132, 24);
2445 else
2446 request_resize(80, 24);
2447 }
2448 }
2449 #endif
2450 break;
2451 }
2452 break;
2453 case SEEN_OSC:
2454 osc_w = FALSE;
2455 switch (c) {
2456 case 'P': /* Linux palette sequence */
2457 termstate = SEEN_OSC_P;
2458 osc_strlen = 0;
2459 break;
2460 case 'R': /* Linux palette reset */
2461 palette_reset();
2462 term_invalidate();
2463 termstate = TOPLEVEL;
2464 break;
2465 case 'W': /* word-set */
2466 termstate = SEEN_OSC_W;
2467 osc_w = TRUE;
2468 break;
2469 case '0':
2470 case '1':
2471 case '2':
2472 case '3':
2473 case '4':
2474 case '5':
2475 case '6':
2476 case '7':
2477 case '8':
2478 case '9':
2479 esc_args[0] = 10 * esc_args[0] + c - '0';
2480 break;
2481 case 'L':
2482 /*
2483 * Grotty hack to support xterm and DECterm title
2484 * sequences concurrently.
2485 */
2486 if (esc_args[0] == 2) {
2487 esc_args[0] = 1;
2488 break;
2489 }
2490 /* else fall through */
2491 default:
2492 termstate = OSC_STRING;
2493 osc_strlen = 0;
2494 }
2495 break;
2496 case OSC_STRING:
2497 /*
2498 * This OSC stuff is EVIL. It takes just one character to get into
2499 * sysline mode and it's not initially obvious how to get out.
2500 * So I've added CR and LF as string aborts.
2501 * This shouldn't effect compatibility as I believe embedded
2502 * control characters are supposed to be interpreted (maybe?)
2503 * and they don't display anything useful anyway.
2504 *
2505 * -- RDB
2506 */
2507 if (c == '\n' || c == '\r') {
2508 termstate = TOPLEVEL;
2509 } else if (c == 0234 || c == '\007') {
2510 /*
2511 * These characters terminate the string; ST and BEL
2512 * terminate the sequence and trigger instant
2513 * processing of it, whereas ESC goes back to SEEN_ESC
2514 * mode unless it is followed by \, in which case it is
2515 * synonymous with ST in the first place.
2516 */
2517 do_osc();
2518 termstate = TOPLEVEL;
2519 } else if (c == '\033')
2520 termstate = OSC_MAYBE_ST;
2521 else if (osc_strlen < OSC_STR_MAX)
2522 osc_string[osc_strlen++] = c;
2523 break;
2524 case SEEN_OSC_P:
2525 {
2526 int max = (osc_strlen == 0 ? 21 : 16);
2527 int val;
2528 if (c >= '0' && c <= '9')
2529 val = c - '0';
2530 else if (c >= 'A' && c <= 'A' + max - 10)
2531 val = c - 'A' + 10;
2532 else if (c >= 'a' && c <= 'a' + max - 10)
2533 val = c - 'a' + 10;
2534 else {
2535 termstate = TOPLEVEL;
2536 break;
2537 }
2538 osc_string[osc_strlen++] = val;
2539 if (osc_strlen >= 7) {
2540 palette_set(osc_string[0],
2541 osc_string[1] * 16 + osc_string[2],
2542 osc_string[3] * 16 + osc_string[4],
2543 osc_string[5] * 16 + osc_string[6]);
2544 term_invalidate();
2545 termstate = TOPLEVEL;
2546 }
2547 }
2548 break;
2549 case SEEN_OSC_W:
2550 switch (c) {
2551 case '0':
2552 case '1':
2553 case '2':
2554 case '3':
2555 case '4':
2556 case '5':
2557 case '6':
2558 case '7':
2559 case '8':
2560 case '9':
2561 esc_args[0] = 10 * esc_args[0] + c - '0';
2562 break;
2563 default:
2564 termstate = OSC_STRING;
2565 osc_strlen = 0;
2566 }
2567 break;
2568 case VT52_ESC:
2569 termstate = TOPLEVEL;
2570 seen_disp_event = TRUE;
2571 switch (c) {
2572 case 'A':
2573 move(curs.x, curs.y - 1, 1);
2574 break;
2575 case 'B':
2576 move(curs.x, curs.y + 1, 1);
2577 break;
2578 case 'C':
2579 move(curs.x + 1, curs.y, 1);
2580 break;
2581 case 'D':
2582 move(curs.x - 1, curs.y, 1);
2583 break;
2584 /*
2585 * From the VT100 Manual
2586 * NOTE: The special graphics characters in the VT100
2587 * are different from those in the VT52
2588 *
2589 * From VT102 manual:
2590 * 137 _ Blank - Same
2591 * 140 ` Reserved - Humm.
2592 * 141 a Solid rectangle - Similar
2593 * 142 b 1/ - Top half of fraction for the
2594 * 143 c 3/ - subscript numbers below.
2595 * 144 d 5/
2596 * 145 e 7/
2597 * 146 f Degrees - Same
2598 * 147 g Plus or minus - Same
2599 * 150 h Right arrow
2600 * 151 i Ellipsis (dots)
2601 * 152 j Divide by
2602 * 153 k Down arrow
2603 * 154 l Bar at scan 0
2604 * 155 m Bar at scan 1
2605 * 156 n Bar at scan 2
2606 * 157 o Bar at scan 3 - Similar
2607 * 160 p Bar at scan 4 - Similar
2608 * 161 q Bar at scan 5 - Similar
2609 * 162 r Bar at scan 6 - Same
2610 * 163 s Bar at scan 7 - Similar
2611 * 164 t Subscript 0
2612 * 165 u Subscript 1
2613 * 166 v Subscript 2
2614 * 167 w Subscript 3
2615 * 170 x Subscript 4
2616 * 171 y Subscript 5
2617 * 172 z Subscript 6
2618 * 173 { Subscript 7
2619 * 174 | Subscript 8
2620 * 175 } Subscript 9
2621 * 176 ~ Paragraph
2622 *
2623 */
2624 case 'F':
2625 cset_attr[cset = 0] = ATTR_LINEDRW;
2626 break;
2627 case 'G':
2628 cset_attr[cset = 0] = ATTR_ASCII;
2629 break;
2630 case 'H':
2631 move(0, 0, 0);
2632 break;
2633 case 'I':
2634 if (curs.y == 0)
2635 scroll(0, rows - 1, -1, TRUE);
2636 else if (curs.y > 0)
2637 curs.y--;
2638 fix_cpos;
2639 wrapnext = FALSE;
2640 break;
2641 case 'J':
2642 erase_lots(FALSE, FALSE, TRUE);
2643 disptop = 0;
2644 break;
2645 case 'K':
2646 erase_lots(TRUE, FALSE, TRUE);
2647 break;
2648 #if 0
2649 case 'V':
2650 /* XXX Print cursor line */
2651 break;
2652 case 'W':
2653 /* XXX Start controller mode */
2654 break;
2655 case 'X':
2656 /* XXX Stop controller mode */
2657 break;
2658 #endif
2659 case 'Y':
2660 termstate = VT52_Y1;
2661 break;
2662 case 'Z':
2663 ldisc_send("\033/Z", 3, 0);
2664 break;
2665 case '=':
2666 app_keypad_keys = TRUE;
2667 break;
2668 case '>':
2669 app_keypad_keys = FALSE;
2670 break;
2671 case '<':
2672 /* XXX This should switch to VT100 mode not current or default
2673 * VT mode. But this will only have effect in a VT220+
2674 * emulation.
2675 */
2676 vt52_mode = FALSE;
2677 blink_is_real = cfg.blinktext;
2678 break;
2679 #if 0
2680 case '^':
2681 /* XXX Enter auto print mode */
2682 break;
2683 case '_':
2684 /* XXX Exit auto print mode */
2685 break;
2686 case ']':
2687 /* XXX Print screen */
2688 break;
2689 #endif
2690
2691 #ifdef VT52_PLUS
2692 case 'E':
2693 /* compatibility(ATARI) */
2694 move(0, 0, 0);
2695 erase_lots(FALSE, FALSE, TRUE);
2696 disptop = 0;
2697 break;
2698 case 'L':
2699 /* compatibility(ATARI) */
2700 if (curs.y <= marg_b)
2701 scroll(curs.y, marg_b, -1, FALSE);
2702 break;
2703 case 'M':
2704 /* compatibility(ATARI) */
2705 if (curs.y <= marg_b)
2706 scroll(curs.y, marg_b, 1, TRUE);
2707 break;
2708 case 'b':
2709 /* compatibility(ATARI) */
2710 termstate = VT52_FG;
2711 break;
2712 case 'c':
2713 /* compatibility(ATARI) */
2714 termstate = VT52_BG;
2715 break;
2716 case 'd':
2717 /* compatibility(ATARI) */
2718 erase_lots(FALSE, TRUE, FALSE);
2719 disptop = 0;
2720 break;
2721 case 'e':
2722 /* compatibility(ATARI) */
2723 cursor_on = TRUE;
2724 break;
2725 case 'f':
2726 /* compatibility(ATARI) */
2727 cursor_on = FALSE;
2728 break;
2729 /* case 'j': Save cursor position - broken on ST */
2730 /* case 'k': Restore cursor position */
2731 case 'l':
2732 /* compatibility(ATARI) */
2733 erase_lots(TRUE, TRUE, TRUE);
2734 curs.x = 0;
2735 wrapnext = FALSE;
2736 fix_cpos;
2737 break;
2738 case 'o':
2739 /* compatibility(ATARI) */
2740 erase_lots(TRUE, TRUE, FALSE);
2741 break;
2742 case 'p':
2743 /* compatibility(ATARI) */
2744 curr_attr |= ATTR_REVERSE;
2745 break;
2746 case 'q':
2747 /* compatibility(ATARI) */
2748 curr_attr &= ~ATTR_REVERSE;
2749 break;
2750 case 'v': /* wrap Autowrap on - Wyse style */
2751 /* compatibility(ATARI) */
2752 wrap = 1;
2753 break;
2754 case 'w': /* Autowrap off */
2755 /* compatibility(ATARI) */
2756 wrap = 0;
2757 break;
2758
2759 case 'R':
2760 /* compatibility(OTHER) */
2761 vt52_bold = FALSE;
2762 curr_attr = ATTR_DEFAULT;
2763 if (use_bce)
2764 erase_char = (' ' | ATTR_ASCII |
2765 (curr_attr &
2766 (ATTR_FGMASK | ATTR_BGMASK)));
2767 break;
2768 case 'S':
2769 /* compatibility(VI50) */
2770 curr_attr |= ATTR_UNDER;
2771 break;
2772 case 'W':
2773 /* compatibility(VI50) */
2774 curr_attr &= ~ATTR_UNDER;
2775 break;
2776 case 'U':
2777 /* compatibility(VI50) */
2778 vt52_bold = TRUE;
2779 curr_attr |= ATTR_BOLD;
2780 break;
2781 case 'T':
2782 /* compatibility(VI50) */
2783 vt52_bold = FALSE;
2784 curr_attr &= ~ATTR_BOLD;
2785 break;
2786 #endif
2787 }
2788 break;
2789 case VT52_Y1:
2790 termstate = VT52_Y2;
2791 move(curs.x, c - ' ', 0);
2792 break;
2793 case VT52_Y2:
2794 termstate = TOPLEVEL;
2795 move(c - ' ', curs.y, 0);
2796 break;
2797
2798 #ifdef VT52_PLUS
2799 case VT52_FG:
2800 termstate = TOPLEVEL;
2801 curr_attr &= ~ATTR_FGMASK;
2802 curr_attr &= ~ATTR_BOLD;
2803 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2804 if ((c & 0x8) || vt52_bold)
2805 curr_attr |= ATTR_BOLD;
2806
2807 if (use_bce)
2808 erase_char = (' ' | ATTR_ASCII |
2809 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2810 break;
2811 case VT52_BG:
2812 termstate = TOPLEVEL;
2813 curr_attr &= ~ATTR_BGMASK;
2814 curr_attr &= ~ATTR_BLINK;
2815 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2816
2817 /* Note: bold background */
2818 if (c & 0x8)
2819 curr_attr |= ATTR_BLINK;
2820
2821 if (use_bce)
2822 erase_char = (' ' | ATTR_ASCII |
2823 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2824 break;
2825 #endif
2826 default: break; /* placate gcc warning about enum use */
2827 }
2828 if (selstate != NO_SELECTION) {
2829 pos cursplus = curs;
2830 incpos(cursplus);
2831 check_selection(curs, cursplus);
2832 }
2833 }
2834
2835 term_print_flush();
2836 }
2837
2838 #if 0
2839 /*
2840 * Compare two lines to determine whether they are sufficiently
2841 * alike to scroll-optimise one to the other. Return the degree of
2842 * similarity.
2843 */
2844 static int linecmp(unsigned long *a, unsigned long *b)
2845 {
2846 int i, n;
2847
2848 for (i = n = 0; i < cols; i++)
2849 n += (*a++ == *b++);
2850 return n;
2851 }
2852 #endif
2853
2854 /*
2855 * Given a context, update the window. Out of paranoia, we don't
2856 * allow WM_PAINT responses to do scrolling optimisations.
2857 */
2858 static void do_paint(Context ctx, int may_optimise)
2859 {
2860 int i, j, our_curs_y;
2861 unsigned long rv, cursor;
2862 pos scrpos;
2863 char ch[1024];
2864 long cursor_background = ERASE_CHAR;
2865 unsigned long ticks;
2866
2867 /*
2868 * Check the visual bell state.
2869 */
2870 if (in_vbell) {
2871 ticks = GETTICKCOUNT();
2872 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
2873 in_vbell = FALSE;
2874 }
2875
2876 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2877
2878 /* Depends on:
2879 * screen array, disptop, scrtop,
2880 * selection, rv,
2881 * cfg.blinkpc, blink_is_real, tblinker,
2882 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2883 */
2884
2885 /* Has the cursor position or type changed ? */
2886 if (cursor_on) {
2887 if (has_focus) {
2888 if (blinker || !cfg.blink_cur)
2889 cursor = TATTR_ACTCURS;
2890 else
2891 cursor = 0;
2892 } else
2893 cursor = TATTR_PASCURS;
2894 if (wrapnext)
2895 cursor |= TATTR_RIGHTCURS;
2896 } else
2897 cursor = 0;
2898 our_curs_y = curs.y - disptop;
2899
2900 if (dispcurs && (curstype != cursor ||
2901 dispcurs !=
2902 disptext + our_curs_y * (cols + 1) + curs.x)) {
2903 if (dispcurs > disptext &&
2904 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2905 dispcurs[-1] |= ATTR_INVALID;
2906 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2907 dispcurs[1] |= ATTR_INVALID;
2908 *dispcurs |= ATTR_INVALID;
2909 curstype = 0;
2910 }
2911 dispcurs = NULL;
2912
2913 /* The normal screen data */
2914 for (i = 0; i < rows; i++) {
2915 unsigned long *ldata;
2916 int lattr;
2917 int idx, dirty_line, dirty_run, selected;
2918 unsigned long attr = 0;
2919 int updated_line = 0;
2920 int start = 0;
2921 int ccount = 0;
2922 int last_run_dirty = 0;
2923
2924 scrpos.y = i + disptop;
2925 ldata = lineptr(scrpos.y);
2926 lattr = (ldata[cols] & LATTR_MODE);
2927
2928 idx = i * (cols + 1);
2929 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2930 disptext[idx + cols] = ldata[cols];
2931
2932 for (j = 0; j < cols; j++, idx++) {
2933 unsigned long tattr, tchar;
2934 unsigned long *d = ldata + j;
2935 int break_run;
2936 scrpos.x = j;
2937
2938 tchar = (*d & (CHAR_MASK | CSET_MASK));
2939 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2940 switch (tchar & CSET_MASK) {
2941 case ATTR_ASCII:
2942 tchar = unitab_line[tchar & 0xFF];
2943 break;
2944 case ATTR_LINEDRW:
2945 tchar = unitab_xterm[tchar & 0xFF];
2946 break;
2947 case ATTR_SCOACS:
2948 tchar = unitab_scoacs[tchar&0xFF];
2949 break;
2950 }
2951 tattr |= (tchar & CSET_MASK);
2952 tchar &= CHAR_MASK;
2953 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2954 tattr |= ATTR_WIDE;
2955
2956 /* Video reversing things */
2957 if (seltype == LEXICOGRAPHIC)
2958 selected = posle(selstart, scrpos) && poslt(scrpos, selend);
2959 else
2960 selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
2961 tattr = (tattr ^ rv
2962 ^ (selected ? ATTR_REVERSE : 0));
2963
2964 /* 'Real' blinking ? */
2965 if (blink_is_real && (tattr & ATTR_BLINK)) {
2966 if (has_focus && tblinker) {
2967 tchar = ' ';
2968 tattr &= ~CSET_MASK;
2969 tattr |= ATTR_ACP;
2970 }
2971 tattr &= ~ATTR_BLINK;
2972 }
2973
2974 /*
2975 * Check the font we'll _probably_ be using to see if
2976 * the character is wide when we don't want it to be.
2977 */
2978 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2979 if ((tattr & ATTR_WIDE) == 0 &&
2980 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2981 tattr |= ATTR_NARROW;
2982 } else if (disptext[idx]&ATTR_NARROW)
2983 tattr |= ATTR_NARROW;
2984
2985 /* Cursor here ? Save the 'background' */
2986 if (i == our_curs_y && j == curs.x) {
2987 cursor_background = tattr | tchar;
2988 dispcurs = disptext + idx;
2989 }
2990
2991 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2992 dirty_line = TRUE;
2993
2994 break_run = (tattr != attr || j - start >= sizeof(ch));
2995
2996 /* Special hack for VT100 Linedraw glyphs */
2997 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2998 && tchar <= 0xBD) break_run = TRUE;
2999
3000 if (!dbcs_screenfont && !dirty_line) {
3001 if ((tchar | tattr) == disptext[idx])
3002 break_run = TRUE;
3003 else if (!dirty_run && ccount == 1)
3004 break_run = TRUE;
3005 }
3006
3007 if (break_run) {
3008 if ((dirty_run || last_run_dirty) && ccount > 0) {
3009 do_text(ctx, start, i, ch, ccount, attr, lattr);
3010 updated_line = 1;
3011 }
3012 start = j;
3013 ccount = 0;
3014 attr = tattr;
3015 if (dbcs_screenfont)
3016 last_run_dirty = dirty_run;
3017 dirty_run = dirty_line;
3018 }
3019
3020 if ((tchar | tattr) != disptext[idx])
3021 dirty_run = TRUE;
3022 ch[ccount++] = (char) tchar;
3023 disptext[idx] = tchar | tattr;
3024
3025 /* If it's a wide char step along to the next one. */
3026 if (tattr & ATTR_WIDE) {
3027 if (++j < cols) {
3028 idx++;
3029 d++;
3030 /* Cursor is here ? Ouch! */
3031 if (i == our_curs_y && j == curs.x) {
3032 cursor_background = *d;
3033 dispcurs = disptext + idx;
3034 }
3035 if (disptext[idx] != *d)
3036 dirty_run = TRUE;
3037 disptext[idx] = *d;
3038 }
3039 }
3040 }
3041 if (dirty_run && ccount > 0) {
3042 do_text(ctx, start, i, ch, ccount, attr, lattr);
3043 updated_line = 1;
3044 }
3045
3046 /* Cursor on this line ? (and changed) */
3047 if (i == our_curs_y && (curstype != cursor || updated_line)) {
3048 ch[0] = (char) (cursor_background & CHAR_MASK);
3049 attr = (cursor_background & ATTR_MASK) | cursor;
3050 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
3051 curstype = cursor;
3052 }
3053 }
3054 }
3055
3056 /*
3057 * Flick the switch that says if blinking things should be shown or hidden.
3058 */
3059
3060 void term_blink(int flg)
3061 {
3062 static long last_blink = 0;
3063 static long last_tblink = 0;
3064 long now, blink_diff;
3065
3066 now = GETTICKCOUNT();
3067 blink_diff = now - last_tblink;
3068
3069 /* Make sure the text blinks no more than 2Hz; we'll use 0.45 s period. */
3070 if (blink_diff < 0 || blink_diff > (TICKSPERSEC * 9 / 20)) {
3071 last_tblink = now;
3072 tblinker = !tblinker;
3073 }
3074
3075 if (flg) {
3076 blinker = 1;
3077 last_blink = now;
3078 return;
3079 }
3080
3081 blink_diff = now - last_blink;
3082
3083 /* Make sure the cursor blinks no faster than system blink rate */
3084 if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
3085 return;
3086
3087 last_blink = now;
3088 blinker = !blinker;
3089 }
3090
3091 /*
3092 * Invalidate the whole screen so it will be repainted in full.
3093 */
3094 void term_invalidate(void)
3095 {
3096 int i;
3097
3098 for (i = 0; i < rows * (cols + 1); i++)
3099 disptext[i] = ATTR_INVALID;
3100 }
3101
3102 /*
3103 * Paint the window in response to a WM_PAINT message.
3104 */
3105 void term_paint(Context ctx, int left, int top, int right, int bottom)
3106 {
3107 int i, j;
3108 if (left < 0) left = 0;
3109 if (top < 0) top = 0;
3110 if (right >= cols) right = cols-1;
3111 if (bottom >= rows) bottom = rows-1;
3112
3113 for (i = top; i <= bottom && i < rows; i++) {
3114 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
3115 for (j = left; j <= right && j < cols; j++)
3116 disptext[i * (cols + 1) + j] = ATTR_INVALID;
3117 else
3118 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
3119 disptext[i * (cols + 1) + j] = ATTR_INVALID;
3120 }
3121
3122 /* This should happen soon enough, also for some reason it sometimes
3123 * fails to actually do anything when re-sizing ... painting the wrong
3124 * window perhaps ?
3125 */
3126 if (alt_pressed)
3127 do_paint (ctx, FALSE);
3128 }
3129
3130 /*
3131 * Attempt to scroll the scrollback. The second parameter gives the
3132 * position we want to scroll to; the first is +1 to denote that
3133 * this position is relative to the beginning of the scrollback, -1
3134 * to denote it is relative to the end, and 0 to denote that it is
3135 * relative to the current position.
3136 */
3137 void term_scroll(int rel, int where)
3138 {
3139 int sbtop = -count234(scrollback);
3140
3141 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
3142 if (disptop < sbtop)
3143 disptop = sbtop;
3144 if (disptop > 0)
3145 disptop = 0;
3146 update_sbar();
3147 term_update();
3148 }
3149
3150 static void clipme(pos top, pos bottom, int rect)
3151 {
3152 wchar_t *workbuf;
3153 wchar_t *wbptr; /* where next char goes within workbuf */
3154 int old_top_x;
3155 int wblen = 0; /* workbuf len */
3156 int buflen; /* amount of memory allocated to workbuf */
3157
3158 buflen = 5120; /* Default size */
3159 workbuf = smalloc(buflen * sizeof(wchar_t));
3160 wbptr = workbuf; /* start filling here */
3161 old_top_x = top.x; /* needed for rect==1 */
3162
3163 while (poslt(top, bottom)) {
3164 int nl = FALSE;
3165 unsigned long *ldata = lineptr(top.y);
3166 pos nlpos;
3167
3168 /*
3169 * nlpos will point at the maximum position on this line we
3170 * should copy up to. So we start it at the end of the
3171 * line...
3172 */
3173 nlpos.y = top.y;
3174 nlpos.x = cols;
3175
3176 /*
3177 * ... move it backwards if there's unused space at the end
3178 * of the line (and also set `nl' if this is the case,
3179 * because in normal selection mode this means we need a
3180 * newline at the end)...
3181 */
3182 if (!(ldata[cols] & LATTR_WRAPPED)) {
3183 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
3184 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
3185 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
3186 && poslt(top, nlpos))
3187 decpos(nlpos);
3188 if (poslt(nlpos, bottom))
3189 nl = TRUE;
3190 }
3191
3192 /*
3193 * ... and then clip it to the terminal x coordinate if
3194 * we're doing rectangular selection. (In this case we
3195 * still did the above, so that copying e.g. the right-hand
3196 * column from a table doesn't fill with spaces on the
3197 * right.)
3198 */
3199 if (rect) {
3200 if (nlpos.x > bottom.x)
3201 nlpos.x = bottom.x;
3202 nl = (top.y < bottom.y);
3203 }
3204
3205 while (poslt(top, bottom) && poslt(top, nlpos)) {
3206 #if 0
3207 char cbuf[16], *p;
3208 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
3209 #else
3210 wchar_t cbuf[16], *p;
3211 int uc = (ldata[top.x] & 0xFFFF);
3212 int set, c;
3213
3214 if (uc == UCSWIDE) {
3215 top.x++;
3216 continue;
3217 }
3218
3219 switch (uc & CSET_MASK) {
3220 case ATTR_LINEDRW:
3221 if (!cfg.rawcnp) {
3222 uc = unitab_xterm[uc & 0xFF];
3223 break;
3224 }
3225 case ATTR_ASCII:
3226 uc = unitab_line[uc & 0xFF];
3227 break;
3228 case ATTR_SCOACS:
3229 uc = unitab_scoacs[uc&0xFF];
3230 break;
3231 }
3232 switch (uc & CSET_MASK) {
3233 case ATTR_ACP:
3234 uc = unitab_font[uc & 0xFF];
3235 break;
3236 case ATTR_OEMCP:
3237 uc = unitab_oemcp[uc & 0xFF];
3238 break;
3239 }
3240
3241 set = (uc & CSET_MASK);
3242 c = (uc & CHAR_MASK);
3243 cbuf[0] = uc;
3244 cbuf[1] = 0;
3245
3246 if (DIRECT_FONT(uc)) {
3247 if (c >= ' ' && c != 0x7F) {
3248 unsigned char buf[4];
3249 WCHAR wbuf[4];
3250 int rv;
3251 if (is_dbcs_leadbyte(font_codepage, (BYTE) c)) {
3252 buf[0] = c;
3253 buf[1] = (unsigned char) ldata[top.x + 1];
3254 rv = mb_to_wc(font_codepage, 0, buf, 2, wbuf, 4);
3255 top.x++;
3256 } else {
3257 buf[0] = c;
3258 rv = mb_to_wc(font_codepage, 0, buf, 1, wbuf, 4);
3259 }
3260
3261 if (rv > 0) {
3262 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3263 cbuf[rv] = 0;
3264 }
3265 }
3266 }
3267 #endif
3268
3269 for (p = cbuf; *p; p++) {
3270 /* Enough overhead for trailing NL and nul */
3271 if (wblen >= buflen - 16) {
3272 workbuf =
3273 srealloc(workbuf,
3274 sizeof(wchar_t) * (buflen += 100));
3275 wbptr = workbuf + wblen;
3276 }
3277 wblen++;
3278 *wbptr++ = *p;
3279 }
3280 top.x++;
3281 }
3282 if (nl) {
3283 int i;
3284 for (i = 0; i < sel_nl_sz; i++) {
3285 wblen++;
3286 *wbptr++ = sel_nl[i];
3287 }
3288 }
3289 top.y++;
3290 top.x = rect ? old_top_x : 0;
3291 }
3292 #if SELECTION_NUL_TERMINATED
3293 wblen++;
3294 *wbptr++ = 0;
3295 #endif
3296 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3297 if (buflen > 0) /* indicates we allocated this buffer */
3298 sfree(workbuf);
3299 }
3300
3301 void term_copyall(void)
3302 {
3303 pos top;
3304 top.y = -count234(scrollback);
3305 top.x = 0;
3306 clipme(top, curs, 0);
3307 }
3308
3309 /*
3310 * The wordness array is mainly for deciding the disposition of the US-ASCII
3311 * characters.
3312 */
3313 static int wordtype(int uc)
3314 {
3315 static struct {
3316 int start, end, ctype;
3317 } *wptr, ucs_words[] = {
3318 {
3319 128, 160, 0}, {
3320 161, 191, 1}, {
3321 215, 215, 1}, {
3322 247, 247, 1}, {
3323 0x037e, 0x037e, 1}, /* Greek question mark */
3324 {
3325 0x0387, 0x0387, 1}, /* Greek ano teleia */
3326 {
3327 0x055a, 0x055f, 1}, /* Armenian punctuation */
3328 {
3329 0x0589, 0x0589, 1}, /* Armenian full stop */
3330 {
3331 0x0700, 0x070d, 1}, /* Syriac punctuation */
3332 {
3333 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3334 {
3335 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3336 {
3337 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3338 {
3339 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3340 {
3341 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3342 {
3343 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3344 {
3345 0x2000, 0x200a, 0}, /* Various spaces */
3346 {
3347 0x2070, 0x207f, 2}, /* superscript */
3348 {
3349 0x2080, 0x208f, 2}, /* subscript */
3350 {
3351 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3352 {
3353 0x3000, 0x3000, 0}, /* ideographic space */
3354 {
3355 0x3001, 0x3020, 1}, /* ideographic punctuation */
3356 {
3357 0x303f, 0x309f, 3}, /* Hiragana */
3358 {
3359 0x30a0, 0x30ff, 3}, /* Katakana */
3360 {
3361 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3362 {
3363 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3364 {
3365 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3366 {
3367 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3368 {
3369 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3370 {
3371 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3372 {
3373 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3374 {
3375 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3376 {
3377 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3378 {
3379 0, 0, 0}
3380 };
3381
3382 uc &= (CSET_MASK | CHAR_MASK);
3383
3384 switch (uc & CSET_MASK) {
3385 case ATTR_LINEDRW:
3386 uc = unitab_xterm[uc & 0xFF];
3387 break;
3388 case ATTR_ASCII:
3389 uc = unitab_line[uc & 0xFF];
3390 break;
3391 case ATTR_SCOACS:
3392 uc = unitab_scoacs[uc&0xFF];
3393 break;
3394 }
3395 switch (uc & CSET_MASK) {
3396 case ATTR_ACP:
3397 uc = unitab_font[uc & 0xFF];
3398 break;
3399 case ATTR_OEMCP:
3400 uc = unitab_oemcp[uc & 0xFF];
3401 break;
3402 }
3403
3404 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3405 * fail as there's such a thing as a double width space. :-(
3406 */
3407 if (dbcs_screenfont && font_codepage == line_codepage)
3408 return (uc != ' ');
3409
3410 if (uc < 0x80)
3411 return wordness[uc];
3412
3413 for (wptr = ucs_words; wptr->start; wptr++) {
3414 if (uc >= wptr->start && uc <= wptr->end)
3415 return wptr->ctype;
3416 }
3417
3418 return 2;
3419 }
3420
3421 /*
3422 * Spread the selection outwards according to the selection mode.
3423 */
3424 static pos sel_spread_half(pos p, int dir)
3425 {
3426 unsigned long *ldata;
3427 short wvalue;
3428 int topy = -count234(scrollback);
3429
3430 ldata = lineptr(p.y);
3431
3432 switch (selmode) {
3433 case SM_CHAR:
3434 /*
3435 * In this mode, every character is a separate unit, except
3436 * for runs of spaces at the end of a non-wrapping line.
3437 */
3438 if (!(ldata[cols] & LATTR_WRAPPED)) {
3439 unsigned long *q = ldata + cols;
3440 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3441 q--;
3442 if (q == ldata + cols)
3443 q--;
3444 if (p.x >= q - ldata)
3445 p.x = (dir == -1 ? q - ldata : cols - 1);
3446 }
3447 break;
3448 case SM_WORD:
3449 /*
3450 * In this mode, the units are maximal runs of characters
3451 * whose `wordness' has the same value.
3452 */
3453 wvalue = wordtype(ldata[p.x]);
3454 if (dir == +1) {
3455 while (1) {
3456 if (p.x < cols-1) {
3457 if (wordtype(ldata[p.x + 1]) == wvalue)
3458 p.x++;
3459 else
3460 break;
3461 } else {
3462 if (ldata[cols] & LATTR_WRAPPED) {
3463 unsigned long *ldata2;
3464 ldata2 = lineptr(p.y+1);
3465 if (wordtype(ldata2[0]) == wvalue) {
3466 p.x = 0;
3467 p.y++;
3468 ldata = ldata2;
3469 } else
3470 break;
3471 } else
3472 break;
3473 }
3474 }
3475 } else {
3476 while (1) {
3477 if (p.x > 0) {
3478 if (wordtype(ldata[p.x - 1]) == wvalue)
3479 p.x--;
3480 else
3481 break;
3482 } else {
3483 unsigned long *ldata2;
3484 if (p.y <= topy)
3485 break;
3486 ldata2 = lineptr(p.y-1);
3487 if ((ldata2[cols] & LATTR_WRAPPED) &&
3488 wordtype(ldata2[cols-1]) == wvalue) {
3489 p.x = cols-1;
3490 p.y--;
3491 ldata = ldata2;
3492 } else
3493 break;
3494 }
3495 }
3496 }
3497 break;
3498 case SM_LINE:
3499 /*
3500 * In this mode, every line is a unit.
3501 */
3502 p.x = (dir == -1 ? 0 : cols - 1);
3503 break;
3504 }
3505 return p;
3506 }
3507
3508 static void sel_spread(void)
3509 {
3510 if (seltype == LEXICOGRAPHIC) {
3511 selstart = sel_spread_half(selstart, -1);
3512 decpos(selend);
3513 selend = sel_spread_half(selend, +1);
3514 incpos(selend);
3515 }
3516 }
3517
3518 void term_do_paste(void)
3519 {
3520 wchar_t *data;
3521 int len;
3522
3523 get_clip(&data, &len);
3524 if (data && len > 0) {
3525 wchar_t *p, *q;
3526
3527 term_seen_key_event(); /* pasted data counts */
3528
3529 if (paste_buffer)
3530 sfree(paste_buffer);
3531 paste_pos = paste_hold = paste_len = 0;
3532 paste_buffer = smalloc(len * sizeof(wchar_t));
3533
3534 p = q = data;
3535 while (p < data + len) {
3536 while (p < data + len &&
3537 !(p <= data + len - sel_nl_sz &&
3538 !memcmp(p, sel_nl, sizeof(sel_nl))))
3539 p++;
3540
3541 {
3542 int i;
3543 for (i = 0; i < p - q; i++) {
3544 paste_buffer[paste_len++] = q[i];
3545 }
3546 }
3547
3548 if (p <= data + len - sel_nl_sz &&
3549 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3550 paste_buffer[paste_len++] = '\r';
3551 p += sel_nl_sz;
3552 }
3553 q = p;
3554 }
3555
3556 /* Assume a small paste will be OK in one go. */
3557 if (paste_len < 256) {
3558 luni_send(paste_buffer, paste_len, 0);
3559 if (paste_buffer)
3560 sfree(paste_buffer);
3561 paste_buffer = 0;
3562 paste_pos = paste_hold = paste_len = 0;
3563 }
3564 }
3565 get_clip(NULL, NULL);
3566 }
3567
3568 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3569 int shift, int ctrl, int alt)
3570 {
3571 pos selpoint;
3572 unsigned long *ldata;
3573 int raw_mouse = (xterm_mouse &&
3574 !cfg.no_mouse_rep &&
3575 !(cfg.mouse_override && shift));
3576 int default_seltype;
3577
3578 if (y < 0) {
3579 y = 0;
3580 if (a == MA_DRAG && !raw_mouse)
3581 term_scroll(0, -1);
3582 }
3583 if (y >= rows) {
3584 y = rows - 1;
3585 if (a == MA_DRAG && !raw_mouse)
3586 term_scroll(0, +1);
3587 }
3588 if (x < 0) {
3589 if (y > 0) {
3590 x = cols - 1;
3591 y--;
3592 } else
3593 x = 0;
3594 }
3595 if (x >= cols)
3596 x = cols - 1;
3597
3598 selpoint.y = y + disptop;
3599 selpoint.x = x;
3600 ldata = lineptr(selpoint.y);
3601 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3602 selpoint.x /= 2;
3603
3604 if (raw_mouse) {
3605 int encstate = 0, r, c;
3606 char abuf[16];
3607 static int is_down = 0;
3608
3609 switch (b) {
3610 case MBT_LEFT:
3611 encstate = 0x20; /* left button down */
3612 break;
3613 case MBT_MIDDLE:
3614 encstate = 0x21;
3615 break;
3616 case MBT_RIGHT:
3617 encstate = 0x22;
3618 break;
3619 case MBT_WHEEL_UP:
3620 encstate = 0x60;
3621 break;
3622 case MBT_WHEEL_DOWN:
3623 encstate = 0x61;
3624 break;
3625 default: break; /* placate gcc warning about enum use */
3626 }
3627 switch (a) {
3628 case MA_DRAG:
3629 if (xterm_mouse == 1)
3630 return;
3631 encstate += 0x20;
3632 break;
3633 case MA_RELEASE:
3634 encstate = 0x23;
3635 is_down = 0;
3636 break;
3637 case MA_CLICK:
3638 if (is_down == b)
3639 return;
3640 is_down = b;
3641 break;
3642 default: break; /* placate gcc warning about enum use */
3643 }
3644 if (shift)
3645 encstate += 0x04;
3646 if (ctrl)
3647 encstate += 0x10;
3648 r = y + 33;
3649 c = x + 33;
3650
3651 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3652 ldisc_send(abuf, 6, 0);
3653 return;
3654 }
3655
3656 b = translate_button(b);
3657
3658 /*
3659 * Set the selection type (rectangular or normal) at the start
3660 * of a selection attempt, from the state of Alt.
3661 */
3662 if (!alt ^ !cfg.rect_select)
3663 default_seltype = RECTANGULAR;
3664 else
3665 default_seltype = LEXICOGRAPHIC;
3666
3667 if (selstate == NO_SELECTION) {
3668 seltype = default_seltype;
3669 }
3670
3671 if (b == MBT_SELECT && a == MA_CLICK) {
3672 deselect();
3673 selstate = ABOUT_TO;
3674 seltype = default_seltype;
3675 selanchor = selpoint;
3676 selmode = SM_CHAR;
3677 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3678 deselect();
3679 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3680 selstate = DRAGGING;
3681 selstart = selanchor = selpoint;
3682 selend = selstart;
3683 incpos(selend);
3684 sel_spread();
3685 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3686 (b == MBT_EXTEND && a != MA_RELEASE)) {
3687 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3688 return;
3689 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3690 if (seltype == LEXICOGRAPHIC) {
3691 /*
3692 * For normal selection, we extend by moving
3693 * whichever end of the current selection is closer
3694 * to the mouse.
3695 */
3696 if (posdiff(selpoint, selstart) <
3697 posdiff(selend, selstart) / 2) {
3698 selanchor = selend;
3699 decpos(selanchor);
3700 } else {
3701 selanchor = selstart;
3702 }
3703 } else {
3704 /*
3705 * For rectangular selection, we have a choice of
3706 * _four_ places to put selanchor and selpoint: the
3707 * four corners of the selection.
3708 */
3709 if (2*selpoint.x < selstart.x + selend.x)
3710 selanchor.x = selend.x-1;
3711 else
3712 selanchor.x = selstart.x;
3713
3714 if (2*selpoint.y < selstart.y + selend.y)
3715 selanchor.y = selend.y;
3716 else
3717 selanchor.y = selstart.y;
3718 }
3719 selstate = DRAGGING;
3720 }
3721 if (selstate != ABOUT_TO && selstate != DRAGGING)
3722 selanchor = selpoint;
3723 selstate = DRAGGING;
3724 if (seltype == LEXICOGRAPHIC) {
3725 /*
3726 * For normal selection, we set (selstart,selend) to
3727 * (selpoint,selanchor) in some order.
3728 */
3729 if (poslt(selpoint, selanchor)) {
3730 selstart = selpoint;
3731 selend = selanchor;
3732 incpos(selend);
3733 } else {
3734 selstart = selanchor;
3735 selend = selpoint;
3736 incpos(selend);
3737 }
3738 } else {
3739 /*
3740 * For rectangular selection, we may need to
3741 * interchange x and y coordinates (if the user has
3742 * dragged in the -x and +y directions, or vice versa).
3743 */
3744 selstart.x = min(selanchor.x, selpoint.x);
3745 selend.x = 1+max(selanchor.x, selpoint.x);
3746 selstart.y = min(selanchor.y, selpoint.y);
3747 selend.y = max(selanchor.y, selpoint.y);
3748 }
3749 sel_spread();
3750 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3751 if (selstate == DRAGGING) {
3752 /*
3753 * We've completed a selection. We now transfer the
3754 * data to the clipboard.
3755 */
3756 clipme(selstart, selend, (seltype == RECTANGULAR));
3757 selstate = SELECTED;
3758 } else
3759 selstate = NO_SELECTION;
3760 } else if (b == MBT_PASTE
3761 && (a == MA_CLICK
3762 #if MULTICLICK_ONLY_EVENT
3763 || a == MA_2CLK || a == MA_3CLK
3764 #endif
3765 )) {
3766 request_paste();
3767 }
3768
3769 term_update();
3770 }
3771
3772 void term_nopaste()
3773 {
3774 if (paste_len == 0)
3775 return;
3776 sfree(paste_buffer);
3777 paste_buffer = 0;
3778 paste_len = 0;
3779 }
3780
3781 int term_paste_pending(void)
3782 {
3783 return paste_len != 0;
3784 }
3785
3786 void term_paste()
3787 {
3788 static long last_paste = 0;
3789 long now, paste_diff;
3790
3791 if (paste_len == 0)
3792 return;
3793
3794 /* Don't wait forever to paste */
3795 if (paste_hold) {
3796 now = GETTICKCOUNT();
3797 paste_diff = now - last_paste;
3798 if (paste_diff >= 0 && paste_diff < 450)
3799 return;
3800 }
3801 paste_hold = 0;
3802
3803 while (paste_pos < paste_len) {
3804 int n = 0;
3805 while (n + paste_pos < paste_len) {
3806 if (paste_buffer[paste_pos + n++] == '\r')
3807 break;
3808 }
3809 luni_send(paste_buffer + paste_pos, n, 0);
3810 paste_pos += n;
3811
3812 if (paste_pos < paste_len) {
3813 paste_hold = 1;
3814 return;
3815 }
3816 }
3817 sfree(paste_buffer);
3818 paste_buffer = 0;
3819 paste_len = 0;
3820 }
3821
3822 static void deselect(void)
3823 {
3824 selstate = NO_SELECTION;
3825 selstart.x = selstart.y = selend.x = selend.y = 0;
3826 }
3827
3828 void term_deselect(void)
3829 {
3830 deselect();
3831 term_update();
3832 }
3833
3834 int term_ldisc(int option)
3835 {
3836 if (option == LD_ECHO)
3837 return term_echoing;
3838 if (option == LD_EDIT)
3839 return term_editing;
3840 return FALSE;
3841 }
3842
3843 /*
3844 * from_backend(), to get data from the backend for the terminal.
3845 */
3846 int from_backend(int is_stderr, char *data, int len)
3847 {
3848 assert(len > 0);
3849
3850 bufchain_add(&inbuf, data, len);
3851
3852 /*
3853 * term_out() always completely empties inbuf. Therefore,
3854 * there's no reason at all to return anything other than zero
3855 * from this function, because there _can't_ be a question of
3856 * the remote side needing to wait until term_out() has cleared
3857 * a backlog.
3858 *
3859 * This is a slightly suboptimal way to deal with SSH2 - in
3860 * principle, the window mechanism would allow us to continue
3861 * to accept data on forwarded ports and X connections even
3862 * while the terminal processing was going slowly - but we
3863 * can't do the 100% right thing without moving the terminal
3864 * processing into a separate thread, and that might hurt
3865 * portability. So we manage stdout buffering the old SSH1 way:
3866 * if the terminal processing goes slowly, the whole SSH
3867 * connection stops accepting data until it's ready.
3868 *
3869 * In practice, I can't imagine this causing serious trouble.
3870 */
3871 return 0;
3872 }