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