2007-07-28 08:12:05 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2007 by Michael Sevakis
|
|
|
|
*
|
|
|
|
* LCD scrolling driver and scheduler
|
|
|
|
*
|
|
|
|
* Much collected and combined from the various Rockbox LCD drivers.
|
|
|
|
*
|
2008-06-28 18:10:04 +00:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2007-07-28 08:12:05 +00:00
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
#include "config.h"
|
2010-08-12 13:38:25 +00:00
|
|
|
#include "gcc_extensions.h"
|
2007-07-28 08:12:05 +00:00
|
|
|
#include "cpu.h"
|
|
|
|
#include "kernel.h"
|
|
|
|
#include "thread.h"
|
|
|
|
#include "usb.h"
|
|
|
|
#include "lcd.h"
|
|
|
|
#include "font.h"
|
|
|
|
#ifdef HAVE_REMOTE_LCD
|
|
|
|
#include "lcd-remote.h"
|
|
|
|
#endif
|
|
|
|
#include "scroll_engine.h"
|
|
|
|
|
2012-01-09 03:26:03 +00:00
|
|
|
static const char scroll_tick_table[18] = {
|
|
|
|
/* Hz values [f(x)=100.8/(x+.048)]:
|
|
|
|
1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33, 49.2, 96.2 */
|
|
|
|
100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3, 2, 1
|
2007-07-28 08:12:05 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void scroll_thread(void);
|
|
|
|
static char scroll_stack[DEFAULT_STACK_SIZE*3];
|
|
|
|
static const char scroll_name[] = "scroll";
|
|
|
|
|
2008-04-20 10:24:15 +00:00
|
|
|
static struct scrollinfo lcd_scroll[LCD_SCROLLABLE_LINES];
|
2007-07-28 08:12:05 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_REMOTE_LCD
|
2010-07-31 13:41:06 +00:00
|
|
|
static struct scrollinfo lcd_remote_scroll[LCD_REMOTE_SCROLLABLE_LINES];
|
2011-02-14 11:27:45 +00:00
|
|
|
static struct event_queue scroll_queue SHAREDBSS_ATTR;
|
2007-07-28 08:12:05 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
struct scroll_screen_info lcd_scroll_info =
|
|
|
|
{
|
|
|
|
.scroll = lcd_scroll,
|
|
|
|
.lines = 0,
|
|
|
|
.ticks = 12,
|
|
|
|
.delay = HZ/2,
|
|
|
|
.bidir_limit = 50,
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
|
|
.step = 6,
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LCD_CHARCELLS
|
|
|
|
.jump_scroll_delay = HZ/4,
|
|
|
|
.jump_scroll = 0,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef HAVE_REMOTE_LCD
|
|
|
|
struct scroll_screen_info lcd_remote_scroll_info =
|
|
|
|
{
|
|
|
|
.scroll = lcd_remote_scroll,
|
|
|
|
.lines = 0,
|
|
|
|
.ticks = 12,
|
|
|
|
.delay = HZ/2,
|
|
|
|
.bidir_limit = 50,
|
|
|
|
.step = 6,
|
|
|
|
};
|
|
|
|
#endif /* HAVE_REMOTE_LCD */
|
|
|
|
|
|
|
|
void lcd_stop_scroll(void)
|
|
|
|
{
|
|
|
|
lcd_scroll_info.lines = 0;
|
|
|
|
}
|
|
|
|
|
2008-01-07 20:34:11 +00:00
|
|
|
/* Stop scrolling line y in the specified viewport, or all lines if y < 0 */
|
2009-09-04 16:04:02 +00:00
|
|
|
void lcd_scroll_stop_line(const struct viewport* current_vp, int y)
|
2008-01-07 20:34:11 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while (i < lcd_scroll_info.lines)
|
|
|
|
{
|
2008-07-02 13:02:57 +00:00
|
|
|
if ((lcd_scroll_info.scroll[i].vp == current_vp) &&
|
|
|
|
((y < 0) || (lcd_scroll_info.scroll[i].y == y)))
|
2008-01-07 20:34:11 +00:00
|
|
|
{
|
|
|
|
/* If i is not the last active line in the array, then move
|
|
|
|
the last item to position i */
|
|
|
|
if ((i + 1) != lcd_scroll_info.lines)
|
|
|
|
{
|
2009-09-04 16:04:02 +00:00
|
|
|
lcd_scroll_info.scroll[i] =
|
|
|
|
lcd_scroll_info.scroll[lcd_scroll_info.lines-1];
|
2008-01-07 20:34:11 +00:00
|
|
|
}
|
|
|
|
lcd_scroll_info.lines--;
|
|
|
|
|
2008-07-02 09:42:36 +00:00
|
|
|
/* A line can only appear once, so we're done,
|
|
|
|
* unless we are clearing the whole viewport */
|
|
|
|
if (y >= 0)
|
|
|
|
return ;
|
2008-01-07 20:34:11 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop all scrolling lines in the specified viewport */
|
2009-09-04 16:04:02 +00:00
|
|
|
void lcd_scroll_stop(const struct viewport* vp)
|
2008-01-07 20:34:11 +00:00
|
|
|
{
|
|
|
|
lcd_scroll_stop_line(vp, -1);
|
|
|
|
}
|
|
|
|
|
2007-07-28 08:12:05 +00:00
|
|
|
void lcd_scroll_speed(int speed)
|
|
|
|
{
|
|
|
|
lcd_scroll_info.ticks = scroll_tick_table[speed];
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(HAVE_LCD_BITMAP)
|
|
|
|
void lcd_scroll_step(int step)
|
|
|
|
{
|
|
|
|
lcd_scroll_info.step = step;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void lcd_scroll_delay(int ms)
|
|
|
|
{
|
|
|
|
lcd_scroll_info.delay = ms / (HZ / 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lcd_bidir_scroll(int percent)
|
|
|
|
{
|
|
|
|
lcd_scroll_info.bidir_limit = percent;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_LCD_CHARCELLS
|
|
|
|
void lcd_jump_scroll(int mode) /* 0=off, 1=once, ..., JUMP_SCROLL_ALWAYS */
|
|
|
|
{
|
|
|
|
lcd_scroll_info.jump_scroll = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lcd_jump_scroll_delay(int ms)
|
|
|
|
{
|
|
|
|
lcd_scroll_info.jump_scroll_delay = ms / (HZ / 10);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_REMOTE_LCD
|
|
|
|
void lcd_remote_stop_scroll(void)
|
|
|
|
{
|
|
|
|
lcd_remote_scroll_info.lines = 0;
|
|
|
|
}
|
|
|
|
|
2008-01-07 20:34:11 +00:00
|
|
|
/* Stop scrolling line y in the specified viewport, or all lines if y < 0 */
|
2009-09-04 16:04:02 +00:00
|
|
|
void lcd_remote_scroll_stop_line(const struct viewport* current_vp, int y)
|
2008-01-07 20:34:11 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while (i < lcd_remote_scroll_info.lines)
|
|
|
|
{
|
2008-07-02 13:02:57 +00:00
|
|
|
if ((lcd_remote_scroll_info.scroll[i].vp == current_vp) &&
|
|
|
|
((y < 0) || (lcd_remote_scroll_info.scroll[i].y == y)))
|
2008-01-07 20:34:11 +00:00
|
|
|
{
|
|
|
|
/* If i is not the last active line in the array, then move
|
|
|
|
the last item to position i */
|
|
|
|
if ((i + 1) != lcd_remote_scroll_info.lines)
|
|
|
|
{
|
2009-09-04 16:04:02 +00:00
|
|
|
lcd_remote_scroll_info.scroll[i] =
|
|
|
|
lcd_remote_scroll_info.scroll[lcd_remote_scroll_info.lines-1];
|
2008-01-07 20:34:11 +00:00
|
|
|
}
|
|
|
|
lcd_remote_scroll_info.lines--;
|
|
|
|
|
2008-07-02 09:42:36 +00:00
|
|
|
/* A line can only appear once, so we're done,
|
|
|
|
* unless we are clearing the whole viewport */
|
|
|
|
if (y >= 0)
|
|
|
|
return ;
|
2008-01-07 20:34:11 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop all scrolling lines in the specified viewport */
|
2009-09-04 16:04:02 +00:00
|
|
|
void lcd_remote_scroll_stop(const struct viewport* vp)
|
2008-01-07 20:34:11 +00:00
|
|
|
{
|
|
|
|
lcd_remote_scroll_stop_line(vp, -1);
|
|
|
|
}
|
|
|
|
|
2007-07-28 08:12:05 +00:00
|
|
|
void lcd_remote_scroll_speed(int speed)
|
|
|
|
{
|
|
|
|
lcd_remote_scroll_info.ticks = scroll_tick_table[speed];
|
|
|
|
}
|
|
|
|
|
|
|
|
void lcd_remote_scroll_step(int step)
|
|
|
|
{
|
|
|
|
lcd_remote_scroll_info.step = step;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lcd_remote_scroll_delay(int ms)
|
|
|
|
{
|
|
|
|
lcd_remote_scroll_info.delay = ms / (HZ / 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lcd_remote_bidir_scroll(int percent)
|
|
|
|
{
|
|
|
|
lcd_remote_scroll_info.bidir_limit = percent;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sync_display_ticks(void)
|
|
|
|
{
|
|
|
|
lcd_scroll_info.last_scroll =
|
|
|
|
lcd_remote_scroll_info.last_scroll = current_tick;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool scroll_process_message(int delay)
|
|
|
|
{
|
2007-10-16 01:25:17 +00:00
|
|
|
struct queue_event ev;
|
2007-07-28 08:12:05 +00:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
long tick = current_tick;
|
|
|
|
queue_wait_w_tmo(&scroll_queue, &ev, delay);
|
|
|
|
|
|
|
|
switch (ev.id)
|
|
|
|
{
|
|
|
|
case SYS_TIMEOUT:
|
|
|
|
return false;
|
|
|
|
case SYS_USB_CONNECTED:
|
|
|
|
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
|
|
|
usb_wait_for_disconnect(&scroll_queue);
|
|
|
|
sync_display_ticks();
|
|
|
|
return true;
|
2010-06-21 16:53:00 +00:00
|
|
|
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
2007-07-28 08:12:05 +00:00
|
|
|
case SYS_REMOTE_PLUGGED:
|
|
|
|
if (!remote_initialized)
|
|
|
|
sync_display_ticks();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
delay -= current_tick - tick;
|
|
|
|
}
|
|
|
|
while (delay > 0);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_REMOTE_LCD */
|
|
|
|
|
2010-08-12 13:38:25 +00:00
|
|
|
static void scroll_thread(void) NORETURN_ATTR;
|
2007-07-28 08:12:05 +00:00
|
|
|
#ifdef HAVE_REMOTE_LCD
|
|
|
|
|
|
|
|
static void scroll_thread(void)
|
|
|
|
{
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
SCROLL_LCD = 0x1,
|
|
|
|
SCROLL_LCD_REMOTE = 0x2,
|
|
|
|
};
|
|
|
|
|
|
|
|
sync_display_ticks();
|
|
|
|
|
|
|
|
while ( 1 )
|
|
|
|
{
|
|
|
|
long delay;
|
|
|
|
int scroll;
|
|
|
|
long tick_lcd, tick_remote;
|
|
|
|
|
|
|
|
tick_lcd = lcd_scroll_info.last_scroll + lcd_scroll_info.ticks;
|
|
|
|
delay = current_tick;
|
|
|
|
|
|
|
|
if (
|
2010-06-21 16:53:00 +00:00
|
|
|
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|
2007-07-28 08:12:05 +00:00
|
|
|
!remote_initialized ||
|
|
|
|
#endif
|
|
|
|
(tick_remote = lcd_remote_scroll_info.last_scroll +
|
|
|
|
lcd_remote_scroll_info.ticks,
|
|
|
|
TIME_BEFORE(tick_lcd, tick_remote)))
|
|
|
|
{
|
|
|
|
scroll = SCROLL_LCD;
|
|
|
|
delay = tick_lcd - delay;
|
|
|
|
}
|
|
|
|
/* TIME_BEFORE(tick_remote, tick_lcd) */
|
|
|
|
else if (tick_lcd != tick_remote)
|
|
|
|
{
|
|
|
|
scroll = SCROLL_LCD_REMOTE;
|
|
|
|
delay = tick_remote - delay;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
scroll = SCROLL_LCD | SCROLL_LCD_REMOTE;
|
|
|
|
delay = tick_lcd - delay;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scroll_process_message(delay))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (scroll & SCROLL_LCD)
|
|
|
|
{
|
2009-03-17 02:43:47 +00:00
|
|
|
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
|
|
|
|
if (lcd_active())
|
2007-07-28 08:12:05 +00:00
|
|
|
#endif
|
|
|
|
lcd_scroll_fn();
|
|
|
|
lcd_scroll_info.last_scroll = current_tick;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scroll == (SCROLL_LCD | SCROLL_LCD_REMOTE))
|
|
|
|
yield();
|
|
|
|
|
|
|
|
if (scroll & SCROLL_LCD_REMOTE)
|
|
|
|
{
|
|
|
|
lcd_remote_scroll_fn();
|
|
|
|
lcd_remote_scroll_info.last_scroll = current_tick;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static void scroll_thread(void)
|
|
|
|
{
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
sleep(lcd_scroll_info.ticks);
|
2009-03-17 02:43:47 +00:00
|
|
|
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
|
|
|
|
if (lcd_active())
|
2007-07-28 08:12:05 +00:00
|
|
|
#endif
|
|
|
|
lcd_scroll_fn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* HAVE_REMOTE_LCD */
|
|
|
|
|
|
|
|
void scroll_init(void)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_REMOTE_LCD
|
|
|
|
queue_init(&scroll_queue, true);
|
|
|
|
#endif
|
|
|
|
create_thread(scroll_thread, scroll_stack,
|
2007-10-16 01:25:17 +00:00
|
|
|
sizeof(scroll_stack), 0, scroll_name
|
2007-07-28 08:12:05 +00:00
|
|
|
IF_PRIO(, PRIORITY_USER_INTERFACE)
|
2007-10-16 01:25:17 +00:00
|
|
|
IF_COP(, CPU));
|
2007-07-28 08:12:05 +00:00
|
|
|
}
|
2009-09-04 16:04:02 +00:00
|
|
|
|