1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * $Id: skin_parser.c 26752 2010-06-10 21:22:16Z bieber $
10 * Copyright (C) 2010 Jonathan Gordon
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
34 #include "skin_display.h"
35 #include "skin_engine.h"
36 #include "skin_parser.h"
37 #include "tag_table.h"
38 #include "skin_scan.h"
47 #include "root_menu.h"
53 struct skin_draw_info
{
55 struct skin_viewport
*skin_vp
;
57 unsigned long refresh_type
;
59 char* cur_align_start
;
60 struct align_pos align
;
68 int offset
; /* used by the playlist viewer */
71 typedef bool (*skin_render_func
)(struct skin_element
* alternator
, struct skin_draw_info
*info
);
72 bool skin_render_alternator(struct skin_element
* alternator
, struct skin_draw_info
*info
);
74 #ifdef HAVE_LCD_BITMAP
75 static void skin_render_playlistviewer(struct playlistviewer
* viewer
,
77 struct skin_viewport
* skin_viewport
,
78 unsigned long refresh_type
);
81 static bool do_non_text_tags(struct gui_wps
*gwps
, struct skin_draw_info
*info
,
82 struct skin_element
*element
, struct viewport
* vp
)
84 #ifndef HAVE_LCD_BITMAP
85 (void)vp
; /* silence warnings */
88 struct wps_token
*token
= (struct wps_token
*)element
->data
;
90 #ifdef HAVE_LCD_BITMAP
91 struct wps_data
*data
= gwps
->data
;
92 bool do_refresh
= (element
->tag
->flags
& info
->refresh_type
) > 0;
96 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
97 case SKIN_TOKEN_VIEWPORT_FGCOLOUR
:
99 struct viewport_colour
*col
= token
->value
.data
;
100 col
->vp
->fg_pattern
= col
->colour
;
103 case SKIN_TOKEN_VIEWPORT_BGCOLOUR
:
105 struct viewport_colour
*col
= token
->value
.data
;
106 col
->vp
->bg_pattern
= col
->colour
;
110 case SKIN_TOKEN_VIEWPORT_ENABLE
:
112 char *label
= token
->value
.data
;
113 char temp
= VP_DRAW_HIDEABLE
;
114 struct skin_element
*viewport
= gwps
->data
->tree
;
117 struct skin_viewport
*skinvp
= (struct skin_viewport
*)viewport
->data
;
118 if (skinvp
->label
&& !skinvp
->is_infovp
&&
119 !strcmp(skinvp
->label
, label
))
121 if (skinvp
->hidden_flags
&VP_DRAW_HIDDEN
)
123 temp
|= VP_DRAW_WASHIDDEN
;
125 skinvp
->hidden_flags
= temp
;
127 viewport
= viewport
->next
;
131 #ifdef HAVE_LCD_BITMAP
132 case SKIN_TOKEN_UIVIEWPORT_ENABLE
:
133 sb_set_info_vp(gwps
->display
->screen_type
,
136 case SKIN_TOKEN_PEAKMETER
:
137 data
->peak_meter_enabled
= true;
139 draw_peakmeters(gwps
, info
->line_number
, vp
);
142 #ifdef HAVE_LCD_BITMAP
143 case SKIN_TOKEN_PEAKMETER_LEFTBAR
:
144 case SKIN_TOKEN_PEAKMETER_RIGHTBAR
:
145 data
->peak_meter_enabled
= true;
146 /* fall through to the progressbar code */
148 case SKIN_TOKEN_VOLUMEBAR
:
149 case SKIN_TOKEN_BATTERY_PERCENTBAR
:
150 #ifdef HAVE_LCD_BITMAP
151 case SKIN_TOKEN_PROGRESSBAR
:
152 case SKIN_TOKEN_TUNER_RSSI_BAR
:
154 struct progressbar
*bar
= (struct progressbar
*)token
->value
.data
;
156 draw_progressbar(gwps
, info
->line_number
, bar
);
160 #ifdef HAVE_LCD_BITMAP
161 case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON
:
162 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY
:
164 struct image_display
*id
= token
->value
.data
;
165 const char* label
= id
->label
;
166 struct gui_img
*img
= skin_find_item(label
,SKIN_FIND_IMAGE
, data
);
167 if (img
&& img
->loaded
)
169 if (id
->token
== NULL
)
171 img
->display
= id
->subimage
;
177 int a
= img
->num_subimages
;
178 out
= get_token_value(gwps
, id
->token
, info
->offset
,
179 buf
, sizeof(buf
), &a
);
181 /* NOTE: get_token_value() returns values starting at 1! */
183 a
= (out
&& *out
) ? 1 : 2;
184 if (token
->type
== SKIN_TOKEN_IMAGE_DISPLAY_LISTICON
)
185 a
-= 2; /* 2 is added in statusbar-skinned.c! */
190 /* Clear the image, as in conditionals */
191 clear_image_pos(gwps
, img
);
193 /* If the token returned a value which is higher than
194 * the amount of subimages, don't draw it. */
195 if (a
>= 0 && a
< img
->num_subimages
)
204 case SKIN_TOKEN_ALBUMART_DISPLAY
:
205 /* now draw the AA */
206 if (do_refresh
&& data
->albumart
)
208 int handle
= playback_current_aa_hid(data
->playback_aa_slot
);
210 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
212 struct dim dim
= {data
->albumart
->width
, data
->albumart
->height
};
213 handle
= radio_get_art_hid(&dim
);
216 data
->albumart
->draw_handle
= handle
;
220 case SKIN_TOKEN_DRAW_INBUILTBAR
:
221 gui_statusbar_draw(&(statusbars
.statusbars
[gwps
->display
->screen_type
]),
222 info
->refresh_type
== SKIN_REFRESH_ALL
,
225 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST
:
227 skin_render_playlistviewer(token
->value
.data
, gwps
,
228 info
->skin_vp
, info
->refresh_type
);
231 #endif /* HAVE_LCD_BITMAP */
232 #ifdef HAVE_SKIN_VARIABLES
233 case SKIN_TOKEN_VAR_SET
:
236 struct skin_var_changer
*data
= token
->value
.data
;
238 data
->var
->value
= data
->newval
;
241 data
->var
->value
+= data
->newval
;
244 if (data
->var
->value
> data
->max
)
245 data
->var
->value
= 1;
246 else if (data
->var
->value
< 1)
247 data
->var
->value
= data
->max
;
250 if (data
->var
->value
< 1)
251 data
->var
->value
= 1;
252 data
->var
->last_changed
= current_tick
;
264 static void do_tags_in_hidden_conditional(struct skin_element
* branch
,
265 struct skin_draw_info
*info
)
267 #ifdef HAVE_LCD_BITMAP
268 struct gui_wps
*gwps
= info
->gwps
;
269 struct wps_data
*data
= gwps
->data
;
271 /* Tags here are ones which need to be "turned off" or cleared
272 * if they are in a conditional branch which isnt being used */
273 if (branch
->type
== LINE_ALTERNATOR
)
276 for (i
=0; i
<branch
->children_count
; i
++)
278 do_tags_in_hidden_conditional(branch
->children
[i
], info
);
281 else if (branch
->type
== LINE
&& branch
->children_count
)
283 struct skin_element
*child
= branch
->children
[0];
284 struct wps_token
*token
;
287 if (child
->type
== CONDITIONAL
)
290 for (i
=0; i
<child
->children_count
; i
++)
292 do_tags_in_hidden_conditional(child
->children
[i
], info
);
297 else if (child
->type
!= TAG
|| !child
->data
)
302 token
= (struct wps_token
*)child
->data
;
303 #ifdef HAVE_LCD_BITMAP
304 /* clear all pictures in the conditional and nested ones */
305 if (token
->type
== SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY
)
307 struct image_display
*id
= token
->value
.data
;
308 struct gui_img
*img
= skin_find_item(id
->label
,
309 SKIN_FIND_IMAGE
, data
);
310 clear_image_pos(gwps
, img
);
312 else if (token
->type
== SKIN_TOKEN_PEAKMETER
)
314 data
->peak_meter_enabled
= false;
316 else if (token
->type
== SKIN_TOKEN_VIEWPORT_ENABLE
)
318 char *label
= token
->value
.data
;
319 struct skin_element
*viewport
;
320 for (viewport
= data
->tree
;
322 viewport
= viewport
->next
)
324 struct skin_viewport
*skin_viewport
= (struct skin_viewport
*)viewport
->data
;
325 if (skin_viewport
->label
&& strcmp(skin_viewport
->label
, label
))
327 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
331 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
333 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
334 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
337 gwps
->display
->set_viewport(&skin_viewport
->vp
);
338 gwps
->display
->clear_viewport();
339 gwps
->display
->scroll_stop(&skin_viewport
->vp
);
340 gwps
->display
->set_viewport(&info
->skin_vp
->vp
);
341 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
348 else if (data
->albumart
&& token
->type
== SKIN_TOKEN_ALBUMART_DISPLAY
)
351 playback_current_aa_hid(data
->playback_aa_slot
), true);
359 static void fix_line_alignment(struct skin_draw_info
*info
, struct skin_element
*element
)
361 struct align_pos
*align
= &info
->align
;
362 char *cur_pos
= info
->cur_align_start
+ strlen(info
->cur_align_start
);
363 switch (element
->tag
->type
)
365 case SKIN_TOKEN_ALIGN_LEFT
:
366 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
367 align
->left
= cur_pos
;
368 info
->cur_align_start
= cur_pos
;
370 case SKIN_TOKEN_ALIGN_LEFT_RTL
:
371 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
373 align
->right
= cur_pos
;
375 align
->left
= cur_pos
;
376 info
->cur_align_start
= cur_pos
;
378 case SKIN_TOKEN_ALIGN_CENTER
:
379 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
380 align
->center
= cur_pos
;
381 info
->cur_align_start
= cur_pos
;
383 case SKIN_TOKEN_ALIGN_RIGHT
:
384 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
385 align
->right
= cur_pos
;
386 info
->cur_align_start
= cur_pos
;
388 case SKIN_TOKEN_ALIGN_RIGHT_RTL
:
389 *cur_pos
= '\0'; cur_pos
++; *cur_pos
= '\0';
391 align
->left
= cur_pos
;
393 align
->right
= cur_pos
;
394 info
->cur_align_start
= cur_pos
;
401 /* Draw a LINE element onto the display */
402 static bool skin_render_line(struct skin_element
* line
, struct skin_draw_info
*info
)
404 bool needs_update
= false;
405 int last_value
, value
;
407 if (line
->children_count
== 0)
408 return false; /* empty line, do nothing */
410 struct skin_element
*child
= line
->children
[0];
411 struct conditional
*conditional
;
412 skin_render_func func
= skin_render_line
;
413 int old_refresh_mode
= info
->refresh_type
;
419 conditional
= (struct conditional
*)child
->data
;
420 last_value
= conditional
->last_value
;
421 value
= evaluate_conditional(info
->gwps
, info
->offset
,
422 conditional
, child
->children_count
);
423 conditional
->last_value
= value
;
424 if (child
->children_count
== 1)
426 /* special handling so
427 * %?aa<true> and %?<true|false> need special handlng here */
429 if (value
== -1) /* tag is false */
431 /* we are in a false branch of a %?aa<true> conditional */
433 do_tags_in_hidden_conditional(child
->children
[0], info
);
439 if (last_value
>= 0 && value
!= last_value
&& last_value
< child
->children_count
)
440 do_tags_in_hidden_conditional(child
->children
[last_value
], info
);
442 if (child
->children
[value
]->type
== LINE_ALTERNATOR
)
444 func
= skin_render_alternator
;
446 else if (child
->children
[value
]->type
== LINE
)
447 func
= skin_render_line
;
449 if (value
!= last_value
)
451 info
->refresh_type
= SKIN_REFRESH_ALL
;
452 info
->force_redraw
= true;
455 if (func(child
->children
[value
], info
))
458 needs_update
= needs_update
|| (last_value
!= value
);
460 info
->refresh_type
= old_refresh_mode
;
463 if (child
->tag
->flags
& NOBREAK
)
464 info
->no_line_break
= true;
465 if (child
->tag
->type
== SKIN_TOKEN_SUBLINE_SCROLL
)
466 info
->line_scrolls
= true;
468 fix_line_alignment(info
, child
);
474 if (!do_non_text_tags(info
->gwps
, info
, child
, &info
->skin_vp
->vp
))
476 static char tempbuf
[128];
477 const char *value
= get_token_value(info
->gwps
, child
->data
,
478 info
->offset
, tempbuf
,
479 sizeof(tempbuf
), NULL
);
483 if (child
->tag
->flags
&SKIN_RTC_REFRESH
)
484 needs_update
= needs_update
|| info
->refresh_type
&SKIN_REFRESH_DYNAMIC
;
486 needs_update
= needs_update
||
487 ((child
->tag
->flags
&info
->refresh_type
)!=0);
488 strlcat(info
->cur_align_start
, value
,
489 info
->buf_size
- (info
->cur_align_start
-info
->buf
));
494 strlcat(info
->cur_align_start
, child
->data
,
495 info
->buf_size
- (info
->cur_align_start
-info
->buf
));
496 needs_update
= needs_update
||
497 (info
->refresh_type
&SKIN_REFRESH_STATIC
) != 0;
509 static int get_subline_timeout(struct gui_wps
*gwps
, struct skin_element
* line
)
511 struct skin_element
*element
=line
;
512 struct wps_token
*token
;
513 int retval
= DEFAULT_SUBLINE_TIME_MULTIPLIER
*TIMEOUT_UNIT
;
514 if (element
->type
== LINE
)
516 if (element
->children_count
== 0)
517 return retval
; /* empty line, so force redraw */
518 element
= element
->children
[0];
522 if (element
->type
== TAG
&&
523 element
->tag
->type
== SKIN_TOKEN_SUBLINE_TIMEOUT
)
525 token
= element
->data
;
526 return token
->value
.i
;
528 else if (element
->type
== CONDITIONAL
)
530 struct conditional
*conditional
= element
->data
;
531 int val
= evaluate_conditional(gwps
, 0, conditional
,
532 element
->children_count
);
535 retval
= get_subline_timeout(gwps
, element
->children
[val
]);
540 element
= element
->next
;
545 bool skin_render_alternator(struct skin_element
* element
, struct skin_draw_info
*info
)
547 bool changed_lines
= false;
548 struct line_alternator
*alternator
= (struct line_alternator
*)element
->data
;
549 unsigned old_refresh
= info
->refresh_type
;
550 if (info
->refresh_type
== SKIN_REFRESH_ALL
)
552 alternator
->current_line
= element
->children_count
-1;
553 changed_lines
= true;
555 else if (TIME_AFTER(current_tick
, alternator
->next_change_tick
))
557 changed_lines
= true;
562 struct skin_element
*current_line
= element
->children
[alternator
->current_line
];
563 int start
= alternator
->current_line
;
564 int try_line
= start
;
565 bool suitable
= false;
566 int rettimeout
= DEFAULT_SUBLINE_TIME_MULTIPLIER
*TIMEOUT_UNIT
;
568 /* find a subline which has at least one token in it,
569 * and that line doesnt have a timeout set to 0 through conditionals */
572 if (try_line
>= element
->children_count
)
574 if (element
->children
[try_line
]->children_count
!= 0)
576 current_line
= element
->children
[try_line
];
577 rettimeout
= get_subline_timeout(info
->gwps
,
578 current_line
->children
[0]);
585 while (try_line
!= start
&& !suitable
);
589 alternator
->current_line
= try_line
;
590 alternator
->next_change_tick
= current_tick
+ rettimeout
;
593 info
->refresh_type
= SKIN_REFRESH_ALL
;
594 info
->force_redraw
= true;
596 bool ret
= skin_render_line(element
->children
[alternator
->current_line
], info
);
597 info
->refresh_type
= old_refresh
;
598 return changed_lines
|| ret
;
601 static void skin_render_viewport(struct skin_element
* viewport
, struct gui_wps
*gwps
,
602 struct skin_viewport
* skin_viewport
, unsigned long refresh_type
)
604 struct screen
*display
= gwps
->display
;
605 char linebuf
[MAX_LINE
];
606 skin_render_func func
= skin_render_line
;
607 struct skin_element
* line
= viewport
;
608 struct skin_draw_info info
= {
611 .buf_size
= sizeof(linebuf
),
613 .no_line_break
= false,
614 .line_scrolls
= false,
615 .refresh_type
= refresh_type
,
616 .skin_vp
= skin_viewport
,
620 struct align_pos
* align
= &info
.align
;
622 #ifdef HAVE_LCD_BITMAP
623 /* Set images to not to be displayed */
624 struct skin_token_list
*imglist
= gwps
->data
->images
;
627 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
629 imglist
= imglist
->next
;
636 info
.no_line_break
= false;
637 info
.line_scrolls
= false;
638 info
.force_redraw
= false;
640 info
.cur_align_start
= info
.buf
;
641 align
->left
= info
.buf
;
642 align
->center
= NULL
;
646 if (line
->type
== LINE_ALTERNATOR
)
647 func
= skin_render_alternator
;
648 else if (line
->type
== LINE
)
649 func
= skin_render_line
;
651 needs_update
= func(line
, &info
);
652 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
653 if (skin_viewport
->vp
.fg_pattern
!= skin_viewport
->start_fgcolour
||
654 skin_viewport
->vp
.bg_pattern
!= skin_viewport
->start_bgcolour
)
656 /* 2bit lcd drivers need lcd_set_viewport() to be called to change
657 * the colour, 16bit doesnt. But doing this makes static text
658 * get the new colour also */
660 display
->set_viewport(&skin_viewport
->vp
);
663 /* only update if the line needs to be, and there is something to write */
664 if (refresh_type
&& needs_update
)
666 if (info
.line_scrolls
)
668 /* if the line is a scrolling one we don't want to update
669 too often, so that it has the time to scroll */
670 if ((refresh_type
& SKIN_REFRESH_SCROLL
) || info
.force_redraw
)
671 write_line(display
, align
, info
.line_number
, true);
674 write_line(display
, align
, info
.line_number
, false);
676 if (!info
.no_line_break
)
680 #ifdef HAVE_LCD_BITMAP
681 wps_display_images(gwps
, &skin_viewport
->vp
);
685 void skin_render(struct gui_wps
*gwps
, unsigned refresh_mode
)
687 struct wps_data
*data
= gwps
->data
;
688 struct screen
*display
= gwps
->display
;
690 struct skin_element
* viewport
= data
->tree
;
691 struct skin_viewport
* skin_viewport
;
693 int old_refresh_mode
= refresh_mode
;
695 #ifdef HAVE_LCD_CHARCELLS
697 for (i
= 0; i
< 8; i
++)
699 if (data
->wps_progress_pat
[i
] == 0)
700 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
703 viewport
= data
->tree
;
704 skin_viewport
= (struct skin_viewport
*)viewport
->data
;
705 if (skin_viewport
->label
&& viewport
->next
&&
706 !strcmp(skin_viewport
->label
,VP_DEFAULT_LABEL
))
709 for (viewport
= data
->tree
;
711 viewport
= viewport
->next
)
714 skin_viewport
= (struct skin_viewport
*)viewport
->data
;
715 unsigned vp_refresh_mode
= refresh_mode
;
716 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
717 skin_viewport
->vp
.fg_pattern
= skin_viewport
->start_fgcolour
;
718 skin_viewport
->vp
.bg_pattern
= skin_viewport
->start_bgcolour
;
721 /* dont redraw the viewport if its disabled */
722 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
723 { /* don't draw anything into this one */
726 else if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
728 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
731 else if (((skin_viewport
->hidden_flags
&
732 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
733 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
735 vp_refresh_mode
= SKIN_REFRESH_ALL
;
736 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
739 display
->set_viewport(&skin_viewport
->vp
);
740 if ((vp_refresh_mode
&SKIN_REFRESH_ALL
) == SKIN_REFRESH_ALL
)
742 display
->clear_viewport();
745 if (viewport
->children_count
)
746 skin_render_viewport(viewport
->children
[0], gwps
,
747 skin_viewport
, vp_refresh_mode
);
748 refresh_mode
= old_refresh_mode
;
751 /* Restore the default viewport */
752 display
->set_viewport(NULL
);
756 #ifdef HAVE_LCD_BITMAP
757 static __attribute__((noinline
)) void skin_render_playlistviewer(struct playlistviewer
* viewer
,
758 struct gui_wps
*gwps
,
759 struct skin_viewport
* skin_viewport
,
760 unsigned long refresh_type
)
762 struct screen
*display
= gwps
->display
;
763 char linebuf
[MAX_LINE
];
764 skin_render_func func
= skin_render_line
;
765 struct skin_element
* line
;
766 struct skin_draw_info info
= {
769 .buf_size
= sizeof(linebuf
),
771 .no_line_break
= false,
772 .line_scrolls
= false,
773 .refresh_type
= refresh_type
,
774 .skin_vp
= skin_viewport
,
775 .offset
= viewer
->start_offset
778 struct align_pos
* align
= &info
.align
;
780 int cur_pos
, start_item
, max
;
781 int nb_lines
= viewport_get_nb_lines(viewer
->vp
);
783 if (get_current_activity() == ACTIVITY_FM
)
785 cur_pos
= radio_current_preset();
786 start_item
= cur_pos
+ viewer
->start_offset
;
787 max
= start_item
+radio_preset_count();
792 struct cuesheet
*cue
= skin_get_global_state()->id3
?
793 skin_get_global_state()->id3
->cuesheet
: NULL
;
794 cur_pos
= playlist_get_display_index();
795 max
= playlist_amount()+1;
797 max
+= cue
->track_count
;
798 start_item
= MAX(0, cur_pos
+ viewer
->start_offset
);
800 if (max
-start_item
> nb_lines
)
801 max
= start_item
+ nb_lines
;
804 while (start_item
< max
)
807 info
.no_line_break
= false;
808 info
.line_scrolls
= false;
809 info
.force_redraw
= false;
811 info
.cur_align_start
= info
.buf
;
812 align
->left
= info
.buf
;
813 align
->center
= NULL
;
817 if (line
->type
== LINE_ALTERNATOR
)
818 func
= skin_render_alternator
;
819 else if (line
->type
== LINE
)
820 func
= skin_render_line
;
822 needs_update
= func(line
, &info
);
824 /* only update if the line needs to be, and there is something to write */
825 if (refresh_type
&& needs_update
)
827 if (info
.line_scrolls
)
829 /* if the line is a scrolling one we don't want to update
830 too often, so that it has the time to scroll */
831 if ((refresh_type
& SKIN_REFRESH_SCROLL
) || info
.force_redraw
)
832 write_line(display
, align
, info
.line_number
, true);
835 write_line(display
, align
, info
.line_number
, false);