https://bitbucket.org/coutts/5dplus
Tip revision: 0d7cbec6dea8a278a468d89a5bf23bf5670a3172 authored by Coutts on 21 June 2012, 12:32:49 UTC
Fix font sizes (thanks Alex!) and fixed problem of bmp_buffer moving around, using a pointer to the bmp_vram now instead of the raw address. Also testing ML menu with skeleton shoot menu, not working yet though.
Fix font sizes (thanks Alex!) and fixed problem of bmp_buffer moving around, using a pointer to the bmp_vram now instead of the raw address. Also testing ML menu with skeleton shoot menu, not working yet though.
Tip revision: 0d7cbec
menu.c
/**
* Magic Lantern GUI
*/
/*
* Copyright (C) 2009 Trammell Hudson <hudson+ml@osresearch.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "vxworks.h"
#include "bmp.h"
#include "gui.h"
#include "font.h"
#include "menu.h"
#include "debug.h"
static int lv = 0;
static int advanced_mode = 1;
static int menu_first_by_icon = ICON_i;
static int sensor_cleaning = 0;
static int recording = 0;
static int hdmi_code = 0;
static int menu_upside_down = 0;
static struct semaphore * menu_sem;
extern struct semaphore * gui_sem;
static struct semaphore * menu_redraw_sem;
static int menu_damage;
static int menu_shown = false;
static int show_only_selected; // for ISO, kelvin...
static int config_dirty = 0;
static char* warning_msg = 0;
int menu_help_active = 0;
int submenu_mode = 0;
static int menu_id_increment = 1;
static int hist_countdown = 3; // histogram is slow, so draw it less often
static void select_menu_by_icon(int icon);
static void menu_help_go_to_selected_entry(struct menu * menu);
static void menu_show_version(void);
static struct menu * get_current_submenu();
static struct menu * get_selected_menu();
static struct menu * menus;
int is_movie_mode() { return 0; }
void menu_open_submenu(struct menu_entry * entry)
{
submenu_mode = 1;
}
void
menu_binary_toggle(
void * priv,
int unused
)
{
unsigned * val = priv;
*val = !*val;
}
void menu_ternary_toggle(void* priv, int delta)
{
unsigned * val = priv;
*val = mod(*val + delta, 3);
}
void menu_quaternary_toggle(void* priv, int delta)
{
unsigned * val = priv;
*val = mod(*val + delta, 4);
}
void menu_quinternary_toggle(void* priv, int delta)
{
unsigned * val = priv;
*val = mod(*val + delta, 5);
}
void menu_numeric_toggle(int* val, int delta, int min, int max)
{
*val = mod(*val - min + delta, max - min + 1) + min;
}
static void entry_draw_icon(
struct menu_entry * entry,
int x,
int y
)
{
if (entry->icon_type == IT_AUTO)
{
if (entry->select == menu_open_submenu)
{
entry->icon_type = IT_SUBMENU;
}
else if (!entry->priv || entry->select == (void(*)(void*,int))run_in_separate_task)
{
entry->icon_type = IT_ACTION;
}
else if(entry->choices)
{
const char* first_choice = entry->choices[0];
if (streq(first_choice, "OFF") || streq(first_choice, "Hide"))
entry->icon_type = IT_BOOL;
else if (streq(first_choice, "ON"))
entry->icon_type = IT_BOOL_NEG;
else if (streq(first_choice, "Small"))
entry->icon_type = IT_SIZE;
else
entry->icon_type = IT_DICE;
}
else if (entry->min != entry->max)
{
entry->icon_type = entry->max == 1 && entry->min == 0 ? IT_BOOL : IT_PERCENT;
}
else
entry->icon_type = IT_BOOL;
}
switch (entry->icon_type)
{
case IT_BOOL:
menu_draw_icon(x, y, MNI_BOOL(MEM(entry->priv)), 0);
break;
case IT_BOOL_NEG:
menu_draw_icon(x, y, MNI_BOOL(!MEM(entry->priv)), 0);
break;
case IT_ACTION:
menu_draw_icon(x, y, MNI_ACTION, 0);
break;
case IT_ALWAYS_ON:
menu_draw_icon(x, y, MNI_ON, 0);
break;
case IT_SIZE:
menu_draw_icon(x, y, MNI_SIZE, MEM(entry->priv) | ((entry->max+1) << 16));
break;
case IT_DICE:
menu_draw_icon(x, y, MNI_DICE, MEM(entry->priv) | ((entry->max+1) << 16));
break;
case IT_PERCENT:
menu_draw_icon(x, y, MNI_PERCENT, (MEM(entry->priv) - entry->min) * 100 / (entry->max - entry->min));
break;
case IT_NAMED_COLOR:
menu_draw_icon(x, y, MNI_NAMED_COLOR, (intptr_t) entry->choices[MEM(entry->priv)]);
break;
case IT_DISABLE_SOME_FEATURE:
menu_draw_icon(x, y, MEM(entry->priv) ? MNI_DISABLE : MNI_NEUTRAL, 0);
break;
case IT_DISABLE_SOME_FEATURE_NEG:
menu_draw_icon(x, y, MEM(entry->priv) ? MNI_NEUTRAL : MNI_DISABLE, 0);
break;
case IT_REPLACE_SOME_FEATURE:
menu_draw_icon(x, y, MEM(entry->priv) ? MNI_ON : MNI_NEUTRAL, 0);
break;
case IT_SUBMENU:
{
int value = 0;
if (entry->priv) value = MEM(entry->priv); // if priv field is present, use it as boolean value
else
{ // otherwise, look in the children submenus; if one is true, then submenu icon is drawn as "true"
struct menu_entry * e = entry->children;
for( ; e ; e = e->next )
{
if( e->priv && MEM(e->priv))
{
value = 1;
break;
}
}
}
menu_draw_icon(x, y, MNI_SUBMENU, value);
break;
}
}
}
//------
int raw2iso( int arg0)
{
return 0;
}
void
submenu_print(
struct menu_entry * entry,
int x,
int y
)
{
static char msg[200];
int i;
msg[0] = '\0';
STR_APPEND(msg, "%s", entry->name);
if (entry->priv && entry->select != (void(*)(void*,int))run_in_separate_task)
{
int l = strlen(entry->name);
for (i = 0; i < 14 - l; i++)
STR_APPEND(msg, " ");
if (entry->choices && MEM(entry->priv) <= entry->max)
{
STR_APPEND(msg, ": %s", entry->choices[MEM(entry->priv)]);
}
else if (entry->min == 0 && entry->max == 1)
{
STR_APPEND(msg, ": %s", MEM(entry->priv) ? "ON" : "OFF");
}
else
{
switch (entry->unit)
{
case UNIT_1_8_EV:
case UNIT_x10:
case UNIT_PERCENT_x10:
{
int v = MEM(entry->priv);
int den = entry->unit == UNIT_1_8_EV ? 8 : 10;
STR_APPEND(msg, ": %s%d", v < 0 ? "-" : "", ABS(v)/den);
int r = (ABS(v)%den)*10/den;
if (r) STR_APPEND(msg, ".%d", r);
STR_APPEND(msg, "%s",
entry->unit == UNIT_1_8_EV ? " EV" :
entry->unit == UNIT_PERCENT_x10 ? "%%" : ""
);
break;
}
case UNIT_PERCENT:
{
STR_APPEND(msg, ": %d%%", MEM(entry->priv));
break;
}
case UNIT_ISO:
{
if (!MEM(entry->priv)) { STR_APPEND(msg, ": Auto"); }
else { STR_APPEND(msg, ": %d", raw2iso(MEM(entry->priv))); }
break;
}
case UNIT_HEX:
{
STR_APPEND(msg, ": 0x%x", MEM(entry->priv));
break;
}
default:
{
STR_APPEND(msg, ": %d", MEM(entry->priv));
break;
}
}
}
}
bmp_printf(
entry->selected ? MENU_FONT_SEL : MENU_FONT,
x, y,
msg
);
entry_draw_icon(entry, x, y);
}
static struct menu *
menu_find_by_name(
const char * name,
int icon
)
{
take_semaphore( menu_sem, 0 );
struct menu * menu = menus;
for( ; menu ; menu = menu->next )
{
if( streq( menu->name, name ) )
{
give_semaphore( menu_sem );
return menu;
}
// Stop just before we get to the end
if( !menu->next )
break;
}
// Not found; create it
struct menu * new_menu = AllocateMemory( sizeof(*new_menu) );
if( !new_menu )
{
give_semaphore( menu_sem );
return NULL;
}
new_menu->id = menu_id_increment++;
new_menu->name = name;
new_menu->icon = icon;
new_menu->prev = menu;
new_menu->next = NULL; // Inserting at end
new_menu->children = NULL;
// menu points to the last entry or NULL if there are none
if( menu )
{
// We are adding to the end
menu->next = new_menu;
new_menu->selected = 0;
} else {
// This is the first one
menus = new_menu;
new_menu->selected = 1;
}
give_semaphore( menu_sem );
return new_menu;
}
void
menu_add(
const char * name,
struct menu_entry * new_entry,
int count
)
{
#if 1
// There is nothing to display. Sounds crazy (but might result from ifdef's)
if ( count == 0 )
return;
// Walk the menu list to find a menu
struct menu * menu = menu_find_by_name( name, 0);
if( !menu )
return;
int count0 = count; // for submenus
take_semaphore( menu_sem, 0 );
struct menu_entry * head = menu->children;
if( !head )
{
// First one -- insert it as the selected item
head = menu->children = new_entry;
if (new_entry->id == 0) new_entry->id = menu_id_increment++;
new_entry->next = NULL;
new_entry->prev = NULL;
new_entry->selected = 1;
if (IS_SUBMENU(menu)) new_entry->essential = FOR_SUBMENU;
new_entry++;
count--;
}
// Find the end of the entries on the menu already
while( head->next )
head = head->next;
int i;
for (i = 0; i < count; i++)
{
if (new_entry->id == 0) new_entry->id = menu_id_increment++;
new_entry->selected = 0;
if (IS_SUBMENU(menu)) new_entry->essential = FOR_SUBMENU;
new_entry->next = head->next;
new_entry->prev = head;
head->next = new_entry;
head = new_entry;
new_entry++;
}
give_semaphore( menu_sem );
// create submenus
struct menu_entry * entry = head;
for (i = 0; i < count0; i++)
{
if (entry->children)
{
int count = 0;
struct menu_entry * child = entry->children;
while (!MENU_IS_EOL(child)) { count++; child++; }
menu_find_by_name( entry->name, ICON_ML_SUBMENU);
menu_add(entry->name, entry->children, count);
}
entry = entry->prev;
if (!entry) break;
}
#else
// Maybe later...
struct menu_entry * child = head->child;
if( !child )
{
// No other child entries; add this one
// and select it
new_entry->highlighted = 1;
new_entry->prev = NULL;
new_entry->next = NULL;
head->child = new_entry;
return;
}
// Walk the child list to find the end
while( child->next )
child = child->next;
// Push the new entry onto the end of the list
new_entry->selected = 0;
new_entry->prev = child;
new_entry->next = NULL;
child->next = new_entry;
#endif
}
static void batsu(int x, int y, int c)
{
int i;
for (i = 1; i < 4; i++)
{
draw_line(x + 8 + i, y + 9, x + 21 + i, y + 22, c);
draw_line(x + 21 + i, y + 9, x + 8 + i, y + 22, c);
}
}
static void crossout(int x, int y, int color)
{
x += 16;
y += 16;
int r;
for (r = 9; r < 10; r++)
{
int i = r-9;
draw_circle(x, y, r, color);
draw_circle(x + 1, y, r, color);
draw_line(x + 5 + i, y - 5, x - 5 + i, y + 5, color);
}
}
void dot(int x, int y, int color, int radius)
{
int r;
for (r = 0; r < radius; r++)
{
draw_circle(x + 16, y + 16, r, color);
draw_circle(x + 17, y + 16, r, color);
}
}
void maru(int x, int y, int color)
{
dot(x, y, color, 10);
}
static void percent(int x, int y, int value)
{
int i;
y -= 2;
value = value * 28 / 100;
for (i = 0; i < 28; i++)
draw_line(x + 2 + i, y + 25, x + 2 + i, y + 25 - i/3 - 5,
i <= value ? 9 : 60
);
}
static void playicon(int x, int y)
{
int i;
for (i = 5; i < 32-5; i++)
{
draw_line(x + 7, y + i, x + 25, y + 16, COLOR_YELLOW);
draw_line(x + 7, y + i, x + 25, y + 16, COLOR_YELLOW);
}
}
static void leftright_sign(int x, int y)
{
int i;
for (i = 5; i < 32-5; i++)
{
draw_line(x + 3, y + i, x + 18 + 3, y + 16, COLOR_WHITE);
draw_line(x + 3, y + i, x + 18 + 3, y + 16, COLOR_WHITE);
draw_line(x - 3, y + i, x - 18 - 3, y + 16, COLOR_WHITE);
draw_line(x - 3, y + i, x - 18 - 3, y + 16, COLOR_WHITE);
}
}
static int playicon_square(int x, int y, int color)
{
bmp_draw_rect(color,x+1,y+4,38,32);
bmp_draw_rect(color,x+2,y+5,36,30);
int i;
for (i = 12; i < 40-12; i++)
{
draw_line(x + 10, y + i, x + 30, y + 20, color);
draw_line(x + 10, y + i, x + 30, y + 20, color);
}
return 40;
}
void submenu_icon(int x, int y)
{
//~ int color = COLOR_WHITE;
x -= 40;
bmp_draw_rect(45, x+2, y+5, 32-3, 32-10+1);
draw_line(x+20, y+28, x+30, y+28, COLOR_WHITE);
int i;
for (i = -2; i <= 2; i++)
draw_line(x+26, y+28+i, x+30, y+28, COLOR_WHITE);
//~ for (int r = 0; r < 2; r++)
//~ {
//~ draw_circle(x + 30, y + 28, r, color);
//~ draw_circle(x + 23, y + 28, r, color);
//~ draw_circle(x + 16, y + 28, r, color);
//~ }
}
void submenu_only_icon(int x, int y, int value)
{
//~ bmp_draw_rect(50, x+2, y+5, 32-3, 32-10);
int color = value ? COLOR_GREEN1 : 45;
int r;
for (r = 0; r < 3; r++)
{
draw_circle(x + 8, y + 10, r, color);
draw_circle(x + 8, y + 16, r, color);
draw_circle(x + 8, y + 22, r, color);
draw_circle(x + 9, y + 10, r, color);
draw_circle(x + 9, y + 16, r, color);
draw_circle(x + 9, y + 22, r, color);
}
color = value ? COLOR_WHITE : 45;
bmp_draw_rect(color, x + 15, y + 10, 10, 1);
bmp_draw_rect(color, x + 15, y + 16, 10, 1);
bmp_draw_rect(color, x + 15, y + 22, 10, 1);
}
int bmp_color_scheme = 0;
void selection_bar(int x0, int y0)
{
int w = x0 + 720 - 40 - 10;
if (submenu_mode==1) w -= 90;
extern int bmp_color_scheme;
uint8_t* B = bmp_vram();
int y;
for (y = y0; y < y0 + 31; y++)
{
int x;
for (x = x0-5; x < w; x++)
{
if (B[BM(x,y)] == COLOR_BLACK)
B[BM(x,y)] = submenu_mode || bmp_color_scheme ? COLOR_LIGHTBLUE : COLOR_BLUE;
}
}
}
void size_icon(int x, int y, int current, int nmax)
{
dot(x, y, COLOR_GREEN1, current * (nmax > 2 ? 9 : 7) / (nmax-1) + 3);
}
void dice_icon(int x, int y, int current, int nmax)
{
#define C(i) (current == (i) ? COLOR_GREEN1 : 50), (current == (i) ? 6 : 4)
//~ x -= 40;
//~ x += 16; y += 16;
switch (nmax)
{
case 2:
dot(x - 6, y + 6, C(0)+2);
dot(x + 6, y - 6, C(1)+2);
break;
case 3:
dot(x , y - 7, C(0));
dot(x - 7, y + 3, C(1));
dot(x + 7, y + 3, C(2));
break;
case 4:
dot(x - 6, y - 6, C(0));
dot(x + 6, y - 6, C(1));
dot(x - 6, y + 6, C(2));
dot(x + 6, y + 6, C(3));
break;
case 5:
dot(x, y, C(0));
dot(x - 8, y - 8, C(1));
dot(x + 8, y - 8, C(2));
dot(x + 8, y + 8, C(3));
dot(x - 8, y + 8, C(4));
break;
case 6:
dot(x - 10, y - 8, C(0));
dot(x , y - 8, C(1));
dot(x + 10, y - 8, C(2));
dot(x - 10, y + 8, C(3));
dot(x , y + 8, C(4));
dot(x + 10, y + 8, C(5));
break;
case 7:
dot(x - 10, y - 10, C(0));
dot(x , y - 10, C(1));
dot(x + 10, y - 10, C(2));
dot(x - 10, y + 10, C(3));
dot(x , y + 10, C(4));
dot(x + 10, y + 10, C(5));
dot(x , y , C(6));
break;
case 8:
dot(x - 10, y - 10, C(0));
dot(x , y - 10, C(1));
dot(x + 10, y - 10, C(2));
dot(x - 10, y + 10, C(3));
dot(x , y + 10, C(4));
dot(x + 10, y + 10, C(5));
dot(x - 5, y , C(6));
dot(x + 5, y , C(7));
break;
case 9:
dot(x - 10, y - 10, C(0));
dot(x , y - 10, C(1));
dot(x + 10, y - 10, C(2));
dot(x - 10, y , C(3));
dot(x , y , C(4));
dot(x + 10, y , C(5));
dot(x - 10, y + 10, C(6));
dot(x , y + 10, C(7));
dot(x + 10, y + 10, C(10));
break;
default:
size_icon(x, y, current, nmax);
break;
}
#undef C
}
void color_icon(int x, int y, const char* color)
{
if (streq(color, "Red"))
maru(x, y, COLOR_RED);
else if (streq(color, "Green"))
maru(x, y, COLOR_GREEN2);
else if (streq(color, "Blue"))
maru(x, y, COLOR_LIGHTBLUE);
else if (streq(color, "Cyan"))
maru(x, y, COLOR_CYAN);
else if (streq(color, "Magenta"))
maru(x, y, 14);
else if (streq(color, "Yellow"))
maru(x, y, COLOR_YELLOW);
else if (streq(color, "Orange"))
maru(x, y, COLOR_ORANGE);
else if (streq(color, "White"))
maru(x, y, COLOR_WHITE);
else if (streq(color, "Black"))
maru(x, y, COLOR_WHITE);
else if (streq(color, "Luma"))
maru(x, y, 60);
else if (streq(color, "RGB"))
{
dot(x, y - 7, COLOR_RED, 5);
dot(x - 7, y + 3, COLOR_GREEN2, 5);
dot(x + 7, y + 3, COLOR_LIGHTBLUE, 5);
}
else if (streq(color, "ON"))
maru(x, y, COLOR_GREEN1);
else if (streq(color, "OFF"))
maru(x, y, 40);
else
{
dot(x, y - 7, COLOR_CYAN, 5);
dot(x - 7, y + 3, COLOR_RED, 5);
dot(x + 7, y + 3, COLOR_YELLOW, 5);
}
}
// By default, icon type is MNI_BOOL(*(int*)priv)
// To override, call menu_draw_icon from the display functions
// Icon is only drawn once for each menu item, even if this is called multiple times
// Only the first call is executed
int icon_drawn = 0;
void menu_draw_icon(int x, int y, int type, intptr_t arg)
{
if (icon_drawn) return;
icon_drawn = type;
x -= 40;
if (type >= 0) bmp_printf(FONT_LARGE, x, y, " "); // cleanup background
warning_msg = 0;
switch(type)
{
case MNI_OFF: maru(x, y, 40); return;
case MNI_ON: maru(x, y, COLOR_GREEN1); return;
case MNI_DISABLE: batsu(x, y, COLOR_RED); return;
case MNI_NEUTRAL: maru(x, y, 60); return;
case MNI_WARNING: maru(x, y, COLOR_RED); warning_msg = (char *) arg; return;
case MNI_AUTO: maru(x, y, 9); return;
case MNI_PERCENT: percent(x, y, arg); return;
case MNI_ACTION: playicon(x, y); return;
case MNI_DICE: dice_icon(x, y, arg & 0xFFFF, arg >> 16); return;
case MNI_SIZE: size_icon(x, y, arg & 0xFFFF, arg >> 16); return;
case MNI_NAMED_COLOR: color_icon(x, y, (char *)arg); return;
case MNI_SUBMENU: submenu_only_icon(x, y, arg); return;
}
}
static int
menu_has_visible_items(struct menu_entry * menu)
{
while( menu )
{
if (/*advanced_mode ||*/ IS_ESSENTIAL(menu))
{
return 1;
}
menu = menu->next;
}
return 0;
}
static void
menu_display(
struct menu_entry * menu,
int x,
int y
)
{
while( menu )
{
if (advanced_mode || IS_ESSENTIAL(menu))
{
// display help (should be first; if there are too many items in menu, the main text should overwrite the help, not viceversa)
if (menu->selected && menu->help)
{
bmp_printf(
FONT(FONT_MED, 0xC, COLOR_BLACK), // red
10, 450,
" "
);
bmp_printf(
FONT(FONT_MED, COLOR_WHITE, COLOR_BLACK),
10 /* + ((700/font_med.width) - strlen(menu->help)) * font_med.width / 2*/, 450,
menu->help
);
}
// display icon (only the first icon is drawn)
icon_drawn = 0;
if (!show_only_selected || menu->selected)
{
if (menu->display)
menu->display(
menu->priv,
x,
y,
menu->selected
);
else
submenu_print(menu, x, y);
}
// this should be after menu->display, in order to allow it to override the icon
if (menu->selected || !show_only_selected)
{
entry_draw_icon(menu, x, y);
}
// display key help
if (menu->selected && !is_menu_active("Help") && (menu->priv || menu->select) && y + font_large.height < 425)
{
char msg[100] = "";
// this should follow exactly the same logic as in menu_entry_select
// todo: remove duplicate code
// exception for action and submenu items
if (icon_drawn == MNI_ACTION && !submenu_mode)
{
STR_APPEND(msg, "SET: run action ");
}
else if (menu->select == menu_open_submenu)
{
STR_APPEND(msg, "SET: open submenu ");
}
// exception end
else if (submenu_mode == 2)
{
STR_APPEND(msg, "SET: toggle edit mode ");
}
else if (show_only_selected)
{
STR_APPEND(msg, "SET: toggle LiveView ");
}
else if (menu->edit_mode == EM_FEW_VALUES) // SET increments
{
STR_APPEND(msg, "SET: change value ");
}
else if (menu->edit_mode == EM_MANY_VALUES)
{
STR_APPEND(msg, "SET: toggle edit mode ");
}
else if (menu->edit_mode == EM_MANY_VALUES_LV)
{
/*if (lv)
{
STR_APPEND(msg, "SET: toggle LiveView ");
}*/
if (submenu_mode != 1)
{
STR_APPEND(msg, "SET: toggle edit mode ");
}
else // increment
{
STR_APPEND(msg, "SET: change value ");
}
}
STR_APPEND(msg, " ", Q_BTN_NAME);
if (submenu_mode || show_only_selected)
{
STR_APPEND(msg, "L/R/Wheel : ");
if (icon_drawn == MNI_ACTION)
{
STR_APPEND(msg, "run action ");
}
else
{
STR_APPEND(msg, "change value");
}
leftright_sign(690, 400);
}
else if (menu->children && !submenu_mode && !show_only_selected)
{
STR_APPEND(msg, "%s: open submenu ", Q_BTN_NAME);
}
bmp_printf(
FONT(FONT_MED, 60, COLOR_BLACK),
10, 425,
msg
);
}
// if there's a warning message set, display it
if (menu->selected && warning_msg)
{
bmp_printf(
FONT(FONT_MED, 0xC, COLOR_BLACK), // red
10, 450,
" "
);
bmp_printf(
FONT(FONT_MED, 0xC, COLOR_BLACK), // red
10, 450,
warning_msg
);
}
// display submenu marker if this item has a submenu
if (menu->children && !show_only_selected)
submenu_icon(x, y);
// display selection bar
if (menu->selected)
selection_bar(x, y);
// move down for next item
y += font_large.height-1;
// stop before attempting to display things outside the screen
if ((unsigned)y > 480 - font_large.height
//~ #if CONFIG_DEBUGMSG
&& !is_menu_active("VRAM")
//~ #endif
)
return;
}
menu = menu->next;
}
}
int override_zoom_buttons = 0;
static void
menus_display(
struct menu * menu,
int orig_x,
int y
)
{
int x = orig_x;
take_semaphore( menu_sem, 0 );
extern int override_zoom_buttons; // from focus.c
override_zoom_buttons = 0; // will override them only if rack focus items are selected
//~ if (!show_only_selected)
//~ bmp_printf(
//~ FONT(FONT_MED, 55, COLOR_BLACK), // gray
//~ 10, 430,
//~ MENU_NAV_HELP_STRING
//~ );
bmp_fill(40, orig_x, y, 720, 42);
bmp_fill(70, orig_x, y+42, 720, 1);
for( ; menu ; menu = menu->next )
{
if (!menu_has_visible_items(menu->children))
continue; // empty menu
if (IS_SUBMENU(menu))
continue;
int fg = menu->selected ? COLOR_WHITE : 70;
int bg = menu->selected ? COLOR_BLUE : 40;
unsigned fontspec = FONT(
menu->selected ? FONT_LARGE : FONT_MED,
fg,
bg
);
if (!show_only_selected)
{
int w = fontspec_font( fontspec )->width * 6;
//int h = fontspec_font( fontspec )->height;
int icon_w = 0;
if (menu->icon)
{
bmp_fill(bg, x+1, y, 200, 40);
//- if (menu->icon == ICON_ML_PLAY) icon_w = playicon_square(x,y,fg);
//- else icon_w = bfnt_draw_char(menu->icon, x, y, fg, bg);
}
if (!menu->icon || menu->selected)
{
//- bfnt_puts(menu->name, x + icon_w, y, fg, bg);
//~ bmp_printf( fontspec, x + icon_w + 5, y + (40 - h)/2, "%6s", menu->name );
x += w;
}
x += 47;
//~ if (menu->selected)
//~ {
//~ bmp_printf( FONT(FONT_LARGE,fg,40), orig_x + 700 - font_large.width * strlen(menu->name), y + 4, menu->name );
//~ }
}
if( menu->selected )
menu_display(
menu->children,
orig_x + 40,
y + 45
);
}
give_semaphore( menu_sem );
}
static void
implicit_submenu_display()
{
struct menu * menu = get_selected_menu();
int sos = show_only_selected;
show_only_selected = 1;
menu_display(
menu->children,
40,
45
);
show_only_selected = sos;
}
static void
submenu_display(struct menu * submenu)
{
if (!submenu) return;
int count = 0;
struct menu_entry * child = submenu->children;
while (child) { count++; child = child->next; }
int h = MIN((count + 4) * font_large.height, 400);
int bx = 45;
int by = (480 - h)/2 - 30;
if (!show_only_selected)
{
bmp_fill(40, bx, by, 720-2*bx+4, 50);
bmp_fill(COLOR_BLACK, bx, by + 50, 720-2*bx+4, h-50);
bmp_draw_rect(70, bx, by, 720-2*bx, 50);
bmp_draw_rect(COLOR_WHITE, bx, by, 720-2*bx, h);
//- bfnt_puts(submenu->name, bx + 15, by + 5, COLOR_WHITE, 40);
}
menu_display(submenu->children, bx + 50, by + 50 + 20);
}
static void
menu_entry_select(
struct menu * menu,
int mode // 0 = increment, 1 = decrement, 2 = Q, 3 = SET
)
{
if( !menu )
return;
take_semaphore( menu_sem, 0 );
struct menu_entry * entry = menu->children;
for( ; entry ; entry = entry->next )
{
if( entry->selected )
break;
}
give_semaphore( menu_sem );
if( !entry )
return;
//~ if (entry->show_liveview)
//~ menu_show_only_selected();
if(mode == 1) // decrement
{
if( entry->select_reverse ) entry->select_reverse( entry->priv, -1 );
else if (entry->select) entry->select( entry->priv, -1);
else menu_numeric_toggle(entry->priv, -1, entry->min, entry->max);
}
else if (mode == 2) // Q
{
if ( entry->select_Q ) entry->select_Q( entry->priv, 1);
else { submenu_mode = !submenu_mode; show_only_selected = 0; }
}
else if (mode == 3) // SET
{
if (submenu_mode == 2) submenu_mode = 0;
else if (show_only_selected && entry->icon_type != IT_ACTION) show_only_selected = 0;
else if (entry->edit_mode == EM_FEW_VALUES) // SET increments
{
if( entry->select ) entry->select( entry->priv, 1);
else menu_numeric_toggle(entry->priv, 1, entry->min, entry->max);
}
else if (entry->edit_mode == EM_MANY_VALUES)
{
submenu_mode = (!submenu_mode)*2;
show_only_selected = 0;
}
else if (entry->edit_mode == EM_MANY_VALUES_LV)
{
//if (lv) show_only_selected = !show_only_selected;
if (submenu_mode != 1) submenu_mode = (!submenu_mode)*2;
else // increment
{
if( entry->select ) entry->select( entry->priv, 1);
else menu_numeric_toggle(entry->priv, 1, entry->min, entry->max);
}
}
}
else // increment
{
if( entry->select ) entry->select( entry->priv, 1);
else menu_numeric_toggle(entry->priv, 1, entry->min, entry->max);
}
config_dirty = 1;
}
/** Scroll side to side in the list of menus */
static void
menu_move(
struct menu * menu,
int direction
)
{
//~ menu_damage = 1;
if( !menu )
return;
int rc = take_semaphore( menu_sem, 1000 );
if( rc != 0 )
return;
// Deselect the current one
menu->selected = 0;
if( direction < 0 )
{
if( menu->prev )
menu = menu->prev;
else {
// Go to the last one
while( menu->next )
menu = menu->next;
}
} else {
if( menu->next )
menu = menu->next;
else {
// Go to the first one
while( menu->prev )
menu = menu->prev;
}
}
// Select the new one (which might be the same)
menu->selected = 1;
menu_first_by_icon = menu->icon;
give_semaphore( menu_sem );
if (IS_SUBMENU(menu) || !menu_has_visible_items(menu->children))
menu_move(menu, direction); // this menu is hidden, skip it (try again)
// will fail if no menus are displayed!
}
/** Scroll up or down in the currently displayed menu */
static void
menu_entry_move(
struct menu * menu,
int direction
)
{
if( !menu )
return;
int rc = take_semaphore( menu_sem, 1000 );
if( rc != 0 )
return;
if (!menu_has_visible_items(menu->children))
return;
struct menu_entry * entry = menu->children;
for( ; entry ; entry = entry->next )
{
if( entry->selected )
break;
}
// Nothing selected?
if( !entry )
{
give_semaphore( menu_sem );
return;
}
// Deslect the current one
entry->selected = 0;
if( direction < 0 )
{
// First and moving up?
if( entry->prev )
entry = entry->prev;
else {
// Go to the last one
while( entry->next )
entry = entry->next;
}
} else {
// Last and moving down?
if( entry->next )
entry = entry->next;
else {
// Go to the first one
while( entry->prev )
entry = entry->prev;
}
}
// Select the new one, which might be the same as the old one
entry->selected = 1;
give_semaphore( menu_sem );
if (!advanced_mode && !IS_ESSENTIAL(entry))
menu_entry_move(menu, direction); // try again, skip hidden items
// warning: would block if the menu is empty
}
int zebra_should_run()
{
return 0;
}
void
menu_redraw_do()
{
menu_damage = 0;
if (!DISPLAY_IS_ON) return;
if (sensor_cleaning) return;
//- if (gui_state == GUISTATE_MENUDISP) return;
int double_buffering = 0;
if (menu_help_active)
{
//- menu_help_redraw();
menu_damage = 0;
}
else
{
if (!lv) show_only_selected = 0;
//~ if (MENU_MODE || lv) clrscr();
//~ menu_damage = 0;
if (double_buffering)
{
// draw to mirror buffer to avoid flicker
//~ bmp_idle_copy(0); // no need, drawing is fullscreen anyway
//- bmp_draw_to_idle(1);
}
static int prev_so = 0;
if (show_only_selected)
{
bmp_fill( 0, 0, 0, 720, 480 );
if (zebra_should_run())
{
//- if (prev_so) copy_zebras_from_mirror();
//- else cropmark_clear_cache(); // will clear BVRAM mirror and reset cropmarks
}
//-
/*if (hist_countdown == 0 && !should_draw_zoom_overlay())
draw_histogram_and_waveform(); // too slow
else
hist_countdown--;*/
}
else
{
bmp_fill(COLOR_BLACK, 0, 0, 720, 480 );
}
prev_so = show_only_selected;
// this part needs to know which items are selected - don't run it in the middle of selection changing
take_semaphore(menu_redraw_sem, 0);
if (!show_only_selected || !submenu_mode)
menus_display( menus, 0, 0 );
if (!show_only_selected && !submenu_mode)
if (is_menu_active("Help")) menu_show_version();
if (submenu_mode)
{
//- if (!show_only_selected) bmp_dim();
struct menu * submenu = get_current_submenu();
if (submenu) submenu_display(submenu);
else implicit_submenu_display();
}
give_semaphore(menu_redraw_sem);
if (show_only_selected)
{
//- draw_ml_topbar(0, 1);
//- draw_ml_bottombar(0, 1);
}
//- if (recording)
//- bmp_make_semitransparent();
#if 0
if (double_buffering)
{
// copy image to main buffer
bmp_draw_to_idle(0);
int screen_layout = get_screen_layout();
if (hdmi_code == 2) // copy at a smaller scale to fit the screen
{
if (screen_layout == SCREENLAYOUT_16_10)
bmp_zoom(bmp_vram(), bmp_vram_idle(), 360, 150, /* 128 div */ 143, /* 128 div */ 169);
else if (screen_layout == SCREENLAYOUT_16_9)
bmp_zoom(bmp_vram(), bmp_vram_idle(), 360, 150, /* 128 div */ 143, /* 128 div */ 185);
else
{
if (menu_upside_down) bmp_flip(bmp_vram(), bmp_vram_idle(), 0);
else bmp_idle_copy(1,0);
}
}
else if (ext_monitor_rca)
bmp_zoom(bmp_vram(), bmp_vram_idle(), 360, 200, /* 128 div */ 135, /* 128 div */ 135);
else
{
if (menu_upside_down) bmp_flip(bmp_vram(), bmp_vram_idle());
else bmp_idle_copy(1,0);
}
//~ bmp_idle_clear();
}
#endif
//~ update_stuff();
//- lens_display_set_dirty();
}
}
void redraw()
{
}
struct msg_queue * menu_redraw_queue = 0;
static void
menu_redraw_task()
{
menu_redraw_queue = (struct msg_queue *) msg_queue_create("menu_redraw_mq", 1);
while(1)
{
msleep(50);
int msg;
int err = msg_queue_receive(menu_redraw_queue, (struct event**)&msg, 500);
if (err) continue;
if (gui_menu_shown()) menu_redraw_do();
else redraw();
}
}
void
menu_redraw()
{
if (!DISPLAY_IS_ON) return;
//- if (ml_shutdown_requested) return;
//- if (menu_help_active) bmp_draw_request_stop();
if (menu_redraw_queue) msg_queue_post(menu_redraw_queue, 1);
}
static struct menu * get_selected_menu()
{
struct menu * menu = menus;
for( ; menu ; menu = menu->next )
if( menu->selected )
break;
return menu;
}
static struct menu * get_current_submenu()
{
if (submenu_mode == 2) return 0;
struct menu * menu = menus;
for( ; menu ; menu = menu->next )
if( menu->selected )
break;
struct menu_entry * entry = menu->children;
for( ; entry ; entry = entry->next )
if( entry->selected )
break;
if (entry->children)
return menu_find_by_name(entry->name, 0);
// no submenu
submenu_mode = 2;
return 0;
}
static int keyrepeat = 0;
static int keyrep_countdown = 4;
static int keyrep_ack = 1;
int handle_ml_menu_keyrepeat(struct event * event)
{
//~ if (menu_shown || arrow_keys_shortcuts_active())
{
switch(event->param)
{
case BGMT_JOY_LEFT:
case BGMT_JOY_RIGHT:
case BGMT_JOY_UP:
case BGMT_JOY_DOWN:
keyrepeat = event->param;
break;
case BGMT_JOY_ALL_UNPRESS:
keyrepeat = 0;
keyrep_countdown = 4;
break;
}
}
return 1;
}
void keyrepeat_ack(int button_code)
{
if (button_code == keyrepeat) keyrep_ack = 1;
}
int
handle_ml_menu_keys(struct event * event)
{
if (menu_shown || arrow_keys_shortcuts_active())
handle_ml_menu_keyrepeat(event);
if (!menu_shown) return 1;
// rack focus may override some menu keys
//- if (handle_rack_focus_menu_overrides(event)==0) return 0;
// the first steps may temporarily change the selected menu item - don't redraw in the middle of this
take_semaphore(menu_redraw_sem, 0);
// Find the selected menu (should be cached?)
struct menu * menu = get_selected_menu();
// Make sure we are not displaying an empty menu
if (!menu_has_visible_items(menu->children))
{
menu_move(menu, -1); menu = get_selected_menu();
menu_move(menu, 1); menu = get_selected_menu();
}
menu_entry_move(menu, -1);
menu_entry_move(menu, 1);
struct menu * help_menu = menu;
if (submenu_mode)
{
help_menu = menu;
menu = get_current_submenu();
if (!menu) menu = help_menu; // no submenu, operate on same item
}
give_semaphore(menu_redraw_sem);
int button_code = event->param;
switch( button_code )
{
case BGMT_MENU:
if (submenu_mode) submenu_mode = 0;
else advanced_mode = !advanced_mode;
show_only_selected = 0;
menu_help_active = 0;
break;
//- need to find event code for shutter button.
/*case BGMT_PRESS_HALFSHUTTER: // If they press the shutter halfway
//~ menu_close();
give_semaphore(gui_sem);
return 1;*/
case BGMT_PRESS_ZOOM_IN:
//if (lv) show_only_selected = !show_only_selected;
/*else */ submenu_mode = (!submenu_mode)*2;
menu_damage = 1;
menu_help_active = 0;
break;
case BGMT_JOY_UP:
case BGMT_WHEEL_LEFT:
//- if (menu_help_active) { menu_help_prev_page(); break; }
menu_entry_move( menu, -1 );
//~ if (!submenu_mode) show_only_selected = 0;
break;
case BGMT_JOY_DOWN:
case BGMT_WHEEL_RIGHT:
//- if (menu_help_active) { menu_help_next_page(); break; }
menu_entry_move( menu, 1 );
//~ if (!submenu_mode) show_only_selected = 0;
break;
case BGMT_JOY_RIGHT:
menu_damage = 1;
//- if (menu_help_active) { menu_help_next_page(); break; }
if (submenu_mode || show_only_selected) menu_entry_select( menu, 0 );
else { menu_move( menu, 1 ); show_only_selected = 0; }
break;
case BGMT_JOY_LEFT:
menu_damage = 1;
//- if (menu_help_active) { menu_help_prev_page(); break; }
if (submenu_mode || show_only_selected) menu_entry_select( menu, 1 );
else { menu_move( menu, -1 ); show_only_selected = 0; }
break;
case BGMT_SET:
if (menu_help_active) { menu_help_active = 0; /* menu_damage = 1; */ break; }
else
{
menu_entry_select( menu, 3 ); // "SET" select
}
//~ menu_damage = 1;
break;
case BGMT_INFO:
menu_help_active = !menu_help_active;
show_only_selected = 0;
//- if (menu_help_active) menu_help_go_to_selected_entry(help_menu);
//~ menu_damage = 1;
break;
case BGMT_PLAY:
//- if (menu_help_active) { menu_help_active = 0; /* menu_damage = 1; */ break; }
menu_entry_select( menu, 1 ); // reverse select
//~ menu_damage = 1;
break;
case BGMT_JUMP:
//- if (menu_help_active) { menu_help_active = 0; /* menu_damage = 1; */ break; }
menu_entry_select( menu, 2 ); // auto setting select
//~ menu_damage = 1;
break;
case BGMT_JOY_UP_RIGHT:
case BGMT_JOY_UP_LEFT:
case BGMT_JOY_DOWN_RIGHT:
case BGMT_JOY_DOWN_LEFT:
break; // ignore
default:
/*DebugMsg( DM_MAGIC, 3, "%s: unknown event %08x? %08x %08x %x08",
__func__,
event,
arg2,
arg3,
arg4
);*/
return 1;
}
// If we end up here, something has been changed.
// Reset the timeout
menu_redraw();
keyrepeat_ack(button_code);
hist_countdown = 3;
return 0;
}
void
menu_init( void )
{
menus = NULL;
menu_sem = create_named_semaphore( "menus", 1 );
gui_sem = create_named_semaphore( "gui", 0 );
//- menu_redraw_sem = create_named_semaphore( "menu_r", 1);
//- menu_find_by_name( "Expo", ICON_AE);
//- menu_find_by_name( "LiveV", ICON_LV);
//- menu_find_by_name( "Movie", ICON_VIDEOCAM );
menu_find_by_name( "Shoot", ICON_PHOTOCAM );
//~ menu_find_by_name( "Brack" );
//- menu_find_by_name( "Focus", ICON_SHARPNESS );
//~ menu_find_by_name( "LUA" );
//menu_find_by_name( "Games" );
//- menu_find_by_name( "Display", ICON_MONITOR );
//- menu_find_by_name( "Tweaks", ICON_SMILE );
//- menu_find_by_name( "Play", ICON_ML_PLAY );
//- menu_find_by_name( "Power", ICON_P_SQUARE );
//- menu_find_by_name( "Debug", ICON_HEAD_WITH_RAYS );
//~ menu_find_by_name( "Config" );
//- menu_find_by_name( "Config", ICON_CF );
//- menu_find_by_name( "Help", ICON_i );
//~ menu_find_by_name( "Boot" );
/*
bmp_printf( FONT_LARGE, 0, 40, "Yes, use this battery" );
gui_control( ELECTRONIC_SUB_DIAL_RIGHT, 1, 0 );
msleep( 2000 );
gui_control( PRESS_SET_BUTTON, 1, 0 );
msleep( 2000 );
// Try to defeat the battery message
//GUI_SetErrBattery( 1 );
//msleep( 100 );
//StopErrBatteryApp();
msleep( 1000 );
*/
}
void
gui_stop_menu( )
{
if (gui_menu_shown())
give_semaphore(gui_sem);
}
int
gui_menu_shown( void )
{
return menu_shown;
}
void piggyback_canon_menu()
{
if (recording) return;
if (sensor_cleaning) return;
//- if (gui_state == GUISTATE_MENUDISP) return;
//- NotifyBoxHide();
//- SetGUIRequestMode(GUIMODE_ML_MENU);
NotifyGUIEvent(6, 0); //- same thing as SetGUIRequestMode(GUIMODE_ML_MENU).
msleep(100);
menu_redraw();
/*if (MENU_MODE)
{
msleep(100);
menu_redraw();
msleep(100);
menu_redraw();
}*/
}
void close_canon_menu()
{
if (recording) return;
if (sensor_cleaning) return;
//- if (gui_state == GUISTATE_MENUDISP) return;
//- SetGUIRequestMode(0); need to find equivilant thing ot pass to NotifyGUIEvent.
msleep(200);
}
static void menu_open()
{
if (menu_shown) return;
show_only_selected = 0;
submenu_mode = 0;
menu_help_active = 0;
keyrepeat = 0;
menu_shown = 1;
piggyback_canon_menu();
//- canon_gui_disable_front_buffer(0);
//if (lv && EXT_MONITOR_CONNECTED) clrscr();
menu_redraw();
}
static void menu_close()
{
if (!menu_shown) return;
menu_shown = false;
//- update_disp_mode_bits_from_params();
//- canon_gui_enable_front_buffer(0);
//- lens_focus_stop();
show_only_selected = 0;
if (!PLAY_MODE) { redraw(); }
else draw_livev_for_playback();
close_canon_menu();
}
int arrow_keys_shortcuts_active()
{
return 0;
}
int should_draw_zoom_overlay()
{
return 0;
}
void fake_simple_button()
{
}
int config_autosave = 0;
static void
menu_task( void* unused )
{
select_menu_by_icon(menu_first_by_icon);
while(1)
{
int menu_or_shortcut_menu_shown = (menu_shown || arrow_keys_shortcuts_active());
int dt = (menu_or_shortcut_menu_shown && keyrepeat) ? COERCE(100 + keyrep_countdown*5, 20, 100) : should_draw_zoom_overlay() && show_only_selected ? 2000 : 500;
int rc = take_semaphore( gui_sem, dt );
if( rc != 0 )
{
if (keyrepeat && menu_or_shortcut_menu_shown)
{
keyrep_countdown--;
if (keyrep_countdown <= 0 && keyrep_ack) { keyrep_ack = 0; fake_simple_button(keyrepeat); }
continue;
}
// We woke up after 1 second
if( !menu_shown )
{
extern int config_autosave;
if (config_autosave && config_dirty && !recording)
{
//- save_config(0);
config_dirty = 0;
}
continue;
}
if ((!menu_help_active && !show_only_selected) || menu_damage) {
menu_redraw();
}
if (sensor_cleaning && menu_shown)
menu_close();
/* if (gui_state == GUISTATE_MENUDISP && menu_shown)
menu_close();
*/
continue;
}
if( menu_shown )
{
menu_close();
continue;
}
//if (recording && !lv) continue;
// Set this flag a bit earlier in order to pause LiveView tasks.
// Otherwise, high priority tasks such as focus peaking might delay the menu a bit.
//~ menu_shown = true;
// ML menu needs to piggyback on Canon menu, in order to receive wheel events
//~ piggyback_canon_menu();
//~ fake_simple_button(BGMT_PICSTYLE);
menu_open();
}
}
int is_menu_active(char* name)
{
if (!menu_shown) return 0;
if (menu_help_active) return 0;
struct menu * menu = menus;
for( ; menu ; menu = menu->next )
if( menu->selected )
break;
return !strcmp(menu->name, name);
}
static void select_menu_by_icon(int icon)
{
take_semaphore(menu_sem, 0);
struct menu * menu = menus;
for( ; menu ; menu = menu->next )
{
if (menu->icon == icon) // found!
{
struct menu * menu = menus;
for( ; menu ; menu = menu->next )
menu->selected = menu->icon == icon;
break;
}
}
give_semaphore(menu_sem);
}
static void
menu_help_go_to_selected_entry(
struct menu * menu
)
{
if( !menu )
return;
take_semaphore(menu_sem, 0);
struct menu_entry * entry = menu->children;
for( ; entry ; entry = entry->next )
{
if( entry->selected )
break;
}
//- menu_help_go_to_label(entry->name);
give_semaphore(menu_sem);
}
static void menu_show_version(void)
{
bmp_printf(FONT_MED, 10, 410,
"5dplus version : 1.0 (Alpha)\n"
"Mercurial changeset : ed9956756d8d\n"
"Built on 6/15/2012 by Coutts.");
}
void create_menu_tasks()
{
CreateTask("menu_redraw_task", 0x1d, 0x4000, menu_redraw_task, 0);
CreateTask("menu_task", 0x1d, 0x1000, menu_task, 0);
}