Updated our source code header to explicitly mention that we are GPL v2 or
[Rockbox.git] / apps / plugins / chessclock.c
blobd808956f9d3b2d7970791042d86cddc3bc73bf42
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Kjell Ericson
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 ****************************************************************************/
21 #include "plugin.h"
23 PLUGIN_HEADER
25 /* variable button definitions */
26 #if CONFIG_KEYPAD == RECORDER_PAD
27 #define CHC_QUIT BUTTON_OFF
28 #define CHC_STARTSTOP BUTTON_PLAY
29 #define CHC_RESET BUTTON_LEFT
30 #define CHC_MENU BUTTON_F1
31 #define CHC_SETTINGS_INC BUTTON_UP
32 #define CHC_SETTINGS_DEC BUTTON_DOWN
33 #define CHC_SETTINGS_OK BUTTON_PLAY
34 #define CHC_SETTINGS_OK2 BUTTON_LEFT
35 #define CHC_SETTINGS_CANCEL BUTTON_OFF
37 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
38 #define CHC_QUIT BUTTON_OFF
39 #define CHC_STARTSTOP BUTTON_SELECT
40 #define CHC_RESET BUTTON_LEFT
41 #define CHC_MENU BUTTON_F1
42 #define CHC_SETTINGS_INC BUTTON_UP
43 #define CHC_SETTINGS_DEC BUTTON_DOWN
44 #define CHC_SETTINGS_OK BUTTON_SELECT
45 #define CHC_SETTINGS_OK2 BUTTON_LEFT
46 #define CHC_SETTINGS_CANCEL BUTTON_OFF
48 #elif CONFIG_KEYPAD == ONDIO_PAD
49 #define CHC_QUIT BUTTON_OFF
50 #define CHC_STARTSTOP BUTTON_RIGHT
51 #define CHC_RESET BUTTON_LEFT
52 #define CHC_MENU BUTTON_MENU
53 #define CHC_SETTINGS_INC BUTTON_UP
54 #define CHC_SETTINGS_DEC BUTTON_DOWN
55 #define CHC_SETTINGS_OK BUTTON_RIGHT
56 #define CHC_SETTINGS_OK2 BUTTON_LEFT
57 #define CHC_SETTINGS_CANCEL BUTTON_MENU
59 #elif CONFIG_KEYPAD == PLAYER_PAD
60 #define CHC_QUIT BUTTON_ON
61 #define CHC_STARTSTOP BUTTON_PLAY
62 #define CHC_RESET BUTTON_STOP
63 #define CHC_MENU BUTTON_MENU
64 #define CHC_SETTINGS_INC BUTTON_RIGHT
65 #define CHC_SETTINGS_DEC BUTTON_LEFT
66 #define CHC_SETTINGS_OK BUTTON_PLAY
67 #define CHC_SETTINGS_CANCEL BUTTON_STOP
68 #define CHC_SETTINGS_CANCEL2 BUTTON_MENU
70 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
71 (CONFIG_KEYPAD == IRIVER_H300_PAD)
72 #define CHC_QUIT BUTTON_SELECT
73 #define CHC_STARTSTOP BUTTON_ON
74 #define CHC_RESET BUTTON_OFF
75 #define CHC_MENU BUTTON_REC
76 #define CHC_SETTINGS_INC BUTTON_RIGHT
77 #define CHC_SETTINGS_DEC BUTTON_LEFT
78 #define CHC_SETTINGS_OK BUTTON_ON
79 #define CHC_SETTINGS_CANCEL BUTTON_OFF
80 #define CHC_SETTINGS_CANCEL2 BUTTON_REC
82 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
83 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
84 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
85 #define CHC_QUIT BUTTON_PLAY
86 #define CHC_STARTSTOP BUTTON_SELECT
87 #define CHC_RESET BUTTON_LEFT
88 #define CHC_MENU BUTTON_MENU
89 #define CHC_SETTINGS_INC BUTTON_SCROLL_FWD
90 #define CHC_SETTINGS_DEC BUTTON_SCROLL_BACK
91 #define CHC_SETTINGS_OK BUTTON_SELECT
92 #define CHC_SETTINGS_CANCEL BUTTON_MENU
94 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
95 #define CHC_QUIT BUTTON_PLAY
96 #define CHC_STARTSTOP BUTTON_MODE
97 #define CHC_RESET BUTTON_EQ
98 #define CHC_MENU BUTTON_SELECT
99 #define CHC_SETTINGS_INC BUTTON_RIGHT
100 #define CHC_SETTINGS_DEC BUTTON_LEFT
101 #define CHC_SETTINGS_OK BUTTON_SELECT
102 #define CHC_SETTINGS_CANCEL BUTTON_PLAY
104 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
105 #define CHC_QUIT BUTTON_REC
106 #define CHC_STARTSTOP BUTTON_PLAY
107 #define CHC_RESET BUTTON_POWER
108 #define CHC_MENU BUTTON_SELECT
109 #define CHC_SETTINGS_INC BUTTON_RIGHT
110 #define CHC_SETTINGS_DEC BUTTON_LEFT
111 #define CHC_SETTINGS_OK BUTTON_SELECT
112 #define CHC_SETTINGS_CANCEL BUTTON_REC
114 #elif CONFIG_KEYPAD == GIGABEAT_PAD
115 #define CHC_QUIT BUTTON_POWER
116 #define CHC_STARTSTOP BUTTON_SELECT
117 #define CHC_RESET BUTTON_A
118 #define CHC_MENU BUTTON_MENU
119 #define CHC_SETTINGS_INC BUTTON_UP
120 #define CHC_SETTINGS_DEC BUTTON_DOWN
121 #define CHC_SETTINGS_OK BUTTON_SELECT
122 #define CHC_SETTINGS_CANCEL BUTTON_POWER
124 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
125 (CONFIG_KEYPAD == SANSA_C200_PAD)
126 #define CHC_QUIT BUTTON_POWER
127 #define CHC_STARTSTOP BUTTON_SELECT
128 #define CHC_RESET BUTTON_DOWN
129 #define CHC_MENU BUTTON_UP
130 #define CHC_SETTINGS_INC BUTTON_RIGHT
131 #define CHC_SETTINGS_DEC BUTTON_LEFT
132 #define CHC_SETTINGS_OK BUTTON_SELECT
133 #define CHC_SETTINGS_CANCEL BUTTON_POWER
135 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
136 #define CHC_QUIT BUTTON_POWER
137 #define CHC_STARTSTOP BUTTON_PLAY
138 #define CHC_RESET BUTTON_FF
139 #define CHC_MENU BUTTON_REW
140 #define CHC_SETTINGS_INC BUTTON_RIGHT
141 #define CHC_SETTINGS_DEC BUTTON_LEFT
142 #define CHC_SETTINGS_OK BUTTON_PLAY
143 #define CHC_SETTINGS_CANCEL BUTTON_POWER
145 #elif CONFIG_KEYPAD == MROBE500_PAD
146 #define CHC_QUIT BUTTON_POWER
147 #define CHC_STARTSTOP BUTTON_RC_PLAY
148 #define CHC_RESET BUTTON_RC_HEART
149 #define CHC_MENU BUTTON_RC_MODE
150 #define CHC_SETTINGS_INC BUTTON_RC_VOL_UP
151 #define CHC_SETTINGS_DEC BUTTON_RC_VOL_DOWN
152 #define CHC_SETTINGS_OK BUTTON_RC_PLAY
153 #define CHC_SETTINGS_CANCEL BUTTON_POWER
155 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
156 #define CHC_QUIT BUTTON_BACK
157 #define CHC_STARTSTOP BUTTON_PLAY
158 #define CHC_RESET BUTTON_PREV
159 #define CHC_MENU BUTTON_MENU
160 #define CHC_SETTINGS_INC BUTTON_UP
161 #define CHC_SETTINGS_DEC BUTTON_DOWN
162 #define CHC_SETTINGS_OK BUTTON_SELECT
163 #define CHC_SETTINGS_CANCEL BUTTON_BACK
165 #elif CONFIG_KEYPAD == MROBE100_PAD
166 #define CHC_QUIT BUTTON_POWER
167 #define CHC_STARTSTOP BUTTON_SELECT
168 #define CHC_RESET BUTTON_DISPLAY
169 #define CHC_MENU BUTTON_MENU
170 #define CHC_SETTINGS_INC BUTTON_UP
171 #define CHC_SETTINGS_DEC BUTTON_DOWN
172 #define CHC_SETTINGS_OK BUTTON_SELECT
173 #define CHC_SETTINGS_CANCEL BUTTON_POWER
175 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
176 #define CHC_QUIT BUTTON_RC_REC
177 #define CHC_STARTSTOP BUTTON_RC_PLAY
178 #define CHC_RESET BUTTON_RC_REW
179 #define CHC_MENU BUTTON_RC_MENU
180 #define CHC_SETTINGS_INC BUTTON_RC_VOL_UP
181 #define CHC_SETTINGS_DEC BUTTON_RC_VOL_DOWN
182 #define CHC_SETTINGS_OK BUTTON_RC_PLAY
183 #define CHC_SETTINGS_CANCEL BUTTON_RC_REC
185 #elif CONFIG_KEYPAD == COWOND2_PAD
186 #define CHC_QUIT BUTTON_POWER
187 #define CHC_RESET (BUTTON_CENTER|BUTTON_MENU)
188 #define CHC_MENU BUTTON_MENU
189 #define CHC_SETTINGS_INC BUTTON_PLUS
190 #define CHC_SETTINGS_DEC BUTTON_MINUS
191 #define CHC_SETTINGS_CANCEL BUTTON_POWER
193 #else
194 #error No keymap defined!
195 #endif
197 #ifdef HAVE_TOUCHPAD
198 #ifndef CHC_SETTINGS_OK
199 #define CHC_SETTINGS_OK BUTTON_CENTER
200 #endif
201 #ifndef CHC_STARTSTOP
202 #define CHC_STARTSTOP BUTTON_CENTER
203 #endif
204 #ifndef CHC_SETTINGS_INC
205 #define CHC_SETTINGS_INC BUTTON_TOPMIDDLE
206 #endif
207 #ifndef CHC_SETTINGS_DEC
208 #define CHC_SETTINGS_DEC BUTTON_BOTTOMMIDDLE
209 #endif
210 #ifndef CHC_RESET
211 #define CHC_RESET BUTTON_TOPLEFT
212 #endif
213 #ifndef CHC_MENU
214 #define CHC_MENU BUTTON_TOPRIGHT
215 #endif
216 #endif
219 /* leave first line blank on bitmap display, for pause icon */
220 #ifdef HAVE_LCD_BITMAP
221 #define FIRST_LINE 1
222 #else
223 #define FIRST_LINE 0
224 #endif
226 /* here is a global api struct pointer. while not strictly necessary,
227 it's nice not to have to pass the api pointer in all function calls
228 in the plugin */
229 static const struct plugin_api* rb;
230 MEM_FUNCTION_WRAPPERS(rb);
231 #define MAX_PLAYERS 10
233 static struct {
234 int nr_timers;
235 int total_time;
236 int round_time;
237 } settings;
239 static struct {
240 int total_time;
241 int used_time;
242 bool hidden;
243 } timer_holder[MAX_PLAYERS];
245 static int run_timer(int nr);
246 static int chessclock_set_int(char* string,
247 int* variable,
248 int step,
249 int min,
250 int max,
251 int flags);
252 #define FLAGS_SET_INT_SECONDS 1
254 static char * show_time(int secs);
255 static int simple_menu(int nr, unsigned char **strarr);
257 static bool pause;
259 #define MAX_TIME 7200
261 /* this is the plugin entry point */
262 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
264 int i;
265 bool done;
266 int nr;
268 (void)parameter;
269 rb=api;
270 rb->memset(&settings, 0, sizeof(settings));
272 /* now go ahead and have fun! */
273 rb->splash(HZ, "Chess Clock");
275 rb->lcd_clear_display();
276 i=0;
277 while (i>=0) {
278 int res;
279 switch (i) {
280 case 0:
281 res=chessclock_set_int("Number of players",
282 &settings.nr_timers, 1, 1,
283 MAX_PLAYERS, 0);
284 break;
285 case 1:
286 res=chessclock_set_int("Total time",
287 &settings.total_time, 10, 0, MAX_TIME,
288 FLAGS_SET_INT_SECONDS);
289 settings.round_time=settings.total_time;
290 break;
291 case 2:
292 res=chessclock_set_int("Max round time", &settings.round_time,
293 10, 0, settings.round_time,
294 FLAGS_SET_INT_SECONDS);
295 break;
296 default:
297 i=-1; /* done */
298 res=-2;
299 break;
301 if (res==-1) {
302 return PLUGIN_USB_CONNECTED;
304 if (res==0) {
305 i--;
306 if (i<0) {
307 return PLUGIN_OK;
310 if (res>0) {
311 i++;
314 for (i=0; i<settings.nr_timers; i++) {
315 timer_holder[i].total_time=settings.total_time;
316 timer_holder[i].used_time=0;
317 timer_holder[i].hidden=false;
320 pause=true; /* We start paused */
322 nr=0;
323 do {
324 int ret=0;
325 done=true;
326 for (i=0; done && i<settings.nr_timers; i++) {
327 if (!timer_holder[i].hidden)
328 done=false;
330 if (done) {
331 return PLUGIN_OK;
333 if (!timer_holder[nr].hidden) {
334 done=false;
335 ret=run_timer(nr);
337 switch (ret) {
338 case -1: /* exit */
339 done=true;
340 break;
341 case 3:
342 return PLUGIN_USB_CONNECTED;
343 case 1:
344 nr++;
345 if (nr>=settings.nr_timers)
346 nr=0;
347 break;
348 case 2:
349 nr--;
350 if (nr<0)
351 nr=settings.nr_timers-1;
352 break;
354 } while (!done);
355 return PLUGIN_OK;
358 #ifdef HAVE_LCD_BITMAP
359 static void show_pause_mode(bool enabled)
361 static const char pause_icon[] = {0x00,0x7f,0x7f,0x00,0x7f,0x7f,0x00};
363 if (enabled)
364 rb->lcd_mono_bitmap((unsigned char *)pause_icon, 52, 0, 7, 8);
365 else
367 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
368 rb->lcd_fillrect(52, 0, 7, 8);
369 rb->lcd_set_drawmode(DRMODE_SOLID);
372 #endif
375 -1= exit
376 1 = next player
377 2 = prev player
378 3 = usb connected
380 static int run_timer(int nr)
382 char buf[40];
383 char player_info[13];
384 long last_tick;
385 bool done=false;
386 int retval=0;
387 long max_ticks=timer_holder[nr].total_time*HZ-timer_holder[nr].used_time;
388 long ticks=0;
389 bool round_time=false;
391 #ifdef HAVE_LCD_CHARCELLS
392 rb->lcd_icon(ICON_PAUSE, pause);
393 #else
394 show_pause_mode(pause);
395 #endif
397 if (settings.round_time*HZ<max_ticks) {
398 max_ticks=settings.round_time*HZ;
399 round_time=true;
401 rb->snprintf(player_info, sizeof(player_info), "Player %d", nr+1);
402 rb->lcd_puts(0, FIRST_LINE, (unsigned char *)player_info);
403 last_tick=*rb->current_tick;
405 while (!done) {
406 int button;
407 long now;
408 if (ticks>max_ticks) {
409 if (round_time)
410 rb->lcd_puts(0, FIRST_LINE+1, (unsigned char *)"ROUND UP!");
411 else
412 rb->lcd_puts(0, FIRST_LINE+1, (unsigned char *)"TIME OUT!");
413 rb->backlight_on();
414 } else {
416 if (((int)(rb->current_tick - start_ticks)/HZ)&1) {
417 rb->lcd_puts(0, FIRST_LINE, player_info);
418 } else {
419 rb->lcd_puts(0, FIRST_LINE, player_info);
422 rb->lcd_puts(0, FIRST_LINE, (unsigned char *)player_info);
423 now=*rb->current_tick;
424 if (!pause) {
425 ticks+=now-last_tick;
426 if ((max_ticks-ticks)/HZ == 10) {
427 /* Backlight on if 10 seconds remain */
428 rb->backlight_on();
431 last_tick=now;
432 if (round_time) {
433 rb->snprintf(buf, sizeof(buf), "%s/",
434 show_time((max_ticks-ticks+HZ-1)/HZ));
435 /* Append total time */
436 rb->strcpy(&buf[rb->strlen(buf)],
437 show_time((timer_holder[nr].total_time*HZ-
438 timer_holder[nr].used_time-
439 ticks+HZ-1)/HZ));
440 rb->lcd_puts(0, FIRST_LINE+1, (unsigned char *)buf);
441 } else {
442 rb->lcd_puts(0, FIRST_LINE+1,
443 (unsigned char *)show_time((max_ticks-ticks+HZ-1)/HZ));
446 rb->lcd_update();
448 button = rb->button_get(false);
449 switch (button) {
450 /* OFF/ON key to exit */
451 case CHC_QUIT:
452 return -1; /* Indicate exit */
454 /* PLAY = Stop/Start toggle */
455 case CHC_STARTSTOP:
456 pause=!pause;
457 #ifdef HAVE_LCD_CHARCELLS
458 rb->lcd_icon(ICON_PAUSE, pause);
459 #else
460 show_pause_mode(pause);
461 #endif
462 break;
464 /* LEFT = Reset timer */
465 case CHC_RESET:
466 ticks=0;
467 break;
469 /* MENU */
470 case CHC_MENU:
472 int ret;
473 char *menu[]={"Delete player", "Restart round",
474 "Set round time", "Set total time"};
475 ret=simple_menu(4, (unsigned char **)menu);
476 if (ret==-1) {
477 retval = 3;
478 done=true;
479 } else if (ret==-2) {
480 } else if (ret==0) {
481 /* delete timer */
482 timer_holder[nr].hidden=true;
483 retval=1;
484 done=true;
485 break;
486 } else if (ret==1) {
487 /* restart */
488 ticks=0;
489 break;
490 } else if (ret==2) {
491 /* set round time */
492 int res;
493 int val=(max_ticks-ticks)/HZ;
494 res=chessclock_set_int("Round time",
495 &val,
496 10, 0, MAX_TIME,
497 FLAGS_SET_INT_SECONDS);
498 if (res==-1) { /*usb*/
499 retval = 3;
500 done=true;
501 } else if (res==1) {
502 ticks=max_ticks-val*HZ;
504 } else if (ret==3) {
505 /* set total time */
506 int res;
507 int val=timer_holder[nr].total_time;
508 res=chessclock_set_int("Total time",
509 &val,
510 10, 0, MAX_TIME,
511 FLAGS_SET_INT_SECONDS);
512 if (res==-1) { /*usb*/
513 retval = 3;
514 done=true;
515 } else if (res==1) {
516 timer_holder[nr].total_time=val;
520 break;
522 /* UP (RIGHT/+) = Scroll Lap timer up */
523 case CHC_SETTINGS_INC:
524 retval = 1;
525 done = true;
526 break;
528 /* DOWN (LEFT/-) = Scroll Lap timer down */
529 case CHC_SETTINGS_DEC:
530 retval = 2;
531 done = true;
532 break;
534 default:
535 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) {
536 retval = 3; /* been in usb mode */
537 done = true;
539 break;
541 rb->sleep(HZ/4); /* Sleep 1/4 of a second */
544 timer_holder[nr].used_time+=ticks;
546 return retval;
549 static int chessclock_set_int(char* string,
550 int* variable,
551 int step,
552 int min,
553 int max,
554 int flags)
556 bool done = false;
557 int button;
559 rb->lcd_clear_display();
560 rb->lcd_puts_scroll(0, FIRST_LINE, (unsigned char *)string);
562 while (!done) {
563 char str[32];
564 if (flags & FLAGS_SET_INT_SECONDS)
565 rb->snprintf(str, sizeof str,"%s (m:s)", show_time(*variable));
566 else
567 rb->snprintf(str, sizeof str,"%d", *variable);
568 rb->lcd_puts(0, FIRST_LINE+1, (unsigned char *)str);
569 rb->lcd_update();
571 button = rb->button_get(true);
572 switch(button) {
573 case CHC_SETTINGS_INC:
574 case CHC_SETTINGS_INC | BUTTON_REPEAT:
575 *variable += step;
576 break;
578 case CHC_SETTINGS_DEC:
579 case CHC_SETTINGS_DEC | BUTTON_REPEAT:
580 *variable -= step;
581 break;
583 case CHC_SETTINGS_OK:
584 #ifdef CHC_SETTINGS_OK2
585 case CHC_SETTINGS_OK2:
586 #endif
587 done = true;
588 break;
590 case CHC_SETTINGS_CANCEL:
591 #ifdef CHC_SETTINGS_CANCEL2
592 case CHC_SETTINGS_CANCEL2:
593 #endif
594 return 0; /* cancel */
595 break;
597 default:
598 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
599 return -1; /* been in usb mode */
600 break;
603 if(*variable > max )
604 *variable = max;
606 if(*variable < min )
607 *variable = min;
610 rb->lcd_stop_scroll();
612 return 1;
615 static char * show_time(int seconds)
617 static char buf[]="00:00";
618 rb->snprintf(buf, sizeof(buf), "%02d:%02d", seconds/60, seconds%60);
619 return buf;
622 /* -1 = USB
623 -2 = cancel
625 static int simple_menu(int nr, unsigned char **strarr)
627 int show=0;
628 int button;
629 rb->lcd_clear_display();
631 while (1) {
632 if (show>=nr)
633 show=0;
634 if (show<0)
635 show=nr-1;
636 rb->lcd_puts_scroll(0, FIRST_LINE, strarr[show]);
637 rb->lcd_update();
639 button = rb->button_get(true);
640 switch(button) {
641 case CHC_SETTINGS_INC:
642 case CHC_SETTINGS_INC | BUTTON_REPEAT:
643 show++;
644 break;
646 case CHC_SETTINGS_DEC:
647 case CHC_SETTINGS_DEC | BUTTON_REPEAT:
648 show--;
649 break;
651 case CHC_SETTINGS_OK:
652 #ifdef CHC_SETTINGS_OK2
653 case CHC_SETTINGS_OK2:
654 #endif
655 return show;
656 break;
658 case CHC_SETTINGS_CANCEL:
659 #ifdef CHC_SETTINGS_CANCEL2
660 case CHC_SETTINGS_CANCEL2:
661 #endif
662 return -2; /* cancel */
663 break;
665 default:
666 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
667 return -1; /* been in usb mode */
668 break;