diff options
author | Miika Pekkarinen <miipekk@ihme.org> | 2007-03-11 10:52:36 +0000 |
---|---|---|
committer | Miika Pekkarinen <miipekk@ihme.org> | 2007-03-11 10:52:36 +0000 |
commit | 2eefb5acb847eeb2d10bac860d37c4cef00be67b (patch) | |
tree | bd08d1908d01e56c10a442c0b2fd8ef224fb982f | |
parent | 408dfd65ad61181b3612cb11574c9ff547e42d24 (diff) |
Optimized the gui list code performance, including automatic frame dropping and cpu boosting when button events are getting queued. Improved scrollwheel acceleration code is needed to notice a real change.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12721 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/gui/list.c | 239 | ||||
-rw-r--r-- | apps/gui/list.h | 5 | ||||
-rw-r--r-- | firmware/drivers/button.c | 36 | ||||
-rw-r--r-- | firmware/export/kernel.h | 1 | ||||
-rw-r--r-- | firmware/kernel.c | 21 |
5 files changed, 221 insertions, 81 deletions
diff --git a/apps/gui/list.c b/apps/gui/list.c index 448c7da7f0..f3a151ccc6 100644 --- a/apps/gui/list.c +++ b/apps/gui/list.c @@ -40,11 +40,17 @@ #define SCROLL_LIMIT 2 #endif +/* The minimum number of pending button events in queue before starting + * to limit list drawing interval. + */ +#define FRAMEDROP_TRIGGER 6 + #ifdef HAVE_LCD_BITMAP static int offset_step = 16; /* pixels per screen scroll step */ /* should lines scroll out of the screen */ static bool offset_out_of_view = false; #endif +static struct gui_list* last_list_displayed[NB_SCREENS]; #define SHOW_LIST_TITLE ((gui_list->title != NULL) && \ (gui_list->display->nb_lines > 1)) @@ -86,6 +92,9 @@ static void gui_list_init(struct gui_list * gui_list, gui_list->title = NULL; gui_list->title_width = 0; gui_list->title_icon = NOICON; + + gui_list->last_displayed_selected_item = -1 ; + gui_list->last_displayed_start_item = -1 ; } /* @@ -162,7 +171,7 @@ static void gui_list_put_selection_in_screen(struct gui_list * gui_list, if(put_from_end) { int list_end = gui_list->selected_item + SCROLL_LIMIT; - + if(list_end-1 == gui_list->nb_items) list_end--; if(list_end > gui_list->nb_items) @@ -186,7 +195,7 @@ static int gui_list_get_item_offset(struct gui_list * gui_list, int item_width, { struct screen * display=gui_list->display; int item_offset; - + if (offset_out_of_view) { item_offset = gui_list->offset_position; @@ -201,7 +210,7 @@ static int gui_list_get_item_offset(struct gui_list * gui_list, int item_width, else { /* if text got out of view */ - if (gui_list->offset_position > + if (gui_list->offset_position > item_width - (display->width - text_pos)) item_offset = item_width - (display->width - text_pos); else @@ -217,7 +226,7 @@ static int gui_list_get_item_offset(struct gui_list * gui_list, int item_width, * Draws the list on the attached screen * - gui_list : the list structure */ -static void gui_list_draw(struct gui_list * gui_list) +static void gui_list_draw_smart(struct gui_list *gui_list) { struct screen * display=gui_list->display; int cursor_pos = 0; @@ -231,15 +240,51 @@ static void gui_list_draw(struct gui_list * gui_list) int item_offset; int old_margin = display->getxmargin(); #endif + int start, end; + bool partial_draw = false; - gui_textarea_clear(display); + /* Speed up UI by drawing the changed contents only. */ + if (gui_list == last_list_displayed[gui_list->display->screen_type] + && gui_list->last_displayed_start_item == gui_list->start_item + && gui_list->selected_size == 1) + { + partial_draw = true; + } - /* position and draw the list title & icon */ if (SHOW_LIST_TITLE) - { - i = 1; lines = display->nb_lines - 1; + else + lines = display->nb_lines; + if (partial_draw) + { + end = gui_list->last_displayed_selected_item - gui_list->start_item; + i = gui_list->selected_item - gui_list->start_item; + if (i < end ) + { + start = i; + end++; + } + else + { + start = end; + end = i + 1; + } + } + else + { + gui_textarea_clear(display); + start = 0; + end = display->nb_lines; + gui_list->last_displayed_start_item = gui_list->start_item; + last_list_displayed[gui_list->display->screen_type] = gui_list; + } + + gui_list->last_displayed_selected_item = gui_list->selected_item; + + /* position and draw the list title & icon */ + if (SHOW_LIST_TITLE && !partial_draw) + { if (gui_list->title_icon != NOICON && draw_icons) { screen_put_iconxy(display, 0, 0, gui_list->title_icon); @@ -266,11 +311,6 @@ static void gui_list_draw(struct gui_list * gui_list) display->puts_scroll(text_pos, 0, gui_list->title); #endif } - else - { - i = 0; - lines = display->nb_lines; - } /* Adjust the position of icon, cursor, text for the list */ #ifdef HAVE_LCD_BITMAP @@ -280,7 +320,7 @@ static void gui_list_draw(struct gui_list * gui_list) draw_scrollbar = (global_settings.scrollbar && lines < gui_list->nb_items); - + draw_cursor = !global_settings.invert_cursor; text_pos = 0; /* here it's in pixels */ if(draw_scrollbar || SHOW_LIST_TITLE) /* indent if there's @@ -309,12 +349,19 @@ static void gui_list_draw(struct gui_list * gui_list) screen_set_xmargin(display, text_pos); /* margin for list */ #endif - while (i < display->nb_lines) + if (SHOW_LIST_TITLE) + { + start++; + if (end < display->nb_lines) + end++; + } + + for (i = start; i < end; i++) { unsigned char *s; char entry_buffer[MAX_PATH]; unsigned char *entry_name; - int current_item = gui_list->start_item + + int current_item = gui_list->start_item + (SHOW_LIST_TITLE ? i-1 : i); /* When there are less items to display than the @@ -338,26 +385,33 @@ static void gui_list_draw(struct gui_list * gui_list) {/* The selected item must be displayed scrolling */ #ifdef HAVE_LCD_BITMAP if (global_settings.invert_cursor)/* Display inverted-line-style*/ + { /* if text got out of view */ if (item_offset > item_width - (display->width - text_pos)) + { /* don't scroll */ display->puts_style_offset(0, i, entry_name, STYLE_INVERT,item_offset); + } else + { display->puts_scroll_style_offset(0, i, entry_name, STYLE_INVERT, item_offset); - + } + } else /* if (!global_settings.invert_cursor) */ + { if (item_offset > item_width - (display->width - text_pos)) display->puts_offset(0, i, entry_name,item_offset); - else - display->puts_scroll_offset(0, i, entry_name,item_offset); + else + display->puts_scroll_offset(0, i, entry_name,item_offset); + } #else - display->puts_scroll(text_pos, i, entry_name); + display->puts_scroll(text_pos, i, entry_name); #endif - if(draw_cursor) + if (draw_cursor) screen_put_cursorxy(display, cursor_pos, i, true); } else @@ -389,10 +443,9 @@ static void gui_list_draw(struct gui_list * gui_list) if(icon) screen_put_iconxy(display, icon_pos, i, icon); } - i++; } - -#ifdef HAVE_LCD_BITMAP + +#ifdef HAVE_LCD_BITMAP /* Draw the scrollbar if needed*/ if(draw_scrollbar) { @@ -414,6 +467,15 @@ static void gui_list_draw(struct gui_list * gui_list) } /* + * Force a full screen update. + */ +static void gui_list_draw(struct gui_list *gui_list) +{ + last_list_displayed[gui_list->display->screen_type] = NULL; + return gui_list_draw_smart(gui_list); +} + +/* * Selects an item in the list * - gui_list : the list structure * - item_number : the number of the item which will be selected @@ -426,6 +488,64 @@ static void gui_list_select_item(struct gui_list * gui_list, int item_number) gui_list_put_selection_in_screen(gui_list, false); } +static void scroll_down(struct gui_list *gui_list, bool paginate) +{ + int nb_lines = gui_list->display->nb_lines; + if (SHOW_LIST_TITLE) + nb_lines--; + int item_pos = gui_list->selected_item - gui_list->start_item; + int end_item = gui_list->start_item + nb_lines; + + if (paginate) + { + /* When we reach the bottom of the list + * we jump to a new page if there are more items*/ + if ((item_pos > nb_lines-gui_list->selected_size) && + (end_item < gui_list->nb_items) ) + { + gui_list->start_item = gui_list->selected_item; + if ( gui_list->start_item > gui_list->nb_items-nb_lines ) + gui_list->start_item = gui_list->nb_items-nb_lines; + } + } + else + { + /* we start scrolling vertically when reaching the line + * (nb_lines-SCROLL_LIMIT) + * and when we are not in the last part of the list*/ + if( (item_pos > nb_lines-SCROLL_LIMIT) && + (end_item < gui_list->nb_items) ) + { + gui_list->start_item+=gui_list->selected_size; + } + } +} + +static void scroll_up(struct gui_list *gui_list, bool paginate) +{ + int item_pos = gui_list->selected_item - gui_list->start_item; + int nb_lines = gui_list->display->nb_lines; + + if (paginate) + { + /* When we reach the top of the list + * we jump to a new page if there are more items*/ + if( item_pos < 0) + gui_list->start_item = gui_list->selected_item - nb_lines + + gui_list->selected_size; + } + else + { + /* we start scrolling vertically when reaching the line + * (nb_lines-SCROLL_LIMIT) + * and when we are not in the last part of the list*/ + if( item_pos < SCROLL_LIMIT-1) + gui_list->start_item-=gui_list->selected_size; + } + if( gui_list->start_item < 0 ) + gui_list->start_item = 0; +} + /* * Selects the next item in the list * (Item 0 gets selected if the end of the list is reached) @@ -444,33 +564,7 @@ static void gui_list_select_next(struct gui_list * gui_list) else { gui_list->selected_item+=gui_list->selected_size; - int nb_lines = gui_list->display->nb_lines; - if (SHOW_LIST_TITLE) - nb_lines--; - int item_pos = gui_list->selected_item - gui_list->start_item; - int end_item = gui_list->start_item + nb_lines; - - if (global_settings.scroll_paginated) - { - /* When we reach the bottom of the list - * we jump to a new page if there are more items*/ - if( (item_pos > nb_lines-gui_list->selected_size) && - (end_item < gui_list->nb_items) ) - { - gui_list->start_item = gui_list->selected_item; - if ( gui_list->start_item > gui_list->nb_items-nb_lines ) - gui_list->start_item = gui_list->nb_items-nb_lines; - } - } - else - { - /* we start scrolling vertically when reaching the line - * (nb_lines-SCROLL_LIMIT) - * and when we are not in the last part of the list*/ - if( (item_pos > nb_lines-SCROLL_LIMIT) && - (end_item < gui_list->nb_items) ) - gui_list->start_item+=gui_list->selected_size; - } + scroll_down(gui_list, global_settings.scroll_paginated); } } @@ -481,7 +575,7 @@ static void gui_list_select_next(struct gui_list * gui_list) */ static void gui_list_select_previous(struct gui_list * gui_list) { - int nb_lines = gui_list->display->nb_lines; + int nb_lines = gui_list->display->nb_lines; if (SHOW_LIST_TITLE) nb_lines--; if( gui_list->selected_item-gui_list->selected_size < 0 ) @@ -499,27 +593,8 @@ static void gui_list_select_previous(struct gui_list * gui_list) } else { - int item_pos; - gui_list->selected_item-=gui_list->selected_size; - item_pos = gui_list->selected_item - gui_list->start_item; - if (global_settings.scroll_paginated) - { - /* When we reach the top of the list - * we jump to a new page if there are more items*/ - if( item_pos < 0) - gui_list->start_item = gui_list->selected_item - nb_lines + - gui_list->selected_size; - } - else - { - /* we start scrolling vertically when reaching the line - * (nb_lines-SCROLL_LIMIT) - * and when we are not in the last part of the list*/ - if( item_pos < SCROLL_LIMIT-1) - gui_list->start_item-=gui_list->selected_size; - } - if( gui_list->start_item < 0 ) - gui_list->start_item = 0; + gui_list->selected_item -= gui_list->selected_size; + scroll_up(gui_list, global_settings.scroll_paginated); } } @@ -623,9 +698,9 @@ static void gui_list_del_item(struct gui_list * gui_list) */ static void gui_list_scroll_right(struct gui_list * gui_list) { - /* FIXME: This is a fake right boundry limiter. there should be some - * callback function to find the longest item on the list in pixels, - * to stop the list from scrolling past that point */ + /* FIXME: This is a fake right boundry limiter. there should be some + * callback function to find the longest item on the list in pixels, + * to stop the list from scrolling past that point */ gui_list->offset_position+=offset_step; if (gui_list->offset_position > 1000) gui_list->offset_position = 1000; @@ -844,20 +919,26 @@ unsigned gui_synclist_do_button(struct gui_synclist * lists, else gui_synclist_limit_scroll(lists, false); break; }; - + switch(button) { case ACTION_STD_PREV: case ACTION_STD_PREVREPEAT: gui_synclist_select_previous(lists); - gui_synclist_draw(lists); +#ifndef SIMULATOR + if (queue_count(&button_queue) < FRAMEDROP_TRIGGER) +#endif + gui_synclist_draw(lists); yield(); return ACTION_STD_PREV; case ACTION_STD_NEXT: case ACTION_STD_NEXTREPEAT: gui_synclist_select_next(lists); - gui_synclist_draw(lists); +#ifndef SIMULATOR + if (queue_count(&button_queue) < FRAMEDROP_TRIGGER) +#endif + gui_synclist_draw(lists); yield(); return ACTION_STD_NEXT; diff --git a/apps/gui/list.h b/apps/gui/list.h index f6e4a5d615..26f58761b6 100644 --- a/apps/gui/list.h +++ b/apps/gui/list.h @@ -91,6 +91,10 @@ struct gui_list int selected_size; /* The data that will be passed to the callback function YOU implement */ void * data; + /* These are used to calculate how much of the screen content we need + to redraw. */ + int last_displayed_selected_item; + int last_displayed_start_item; /* The optional title, set to NULL for none */ char *title; /* Cache the width of the title string in pixels/characters */ @@ -162,6 +166,7 @@ extern void gui_list_screen_scroll_out_of_view(bool enable); struct gui_synclist { struct gui_list gui_list[NB_SCREENS]; + struct gui_list *last_displayed[NB_SCREENS]; }; extern void gui_synclist_init( diff --git a/firmware/drivers/button.c b/firmware/drivers/button.c index a1fd58c447..7c5858a1e3 100644 --- a/firmware/drivers/button.c +++ b/firmware/drivers/button.c @@ -26,6 +26,7 @@ #include "system.h" #include "button.h" #include "kernel.h" +#include "thread.h" #include "backlight.h" #include "serial.h" #include "power.h" @@ -255,21 +256,52 @@ static void button_tick(void) lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT); } +void button_boost(bool state) +{ + static bool boosted = false; + + if (state && !boosted) + { + cpu_boost(true); + boosted = true; + } + else if (!state && boosted) + { + cpu_boost(false); + boosted = false; + } +} + long button_get(bool block) { struct event ev; - - if ( block || !queue_empty(&button_queue) ) + int pending_count = queue_count(&button_queue); + + /* Control the CPU boost trying to keep queue empty. */ + if (pending_count == 0) + button_boost(false); + else if (pending_count > 2) + button_boost(true); + + if ( block || pending_count ) { queue_wait(&button_queue, &ev); return ev.id; } + return BUTTON_NONE; } long button_get_w_tmo(int ticks) { struct event ev; + + /* Be sure to keep boosted state. */ + if (!queue_empty(&button_queue)) + return button_get(true); + + button_boost(false); + queue_wait_w_tmo(&button_queue, &ev, ticks); return (ev.id != SYS_TIMEOUT)? ev.id: BUTTON_NONE; } diff --git a/firmware/export/kernel.h b/firmware/export/kernel.h index cd6a810bad..13e353d736 100644 --- a/firmware/export/kernel.h +++ b/firmware/export/kernel.h @@ -123,6 +123,7 @@ extern bool queue_in_queue_send(struct event_queue *q); extern bool queue_empty(const struct event_queue* q); extern void queue_clear(struct event_queue* q); extern void queue_remove_from_head(struct event_queue *q, long id); +extern int queue_count(const struct event_queue *q); extern int queue_broadcast(long id, intptr_t data); extern void mutex_init(struct mutex *m); diff --git a/firmware/kernel.c b/firmware/kernel.c index db7249fdee..c304e455c2 100644 --- a/firmware/kernel.c +++ b/firmware/kernel.c @@ -374,6 +374,27 @@ void queue_remove_from_head(struct event_queue *q, long id) set_irq_level(oldlevel); } +/** + * The number of events waiting in the queue. + * + * @param struct of event_queue + * @return number of events in the queue + */ +int queue_count(const struct event_queue *q) +{ + int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL); + int result = 0; + + if (q->read <= q->write) + result = q->write - q->read; + else + result = QUEUE_LENGTH - (q->read - q->write); + + set_irq_level(oldlevel); + + return result; +} + int queue_broadcast(long id, intptr_t data) { int i; |