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 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
20 #include "gwps-common.h"
27 #include "rbunicode.h"
32 #include "powermgmt.h"
35 #ifdef HAVE_LCD_CHARCELLS
39 #include "mp3_playback.h"
40 #include "backlight.h"
44 #include "scrollbar.h"
47 #ifdef HAVE_LCD_BITMAP
48 #include "peakmeter.h"
57 #if CONFIG_CODEC == SWCODEC
62 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
63 /* 3% of 30min file == 54s step size */
64 #define MIN_FF_REWIND_STEP 500
66 /* draws the statusbar on the given wps-screen */
67 #ifdef HAVE_LCD_BITMAP
68 static void gui_wps_statusbar_draw(struct gui_wps
*wps
, bool force
)
70 bool draw
= global_settings
.statusbar
;
72 if (wps
->data
->wps_sb_tag
)
73 draw
= wps
->data
->show_sb_on_wps
;
76 gui_statusbar_draw(wps
->statusbar
, force
);
79 #define gui_wps_statusbar_draw(wps, force) \
80 gui_statusbar_draw((wps)->statusbar, (force))
84 /* fades the volume */
85 bool wps_fading_out
= false;
86 void fade(bool fade_in
, bool updatewps
)
88 int fp_global_vol
= global_settings
.volume
<< 8;
89 int fp_min_vol
= sound_min(SOUND_VOLUME
) << 8;
90 int fp_step
= (fp_global_vol
- fp_min_vol
) / 30;
92 wps_fading_out
= !fade_in
;
95 int fp_volume
= fp_min_vol
;
97 /* zero out the sound */
98 sound_set_volume(fp_min_vol
>> 8);
100 sleep(HZ
/10); /* let audio thread run */
103 while (fp_volume
< fp_global_vol
- fp_step
) {
104 fp_volume
+= fp_step
;
105 sound_set_volume(fp_volume
>> 8);
109 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_NON_STATIC
);
113 sound_set_volume(global_settings
.volume
);
117 int fp_volume
= fp_global_vol
;
119 while (fp_volume
> fp_min_vol
+ fp_step
) {
120 fp_volume
-= fp_step
;
121 sound_set_volume(fp_volume
>> 8);
125 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_NON_STATIC
);
130 wps_fading_out
= false;
131 #if CONFIG_CODEC != SWCODEC
133 /* let audio thread run and wait for the mas to run out of data */
134 while (!mp3_pause_done())
139 /* reset volume to what it was before the fade */
140 sound_set_volume(global_settings
.volume
);
144 /* return true if screen restore is needed
145 return false otherwise
147 bool update_onvol_change(struct gui_wps
* gwps
)
149 gui_wps_statusbar_draw(gwps
, false);
150 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
152 #ifdef HAVE_LCD_CHARCELLS
153 gui_splash(gwps
->display
, 0, "Vol: %3d dB",
154 sound_val2phys(SOUND_VOLUME
, global_settings
.volume
));
160 void play_hop(int direction
)
162 if(!wps_state
.id3
|| !wps_state
.id3
->length
163 || global_settings
.study_hop_step
== 0)
165 #define STEP ((unsigned)global_settings.study_hop_step *1000)
167 && wps_state
.id3
->length
- wps_state
.id3
->elapsed
< STEP
+1000) {
168 #if CONFIG_CODEC == SWCODEC
169 if(global_settings
.beep
)
170 pcmbuf_beep(1000, 150, 1500*global_settings
.beep
);
174 if((direction
== -1 && wps_state
.id3
->elapsed
< STEP
))
175 wps_state
.id3
->elapsed
= 0;
177 wps_state
.id3
->elapsed
+= STEP
*direction
;
178 if((audio_status() & AUDIO_STATUS_PLAY
) && !wps_state
.paused
) {
179 #if (CONFIG_CODEC == SWCODEC)
180 audio_pre_ff_rewind();
185 audio_ff_rewind(wps_state
.id3
->elapsed
);
186 #if (CONFIG_CODEC != SWCODEC)
187 if (!wps_state
.paused
)
193 bool ffwd_rew(int button
)
195 unsigned int step
= 0; /* current ff/rewind step */
196 unsigned int max_step
= 0; /* maximum ff/rewind step */
197 int ff_rewind_count
= 0; /* current ff/rewind count (in ticks) */
198 int direction
= -1; /* forward=1 or backward=-1 */
199 long accel_tick
= 0; /* next time at which to bump the step size */
204 if (button
== ACTION_NONE
)
206 status_set_ffmode(0);
213 case ACTION_WPS_SEEKFWD
:
215 case ACTION_WPS_SEEKBACK
:
216 if (wps_state
.ff_rewind
)
220 /* fast forwarding, calc max step relative to end */
221 max_step
= (wps_state
.id3
->length
-
222 (wps_state
.id3
->elapsed
+
224 FF_REWIND_MAX_PERCENT
/ 100;
228 /* rewinding, calc max step relative to start */
229 max_step
= (wps_state
.id3
->elapsed
+ ff_rewind_count
) *
230 FF_REWIND_MAX_PERCENT
/ 100;
233 max_step
= MAX(max_step
, MIN_FF_REWIND_STEP
);
238 ff_rewind_count
+= step
* direction
;
240 if (global_settings
.ff_rewind_accel
!= 0 &&
241 current_tick
>= accel_tick
)
244 accel_tick
= current_tick
+
245 global_settings
.ff_rewind_accel
*HZ
;
250 if ( (audio_status() & AUDIO_STATUS_PLAY
) &&
251 wps_state
.id3
&& wps_state
.id3
->length
)
253 if (!wps_state
.paused
)
254 #if (CONFIG_CODEC == SWCODEC)
255 audio_pre_ff_rewind();
259 #if CONFIG_KEYPAD == PLAYER_PAD
261 gui_wps
[i
].display
->stop_scroll();
264 status_set_ffmode(STATUS_FASTFORWARD
);
266 status_set_ffmode(STATUS_FASTBACKWARD
);
268 wps_state
.ff_rewind
= true;
270 step
= 1000 * global_settings
.ff_rewind_min_step
;
272 accel_tick
= current_tick
+
273 global_settings
.ff_rewind_accel
*HZ
;
280 if ((wps_state
.id3
->elapsed
+ ff_rewind_count
) >
281 wps_state
.id3
->length
)
282 ff_rewind_count
= wps_state
.id3
->length
-
283 wps_state
.id3
->elapsed
;
286 if ((int)(wps_state
.id3
->elapsed
+ ff_rewind_count
) < 0)
287 ff_rewind_count
= -wps_state
.id3
->elapsed
;
291 gui_wps_refresh(&gui_wps
[i
],
292 (wps_state
.wps_time_countup
== false)?
293 ff_rewind_count
:-ff_rewind_count
,
294 WPS_REFRESH_PLAYER_PROGRESS
|
295 WPS_REFRESH_DYNAMIC
);
299 case ACTION_WPS_STOPSEEK
:
300 wps_state
.id3
->elapsed
= wps_state
.id3
->elapsed
+ff_rewind_count
;
301 audio_ff_rewind(wps_state
.id3
->elapsed
);
303 wps_state
.ff_rewind
= false;
304 status_set_ffmode(0);
305 #if (CONFIG_CODEC != SWCODEC)
306 if (!wps_state
.paused
)
309 #ifdef HAVE_LCD_CHARCELLS
316 if(default_event_handler(button
) == SYS_USB_CONNECTED
) {
317 status_set_ffmode(0);
324 button
= get_action(CONTEXT_WPS
|ALLOW_SOFTLOCK
,TIMEOUT_BLOCK
);
329 bool gui_wps_display(void)
332 if (!wps_state
.id3
&& !(audio_status() & AUDIO_STATUS_PLAY
))
334 global_status
.resume_index
= -1;
335 #ifdef HAVE_LCD_BITMAP
336 gui_syncstatusbar_draw(&statusbars
, true);
338 gui_syncsplash(HZ
, ID2P(LANG_END_PLAYLIST
));
345 /* Update the values in the first (default) viewport - in case the user
346 has modified the statusbar or colour settings */
347 #ifdef HAVE_LCD_BITMAP
349 if (gui_wps
[i
].display
->depth
> 1)
351 gui_wps
[i
].data
->viewports
[0].vp
.fg_pattern
= gui_wps
[i
].display
->get_foreground();
352 gui_wps
[i
].data
->viewports
[0].vp
.bg_pattern
= gui_wps
[i
].display
->get_background();
357 gui_wps
[i
].display
->clear_display();
358 if (!gui_wps
[i
].data
->wps_loaded
) {
359 if ( !gui_wps
[i
].data
->num_tokens
) {
360 /* set the default wps for the main-screen */
363 #ifdef HAVE_LCD_BITMAP
365 unload_wps_backdrop();
367 wps_data_load(gui_wps
[i
].data
,
369 "%s%?it<%?in<%in. |>%it|%fn>\n"
370 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
371 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
373 "%al%pc/%pt%ar[%pp:%pe]\n"
374 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
378 wps_data_load(gui_wps
[i
].data
,
380 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
381 "%pc%?ps<*|/>%pt\n", false);
385 /* set the default wps for the remote-screen */
388 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
389 unload_remote_wps_backdrop();
391 wps_data_load(gui_wps
[i
].data
,
393 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
394 "%s%?it<%?in<%in. |>%it|%fn>\n"
395 "%al%pc/%pt%ar[%pp:%pe]\n"
396 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
407 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_ALL
);
412 bool update(struct gui_wps
*gwps
)
414 bool track_changed
= audio_has_changed_track();
415 bool retcode
= false;
417 gwps
->state
->nid3
= audio_next_track();
420 gwps
->display
->stop_scroll();
421 gwps
->state
->id3
= audio_current_track();
423 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
424 && strcmp(gwps
->state
->id3
->path
, curr_cue
->audio_filename
))
426 /* the current cuesheet isn't the right one any more */
428 if (!strcmp(gwps
->state
->id3
->path
, temp_cue
->audio_filename
)) {
429 /* We have the new cuesheet in memory (temp_cue),
430 let's make it the current one ! */
431 memcpy(curr_cue
, temp_cue
, sizeof(struct cuesheet
));
434 /* We need to parse the new cuesheet */
436 char cuepath
[MAX_PATH
];
438 if (look_for_cuesheet_file(gwps
->state
->id3
->path
, cuepath
) &&
439 parse_cuesheet(cuepath
, curr_cue
))
441 gwps
->state
->id3
->cuesheet_type
= 1;
442 strcpy(curr_cue
->audio_filename
, gwps
->state
->id3
->path
);
446 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
449 if (gui_wps_display())
452 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
455 if (gwps
->state
->id3
)
457 strncpy(gwps
->state
->current_track_path
, gwps
->state
->id3
->path
,
458 sizeof(gwps
->state
->current_track_path
));
459 gwps
->state
->current_track_path
[sizeof(gwps
->state
->current_track_path
)-1] = '\0';
463 if (gwps
->state
->id3
)
465 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
466 && (gwps
->state
->id3
->elapsed
< curr_cue
->curr_track
->offset
467 || (curr_cue
->curr_track_idx
< curr_cue
->track_count
- 1
468 && gwps
->state
->id3
->elapsed
>= (curr_cue
->curr_track
+1)->offset
)))
470 /* We've changed tracks within the cuesheet :
471 we need to update the ID3 info and refresh the WPS */
473 cue_find_current_track(curr_cue
, gwps
->state
->id3
->elapsed
);
474 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
476 gwps
->display
->stop_scroll();
477 if (gui_wps_display())
480 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
483 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
486 gui_wps_statusbar_draw(gwps
, false);
492 void display_keylock_text(bool locked
)
497 gui_wps
[i
].display
->stop_scroll();
500 s
= str(LANG_KEYLOCK_ON
);
502 s
= str(LANG_KEYLOCK_OFF
);
503 gui_syncsplash(HZ
, s
);
506 #ifdef HAVE_LCD_BITMAP
508 static void draw_progressbar(struct gui_wps
*gwps
,
509 struct progressbar
*pb
)
511 struct screen
*display
= gwps
->display
;
512 struct wps_state
*state
= gwps
->state
;
513 if (pb
->have_bitmap_pb
)
514 gui_bitmap_scrollbar_draw(display
, pb
->bm
,
515 pb
->x
, pb
->y
, pb
->width
, pb
->bm
.height
,
516 state
->id3
->length
? state
->id3
->length
: 1, 0,
517 state
->id3
->length
? state
->id3
->elapsed
518 + state
->ff_rewind_count
: 0,
521 gui_scrollbar_draw(display
, pb
->x
, pb
->y
, pb
->width
, pb
->height
,
522 state
->id3
->length
? state
->id3
->length
: 1, 0,
523 state
->id3
->length
? state
->id3
->elapsed
524 + state
->ff_rewind_count
: 0,
526 #ifdef AB_REPEAT_ENABLE
527 if ( ab_repeat_mode_enabled() && state
->id3
->length
!= 0 )
528 ab_draw_markers(display
, state
->id3
->length
,
529 pb
->x
, pb
->x
+ pb
->width
, pb
->y
, pb
->height
);
532 if ( cuesheet_is_enabled() && state
->id3
->cuesheet_type
)
533 cue_draw_markers(display
, state
->id3
->length
,
534 pb
->x
, pb
->x
+ pb
->width
, pb
->y
+1, pb
->height
-2);
537 /* clears the area where the image was shown */
538 static void clear_image_pos(struct gui_wps
*gwps
, int n
)
542 struct wps_data
*data
= gwps
->data
;
543 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
544 gwps
->display
->fillrect(data
->img
[n
].x
, data
->img
[n
].y
,
545 data
->img
[n
].bm
.width
, data
->img
[n
].subimage_height
);
546 gwps
->display
->set_drawmode(DRMODE_SOLID
);
549 static void wps_draw_image(struct gui_wps
*gwps
, int n
, int subimage
)
551 struct screen
*display
= gwps
->display
;
552 struct wps_data
*data
= gwps
->data
;
553 if(data
->img
[n
].always_display
)
554 display
->set_drawmode(DRMODE_FG
);
556 display
->set_drawmode(DRMODE_SOLID
);
559 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
561 display
->mono_bitmap_part(data
->img
[n
].bm
.data
,
562 0, data
->img
[n
].subimage_height
* subimage
,
563 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
564 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
565 data
->img
[n
].subimage_height
);
568 display
->transparent_bitmap_part((fb_data
*)data
->img
[n
].bm
.data
,
569 0, data
->img
[n
].subimage_height
* subimage
,
570 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
571 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
572 data
->img
[n
].subimage_height
);
577 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
579 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
583 struct wps_data
*data
= gwps
->data
;
584 struct screen
*display
= gwps
->display
;
586 for (n
= 0; n
< MAX_IMAGES
; n
++)
588 if (data
->img
[n
].loaded
)
590 if (data
->img
[n
].display
>= 0)
592 wps_draw_image(gwps
, n
, data
->img
[n
].display
);
593 } else if (data
->img
[n
].always_display
&& data
->img
[n
].vp
== vp
)
595 wps_draw_image(gwps
, n
, 0);
599 display
->set_drawmode(DRMODE_SOLID
);
602 #else /* HAVE_LCD_CHARCELL */
604 static bool draw_player_progress(struct gui_wps
*gwps
)
606 struct wps_state
*state
= gwps
->state
;
607 struct screen
*display
= gwps
->display
;
608 unsigned char progress_pattern
[7];
615 if (state
->id3
->length
)
616 pos
= 36 * (state
->id3
->elapsed
+ state
->ff_rewind_count
)
617 / state
->id3
->length
;
619 for (i
= 0; i
< 7; i
++, pos
-= 5)
622 progress_pattern
[i
] = 0x1f;
624 progress_pattern
[i
] = 0x00;
626 progress_pattern
[i
] = 0x1f >> pos
;
629 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
633 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
635 static const unsigned char numbers
[10][4] = {
636 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
637 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
638 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
639 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
640 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
641 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
642 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
643 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
644 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
645 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
648 struct wps_state
*state
= gwps
->state
;
649 struct screen
*display
= gwps
->display
;
650 struct wps_data
*data
= gwps
->data
;
651 unsigned char progress_pattern
[7];
660 if (!state
->id3
|| buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
663 time
= state
->id3
->elapsed
+ state
->ff_rewind_count
;
664 if (state
->id3
->length
)
665 pos
= 55 * time
/ state
->id3
->length
;
667 memset(timestr
, 0, sizeof(timestr
));
668 format_time(timestr
, sizeof(timestr
)-2, time
);
669 timestr
[strlen(timestr
)] = ':'; /* always safe */
671 for (i
= 0; i
< 11; i
++, pos
-= 5)
674 memset(progress_pattern
, 0, sizeof(progress_pattern
));
676 if ((digit
= timestr
[time_idx
]))
681 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
683 memcpy(progress_pattern
, numbers
[digit
], 4);
686 else /* tens, shifted right */
688 for (j
= 0; j
< 4; j
++)
689 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
691 if (time_idx
> 0) /* not the first group, add colon in front */
693 progress_pattern
[1] |= 0x10;
694 progress_pattern
[3] |= 0x10;
700 progress_pattern
[5] = progress_pattern
[6] = 0x1f;
703 if (pos
> 0 && pos
< 5)
706 progress_pattern
[5] = progress_pattern
[6] = (~0x1f >> pos
) & 0x1f;
709 if (softchar
&& pat_idx
< 8)
711 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
713 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
717 buf
= utf8encode(' ', buf
);
719 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
724 #endif /* HAVE_LCD_CHARCELL */
726 static char* get_codectype(const struct mp3entry
* id3
)
728 if (id3
->codectype
< AFMT_NUM_CODECS
) {
729 return (char*)audio_formats
[id3
->codectype
].label
;
735 /* Extract a part from a path.
737 * buf - buffer extract part to.
738 * buf_size - size of buffer.
739 * path - path to extract from.
740 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
741 * parent of parent, etc.
743 * Returns buf if the desired level was found, NULL otherwise.
745 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
748 const char* last_sep
;
751 sep
= path
+ strlen(path
);
766 if (level
|| (last_sep
<= sep
))
769 len
= MIN(last_sep
- sep
, buf_size
- 1);
770 strncpy(buf
, sep
+ 1, len
);
775 /* Return the tag found at index i and write its value in buf.
776 The return value is buf if the tag had a value, or NULL if not.
778 intval is used with conditionals/enums: when this function is called,
779 intval should contain the number of options in the conditional/enum.
780 When this function returns, intval is -1 if the tag is non numeric or,
781 if the tag is numeric, intval is the enum case we want to go to.
782 When not treating a conditional/enum, intval should be NULL.
784 static char *get_token_value(struct gui_wps
*gwps
,
785 struct wps_token
*token
,
786 char *buf
, int buf_size
,
792 struct wps_data
*data
= gwps
->data
;
793 struct wps_state
*state
= gwps
->state
;
798 struct mp3entry
*id3
;
809 struct tm
* tm
= NULL
;
811 /* if the token is an RTC one, update the time
812 and do the necessary checks */
813 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
814 && token
->type
<= WPS_TOKENS_RTC_END
)
832 case WPS_TOKEN_CHARACTER
:
833 return &(token
->value
.c
);
835 case WPS_TOKEN_STRING
:
836 return data
->strings
[token
->value
.i
];
838 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
839 format_time(buf
, buf_size
,
840 id3
->elapsed
+ state
->ff_rewind_count
);
843 case WPS_TOKEN_TRACK_TIME_REMAINING
:
844 format_time(buf
, buf_size
,
845 id3
->length
- id3
->elapsed
-
846 state
->ff_rewind_count
);
849 case WPS_TOKEN_TRACK_LENGTH
:
850 format_time(buf
, buf_size
, id3
->length
);
853 case WPS_TOKEN_PLAYLIST_ENTRIES
:
854 snprintf(buf
, buf_size
, "%d", playlist_amount());
857 case WPS_TOKEN_PLAYLIST_NAME
:
858 return playlist_name(NULL
, buf
, buf_size
);
860 case WPS_TOKEN_PLAYLIST_POSITION
:
861 snprintf(buf
, buf_size
, "%d", playlist_get_display_index());
864 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
865 if ( global_settings
.playlist_shuffle
)
871 case WPS_TOKEN_VOLUME
:
872 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
875 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
879 else if (global_settings
.volume
== 0)
883 else if (global_settings
.volume
> 0)
889 *intval
= (limit
- 3) * (global_settings
.volume
890 - sound_min(SOUND_VOLUME
) - 1)
891 / (-1 - sound_min(SOUND_VOLUME
)) + 2;
896 case WPS_TOKEN_TRACK_ELAPSED_PERCENT
:
897 if (id3
->length
<= 0)
902 *intval
= limit
* (id3
->elapsed
+ state
->ff_rewind_count
)
905 snprintf(buf
, buf_size
, "%d",
906 100*(id3
->elapsed
+ state
->ff_rewind_count
) / id3
->length
);
909 case WPS_TOKEN_METADATA_ARTIST
:
912 case WPS_TOKEN_METADATA_COMPOSER
:
913 return id3
->composer
;
915 case WPS_TOKEN_METADATA_ALBUM
:
918 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
919 return id3
->albumartist
;
921 case WPS_TOKEN_METADATA_GROUPING
:
922 return id3
->grouping
;
924 case WPS_TOKEN_METADATA_GENRE
:
925 return id3
->genre_string
;
927 case WPS_TOKEN_METADATA_DISC_NUMBER
:
928 if (id3
->disc_string
)
929 return id3
->disc_string
;
931 snprintf(buf
, buf_size
, "%d", id3
->discnum
);
936 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
937 if (id3
->track_string
)
938 return id3
->track_string
;
941 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
946 case WPS_TOKEN_METADATA_TRACK_TITLE
:
949 case WPS_TOKEN_METADATA_VERSION
:
950 switch (id3
->id3version
)
971 case WPS_TOKEN_METADATA_YEAR
:
972 if( id3
->year_string
)
973 return id3
->year_string
;
976 snprintf(buf
, buf_size
, "%d", id3
->year
);
981 case WPS_TOKEN_METADATA_COMMENT
:
985 case WPS_TOKEN_ALBUMART_DISPLAY
:
986 draw_album_art(gwps
, audio_current_aa_hid(), false);
989 case WPS_TOKEN_ALBUMART_FOUND
:
990 if (audio_current_aa_hid() >= 0) {
991 snprintf(buf
, buf_size
, "C");
997 case WPS_TOKEN_FILE_BITRATE
:
999 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
1001 snprintf(buf
, buf_size
, "?");
1004 case WPS_TOKEN_FILE_CODEC
:
1007 if(id3
->codectype
== AFMT_UNKNOWN
)
1008 *intval
= AFMT_NUM_CODECS
;
1010 *intval
= id3
->codectype
;
1012 return get_codectype(id3
);
1014 case WPS_TOKEN_FILE_FREQUENCY
:
1015 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
1018 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
1019 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
1020 if ((id3
->frequency
% 1000) < 100)
1021 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
1023 snprintf(buf
, buf_size
, "%ld.%d",
1024 id3
->frequency
/ 1000,
1025 (id3
->frequency
% 1000) / 100);
1028 case WPS_TOKEN_FILE_NAME
:
1029 if (get_dir(buf
, buf_size
, id3
->path
, 0)) {
1030 /* Remove extension */
1031 char* sep
= strrchr(buf
, '.');
1041 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
1042 return get_dir(buf
, buf_size
, id3
->path
, 0);
1044 case WPS_TOKEN_FILE_PATH
:
1047 case WPS_TOKEN_FILE_SIZE
:
1048 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
1051 case WPS_TOKEN_FILE_VBR
:
1052 return id3
->vbr
? "(avg)" : NULL
;
1054 case WPS_TOKEN_FILE_DIRECTORY
:
1055 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
1057 case WPS_TOKEN_BATTERY_PERCENT
:
1059 int l
= battery_level();
1063 limit
= MAX(limit
, 2);
1065 /* First enum is used for "unknown level". */
1066 *intval
= (limit
- 1) * l
/ 100 + 2;
1073 snprintf(buf
, buf_size
, "%d", l
);
1080 case WPS_TOKEN_BATTERY_VOLTS
:
1082 unsigned int v
= battery_voltage();
1083 snprintf(buf
, buf_size
, "%d.%02d", v
/ 1000, (v
% 1000) / 10);
1087 case WPS_TOKEN_BATTERY_TIME
:
1089 int t
= battery_time();
1091 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
1093 strncpy(buf
, "?h ?m", buf_size
);
1098 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
1100 if(charger_input_state
==CHARGER
)
1106 #if CONFIG_CHARGING >= CHARGING_MONITOR
1107 case WPS_TOKEN_BATTERY_CHARGING
:
1109 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
1116 case WPS_TOKEN_BATTERY_SLEEPTIME
:
1118 if (get_sleep_timer() == 0)
1122 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
1127 case WPS_TOKEN_PLAYBACK_STATUS
:
1129 int status
= audio_status();
1131 if (status
== AUDIO_STATUS_PLAY
)
1133 if (wps_fading_out
||
1134 (status
& AUDIO_STATUS_PAUSE
&& !status_get_ffmode()))
1136 if (status_get_ffmode() == STATUS_FASTFORWARD
)
1138 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
1145 snprintf(buf
, buf_size
, "%d", mode
-1);
1149 case WPS_TOKEN_REPEAT_MODE
:
1151 *intval
= global_settings
.repeat_mode
+ 1;
1152 snprintf(buf
, buf_size
, "%d", *intval
);
1154 case WPS_TOKEN_RTC_12HOUR_CFG
:
1156 *intval
= global_settings
.timeformat
+ 1;
1157 snprintf(buf
, buf_size
, "%d", *intval
);
1160 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1161 /* d: day of month (01..31) */
1162 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
1165 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1166 /* e: day of month, blank padded ( 1..31) */
1167 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
1170 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1171 /* H: hour (00..23) */
1172 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
1175 case WPS_TOKEN_RTC_HOUR_24
:
1176 /* k: hour ( 0..23) */
1177 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
1180 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1181 /* I: hour (01..12) */
1182 snprintf(buf
, buf_size
, "%02d",
1183 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1186 case WPS_TOKEN_RTC_HOUR_12
:
1187 /* l: hour ( 1..12) */
1188 snprintf(buf
, buf_size
, "%2d",
1189 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1192 case WPS_TOKEN_RTC_MONTH
:
1193 /* m: month (01..12) */
1195 *intval
= tm
->tm_mon
+ 1;
1196 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
1199 case WPS_TOKEN_RTC_MINUTE
:
1200 /* M: minute (00..59) */
1201 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
1204 case WPS_TOKEN_RTC_SECOND
:
1205 /* S: second (00..59) */
1206 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
1209 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1210 /* y: last two digits of year (00..99) */
1211 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
1214 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1215 /* Y: year (1970...) */
1216 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
1219 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1220 /* p: upper case AM or PM indicator */
1221 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "AM" : "PM");
1224 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1225 /* P: lower case am or pm indicator */
1226 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "am" : "pm");
1229 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1230 /* a: abbreviated weekday name (Sun..Sat) */
1231 snprintf(buf
, buf_size
, "%s",str(LANG_WEEKDAY_SUNDAY
+ tm
->tm_wday
));
1234 case WPS_TOKEN_RTC_MONTH_NAME
:
1235 /* b: abbreviated month name (Jan..Dec) */
1236 snprintf(buf
, buf_size
, "%s",str(LANG_MONTH_JANUARY
+ tm
->tm_mon
));
1239 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1240 /* u: day of week (1..7); 1 is Monday */
1242 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
1243 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
1246 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1247 /* w: day of week (0..6); 0 is Sunday */
1249 *intval
= tm
->tm_wday
+ 1;
1250 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
1253 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1254 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1255 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1256 case WPS_TOKEN_RTC_HOUR_24
:
1257 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1258 case WPS_TOKEN_RTC_HOUR_12
:
1259 case WPS_TOKEN_RTC_MONTH
:
1260 case WPS_TOKEN_RTC_MINUTE
:
1261 case WPS_TOKEN_RTC_SECOND
:
1262 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1263 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1264 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1265 strncpy(buf
, "--", buf_size
);
1267 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1268 strncpy(buf
, "----", buf_size
);
1270 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1271 case WPS_TOKEN_RTC_MONTH_NAME
:
1272 strncpy(buf
, "---", buf_size
);
1274 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1275 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1276 strncpy(buf
, "-", buf_size
);
1280 #ifdef HAVE_LCD_CHARCELLS
1281 case WPS_TOKEN_PROGRESSBAR
:
1283 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
1288 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
1291 /* we need 11 characters (full line) for
1293 snprintf(buf
, buf_size
, " ");
1297 /* Tell the user if we have an OldPlayer */
1298 snprintf(buf
, buf_size
, " <Old LCD> ");
1303 #ifdef HAVE_TAGCACHE
1304 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
1306 *intval
= id3
->playcount
+ 1;
1308 snprintf(buf
, buf_size
, "%ld", id3
->playcount
);
1311 case WPS_TOKEN_DATABASE_RATING
:
1313 *intval
= id3
->rating
+ 1;
1315 snprintf(buf
, buf_size
, "%d", id3
->rating
);
1318 case WPS_TOKEN_DATABASE_AUTOSCORE
:
1320 *intval
= id3
->score
+ 1;
1322 snprintf(buf
, buf_size
, "%d", id3
->score
);
1326 #if (CONFIG_CODEC == SWCODEC)
1327 case WPS_TOKEN_CROSSFADE
:
1329 *intval
= global_settings
.crossfade
+ 1;
1330 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
1333 case WPS_TOKEN_REPLAYGAIN
:
1337 if (global_settings
.replaygain
== 0)
1342 get_replaygain_mode(id3
->track_gain_string
!= NULL
,
1343 id3
->album_gain_string
!= NULL
);
1345 val
= 6; /* no tag */
1349 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
1364 strncpy(buf
, id3
->track_gain_string
, buf_size
);
1368 strncpy(buf
, id3
->album_gain_string
, buf_size
);
1373 #endif /* (CONFIG_CODEC == SWCODEC) */
1375 #if (CONFIG_CODEC != MAS3507D)
1376 case WPS_TOKEN_SOUND_PITCH
:
1378 int val
= sound_get_pitch();
1379 snprintf(buf
, buf_size
, "%d.%d",
1380 val
/ 10, val
% 10);
1385 case WPS_TOKEN_MAIN_HOLD
:
1386 #ifdef HAS_BUTTON_HOLD
1389 if (is_keys_locked())
1390 #endif /*hold switch or softlock*/
1395 #ifdef HAS_REMOTE_BUTTON_HOLD
1396 case WPS_TOKEN_REMOTE_HOLD
:
1397 if (remote_button_hold())
1403 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
1404 case WPS_TOKEN_VLED_HDD
:
1415 /* Return the index to the end token for the conditional token at index.
1416 The conditional token can be either a start token or a separator
1417 (i.e. option) token.
1419 static int find_conditional_end(struct wps_data
*data
, int index
)
1422 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
1423 ret
= data
->tokens
[ret
].value
.i
;
1425 /* ret now is the index to the end token for the conditional. */
1429 /* Return the index of the appropriate case for the conditional
1430 that starts at cond_index.
1432 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
1437 struct wps_data
*data
= gwps
->data
;
1440 int cond_index
= *token_index
;
1441 char result
[128], *value
;
1442 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
1443 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
1445 /* treat ?xx<true> constructs as if they had 2 options. */
1446 if (num_options
< 2)
1449 int intval
= num_options
;
1450 /* get_token_value needs to know the number of options in the enum */
1451 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
1452 result
, sizeof(result
), &intval
);
1454 /* intval is now the number of the enum option we want to read,
1455 starting from 1. If intval is -1, we check if value is empty. */
1457 intval
= (value
&& *value
) ? 1 : num_options
;
1458 else if (intval
> num_options
|| intval
< 1)
1459 intval
= num_options
;
1461 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
1463 /* skip to the right enum case */
1464 int next
= cond_index
+ 2;
1465 for (i
= 1; i
< intval
; i
++)
1467 next
= data
->tokens
[next
].value
.i
;
1469 *token_index
= next
;
1471 if (prev_val
== intval
)
1473 /* Same conditional case as previously. Return without clearing the
1478 cond_end
= find_conditional_end(data
, cond_index
+ 2);
1479 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
1481 #ifdef HAVE_LCD_BITMAP
1482 /* clear all pictures in the conditional and nested ones */
1483 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
1484 clear_image_pos(gwps
, data
->tokens
[i
].value
.i
& 0xFF);
1486 #ifdef HAVE_ALBUMART
1487 if (data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
1488 draw_album_art(gwps
, audio_current_aa_hid(), true);
1495 /* Read a (sub)line to the given alignment format buffer.
1496 linebuf is the buffer where the data is actually stored.
1497 align is the alignment format that'll be used to display the text.
1498 The return value indicates whether the line needs to be updated.
1500 static bool get_line(struct gui_wps
*gwps
,
1501 int line
, int subline
,
1502 struct align_pos
*align
,
1506 struct wps_data
*data
= gwps
->data
;
1509 char *buf
= linebuf
; /* will always point to the writing position */
1510 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
1511 int i
, last_token_idx
;
1512 bool update
= false;
1514 /* alignment-related variables */
1516 char* cur_align_start
;
1517 cur_align_start
= buf
;
1518 cur_align
= WPS_ALIGN_LEFT
;
1520 align
->center
= NULL
;
1521 align
->right
= NULL
;
1523 /* Process all tokens of the desired subline */
1524 last_token_idx
= wps_last_token_index(data
, line
, subline
);
1525 for (i
= wps_first_token_index(data
, line
, subline
);
1526 i
<= last_token_idx
; i
++)
1528 switch(data
->tokens
[i
].type
)
1530 case WPS_TOKEN_CONDITIONAL
:
1531 /* place ourselves in the right conditional case */
1532 update
|= evaluate_conditional(gwps
, &i
);
1535 case WPS_TOKEN_CONDITIONAL_OPTION
:
1536 /* we've finished in the curent conditional case,
1537 skip to the end of the conditional structure */
1538 i
= find_conditional_end(data
, i
);
1541 #ifdef HAVE_LCD_BITMAP
1542 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1544 struct gui_img
*img
= data
->img
;
1545 int n
= data
->tokens
[i
].value
.i
& 0xFF;
1546 int subimage
= data
->tokens
[i
].value
.i
>> 8;
1548 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
)
1549 img
[n
].display
= subimage
;
1554 case WPS_TOKEN_ALIGN_LEFT
:
1555 case WPS_TOKEN_ALIGN_CENTER
:
1556 case WPS_TOKEN_ALIGN_RIGHT
:
1557 /* remember where the current aligned text started */
1560 case WPS_ALIGN_LEFT
:
1561 align
->left
= cur_align_start
;
1564 case WPS_ALIGN_CENTER
:
1565 align
->center
= cur_align_start
;
1568 case WPS_ALIGN_RIGHT
:
1569 align
->right
= cur_align_start
;
1572 /* start a new alignment */
1573 switch (data
->tokens
[i
].type
)
1575 case WPS_TOKEN_ALIGN_LEFT
:
1576 cur_align
= WPS_ALIGN_LEFT
;
1578 case WPS_TOKEN_ALIGN_CENTER
:
1579 cur_align
= WPS_ALIGN_CENTER
;
1581 case WPS_TOKEN_ALIGN_RIGHT
:
1582 cur_align
= WPS_ALIGN_RIGHT
;
1588 cur_align_start
= buf
;
1590 case WPS_VIEWPORT_ENABLE
:
1592 char label
= data
->tokens
[i
].value
.i
;
1594 char temp
= VP_DRAW_HIDEABLE
;
1595 for(j
=0;j
<data
->num_viewports
;j
++)
1597 temp
= VP_DRAW_HIDEABLE
;
1598 if ((data
->viewports
[j
].hidden_flags
&VP_DRAW_HIDEABLE
) &&
1599 (data
->viewports
[j
].label
== label
))
1601 if (data
->viewports
[j
].hidden_flags
&VP_DRAW_WASHIDDEN
)
1602 temp
|= VP_DRAW_WASHIDDEN
;
1603 data
->viewports
[j
].hidden_flags
= temp
;
1610 /* get the value of the tag and copy it to the buffer */
1611 char *value
= get_token_value(gwps
, &data
->tokens
[i
],
1612 temp_buf
, sizeof(temp_buf
), NULL
);
1616 while (*value
&& (buf
< linebuf_end
))
1624 /* close the current alignment */
1627 case WPS_ALIGN_LEFT
:
1628 align
->left
= cur_align_start
;
1631 case WPS_ALIGN_CENTER
:
1632 align
->center
= cur_align_start
;
1635 case WPS_ALIGN_RIGHT
:
1636 align
->right
= cur_align_start
;
1643 static void get_subline_timeout(struct gui_wps
*gwps
, int line
, int subline
)
1645 struct wps_data
*data
= gwps
->data
;
1647 int subline_idx
= wps_subline_index(data
, line
, subline
);
1648 int last_token_idx
= wps_last_token_index(data
, line
, subline
);
1650 data
->sublines
[subline_idx
].time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1652 for (i
= wps_first_token_index(data
, line
, subline
);
1653 i
<= last_token_idx
; i
++)
1655 switch(data
->tokens
[i
].type
)
1657 case WPS_TOKEN_CONDITIONAL
:
1658 /* place ourselves in the right conditional case */
1659 evaluate_conditional(gwps
, &i
);
1662 case WPS_TOKEN_CONDITIONAL_OPTION
:
1663 /* we've finished in the curent conditional case,
1664 skip to the end of the conditional structure */
1665 i
= find_conditional_end(data
, i
);
1668 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1669 data
->sublines
[subline_idx
].time_mult
= data
->tokens
[i
].value
.i
;
1678 /* Calculates which subline should be displayed for the specified line
1679 Returns true iff the subline must be refreshed */
1680 static bool update_curr_subline(struct gui_wps
*gwps
, int line
)
1682 struct wps_data
*data
= gwps
->data
;
1684 int search
, search_start
, num_sublines
;
1686 bool new_subline_refresh
;
1687 bool only_one_subline
;
1689 num_sublines
= data
->lines
[line
].num_sublines
;
1690 reset_subline
= (data
->lines
[line
].curr_subline
== SUBLINE_RESET
);
1691 new_subline_refresh
= false;
1692 only_one_subline
= false;
1694 /* if time to advance to next sub-line */
1695 if (TIME_AFTER(current_tick
, data
->lines
[line
].subline_expire_time
- 1) ||
1698 /* search all sublines until the next subline with time > 0
1699 is found or we get back to the subline we started with */
1703 search_start
= data
->lines
[line
].curr_subline
;
1705 for (search
= 0; search
< num_sublines
; search
++)
1707 data
->lines
[line
].curr_subline
++;
1709 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1710 if (data
->lines
[line
].curr_subline
== num_sublines
)
1712 if (data
->lines
[line
].curr_subline
== 1)
1713 only_one_subline
= true;
1714 data
->lines
[line
].curr_subline
= 0;
1717 /* if back where we started after search or
1718 only one subline is defined on the line */
1719 if (((search
> 0) &&
1720 (data
->lines
[line
].curr_subline
== search_start
)) ||
1723 /* no other subline with a time > 0 exists */
1724 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1726 data
->lines
[line
].subline_expire_time
) + 100 * HZ
;
1731 /* get initial time multiplier for this subline */
1732 get_subline_timeout(gwps
, line
, data
->lines
[line
].curr_subline
);
1734 int subline_idx
= wps_subline_index(data
, line
,
1735 data
->lines
[line
].curr_subline
);
1737 /* only use this subline if subline time > 0 */
1738 if (data
->sublines
[subline_idx
].time_mult
> 0)
1740 new_subline_refresh
= true;
1741 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1742 current_tick
: data
->lines
[line
].subline_expire_time
) +
1743 BASE_SUBLINE_TIME
*data
->sublines
[subline_idx
].time_mult
;
1750 return new_subline_refresh
;
1753 /* Display a line appropriately according to its alignment format.
1754 format_align contains the text, separated between left, center and right.
1755 line is the index of the line on the screen.
1756 scroll indicates whether the line is a scrolling one or not.
1758 static void write_line(struct screen
*display
,
1759 struct align_pos
*format_align
,
1764 int left_width
= 0, left_xpos
;
1765 int center_width
= 0, center_xpos
;
1766 int right_width
= 0, right_xpos
;
1772 /* calculate different string sizes and positions */
1773 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
1774 if (format_align
->left
!= 0) {
1775 display
->getstringsize((unsigned char *)format_align
->left
,
1776 &left_width
, &string_height
);
1779 if (format_align
->right
!= 0) {
1780 display
->getstringsize((unsigned char *)format_align
->right
,
1781 &right_width
, &string_height
);
1784 if (format_align
->center
!= 0) {
1785 display
->getstringsize((unsigned char *)format_align
->center
,
1786 ¢er_width
, &string_height
);
1790 right_xpos
= (display
->getwidth() - right_width
);
1791 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
1793 scroll_width
= display
->getwidth() - left_xpos
;
1795 /* Checks for overlapping strings.
1796 If needed the overlapping strings will be merged, separated by a
1799 /* CASE 1: left and centered string overlap */
1800 /* there is a left string, need to merge left and center */
1801 if ((left_width
!= 0 && center_width
!= 0) &&
1802 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1803 /* replace the former separator '\0' of left and
1804 center string with a space */
1805 *(--format_align
->center
) = ' ';
1806 /* calculate the new width and position of the merged string */
1807 left_width
= left_width
+ space_width
+ center_width
;
1808 /* there is no centered string anymore */
1811 /* there is no left string, move center to left */
1812 if ((left_width
== 0 && center_width
!= 0) &&
1813 (left_xpos
+ left_width
> center_xpos
)) {
1814 /* move the center string to the left string */
1815 format_align
->left
= format_align
->center
;
1816 /* calculate the new width and position of the string */
1817 left_width
= center_width
;
1818 /* there is no centered string anymore */
1822 /* CASE 2: centered and right string overlap */
1823 /* there is a right string, need to merge center and right */
1824 if ((center_width
!= 0 && right_width
!= 0) &&
1825 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1826 /* replace the former separator '\0' of center and
1827 right string with a space */
1828 *(--format_align
->right
) = ' ';
1829 /* move the center string to the right after merge */
1830 format_align
->right
= format_align
->center
;
1831 /* calculate the new width and position of the merged string */
1832 right_width
= center_width
+ space_width
+ right_width
;
1833 right_xpos
= (display
->getwidth() - right_width
);
1834 /* there is no centered string anymore */
1837 /* there is no right string, move center to right */
1838 if ((center_width
!= 0 && right_width
== 0) &&
1839 (center_xpos
+ center_width
> right_xpos
)) {
1840 /* move the center string to the right string */
1841 format_align
->right
= format_align
->center
;
1842 /* calculate the new width and position of the string */
1843 right_width
= center_width
;
1844 right_xpos
= (display
->getwidth() - right_width
);
1845 /* there is no centered string anymore */
1849 /* CASE 3: left and right overlap
1850 There is no center string anymore, either there never
1851 was one or it has been merged in case 1 or 2 */
1852 /* there is a left string, need to merge left and right */
1853 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1854 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1855 /* replace the former separator '\0' of left and
1856 right string with a space */
1857 *(--format_align
->right
) = ' ';
1858 /* calculate the new width and position of the string */
1859 left_width
= left_width
+ space_width
+ right_width
;
1860 /* there is no right string anymore */
1863 /* there is no left string, move right to left */
1864 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1865 (left_width
> right_xpos
)) {
1866 /* move the right string to the left string */
1867 format_align
->left
= format_align
->right
;
1868 /* calculate the new width and position of the string */
1869 left_width
= right_width
;
1870 /* there is no right string anymore */
1874 ypos
= (line
* string_height
);
1877 if (scroll
&& ((left_width
> scroll_width
) ||
1878 (center_width
> scroll_width
) ||
1879 (right_width
> scroll_width
)))
1881 display
->puts_scroll(0, line
,
1882 (unsigned char *)format_align
->left
);
1886 #ifdef HAVE_LCD_BITMAP
1887 /* clear the line first */
1888 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1889 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1890 display
->set_drawmode(DRMODE_SOLID
);
1893 /* Nasty hack: we output an empty scrolling string,
1894 which will reset the scroller for that line */
1895 display
->puts_scroll(0, line
, (unsigned char *)"");
1897 /* print aligned strings */
1898 if (left_width
!= 0)
1900 display
->putsxy(left_xpos
, ypos
,
1901 (unsigned char *)format_align
->left
);
1903 if (center_width
!= 0)
1905 display
->putsxy(center_xpos
, ypos
,
1906 (unsigned char *)format_align
->center
);
1908 if (right_width
!= 0)
1910 display
->putsxy(right_xpos
, ypos
,
1911 (unsigned char *)format_align
->right
);
1916 /* Refresh the WPS according to refresh_mode. */
1917 bool gui_wps_refresh(struct gui_wps
*gwps
,
1919 unsigned char refresh_mode
)
1921 struct wps_data
*data
= gwps
->data
;
1922 struct screen
*display
= gwps
->display
;
1923 struct wps_state
*state
= gwps
->state
;
1925 if(!gwps
|| !data
|| !state
|| !display
)
1928 int v
, line
, i
, subline_idx
;
1929 unsigned char flags
;
1930 char linebuf
[MAX_PATH
];
1931 unsigned char vp_refresh_mode
;
1933 struct align_pos align
;
1935 align
.center
= NULL
;
1938 bool update_line
, new_subline_refresh
;
1940 #ifdef HAVE_LCD_BITMAP
1941 gui_wps_statusbar_draw(gwps
, true);
1943 /* to find out wether the peak meter is enabled we
1944 assume it wasn't until we find a line that contains
1945 the peak meter. We can't use peak_meter_enabled itself
1946 because that would mean to turn off the meter thread
1947 temporarily. (That shouldn't matter unless yield
1948 or sleep is called but who knows...)
1950 bool enable_pm
= false;
1954 /* reset to first subline if refresh all flag is set */
1955 if (refresh_mode
== WPS_REFRESH_ALL
)
1957 display
->set_viewport(&data
->viewports
[0].vp
);
1958 display
->clear_viewport();
1960 for (i
= 0; i
<= data
->num_lines
; i
++)
1962 data
->lines
[i
].curr_subline
= SUBLINE_RESET
;
1966 #ifdef HAVE_LCD_CHARCELLS
1967 for (i
= 0; i
< 8; i
++)
1969 if (data
->wps_progress_pat
[i
] == 0)
1970 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1976 display
->stop_scroll();
1980 state
->ff_rewind_count
= ffwd_offset
;
1982 /* disable any viewports which are conditionally displayed */
1983 for (v
= 0; v
< data
->num_viewports
; v
++)
1985 if (data
->viewports
[v
].hidden_flags
&VP_DRAW_HIDEABLE
)
1987 if (data
->viewports
[v
].hidden_flags
&VP_DRAW_HIDDEN
)
1988 data
->viewports
[v
].hidden_flags
|= VP_DRAW_WASHIDDEN
;
1990 data
->viewports
[v
].hidden_flags
|= VP_DRAW_HIDDEN
;
1993 for (v
= 0; v
< data
->num_viewports
; v
++)
1995 display
->set_viewport(&data
->viewports
[v
].vp
);
1996 vp_refresh_mode
= refresh_mode
;
1998 #ifdef HAVE_LCD_BITMAP
1999 /* Set images to not to be displayed */
2000 for (i
= 0; i
< MAX_IMAGES
; i
++)
2002 data
->img
[i
].display
= -1;
2005 /* dont redraw the viewport if its disabled */
2006 if ((data
->viewports
[v
].hidden_flags
&VP_DRAW_HIDDEN
))
2008 if (!(data
->viewports
[v
].hidden_flags
&VP_DRAW_WASHIDDEN
))
2009 display
->scroll_stop(&data
->viewports
[v
].vp
);
2010 data
->viewports
[v
].hidden_flags
|= VP_DRAW_WASHIDDEN
;
2013 else if (((data
->viewports
[v
].hidden_flags
&
2014 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
2015 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
2017 vp_refresh_mode
= WPS_REFRESH_ALL
;
2018 data
->viewports
[v
].hidden_flags
= VP_DRAW_HIDEABLE
;
2020 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
2022 display
->clear_viewport();
2025 for (line
= data
->viewports
[v
].first_line
;
2026 line
<= data
->viewports
[v
].last_line
; line
++)
2028 memset(linebuf
, 0, sizeof(linebuf
));
2029 update_line
= false;
2031 /* get current subline for the line */
2032 new_subline_refresh
= update_curr_subline(gwps
, line
);
2034 subline_idx
= wps_subline_index(data
, line
,
2035 data
->lines
[line
].curr_subline
);
2036 flags
= data
->sublines
[subline_idx
].line_type
;
2038 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
2039 || new_subline_refresh
)
2041 /* get_line tells us if we need to update the line */
2042 update_line
= get_line(gwps
, line
, data
->lines
[line
].curr_subline
,
2043 &align
, linebuf
, sizeof(linebuf
));
2045 #ifdef HAVE_LCD_BITMAP
2047 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
2049 /* the peakmeter should be alone on its line */
2050 update_line
= false;
2052 int h
= font_get(data
->viewports
[v
].vp
.font
)->height
;
2053 int peak_meter_y
= (line
- data
->viewports
[v
].first_line
)* h
;
2055 /* The user might decide to have the peak meter in the last
2056 line so that it is only displayed if no status bar is
2057 visible. If so we neither want do draw nor enable the
2059 if (peak_meter_y
+ h
<= display
->getheight()) {
2060 /* found a line with a peak meter -> remember that we must
2063 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
2064 MIN(h
, display
->getheight() - peak_meter_y
));
2068 #else /* HAVE_LCD_CHARCELL */
2071 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
2073 if (data
->full_line_progressbar
)
2074 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
2076 draw_player_progress(gwps
);
2081 /* conditionals clear the line which means if the %Vd is put into the default
2082 viewport there will be a blank line.
2083 To get around this we dont allow any actual drawing to happen in the
2084 deault vp if other vp's are defined */
2085 ((data
->num_viewports
>1 && v
!=0) || data
->num_viewports
== 1))
2087 if (flags
& WPS_REFRESH_SCROLL
)
2089 /* if the line is a scrolling one we don't want to update
2090 too often, so that it has the time to scroll */
2091 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
2092 write_line(display
, &align
, line
- data
->viewports
[v
].first_line
, true);
2095 write_line(display
, &align
, line
- data
->viewports
[v
].first_line
, false);
2099 #ifdef HAVE_LCD_BITMAP
2101 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
2103 if (data
->viewports
[v
].pb
)
2104 draw_progressbar(gwps
, data
->viewports
[v
].pb
);
2106 /* Now display any images in this viewport */
2107 wps_display_images(gwps
, &data
->viewports
[v
].vp
);
2111 #ifdef HAVE_LCD_BITMAP
2112 data
->peak_meter_enabled
= enable_pm
;
2115 /* Restore the default viewport */
2116 display
->set_viewport(NULL
);
2120 #ifdef HAVE_BACKLIGHT
2121 if (global_settings
.caption_backlight
&& state
->id3
)
2123 /* turn on backlight n seconds before track ends, and turn it off n
2124 seconds into the new track. n == backlight_timeout, or 5s */
2125 int n
= global_settings
.backlight_timeout
* 1000;
2128 n
= 5000; /* use 5s if backlight is always on or off */
2130 if (((state
->id3
->elapsed
< 1000) ||
2131 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2132 (state
->paused
== false))
2136 #ifdef HAVE_REMOTE_LCD
2137 if (global_settings
.remote_caption_backlight
&& state
->id3
)
2139 /* turn on remote backlight n seconds before track ends, and turn it
2140 off n seconds into the new track. n == remote_backlight_timeout,
2142 int n
= global_settings
.remote_backlight_timeout
* 1000;
2145 n
= 5000; /* use 5s if backlight is always on or off */
2147 if (((state
->id3
->elapsed
< 1000) ||
2148 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2149 (state
->paused
== false))
2150 remote_backlight_on();