1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Nicolas Pennequin
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "gwps-common.h"
26 #include "rbunicode.h"
31 #include "powermgmt.h"
34 #ifdef HAVE_LCD_CHARCELLS
38 #include "mp3_playback.h"
39 #include "backlight.h"
43 #include "scrollbar.h"
46 #ifdef HAVE_LCD_BITMAP
47 #include "peakmeter.h"
58 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
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 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))
83 /* fades the volume */
84 void fade(bool fade_in
)
86 int fp_global_vol
= global_settings
.volume
<< 8;
87 int fp_min_vol
= sound_min(SOUND_VOLUME
) << 8;
88 int fp_step
= (fp_global_vol
- fp_min_vol
) / 30;
92 int fp_volume
= fp_min_vol
;
94 /* zero out the sound */
95 sound_set_volume(fp_min_vol
>> 8);
97 sleep(HZ
/10); /* let audio thread run */
100 while (fp_volume
< fp_global_vol
- fp_step
) {
101 fp_volume
+= fp_step
;
102 sound_set_volume(fp_volume
>> 8);
105 sound_set_volume(global_settings
.volume
);
109 int fp_volume
= fp_global_vol
;
111 while (fp_volume
> fp_min_vol
+ fp_step
) {
112 fp_volume
-= fp_step
;
113 sound_set_volume(fp_volume
>> 8);
118 #if CONFIG_CODEC != SWCODEC
120 /* let audio thread run and wait for the mas to run out of data */
121 while (!mp3_pause_done())
126 /* reset volume to what it was before the fade */
127 sound_set_volume(global_settings
.volume
);
131 /* return true if screen restore is needed
132 return false otherwise
134 bool update_onvol_change(struct gui_wps
* gwps
)
136 gui_wps_statusbar_draw(gwps
, false);
137 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
139 #ifdef HAVE_LCD_CHARCELLS
140 gui_splash(gwps
->display
, 0, "Vol: %3d dB",
141 sound_val2phys(SOUND_VOLUME
, global_settings
.volume
));
147 bool ffwd_rew(int button
)
149 unsigned int step
= 0; /* current ff/rewind step */
150 unsigned int max_step
= 0; /* maximum ff/rewind step */
151 int ff_rewind_count
= 0; /* current ff/rewind count (in ticks) */
152 int direction
= -1; /* forward=1 or backward=-1 */
153 long accel_tick
= 0; /* next time at which to bump the step size */
158 if (button
== ACTION_NONE
)
160 status_set_ffmode(0);
167 case ACTION_WPS_SEEKFWD
:
169 case ACTION_WPS_SEEKBACK
:
170 if (wps_state
.ff_rewind
)
174 /* fast forwarding, calc max step relative to end */
175 max_step
= (wps_state
.id3
->length
-
176 (wps_state
.id3
->elapsed
+
178 FF_REWIND_MAX_PERCENT
/ 100;
182 /* rewinding, calc max step relative to start */
183 max_step
= (wps_state
.id3
->elapsed
+ ff_rewind_count
) *
184 FF_REWIND_MAX_PERCENT
/ 100;
187 max_step
= MAX(max_step
, MIN_FF_REWIND_STEP
);
192 ff_rewind_count
+= step
* direction
;
194 if (global_settings
.ff_rewind_accel
!= 0 &&
195 current_tick
>= accel_tick
)
198 accel_tick
= current_tick
+
199 global_settings
.ff_rewind_accel
*HZ
;
204 if ( (audio_status() & AUDIO_STATUS_PLAY
) &&
205 wps_state
.id3
&& wps_state
.id3
->length
)
207 if (!wps_state
.paused
)
208 #if (CONFIG_CODEC == SWCODEC)
209 audio_pre_ff_rewind();
213 #if CONFIG_KEYPAD == PLAYER_PAD
215 gui_wps
[i
].display
->stop_scroll();
218 status_set_ffmode(STATUS_FASTFORWARD
);
220 status_set_ffmode(STATUS_FASTBACKWARD
);
222 wps_state
.ff_rewind
= true;
224 step
= 1000 * global_settings
.ff_rewind_min_step
;
226 accel_tick
= current_tick
+
227 global_settings
.ff_rewind_accel
*HZ
;
234 if ((wps_state
.id3
->elapsed
+ ff_rewind_count
) >
235 wps_state
.id3
->length
)
236 ff_rewind_count
= wps_state
.id3
->length
-
237 wps_state
.id3
->elapsed
;
240 if ((int)(wps_state
.id3
->elapsed
+ ff_rewind_count
) < 0)
241 ff_rewind_count
= -wps_state
.id3
->elapsed
;
245 gui_wps_refresh(&gui_wps
[i
],
246 (wps_state
.wps_time_countup
== false)?
247 ff_rewind_count
:-ff_rewind_count
,
248 WPS_REFRESH_PLAYER_PROGRESS
|
249 WPS_REFRESH_DYNAMIC
);
253 case ACTION_WPS_STOPSEEK
:
254 wps_state
.id3
->elapsed
= wps_state
.id3
->elapsed
+ff_rewind_count
;
255 audio_ff_rewind(wps_state
.id3
->elapsed
);
257 wps_state
.ff_rewind
= false;
258 status_set_ffmode(0);
259 #if (CONFIG_CODEC != SWCODEC)
260 if (!wps_state
.paused
)
263 #ifdef HAVE_LCD_CHARCELLS
270 if(default_event_handler(button
) == SYS_USB_CONNECTED
) {
271 status_set_ffmode(0);
278 button
= get_action(CONTEXT_WPS
|ALLOW_SOFTLOCK
,TIMEOUT_BLOCK
);
283 bool gui_wps_display(void)
286 if (!wps_state
.id3
&& !(audio_status() & AUDIO_STATUS_PLAY
))
288 global_status
.resume_index
= -1;
289 #ifdef HAVE_LCD_BITMAP
290 gui_syncstatusbar_draw(&statusbars
, true);
292 gui_syncsplash(HZ
, ID2P(LANG_END_PLAYLIST
));
299 /* Update the values in the first (default) viewport - in case the user
300 has modified the statusbar or colour settings */
301 #ifdef HAVE_LCD_BITMAP
302 gui_wps
[i
].data
->viewports
[0].vp
.ymargin
= gui_wps
[i
].display
->getymargin();
304 if (gui_wps
[i
].display
->depth
> 1)
306 gui_wps
[i
].data
->viewports
[0].vp
.fg_pattern
= gui_wps
[i
].display
->get_foreground();
307 gui_wps
[i
].data
->viewports
[0].vp
.bg_pattern
= gui_wps
[i
].display
->get_background();
312 gui_wps
[i
].display
->clear_display();
313 if (!gui_wps
[i
].data
->wps_loaded
) {
314 if ( !gui_wps
[i
].data
->num_tokens
) {
315 /* set the default wps for the main-screen */
318 #ifdef HAVE_LCD_BITMAP
320 unload_wps_backdrop();
322 wps_data_load(gui_wps
[i
].data
,
324 "%s%?it<%?in<%in. |>%it|%fn>\n"
325 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
326 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
328 "%al%pc/%pt%ar[%pp:%pe]\n"
329 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
333 wps_data_load(gui_wps
[i
].data
,
335 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
336 "%pc%?ps<*|/>%pt\n", false);
340 /* set the default wps for the remote-screen */
343 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
344 unload_remote_wps_backdrop();
346 wps_data_load(gui_wps
[i
].data
,
348 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
349 "%s%?it<%?in<%in. |>%it|%fn>\n"
350 "%al%pc/%pt%ar[%pp:%pe]\n"
351 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
362 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_ALL
);
367 bool update(struct gui_wps
*gwps
)
369 bool track_changed
= audio_has_changed_track();
370 bool retcode
= false;
372 gwps
->state
->nid3
= audio_next_track();
375 gwps
->display
->stop_scroll();
376 gwps
->state
->id3
= audio_current_track();
378 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
379 && strcmp(gwps
->state
->id3
->path
, curr_cue
->audio_filename
))
381 /* the current cuesheet isn't the right one any more */
383 if (!strcmp(gwps
->state
->id3
->path
, temp_cue
->audio_filename
)) {
384 /* We have the new cuesheet in memory (temp_cue),
385 let's make it the current one ! */
386 memcpy(curr_cue
, temp_cue
, sizeof(struct cuesheet
));
389 /* We need to parse the new cuesheet */
391 char cuepath
[MAX_PATH
];
393 if (look_for_cuesheet_file(gwps
->state
->id3
->path
, cuepath
) &&
394 parse_cuesheet(cuepath
, curr_cue
))
396 gwps
->state
->id3
->cuesheet_type
= 1;
397 strcpy(curr_cue
->audio_filename
, gwps
->state
->id3
->path
);
401 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
404 if (gui_wps_display())
407 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
410 if (gwps
->state
->id3
)
412 strncpy(gwps
->state
->current_track_path
, gwps
->state
->id3
->path
,
413 sizeof(gwps
->state
->current_track_path
));
414 gwps
->state
->current_track_path
[sizeof(gwps
->state
->current_track_path
)-1] = '\0';
418 if (gwps
->state
->id3
)
420 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
421 && (gwps
->state
->id3
->elapsed
< curr_cue
->curr_track
->offset
422 || (curr_cue
->curr_track_idx
< curr_cue
->track_count
- 1
423 && gwps
->state
->id3
->elapsed
>= (curr_cue
->curr_track
+1)->offset
)))
425 /* We've changed tracks within the cuesheet :
426 we need to update the ID3 info and refresh the WPS */
428 cue_find_current_track(curr_cue
, gwps
->state
->id3
->elapsed
);
429 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
431 gwps
->display
->stop_scroll();
432 if (gui_wps_display())
435 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
438 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
441 gui_wps_statusbar_draw(gwps
, false);
447 void display_keylock_text(bool locked
)
452 gui_wps
[i
].display
->stop_scroll();
455 s
= str(LANG_KEYLOCK_ON
);
457 s
= str(LANG_KEYLOCK_OFF
);
458 gui_syncsplash(HZ
, s
);
461 #ifdef HAVE_LCD_BITMAP
463 static void draw_progressbar(struct gui_wps
*gwps
, int line
)
465 struct wps_data
*data
= gwps
->data
;
466 struct screen
*display
= gwps
->display
;
467 struct wps_state
*state
= gwps
->state
;
468 int h
= font_get(display
->getfont())->height
;
471 if (data
->progress_top
< 0)
472 sb_y
= line
*h
+ display
->getymargin() +
473 ((h
> data
->progress_height
+ 1)
474 ? (h
- data
->progress_height
) / 2 : 1);
476 sb_y
= data
->progress_top
;
478 if (!data
->progress_end
)
479 data
->progress_end
=display
->getwidth();
481 if (gwps
->data
->progressbar
.have_bitmap_pb
)
482 gui_bitmap_scrollbar_draw(display
, data
->progressbar
.bm
,
483 data
->progress_start
, sb_y
,
484 data
->progress_end
-data
->progress_start
,
485 data
->progressbar
.bm
.height
,
486 state
->id3
->length
? state
->id3
->length
: 1, 0,
487 state
->id3
->length
? state
->id3
->elapsed
488 + state
->ff_rewind_count
: 0,
491 gui_scrollbar_draw(display
, data
->progress_start
, sb_y
,
492 data
->progress_end
-data
->progress_start
,
493 data
->progress_height
,
494 state
->id3
->length
? state
->id3
->length
: 1, 0,
495 state
->id3
->length
? state
->id3
->elapsed
496 + state
->ff_rewind_count
: 0,
499 #ifdef AB_REPEAT_ENABLE
500 if ( ab_repeat_mode_enabled() && state
->id3
->length
!= 0 )
501 ab_draw_markers(display
, state
->id3
->length
,
502 data
->progress_start
, data
->progress_end
, sb_y
,
503 data
->progress_height
);
506 if ( cuesheet_is_enabled() && state
->id3
->cuesheet_type
)
507 cue_draw_markers(display
, state
->id3
->length
,
508 data
->progress_start
, data
->progress_end
,
509 sb_y
+1, data
->progress_height
-2);
512 /* clears the area where the image was shown */
513 static void clear_image_pos(struct gui_wps
*gwps
, int n
)
517 struct wps_data
*data
= gwps
->data
;
518 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
519 gwps
->display
->fillrect(data
->img
[n
].x
, data
->img
[n
].y
,
520 data
->img
[n
].bm
.width
, data
->img
[n
].subimage_height
);
521 gwps
->display
->set_drawmode(DRMODE_SOLID
);
524 static void wps_draw_image(struct gui_wps
*gwps
, int n
, int subimage
)
526 struct screen
*display
= gwps
->display
;
527 struct wps_data
*data
= gwps
->data
;
528 if(data
->img
[n
].always_display
)
529 display
->set_drawmode(DRMODE_FG
);
531 display
->set_drawmode(DRMODE_SOLID
);
534 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
536 display
->mono_bitmap_part(data
->img
[n
].bm
.data
,
537 0, data
->img
[n
].subimage_height
* subimage
,
538 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
539 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
540 data
->img
[n
].subimage_height
);
543 display
->transparent_bitmap_part((fb_data
*)data
->img
[n
].bm
.data
,
544 0, data
->img
[n
].subimage_height
* subimage
,
545 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
546 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
547 data
->img
[n
].subimage_height
);
552 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
554 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
558 struct wps_data
*data
= gwps
->data
;
559 struct screen
*display
= gwps
->display
;
561 for (n
= 0; n
< MAX_IMAGES
; n
++)
563 if (data
->img
[n
].loaded
)
565 if (data
->img
[n
].display
>= 0)
567 wps_draw_image(gwps
, n
, data
->img
[n
].display
);
568 } else if (data
->img
[n
].always_display
&& data
->img
[n
].vp
== vp
)
570 wps_draw_image(gwps
, n
, 0);
574 display
->set_drawmode(DRMODE_SOLID
);
577 #else /* HAVE_LCD_CHARCELL */
579 static bool draw_player_progress(struct gui_wps
*gwps
)
581 struct wps_state
*state
= gwps
->state
;
582 struct screen
*display
= gwps
->display
;
583 unsigned char progress_pattern
[7];
590 if (state
->id3
->length
)
591 pos
= 36 * (state
->id3
->elapsed
+ state
->ff_rewind_count
)
592 / state
->id3
->length
;
594 for (i
= 0; i
< 7; i
++, pos
-= 5)
597 progress_pattern
[i
] = 0x1f;
599 progress_pattern
[i
] = 0x00;
601 progress_pattern
[i
] = 0x1f >> pos
;
604 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
608 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
610 static const unsigned char numbers
[10][4] = {
611 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
612 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
613 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
614 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
615 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
616 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
617 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
618 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
619 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
620 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
623 struct wps_state
*state
= gwps
->state
;
624 struct screen
*display
= gwps
->display
;
625 struct wps_data
*data
= gwps
->data
;
626 unsigned char progress_pattern
[7];
635 if (!state
->id3
|| buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
638 time
= state
->id3
->elapsed
+ state
->ff_rewind_count
;
639 if (state
->id3
->length
)
640 pos
= 55 * time
/ state
->id3
->length
;
642 memset(timestr
, 0, sizeof(timestr
));
643 format_time(timestr
, sizeof(timestr
)-2, time
);
644 timestr
[strlen(timestr
)] = ':'; /* always safe */
646 for (i
= 0; i
< 11; i
++, pos
-= 5)
649 memset(progress_pattern
, 0, sizeof(progress_pattern
));
651 if ((digit
= timestr
[time_idx
]))
656 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
658 memcpy(progress_pattern
, numbers
[digit
], 4);
661 else /* tens, shifted right */
663 for (j
= 0; j
< 4; j
++)
664 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
666 if (time_idx
> 0) /* not the first group, add colon in front */
668 progress_pattern
[1] |= 0x10;
669 progress_pattern
[3] |= 0x10;
675 progress_pattern
[5] = progress_pattern
[6] = 0x1f;
678 if (pos
> 0 && pos
< 5)
681 progress_pattern
[5] = progress_pattern
[6] = (~0x1f >> pos
) & 0x1f;
684 if (softchar
&& pat_idx
< 8)
686 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
688 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
692 buf
= utf8encode(' ', buf
);
694 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
699 #endif /* HAVE_LCD_CHARCELL */
701 /* Extract a part from a path.
703 * buf - buffer extract part to.
704 * buf_size - size of buffer.
705 * path - path to extract from.
706 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
707 * parent of parent, etc.
709 * Returns buf if the desired level was found, NULL otherwise.
711 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
714 const char* last_sep
;
717 sep
= path
+ strlen(path
);
732 if (level
|| (last_sep
<= sep
))
735 len
= MIN(last_sep
- sep
, buf_size
- 1);
736 strncpy(buf
, sep
+ 1, len
);
741 /* Return the tag found at index i and write its value in buf.
742 The return value is buf if the tag had a value, or NULL if not.
744 intval is used with conditionals/enums: when this function is called,
745 intval should contain the number of options in the conditional/enum.
746 When this function returns, intval is -1 if the tag is non numeric or,
747 if the tag is numeric, intval is the enum case we want to go to.
748 When not treating a conditional/enum, intval should be NULL.
750 static char *get_token_value(struct gui_wps
*gwps
,
751 struct wps_token
*token
,
752 char *buf
, int buf_size
,
758 struct wps_data
*data
= gwps
->data
;
759 struct wps_state
*state
= gwps
->state
;
764 struct mp3entry
*id3
;
775 struct tm
* tm
= NULL
;
777 /* if the token is an RTC one, update the time
778 and do the necessary checks */
779 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
780 && token
->type
<= WPS_TOKENS_RTC_END
)
798 case WPS_TOKEN_CHARACTER
:
799 return &(token
->value
.c
);
801 case WPS_TOKEN_STRING
:
802 return data
->strings
[token
->value
.i
];
804 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
805 format_time(buf
, buf_size
,
806 id3
->elapsed
+ state
->ff_rewind_count
);
809 case WPS_TOKEN_TRACK_TIME_REMAINING
:
810 format_time(buf
, buf_size
,
811 id3
->length
- id3
->elapsed
-
812 state
->ff_rewind_count
);
815 case WPS_TOKEN_TRACK_LENGTH
:
816 format_time(buf
, buf_size
, id3
->length
);
819 case WPS_TOKEN_PLAYLIST_ENTRIES
:
820 snprintf(buf
, buf_size
, "%d", playlist_amount());
823 case WPS_TOKEN_PLAYLIST_NAME
:
824 return playlist_name(NULL
, buf
, buf_size
);
826 case WPS_TOKEN_PLAYLIST_POSITION
:
827 snprintf(buf
, buf_size
, "%d",
828 playlist_get_display_index());
831 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
832 if ( global_settings
.playlist_shuffle
)
838 case WPS_TOKEN_VOLUME
:
839 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
842 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
846 else if (global_settings
.volume
== 0)
850 else if (global_settings
.volume
> 0)
856 *intval
= (limit
- 3) * (global_settings
.volume
857 - sound_min(SOUND_VOLUME
) - 1)
858 / (-1 - sound_min(SOUND_VOLUME
)) + 2;
863 case WPS_TOKEN_TRACK_ELAPSED_PERCENT
:
864 if (id3
->length
<= 0)
869 *intval
= limit
* (id3
->elapsed
+ state
->ff_rewind_count
)
872 snprintf(buf
, buf_size
, "%d",
873 100*(id3
->elapsed
+ state
->ff_rewind_count
) / id3
->length
);
876 case WPS_TOKEN_METADATA_ARTIST
:
879 case WPS_TOKEN_METADATA_COMPOSER
:
880 return id3
->composer
;
882 case WPS_TOKEN_METADATA_ALBUM
:
885 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
886 return id3
->albumartist
;
888 case WPS_TOKEN_METADATA_GROUPING
:
889 return id3
->grouping
;
891 case WPS_TOKEN_METADATA_GENRE
:
892 return id3
->genre_string
;
894 case WPS_TOKEN_METADATA_DISC_NUMBER
:
895 if (id3
->disc_string
)
896 return id3
->disc_string
;
898 snprintf(buf
, buf_size
, "%d", id3
->discnum
);
903 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
904 if (id3
->track_string
)
905 return id3
->track_string
;
908 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
913 case WPS_TOKEN_METADATA_TRACK_TITLE
:
916 case WPS_TOKEN_METADATA_VERSION
:
917 switch (id3
->id3version
)
938 case WPS_TOKEN_METADATA_YEAR
:
939 if( id3
->year_string
)
940 return id3
->year_string
;
943 snprintf(buf
, buf_size
, "%d", id3
->year
);
948 case WPS_TOKEN_METADATA_COMMENT
:
952 case WPS_TOKEN_ALBUMART_DISPLAY
:
953 draw_album_art(gwps
, audio_current_aa_hid(), false);
956 case WPS_TOKEN_ALBUMART_FOUND
:
957 if (audio_current_aa_hid() >= 0) {
958 snprintf(buf
, buf_size
, "C");
964 case WPS_TOKEN_FILE_BITRATE
:
966 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
968 snprintf(buf
, buf_size
, "?");
971 case WPS_TOKEN_FILE_CODEC
:
974 if(id3
->codectype
== AFMT_UNKNOWN
)
975 *intval
= AFMT_NUM_CODECS
;
977 *intval
= id3
->codectype
;
979 return id3_get_codec(id3
);
981 case WPS_TOKEN_FILE_FREQUENCY
:
982 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
985 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
986 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
987 if ((id3
->frequency
% 1000) < 100)
988 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
990 snprintf(buf
, buf_size
, "%ld.%d",
991 id3
->frequency
/ 1000,
992 (id3
->frequency
% 1000) / 100);
995 case WPS_TOKEN_FILE_NAME
:
996 if (get_dir(buf
, buf_size
, id3
->path
, 0)) {
997 /* Remove extension */
998 char* sep
= strrchr(buf
, '.');
1008 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
1009 return get_dir(buf
, buf_size
, id3
->path
, 0);
1011 case WPS_TOKEN_FILE_PATH
:
1014 case WPS_TOKEN_FILE_SIZE
:
1015 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
1018 case WPS_TOKEN_FILE_VBR
:
1019 return id3
->vbr
? "(avg)" : NULL
;
1021 case WPS_TOKEN_FILE_DIRECTORY
:
1022 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
1024 case WPS_TOKEN_BATTERY_PERCENT
:
1026 int l
= battery_level();
1030 limit
= MAX(limit
, 2);
1032 /* First enum is used for "unknown level". */
1033 *intval
= (limit
- 1) * l
/ 100 + 2;
1040 snprintf(buf
, buf_size
, "%d", l
);
1047 case WPS_TOKEN_BATTERY_VOLTS
:
1049 unsigned int v
= battery_voltage();
1050 snprintf(buf
, buf_size
, "%d.%02d", v
/ 1000, (v
% 1000) / 10);
1054 case WPS_TOKEN_BATTERY_TIME
:
1056 int t
= battery_time();
1058 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
1060 strncpy(buf
, "?h ?m", buf_size
);
1065 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
1067 if(charger_input_state
==CHARGER
)
1073 #if CONFIG_CHARGING >= CHARGING_MONITOR
1074 case WPS_TOKEN_BATTERY_CHARGING
:
1076 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
1083 case WPS_TOKEN_BATTERY_SLEEPTIME
:
1085 if (get_sleep_timer() == 0)
1089 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
1094 case WPS_TOKEN_PLAYBACK_STATUS
:
1096 int status
= audio_status();
1098 if (status
== AUDIO_STATUS_PLAY
&& \
1099 !(status
& AUDIO_STATUS_PAUSE
))
1101 if (audio_status() & AUDIO_STATUS_PAUSE
&& \
1102 (! status_get_ffmode()))
1104 if (status_get_ffmode() == STATUS_FASTFORWARD
)
1106 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
1113 snprintf(buf
, buf_size
, "%d", mode
);
1117 case WPS_TOKEN_REPEAT_MODE
:
1119 *intval
= global_settings
.repeat_mode
+ 1;
1120 snprintf(buf
, buf_size
, "%d", *intval
);
1122 case WPS_TOKEN_RTC_12HOUR_CFG
:
1124 *intval
= global_settings
.timeformat
+ 1;
1125 snprintf(buf
, buf_size
, "%d", *intval
);
1128 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1129 /* d: day of month (01..31) */
1130 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
1133 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1134 /* e: day of month, blank padded ( 1..31) */
1135 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
1138 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1139 /* H: hour (00..23) */
1140 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
1143 case WPS_TOKEN_RTC_HOUR_24
:
1144 /* k: hour ( 0..23) */
1145 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
1148 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1149 /* I: hour (01..12) */
1150 snprintf(buf
, buf_size
, "%02d",
1151 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1154 case WPS_TOKEN_RTC_HOUR_12
:
1155 /* l: hour ( 1..12) */
1156 snprintf(buf
, buf_size
, "%2d",
1157 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1160 case WPS_TOKEN_RTC_MONTH
:
1161 /* m: month (01..12) */
1163 *intval
= tm
->tm_mon
+ 1;
1164 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
1167 case WPS_TOKEN_RTC_MINUTE
:
1168 /* M: minute (00..59) */
1169 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
1172 case WPS_TOKEN_RTC_SECOND
:
1173 /* S: second (00..59) */
1174 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
1177 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1178 /* y: last two digits of year (00..99) */
1179 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
1182 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1183 /* Y: year (1970...) */
1184 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
1187 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1188 /* p: upper case AM or PM indicator */
1189 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "AM" : "PM");
1192 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1193 /* P: lower case am or pm indicator */
1194 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "am" : "pm");
1197 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1198 /* a: abbreviated weekday name (Sun..Sat) */
1199 snprintf(buf
, buf_size
, "%s",str(LANG_WEEKDAY_SUNDAY
+ tm
->tm_wday
));
1202 case WPS_TOKEN_RTC_MONTH_NAME
:
1203 /* b: abbreviated month name (Jan..Dec) */
1204 snprintf(buf
, buf_size
, "%s",str(LANG_MONTH_JANUARY
+ tm
->tm_mon
));
1207 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1208 /* u: day of week (1..7); 1 is Monday */
1210 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
1211 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
1214 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1215 /* w: day of week (0..6); 0 is Sunday */
1217 *intval
= tm
->tm_wday
+ 1;
1218 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
1221 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1222 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1223 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1224 case WPS_TOKEN_RTC_HOUR_24
:
1225 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1226 case WPS_TOKEN_RTC_HOUR_12
:
1227 case WPS_TOKEN_RTC_MONTH
:
1228 case WPS_TOKEN_RTC_MINUTE
:
1229 case WPS_TOKEN_RTC_SECOND
:
1230 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1231 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1232 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1233 strncpy(buf
, "--", buf_size
);
1235 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1236 strncpy(buf
, "----", buf_size
);
1238 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1239 case WPS_TOKEN_RTC_MONTH_NAME
:
1240 strncpy(buf
, "---", buf_size
);
1242 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1243 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1244 strncpy(buf
, "-", buf_size
);
1248 #ifdef HAVE_LCD_CHARCELLS
1249 case WPS_TOKEN_PROGRESSBAR
:
1251 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
1256 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
1259 /* we need 11 characters (full line) for
1261 snprintf(buf
, buf_size
, " ");
1265 /* Tell the user if we have an OldPlayer */
1266 snprintf(buf
, buf_size
, " <Old LCD> ");
1271 #ifdef HAVE_TAGCACHE
1272 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
1274 *intval
= id3
->playcount
+ 1;
1276 snprintf(buf
, buf_size
, "%ld", id3
->playcount
);
1279 case WPS_TOKEN_DATABASE_RATING
:
1281 *intval
= id3
->rating
+ 1;
1283 snprintf(buf
, buf_size
, "%d", id3
->rating
);
1286 case WPS_TOKEN_DATABASE_AUTOSCORE
:
1288 *intval
= id3
->score
+ 1;
1290 snprintf(buf
, buf_size
, "%d", id3
->score
);
1294 #if (CONFIG_CODEC == SWCODEC)
1295 case WPS_TOKEN_CROSSFADE
:
1297 *intval
= global_settings
.crossfade
+ 1;
1298 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
1301 case WPS_TOKEN_REPLAYGAIN
:
1305 if (global_settings
.replaygain
== 0)
1310 get_replaygain_mode(id3
->track_gain_string
!= NULL
,
1311 id3
->album_gain_string
!= NULL
);
1313 val
= 6; /* no tag */
1317 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
1332 strncpy(buf
, id3
->track_gain_string
, buf_size
);
1336 strncpy(buf
, id3
->album_gain_string
, buf_size
);
1341 #endif /* (CONFIG_CODEC == SWCODEC) */
1343 #if (CONFIG_CODEC != MAS3507D)
1344 case WPS_TOKEN_SOUND_PITCH
:
1346 int val
= sound_get_pitch();
1347 snprintf(buf
, buf_size
, "%d.%d",
1348 val
/ 10, val
% 10);
1353 case WPS_TOKEN_MAIN_HOLD
:
1354 #ifdef HAS_BUTTON_HOLD
1357 if (is_keys_locked())
1358 #endif /*hold switch or softlock*/
1363 #ifdef HAS_REMOTE_BUTTON_HOLD
1364 case WPS_TOKEN_REMOTE_HOLD
:
1365 if (remote_button_hold())
1371 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
1372 case WPS_TOKEN_VLED_HDD
:
1379 #ifdef HAVE_LCD_BITMAP
1380 case WPS_TOKEN_LEFTMARGIN
:
1381 gwps
->display
->setmargins(token
->value
.i
,
1382 gwps
->display
->getymargin());
1391 /* Return the index to the end token for the conditional token at index.
1392 The conditional token can be either a start token or a separator
1393 (i.e. option) token.
1395 static int find_conditional_end(struct wps_data
*data
, int index
)
1398 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
1399 ret
= data
->tokens
[ret
].value
.i
;
1401 /* ret now is the index to the end token for the conditional. */
1405 /* Return the index of the appropriate case for the conditional
1406 that starts at cond_index.
1408 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
1413 struct wps_data
*data
= gwps
->data
;
1416 int cond_index
= *token_index
;
1417 char result
[128], *value
;
1418 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
1419 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
1421 /* treat ?xx<true> constructs as if they had 2 options. */
1422 if (num_options
< 2)
1425 int intval
= num_options
;
1426 /* get_token_value needs to know the number of options in the enum */
1427 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
1428 result
, sizeof(result
), &intval
);
1430 /* intval is now the number of the enum option we want to read,
1431 starting from 1. If intval is -1, we check if value is empty. */
1433 intval
= (value
&& *value
) ? 1 : num_options
;
1434 else if (intval
> num_options
|| intval
< 1)
1435 intval
= num_options
;
1437 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
1439 /* skip to the right enum case */
1440 int next
= cond_index
+ 2;
1441 for (i
= 1; i
< intval
; i
++)
1443 next
= data
->tokens
[next
].value
.i
;
1445 *token_index
= next
;
1447 if (prev_val
== intval
)
1449 /* Same conditional case as previously. Return without clearing the
1454 cond_end
= find_conditional_end(data
, cond_index
+ 2);
1455 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
1457 #ifdef HAVE_LCD_BITMAP
1458 /* clear all pictures in the conditional and nested ones */
1459 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
1460 clear_image_pos(gwps
, data
->tokens
[i
].value
.i
& 0xFF);
1462 #ifdef HAVE_ALBUMART
1463 if (data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
1464 draw_album_art(gwps
, audio_current_aa_hid(), true);
1471 /* Read a (sub)line to the given alignment format buffer.
1472 linebuf is the buffer where the data is actually stored.
1473 align is the alignment format that'll be used to display the text.
1474 The return value indicates whether the line needs to be updated.
1476 static bool get_line(struct gui_wps
*gwps
,
1477 int line
, int subline
,
1478 struct align_pos
*align
,
1482 struct wps_data
*data
= gwps
->data
;
1485 char *buf
= linebuf
; /* will always point to the writing position */
1486 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
1487 int i
, last_token_idx
;
1488 bool update
= false;
1490 /* alignment-related variables */
1492 char* cur_align_start
;
1493 cur_align_start
= buf
;
1494 cur_align
= WPS_ALIGN_LEFT
;
1496 align
->center
= NULL
;
1497 align
->right
= NULL
;
1499 #ifdef HAVE_LCD_BITMAP
1500 /* Reset margins - only bitmap targets modify them */
1501 gwps
->display
->setmargins(0, gwps
->display
->getymargin());
1504 /* Process all tokens of the desired subline */
1505 last_token_idx
= wps_last_token_index(data
, line
, subline
);
1506 for (i
= wps_first_token_index(data
, line
, subline
);
1507 i
<= last_token_idx
; i
++)
1509 switch(data
->tokens
[i
].type
)
1511 case WPS_TOKEN_CONDITIONAL
:
1512 /* place ourselves in the right conditional case */
1513 update
|= evaluate_conditional(gwps
, &i
);
1516 case WPS_TOKEN_CONDITIONAL_OPTION
:
1517 /* we've finished in the curent conditional case,
1518 skip to the end of the conditional structure */
1519 i
= find_conditional_end(data
, i
);
1522 #ifdef HAVE_LCD_BITMAP
1523 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1525 struct gui_img
*img
= data
->img
;
1526 int n
= data
->tokens
[i
].value
.i
& 0xFF;
1527 int subimage
= data
->tokens
[i
].value
.i
>> 8;
1529 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
)
1530 img
[n
].display
= subimage
;
1535 case WPS_TOKEN_ALIGN_LEFT
:
1536 case WPS_TOKEN_ALIGN_CENTER
:
1537 case WPS_TOKEN_ALIGN_RIGHT
:
1538 /* remember where the current aligned text started */
1541 case WPS_ALIGN_LEFT
:
1542 align
->left
= cur_align_start
;
1545 case WPS_ALIGN_CENTER
:
1546 align
->center
= cur_align_start
;
1549 case WPS_ALIGN_RIGHT
:
1550 align
->right
= cur_align_start
;
1553 /* start a new alignment */
1554 switch (data
->tokens
[i
].type
)
1556 case WPS_TOKEN_ALIGN_LEFT
:
1557 cur_align
= WPS_ALIGN_LEFT
;
1559 case WPS_TOKEN_ALIGN_CENTER
:
1560 cur_align
= WPS_ALIGN_CENTER
;
1562 case WPS_TOKEN_ALIGN_RIGHT
:
1563 cur_align
= WPS_ALIGN_RIGHT
;
1569 cur_align_start
= buf
;
1574 /* get the value of the tag and copy it to the buffer */
1575 char *value
= get_token_value(gwps
, &data
->tokens
[i
],
1576 temp_buf
, sizeof(temp_buf
), NULL
);
1580 while (*value
&& (buf
< linebuf_end
))
1588 /* close the current alignment */
1591 case WPS_ALIGN_LEFT
:
1592 align
->left
= cur_align_start
;
1595 case WPS_ALIGN_CENTER
:
1596 align
->center
= cur_align_start
;
1599 case WPS_ALIGN_RIGHT
:
1600 align
->right
= cur_align_start
;
1607 static void get_subline_timeout(struct gui_wps
*gwps
, int line
, int subline
)
1609 struct wps_data
*data
= gwps
->data
;
1611 int subline_idx
= wps_subline_index(data
, line
, subline
);
1612 int last_token_idx
= wps_last_token_index(data
, line
, subline
);
1614 data
->sublines
[subline_idx
].time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1616 for (i
= wps_first_token_index(data
, line
, subline
);
1617 i
<= last_token_idx
; i
++)
1619 switch(data
->tokens
[i
].type
)
1621 case WPS_TOKEN_CONDITIONAL
:
1622 /* place ourselves in the right conditional case */
1623 evaluate_conditional(gwps
, &i
);
1626 case WPS_TOKEN_CONDITIONAL_OPTION
:
1627 /* we've finished in the curent conditional case,
1628 skip to the end of the conditional structure */
1629 i
= find_conditional_end(data
, i
);
1632 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1633 data
->sublines
[subline_idx
].time_mult
= data
->tokens
[i
].value
.i
;
1642 /* Calculates which subline should be displayed for the specified line
1643 Returns true iff the subline must be refreshed */
1644 static bool update_curr_subline(struct gui_wps
*gwps
, int line
)
1646 struct wps_data
*data
= gwps
->data
;
1648 int search
, search_start
, num_sublines
;
1650 bool new_subline_refresh
;
1651 bool only_one_subline
;
1653 num_sublines
= data
->lines
[line
].num_sublines
;
1654 reset_subline
= (data
->lines
[line
].curr_subline
== SUBLINE_RESET
);
1655 new_subline_refresh
= false;
1656 only_one_subline
= false;
1658 /* if time to advance to next sub-line */
1659 if (TIME_AFTER(current_tick
, data
->lines
[line
].subline_expire_time
- 1) ||
1662 /* search all sublines until the next subline with time > 0
1663 is found or we get back to the subline we started with */
1667 search_start
= data
->lines
[line
].curr_subline
;
1669 for (search
= 0; search
< num_sublines
; search
++)
1671 data
->lines
[line
].curr_subline
++;
1673 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1674 if (data
->lines
[line
].curr_subline
== num_sublines
)
1676 if (data
->lines
[line
].curr_subline
== 1)
1677 only_one_subline
= true;
1678 data
->lines
[line
].curr_subline
= 0;
1681 /* if back where we started after search or
1682 only one subline is defined on the line */
1683 if (((search
> 0) &&
1684 (data
->lines
[line
].curr_subline
== search_start
)) ||
1687 /* no other subline with a time > 0 exists */
1688 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1690 data
->lines
[line
].subline_expire_time
) + 100 * HZ
;
1695 /* get initial time multiplier for this subline */
1696 get_subline_timeout(gwps
, line
, data
->lines
[line
].curr_subline
);
1698 int subline_idx
= wps_subline_index(data
, line
,
1699 data
->lines
[line
].curr_subline
);
1701 /* only use this subline if subline time > 0 */
1702 if (data
->sublines
[subline_idx
].time_mult
> 0)
1704 new_subline_refresh
= true;
1705 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1706 current_tick
: data
->lines
[line
].subline_expire_time
) +
1707 BASE_SUBLINE_TIME
*data
->sublines
[subline_idx
].time_mult
;
1714 return new_subline_refresh
;
1717 /* Display a line appropriately according to its alignment format.
1718 format_align contains the text, separated between left, center and right.
1719 line is the index of the line on the screen.
1720 scroll indicates whether the line is a scrolling one or not.
1722 static void write_line(struct screen
*display
,
1723 struct align_pos
*format_align
,
1728 int left_width
= 0, left_xpos
;
1729 int center_width
= 0, center_xpos
;
1730 int right_width
= 0, right_xpos
;
1736 /* calculate different string sizes and positions */
1737 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
1738 if (format_align
->left
!= 0) {
1739 display
->getstringsize((unsigned char *)format_align
->left
,
1740 &left_width
, &string_height
);
1743 if (format_align
->right
!= 0) {
1744 display
->getstringsize((unsigned char *)format_align
->right
,
1745 &right_width
, &string_height
);
1748 if (format_align
->center
!= 0) {
1749 display
->getstringsize((unsigned char *)format_align
->center
,
1750 ¢er_width
, &string_height
);
1753 left_xpos
= display
->getxmargin();
1754 right_xpos
= (display
->getwidth() - right_width
);
1755 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
1757 scroll_width
= display
->getwidth() - left_xpos
;
1759 /* Checks for overlapping strings.
1760 If needed the overlapping strings will be merged, separated by a
1763 /* CASE 1: left and centered string overlap */
1764 /* there is a left string, need to merge left and center */
1765 if ((left_width
!= 0 && center_width
!= 0) &&
1766 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1767 /* replace the former separator '\0' of left and
1768 center string with a space */
1769 *(--format_align
->center
) = ' ';
1770 /* calculate the new width and position of the merged string */
1771 left_width
= left_width
+ space_width
+ center_width
;
1772 /* there is no centered string anymore */
1775 /* there is no left string, move center to left */
1776 if ((left_width
== 0 && center_width
!= 0) &&
1777 (left_xpos
+ left_width
> center_xpos
)) {
1778 /* move the center string to the left string */
1779 format_align
->left
= format_align
->center
;
1780 /* calculate the new width and position of the string */
1781 left_width
= center_width
;
1782 /* there is no centered string anymore */
1786 /* CASE 2: centered and right string overlap */
1787 /* there is a right string, need to merge center and right */
1788 if ((center_width
!= 0 && right_width
!= 0) &&
1789 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1790 /* replace the former separator '\0' of center and
1791 right string with a space */
1792 *(--format_align
->right
) = ' ';
1793 /* move the center string to the right after merge */
1794 format_align
->right
= format_align
->center
;
1795 /* calculate the new width and position of the merged string */
1796 right_width
= center_width
+ space_width
+ right_width
;
1797 right_xpos
= (display
->getwidth() - right_width
);
1798 /* there is no centered string anymore */
1801 /* there is no right string, move center to right */
1802 if ((center_width
!= 0 && right_width
== 0) &&
1803 (center_xpos
+ center_width
> right_xpos
)) {
1804 /* move the center string to the right string */
1805 format_align
->right
= format_align
->center
;
1806 /* calculate the new width and position of the string */
1807 right_width
= center_width
;
1808 right_xpos
= (display
->getwidth() - right_width
);
1809 /* there is no centered string anymore */
1813 /* CASE 3: left and right overlap
1814 There is no center string anymore, either there never
1815 was one or it has been merged in case 1 or 2 */
1816 /* there is a left string, need to merge left and right */
1817 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1818 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1819 /* replace the former separator '\0' of left and
1820 right string with a space */
1821 *(--format_align
->right
) = ' ';
1822 /* calculate the new width and position of the string */
1823 left_width
= left_width
+ space_width
+ right_width
;
1824 /* there is no right string anymore */
1827 /* there is no left string, move right to left */
1828 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1829 (left_width
> right_xpos
)) {
1830 /* move the right string to the left string */
1831 format_align
->left
= format_align
->right
;
1832 /* calculate the new width and position of the string */
1833 left_width
= right_width
;
1834 /* there is no right string anymore */
1838 ypos
= (line
* string_height
) + display
->getymargin();
1841 if (scroll
&& ((left_width
> scroll_width
) ||
1842 (center_width
> scroll_width
) ||
1843 (right_width
> scroll_width
)))
1845 display
->puts_scroll(0, line
,
1846 (unsigned char *)format_align
->left
);
1850 #ifdef HAVE_LCD_BITMAP
1851 /* clear the line first */
1852 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1853 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1854 display
->set_drawmode(DRMODE_SOLID
);
1857 /* Nasty hack: we output an empty scrolling string,
1858 which will reset the scroller for that line */
1859 display
->puts_scroll(0, line
, (unsigned char *)"");
1861 /* print aligned strings */
1862 if (left_width
!= 0)
1864 display
->putsxy(left_xpos
, ypos
,
1865 (unsigned char *)format_align
->left
);
1867 if (center_width
!= 0)
1869 display
->putsxy(center_xpos
, ypos
,
1870 (unsigned char *)format_align
->center
);
1872 if (right_width
!= 0)
1874 display
->putsxy(right_xpos
, ypos
,
1875 (unsigned char *)format_align
->right
);
1880 /* Refresh the WPS according to refresh_mode. */
1881 bool gui_wps_refresh(struct gui_wps
*gwps
,
1883 unsigned char refresh_mode
)
1885 struct wps_data
*data
= gwps
->data
;
1886 struct screen
*display
= gwps
->display
;
1887 struct wps_state
*state
= gwps
->state
;
1889 if(!gwps
|| !data
|| !state
|| !display
)
1892 int v
, line
, i
, subline_idx
;
1893 unsigned char flags
;
1894 char linebuf
[MAX_PATH
];
1896 struct align_pos align
;
1898 align
.center
= NULL
;
1901 bool update_line
, new_subline_refresh
;
1903 #ifdef HAVE_LCD_BITMAP
1904 gui_wps_statusbar_draw(gwps
, true);
1906 /* to find out wether the peak meter is enabled we
1907 assume it wasn't until we find a line that contains
1908 the peak meter. We can't use peak_meter_enabled itself
1909 because that would mean to turn off the meter thread
1910 temporarily. (That shouldn't matter unless yield
1911 or sleep is called but who knows...)
1913 bool enable_pm
= false;
1917 /* reset to first subline if refresh all flag is set */
1918 if (refresh_mode
== WPS_REFRESH_ALL
)
1920 display
->clear_display();
1922 for (i
= 0; i
<= data
->num_lines
; i
++)
1924 data
->lines
[i
].curr_subline
= SUBLINE_RESET
;
1928 #ifdef HAVE_LCD_CHARCELLS
1929 for (i
= 0; i
< 8; i
++)
1931 if (data
->wps_progress_pat
[i
] == 0)
1932 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1938 display
->stop_scroll();
1942 state
->ff_rewind_count
= ffwd_offset
;
1944 for (v
= 0; v
< data
->num_viewports
; v
++)
1946 display
->set_viewport(&data
->viewports
[v
].vp
);
1948 if (refresh_mode
== WPS_REFRESH_ALL
)
1950 display
->clear_viewport();
1953 #ifdef HAVE_LCD_BITMAP
1954 /* Set images to not to be displayed */
1955 for (i
= 0; i
< MAX_IMAGES
; i
++)
1957 data
->img
[i
].display
= -1;
1961 for (line
= data
->viewports
[v
].first_line
;
1962 line
<= data
->viewports
[v
].last_line
; line
++)
1964 memset(linebuf
, 0, sizeof(linebuf
));
1965 update_line
= false;
1967 /* get current subline for the line */
1968 new_subline_refresh
= update_curr_subline(gwps
, line
);
1970 subline_idx
= wps_subline_index(data
, line
,
1971 data
->lines
[line
].curr_subline
);
1972 flags
= data
->sublines
[subline_idx
].line_type
;
1974 if (refresh_mode
== WPS_REFRESH_ALL
|| (flags
& refresh_mode
)
1975 || new_subline_refresh
)
1977 /* get_line tells us if we need to update the line */
1978 update_line
= get_line(gwps
, line
, data
->lines
[line
].curr_subline
,
1979 &align
, linebuf
, sizeof(linebuf
));
1982 #ifdef HAVE_LCD_BITMAP
1984 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1986 /* the progressbar should be alone on its line */
1987 update_line
= false;
1988 draw_progressbar(gwps
, line
- data
->viewports
[v
].first_line
);
1992 if (flags
& refresh_mode
& WPS_REFRESH_PEAK_METER
)
1994 /* the peakmeter should be alone on its line */
1995 update_line
= false;
1997 int h
= font_get(display
->getfont())->height
;
1998 int peak_meter_y
= display
->getymargin() + (line
- data
->viewports
[v
].first_line
)* h
;
2000 /* The user might decide to have the peak meter in the last
2001 line so that it is only displayed if no status bar is
2002 visible. If so we neither want do draw nor enable the
2004 if (peak_meter_y
+ h
<= display
->getheight()) {
2005 /* found a line with a peak meter -> remember that we must
2008 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
2009 MIN(h
, display
->getheight() - peak_meter_y
));
2013 #else /* HAVE_LCD_CHARCELL */
2016 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
2018 if (data
->full_line_progressbar
)
2019 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
2021 draw_player_progress(gwps
);
2027 if (flags
& WPS_REFRESH_SCROLL
)
2029 /* if the line is a scrolling one we don't want to update
2030 too often, so that it has the time to scroll */
2031 if ((refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
2032 write_line(display
, &align
, line
- data
->viewports
[v
].first_line
, true);
2035 write_line(display
, &align
, line
- data
->viewports
[v
].first_line
, false);
2040 #ifdef HAVE_LCD_BITMAP
2041 /* Now display any images in this viewport */
2042 wps_display_images(gwps
, &data
->viewports
[v
].vp
);
2046 #ifdef HAVE_LCD_BITMAP
2047 data
->peak_meter_enabled
= enable_pm
;
2050 /* Restore the default viewport */
2051 display
->set_viewport(NULL
);
2055 #ifdef HAVE_BACKLIGHT
2056 if (global_settings
.caption_backlight
&& state
->id3
)
2058 /* turn on backlight n seconds before track ends, and turn it off n
2059 seconds into the new track. n == backlight_timeout, or 5s */
2060 int n
= global_settings
.backlight_timeout
* 1000;
2063 n
= 5000; /* use 5s if backlight is always on or off */
2065 if (((state
->id3
->elapsed
< 1000) ||
2066 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2067 (state
->paused
== false))
2071 #ifdef HAVE_REMOTE_LCD
2072 if (global_settings
.remote_caption_backlight
&& state
->id3
)
2074 /* turn on remote backlight n seconds before track ends, and turn it
2075 off n seconds into the new track. n == remote_backlight_timeout,
2077 int n
= global_settings
.remote_backlight_timeout
* 1000;
2080 n
= 5000; /* use 5s if backlight is always on or off */
2082 if (((state
->id3
->elapsed
< 1000) ||
2083 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2084 (state
->paused
== false))
2085 remote_backlight_on();