xref: /haiku/src/system/boot/platform/openfirmware/console.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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