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 21 class 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 53 extern ConsoleNode* gConsoleNode; 54 static Console sConsole; 55 FILE *stdin, *stdout, *stderr; 56 57 58 Console::Console() 59 : 60 ConsoleNode() 61 { 62 } 63 64 65 void 66 Console::SetHandles(intptr_t readHandle, intptr_t writeHandle, bool takeOwnership) 67 { 68 fReadHandle.SetHandle(readHandle, takeOwnership); 69 fWriteHandle.SetHandle(readHandle, takeOwnership); 70 } 71 72 73 ssize_t 74 Console::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 99 ssize_t 100 Console::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 144 void 145 Console::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 156 void 157 Console::PutBackChars(const char *buffer, int count) 158 { 159 for (int i = 0; i < count; i++) 160 PutBackChar(buffer[i]); 161 } 162 163 164 char 165 Console::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 180 status_t 181 console_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 205 void 206 Console::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 218 int32 219 Console::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 232 int32 233 Console::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 246 void 247 Console::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 266 void 267 Console::SetCursorVisible(bool visible) 268 { 269 } 270 271 272 #ifndef __sparc__ 273 static int 274 translate_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 302 void 303 Console::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 323 static int 324 translate_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 348 int 349 console_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 375 int 376 console_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