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