1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
29 #include "settings_list.h"
30 #include "rbunicode.h"
31 #include "timefuncs.h"
34 #include "powermgmt.h"
37 #ifdef HAVE_LCD_CHARCELLS
44 #ifdef HAVE_LCD_BITMAP
50 #if CONFIG_CODEC == SWCODEC
55 #include "wps_internals.h"
56 #include "root_menu.h"
58 #include "recording.h"
61 static char* get_codectype(const struct mp3entry
* id3
)
63 if (id3
&& id3
->codectype
< AFMT_NUM_CODECS
) {
64 return (char*)audio_formats
[id3
->codectype
].label
;
70 /* Extract a part from a path.
72 * buf - buffer extract part to.
73 * buf_size - size of buffer.
74 * path - path to extract from.
75 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
76 * parent of parent, etc.
78 * Returns buf if the desired level was found, NULL otherwise.
80 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
86 sep
= path
+ strlen(path
);
101 if (level
|| (last_sep
<= sep
))
104 len
= MIN(last_sep
- sep
, buf_size
- 1);
105 strlcpy(buf
, sep
+ 1, len
+ 1);
109 /* Return the tag found at index i and write its value in buf.
110 The return value is buf if the tag had a value, or NULL if not.
112 intval is used with conditionals/enums: when this function is called,
113 intval should contain the number of options in the conditional/enum.
114 When this function returns, intval is -1 if the tag is non numeric or,
115 if the tag is numeric, *intval is the enum case we want to go to (between 1
116 and the original value of *intval, inclusive).
117 When not treating a conditional/enum, intval should be NULL.
120 /* a few convinience macros for the id3 == NULL case
121 * depends on a few variable names in get_token_value() */
123 #define HANDLE_NULL_ID3(id3field) (LIKELY(id3) ? (id3field) : NULL)
125 #define HANDLE_NULL_ID3_NUM_ZERO { if (UNLIKELY(!id3)) return zero_str; }
127 #define HANDLE_NULL_ID3_NUM_INTVAL(id3field) \
130 *intval = (LIKELY(id3) ? (id3field) + 1 : 0); \
134 snprintf(buf, buf_size, "%ld", (id3field)); \
140 const char *get_token_value(struct gui_wps
*gwps
,
141 struct wps_token
*token
,
142 char *buf
, int buf_size
,
148 struct wps_data
*data
= gwps
->data
;
149 struct wps_state
*state
= gwps
->state
;
151 static const char * const zero_str
= "0";
156 struct mp3entry
*id3
;
165 elapsed
= id3
->elapsed
;
166 length
= id3
->length
;
175 struct tm
* tm
= NULL
;
177 /* if the token is an RTC one, update the time
178 and do the necessary checks */
179 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
180 && token
->type
<= WPS_TOKENS_RTC_END
)
198 case WPS_TOKEN_CHARACTER
:
199 if (token
->value
.c
== '\n')
201 return &(token
->value
.c
);
203 case WPS_TOKEN_STRING
:
204 return (char*)token
->value
.data
;
206 case WPS_TOKEN_TRANSLATEDSTRING
:
207 return (char*)P2STR(ID2P(token
->value
.i
));
209 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
210 format_time(buf
, buf_size
,
211 elapsed
+ state
->ff_rewind_count
);
214 case WPS_TOKEN_TRACK_TIME_REMAINING
:
215 format_time(buf
, buf_size
,
217 state
->ff_rewind_count
);
220 case WPS_TOKEN_TRACK_LENGTH
:
221 format_time(buf
, buf_size
, length
);
224 case WPS_TOKEN_PLAYLIST_ENTRIES
:
225 snprintf(buf
, buf_size
, "%d", playlist_amount());
228 case WPS_TOKEN_PLAYLIST_NAME
:
229 return playlist_name(NULL
, buf
, buf_size
);
231 case WPS_TOKEN_PLAYLIST_POSITION
:
232 snprintf(buf
, buf_size
, "%d", playlist_get_display_index());
235 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
236 if ( global_settings
.playlist_shuffle
)
242 case WPS_TOKEN_VOLUME
:
243 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
246 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
250 else if (global_settings
.volume
== 0)
254 else if (global_settings
.volume
> 0)
260 *intval
= (limit
- 3) * (global_settings
.volume
261 - sound_min(SOUND_VOLUME
) - 1)
262 / (-1 - sound_min(SOUND_VOLUME
)) + 2;
267 case WPS_TOKEN_TRACK_ELAPSED_PERCENT
:
273 *intval
= limit
* (elapsed
+ state
->ff_rewind_count
)
276 snprintf(buf
, buf_size
, "%d",
277 100*(elapsed
+ state
->ff_rewind_count
) / length
);
280 case WPS_TOKEN_METADATA_ARTIST
:
281 return HANDLE_NULL_ID3(id3
->artist
);
283 case WPS_TOKEN_METADATA_COMPOSER
:
284 return HANDLE_NULL_ID3(id3
->composer
);
286 case WPS_TOKEN_METADATA_ALBUM
:
287 return HANDLE_NULL_ID3(id3
->album
);
289 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
290 return HANDLE_NULL_ID3(id3
->albumartist
);
292 case WPS_TOKEN_METADATA_GROUPING
:
293 return HANDLE_NULL_ID3(id3
->grouping
);
295 case WPS_TOKEN_METADATA_GENRE
:
296 return HANDLE_NULL_ID3(id3
->genre_string
);
298 case WPS_TOKEN_METADATA_DISC_NUMBER
:
300 if (id3
->disc_string
)
301 return id3
->disc_string
;
303 snprintf(buf
, buf_size
, "%d", id3
->discnum
);
309 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
311 if (id3
->track_string
)
312 return id3
->track_string
;
315 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
321 case WPS_TOKEN_METADATA_TRACK_TITLE
:
322 return HANDLE_NULL_ID3(id3
->title
);
324 case WPS_TOKEN_METADATA_VERSION
:
327 switch (id3
->id3version
)
350 case WPS_TOKEN_METADATA_YEAR
:
352 if( id3
->year_string
)
353 return id3
->year_string
;
356 snprintf(buf
, buf_size
, "%d", id3
->year
);
362 case WPS_TOKEN_METADATA_COMMENT
:
363 return HANDLE_NULL_ID3(id3
->comment
);
366 case WPS_TOKEN_ALBUMART_FOUND
:
367 if (data
->albumart
) {
368 if (playback_current_aa_hid(data
->playback_aa_slot
) >= 0)
373 case WPS_TOKEN_ALBUMART_DISPLAY
:
376 if (!data
->albumart
->draw
)
377 data
->albumart
->draw
= true;
381 case WPS_TOKEN_FILE_BITRATE
:
382 if(id3
&& id3
->bitrate
)
383 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
388 case WPS_TOKEN_FILE_CODEC
:
393 else if(id3
->codectype
== AFMT_UNKNOWN
)
394 *intval
= AFMT_NUM_CODECS
;
396 *intval
= id3
->codectype
;
398 return get_codectype(id3
);
400 case WPS_TOKEN_FILE_FREQUENCY
:
401 HANDLE_NULL_ID3_NUM_ZERO
;
402 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
405 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
406 HANDLE_NULL_ID3_NUM_ZERO
;
407 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
408 if ((id3
->frequency
% 1000) < 100)
409 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
411 snprintf(buf
, buf_size
, "%ld.%d",
412 id3
->frequency
/ 1000,
413 (id3
->frequency
% 1000) / 100);
416 case WPS_TOKEN_FILE_NAME
:
417 if (LIKELY(id3
) && get_dir(buf
, buf_size
, id3
->path
, 0)) {
418 /* Remove extension */
419 char* sep
= strrchr(buf
, '.');
429 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
431 return get_dir(buf
, buf_size
, id3
->path
, 0);
434 case WPS_TOKEN_FILE_PATH
:
435 return HANDLE_NULL_ID3(id3
->path
);
437 case WPS_TOKEN_FILE_SIZE
:
438 HANDLE_NULL_ID3_NUM_ZERO
;
439 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
442 case WPS_TOKEN_FILE_VBR
:
443 return (LIKELY(id3
) && id3
->vbr
) ? "(avg)" : NULL
;
445 case WPS_TOKEN_FILE_DIRECTORY
:
447 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
450 case WPS_TOKEN_BATTERY_PERCENT
:
452 int l
= battery_level();
456 limit
= MAX(limit
, 2);
458 /* First enum is used for "unknown level". */
459 *intval
= (limit
- 1) * l
/ 100 + 2;
466 snprintf(buf
, buf_size
, "%d", l
);
473 case WPS_TOKEN_BATTERY_VOLTS
:
475 unsigned int v
= battery_voltage();
476 snprintf(buf
, buf_size
, "%d.%02d", v
/ 1000, (v
% 1000) / 10);
480 case WPS_TOKEN_BATTERY_TIME
:
482 int t
= battery_time();
484 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
491 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
493 if(charger_input_state
==CHARGER
)
499 #if CONFIG_CHARGING >= CHARGING_MONITOR
500 case WPS_TOKEN_BATTERY_CHARGING
:
502 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
509 case WPS_TOKEN_BATTERY_SLEEPTIME
:
511 if (get_sleep_timer() == 0)
515 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
520 case WPS_TOKEN_PLAYBACK_STATUS
:
522 int status
= current_playmode();
524 int mode
= 1; /* stop */
525 if (status
== STATUS_PLAY
)
527 if (state
->is_fading
||
528 (status
== STATUS_PAUSE
&& !status_get_ffmode()))
529 mode
= 3; /* pause */
532 if (status_get_ffmode() == STATUS_FASTFORWARD
)
534 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
537 #ifdef HAVE_RECORDING
539 if (status
== STATUS_RECORD
)
541 else if (status
== STATUS_RECORD_PAUSE
)
546 if (status
== STATUS_RADIO
)
548 else if (status
== STATUS_RADIO_PAUSE
)
556 snprintf(buf
, buf_size
, "%d", mode
-1);
560 case WPS_TOKEN_REPEAT_MODE
:
562 *intval
= global_settings
.repeat_mode
+ 1;
563 snprintf(buf
, buf_size
, "%d", global_settings
.repeat_mode
);
566 case WPS_TOKEN_RTC_PRESENT
:
572 case WPS_TOKEN_RTC_24HR_SETTING_MODE
:
574 return global_settings
.timeformat
?NULL
:"z";
580 case WPS_TOKEN_RTC_12HOUR_CFG
:
582 *intval
= global_settings
.timeformat
+ 1;
583 snprintf(buf
, buf_size
, "%d", global_settings
.timeformat
);
586 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
587 /* d: day of month (01..31) */
588 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
591 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
592 /* e: day of month, blank padded ( 1..31) */
593 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
596 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
597 /* H: hour (00..23) */
598 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
601 case WPS_TOKEN_RTC_HOUR_24
:
602 /* k: hour ( 0..23) */
603 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
606 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
607 /* I: hour (01..12) */
608 snprintf(buf
, buf_size
, "%02d",
609 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
612 case WPS_TOKEN_RTC_HOUR_12
:
613 /* l: hour ( 1..12) */
614 snprintf(buf
, buf_size
, "%2d",
615 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
618 case WPS_TOKEN_RTC_MONTH
:
619 /* m: month (01..12) */
621 *intval
= tm
->tm_mon
+ 1;
622 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
625 case WPS_TOKEN_RTC_MINUTE
:
626 /* M: minute (00..59) */
627 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
630 case WPS_TOKEN_RTC_SECOND
:
631 /* S: second (00..59) */
632 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
635 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
636 /* y: last two digits of year (00..99) */
637 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
640 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
641 /* Y: year (1970...) */
642 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
645 case WPS_TOKEN_RTC_AM_PM_UPPER
:
646 /* p: upper case AM or PM indicator */
647 return tm
->tm_hour
/12 == 0 ? "AM" : "PM";
649 case WPS_TOKEN_RTC_AM_PM_LOWER
:
650 /* P: lower case am or pm indicator */
651 return tm
->tm_hour
/12 == 0 ? "am" : "pm";
653 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
654 /* a: abbreviated weekday name (Sun..Sat) */
655 return str(LANG_WEEKDAY_SUNDAY
+ tm
->tm_wday
);
657 case WPS_TOKEN_RTC_MONTH_NAME
:
658 /* b: abbreviated month name (Jan..Dec) */
659 return str(LANG_MONTH_JANUARY
+ tm
->tm_mon
);
661 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
662 /* u: day of week (1..7); 1 is Monday */
664 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
665 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
668 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
669 /* w: day of week (0..6); 0 is Sunday */
671 *intval
= tm
->tm_wday
+ 1;
672 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
675 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
676 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
677 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
678 case WPS_TOKEN_RTC_HOUR_24
:
679 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
680 case WPS_TOKEN_RTC_HOUR_12
:
681 case WPS_TOKEN_RTC_MONTH
:
682 case WPS_TOKEN_RTC_MINUTE
:
683 case WPS_TOKEN_RTC_SECOND
:
684 case WPS_TOKEN_RTC_AM_PM_UPPER
:
685 case WPS_TOKEN_RTC_AM_PM_LOWER
:
686 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
688 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
690 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
691 case WPS_TOKEN_RTC_MONTH_NAME
:
693 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
694 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
698 #ifdef HAVE_LCD_CHARCELLS
699 case WPS_TOKEN_PROGRESSBAR
:
701 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
706 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
709 /* we need 11 characters (full line) for
711 strlcpy(buf
, " ", buf_size
);
715 /* Tell the user if we have an OldPlayer */
716 strlcpy(buf
, " <Old LCD> ", buf_size
);
723 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
724 HANDLE_NULL_ID3_NUM_INTVAL(id3
->playcount
);
726 case WPS_TOKEN_DATABASE_RATING
:
727 HANDLE_NULL_ID3_NUM_INTVAL(id3
->rating
);
729 case WPS_TOKEN_DATABASE_AUTOSCORE
:
730 HANDLE_NULL_ID3_NUM_INTVAL(id3
->score
);
733 #if (CONFIG_CODEC == SWCODEC)
734 case WPS_TOKEN_CROSSFADE
:
736 *intval
= global_settings
.crossfade
+ 1;
737 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
740 case WPS_TOKEN_REPLAYGAIN
:
744 if (global_settings
.replaygain_type
== REPLAYGAIN_OFF
)
750 type
= get_replaygain_mode(id3
->track_gain_string
!= NULL
,
751 id3
->album_gain_string
!= NULL
);
756 val
= 6; /* no tag */
760 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
773 /* due to above, coming here with !id3 shouldn't be possible */
776 strlcpy(buf
, id3
->track_gain_string
, buf_size
);
780 strlcpy(buf
, id3
->album_gain_string
, buf_size
);
785 #endif /* (CONFIG_CODEC == SWCODEC) */
787 #if (CONFIG_CODEC != MAS3507D)
788 case WPS_TOKEN_SOUND_PITCH
:
790 int32_t pitch
= sound_get_pitch();
791 snprintf(buf
, buf_size
, "%ld.%ld",
792 pitch
/ PITCH_SPEED_PRECISION
,
793 (pitch
% PITCH_SPEED_PRECISION
) / (PITCH_SPEED_PRECISION
/ 10));
798 case WPS_TOKEN_MAIN_HOLD
:
799 #ifdef HAS_BUTTON_HOLD
802 if (is_keys_locked())
803 #endif /*hold switch or softlock*/
808 #ifdef HAS_REMOTE_BUTTON_HOLD
809 case WPS_TOKEN_REMOTE_HOLD
:
810 if (remote_button_hold())
816 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
817 case WPS_TOKEN_VLED_HDD
:
823 case WPS_TOKEN_BUTTON_VOLUME
:
824 if (global_status
.last_volume_change
&&
825 TIME_BEFORE(current_tick
, global_status
.last_volume_change
+
826 token
->value
.i
* TIMEOUT_UNIT
))
829 case WPS_TOKEN_LASTTOUCH
:
830 #ifdef HAVE_TOUCHSCREEN
831 if (TIME_BEFORE(current_tick
, token
->value
.i
* TIMEOUT_UNIT
+
832 touchscreen_last_touch()))
837 case WPS_TOKEN_SETTING
:
841 /* Handle contionals */
842 const struct settings_list
*s
= settings
+token
->value
.i
;
843 switch (s
->flags
&F_T_MASK
)
848 /* %?St|name|<#000000|#000001|...|#FFFFFF> */
849 /* shouldn't overflow since colors are stored
851 * but this is pretty useless anyway */
852 *intval
= *(int*)s
->setting
+ 1;
853 else if (s
->cfg_vals
== NULL
)
854 /* %?St|name|<1st choice|2nd choice|...> */
855 *intval
= (*(int*)s
->setting
-s
->int_setting
->min
)
856 /s
->int_setting
->step
+ 1;
858 /* %?St|name|<1st choice|2nd choice|...> */
859 /* Not sure about this one. cfg_name/vals are
860 * indexed from 0 right? */
861 *intval
= *(int*)s
->setting
+ 1;
864 /* %?St|name|<if true|if false> */
865 *intval
= *(bool*)s
->setting
?1:2;
868 /* %?St|name|<if non empty string|if empty>
869 * The string's emptyness discards the setting's
870 * prefix and suffix */
871 *intval
= ((char*)s
->setting
)[0]?1:2;
874 /* This shouldn't happen ... but you never know */
879 cfg_to_string(token
->value
.i
,buf
,buf_size
);
882 case WPS_TOKEN_CURRENT_SCREEN
:
884 int curr_screen
= current_screen();
886 #ifdef HAVE_RECORDING
887 /* override current_screen() for recording screen since it may
888 * be entered from the radio screen */
889 if (in_recording_screen())
890 curr_screen
= GO_TO_RECSCREEN
;
898 #ifdef HAVE_RECORDING
899 case GO_TO_RECSCREEN
:
915 *intval
= curr_screen
;
917 snprintf(buf
, buf_size
, "%d", curr_screen
);