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"
25 #include "core_alloc.h"
29 #include "rbunicode.h"
31 #include "powermgmt.h"
39 #include "statusbar.h"
41 #include "scrollbar.h"
42 #include "screen_access.h"
48 #ifdef HAVE_LCD_BITMAP
49 #include "peakmeter.h"
58 #if CONFIG_CODEC == SWCODEC
67 #include "root_menu.h"
70 #include "wps_internals.h"
71 #include "skin_engine.h"
72 #include "statusbar-skinned.h"
73 #include "skin_display.h"
75 void skin_render(struct gui_wps
*gwps
, unsigned refresh_mode
);
77 /* update a skinned screen, update_type is WPS_REFRESH_* values.
78 * Usually it should only be WPS_REFRESH_NON_STATIC
79 * A full update will be done if required (skin_do_full_update() == true)
81 void skin_update(enum skinnable_screens skin
, enum screen_type screen
,
82 unsigned int update_type
)
84 struct gui_wps
*gwps
= skin_get_gwps(skin
, screen
);
85 /* This maybe shouldnt be here,
86 * This is also safe for skined screen which dont use the id3 */
87 struct mp3entry
*id3
= skin_get_global_state()->id3
;
88 bool cuesheet_update
= (id3
!= NULL
? cuesheet_subtrack_changed(id3
) : false);
90 skin_request_full_update(skin
);
92 skin_render(gwps
, skin_do_full_update(skin
, screen
) ?
93 SKIN_REFRESH_ALL
: update_type
);
96 #ifdef HAVE_LCD_BITMAP
98 void draw_progressbar(struct gui_wps
*gwps
, int line
, struct progressbar
*pb
)
100 struct screen
*display
= gwps
->display
;
101 struct viewport
*vp
= SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->vp
);
102 struct wps_state
*state
= skin_get_global_state();
103 struct mp3entry
*id3
= state
->id3
;
104 int x
= pb
->x
, y
= pb
->y
, width
= pb
->width
, height
= pb
->height
;
105 unsigned long length
, end
;
106 int flags
= HORIZONTAL
;
109 height
= font_get(vp
->font
)->height
;
113 int line_height
= font_get(vp
->font
)->height
;
114 /* center the pb in the line, but only if the line is higher than the pb */
115 int center
= (line_height
-height
)/2;
116 /* if Y was not set calculate by font height,Y is -line_number-1 */
117 y
= line
*line_height
+ (0 > center
? 0 : center
);
120 if (pb
->type
== SKIN_TOKEN_VOLUMEBAR
)
122 int minvol
= sound_min(SOUND_VOLUME
);
123 int maxvol
= sound_max(SOUND_VOLUME
);
124 length
= maxvol
-minvol
;
125 end
= global_settings
.volume
-minvol
;
127 else if (pb
->type
== SKIN_TOKEN_BATTERY_PERCENTBAR
)
130 end
= battery_level();
132 else if (pb
->type
== SKIN_TOKEN_PEAKMETER_LEFTBAR
||
133 pb
->type
== SKIN_TOKEN_PEAKMETER_RIGHTBAR
)
135 int left
, right
, val
;
136 peak_meter_current_vals(&left
, &right
);
137 val
= pb
->type
== SKIN_TOKEN_PEAKMETER_LEFTBAR
? left
: right
;
139 end
= peak_meter_scale_value(val
, length
);
141 else if (pb
->type
== SKIN_TOKEN_LIST_SCROLLBAR
)
144 skinlist_get_scrollbar(&val
, &min
, &max
);
149 else if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
151 #ifdef HAVE_RADIO_RSSI
152 if (pb
->type
== SKIN_TOKEN_TUNER_RSSI_BAR
)
154 int val
= tuner_get(RADIO_RSSI
);
155 int min
= tuner_get(RADIO_RSSI_MIN
);
156 int max
= tuner_get(RADIO_RSSI_MAX
);
163 int min
= fm_region_data
[global_settings
.fm_region
].freq_min
;
164 end
= radio_current_frequency() - min
;
165 length
= fm_region_data
[global_settings
.fm_region
].freq_max
- min
;
169 else if (id3
&& id3
->length
)
171 length
= id3
->length
;
172 end
= id3
->elapsed
+ state
->ff_rewind_count
;
182 /* we want to fill upwards which is technically inverted. */
186 if (pb
->invert_fill_direction
)
193 flags
|= INNER_NOFILL
;
196 if (SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->slider
))
198 struct gui_img
*img
= SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->slider
);
199 /* clear the slider */
200 screen_clear_area(display
, x
, y
, width
, height
);
202 /* shrink the bar so the slider is inside bounds */
203 if (flags
&HORIZONTAL
)
205 width
-= img
->bm
.width
;
206 x
+= img
->bm
.width
/ 2;
210 height
-= img
->bm
.height
;
211 y
+= img
->bm
.height
/ 2;
215 if (SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->backdrop
))
217 struct gui_img
*img
= SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->backdrop
);
218 img
->bm
.data
= core_get_data(img
->buflib_handle
);
219 display
->bmp_part(&img
->bm
, 0, 0, x
, y
, width
, height
);
220 flags
|= DONT_CLEAR_EXCESS
;
225 struct gui_img
*img
= SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->image
);
228 char *img_data
= core_get_data(img
->buflib_handle
);
229 img
->bm
.data
= img_data
;
230 gui_bitmap_scrollbar_draw(display
, &img
->bm
,
232 length
, 0, end
, flags
);
235 gui_scrollbar_draw(display
, x
, y
, width
, height
,
236 length
, 0, end
, flags
);
239 if (SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->slider
))
241 int xoff
= 0, yoff
= 0;
242 int w
= width
, h
= height
;
243 struct gui_img
*img
= SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->slider
);
244 img
->bm
.data
= core_get_data(img
->buflib_handle
);
246 if (flags
&HORIZONTAL
)
249 xoff
= width
* end
/ length
;
250 if (flags
&INVERTFILL
)
257 yoff
= height
* end
/ length
;
258 if (flags
&INVERTFILL
)
259 yoff
= height
- yoff
;
262 display
->bmp_part(&img
->bm
, 0, 0, x
+ xoff
, y
+ yoff
, w
, h
);
265 if (pb
->type
== SKIN_TOKEN_PROGRESSBAR
)
267 if (id3
&& id3
->length
)
269 #ifdef AB_REPEAT_ENABLE
270 if (ab_repeat_mode_enabled())
271 ab_draw_markers(display
, id3
->length
, x
, y
, width
, height
);
275 cue_draw_markers(display
, id3
->cuesheet
, id3
->length
,
276 x
, y
+1, width
, height
-2);
278 #if 0 /* disable for now CONFIG_TUNER */
279 else if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
281 presets_draw_markers(display
, x
, y
, width
, height
);
287 /* clears the area where the image was shown */
288 void clear_image_pos(struct gui_wps
*gwps
, struct gui_img
*img
)
292 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
293 gwps
->display
->fillrect(img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
294 gwps
->display
->set_drawmode(DRMODE_SOLID
);
297 void wps_draw_image(struct gui_wps
*gwps
, struct gui_img
*img
, int subimage
)
299 struct screen
*display
= gwps
->display
;
300 img
->bm
.data
= core_get_data(img
->buflib_handle
);
301 if(img
->always_display
)
302 display
->set_drawmode(DRMODE_FG
);
304 display
->set_drawmode(DRMODE_SOLID
);
306 display
->bmp_part(&img
->bm
, 0, img
->subimage_height
* subimage
,
307 img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
311 void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
313 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
316 struct wps_data
*data
= gwps
->data
;
317 struct screen
*display
= gwps
->display
;
318 struct skin_token_list
*list
= SKINOFFSETTOPTR(get_skin_buffer(data
), data
->images
);
322 struct wps_token
*token
= SKINOFFSETTOPTR(get_skin_buffer(data
), list
->token
);
323 struct gui_img
*img
= (struct gui_img
*)SKINOFFSETTOPTR(get_skin_buffer(data
), token
->value
.data
);
324 if (img
->using_preloaded_icons
&& img
->display
>= 0)
326 screen_put_icon(display
, img
->x
, img
->y
, img
->display
);
328 else if (img
->loaded
)
330 if (img
->display
>= 0)
332 wps_draw_image(gwps
, img
, img
->display
);
334 else if (img
->always_display
&& SKINOFFSETTOPTR(get_skin_buffer(data
), img
->vp
) == vp
)
336 wps_draw_image(gwps
, img
, 0);
339 list
= SKINOFFSETTOPTR(get_skin_buffer(data
), list
->next
);
342 /* now draw the AA */
343 struct skin_albumart
*aa
= SKINOFFSETTOPTR(get_skin_buffer(data
), data
->albumart
);
344 if (aa
&& SKINOFFSETTOPTR(get_skin_buffer(data
), aa
->vp
) == vp
345 && aa
->draw_handle
>= 0)
347 draw_album_art(gwps
, aa
->draw_handle
, false);
348 aa
->draw_handle
= -1;
352 display
->set_drawmode(DRMODE_SOLID
);
355 #endif /* HAVE_LCD_BITMAP */
357 /* Evaluate the conditional that is at *token_index and return whether a skip
358 has ocurred. *token_index is updated with the new position.
360 int evaluate_conditional(struct gui_wps
*gwps
, int offset
,
361 struct conditional
*conditional
, int num_options
)
369 int intval
= num_options
< 2 ? 2 : num_options
;
370 /* get_token_value needs to know the number of options in the enum */
371 value
= get_token_value(gwps
, SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), conditional
->token
),
372 offset
, result
, sizeof(result
), &intval
);
374 /* intval is now the number of the enum option we want to read,
375 starting from 1. If intval is -1, we check if value is empty. */
378 if (num_options
== 1) /* so %?AA<true> */
379 intval
= (value
&& *value
) ? 1 : 0; /* returned as 0 for true, -1 for false */
381 intval
= (value
&& *value
) ? 1 : num_options
;
383 else if (intval
> num_options
|| intval
< 1)
384 intval
= num_options
;
390 /* Display a line appropriately according to its alignment format.
391 format_align contains the text, separated between left, center and right.
392 line is the index of the line on the screen.
393 scroll indicates whether the line is a scrolling one or not.
395 void write_line(struct screen
*display
, struct align_pos
*format_align
,
396 int line
, bool scroll
, unsigned style
)
398 #ifndef HAVE_LCD_BITMAP
402 int center_width
= 0, center_xpos
;
403 int right_width
= 0, right_xpos
;
407 int viewport_width
= display
->getwidth();
409 /* calculate different string sizes and positions */
410 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
411 if (format_align
->left
!= 0) {
412 display
->getstringsize((unsigned char *)format_align
->left
,
413 &left_width
, &string_height
);
416 if (format_align
->right
!= 0) {
417 display
->getstringsize((unsigned char *)format_align
->right
,
418 &right_width
, &string_height
);
421 if (format_align
->center
!= 0) {
422 display
->getstringsize((unsigned char *)format_align
->center
,
423 ¢er_width
, &string_height
);
426 right_xpos
= (viewport_width
- right_width
);
427 center_xpos
= (viewport_width
- center_width
) / 2;
429 scroll_width
= viewport_width
;
431 /* Checks for overlapping strings.
432 If needed the overlapping strings will be merged, separated by a
435 /* CASE 1: left and centered string overlap */
436 /* there is a left string, need to merge left and center */
437 if ((left_width
!= 0 && center_width
!= 0) &&
438 (left_width
+ space_width
> center_xpos
)) {
439 /* replace the former separator '\0' of left and
440 center string with a space */
441 *(--format_align
->center
) = ' ';
442 /* calculate the new width and position of the merged string */
443 left_width
= left_width
+ space_width
+ center_width
;
444 /* there is no centered string anymore */
447 /* there is no left string, move center to left */
448 if ((left_width
== 0 && center_width
!= 0) &&
449 (left_width
> center_xpos
)) {
450 /* move the center string to the left string */
451 format_align
->left
= format_align
->center
;
452 /* calculate the new width and position of the string */
453 left_width
= center_width
;
454 /* there is no centered string anymore */
458 /* CASE 2: centered and right string overlap */
459 /* there is a right string, need to merge center and right */
460 if ((center_width
!= 0 && right_width
!= 0) &&
461 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
462 /* replace the former separator '\0' of center and
463 right string with a space */
464 *(--format_align
->right
) = ' ';
465 /* move the center string to the right after merge */
466 format_align
->right
= format_align
->center
;
467 /* calculate the new width and position of the merged string */
468 right_width
= center_width
+ space_width
+ right_width
;
469 right_xpos
= (viewport_width
- right_width
);
470 /* there is no centered string anymore */
473 /* there is no right string, move center to right */
474 if ((center_width
!= 0 && right_width
== 0) &&
475 (center_xpos
+ center_width
> right_xpos
)) {
476 /* move the center string to the right string */
477 format_align
->right
= format_align
->center
;
478 /* calculate the new width and position of the string */
479 right_width
= center_width
;
480 right_xpos
= (viewport_width
- right_width
);
481 /* there is no centered string anymore */
485 /* CASE 3: left and right overlap
486 There is no center string anymore, either there never
487 was one or it has been merged in case 1 or 2 */
488 /* there is a left string, need to merge left and right */
489 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
490 (left_width
+ space_width
> right_xpos
)) {
491 /* replace the former separator '\0' of left and
492 right string with a space */
493 *(--format_align
->right
) = ' ';
494 /* calculate the new width and position of the string */
495 left_width
= left_width
+ space_width
+ right_width
;
496 /* there is no right string anymore */
499 /* there is no left string, move right to left */
500 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
501 (left_width
> right_xpos
)) {
502 /* move the right string to the left string */
503 format_align
->left
= format_align
->right
;
504 /* calculate the new width and position of the string */
505 left_width
= right_width
;
506 /* there is no right string anymore */
510 if (scroll
&& ((left_width
> scroll_width
) ||
511 (center_width
> scroll_width
) ||
512 (right_width
> scroll_width
)))
514 #ifdef HAVE_LCD_BITMAP
515 display
->puts_scroll_style(0, line
,
516 (unsigned char *)format_align
->left
, style
);
518 display
->puts_scroll(0, line
,
519 (unsigned char *)format_align
->left
);
524 #ifdef HAVE_LCD_BITMAP
525 /* clear the line first */
526 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
527 display
->fillrect(0, line
*string_height
, viewport_width
, string_height
);
528 display
->set_drawmode(DRMODE_SOLID
);
531 /* Nasty hack: we output an empty scrolling string,
532 which will reset the scroller for that line */
533 display
->puts_scroll(0, line
, (unsigned char *)"");
534 #ifdef HAVE_LCD_BITMAP
535 style
|= STYLE_XY_PIXELS
;
536 line
*= string_height
;
537 /* print aligned strings */
540 display
->puts_style_xyoffset(0, line
,
541 (unsigned char *)format_align
->left
, style
, 0, 0);
544 if (center_width
!= 0)
546 display
->puts_style_xyoffset((viewport_width
-center_width
)/2, line
,
547 (unsigned char *)format_align
->center
, style
, 0, 0);
549 if (right_width
!= 0)
551 display
->puts_style_xyoffset(viewport_width
-right_width
, line
,
552 (unsigned char *)format_align
->right
, style
, 0, 0);
557 display
->putsxy(0, line
,
558 (unsigned char *)format_align
->left
);
560 if (center_width
!= 0)
562 display
->putsxy(center_xpos
, line
,
563 (unsigned char *)format_align
->center
);
565 if (right_width
!= 0)
567 display
->putsxy(right_xpos
, line
,
568 (unsigned char *)format_align
->right
);
574 #ifdef HAVE_LCD_BITMAP
575 void draw_peakmeters(struct gui_wps
*gwps
, int line_number
,
576 struct viewport
*viewport
)
578 struct wps_data
*data
= gwps
->data
;
579 if (!data
->peak_meter_enabled
)
581 peak_meter_enable(false);
585 int h
= font_get(viewport
->font
)->height
;
586 int peak_meter_y
= line_number
* h
;
588 /* The user might decide to have the peak meter in the last
589 line so that it is only displayed if no status bar is
590 visible. If so we neither want do draw nor enable the
592 if (peak_meter_y
+ h
<= viewport
->y
+viewport
->height
) {
593 peak_meter_enable(true);
594 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
595 MIN(h
, viewport
->y
+viewport
->height
- peak_meter_y
));
600 bool skin_has_sbs(enum screen_type screen
, struct wps_data
*data
)
605 #ifdef HAVE_LCD_BITMAP
606 if (data
->wps_sb_tag
)
607 draw
= data
->show_sb_on_wps
;
608 else if (statusbar_position(screen
) != STATUSBAR_OFF
)
615 /* do the button loop as often as required for the peak meters to update
616 * with a good refresh rate.
618 int skin_wait_for_action(enum skinnable_screens skin
, int context
, int timeout
)
620 (void)skin
; /* silence charcell warning */
621 int button
= ACTION_NONE
;
622 #ifdef HAVE_LCD_BITMAP
623 /* when the peak meter is enabled we want to have a
624 few extra updates to make it look smooth. On the
625 other hand we don't want to waste energy if it
630 if(skin_get_gwps(skin
, i
)->data
->peak_meter_enabled
)
635 long next_refresh
= current_tick
;
636 long next_big_refresh
= current_tick
+ timeout
;
637 button
= BUTTON_NONE
;
638 while (TIME_BEFORE(current_tick
, next_big_refresh
)) {
639 button
= get_action(context
,TIMEOUT_NOBLOCK
);
640 if (button
!= ACTION_NONE
) {
644 sleep(0); /* Sleep until end of current tick. */
646 if (TIME_AFTER(current_tick
, next_refresh
)) {
649 if(skin_get_gwps(skin
, i
)->data
->peak_meter_enabled
)
650 skin_update(skin
, i
, SKIN_REFRESH_PEAK_METER
);
651 next_refresh
+= HZ
/ PEAK_METER_FPS
;
658 /* The peak meter is disabled
659 -> no additional screen updates needed */
663 button
= get_action(context
, timeout
);