xref: /haiku/src/system/boot/platform/efi/console.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2014-2016 Haiku, Inc. All rights reserved.
3  * Copyright 2013 Fredrik Holmqvist, fredrik.holmqvist@gmail.com. All rights
4  * reserved.
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "console.h"
10 
11 #include <string.h>
12 
13 #include <SupportDefs.h>
14 
15 #include <boot/stage2.h>
16 #include <boot/platform.h>
17 #include <util/kernel_cpp.h>
18 
19 #include "efi_platform.h"
20 
21 
22 class Console : public ConsoleNode {
23 	public:
24 		Console();
25 
26 		virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer,
27 			size_t bufferSize);
28 		virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer,
29 			size_t bufferSize);
30 };
31 
32 
33 static uint32 sScreenWidth;
34 static uint32 sScreenHeight;
35 static uint32 sScreenMode;
36 static Console sInput, sOutput;
37 FILE *stdin, *stdout, *stderr;
38 
39 
40 //	#pragma mark -
41 
42 
43 Console::Console()
44 	: ConsoleNode()
45 {
46 }
47 
48 
49 ssize_t
50 Console::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
51 {
52 	return B_ERROR;
53 }
54 
55 
56 ssize_t
57 Console::WriteAt(void *cookie, off_t /*pos*/, const void *buffer,
58 	size_t bufferSize)
59 {
60 	const char *string = (const char *)buffer;
61 	char16_t ucsBuffer[bufferSize + 3];
62 	uint32 j = 0;
63 
64 	for (uint32 i = 0; i < bufferSize; i++) {
65 		switch (string[i]) {
66 			case '\n': {
67 				ucsBuffer[j++] = '\r';
68 				ucsBuffer[j++] = '\n';
69 			} //fallthrough
70 			case 0 : {
71 				//Not sure if we should keep going or abort for 0.
72 				//Keep going was easy anyway.
73 				ucsBuffer[j] = 0;
74 				kSystemTable->ConOut->OutputString(kSystemTable->ConOut,
75 					ucsBuffer);
76 				j = 0;
77 				continue;
78 			}
79 			default:
80 				ucsBuffer[j++] = (char16_t)string[i];
81 		}
82 	}
83 
84 	if (j > 0) {
85 		ucsBuffer[j] = 0;
86 		kSystemTable->ConOut->OutputString(kSystemTable->ConOut, ucsBuffer);
87 	}
88 	return bufferSize;
89 }
90 
91 
92 //	#pragma mark -
93 
94 
95 void
96 console_clear_screen(void)
97 {
98 	kSystemTable->ConOut->ClearScreen(kSystemTable->ConOut);
99 }
100 
101 
102 int32
103 console_width(void)
104 {
105 	return sScreenWidth;
106 }
107 
108 
109 int32
110 console_height(void)
111 {
112 	return sScreenHeight;
113 }
114 
115 
116 void
117 console_set_cursor(int32 x, int32 y)
118 {
119 	kSystemTable->ConOut->SetCursorPosition(kSystemTable->ConOut, x, y);
120 }
121 
122 
123 void
124 console_show_cursor(void)
125 {
126 	kSystemTable->ConOut->EnableCursor(kSystemTable->ConOut, true);
127 }
128 
129 
130 void
131 console_hide_cursor(void)
132 {
133 	kSystemTable->ConOut->EnableCursor(kSystemTable->ConOut, false);
134 }
135 
136 
137 void
138 console_set_color(int32 foreground, int32 background)
139 {
140 	kSystemTable->ConOut->SetAttribute(kSystemTable->ConOut,
141 		EFI_TEXT_ATTR((foreground & 0xf), (background & 0xf)));
142 }
143 
144 
145 int
146 console_wait_for_key(void)
147 {
148 	size_t index;
149 	efi_status status;
150 	efi_input_key key;
151 	efi_event event = kSystemTable->ConIn->WaitForKey;
152 
153 	do {
154 		kBootServices->WaitForEvent(1, &event, &index);
155 		status = kSystemTable->ConIn->ReadKeyStroke(kSystemTable->ConIn, &key);
156 	} while (status == EFI_NOT_READY);
157 
158 	if (key.UnicodeChar > 0)
159 		return (int) key.UnicodeChar;
160 
161 	switch (key.ScanCode) {
162 		case SCAN_ESC:
163 			return TEXT_CONSOLE_KEY_ESCAPE;
164 		case SCAN_UP:
165 			return TEXT_CONSOLE_KEY_UP;
166 		case SCAN_DOWN:
167 			return TEXT_CONSOLE_KEY_DOWN;
168 		case SCAN_LEFT:
169 			return TEXT_CONSOLE_KEY_LEFT;
170 		case SCAN_RIGHT:
171 			return TEXT_CONSOLE_KEY_RIGHT;
172 		case SCAN_PAGE_UP:
173 			return TEXT_CONSOLE_KEY_PAGE_UP;
174 		case SCAN_PAGE_DOWN:
175 			return TEXT_CONSOLE_KEY_PAGE_DOWN;
176 		case SCAN_HOME:
177 			return TEXT_CONSOLE_KEY_HOME;
178 		case SCAN_END:
179 			return TEXT_CONSOLE_KEY_END;
180 	}
181 	return 0;
182 }
183 
184 
185 static void update_screen_size(void)
186 {
187 	size_t width, height;
188 	size_t area = 0;
189 	efi_simple_text_output_protocol *ConOut = kSystemTable->ConOut;
190 
191 	for (int mode = 0; mode < ConOut->Mode->MaxMode; ++mode) {
192 		if (ConOut->QueryMode(ConOut, mode, &width, &height) == EFI_SUCCESS) {
193 			if (width * height > area) {
194 				sScreenWidth = width;
195 				sScreenHeight = height;
196 				sScreenMode = mode;
197 			}
198 		}
199 	}
200 
201 	ConOut->SetMode(ConOut, sScreenMode);
202 }
203 
204 
205 status_t
206 console_init(void)
207 {
208 	update_screen_size();
209 	console_hide_cursor();
210 	console_clear_screen();
211 
212 	// enable stdio functionality
213 	stdin = (FILE *)&sInput;
214 	stdout = stderr = (FILE *)&sOutput;
215 
216 	return B_OK;
217 }
218 
219 
220 uint32
221 console_check_boot_keys(void)
222 {
223 	efi_status status;
224 	efi_input_key key;
225 
226 	// give the user a chance to press a key
227 	kBootServices->Stall(500000);
228 
229 	status = kSystemTable->ConIn->ReadKeyStroke(kSystemTable->ConIn, &key);
230 
231 	if (status != EFI_SUCCESS)
232 		return 0;
233 
234 	if (key.UnicodeChar == 0 && key.ScanCode == SCAN_ESC)
235 		return BOOT_OPTION_DEBUG_OUTPUT;
236 	if (key.UnicodeChar == ' ')
237 		return BOOT_OPTION_MENU;
238 
239 	return 0;
240 }
241 
242 
243 extern "C" void
244 platform_switch_to_text_mode(void)
245 {
246 	kSystemTable->ConOut->Reset(kSystemTable->ConOut, false);
247 	kSystemTable->ConOut->SetMode(kSystemTable->ConOut, sScreenMode);
248 }
249