AmendHub

Download

jcs

/

subtext

/

console.c

 

(View History)

jcs   user: Add user_first_sysop_id() Latest amendment: 562 on 2023-11-27

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