Accept FS#9224 by Teruaki Kawashima, adding support for all RTC equipped targets...
[kugel-rb.git] / apps / plugins / stopwatch.c
blobb78225488dbd81836637cfbf6adf265f5cd69133
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004 Mike Holden
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "plugin.h"
24 PLUGIN_HEADER
26 #ifdef HAVE_LCD_BITMAP
27 #define TIMER_Y 1
28 #else
29 #define TIMER_Y 0
30 #endif
32 #define LAP_Y TIMER_Y+1
33 #define MAX_LAPS 64
35 #define STOPWATCH_FILE ROCKBOX_DIR "/apps/stopwatch.dat"
37 /* variable button definitions */
38 #if CONFIG_KEYPAD == RECORDER_PAD
39 #define STOPWATCH_QUIT BUTTON_OFF
40 #define STOPWATCH_START_STOP BUTTON_PLAY
41 #define STOPWATCH_RESET_TIMER BUTTON_LEFT
42 #define STOPWATCH_LAP_TIMER BUTTON_ON
43 #define STOPWATCH_SCROLL_UP BUTTON_UP
44 #define STOPWATCH_SCROLL_DOWN BUTTON_DOWN
45 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
46 #define STOPWATCH_QUIT BUTTON_OFF
47 #define STOPWATCH_START_STOP BUTTON_SELECT
48 #define STOPWATCH_RESET_TIMER BUTTON_LEFT
49 #define STOPWATCH_LAP_TIMER BUTTON_ON
50 #define STOPWATCH_SCROLL_UP BUTTON_UP
51 #define STOPWATCH_SCROLL_DOWN BUTTON_DOWN
52 #elif CONFIG_KEYPAD == ONDIO_PAD
53 #define STOPWATCH_QUIT BUTTON_OFF
54 #define STOPWATCH_START_STOP BUTTON_RIGHT
55 #define STOPWATCH_RESET_TIMER BUTTON_LEFT
56 #define STOPWATCH_LAP_TIMER BUTTON_MENU
57 #define STOPWATCH_SCROLL_UP BUTTON_UP
58 #define STOPWATCH_SCROLL_DOWN BUTTON_DOWN
59 #elif CONFIG_KEYPAD == PLAYER_PAD
60 #define STOPWATCH_QUIT BUTTON_MENU
61 #define STOPWATCH_START_STOP BUTTON_PLAY
62 #define STOPWATCH_RESET_TIMER BUTTON_STOP
63 #define STOPWATCH_LAP_TIMER BUTTON_ON
64 #define STOPWATCH_SCROLL_UP BUTTON_RIGHT
65 #define STOPWATCH_SCROLL_DOWN BUTTON_LEFT
66 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
67 (CONFIG_KEYPAD == IRIVER_H300_PAD)
68 #define STOPWATCH_QUIT BUTTON_OFF
69 #define STOPWATCH_START_STOP BUTTON_SELECT
70 #define STOPWATCH_RESET_TIMER BUTTON_DOWN
71 #define STOPWATCH_LAP_TIMER BUTTON_ON
72 #define STOPWATCH_SCROLL_UP BUTTON_RIGHT
73 #define STOPWATCH_SCROLL_DOWN BUTTON_LEFT
75 #define STOPWATCH_RC_QUIT BUTTON_RC_STOP
77 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
78 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
79 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
80 #define STOPWATCH_QUIT BUTTON_MENU
81 #define STOPWATCH_START_STOP BUTTON_SELECT
82 #define STOPWATCH_RESET_TIMER BUTTON_LEFT
83 #define STOPWATCH_LAP_TIMER BUTTON_RIGHT
84 #define STOPWATCH_SCROLL_UP BUTTON_SCROLL_FWD
85 #define STOPWATCH_SCROLL_DOWN BUTTON_SCROLL_BACK
86 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
87 #define STOPWATCH_QUIT BUTTON_PLAY
88 #define STOPWATCH_START_STOP BUTTON_MODE
89 #define STOPWATCH_RESET_TIMER BUTTON_EQ
90 #define STOPWATCH_LAP_TIMER BUTTON_SELECT
91 #define STOPWATCH_SCROLL_UP BUTTON_UP
92 #define STOPWATCH_SCROLL_DOWN BUTTON_DOWN
93 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
94 #define STOPWATCH_QUIT BUTTON_POWER
95 #define STOPWATCH_START_STOP BUTTON_PLAY
96 #define STOPWATCH_RESET_TIMER BUTTON_REC
97 #define STOPWATCH_LAP_TIMER BUTTON_SELECT
98 #define STOPWATCH_SCROLL_UP BUTTON_UP
99 #define STOPWATCH_SCROLL_DOWN BUTTON_DOWN
100 #elif CONFIG_KEYPAD == GIGABEAT_PAD
101 #define STOPWATCH_QUIT BUTTON_POWER
102 #define STOPWATCH_START_STOP BUTTON_SELECT
103 #define STOPWATCH_RESET_TIMER BUTTON_A
104 #define STOPWATCH_LAP_TIMER BUTTON_MENU
105 #define STOPWATCH_SCROLL_UP BUTTON_UP
106 #define STOPWATCH_SCROLL_DOWN BUTTON_DOWN
107 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
108 (CONFIG_KEYPAD == SANSA_C200_PAD)
109 #define STOPWATCH_QUIT BUTTON_POWER
110 #define STOPWATCH_START_STOP BUTTON_RIGHT
111 #define STOPWATCH_RESET_TIMER BUTTON_LEFT
112 #define STOPWATCH_LAP_TIMER BUTTON_SELECT
113 #define STOPWATCH_SCROLL_UP BUTTON_UP
114 #define STOPWATCH_SCROLL_DOWN BUTTON_DOWN
115 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
116 #define STOPWATCH_QUIT BUTTON_POWER
117 #define STOPWATCH_START_STOP BUTTON_PLAY
118 #define STOPWATCH_RESET_TIMER BUTTON_REW
119 #define STOPWATCH_LAP_TIMER BUTTON_FF
120 #define STOPWATCH_SCROLL_UP BUTTON_SCROLL_UP
121 #define STOPWATCH_SCROLL_DOWN BUTTON_SCROLL_DOWN
122 #elif CONFIG_KEYPAD == MROBE500_PAD
123 #define STOPWATCH_QUIT BUTTON_POWER
124 #define STOPWATCH_START_STOP BUTTON_RC_HEART
125 #define STOPWATCH_RESET_TIMER BUTTON_RC_MODE
126 #define STOPWATCH_LAP_TIMER BUTTON_RC_PLAY
127 #define STOPWATCH_SCROLL_UP BUTTON_RIGHT
128 #define STOPWATCH_SCROLL_DOWN BUTTON_LEFT
129 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
130 #define STOPWATCH_QUIT BUTTON_BACK
131 #define STOPWATCH_START_STOP BUTTON_PLAY
132 #define STOPWATCH_RESET_TIMER BUTTON_MENU
133 #define STOPWATCH_LAP_TIMER BUTTON_SELECT
134 #define STOPWATCH_SCROLL_UP BUTTON_UP
135 #define STOPWATCH_SCROLL_DOWN BUTTON_DOWN
136 #elif CONFIG_KEYPAD == MROBE100_PAD
137 #define STOPWATCH_QUIT BUTTON_POWER
138 #define STOPWATCH_START_STOP BUTTON_SELECT
139 #define STOPWATCH_RESET_TIMER BUTTON_DISPLAY
140 #define STOPWATCH_LAP_TIMER BUTTON_MENU
141 #define STOPWATCH_SCROLL_UP BUTTON_UP
142 #define STOPWATCH_SCROLL_DOWN BUTTON_DOWN
143 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
144 #define STOPWATCH_QUIT BUTTON_RC_REC
145 #define STOPWATCH_START_STOP BUTTON_RC_PLAY
146 #define STOPWATCH_RESET_TIMER BUTTON_RC_REW
147 #define STOPWATCH_LAP_TIMER BUTTON_RC_FF
148 #define STOPWATCH_SCROLL_UP BUTTON_RC_VOL_UP
149 #define STOPWATCH_SCROLL_DOWN BUTTON_RC_VOL_DOWN
150 #define STOPWATCH_RC_QUIT BUTTON_REC
151 #elif CONFIG_KEYPAD == COWOND2_PAD
152 #define STOPWATCH_QUIT BUTTON_POWER
153 #elif CONFIG_KEYPAD == IAUDIO67_PAD
154 #define STOPWATCH_QUIT BUTTON_MENU
155 #define STOPWATCH_START_STOP BUTTON_PLAY
156 #define STOPWATCH_RESET_TIMER BUTTON_STOP
157 #define STOPWATCH_LAP_TIMER BUTTON_LEFT
158 #define STOPWATCH_SCROLL_UP BUTTON_VOLUP
159 #define STOPWATCH_SCROLL_DOWN BUTTON_VOLDOWN
160 #define STOPWATCH_RC_QUIT BUTTON_POWER
161 #else
162 #error No keymap defined!
163 #endif
165 #ifdef HAVE_TOUCHSCREEN
166 #ifndef STOPWATCH_QUIT
167 #define STOPWATCH_QUIT BUTTON_TOPLEFT
168 #endif
169 #ifndef STOPWATCH_START_STOP
170 #define STOPWATCH_START_STOP BUTTON_CENTER
171 #endif
172 #ifndef STOPWATCH_RESET_TIMER
173 #define STOPWATCH_RESET_TIMER BUTTON_MIDRIGHT
174 #endif
175 #ifndef STOPWATCH_LAP_TIMER
176 #define STOPWATCH_LAP_TIMER BUTTON_MIDLEFT
177 #endif
178 #ifndef STOPWATCH_SCROLL_UP
179 #define STOPWATCH_SCROLL_UP BUTTON_TOPMIDDLE
180 #endif
181 #ifndef STOPWATCH_SCROLL_DOWN
182 #define STOPWATCH_SCROLL_DOWN BUTTON_BOTTOMMIDDLE
183 #endif
184 #endif
186 static const struct plugin_api* rb;
188 static int stopwatch = 0;
189 static long start_at = 0;
190 static int prev_total = 0;
191 static bool counting = false;
192 static int curr_lap = 0;
193 static int lap_scroll = 0;
194 static int lap_start;
195 static int lap_times[MAX_LAPS];
197 static void ticks_to_string(int ticks,int lap,int buflen, char * buf)
199 int hours, minutes, seconds, cs;
201 hours = ticks / (HZ * 3600);
202 ticks -= (HZ * hours * 3600);
203 minutes = ticks / (HZ * 60);
204 ticks -= (HZ * minutes * 60);
205 seconds = ticks / HZ;
206 ticks -= (HZ * seconds);
207 cs = ticks;
208 if (!lap)
210 rb->snprintf(buf, buflen,
211 "%2d:%02d:%02d.%02d",
212 hours, minutes, seconds, cs);
214 else
217 if (lap > 1)
219 int last_ticks, last_hours, last_minutes, last_seconds, last_cs;
220 last_ticks = lap_times[(lap-1)%MAX_LAPS] - lap_times[(lap-2)%MAX_LAPS];
221 last_hours = last_ticks / (HZ * 3600);
222 last_ticks -= (HZ * last_hours * 3600);
223 last_minutes = last_ticks / (HZ * 60);
224 last_ticks -= (HZ * last_minutes * 60);
225 last_seconds = last_ticks / HZ;
226 last_ticks -= (HZ * last_seconds);
227 last_cs = last_ticks;
229 rb->snprintf(buf, buflen,
230 "%2d %2d:%02d:%02d.%02d [%2d:%02d:%02d.%02d]",
231 lap, hours, minutes, seconds, cs, last_hours,
232 last_minutes, last_seconds, last_cs);
234 else
236 rb->snprintf(buf, buflen,
237 "%2d %2d:%02d:%02d.%02d",
238 lap, hours, minutes, seconds, cs);
244 * Load saved stopwatch state, if exists.
246 void load_stopwatch(void)
248 int fd;
250 fd = rb->open(STOPWATCH_FILE, O_RDONLY);
252 if (fd < 0)
254 return;
257 /* variable stopwatch isn't saved/loaded, because it is only used
258 * temporarily in main loop
261 rb->read(fd, &start_at, sizeof(start_at));
262 rb->read(fd, &prev_total, sizeof(prev_total));
263 rb->read(fd, &counting, sizeof(counting));
264 rb->read(fd, &curr_lap, sizeof(curr_lap));
265 rb->read(fd, &lap_scroll, sizeof(lap_scroll));
266 rb->read(fd, &lap_start, sizeof(lap_start));
267 rb->read(fd, lap_times, sizeof(lap_times));
269 if (counting && start_at > *rb->current_tick)
271 /* Stopwatch started in the future? Unlikely; probably started on a
272 * previous session and powered off in-between. We'll keep
273 * everything intact (user can clear manually) but stop the
274 * stopwatch to avoid negative timing.
276 start_at = 0;
277 counting = false;
280 rb->close(fd);
284 * Save stopwatch state.
286 void save_stopwatch(void)
288 int fd;
290 fd = rb->open(STOPWATCH_FILE, O_CREAT|O_WRONLY|O_TRUNC);
292 if (fd < 0)
294 return;
297 /* variable stopwatch isn't saved/loaded, because it is only used
298 * temporarily in main loop
301 rb->write(fd, &start_at, sizeof(start_at));
302 rb->write(fd, &prev_total, sizeof(prev_total));
303 rb->write(fd, &counting, sizeof(counting));
304 rb->write(fd, &curr_lap, sizeof(curr_lap));
305 rb->write(fd, &lap_scroll, sizeof(lap_scroll));
306 rb->write(fd, &lap_start, sizeof(lap_start));
307 rb->write(fd, lap_times, sizeof(lap_times));
309 rb->close(fd);
312 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
314 char buf[32];
315 int button;
316 int lap;
317 int done = false;
318 bool update_lap = true;
319 int lines;
321 (void)parameter;
322 rb = api;
324 #ifdef HAVE_LCD_BITMAP
325 int h;
326 rb->lcd_setfont(FONT_UI);
327 rb->lcd_getstringsize("M", NULL, &h);
328 lines = (LCD_HEIGHT / h) - (LAP_Y);
329 #else
330 lines = 1;
331 #endif
333 load_stopwatch();
335 rb->lcd_clear_display();
337 while (!done)
339 if (counting)
341 stopwatch = prev_total + *rb->current_tick - start_at;
343 else
345 stopwatch = prev_total;
348 ticks_to_string(stopwatch,0,32,buf);
349 rb->lcd_puts(0, TIMER_Y, buf);
351 if(update_lap)
353 lap_start = curr_lap - lap_scroll;
354 for (lap = lap_start; lap > lap_start - lines; lap--)
356 if (lap > 0)
358 ticks_to_string(lap_times[(lap-1)%MAX_LAPS],lap,32,buf);
359 rb->lcd_puts_scroll(0, LAP_Y + lap_start - lap, buf);
361 else
363 rb->lcd_puts(0, LAP_Y + lap_start - lap,
364 " ");
367 update_lap = false;
370 rb->lcd_update();
372 if (! counting)
374 button = rb->button_get(true);
376 else
378 button = rb->button_get_w_tmo(10);
380 /* Make sure that the jukebox isn't powered off
381 automatically */
382 rb->reset_poweroff_timer();
384 switch (button)
387 /* exit */
388 #ifdef STOPWATCH_RC_QUIT
389 case STOPWATCH_RC_QUIT:
390 #endif
391 case STOPWATCH_QUIT:
392 save_stopwatch();
393 done = true;
394 break;
396 /* Stop/Start toggle */
397 case STOPWATCH_START_STOP:
398 counting = ! counting;
399 if (counting)
401 start_at = *rb->current_tick;
402 stopwatch = prev_total + *rb->current_tick - start_at;
404 else
406 prev_total += *rb->current_tick - start_at;
407 stopwatch = prev_total;
409 break;
411 /* Reset timer */
412 case STOPWATCH_RESET_TIMER:
413 if (!counting)
415 prev_total = 0;
416 curr_lap = 0;
417 update_lap = true;
419 break;
421 /* Lap timer */
422 case STOPWATCH_LAP_TIMER:
423 lap_times[curr_lap%MAX_LAPS] = stopwatch;
424 curr_lap++;
425 update_lap = true;
426 break;
428 /* Scroll Lap timer up */
429 case STOPWATCH_SCROLL_UP:
430 if (lap_scroll > 0)
432 lap_scroll --;
433 update_lap = true;
435 break;
437 /* Scroll Lap timer down */
438 case STOPWATCH_SCROLL_DOWN:
439 if ((lap_scroll < curr_lap - lines) &&
440 (lap_scroll < (MAX_LAPS - lines)) )
442 lap_scroll ++;
443 update_lap = true;
445 break;
447 default:
448 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
449 return PLUGIN_USB_CONNECTED;
450 break;
453 return PLUGIN_OK;