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
21class 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
53extern ConsoleNode* gConsoleNode;
54static Console sConsole;
55FILE *stdin, *stdout, *stderr;
56
57
58Console::Console()
59	:
60	ConsoleNode()
61{
62}
63
64
65void
66Console::SetHandles(intptr_t readHandle, intptr_t writeHandle, bool takeOwnership)
67{
68	fReadHandle.SetHandle(readHandle, takeOwnership);
69	fWriteHandle.SetHandle(readHandle, takeOwnership);
70}
71
72
73ssize_t
74Console::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
99ssize_t
100Console::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
144void
145Console::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
156void
157Console::PutBackChars(const char *buffer, int count)
158{
159	for (int i = 0; i < count; i++)
160		PutBackChar(buffer[i]);
161}
162
163
164char
165Console::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
180status_t
181console_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
205void
206Console::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
218int32
219Console::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
232int32
233Console::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
246void
247Console::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
266void
267Console::SetCursorVisible(bool visible)
268{
269}
270
271
272#ifndef __sparc__
273static int
274translate_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
302void
303Console::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
323static int
324translate_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
348int
349console_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
375int
376console_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