1 /*
2 * Console.cpp - Mimicing the console driver
3 * Based on the console driver.
4 *
5 * Copyright 2005 Michael Lotz. All rights reserved.
6 * Distributed under the MIT License.
7 *
8 * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
9 * Distributed under the terms of the MIT License.
10 *
11 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
12 * Distributed under the terms of the NewOS License.
13 */
14
15 #include "Console.h"
16 #include "ViewBuffer.h"
17 #include <stdio.h>
18
Console(ViewBuffer * output)19 Console::Console(ViewBuffer *output)
20 : fState(CONSOLE_STATE_NORMAL),
21 fOutput(output)
22 {
23 fOutput->GetSize(&fColumns, &fLines);
24 fOutput->SetResizeCallback(&ResizeCallback, this);
25 ResetConsole();
26 GotoXY(0, 0);
27 SaveCursor(true);
28 fOutput->Clear(0x0f);
29 }
30
31
~Console()32 Console::~Console()
33 {
34 }
35
36
37 void
ResetConsole()38 Console::ResetConsole()
39 {
40 fAttr = 0x0f;
41 fScrollTop = 0;
42 fScrollBottom = fLines - 1;
43 fBrightAttr = true;
44 fReverseAttr = false;
45 }
46
47
48 void
ResizeCallback(int32 width,int32 height,void * data)49 Console::ResizeCallback(int32 width, int32 height, void *data)
50 {
51 Console *console = (Console *)data;
52
53 console->fColumns = width;
54 console->fLines = height;
55 console->SetScrollRegion(console->fScrollTop, height - 1);
56 }
57
58
59 void
SetScrollRegion(int top,int bottom)60 Console::SetScrollRegion(int top, int bottom)
61 {
62 if (top < 0)
63 top = 0;
64 if (bottom >= fLines)
65 bottom = fLines - 1;
66 if (top > bottom)
67 return;
68
69 fScrollTop = top;
70 fScrollBottom = bottom;
71 }
72
73
74 void
ScrollUp()75 Console::ScrollUp()
76 {
77 // see if cursor is outside of scroll region
78 if (fY < fScrollTop || fY > fScrollBottom)
79 return;
80
81 if (fY - fScrollTop > 1) {
82 // move the screen up one
83 fOutput->Blit(0, fScrollTop + 1, fColumns, fY - fScrollTop, 0, fScrollTop);
84 }
85
86 // clear the bottom line
87 fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr);
88 }
89
90
91 void
ScrollDown()92 Console::ScrollDown()
93 {
94 // see if cursor is outside of scroll region
95 if (fY < fScrollTop || fY > fScrollBottom)
96 return;
97
98 if (fScrollBottom - fY > 1) {
99 // move the screen down one
100 fOutput->Blit(0, fY, fColumns, fScrollBottom - fY, 0, fY + 1);
101 }
102
103 // clear the top line
104 fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr);
105 }
106
107
108 void
LineFeed()109 Console::LineFeed()
110 {
111 if (fY == fScrollBottom) {
112 // we hit the bottom of our scroll region
113 ScrollUp();
114 } else if (fY < fScrollBottom) {
115 fY++;
116 }
117 }
118
119
120 void
RLineFeed()121 Console::RLineFeed()
122 {
123 if (fY == fScrollTop) {
124 // we hit the top of our scroll region
125 ScrollDown();
126 } else if (fY > fScrollTop) {
127 fY--;
128 }
129 }
130
131
132 void
CariageReturn()133 Console::CariageReturn()
134 {
135 fX = 0;
136 }
137
138
139 void
Delete()140 Console::Delete()
141 {
142 if (fX > 0) {
143 fX--;
144 } else if (fY > 0) {
145 fY--;
146 fX = fColumns - 1;
147 } else {
148 ScrollDown();
149 fY--;
150 fX = fColumns - 1;
151 return;
152 }
153
154 fOutput->PutGlyph(fX, fY, ' ', fAttr);
155 }
156
157
158 void
Tab()159 Console::Tab()
160 {
161 fX = (fX + TAB_SIZE) & ~TAB_MASK;
162 if (fX >= fColumns) {
163 fX -= fColumns;
164 LineFeed();
165 }
166 }
167
168
169 void
EraseLine(erase_line_mode mode)170 Console::EraseLine(erase_line_mode mode)
171 {
172 switch (mode) {
173 case LINE_ERASE_WHOLE:
174 fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr);
175 break;
176 case LINE_ERASE_LEFT:
177 fOutput->FillGlyph(0, fY, fX + 1, 1, ' ', fAttr);
178 break;
179 case LINE_ERASE_RIGHT:
180 fOutput->FillGlyph(fX, fY, fColumns - fX, 1, ' ', fAttr);
181 break;
182 default:
183 return;
184 }
185 }
186
187
188 void
EraseScreen(erase_screen_mode mode)189 Console::EraseScreen(erase_screen_mode mode)
190 {
191 switch (mode) {
192 case SCREEN_ERASE_WHOLE:
193 fOutput->Clear(fAttr);
194 break;
195 case SCREEN_ERASE_UP:
196 fOutput->FillGlyph(0, 0, fColumns, fY + 1, ' ', fAttr);
197 break;
198 case SCREEN_ERASE_DOWN:
199 fOutput->FillGlyph(fY, 0, fColumns, fLines - fY, ' ', fAttr);
200 break;
201 default:
202 return;
203 }
204 }
205
206
207 void
SaveCursor(bool save_attrs)208 Console::SaveCursor(bool save_attrs)
209 {
210 fSavedX = fX;
211 fSavedY = fY;
212
213 if (save_attrs)
214 fSavedAttr = fAttr;
215 }
216
217
218 void
RestoreCursor(bool restore_attrs)219 Console::RestoreCursor(bool restore_attrs)
220 {
221 fX = fSavedX;
222 fY = fSavedY;
223
224 if (restore_attrs)
225 fAttr = fSavedAttr;
226 }
227
228
229 void
UpdateCursor(int x,int y)230 Console::UpdateCursor(int x, int y)
231 {
232 fOutput->MoveCursor(x, y);
233 }
234
235
236 void
GotoXY(int new_x,int new_y)237 Console::GotoXY(int new_x, int new_y)
238 {
239 if (new_x >= fColumns)
240 new_x = fColumns - 1;
241 if (new_x < 0)
242 new_x = 0;
243 if (new_y >= fLines)
244 new_y = fLines - 1;
245 if (new_y < 0)
246 new_y = 0;
247
248 fX = new_x;
249 fY = new_y;
250 }
251
252
253 void
PutChar(const char c)254 Console::PutChar(const char c)
255 {
256 fOutput->PutGlyph(fX, fY, c, fAttr);
257 if (++fX >= fColumns) {
258 CariageReturn();
259 LineFeed();
260 }
261 }
262
263
264 void
SetVT100Attributes(int32 * args,int32 argCount)265 Console::SetVT100Attributes(int32 *args, int32 argCount)
266 {
267 if (argCount == 0) {
268 // that's the default (attributes off)
269 argCount++;
270 args[0] = 0;
271 }
272
273 for (int32 i = 0; i < argCount; i++) {
274 switch (args[i]) {
275 case 0: // reset
276 fAttr = 0x0f;
277 fBrightAttr = true;
278 fReverseAttr = false;
279 break;
280 case 1: // bright
281 fBrightAttr = true;
282 fAttr |= 0x08; // set the bright bit
283 break;
284 case 2: // dim
285 fBrightAttr = false;
286 fAttr &= ~0x08; // unset the bright bit
287 break;
288 case 4: // underscore we can't do
289 break;
290 case 5: // blink
291 fAttr |= 0x80; // set the blink bit
292 break;
293 case 7: // reverse
294 fReverseAttr = true;
295 fAttr = ((fAttr & BMASK) >> 4) | ((fAttr & FMASK) << 4);
296 if (fBrightAttr)
297 fAttr |= 0x08;
298 break;
299 case 8: // hidden?
300 break;
301
302 /* foreground colors */
303 case 30: fAttr = (fAttr & ~FMASK) | 0 | (fBrightAttr ? 0x08 : 0); break; // black
304 case 31: fAttr = (fAttr & ~FMASK) | 4 | (fBrightAttr ? 0x08 : 0); break; // red
305 case 32: fAttr = (fAttr & ~FMASK) | 2 | (fBrightAttr ? 0x08 : 0); break; // green
306 case 33: fAttr = (fAttr & ~FMASK) | 6 | (fBrightAttr ? 0x08 : 0); break; // yellow
307 case 34: fAttr = (fAttr & ~FMASK) | 1 | (fBrightAttr ? 0x08 : 0); break; // blue
308 case 35: fAttr = (fAttr & ~FMASK) | 5 | (fBrightAttr ? 0x08 : 0); break; // magenta
309 case 36: fAttr = (fAttr & ~FMASK) | 3 | (fBrightAttr ? 0x08 : 0); break; // cyan
310 case 37: fAttr = (fAttr & ~FMASK) | 7 | (fBrightAttr ? 0x08 : 0); break; // white
311
312 /* background colors */
313 case 40: fAttr = (fAttr & ~BMASK) | (0 << 4); break; // black
314 case 41: fAttr = (fAttr & ~BMASK) | (4 << 4); break; // red
315 case 42: fAttr = (fAttr & ~BMASK) | (2 << 4); break; // green
316 case 43: fAttr = (fAttr & ~BMASK) | (6 << 4); break; // yellow
317 case 44: fAttr = (fAttr & ~BMASK) | (1 << 4); break; // blue
318 case 45: fAttr = (fAttr & ~BMASK) | (5 << 4); break; // magenta
319 case 46: fAttr = (fAttr & ~BMASK) | (3 << 4); break; // cyan
320 case 47: fAttr = (fAttr & ~BMASK) | (7 << 4); break; // white
321 }
322 }
323 }
324
325
326 bool
ProcessVT100Command(const char c,bool seen_bracket,int32 * args,int32 argCount)327 Console::ProcessVT100Command(const char c, bool seen_bracket, int32 *args, int32 argCount)
328 {
329 bool ret = true;
330
331 if (seen_bracket) {
332 switch(c) {
333 case 'H': /* set cursor position */
334 case 'f': {
335 int32 row = argCount > 0 ? args[0] : 1;
336 int32 col = argCount > 1 ? args[1] : 1;
337 if (row > 0)
338 row--;
339 if (col > 0)
340 col--;
341 GotoXY(col, row);
342 break;
343 }
344 case 'A': { /* move up */
345 int32 deltay = argCount > 0 ? -args[0] : -1;
346 if (deltay == 0)
347 deltay = -1;
348 GotoXY(fX, fY + deltay);
349 break;
350 }
351 case 'e':
352 case 'B': { /* move down */
353 int32 deltay = argCount > 0 ? args[0] : 1;
354 if (deltay == 0)
355 deltay = 1;
356 GotoXY(fX, fY + deltay);
357 break;
358 }
359 case 'D': { /* move left */
360 int32 deltax = argCount > 0 ? -args[0] : -1;
361 if (deltax == 0)
362 deltax = -1;
363 GotoXY(fX + deltax, fY);
364 break;
365 }
366 case 'a':
367 case 'C': { /* move right */
368 int32 deltax = argCount > 0 ? args[0] : 1;
369 if (deltax == 0)
370 deltax = 1;
371 GotoXY(fX + deltax, fY);
372 break;
373 }
374 case '`':
375 case 'G': { /* set X position */
376 int32 newx = argCount > 0 ? args[0] : 1;
377 if (newx > 0)
378 newx--;
379 GotoXY(newx, fY);
380 break;
381 }
382 case 'd': { /* set y position */
383 int32 newy = argCount > 0 ? args[0] : 1;
384 if (newy > 0)
385 newy--;
386 GotoXY(fX, newy);
387 break;
388 }
389 case 's': /* save current cursor */
390 SaveCursor(false);
391 break;
392 case 'u': /* restore cursor */
393 RestoreCursor(false);
394 break;
395 case 'r': { /* set scroll region */
396 int32 low = argCount > 0 ? args[0] : 1;
397 int32 high = argCount > 1 ? args[1] : fLines;
398 if (low <= high)
399 SetScrollRegion(low - 1, high - 1);
400 break;
401 }
402 case 'L': { /* scroll virtual down at cursor */
403 int32 lines = argCount > 0 ? args[0] : 1;
404 while (lines > 0) {
405 ScrollDown();
406 lines--;
407 }
408 break;
409 }
410 case 'M': { /* scroll virtual up at cursor */
411 int32 lines = argCount > 0 ? args[0] : 1;
412 while (lines > 0) {
413 ScrollUp();
414 lines--;
415 }
416 break;
417 }
418 case 'K':
419 if (argCount == 0 || args[0] == 0) {
420 // erase to end of line
421 EraseLine(LINE_ERASE_RIGHT);
422 } else if (argCount > 0) {
423 if (args[0] == 1)
424 EraseLine(LINE_ERASE_LEFT);
425 else if (args[0] == 2)
426 EraseLine(LINE_ERASE_WHOLE);
427 }
428 break;
429 case 'J':
430 if (argCount == 0 || args[0] == 0) {
431 // erase to end of screen
432 EraseScreen(SCREEN_ERASE_DOWN);
433 } else if (argCount > 0) {
434 if (args[0] == 1)
435 EraseScreen(SCREEN_ERASE_UP);
436 else if (args[0] == 2)
437 EraseScreen(SCREEN_ERASE_WHOLE);
438 }
439 break;
440 case 'm':
441 if (argCount >= 0)
442 SetVT100Attributes(args, argCount);
443 break;
444 default:
445 ret = false;
446 }
447 } else {
448 switch (c) {
449 case 'c':
450 ResetConsole();
451 break;
452 case 'D':
453 RLineFeed();
454 break;
455 case 'M':
456 LineFeed();
457 break;
458 case '7':
459 SaveCursor(true);
460 break;
461 case '8':
462 RestoreCursor(true);
463 break;
464 default:
465 ret = false;
466 }
467 }
468
469 return ret;
470 }
471
472
473 void
Write(const void * buf,size_t len)474 Console::Write(const void *buf, size_t len)
475 {
476 UpdateCursor(-1, -1); // hide the cursor
477
478 const char *c;
479 size_t pos = 0;
480
481 while (pos < len) {
482 c = &((const char *)buf)[pos++];
483
484 switch (fState) {
485 case CONSOLE_STATE_NORMAL:
486 // just output the stuff
487 switch (*c) {
488 case '\n':
489 LineFeed();
490 break;
491 case '\r':
492 CariageReturn();
493 break;
494 case 0x8: // backspace
495 Delete();
496 break;
497 case '\t':
498 Tab();
499 break;
500 case '\a':
501 // beep
502 //printf("<BEEP>\n");
503 break;
504 case '\0':
505 break;
506 case 0x1b:
507 // escape character
508 fArgCount = -1;
509 fState = CONSOLE_STATE_GOT_ESCAPE;
510 break;
511 default:
512 PutChar(*c);
513 }
514 break;
515 case CONSOLE_STATE_GOT_ESCAPE:
516 // look for either commands with no argument, or the '[' character
517 switch (*c) {
518 case '[':
519 fState = CONSOLE_STATE_SEEN_BRACKET;
520 break;
521 default:
522 fArgs[fArgCount] = 0;
523 ProcessVT100Command(*c, false, fArgs, fArgCount + 1);
524 fState = CONSOLE_STATE_NORMAL;
525 }
526 break;
527 case CONSOLE_STATE_SEEN_BRACKET:
528 switch (*c) {
529 case '0'...'9':
530 fArgCount = 0;
531 fArgs[fArgCount] = *c - '0';
532 fState = CONSOLE_STATE_PARSING_ARG;
533 break;
534 case '?':
535 // private DEC mode parameter follows - we ignore those anyway
536 // ToDo: check if it was really used in combination with a mode command
537 break;
538 default:
539 ProcessVT100Command(*c, true, fArgs, fArgCount + 1);
540 fState = CONSOLE_STATE_NORMAL;
541 }
542 break;
543 case CONSOLE_STATE_NEW_ARG:
544 switch (*c) {
545 case '0'...'9':
546 fArgCount++;
547 if (fArgCount == MAX_ARGS) {
548 fState = CONSOLE_STATE_NORMAL;
549 break;
550 }
551 fArgs[fArgCount] = *c - '0';
552 fState = CONSOLE_STATE_PARSING_ARG;
553 break;
554 default:
555 ProcessVT100Command(*c, true, fArgs, fArgCount + 1);
556 fState = CONSOLE_STATE_NORMAL;
557 }
558 break;
559 case CONSOLE_STATE_PARSING_ARG:
560 // parse args
561 switch (*c) {
562 case '0'...'9':
563 fArgs[fArgCount] *= 10;
564 fArgs[fArgCount] += *c - '0';
565 break;
566 case ';':
567 fState = CONSOLE_STATE_NEW_ARG;
568 break;
569 default:
570 ProcessVT100Command(*c, true, fArgs, fArgCount + 1);
571 fState = CONSOLE_STATE_NORMAL;
572 }
573 }
574 }
575
576 UpdateCursor(fX, fY); // show it again
577 }
578