rockbox/apps/screens.c

702 lines
21 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 Bj<EFBFBD>rn Stenberg
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "backlight.h"
#include "button.h"
#include "lcd.h"
#include "lang.h"
#include "icons.h"
#include "font.h"
#include "mpeg.h"
#include "usb.h"
#include "settings.h"
#include "status.h"
#include "playlist.h"
#include "sprintf.h"
#include "kernel.h"
#include "power.h"
#ifdef HAVE_LCD_BITMAP
#define BMPHEIGHT_usb_logo 32
#define BMPWIDTH_usb_logo 100
static unsigned char usb_logo[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08,
0x04, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x81, 0x81, 0x81, 0x81,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0xf1, 0x4f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0,
0x00, 0x00, 0xe0, 0x1c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x81, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0,
0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x1c,
0x0c, 0x0e, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xc0, 0xc0, 0x80, 0x80, 0x00, 0x00,
0x00, 0x00, 0xe0, 0x1f, 0x00, 0xf8, 0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x82, 0x7e, 0x00, 0xc0, 0x3e, 0x01,
0x70, 0x4f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x80, 0x00, 0x07, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f,
0x0f, 0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x0f,
0x1f, 0x3f, 0x7b, 0xf3, 0xe3, 0xc3, 0x83, 0x83, 0x83, 0x83, 0xe3, 0xe3, 0xe3,
0xe3, 0xe3, 0xe3, 0x03, 0x03, 0x03, 0x3f, 0x1f, 0x1f, 0x0f, 0x0f, 0x07, 0x02,
0xc0, 0x3e, 0x01, 0xe0, 0x9f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0xf0, 0x0f, 0x80, 0x78, 0x07, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x10, 0x20, 0x40, 0x40, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x87, 0x87, 0x87,
0x87, 0x87, 0x87, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf0,
0x0f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x07, 0x00, 0x00, 0x00, 0x00,
};
#endif
void usb_display_info(void)
{
lcd_clear_display();
#ifdef HAVE_LCD_BITMAP
/* lcd_bitmap() only supports 16 pixels height! */
lcd_bitmap(usb_logo, 6, 16,
BMPWIDTH_usb_logo, 8, false);
lcd_bitmap(usb_logo+BMPWIDTH_usb_logo, 6, 24,
BMPWIDTH_usb_logo, 8, false);
lcd_bitmap(usb_logo+BMPWIDTH_usb_logo*2, 6, 32,
BMPWIDTH_usb_logo, 8, false);
lcd_bitmap(usb_logo+BMPWIDTH_usb_logo*3, 6, 40,
BMPWIDTH_usb_logo, 8, false);
status_draw(true);
lcd_update();
#else
lcd_puts(0, 0, "[USB Mode]");
status_set_param(false);
status_set_audio(false);
status_set_usb(true);
status_draw(false);
#endif
}
void usb_screen(void)
{
#ifndef SIMULATOR
backlight_on();
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_display_info();
while(usb_wait_for_disconnect_w_tmo(&button_queue, HZ)) {
if(usb_inserted()) {
status_draw(false);
}
}
#ifdef HAVE_LCD_CHARCELLS
status_set_usb(false);
#endif
backlight_on();
#endif
}
/* blocks while charging, returns on event:
1 if charger cable was removed
2 if Off/Stop key was pressed
3 if On key was pressed
4 if USB was connected */
int charging_screen(void)
{
int button;
int rc = 0;
#ifdef HAVE_RECORDER_KEYPAD
const int offbutton = BUTTON_OFF;
#else
const int offbutton = BUTTON_STOP;
#endif
ide_power_enable(false); /* power down the disk, else would be spinning */
backlight_on();
lcd_clear_display();
status_draw(true);
#ifdef HAVE_LCD_BITMAP
lcd_puts(0, 3, "[Rockbox charging]"); /* ToDo: show some logo instead */
lcd_update();
#else
status_set_playmode(STATUS_STOP);
lcd_puts(0, 1, "[charging]");
#endif
charger_enable(true);
do
{
status_draw(false);
button = button_get_w_tmo(HZ/2);
if (button == BUTTON_ON)
rc = 3;
else if (button == offbutton)
rc = 2;
else
{
if (usb_detect())
rc = 4;
else if (!charger_inserted())
rc = 1;
}
} while (!rc);
return rc;
}
#ifdef HAVE_RECORDER_KEYPAD
/* returns:
0 if no key was pressed
1 if a key was pressed (or if ON was held down long enough to repeat)
2 if USB was connected */
int on_screen(void)
{
static int pitch = 1000;
bool exit = false;
bool used = false;
while (!exit) {
if ( used ) {
char* ptr;
char buf[32];
int w, h;
lcd_clear_display();
lcd_setfont(FONT_SYSFIXED);
ptr = str(LANG_PITCH_UP);
lcd_getstringsize(ptr,&w,&h);
lcd_putsxy((LCD_WIDTH-w)/2, 0, ptr);
lcd_bitmap(bitmap_icons_7x8[Icon_UpArrow],
LCD_WIDTH/2 - 3, h*2, 7, 8, true);
snprintf(buf, sizeof buf, "%d.%d%%", pitch / 10, pitch % 10 );
lcd_getstringsize(buf,&w,&h);
lcd_putsxy((LCD_WIDTH-w)/2, h, buf);
ptr = str(LANG_PITCH_DOWN);
lcd_getstringsize(ptr,&w,&h);
lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h, ptr);
lcd_bitmap(bitmap_icons_7x8[Icon_DownArrow],
LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8, true);
ptr = str(LANG_PAUSE);
lcd_getstringsize(ptr,&w,&h);
lcd_putsxy((LCD_WIDTH-(w/2))/2, LCD_HEIGHT/2 - h/2, ptr);
lcd_bitmap(bitmap_icons_7x8[Icon_Pause],
(LCD_WIDTH-(w/2))/2-10, LCD_HEIGHT/2 - h/2, 7, 8, true);
lcd_update();
}
/* use lastbutton, so the main loop can decide whether to
exit to browser or not */
switch (button_get(true)) {
case BUTTON_UP:
case BUTTON_ON | BUTTON_UP:
case BUTTON_ON | BUTTON_UP | BUTTON_REPEAT:
used = true;
pitch++;
if ( pitch > 2000 )
pitch = 2000;
mpeg_set_pitch(pitch);
break;
case BUTTON_DOWN:
case BUTTON_ON | BUTTON_DOWN:
case BUTTON_ON | BUTTON_DOWN | BUTTON_REPEAT:
used = true;
pitch--;
if ( pitch < 500 )
pitch = 500;
mpeg_set_pitch(pitch);
break;
case BUTTON_ON | BUTTON_PLAY:
mpeg_pause();
used = true;
break;
case BUTTON_PLAY | BUTTON_REL:
mpeg_resume();
used = true;
break;
case BUTTON_ON | BUTTON_PLAY | BUTTON_REL:
mpeg_resume();
exit = true;
break;
case BUTTON_ON | BUTTON_RIGHT:
if ( pitch < 2000 ) {
pitch += 20;
mpeg_set_pitch(pitch);
}
break;
case BUTTON_RIGHT | BUTTON_REL:
if ( pitch > 500 ) {
pitch -= 20;
mpeg_set_pitch(pitch);
}
break;
case BUTTON_ON | BUTTON_LEFT:
if ( pitch > 500 ) {
pitch -= 20;
mpeg_set_pitch(pitch);
}
break;
case BUTTON_LEFT | BUTTON_REL:
if ( pitch < 2000 ) {
pitch += 20;
mpeg_set_pitch(pitch);
}
break;
#ifdef SIMULATOR
case BUTTON_ON:
#else
case BUTTON_ON | BUTTON_REL:
case BUTTON_ON | BUTTON_UP | BUTTON_REL:
case BUTTON_ON | BUTTON_DOWN | BUTTON_REL:
#endif
exit = true;
break;
case BUTTON_ON | BUTTON_REPEAT:
used = true;
break;
case SYS_USB_CONNECTED:
usb_screen();
return 2;
}
}
lcd_setfont(FONT_UI);
if ( used )
return 1;
else
return 0;
}
bool f2_screen(void)
{
bool exit = false;
bool used = false;
int w, h;
char buf[32];
int oldrepeat = global_settings.repeat_mode;
lcd_setfont(FONT_SYSFIXED);
lcd_getstringsize("A",&w,&h);
while (!exit) {
char* ptr=NULL;
lcd_clear_display();
/* Shuffle mode */
lcd_putsxy(0, LCD_HEIGHT/2 - h*2, str(LANG_SHUFFLE));
lcd_putsxy(0, LCD_HEIGHT/2 - h, str(LANG_F2_MODE));
lcd_putsxy(0, LCD_HEIGHT/2,
global_settings.playlist_shuffle ?
str(LANG_ON) : str(LANG_OFF));
lcd_bitmap(bitmap_icons_7x8[Icon_FastBackward],
LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8, true);
/* Directory Filter */
switch ( global_settings.dirfilter ) {
case SHOW_ALL:
ptr = str(LANG_FILTER_ALL);
break;
case SHOW_SUPPORTED:
ptr = str(LANG_FILTER_SUPPORTED);
break;
case SHOW_MUSIC:
ptr = str(LANG_FILTER_MUSIC);
break;
case SHOW_PLAYLIST:
ptr = str(LANG_FILTER_PLAYLIST);
break;
}
snprintf(buf, sizeof buf, "%s:", str(LANG_FILTER));
lcd_getstringsize(buf,&w,&h);
lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h*2, buf);
lcd_getstringsize(ptr,&w,&h);
lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h, ptr);
lcd_bitmap(bitmap_icons_7x8[Icon_DownArrow],
LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8, true);
/* Repeat Mode */
switch ( global_settings.repeat_mode ) {
case REPEAT_OFF:
ptr = str(LANG_OFF);
break;
case REPEAT_ALL:
ptr = str(LANG_REPEAT_ALL);
break;
case REPEAT_ONE:
ptr = str(LANG_REPEAT_ONE);
break;
}
lcd_getstringsize(str(LANG_REPEAT),&w,&h);
lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h*2, str(LANG_REPEAT));
lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h, str(LANG_F2_MODE));
lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2, ptr);
lcd_bitmap(bitmap_icons_7x8[Icon_FastForward],
LCD_WIDTH/2 + 8, LCD_HEIGHT/2 - 4, 7, 8, true);
lcd_update();
switch (button_get(true)) {
case BUTTON_LEFT:
case BUTTON_F2 | BUTTON_LEFT:
global_settings.playlist_shuffle =
!global_settings.playlist_shuffle;
if(mpeg_status() & MPEG_STATUS_PLAY)
{
if (global_settings.playlist_shuffle)
playlist_randomise(current_tick, true);
else
playlist_sort(true);
}
used = true;
break;
case BUTTON_DOWN:
case BUTTON_F2 | BUTTON_DOWN:
global_settings.dirfilter++;
if ( global_settings.dirfilter >= NUM_FILTER_MODES )
global_settings.dirfilter = 0;
used = true;
break;
case BUTTON_RIGHT:
case BUTTON_F2 | BUTTON_RIGHT:
global_settings.repeat_mode++;
if ( global_settings.repeat_mode >= NUM_REPEAT_MODES )
global_settings.repeat_mode = 0;
used = true;
break;
case BUTTON_F2 | BUTTON_REL:
if ( used )
exit = true;
used = true;
break;
case BUTTON_F2 | BUTTON_REPEAT:
used = true;
break;
case SYS_USB_CONNECTED:
usb_screen();
return true;
}
}
settings_save();
lcd_setfont(FONT_UI);
if ( oldrepeat != global_settings.repeat_mode )
mpeg_flush_and_reload_tracks();
return false;
}
bool f3_screen(void)
{
bool exit = false;
bool used = false;
lcd_setfont(FONT_SYSFIXED);
while (!exit) {
int w,h;
char* ptr;
ptr = str(LANG_F3_STATUS);
lcd_getstringsize(ptr,&w,&h);
lcd_clear_display();
lcd_putsxy(0, LCD_HEIGHT/2 - h*2, str(LANG_F3_SCROLL));
lcd_putsxy(0, LCD_HEIGHT/2 - h, str(LANG_F3_BAR));
lcd_putsxy(0, LCD_HEIGHT/2,
global_settings.scrollbar ? str(LANG_ON) : str(LANG_OFF));
lcd_bitmap(bitmap_icons_7x8[Icon_FastBackward],
LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8, true);
lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h*2, ptr);
lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h, str(LANG_F3_BAR));
lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2,
global_settings.statusbar ? str(LANG_ON) : str(LANG_OFF));
lcd_bitmap(bitmap_icons_7x8[Icon_FastForward],
LCD_WIDTH/2 + 8, LCD_HEIGHT/2 - 4, 7, 8, true);
/* Invert */
lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h*2, str(LANG_INVERT));
lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h,
global_settings.invert ?
str(LANG_INVERT_LCD_INVERSE) : str(LANG_INVERT_LCD_NORMAL));
lcd_bitmap(bitmap_icons_7x8[Icon_DownArrow],
LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8, true);
lcd_update();
switch (button_get(true)) {
case BUTTON_LEFT:
case BUTTON_F3 | BUTTON_LEFT:
global_settings.scrollbar = !global_settings.scrollbar;
used = true;
break;
case BUTTON_RIGHT:
case BUTTON_F3 | BUTTON_RIGHT:
global_settings.statusbar = !global_settings.statusbar;
used = true;
break;
case BUTTON_DOWN:
case BUTTON_F3 | BUTTON_DOWN:
global_settings.invert = !global_settings.invert;
lcd_set_invert_display(global_settings.invert);
used = true;
break;
case BUTTON_F3 | BUTTON_REL:
if ( used )
exit = true;
used = true;
break;
case BUTTON_F3 | BUTTON_REPEAT:
used = true;
break;
case SYS_USB_CONNECTED:
usb_screen();
return true;
}
}
settings_save();
if (global_settings.statusbar)
lcd_setmargins(0, STATUSBAR_HEIGHT);
else
lcd_setmargins(0, 0);
lcd_setfont(FONT_UI);
return false;
}
#endif
#ifdef HAVE_LCD_BITMAP
#define SPACE 3 /* pixels between words */
#define MAXLETTERS 128 /* 16*8 */
#define MAXLINES 10
#else
#define SPACE 1 /* one letter space */
#undef LCD_WIDTH
#define LCD_WIDTH 11
#undef LCD_HEIGHT
#define LCD_HEIGHT 2
#define MAXLETTERS 22 /* 11 * 2 */
#define MAXLINES 2
#endif
void splash(int ticks, /* how long */
int keymask, /* what keymask aborts the waiting (if any) */
bool center, /* FALSE means left-justified, TRUE means
horizontal and vertical center */
char *fmt, /* what to say *printf style */
...)
{
char *next;
char *store=NULL;
int x=0;
int y=0;
int w, h;
unsigned char splash_buf[MAXLETTERS];
va_list ap;
unsigned char widths[MAXLINES];
int line=0;
bool first=true;
#ifdef HAVE_LCD_BITMAP
int maxw=0;
#endif
va_start( ap, fmt );
vsnprintf( splash_buf, sizeof(splash_buf), fmt, ap );
if(center) {
/* first a pass to measure sizes */
next = strtok_r(splash_buf, " ", &store);
while (next) {
#ifdef HAVE_LCD_BITMAP
lcd_getstringsize(next, &w, &h);
#else
w = strlen(next);
h = 1; /* store height in characters */
#endif
if(!first) {
if(x+w> LCD_WIDTH) {
/* Too wide, wrap */
y+=h;
line++;
if((y > (LCD_HEIGHT-h)) || (line > MAXLINES))
/* STOP */
break;
x=0;
first=true;
}
}
else
first = false;
/* think of it as if the text was written here at position x,y
being w pixels/chars wide and h high */
x += w+SPACE;
widths[line]=x-SPACE; /* don't count the trailing space */
#ifdef HAVE_LCD_BITMAP
/* store the widest line */
if(widths[line]>maxw)
maxw = widths[line];
#endif
next = strtok_r(NULL, " ", &store);
}
#ifdef HAVE_LCD_BITMAP
/* Start displaying the message at position y. The reason for the
added h here is that it isn't added until the end of lines in the
loop above and we always break the loop in the middle of a line. */
y = (LCD_HEIGHT - (y+h) )/2;
#else
y = 0; /* vertical center on 2 lines would be silly */
#endif
first=true;
/* Now recreate the string again since the strtok_r() above has ruined
the one we already have! Here's room for improvements! */
vsnprintf( splash_buf, sizeof(splash_buf), fmt, ap );
}
va_end( ap );
if(center)
{
x = (LCD_WIDTH-widths[0])/2;
if(x < 0)
x = 0;
}
#ifdef HAVE_LCD_BITMAP
/* If we center the display and it wouldn't cover the full screen,
then just clear the box we need and put a nice little frame and
put the text in there! */
if(center && (y > 2)) {
if(maxw < (LCD_WIDTH -4)) {
int xx = (LCD_WIDTH-maxw)/2 - 2;
lcd_clearrect(xx, y-2, maxw+4, LCD_HEIGHT-y*2+4);
lcd_drawrect(xx, y-2, maxw+4, LCD_HEIGHT-y*2+4);
}
else {
lcd_clearrect(0, y-2, LCD_WIDTH, LCD_HEIGHT-y*2+4);
lcd_drawline(0, y-2, LCD_WIDTH-1, y-2);
lcd_drawline(0, LCD_HEIGHT-y+2, LCD_WIDTH-1, LCD_HEIGHT-y+2);
}
}
else
#endif
lcd_clear_display();
line=0;
next = strtok_r(splash_buf, " ", &store);
while (next) {
#ifdef HAVE_LCD_BITMAP
lcd_getstringsize(next, &w, &h);
#else
w = strlen(next);
h = 1;
#endif
if(!first) {
if(x+w> LCD_WIDTH) {
/* too wide */
y+=h;
line++; /* goto next line */
first=true;
if(y > (LCD_HEIGHT-h))
/* STOP */
break;
if(center)
x = (LCD_WIDTH-widths[line])/2;
else
x=0;
}
}
else
first=false;
#ifdef HAVE_LCD_BITMAP
lcd_putsxy(x, y, next);
#else
lcd_puts(x, y, next);
#endif
x += w+SPACE; /* pixels space! */
next = strtok_r(NULL, " ", &store);
}
lcd_update();
if(ticks) {
if(keymask) {
int start = current_tick;
int done = ticks + current_tick + 1;
while (TIME_BEFORE( current_tick, done)) {
int button = button_get_w_tmo(ticks - (current_tick-start));
if((button & keymask) == keymask)
break;
}
}
else
/* unbreakable! */
sleep(ticks);
}
}