xref: /haiku/src/tests/apps/miniterminal/Console.cpp (revision 47a21c5c89fc9fd155a3929e5a8f6056b92a2053)
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