2008-12-31 17:20:47 +01:00
|
|
|
/*********************************************************************
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004,2008, Simon Kagstrom
|
|
|
|
*
|
|
|
|
* Filename: menu.c
|
|
|
|
* Author: Simon Kagstrom <simon.kagstrom@gmail.com>
|
|
|
|
* Description: Code for menus (originally for Mophun)
|
|
|
|
*
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
********************************************************************/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#if defined(GEKKO)
|
|
|
|
# include <wiiuse/wpad.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "menu.h"
|
|
|
|
|
|
|
|
#define KEY_UP 1
|
|
|
|
#define KEY_DOWN 2
|
|
|
|
#define KEY_LEFT 4
|
|
|
|
#define KEY_RIGHT 8
|
|
|
|
#define KEY_SELECT 16
|
|
|
|
#define KEY_ESCAPE 32
|
|
|
|
#define KEY_PAGEDOWN 64
|
|
|
|
#define KEY_PAGEUP 128
|
|
|
|
|
|
|
|
#define IS_SUBMENU(p_msg) ( (p_msg)[0] == '^' )
|
|
|
|
|
|
|
|
static submenu_t *find_submenu(menu_t *p_menu, int index)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i=0; i<p_menu->n_submenus; i++)
|
|
|
|
{
|
|
|
|
if (p_menu->p_submenus[i].index == index)
|
|
|
|
return &p_menu->p_submenus[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-01-02 16:07:43 +01:00
|
|
|
static void print_font(SDL_Surface *screen, TTF_Font *font, int r, int g, int b,
|
2009-01-04 13:19:26 +01:00
|
|
|
int x, int y, const char *msg)
|
2008-12-31 17:20:47 +01:00
|
|
|
{
|
2009-01-02 16:07:43 +01:00
|
|
|
SDL_Surface *font_surf;
|
|
|
|
SDL_Rect dst = {x, y, 0, 0};
|
|
|
|
SDL_Color color = {r, g, b};
|
2008-12-31 17:20:47 +01:00
|
|
|
char buf[255];
|
2009-01-02 17:23:21 +01:00
|
|
|
unsigned int i;
|
2008-12-31 17:20:47 +01:00
|
|
|
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
strncpy(buf, msg, 254);
|
|
|
|
|
|
|
|
/* Fixup multi-menu option look */
|
|
|
|
for (i = 0; i < strlen(buf) ; i++)
|
|
|
|
{
|
|
|
|
if (buf[i] == '^' || buf[i] == '|')
|
|
|
|
buf[i] = ' ';
|
|
|
|
}
|
|
|
|
|
2009-01-02 16:07:43 +01:00
|
|
|
font_surf = TTF_RenderText_Solid(font, buf,
|
|
|
|
color);
|
|
|
|
if (!font_surf)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s\n", TTF_GetError());
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_BlitSurface(font_surf, NULL, screen, &dst);
|
|
|
|
|
|
|
|
SDL_FreeSurface(font_surf);
|
2008-12-31 17:20:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void menu_draw(SDL_Surface *screen, menu_t *p_menu)
|
|
|
|
{
|
|
|
|
int x_start = p_menu->x1 + (p_menu->x2 - p_menu->x1) / 2 - p_menu->text_w / 2;
|
|
|
|
int y_start = p_menu->y1 + (p_menu->y2 - p_menu->y1) / 2 - p_menu->text_h / 2;
|
2009-01-02 16:07:43 +01:00
|
|
|
int font_height = TTF_FontHeight(p_menu->p_font);
|
2008-12-31 17:20:47 +01:00
|
|
|
int line_height = (font_height + font_height / 4);
|
|
|
|
int entries_visible = p_menu->y2 / line_height;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ( p_menu->n_entries * line_height > p_menu->y2 )
|
|
|
|
y_start = p_menu->y1;
|
|
|
|
if ( p_menu->cur_sel - p_menu->start_entry_visible > entries_visible )
|
|
|
|
p_menu->start_entry_visible += p_menu->cur_sel - entries_visible;
|
|
|
|
else if ( p_menu->cur_sel < p_menu->start_entry_visible )
|
|
|
|
p_menu->start_entry_visible = p_menu->cur_sel;
|
|
|
|
|
|
|
|
for (i = p_menu->start_entry_visible; i < p_menu->n_entries; i++)
|
|
|
|
{
|
2009-01-04 13:19:26 +01:00
|
|
|
const char *msg = p_menu->pp_msgs[i];
|
2008-12-31 17:20:47 +01:00
|
|
|
int y = (i - p_menu->start_entry_visible) * line_height;
|
|
|
|
|
|
|
|
if ((p_menu->available_options & (1<<i)) == 0) /* Gray (not available) */
|
2009-01-02 16:07:43 +01:00
|
|
|
print_font(screen, p_menu->p_font, 128,128,128, x_start,
|
2008-12-31 17:20:47 +01:00
|
|
|
y_start + y, msg);
|
|
|
|
else if (p_menu->cur_sel == i) /* Selected - color */
|
2009-01-02 16:07:43 +01:00
|
|
|
print_font(screen, p_menu->p_font, 255,255,0, x_start,
|
2008-12-31 17:20:47 +01:00
|
|
|
y_start + y, msg);
|
|
|
|
else /* Otherwise white */
|
2009-01-02 16:07:43 +01:00
|
|
|
print_font(screen, p_menu->p_font, 255,255,255, x_start,
|
2008-12-31 17:20:47 +01:00
|
|
|
y_start + y, msg);
|
|
|
|
if (IS_SUBMENU(msg))
|
|
|
|
{
|
|
|
|
submenu_t *p_submenu = find_submenu(p_menu, i);
|
|
|
|
int n_pipe = 0;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
for (n=0; msg[n] != '\0'; n++)
|
|
|
|
{
|
|
|
|
/* Underline the selected entry */
|
|
|
|
if (msg[n] == '|')
|
|
|
|
{
|
|
|
|
int16_t n_chars;
|
|
|
|
|
|
|
|
for (n_chars = 1; msg[n+n_chars] && msg[n+n_chars] != '|'; n_chars++);
|
|
|
|
|
|
|
|
n_pipe++;
|
|
|
|
if (p_submenu->sel == n_pipe-1)
|
|
|
|
{
|
|
|
|
SDL_Rect r;
|
2009-01-02 16:07:43 +01:00
|
|
|
int w;
|
|
|
|
int h;
|
|
|
|
|
|
|
|
if (TTF_SizeText(p_menu->p_font, "X", &w, &h) < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s\n", TTF_GetError());
|
|
|
|
exit(1);
|
|
|
|
}
|
2008-12-31 17:20:47 +01:00
|
|
|
|
|
|
|
r = (SDL_Rect) { x_start + (n+1) * w-1,
|
|
|
|
y_start + (i+1 - p_menu->start_entry_visible) * ((h + h/4)-1),
|
|
|
|
(n_chars - 1) * w,
|
|
|
|
2 };
|
|
|
|
SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 0, 255, 0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void select_next(menu_t *p_menu, int dx, int dy)
|
|
|
|
{
|
|
|
|
int next;
|
|
|
|
|
|
|
|
p_menu->cur_sel = (p_menu->cur_sel + dy) < 0 ? p_menu->n_entries - 1 :
|
|
|
|
(p_menu->cur_sel + dy) % p_menu->n_entries;
|
|
|
|
next = (p_menu->cur_sel + dy + 1) < 0 ? p_menu->n_entries - 1 :
|
|
|
|
(p_menu->cur_sel + dy + 1) % p_menu->n_entries;
|
|
|
|
|
|
|
|
if (p_menu->pp_msgs[p_menu->cur_sel][0] == ' ' ||
|
|
|
|
( (p_menu->available_options & (1<<p_menu->cur_sel)) == 0) ||
|
|
|
|
IS_SUBMENU(p_menu->pp_msgs[p_menu->cur_sel]) )
|
|
|
|
select_next(p_menu, dx, dy);
|
|
|
|
/* If the next is a submenu */
|
|
|
|
if (dx != 0 &&
|
|
|
|
IS_SUBMENU(p_menu->pp_msgs[next]))
|
|
|
|
{
|
|
|
|
submenu_t *p_submenu = find_submenu(p_menu, next);
|
|
|
|
|
|
|
|
p_submenu->sel = (p_submenu->sel + dx) < 0 ? p_submenu->n_entries - 1 :
|
|
|
|
(p_submenu->sel + dx) % p_submenu->n_entries;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int is_submenu_title(menu_t *p_menu, int n)
|
|
|
|
{
|
|
|
|
if (n+1 >= p_menu->n_entries)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return IS_SUBMENU(p_menu->pp_msgs[n+1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-04 13:19:26 +01:00
|
|
|
void menu_init(menu_t *p_menu, TTF_Font *p_font, const char **pp_msgs,
|
2008-12-31 17:20:47 +01:00
|
|
|
int16_t x1, int16_t y1, int16_t x2, int16_t y2)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
memset(p_menu, 0, sizeof(menu_t));
|
|
|
|
|
|
|
|
p_menu->pp_msgs = pp_msgs;
|
|
|
|
p_menu->p_font = p_font;
|
|
|
|
p_menu->x1 = x1;
|
|
|
|
p_menu->y1 = y1;
|
|
|
|
p_menu->x2 = x2;
|
|
|
|
p_menu->y2 = y2;
|
|
|
|
|
|
|
|
p_menu->text_w = 0;
|
|
|
|
p_menu->n_submenus = 0;
|
|
|
|
|
|
|
|
for (p_menu->n_entries = 0; p_menu->pp_msgs[p_menu->n_entries]; p_menu->n_entries++)
|
|
|
|
{
|
|
|
|
int text_w_font;
|
|
|
|
|
|
|
|
/* Is this a submenu? */
|
|
|
|
if (IS_SUBMENU(p_menu->pp_msgs[p_menu->n_entries]))
|
|
|
|
{
|
|
|
|
p_menu->n_submenus++;
|
|
|
|
continue; /* Length of submenus is unimportant */
|
|
|
|
}
|
|
|
|
|
2009-01-02 16:07:43 +01:00
|
|
|
if (TTF_SizeText(p_font, p_menu->pp_msgs[p_menu->n_entries], &text_w_font, NULL) != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s\n", TTF_GetError());
|
|
|
|
exit(1);
|
|
|
|
}
|
2008-12-31 17:20:47 +01:00
|
|
|
if (text_w_font > p_menu->text_w)
|
|
|
|
p_menu->text_w = text_w_font;
|
|
|
|
}
|
2009-01-02 16:07:43 +01:00
|
|
|
if ( !(p_menu->p_submenus = (submenu_t *)malloc(sizeof(submenu_t) * p_menu->n_submenus)) )
|
2008-12-31 17:20:47 +01:00
|
|
|
{
|
|
|
|
perror("malloc failed!\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
j=0;
|
|
|
|
for (i=0; i<p_menu->n_submenus; i++)
|
|
|
|
{
|
|
|
|
|
|
|
|
for (; j < p_menu->n_entries; j++)
|
|
|
|
{
|
|
|
|
if (IS_SUBMENU(p_menu->pp_msgs[j]))
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
p_menu->p_submenus[i].index = j;
|
|
|
|
p_menu->p_submenus[i].sel = 0;
|
|
|
|
p_menu->p_submenus[i].n_entries = 0;
|
|
|
|
for (n=0; p_menu->pp_msgs[j][n] != '\0'; n++)
|
|
|
|
{
|
|
|
|
if (p_menu->pp_msgs[j][n] == '|')
|
|
|
|
p_menu->p_submenus[i].n_entries++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-01-02 16:07:43 +01:00
|
|
|
p_menu->text_h = p_menu->n_entries * (TTF_FontHeight(p_font) + TTF_FontHeight(p_font) / 4);
|
2008-12-31 17:20:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void menu_fini(menu_t *p_menu)
|
|
|
|
{
|
|
|
|
free(p_menu->p_submenus);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static uint32_t wait_key_press(void)
|
|
|
|
{
|
|
|
|
SDL_Event ev;
|
|
|
|
uint32_t keys = 0;
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
#if defined(GEKKO)
|
|
|
|
Uint32 remote_keys;
|
|
|
|
|
|
|
|
WPAD_ScanPads();
|
2009-01-02 16:07:43 +01:00
|
|
|
remote_keys = WPAD_ButtonsDown(WPAD_CHAN_0);
|
2008-12-31 17:20:47 +01:00
|
|
|
|
|
|
|
if (remote_keys & WPAD_BUTTON_DOWN)
|
2009-01-02 17:23:21 +01:00
|
|
|
keys |= KEY_RIGHT;
|
2008-12-31 17:20:47 +01:00
|
|
|
if (remote_keys & WPAD_BUTTON_UP)
|
|
|
|
keys |= KEY_LEFT;
|
2009-01-02 17:23:21 +01:00
|
|
|
if (remote_keys & WPAD_BUTTON_LEFT)
|
|
|
|
keys |= KEY_DOWN;
|
2008-12-31 17:20:47 +01:00
|
|
|
if (remote_keys & WPAD_BUTTON_RIGHT)
|
2009-01-02 17:23:21 +01:00
|
|
|
keys |= KEY_UP;
|
2008-12-31 17:20:47 +01:00
|
|
|
if (remote_keys & WPAD_BUTTON_PLUS)
|
|
|
|
keys |= KEY_PAGEUP;
|
|
|
|
if (remote_keys & WPAD_BUTTON_MINUS)
|
|
|
|
keys |= KEY_PAGEDOWN;
|
2009-01-02 17:23:21 +01:00
|
|
|
if (remote_keys & (WPAD_BUTTON_A | WPAD_BUTTON_2) )
|
2008-12-31 17:20:47 +01:00
|
|
|
keys |= KEY_SELECT;
|
|
|
|
if (remote_keys & (WPAD_BUTTON_1 | WPAD_BUTTON_HOME) )
|
|
|
|
keys |= KEY_ESCAPE;
|
|
|
|
#endif
|
|
|
|
if (SDL_PollEvent(&ev))
|
|
|
|
{
|
|
|
|
switch(ev.type)
|
|
|
|
{
|
|
|
|
case SDL_KEYDOWN:
|
|
|
|
switch (ev.key.keysym.sym)
|
|
|
|
{
|
|
|
|
case SDLK_UP:
|
|
|
|
keys |= KEY_UP;
|
|
|
|
break;
|
|
|
|
case SDLK_DOWN:
|
|
|
|
keys |= KEY_DOWN;
|
|
|
|
break;
|
|
|
|
case SDLK_LEFT:
|
|
|
|
keys |= KEY_LEFT;
|
|
|
|
break;
|
|
|
|
case SDLK_RIGHT:
|
|
|
|
keys |= KEY_RIGHT;
|
|
|
|
break;
|
|
|
|
case SDLK_PAGEDOWN:
|
|
|
|
keys |= KEY_PAGEDOWN;
|
|
|
|
break;
|
|
|
|
case SDLK_PAGEUP:
|
|
|
|
keys |= KEY_PAGEUP;
|
|
|
|
break;
|
|
|
|
case SDLK_RETURN:
|
|
|
|
case SDLK_SPACE:
|
|
|
|
keys |= KEY_SELECT;
|
|
|
|
break;
|
|
|
|
case SDLK_ESCAPE:
|
|
|
|
keys |= KEY_ESCAPE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_QUIT:
|
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2009-01-02 16:07:43 +01:00
|
|
|
if (keys != 0)
|
|
|
|
return keys;
|
|
|
|
SDL_Delay(100);
|
2008-12-31 17:20:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return keys;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int menu_select(SDL_Surface *screen, menu_t *p_menu,
|
|
|
|
uint32_t available_options, int *p_submenus)
|
|
|
|
{
|
2009-01-02 17:23:21 +01:00
|
|
|
int ret = -1;
|
2008-12-31 17:20:47 +01:00
|
|
|
|
2009-01-02 17:23:21 +01:00
|
|
|
p_menu->available_options = available_options;
|
|
|
|
|
|
|
|
while(1)
|
2008-12-31 17:20:47 +01:00
|
|
|
{
|
2009-01-02 17:23:21 +01:00
|
|
|
uint32_t keys;
|
2008-12-31 17:20:47 +01:00
|
|
|
|
2009-01-02 17:23:21 +01:00
|
|
|
SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
|
2008-12-31 17:20:47 +01:00
|
|
|
|
2009-01-02 17:23:21 +01:00
|
|
|
menu_draw(screen, p_menu);
|
|
|
|
SDL_Flip(screen);
|
|
|
|
|
|
|
|
keys = wait_key_press();
|
|
|
|
|
|
|
|
if (keys & KEY_UP)
|
|
|
|
select_next(p_menu, 0, -1);
|
|
|
|
else if (keys & KEY_DOWN)
|
|
|
|
select_next(p_menu, 0, 1);
|
|
|
|
else if (keys & KEY_LEFT)
|
|
|
|
select_next(p_menu, -1, 0);
|
|
|
|
else if (keys & KEY_RIGHT)
|
|
|
|
select_next(p_menu, 1, 0);
|
|
|
|
else if (keys & KEY_ESCAPE)
|
|
|
|
break;
|
|
|
|
else if (keys & KEY_SELECT)
|
|
|
|
{
|
|
|
|
ret = p_menu->cur_sel;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!is_submenu_title(p_menu, ret))
|
|
|
|
{
|
|
|
|
for (i=0; i<p_menu->n_submenus; i++)
|
|
|
|
p_submenus[i] = p_menu->p_submenus[i].sel;
|
|
|
|
p_menu->cur_sel = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2008-12-31 17:20:47 +01:00
|
|
|
}
|
|
|
|
|
2009-01-02 17:23:21 +01:00
|
|
|
SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
|
|
|
|
return ret;
|
2008-12-31 17:20:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(TEST_MENU)
|
|
|
|
char *main_menu[] = {
|
|
|
|
"Insert disc",
|
|
|
|
"Reset C64",
|
|
|
|
"Joystick port",
|
|
|
|
"^|1|2",
|
|
|
|
" ",
|
|
|
|
"Quit",
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
SDL_Surface *screen;
|
|
|
|
TTF_Font *font;
|
|
|
|
const SDL_VideoInfo *info;
|
|
|
|
int submenus[1] = {0};
|
|
|
|
int selected;
|
|
|
|
menu_t menu;
|
|
|
|
|
|
|
|
if (SDL_Init( SDL_INIT_EVERYTHING ) < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError() );
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (TTF_Init() < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Unable to init TTF: %s\n", TTF_GetError() );
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
atexit(SDL_Quit);
|
|
|
|
atexit(TTF_Quit);
|
|
|
|
font = TTF_OpenFont("FreeMono.ttf", 20);
|
|
|
|
if (!font)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Unable to open font: %s\n", TTF_GetError() );
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
info = SDL_GetVideoInfo();
|
|
|
|
/* Open a 640x480 display with the optimal color depth */
|
|
|
|
screen = SDL_SetVideoMode(640, 480, info->vfmt->BitsPerPixel,
|
|
|
|
info->hw_available ? SDL_HWSURFACE : SDL_SWSURFACE);
|
|
|
|
menu_init(&menu, font, main_menu, 640 / 3, 480 / 3, 400, 400);
|
|
|
|
|
|
|
|
selected = menu_select(screen, &menu, ~0, submenus);
|
|
|
|
printf("Selected: %d:%d\n",
|
|
|
|
selected, submenus[0]);
|
|
|
|
|
|
|
|
menu_fini(&menu);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|