1/* 2 * Copyright 2003-2005, Axel D��rfler, axeld@pinc-software.de. 3 * Copyright 2010 Andreas F��rber <andreas.faerber@web.de> 4 * All rights reserved. Distributed under the terms of the MIT License. 5 */ 6 7 8#include "console.h" 9 10#include <string.h> 11 12#include <SupportDefs.h> 13 14#include <boot/stage2.h> 15#include <platform/openfirmware/openfirmware.h> 16#include <util/kernel_cpp.h> 17 18#include "Handle.h" 19 20 21class Console : public ConsoleNode { 22 public: 23 Console(); 24 25 void SetHandles(intptr_t readHandle, intptr_t writeHandle, bool takeOwnership = true); 26 27 virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, 28 size_t bufferSize); 29 virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, 30 size_t bufferSize); 31 32 virtual void ClearScreen(); 33 virtual int32 Width(); 34 virtual int32 Height(); 35 virtual void SetCursor(int32 x, int32 y); 36 virtual void SetCursorVisible(bool visible); 37 virtual void SetColors(int32 foreground, int32 background); 38 39 void PutBackChar(char c); 40 void PutBackChars(const char *buffer, int count); 41 char GetChar(); 42 43 private: 44 Handle fReadHandle, fWriteHandle; 45 46 enum { BUFFER_SIZE = 32 }; 47 char fReadBuffer[BUFFER_SIZE]; 48 int fStart; 49 int fCount; 50}; 51 52 53extern ConsoleNode* gConsoleNode; 54static Console sConsole; 55FILE *stdin, *stdout, *stderr; 56 57 58Console::Console() 59 : 60 ConsoleNode() 61{ 62} 63 64 65void 66Console::SetHandles(intptr_t readHandle, intptr_t writeHandle, bool takeOwnership) 67{ 68 fReadHandle.SetHandle(readHandle, takeOwnership); 69 fWriteHandle.SetHandle(readHandle, takeOwnership); 70} 71 72 73ssize_t 74Console::ReadAt(void */*cookie*/, off_t /*pos*/, void *_buffer, 75 size_t bufferSize) 76{ 77 char *buffer = (char*)_buffer; 78 79 // copy buffered bytes first 80 int bytesTotal = 0; 81 while (bufferSize > 0 && fCount > 0) { 82 *buffer++ = GetChar(); 83 bytesTotal++; 84 bufferSize--; 85 } 86 87 // read from console 88 if (bufferSize > 0) { 89 ssize_t bytesRead = fReadHandle.ReadAt(NULL, -1, buffer, bufferSize); 90 if (bytesRead < 0) 91 return bytesRead; 92 bytesTotal += bytesRead; 93 } 94 95 return bytesTotal; 96} 97 98 99ssize_t 100Console::WriteAt(void */*cookie*/, off_t /*pos*/, const void *buffer, 101 size_t bufferSize) 102{ 103 const char *string = (const char *)buffer; 104 105 // If the frame buffer is enabled, don't write to the chosen stdout. 106 // On Apple's OpenFirmware this would overwrite parts of the frame buffer. 107 if (gKernelArgs.frame_buffer.enabled) 108 return bufferSize; 109 110 // be nice to our audience and replace single "\n" with "\r\n" 111 112 while (bufferSize > 0) { 113 bool newLine = false; 114 size_t length = 0; 115 116 for (; length < bufferSize; length++) { 117 if (string[length] == '\r' && length + 1 < bufferSize) { 118 length += 2; 119 } else if (string[length] == '\n') { 120 newLine = true; 121 break; 122 } 123 } 124 125 if (length > 0) { 126 fWriteHandle.WriteAt(NULL, -1, string, length); 127 string += length; 128 bufferSize -= length; 129 } 130 131 if (newLine) { 132 // this code replaces a single '\n' with '\r\n', so it 133 // bumps the string/bufferSize only a single character 134 fWriteHandle.WriteAt(NULL, -1, "\r\n", 2); 135 string++; 136 bufferSize--; 137 } 138 } 139 140 return string - (char *)buffer; 141} 142 143 144void 145Console::PutBackChar(char c) 146{ 147 if (fCount >= BUFFER_SIZE) 148 return; 149 150 int pos = (fStart + fCount) % BUFFER_SIZE; 151 fReadBuffer[pos] = c; 152 fCount++; 153} 154 155 156void 157Console::PutBackChars(const char *buffer, int count) 158{ 159 for (int i = 0; i < count; i++) 160 PutBackChar(buffer[i]); 161} 162 163 164char 165Console::GetChar() 166{ 167 if (fCount == 0) 168 return 0; 169 170 fCount--; 171 char c = fReadBuffer[fStart]; 172 fStart = (fStart + 1) % BUFFER_SIZE; 173 return c; 174} 175 176 177// #pragma mark - 178 179 180status_t 181console_init(void) 182{ 183 unsigned int input, output; 184 if (of_getprop(gChosen, "stdin", &input, sizeof(input)) != sizeof(input)) 185 return B_ERROR; 186 if (of_getprop(gChosen, "stdout", &output, sizeof(output)) 187 != sizeof(output)) { 188 return B_ERROR; 189 } 190 191 sConsole.SetHandles(input, output); 192 gConsoleNode = &sConsole; 193 194 // now that we're initialized, enable stdio functionality 195 stdin = (FILE *)gConsoleNode; 196 stdout = stderr = (FILE *)gConsoleNode; 197 198 return B_OK; 199} 200 201 202// #pragma mark - 203 204 205void 206Console::ClearScreen() 207{ 208#ifdef __sparc__ 209 // Send both a clear screen (for serial terminal) and a vertical form 210 // feed for on-screen console 211 Write("\014\033[2J", 5); 212#else 213 of_interpret("erase-screen", 0, 0); 214#endif 215} 216 217 218int32 219Console::Width() 220{ 221 intptr_t columnCount; 222#ifdef __sparc__ 223 if (of_interpret("screen-#columns", 0, 1, &columnCount) == OF_FAILED) 224#else 225 if (of_interpret("#columns", 0, 1, &columnCount) == OF_FAILED) 226#endif 227 return 0; 228 return columnCount; 229} 230 231 232int32 233Console::Height() 234{ 235 intptr_t lineCount; 236#ifdef __sparc__ 237 if (of_interpret("screen-#rows", 0, 1, &lineCount) == OF_FAILED) 238#else 239 if (of_interpret("#lines", 0, 1, &lineCount) == OF_FAILED) 240#endif 241 return 0; 242 return lineCount; 243} 244 245 246void 247Console::SetCursor(int32 x, int32 y) 248{ 249#ifdef __sparc__ 250 char buffer[11]; 251 int len = snprintf(buffer, sizeof(buffer), 252 "\033[%" B_PRId32 ";%" B_PRId32 "H", y, x); 253 Write(buffer, len); 254#else 255 // Note: We toggle the cursor temporarily to prevent a cursor artifact at 256 // at the old location. 257 of_interpret("toggle-cursor" 258 " to line#" 259 " to column#" 260 " toggle-cursor", 261 2, 0, y, x); 262#endif 263} 264 265 266void 267Console::SetCursorVisible(bool visible) 268{ 269} 270 271 272#ifndef __sparc__ 273static int 274translate_color(int32 color) 275{ 276 /* 277 r g b 278 0: 0 0 0 // black 279 1: 0 0 aa // dark blue 280 2: 0 aa 0 // dark green 281 3: 0 aa aa // cyan 282 4: aa 0 0 // dark red 283 5: aa 0 aa // purple 284 6: aa 55 0 // brown 285 7: aa aa aa // light gray 286 8: 55 55 55 // dark gray 287 9: 55 55 ff // light blue 288 a: 55 ff 55 // light green 289 b: 55 ff ff // light cyan 290 c: ff 55 55 // light red 291 d: ff 55 ff // magenta 292 e: ff ff 55 // yellow 293 f: ff ff ff // white 294 */ 295 if (color >= 0 && color < 16) 296 return color; 297 return 0; 298} 299#endif 300 301 302void 303Console::SetColors(int32 foreground, int32 background) 304{ 305#ifdef __sparc__ 306 // Sadly it seems we can only get inverse video, nothing else seems to work 307 if (background != 0) 308 Write("\033[7m", 4); 309 else 310 Write("\033[0m", 4); 311#else 312 // Note: Toggling the cursor doesn't seem to help. We still get cursor 313 // artifacts. 314 of_interpret("toggle-cursor" 315 " to foreground-color" 316 " to background-color" 317 " toggle-cursor", 318 2, 0, translate_color(foreground), translate_color(background)); 319#endif 320} 321 322 323static int 324translate_key(char escapeCode) 325{ 326 switch (escapeCode) { 327 case 65: 328 return TEXT_CONSOLE_KEY_UP; 329 case 66: 330 return TEXT_CONSOLE_KEY_DOWN; 331 case 67: 332 return TEXT_CONSOLE_KEY_RIGHT; 333 case 68: 334 return TEXT_CONSOLE_KEY_LEFT; 335// TODO: Translate the codes for the following keys. Unfortunately my OF just 336// returns a '\0' character. :-/ 337// TEXT_CONSOLE_KEY_PAGE_UP, 338// TEXT_CONSOLE_KEY_PAGE_DOWN, 339// TEXT_CONSOLE_KEY_HOME, 340// TEXT_CONSOLE_KEY_END, 341 342 default: 343 return 0; 344 } 345} 346 347 348int 349console_wait_for_key(void) 350{ 351 // wait for a key 352 char buffer[3]; 353 ssize_t bytesRead; 354 do { 355 bytesRead = sConsole.ReadAt(NULL, 0, buffer, 3); 356 if (bytesRead < 0) 357 return 0; 358 } while (bytesRead == 0); 359 360 // translate the ESC sequences for cursor keys 361 if (bytesRead == 3 && buffer[0] == 27 && buffer[1] == 91) { 362 int key = translate_key(buffer[2]); 363 if (key != 0) 364 return key; 365 } 366 367 // put back unread chars 368 if (bytesRead > 1) 369 sConsole.PutBackChars(buffer + 1, bytesRead - 1); 370 371 return buffer[0]; 372} 373 374 375int 376console_check_for_key(void) 377{ 378 char buffer[3]; 379 ssize_t bytesRead = sConsole.ReadAt(NULL, 0, buffer, 3); 380 if (bytesRead <= 0) 381 return 0; 382 383 // translate the ESC sequences for cursor keys 384 if (bytesRead == 3 && buffer[0] == 27 && buffer[1] == 91) { 385 int key = translate_key(buffer[2]); 386 if (key != 0) 387 return key; 388 } 389 390 // put back unread chars 391 if (bytesRead > 1) 392 sConsole.PutBackChars(buffer + 1, bytesRead - 1); 393 394 return buffer[0]; 395} 396