Pulled platform independent part of the BIOS boot menu out into
platform/generic/... git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15587 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
59d0807e01
commit
4c62b0919f
75
headers/private/kernel/boot/platform/generic/text_console.h
Normal file
75
headers/private/kernel/boot/platform/generic/text_console.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
** Distributed under the terms of the Haiku License.
|
||||
*/
|
||||
#ifndef GENERIC_TEXT_CONSOLE_H
|
||||
#define GENERIC_TEXT_CONSOLE_H
|
||||
|
||||
|
||||
#include <boot/vfs.h>
|
||||
#include <boot/stdio.h>
|
||||
|
||||
// Standard 16 color palette. Common for BIOS and OpenFirmware.
|
||||
enum console_color {
|
||||
// foreground and background colors
|
||||
BLACK,
|
||||
BLUE,
|
||||
GREEN,
|
||||
CYAN,
|
||||
RED,
|
||||
PURPLE,
|
||||
BROWN,
|
||||
GRAY,
|
||||
// foreground colors only if blinking is enabled (restriction applies
|
||||
// to BIOS only)
|
||||
DARK_GRAY,
|
||||
BRIGHT_BLUE,
|
||||
BRIGHT_GREEN,
|
||||
BRIGHT_CYAN,
|
||||
BRIGHT_RED,
|
||||
MAGENTA,
|
||||
YELLOW,
|
||||
WHITE
|
||||
};
|
||||
|
||||
enum {
|
||||
// ASCII
|
||||
TEXT_CONSOLE_NO_KEY = '\0',
|
||||
TEXT_CONSOLE_KEY_RETURN = '\r',
|
||||
TEXT_CONSOLE_KEY_ESCAPE = 0x1b,
|
||||
TEXT_CONSOLE_KEY_SPACE = ' ',
|
||||
|
||||
// cursor keys
|
||||
TEXT_CONSOLE_CURSOR_KEYS_START = 0x40000000,
|
||||
TEXT_CONSOLE_KEY_UP = TEXT_CONSOLE_CURSOR_KEYS_START,
|
||||
TEXT_CONSOLE_KEY_DOWN,
|
||||
TEXT_CONSOLE_KEY_LEFT,
|
||||
TEXT_CONSOLE_KEY_RIGHT,
|
||||
TEXT_CONSOLE_KEY_PAGE_UP,
|
||||
TEXT_CONSOLE_KEY_PAGE_DOWN,
|
||||
TEXT_CONSOLE_KEY_HOME,
|
||||
TEXT_CONSOLE_KEY_END,
|
||||
TEXT_CONSOLE_CURSOR_KEYS_END,
|
||||
};
|
||||
|
||||
#define TEXT_CONSOLE_IS_CURSOR_KEY(key) \
|
||||
(key >= TEXT_CONSOLE_CURSOR_KEYS_START \
|
||||
&& key < TEXT_CONSOLE_CURSOR_KEYS_END)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void console_clear_screen(void);
|
||||
extern int32 console_width(void);
|
||||
extern int32 console_height(void);
|
||||
extern void console_set_cursor(int32 x, int32 y);
|
||||
extern void console_set_color(int32 foreground, int32 background);
|
||||
|
||||
extern int console_wait_for_key(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GENERIC_TEXT_CONSOLE_H */
|
15
headers/private/kernel/boot/platform/generic/text_menu.h
Normal file
15
headers/private/kernel/boot/platform/generic/text_menu.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
|
||||
* All rights reserved. Distributed under the terms of the Haiku License.
|
||||
*/
|
||||
|
||||
#ifndef GENERIC_TEXT_CONSOLE_H
|
||||
#define GENERIC_TEXT_CONSOLE_H
|
||||
|
||||
class Menu;
|
||||
class MenuItem;
|
||||
|
||||
void platform_generic_update_text_menu_item(Menu *menu, MenuItem *item);
|
||||
void platform_generic_run_text_menu(Menu *menu);
|
||||
|
||||
#endif /* GENERIC_TEXT_CONSOLE_H */
|
@ -24,8 +24,14 @@ KernelMergeObject boot_platform_bios_ia32.o :
|
||||
smp_trampoline.S
|
||||
support.S
|
||||
video.cpp
|
||||
|
||||
# generic
|
||||
text_menu.cpp
|
||||
: -fno-pic
|
||||
;
|
||||
;
|
||||
|
||||
SEARCH on [ FGristFiles text_menu.cpp ]
|
||||
= [ FDirName $(HAIKU_TOP) src system boot platform generic ] ;
|
||||
|
||||
# Tell the build system to where stage1.bin can be found, so it can be used
|
||||
# elsewhere.
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
|
||||
#include "console.h"
|
||||
#include "keyboard.h"
|
||||
|
||||
#include <SupportDefs.h>
|
||||
#include <util/kernel_cpp.h>
|
||||
@ -138,6 +139,33 @@ console_set_color(int32 foreground, int32 background)
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
console_wait_for_key(void)
|
||||
{
|
||||
union key key = wait_for_key();
|
||||
|
||||
if (key.code.ascii == 0) {
|
||||
switch (key.code.bios) {
|
||||
case BIOS_KEY_UP:
|
||||
return TEXT_CONSOLE_KEY_UP;
|
||||
case BIOS_KEY_DOWN:
|
||||
return TEXT_CONSOLE_KEY_DOWN;
|
||||
case BIOS_KEY_PAGE_UP:
|
||||
return TEXT_CONSOLE_KEY_PAGE_UP;
|
||||
case BIOS_KEY_PAGE_DOWN:
|
||||
return TEXT_CONSOLE_KEY_PAGE_DOWN;
|
||||
case BIOS_KEY_HOME:
|
||||
return TEXT_CONSOLE_KEY_HOME;
|
||||
case BIOS_KEY_END:
|
||||
return TEXT_CONSOLE_KEY_END;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
return key.code.ascii;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
console_init(void)
|
||||
{
|
||||
@ -152,4 +180,3 @@ console_init(void)
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,48 +5,14 @@
|
||||
#ifndef CONSOLE_H
|
||||
#define CONSOLE_H
|
||||
|
||||
|
||||
#include <boot/vfs.h>
|
||||
#include <boot/stdio.h>
|
||||
|
||||
|
||||
enum console_color {
|
||||
// foreground and background colors
|
||||
BLACK,
|
||||
BLUE,
|
||||
GREEN,
|
||||
CYAN,
|
||||
RED,
|
||||
PURPLE,
|
||||
BROWN,
|
||||
GRAY,
|
||||
// foreground colors only if blinking is enabled
|
||||
DARK_GRAY,
|
||||
BRIGHT_BLUE,
|
||||
BRIGHT_GREEN,
|
||||
BRIGHT_CYAN,
|
||||
BRIGHT_RED,
|
||||
MAGENTA,
|
||||
YELLOW,
|
||||
WHITE
|
||||
};
|
||||
|
||||
#include <boot/platform/generic/text_console.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void console_clear_screen(void);
|
||||
extern int32 console_width(void);
|
||||
extern int32 console_height(void);
|
||||
extern void console_set_cursor(int32 x, int32 y);
|
||||
extern void console_set_color(int32 foreground, int32 background);
|
||||
|
||||
extern status_t console_init(void);
|
||||
|
||||
extern void serial_disable(void);
|
||||
extern void serial_enable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -4,455 +4,13 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "bios.h"
|
||||
#include "console.h"
|
||||
#include "keyboard.h"
|
||||
#include "smp.h"
|
||||
#include "video.h"
|
||||
|
||||
#include <boot/platform.h>
|
||||
#include <boot/menu.h>
|
||||
#include <boot/platform/generic/text_menu.h>
|
||||
#include <safemode.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// position
|
||||
static const int32 kFirstLine = 8;
|
||||
static const int32 kOffsetX = 10;
|
||||
static const int32 kHelpLines = 3;
|
||||
|
||||
// colors
|
||||
static const console_color kBackgroundColor = BLACK;
|
||||
static const console_color kTextColor = WHITE;
|
||||
static const console_color kCopyrightColor = CYAN;
|
||||
static const console_color kTitleColor = WHITE;
|
||||
static const console_color kTitleBackgroundColor = RED;
|
||||
static const console_color kHelpTextColor = WHITE;
|
||||
|
||||
static const console_color kItemColor = GRAY;
|
||||
static const console_color kSelectedItemColor = WHITE;
|
||||
static const console_color kItemBackgroundColor = kBackgroundColor;
|
||||
static const console_color kSelectedItemBackgroundColor = GRAY;
|
||||
static const console_color kDisabledColor = DARK_GRAY;
|
||||
|
||||
static const console_color kSliderColor = CYAN;
|
||||
static const console_color kSliderBackgroundColor = DARK_GRAY;
|
||||
static const console_color kArrowColor = GRAY;
|
||||
|
||||
static int32 sMenuOffset = 0;
|
||||
|
||||
|
||||
static int32
|
||||
menu_height()
|
||||
{
|
||||
return console_height() - kFirstLine - 1 - kHelpLines;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_spacing(int32 count)
|
||||
{
|
||||
for (int32 i = 0; i < count; i++)
|
||||
putchar(' ');
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_centered(int32 line, const char *text)
|
||||
{
|
||||
console_set_cursor(console_width() / 2 - strlen(text) / 2, line);
|
||||
printf("%s", text);
|
||||
|
||||
console_set_cursor(0, 0);
|
||||
// this avoids unwanted line feeds
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_item_at(int32 line, MenuItem *item, bool clearHelp = true)
|
||||
{
|
||||
bool selected = item->IsSelected();
|
||||
|
||||
line -= sMenuOffset;
|
||||
if (line < 0 || line >= menu_height())
|
||||
return;
|
||||
|
||||
console_color background = selected ? kSelectedItemBackgroundColor : kItemBackgroundColor;
|
||||
console_color foreground = selected ? kSelectedItemColor : kItemColor;
|
||||
|
||||
if (!item->IsEnabled())
|
||||
foreground = kDisabledColor;
|
||||
|
||||
console_set_cursor(kOffsetX, line + kFirstLine);
|
||||
console_set_color(foreground, background);
|
||||
|
||||
size_t length = strlen(item->Label()) + 1;
|
||||
|
||||
if (item->Type() == MENU_ITEM_MARKABLE) {
|
||||
console_set_color(DARK_GRAY, background);
|
||||
printf(" [");
|
||||
console_set_color(foreground, background);
|
||||
printf("%c", item->IsMarked() ? 'x' : ' ');
|
||||
console_set_color(DARK_GRAY, background);
|
||||
printf("] ");
|
||||
console_set_color(foreground, background);
|
||||
|
||||
length += 4;
|
||||
} else
|
||||
printf(" ");
|
||||
|
||||
printf(item->Label());
|
||||
|
||||
if (item->Submenu() && item->Submenu()->Type() == CHOICE_MENU) {
|
||||
// show the current choice (if any)
|
||||
const char *text = " (Current: ";
|
||||
printf(text);
|
||||
length += strlen(text);
|
||||
|
||||
Menu *subMenu = item->Submenu();
|
||||
if (subMenu->ChoiceText() != NULL)
|
||||
text = subMenu->ChoiceText();
|
||||
else
|
||||
text = "None";
|
||||
length += strlen(text);
|
||||
|
||||
console_set_color(selected ? DARK_GRAY : WHITE, background);
|
||||
|
||||
printf(text);
|
||||
|
||||
console_set_color(foreground, background);
|
||||
putchar(')');
|
||||
length++;
|
||||
}
|
||||
|
||||
print_spacing(console_width() - length - 2*kOffsetX);
|
||||
|
||||
if (!selected)
|
||||
return;
|
||||
|
||||
console_set_cursor(0, console_height() - kHelpLines);
|
||||
console_set_color(kHelpTextColor, kBackgroundColor);
|
||||
|
||||
if (clearHelp) {
|
||||
// clear help text area
|
||||
for (int32 i = 0; i < console_width() * 2 - 1; i++)
|
||||
putchar(' ');
|
||||
|
||||
console_set_cursor(0, console_height() - kHelpLines);
|
||||
}
|
||||
|
||||
if (item->HelpText() != NULL) {
|
||||
// show help text at the bottom of the screen,
|
||||
// center it, and wrap it correctly
|
||||
|
||||
const char *text = item->HelpText();
|
||||
int32 width = console_width() - 2 * kOffsetX;
|
||||
int32 length = strlen(text);
|
||||
|
||||
if (length > width * 2)
|
||||
width += 2 * kOffsetX - 1;
|
||||
|
||||
char buffer[width + 1];
|
||||
buffer[width] = '\0';
|
||||
// make sure the buffer is always terminated
|
||||
|
||||
int32 row = 0;
|
||||
|
||||
for (int32 i = 0; i < length && row < 2; i++) {
|
||||
while (text[i] == ' ')
|
||||
i++;
|
||||
|
||||
// copy as much bytes as possible
|
||||
int32 bytes = width;
|
||||
if (bytes > length - i)
|
||||
bytes = length - i;
|
||||
|
||||
memcpy(buffer, text + i, bytes);
|
||||
buffer[bytes] = '\0';
|
||||
|
||||
char *pos = strchr(buffer, '\n');
|
||||
if (pos != NULL)
|
||||
i = pos - buffer;
|
||||
else if (bytes < length - i) {
|
||||
// search for possible line breaks
|
||||
pos = strrchr(buffer, ' ');
|
||||
if (pos != NULL)
|
||||
i = pos - buffer;
|
||||
else {
|
||||
// no wrapping possible
|
||||
i += bytes;
|
||||
}
|
||||
} else
|
||||
i += bytes;
|
||||
|
||||
buffer[i] = '\0';
|
||||
print_centered(console_height() - kHelpLines + row, buffer);
|
||||
row++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
draw_menu(Menu *menu)
|
||||
{
|
||||
console_set_color(kTextColor, kBackgroundColor);
|
||||
console_clear_screen();
|
||||
|
||||
print_centered(1, "Welcome To The");
|
||||
print_centered(2, "Haiku Boot Loader");
|
||||
|
||||
console_set_color(kCopyrightColor, kBackgroundColor);
|
||||
print_centered(4, "Copyright 2004-2005 Haiku Inc.");
|
||||
|
||||
if (menu->Title()) {
|
||||
console_set_cursor(kOffsetX, kFirstLine - 2);
|
||||
console_set_color(kTitleColor, kTitleBackgroundColor);
|
||||
|
||||
printf(" %s", menu->Title());
|
||||
print_spacing(console_width() - 1 - strlen(menu->Title()) - 2*kOffsetX);
|
||||
}
|
||||
|
||||
MenuItemIterator iterator = menu->ItemIterator();
|
||||
MenuItem *item;
|
||||
int32 i = 0;
|
||||
|
||||
while ((item = iterator.Next()) != NULL) {
|
||||
if (item->Type() == MENU_ITEM_SEPARATOR) {
|
||||
putchar('\n');
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
print_item_at(i++, item, false);
|
||||
}
|
||||
|
||||
int32 height = menu_height();
|
||||
if (menu->CountItems() >= height) {
|
||||
int32 x = console_width() - kOffsetX;
|
||||
console_set_cursor(x, kFirstLine);
|
||||
console_set_color(kArrowColor, kBackgroundColor);
|
||||
putchar(30/*24*/);
|
||||
height--;
|
||||
|
||||
int32 start = sMenuOffset * height / menu->CountItems();
|
||||
int32 end = (sMenuOffset + height) * height / menu->CountItems();
|
||||
|
||||
for (i = 1; i < height; i++) {
|
||||
console_set_cursor(x, kFirstLine + i);
|
||||
if (i >= start && i <= end)
|
||||
console_set_color(WHITE, kSliderColor);
|
||||
else
|
||||
console_set_color(WHITE, kSliderBackgroundColor);
|
||||
|
||||
putchar(' ');
|
||||
}
|
||||
|
||||
console_set_cursor(x, kFirstLine + i);
|
||||
console_set_color(kArrowColor, kBackgroundColor);
|
||||
putchar(31/*25*/);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
first_selectable_item(Menu *menu)
|
||||
{
|
||||
int32 index = -1;
|
||||
MenuItem *item;
|
||||
|
||||
while ((item = menu->ItemAt(++index)) != NULL) {
|
||||
if (item->IsEnabled() && item->Type() != MENU_ITEM_SEPARATOR)
|
||||
break;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
last_selectable_item(Menu *menu)
|
||||
{
|
||||
int32 index = menu->CountItems();
|
||||
MenuItem *item;
|
||||
|
||||
while ((item = menu->ItemAt(--index)) != NULL) {
|
||||
if (item->IsEnabled() && item->Type() != MENU_ITEM_SEPARATOR)
|
||||
break;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
make_item_visible(Menu *menu, int32 selected)
|
||||
{
|
||||
if (sMenuOffset > selected
|
||||
|| sMenuOffset + menu_height() <= selected) {
|
||||
if (sMenuOffset > selected)
|
||||
sMenuOffset = selected;
|
||||
else
|
||||
sMenuOffset = selected + 1 - menu_height();
|
||||
|
||||
draw_menu(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
select_previous_valid_item(Menu *menu, int32 selected)
|
||||
{
|
||||
MenuItem *item;
|
||||
while ((item = menu->ItemAt(selected)) != NULL) {
|
||||
if (item->IsEnabled() && item->Type() != MENU_ITEM_SEPARATOR)
|
||||
break;
|
||||
|
||||
selected--;
|
||||
}
|
||||
|
||||
if (selected < 0)
|
||||
return first_selectable_item(menu);
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
select_next_valid_item(Menu *menu, int32 selected)
|
||||
{
|
||||
MenuItem *item;
|
||||
while ((item = menu->ItemAt(selected)) != NULL) {
|
||||
if (item->IsEnabled() && item->Type() != MENU_ITEM_SEPARATOR)
|
||||
break;
|
||||
|
||||
selected++;
|
||||
}
|
||||
|
||||
if (selected >= menu->CountItems())
|
||||
return last_selectable_item(menu);
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run_menu(Menu *menu)
|
||||
{
|
||||
sMenuOffset = 0;
|
||||
menu->Show();
|
||||
|
||||
draw_menu(menu);
|
||||
|
||||
// Get selected entry, or select the last one, if there is none
|
||||
int32 selected;
|
||||
MenuItem *item = menu->FindSelected(&selected);
|
||||
if (item == NULL) {
|
||||
selected = menu->CountItems() - 1;
|
||||
item = menu->ItemAt(selected);
|
||||
if (item != NULL)
|
||||
item->Select(true);
|
||||
}
|
||||
|
||||
make_item_visible(menu, selected);
|
||||
|
||||
while (true) {
|
||||
union key key = wait_for_key();
|
||||
|
||||
item = menu->ItemAt(selected);
|
||||
|
||||
if (key.code.ascii == 0) {
|
||||
int32 oldSelected = selected;
|
||||
|
||||
switch (key.code.bios) {
|
||||
case BIOS_KEY_UP:
|
||||
selected = select_previous_valid_item(menu, selected - 1);
|
||||
break;
|
||||
case BIOS_KEY_DOWN:
|
||||
selected = select_next_valid_item(menu, selected + 1);
|
||||
break;
|
||||
case BIOS_KEY_PAGE_UP:
|
||||
selected = select_previous_valid_item(menu, selected - menu_height() + 1);
|
||||
break;
|
||||
case BIOS_KEY_PAGE_DOWN:
|
||||
selected = select_next_valid_item(menu, selected + menu_height() - 1);
|
||||
break;
|
||||
case BIOS_KEY_HOME:
|
||||
selected = first_selectable_item(menu);
|
||||
break;
|
||||
case BIOS_KEY_END:
|
||||
selected = last_selectable_item(menu);
|
||||
break;
|
||||
}
|
||||
|
||||
// check if selected has changed
|
||||
if (selected != oldSelected) {
|
||||
MenuItem *item = menu->ItemAt(selected);
|
||||
if (item != NULL)
|
||||
item->Select(true);
|
||||
|
||||
make_item_visible(menu, selected);
|
||||
// make sure that the new selected entry is visible
|
||||
if (sMenuOffset > selected
|
||||
|| sMenuOffset + menu_height() <= selected) {
|
||||
if (sMenuOffset > selected)
|
||||
sMenuOffset = selected;
|
||||
else
|
||||
sMenuOffset = selected + 1 - menu_height();
|
||||
|
||||
draw_menu(menu);
|
||||
}
|
||||
}
|
||||
} else if (key.code.ascii == 0xd || key.code.ascii == ' ') {
|
||||
// leave the menu
|
||||
if (item->Submenu() != NULL) {
|
||||
int32 offset = sMenuOffset;
|
||||
menu->Hide();
|
||||
|
||||
run_menu(item->Submenu());
|
||||
if (item->Target() != NULL)
|
||||
(*item->Target())(menu, item);
|
||||
|
||||
// restore current menu
|
||||
sMenuOffset = offset;
|
||||
menu->FindSelected(&selected);
|
||||
menu->Show();
|
||||
draw_menu(menu);
|
||||
} else if (item->Type() == MENU_ITEM_MARKABLE) {
|
||||
// toggle state
|
||||
item->SetMarked(!item->IsMarked());
|
||||
print_item_at(selected, item);
|
||||
|
||||
if (item->Target() != NULL)
|
||||
(*item->Target())(menu, item);
|
||||
} else if (key.code.ascii == 0xd) {
|
||||
// the space key does not exit the menu
|
||||
|
||||
if (menu->Type() == CHOICE_MENU
|
||||
&& item->Type() != MENU_ITEM_NO_CHOICE
|
||||
&& item->Type() != MENU_ITEM_TITLE)
|
||||
item->SetMarked(true);
|
||||
|
||||
if (item->Target() != NULL)
|
||||
(*item->Target())(menu, item);
|
||||
|
||||
break;
|
||||
}
|
||||
} else if (key.code.ascii == 0x1b && menu->Type() != MAIN_MENU)
|
||||
// escape key was hit
|
||||
break;
|
||||
}
|
||||
|
||||
menu->Hide();
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
void
|
||||
platform_add_menus(Menu *menu)
|
||||
@ -479,24 +37,13 @@ platform_add_menus(Menu *menu)
|
||||
void
|
||||
platform_update_menu_item(Menu *menu, MenuItem *item)
|
||||
{
|
||||
if (menu->IsHidden())
|
||||
return;
|
||||
|
||||
int32 index = menu->IndexOf(item);
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
print_item_at(index, item);
|
||||
platform_generic_update_text_menu_item(menu, item);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
platform_run_menu(Menu *menu)
|
||||
{
|
||||
// platform_switch_to_text_mode();
|
||||
|
||||
run_menu(menu);
|
||||
|
||||
// platform_switch_to_logo();
|
||||
platform_generic_run_text_menu(menu);
|
||||
}
|
||||
|
||||
|
478
src/system/boot/platform/generic/text_menu.cpp
Normal file
478
src/system/boot/platform/generic/text_menu.cpp
Normal file
@ -0,0 +1,478 @@
|
||||
/*
|
||||
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <boot/platform.h>
|
||||
#include <boot/menu.h>
|
||||
#include <boot/platform/generic/text_console.h>
|
||||
#include <boot/platform/generic/text_menu.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// position
|
||||
static const int32 kFirstLine = 8;
|
||||
static const int32 kOffsetX = 10;
|
||||
static const int32 kHelpLines = 3;
|
||||
|
||||
// colors
|
||||
static const console_color kBackgroundColor = BLACK;
|
||||
static const console_color kTextColor = WHITE;
|
||||
static const console_color kCopyrightColor = CYAN;
|
||||
static const console_color kTitleColor = WHITE;
|
||||
static const console_color kTitleBackgroundColor = RED;
|
||||
static const console_color kHelpTextColor = WHITE;
|
||||
|
||||
static const console_color kItemColor = GRAY;
|
||||
static const console_color kSelectedItemColor = WHITE;
|
||||
static const console_color kItemBackgroundColor = kBackgroundColor;
|
||||
static const console_color kSelectedItemBackgroundColor = GRAY;
|
||||
static const console_color kDisabledColor = DARK_GRAY;
|
||||
|
||||
static const console_color kSliderColor = CYAN;
|
||||
static const console_color kSliderBackgroundColor = DARK_GRAY;
|
||||
static const console_color kArrowColor = GRAY;
|
||||
|
||||
static int32 sMenuOffset = 0;
|
||||
|
||||
|
||||
static int32
|
||||
menu_height()
|
||||
{
|
||||
return console_height() - kFirstLine - 1 - kHelpLines;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_spacing(int32 count)
|
||||
{
|
||||
for (int32 i = 0; i < count; i++)
|
||||
putchar(' ');
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_centered(int32 line, const char *text)
|
||||
{
|
||||
console_set_cursor(console_width() / 2 - strlen(text) / 2, line);
|
||||
printf("%s", text);
|
||||
|
||||
console_set_cursor(0, 0);
|
||||
// this avoids unwanted line feeds
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_item_at(int32 line, MenuItem *item, bool clearHelp = true)
|
||||
{
|
||||
bool selected = item->IsSelected();
|
||||
|
||||
line -= sMenuOffset;
|
||||
if (line < 0 || line >= menu_height())
|
||||
return;
|
||||
|
||||
console_color background = selected ? kSelectedItemBackgroundColor : kItemBackgroundColor;
|
||||
console_color foreground = selected ? kSelectedItemColor : kItemColor;
|
||||
|
||||
if (!item->IsEnabled())
|
||||
foreground = kDisabledColor;
|
||||
|
||||
console_set_cursor(kOffsetX, line + kFirstLine);
|
||||
console_set_color(foreground, background);
|
||||
|
||||
size_t length = strlen(item->Label()) + 1;
|
||||
|
||||
if (item->Type() == MENU_ITEM_MARKABLE) {
|
||||
console_set_color(DARK_GRAY, background);
|
||||
printf(" [");
|
||||
console_set_color(foreground, background);
|
||||
printf("%c", item->IsMarked() ? 'x' : ' ');
|
||||
console_set_color(DARK_GRAY, background);
|
||||
printf("] ");
|
||||
console_set_color(foreground, background);
|
||||
|
||||
length += 4;
|
||||
} else
|
||||
printf(" ");
|
||||
|
||||
printf(item->Label());
|
||||
|
||||
if (item->Submenu() && item->Submenu()->Type() == CHOICE_MENU) {
|
||||
// show the current choice (if any)
|
||||
const char *text = " (Current: ";
|
||||
printf(text);
|
||||
length += strlen(text);
|
||||
|
||||
Menu *subMenu = item->Submenu();
|
||||
if (subMenu->ChoiceText() != NULL)
|
||||
text = subMenu->ChoiceText();
|
||||
else
|
||||
text = "None";
|
||||
length += strlen(text);
|
||||
|
||||
console_set_color(selected ? DARK_GRAY : WHITE, background);
|
||||
|
||||
printf(text);
|
||||
|
||||
console_set_color(foreground, background);
|
||||
putchar(')');
|
||||
length++;
|
||||
}
|
||||
|
||||
print_spacing(console_width() - length - 2*kOffsetX);
|
||||
|
||||
if (!selected)
|
||||
return;
|
||||
|
||||
console_set_cursor(0, console_height() - kHelpLines);
|
||||
console_set_color(kHelpTextColor, kBackgroundColor);
|
||||
|
||||
if (clearHelp) {
|
||||
// clear help text area
|
||||
for (int32 i = 0; i < console_width() * 2 - 1; i++)
|
||||
putchar(' ');
|
||||
|
||||
console_set_cursor(0, console_height() - kHelpLines);
|
||||
}
|
||||
|
||||
if (item->HelpText() != NULL) {
|
||||
// show help text at the bottom of the screen,
|
||||
// center it, and wrap it correctly
|
||||
|
||||
const char *text = item->HelpText();
|
||||
int32 width = console_width() - 2 * kOffsetX;
|
||||
int32 length = strlen(text);
|
||||
|
||||
if (length > width * 2)
|
||||
width += 2 * kOffsetX - 1;
|
||||
|
||||
char buffer[width + 1];
|
||||
buffer[width] = '\0';
|
||||
// make sure the buffer is always terminated
|
||||
|
||||
int32 row = 0;
|
||||
|
||||
for (int32 i = 0; i < length && row < 2; i++) {
|
||||
while (text[i] == ' ')
|
||||
i++;
|
||||
|
||||
// copy as much bytes as possible
|
||||
int32 bytes = width;
|
||||
if (bytes > length - i)
|
||||
bytes = length - i;
|
||||
|
||||
memcpy(buffer, text + i, bytes);
|
||||
buffer[bytes] = '\0';
|
||||
|
||||
char *pos = strchr(buffer, '\n');
|
||||
if (pos != NULL)
|
||||
i = pos - buffer;
|
||||
else if (bytes < length - i) {
|
||||
// search for possible line breaks
|
||||
pos = strrchr(buffer, ' ');
|
||||
if (pos != NULL)
|
||||
i = pos - buffer;
|
||||
else {
|
||||
// no wrapping possible
|
||||
i += bytes;
|
||||
}
|
||||
} else
|
||||
i += bytes;
|
||||
|
||||
buffer[i] = '\0';
|
||||
print_centered(console_height() - kHelpLines + row, buffer);
|
||||
row++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
draw_menu(Menu *menu)
|
||||
{
|
||||
console_set_color(kTextColor, kBackgroundColor);
|
||||
console_clear_screen();
|
||||
|
||||
print_centered(1, "Welcome To The");
|
||||
print_centered(2, "Haiku Boot Loader");
|
||||
|
||||
console_set_color(kCopyrightColor, kBackgroundColor);
|
||||
print_centered(4, "Copyright 2004-2005 Haiku Inc.");
|
||||
|
||||
if (menu->Title()) {
|
||||
console_set_cursor(kOffsetX, kFirstLine - 2);
|
||||
console_set_color(kTitleColor, kTitleBackgroundColor);
|
||||
|
||||
printf(" %s", menu->Title());
|
||||
print_spacing(console_width() - 1 - strlen(menu->Title()) - 2*kOffsetX);
|
||||
}
|
||||
|
||||
MenuItemIterator iterator = menu->ItemIterator();
|
||||
MenuItem *item;
|
||||
int32 i = 0;
|
||||
|
||||
while ((item = iterator.Next()) != NULL) {
|
||||
if (item->Type() == MENU_ITEM_SEPARATOR) {
|
||||
putchar('\n');
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
print_item_at(i++, item, false);
|
||||
}
|
||||
|
||||
int32 height = menu_height();
|
||||
if (menu->CountItems() >= height) {
|
||||
int32 x = console_width() - kOffsetX;
|
||||
console_set_cursor(x, kFirstLine);
|
||||
console_set_color(kArrowColor, kBackgroundColor);
|
||||
putchar(30/*24*/);
|
||||
height--;
|
||||
|
||||
int32 start = sMenuOffset * height / menu->CountItems();
|
||||
int32 end = (sMenuOffset + height) * height / menu->CountItems();
|
||||
|
||||
for (i = 1; i < height; i++) {
|
||||
console_set_cursor(x, kFirstLine + i);
|
||||
if (i >= start && i <= end)
|
||||
console_set_color(WHITE, kSliderColor);
|
||||
else
|
||||
console_set_color(WHITE, kSliderBackgroundColor);
|
||||
|
||||
putchar(' ');
|
||||
}
|
||||
|
||||
console_set_cursor(x, kFirstLine + i);
|
||||
console_set_color(kArrowColor, kBackgroundColor);
|
||||
putchar(31/*25*/);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
first_selectable_item(Menu *menu)
|
||||
{
|
||||
int32 index = -1;
|
||||
MenuItem *item;
|
||||
|
||||
while ((item = menu->ItemAt(++index)) != NULL) {
|
||||
if (item->IsEnabled() && item->Type() != MENU_ITEM_SEPARATOR)
|
||||
break;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
last_selectable_item(Menu *menu)
|
||||
{
|
||||
int32 index = menu->CountItems();
|
||||
MenuItem *item;
|
||||
|
||||
while ((item = menu->ItemAt(--index)) != NULL) {
|
||||
if (item->IsEnabled() && item->Type() != MENU_ITEM_SEPARATOR)
|
||||
break;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
make_item_visible(Menu *menu, int32 selected)
|
||||
{
|
||||
if (sMenuOffset > selected
|
||||
|| sMenuOffset + menu_height() <= selected) {
|
||||
if (sMenuOffset > selected)
|
||||
sMenuOffset = selected;
|
||||
else
|
||||
sMenuOffset = selected + 1 - menu_height();
|
||||
|
||||
draw_menu(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
select_previous_valid_item(Menu *menu, int32 selected)
|
||||
{
|
||||
MenuItem *item;
|
||||
while ((item = menu->ItemAt(selected)) != NULL) {
|
||||
if (item->IsEnabled() && item->Type() != MENU_ITEM_SEPARATOR)
|
||||
break;
|
||||
|
||||
selected--;
|
||||
}
|
||||
|
||||
if (selected < 0)
|
||||
return first_selectable_item(menu);
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
select_next_valid_item(Menu *menu, int32 selected)
|
||||
{
|
||||
MenuItem *item;
|
||||
while ((item = menu->ItemAt(selected)) != NULL) {
|
||||
if (item->IsEnabled() && item->Type() != MENU_ITEM_SEPARATOR)
|
||||
break;
|
||||
|
||||
selected++;
|
||||
}
|
||||
|
||||
if (selected >= menu->CountItems())
|
||||
return last_selectable_item(menu);
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run_menu(Menu *menu)
|
||||
{
|
||||
sMenuOffset = 0;
|
||||
menu->Show();
|
||||
|
||||
draw_menu(menu);
|
||||
|
||||
// Get selected entry, or select the last one, if there is none
|
||||
int32 selected;
|
||||
MenuItem *item = menu->FindSelected(&selected);
|
||||
if (item == NULL) {
|
||||
selected = menu->CountItems() - 1;
|
||||
item = menu->ItemAt(selected);
|
||||
if (item != NULL)
|
||||
item->Select(true);
|
||||
}
|
||||
|
||||
make_item_visible(menu, selected);
|
||||
|
||||
while (true) {
|
||||
int key = console_wait_for_key();
|
||||
|
||||
item = menu->ItemAt(selected);
|
||||
|
||||
if (TEXT_CONSOLE_IS_CURSOR_KEY(key)) {
|
||||
int32 oldSelected = selected;
|
||||
|
||||
switch (key) {
|
||||
case TEXT_CONSOLE_KEY_UP:
|
||||
selected = select_previous_valid_item(menu, selected - 1);
|
||||
break;
|
||||
case TEXT_CONSOLE_KEY_DOWN:
|
||||
selected = select_next_valid_item(menu, selected + 1);
|
||||
break;
|
||||
case TEXT_CONSOLE_KEY_PAGE_UP:
|
||||
case TEXT_CONSOLE_KEY_LEFT:
|
||||
selected = select_previous_valid_item(menu, selected - menu_height() + 1);
|
||||
break;
|
||||
case TEXT_CONSOLE_KEY_PAGE_DOWN:
|
||||
case TEXT_CONSOLE_KEY_RIGHT:
|
||||
selected = select_next_valid_item(menu, selected + menu_height() - 1);
|
||||
break;
|
||||
case TEXT_CONSOLE_KEY_HOME:
|
||||
selected = first_selectable_item(menu);
|
||||
break;
|
||||
case TEXT_CONSOLE_KEY_END:
|
||||
selected = last_selectable_item(menu);
|
||||
break;
|
||||
}
|
||||
|
||||
// check if selected has changed
|
||||
if (selected != oldSelected) {
|
||||
MenuItem *item = menu->ItemAt(selected);
|
||||
if (item != NULL)
|
||||
item->Select(true);
|
||||
|
||||
make_item_visible(menu, selected);
|
||||
// make sure that the new selected entry is visible
|
||||
if (sMenuOffset > selected
|
||||
|| sMenuOffset + menu_height() <= selected) {
|
||||
if (sMenuOffset > selected)
|
||||
sMenuOffset = selected;
|
||||
else
|
||||
sMenuOffset = selected + 1 - menu_height();
|
||||
|
||||
draw_menu(menu);
|
||||
}
|
||||
}
|
||||
} else if (key == TEXT_CONSOLE_KEY_RETURN
|
||||
|| key == TEXT_CONSOLE_KEY_SPACE) {
|
||||
// leave the menu
|
||||
if (item->Submenu() != NULL) {
|
||||
int32 offset = sMenuOffset;
|
||||
menu->Hide();
|
||||
|
||||
run_menu(item->Submenu());
|
||||
if (item->Target() != NULL)
|
||||
(*item->Target())(menu, item);
|
||||
|
||||
// restore current menu
|
||||
sMenuOffset = offset;
|
||||
menu->FindSelected(&selected);
|
||||
menu->Show();
|
||||
draw_menu(menu);
|
||||
} else if (item->Type() == MENU_ITEM_MARKABLE) {
|
||||
// toggle state
|
||||
item->SetMarked(!item->IsMarked());
|
||||
print_item_at(selected, item);
|
||||
|
||||
if (item->Target() != NULL)
|
||||
(*item->Target())(menu, item);
|
||||
} else if (key == TEXT_CONSOLE_KEY_RETURN) {
|
||||
// the space key does not exit the menu
|
||||
|
||||
if (menu->Type() == CHOICE_MENU
|
||||
&& item->Type() != MENU_ITEM_NO_CHOICE
|
||||
&& item->Type() != MENU_ITEM_TITLE)
|
||||
item->SetMarked(true);
|
||||
|
||||
if (item->Target() != NULL)
|
||||
(*item->Target())(menu, item);
|
||||
|
||||
break;
|
||||
}
|
||||
} else if (key == TEXT_CONSOLE_KEY_ESCAPE && menu->Type() != MAIN_MENU)
|
||||
// escape key was hit
|
||||
break;
|
||||
}
|
||||
|
||||
menu->Hide();
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
void
|
||||
platform_generic_update_text_menu_item(Menu *menu, MenuItem *item)
|
||||
{
|
||||
if (menu->IsHidden())
|
||||
return;
|
||||
|
||||
int32 index = menu->IndexOf(item);
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
print_item_at(index, item);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
platform_generic_run_text_menu(Menu *menu)
|
||||
{
|
||||
// platform_switch_to_text_mode();
|
||||
|
||||
run_menu(menu);
|
||||
|
||||
// platform_switch_to_logo();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user