xref: /haiku/src/system/boot/platform/openfirmware/console.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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(writeHandle, 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