AmendHub

Download

jcs

/

subtext

/

console.c

 

(View History)

jcs   logger: Add buffered logging, support logging without updating window Latest amendment: 245 on 2022-08-14

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