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
)
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);
107 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_NON_STATIC
);
110 sound_set_volume(global_settings
.volume
);
114 int fp_volume
= fp_global_vol
;
116 while (fp_volume
> fp_min_vol
+ fp_step
) {
117 fp_volume
-= fp_step
;
118 sound_set_volume(fp_volume
>> 8);
120 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_NON_STATIC
);
124 wps_fading_out
= false;
125 #if CONFIG_CODEC != SWCODEC
127 /* let audio thread run and wait for the mas to run out of data */
128 while (!mp3_pause_done())
133 /* reset volume to what it was before the fade */
134 sound_set_volume(global_settings
.volume
);
138 /* return true if screen restore is needed
139 return false otherwise
141 bool update_onvol_change(struct gui_wps
* gwps
)
143 gui_wps_statusbar_draw(gwps
, false);
144 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
146 #ifdef HAVE_LCD_CHARCELLS
147 gui_splash(gwps
->display
, 0, "Vol: %3d dB",
148 sound_val2phys(SOUND_VOLUME
, global_settings
.volume
));
154 void play_hop(int direction
)
156 if(!wps_state
.id3
|| !wps_state
.id3
->length
157 || global_settings
.study_hop_step
== 0)
159 #define STEP ((unsigned)global_settings.study_hop_step *1000)
161 && wps_state
.id3
->length
- wps_state
.id3
->elapsed
< STEP
+1000) {
162 #if CONFIG_CODEC == SWCODEC
163 if(global_settings
.beep
)
164 pcmbuf_beep(1000, 150, 1500*global_settings
.beep
);
168 if((direction
== -1 && wps_state
.id3
->elapsed
< STEP
))
169 wps_state
.id3
->elapsed
= 0;
171 wps_state
.id3
->elapsed
+= STEP
*direction
;
172 if((audio_status() & AUDIO_STATUS_PLAY
) && !wps_state
.paused
) {
173 #if (CONFIG_CODEC == SWCODEC)
174 audio_pre_ff_rewind();
179 audio_ff_rewind(wps_state
.id3
->elapsed
);
180 #if (CONFIG_CODEC != SWCODEC)
181 if (!wps_state
.paused
)
187 bool ffwd_rew(int button
)
189 unsigned int step
= 0; /* current ff/rewind step */
190 unsigned int max_step
= 0; /* maximum ff/rewind step */
191 int ff_rewind_count
= 0; /* current ff/rewind count (in ticks) */
192 int direction
= -1; /* forward=1 or backward=-1 */
193 long accel_tick
= 0; /* next time at which to bump the step size */
198 if (button
== ACTION_NONE
)
200 status_set_ffmode(0);
207 case ACTION_WPS_SEEKFWD
:
209 case ACTION_WPS_SEEKBACK
:
210 if (wps_state
.ff_rewind
)
214 /* fast forwarding, calc max step relative to end */
215 max_step
= (wps_state
.id3
->length
-
216 (wps_state
.id3
->elapsed
+
218 FF_REWIND_MAX_PERCENT
/ 100;
222 /* rewinding, calc max step relative to start */
223 max_step
= (wps_state
.id3
->elapsed
+ ff_rewind_count
) *
224 FF_REWIND_MAX_PERCENT
/ 100;
227 max_step
= MAX(max_step
, MIN_FF_REWIND_STEP
);
232 ff_rewind_count
+= step
* direction
;
234 if (global_settings
.ff_rewind_accel
!= 0 &&
235 current_tick
>= accel_tick
)
238 accel_tick
= current_tick
+
239 global_settings
.ff_rewind_accel
*HZ
;
244 if ( (audio_status() & AUDIO_STATUS_PLAY
) &&
245 wps_state
.id3
&& wps_state
.id3
->length
)
247 if (!wps_state
.paused
)
248 #if (CONFIG_CODEC == SWCODEC)
249 audio_pre_ff_rewind();
253 #if CONFIG_KEYPAD == PLAYER_PAD
255 gui_wps
[i
].display
->stop_scroll();
258 status_set_ffmode(STATUS_FASTFORWARD
);
260 status_set_ffmode(STATUS_FASTBACKWARD
);
262 wps_state
.ff_rewind
= true;
264 step
= 1000 * global_settings
.ff_rewind_min_step
;
266 accel_tick
= current_tick
+
267 global_settings
.ff_rewind_accel
*HZ
;
274 if ((wps_state
.id3
->elapsed
+ ff_rewind_count
) >
275 wps_state
.id3
->length
)
276 ff_rewind_count
= wps_state
.id3
->length
-
277 wps_state
.id3
->elapsed
;
280 if ((int)(wps_state
.id3
->elapsed
+ ff_rewind_count
) < 0)
281 ff_rewind_count
= -wps_state
.id3
->elapsed
;
285 gui_wps_refresh(&gui_wps
[i
],
286 (wps_state
.wps_time_countup
== false)?
287 ff_rewind_count
:-ff_rewind_count
,
288 WPS_REFRESH_PLAYER_PROGRESS
|
289 WPS_REFRESH_DYNAMIC
);
293 case ACTION_WPS_STOPSEEK
:
294 wps_state
.id3
->elapsed
= wps_state
.id3
->elapsed
+ff_rewind_count
;
295 audio_ff_rewind(wps_state
.id3
->elapsed
);
297 wps_state
.ff_rewind
= false;
298 status_set_ffmode(0);
299 #if (CONFIG_CODEC != SWCODEC)
300 if (!wps_state
.paused
)
303 #ifdef HAVE_LCD_CHARCELLS
310 if(default_event_handler(button
) == SYS_USB_CONNECTED
) {
311 status_set_ffmode(0);
318 button
= get_action(CONTEXT_WPS
|ALLOW_SOFTLOCK
,TIMEOUT_BLOCK
);
323 bool gui_wps_display(void)
326 if (!wps_state
.id3
&& !(audio_status() & AUDIO_STATUS_PLAY
))
328 global_status
.resume_index
= -1;
329 #ifdef HAVE_LCD_BITMAP
330 gui_syncstatusbar_draw(&statusbars
, true);
332 gui_syncsplash(HZ
, ID2P(LANG_END_PLAYLIST
));
339 /* Update the values in the first (default) viewport - in case the user
340 has modified the statusbar or colour settings */
341 #ifdef HAVE_LCD_BITMAP
342 gui_wps
[i
].data
->viewports
[0].vp
.ymargin
= gui_wps
[i
].display
->getymargin();
344 if (gui_wps
[i
].display
->depth
> 1)
346 gui_wps
[i
].data
->viewports
[0].vp
.fg_pattern
= gui_wps
[i
].display
->get_foreground();
347 gui_wps
[i
].data
->viewports
[0].vp
.bg_pattern
= gui_wps
[i
].display
->get_background();
352 gui_wps
[i
].display
->clear_display();
353 if (!gui_wps
[i
].data
->wps_loaded
) {
354 if ( !gui_wps
[i
].data
->num_tokens
) {
355 /* set the default wps for the main-screen */
358 #ifdef HAVE_LCD_BITMAP
360 unload_wps_backdrop();
362 wps_data_load(gui_wps
[i
].data
,
364 "%s%?it<%?in<%in. |>%it|%fn>\n"
365 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
366 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
368 "%al%pc/%pt%ar[%pp:%pe]\n"
369 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
373 wps_data_load(gui_wps
[i
].data
,
375 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
376 "%pc%?ps<*|/>%pt\n", false);
380 /* set the default wps for the remote-screen */
383 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
384 unload_remote_wps_backdrop();
386 wps_data_load(gui_wps
[i
].data
,
388 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
389 "%s%?it<%?in<%in. |>%it|%fn>\n"
390 "%al%pc/%pt%ar[%pp:%pe]\n"
391 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
402 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_ALL
);
407 bool update(struct gui_wps
*gwps
)
409 bool track_changed
= audio_has_changed_track();
410 bool retcode
= false;
412 gwps
->state
->nid3
= audio_next_track();
415 gwps
->display
->stop_scroll();
416 gwps
->state
->id3
= audio_current_track();
418 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
419 && strcmp(gwps
->state
->id3
->path
, curr_cue
->audio_filename
))
421 /* the current cuesheet isn't the right one any more */
423 if (!strcmp(gwps
->state
->id3
->path
, temp_cue
->audio_filename
)) {
424 /* We have the new cuesheet in memory (temp_cue),
425 let's make it the current one ! */
426 memcpy(curr_cue
, temp_cue
, sizeof(struct cuesheet
));
429 /* We need to parse the new cuesheet */
431 char cuepath
[MAX_PATH
];
433 if (look_for_cuesheet_file(gwps
->state
->id3
->path
, cuepath
) &&
434 parse_cuesheet(cuepath
, curr_cue
))
436 gwps
->state
->id3
->cuesheet_type
= 1;
437 strcpy(curr_cue
->audio_filename
, gwps
->state
->id3
->path
);
441 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
444 if (gui_wps_display())
447 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
450 if (gwps
->state
->id3
)
452 strncpy(gwps
->state
->current_track_path
, gwps
->state
->id3
->path
,
453 sizeof(gwps
->state
->current_track_path
));
454 gwps
->state
->current_track_path
[sizeof(gwps
->state
->current_track_path
)-1] = '\0';
458 if (gwps
->state
->id3
)
460 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
461 && (gwps
->state
->id3
->elapsed
< curr_cue
->curr_track
->offset
462 || (curr_cue
->curr_track_idx
< curr_cue
->track_count
- 1
463 && gwps
->state
->id3
->elapsed
>= (curr_cue
->curr_track
+1)->offset
)))
465 /* We've changed tracks within the cuesheet :
466 we need to update the ID3 info and refresh the WPS */
468 cue_find_current_track(curr_cue
, gwps
->state
->id3
->elapsed
);
469 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
471 gwps
->display
->stop_scroll();
472 if (gui_wps_display())
475 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
478 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
481 gui_wps_statusbar_draw(gwps
, false);
487 void display_keylock_text(bool locked
)
492 gui_wps
[i
].display
->stop_scroll();
495 s
= str(LANG_KEYLOCK_ON
);
497 s
= str(LANG_KEYLOCK_OFF
);
498 gui_syncsplash(HZ
, s
);
501 #ifdef HAVE_LCD_BITMAP
503 static void draw_progressbar(struct gui_wps
*gwps
, int line
)
505 struct wps_data
*data
= gwps
->data
;
506 struct screen
*display
= gwps
->display
;
507 struct wps_state
*state
= gwps
->state
;
508 int h
= font_get(display
->getfont())->height
;
511 if (data
->progress_top
< 0)
512 sb_y
= line
*h
+ display
->getymargin() +
513 ((h
> data
->progress_height
+ 1)
514 ? (h
- data
->progress_height
) / 2 : 1);
516 sb_y
= data
->progress_top
;
518 if (!data
->progress_end
)
519 data
->progress_end
=display
->getwidth();
521 if (gwps
->data
->progressbar
.have_bitmap_pb
)
522 gui_bitmap_scrollbar_draw(display
, data
->progressbar
.bm
,
523 data
->progress_start
, sb_y
,
524 data
->progress_end
-data
->progress_start
,
525 data
->progressbar
.bm
.height
,
526 state
->id3
->length
? state
->id3
->length
: 1, 0,
527 state
->id3
->length
? state
->id3
->elapsed
528 + state
->ff_rewind_count
: 0,
531 gui_scrollbar_draw(display
, data
->progress_start
, sb_y
,
532 data
->progress_end
-data
->progress_start
,
533 data
->progress_height
,
534 state
->id3
->length
? state
->id3
->length
: 1, 0,
535 state
->id3
->length
? state
->id3
->elapsed
536 + state
->ff_rewind_count
: 0,
539 #ifdef AB_REPEAT_ENABLE
540 if ( ab_repeat_mode_enabled() && state
->id3
->length
!= 0 )
541 ab_draw_markers(display
, state
->id3
->length
,
542 data
->progress_start
, data
->progress_end
, sb_y
,
543 data
->progress_height
);
546 if ( cuesheet_is_enabled() && state
->id3
->cuesheet_type
)
547 cue_draw_markers(display
, state
->id3
->length
,
548 data
->progress_start
, data
->progress_end
,
549 sb_y
+1, data
->progress_height
-2);
552 /* clears the area where the image was shown */
553 static void clear_image_pos(struct gui_wps
*gwps
, int n
)
557 struct wps_data
*data
= gwps
->data
;
558 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
559 gwps
->display
->fillrect(data
->img
[n
].x
, data
->img
[n
].y
,
560 data
->img
[n
].bm
.width
, data
->img
[n
].subimage_height
);
561 gwps
->display
->set_drawmode(DRMODE_SOLID
);
564 static void wps_draw_image(struct gui_wps
*gwps
, int n
, int subimage
)
566 struct screen
*display
= gwps
->display
;
567 struct wps_data
*data
= gwps
->data
;
568 if(data
->img
[n
].always_display
)
569 display
->set_drawmode(DRMODE_FG
);
571 display
->set_drawmode(DRMODE_SOLID
);
574 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
576 display
->mono_bitmap_part(data
->img
[n
].bm
.data
,
577 0, data
->img
[n
].subimage_height
* subimage
,
578 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
579 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
580 data
->img
[n
].subimage_height
);
583 display
->transparent_bitmap_part((fb_data
*)data
->img
[n
].bm
.data
,
584 0, data
->img
[n
].subimage_height
* subimage
,
585 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
586 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
587 data
->img
[n
].subimage_height
);
592 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
594 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
598 struct wps_data
*data
= gwps
->data
;
599 struct screen
*display
= gwps
->display
;
601 for (n
= 0; n
< MAX_IMAGES
; n
++)
603 if (data
->img
[n
].loaded
)
605 if (data
->img
[n
].display
>= 0)
607 wps_draw_image(gwps
, n
, data
->img
[n
].display
);
608 } else if (data
->img
[n
].always_display
&& data
->img
[n
].vp
== vp
)
610 wps_draw_image(gwps
, n
, 0);
614 display
->set_drawmode(DRMODE_SOLID
);
617 #else /* HAVE_LCD_CHARCELL */
619 static bool draw_player_progress(struct gui_wps
*gwps
)
621 struct wps_state
*state
= gwps
->state
;
622 struct screen
*display
= gwps
->display
;
623 unsigned char progress_pattern
[7];
630 if (state
->id3
->length
)
631 pos
= 36 * (state
->id3
->elapsed
+ state
->ff_rewind_count
)
632 / state
->id3
->length
;
634 for (i
= 0; i
< 7; i
++, pos
-= 5)
637 progress_pattern
[i
] = 0x1f;
639 progress_pattern
[i
] = 0x00;
641 progress_pattern
[i
] = 0x1f >> pos
;
644 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
648 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
650 static const unsigned char numbers
[10][4] = {
651 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
652 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
653 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
654 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
655 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
656 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
657 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
658 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
659 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
660 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
663 struct wps_state
*state
= gwps
->state
;
664 struct screen
*display
= gwps
->display
;
665 struct wps_data
*data
= gwps
->data
;
666 unsigned char progress_pattern
[7];
675 if (!state
->id3
|| buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
678 time
= state
->id3
->elapsed
+ state
->ff_rewind_count
;
679 if (state
->id3
->length
)
680 pos
= 55 * time
/ state
->id3
->length
;
682 memset(timestr
, 0, sizeof(timestr
));
683 format_time(timestr
, sizeof(timestr
)-2, time
);
684 timestr
[strlen(timestr
)] = ':'; /* always safe */
686 for (i
= 0; i
< 11; i
++, pos
-= 5)
689 memset(progress_pattern
, 0, sizeof(progress_pattern
));
691 if ((digit
= timestr
[time_idx
]))
696 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
698 memcpy(progress_pattern
, numbers
[digit
], 4);
701 else /* tens, shifted right */
703 for (j
= 0; j
< 4; j
++)
704 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
706 if (time_idx
> 0) /* not the first group, add colon in front */
708 progress_pattern
[1] |= 0x10;
709 progress_pattern
[3] |= 0x10;
715 progress_pattern
[5] = progress_pattern
[6] = 0x1f;
718 if (pos
> 0 && pos
< 5)
721 progress_pattern
[5] = progress_pattern
[6] = (~0x1f >> pos
) & 0x1f;
724 if (softchar
&& pat_idx
< 8)
726 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
728 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
732 buf
= utf8encode(' ', buf
);
734 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
739 #endif /* HAVE_LCD_CHARCELL */
741 static char* get_codectype(const struct mp3entry
* id3
)
743 if (id3
->codectype
< AFMT_NUM_CODECS
) {
744 return (char*)audio_formats
[id3
->codectype
].label
;
750 /* Extract a part from a path.
752 * buf - buffer extract part to.
753 * buf_size - size of buffer.
754 * path - path to extract from.
755 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
756 * parent of parent, etc.
758 * Returns buf if the desired level was found, NULL otherwise.
760 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
763 const char* last_sep
;
766 sep
= path
+ strlen(path
);
781 if (level
|| (last_sep
<= sep
))
784 len
= MIN(last_sep
- sep
, buf_size
- 1);
785 strncpy(buf
, sep
+ 1, len
);
790 /* Return the tag found at index i and write its value in buf.
791 The return value is buf if the tag had a value, or NULL if not.
793 intval is used with conditionals/enums: when this function is called,
794 intval should contain the number of options in the conditional/enum.
795 When this function returns, intval is -1 if the tag is non numeric or,
796 if the tag is numeric, intval is the enum case we want to go to.
797 When not treating a conditional/enum, intval should be NULL.
799 static char *get_token_value(struct gui_wps
*gwps
,
800 struct wps_token
*token
,
801 char *buf
, int buf_size
,
807 struct wps_data
*data
= gwps
->data
;
808 struct wps_state
*state
= gwps
->state
;
813 struct mp3entry
*id3
;
824 struct tm
* tm
= NULL
;
826 /* if the token is an RTC one, update the time
827 and do the necessary checks */
828 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
829 && token
->type
<= WPS_TOKENS_RTC_END
)
847 case WPS_TOKEN_CHARACTER
:
848 return &(token
->value
.c
);
850 case WPS_TOKEN_STRING
:
851 return data
->strings
[token
->value
.i
];
853 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
854 format_time(buf
, buf_size
,
855 id3
->elapsed
+ state
->ff_rewind_count
);
858 case WPS_TOKEN_TRACK_TIME_REMAINING
:
859 format_time(buf
, buf_size
,
860 id3
->length
- id3
->elapsed
-
861 state
->ff_rewind_count
);
864 case WPS_TOKEN_TRACK_LENGTH
:
865 format_time(buf
, buf_size
, id3
->length
);
868 case WPS_TOKEN_PLAYLIST_ENTRIES
:
869 snprintf(buf
, buf_size
, "%d", playlist_amount());
872 case WPS_TOKEN_PLAYLIST_NAME
:
873 return playlist_name(NULL
, buf
, buf_size
);
875 case WPS_TOKEN_PLAYLIST_POSITION
:
876 snprintf(buf
, buf_size
, "%d", playlist_get_display_index());
879 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
880 if ( global_settings
.playlist_shuffle
)
886 case WPS_TOKEN_VOLUME
:
887 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
890 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
894 else if (global_settings
.volume
== 0)
898 else if (global_settings
.volume
> 0)
904 *intval
= (limit
- 3) * (global_settings
.volume
905 - sound_min(SOUND_VOLUME
) - 1)
906 / (-1 - sound_min(SOUND_VOLUME
)) + 2;
911 case WPS_TOKEN_TRACK_ELAPSED_PERCENT
:
912 if (id3
->length
<= 0)
917 *intval
= limit
* (id3
->elapsed
+ state
->ff_rewind_count
)
920 snprintf(buf
, buf_size
, "%d",
921 100*(id3
->elapsed
+ state
->ff_rewind_count
) / id3
->length
);
924 case WPS_TOKEN_METADATA_ARTIST
:
927 case WPS_TOKEN_METADATA_COMPOSER
:
928 return id3
->composer
;
930 case WPS_TOKEN_METADATA_ALBUM
:
933 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
934 return id3
->albumartist
;
936 case WPS_TOKEN_METADATA_GROUPING
:
937 return id3
->grouping
;
939 case WPS_TOKEN_METADATA_GENRE
:
940 return id3
->genre_string
;
942 case WPS_TOKEN_METADATA_DISC_NUMBER
:
943 if (id3
->disc_string
)
944 return id3
->disc_string
;
946 snprintf(buf
, buf_size
, "%d", id3
->discnum
);
951 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
952 if (id3
->track_string
)
953 return id3
->track_string
;
956 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
961 case WPS_TOKEN_METADATA_TRACK_TITLE
:
964 case WPS_TOKEN_METADATA_VERSION
:
965 switch (id3
->id3version
)
986 case WPS_TOKEN_METADATA_YEAR
:
987 if( id3
->year_string
)
988 return id3
->year_string
;
991 snprintf(buf
, buf_size
, "%d", id3
->year
);
996 case WPS_TOKEN_METADATA_COMMENT
:
1000 case WPS_TOKEN_ALBUMART_DISPLAY
:
1001 draw_album_art(gwps
, audio_current_aa_hid(), false);
1004 case WPS_TOKEN_ALBUMART_FOUND
:
1005 if (audio_current_aa_hid() >= 0) {
1006 snprintf(buf
, buf_size
, "C");
1012 case WPS_TOKEN_FILE_BITRATE
:
1014 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
1016 snprintf(buf
, buf_size
, "?");
1019 case WPS_TOKEN_FILE_CODEC
:
1022 if(id3
->codectype
== AFMT_UNKNOWN
)
1023 *intval
= AFMT_NUM_CODECS
;
1025 *intval
= id3
->codectype
;
1027 return get_codectype(id3
);
1029 case WPS_TOKEN_FILE_FREQUENCY
:
1030 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
1033 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
1034 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
1035 if ((id3
->frequency
% 1000) < 100)
1036 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
1038 snprintf(buf
, buf_size
, "%ld.%d",
1039 id3
->frequency
/ 1000,
1040 (id3
->frequency
% 1000) / 100);
1043 case WPS_TOKEN_FILE_NAME
:
1044 if (get_dir(buf
, buf_size
, id3
->path
, 0)) {
1045 /* Remove extension */
1046 char* sep
= strrchr(buf
, '.');
1056 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
1057 return get_dir(buf
, buf_size
, id3
->path
, 0);
1059 case WPS_TOKEN_FILE_PATH
:
1062 case WPS_TOKEN_FILE_SIZE
:
1063 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
1066 case WPS_TOKEN_FILE_VBR
:
1067 return id3
->vbr
? "(avg)" : NULL
;
1069 case WPS_TOKEN_FILE_DIRECTORY
:
1070 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
1072 case WPS_TOKEN_BATTERY_PERCENT
:
1074 int l
= battery_level();
1078 limit
= MAX(limit
, 2);
1080 /* First enum is used for "unknown level". */
1081 *intval
= (limit
- 1) * l
/ 100 + 2;
1088 snprintf(buf
, buf_size
, "%d", l
);
1095 case WPS_TOKEN_BATTERY_VOLTS
:
1097 unsigned int v
= battery_voltage();
1098 snprintf(buf
, buf_size
, "%d.%02d", v
/ 1000, (v
% 1000) / 10);
1102 case WPS_TOKEN_BATTERY_TIME
:
1104 int t
= battery_time();
1106 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
1108 strncpy(buf
, "?h ?m", buf_size
);
1113 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
1115 if(charger_input_state
==CHARGER
)
1121 #if CONFIG_CHARGING >= CHARGING_MONITOR
1122 case WPS_TOKEN_BATTERY_CHARGING
:
1124 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
1131 case WPS_TOKEN_BATTERY_SLEEPTIME
:
1133 if (get_sleep_timer() == 0)
1137 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
1142 case WPS_TOKEN_PLAYBACK_STATUS
:
1144 int status
= audio_status();
1146 if (status
== AUDIO_STATUS_PLAY
)
1148 if (wps_fading_out
||
1149 (status
& AUDIO_STATUS_PAUSE
&& !status_get_ffmode()))
1151 if (status_get_ffmode() == STATUS_FASTFORWARD
)
1153 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
1160 snprintf(buf
, buf_size
, "%d", mode
-1);
1164 case WPS_TOKEN_REPEAT_MODE
:
1166 *intval
= global_settings
.repeat_mode
+ 1;
1167 snprintf(buf
, buf_size
, "%d", *intval
);
1169 case WPS_TOKEN_RTC_12HOUR_CFG
:
1171 *intval
= global_settings
.timeformat
+ 1;
1172 snprintf(buf
, buf_size
, "%d", *intval
);
1175 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1176 /* d: day of month (01..31) */
1177 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
1180 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1181 /* e: day of month, blank padded ( 1..31) */
1182 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
1185 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1186 /* H: hour (00..23) */
1187 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
1190 case WPS_TOKEN_RTC_HOUR_24
:
1191 /* k: hour ( 0..23) */
1192 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
1195 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1196 /* I: hour (01..12) */
1197 snprintf(buf
, buf_size
, "%02d",
1198 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1201 case WPS_TOKEN_RTC_HOUR_12
:
1202 /* l: hour ( 1..12) */
1203 snprintf(buf
, buf_size
, "%2d",
1204 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1207 case WPS_TOKEN_RTC_MONTH
:
1208 /* m: month (01..12) */
1210 *intval
= tm
->tm_mon
+ 1;
1211 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
1214 case WPS_TOKEN_RTC_MINUTE
:
1215 /* M: minute (00..59) */
1216 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
1219 case WPS_TOKEN_RTC_SECOND
:
1220 /* S: second (00..59) */
1221 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
1224 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1225 /* y: last two digits of year (00..99) */
1226 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
1229 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1230 /* Y: year (1970...) */
1231 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
1234 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1235 /* p: upper case AM or PM indicator */
1236 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "AM" : "PM");
1239 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1240 /* P: lower case am or pm indicator */
1241 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "am" : "pm");
1244 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1245 /* a: abbreviated weekday name (Sun..Sat) */
1246 snprintf(buf
, buf_size
, "%s",str(LANG_WEEKDAY_SUNDAY
+ tm
->tm_wday
));
1249 case WPS_TOKEN_RTC_MONTH_NAME
:
1250 /* b: abbreviated month name (Jan..Dec) */
1251 snprintf(buf
, buf_size
, "%s",str(LANG_MONTH_JANUARY
+ tm
->tm_mon
));
1254 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1255 /* u: day of week (1..7); 1 is Monday */
1257 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
1258 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
1261 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1262 /* w: day of week (0..6); 0 is Sunday */
1264 *intval
= tm
->tm_wday
+ 1;
1265 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
1268 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1269 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1270 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1271 case WPS_TOKEN_RTC_HOUR_24
:
1272 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1273 case WPS_TOKEN_RTC_HOUR_12
:
1274 case WPS_TOKEN_RTC_MONTH
:
1275 case WPS_TOKEN_RTC_MINUTE
:
1276 case WPS_TOKEN_RTC_SECOND
:
1277 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1278 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1279 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1280 strncpy(buf
, "--", buf_size
);
1282 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1283 strncpy(buf
, "----", buf_size
);
1285 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1286 case WPS_TOKEN_RTC_MONTH_NAME
:
1287 strncpy(buf
, "---", buf_size
);
1289 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1290 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1291 strncpy(buf
, "-", buf_size
);
1295 #ifdef HAVE_LCD_CHARCELLS
1296 case WPS_TOKEN_PROGRESSBAR
:
1298 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
1303 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
1306 /* we need 11 characters (full line) for
1308 snprintf(buf
, buf_size
, " ");
1312 /* Tell the user if we have an OldPlayer */
1313 snprintf(buf
, buf_size
, " <Old LCD> ");
1318 #ifdef HAVE_TAGCACHE
1319 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
1321 *intval
= id3
->playcount
+ 1;
1323 snprintf(buf
, buf_size
, "%ld", id3
->playcount
);
1326 case WPS_TOKEN_DATABASE_RATING
:
1328 *intval
= id3
->rating
+ 1;
1330 snprintf(buf
, buf_size
, "%d", id3
->rating
);
1333 case WPS_TOKEN_DATABASE_AUTOSCORE
:
1335 *intval
= id3
->score
+ 1;
1337 snprintf(buf
, buf_size
, "%d", id3
->score
);
1341 #if (CONFIG_CODEC == SWCODEC)
1342 case WPS_TOKEN_CROSSFADE
:
1344 *intval
= global_settings
.crossfade
+ 1;
1345 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
1348 case WPS_TOKEN_REPLAYGAIN
:
1352 if (global_settings
.replaygain
== 0)
1357 get_replaygain_mode(id3
->track_gain_string
!= NULL
,
1358 id3
->album_gain_string
!= NULL
);
1360 val
= 6; /* no tag */
1364 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
1379 strncpy(buf
, id3
->track_gain_string
, buf_size
);
1383 strncpy(buf
, id3
->album_gain_string
, buf_size
);
1388 #endif /* (CONFIG_CODEC == SWCODEC) */
1390 #if (CONFIG_CODEC != MAS3507D)
1391 case WPS_TOKEN_SOUND_PITCH
:
1393 int val
= sound_get_pitch();
1394 snprintf(buf
, buf_size
, "%d.%d",
1395 val
/ 10, val
% 10);
1400 case WPS_TOKEN_MAIN_HOLD
:
1401 #ifdef HAS_BUTTON_HOLD
1404 if (is_keys_locked())
1405 #endif /*hold switch or softlock*/
1410 #ifdef HAS_REMOTE_BUTTON_HOLD
1411 case WPS_TOKEN_REMOTE_HOLD
:
1412 if (remote_button_hold())
1418 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
1419 case WPS_TOKEN_VLED_HDD
:
1426 #ifdef HAVE_LCD_BITMAP
1427 case WPS_TOKEN_LEFTMARGIN
:
1428 gwps
->display
->setmargins(token
->value
.i
,
1429 gwps
->display
->getymargin());
1438 /* Return the index to the end token for the conditional token at index.
1439 The conditional token can be either a start token or a separator
1440 (i.e. option) token.
1442 static int find_conditional_end(struct wps_data
*data
, int index
)
1445 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
1446 ret
= data
->tokens
[ret
].value
.i
;
1448 /* ret now is the index to the end token for the conditional. */
1452 /* Return the index of the appropriate case for the conditional
1453 that starts at cond_index.
1455 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
1460 struct wps_data
*data
= gwps
->data
;
1463 int cond_index
= *token_index
;
1464 char result
[128], *value
;
1465 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
1466 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
1468 /* treat ?xx<true> constructs as if they had 2 options. */
1469 if (num_options
< 2)
1472 int intval
= num_options
;
1473 /* get_token_value needs to know the number of options in the enum */
1474 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
1475 result
, sizeof(result
), &intval
);
1477 /* intval is now the number of the enum option we want to read,
1478 starting from 1. If intval is -1, we check if value is empty. */
1480 intval
= (value
&& *value
) ? 1 : num_options
;
1481 else if (intval
> num_options
|| intval
< 1)
1482 intval
= num_options
;
1484 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
1486 /* skip to the right enum case */
1487 int next
= cond_index
+ 2;
1488 for (i
= 1; i
< intval
; i
++)
1490 next
= data
->tokens
[next
].value
.i
;
1492 *token_index
= next
;
1494 if (prev_val
== intval
)
1496 /* Same conditional case as previously. Return without clearing the
1501 cond_end
= find_conditional_end(data
, cond_index
+ 2);
1502 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
1504 #ifdef HAVE_LCD_BITMAP
1505 /* clear all pictures in the conditional and nested ones */
1506 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
1507 clear_image_pos(gwps
, data
->tokens
[i
].value
.i
& 0xFF);
1509 #ifdef HAVE_ALBUMART
1510 if (data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
1511 draw_album_art(gwps
, audio_current_aa_hid(), true);
1518 /* Read a (sub)line to the given alignment format buffer.
1519 linebuf is the buffer where the data is actually stored.
1520 align is the alignment format that'll be used to display the text.
1521 The return value indicates whether the line needs to be updated.
1523 static bool get_line(struct gui_wps
*gwps
,
1524 int line
, int subline
,
1525 struct align_pos
*align
,
1529 struct wps_data
*data
= gwps
->data
;
1532 char *buf
= linebuf
; /* will always point to the writing position */
1533 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
1534 int i
, last_token_idx
;
1535 bool update
= false;
1537 /* alignment-related variables */
1539 char* cur_align_start
;
1540 cur_align_start
= buf
;
1541 cur_align
= WPS_ALIGN_LEFT
;
1543 align
->center
= NULL
;
1544 align
->right
= NULL
;
1546 #ifdef HAVE_LCD_BITMAP
1547 /* Reset margins - only bitmap targets modify them */
1548 gwps
->display
->setmargins(0, gwps
->display
->getymargin());
1551 /* Process all tokens of the desired subline */
1552 last_token_idx
= wps_last_token_index(data
, line
, subline
);
1553 for (i
= wps_first_token_index(data
, line
, subline
);
1554 i
<= last_token_idx
; i
++)
1556 switch(data
->tokens
[i
].type
)
1558 case WPS_TOKEN_CONDITIONAL
:
1559 /* place ourselves in the right conditional case */
1560 update
|= evaluate_conditional(gwps
, &i
);
1563 case WPS_TOKEN_CONDITIONAL_OPTION
:
1564 /* we've finished in the curent conditional case,
1565 skip to the end of the conditional structure */
1566 i
= find_conditional_end(data
, i
);
1569 #ifdef HAVE_LCD_BITMAP
1570 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1572 struct gui_img
*img
= data
->img
;
1573 int n
= data
->tokens
[i
].value
.i
& 0xFF;
1574 int subimage
= data
->tokens
[i
].value
.i
>> 8;
1576 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
)
1577 img
[n
].display
= subimage
;
1582 case WPS_TOKEN_ALIGN_LEFT
:
1583 case WPS_TOKEN_ALIGN_CENTER
:
1584 case WPS_TOKEN_ALIGN_RIGHT
:
1585 /* remember where the current aligned text started */
1588 case WPS_ALIGN_LEFT
:
1589 align
->left
= cur_align_start
;
1592 case WPS_ALIGN_CENTER
:
1593 align
->center
= cur_align_start
;
1596 case WPS_ALIGN_RIGHT
:
1597 align
->right
= cur_align_start
;
1600 /* start a new alignment */
1601 switch (data
->tokens
[i
].type
)
1603 case WPS_TOKEN_ALIGN_LEFT
:
1604 cur_align
= WPS_ALIGN_LEFT
;
1606 case WPS_TOKEN_ALIGN_CENTER
:
1607 cur_align
= WPS_ALIGN_CENTER
;
1609 case WPS_TOKEN_ALIGN_RIGHT
:
1610 cur_align
= WPS_ALIGN_RIGHT
;
1616 cur_align_start
= buf
;
1621 /* get the value of the tag and copy it to the buffer */
1622 char *value
= get_token_value(gwps
, &data
->tokens
[i
],
1623 temp_buf
, sizeof(temp_buf
), NULL
);
1627 while (*value
&& (buf
< linebuf_end
))
1635 /* close the current alignment */
1638 case WPS_ALIGN_LEFT
:
1639 align
->left
= cur_align_start
;
1642 case WPS_ALIGN_CENTER
:
1643 align
->center
= cur_align_start
;
1646 case WPS_ALIGN_RIGHT
:
1647 align
->right
= cur_align_start
;
1654 static void get_subline_timeout(struct gui_wps
*gwps
, int line
, int subline
)
1656 struct wps_data
*data
= gwps
->data
;
1658 int subline_idx
= wps_subline_index(data
, line
, subline
);
1659 int last_token_idx
= wps_last_token_index(data
, line
, subline
);
1661 data
->sublines
[subline_idx
].time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1663 for (i
= wps_first_token_index(data
, line
, subline
);
1664 i
<= last_token_idx
; i
++)
1666 switch(data
->tokens
[i
].type
)
1668 case WPS_TOKEN_CONDITIONAL
:
1669 /* place ourselves in the right conditional case */
1670 evaluate_conditional(gwps
, &i
);
1673 case WPS_TOKEN_CONDITIONAL_OPTION
:
1674 /* we've finished in the curent conditional case,
1675 skip to the end of the conditional structure */
1676 i
= find_conditional_end(data
, i
);
1679 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1680 data
->sublines
[subline_idx
].time_mult
= data
->tokens
[i
].value
.i
;
1689 /* Calculates which subline should be displayed for the specified line
1690 Returns true iff the subline must be refreshed */
1691 static bool update_curr_subline(struct gui_wps
*gwps
, int line
)
1693 struct wps_data
*data
= gwps
->data
;
1695 int search
, search_start
, num_sublines
;
1697 bool new_subline_refresh
;
1698 bool only_one_subline
;
1700 num_sublines
= data
->lines
[line
].num_sublines
;
1701 reset_subline
= (data
->lines
[line
].curr_subline
== SUBLINE_RESET
);
1702 new_subline_refresh
= false;
1703 only_one_subline
= false;
1705 /* if time to advance to next sub-line */
1706 if (TIME_AFTER(current_tick
, data
->lines
[line
].subline_expire_time
- 1) ||
1709 /* search all sublines until the next subline with time > 0
1710 is found or we get back to the subline we started with */
1714 search_start
= data
->lines
[line
].curr_subline
;
1716 for (search
= 0; search
< num_sublines
; search
++)
1718 data
->lines
[line
].curr_subline
++;
1720 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1721 if (data
->lines
[line
].curr_subline
== num_sublines
)
1723 if (data
->lines
[line
].curr_subline
== 1)
1724 only_one_subline
= true;
1725 data
->lines
[line
].curr_subline
= 0;
1728 /* if back where we started after search or
1729 only one subline is defined on the line */
1730 if (((search
> 0) &&
1731 (data
->lines
[line
].curr_subline
== search_start
)) ||
1734 /* no other subline with a time > 0 exists */
1735 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1737 data
->lines
[line
].subline_expire_time
) + 100 * HZ
;
1742 /* get initial time multiplier for this subline */
1743 get_subline_timeout(gwps
, line
, data
->lines
[line
].curr_subline
);
1745 int subline_idx
= wps_subline_index(data
, line
,
1746 data
->lines
[line
].curr_subline
);
1748 /* only use this subline if subline time > 0 */
1749 if (data
->sublines
[subline_idx
].time_mult
> 0)
1751 new_subline_refresh
= true;
1752 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1753 current_tick
: data
->lines
[line
].subline_expire_time
) +
1754 BASE_SUBLINE_TIME
*data
->sublines
[subline_idx
].time_mult
;
1761 return new_subline_refresh
;
1764 /* Display a line appropriately according to its alignment format.
1765 format_align contains the text, separated between left, center and right.
1766 line is the index of the line on the screen.
1767 scroll indicates whether the line is a scrolling one or not.
1769 static void write_line(struct screen
*display
,
1770 struct align_pos
*format_align
,
1775 int left_width
= 0, left_xpos
;
1776 int center_width
= 0, center_xpos
;
1777 int right_width
= 0, right_xpos
;
1783 /* calculate different string sizes and positions */
1784 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
1785 if (format_align
->left
!= 0) {
1786 display
->getstringsize((unsigned char *)format_align
->left
,
1787 &left_width
, &string_height
);
1790 if (format_align
->right
!= 0) {
1791 display
->getstringsize((unsigned char *)format_align
->right
,
1792 &right_width
, &string_height
);
1795 if (format_align
->center
!= 0) {
1796 display
->getstringsize((unsigned char *)format_align
->center
,
1797 ¢er_width
, &string_height
);
1800 left_xpos
= display
->getxmargin();
1801 right_xpos
= (display
->getwidth() - right_width
);
1802 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
1804 scroll_width
= display
->getwidth() - left_xpos
;
1806 /* Checks for overlapping strings.
1807 If needed the overlapping strings will be merged, separated by a
1810 /* CASE 1: left and centered string overlap */
1811 /* there is a left string, need to merge left and center */
1812 if ((left_width
!= 0 && center_width
!= 0) &&
1813 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1814 /* replace the former separator '\0' of left and
1815 center string with a space */
1816 *(--format_align
->center
) = ' ';
1817 /* calculate the new width and position of the merged string */
1818 left_width
= left_width
+ space_width
+ center_width
;
1819 /* there is no centered string anymore */
1822 /* there is no left string, move center to left */
1823 if ((left_width
== 0 && center_width
!= 0) &&
1824 (left_xpos
+ left_width
> center_xpos
)) {
1825 /* move the center string to the left string */
1826 format_align
->left
= format_align
->center
;
1827 /* calculate the new width and position of the string */
1828 left_width
= center_width
;
1829 /* there is no centered string anymore */
1833 /* CASE 2: centered and right string overlap */
1834 /* there is a right string, need to merge center and right */
1835 if ((center_width
!= 0 && right_width
!= 0) &&
1836 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1837 /* replace the former separator '\0' of center and
1838 right string with a space */
1839 *(--format_align
->right
) = ' ';
1840 /* move the center string to the right after merge */
1841 format_align
->right
= format_align
->center
;
1842 /* calculate the new width and position of the merged string */
1843 right_width
= center_width
+ space_width
+ right_width
;
1844 right_xpos
= (display
->getwidth() - right_width
);
1845 /* there is no centered string anymore */
1848 /* there is no right string, move center to right */
1849 if ((center_width
!= 0 && right_width
== 0) &&
1850 (center_xpos
+ center_width
> right_xpos
)) {
1851 /* move the center string to the right string */
1852 format_align
->right
= format_align
->center
;
1853 /* calculate the new width and position of the string */
1854 right_width
= center_width
;
1855 right_xpos
= (display
->getwidth() - right_width
);
1856 /* there is no centered string anymore */
1860 /* CASE 3: left and right overlap
1861 There is no center string anymore, either there never
1862 was one or it has been merged in case 1 or 2 */
1863 /* there is a left string, need to merge left and right */
1864 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1865 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1866 /* replace the former separator '\0' of left and
1867 right string with a space */
1868 *(--format_align
->right
) = ' ';
1869 /* calculate the new width and position of the string */
1870 left_width
= left_width
+ space_width
+ right_width
;
1871 /* there is no right string anymore */
1874 /* there is no left string, move right to left */
1875 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1876 (left_width
> right_xpos
)) {
1877 /* move the right string to the left string */
1878 format_align
->left
= format_align
->right
;
1879 /* calculate the new width and position of the string */
1880 left_width
= right_width
;
1881 /* there is no right string anymore */
1885 ypos
= (line
* string_height
) + display
->getymargin();
1888 if (scroll
&& ((left_width
> scroll_width
) ||
1889 (center_width
> scroll_width
) ||
1890 (right_width
> scroll_width
)))
1892 display
->puts_scroll(0, line
,
1893 (unsigned char *)format_align
->left
);
1897 #ifdef HAVE_LCD_BITMAP
1898 /* clear the line first */
1899 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1900 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1901 display
->set_drawmode(DRMODE_SOLID
);
1904 /* Nasty hack: we output an empty scrolling string,
1905 which will reset the scroller for that line */
1906 display
->puts_scroll(0, line
, (unsigned char *)"");
1908 /* print aligned strings */
1909 if (left_width
!= 0)
1911 display
->putsxy(left_xpos
, ypos
,
1912 (unsigned char *)format_align
->left
);
1914 if (center_width
!= 0)
1916 display
->putsxy(center_xpos
, ypos
,
1917 (unsigned char *)format_align
->center
);
1919 if (right_width
!= 0)
1921 display
->putsxy(right_xpos
, ypos
,
1922 (unsigned char *)format_align
->right
);
1927 /* Refresh the WPS according to refresh_mode. */
1928 bool gui_wps_refresh(struct gui_wps
*gwps
,
1930 unsigned char refresh_mode
)
1932 struct wps_data
*data
= gwps
->data
;
1933 struct screen
*display
= gwps
->display
;
1934 struct wps_state
*state
= gwps
->state
;
1936 if(!gwps
|| !data
|| !state
|| !display
)
1939 int v
, line
, i
, subline_idx
;
1940 unsigned char flags
;
1941 char linebuf
[MAX_PATH
];
1943 struct align_pos align
;
1945 align
.center
= NULL
;
1948 bool update_line
, new_subline_refresh
;
1950 #ifdef HAVE_LCD_BITMAP
1951 gui_wps_statusbar_draw(gwps
, true);
1953 /* to find out wether the peak meter is enabled we
1954 assume it wasn't until we find a line that contains
1955 the peak meter. We can't use peak_meter_enabled itself
1956 because that would mean to turn off the meter thread
1957 temporarily. (That shouldn't matter unless yield
1958 or sleep is called but who knows...)
1960 bool enable_pm
= false;
1964 /* reset to first subline if refresh all flag is set */
1965 if (refresh_mode
== WPS_REFRESH_ALL
)
1967 display
->clear_display();
1969 for (i
= 0; i
<= data
->num_lines
; i
++)
1971 data
->lines
[i
].curr_subline
= SUBLINE_RESET
;
1975 #ifdef HAVE_LCD_CHARCELLS
1976 for (i
= 0; i
< 8; i
++)
1978 if (data
->wps_progress_pat
[i
] == 0)
1979 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1985 display
->stop_scroll();
1989 state
->ff_rewind_count
= ffwd_offset
;
1991 for (v
= 0; v
< data
->num_viewports
; v
++)
1993 display
->set_viewport(&data
->viewports
[v
].vp
);
1995 if (refresh_mode
== WPS_REFRESH_ALL
)
1997 display
->clear_viewport();
2000 #ifdef HAVE_LCD_BITMAP
2001 /* Set images to not to be displayed */
2002 for (i
= 0; i
< MAX_IMAGES
; i
++)
2004 data
->img
[i
].display
= -1;
2008 for (line
= data
->viewports
[v
].first_line
;
2009 line
<= data
->viewports
[v
].last_line
; line
++)
2011 memset(linebuf
, 0, sizeof(linebuf
));
2012 update_line
= false;
2014 /* get current subline for the line */
2015 new_subline_refresh
= update_curr_subline(gwps
, line
);
2017 subline_idx
= wps_subline_index(data
, line
,
2018 data
->lines
[line
].curr_subline
);
2019 flags
= data
->sublines
[subline_idx
].line_type
;
2021 if (refresh_mode
== WPS_REFRESH_ALL
|| (flags
& refresh_mode
)
2022 || new_subline_refresh
)
2024 /* get_line tells us if we need to update the line */
2025 update_line
= get_line(gwps
, line
, data
->lines
[line
].curr_subline
,
2026 &align
, linebuf
, sizeof(linebuf
));
2029 #ifdef HAVE_LCD_BITMAP
2031 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
2033 /* the progressbar should be alone on its line */
2034 update_line
= false;
2035 draw_progressbar(gwps
, line
- data
->viewports
[v
].first_line
);
2039 if (flags
& refresh_mode
& WPS_REFRESH_PEAK_METER
)
2041 /* the peakmeter should be alone on its line */
2042 update_line
= false;
2044 int h
= font_get(display
->getfont())->height
;
2045 int peak_meter_y
= display
->getymargin() + (line
- data
->viewports
[v
].first_line
)* h
;
2047 /* The user might decide to have the peak meter in the last
2048 line so that it is only displayed if no status bar is
2049 visible. If so we neither want do draw nor enable the
2051 if (peak_meter_y
+ h
<= display
->getheight()) {
2052 /* found a line with a peak meter -> remember that we must
2055 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
2056 MIN(h
, display
->getheight() - peak_meter_y
));
2060 #else /* HAVE_LCD_CHARCELL */
2063 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
2065 if (data
->full_line_progressbar
)
2066 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
2068 draw_player_progress(gwps
);
2074 if (flags
& WPS_REFRESH_SCROLL
)
2076 /* if the line is a scrolling one we don't want to update
2077 too often, so that it has the time to scroll */
2078 if ((refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
2079 write_line(display
, &align
, line
- data
->viewports
[v
].first_line
, true);
2082 write_line(display
, &align
, line
- data
->viewports
[v
].first_line
, false);
2087 #ifdef HAVE_LCD_BITMAP
2088 /* Now display any images in this viewport */
2089 wps_display_images(gwps
, &data
->viewports
[v
].vp
);
2093 #ifdef HAVE_LCD_BITMAP
2094 data
->peak_meter_enabled
= enable_pm
;
2097 /* Restore the default viewport */
2098 display
->set_viewport(NULL
);
2102 #ifdef HAVE_BACKLIGHT
2103 if (global_settings
.caption_backlight
&& state
->id3
)
2105 /* turn on backlight n seconds before track ends, and turn it off n
2106 seconds into the new track. n == backlight_timeout, or 5s */
2107 int n
= global_settings
.backlight_timeout
* 1000;
2110 n
= 5000; /* use 5s if backlight is always on or off */
2112 if (((state
->id3
->elapsed
< 1000) ||
2113 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2114 (state
->paused
== false))
2118 #ifdef HAVE_REMOTE_LCD
2119 if (global_settings
.remote_caption_backlight
&& state
->id3
)
2121 /* turn on remote backlight n seconds before track ends, and turn it
2122 off n seconds into the new track. n == remote_backlight_timeout,
2124 int n
= global_settings
.remote_backlight_timeout
* 1000;
2127 n
= 5000; /* use 5s if backlight is always on or off */
2129 if (((state
->id3
->elapsed
< 1000) ||
2130 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2131 (state
->paused
== false))
2132 remote_backlight_on();