AmendHub

Download

jcs

/

subtext

/

console.c

 

(View History)

jcs   console: Dismiss any progress dialog before opening Latest amendment: 479 on 2023-04-10

1 /*
2 * Copyright (c) 2021 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <string.h>
19
20 #include "subtext.h"
21 #include "ansi.h"
22 #include "console.h"
23 #include "focusable.h"
24 #include "session.h"
25 #include "user.h"
26 #include "util.h"
27
28 /* for monaco 9 */
29 #define TEXT_FONT monaco
30 #define TEXT_SIZE 9
31 #define FONT_WIDTH 6
32 #define FONT_HEIGHT 11
33
34 #define CONSOLE_FORCE_REDRAW 1
35 #define CONSOLE_PADDING 6
36 #define CONSOLE_SMOOTH_SCROLLING 0
37
38 void console_idle(struct focusable *focusable, EventRecord *event);
39 void console_close(struct focusable *focusable, EventRecord *event);
40 void console_suspend(struct focusable *focusable, EventRecord *event);
41 void console_resume(struct focusable *focusable, EventRecord *event);
42 void console_update(struct focusable *focusable, EventRecord *event);
43 void console_mouse_down(struct focusable *focusable, EventRecord *event);
44 void console_key_down(struct focusable *focusable, EventRecord *event);
45
46 void console_setup(struct session *session);
47 void console_redraw(struct console *console, short force);
48 short console_bound(struct console *console);
49 void console_erase_chars(struct console *console, short start, short count);
50 void console_shift_chars(struct console *console, short start,
51 short offset);
52 void console_parse_csi(struct console *console);
53
54 struct node_funcs console_node_funcs = {
55 console_setup,
56 console_input,
57 console_output,
58 console_close_from_session
59 };
60
61 struct console *
62 console_init(void)
63 {
64 char title[64];
65 char *sysop_username = NULL;
66 struct console *console;
67 struct focusable *focusable;
68 Rect bounds;
69 short width, height;
70
71 /* dismiss any dialog */
72 progress(NULL);
73
74 console = xmalloczero(sizeof(struct console));
75 if (console == NULL)
76 return NULL;
77
78 console->session = session_create("console", "console",
79 &console_node_funcs);
80 if (console->session == NULL) {
81 xfree(&console);
82 warn("No free nodes for a console");
83 return NULL;
84 }
85
86 memset(console->chars, ' ', sizeof(console->chars));
87 console->ncolumns = DEFAULT_TERMINAL_COLUMNS;
88 console->nlines = DEFAULT_TERMINAL_LINES;
89
90 width = CONSOLE_PADDING + (FONT_WIDTH * console->ncolumns) +
91 CONSOLE_PADDING;
92 height = CONSOLE_PADDING + (FONT_HEIGHT * console->nlines) +
93 CONSOLE_PADDING;
94
95 bounds.left = (screenBits.bounds.right - width) / 2;
96 bounds.right = bounds.left + width;
97 bounds.top = GetMBarHeight() +
98 ((screenBits.bounds.bottom - height) / 2);
99 bounds.bottom = bounds.top + height;
100
101 snprintf(title, sizeof(title), "%s: %s: console", PROGRAM_NAME,
102 db->config.hostname);
103 CtoPstr(title);
104
105 console->win = NewWindow(0L, &bounds, title, false, noGrowDocProc,
106 (WindowPtr)-1L, true, 0);
107 if (!console->win) {
108 warn("Can't create window");
109 xfree(&console);
110 return NULL;
111 }
112
113 focusable = xmalloczero(sizeof(struct focusable));
114 if (focusable == NULL) {
115 xfree(&console);
116 return NULL;
117 }
118 focusable->win = console->win;
119 focusable->cookie = console;
120 focusable->update = console_update;
121 focusable->key_down = console_key_down;
122 focusable->close = console_close;
123 console->focusable = focusable;
124 if (!add_focusable(focusable)) {
125 xfree(&console);
126 return NULL;
127 }
128
129 console->session->cookie = (void *)console;
130 console->session->vt100 = 1;
131 console->session->tspeed = 19200;
132
133 if ((sysop_username = user_first_sysop_username()) != NULL) {
134 strlcpy(console->session->autologin_username, sysop_username,
135 sizeof(console->session->autologin_username));
136 xfree(&sysop_username);
137 }
138
139 return console;
140 }
141
142 void
143 console_idle(struct focusable *focusable, EventRecord *event)
144 {
145 struct console *console = (struct console *)focusable->cookie;
146 struct session *session = console->session;
147 GrafPtr old_port;
148 short n, cursor;
149
150 if (session->obuflen == 0)
151 return;
152
153 GetPort(&old_port);
154 SetPort(console->win);
155
156 /* uncursor */
157 cursor = (console->cursor_line * console->ncolumns) +
158 console->cursor_column;
159 if (cursor >= sizeof(console->attrs) - 1)
160 panic("console_idle: cursor out of bounds!");
161 console->attrs[cursor] &= ~ATTR_CURSOR;
162 console->attrs[cursor] |= ATTR_DIRTY;
163
164 for (n = 0; n < session->obuflen; n++) {
165 if (console->in_csi) {
166 if (session->obuf[n] == '\33' ||
167 console->csilen >= nitems(console->csi) - 1) {
168 console_parse_csi(console);
169 console->in_csi = 0;
170 console->csi[0] = '\0';
171 console->csilen = 0;
172 } else {
173 console->csi[console->csilen] = session->obuf[n];
174 console->csilen++;
175 console_parse_csi(console);
176 }
177
178 continue;
179 }
180
181 switch (session->obuf[n]) {
182 case '\r':
183 console->cursor_column = 0;
184 break;
185 case '\n':
186 console->cursor_line++;
187 console->cursor_column = 0; /* \r should do this, but JIC */
188 console_bound(console);
189 break;
190 case '\33': /* \e */
191 if (session->obuflen <= n + 1) {
192 /* lone \e at end of buffer, keep it until we see next */
193 session->obuflen = 1;
194 session->obuf[0] = '\33';
195 goto output_done;
196 }
197 if (session->obuf[n + 1] == '[') {
198 console->in_csi = 1;
199 n++;
200 continue;
201 }
202 /* escape but not CSI, fall through */
203 default:
204 console_bound(console);
205 cursor = (console->cursor_line * console->ncolumns) +
206 console->cursor_column;
207 console->chars[cursor] = session->obuf[n];
208 console->attrs[cursor] = console->cur_attr | ATTR_DIRTY;
209 console->cursor_column++;
210 break;
211 }
212 }
213
214 session->obuflen = 0;
215
216 output_done:
217 console_bound(console);
218 console_redraw(console, 0);
219 SetPort(old_port);
220 }
221
222 void
223 console_suspend(struct focusable *focusable, EventRecord *event)
224 {
225 }
226
227 void
228 console_resume(struct focusable *focusable, EventRecord *event)
229 {
230 }
231
232 void
233 console_update(struct focusable *focusable, EventRecord *event)
234 {
235 struct console *console = (struct console *)focusable->cookie;
236 short what = -1;
237
238 if (event != NULL)
239 what = event->what;
240
241 switch (what) {
242 case -1:
243 case updateEvt:
244 console_redraw(console, CONSOLE_FORCE_REDRAW);
245 break;
246 case activateEvt:
247 break;
248 }
249 }
250
251 void
252 console_mouse_down(struct focusable *focusable, EventRecord *event)
253 {
254 }
255
256 void
257 console_key_down(struct focusable *focusable, EventRecord *event)
258 {
259 struct console *console = (struct console *)focusable->cookie;
260 unsigned char k;
261
262 if (console->session->ibuflen >= nitems(console->session->ibuf))
263 return;
264
265 console->session->last_input_at = Time;
266
267 k = (event->message & charCodeMask);
268 if (event->modifiers & optionKey) {
269 /* these are only correct on a US keyboard */
270 switch (k) {
271 case 0x8d: /* opt+C */
272 /* ^C */
273 console->session->ibuf[console->session->ibuflen++] = CONTROL_C;
274 break;
275 case 0xb6: /* opt+D */
276 /* ^D */
277 console->session->ibuf[console->session->ibuflen++] = CONTROL_D;
278 break;
279 case 0xc2: /* opt+L */
280 console_redraw(console, CONSOLE_FORCE_REDRAW);
281 break;
282 }
283 } else {
284 switch (k) {
285 case 0x1C: /* left */
286 console->session->ibuf[console->session->ibuflen++] = '\33';
287 console->session->ibuf[console->session->ibuflen++] = '[';
288 console->session->ibuf[console->session->ibuflen++] = 'D';
289 break;
290 case 0x1D: /* right */
291 console->session->ibuf[console->session->ibuflen++] = '\33';
292 console->session->ibuf[console->session->ibuflen++] = '[';
293 console->session->ibuf[console->session->ibuflen++] = 'C';
294 break;
295 case 0x1E: /* up */
296 console->session->ibuf[console->session->ibuflen++] = '\33';
297 console->session->ibuf[console->session->ibuflen++] = '[';
298 console->session->ibuf[console->session->ibuflen++] = 'A';
299 break;
300 case 0x1F: /* down */
301 console->session->ibuf[console->session->ibuflen++] = '\33';
302 console->session->ibuf[console->session->ibuflen++] = '[';
303 console->session->ibuf[console->session->ibuflen++] = 'B';
304 break;
305 default:
306 console->session->ibuf[console->session->ibuflen++] = k;
307 if (k == '\r')
308 console->session->ibuf[console->session->ibuflen++] = '\n';
309 }
310 }
311 }
312
313 void
314 console_close(struct focusable *focusable, EventRecord *event)
315 {
316 struct console *console = (struct console *)focusable->cookie;
317 console->session->ending = 1;
318 }
319
320 /* session API */
321
322 void
323 console_setup(struct session *session)
324 {
325 struct console *console = (struct console *)session->cookie;
326 session->terminal_columns = console->ncolumns;
327 session->terminal_lines = console->nlines;
328 snprintf(session->terminal_type, sizeof(session->terminal_type),
329 "vt100");
330 }
331
332 short
333 console_output(struct session *session)
334 {
335 struct console *console = (struct console *)session->cookie;
336 console_idle(console->focusable, NULL);
337 return 0;
338 }
339
340 short
341 console_input(struct session *session)
342 {
343 /* nothing to do here, input is fed from main loop */
344 return 0;
345 }
346
347 void
348 console_close_from_session(struct session *session)
349 {
350 struct console *console = (struct console *)session->cookie;
351 session_logf(session, "Closing console session");
352 destroy_focusable(console->focusable);
353 xfree(&console);
354 }
355
356 short
357 console_bound(struct console *console)
358 {
359 unsigned short shift_lines, shift_cols, chars_left, pxout,
360 pxout_scroll;
361 RgnHandle rgn;
362 Rect r, r2;
363 GrafPtr old_port;
364 short ret = 0, n;
365
366 while (console->cursor_column > console->ncolumns) {
367 console->cursor_line++;
368 console->cursor_column -= console->ncolumns;
369 }
370
371 if (console->cursor_line >= console->nlines) {
372 shift_lines = console->cursor_line - console->nlines + 1;
373 shift_cols = shift_lines * console->ncolumns;
374 chars_left = (console->nlines * console->ncolumns) - shift_cols;
375 pxout = shift_lines * FONT_HEIGHT;
376
377 GetPort(&old_port);
378 SetPort(console->win);
379 rgn = NewRgn();
380 r.left = console->win->portRect.left + CONSOLE_PADDING;
381 r.top = console->win->portRect.top + CONSOLE_PADDING;
382 r.right = r.left + (console->ncolumns * FONT_WIDTH);
383 r.bottom = r.top + ((console->nlines) * FONT_HEIGHT);
384
385 n = CONSOLE_SMOOTH_SCROLLING ? CONSOLE_SMOOTH_SCROLLING : pxout;
386 pxout_scroll = pxout;
387 while (pxout_scroll > 0) {
388 if (pxout_scroll < n)
389 n = pxout_scroll;
390 ScrollRect(&r, 0, -n, rgn);
391 r2 = r;
392 r2.top = r.bottom - 1;
393 r2.bottom = r.top + n;
394 FillRect(&r2, white);
395 pxout_scroll -= n;
396 }
397 DisposeRgn(rgn);
398
399 BlockMove(console->chars + shift_cols, console->chars, chars_left);
400 BlockMove(console->attrs + shift_cols, console->attrs, chars_left);
401 memset(console->chars + chars_left, ' ', shift_cols);
402 memset(console->attrs + chars_left, console->cur_attr, shift_cols);
403
404 SetPort(old_port);
405
406 console->cursor_line = console->nlines - 1;
407 ret = 1;
408 }
409
410 return ret;
411 }
412
413 #define CONSOLE_ATTRS_DRAWABLE (ATTR_REVERSE | ATTR_BOLD | ATTR_DIRTY)
414
415 void
416 console_redraw(struct console *console, short force)
417 {
418 Rect chunk;
419 GrafPtr old_port;
420 short curbold = -1, line, off, c, firstdirty;
421 unsigned char curattr = 0, a;
422
423 GetPort(&old_port);
424 SetPort(console->win);
425
426 TextFont(TEXT_FONT);
427 TextSize(TEXT_SIZE);
428
429 for (line = 0; line < console->nlines; line++) {
430 off = line * console->ncolumns;
431 chunk.top = console->win->portRect.top + CONSOLE_PADDING +
432 (line * FONT_HEIGHT);
433 for (firstdirty = -1, c = 0; c < console->ncolumns; c++) {
434 a = console->attrs[off + c] & CONSOLE_ATTRS_DRAWABLE;
435 if (!force && !(a & ATTR_DIRTY) && firstdirty == -1)
436 continue;
437 console->attrs[off + c] &= ~ATTR_DIRTY;
438
439 if (c == console->ncolumns - 1) {
440 if (firstdirty == -1)
441 firstdirty = c;
442 } else if (firstdirty == -1) {
443 firstdirty = MAX(c - 1, 0);
444 curattr = a;
445 continue;
446 } else if (a == curattr) {
447 continue;
448 }
449
450 /* draw current chunk of dirty chars */
451 chunk.left = console->win->portRect.left + CONSOLE_PADDING +
452 (firstdirty * FONT_WIDTH);
453 chunk.right = chunk.left +
454 ((c - firstdirty) * FONT_WIDTH);
455 chunk.bottom = chunk.top + FONT_HEIGHT;
456 FillRect(&chunk, white);
457
458 if (curattr & ATTR_BOLD) {
459 if (curbold != 1) {
460 TextFace(thePort->txFace | bold | condense);
461 curbold = 1;
462 }
463 } else if (curbold != 0) {
464 TextFace(thePort->txFace & ~(bold | condense));
465 curbold = 0;
466 }
467 MoveTo(chunk.left, chunk.top + FONT_HEIGHT - 2);
468 DrawText(console->chars, off + firstdirty, c - firstdirty);
469
470 if (curattr & ATTR_REVERSE)
471 InvertRect(&chunk);
472
473 if (c != console->ncolumns - 1) {
474 if (a & ATTR_DIRTY)
475 firstdirty = c;
476 else
477 firstdirty = -1;
478 curattr = a;
479 }
480 }
481 }
482
483 /* redraw cursor */
484 chunk.top = console->win->portRect.top + CONSOLE_PADDING +
485 (console->cursor_line * FONT_HEIGHT);
486 chunk.left = console->win->portRect.left + CONSOLE_PADDING +
487 (console->cursor_column * FONT_WIDTH);
488 chunk.bottom = chunk.top + FONT_HEIGHT;
489 chunk.right = chunk.left + FONT_WIDTH;
490 InvertRect(&chunk);
491
492 ValidRect(&console->win->portRect);
493 SetPort(old_port);
494 }
495
496 void
497 console_erase_chars(struct console *console, short start, short count)
498 {
499 Rect eraser;
500 short start_line, end_line, start_col, end_col, line;
501
502 memset(console->chars + start, ' ', count);
503 memset(console->attrs + start, console->cur_attr, count);
504
505 start_line = start / console->ncolumns;
506 start_col = start - (start_line * console->ncolumns);
507 end_line = (start + count) / console->ncolumns;
508 end_col = start + count - (end_line * console->ncolumns);
509
510 for (line = start_line; line <= end_line; line++) {
511 eraser.top = console->win->portRect.top + CONSOLE_PADDING +
512 (line * FONT_HEIGHT);
513 eraser.left = console->win->portRect.left + CONSOLE_PADDING;
514 if (line == start_line)
515 eraser.left += start_col * FONT_WIDTH;
516 eraser.bottom = eraser.top + FONT_HEIGHT;
517 if (line == end_line)
518 eraser.right = eraser.left + (end_col * FONT_WIDTH);
519 else
520 eraser.right = eraser.left + (console->ncolumns * FONT_WIDTH);
521 FillRect(&eraser,
522 (console->cur_attr & ATTR_REVERSE) ? black : white);
523 }
524 }
525
526 void
527 console_shift_chars(struct console *console, short start, short offset)
528 {
529 Rect mover;
530 RgnHandle rgn;
531 short count;
532
533 /* XXX: this currently only works on whole lines */
534
535 /* move all chars at start by offset, overwriting what's there */
536 count = (console->ncolumns * console->nlines) - start;
537 if (start + offset + count > sizeof(console->chars))
538 /* inserting lines, losing trailing lines */
539 count = sizeof(console->chars) - start - offset;
540 memmove(console->chars + start + offset, console->chars + start,
541 count);
542 memmove(console->attrs + start + offset, console->attrs + start,
543 count);
544
545 if (start % console->ncolumns != 0)
546 warn("TODO partial console_shift_chars");
547 if (offset % console->ncolumns != 0)
548 warn("TODO partial console_shift_chars");
549
550 rgn = NewRgn();
551 mover.top = console->win->portRect.top + CONSOLE_PADDING +
552 ((start / console->ncolumns) * FONT_HEIGHT);
553 if (offset < 0)
554 mover.top -= FONT_HEIGHT;
555 mover.left = console->win->portRect.left + CONSOLE_PADDING;
556 mover.bottom = mover.top + (FONT_HEIGHT * ((count / console->ncolumns) + 1));
557 mover.right = mover.left + (FONT_WIDTH * console->ncolumns);
558 ScrollRect(&mover, 0, (offset / console->ncolumns) * FONT_HEIGHT, rgn);
559 DisposeRgn(rgn);
560 }
561
562 void
563 console_parse_csi(struct console *console)
564 {
565 short x, y;
566 long cursor;
567 short serviced = 0;
568 short param1 = -1, param2 = -1;
569 short parambuflen;
570 short start, count, offset;
571 struct session *session;
572 char parambuf[5];
573 unsigned char c;
574
575 if (console->csilen == 0)
576 return;
577
578 c = console->csi[console->csilen - 1];
579
580 if (c < 'A' || (c > 'Z' && c < 'a') || c > 'z')
581 return;
582
583 switch (c) {
584 case 'A':
585 case 'B':
586 case 'C':
587 case 'D':
588 case 'E':
589 case 'F':
590 case 'G':
591 case 'I':
592 case 'J':
593 case 'K':
594 case 'L':
595 case 'M':
596 case 'S':
597 case 'T':
598 case 'd':
599 case 'g':
600 case 'n':
601 case 's':
602 case 'u':
603 case '7':
604 case '8':
605 /* optional multiplier */
606 if (c == 'J' || c == 'K')
607 param1 = 0;
608 else
609 param1 = 1;
610
611 if (console->csilen > 1) {
612 for (x = 0; x < console->csilen - 1 &&
613 x < nitems(parambuf) - 1; x++) {
614 parambuf[x] = console->csi[x];
615 parambuf[x + 1] = '\0';
616 }
617 param1 = atoi(parambuf);
618 }
619 break;
620 case 'H':
621 case 'f':
622 /* two optional parameters separated by ; each defaulting to 1 */
623 param1 = 1;
624 param2 = 1;
625
626 y = -1;
627 for (x = 0; x < console->csilen; x++) {
628 if (console->csi[x] == ';') {
629 y = x;
630 break;
631 }
632 }
633 if (y == -1)
634 /* CSI 17H -> CSI 17; */
635 y = console->csilen - 1;
636
637 if (y > 0) {
638 for (x = 0; x < y && x < nitems(parambuf) - 1; x++) {
639 parambuf[x] = console->csi[x];
640 parambuf[x + 1] = '\0';
641 }
642 param1 = atoi(parambuf);
643
644 if (y < console->csilen - 2) {
645 parambuf[0] = '\0';
646 for (x = 0; x < (console->csilen - 1 - y) &&
647 x < nitems(parambuf) - 1; x++) {
648 parambuf[x] = console->csi[y + 1 + x];
649 parambuf[x + 1] = '\0';
650 }
651 param2 = atoi(parambuf);
652 }
653 }
654 break;
655 }
656
657 serviced = 1;
658
659 /* uncursor */
660 cursor = (console->cursor_line * console->ncolumns) +
661 console->cursor_column;
662 console->attrs[cursor] &= ~ATTR_CURSOR;
663 console->attrs[cursor] |= ATTR_DIRTY;
664
665 switch (c) {
666 case 'A': /* CUU - cursor up, stop at top of screen */
667 console->cursor_line = MAX(0, console->cursor_line - param1);
668 break;
669 case 'B': /* CUD - cursor down, stop at bottom of screen */
670 console->cursor_line = MIN(console->nlines - 1,
671 console->cursor_line + param1);
672 break;
673 case 'C': /* CUF - cursor forward, stop at screen edge */
674 console->cursor_column = MIN(console->ncolumns - 1,
675 console->cursor_column + param1);
676 break;
677 case 'D': /* CUB - cursor back, stop at left edge of screen */
678 console->cursor_column = MAX(0, console->cursor_column - param1);
679 break;
680 case 'E': /* CNL - cursor next line */
681 console->cursor_column = 0;
682 console->cursor_line = MAX(console->nlines - 1,
683 console->cursor_line + param1);
684 break;
685 case 'F': /* CPL - cursor previous line */
686 console->cursor_column = 0;
687 console->cursor_line = MAX(0, console->cursor_line - param1);
688 break;
689 case 'G': /* CHA - cursor horizontal absolute */
690 console->cursor_column = BOUND(param1, 0, console->ncolumns - 1);
691 break;
692 case 'H': /* CUP - cursor absolute position */
693 case 'f': /* HVP - horizontal vertical position */
694 if (param1 - 1 < 0)
695 console->cursor_line = 0;
696 else if (param1 > console->nlines)
697 console->cursor_line = console->nlines - 1;
698 else
699 console->cursor_line = param1 - 1;
700
701 if (param2 - 1 < 0)
702 console->cursor_column = 0;
703 else if (param2 > console->ncolumns)
704 console->cursor_column = console->ncolumns - 1;
705 else
706 console->cursor_column = param2 - 1;
707 break;
708 case 'J': /* ED - erase in display */
709 switch (param1) {
710 case 0:
711 /* clear from cursor (inclusive) to end of screen */
712 start = (console->cursor_line * console->ncolumns) +
713 console->cursor_column;
714 count = (console->ncolumns * console->nlines) - start;
715 console_erase_chars(console, start, count);
716 break;
717 case 1:
718 /* clear from cursor to beginning of the screen */
719 start = 0;
720 count = (console->cursor_line * console->ncolumns) +
721 console->cursor_column;
722 console_erase_chars(console, start, count);
723 break;
724 case 2:
725 /* clear entire screen, mark everything clean */
726 start = 0;
727 count = console->ncolumns * console->nlines;
728 console_erase_chars(console, start, count);
729 break;
730 }
731 break;
732 case 'K': /* EL - erase in line */
733 switch (param1) {
734 case 0:
735 /* clear from cursor to end of line */
736 start = (console->cursor_line * console->ncolumns) +
737 console->cursor_column;
738 count = console->ncolumns - console->cursor_column;
739 console_erase_chars(console, start, count);
740 break;
741 case 1:
742 /* clear from cursor to beginning of line */
743 start = (console->cursor_line * console->ncolumns);
744 count = console->ncolumns - console->cursor_column;
745 console_erase_chars(console, start, count);
746 break;
747 case 2:
748 /* clear entire line */
749 start = (console->cursor_line * console->ncolumns);
750 count = console->ncolumns;
751 console_erase_chars(console, start, count);
752 break;
753 }
754 break;
755 case 'L': /* IL - insert line */
756 param1 = BOUND(param1, 0, console->nlines - console->cursor_line);
757 if (param1 == 0)
758 break;
759 if (console->cursor_line != console->nlines - 1) {
760 /* shift lines down to insert new ones */
761 start = console->ncolumns * console->cursor_line;
762 offset = console->ncolumns * param1;
763 console_shift_chars(console, start, offset);
764 }
765 /* clear new lines at cursor line */
766 start = console->ncolumns * console->cursor_line;
767 count = console->ncolumns * param1;
768 console_erase_chars(console, start, count);
769 break;
770 case 'M': /* DL - delete line */
771 param1 = BOUND(param1, 0, console->nlines - console->cursor_line);
772 if (param1 == 0)
773 break;
774 if (console->cursor_line != console->nlines - 1) {
775 start = console->ncolumns * (console->cursor_line + param1);
776 offset = console->ncolumns * -param1;
777 console_shift_chars(console, start, offset);
778 }
779 /* fill in new blank lines after lines that moved up */
780 start = console->ncolumns * (console->nlines - param1);
781 count = console->ncolumns * param1;
782 console_erase_chars(console, start, count);
783 break;
784 case 'S': /* SU - scroll up */
785 /* TODO */
786 break;
787 case 'T': /* SD - scroll down */
788 /* TODO */
789 break;
790 case 'd': /* absolute line number */
791 if (param1 < 1)
792 console->cursor_line = 0;
793 else if (param1 > console->nlines)
794 console->cursor_line = console->nlines;
795 else
796 console->cursor_line = param1 - 1;
797 break;
798 case 'g': /* clear tabs, ignore */
799 break;
800 case 'h': /* reset, ignore */
801 break;
802 case 'm': /* graphic changes */
803 parambuf[0] = '\0';
804 parambuflen = 0;
805 param2 = console->cur_attr;
806
807 for (x = 0; x < console->csilen; x++) {
808 /* all the way to console->csilen to catch 'm' */
809 if (console->csi[x] == ';' || console->csi[x] == 'm') {
810 param1 = atoi(parambuf);
811
812 switch (param1) {
813 case 0: /* reset */
814 #ifdef COLOR
815 cur_color = FG_WHITE | BG_BLACK;
816 #endif
817 param2 = 0;
818 break;
819 case 1: /* bold */
820 param2 |= ATTR_BOLD;
821 break;
822 case 4: /* underline */
823 param2 |= ATTR_UNDERLINE;
824 break;
825 case 7: /* reverse */
826 param2 |= ATTR_REVERSE;
827 break;
828 case 22: /* normal color */
829 param2 = 0;
830 break;
831 case 21: /* bold off */
832 param2 &= ~ATTR_BOLD;
833 break;
834 case 24: /* underline off */
835 param2 &= ~ATTR_UNDERLINE;
836 break;
837 case 27: /* inverse off */
838 param2 &= ~ATTR_REVERSE;
839 break;
840 case 30: /* fg black */
841 case 31: /* fg red */
842 case 32: /* fg green */
843 case 33: /* fg yellow */
844 case 34: /* fg blue */
845 case 35: /* fg magenta */
846 case 36: /* fg cyan */
847 case 37: /* fg white */
848 #ifdef COLOR
849 cur_color &= ~0xff;
850 cur_color |= (1 << (param1 - 30));
851 #endif
852 break;
853 case 40: /* bg black */
854 case 41: /* bg red */
855 case 42: /* bg green */
856 case 43: /* bg yellow */
857 case 44: /* bg blue */
858 case 45: /* bg magenta */
859 case 46: /* bg cyan */
860 case 47: /* bg white */
861 #ifdef COLOR
862 cur_color = (1 << (8 + param1 - 40)) |
863 (cur_color & 0xff);
864 #endif
865 break;
866 }
867
868 parambuf[0] = '\0';
869 parambuflen = 0;
870 } else if (parambuflen < nitems(parambuf) - 1) {
871 parambuf[parambuflen] = console->csi[x];
872 parambuflen++;
873 parambuf[parambuflen] = '\0';
874 }
875 }
876
877 console->cur_attr = param2;
878 break;
879 case 'n': /* DSR - device status report */
880 switch (param1) {
881 case 5: /* terminal is ready */
882 session = console->session;
883 if (session->ibuflen >= sizeof(session->ibuf) - 4)
884 break;
885 session->ibuf[session->ibuflen++] = ESC;
886 session->ibuf[session->ibuflen++] = '[';
887 session->ibuf[session->ibuflen++] = '0';
888 session->ibuf[session->ibuflen++] = 'n';
889 break;
890 case 6: /* CPR - report cursor position */
891 session = console->session;
892 if (session->ibuflen >= sizeof(session->ibuf) - 10)
893 break;
894 session->ibuf[session->ibuflen++] = ESC;
895 session->ibuf[session->ibuflen++] = '[';
896
897 if (console->cursor_line >= 99)
898 session->ibuf[session->ibuflen++] = '0' +
899 ((console->cursor_line + 1) / 100);
900 if (console->cursor_line >= 9)
901 session->ibuf[session->ibuflen++] = '0' +
902 (((console->cursor_line + 1) % 100) / 10);
903 session->ibuf[session->ibuflen++] = '0' +
904 ((console->cursor_line + 1) % 10);
905
906 session->ibuf[session->ibuflen++] = ';';
907
908 if (console->cursor_column >= 99)
909 session->ibuf[session->ibuflen++] = '0' +
910 ((console->cursor_column + 1) / 100);
911 if (console->cursor_column >= 9)
912 session->ibuf[session->ibuflen++] = '0' +
913 (((console->cursor_column + 1) % 100) / 10);
914 session->ibuf[session->ibuflen++] = '0' +
915 ((console->cursor_column + 1) % 10);
916
917 session->ibuf[session->ibuflen++] = 'R';
918 break;
919 }
920 break;
921 case 's': /* SCO - save cursor position */
922 console->saved_cursor_column = console->cursor_column;
923 console->saved_cursor_line = console->cursor_line;
924 break;
925 case 'u': /* SCO - restore saved cursor position */
926 console->cursor_column = console->saved_cursor_column;
927 console->cursor_line = console->saved_cursor_line;
928 break;
929 default:
930 /*
931 * if the last character is a letter and we haven't serviced
932 * it, assume it's a sequence we don't support and should just
933 * suppress
934 */
935 if (c < 65 || (c > 90 && c < 97) || c > 122)
936 serviced = 0;
937 }
938
939 if (serviced) {
940 console->csilen = 0;
941 console->csi[0] = '\0';
942 console->in_csi = 0;
943 console_bound(console);
944 }
945 }