diff options
Diffstat (limited to 'apps/plugins/chessclock.c')
-rw-r--r-- | apps/plugins/chessclock.c | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/apps/plugins/chessclock.c b/apps/plugins/chessclock.c new file mode 100644 index 0000000000..897f05aebb --- /dev/null +++ b/apps/plugins/chessclock.c | |||
@@ -0,0 +1,488 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Kjell Ericson | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "plugin.h" | ||
20 | #ifdef HAVE_LCD_CHARCELLS | ||
21 | /* We only support players at a start. I don't have simulator capacity at | ||
22 | the time. */ | ||
23 | |||
24 | /* here is a global api struct pointer. while not strictly necessary, | ||
25 | it's nice not to have to pass the api pointer in all function calls | ||
26 | in the plugin */ | ||
27 | static struct plugin_api* rb; | ||
28 | #define MAX_PLAYERS 10 | ||
29 | |||
30 | static struct { | ||
31 | int nr_timers; | ||
32 | int total_time; | ||
33 | int round_time; | ||
34 | } settings; | ||
35 | |||
36 | static struct { | ||
37 | int total_time; | ||
38 | int used_time; | ||
39 | bool hidden; | ||
40 | } timer_holder[MAX_PLAYERS]; | ||
41 | |||
42 | static int run_timer(int nr); | ||
43 | static int chessclock_set_int(char* string, | ||
44 | int* variable, | ||
45 | int step, | ||
46 | int min, | ||
47 | int max, | ||
48 | int flags); | ||
49 | #define FLAGS_SET_INT_SECONDS 1 | ||
50 | |||
51 | static char * show_time(int secs); | ||
52 | static int simple_menu(int nr, char **strarr); | ||
53 | |||
54 | static bool pause; | ||
55 | |||
56 | #define MAX_TIME 7200 | ||
57 | |||
58 | /* this is the plugin entry point */ | ||
59 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
60 | { | ||
61 | int i; | ||
62 | bool done; | ||
63 | int nr; | ||
64 | |||
65 | TEST_PLUGIN_API(api); | ||
66 | |||
67 | (void)parameter; | ||
68 | rb=api; | ||
69 | |||
70 | rb->memset(&settings, 0, sizeof(settings)); | ||
71 | |||
72 | /* now go ahead and have fun! */ | ||
73 | rb->splash(HZ, true, "Chess Clock"); | ||
74 | |||
75 | rb->lcd_clear_display(); | ||
76 | i=0; | ||
77 | while (i>=0) { | ||
78 | int res; | ||
79 | switch (i) { | ||
80 | case 0: | ||
81 | res=chessclock_set_int("Number of players", | ||
82 | &settings.nr_timers, 1, 1, | ||
83 | MAX_PLAYERS, 0); | ||
84 | break; | ||
85 | case 1: | ||
86 | res=chessclock_set_int("Total time", | ||
87 | &settings.total_time, 10, 0, MAX_TIME, | ||
88 | FLAGS_SET_INT_SECONDS); | ||
89 | settings.round_time=settings.total_time; | ||
90 | break; | ||
91 | case 2: | ||
92 | res=chessclock_set_int("Max round time", &settings.round_time, | ||
93 | 10, 0, settings.round_time, | ||
94 | FLAGS_SET_INT_SECONDS); | ||
95 | break; | ||
96 | default: | ||
97 | i=-1; /* done */ | ||
98 | res=-2; | ||
99 | break; | ||
100 | } | ||
101 | if (res==-1) { | ||
102 | rb->usb_screen(); | ||
103 | return PLUGIN_USB_CONNECTED; | ||
104 | } | ||
105 | if (res==0) { | ||
106 | i--; | ||
107 | if (i<0) { | ||
108 | return PLUGIN_OK; | ||
109 | } | ||
110 | } | ||
111 | if (res>0) { | ||
112 | i++; | ||
113 | } | ||
114 | } | ||
115 | for (i=0; i<settings.nr_timers; i++) { | ||
116 | timer_holder[i].total_time=settings.total_time; | ||
117 | timer_holder[i].used_time=0; | ||
118 | timer_holder[i].hidden=false; | ||
119 | } | ||
120 | |||
121 | pause=true; /* We start paused */ | ||
122 | |||
123 | nr=0; | ||
124 | do { | ||
125 | int ret=0; | ||
126 | done=true; | ||
127 | for (i=0; done && i<settings.nr_timers; i++) { | ||
128 | if (!timer_holder[i].hidden) | ||
129 | done=false; | ||
130 | } | ||
131 | if (done) { | ||
132 | return PLUGIN_OK; | ||
133 | } | ||
134 | if (!timer_holder[nr].hidden) { | ||
135 | done=false; | ||
136 | ret=run_timer(nr); | ||
137 | } | ||
138 | switch (ret) { | ||
139 | case -1: /* exit */ | ||
140 | done=true; | ||
141 | break; | ||
142 | case 3: | ||
143 | rb->usb_screen(); | ||
144 | return PLUGIN_USB_CONNECTED; | ||
145 | case 1: | ||
146 | nr++; | ||
147 | if (nr>=settings.nr_timers) | ||
148 | nr=0; | ||
149 | break; | ||
150 | case 2: | ||
151 | nr--; | ||
152 | if (nr<0) | ||
153 | nr=settings.nr_timers-1; | ||
154 | break; | ||
155 | } | ||
156 | } while (!done); | ||
157 | return PLUGIN_OK; | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | -1= exit | ||
162 | 1 = next player | ||
163 | 2 = prev player | ||
164 | 3 = usb connected | ||
165 | */ | ||
166 | static int run_timer(int nr) | ||
167 | { | ||
168 | char buf[40]; | ||
169 | char player_info[13]; | ||
170 | long last_tick; | ||
171 | bool done=false; | ||
172 | int retval=0; | ||
173 | long max_ticks=timer_holder[nr].total_time*HZ-timer_holder[nr].used_time; | ||
174 | long ticks=0; | ||
175 | bool round_time=false; | ||
176 | |||
177 | rb->lcd_icon(ICON_PAUSE, pause); | ||
178 | |||
179 | if (settings.round_time*HZ<max_ticks) { | ||
180 | max_ticks=settings.round_time*HZ; | ||
181 | round_time=true; | ||
182 | } | ||
183 | rb->snprintf(player_info, sizeof(player_info), "Player %d", nr+1); | ||
184 | rb->lcd_puts(0, 0, player_info); | ||
185 | last_tick=*rb->current_tick; | ||
186 | |||
187 | while (!done) { | ||
188 | int button; | ||
189 | if (ticks>max_ticks) { | ||
190 | if (round_time) | ||
191 | rb->lcd_puts(0, 1, "ROUND UP!"); | ||
192 | else | ||
193 | rb->lcd_puts(0, 1, "TIME OUT!"); | ||
194 | rb->backlight_on(); | ||
195 | } else { | ||
196 | /* | ||
197 | if (((int)(rb->current_tick - start_ticks)/HZ)&1) { | ||
198 | rb->lcd_puts(0, 0, player_info); | ||
199 | } else { | ||
200 | rb->lcd_puts(0, 0, player_info); | ||
201 | } | ||
202 | */ | ||
203 | rb->lcd_puts(0, 0, player_info); | ||
204 | long now=*rb->current_tick; | ||
205 | if (!pause) { | ||
206 | ticks+=now-last_tick; | ||
207 | if ((max_ticks-ticks)/HZ == 10) { | ||
208 | /* Backlight on if 10 seconds remain */ | ||
209 | rb->backlight_on(); | ||
210 | } | ||
211 | } | ||
212 | last_tick=now; | ||
213 | if (round_time) { | ||
214 | rb->snprintf(buf, sizeof(buf), "%s/", | ||
215 | show_time((max_ticks-ticks+HZ-1)/HZ)); | ||
216 | /* Append total time */ | ||
217 | rb->strcpy(&buf[rb->strlen(buf)], | ||
218 | show_time((timer_holder[nr].total_time*HZ- | ||
219 | timer_holder[nr].used_time- | ||
220 | ticks+HZ-1)/HZ)); | ||
221 | rb->lcd_puts(0, 1, buf); | ||
222 | } else { | ||
223 | rb->lcd_puts(0, 1, show_time((max_ticks-ticks+HZ-1)/HZ)); | ||
224 | } | ||
225 | } | ||
226 | button = rb->button_get_w_tmo(10); | ||
227 | switch (button) { | ||
228 | /* OFF/MENU key to exit */ | ||
229 | #ifdef HAVE_RECORDER_KEYPAD | ||
230 | case BUTTON_OFF: | ||
231 | #else | ||
232 | case BUTTON_ON: | ||
233 | #endif | ||
234 | return -1; /* Indicate exit */ | ||
235 | |||
236 | /* PLAY = Stop/Start toggle */ | ||
237 | case BUTTON_PLAY: | ||
238 | pause=!pause; | ||
239 | rb->lcd_icon(ICON_PAUSE, pause); | ||
240 | break; | ||
241 | |||
242 | /* LEFT = Reset timer */ | ||
243 | case BUTTON_STOP: | ||
244 | ticks=0; | ||
245 | break; | ||
246 | |||
247 | /* MENU */ | ||
248 | case BUTTON_MENU: | ||
249 | { | ||
250 | int ret; | ||
251 | char *menu[]={"Delete player", "Restart round", | ||
252 | "Set round time", "Set total time"}; | ||
253 | ret=simple_menu(4, menu); | ||
254 | if (ret==-1) { | ||
255 | retval = 3; | ||
256 | done=true; | ||
257 | } else if (ret==-2) { | ||
258 | } else if (ret==0) { | ||
259 | /* delete timer */ | ||
260 | timer_holder[nr].hidden=true; | ||
261 | retval=1; | ||
262 | done=true; | ||
263 | break; | ||
264 | } else if (ret==1) { | ||
265 | /* restart */ | ||
266 | ticks=0; | ||
267 | break; | ||
268 | } else if (ret==2) { | ||
269 | /* set round time */ | ||
270 | int res; | ||
271 | int val=(max_ticks-ticks)/HZ; | ||
272 | res=chessclock_set_int("Round time", | ||
273 | &val, | ||
274 | 10, 0, MAX_TIME, | ||
275 | FLAGS_SET_INT_SECONDS); | ||
276 | if (res==-1) { /*usb*/ | ||
277 | retval = 3; | ||
278 | done=true; | ||
279 | } else if (res==1) { | ||
280 | ticks=max_ticks-val*HZ; | ||
281 | } | ||
282 | } else if (ret==3) { | ||
283 | /* set total time */ | ||
284 | int res; | ||
285 | int val=timer_holder[nr].total_time; | ||
286 | res=chessclock_set_int("Total time", | ||
287 | &val, | ||
288 | 10, 0, MAX_TIME, | ||
289 | FLAGS_SET_INT_SECONDS); | ||
290 | if (res==-1) { /*usb*/ | ||
291 | retval = 3; | ||
292 | done=true; | ||
293 | } else if (res==1) { | ||
294 | timer_holder[nr].total_time=val; | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | break; | ||
299 | |||
300 | /* UP (RIGHT/+) = Scroll Lap timer up */ | ||
301 | #ifdef HAVE_RECORDER_KEYPAD | ||
302 | case BUTTON_UP: | ||
303 | #else | ||
304 | case BUTTON_RIGHT: | ||
305 | #endif | ||
306 | retval = 1; | ||
307 | done = true; | ||
308 | break; | ||
309 | |||
310 | /* DOWN (LEFT/-) = Scroll Lap timer down */ | ||
311 | #ifdef HAVE_RECORDER_KEYPAD | ||
312 | case BUTTON_DOWN: | ||
313 | #else | ||
314 | case BUTTON_LEFT: | ||
315 | #endif | ||
316 | retval = 2; | ||
317 | done = true; | ||
318 | break; | ||
319 | |||
320 | case SYS_USB_CONNECTED: | ||
321 | retval = 3; | ||
322 | done=true; | ||
323 | break; | ||
324 | } | ||
325 | rb->sleep(HZ/4); /* Sleep 1/4 of a second */ | ||
326 | } | ||
327 | |||
328 | timer_holder[nr].used_time+=ticks; | ||
329 | |||
330 | return retval; | ||
331 | } | ||
332 | |||
333 | static int chessclock_set_int(char* string, | ||
334 | int* variable, | ||
335 | int step, | ||
336 | int min, | ||
337 | int max, | ||
338 | int flags) | ||
339 | { | ||
340 | bool done = false; | ||
341 | int button; | ||
342 | |||
343 | #ifdef HAVE_LCD_BITMAP | ||
344 | if(global_settings.statusbar) | ||
345 | rb->lcd_setmargins(0, STATUSBAR_HEIGHT); | ||
346 | else | ||
347 | rb->lcd_setmargins(0, 0); | ||
348 | #endif | ||
349 | |||
350 | rb->lcd_clear_display(); | ||
351 | rb->lcd_puts_scroll(0, 0, string); | ||
352 | |||
353 | while (!done) { | ||
354 | char str[32]; | ||
355 | if (flags & FLAGS_SET_INT_SECONDS) | ||
356 | rb->snprintf(str, sizeof str,"%s (m:s)", show_time(*variable)); | ||
357 | else | ||
358 | rb->snprintf(str, sizeof str,"%d", *variable); | ||
359 | rb->lcd_puts(0, 1, str); | ||
360 | #ifdef HAVE_LCD_BITMAP | ||
361 | rb->status_draw(true); | ||
362 | rb->lcd_update(); | ||
363 | #endif | ||
364 | button = rb->button_get_w_tmo(HZ/2); | ||
365 | switch(button) { | ||
366 | #ifdef HAVE_RECORDER_KEYPAD | ||
367 | case BUTTON_UP: | ||
368 | case BUTTON_UP | BUTTON_REPEAT: | ||
369 | #else | ||
370 | case BUTTON_RIGHT: | ||
371 | case BUTTON_RIGHT | BUTTON_REPEAT: | ||
372 | #endif | ||
373 | *variable += step; | ||
374 | break; | ||
375 | |||
376 | #ifdef HAVE_RECORDER_KEYPAD | ||
377 | case BUTTON_DOWN: | ||
378 | case BUTTON_DOWN | BUTTON_REPEAT: | ||
379 | #else | ||
380 | case BUTTON_LEFT: | ||
381 | case BUTTON_LEFT | BUTTON_REPEAT: | ||
382 | #endif | ||
383 | *variable -= step; | ||
384 | break; | ||
385 | |||
386 | #ifdef HAVE_RECORDER_KEYPAD | ||
387 | case BUTTON_LEFT: | ||
388 | case BUTTON_PLAY: | ||
389 | #else | ||
390 | case BUTTON_PLAY: | ||
391 | #endif | ||
392 | done = true; | ||
393 | break; | ||
394 | |||
395 | #ifdef HAVE_RECORDER_KEYPAD | ||
396 | case BUTTON_OFF: | ||
397 | #else | ||
398 | case BUTTON_STOP: | ||
399 | case BUTTON_MENU: | ||
400 | #endif | ||
401 | return 0; /* cancel */ | ||
402 | break; | ||
403 | |||
404 | case SYS_USB_CONNECTED: | ||
405 | return -1; | ||
406 | |||
407 | } | ||
408 | if(*variable > max ) | ||
409 | *variable = max; | ||
410 | |||
411 | if(*variable < min ) | ||
412 | *variable = min; | ||
413 | |||
414 | } | ||
415 | rb->lcd_stop_scroll(); | ||
416 | |||
417 | return 1; | ||
418 | } | ||
419 | |||
420 | static char * show_time(int seconds) | ||
421 | { | ||
422 | static char buf[]="00:00"; | ||
423 | rb->snprintf(buf, sizeof(buf), "%02d:%02d", seconds/60, seconds%60); | ||
424 | return buf; | ||
425 | } | ||
426 | |||
427 | /* -1 = USB | ||
428 | -2 = cancel | ||
429 | */ | ||
430 | static int simple_menu(int nr, char **strarr) | ||
431 | { | ||
432 | int show=0; | ||
433 | int button; | ||
434 | rb->lcd_clear_display(); | ||
435 | |||
436 | while (1) { | ||
437 | if (show>=nr) | ||
438 | show=0; | ||
439 | if (show<0) | ||
440 | show=nr-1; | ||
441 | rb->lcd_puts_scroll(0, 0, strarr[show]); | ||
442 | button = rb->button_get(false); | ||
443 | switch(button) { | ||
444 | #ifdef HAVE_RECORDER_KEYPAD | ||
445 | case BUTTON_UP: | ||
446 | case BUTTON_UP | BUTTON_REPEAT: | ||
447 | #else | ||
448 | case BUTTON_RIGHT: | ||
449 | case BUTTON_RIGHT | BUTTON_REPEAT: | ||
450 | #endif | ||
451 | show++; | ||
452 | break; | ||
453 | |||
454 | #ifdef HAVE_RECORDER_KEYPAD | ||
455 | case BUTTON_DOWN: | ||
456 | case BUTTON_DOWN | BUTTON_REPEAT: | ||
457 | #else | ||
458 | case BUTTON_LEFT: | ||
459 | case BUTTON_LEFT | BUTTON_REPEAT: | ||
460 | #endif | ||
461 | show--; | ||
462 | break; | ||
463 | |||
464 | #ifdef HAVE_RECORDER_KEYPAD | ||
465 | case BUTTON_LEFT: | ||
466 | case BUTTON_PLAY: | ||
467 | #else | ||
468 | case BUTTON_PLAY: | ||
469 | #endif | ||
470 | return show; | ||
471 | break; | ||
472 | |||
473 | #ifdef HAVE_RECORDER_KEYPAD | ||
474 | case BUTTON_OFF: | ||
475 | #else | ||
476 | case BUTTON_STOP: | ||
477 | case BUTTON_MENU: | ||
478 | #endif | ||
479 | return -2; /* cancel */ | ||
480 | break; | ||
481 | |||
482 | case SYS_USB_CONNECTED: | ||
483 | return -1; | ||
484 | } | ||
485 | } | ||
486 | } | ||
487 | |||
488 | #endif /* HAVE_LCD_CHARCELLS */ | ||