#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 20u #define CONTAINER_LEFTTOP_Y 15u #define CONTAINER_RECT_WIDTH 282u #define CONTAINER_MAX_LINES 13u #define WINDOW_WIDTH 320u #define WINDOW_HEIGHT 240u #define FONT_HEIGHT 15u 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 > cnt ) top = 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 ) { void **data = (void**)datain; atp_text_t title = *(atp_text_t*)data[0]; uint8_t *title_offset = *(uint8_t*)data[1]; for( int i=0; i < idx; ++i ) title += title_offset[i]; config->text = title; } 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_callerdata_t data, atp_itemprovider_t provider ) { 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)) 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 == NULL ) break; title_offset[i] = next - cursor; ++title_len; cursor = next; } // 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 ) { *res = config.value; return ATP_SUCCESS; } else { *res = result; return ATP_INVALID_VALUE; } } else { return ATP_NO_ACTION; } } else if( key & (KEY_X | KEY_Y | KEY_SELECT | KEY_START) ) { atp_pageopt_t opt = handler( data, key&KEY_X, key&KEY_Y, key&KEY_START, key&KEY_SELECT ); switch( opt ) { case ATP_PAGE_REFRESH: if( idx_top < title_len ) { void *title_data[2] = {(void*)title, (void*)title_offset}; container_paint( title_paint, 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 ); 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 >= cnt ) sel = 0; else if( sel < 0 ) sel = 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 ) {// 重新绘制全部 if( idx_top < title_len ) { void *title_data[2] = {(void*)title, (void*)title_offset}; container_paint( title_paint, title_data, title_len-idx_top, idx_top ); draw_options( title_len - idx_top, 0, cnt, sel, provider, data ); } else draw_options( 0, idx_top - title_len, cnt, sel, provider, data ); } 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; }