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 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
24 #include "string-extra.h"
28 #include "rbunicode.h"
30 #include "powermgmt.h"
38 #include "statusbar.h"
40 #include "scrollbar.h"
41 #include "screen_access.h"
46 #ifdef HAVE_LCD_BITMAP
47 #include "peakmeter.h"
56 #if CONFIG_CODEC == SWCODEC
65 #include "root_menu.h"
68 #include "wps_internals.h"
69 #include "skin_engine.h"
70 #include "statusbar-skinned.h"
71 #include "skin_display.h"
73 void skin_render(struct gui_wps
*gwps
, unsigned refresh_mode
);
75 /* update a skinned screen, update_type is WPS_REFRESH_* values.
76 * Usually it should only be WPS_REFRESH_NON_STATIC
77 * A full update will be done if required (skin_do_full_update() == true)
79 void skin_update(enum skinnable_screens skin
, enum screen_type screen
,
80 unsigned int update_type
)
82 struct gui_wps
*gwps
= skin_get_gwps(skin
, screen
);
83 /* This maybe shouldnt be here,
84 * This is also safe for skined screen which dont use the id3 */
85 struct mp3entry
*id3
= skin_get_global_state()->id3
;
86 bool cuesheet_update
= (id3
!= NULL
? cuesheet_subtrack_changed(id3
) : false);
88 skin_request_full_update(skin
);
90 skin_render(gwps
, skin_do_full_update(skin
, screen
) ?
91 SKIN_REFRESH_ALL
: update_type
);
94 #ifdef HAVE_LCD_BITMAP
96 void skin_statusbar_changed(struct gui_wps
*skin
)
100 struct wps_data
*data
= skin
->data
;
101 const struct screen
*display
= skin
->display
;
102 const int screen
= display
->screen_type
;
104 struct viewport
*vp
= &find_viewport(VP_DEFAULT_LABEL
, false, data
)->vp
;
105 viewport_set_defaults(vp
, screen
);
107 if (data
->wps_sb_tag
)
108 { /* fix up the default viewport */
109 if (data
->show_sb_on_wps
)
111 if (statusbar_position(screen
) != STATUSBAR_OFF
)
112 return; /* vp is fixed already */
114 vp
->y
= STATUSBAR_HEIGHT
;
115 vp
->height
= display
->lcdheight
- STATUSBAR_HEIGHT
;
119 if (statusbar_position(screen
) == STATUSBAR_OFF
)
120 return; /* vp is fixed already */
122 vp
->height
= display
->lcdheight
;
123 vp
->width
= display
->lcdwidth
;
128 void draw_progressbar(struct gui_wps
*gwps
, int line
, struct progressbar
*pb
)
130 struct screen
*display
= gwps
->display
;
131 struct viewport
*vp
= pb
->vp
;
132 struct wps_state
*state
= skin_get_global_state();
133 struct mp3entry
*id3
= state
->id3
;
134 int x
= pb
->x
, y
= pb
->y
, width
= pb
->width
, height
= pb
->height
;
135 unsigned long length
, end
;
136 int flags
= HORIZONTAL
;
139 height
= font_get(vp
->font
)->height
;
143 int line_height
= font_get(vp
->font
)->height
;
144 /* center the pb in the line, but only if the line is higher than the pb */
145 int center
= (line_height
-height
)/2;
146 /* if Y was not set calculate by font height,Y is -line_number-1 */
147 y
= line
*line_height
+ (0 > center
? 0 : center
);
150 if (pb
->type
== SKIN_TOKEN_VOLUMEBAR
)
152 int minvol
= sound_min(SOUND_VOLUME
);
153 int maxvol
= sound_max(SOUND_VOLUME
);
154 length
= maxvol
-minvol
;
155 end
= global_settings
.volume
-minvol
;
157 else if (pb
->type
== SKIN_TOKEN_BATTERY_PERCENTBAR
)
160 end
= battery_level();
162 else if (pb
->type
== SKIN_TOKEN_PEAKMETER_LEFTBAR
||
163 pb
->type
== SKIN_TOKEN_PEAKMETER_RIGHTBAR
)
165 int left
, right
, val
;
166 peak_meter_current_vals(&left
, &right
);
167 val
= pb
->type
== SKIN_TOKEN_PEAKMETER_LEFTBAR
? left
: right
;
169 end
= peak_meter_scale_value(val
, length
);
172 else if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
174 #ifdef HAVE_RADIO_RSSI
175 if (pb
->type
== SKIN_TOKEN_TUNER_RSSI_BAR
)
177 int val
= tuner_get(RADIO_RSSI
);
178 int min
= tuner_get(RADIO_RSSI_MIN
);
179 int max
= tuner_get(RADIO_RSSI_MAX
);
186 int min
= fm_region_data
[global_settings
.fm_region
].freq_min
;
187 end
= radio_current_frequency() - min
;
188 length
= fm_region_data
[global_settings
.fm_region
].freq_max
- min
;
192 else if (id3
&& id3
->length
)
194 length
= id3
->length
;
195 end
= id3
->elapsed
+ state
->ff_rewind_count
;
205 /* we want to fill upwards which is technically inverted. */
209 if (pb
->invert_fill_direction
)
216 flags
|= INNER_NOFILL
;
221 struct gui_img
*img
= pb
->slider
;
222 /* clear the slider */
223 screen_clear_area(display
, x
, y
, width
, height
);
225 /* shrink the bar so the slider is inside bounds */
226 if (flags
&HORIZONTAL
)
228 width
-= img
->bm
.width
;
229 x
+= img
->bm
.width
/ 2;
233 height
-= img
->bm
.height
;
234 y
+= img
->bm
.height
/ 2;
240 gui_bitmap_scrollbar_draw(display
, &pb
->image
->bm
,
242 length
, 0, end
, flags
);
244 gui_scrollbar_draw(display
, x
, y
, width
, height
,
245 length
, 0, end
, flags
);
250 int xoff
= 0, yoff
= 0;
251 int w
= width
, h
= height
;
252 struct gui_img
*img
= pb
->slider
;
254 if (flags
&HORIZONTAL
)
257 xoff
= width
* end
/ length
;
258 if (flags
&INVERTFILL
)
265 yoff
= height
* end
/ length
;
266 if (flags
&INVERTFILL
)
267 yoff
= height
- yoff
;
271 if(img
->bm
.format
== FORMAT_MONO
) {
273 display
->mono_bitmap_part(img
->bm
.data
,
275 x
+ xoff
, y
+ yoff
, w
, h
);
278 display
->transparent_bitmap_part((fb_data
*)img
->bm
.data
,
280 STRIDE(display
->screen_type
,
281 img
->bm
.width
, img
->bm
.height
),
282 x
+ xoff
, y
+ yoff
, w
, h
);
287 if (pb
->type
== SKIN_TOKEN_PROGRESSBAR
)
289 if (id3
&& id3
->length
)
291 #ifdef AB_REPEAT_ENABLE
292 if (ab_repeat_mode_enabled())
293 ab_draw_markers(display
, id3
->length
, x
, y
, width
, height
);
297 cue_draw_markers(display
, id3
->cuesheet
, id3
->length
,
298 x
, y
+1, width
, height
-2);
300 #if 0 /* disable for now CONFIG_TUNER */
301 else if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
303 presets_draw_markers(display
, x
, y
, width
, height
);
309 /* clears the area where the image was shown */
310 void clear_image_pos(struct gui_wps
*gwps
, struct gui_img
*img
)
314 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
315 gwps
->display
->fillrect(img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
316 gwps
->display
->set_drawmode(DRMODE_SOLID
);
319 void wps_draw_image(struct gui_wps
*gwps
, struct gui_img
*img
, int subimage
)
321 struct screen
*display
= gwps
->display
;
322 if(img
->always_display
)
323 display
->set_drawmode(DRMODE_FG
);
325 display
->set_drawmode(DRMODE_SOLID
);
328 if(img
->bm
.format
== FORMAT_MONO
) {
330 display
->mono_bitmap_part(img
->bm
.data
,
331 0, img
->subimage_height
* subimage
,
332 img
->bm
.width
, img
->x
,
333 img
->y
, img
->bm
.width
,
334 img
->subimage_height
);
337 display
->transparent_bitmap_part((fb_data
*)img
->bm
.data
,
338 0, img
->subimage_height
* subimage
,
339 STRIDE(display
->screen_type
,
340 img
->bm
.width
, img
->bm
.height
),
341 img
->x
, img
->y
, img
->bm
.width
,
342 img
->subimage_height
);
348 void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
350 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
353 struct wps_data
*data
= gwps
->data
;
354 struct screen
*display
= gwps
->display
;
355 struct skin_token_list
*list
= data
->images
;
359 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
360 if (img
->using_preloaded_icons
&& img
->display
>= 0)
362 screen_put_icon(display
, img
->x
, img
->y
, img
->display
);
364 else if (img
->loaded
)
366 if (img
->display
>= 0)
368 wps_draw_image(gwps
, img
, img
->display
);
370 else if (img
->always_display
&& img
->vp
== vp
)
372 wps_draw_image(gwps
, img
, 0);
378 /* now draw the AA */
379 if (data
->albumart
&& data
->albumart
->vp
== vp
380 && data
->albumart
->draw_handle
>= 0)
382 draw_album_art(gwps
, data
->albumart
->draw_handle
, false);
383 data
->albumart
->draw_handle
= -1;
387 display
->set_drawmode(DRMODE_SOLID
);
390 #endif /* HAVE_LCD_BITMAP */
392 /* Evaluate the conditional that is at *token_index and return whether a skip
393 has ocurred. *token_index is updated with the new position.
395 int evaluate_conditional(struct gui_wps
*gwps
, int offset
,
396 struct conditional
*conditional
, int num_options
)
404 int intval
= num_options
< 2 ? 2 : num_options
;
405 /* get_token_value needs to know the number of options in the enum */
406 value
= get_token_value(gwps
, conditional
->token
, offset
,
407 result
, sizeof(result
), &intval
);
409 /* intval is now the number of the enum option we want to read,
410 starting from 1. If intval is -1, we check if value is empty. */
413 if (num_options
== 1) /* so %?AA<true> */
414 intval
= (value
&& *value
) ? 1 : 0; /* returned as 0 for true, -1 for false */
416 intval
= (value
&& *value
) ? 1 : num_options
;
418 else if (intval
> num_options
|| intval
< 1)
419 intval
= num_options
;
425 /* Display a line appropriately according to its alignment format.
426 format_align contains the text, separated between left, center and right.
427 line is the index of the line on the screen.
428 scroll indicates whether the line is a scrolling one or not.
430 void write_line(struct screen
*display
,
431 struct align_pos
*format_align
,
435 int left_width
= 0, left_xpos
;
436 int center_width
= 0, center_xpos
;
437 int right_width
= 0, right_xpos
;
443 /* calculate different string sizes and positions */
444 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
445 if (format_align
->left
!= 0) {
446 display
->getstringsize((unsigned char *)format_align
->left
,
447 &left_width
, &string_height
);
450 if (format_align
->right
!= 0) {
451 display
->getstringsize((unsigned char *)format_align
->right
,
452 &right_width
, &string_height
);
455 if (format_align
->center
!= 0) {
456 display
->getstringsize((unsigned char *)format_align
->center
,
457 ¢er_width
, &string_height
);
461 right_xpos
= (display
->getwidth() - right_width
);
462 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
464 scroll_width
= display
->getwidth() - left_xpos
;
466 /* Checks for overlapping strings.
467 If needed the overlapping strings will be merged, separated by a
470 /* CASE 1: left and centered string overlap */
471 /* there is a left string, need to merge left and center */
472 if ((left_width
!= 0 && center_width
!= 0) &&
473 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
474 /* replace the former separator '\0' of left and
475 center string with a space */
476 *(--format_align
->center
) = ' ';
477 /* calculate the new width and position of the merged string */
478 left_width
= left_width
+ space_width
+ center_width
;
479 /* there is no centered string anymore */
482 /* there is no left string, move center to left */
483 if ((left_width
== 0 && center_width
!= 0) &&
484 (left_xpos
+ left_width
> center_xpos
)) {
485 /* move the center string to the left string */
486 format_align
->left
= format_align
->center
;
487 /* calculate the new width and position of the string */
488 left_width
= center_width
;
489 /* there is no centered string anymore */
493 /* CASE 2: centered and right string overlap */
494 /* there is a right string, need to merge center and right */
495 if ((center_width
!= 0 && right_width
!= 0) &&
496 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
497 /* replace the former separator '\0' of center and
498 right string with a space */
499 *(--format_align
->right
) = ' ';
500 /* move the center string to the right after merge */
501 format_align
->right
= format_align
->center
;
502 /* calculate the new width and position of the merged string */
503 right_width
= center_width
+ space_width
+ right_width
;
504 right_xpos
= (display
->getwidth() - right_width
);
505 /* there is no centered string anymore */
508 /* there is no right string, move center to right */
509 if ((center_width
!= 0 && right_width
== 0) &&
510 (center_xpos
+ center_width
> right_xpos
)) {
511 /* move the center string to the right string */
512 format_align
->right
= format_align
->center
;
513 /* calculate the new width and position of the string */
514 right_width
= center_width
;
515 right_xpos
= (display
->getwidth() - right_width
);
516 /* there is no centered string anymore */
520 /* CASE 3: left and right overlap
521 There is no center string anymore, either there never
522 was one or it has been merged in case 1 or 2 */
523 /* there is a left string, need to merge left and right */
524 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
525 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
526 /* replace the former separator '\0' of left and
527 right string with a space */
528 *(--format_align
->right
) = ' ';
529 /* calculate the new width and position of the string */
530 left_width
= left_width
+ space_width
+ right_width
;
531 /* there is no right string anymore */
534 /* there is no left string, move right to left */
535 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
536 (left_width
> right_xpos
)) {
537 /* move the right string to the left string */
538 format_align
->left
= format_align
->right
;
539 /* calculate the new width and position of the string */
540 left_width
= right_width
;
541 /* there is no right string anymore */
545 ypos
= (line
* string_height
);
548 if (scroll
&& ((left_width
> scroll_width
) ||
549 (center_width
> scroll_width
) ||
550 (right_width
> scroll_width
)))
552 display
->puts_scroll(0, line
,
553 (unsigned char *)format_align
->left
);
557 #ifdef HAVE_LCD_BITMAP
558 /* clear the line first */
559 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
560 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
561 display
->set_drawmode(DRMODE_SOLID
);
564 /* Nasty hack: we output an empty scrolling string,
565 which will reset the scroller for that line */
566 display
->puts_scroll(0, line
, (unsigned char *)"");
568 /* print aligned strings */
571 display
->putsxy(left_xpos
, ypos
,
572 (unsigned char *)format_align
->left
);
574 if (center_width
!= 0)
576 display
->putsxy(center_xpos
, ypos
,
577 (unsigned char *)format_align
->center
);
579 if (right_width
!= 0)
581 display
->putsxy(right_xpos
, ypos
,
582 (unsigned char *)format_align
->right
);
587 #ifdef HAVE_LCD_BITMAP
588 void draw_peakmeters(struct gui_wps
*gwps
, int line_number
,
589 struct viewport
*viewport
)
591 struct wps_data
*data
= gwps
->data
;
592 if (!data
->peak_meter_enabled
)
594 peak_meter_enable(false);
598 int h
= font_get(viewport
->font
)->height
;
599 int peak_meter_y
= line_number
* h
;
601 /* The user might decide to have the peak meter in the last
602 line so that it is only displayed if no status bar is
603 visible. If so we neither want do draw nor enable the
605 if (peak_meter_y
+ h
<= viewport
->y
+viewport
->height
) {
606 peak_meter_enable(true);
607 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
608 MIN(h
, viewport
->y
+viewport
->height
- peak_meter_y
));
613 bool skin_has_sbs(enum screen_type screen
, struct wps_data
*data
)
618 #ifdef HAVE_LCD_BITMAP
619 if (data
->wps_sb_tag
)
620 draw
= data
->show_sb_on_wps
;
621 else if (statusbar_position(screen
) != STATUSBAR_OFF
)
628 /* do the button loop as often as required for the peak meters to update
629 * with a good refresh rate.
631 int skin_wait_for_action(enum skinnable_screens skin
, int context
, int timeout
)
633 (void)skin
; /* silence charcell warning */
634 int button
= ACTION_NONE
;
635 #ifdef HAVE_LCD_BITMAP
637 /* when the peak meter is enabled we want to have a
638 few extra updates to make it look smooth. On the
639 other hand we don't want to waste energy if it
644 if(skin_get_gwps(skin
, i
)->data
->peak_meter_enabled
)
649 long next_refresh
= current_tick
;
650 long next_big_refresh
= current_tick
+ timeout
;
651 button
= BUTTON_NONE
;
652 while (TIME_BEFORE(current_tick
, next_big_refresh
)) {
653 button
= get_action(context
,TIMEOUT_NOBLOCK
);
654 if (button
!= ACTION_NONE
) {
658 sleep(0); /* Sleep until end of current tick. */
660 if (TIME_AFTER(current_tick
, next_refresh
)) {
663 if(skin_get_gwps(skin
, i
)->data
->peak_meter_enabled
)
664 skin_update(skin
, i
, SKIN_REFRESH_PEAK_METER
);
665 next_refresh
+= HZ
/ PEAK_METER_FPS
;
672 /* The peak meter is disabled
673 -> no additional screen updates needed */
677 button
= get_action(context
, timeout
);