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