mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-05-12 11:43:16 -04:00
plugins: add timer/countdown plugin
-New countdown timer plugin with pause, overtime support -Add full name to credits and manual entry -Make status strings translatable Change-Id: I1437b2e5ac5ede292bdab8d36e58b81326ea2ba3
This commit is contained in:
parent
cc7418dd8b
commit
a1040cda5d
8 changed files with 438 additions and 0 deletions
|
|
@ -16984,3 +16984,73 @@
|
|||
*: "U S B"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_COUNTDOWN_TIMER_SET
|
||||
desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration
|
||||
user: core
|
||||
<source>
|
||||
*: "SET TIMER"
|
||||
</source>
|
||||
<dest>
|
||||
*: "SET TIMER"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Set timer"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_COUNTDOWN_TIMER_RUNNING
|
||||
desc: countdown_timer plugin - status label shown while the countdown is active
|
||||
user: core
|
||||
<source>
|
||||
*: "RUNNING"
|
||||
</source>
|
||||
<dest>
|
||||
*: "RUNNING"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Running"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_COUNTDOWN_TIMER_PAUSED
|
||||
desc: countdown_timer plugin - status label shown while the countdown is paused
|
||||
user: core
|
||||
<source>
|
||||
*: "PAUSED"
|
||||
</source>
|
||||
<dest>
|
||||
*: "PAUSED"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Paused"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_COUNTDOWN_TIMER_OVERTIME
|
||||
desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up
|
||||
user: core
|
||||
<source>
|
||||
*: "OVERTIME"
|
||||
</source>
|
||||
<dest>
|
||||
*: "OVERTIME"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Overtime"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_COUNTDOWN_TIMER_FINISHED
|
||||
desc: countdown_timer plugin - status label shown at the moment the countdown expires
|
||||
user: core
|
||||
<source>
|
||||
*: "FINISHED"
|
||||
</source>
|
||||
<dest>
|
||||
*: "FINISHED"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Finished"
|
||||
</voice>
|
||||
</phrase>
|
||||
|
|
|
|||
|
|
@ -17099,3 +17099,73 @@
|
|||
*: "Swap Left & Right"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_COUNTDOWN_TIMER_SET
|
||||
desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration
|
||||
user: core
|
||||
<source>
|
||||
*: "SET TIMER"
|
||||
</source>
|
||||
<dest>
|
||||
*: "SET TIMER"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Set timer"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_COUNTDOWN_TIMER_RUNNING
|
||||
desc: countdown_timer plugin - status label shown while the countdown is active
|
||||
user: core
|
||||
<source>
|
||||
*: "RUNNING"
|
||||
</source>
|
||||
<dest>
|
||||
*: "RUNNING"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Running"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_COUNTDOWN_TIMER_PAUSED
|
||||
desc: countdown_timer plugin - status label shown while the countdown is paused
|
||||
user: core
|
||||
<source>
|
||||
*: "PAUSED"
|
||||
</source>
|
||||
<dest>
|
||||
*: "PAUSED"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Paused"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_COUNTDOWN_TIMER_OVERTIME
|
||||
desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up
|
||||
user: core
|
||||
<source>
|
||||
*: "OVERTIME"
|
||||
</source>
|
||||
<dest>
|
||||
*: "OVERTIME"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Overtime"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_COUNTDOWN_TIMER_FINISHED
|
||||
desc: countdown_timer plugin - status label shown at the moment the countdown expires
|
||||
user: core
|
||||
<source>
|
||||
*: "FINISHED"
|
||||
</source>
|
||||
<dest>
|
||||
*: "FINISHED"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Finished"
|
||||
</voice>
|
||||
</phrase>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ chopper,games
|
|||
clix,games
|
||||
clock,apps
|
||||
codebuster,games
|
||||
countdown_timer,apps
|
||||
credits,viewers
|
||||
cube,demos
|
||||
cue_playlist,viewers
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ db_folder_select.c
|
|||
tagcache/tagcache.c
|
||||
#endif
|
||||
chessclock.c
|
||||
countdown_timer.c
|
||||
credits.c
|
||||
cube.c
|
||||
cue_playlist.c
|
||||
|
|
|
|||
242
apps/plugins/countdown_timer.c
Normal file
242
apps/plugins/countdown_timer.c
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2026 Teun van Dalen
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include "plugin.h"
|
||||
#include "lib/pluginlib_actions.h"
|
||||
#include "lib/pluginlib_exit.h"
|
||||
|
||||
const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
|
||||
|
||||
#define MAX_MINUTES 99
|
||||
#define MAX_SECONDS 59
|
||||
|
||||
typedef enum {
|
||||
STATE_SETUP,
|
||||
STATE_RUNNING,
|
||||
STATE_PAUSED,
|
||||
STATE_FINISHED,
|
||||
} timer_state_t;
|
||||
|
||||
typedef struct {
|
||||
int set_minutes;
|
||||
int set_seconds;
|
||||
int current_field;
|
||||
long remaining_ticks;
|
||||
long last_tick;
|
||||
timer_state_t state;
|
||||
} timer_ctx_t;
|
||||
|
||||
static inline int get_button(void)
|
||||
{
|
||||
return pluginlib_getaction(HZ / 10, plugin_contexts,
|
||||
ARRAYLEN(plugin_contexts));
|
||||
}
|
||||
|
||||
static void draw(const timer_ctx_t *ctx)
|
||||
{
|
||||
char time_str[12];
|
||||
const char *status_str;
|
||||
int disp_min, disp_sec;
|
||||
int w, h, x, y;
|
||||
|
||||
rb->lcd_clear_display();
|
||||
|
||||
if (ctx->state == STATE_SETUP) {
|
||||
disp_min = ctx->set_minutes;
|
||||
disp_sec = ctx->set_seconds;
|
||||
if (ctx->current_field == 0)
|
||||
rb->snprintf(time_str, sizeof(time_str), "[%02d]:%02d",
|
||||
disp_min, disp_sec);
|
||||
else
|
||||
rb->snprintf(time_str, sizeof(time_str), "%02d:[%02d]",
|
||||
disp_min, disp_sec);
|
||||
status_str = rb->str(LANG_COUNTDOWN_TIMER_SET);
|
||||
} else if (ctx->state == STATE_FINISHED) {
|
||||
rb->snprintf(time_str, sizeof(time_str), "00:00");
|
||||
status_str = rb->str(LANG_COUNTDOWN_TIMER_FINISHED);
|
||||
} else {
|
||||
if (ctx->remaining_ticks >= 0) {
|
||||
int remaining_secs = (ctx->remaining_ticks + HZ - 1) / HZ;
|
||||
disp_min = remaining_secs / 60;
|
||||
disp_sec = remaining_secs % 60;
|
||||
rb->snprintf(time_str, sizeof(time_str), "%02d:%02d",
|
||||
disp_min, disp_sec);
|
||||
} else {
|
||||
int over_secs = ((-ctx->remaining_ticks) + HZ - 1) / HZ;
|
||||
disp_min = over_secs / 60;
|
||||
disp_sec = over_secs % 60;
|
||||
rb->snprintf(time_str, sizeof(time_str), "-%02d:%02d",
|
||||
disp_min, disp_sec);
|
||||
}
|
||||
|
||||
if (ctx->state == STATE_RUNNING && ctx->remaining_ticks < 0)
|
||||
status_str = rb->str(LANG_COUNTDOWN_TIMER_OVERTIME);
|
||||
else if (ctx->state == STATE_RUNNING)
|
||||
status_str = rb->str(LANG_COUNTDOWN_TIMER_RUNNING);
|
||||
else
|
||||
status_str = rb->str(LANG_COUNTDOWN_TIMER_PAUSED);
|
||||
}
|
||||
|
||||
/* Draw status string above center */
|
||||
rb->lcd_getstringsize(status_str, &w, &h);
|
||||
x = (LCD_WIDTH - w) / 2;
|
||||
y = (LCD_HEIGHT / 2) - h - 2;
|
||||
rb->lcd_putsxy(x, y, status_str);
|
||||
|
||||
/* Draw time string at center */
|
||||
rb->lcd_getstringsize(time_str, &w, &h);
|
||||
x = (LCD_WIDTH - w) / 2;
|
||||
y = LCD_HEIGHT / 2;
|
||||
rb->lcd_putsxy(x, y, time_str);
|
||||
|
||||
rb->lcd_update();
|
||||
}
|
||||
|
||||
enum plugin_status plugin_start(const void *parameter)
|
||||
{
|
||||
timer_ctx_t ctx = {
|
||||
.set_minutes = 10,
|
||||
.set_seconds = 0,
|
||||
.current_field = 0,
|
||||
.remaining_ticks = 60 * HZ,
|
||||
.last_tick = 0,
|
||||
.state = STATE_SETUP,
|
||||
};
|
||||
bool overtime_alerted = false;
|
||||
int button;
|
||||
|
||||
(void)parameter;
|
||||
|
||||
while (true) {
|
||||
if (ctx.state == STATE_RUNNING) {
|
||||
long now = *rb->current_tick;
|
||||
ctx.remaining_ticks -= now - ctx.last_tick;
|
||||
ctx.last_tick = now;
|
||||
|
||||
if (ctx.remaining_ticks < 0 && !overtime_alerted) {
|
||||
overtime_alerted = true;
|
||||
ctx.state = STATE_FINISHED;
|
||||
draw(&ctx);
|
||||
rb->audio_pause();
|
||||
#ifdef HAVE_BACKLIGHT
|
||||
rb->backlight_on();
|
||||
#endif
|
||||
rb->beep_play(1000, 200, 1000);
|
||||
rb->sleep(HZ / 4);
|
||||
rb->beep_play(1000, 200, 1000);
|
||||
rb->sleep(HZ / 4);
|
||||
rb->beep_play(1000, 400, 1000);
|
||||
rb->sleep(HZ + HZ / 2);
|
||||
rb->audio_resume();
|
||||
ctx.state = STATE_RUNNING;
|
||||
/* Reset last_tick so the beep/sleep time is not counted
|
||||
* against remaining_ticks on the next iteration. */
|
||||
ctx.last_tick = *rb->current_tick;
|
||||
}
|
||||
}
|
||||
|
||||
draw(&ctx);
|
||||
button = get_button();
|
||||
|
||||
switch (ctx.state) {
|
||||
case STATE_SETUP: {
|
||||
bool time_changed = false;
|
||||
switch (button) {
|
||||
#ifdef HAVE_SCROLLWHEEL
|
||||
case PLA_SCROLL_FWD:
|
||||
case PLA_SCROLL_FWD_REPEAT:
|
||||
#endif
|
||||
case PLA_UP:
|
||||
case PLA_UP_REPEAT:
|
||||
if (ctx.current_field == 0)
|
||||
ctx.set_minutes = (ctx.set_minutes + 1) % (MAX_MINUTES + 1);
|
||||
else
|
||||
ctx.set_seconds = (ctx.set_seconds + 1) % (MAX_SECONDS + 1);
|
||||
time_changed = true;
|
||||
break;
|
||||
#ifdef HAVE_SCROLLWHEEL
|
||||
case PLA_SCROLL_BACK:
|
||||
case PLA_SCROLL_BACK_REPEAT:
|
||||
#endif
|
||||
case PLA_DOWN:
|
||||
case PLA_DOWN_REPEAT:
|
||||
if (ctx.current_field == 0)
|
||||
ctx.set_minutes = (ctx.set_minutes + MAX_MINUTES) % (MAX_MINUTES + 1);
|
||||
else
|
||||
ctx.set_seconds = (ctx.set_seconds + MAX_SECONDS) % (MAX_SECONDS + 1);
|
||||
time_changed = true;
|
||||
break;
|
||||
case PLA_LEFT:
|
||||
case PLA_LEFT_REPEAT:
|
||||
case PLA_RIGHT:
|
||||
case PLA_RIGHT_REPEAT:
|
||||
ctx.current_field = (ctx.current_field + 1) % 2;
|
||||
break;
|
||||
case PLA_SELECT:
|
||||
if (ctx.set_minutes > 0 || ctx.set_seconds > 0) {
|
||||
ctx.remaining_ticks = (ctx.set_minutes * 60 + ctx.set_seconds) * HZ;
|
||||
ctx.last_tick = *rb->current_tick;
|
||||
ctx.state = STATE_RUNNING;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
exit_on_usb(button);
|
||||
break;
|
||||
}
|
||||
if (time_changed)
|
||||
ctx.remaining_ticks = (ctx.set_minutes * 60 + ctx.set_seconds) * HZ;
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_RUNNING:
|
||||
switch (button) {
|
||||
case PLA_SELECT:
|
||||
ctx.state = STATE_PAUSED;
|
||||
break;
|
||||
default:
|
||||
exit_on_usb(button);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_FINISHED:
|
||||
exit_on_usb(button);
|
||||
break;
|
||||
|
||||
case STATE_PAUSED:
|
||||
switch (button) {
|
||||
case PLA_SELECT:
|
||||
ctx.last_tick = *rb->current_tick;
|
||||
ctx.state = STATE_RUNNING;
|
||||
break;
|
||||
case PLA_UP:
|
||||
case PLA_UP_REPEAT:
|
||||
case PLA_CANCEL:
|
||||
case PLA_EXIT:
|
||||
return PLUGIN_OK;
|
||||
default:
|
||||
exit_on_usb(button);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -760,6 +760,7 @@ Yegor Chernyshov
|
|||
Javier Gutiérrez Gertrúdix
|
||||
Sergey Puskov
|
||||
Eivind Ødegård
|
||||
Teun van Dalen
|
||||
|
||||
The libmad team
|
||||
The wavpack team
|
||||
|
|
|
|||
51
manual/plugins/countdown_timer.tex
Normal file
51
manual/plugins/countdown_timer.tex
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
% $Id$ %
|
||||
\subsection{Countdown_timer}
|
||||
|
||||
A countdown timer. Set the desired duration, start the countdown, and the
|
||||
\dap{} will alert you with a beep sequence when time is up. The timer
|
||||
defaults to 10 minutes on launch.
|
||||
|
||||
\subsubsection{Setting the timer}
|
||||
|
||||
When the plugin starts, the display shows the time to count down from with
|
||||
the active field highlighted in brackets (e.g.\ \texttt{[10]:00}).
|
||||
|
||||
\begin{btnmap}
|
||||
\opt{scrollwheel}{\PluginScrollFwd{} / \PluginScrollBack{} or }
|
||||
\PluginUp{} / \PluginDown
|
||||
\opt{HAVEREMOTEKEYMAP}{& \PluginRCUp{} / \PluginRCDown}
|
||||
& Increase / decrease the selected field \\
|
||||
|
||||
\PluginLeft{} / \PluginRight
|
||||
\opt{HAVEREMOTEKEYMAP}{& \PluginRCLeft{} / \PluginRCRight}
|
||||
& Switch between the minutes and seconds fields \\
|
||||
|
||||
\PluginSelect
|
||||
\opt{HAVEREMOTEKEYMAP}{& \PluginRCSelect}
|
||||
& Start the countdown \\
|
||||
\end{btnmap}
|
||||
|
||||
Minutes can be set from 0 to 99 and seconds from 0 to 59. The timer cannot
|
||||
be started if both fields are zero. Values wrap around when incremented or
|
||||
decremented past their limits.
|
||||
|
||||
\subsubsection{Running the timer}
|
||||
|
||||
Once started, the remaining time counts down in \texttt{MM:SS} format.
|
||||
|
||||
\begin{btnmap}
|
||||
\PluginSelect
|
||||
\opt{HAVEREMOTEKEYMAP}{& \PluginRCSelect}
|
||||
& Pause / resume the timer \\
|
||||
|
||||
\nopt{IPOD_4G_PAD,IPOD_3G_PAD}{\PluginCancel}
|
||||
\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu}
|
||||
\opt{HAVEREMOTEKEYMAP}{& \PluginRCCancel}
|
||||
& Quit (only available while paused) \\
|
||||
\end{btnmap}
|
||||
|
||||
When the countdown reaches zero the \dap{} pauses playback, turns on the
|
||||
backlight, and plays three beeps. After the alert the timer continues
|
||||
running into overtime, displaying the elapsed overtime as
|
||||
\texttt{-MM:SS}. Playback resumes automatically after the beep sequence.
|
||||
The timer can be quit by pausing it and pressing the quit button.
|
||||
|
|
@ -297,3 +297,5 @@ option from the \setting{Context Menu} (see \reference{ref:Contextmenu}).}
|
|||
\input{plugins/stopwatch.tex}
|
||||
|
||||
\input{plugins/text_editor.tex}
|
||||
|
||||
\input{plugins/countdown_timer.tex}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue