* Copyright 2014-2016 Haiku, Inc. All rights reserved.
* Copyright 2013 Fredrik Holmqvist, fredrik.holmqvist@gmail.com. All rights
* reserved.
* Distributed under the terms of the MIT License.
*/
#include "console.h"
#include <string.h>
#include <SupportDefs.h>
#include <boot/stage2.h>
#include <boot/platform.h>
#include <boot/platform/generic/video.h>
#include <efi/protocol/console-control.h>
#include <util/kernel_cpp.h>
#include "efi_platform.h"
#ifdef TRACE_CONSOLE
# define TRACE(x...) dprintf(x)
#else
# define TRACE(x...)
#endif
class EFITextConsole : public ConsoleNode {
public:
EFITextConsole();
virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer,
size_t bufferSize);
virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer,
size_t bufferSize);
virtual void ClearScreen();
virtual int32 Width();
virtual int32 Height();
virtual void SetCursor(int32 x, int32 y);
virtual void SetCursorVisible(bool visible);
virtual void SetColors(int32 foreground, int32 background);
public:
uint32 fScreenWidth, fScreenHeight;
};
extern ConsoleNode* gConsoleNode;
static uint32 sScreenMode;
static EFITextConsole sConsole;
FILE *stdin, *stdout, *stderr;
EFITextConsole::EFITextConsole()
: ConsoleNode()
{
}
ssize_t
EFITextConsole::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
{
return B_ERROR;
}
ssize_t
EFITextConsole::WriteAt(void *cookie, off_t , const void *buffer,
size_t bufferSize)
{
const char *string = (const char *)buffer;
char16_t ucsBuffer[bufferSize + 3];
uint32 j = 0;
for (uint32 i = 0; i < bufferSize; i++) {
switch (string[i]) {
case '\n': {
ucsBuffer[j++] = '\r';
ucsBuffer[j++] = '\n';
}
case 0 : {
ucsBuffer[j] = 0;
kSystemTable->ConOut->OutputString(kSystemTable->ConOut,
ucsBuffer);
j = 0;
continue;
}
default:
ucsBuffer[j++] = (char16_t)string[i];
}
}
if (j > 0) {
ucsBuffer[j] = 0;
kSystemTable->ConOut->OutputString(kSystemTable->ConOut, ucsBuffer);
}
return bufferSize;
}
void
EFITextConsole::ClearScreen()
{
kSystemTable->ConOut->ClearScreen(kSystemTable->ConOut);
}
int32
EFITextConsole::Width()
{
return fScreenWidth;
}
int32
EFITextConsole::Height()
{
return fScreenHeight;
}
void
EFITextConsole::SetCursor(int32 x, int32 y)
{
kSystemTable->ConOut->SetCursorPosition(kSystemTable->ConOut, x, y);
}
void
EFITextConsole::SetCursorVisible(bool visible)
{
kSystemTable->ConOut->EnableCursor(kSystemTable->ConOut, visible);
}
void
EFITextConsole::SetColors(int32 foreground, int32 background)
{
kSystemTable->ConOut->SetAttribute(kSystemTable->ConOut,
EFI_TEXT_ATTR((foreground & 0xf), (background & 0xf)));
}
int
console_wait_for_key(void)
{
size_t index;
efi_status status;
efi_input_key key;
efi_event event = kSystemTable->ConIn->WaitForKey;
do {
kBootServices->WaitForEvent(1, &event, &index);
status = kSystemTable->ConIn->ReadKeyStroke(kSystemTable->ConIn, &key);
} while (status == EFI_NOT_READY);
if (key.UnicodeChar > 0)
return (int) key.UnicodeChar;
switch (key.ScanCode) {
case SCAN_ESC:
return TEXT_CONSOLE_KEY_ESCAPE;
case SCAN_UP:
return TEXT_CONSOLE_KEY_UP;
case SCAN_DOWN:
return TEXT_CONSOLE_KEY_DOWN;
case SCAN_LEFT:
return TEXT_CONSOLE_KEY_LEFT;
case SCAN_RIGHT:
return TEXT_CONSOLE_KEY_RIGHT;
case SCAN_PAGE_UP:
return TEXT_CONSOLE_KEY_PAGE_UP;
case SCAN_PAGE_DOWN:
return TEXT_CONSOLE_KEY_PAGE_DOWN;
case SCAN_HOME:
return TEXT_CONSOLE_KEY_HOME;
case SCAN_END:
return TEXT_CONSOLE_KEY_END;
}
return 0;
}
static void update_screen_size(void)
{
size_t width, height;
size_t area = 0;
efi_simple_text_output_protocol *ConOut = kSystemTable->ConOut;
for (int mode = 0; mode < ConOut->Mode->MaxMode; ++mode) {
if (ConOut->QueryMode(ConOut, mode, &width, &height) == EFI_SUCCESS) {
if (width * height > area) {
sConsole.fScreenWidth = width;
sConsole.fScreenHeight = height;
sScreenMode = mode;
}
}
}
ConOut->SetMode(ConOut, sScreenMode);
}
status_t
console_init(void)
{
#if 1
gConsoleNode = &sConsole;
update_screen_size();
console_hide_cursor();
console_clear_screen();
#else
platform_init_video();
platform_switch_to_logo();
gConsoleNode = video_text_console_init(gKernelArgs.frame_buffer.physical_buffer.start);
#endif
stdin = (FILE *)gConsoleNode;
stdout = stderr = (FILE *)gConsoleNode;
return B_OK;
}
uint32
console_check_boot_keys(void)
{
efi_input_key key;
for (int i = 0; i < 3; i++) {
kBootServices->Stall(100000);
efi_status status = kSystemTable->ConIn->ReadKeyStroke(
kSystemTable->ConIn, &key);
if (status != EFI_SUCCESS)
continue;
if (key.UnicodeChar == 0 && key.ScanCode == SCAN_ESC)
return BOOT_OPTION_DEBUG_OUTPUT;
if (key.UnicodeChar == ' ')
return BOOT_OPTION_MENU;
}
return 0;
}
extern "C" void
platform_switch_to_text_mode(void)
{
kSystemTable->ConOut->Reset(kSystemTable->ConOut, false);
kSystemTable->ConOut->SetMode(kSystemTable->ConOut, sScreenMode);
}