rockbox/firmware/scroll_engine.c
Jonathan Gordon 88055b2f2e fix FS#9140 by changing stop_scroll so it stops scrolling lines which would overlap the current viewport. This shouldnt cause any problems because when this happens the vp owning the line should redraw when the new vp's screen is exited.
Also fix a potential bug where only one scrolling line in a viewport was stopped


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17913 a1c6a512-1295-4272-9138-f99709370657
2008-07-02 09:42:36 +00:00

366 lines
9.4 KiB
C

/***************************************************************************
* __________ __ ___.
* 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.
*
* 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.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#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"
static const char scroll_tick_table[16] = {
/* Hz values:
1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
};
static void scroll_thread(void);
static char scroll_stack[DEFAULT_STACK_SIZE*3];
static const char scroll_name[] = "scroll";
static struct scrollinfo lcd_scroll[LCD_SCROLLABLE_LINES];
#ifdef HAVE_REMOTE_LCD
struct scrollinfo lcd_remote_scroll[LCD_REMOTE_SCROLLABLE_LINES];
struct event_queue scroll_queue;
#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;
}
/* returns true if the 'line' in 'lines_vp' would scroll into 'othervp' */
static bool line_overlaps_viewport(struct viewport *lines_vp, int line,
struct viewport *othervp)
{
int y = (font_get(lines_vp->font)->height*line) + lines_vp->y;
if (y < othervp->y || y > othervp->y + othervp->height)
return false;
else if ((lines_vp->x + lines_vp->width < othervp->x) ||
(othervp->x + othervp->width < lines_vp->x))
return false;
return true;
}
/* Stop scrolling line y in the specified viewport, or all lines if y < 0 */
void lcd_scroll_stop_line(struct viewport* current_vp, int y)
{
int i = 0;
while (i < lcd_scroll_info.lines)
{
if (((lcd_scroll_info.scroll[i].vp == current_vp) &&
((y < 0) || (lcd_scroll_info.scroll[i].y == y))) ||
((lcd_scroll_info.scroll[i].vp != current_vp) &&
line_overlaps_viewport(lcd_scroll_info.scroll[i].vp,
lcd_scroll_info.scroll[i].y, current_vp)))
{
/* 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)
{
lcd_scroll_info.scroll[i] = lcd_scroll_info.scroll[lcd_scroll_info.lines-1];
}
lcd_scroll_info.lines--;
/* A line can only appear once, so we're done,
* unless we are clearing the whole viewport */
if (y >= 0)
return ;
}
else
{
i++;
}
}
}
/* Stop all scrolling lines in the specified viewport */
void lcd_scroll_stop(struct viewport* vp)
{
lcd_scroll_stop_line(vp, -1);
}
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;
}
/* Stop scrolling line y in the specified viewport, or all lines if y < 0 */
void lcd_remote_scroll_stop_line(struct viewport* current_vp, int y)
{
int i = 0;
while (i < lcd_remote_scroll_info.lines)
{
if (((lcd_remote_scroll_info.scroll[i].vp == current_vp) &&
((y < 0) || (lcd_remote_scroll_info.scroll[i].y == y))) ||
(((lcd_remote_scroll_info.scroll[i].vp != current_vp) &&
line_overlaps_viewport(lcd_scroll_info.scroll[i].vp,
lcd_scroll_info.scroll[i].y, current_vp))))
{
/* 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)
{
lcd_remote_scroll_info.scroll[i] = lcd_remote_scroll_info.scroll[lcd_remote_scroll_info.lines-1];
}
lcd_remote_scroll_info.lines--;
/* A line can only appear once, so we're done,
* unless we are clearing the whole viewport */
if (y >= 0)
return ;
}
else
{
i++;
}
}
}
/* Stop all scrolling lines in the specified viewport */
void lcd_remote_scroll_stop(struct viewport* vp)
{
lcd_remote_scroll_stop_line(vp, -1);
}
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)
{
struct queue_event ev;
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;
#ifndef SIMULATOR
case SYS_REMOTE_PLUGGED:
if (!remote_initialized)
sync_display_ticks();
#endif
}
delay -= current_tick - tick;
}
while (delay > 0);
return false;
}
#endif /* HAVE_REMOTE_LCD */
static void scroll_thread(void) __attribute__((noreturn));
#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 (
#ifndef SIMULATOR
!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)
{
#ifdef HAVE_LCD_ENABLE
if (lcd_enabled())
#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);
#ifdef HAVE_LCD_ENABLE
if (lcd_enabled())
#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,
sizeof(scroll_stack), 0, scroll_name
IF_PRIO(, PRIORITY_USER_INTERFACE)
IF_COP(, CPU));
}