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 int input, output; 195 if (of_getprop(gChosen, "stdin", &input, sizeof(int)) == OF_FAILED) 196 return B_ERROR; 197 if (of_getprop(gChosen, "stdout", &output, sizeof(int)) == OF_FAILED) 198 return B_ERROR; 199 200 sInput.SetHandle(input); 201 sOutput.SetHandle(output); 202 203 // now that we're initialized, enable stdio functionality 204 stdin = (FILE *)&sInput; 205 stdout = stderr = (FILE *)&sOutput; 206 207 return B_OK; 208 } 209 210 211 // #pragma mark - 212 213 214 void 215 console_clear_screen(void) 216 { 217 of_interpret("erase-screen", 0, 0); 218 } 219 220 221 int32 222 console_width(void) 223 { 224 int columnCount; 225 if (of_interpret("#columns", 0, 1, &columnCount) == OF_FAILED) 226 return 0; 227 return columnCount; 228 } 229 230 231 int32 232 console_height(void) 233 { 234 int lineCount; 235 if (of_interpret("#lines", 0, 1, &lineCount) == OF_FAILED) 236 return 0; 237 return lineCount; 238 } 239 240 241 void 242 console_set_cursor(int32 x, int32 y) 243 { 244 // Note: We toggle the cursor temporarily to prevent a cursor artifact at 245 // at the old location. 246 of_interpret("toggle-cursor" 247 " to line#" 248 " to column#" 249 " toggle-cursor", 250 2, 0, y, x); 251 252 } 253 254 255 void 256 console_show_cursor(void) 257 { 258 } 259 260 261 void 262 console_hide_cursor(void) 263 { 264 } 265 266 267 static int 268 translate_color(int32 color) 269 { 270 /* 271 r g b 272 0: 0 0 0 // black 273 1: 0 0 aa // dark blue 274 2: 0 aa 0 // dark green 275 3: 0 aa aa // cyan 276 4: aa 0 0 // dark red 277 5: aa 0 aa // purple 278 6: aa 55 0 // brown 279 7: aa aa aa // light gray 280 8: 55 55 55 // dark gray 281 9: 55 55 ff // light blue 282 a: 55 ff 55 // light green 283 b: 55 ff ff // light cyan 284 c: ff 55 55 // light red 285 d: ff 55 ff // magenta 286 e: ff ff 55 // yellow 287 f: ff ff ff // white 288 */ 289 if (color >= 0 && color < 16) 290 return color; 291 return 0; 292 } 293 294 295 void 296 console_set_color(int32 foreground, int32 background) 297 { 298 // Note: Toggling the cursor doesn't seem to help. We still get cursor 299 // artifacts. 300 of_interpret("toggle-cursor" 301 " to foreground-color" 302 " to background-color" 303 " toggle-cursor", 304 2, 0, translate_color(foreground), translate_color(background)); 305 } 306 307 308 static int 309 translate_key(char escapeCode) 310 { 311 switch (escapeCode) { 312 case 65: 313 return TEXT_CONSOLE_KEY_UP; 314 case 66: 315 return TEXT_CONSOLE_KEY_DOWN; 316 case 67: 317 return TEXT_CONSOLE_KEY_RIGHT; 318 case 68: 319 return TEXT_CONSOLE_KEY_LEFT; 320 // TODO: Translate the codes for the following keys. Unfortunately my OF just 321 // returns a '\0' character. :-/ 322 // TEXT_CONSOLE_KEY_PAGE_UP, 323 // TEXT_CONSOLE_KEY_PAGE_DOWN, 324 // TEXT_CONSOLE_KEY_HOME, 325 // TEXT_CONSOLE_KEY_END, 326 327 default: 328 return 0; 329 } 330 } 331 332 333 int 334 console_wait_for_key(void) 335 { 336 // wait for a key 337 char buffer[3]; 338 ssize_t bytesRead; 339 do { 340 bytesRead = sInput.ReadAt(NULL, 0, buffer, 3); 341 if (bytesRead < 0) 342 return 0; 343 } while (bytesRead == 0); 344 345 // translate the ESC sequences for cursor keys 346 if (bytesRead == 3 && buffer[0] == 27 && buffer[1] == 91) { 347 int key = translate_key(buffer[2]); 348 if (key != 0) 349 return key; 350 } 351 352 // put back unread chars 353 if (bytesRead > 1) 354 sInput.PutChars(buffer + 1, bytesRead - 1); 355 356 return buffer[0]; 357 } 358 359 360 int 361 console_check_for_key(void) 362 { 363 char buffer[3]; 364 ssize_t bytesRead = sInput.ReadAt(NULL, 0, buffer, 3); 365 if (bytesRead <= 0) 366 return 0; 367 368 // translate the ESC sequences for cursor keys 369 if (bytesRead == 3 && buffer[0] == 27 && buffer[1] == 91) { 370 int key = translate_key(buffer[2]); 371 if (key != 0) 372 return key; 373 } 374 375 // put back unread chars 376 if (bytesRead > 1) 377 sInput.PutChars(buffer + 1, bytesRead - 1); 378 379 return buffer[0]; 380 } 381