Colour targets: Revert an optimisation from almost 18 months ago that actually turned...
[Rockbox.git] / apps / plugins / stopwatch.c
blob6bd6d2be2a169c533066670308c11689fc67bea5
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 #else
154 #error No keymap defined!
155 #endif
157 #ifdef HAVE_TOUCHPAD
158 #ifndef STOPWATCH_QUIT
159 #define STOPWATCH_QUIT BUTTON_TOPLEFT
160 #endif
161 #ifndef STOPWATCH_START_STOP
162 #define STOPWATCH_START_STOP BUTTON_CENTER
163 #endif
164 #ifndef STOPWATCH_RESET_TIMER
165 #define STOPWATCH_RESET_TIMER BUTTON_MIDRIGHT
166 #endif
167 #ifndef STOPWATCH_LAP_TIMER
168 #define STOPWATCH_LAP_TIMER BUTTON_MIDLEFT
169 #endif
170 #ifndef STOPWATCH_SCROLL_UP
171 #define STOPWATCH_SCROLL_UP BUTTON_TOPMIDDLE
172 #endif
173 #ifndef STOPWATCH_SCROLL_DOWN
174 #define STOPWATCH_SCROLL_DOWN BUTTON_BOTTOMMIDDLE
175 #endif
176 #endif
178 static const struct plugin_api* rb;
180 static int stopwatch = 0;
181 static long start_at = 0;
182 static int prev_total = 0;
183 static bool counting = false;
184 static int curr_lap = 0;
185 static int lap_scroll = 0;
186 static int lap_start;
187 static int lap_times[MAX_LAPS];
189 static void ticks_to_string(int ticks,int lap,int buflen, char * buf)
191 int hours, minutes, seconds, cs;
193 hours = ticks / (HZ * 3600);
194 ticks -= (HZ * hours * 3600);
195 minutes = ticks / (HZ * 60);
196 ticks -= (HZ * minutes * 60);
197 seconds = ticks / HZ;
198 ticks -= (HZ * seconds);
199 cs = ticks;
200 if (!lap)
202 rb->snprintf(buf, buflen,
203 "%2d:%02d:%02d.%02d",
204 hours, minutes, seconds, cs);
206 else
209 if (lap > 1)
211 int last_ticks, last_hours, last_minutes, last_seconds, last_cs;
212 last_ticks = lap_times[(lap-1)%MAX_LAPS] - lap_times[(lap-2)%MAX_LAPS];
213 last_hours = last_ticks / (HZ * 3600);
214 last_ticks -= (HZ * last_hours * 3600);
215 last_minutes = last_ticks / (HZ * 60);
216 last_ticks -= (HZ * last_minutes * 60);
217 last_seconds = last_ticks / HZ;
218 last_ticks -= (HZ * last_seconds);
219 last_cs = last_ticks;
221 rb->snprintf(buf, buflen,
222 "%2d %2d:%02d:%02d.%02d [%2d:%02d:%02d.%02d]",
223 lap, hours, minutes, seconds, cs, last_hours,
224 last_minutes, last_seconds, last_cs);
226 else
228 rb->snprintf(buf, buflen,
229 "%2d %2d:%02d:%02d.%02d",
230 lap, hours, minutes, seconds, cs);
236 * Load saved stopwatch state, if exists.
238 void load_stopwatch(void)
240 int fd;
242 fd = rb->open(STOPWATCH_FILE, O_RDONLY);
244 if (fd < 0)
246 return;
249 /* variable stopwatch isn't saved/loaded, because it is only used
250 * temporarily in main loop
253 rb->read(fd, &start_at, sizeof(start_at));
254 rb->read(fd, &prev_total, sizeof(prev_total));
255 rb->read(fd, &counting, sizeof(counting));
256 rb->read(fd, &curr_lap, sizeof(curr_lap));
257 rb->read(fd, &lap_scroll, sizeof(lap_scroll));
258 rb->read(fd, &lap_start, sizeof(lap_start));
259 rb->read(fd, lap_times, sizeof(lap_times));
261 if (counting && start_at > *rb->current_tick)
263 /* Stopwatch started in the future? Unlikely; probably started on a
264 * previous session and powered off in-between. We'll keep
265 * everything intact (user can clear manually) but stop the
266 * stopwatch to avoid negative timing.
268 start_at = 0;
269 counting = false;
272 rb->close(fd);
276 * Save stopwatch state.
278 void save_stopwatch(void)
280 int fd;
282 fd = rb->open(STOPWATCH_FILE, O_CREAT|O_WRONLY|O_TRUNC);
284 if (fd < 0)
286 return;
289 /* variable stopwatch isn't saved/loaded, because it is only used
290 * temporarily in main loop
293 rb->write(fd, &start_at, sizeof(start_at));
294 rb->write(fd, &prev_total, sizeof(prev_total));
295 rb->write(fd, &counting, sizeof(counting));
296 rb->write(fd, &curr_lap, sizeof(curr_lap));
297 rb->write(fd, &lap_scroll, sizeof(lap_scroll));
298 rb->write(fd, &lap_start, sizeof(lap_start));
299 rb->write(fd, lap_times, sizeof(lap_times));
301 rb->close(fd);
304 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
306 char buf[32];
307 int button;
308 int lap;
309 int done = false;
310 bool update_lap = true;
311 int lines;
313 (void)parameter;
314 rb = api;
316 #ifdef HAVE_LCD_BITMAP
317 int h;
318 rb->lcd_setfont(FONT_UI);
319 rb->lcd_getstringsize("M", NULL, &h);
320 lines = (LCD_HEIGHT / h) - (LAP_Y);
321 #else
322 lines = 1;
323 #endif
325 load_stopwatch();
327 rb->lcd_clear_display();
329 while (!done)
331 if (counting)
333 stopwatch = prev_total + *rb->current_tick - start_at;
335 else
337 stopwatch = prev_total;
340 ticks_to_string(stopwatch,0,32,buf);
341 rb->lcd_puts(0, TIMER_Y, buf);
343 if(update_lap)
345 lap_start = curr_lap - lap_scroll;
346 for (lap = lap_start; lap > lap_start - lines; lap--)
348 if (lap > 0)
350 ticks_to_string(lap_times[(lap-1)%MAX_LAPS],lap,32,buf);
351 rb->lcd_puts_scroll(0, LAP_Y + lap_start - lap, buf);
353 else
355 rb->lcd_puts(0, LAP_Y + lap_start - lap,
356 " ");
359 update_lap = false;
362 rb->lcd_update();
364 if (! counting)
366 button = rb->button_get(true);
368 else
370 button = rb->button_get_w_tmo(10);
372 /* Make sure that the jukebox isn't powered off
373 automatically */
374 rb->reset_poweroff_timer();
376 switch (button)
379 /* exit */
380 #ifdef STOPWATCH_RC_QUIT
381 case STOPWATCH_RC_QUIT:
382 #endif
383 case STOPWATCH_QUIT:
384 save_stopwatch();
385 done = true;
386 break;
388 /* Stop/Start toggle */
389 case STOPWATCH_START_STOP:
390 counting = ! counting;
391 if (counting)
393 start_at = *rb->current_tick;
394 stopwatch = prev_total + *rb->current_tick - start_at;
396 else
398 prev_total += *rb->current_tick - start_at;
399 stopwatch = prev_total;
401 break;
403 /* Reset timer */
404 case STOPWATCH_RESET_TIMER:
405 if (!counting)
407 prev_total = 0;
408 curr_lap = 0;
409 update_lap = true;
411 break;
413 /* Lap timer */
414 case STOPWATCH_LAP_TIMER:
415 lap_times[curr_lap%MAX_LAPS] = stopwatch;
416 curr_lap++;
417 update_lap = true;
418 break;
420 /* Scroll Lap timer up */
421 case STOPWATCH_SCROLL_UP:
422 if (lap_scroll > 0)
424 lap_scroll --;
425 update_lap = true;
427 break;
429 /* Scroll Lap timer down */
430 case STOPWATCH_SCROLL_DOWN:
431 if ((lap_scroll < curr_lap - lines) &&
432 (lap_scroll < (MAX_LAPS - lines)) )
434 lap_scroll ++;
435 update_lap = true;
437 break;
439 default:
440 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
441 return PLUGIN_USB_CONNECTED;
442 break;
445 return PLUGIN_OK;