remove unecessary (duplicated even) header
[maemo-rb.git] / apps / gui / skin_engine / skin_display.c
blobccedea92a80f254d2c17e4407ce2c3684da565bb
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
22 #include "config.h"
23 #include <stdio.h>
24 #include "string-extra.h"
25 #include "misc.h"
26 #include "font.h"
27 #include "system.h"
28 #include "rbunicode.h"
29 #include "sound.h"
30 #include "powermgmt.h"
31 #ifdef DEBUG
32 #include "debug.h"
33 #endif
34 #include "action.h"
35 #include "abrepeat.h"
36 #include "lang.h"
37 #include "language.h"
38 #include "statusbar.h"
39 #include "settings.h"
40 #include "scrollbar.h"
41 #include "screen_access.h"
42 #include "playlist.h"
43 #include "audio.h"
44 #include "tagcache.h"
46 #ifdef HAVE_LCD_BITMAP
47 #include "peakmeter.h"
48 /* Image stuff */
49 #include "bmp.h"
50 #ifdef HAVE_ALBUMART
51 #include "albumart.h"
52 #endif
53 #endif
55 #include "cuesheet.h"
56 #if CONFIG_CODEC == SWCODEC
57 #include "playback.h"
58 #endif
59 #include "backdrop.h"
60 #include "viewport.h"
61 #if CONFIG_TUNER
62 #include "radio.h"
63 #include "tuner.h"
64 #endif
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);
87 if (cuesheet_update)
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)
98 if (!skin)
99 return;
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;
117 else
119 if (statusbar_position(screen) == STATUSBAR_OFF)
120 return; /* vp is fixed already */
121 vp->y = vp->x = 0;
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;
138 if (height < 0)
139 height = font_get(vp->font)->height;
141 if (y < 0)
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)
159 length = 100;
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;
168 length = MAX_PEAK;
169 end = peak_meter_scale_value(val, length);
171 #if CONFIG_TUNER
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);
180 end = val - min;
181 length = max - min;
183 else
184 #endif
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;
191 #endif
192 else if (id3 && id3->length)
194 length = id3->length;
195 end = id3->elapsed + state->ff_rewind_count;
197 else
199 length = 1;
200 end = 0;
203 if (!pb->horizontal)
205 /* we want to fill upwards which is technically inverted. */
206 flags = INVERTFILL;
209 if (pb->invert_fill_direction)
211 flags ^= INVERTFILL;
214 if (pb->nofill)
216 flags |= INNER_NOFILL;
219 if (pb->slider)
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;
231 else
233 height -= img->bm.height;
234 y += img->bm.height / 2;
237 if (!pb->nobar)
239 if (pb->image)
240 gui_bitmap_scrollbar_draw(display, &pb->image->bm,
241 x, y, width, height,
242 length, 0, end, flags);
243 else
244 gui_scrollbar_draw(display, x, y, width, height,
245 length, 0, end, flags);
248 if (pb->slider)
250 int xoff = 0, yoff = 0;
251 int w = width, h = height;
252 struct gui_img *img = pb->slider;
254 if (flags&HORIZONTAL)
256 w = img->bm.width;
257 xoff = width * end / length;
258 if (flags&INVERTFILL)
259 xoff = width - xoff;
260 xoff -= w / 2;
262 else
264 h = img->bm.height;
265 yoff = height * end / length;
266 if (flags&INVERTFILL)
267 yoff = height - yoff;
268 yoff -= h / 2;
270 #if LCD_DEPTH > 1
271 if(img->bm.format == FORMAT_MONO) {
272 #endif
273 display->mono_bitmap_part(img->bm.data,
274 0, 0, img->bm.width,
275 x + xoff, y + yoff, w, h);
276 #if LCD_DEPTH > 1
277 } else {
278 display->transparent_bitmap_part((fb_data *)img->bm.data,
279 0, 0,
280 STRIDE(display->screen_type,
281 img->bm.width, img->bm.height),
282 x + xoff, y + yoff, w, h);
284 #endif
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);
294 #endif
296 if (id3->cuesheet)
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);
305 #endif
309 /* clears the area where the image was shown */
310 void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
312 if(!gwps)
313 return;
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);
324 else
325 display->set_drawmode(DRMODE_SOLID);
327 #if LCD_DEPTH > 1
328 if(img->bm.format == FORMAT_MONO) {
329 #endif
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);
335 #if LCD_DEPTH > 1
336 } else {
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);
344 #endif
348 void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
350 if(!gwps || !gwps->data || !gwps->display)
351 return;
353 struct wps_data *data = gwps->data;
354 struct screen *display = gwps->display;
355 struct skin_token_list *list = data->images;
357 while (list)
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);
375 list = list->next;
377 #ifdef HAVE_ALBUMART
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;
385 #endif
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)
398 if (!gwps)
399 return false;
401 char result[128];
402 const char *value;
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. */
411 if (intval == -1)
413 if (num_options == 1) /* so %?AA<true> */
414 intval = (value && *value) ? 1 : 0; /* returned as 0 for true, -1 for false */
415 else
416 intval = (value && *value) ? 1 : num_options;
418 else if (intval > num_options || intval < 1)
419 intval = num_options;
421 return intval -1;
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,
432 int line,
433 bool scroll)
435 int left_width = 0, left_xpos;
436 int center_width = 0, center_xpos;
437 int right_width = 0, right_xpos;
438 int ypos;
439 int space_width;
440 int string_height;
441 int scroll_width;
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 &center_width, &string_height);
460 left_xpos = 0;
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
468 space */
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 */
480 center_width = 0;
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 */
490 center_width = 0;
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 */
506 center_width = 0;
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 */
517 center_width = 0;
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 */
532 right_width = 0;
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 */
542 right_width = 0;
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);
555 else
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);
562 #endif
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 */
569 if (left_width != 0)
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);
596 else
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
604 peak meter. */
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)
615 (void)screen;
616 (void)data;
617 bool draw = false;
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)
622 draw = true;
623 #endif
624 return draw;
626 #endif
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
636 int i;
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
640 isn't displayed */
641 bool pm=false;
642 FOR_NB_SCREENS(i)
644 if(skin_get_gwps(skin, i)->data->peak_meter_enabled)
645 pm = true;
648 if (pm) {
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) {
655 break;
657 peak_meter_peek();
658 sleep(0); /* Sleep until end of current tick. */
660 if (TIME_AFTER(current_tick, next_refresh)) {
661 FOR_NB_SCREENS(i)
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 */
674 else
675 #endif
677 button = get_action(context, timeout);
679 return button;