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