rockbox/apps/plugins/clock.c

749 lines
26 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id: clock.c,v 1.0 2003/12/8
*
* Copyright (C) 2003 Zakk Roberts
*
* 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 "plugin.h"
#include "time.h"
#ifdef HAVE_LCD_BITMAP
void draw_7seg_time(int hour, int minute, int x, int y, int width, int height,
bool colon);
void loadSettings(void);
static struct plugin_api* rb;
/* Used for hands to define lengths at a given time */
static unsigned char yhour[] = {
47,47,46,46,46,45,44,43,42,41,39,38,36,35,33,32,31,29,28,26,25,23,22,21,20,19,18,18,18,17,17,17,18,18,18,19,20,21,22,23,25,26,28,29,31,32,33,35,36,38,39,41,42,43,44,45,46,46,46,47,
};
static unsigned char yminute[] = {
55,54,54,53,53,51,50,49,47,45,43,41,39,36,34,32,30,28,25,23,21,19,17,15,14,13,11,11,10,10,9,10,10,11,11,13,14,15,17,19,21,23,25,28,30,32,34,36,39,41,43,45,47,49,50,51,53,53,54,54,
};
static unsigned char xhour[] = {
56,58,59,61,63,65,67,68,70,71,72,73,74,74,75,75,75,74,74,73,72,71,70,68,67,65,63,61,59,58,56,54,53,51,49,47,45,44,42,41,40,39,38,38,37,37,37,38,38,39,40,41,42,44,45,47,49,51,53,54,
};
static unsigned char xminute[] = {
56,59,61,64,67,70,72,75,77,79,80,82,83,84,84,84,84,84,83,82,80,79,77,75,72,70,67,64,61,59,56,53,51,48,45,42,40,37,35,33,32,30,29,28,28,28,28,28,29,30,32,33,35,37,40,42,45,48,51,53,
};
static char default_filename[] = "/.rockbox/rocks/.clock_settings";
struct saved_settings
{
bool is_date_displayed;
bool are_digits_displayed;
bool is_time_displayed;
bool is_rect_displayed;
bool analog_clock;
} settings;
void save_settings(void)
{
int fd;
fd = rb->creat(default_filename, O_WRONLY);
if(fd >= 0)
{
rb->write (fd, &settings, sizeof(struct saved_settings));
rb->close(fd);
}
else
{
rb->splash(HZ, 0, true, "Setting save failed");
}
}
void load_settings(void)
{
int fd;
fd = rb->open(default_filename, O_RDONLY);
/* if file exists, then load. */
if(fd >= 0)
{
rb->read (fd, &settings, sizeof(struct saved_settings));
rb->close(fd);
}
/* Else, loading failed */
else
{
rb->splash(HZ, 0, true, "Setting load failed, using default settings.");
}
}
enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
{
/* time ints */
int i;
int hour;
int minute;
int second;
int last_second = -1;
int pos = 0;
/* date ints */
int year;
int day;
int month;
char moday[6];
char dateyr[5];
char tmhrmin[5];
char tmsec[2];
char current_date[12];
struct tm* current_time;
TEST_PLUGIN_API(api);
(void)parameter;
rb = api;
/* load settings from disk */
load_settings();
rb->lcd_clear_display();
rb->splash(HZ, 0, true, "F1 for INFO");
while (!PLUGIN_OK)
{
/* universal font for better display */
rb->lcd_setfont(FONT_SYSFIXED);
/* start all the clock stuff */
bool exit = false;
bool used = false;
/* Time info */
current_time = rb->get_time();
hour = current_time->tm_hour;
minute = current_time->tm_min;
second = current_time->tm_sec;
/* Date info */
year = current_time->tm_year+1900;
day = current_time->tm_mday;
month = current_time->tm_mon+1;
if(second != last_second)
{
rb->lcd_clear_display();
rb->snprintf( moday, 5, "%d/%d", month, day );
rb->snprintf( dateyr, 4, "%d", year );
rb->snprintf( tmhrmin, 5, "%02d:%02d", hour, minute );
rb->snprintf( tmsec, 2, "%02d", second );
rb->snprintf( current_date, 12, "%d/%d/%d", month, day, year );
if(settings.analog_clock)
{
/* Now draw the analog clock */
pos = 90-second;
if(pos >= 60)
pos -= 60;
/* Second hand */
rb->lcd_drawline((LCD_WIDTH/2), (LCD_HEIGHT/2),
xminute[pos], yminute[pos]);
pos = 90-minute;
if(pos >= 60)
pos -= 60;
/* Minute hand, thicker than the second hand */
rb->lcd_drawline(LCD_WIDTH/2, LCD_HEIGHT/2,
xminute[pos], yminute[pos]);
rb->lcd_drawline(LCD_WIDTH/2-1, LCD_HEIGHT/2-1,
xminute[pos], yminute[pos]);
rb->lcd_drawline(LCD_WIDTH/2+1, LCD_HEIGHT/2+1,
xminute[pos], yminute[pos]);
rb->lcd_drawline(LCD_WIDTH/2-1, LCD_HEIGHT/2+1,
xminute[pos], yminute[pos]);
rb->lcd_drawline(LCD_WIDTH/2+1, LCD_HEIGHT/2-1,
xminute[pos], yminute[pos]);
if(hour > 12)
hour -= 12;
hour = hour*5 + minute/12;;
pos = 90-hour;
if(pos >= 60)
pos -= 60;
/* Hour hand, thick as the minute hand but shorter */
rb->lcd_drawline(LCD_WIDTH/2, LCD_HEIGHT/2, xhour[pos], yhour[pos]);
rb->lcd_drawline(LCD_WIDTH/2-1, LCD_HEIGHT/2-1,
xhour[pos], yhour[pos]);
rb->lcd_drawline(LCD_WIDTH/2+1, LCD_HEIGHT/2+1,
xhour[pos], yhour[pos]);
rb->lcd_drawline(LCD_WIDTH/2-1, LCD_HEIGHT/2+1,
xhour[pos], yhour[pos]);
rb->lcd_drawline(LCD_WIDTH/2+1, LCD_HEIGHT/2-1,
xhour[pos], yhour[pos]);
/* Draw the circle */
for(i=0; i < 60; i+=5)
{
rb->lcd_drawpixel(xminute[i],
yminute[i]);
rb->lcd_drawrect(xminute[i]-1,
yminute[i]-1,
3, 3);
}
/* Draw the cover over the center */
rb->lcd_drawline((LCD_WIDTH/2)-1, (LCD_HEIGHT/2)+3,
(LCD_WIDTH/2)+1, (LCD_HEIGHT/2)+3);
rb->lcd_drawline((LCD_WIDTH/2)-3, (LCD_HEIGHT/2)+2,
(LCD_WIDTH/2)+3, (LCD_HEIGHT/2)+2);
rb->lcd_drawline((LCD_WIDTH/2)-4, (LCD_HEIGHT/2)+1,
(LCD_WIDTH/2)+4, (LCD_HEIGHT/2)+1);
rb->lcd_drawline((LCD_WIDTH/2)-4, LCD_HEIGHT/2,
(LCD_WIDTH/2)+4, LCD_HEIGHT/2);
rb->lcd_drawline((LCD_WIDTH/2)-4, (LCD_HEIGHT/2)-1,
(LCD_WIDTH/2)+4, (LCD_HEIGHT/2)-1);
rb->lcd_drawline((LCD_WIDTH/2)-3, (LCD_HEIGHT/2)-2,
(LCD_WIDTH/2)+3, (LCD_HEIGHT/2)-2);
rb->lcd_drawline((LCD_WIDTH/2)-1, (LCD_HEIGHT/2)-3,
(LCD_WIDTH/2)+1, (LCD_HEIGHT/2)-3);
rb->lcd_drawpixel(LCD_WIDTH/2, LCD_HEIGHT/2);
/* Draw the digits around the clock */
if (settings.are_digits_displayed)
{
rb->lcd_putsxy((LCD_WIDTH/2)-6, 1, "12");
rb->lcd_putsxy(20, (LCD_HEIGHT/2)-4, "9");
rb->lcd_putsxy((LCD_WIDTH/2)-4, 56, "6");
rb->lcd_putsxy(86, (LCD_HEIGHT/2)-4, "3");
}
/* Draw digital readout */
if (settings.is_time_displayed)
{
/* HH:MM */
rb->lcd_putsxy(1, 4, tmhrmin);
/* SS */
rb->lcd_putsxy(10, 12, tmsec);
}
}
else
{
draw_7seg_time(hour, minute, 8, 16, 16, 32, second & 1);
}
/* Draw the border */
if (settings.is_rect_displayed)
{
rb->lcd_drawrect(0, 0, 112, 64);
/* top left corner */
rb->lcd_drawpixel(1, 1);
rb->lcd_drawpixel(2, 2);
rb->lcd_drawpixel(1, 2);
rb->lcd_drawpixel(1, 3);
rb->lcd_drawpixel(2, 1);
rb->lcd_drawpixel(3, 1);
/* bottom left corner */
rb->lcd_drawpixel(1, 62);
rb->lcd_drawpixel(2, 61);
rb->lcd_drawpixel(1, 61);
rb->lcd_drawpixel(1, 60);
rb->lcd_drawpixel(2, 62);
rb->lcd_drawpixel(3, 62);
/* top right corner */
rb->lcd_drawpixel(110, 1);
rb->lcd_drawpixel(109, 2);
rb->lcd_drawpixel(110, 2);
rb->lcd_drawpixel(110, 3);
rb->lcd_drawpixel(109, 1);
rb->lcd_drawpixel(108, 1);
/* bottom right corner */
rb->lcd_drawpixel(110, 62);
rb->lcd_drawpixel(109, 61);
rb->lcd_drawpixel(110, 61);
rb->lcd_drawpixel(110, 60);
rb->lcd_drawpixel(109, 62);
rb->lcd_drawpixel(108, 62);
}
/* Draw the date */
if (settings.is_date_displayed)
{
if(settings.analog_clock)
{
rb->lcd_putsxy(3, 53, moday);
rb->lcd_putsxy(80, 53, dateyr);
}
else
rb->lcd_putsxy(25, 3, current_date);
}
if (settings.is_time_displayed)
{
if(settings.analog_clock)
{
/* HH:MM */
rb->lcd_putsxy(1, 4, tmhrmin);
/* SS */
rb->lcd_putsxy(10, 12, tmsec);
}
else
{
/* Just seconds */
rb->lcd_putsxy(50, 55, tmsec);
}
}
/* Draw all to LCD */
rb->lcd_update();
}
switch (rb->button_get_w_tmo(HZ/6))
{
/* OFF exits */
case BUTTON_OFF:
/* Tell the user what's going on */
rb->lcd_clear_display();
rb->splash(HZ/2, 0, true, "Saving settings...");
/* Save to disk */
save_settings();
rb->lcd_clear_display();
rb->splash(HZ, 0, true, "Saved!");
/* ...and exit. */
return PLUGIN_OK;
break;
/* F1 & OFF exits without saving */
case BUTTON_F1 | BUTTON_OFF:
return PLUGIN_OK;
break;
/* F1 screen - plugin info */
case BUTTON_F1:
while (!exit)
{
if(settings.analog_clock)
{
rb->lcd_clear_display();
rb->lcd_puts(0, 0, "F1 > Help");
rb->lcd_puts(0, 1, "PLAY > Digital");
rb->lcd_puts(0, 2, "FF > Show Time");
rb->lcd_puts(0, 3, "DOWN > Border");
rb->lcd_puts(0, 4, "REW > Digits");
rb->lcd_puts(0, 5, "UP > Date");
rb->lcd_puts(0, 6, "OFF > Save & Exit");
rb->lcd_update();
}
else
{
rb->lcd_clear_display();
rb->lcd_puts(0, 0, "F1 > Help");
rb->lcd_puts(0, 1, "PLAY > Analog");
rb->lcd_puts(0, 3, "DOWN > Border");
rb->lcd_puts(0, 4, "UP > Date");
rb->lcd_puts(0, 5, "FF > Show Seconds");
rb->lcd_puts(0, 7, "OFF > Save & Exit");
rb->lcd_update();
}
switch (rb->button_get(true))
{
case BUTTON_F1 | BUTTON_REL:
if (used)
exit = true;
used = true;
break;
case BUTTON_OFF:
used = true;
break;
case SYS_USB_CONNECTED:
return PLUGIN_USB_CONNECTED;
rb->usb_screen();
break;
}
}
break;
/* options screen */
case BUTTON_F3:
while (!exit)
{
if(settings.analog_clock)
{
rb->lcd_clear_display();
rb->lcd_putsxy(7, 0, "FF > Show Time");
rb->lcd_putsxy(7, 10, "DOWN> Show Frame");
rb->lcd_putsxy(7, 20, "UP > Show Date");
rb->lcd_putsxy(7, 30, "REW > Show Digits");
rb->lcd_putsxy(15, 46, "OFF > All OFF");
rb->lcd_putsxy(17, 56, "ON > All ON");
rb->lcd_drawline(0, 41, 112, 41);
/* draw checkmarks */
if(settings.is_time_displayed)
{
rb->lcd_drawline(0, 4, 3, 7);
rb->lcd_drawline(3, 7, 5, 0);
}
if(settings.is_rect_displayed)
{
rb->lcd_drawline(0, 14, 3, 17);
rb->lcd_drawline(3, 17, 5, 10);
}
if(settings.is_date_displayed)
{
rb->lcd_drawline(0, 24, 3, 27);
rb->lcd_drawline(3, 27, 5, 20);
}
if(settings.are_digits_displayed)
{
rb->lcd_drawline(0, 34, 3, 37);
rb->lcd_drawline(3, 37, 5, 30);
}
/* update the LCD after all this madness */
rb->lcd_update();
/* ... and finally, setting options from screen: */
switch (rb->button_get(true))
{
case BUTTON_F3 | BUTTON_REL:
if (used)
exit = true;
used = true;
break;
case BUTTON_RIGHT:
settings.is_time_displayed =
!settings.is_time_displayed;
break;
case BUTTON_DOWN:
settings.is_rect_displayed =
!settings.is_rect_displayed;
break;
case BUTTON_UP:
settings.is_date_displayed =
!settings.is_date_displayed;
break;
case BUTTON_LEFT:
settings.are_digits_displayed =
!settings.are_digits_displayed;
break;
case BUTTON_ON:
settings.is_time_displayed = true;
settings.is_rect_displayed = true;
settings.is_date_displayed = true;
settings.are_digits_displayed = true;
break;
case BUTTON_OFF:
settings.is_time_displayed = false;
settings.is_rect_displayed = false;
settings.is_date_displayed = false;
settings.are_digits_displayed = false;
break;
case SYS_USB_CONNECTED:
return PLUGIN_USB_CONNECTED;
rb->usb_screen();
break;
}
}
else /* 7-Segment clock shown, less options */
{
rb->lcd_clear_display();
rb->lcd_putsxy(7, 0, "DOWN> Show Frame");
rb->lcd_putsxy(7, 10, "UP > Show Date");
rb->lcd_putsxy(7, 20, "FF > Seconds");
rb->lcd_putsxy(15, 46, "OFF > All OFF");
rb->lcd_putsxy(17, 56, "ON > All ON");
rb->lcd_drawline(0, 41, 112, 41);
/* draw checkmarks */
if(settings.is_rect_displayed)
{
rb->lcd_drawline(0, 4, 3, 7);
rb->lcd_drawline(3, 7, 5, 0);
}
if(settings.is_date_displayed)
{
rb->lcd_drawline(0, 14, 3, 17);
rb->lcd_drawline(3, 17, 5, 10);
}
if(settings.is_time_displayed)
{
rb->lcd_drawline(0, 24, 3, 27);
rb->lcd_drawline(3, 27, 5, 20);
}
/* And finally, update the LCD */
rb->lcd_update();
switch (rb->button_get(true))
{
case BUTTON_F3 | BUTTON_REL:
if (used)
exit = true;
used = true;
break;
case BUTTON_DOWN:
settings.is_rect_displayed =
!settings.is_rect_displayed;
break;
case BUTTON_UP:
settings.is_date_displayed =
!settings.is_date_displayed;
break;
case BUTTON_RIGHT:
settings.is_time_displayed =
!settings.is_time_displayed;
break;
case BUTTON_ON:
settings.is_time_displayed = true;
settings.is_rect_displayed = true;
settings.is_date_displayed = true;
break;
case BUTTON_OFF:
settings.is_time_displayed = false;
settings.is_rect_displayed = false;
settings.is_date_displayed = false;
break;
case SYS_USB_CONNECTED:
return PLUGIN_USB_CONNECTED;
rb->usb_screen();
break;
}
}
}
break;
/* Toggle analog/digital */
case BUTTON_PLAY:
settings.analog_clock = !settings.analog_clock;
break;
/* Show time */
case BUTTON_RIGHT:
settings.is_time_displayed = !settings.is_time_displayed;
break;
/* Show border */
case BUTTON_DOWN:
settings.is_rect_displayed = !settings.is_rect_displayed ;
break;
/* Show date */
case BUTTON_UP:
settings.is_date_displayed = !settings.is_date_displayed;
break;
/* Show digits */
case BUTTON_LEFT:
settings.are_digits_displayed = !settings.are_digits_displayed;
break;
/* USB plugged? */
case SYS_USB_CONNECTED:
rb->usb_screen();
return PLUGIN_USB_CONNECTED;
break;
}
}
}
/* 7 Segment LED imitation code by Linus Nielsen Feltzing */
/*
a 0 b
#########
# #
# #
1# #2
# #
# 3 #
c ######### d
# #
# #
4# #5
# #
# 6 #
e ######### f
*/
/* The coordinates of each end point (a-f) of the segment lines (0-6) */
static unsigned int point_coords[6][2] =
{
{0, 0}, /* a */
{1, 0}, /* b */
{0, 1}, /* c */
{1, 1}, /* d */
{0, 2}, /* e */
{1, 2} /* f */
};
/* The end points (a-f) for each segment line */
static unsigned int seg_points[7][2] =
{
{0,1}, /* a to b */
{0,2}, /* a to c */
{1,3}, /* b to d */
{2,3}, /* c to d */
{2,4}, /* c to e */
{3,5}, /* d to f */
{4,5} /* e to f */
};
/* Lists that tell which segments (0-6) to enable for each digit (0-9),
the list is terminated with -1 */
static int digit_segs[10][8] =
{
{0,1,2,4,5,6, -1}, /* 0 */
{2,5, -1}, /* 1 */
{0,2,3,4,6, -1}, /* 2 */
{0,2,3,5,6, -1}, /* 3 */
{1,2,3,5, -1}, /* 4 */
{0,1,3,5,6, -1}, /* 5 */
{0,1,3,4,5,6, -1}, /* 6 */
{0,2,5, -1}, /* 7 */
{0,1,2,3,4,5,6, -1}, /* 8 */
{0,1,2,3,5,6, -1} /* 9 */
};
/* Draws one segment */
void draw_seg(int seg, int x, int y, int width, int height)
{
int p1 = seg_points[seg][0];
int p2 = seg_points[seg][1];
int x1 = point_coords[p1][0];
int y1 = point_coords[p1][1];
int x2 = point_coords[p2][0];
int y2 = point_coords[p2][1];
/* It draws parallel lines of different lengths for thicker segments */
if(seg == 0 || seg == 3 || seg == 6)
{
rb->lcd_drawline(x + x1 * width + 1, y + y1 * height / 2,
x + x2 * width - 1 , y + y2 * height / 2);
rb->lcd_drawline(x + x1 * width + 2, y + y1 * height / 2 - 1,
x + x2 * width - 2, y + y2 * height / 2 - 1);
rb->lcd_drawline(x + x1 * width + 2, y + y1 * height / 2 + 1,
x + x2 * width - 2, y + y2 * height / 2 + 1);
rb->lcd_drawline(x + x1 * width + 3, y + y1 * height / 2 - 2,
x + x2 * width - 3, y + y2 * height / 2 - 2);
rb->lcd_drawline(x + x1 * width + 3, y + y1 * height / 2 + 2,
x + x2 * width - 3, y + y2 * height / 2 + 2);
}
else
{
rb->lcd_drawline(x + x1 * width, y + y1 * height / 2 + 1,
x + x2 * width , y + y2 * height / 2 - 1);
rb->lcd_drawline(x + x1 * width - 1, y + y1 * height / 2 + 2,
x + x2 * width - 1, y + y2 * height / 2 - 2);
rb->lcd_drawline(x + x1 * width + 1, y + y1 * height / 2 + 2,
x + x2 * width + 1, y + y2 * height / 2 - 2);
rb->lcd_drawline(x + x1 * width - 2, y + y1 * height / 2 + 3,
x + x2 * width - 2, y + y2 * height / 2 - 3);
rb->lcd_drawline(x + x1 * width + 2, y + y1 * height / 2 + 3,
x + x2 * width + 2, y + y2 * height / 2 - 3);
}
}
/* Draws one digit */
void draw_7seg_digit(int digit, int x, int y, int width, int height)
{
int i;
int c;
for(i = 0;digit_segs[digit][i] >= 0;i++)
{
c = digit_segs[digit][i];
draw_seg(c, x, y, width, height);
}
}
/* Draws the entire 7-segment hour-minute time display */
void draw_7seg_time(int hour, int minute, int x, int y, int width, int height,
bool colon)
{
int xpos = x;
draw_7seg_digit(hour / 10, xpos, y, width, height);
xpos += width + 6;
draw_7seg_digit(hour % 10, xpos, y, width, height);
xpos += width + 6;
if(colon)
{
rb->lcd_drawline(xpos, y + height/3 + 2,
xpos, y + height/3 + 2);
rb->lcd_drawline(xpos+1, y + height/3 + 1,
xpos+1, y + height/3 + 3);
rb->lcd_drawline(xpos+2, y + height/3,
xpos+2, y + height/3 + 4);
rb->lcd_drawline(xpos+3, y + height/3 + 1,
xpos+3, y + height/3 + 3);
rb->lcd_drawline(xpos+4, y + height/3 + 2,
xpos+4, y + height/3 + 2);
rb->lcd_drawline(xpos, y + height-height/3 + 2,
xpos, y + height-height/3 + 2);
rb->lcd_drawline(xpos+1, y + height-height/3 + 1,
xpos+1, y + height-height/3 + 3);
rb->lcd_drawline(xpos+2, y + height-height/3,
xpos+2, y + height-height/3 + 4);
rb->lcd_drawline(xpos+3, y + height-height/3 + 1,
xpos+3, y + height-height/3 + 3);
rb->lcd_drawline(xpos+4, y + height-height/3 + 2,
xpos+4, y + height-height/3 + 2);
}
xpos += 12;
draw_7seg_digit(minute / 10, xpos, y, width, height);
xpos += width + 6;
draw_7seg_digit(minute % 10, xpos, y, width, height);
xpos += width + 6;
}
#endif