mirror of
https://gitee.com/anod/open_agb_firm.git
synced 2025-05-06 22:04:10 +08:00
349 lines
11 KiB
C
349 lines
11 KiB
C
#include "arm11/atp.h"
|
|
#include "arm11/acf.h"
|
|
#include "arm11/console.h"
|
|
#include "arm11/drivers/hid.h"
|
|
#include "drivers/gfx.h"
|
|
|
|
#define TITLE_MAX 8
|
|
#define TIPS_MAX 64
|
|
static char ta[TIPS_MAX] = {'\0'};
|
|
static char tb[TIPS_MAX] = {'\0'};
|
|
|
|
//---------------------------------------------------
|
|
// basic print helper
|
|
|
|
static void set_screen_color( acf_callerdata_t data, acf_position_t tx, acf_position_t ty, acf_color_t b )
|
|
{
|
|
if( b == 0 ) return;
|
|
|
|
int *draw_data = data;
|
|
u16 *frame = consoleGet()->frameBuffer;
|
|
|
|
int x = tx + draw_data[0];
|
|
int y = ty + draw_data[1];
|
|
if( 0 <= x && x < draw_data[2] && 0 <= y && y < draw_data[3] )
|
|
{
|
|
frame[ x*draw_data[3] + (draw_data[3]-1-y) ] = (u16)draw_data[4];
|
|
}
|
|
}
|
|
|
|
const char *acf_put_text(int x, int y, int width, int height, int maxwidth, u8 color, u8 placement, const char* text)
|
|
{
|
|
uint8_t draw_data[sizeof(int)*5];
|
|
|
|
int *draw_data_int = (int*)&draw_data;
|
|
draw_data_int[0] = x;
|
|
draw_data_int[1] = y;
|
|
draw_data_int[2] = width;
|
|
draw_data_int[3] = height;
|
|
draw_data_int[4] = (int)consoleGetRGB565Color(color);
|
|
|
|
const char *retval = text;
|
|
acf_rectedge_t realwid;
|
|
acf_canvas_t canvas = acf_get_canvas(maxwidth, text, &realwid, NULL, &retval);
|
|
if( !canvas ) return retval;
|
|
|
|
if( placement == ATP_PLACEMENT_RIGHT ) draw_data_int[0] += maxwidth - realwid;
|
|
else if( placement == ATP_PLACEMENT_CENTER ) draw_data_int[0] += (maxwidth-realwid) >> 1;
|
|
acf_recycle( acf_use_canvas(canvas, 1, set_screen_color, draw_data) );
|
|
return retval;
|
|
}
|
|
|
|
//---------------------------------------------------
|
|
|
|
#define CONTAINER_LEFTTOP_X 20
|
|
#define CONTAINER_LEFTTOP_Y 15
|
|
#define CONTAINER_RECT_WIDTH 282
|
|
#define CONTAINER_MAX_LINES 13
|
|
#define WINDOW_WIDTH 320
|
|
#define WINDOW_HEIGHT 240
|
|
|
|
#define FONT_HEIGHT 15
|
|
|
|
static void screen_clean()
|
|
{
|
|
memset(consoleGet()->frameBuffer, 0, WINDOW_WIDTH*WINDOW_HEIGHT*sizeof(uint16_t));
|
|
if( ta[0] ) acf_put_text( 5, 215, WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_WIDTH-10, ATP_COLOR_BLUE, ATP_PLACEMENT_LEFT, ta );
|
|
if( tb[0] ) acf_put_text( 5, 215, WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_WIDTH-10, ATP_COLOR_BLUE, ATP_PLACEMENT_RIGHT, tb );
|
|
}
|
|
|
|
// wait key pressed, if power key is pressed, return 0
|
|
static uint32_t waitKey()
|
|
{
|
|
uint32_t ek, down;
|
|
do{
|
|
GFX_waitForVBlank0();
|
|
hidScanInput();
|
|
ek = hidGetExtraKeys(0);
|
|
if( ek & (KEY_POWER_HELD | KEY_POWER) )
|
|
return 0;
|
|
|
|
down = hidKeysDown();
|
|
}while ( down == 0 );
|
|
|
|
return down;
|
|
}
|
|
|
|
#define easy_put(text, align, color, row) acf_put_text( \
|
|
CONTAINER_LEFTTOP_X, \
|
|
CONTAINER_LEFTTOP_Y+FONT_HEIGHT*(row), \
|
|
WINDOW_WIDTH, \
|
|
WINDOW_HEIGHT, \
|
|
CONTAINER_RECT_WIDTH, \
|
|
(color) > ATP_COLOR_WHITE ? ATP_COLOR_WHITE : (color), \
|
|
(align) > ATP_PLACEMENT_CENTER ? ATP_PLACEMENT_CENTER : (align), \
|
|
(text) \
|
|
)
|
|
|
|
static void paint_one_line( atp_lineprovider_t provider, acf_callerdata_t data, int idx, int row )
|
|
{
|
|
atp_linecfg_t config;
|
|
config.text_color = ATP_COLOR_WHITE;
|
|
config.text_align = ATP_PLACEMENT_LEFT;
|
|
atp_error_t err = provider( data, idx, &config );
|
|
if( !err )
|
|
{
|
|
easy_put( config.text, config.text_align, config.text_color, row );
|
|
}
|
|
}
|
|
|
|
static void container_paint( atp_lineprovider_t provider, atp_callerdata_t data, atp_counter_t nlines, int top )
|
|
{
|
|
int end = top + CONTAINER_MAX_LINES;
|
|
if( end > (int)nlines ) end = nlines;
|
|
for( int n = top; n < end; ++n )
|
|
{
|
|
paint_one_line( provider, data, n, n-top );
|
|
}
|
|
}
|
|
|
|
atp_error_t atp_show( atp_counter_t cnt, atp_lineprovider_t provider, atp_callerdata_t data )
|
|
{
|
|
int idx_top = 0;
|
|
screen_clean();
|
|
container_paint( provider, data, cnt, idx_top );
|
|
|
|
while( 1 )
|
|
{
|
|
int top = idx_top;
|
|
u32 kDown = waitKey();
|
|
|
|
if( kDown == 0 ) return ATP_POWER_OFF;
|
|
|
|
if( kDown == KEY_B || kDown == KEY_A ) return ATP_SUCCESS;
|
|
else if( cnt > CONTAINER_MAX_LINES )
|
|
{ // may scroll
|
|
if( kDown == KEY_UP || kDown == KEY_DUP )
|
|
{
|
|
top = idx_top - 1;
|
|
}
|
|
else if( kDown == KEY_DOWN || kDown == KEY_DDOWN )
|
|
{
|
|
top = idx_top + 1;
|
|
}
|
|
|
|
if( top+CONTAINER_MAX_LINES > (int)cnt ) top = (int)cnt - CONTAINER_MAX_LINES;
|
|
else if( top < 0 ) top = 0;
|
|
}
|
|
|
|
if( top != idx_top )
|
|
{
|
|
idx_top = top;
|
|
screen_clean();
|
|
container_paint(provider, data, cnt, idx_top);
|
|
}
|
|
}
|
|
}
|
|
|
|
static atp_error_t title_paint( atp_callerdata_t datain, atp_counter_t idx, atp_linecfg_t *config )
|
|
{
|
|
uint8_t **data = datain;
|
|
atp_text_t title = (atp_text_t)data[0];
|
|
uint8_t *title_offset = data[1];
|
|
for( atp_counter_t i=0; i < idx; ++i )
|
|
title += title_offset[i];
|
|
config->text = title;
|
|
return ATP_SUCCESS;
|
|
}
|
|
|
|
static void draw_one_option( int index, int selected, int row, atp_itemprovider_t provider, atp_callerdata_t data )
|
|
{
|
|
atp_itemcfg_t item;
|
|
item.value = -1;
|
|
item.text = item.extra_text = NULL;
|
|
item.extra_text_color = ATP_COLOR_RED;
|
|
item.text_color = ATP_COLOR_WHITE;
|
|
|
|
atp_error_t e = provider( data, index, &item );
|
|
if( e == ATP_SUCCESS )
|
|
{
|
|
if( item.extra_text ) easy_put( item.extra_text, ATP_PLACEMENT_RIGHT, item.extra_text_color, row );
|
|
easy_put( item.text, ATP_PLACEMENT_LEFT, index == selected ? ATP_COLOR_YELLOW : item.text_color, row );
|
|
}
|
|
}
|
|
|
|
static void draw_options( int start_row, int start_idx, int option_cnt, int selected_idx, atp_itemprovider_t provider, atp_callerdata_t data )
|
|
{
|
|
int max_draw = CONTAINER_MAX_LINES - start_row;
|
|
int end_idx = start_idx + max_draw;
|
|
if( end_idx > option_cnt ) end_idx = option_cnt;
|
|
|
|
// draw
|
|
for( int i=start_idx; i < end_idx; ++i )
|
|
{
|
|
draw_one_option( i, selected_idx, start_row+i-start_idx, provider, data );
|
|
}
|
|
}
|
|
|
|
#define SELECTED_ROW(top, len, sel) (((top) < (len)) ? ((len)-(top)+(sel)) : ((sel)+(len)-(top)))
|
|
#define REFRESH_PAGE { \
|
|
screen_clean(); \
|
|
if( idx_top < title_len ) \
|
|
{ \
|
|
uint8_t* title_data[2]; \
|
|
title_data[0] = (uint8_t*)title; \
|
|
title_data[1] = &title_offset[idx_top]; \
|
|
for( int i=0; i < idx_top; ++i ) \
|
|
{ \
|
|
title_data[0] += title_offset[i]; \
|
|
} \
|
|
container_paint( title_paint, (atp_callerdata_t)title_data, title_len-idx_top, idx_top ); \
|
|
draw_options( title_len - idx_top, 0, cnt, item_sel, provider, data ); \
|
|
} \
|
|
else draw_options( 0, idx_top - title_len, cnt, item_sel, provider, data ); \
|
|
}
|
|
|
|
atp_error_t atp_select( atp_text_t title, atp_counter_t cnt, atp_itemprovider_t provider, atp_keyhandler_t handler, atp_callerdata_t data, atp_itemval_t *res )
|
|
{
|
|
uint8_t title_offset[TITLE_MAX];
|
|
int idx_top = 0, title_len = 1;
|
|
int item_sel = 0;
|
|
atp_itemcfg_t config;
|
|
|
|
screen_clean();
|
|
|
|
// draw title
|
|
const char *cursor = title;
|
|
for( int i=0; i < TITLE_MAX; ++i )
|
|
{
|
|
const char *next = easy_put( cursor, ATP_PLACEMENT_LEFT, ATP_COLOR_WHITE, i );
|
|
if( *next == '\0' ) break;
|
|
|
|
title_offset[i] = next - cursor;
|
|
++title_len;
|
|
cursor = next;
|
|
}
|
|
for( int i=title_len; i < TITLE_MAX; ++i ) title_offset[i] = 0;
|
|
|
|
// draw item
|
|
draw_options( title_len, 0, cnt, item_sel, provider, data );
|
|
|
|
while( 1 )
|
|
{
|
|
uint32_t key = waitKey();
|
|
|
|
if( key == 0 ) return ATP_POWER_OFF;
|
|
|
|
if( key & (KEY_A | KEY_B) )
|
|
{
|
|
if( key & KEY_A )
|
|
{
|
|
atp_error_t result = provider(data, item_sel, &config);
|
|
if( ATP_SUCCESS == result )
|
|
{
|
|
if( res != NULL ) *res = config.value;
|
|
return ATP_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
if( res != NULL ) *res = result;
|
|
return ATP_INVALID_VALUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return ATP_NO_ACTION;
|
|
}
|
|
}
|
|
else if( key & (KEY_X | KEY_Y | KEY_SELECT | KEY_START) )
|
|
{
|
|
if( handler == NULL ) continue;
|
|
|
|
atp_pageopt_t opt = handler( data, key&KEY_X, key&KEY_Y, key&KEY_START, key&KEY_SELECT );
|
|
switch( opt )
|
|
{
|
|
case ATP_PAGE_REFRESH:
|
|
REFRESH_PAGE;
|
|
break;
|
|
case ATP_PAGE_UPDATE:
|
|
draw_one_option( item_sel, item_sel, SELECTED_ROW(idx_top, title_len, item_sel), provider, data );
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
else if( key & (KEY_DOWN | KEY_UP) )
|
|
{
|
|
int sel = item_sel + (key&KEY_DOWN ? 1 : -1);
|
|
|
|
// 更新item_sel
|
|
if( sel >= (int)cnt ) sel = 0;
|
|
else if( sel < 0 ) sel = (int)cnt-1;
|
|
|
|
// 更新idx_top
|
|
int top = title_len + sel;
|
|
atp_boolean_t full_refresh = 0;
|
|
if( top >= idx_top + CONTAINER_MAX_LINES )
|
|
{
|
|
idx_top = top - CONTAINER_MAX_LINES;
|
|
full_refresh = 1;
|
|
}
|
|
else if( top < idx_top )
|
|
{
|
|
idx_top = top;
|
|
full_refresh = 1;
|
|
}
|
|
|
|
// 更新view
|
|
if( full_refresh )
|
|
{// 重新绘制全部
|
|
REFRESH_PAGE;
|
|
}
|
|
else
|
|
{// 重新绘制item_sel和sel
|
|
draw_one_option( item_sel, sel, SELECTED_ROW(idx_top, title_len, item_sel), provider, data );
|
|
draw_one_option( sel, sel, SELECTED_ROW(idx_top, title_len, sel), provider, data);
|
|
}
|
|
item_sel = sel;
|
|
}
|
|
else if( key & (KEY_LEFT | KEY_RIGHT) )
|
|
{
|
|
if( title_len + cnt <= CONTAINER_MAX_LINES )
|
|
continue;
|
|
|
|
// 更新idx_top
|
|
// 更新item_sel
|
|
// 更新view
|
|
}
|
|
else if( key & (KEY_L | KEY_R) )
|
|
{
|
|
// 暂时没用
|
|
}
|
|
}
|
|
}
|
|
|
|
atp_error_t atp_tips( atp_text_t tipsA, atp_text_t tipsB )
|
|
{
|
|
if( tipsA != NULL )
|
|
{
|
|
strncpy( ta, tipsA, TIPS_MAX-1 );
|
|
ta[TIPS_MAX-1] = 0;
|
|
}
|
|
if( tipsB != NULL )
|
|
{
|
|
strncpy( tb, tipsB, TIPS_MAX-1 );
|
|
tb[TIPS_MAX-1] = 0;
|
|
}
|
|
return ATP_SUCCESS;
|
|
}
|