summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiika Pekkarinen <miipekk@ihme.org>2007-03-11 10:52:36 +0000
committerMiika Pekkarinen <miipekk@ihme.org>2007-03-11 10:52:36 +0000
commit2eefb5acb847eeb2d10bac860d37c4cef00be67b (patch)
treebd08d1908d01e56c10a442c0b2fd8ef224fb982f
parent408dfd65ad61181b3612cb11574c9ff547e42d24 (diff)
downloadrockbox-2eefb5acb847eeb2d10bac860d37c4cef00be67b.zip
rockbox-2eefb5acb847eeb2d10bac860d37c4cef00be67b.tar.gz
rockbox-2eefb5acb847eeb2d10bac860d37c4cef00be67b.tar.bz2
rockbox-2eefb5acb847eeb2d10bac860d37c4cef00be67b.tar.xz
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.c239
-rw-r--r--apps/gui/list.h5
-rw-r--r--firmware/drivers/button.c36
-rw-r--r--firmware/export/kernel.h1
-rw-r--r--firmware/kernel.c21
5 files changed, 221 insertions, 81 deletions
diff --git a/apps/gui/list.c b/apps/gui/list.c
index 448c7da..f3a151c 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 f6e4a5d..26f5876 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 a1fd58c..7c5858a 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 cd6a810..13e353d 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 db7249f..c304e45 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;