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"
56 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
60 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
61 /* 3% of 30min file == 54s step size */
62 #define MIN_FF_REWIND_STEP 500
64 /* draws the statusbar on the given wps-screen */
65 #ifdef HAVE_LCD_BITMAP
66 void gui_wps_statusbar_draw(struct gui_wps
*wps
, bool force
)
68 bool draw
= global_settings
.statusbar
;
70 if (wps
->data
->wps_sb_tag
)
71 draw
= wps
->data
->show_sb_on_wps
;
74 gui_statusbar_draw(wps
->statusbar
, force
);
77 #define gui_wps_statusbar_draw(wps, force) \
78 gui_statusbar_draw((wps)->statusbar, (force))
81 /* fades the volume */
82 void fade(bool fade_in
)
84 int fp_global_vol
= global_settings
.volume
<< 8;
85 int fp_min_vol
= sound_min(SOUND_VOLUME
) << 8;
86 int fp_step
= (fp_global_vol
- fp_min_vol
) / 30;
90 int fp_volume
= fp_min_vol
;
92 /* zero out the sound */
93 sound_set_volume(fp_min_vol
>> 8);
95 sleep(HZ
/10); /* let audio thread run */
98 while (fp_volume
< fp_global_vol
- fp_step
) {
100 sound_set_volume(fp_volume
>> 8);
103 sound_set_volume(global_settings
.volume
);
107 int fp_volume
= fp_global_vol
;
109 while (fp_volume
> fp_min_vol
+ fp_step
) {
110 fp_volume
-= fp_step
;
111 sound_set_volume(fp_volume
>> 8);
116 #if CONFIG_CODEC != SWCODEC
118 /* let audio thread run and wait for the mas to run out of data */
119 while (!mp3_pause_done())
124 /* reset volume to what it was before the fade */
125 sound_set_volume(global_settings
.volume
);
129 /* return true if screen restore is needed
130 return false otherwise
132 bool update_onvol_change(struct gui_wps
* gwps
)
134 gui_wps_statusbar_draw(gwps
, false);
135 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
137 #ifdef HAVE_LCD_CHARCELLS
138 gui_splash(gwps
->display
, 0, "Vol: %3d dB",
139 sound_val2phys(SOUND_VOLUME
, global_settings
.volume
));
145 bool ffwd_rew(int button
)
147 static const int ff_rew_steps
[] = {
148 1000, 2000, 3000, 4000,
149 5000, 6000, 8000, 10000,
150 15000, 20000, 25000, 30000,
154 unsigned int step
= 0; /* current ff/rewind step */
155 unsigned int max_step
= 0; /* maximum ff/rewind step */
156 int ff_rewind_count
= 0; /* current ff/rewind count (in ticks) */
157 int direction
= -1; /* forward=1 or backward=-1 */
158 long accel_tick
= 0; /* next time at which to bump the step size */
163 if (button
== ACTION_NONE
)
165 status_set_ffmode(0);
172 case ACTION_WPS_SEEKFWD
:
174 case ACTION_WPS_SEEKBACK
:
175 if (wps_state
.ff_rewind
)
179 /* fast forwarding, calc max step relative to end */
180 max_step
= (wps_state
.id3
->length
-
181 (wps_state
.id3
->elapsed
+
183 FF_REWIND_MAX_PERCENT
/ 100;
187 /* rewinding, calc max step relative to start */
188 max_step
= (wps_state
.id3
->elapsed
+ ff_rewind_count
) *
189 FF_REWIND_MAX_PERCENT
/ 100;
192 max_step
= MAX(max_step
, MIN_FF_REWIND_STEP
);
197 ff_rewind_count
+= step
* direction
;
199 if (global_settings
.ff_rewind_accel
!= 0 &&
200 current_tick
>= accel_tick
)
203 accel_tick
= current_tick
+
204 global_settings
.ff_rewind_accel
*HZ
;
209 if ( (audio_status() & AUDIO_STATUS_PLAY
) &&
210 wps_state
.id3
&& wps_state
.id3
->length
)
212 if (!wps_state
.paused
)
213 #if (CONFIG_CODEC == SWCODEC)
214 audio_pre_ff_rewind();
218 #if CONFIG_KEYPAD == PLAYER_PAD
220 gui_wps
[i
].display
->stop_scroll();
223 status_set_ffmode(STATUS_FASTFORWARD
);
225 status_set_ffmode(STATUS_FASTBACKWARD
);
227 wps_state
.ff_rewind
= true;
229 step
= ff_rew_steps
[global_settings
.ff_rewind_min_step
];
231 accel_tick
= current_tick
+
232 global_settings
.ff_rewind_accel
*HZ
;
239 if ((wps_state
.id3
->elapsed
+ ff_rewind_count
) >
240 wps_state
.id3
->length
)
241 ff_rewind_count
= wps_state
.id3
->length
-
242 wps_state
.id3
->elapsed
;
245 if ((int)(wps_state
.id3
->elapsed
+ ff_rewind_count
) < 0)
246 ff_rewind_count
= -wps_state
.id3
->elapsed
;
250 gui_wps_refresh(&gui_wps
[i
],
251 (wps_state
.wps_time_countup
== false)?
252 ff_rewind_count
:-ff_rewind_count
,
253 WPS_REFRESH_PLAYER_PROGRESS
|
254 WPS_REFRESH_DYNAMIC
);
258 case ACTION_WPS_STOPSEEK
:
259 wps_state
.id3
->elapsed
= wps_state
.id3
->elapsed
+ff_rewind_count
;
260 audio_ff_rewind(wps_state
.id3
->elapsed
);
262 wps_state
.ff_rewind
= false;
263 status_set_ffmode(0);
264 #if (CONFIG_CODEC != SWCODEC)
265 if (!wps_state
.paused
)
268 #ifdef HAVE_LCD_CHARCELLS
275 if(default_event_handler(button
) == SYS_USB_CONNECTED
) {
276 status_set_ffmode(0);
283 button
= get_action(CONTEXT_WPS
,TIMEOUT_BLOCK
);
288 bool gui_wps_display(void)
291 if (!wps_state
.id3
&& !(audio_status() & AUDIO_STATUS_PLAY
))
293 global_status
.resume_index
= -1;
294 #ifdef HAVE_LCD_BITMAP
295 gui_syncstatusbar_draw(&statusbars
, true);
297 gui_syncsplash(HZ
, str(LANG_END_PLAYLIST_RECORDER
));
304 gui_wps
[i
].display
->clear_display();
305 if (!gui_wps
[i
].data
->wps_loaded
) {
306 if ( !gui_wps
[i
].data
->num_tokens
) {
307 /* set the default wps for the main-screen */
310 #ifdef HAVE_LCD_BITMAP
312 unload_wps_backdrop();
314 wps_data_load(gui_wps
[i
].data
,
315 "%s%?it<%?in<%in. |>%it|%fn>\n"
316 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
317 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
319 "%al%pc/%pt%ar[%pp:%pe]\n"
320 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
324 wps_data_load(gui_wps
[i
].data
,
325 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
326 "%pc%?ps<*|/>%pt\n", false);
330 /* set the default wps for the remote-screen */
333 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
334 unload_remote_wps_backdrop();
336 wps_data_load(gui_wps
[i
].data
,
337 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
338 "%s%?it<%?in<%in. |>%it|%fn>\n"
339 "%al%pc/%pt%ar[%pp:%pe]\n"
340 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
351 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_ALL
);
356 bool update(struct gui_wps
*gwps
)
358 bool track_changed
= audio_has_changed_track();
359 bool retcode
= false;
361 gwps
->state
->nid3
= audio_next_track();
364 gwps
->display
->stop_scroll();
365 gwps
->state
->id3
= audio_current_track();
367 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
368 && strcmp(gwps
->state
->id3
->path
, curr_cue
->audio_filename
))
370 /* the current cuesheet isn't the right one any more */
372 if (!strcmp(gwps
->state
->id3
->path
, temp_cue
->audio_filename
)) {
373 /* We have the new cuesheet in memory (temp_cue),
374 let's make it the current one ! */
375 memcpy(curr_cue
, temp_cue
, sizeof(struct cuesheet
));
378 /* We need to parse the new cuesheet */
380 char cuepath
[MAX_PATH
];
382 if (look_for_cuesheet_file(gwps
->state
->id3
->path
, cuepath
) &&
383 parse_cuesheet(cuepath
, curr_cue
))
385 gwps
->state
->id3
->cuesheet_type
= 1;
386 strcpy(curr_cue
->audio_filename
, gwps
->state
->id3
->path
);
390 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
393 if (gui_wps_display())
396 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
399 if (gwps
->state
->id3
)
401 strncpy(gwps
->state
->current_track_path
, gwps
->state
->id3
->path
,
402 sizeof(gwps
->state
->current_track_path
));
403 gwps
->state
->current_track_path
[sizeof(gwps
->state
->current_track_path
)-1] = '\0';
407 if (gwps
->state
->id3
)
409 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
410 && (gwps
->state
->id3
->elapsed
< curr_cue
->curr_track
->offset
411 || (curr_cue
->curr_track_idx
< curr_cue
->track_count
- 1
412 && gwps
->state
->id3
->elapsed
>= (curr_cue
->curr_track
+1)->offset
)))
414 /* We've changed tracks within the cuesheet :
415 we need to update the ID3 info and refresh the WPS */
417 cue_find_current_track(curr_cue
, gwps
->state
->id3
->elapsed
);
418 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
420 gwps
->display
->stop_scroll();
421 if (gui_wps_display())
424 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
427 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
430 gui_wps_statusbar_draw(gwps
, false);
436 void display_keylock_text(bool locked
)
441 gui_wps
[i
].display
->stop_scroll();
443 #ifdef HAVE_LCD_CHARCELLS
445 s
= str(LANG_KEYLOCK_ON_PLAYER
);
447 s
= str(LANG_KEYLOCK_OFF_PLAYER
);
450 s
= str(LANG_KEYLOCK_ON_RECORDER
);
452 s
= str(LANG_KEYLOCK_OFF_RECORDER
);
454 gui_syncsplash(HZ
, s
);
457 #ifdef HAVE_LCD_BITMAP
459 static void draw_progressbar(struct gui_wps
*gwps
, int line
)
461 struct wps_data
*data
= gwps
->data
;
462 struct screen
*display
= gwps
->display
;
463 struct wps_state
*state
= gwps
->state
;
464 int h
= font_get(FONT_UI
)->height
;
467 if (data
->progress_top
< 0)
468 sb_y
= line
*h
+ display
->getymargin() +
469 ((h
> data
->progress_height
+ 1)
470 ? (h
- data
->progress_height
) / 2 : 1);
472 sb_y
= data
->progress_top
;
474 if (!data
->progress_end
)
475 data
->progress_end
=display
->width
;
477 if (gwps
->data
->progressbar
.have_bitmap_pb
)
478 gui_bitmap_scrollbar_draw(display
, data
->progressbar
.bm
,
479 data
->progress_start
, sb_y
,
480 data
->progress_end
-data
->progress_start
,
481 data
->progressbar
.bm
.height
,
482 state
->id3
->length
? state
->id3
->length
: 1, 0,
483 state
->id3
->length
? state
->id3
->elapsed
484 + state
->ff_rewind_count
: 0,
487 gui_scrollbar_draw(display
, data
->progress_start
, sb_y
,
488 data
->progress_end
-data
->progress_start
,
489 data
->progress_height
,
490 state
->id3
->length
? state
->id3
->length
: 1, 0,
491 state
->id3
->length
? state
->id3
->elapsed
492 + state
->ff_rewind_count
: 0,
495 #ifdef AB_REPEAT_ENABLE
496 if ( ab_repeat_mode_enabled() )
497 ab_draw_markers(display
, state
->id3
->length
,
498 data
->progress_start
, data
->progress_end
, sb_y
,
499 data
->progress_height
);
502 if ( cuesheet_is_enabled() && state
->id3
->cuesheet_type
)
503 cue_draw_markers(display
, state
->id3
->length
,
504 data
->progress_start
, data
->progress_end
,
505 sb_y
+1, data
->progress_height
-2);
508 /* clears the area where the image was shown */
509 static void clear_image_pos(struct gui_wps
*gwps
, int n
)
513 struct wps_data
*data
= gwps
->data
;
514 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
515 gwps
->display
->fillrect(data
->img
[n
].x
, data
->img
[n
].y
,
516 data
->img
[n
].bm
.width
, data
->img
[n
].bm
.height
);
517 gwps
->display
->set_drawmode(DRMODE_SOLID
);
520 static void wps_draw_image(struct gui_wps
*gwps
, int n
)
522 struct screen
*display
= gwps
->display
;
523 struct wps_data
*data
= gwps
->data
;
524 if(data
->img
[n
].always_display
)
525 display
->set_drawmode(DRMODE_FG
);
527 display
->set_drawmode(DRMODE_SOLID
);
530 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
532 display
->mono_bitmap(data
->img
[n
].bm
.data
, data
->img
[n
].x
,
533 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
534 data
->img
[n
].bm
.height
);
537 display
->transparent_bitmap((fb_data
*)data
->img
[n
].bm
.data
,
539 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
540 data
->img
[n
].bm
.height
);
545 static void wps_display_images(struct gui_wps
*gwps
)
547 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
551 struct wps_data
*data
= gwps
->data
;
552 struct screen
*display
= gwps
->display
;
554 for (n
= 0; n
< MAX_IMAGES
; n
++)
556 if (data
->img
[n
].loaded
&&
557 (data
->img
[n
].display
|| data
->img
[n
].always_display
))
559 wps_draw_image(gwps
, n
);
562 display
->set_drawmode(DRMODE_SOLID
);
565 #else /* HAVE_LCD_CHARCELL */
567 static bool draw_player_progress(struct gui_wps
*gwps
)
569 struct wps_state
*state
= gwps
->state
;
570 struct screen
*display
= gwps
->display
;
571 unsigned char progress_pattern
[7];
578 if (state
->id3
->length
)
579 pos
= 36 * (state
->id3
->elapsed
+ state
->ff_rewind_count
)
580 / state
->id3
->length
;
582 for (i
= 0; i
< 7; i
++, pos
-= 5)
585 progress_pattern
[i
] = 0x1f;
587 progress_pattern
[i
] = 0x00;
589 progress_pattern
[i
] = 0x1f >> pos
;
592 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
596 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
598 static const unsigned char numbers
[10][4] = {
599 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
600 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
601 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
602 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
603 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
604 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
605 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
606 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
607 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
608 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
611 struct wps_state
*state
= gwps
->state
;
612 struct screen
*display
= gwps
->display
;
613 struct wps_data
*data
= gwps
->data
;
614 unsigned char progress_pattern
[7];
623 if (!state
->id3
|| buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
626 time
= state
->id3
->elapsed
+ state
->ff_rewind_count
;
627 if (state
->id3
->length
)
628 pos
= 55 * time
/ state
->id3
->length
;
630 memset(timestr
, 0, sizeof(timestr
));
631 format_time(timestr
, sizeof(timestr
)-2, time
);
632 timestr
[strlen(timestr
)] = ':'; /* always safe */
634 for (i
= 0; i
< 11; i
++, pos
-= 5)
637 memset(progress_pattern
, 0, sizeof(progress_pattern
));
639 if ((digit
= timestr
[time_idx
]))
644 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
646 memcpy(progress_pattern
, numbers
[digit
], 4);
649 else /* tens, shifted right */
651 for (j
= 0; j
< 4; j
++)
652 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
654 if (time_idx
> 0) /* not the first group, add colon in front */
656 progress_pattern
[1] |= 0x10;
657 progress_pattern
[3] |= 0x10;
663 progress_pattern
[5] = progress_pattern
[6] = 0x1f;
666 if (pos
> 0 && pos
< 5)
669 progress_pattern
[5] = progress_pattern
[6] = (~0x1f >> pos
) & 0x1f;
672 if (softchar
&& pat_idx
< 8)
674 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
676 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
680 buf
= utf8encode(' ', buf
);
682 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
687 #endif /* HAVE_LCD_CHARCELL */
689 /* Extract a part from a path.
691 * buf - buffer extract part to.
692 * buf_size - size of buffer.
693 * path - path to extract from.
694 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
695 * parent of parent, etc.
697 * Returns buf if the desired level was found, NULL otherwise.
699 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
702 const char* last_sep
;
705 sep
= path
+ strlen(path
);
720 if (level
|| (last_sep
<= sep
))
723 len
= MIN(last_sep
- sep
, buf_size
- 1);
724 strncpy(buf
, sep
+ 1, len
);
729 /* Return the tag found at index i and write its value in buf.
730 The return value is buf if the tag had a value, or NULL if not.
732 intval is used with conditionals/enums: when this function is called,
733 intval should contain the number of options in the conditional/enum.
734 When this function returns, intval is -1 if the tag is non numeric or,
735 if the tag is numeric, intval is the enum case we want to go to.
736 When not treating a conditional/enum, intval should be NULL.
738 static char *get_token_value(struct gui_wps
*gwps
,
739 struct wps_token
*token
,
740 char *buf
, int buf_size
,
746 struct wps_data
*data
= gwps
->data
;
747 struct wps_state
*state
= gwps
->state
;
752 struct mp3entry
*id3
;
763 struct tm
* tm
= NULL
;
765 /* if the token is an RTC one, update the time
766 and do the necessary checks */
767 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
768 && token
->type
<= WPS_TOKENS_RTC_END
)
786 case WPS_TOKEN_CHARACTER
:
787 return &(token
->value
.c
);
789 case WPS_TOKEN_STRING
:
790 return data
->strings
[token
->value
.i
];
792 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
793 format_time(buf
, buf_size
,
794 id3
->elapsed
+ state
->ff_rewind_count
);
797 case WPS_TOKEN_TRACK_TIME_REMAINING
:
798 format_time(buf
, buf_size
,
799 id3
->length
- id3
->elapsed
-
800 state
->ff_rewind_count
);
803 case WPS_TOKEN_TRACK_LENGTH
:
804 format_time(buf
, buf_size
, id3
->length
);
807 case WPS_TOKEN_PLAYLIST_ENTRIES
:
808 snprintf(buf
, buf_size
, "%d", playlist_amount());
811 case WPS_TOKEN_PLAYLIST_NAME
:
812 return playlist_name(NULL
, buf
, buf_size
);
814 case WPS_TOKEN_PLAYLIST_POSITION
:
815 snprintf(buf
, buf_size
, "%d",
816 playlist_get_display_index());
819 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
820 if ( global_settings
.playlist_shuffle
)
826 case WPS_TOKEN_VOLUME
:
827 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
830 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
834 else if (global_settings
.volume
== 0)
838 else if (global_settings
.volume
> 0)
844 *intval
= (limit
- 3) * (global_settings
.volume
845 - sound_min(SOUND_VOLUME
))
846 / (sound_max(SOUND_VOLUME
)
847 - sound_min(SOUND_VOLUME
)) + 2;
852 case WPS_TOKEN_METADATA_ARTIST
:
855 case WPS_TOKEN_METADATA_COMPOSER
:
856 return id3
->composer
;
858 case WPS_TOKEN_METADATA_ALBUM
:
861 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
862 return id3
->albumartist
;
864 case WPS_TOKEN_METADATA_GENRE
:
865 return id3
->genre_string
;
867 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
868 if (id3
->track_string
)
869 return id3
->track_string
;
872 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
877 case WPS_TOKEN_METADATA_TRACK_TITLE
:
880 case WPS_TOKEN_METADATA_VERSION
:
881 switch (id3
->id3version
)
902 case WPS_TOKEN_METADATA_YEAR
:
903 if( id3
->year_string
)
904 return id3
->year_string
;
907 snprintf(buf
, buf_size
, "%d", id3
->year
);
912 case WPS_TOKEN_METADATA_COMMENT
:
915 case WPS_TOKEN_FILE_BITRATE
:
917 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
919 snprintf(buf
, buf_size
, "?");
922 case WPS_TOKEN_FILE_CODEC
:
925 if(id3
->codectype
== AFMT_UNKNOWN
)
926 *intval
= AFMT_NUM_CODECS
;
928 *intval
= id3
->codectype
;
930 return id3_get_codec(id3
);
932 case WPS_TOKEN_FILE_FREQUENCY
:
933 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
936 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
937 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
938 if ((id3
->frequency
% 1000) < 100)
939 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
941 snprintf(buf
, buf_size
, "%ld.%d",
942 id3
->frequency
/ 1000,
943 (id3
->frequency
% 1000) / 100);
946 case WPS_TOKEN_FILE_NAME
:
947 if (get_dir(buf
, buf_size
, id3
->path
, 0)) {
948 /* Remove extension */
949 char* sep
= strrchr(buf
, '.');
959 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
960 return get_dir(buf
, buf_size
, id3
->path
, 0);
962 case WPS_TOKEN_FILE_PATH
:
965 case WPS_TOKEN_FILE_SIZE
:
966 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
969 case WPS_TOKEN_FILE_VBR
:
970 return id3
->vbr
? "(avg)" : NULL
;
972 case WPS_TOKEN_FILE_DIRECTORY
:
973 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
975 case WPS_TOKEN_BATTERY_PERCENT
:
977 int l
= battery_level();
981 limit
= MAX(limit
, 2);
983 /* First enum is used for "unknown level". */
984 *intval
= (limit
- 1) * l
/ 100 + 2;
991 snprintf(buf
, buf_size
, "%d", l
);
998 case WPS_TOKEN_BATTERY_VOLTS
:
1000 unsigned int v
= battery_voltage();
1001 snprintf(buf
, buf_size
, "%d.%02d", v
/100, v
%100);
1005 case WPS_TOKEN_BATTERY_TIME
:
1007 int t
= battery_time();
1009 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
1011 strncpy(buf
, "?h ?m", buf_size
);
1016 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
1018 if(charger_input_state
==CHARGER
)
1024 #if CONFIG_CHARGING >= CHARGING_MONITOR
1025 case WPS_TOKEN_BATTERY_CHARGING
:
1027 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
1034 case WPS_TOKEN_BATTERY_SLEEPTIME
:
1036 if (get_sleep_timer() == 0)
1040 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
1045 case WPS_TOKEN_PLAYBACK_STATUS
:
1047 int status
= audio_status();
1049 if (status
== AUDIO_STATUS_PLAY
&& \
1050 !(status
& AUDIO_STATUS_PAUSE
))
1052 if (audio_status() & AUDIO_STATUS_PAUSE
&& \
1053 (! status_get_ffmode()))
1055 if (status_get_ffmode() == STATUS_FASTFORWARD
)
1057 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
1064 snprintf(buf
, buf_size
, "%d", mode
);
1068 case WPS_TOKEN_REPEAT_MODE
:
1070 *intval
= global_settings
.repeat_mode
+ 1;
1071 snprintf(buf
, buf_size
, "%d", *intval
);
1075 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1076 /* d: day of month (01..31) */
1077 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
1080 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1081 /* e: day of month, blank padded ( 1..31) */
1082 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
1085 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1086 /* H: hour (00..23) */
1087 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
1090 case WPS_TOKEN_RTC_HOUR_24
:
1091 /* k: hour ( 0..23) */
1092 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
1095 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1096 /* I: hour (01..12) */
1097 snprintf(buf
, buf_size
, "%02d",
1098 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1101 case WPS_TOKEN_RTC_HOUR_12
:
1102 /* l: hour ( 1..12) */
1103 snprintf(buf
, buf_size
, "%2d",
1104 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1107 case WPS_TOKEN_RTC_MONTH
:
1108 /* m: month (01..12) */
1110 *intval
= tm
->tm_mon
+ 1;
1111 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
1114 case WPS_TOKEN_RTC_MINUTE
:
1115 /* M: minute (00..59) */
1116 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
1119 case WPS_TOKEN_RTC_SECOND
:
1120 /* S: second (00..59) */
1121 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
1124 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1125 /* y: last two digits of year (00..99) */
1126 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
1129 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1130 /* Y: year (1970...) */
1131 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
1134 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1135 /* p: upper case AM or PM indicator */
1136 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "AM" : "PM");
1139 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1140 /* P: lower case am or pm indicator */
1141 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "am" : "pm");
1144 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1145 /* a: abbreviated weekday name (Sun..Sat) */
1146 snprintf(buf
, buf_size
, "%s",str(dayname
[tm
->tm_wday
]));
1149 case WPS_TOKEN_RTC_MONTH_NAME
:
1150 /* b: abbreviated month name (Jan..Dec) */
1151 snprintf(buf
, buf_size
, "%s",str(monthname
[tm
->tm_mon
]));
1154 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1155 /* u: day of week (1..7); 1 is Monday */
1157 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
1158 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
1161 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1162 /* w: day of week (0..6); 0 is Sunday */
1164 *intval
= tm
->tm_wday
+ 1;
1165 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
1168 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1169 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1170 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1171 case WPS_TOKEN_RTC_HOUR_24
:
1172 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1173 case WPS_TOKEN_RTC_HOUR_12
:
1174 case WPS_TOKEN_RTC_MONTH
:
1175 case WPS_TOKEN_RTC_MINUTE
:
1176 case WPS_TOKEN_RTC_SECOND
:
1177 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1178 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1179 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1180 strncpy(buf
, "--", buf_size
);
1182 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1183 strncpy(buf
, "----", buf_size
);
1185 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1186 case WPS_TOKEN_RTC_MONTH_NAME
:
1187 strncpy(buf
, "---", buf_size
);
1189 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1190 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1191 strncpy(buf
, "-", buf_size
);
1195 #ifdef HAVE_LCD_CHARCELLS
1196 case WPS_TOKEN_PROGRESSBAR
:
1198 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
1203 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
1206 /* we need 11 characters (full line) for
1208 snprintf(buf
, buf_size
, " ");
1212 /* Tell the user if we have an OldPlayer */
1213 snprintf(buf
, buf_size
, " <Old LCD> ");
1218 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
1220 *intval
= id3
->playcount
+ 1;
1222 snprintf(buf
, buf_size
, "%ld", id3
->playcount
);
1225 case WPS_TOKEN_DATABASE_RATING
:
1227 *intval
= id3
->rating
+ 1;
1229 snprintf(buf
, buf_size
, "%d", id3
->rating
);
1232 case WPS_TOKEN_DATABASE_AUTOSCORE
:
1234 *intval
= id3
->score
+ 1;
1236 snprintf(buf
, buf_size
, "%d", id3
->score
);
1239 #if (CONFIG_CODEC == SWCODEC)
1240 case WPS_TOKEN_CROSSFADE
:
1242 *intval
= global_settings
.crossfade
+ 1;
1243 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
1246 case WPS_TOKEN_REPLAYGAIN
:
1250 if (global_settings
.replaygain
== 0)
1255 get_replaygain_mode(id3
->track_gain_string
!= NULL
,
1256 id3
->album_gain_string
!= NULL
);
1258 val
= 6; /* no tag */
1262 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
1277 strncpy(buf
, id3
->track_gain_string
, buf_size
);
1281 strncpy(buf
, id3
->album_gain_string
, buf_size
);
1286 #endif /* (CONFIG_CODEC == SWCODEC) */
1288 #if (CONFIG_CODEC != MAS3507D)
1289 case WPS_TOKEN_SOUND_PITCH
:
1291 int val
= sound_get_pitch();
1292 snprintf(buf
, buf_size
, "%d.%d",
1293 val
/ 10, val
% 10);
1298 case WPS_TOKEN_MAIN_HOLD
:
1299 #ifdef HAS_BUTTON_HOLD
1302 if (is_keys_locked())
1303 #endif /*hold switch or softlock*/
1308 #ifdef HAS_REMOTE_BUTTON_HOLD
1309 case WPS_TOKEN_REMOTE_HOLD
:
1310 if (remote_button_hold())
1316 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
1317 case WPS_TOKEN_VLED_HDD
:
1329 /* Return the index to the end token for the conditional token at index.
1330 The conditional token can be either a start token or a separator
1331 (i.e. option) token.
1333 static int find_conditional_end(struct wps_data
*data
, int index
)
1336 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
1337 ret
= data
->tokens
[ret
].value
.i
;
1339 /* ret now is the index to the end token for the conditional. */
1343 /* Return the index of the appropriate case for the conditional
1344 that starts at cond_index.
1346 static int evaluate_conditional(struct gui_wps
*gwps
, int cond_index
)
1351 struct wps_data
*data
= gwps
->data
;
1354 char result
[128], *value
;
1355 int num_options
= data
->tokens
[cond_index
].value
.i
;
1357 /* treat ?xx<true> constructs as if they had 2 options. */
1358 if (num_options
< 2)
1361 int intval
= num_options
;
1362 /* get_token_value needs to know the number of options in the enum */
1363 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
1364 result
, sizeof(result
), &intval
);
1366 /* intval is now the number of the enum option we want to read,
1367 starting from 1. If intval is -1, we check if value is empty. */
1369 intval
= (value
&& *value
) ? 1 : num_options
;
1370 else if (intval
> num_options
|| intval
< 1)
1371 intval
= num_options
;
1373 /* skip to the right enum case */
1374 int next
= cond_index
+ 2;
1375 for (i
= 1; i
< intval
; i
++)
1377 next
= data
->tokens
[next
].value
.i
;
1381 #ifdef HAVE_LCD_BITMAP
1382 /* clear all pictures in the conditional */
1383 for (i
= 0; i
< MAX_IMAGES
; i
++)
1385 if (data
->img
[i
].cond_index
== cond_index
)
1386 clear_image_pos(gwps
, i
);
1393 /* Read a (sub)line to the given alignment format buffer.
1394 linebuf is the buffer where the data is actually stored.
1395 align is the alignment format that'll be used to display the text.
1396 The return value indicates whether the line needs to be updated.
1398 static bool get_line(struct gui_wps
*gwps
,
1399 int line
, int subline
,
1400 struct align_pos
*align
,
1404 struct wps_data
*data
= gwps
->data
;
1407 char *buf
= linebuf
; /* will always point to the writing position */
1408 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
1409 int i
, last_token_idx
;
1410 bool update
= false;
1412 /* alignment-related variables */
1414 char* cur_align_start
;
1415 cur_align_start
= buf
;
1416 cur_align
= WPS_ALIGN_LEFT
;
1418 align
->center
= NULL
;
1419 align
->right
= NULL
;
1421 /* Process all tokens of the desired subline */
1422 last_token_idx
= wps_last_token_index(data
, line
, subline
);
1423 for (i
= wps_first_token_index(data
, line
, subline
);
1424 i
<= last_token_idx
; i
++)
1426 switch(data
->tokens
[i
].type
)
1428 case WPS_TOKEN_CONDITIONAL
:
1429 /* place ourselves in the right conditional case */
1430 i
= evaluate_conditional(gwps
, i
);
1434 case WPS_TOKEN_CONDITIONAL_OPTION
:
1435 /* we've finished in the curent conditional case,
1436 skip to the end of the conditional structure */
1437 i
= find_conditional_end(data
, i
);
1440 #ifdef HAVE_LCD_BITMAP
1441 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1443 struct gui_img
*img
= data
->img
;
1444 int n
= data
->tokens
[i
].value
.i
;
1445 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
)
1446 img
[n
].display
= true;
1451 case WPS_TOKEN_ALIGN_LEFT
:
1452 case WPS_TOKEN_ALIGN_CENTER
:
1453 case WPS_TOKEN_ALIGN_RIGHT
:
1454 /* remember where the current aligned text started */
1457 case WPS_ALIGN_LEFT
:
1458 align
->left
= cur_align_start
;
1461 case WPS_ALIGN_CENTER
:
1462 align
->center
= cur_align_start
;
1465 case WPS_ALIGN_RIGHT
:
1466 align
->right
= cur_align_start
;
1469 /* start a new alignment */
1470 switch (data
->tokens
[i
].type
)
1472 case WPS_TOKEN_ALIGN_LEFT
:
1473 cur_align
= WPS_ALIGN_LEFT
;
1475 case WPS_TOKEN_ALIGN_CENTER
:
1476 cur_align
= WPS_ALIGN_CENTER
;
1478 case WPS_TOKEN_ALIGN_RIGHT
:
1479 cur_align
= WPS_ALIGN_RIGHT
;
1485 cur_align_start
= buf
;
1490 /* get the value of the tag and copy it to the buffer */
1491 char *value
= get_token_value(gwps
, &data
->tokens
[i
],
1492 temp_buf
, sizeof(temp_buf
), NULL
);
1496 while (*value
&& (buf
< linebuf_end
))
1504 /* close the current alignment */
1507 case WPS_ALIGN_LEFT
:
1508 align
->left
= cur_align_start
;
1511 case WPS_ALIGN_CENTER
:
1512 align
->center
= cur_align_start
;
1515 case WPS_ALIGN_RIGHT
:
1516 align
->right
= cur_align_start
;
1523 static void get_subline_timeout(struct gui_wps
*gwps
, int line
, int subline
)
1525 struct wps_data
*data
= gwps
->data
;
1527 int subline_idx
= wps_subline_index(data
, line
, subline
);
1528 int last_token_idx
= wps_last_token_index(data
, line
, subline
);
1530 data
->sublines
[subline_idx
].time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1532 for (i
= wps_first_token_index(data
, line
, subline
);
1533 i
<= last_token_idx
; i
++)
1535 switch(data
->tokens
[i
].type
)
1537 case WPS_TOKEN_CONDITIONAL
:
1538 /* place ourselves in the right conditional case */
1539 i
= evaluate_conditional(gwps
, i
);
1542 case WPS_TOKEN_CONDITIONAL_OPTION
:
1543 /* we've finished in the curent conditional case,
1544 skip to the end of the conditional structure */
1545 i
= find_conditional_end(data
, i
);
1548 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1549 data
->sublines
[subline_idx
].time_mult
= data
->tokens
[i
].value
.i
;
1558 /* Calculates which subline should be displayed for the specified line
1559 Returns true iff the subline must be refreshed */
1560 static bool update_curr_subline(struct gui_wps
*gwps
, int line
)
1562 struct wps_data
*data
= gwps
->data
;
1564 int search
, search_start
, num_sublines
;
1566 bool new_subline_refresh
;
1567 bool only_one_subline
;
1569 num_sublines
= data
->lines
[line
].num_sublines
;
1570 reset_subline
= (data
->lines
[line
].curr_subline
== SUBLINE_RESET
);
1571 new_subline_refresh
= false;
1572 only_one_subline
= false;
1574 /* if time to advance to next sub-line */
1575 if (TIME_AFTER(current_tick
, data
->lines
[line
].subline_expire_time
- 1) ||
1578 /* search all sublines until the next subline with time > 0
1579 is found or we get back to the subline we started with */
1583 search_start
= data
->lines
[line
].curr_subline
;
1585 for (search
= 0; search
< num_sublines
; search
++)
1587 data
->lines
[line
].curr_subline
++;
1589 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1590 if (data
->lines
[line
].curr_subline
== num_sublines
)
1592 if (data
->lines
[line
].curr_subline
== 1)
1593 only_one_subline
= true;
1594 data
->lines
[line
].curr_subline
= 0;
1597 /* if back where we started after search or
1598 only one subline is defined on the line */
1599 if (((search
> 0) &&
1600 (data
->lines
[line
].curr_subline
== search_start
)) ||
1603 /* no other subline with a time > 0 exists */
1604 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1606 data
->lines
[line
].subline_expire_time
) + 100 * HZ
;
1611 /* get initial time multiplier for this subline */
1612 get_subline_timeout(gwps
, line
, data
->lines
[line
].curr_subline
);
1614 int subline_idx
= wps_subline_index(data
, line
,
1615 data
->lines
[line
].curr_subline
);
1617 /* only use this subline if subline time > 0 */
1618 if (data
->sublines
[subline_idx
].time_mult
> 0)
1620 new_subline_refresh
= true;
1621 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1622 current_tick
: data
->lines
[line
].subline_expire_time
) +
1623 BASE_SUBLINE_TIME
*data
->sublines
[subline_idx
].time_mult
;
1630 return new_subline_refresh
;
1633 /* Display a line appropriately according to its alignment format.
1634 format_align contains the text, separated between left, center and right.
1635 line is the index of the line on the screen.
1636 scroll indicates whether the line is a scrolling one or not.
1638 static void write_line(struct screen
*display
,
1639 struct align_pos
*format_align
,
1644 int left_width
, left_xpos
;
1645 int center_width
, center_xpos
;
1646 int right_width
, right_xpos
;
1651 /* calculate different string sizes and positions */
1652 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
1653 if (format_align
->left
!= 0) {
1654 display
->getstringsize((unsigned char *)format_align
->left
,
1655 &left_width
, &string_height
);
1662 if (format_align
->center
!= 0) {
1663 display
->getstringsize((unsigned char *)format_align
->center
,
1664 ¢er_width
, &string_height
);
1669 center_xpos
=(display
->width
- center_width
) / 2;
1671 if (format_align
->right
!= 0) {
1672 display
->getstringsize((unsigned char *)format_align
->right
,
1673 &right_width
, &string_height
);
1678 right_xpos
= (display
->width
- right_width
);
1680 /* Checks for overlapping strings.
1681 If needed the overlapping strings will be merged, separated by a
1684 /* CASE 1: left and centered string overlap */
1685 /* there is a left string, need to merge left and center */
1686 if ((left_width
!= 0 && center_width
!= 0) &&
1687 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1688 /* replace the former separator '\0' of left and
1689 center string with a space */
1690 *(--format_align
->center
) = ' ';
1691 /* calculate the new width and position of the merged string */
1692 left_width
= left_width
+ space_width
+ center_width
;
1694 /* there is no centered string anymore */
1697 /* there is no left string, move center to left */
1698 if ((left_width
== 0 && center_width
!= 0) &&
1699 (left_xpos
+ left_width
> center_xpos
)) {
1700 /* move the center string to the left string */
1701 format_align
->left
= format_align
->center
;
1702 /* calculate the new width and position of the string */
1703 left_width
= center_width
;
1705 /* there is no centered string anymore */
1709 /* CASE 2: centered and right string overlap */
1710 /* there is a right string, need to merge center and right */
1711 if ((center_width
!= 0 && right_width
!= 0) &&
1712 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1713 /* replace the former separator '\0' of center and
1714 right string with a space */
1715 *(--format_align
->right
) = ' ';
1716 /* move the center string to the right after merge */
1717 format_align
->right
= format_align
->center
;
1718 /* calculate the new width and position of the merged string */
1719 right_width
= center_width
+ space_width
+ right_width
;
1720 right_xpos
= (display
->width
- right_width
);
1721 /* there is no centered string anymore */
1724 /* there is no right string, move center to right */
1725 if ((center_width
!= 0 && right_width
== 0) &&
1726 (center_xpos
+ center_width
> right_xpos
)) {
1727 /* move the center string to the right string */
1728 format_align
->right
= format_align
->center
;
1729 /* calculate the new width and position of the string */
1730 right_width
= center_width
;
1731 right_xpos
= (display
->width
- right_width
);
1732 /* there is no centered string anymore */
1736 /* CASE 3: left and right overlap
1737 There is no center string anymore, either there never
1738 was one or it has been merged in case 1 or 2 */
1739 /* there is a left string, need to merge left and right */
1740 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1741 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1742 /* replace the former separator '\0' of left and
1743 right string with a space */
1744 *(--format_align
->right
) = ' ';
1745 /* calculate the new width and position of the string */
1746 left_width
= left_width
+ space_width
+ right_width
;
1748 /* there is no right string anymore */
1751 /* there is no left string, move right to left */
1752 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1753 (left_xpos
+ left_width
> right_xpos
)) {
1754 /* move the right string to the left string */
1755 format_align
->left
= format_align
->right
;
1756 /* calculate the new width and position of the string */
1757 left_width
= right_width
;
1759 /* there is no right string anymore */
1763 ypos
= (line
* string_height
) + display
->getymargin();
1766 if (scroll
&& left_width
> display
->width
)
1768 display
->puts_scroll(0, line
,
1769 (unsigned char *)format_align
->left
);
1773 #ifdef HAVE_LCD_BITMAP
1774 /* clear the line first */
1775 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1776 display
->fillrect(0, ypos
, display
->width
, string_height
);
1777 display
->set_drawmode(DRMODE_SOLID
);
1780 /* Nasty hack: we output an empty scrolling string,
1781 which will reset the scroller for that line */
1782 display
->puts_scroll(0, line
, (unsigned char *)"");
1784 /* print aligned strings */
1785 if (left_width
!= 0)
1787 display
->putsxy(left_xpos
, ypos
,
1788 (unsigned char *)format_align
->left
);
1790 if (center_width
!= 0)
1792 display
->putsxy(center_xpos
, ypos
,
1793 (unsigned char *)format_align
->center
);
1795 if (right_width
!= 0)
1797 display
->putsxy(right_xpos
, ypos
,
1798 (unsigned char *)format_align
->right
);
1803 /* Refresh the WPS according to refresh_mode. */
1804 bool gui_wps_refresh(struct gui_wps
*gwps
,
1806 unsigned char refresh_mode
)
1808 struct wps_data
*data
= gwps
->data
;
1809 struct screen
*display
= gwps
->display
;
1810 struct wps_state
*state
= gwps
->state
;
1812 if(!gwps
|| !data
|| !state
|| !display
)
1815 int line
, i
, subline_idx
;
1816 unsigned char flags
;
1817 char linebuf
[MAX_PATH
];
1819 struct align_pos align
;
1821 align
.center
= NULL
;
1824 bool update_line
, new_subline_refresh
;
1826 #ifdef HAVE_LCD_BITMAP
1827 gui_wps_statusbar_draw(gwps
, true);
1829 /* to find out wether the peak meter is enabled we
1830 assume it wasn't until we find a line that contains
1831 the peak meter. We can't use peak_meter_enabled itself
1832 because that would mean to turn off the meter thread
1833 temporarily. (That shouldn't matter unless yield
1834 or sleep is called but who knows...)
1836 bool enable_pm
= false;
1838 /* Set images to not to be displayed */
1839 for (i
= 0; i
< MAX_IMAGES
; i
++)
1841 data
->img
[i
].display
= false;
1845 /* reset to first subline if refresh all flag is set */
1846 if (refresh_mode
== WPS_REFRESH_ALL
)
1848 for (i
= 0; i
< data
->num_lines
; i
++)
1850 data
->lines
[i
].curr_subline
= SUBLINE_RESET
;
1854 #ifdef HAVE_LCD_CHARCELLS
1855 for (i
= 0; i
< 8; i
++)
1857 if (data
->wps_progress_pat
[i
] == 0)
1858 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1864 display
->stop_scroll();
1868 state
->ff_rewind_count
= ffwd_offset
;
1870 for (line
= 0; line
< data
->num_lines
; line
++)
1872 memset(linebuf
, 0, sizeof(linebuf
));
1873 update_line
= false;
1875 /* get current subline for the line */
1876 new_subline_refresh
= update_curr_subline(gwps
, line
);
1878 subline_idx
= wps_subline_index(data
, line
,
1879 data
->lines
[line
].curr_subline
);
1880 flags
= data
->sublines
[subline_idx
].line_type
;
1882 if (refresh_mode
== WPS_REFRESH_ALL
|| (flags
& refresh_mode
)
1883 || new_subline_refresh
)
1885 /* get_line tells us if we need to update the line */
1886 update_line
= get_line(gwps
, line
, data
->lines
[line
].curr_subline
,
1887 &align
, linebuf
, sizeof(linebuf
));
1890 #ifdef HAVE_LCD_BITMAP
1892 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1894 /* the progressbar should be alone on its line */
1895 update_line
= false;
1896 draw_progressbar(gwps
, line
);
1900 if (flags
& refresh_mode
& WPS_REFRESH_PEAK_METER
)
1902 /* the peakmeter should be alone on its line */
1903 update_line
= false;
1905 int h
= font_get(FONT_UI
)->height
;
1906 int peak_meter_y
= display
->getymargin() + line
* h
;
1908 /* The user might decide to have the peak meter in the last
1909 line so that it is only displayed if no status bar is
1910 visible. If so we neither want do draw nor enable the
1912 if (peak_meter_y
+ h
<= display
->height
) {
1913 /* found a line with a peak meter -> remember that we must
1916 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1917 MIN(h
, display
->height
- peak_meter_y
));
1921 #else /* HAVE_LCD_CHARCELL */
1924 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1926 if (data
->full_line_progressbar
)
1927 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1929 draw_player_progress(gwps
);
1935 if (flags
& WPS_REFRESH_SCROLL
)
1937 /* if the line is a scrolling one we don't want to update
1938 too often, so that it has the time to scroll */
1939 if ((refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1940 write_line(display
, &align
, line
, true);
1943 write_line(display
, &align
, line
, false);
1947 #ifdef HAVE_LCD_BITMAP
1948 data
->peak_meter_enabled
= enable_pm
;
1949 wps_display_images(gwps
);
1954 #ifdef HAVE_BACKLIGHT
1955 if (global_settings
.caption_backlight
&& state
->id3
)
1957 /* turn on backlight n seconds before track ends, and turn it off n
1958 seconds into the new track. n == backlight_timeout, or 5s */
1959 int n
= backlight_timeout_value
[global_settings
.backlight_timeout
]
1963 n
= 5000; /* use 5s if backlight is always on or off */
1965 if (((state
->id3
->elapsed
< 1000) ||
1966 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
1967 (state
->paused
== false))
1971 #ifdef HAVE_REMOTE_LCD
1972 if (global_settings
.remote_caption_backlight
&& state
->id3
)
1974 /* turn on remote backlight n seconds before track ends, and turn it
1975 off n seconds into the new track. n == remote_backlight_timeout,
1977 int n
= backlight_timeout_value
[global_settings
.remote_backlight_timeout
]
1981 n
= 5000; /* use 5s if backlight is always on or off */
1983 if (((state
->id3
->elapsed
< 1000) ||
1984 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
1985 (state
->paused
== false))
1986 remote_backlight_on();