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