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