Wheel acceleration for e200. A general acceleration interface intended for use on any scroll target and by any code. A general interface to obtain data associated with most recently dequeued button presses and actions. Use #define HAVE_SCROLLWHEEL and set appropriate constants, values in the scroller driver that feel right.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13959 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2007-07-22 21:02:24 +00:00
parent 3213d4a0f5
commit 873e0fd1ef
11 changed files with 168 additions and 30 deletions

View file

@ -31,6 +31,7 @@
static int last_button = BUTTON_NONE|BUTTON_REL; /* allow the ipod wheel to
work on startup */
static intptr_t last_data = 0;
static int last_action = ACTION_NONE;
static bool repeated = false;
@ -113,6 +114,7 @@ static int get_action_worker(int context, int timeout,
else
button = button_get_w_tmo(timeout);
/* Data from sys events can be pulled with button_get_data */
if (button == BUTTON_NONE || button&SYS_EVENT)
{
return button;
@ -201,6 +203,7 @@ static int get_action_worker(int context, int timeout,
last_button = button;
last_action = ret;
last_data = button_get_data();
last_action_tick = current_tick;
return ret;
}
@ -230,6 +233,11 @@ bool is_keys_locked(void)
}
#endif
intptr_t get_action_data(void)
{
return last_data;
}
int get_action_statuscode(int *button)
{
int ret = 0;

View file

@ -253,6 +253,8 @@ bool is_keys_locked(void);
#define ACTION_REPEAT 0x2 /* action was repeated (NOT button) */
int get_action_statuscode(int *button);
/* returns the data value associated with the last action that is not
BUTTON_NONE or flagged with SYS_EVENT */
intptr_t get_action_data(void);
#endif
#endif /* __ACTION_H__ */

View file

@ -887,15 +887,22 @@ static void gui_synclist_scroll_left(struct gui_synclist * lists)
}
#endif /* HAVE_LCD_BITMAP */
extern intptr_t get_action_data(void);
unsigned gui_synclist_do_button(struct gui_synclist * lists,
unsigned button,enum list_wrap wrap)
{
#ifdef HAVE_LCD_BITMAP
static bool scrolling_left = false;
#endif
int i;
#ifdef HAVE_SCROLLWHEEL
int next_item_modifier = button_apply_acceleration(get_action_data(),
WHEEL_ACCELERATION_FACTOR);
#else
static int next_item_modifier = 1;
static int last_accel_tick = 0;
int i;
if (global_settings.list_accel_start_delay)
{
@ -919,6 +926,7 @@ unsigned gui_synclist_do_button(struct gui_synclist * lists,
last_accel_tick = 0;
}
}
#endif
switch (wrap)
{
@ -953,8 +961,12 @@ unsigned gui_synclist_do_button(struct gui_synclist * lists,
case ACTION_STD_PREVREPEAT:
FOR_NB_SCREENS(i)
gui_list_select_at_offset(&(lists->gui_list[i]), -next_item_modifier);
#ifndef HAVE_SCROLLWHEEL
if (queue_count(&button_queue) < FRAMEDROP_TRIGGER)
#endif
{
gui_synclist_draw(lists);
}
yield();
return ACTION_STD_PREV;
@ -962,8 +974,12 @@ unsigned gui_synclist_do_button(struct gui_synclist * lists,
case ACTION_STD_NEXTREPEAT:
FOR_NB_SCREENS(i)
gui_list_select_at_offset(&(lists->gui_list[i]), next_item_modifier);
#ifndef HAVE_SCROLLWHEEL
if (queue_count(&button_queue) < FRAMEDROP_TRIGGER)
#endif
{
gui_synclist_draw(lists);
}
yield();
return ACTION_STD_NEXT;

View file

@ -10955,12 +10955,15 @@
desc: Delay before list starts accelerating
user:
<source>
e200: ""
*: "List Acceleration Start Delay"
</source>
<dest>
e200: ""
*: "List Acceleration Start Delay"
</dest>
<voice>
e200: ""
*: "List Acceleration Start Delay"
</voice>
</phrase>
@ -10969,12 +10972,15 @@
desc: list acceleration speed
user:
<source>
e200: ""
*: "List Acceleration Speed"
</source>
<dest>
e200: ""
*: "List Acceleration Speed"
</dest>
<voice>
e200: ""
*: "List Acceleration Speed"
</voice>
</phrase>

View file

@ -313,9 +313,11 @@ MENUITEM_SETTING(jump_scroll, &global_settings.jump_scroll, NULL);
MENUITEM_SETTING(jump_scroll_delay, &global_settings.jump_scroll_delay, NULL);
#endif
/* list acceleration */
#ifndef HAVE_SCROLLWHEEL
MENUITEM_SETTING(list_accel_start_delay,
&global_settings.list_accel_start_delay, NULL);
MENUITEM_SETTING(list_accel_wait, &global_settings.list_accel_wait, NULL);
#endif /* HAVE_SCROLLWHEEL */
#ifdef HAVE_LCD_BITMAP
int screenscroll_callback(int action,const struct menu_item_ex *this_item)
{
@ -350,7 +352,9 @@ MAKE_MENU(scroll_settings_menu, ID2P(LANG_SCROLL_MENU), 0, Icon_NOICON,
&offset_out_of_view, &screen_scroll_step,
#endif
&scroll_paginated,
#ifndef HAVE_SCROLLWHEEL
&list_accel_start_delay, &list_accel_wait
#endif
);
/* SCROLL MENU */
/***********************************/

View file

@ -731,8 +731,10 @@ struct user_settings
#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
int buttonlight_brightness;
#endif
#ifndef HAVE_SCROLLWHEEL
int list_accel_start_delay; /* ms before we start increaseing step size */
int list_accel_wait; /* ms between increases */
#endif
};
/** global variables **/

View file

@ -274,6 +274,7 @@ static void poweroff_idle_timer_formatter(char *buffer, int buffer_size,
snprintf(buffer, buffer_size, "%dm", poweroff_idle_timer_times[val]);
}
#ifndef HAVE_SCROLLWHEEL
static long listaccel_getlang(int value)
{
if (value == 0)
@ -289,6 +290,7 @@ static void listaccel_formatter(char *buffer, int buffer_size,
else
snprintf(buffer, buffer_size, "%d ms", 5*HZ*val);
}
#endif /* HAVE_SCROLLWHEEL */
#if CONFIG_CODEC == SWCODEC
static void crossfeed_format(char* buffer, int buffer_size, int value,
@ -1235,12 +1237,14 @@ const struct settings_list settings[] = {
"button light brightness",UNIT_INT, MIN_BRIGHTNESS_SETTING, MAX_BRIGHTNESS_SETTING, 1,
NULL, NULL, buttonlight_set_brightness),
#endif
#ifndef HAVE_SCROLLWHEEL
INT_SETTING(0, list_accel_start_delay, LANG_LISTACCEL_START_DELAY,
2, "list_accel_start_delay", UNIT_MS, 0, 10, 1,
listaccel_formatter, listaccel_getlang, NULL),
INT_SETTING(0, list_accel_wait, LANG_LISTACCEL_ACCEL_SPEED,
3, "list_accel_wait", UNIT_SEC, 1, 10, 1,
scanaccel_formatter, scanaccel_getlang, NULL),
#endif /* HAVE_SCROLLWHEEL */
};
const int nb_settings = sizeof(settings)/sizeof(*settings);

View file

@ -49,6 +49,7 @@ struct event_queue button_queue;
static long lastbtn; /* Last valid button status */
static long last_read; /* Last button status, for debouncing/filtering */
static intptr_t button_data; /* data value from last message dequeued */
#ifdef HAVE_LCD_BITMAP
static bool flipped; /* buttons can be flipped to match the LCD flip */
#endif
@ -309,7 +310,7 @@ long button_get(bool block)
if (current_tick - ev.tick > MAX_EVENT_AGE)
return BUTTON_NONE;
#endif
button_data = ev.data;
return ev.id;
}
@ -329,7 +330,17 @@ long button_get_w_tmo(int ticks)
#endif
queue_wait_w_tmo(&button_queue, &ev, ticks);
return (ev.id != SYS_TIMEOUT)? ev.id: BUTTON_NONE;
if (ev.id == SYS_TIMEOUT)
ev.id = BUTTON_NONE;
else
button_data = ev.data;
return ev.id;
}
intptr_t button_get_data(void)
{
return button_data;
}
void button_init(void)
@ -470,3 +481,33 @@ void button_clear_queue(void)
{
queue_clear(&button_queue);
}
#ifdef HAVE_SCROLLWHEEL
/**
* data:
* [31] Use acceleration
* [30:24] Message post count (skipped + 1) (1-127)
* [23:0] Velocity - clicks/uS - 0.24 fixed point
*
* factor:
* Wheel acceleration scaling factor - x.24 fixed point -
* no greater than what will not overflow 64 bits when multiplied
* by the driver's maximum velocity in (clicks/usec)^2 in 0.24
*/
int button_apply_acceleration(unsigned int data, unsigned int factor)
{
int delta = (data >> 24) & 0x7f;
if ((data & (1 << 31)) != 0)
{
unsigned int v = data & 0xffffff;
v = factor * (unsigned long long)v*v / 0xffffffffffffull;
if (v > 1)
delta *= v;
}
return delta;
}
#endif /* HAVE_SCROLLWHEEL */

View file

@ -20,6 +20,7 @@
#define _BUTTON_H_
#include <stdbool.h>
#include <inttypes.h>
#include "config.h"
#include "button-target.h"
@ -28,6 +29,7 @@ extern struct event_queue button_queue;
void button_init (void);
long button_get (bool block);
long button_get_w_tmo(int ticks);
intptr_t button_get_data(void);
int button_status(void);
void button_clear_queue(void);
#ifdef HAVE_LCD_BITMAP
@ -48,6 +50,10 @@ int wheel_status(void);
void wheel_send_events(bool send);
#endif
#ifdef HAVE_SCROLLWHEEL
int button_apply_acceleration(unsigned int data, unsigned int factor);
#endif
#define BUTTON_NONE 0x00000000
/* Button modifiers */

View file

@ -170,4 +170,10 @@
#define ICODE_ATTR_TREMOR_NOT_MDCT
#endif
/* define this if the unit uses a scrollwheel for navigation */
#define HAVE_SCROLLWHEEL
/* define wheel acceleration scaling factor */
/* Range for this target: 0xffffff*(0.0-16.000000894069724921567733381255) */
#define WHEEL_ACCELERATION_FACTOR (0xffffff*7)
#endif /* SIMULATOR */

View file

@ -23,19 +23,21 @@
#include "button.h"
#include "backlight.h"
#define WHEEL_REPEAT_INTERVAL 30
#define WHEEL_FAST_ON_INTERVAL 2
#define WHEEL_FAST_OFF_INTERVAL 6
#define WHEEL_REPEAT_INTERVAL 300000
#define WHEEL_FAST_ON_INTERVAL 20000
#define WHEEL_FAST_OFF_INTERVAL 60000
/* Clickwheel */
#ifndef BOOTLOADER
static unsigned int old_wheel_value = 0;
static unsigned int wheel_repeat = BUTTON_NONE;
static unsigned int wheel_click_count = 0;
static unsigned int wheel_delta = 0;
static int wheel_fast_mode = 0;
static unsigned long last_wheel_tick = 0;
static unsigned long last_wheel_post = 0;
static unsigned long next_backlight_on = 0;
static unsigned long last_wheel_usec = 0;
static unsigned long wheel_velocity = 0;
static long last_wheel_post = 0;
static long next_backlight_on = 0;
/* Buttons */
static bool hold_button = false;
static bool hold_button_old = false;
@ -64,8 +66,8 @@ void button_init_device(void)
GPIOH_INT_EN &= ~0xc0;
/* Get current tick before enabling button interrupts */
last_wheel_tick = current_tick;
last_wheel_post = current_tick;
last_wheel_usec = USEC_TIMER;
last_wheel_post = last_wheel_usec;
GPIOH_ENABLE |= 0xc0;
GPIOH_OUTPUT_EN &= ~0xc0;
@ -130,38 +132,67 @@ void clickwheel_int(void)
if (btn != BUTTON_NONE)
{
int repeat = 1;
int repeat = 1; /* assume repeat */
unsigned long usec = USEC_TIMER;
unsigned v = (usec - last_wheel_usec) & 0x7fffffff;
/* wheel velocity in 0.24 fixed point - clicks/uS */
/* velocity cap to 18 bits to allow up to x16 scaling */
v = (v < 0x40) ? 0xffffff / 0x40 : 0xffffff / v;
/* some velocity filtering to smooth things out */
wheel_velocity = (7*wheel_velocity + v) / 8;
if (btn != wheel_repeat)
{
/* direction reversals nullify all fast mode states */
wheel_repeat = btn;
repeat =
wheel_fast_mode =
wheel_velocity =
wheel_click_count = 0;
}
if (wheel_fast_mode)
if (wheel_fast_mode != 0)
{
if (TIME_AFTER(current_tick,
last_wheel_tick + WHEEL_FAST_OFF_INTERVAL))
/* fast OFF happens immediately when velocity drops below
threshold */
if (TIME_AFTER(usec,
last_wheel_usec + WHEEL_FAST_OFF_INTERVAL))
{
if (++wheel_click_count < 2)
btn = BUTTON_NONE;
/* moving out of fast mode */
wheel_fast_mode = 0;
/* reset velocity */
wheel_velocity = 0;
/* wheel_delta is always 1 in slow mode */
wheel_delta = 1;
}
}
else
{
if (repeat && TIME_BEFORE(current_tick,
last_wheel_tick + WHEEL_FAST_ON_INTERVAL))
wheel_fast_mode = 1;
/* fast ON gets filtered to avoid inadvertent jumps to fast mode */
if (repeat && wheel_velocity > 0xffffff/WHEEL_FAST_ON_INTERVAL)
{
/* moving into fast mode */
wheel_fast_mode = 1 << 31;
wheel_click_count = 0;
wheel_velocity = 0xffffff/WHEEL_FAST_OFF_INTERVAL;
}
else if (++wheel_click_count < 2)
{
btn = BUTTON_NONE;
}
if (TIME_AFTER(current_tick, next_backlight_on))
/* wheel_delta is always 1 in slow mode */
wheel_delta = 1;
}
if (TIME_AFTER(usec, next_backlight_on))
{
next_backlight_on = current_tick + HZ/4;
/* poke backlight to turn it on or maintain it no more often
than every 1/4 second*/
next_backlight_on = usec + 1000000/4;
backlight_on();
button_backlight_on();
}
@ -170,17 +201,29 @@ void clickwheel_int(void)
{
wheel_click_count = 0;
if (repeat && TIME_BEFORE(current_tick,
/* generate repeats if quick enough */
if (repeat && TIME_BEFORE(usec,
last_wheel_post + WHEEL_REPEAT_INTERVAL))
btn |= BUTTON_REPEAT;
last_wheel_post = current_tick;
last_wheel_post = usec;
if (queue_empty(&button_queue))
queue_post(&button_queue, btn, 0);
{
queue_post(&button_queue, btn, wheel_fast_mode |
(wheel_delta << 24) | wheel_velocity);
/* message posted - reset delta */
wheel_delta = 1;
}
else
{
/* skipped post - increment delta */
if (++wheel_delta > 0x7f)
wheel_delta = 0x7f;
}
}
last_wheel_tick = current_tick;
last_wheel_usec = usec;
}
}