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 */
202 const long ff_rw_accel
= (global_settings
.ff_rewind_accel
+ 3);
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 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
241 step
+= step
>> ff_rw_accel
;
245 if ( (audio_status() & AUDIO_STATUS_PLAY
) &&
246 wps_state
.id3
&& wps_state
.id3
->length
)
248 if (!wps_state
.paused
)
249 #if (CONFIG_CODEC == SWCODEC)
250 audio_pre_ff_rewind();
254 #if CONFIG_KEYPAD == PLAYER_PAD
256 gui_wps
[i
].display
->stop_scroll();
259 status_set_ffmode(STATUS_FASTFORWARD
);
261 status_set_ffmode(STATUS_FASTBACKWARD
);
263 wps_state
.ff_rewind
= true;
265 step
= 1000 * global_settings
.ff_rewind_min_step
;
272 if ((wps_state
.id3
->elapsed
+ ff_rewind_count
) >
273 wps_state
.id3
->length
)
274 ff_rewind_count
= wps_state
.id3
->length
-
275 wps_state
.id3
->elapsed
;
278 if ((int)(wps_state
.id3
->elapsed
+ ff_rewind_count
) < 0)
279 ff_rewind_count
= -wps_state
.id3
->elapsed
;
283 gui_wps_refresh(&gui_wps
[i
],
284 (wps_state
.wps_time_countup
== false)?
285 ff_rewind_count
:-ff_rewind_count
,
286 WPS_REFRESH_PLAYER_PROGRESS
|
287 WPS_REFRESH_DYNAMIC
);
291 case ACTION_WPS_STOPSEEK
:
292 wps_state
.id3
->elapsed
= wps_state
.id3
->elapsed
+ff_rewind_count
;
293 audio_ff_rewind(wps_state
.id3
->elapsed
);
295 wps_state
.ff_rewind
= false;
296 status_set_ffmode(0);
297 #if (CONFIG_CODEC != SWCODEC)
298 if (!wps_state
.paused
)
301 #ifdef HAVE_LCD_CHARCELLS
308 if(default_event_handler(button
) == SYS_USB_CONNECTED
) {
309 status_set_ffmode(0);
316 button
= get_action(CONTEXT_WPS
|ALLOW_SOFTLOCK
,TIMEOUT_BLOCK
);
321 bool gui_wps_display(void)
324 if (!wps_state
.id3
&& !(audio_status() & AUDIO_STATUS_PLAY
))
326 global_status
.resume_index
= -1;
327 #ifdef HAVE_LCD_BITMAP
328 gui_syncstatusbar_draw(&statusbars
, true);
330 gui_syncsplash(HZ
, ID2P(LANG_END_PLAYLIST
));
337 /* Update the values in the first (default) viewport - in case the user
338 has modified the statusbar or colour settings */
339 #ifdef HAVE_LCD_BITMAP
341 if (gui_wps
[i
].display
->depth
> 1)
343 gui_wps
[i
].data
->viewports
[0].vp
.fg_pattern
= gui_wps
[i
].display
->get_foreground();
344 gui_wps
[i
].data
->viewports
[0].vp
.bg_pattern
= gui_wps
[i
].display
->get_background();
349 gui_wps
[i
].display
->clear_display();
350 if (!gui_wps
[i
].data
->wps_loaded
) {
351 if ( !gui_wps
[i
].data
->num_tokens
) {
352 /* set the default wps for the main-screen */
355 #ifdef HAVE_LCD_BITMAP
357 unload_wps_backdrop();
359 wps_data_load(gui_wps
[i
].data
,
361 "%s%?it<%?in<%in. |>%it|%fn>\n"
362 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
363 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
365 "%al%pc/%pt%ar[%pp:%pe]\n"
366 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
370 wps_data_load(gui_wps
[i
].data
,
372 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
373 "%pc%?ps<*|/>%pt\n", false);
377 /* set the default wps for the remote-screen */
380 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
381 unload_remote_wps_backdrop();
383 wps_data_load(gui_wps
[i
].data
,
385 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
386 "%s%?it<%?in<%in. |>%it|%fn>\n"
387 "%al%pc/%pt%ar[%pp:%pe]\n"
388 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
399 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_ALL
);
404 bool update(struct gui_wps
*gwps
)
406 bool track_changed
= audio_has_changed_track();
407 bool retcode
= false;
409 gwps
->state
->nid3
= audio_next_track();
412 gwps
->display
->stop_scroll();
413 gwps
->state
->id3
= audio_current_track();
415 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
416 && strcmp(gwps
->state
->id3
->path
, curr_cue
->audio_filename
))
418 /* the current cuesheet isn't the right one any more */
420 if (!strcmp(gwps
->state
->id3
->path
, temp_cue
->audio_filename
)) {
421 /* We have the new cuesheet in memory (temp_cue),
422 let's make it the current one ! */
423 memcpy(curr_cue
, temp_cue
, sizeof(struct cuesheet
));
426 /* We need to parse the new cuesheet */
428 char cuepath
[MAX_PATH
];
430 if (look_for_cuesheet_file(gwps
->state
->id3
->path
, cuepath
) &&
431 parse_cuesheet(cuepath
, curr_cue
))
433 gwps
->state
->id3
->cuesheet_type
= 1;
434 strcpy(curr_cue
->audio_filename
, gwps
->state
->id3
->path
);
438 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
441 if (gui_wps_display())
444 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
447 if (gwps
->state
->id3
)
449 strncpy(gwps
->state
->current_track_path
, gwps
->state
->id3
->path
,
450 sizeof(gwps
->state
->current_track_path
));
451 gwps
->state
->current_track_path
[sizeof(gwps
->state
->current_track_path
)-1] = '\0';
455 if (gwps
->state
->id3
)
457 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
458 && (gwps
->state
->id3
->elapsed
< curr_cue
->curr_track
->offset
459 || (curr_cue
->curr_track_idx
< curr_cue
->track_count
- 1
460 && gwps
->state
->id3
->elapsed
>= (curr_cue
->curr_track
+1)->offset
)))
462 /* We've changed tracks within the cuesheet :
463 we need to update the ID3 info and refresh the WPS */
465 cue_find_current_track(curr_cue
, gwps
->state
->id3
->elapsed
);
466 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
468 gwps
->display
->stop_scroll();
469 if (gui_wps_display())
472 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
475 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
478 gui_wps_statusbar_draw(gwps
, false);
484 void display_keylock_text(bool locked
)
489 gui_wps
[i
].display
->stop_scroll();
492 s
= str(LANG_KEYLOCK_ON
);
494 s
= str(LANG_KEYLOCK_OFF
);
495 gui_syncsplash(HZ
, s
);
498 #ifdef HAVE_LCD_BITMAP
500 static void draw_progressbar(struct gui_wps
*gwps
,
501 struct progressbar
*pb
)
503 struct screen
*display
= gwps
->display
;
504 struct wps_state
*state
= gwps
->state
;
505 if (pb
->have_bitmap_pb
)
506 gui_bitmap_scrollbar_draw(display
, pb
->bm
,
507 pb
->x
, pb
->y
, pb
->width
, pb
->bm
.height
,
508 state
->id3
->length
? state
->id3
->length
: 1, 0,
509 state
->id3
->length
? state
->id3
->elapsed
510 + state
->ff_rewind_count
: 0,
513 gui_scrollbar_draw(display
, pb
->x
, pb
->y
, pb
->width
, pb
->height
,
514 state
->id3
->length
? state
->id3
->length
: 1, 0,
515 state
->id3
->length
? state
->id3
->elapsed
516 + state
->ff_rewind_count
: 0,
518 #ifdef AB_REPEAT_ENABLE
519 if ( ab_repeat_mode_enabled() && state
->id3
->length
!= 0 )
520 ab_draw_markers(display
, state
->id3
->length
,
521 pb
->x
, pb
->x
+ pb
->width
, pb
->y
, pb
->height
);
524 if ( cuesheet_is_enabled() && state
->id3
->cuesheet_type
)
525 cue_draw_markers(display
, state
->id3
->length
,
526 pb
->x
, pb
->x
+ pb
->width
, pb
->y
+1, pb
->height
-2);
529 /* clears the area where the image was shown */
530 static void clear_image_pos(struct gui_wps
*gwps
, int n
)
534 struct wps_data
*data
= gwps
->data
;
535 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
536 gwps
->display
->fillrect(data
->img
[n
].x
, data
->img
[n
].y
,
537 data
->img
[n
].bm
.width
, data
->img
[n
].subimage_height
);
538 gwps
->display
->set_drawmode(DRMODE_SOLID
);
541 static void wps_draw_image(struct gui_wps
*gwps
, int n
, int subimage
)
543 struct screen
*display
= gwps
->display
;
544 struct wps_data
*data
= gwps
->data
;
545 if(data
->img
[n
].always_display
)
546 display
->set_drawmode(DRMODE_FG
);
548 display
->set_drawmode(DRMODE_SOLID
);
551 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
553 display
->mono_bitmap_part(data
->img
[n
].bm
.data
,
554 0, data
->img
[n
].subimage_height
* subimage
,
555 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
556 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
557 data
->img
[n
].subimage_height
);
560 display
->transparent_bitmap_part((fb_data
*)data
->img
[n
].bm
.data
,
561 0, data
->img
[n
].subimage_height
* subimage
,
562 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
563 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
564 data
->img
[n
].subimage_height
);
569 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
571 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
575 struct wps_data
*data
= gwps
->data
;
576 struct screen
*display
= gwps
->display
;
578 for (n
= 0; n
< MAX_IMAGES
; n
++)
580 if (data
->img
[n
].loaded
)
582 if (data
->img
[n
].display
>= 0)
584 wps_draw_image(gwps
, n
, data
->img
[n
].display
);
585 } else if (data
->img
[n
].always_display
&& data
->img
[n
].vp
== vp
)
587 wps_draw_image(gwps
, n
, 0);
591 display
->set_drawmode(DRMODE_SOLID
);
594 #else /* HAVE_LCD_CHARCELL */
596 static bool draw_player_progress(struct gui_wps
*gwps
)
598 struct wps_state
*state
= gwps
->state
;
599 struct screen
*display
= gwps
->display
;
600 unsigned char progress_pattern
[7];
607 if (state
->id3
->length
)
608 pos
= 36 * (state
->id3
->elapsed
+ state
->ff_rewind_count
)
609 / state
->id3
->length
;
611 for (i
= 0; i
< 7; i
++, pos
-= 5)
614 progress_pattern
[i
] = 0x1f;
616 progress_pattern
[i
] = 0x00;
618 progress_pattern
[i
] = 0x1f >> pos
;
621 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
625 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
627 static const unsigned char numbers
[10][4] = {
628 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
629 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
630 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
631 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
632 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
633 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
634 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
635 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
636 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
637 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
640 struct wps_state
*state
= gwps
->state
;
641 struct screen
*display
= gwps
->display
;
642 struct wps_data
*data
= gwps
->data
;
643 unsigned char progress_pattern
[7];
652 if (!state
->id3
|| buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
655 time
= state
->id3
->elapsed
+ state
->ff_rewind_count
;
656 if (state
->id3
->length
)
657 pos
= 55 * time
/ state
->id3
->length
;
659 memset(timestr
, 0, sizeof(timestr
));
660 format_time(timestr
, sizeof(timestr
)-2, time
);
661 timestr
[strlen(timestr
)] = ':'; /* always safe */
663 for (i
= 0; i
< 11; i
++, pos
-= 5)
666 memset(progress_pattern
, 0, sizeof(progress_pattern
));
668 if ((digit
= timestr
[time_idx
]))
673 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
675 memcpy(progress_pattern
, numbers
[digit
], 4);
678 else /* tens, shifted right */
680 for (j
= 0; j
< 4; j
++)
681 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
683 if (time_idx
> 0) /* not the first group, add colon in front */
685 progress_pattern
[1] |= 0x10;
686 progress_pattern
[3] |= 0x10;
692 progress_pattern
[5] = progress_pattern
[6] = 0x1f;
695 if (pos
> 0 && pos
< 5)
698 progress_pattern
[5] = progress_pattern
[6] = (~0x1f >> pos
) & 0x1f;
701 if (softchar
&& pat_idx
< 8)
703 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
705 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
709 buf
= utf8encode(' ', buf
);
711 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
716 #endif /* HAVE_LCD_CHARCELL */
718 static char* get_codectype(const struct mp3entry
* id3
)
720 if (id3
->codectype
< AFMT_NUM_CODECS
) {
721 return (char*)audio_formats
[id3
->codectype
].label
;
727 /* Extract a part from a path.
729 * buf - buffer extract part to.
730 * buf_size - size of buffer.
731 * path - path to extract from.
732 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
733 * parent of parent, etc.
735 * Returns buf if the desired level was found, NULL otherwise.
737 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
740 const char* last_sep
;
743 sep
= path
+ strlen(path
);
758 if (level
|| (last_sep
<= sep
))
761 len
= MIN(last_sep
- sep
, buf_size
- 1);
762 strncpy(buf
, sep
+ 1, len
);
767 /* Return the tag found at index i and write its value in buf.
768 The return value is buf if the tag had a value, or NULL if not.
770 intval is used with conditionals/enums: when this function is called,
771 intval should contain the number of options in the conditional/enum.
772 When this function returns, intval is -1 if the tag is non numeric or,
773 if the tag is numeric, intval is the enum case we want to go to.
774 When not treating a conditional/enum, intval should be NULL.
776 static char *get_token_value(struct gui_wps
*gwps
,
777 struct wps_token
*token
,
778 char *buf
, int buf_size
,
784 struct wps_data
*data
= gwps
->data
;
785 struct wps_state
*state
= gwps
->state
;
790 struct mp3entry
*id3
;
801 struct tm
* tm
= NULL
;
803 /* if the token is an RTC one, update the time
804 and do the necessary checks */
805 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
806 && token
->type
<= WPS_TOKENS_RTC_END
)
824 case WPS_TOKEN_CHARACTER
:
825 return &(token
->value
.c
);
827 case WPS_TOKEN_STRING
:
828 return data
->strings
[token
->value
.i
];
830 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
831 format_time(buf
, buf_size
,
832 id3
->elapsed
+ state
->ff_rewind_count
);
835 case WPS_TOKEN_TRACK_TIME_REMAINING
:
836 format_time(buf
, buf_size
,
837 id3
->length
- id3
->elapsed
-
838 state
->ff_rewind_count
);
841 case WPS_TOKEN_TRACK_LENGTH
:
842 format_time(buf
, buf_size
, id3
->length
);
845 case WPS_TOKEN_PLAYLIST_ENTRIES
:
846 snprintf(buf
, buf_size
, "%d", playlist_amount());
849 case WPS_TOKEN_PLAYLIST_NAME
:
850 return playlist_name(NULL
, buf
, buf_size
);
852 case WPS_TOKEN_PLAYLIST_POSITION
:
853 snprintf(buf
, buf_size
, "%d", playlist_get_display_index());
856 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
857 if ( global_settings
.playlist_shuffle
)
863 case WPS_TOKEN_VOLUME
:
864 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
867 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
871 else if (global_settings
.volume
== 0)
875 else if (global_settings
.volume
> 0)
881 *intval
= (limit
- 3) * (global_settings
.volume
882 - sound_min(SOUND_VOLUME
) - 1)
883 / (-1 - sound_min(SOUND_VOLUME
)) + 2;
888 case WPS_TOKEN_TRACK_ELAPSED_PERCENT
:
889 if (id3
->length
<= 0)
894 *intval
= limit
* (id3
->elapsed
+ state
->ff_rewind_count
)
897 snprintf(buf
, buf_size
, "%d",
898 100*(id3
->elapsed
+ state
->ff_rewind_count
) / id3
->length
);
901 case WPS_TOKEN_METADATA_ARTIST
:
904 case WPS_TOKEN_METADATA_COMPOSER
:
905 return id3
->composer
;
907 case WPS_TOKEN_METADATA_ALBUM
:
910 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
911 return id3
->albumartist
;
913 case WPS_TOKEN_METADATA_GROUPING
:
914 return id3
->grouping
;
916 case WPS_TOKEN_METADATA_GENRE
:
917 return id3
->genre_string
;
919 case WPS_TOKEN_METADATA_DISC_NUMBER
:
920 if (id3
->disc_string
)
921 return id3
->disc_string
;
923 snprintf(buf
, buf_size
, "%d", id3
->discnum
);
928 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
929 if (id3
->track_string
)
930 return id3
->track_string
;
933 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
938 case WPS_TOKEN_METADATA_TRACK_TITLE
:
941 case WPS_TOKEN_METADATA_VERSION
:
942 switch (id3
->id3version
)
963 case WPS_TOKEN_METADATA_YEAR
:
964 if( id3
->year_string
)
965 return id3
->year_string
;
968 snprintf(buf
, buf_size
, "%d", id3
->year
);
973 case WPS_TOKEN_METADATA_COMMENT
:
977 case WPS_TOKEN_ALBUMART_DISPLAY
:
978 draw_album_art(gwps
, audio_current_aa_hid(), false);
981 case WPS_TOKEN_ALBUMART_FOUND
:
982 if (audio_current_aa_hid() >= 0) {
983 snprintf(buf
, buf_size
, "C");
989 case WPS_TOKEN_FILE_BITRATE
:
991 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
993 snprintf(buf
, buf_size
, "?");
996 case WPS_TOKEN_FILE_CODEC
:
999 if(id3
->codectype
== AFMT_UNKNOWN
)
1000 *intval
= AFMT_NUM_CODECS
;
1002 *intval
= id3
->codectype
;
1004 return get_codectype(id3
);
1006 case WPS_TOKEN_FILE_FREQUENCY
:
1007 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
1010 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
1011 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
1012 if ((id3
->frequency
% 1000) < 100)
1013 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
1015 snprintf(buf
, buf_size
, "%ld.%d",
1016 id3
->frequency
/ 1000,
1017 (id3
->frequency
% 1000) / 100);
1020 case WPS_TOKEN_FILE_NAME
:
1021 if (get_dir(buf
, buf_size
, id3
->path
, 0)) {
1022 /* Remove extension */
1023 char* sep
= strrchr(buf
, '.');
1033 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
1034 return get_dir(buf
, buf_size
, id3
->path
, 0);
1036 case WPS_TOKEN_FILE_PATH
:
1039 case WPS_TOKEN_FILE_SIZE
:
1040 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
1043 case WPS_TOKEN_FILE_VBR
:
1044 return id3
->vbr
? "(avg)" : NULL
;
1046 case WPS_TOKEN_FILE_DIRECTORY
:
1047 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
1049 case WPS_TOKEN_BATTERY_PERCENT
:
1051 int l
= battery_level();
1055 limit
= MAX(limit
, 2);
1057 /* First enum is used for "unknown level". */
1058 *intval
= (limit
- 1) * l
/ 100 + 2;
1065 snprintf(buf
, buf_size
, "%d", l
);
1072 case WPS_TOKEN_BATTERY_VOLTS
:
1074 unsigned int v
= battery_voltage();
1075 snprintf(buf
, buf_size
, "%d.%02d", v
/ 1000, (v
% 1000) / 10);
1079 case WPS_TOKEN_BATTERY_TIME
:
1081 int t
= battery_time();
1083 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
1085 strncpy(buf
, "?h ?m", buf_size
);
1090 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
1092 if(charger_input_state
==CHARGER
)
1098 #if CONFIG_CHARGING >= CHARGING_MONITOR
1099 case WPS_TOKEN_BATTERY_CHARGING
:
1101 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
1108 case WPS_TOKEN_BATTERY_SLEEPTIME
:
1110 if (get_sleep_timer() == 0)
1114 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
1119 case WPS_TOKEN_PLAYBACK_STATUS
:
1121 int status
= audio_status();
1123 if (status
== AUDIO_STATUS_PLAY
)
1125 if (wps_fading_out
||
1126 (status
& AUDIO_STATUS_PAUSE
&& !status_get_ffmode()))
1128 if (status_get_ffmode() == STATUS_FASTFORWARD
)
1130 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
1137 snprintf(buf
, buf_size
, "%d", mode
-1);
1141 case WPS_TOKEN_REPEAT_MODE
:
1143 *intval
= global_settings
.repeat_mode
+ 1;
1144 snprintf(buf
, buf_size
, "%d", *intval
);
1146 case WPS_TOKEN_RTC_12HOUR_CFG
:
1148 *intval
= global_settings
.timeformat
+ 1;
1149 snprintf(buf
, buf_size
, "%d", *intval
);
1152 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1153 /* d: day of month (01..31) */
1154 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
1157 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1158 /* e: day of month, blank padded ( 1..31) */
1159 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
1162 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1163 /* H: hour (00..23) */
1164 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
1167 case WPS_TOKEN_RTC_HOUR_24
:
1168 /* k: hour ( 0..23) */
1169 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
1172 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1173 /* I: hour (01..12) */
1174 snprintf(buf
, buf_size
, "%02d",
1175 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1178 case WPS_TOKEN_RTC_HOUR_12
:
1179 /* l: hour ( 1..12) */
1180 snprintf(buf
, buf_size
, "%2d",
1181 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1184 case WPS_TOKEN_RTC_MONTH
:
1185 /* m: month (01..12) */
1187 *intval
= tm
->tm_mon
+ 1;
1188 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
1191 case WPS_TOKEN_RTC_MINUTE
:
1192 /* M: minute (00..59) */
1193 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
1196 case WPS_TOKEN_RTC_SECOND
:
1197 /* S: second (00..59) */
1198 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
1201 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1202 /* y: last two digits of year (00..99) */
1203 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
1206 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1207 /* Y: year (1970...) */
1208 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
1211 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1212 /* p: upper case AM or PM indicator */
1213 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "AM" : "PM");
1216 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1217 /* P: lower case am or pm indicator */
1218 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "am" : "pm");
1221 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1222 /* a: abbreviated weekday name (Sun..Sat) */
1223 snprintf(buf
, buf_size
, "%s",str(LANG_WEEKDAY_SUNDAY
+ tm
->tm_wday
));
1226 case WPS_TOKEN_RTC_MONTH_NAME
:
1227 /* b: abbreviated month name (Jan..Dec) */
1228 snprintf(buf
, buf_size
, "%s",str(LANG_MONTH_JANUARY
+ tm
->tm_mon
));
1231 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1232 /* u: day of week (1..7); 1 is Monday */
1234 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
1235 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
1238 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1239 /* w: day of week (0..6); 0 is Sunday */
1241 *intval
= tm
->tm_wday
+ 1;
1242 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
1245 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1246 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1247 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1248 case WPS_TOKEN_RTC_HOUR_24
:
1249 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1250 case WPS_TOKEN_RTC_HOUR_12
:
1251 case WPS_TOKEN_RTC_MONTH
:
1252 case WPS_TOKEN_RTC_MINUTE
:
1253 case WPS_TOKEN_RTC_SECOND
:
1254 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1255 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1256 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1257 strncpy(buf
, "--", buf_size
);
1259 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1260 strncpy(buf
, "----", buf_size
);
1262 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1263 case WPS_TOKEN_RTC_MONTH_NAME
:
1264 strncpy(buf
, "---", buf_size
);
1266 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1267 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1268 strncpy(buf
, "-", buf_size
);
1272 #ifdef HAVE_LCD_CHARCELLS
1273 case WPS_TOKEN_PROGRESSBAR
:
1275 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
1280 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
1283 /* we need 11 characters (full line) for
1285 snprintf(buf
, buf_size
, " ");
1289 /* Tell the user if we have an OldPlayer */
1290 snprintf(buf
, buf_size
, " <Old LCD> ");
1295 #ifdef HAVE_TAGCACHE
1296 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
1298 *intval
= id3
->playcount
+ 1;
1300 snprintf(buf
, buf_size
, "%ld", id3
->playcount
);
1303 case WPS_TOKEN_DATABASE_RATING
:
1305 *intval
= id3
->rating
+ 1;
1307 snprintf(buf
, buf_size
, "%d", id3
->rating
);
1310 case WPS_TOKEN_DATABASE_AUTOSCORE
:
1312 *intval
= id3
->score
+ 1;
1314 snprintf(buf
, buf_size
, "%d", id3
->score
);
1318 #if (CONFIG_CODEC == SWCODEC)
1319 case WPS_TOKEN_CROSSFADE
:
1321 *intval
= global_settings
.crossfade
+ 1;
1322 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
1325 case WPS_TOKEN_REPLAYGAIN
:
1329 if (global_settings
.replaygain
== 0)
1334 get_replaygain_mode(id3
->track_gain_string
!= NULL
,
1335 id3
->album_gain_string
!= NULL
);
1337 val
= 6; /* no tag */
1341 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
1356 strncpy(buf
, id3
->track_gain_string
, buf_size
);
1360 strncpy(buf
, id3
->album_gain_string
, buf_size
);
1365 #endif /* (CONFIG_CODEC == SWCODEC) */
1367 #if (CONFIG_CODEC != MAS3507D)
1368 case WPS_TOKEN_SOUND_PITCH
:
1370 int val
= sound_get_pitch();
1371 snprintf(buf
, buf_size
, "%d.%d",
1372 val
/ 10, val
% 10);
1377 case WPS_TOKEN_MAIN_HOLD
:
1378 #ifdef HAS_BUTTON_HOLD
1381 if (is_keys_locked())
1382 #endif /*hold switch or softlock*/
1387 #ifdef HAS_REMOTE_BUTTON_HOLD
1388 case WPS_TOKEN_REMOTE_HOLD
:
1389 if (remote_button_hold())
1395 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
1396 case WPS_TOKEN_VLED_HDD
:
1407 /* Return the index to the end token for the conditional token at index.
1408 The conditional token can be either a start token or a separator
1409 (i.e. option) token.
1411 static int find_conditional_end(struct wps_data
*data
, int index
)
1414 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
1415 ret
= data
->tokens
[ret
].value
.i
;
1417 /* ret now is the index to the end token for the conditional. */
1421 /* Return the index of the appropriate case for the conditional
1422 that starts at cond_index.
1424 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
1429 struct wps_data
*data
= gwps
->data
;
1432 int cond_index
= *token_index
;
1433 char result
[128], *value
;
1434 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
1435 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
1437 /* treat ?xx<true> constructs as if they had 2 options. */
1438 if (num_options
< 2)
1441 int intval
= num_options
;
1442 /* get_token_value needs to know the number of options in the enum */
1443 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
1444 result
, sizeof(result
), &intval
);
1446 /* intval is now the number of the enum option we want to read,
1447 starting from 1. If intval is -1, we check if value is empty. */
1449 intval
= (value
&& *value
) ? 1 : num_options
;
1450 else if (intval
> num_options
|| intval
< 1)
1451 intval
= num_options
;
1453 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
1455 /* skip to the right enum case */
1456 int next
= cond_index
+ 2;
1457 for (i
= 1; i
< intval
; i
++)
1459 next
= data
->tokens
[next
].value
.i
;
1461 *token_index
= next
;
1463 if (prev_val
== intval
)
1465 /* Same conditional case as previously. Return without clearing the
1470 cond_end
= find_conditional_end(data
, cond_index
+ 2);
1471 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
1473 #ifdef HAVE_LCD_BITMAP
1474 /* clear all pictures in the conditional and nested ones */
1475 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
1476 clear_image_pos(gwps
, data
->tokens
[i
].value
.i
& 0xFF);
1478 #ifdef HAVE_ALBUMART
1479 if (data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
1480 draw_album_art(gwps
, audio_current_aa_hid(), true);
1487 /* Read a (sub)line to the given alignment format buffer.
1488 linebuf is the buffer where the data is actually stored.
1489 align is the alignment format that'll be used to display the text.
1490 The return value indicates whether the line needs to be updated.
1492 static bool get_line(struct gui_wps
*gwps
,
1493 int line
, int subline
,
1494 struct align_pos
*align
,
1498 struct wps_data
*data
= gwps
->data
;
1501 char *buf
= linebuf
; /* will always point to the writing position */
1502 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
1503 int i
, last_token_idx
;
1504 bool update
= false;
1506 /* alignment-related variables */
1508 char* cur_align_start
;
1509 cur_align_start
= buf
;
1510 cur_align
= WPS_ALIGN_LEFT
;
1512 align
->center
= NULL
;
1513 align
->right
= NULL
;
1515 /* Process all tokens of the desired subline */
1516 last_token_idx
= wps_last_token_index(data
, line
, subline
);
1517 for (i
= wps_first_token_index(data
, line
, subline
);
1518 i
<= last_token_idx
; i
++)
1520 switch(data
->tokens
[i
].type
)
1522 case WPS_TOKEN_CONDITIONAL
:
1523 /* place ourselves in the right conditional case */
1524 update
|= evaluate_conditional(gwps
, &i
);
1527 case WPS_TOKEN_CONDITIONAL_OPTION
:
1528 /* we've finished in the curent conditional case,
1529 skip to the end of the conditional structure */
1530 i
= find_conditional_end(data
, i
);
1533 #ifdef HAVE_LCD_BITMAP
1534 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1536 struct gui_img
*img
= data
->img
;
1537 int n
= data
->tokens
[i
].value
.i
& 0xFF;
1538 int subimage
= data
->tokens
[i
].value
.i
>> 8;
1540 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
)
1541 img
[n
].display
= subimage
;
1546 case WPS_TOKEN_ALIGN_LEFT
:
1547 case WPS_TOKEN_ALIGN_CENTER
:
1548 case WPS_TOKEN_ALIGN_RIGHT
:
1549 /* remember where the current aligned text started */
1552 case WPS_ALIGN_LEFT
:
1553 align
->left
= cur_align_start
;
1556 case WPS_ALIGN_CENTER
:
1557 align
->center
= cur_align_start
;
1560 case WPS_ALIGN_RIGHT
:
1561 align
->right
= cur_align_start
;
1564 /* start a new alignment */
1565 switch (data
->tokens
[i
].type
)
1567 case WPS_TOKEN_ALIGN_LEFT
:
1568 cur_align
= WPS_ALIGN_LEFT
;
1570 case WPS_TOKEN_ALIGN_CENTER
:
1571 cur_align
= WPS_ALIGN_CENTER
;
1573 case WPS_TOKEN_ALIGN_RIGHT
:
1574 cur_align
= WPS_ALIGN_RIGHT
;
1580 cur_align_start
= buf
;
1582 case WPS_VIEWPORT_ENABLE
:
1584 char label
= data
->tokens
[i
].value
.i
;
1586 char temp
= VP_DRAW_HIDEABLE
;
1587 for(j
=0;j
<data
->num_viewports
;j
++)
1589 temp
= VP_DRAW_HIDEABLE
;
1590 if ((data
->viewports
[j
].hidden_flags
&VP_DRAW_HIDEABLE
) &&
1591 (data
->viewports
[j
].label
== label
))
1593 if (data
->viewports
[j
].hidden_flags
&VP_DRAW_WASHIDDEN
)
1594 temp
|= VP_DRAW_WASHIDDEN
;
1595 data
->viewports
[j
].hidden_flags
= temp
;
1602 /* get the value of the tag and copy it to the buffer */
1603 char *value
= get_token_value(gwps
, &data
->tokens
[i
],
1604 temp_buf
, sizeof(temp_buf
), NULL
);
1608 while (*value
&& (buf
< linebuf_end
))
1616 /* close the current alignment */
1619 case WPS_ALIGN_LEFT
:
1620 align
->left
= cur_align_start
;
1623 case WPS_ALIGN_CENTER
:
1624 align
->center
= cur_align_start
;
1627 case WPS_ALIGN_RIGHT
:
1628 align
->right
= cur_align_start
;
1635 static void get_subline_timeout(struct gui_wps
*gwps
, int line
, int subline
)
1637 struct wps_data
*data
= gwps
->data
;
1639 int subline_idx
= wps_subline_index(data
, line
, subline
);
1640 int last_token_idx
= wps_last_token_index(data
, line
, subline
);
1642 data
->sublines
[subline_idx
].time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1644 for (i
= wps_first_token_index(data
, line
, subline
);
1645 i
<= last_token_idx
; i
++)
1647 switch(data
->tokens
[i
].type
)
1649 case WPS_TOKEN_CONDITIONAL
:
1650 /* place ourselves in the right conditional case */
1651 evaluate_conditional(gwps
, &i
);
1654 case WPS_TOKEN_CONDITIONAL_OPTION
:
1655 /* we've finished in the curent conditional case,
1656 skip to the end of the conditional structure */
1657 i
= find_conditional_end(data
, i
);
1660 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1661 data
->sublines
[subline_idx
].time_mult
= data
->tokens
[i
].value
.i
;
1670 /* Calculates which subline should be displayed for the specified line
1671 Returns true iff the subline must be refreshed */
1672 static bool update_curr_subline(struct gui_wps
*gwps
, int line
)
1674 struct wps_data
*data
= gwps
->data
;
1676 int search
, search_start
, num_sublines
;
1678 bool new_subline_refresh
;
1679 bool only_one_subline
;
1681 num_sublines
= data
->lines
[line
].num_sublines
;
1682 reset_subline
= (data
->lines
[line
].curr_subline
== SUBLINE_RESET
);
1683 new_subline_refresh
= false;
1684 only_one_subline
= false;
1686 /* if time to advance to next sub-line */
1687 if (TIME_AFTER(current_tick
, data
->lines
[line
].subline_expire_time
- 1) ||
1690 /* search all sublines until the next subline with time > 0
1691 is found or we get back to the subline we started with */
1695 search_start
= data
->lines
[line
].curr_subline
;
1697 for (search
= 0; search
< num_sublines
; search
++)
1699 data
->lines
[line
].curr_subline
++;
1701 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1702 if (data
->lines
[line
].curr_subline
== num_sublines
)
1704 if (data
->lines
[line
].curr_subline
== 1)
1705 only_one_subline
= true;
1706 data
->lines
[line
].curr_subline
= 0;
1709 /* if back where we started after search or
1710 only one subline is defined on the line */
1711 if (((search
> 0) &&
1712 (data
->lines
[line
].curr_subline
== search_start
)) ||
1715 /* no other subline with a time > 0 exists */
1716 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1718 data
->lines
[line
].subline_expire_time
) + 100 * HZ
;
1723 /* get initial time multiplier for this subline */
1724 get_subline_timeout(gwps
, line
, data
->lines
[line
].curr_subline
);
1726 int subline_idx
= wps_subline_index(data
, line
,
1727 data
->lines
[line
].curr_subline
);
1729 /* only use this subline if subline time > 0 */
1730 if (data
->sublines
[subline_idx
].time_mult
> 0)
1732 new_subline_refresh
= true;
1733 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1734 current_tick
: data
->lines
[line
].subline_expire_time
) +
1735 BASE_SUBLINE_TIME
*data
->sublines
[subline_idx
].time_mult
;
1742 return new_subline_refresh
;
1745 /* Display a line appropriately according to its alignment format.
1746 format_align contains the text, separated between left, center and right.
1747 line is the index of the line on the screen.
1748 scroll indicates whether the line is a scrolling one or not.
1750 static void write_line(struct screen
*display
,
1751 struct align_pos
*format_align
,
1756 int left_width
= 0, left_xpos
;
1757 int center_width
= 0, center_xpos
;
1758 int right_width
= 0, right_xpos
;
1764 /* calculate different string sizes and positions */
1765 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
1766 if (format_align
->left
!= 0) {
1767 display
->getstringsize((unsigned char *)format_align
->left
,
1768 &left_width
, &string_height
);
1771 if (format_align
->right
!= 0) {
1772 display
->getstringsize((unsigned char *)format_align
->right
,
1773 &right_width
, &string_height
);
1776 if (format_align
->center
!= 0) {
1777 display
->getstringsize((unsigned char *)format_align
->center
,
1778 ¢er_width
, &string_height
);
1782 right_xpos
= (display
->getwidth() - right_width
);
1783 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
1785 scroll_width
= display
->getwidth() - left_xpos
;
1787 /* Checks for overlapping strings.
1788 If needed the overlapping strings will be merged, separated by a
1791 /* CASE 1: left and centered string overlap */
1792 /* there is a left string, need to merge left and center */
1793 if ((left_width
!= 0 && center_width
!= 0) &&
1794 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1795 /* replace the former separator '\0' of left and
1796 center string with a space */
1797 *(--format_align
->center
) = ' ';
1798 /* calculate the new width and position of the merged string */
1799 left_width
= left_width
+ space_width
+ center_width
;
1800 /* there is no centered string anymore */
1803 /* there is no left string, move center to left */
1804 if ((left_width
== 0 && center_width
!= 0) &&
1805 (left_xpos
+ left_width
> center_xpos
)) {
1806 /* move the center string to the left string */
1807 format_align
->left
= format_align
->center
;
1808 /* calculate the new width and position of the string */
1809 left_width
= center_width
;
1810 /* there is no centered string anymore */
1814 /* CASE 2: centered and right string overlap */
1815 /* there is a right string, need to merge center and right */
1816 if ((center_width
!= 0 && right_width
!= 0) &&
1817 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1818 /* replace the former separator '\0' of center and
1819 right string with a space */
1820 *(--format_align
->right
) = ' ';
1821 /* move the center string to the right after merge */
1822 format_align
->right
= format_align
->center
;
1823 /* calculate the new width and position of the merged string */
1824 right_width
= center_width
+ space_width
+ right_width
;
1825 right_xpos
= (display
->getwidth() - right_width
);
1826 /* there is no centered string anymore */
1829 /* there is no right string, move center to right */
1830 if ((center_width
!= 0 && right_width
== 0) &&
1831 (center_xpos
+ center_width
> right_xpos
)) {
1832 /* move the center string to the right string */
1833 format_align
->right
= format_align
->center
;
1834 /* calculate the new width and position of the string */
1835 right_width
= center_width
;
1836 right_xpos
= (display
->getwidth() - right_width
);
1837 /* there is no centered string anymore */
1841 /* CASE 3: left and right overlap
1842 There is no center string anymore, either there never
1843 was one or it has been merged in case 1 or 2 */
1844 /* there is a left string, need to merge left and right */
1845 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1846 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1847 /* replace the former separator '\0' of left and
1848 right string with a space */
1849 *(--format_align
->right
) = ' ';
1850 /* calculate the new width and position of the string */
1851 left_width
= left_width
+ space_width
+ right_width
;
1852 /* there is no right string anymore */
1855 /* there is no left string, move right to left */
1856 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1857 (left_width
> right_xpos
)) {
1858 /* move the right string to the left string */
1859 format_align
->left
= format_align
->right
;
1860 /* calculate the new width and position of the string */
1861 left_width
= right_width
;
1862 /* there is no right string anymore */
1866 ypos
= (line
* string_height
);
1869 if (scroll
&& ((left_width
> scroll_width
) ||
1870 (center_width
> scroll_width
) ||
1871 (right_width
> scroll_width
)))
1873 display
->puts_scroll(0, line
,
1874 (unsigned char *)format_align
->left
);
1878 #ifdef HAVE_LCD_BITMAP
1879 /* clear the line first */
1880 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1881 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1882 display
->set_drawmode(DRMODE_SOLID
);
1885 /* Nasty hack: we output an empty scrolling string,
1886 which will reset the scroller for that line */
1887 display
->puts_scroll(0, line
, (unsigned char *)"");
1889 /* print aligned strings */
1890 if (left_width
!= 0)
1892 display
->putsxy(left_xpos
, ypos
,
1893 (unsigned char *)format_align
->left
);
1895 if (center_width
!= 0)
1897 display
->putsxy(center_xpos
, ypos
,
1898 (unsigned char *)format_align
->center
);
1900 if (right_width
!= 0)
1902 display
->putsxy(right_xpos
, ypos
,
1903 (unsigned char *)format_align
->right
);
1908 /* Refresh the WPS according to refresh_mode. */
1909 bool gui_wps_refresh(struct gui_wps
*gwps
,
1911 unsigned char refresh_mode
)
1913 struct wps_data
*data
= gwps
->data
;
1914 struct screen
*display
= gwps
->display
;
1915 struct wps_state
*state
= gwps
->state
;
1917 if(!gwps
|| !data
|| !state
|| !display
)
1920 int v
, line
, i
, subline_idx
;
1921 unsigned char flags
;
1922 char linebuf
[MAX_PATH
];
1923 unsigned char vp_refresh_mode
;
1925 struct align_pos align
;
1927 align
.center
= NULL
;
1930 bool update_line
, new_subline_refresh
;
1932 #ifdef HAVE_LCD_BITMAP
1933 gui_wps_statusbar_draw(gwps
, true);
1935 /* to find out wether the peak meter is enabled we
1936 assume it wasn't until we find a line that contains
1937 the peak meter. We can't use peak_meter_enabled itself
1938 because that would mean to turn off the meter thread
1939 temporarily. (That shouldn't matter unless yield
1940 or sleep is called but who knows...)
1942 bool enable_pm
= false;
1946 /* reset to first subline if refresh all flag is set */
1947 if (refresh_mode
== WPS_REFRESH_ALL
)
1949 display
->set_viewport(&data
->viewports
[0].vp
);
1950 display
->clear_viewport();
1952 for (i
= 0; i
<= data
->num_lines
; i
++)
1954 data
->lines
[i
].curr_subline
= SUBLINE_RESET
;
1958 #ifdef HAVE_LCD_CHARCELLS
1959 for (i
= 0; i
< 8; i
++)
1961 if (data
->wps_progress_pat
[i
] == 0)
1962 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1968 display
->stop_scroll();
1972 state
->ff_rewind_count
= ffwd_offset
;
1974 /* disable any viewports which are conditionally displayed */
1975 for (v
= 0; v
< data
->num_viewports
; v
++)
1977 if (data
->viewports
[v
].hidden_flags
&VP_DRAW_HIDEABLE
)
1979 if (data
->viewports
[v
].hidden_flags
&VP_DRAW_HIDDEN
)
1980 data
->viewports
[v
].hidden_flags
|= VP_DRAW_WASHIDDEN
;
1982 data
->viewports
[v
].hidden_flags
|= VP_DRAW_HIDDEN
;
1985 for (v
= 0; v
< data
->num_viewports
; v
++)
1987 display
->set_viewport(&data
->viewports
[v
].vp
);
1988 vp_refresh_mode
= refresh_mode
;
1990 #ifdef HAVE_LCD_BITMAP
1991 /* Set images to not to be displayed */
1992 for (i
= 0; i
< MAX_IMAGES
; i
++)
1994 data
->img
[i
].display
= -1;
1997 /* dont redraw the viewport if its disabled */
1998 if ((data
->viewports
[v
].hidden_flags
&VP_DRAW_HIDDEN
))
2000 if (!(data
->viewports
[v
].hidden_flags
&VP_DRAW_WASHIDDEN
))
2001 display
->scroll_stop(&data
->viewports
[v
].vp
);
2002 data
->viewports
[v
].hidden_flags
|= VP_DRAW_WASHIDDEN
;
2005 else if (((data
->viewports
[v
].hidden_flags
&
2006 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
2007 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
2009 vp_refresh_mode
= WPS_REFRESH_ALL
;
2010 data
->viewports
[v
].hidden_flags
= VP_DRAW_HIDEABLE
;
2012 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
2014 display
->clear_viewport();
2017 for (line
= data
->viewports
[v
].first_line
;
2018 line
<= data
->viewports
[v
].last_line
; line
++)
2020 memset(linebuf
, 0, sizeof(linebuf
));
2021 update_line
= false;
2023 /* get current subline for the line */
2024 new_subline_refresh
= update_curr_subline(gwps
, line
);
2026 subline_idx
= wps_subline_index(data
, line
,
2027 data
->lines
[line
].curr_subline
);
2028 flags
= data
->sublines
[subline_idx
].line_type
;
2030 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
2031 || new_subline_refresh
)
2033 /* get_line tells us if we need to update the line */
2034 update_line
= get_line(gwps
, line
, data
->lines
[line
].curr_subline
,
2035 &align
, linebuf
, sizeof(linebuf
));
2037 #ifdef HAVE_LCD_BITMAP
2039 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
2041 /* the peakmeter should be alone on its line */
2042 update_line
= false;
2044 int h
= font_get(data
->viewports
[v
].vp
.font
)->height
;
2045 int peak_meter_y
= (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
& vp_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
);
2073 /* conditionals clear the line which means if the %Vd is put into the default
2074 viewport there will be a blank line.
2075 To get around this we dont allow any actual drawing to happen in the
2076 deault vp if other vp's are defined */
2077 ((data
->num_viewports
>1 && v
!=0) || data
->num_viewports
== 1))
2079 if (flags
& WPS_REFRESH_SCROLL
)
2081 /* if the line is a scrolling one we don't want to update
2082 too often, so that it has the time to scroll */
2083 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
2084 write_line(display
, &align
, line
- data
->viewports
[v
].first_line
, true);
2087 write_line(display
, &align
, line
- data
->viewports
[v
].first_line
, false);
2091 #ifdef HAVE_LCD_BITMAP
2093 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
2095 if (data
->viewports
[v
].pb
)
2096 draw_progressbar(gwps
, data
->viewports
[v
].pb
);
2098 /* Now display any images in this viewport */
2099 wps_display_images(gwps
, &data
->viewports
[v
].vp
);
2103 #ifdef HAVE_LCD_BITMAP
2104 data
->peak_meter_enabled
= enable_pm
;
2107 /* Restore the default viewport */
2108 display
->set_viewport(NULL
);
2112 #ifdef HAVE_BACKLIGHT
2113 if (global_settings
.caption_backlight
&& state
->id3
)
2115 /* turn on backlight n seconds before track ends, and turn it off n
2116 seconds into the new track. n == backlight_timeout, or 5s */
2117 int n
= global_settings
.backlight_timeout
* 1000;
2120 n
= 5000; /* use 5s if backlight is always on or off */
2122 if (((state
->id3
->elapsed
< 1000) ||
2123 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2124 (state
->paused
== false))
2128 #ifdef HAVE_REMOTE_LCD
2129 if (global_settings
.remote_caption_backlight
&& state
->id3
)
2131 /* turn on remote backlight n seconds before track ends, and turn it
2132 off n seconds into the new track. n == remote_backlight_timeout,
2134 int n
= global_settings
.remote_backlight_timeout
* 1000;
2137 n
= 5000; /* use 5s if backlight is always on or off */
2139 if (((state
->id3
->elapsed
< 1000) ||
2140 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2141 (state
->paused
== false))
2142 remote_backlight_on();