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"
47 #include "option_select.h"
49 #ifdef HAVE_LCD_BITMAP
50 #include "peakmeter.h"
59 #if CONFIG_CODEC == SWCODEC
68 #include "root_menu.h"
71 #include "wps_internals.h"
72 #include "skin_engine.h"
73 #include "statusbar-skinned.h"
74 #include "skin_display.h"
76 void skin_render(struct gui_wps
*gwps
, unsigned refresh_mode
);
78 /* update a skinned screen, update_type is WPS_REFRESH_* values.
79 * Usually it should only be WPS_REFRESH_NON_STATIC
80 * A full update will be done if required (skin_do_full_update() == true)
82 void skin_update(enum skinnable_screens skin
, enum screen_type screen
,
83 unsigned int update_type
)
85 struct gui_wps
*gwps
= skin_get_gwps(skin
, screen
);
86 /* This maybe shouldnt be here,
87 * This is also safe for skined screen which dont use the id3 */
88 struct mp3entry
*id3
= skin_get_global_state()->id3
;
89 bool cuesheet_update
= (id3
!= NULL
? cuesheet_subtrack_changed(id3
) : false);
91 skin_request_full_update(skin
);
93 skin_render(gwps
, skin_do_full_update(skin
, screen
) ?
94 SKIN_REFRESH_ALL
: update_type
);
97 #ifdef HAVE_LCD_BITMAP
99 void draw_progressbar(struct gui_wps
*gwps
, int line
, struct progressbar
*pb
)
101 struct screen
*display
= gwps
->display
;
102 struct viewport
*vp
= SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->vp
);
103 struct wps_state
*state
= skin_get_global_state();
104 struct mp3entry
*id3
= state
->id3
;
105 int x
= pb
->x
, y
= pb
->y
, width
= pb
->width
, height
= pb
->height
;
106 unsigned long length
, end
;
107 int flags
= HORIZONTAL
;
110 height
= font_get(vp
->font
)->height
;
114 int line_height
= font_get(vp
->font
)->height
;
115 /* center the pb in the line, but only if the line is higher than the pb */
116 int center
= (line_height
-height
)/2;
117 /* if Y was not set calculate by font height,Y is -line_number-1 */
118 y
= line
*line_height
+ (0 > center
? 0 : center
);
121 if (pb
->type
== SKIN_TOKEN_VOLUMEBAR
)
123 int minvol
= sound_min(SOUND_VOLUME
);
124 int maxvol
= sound_max(SOUND_VOLUME
);
125 length
= maxvol
-minvol
;
126 end
= global_settings
.volume
-minvol
;
128 else if (pb
->type
== SKIN_TOKEN_BATTERY_PERCENTBAR
)
131 end
= battery_level();
133 else if (pb
->type
== SKIN_TOKEN_PEAKMETER_LEFTBAR
||
134 pb
->type
== SKIN_TOKEN_PEAKMETER_RIGHTBAR
)
136 int left
, right
, val
;
137 peak_meter_current_vals(&left
, &right
);
138 val
= pb
->type
== SKIN_TOKEN_PEAKMETER_LEFTBAR
? left
: right
;
140 end
= peak_meter_scale_value(val
, length
);
142 else if (pb
->type
== SKIN_TOKEN_LIST_SCROLLBAR
)
145 skinlist_get_scrollbar(&val
, &min
, &max
);
149 else if (pb
->type
== SKIN_TOKEN_SETTINGBAR
)
152 get_setting_info_for_bar(pb
->setting_id
, &count
, &val
);
157 else if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
159 #ifdef HAVE_RADIO_RSSI
160 if (pb
->type
== SKIN_TOKEN_TUNER_RSSI_BAR
)
162 int val
= tuner_get(RADIO_RSSI
);
163 int min
= tuner_get(RADIO_RSSI_MIN
);
164 int max
= tuner_get(RADIO_RSSI_MAX
);
171 int min
= fm_region_data
[global_settings
.fm_region
].freq_min
;
172 end
= radio_current_frequency() - min
;
173 length
= fm_region_data
[global_settings
.fm_region
].freq_max
- min
;
177 else if (id3
&& id3
->length
)
179 length
= id3
->length
;
180 end
= id3
->elapsed
+ state
->ff_rewind_count
;
190 /* we want to fill upwards which is technically inverted. */
194 if (pb
->invert_fill_direction
)
201 flags
|= INNER_NOFILL
;
204 if (SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->slider
))
206 struct gui_img
*img
= SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->slider
);
207 /* clear the slider */
208 screen_clear_area(display
, x
, y
, width
, height
);
210 /* shrink the bar so the slider is inside bounds */
211 if (flags
&HORIZONTAL
)
213 width
-= img
->bm
.width
;
214 x
+= img
->bm
.width
/ 2;
218 height
-= img
->bm
.height
;
219 y
+= img
->bm
.height
/ 2;
223 if (SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->backdrop
))
225 struct gui_img
*img
= SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->backdrop
);
226 img
->bm
.data
= core_get_data(img
->buflib_handle
);
227 display
->bmp_part(&img
->bm
, 0, 0, x
, y
, width
, height
);
228 flags
|= DONT_CLEAR_EXCESS
;
233 struct gui_img
*img
= SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->image
);
236 char *img_data
= core_get_data(img
->buflib_handle
);
237 img
->bm
.data
= img_data
;
238 gui_bitmap_scrollbar_draw(display
, &img
->bm
,
240 length
, 0, end
, flags
);
243 gui_scrollbar_draw(display
, x
, y
, width
, height
,
244 length
, 0, end
, flags
);
247 if (SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->slider
))
249 int xoff
= 0, yoff
= 0;
250 int w
= width
, h
= height
;
251 struct gui_img
*img
= SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), pb
->slider
);
252 img
->bm
.data
= core_get_data(img
->buflib_handle
);
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
;
270 display
->bmp_part(&img
->bm
, 0, 0, x
+ xoff
, y
+ yoff
, w
, h
);
273 if (pb
->type
== SKIN_TOKEN_PROGRESSBAR
)
275 if (id3
&& id3
->length
)
277 #ifdef AB_REPEAT_ENABLE
278 if (ab_repeat_mode_enabled())
279 ab_draw_markers(display
, id3
->length
, x
, y
, width
, height
);
283 cue_draw_markers(display
, id3
->cuesheet
, id3
->length
,
284 x
, y
+1, width
, height
-2);
286 #if 0 /* disable for now CONFIG_TUNER */
287 else if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
289 presets_draw_markers(display
, x
, y
, width
, height
);
295 /* clears the area where the image was shown */
296 void clear_image_pos(struct gui_wps
*gwps
, struct gui_img
*img
)
300 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
301 gwps
->display
->fillrect(img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
302 gwps
->display
->set_drawmode(DRMODE_SOLID
);
305 void wps_draw_image(struct gui_wps
*gwps
, struct gui_img
*img
, int subimage
)
307 struct screen
*display
= gwps
->display
;
308 img
->bm
.data
= core_get_data(img
->buflib_handle
);
309 display
->set_drawmode(DRMODE_SOLID
);
311 display
->bmp_part(&img
->bm
, 0, img
->subimage_height
* subimage
,
312 img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
316 void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
318 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
321 struct wps_data
*data
= gwps
->data
;
322 struct screen
*display
= gwps
->display
;
323 struct skin_token_list
*list
= SKINOFFSETTOPTR(get_skin_buffer(data
), data
->images
);
327 struct wps_token
*token
= SKINOFFSETTOPTR(get_skin_buffer(data
), list
->token
);
328 struct gui_img
*img
= (struct gui_img
*)SKINOFFSETTOPTR(get_skin_buffer(data
), token
->value
.data
);
329 if (img
->using_preloaded_icons
&& img
->display
>= 0)
331 screen_put_icon(display
, img
->x
, img
->y
, img
->display
);
333 else if (img
->loaded
)
335 if (img
->display
>= 0)
337 wps_draw_image(gwps
, img
, img
->display
);
340 list
= SKINOFFSETTOPTR(get_skin_buffer(data
), list
->next
);
343 /* now draw the AA */
344 struct skin_albumart
*aa
= SKINOFFSETTOPTR(get_skin_buffer(data
), data
->albumart
);
345 if (aa
&& SKINOFFSETTOPTR(get_skin_buffer(data
), aa
->vp
) == vp
346 && aa
->draw_handle
>= 0)
348 draw_album_art(gwps
, aa
->draw_handle
, false);
349 aa
->draw_handle
= -1;
353 display
->set_drawmode(DRMODE_SOLID
);
356 #endif /* HAVE_LCD_BITMAP */
358 /* Evaluate the conditional that is at *token_index and return whether a skip
359 has ocurred. *token_index is updated with the new position.
361 int evaluate_conditional(struct gui_wps
*gwps
, int offset
,
362 struct conditional
*conditional
, int num_options
)
370 int intval
= num_options
< 2 ? 2 : num_options
;
371 /* get_token_value needs to know the number of options in the enum */
372 value
= get_token_value(gwps
, SKINOFFSETTOPTR(get_skin_buffer(gwps
->data
), conditional
->token
),
373 offset
, result
, sizeof(result
), &intval
);
375 /* intval is now the number of the enum option we want to read,
376 starting from 1. If intval is -1, we check if value is empty. */
379 if (num_options
== 1) /* so %?AA<true> */
380 intval
= (value
&& *value
) ? 1 : 0; /* returned as 0 for true, -1 for false */
382 intval
= (value
&& *value
) ? 1 : num_options
;
384 else if (intval
> num_options
|| intval
< 1)
385 intval
= num_options
;
391 /* Display a line appropriately according to its alignment format.
392 format_align contains the text, separated between left, center and right.
393 line is the index of the line on the screen.
394 scroll indicates whether the line is a scrolling one or not.
396 void write_line(struct screen
*display
, struct align_pos
*format_align
,
397 int line
, bool scroll
, unsigned style
)
399 #ifndef HAVE_LCD_BITMAP
403 int center_width
= 0, center_xpos
;
404 int right_width
= 0, right_xpos
;
408 int viewport_width
= display
->getwidth();
410 /* calculate different string sizes and positions */
411 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
412 if (format_align
->left
!= 0) {
413 display
->getstringsize((unsigned char *)format_align
->left
,
414 &left_width
, &string_height
);
417 if (format_align
->right
!= 0) {
418 display
->getstringsize((unsigned char *)format_align
->right
,
419 &right_width
, &string_height
);
422 if (format_align
->center
!= 0) {
423 display
->getstringsize((unsigned char *)format_align
->center
,
424 ¢er_width
, &string_height
);
427 right_xpos
= (viewport_width
- right_width
);
428 center_xpos
= (viewport_width
- center_width
) / 2;
430 scroll_width
= viewport_width
;
432 /* Checks for overlapping strings.
433 If needed the overlapping strings will be merged, separated by a
436 /* CASE 1: left and centered string overlap */
437 /* there is a left string, need to merge left and center */
438 if ((left_width
!= 0 && center_width
!= 0) &&
439 (left_width
+ space_width
> center_xpos
)) {
440 /* replace the former separator '\0' of left and
441 center string with a space */
442 *(--format_align
->center
) = ' ';
443 /* calculate the new width and position of the merged string */
444 left_width
= left_width
+ space_width
+ center_width
;
445 /* there is no centered string anymore */
448 /* there is no left string, move center to left */
449 if ((left_width
== 0 && center_width
!= 0) &&
450 (left_width
> center_xpos
)) {
451 /* move the center string to the left string */
452 format_align
->left
= format_align
->center
;
453 /* calculate the new width and position of the string */
454 left_width
= center_width
;
455 /* there is no centered string anymore */
459 /* CASE 2: centered and right string overlap */
460 /* there is a right string, need to merge center and right */
461 if ((center_width
!= 0 && right_width
!= 0) &&
462 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
463 /* replace the former separator '\0' of center and
464 right string with a space */
465 *(--format_align
->right
) = ' ';
466 /* move the center string to the right after merge */
467 format_align
->right
= format_align
->center
;
468 /* calculate the new width and position of the merged string */
469 right_width
= center_width
+ space_width
+ right_width
;
470 right_xpos
= (viewport_width
- right_width
);
471 /* there is no centered string anymore */
474 /* there is no right string, move center to right */
475 if ((center_width
!= 0 && right_width
== 0) &&
476 (center_xpos
+ center_width
> right_xpos
)) {
477 /* move the center string to the right string */
478 format_align
->right
= format_align
->center
;
479 /* calculate the new width and position of the string */
480 right_width
= center_width
;
481 right_xpos
= (viewport_width
- right_width
);
482 /* there is no centered string anymore */
486 /* CASE 3: left and right overlap
487 There is no center string anymore, either there never
488 was one or it has been merged in case 1 or 2 */
489 /* there is a left string, need to merge left and right */
490 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
491 (left_width
+ space_width
> right_xpos
)) {
492 /* replace the former separator '\0' of left and
493 right string with a space */
494 *(--format_align
->right
) = ' ';
495 /* calculate the new width and position of the string */
496 left_width
= left_width
+ space_width
+ right_width
;
497 /* there is no right string anymore */
500 /* there is no left string, move right to left */
501 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
502 (left_width
> right_xpos
)) {
503 /* move the right string to the left string */
504 format_align
->left
= format_align
->right
;
505 /* calculate the new width and position of the string */
506 left_width
= right_width
;
507 /* there is no right string anymore */
511 if (scroll
&& ((left_width
> scroll_width
) ||
512 (center_width
> scroll_width
) ||
513 (right_width
> scroll_width
)))
515 #ifdef HAVE_LCD_BITMAP
516 display
->puts_scroll_style(0, line
,
517 (unsigned char *)format_align
->left
, style
);
519 display
->puts_scroll(0, line
,
520 (unsigned char *)format_align
->left
);
525 #ifdef HAVE_LCD_BITMAP
526 /* clear the line first */
527 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
528 display
->fillrect(0, line
*string_height
, viewport_width
, string_height
);
529 display
->set_drawmode(DRMODE_SOLID
);
532 /* Nasty hack: we output an empty scrolling string,
533 which will reset the scroller for that line */
534 display
->puts_scroll(0, line
, (unsigned char *)"");
535 #ifdef HAVE_LCD_BITMAP
536 style
|= STYLE_XY_PIXELS
;
537 line
*= string_height
;
538 /* print aligned strings */
541 display
->puts_style_xyoffset(0, line
,
542 (unsigned char *)format_align
->left
, style
, 0, 0);
545 if (center_width
!= 0)
547 display
->puts_style_xyoffset((viewport_width
-center_width
)/2, line
,
548 (unsigned char *)format_align
->center
, style
, 0, 0);
550 if (right_width
!= 0)
552 display
->puts_style_xyoffset(viewport_width
-right_width
, line
,
553 (unsigned char *)format_align
->right
, style
, 0, 0);
558 display
->putsxy(0, line
,
559 (unsigned char *)format_align
->left
);
561 if (center_width
!= 0)
563 display
->putsxy(center_xpos
, line
,
564 (unsigned char *)format_align
->center
);
566 if (right_width
!= 0)
568 display
->putsxy(right_xpos
, line
,
569 (unsigned char *)format_align
->right
);
575 #ifdef HAVE_LCD_BITMAP
576 void draw_peakmeters(struct gui_wps
*gwps
, int line_number
,
577 struct viewport
*viewport
)
579 struct wps_data
*data
= gwps
->data
;
580 if (!data
->peak_meter_enabled
)
582 peak_meter_enable(false);
586 int h
= font_get(viewport
->font
)->height
;
587 int peak_meter_y
= line_number
* h
;
589 /* The user might decide to have the peak meter in the last
590 line so that it is only displayed if no status bar is
591 visible. If so we neither want do draw nor enable the
593 if (peak_meter_y
+ h
<= viewport
->y
+viewport
->height
) {
594 peak_meter_enable(true);
595 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
596 MIN(h
, viewport
->y
+viewport
->height
- peak_meter_y
));
601 bool skin_has_sbs(enum screen_type screen
, struct wps_data
*data
)
606 #ifdef HAVE_LCD_BITMAP
607 if (data
->wps_sb_tag
)
608 draw
= data
->show_sb_on_wps
;
609 else if (statusbar_position(screen
) != STATUSBAR_OFF
)
616 /* do the button loop as often as required for the peak meters to update
617 * with a good refresh rate.
619 int skin_wait_for_action(enum skinnable_screens skin
, int context
, int timeout
)
621 (void)skin
; /* silence charcell warning */
622 int button
= ACTION_NONE
;
623 #ifdef HAVE_LCD_BITMAP
624 /* when the peak meter is enabled we want to have a
625 few extra updates to make it look smooth. On the
626 other hand we don't want to waste energy if it
631 if(skin_get_gwps(skin
, i
)->data
->peak_meter_enabled
)
636 long next_refresh
= current_tick
;
637 long next_big_refresh
= current_tick
+ timeout
;
638 button
= BUTTON_NONE
;
639 while (TIME_BEFORE(current_tick
, next_big_refresh
)) {
640 button
= get_action(context
,TIMEOUT_NOBLOCK
);
641 if (button
!= ACTION_NONE
) {
645 sleep(0); /* Sleep until end of current tick. */
647 if (TIME_AFTER(current_tick
, next_refresh
)) {
650 if(skin_get_gwps(skin
, i
)->data
->peak_meter_enabled
)
651 skin_update(skin
, i
, SKIN_REFRESH_PEAK_METER
);
652 next_refresh
+= HZ
/ PEAK_METER_FPS
;
659 /* The peak meter is disabled
660 -> no additional screen updates needed */
664 button
= get_action(context
, timeout
);