diff options
Diffstat (limited to 'apps/gui/gwps-common.c')
-rw-r--r-- | apps/gui/gwps-common.c | 2024 |
1 files changed, 2024 insertions, 0 deletions
diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c new file mode 100644 index 0000000000..ed3e852661 --- /dev/null +++ b/apps/gui/gwps-common.c | |||
@@ -0,0 +1,2024 @@ | |||
1 | #include "gwps-common.h" | ||
2 | #include "gwps.h" | ||
3 | #include "font.h" | ||
4 | #include <stdio.h> | ||
5 | #include <string.h> | ||
6 | #include <stdlib.h> | ||
7 | #include "system.h" | ||
8 | #include "settings.h" | ||
9 | #include "audio.h" | ||
10 | #include "status.h" | ||
11 | #include "power.h" | ||
12 | #include "powermgmt.h" | ||
13 | #include "sound.h" | ||
14 | #ifdef HAVE_LCD_CHARCELLS | ||
15 | #include "hwcompat.h" | ||
16 | #endif | ||
17 | #include "mp3_playback.h" | ||
18 | #include "backlight.h" | ||
19 | #include "lang.h" | ||
20 | #include "misc.h" | ||
21 | |||
22 | #include "statusbar.h" | ||
23 | #include "splash.h" | ||
24 | #include "scrollbar.h" | ||
25 | #ifdef HAVE_LCD_BITMAP | ||
26 | #include "peakmeter.h" | ||
27 | /* Image stuff */ | ||
28 | #include "bmp.h" | ||
29 | #include "atoi.h" | ||
30 | #endif | ||
31 | |||
32 | #ifdef HAVE_LCD_CHARCELLS | ||
33 | static bool draw_player_progress(struct gui_wps *gwps); | ||
34 | static void draw_player_fullbar(struct gui_wps *gwps, | ||
35 | char* buf, int buf_size); | ||
36 | #endif | ||
37 | |||
38 | #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ | ||
39 | /* 3% of 30min file == 54s step size */ | ||
40 | #define MIN_FF_REWIND_STEP 500 | ||
41 | |||
42 | /* Format time into buf. | ||
43 | * | ||
44 | * buf - buffer to format to. | ||
45 | * buf_size - size of buffer. | ||
46 | * time - time to format, in milliseconds. | ||
47 | */ | ||
48 | void gui_wps_format_time(char* buf, int buf_size, long time) | ||
49 | { | ||
50 | if ( time < 3600000 ) { | ||
51 | snprintf(buf, buf_size, "%d:%02d", | ||
52 | (int) (time % 3600000 / 60000), (int) (time % 60000 / 1000)); | ||
53 | } else { | ||
54 | snprintf(buf, buf_size, "%d:%02d:%02d", | ||
55 | (int) (time / 3600000), (int) (time % 3600000 / 60000), | ||
56 | (int) (time % 60000 / 1000)); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | /* Extract a part from a path. | ||
61 | * | ||
62 | * buf - buffer extract part to. | ||
63 | * buf_size - size of buffer. | ||
64 | * path - path to extract from. | ||
65 | * level - what to extract. 0 is file name, 1 is parent of file, 2 is | ||
66 | * parent of parent, etc. | ||
67 | * | ||
68 | * Returns buf if the desired level was found, NULL otherwise. | ||
69 | */ | ||
70 | static char* get_dir(char* buf, int buf_size, const char* path, int level) | ||
71 | { | ||
72 | const char* sep; | ||
73 | const char* last_sep; | ||
74 | int len; | ||
75 | |||
76 | sep = path + strlen(path); | ||
77 | last_sep = sep; | ||
78 | |||
79 | while (sep > path) | ||
80 | { | ||
81 | if ('/' == *(--sep)) | ||
82 | { | ||
83 | if (!level) | ||
84 | { | ||
85 | break; | ||
86 | } | ||
87 | |||
88 | level--; | ||
89 | last_sep = sep - 1; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | if (level || (last_sep <= sep)) | ||
94 | { | ||
95 | return NULL; | ||
96 | } | ||
97 | |||
98 | len = MIN(last_sep - sep, buf_size - 1); | ||
99 | strncpy(buf, sep + 1, len); | ||
100 | buf[len] = 0; | ||
101 | return buf; | ||
102 | } | ||
103 | |||
104 | /* Get the tag specified by the two characters at fmt. | ||
105 | * | ||
106 | * cid3 - ID3 data to get tag values from. | ||
107 | * nid3 - next-song ID3 data to get tag values from. | ||
108 | * tag - string (of two characters) specifying the tag to get. | ||
109 | * buf - buffer to certain tags, such as track number, play time or | ||
110 | * directory name. | ||
111 | * buf_size - size of buffer. | ||
112 | * flags - returns the type of the line. See constants i wps-display.h | ||
113 | * | ||
114 | * Returns the tag. NULL indicates the tag wasn't available. | ||
115 | */ | ||
116 | static char* get_tag(struct wps_data* wps_data, | ||
117 | struct mp3entry* cid3, | ||
118 | struct mp3entry* nid3, | ||
119 | const char* tag, | ||
120 | char* buf, | ||
121 | int buf_size, | ||
122 | unsigned char* tag_len, | ||
123 | unsigned short* subline_time_mult, | ||
124 | unsigned char* flags, | ||
125 | int *intval) | ||
126 | { | ||
127 | struct mp3entry *id3 = cid3; /* default to current song */ | ||
128 | #ifndef HAVE_LCD_CHARCELLS | ||
129 | (void)wps_data; | ||
130 | #endif | ||
131 | if ((0 == tag[0]) || (0 == tag[1])) | ||
132 | { | ||
133 | *tag_len = 0; | ||
134 | return NULL; | ||
135 | } | ||
136 | |||
137 | *tag_len = 2; | ||
138 | |||
139 | *intval = 0; | ||
140 | |||
141 | switch (tag[0]) | ||
142 | { | ||
143 | case 'I': /* ID3 Information */ | ||
144 | id3 = nid3; /* display next-song data */ | ||
145 | *flags |= WPS_REFRESH_DYNAMIC; | ||
146 | if(!id3) | ||
147 | return NULL; /* no such info (yet) */ | ||
148 | /* fall-through */ | ||
149 | case 'i': /* ID3 Information */ | ||
150 | *flags |= WPS_REFRESH_STATIC; | ||
151 | switch (tag[1]) | ||
152 | { | ||
153 | case 't': /* ID3 Title */ | ||
154 | return id3->title; | ||
155 | |||
156 | case 'a': /* ID3 Artist */ | ||
157 | return id3->artist; | ||
158 | |||
159 | case 'n': /* ID3 Track Number */ | ||
160 | if (id3->track_string) | ||
161 | return id3->track_string; | ||
162 | |||
163 | if (id3->tracknum) { | ||
164 | snprintf(buf, buf_size, "%d", id3->tracknum); | ||
165 | return buf; | ||
166 | } | ||
167 | return NULL; | ||
168 | |||
169 | case 'd': /* ID3 Album/Disc */ | ||
170 | return id3->album; | ||
171 | |||
172 | case 'c': /* ID3 Composer */ | ||
173 | return id3->composer; | ||
174 | |||
175 | case 'y': /* year */ | ||
176 | if( id3->year_string ) | ||
177 | return id3->year_string; | ||
178 | |||
179 | if (id3->year) { | ||
180 | snprintf(buf, buf_size, "%d", id3->year); | ||
181 | return buf; | ||
182 | } | ||
183 | return NULL; | ||
184 | |||
185 | case 'g': /* genre */ | ||
186 | return id3_get_genre(id3); | ||
187 | |||
188 | case 'v': /* id3 version */ | ||
189 | switch (id3->id3version) { | ||
190 | case ID3_VER_1_0: | ||
191 | return "1"; | ||
192 | |||
193 | case ID3_VER_1_1: | ||
194 | return "1.1"; | ||
195 | |||
196 | case ID3_VER_2_2: | ||
197 | return "2.2"; | ||
198 | |||
199 | case ID3_VER_2_3: | ||
200 | return "2.3"; | ||
201 | |||
202 | case ID3_VER_2_4: | ||
203 | return "2.4"; | ||
204 | |||
205 | default: | ||
206 | return NULL; | ||
207 | } | ||
208 | } | ||
209 | break; | ||
210 | |||
211 | case 'F': /* File Information */ | ||
212 | id3 = nid3; | ||
213 | *flags |= WPS_REFRESH_DYNAMIC; | ||
214 | if(!id3) | ||
215 | return NULL; /* no such info (yet) */ | ||
216 | /* fall-through */ | ||
217 | case 'f': /* File Information */ | ||
218 | *flags |= WPS_REFRESH_STATIC; | ||
219 | switch(tag[1]) | ||
220 | { | ||
221 | case 'v': /* VBR file? */ | ||
222 | return id3->vbr ? "(avg)" : NULL; | ||
223 | |||
224 | case 'b': /* File Bitrate */ | ||
225 | if(id3->bitrate) | ||
226 | snprintf(buf, buf_size, "%d", id3->bitrate); | ||
227 | else | ||
228 | snprintf(buf, buf_size, "?"); | ||
229 | return buf; | ||
230 | |||
231 | case 'f': /* File Frequency */ | ||
232 | snprintf(buf, buf_size, "%ld", id3->frequency); | ||
233 | return buf; | ||
234 | |||
235 | case 'p': /* File Path */ | ||
236 | return id3->path; | ||
237 | |||
238 | case 'm': /* File Name - With Extension */ | ||
239 | return get_dir(buf, buf_size, id3->path, 0); | ||
240 | |||
241 | case 'n': /* File Name */ | ||
242 | if (get_dir(buf, buf_size, id3->path, 0)) | ||
243 | { | ||
244 | /* Remove extension */ | ||
245 | char* sep = strrchr(buf, '.'); | ||
246 | |||
247 | if (NULL != sep) | ||
248 | { | ||
249 | *sep = 0; | ||
250 | } | ||
251 | |||
252 | return buf; | ||
253 | } | ||
254 | else | ||
255 | { | ||
256 | return NULL; | ||
257 | } | ||
258 | |||
259 | case 's': /* File Size (in kilobytes) */ | ||
260 | snprintf(buf, buf_size, "%ld", id3->filesize / 1024); | ||
261 | return buf; | ||
262 | |||
263 | case 'c': /* File Codec */ | ||
264 | if(id3->codectype == AFMT_UNKNOWN) | ||
265 | *intval = AFMT_NUM_CODECS; | ||
266 | else | ||
267 | *intval = id3->codectype; | ||
268 | return id3_get_codec(id3); | ||
269 | } | ||
270 | break; | ||
271 | |||
272 | case 'p': /* Playlist/Song Information */ | ||
273 | switch(tag[1]) | ||
274 | { | ||
275 | case 'b': /* progress bar */ | ||
276 | *flags |= WPS_REFRESH_PLAYER_PROGRESS; | ||
277 | #ifdef HAVE_LCD_CHARCELLS | ||
278 | snprintf(buf, buf_size, "%c", wps_data->wps_progress_pat[0]); | ||
279 | wps_data->full_line_progressbar=0; | ||
280 | return buf; | ||
281 | #else | ||
282 | return "\x01"; | ||
283 | #endif | ||
284 | case 'f': /* full-line progress bar */ | ||
285 | #ifdef HAVE_LCD_CHARCELLS | ||
286 | if(is_new_player()) { | ||
287 | *flags |= WPS_REFRESH_PLAYER_PROGRESS; | ||
288 | *flags |= WPS_REFRESH_DYNAMIC; | ||
289 | wps_data->full_line_progressbar=1; | ||
290 | /* we need 11 characters (full line) for | ||
291 | progress-bar */ | ||
292 | snprintf(buf, buf_size, " "); | ||
293 | } | ||
294 | else | ||
295 | { | ||
296 | /* Tell the user if we have an OldPlayer */ | ||
297 | snprintf(buf, buf_size, " <Old LCD> "); | ||
298 | } | ||
299 | return buf; | ||
300 | #endif | ||
301 | case 'p': /* Playlist Position */ | ||
302 | *flags |= WPS_REFRESH_STATIC; | ||
303 | snprintf(buf, buf_size, "%d", playlist_get_display_index()); | ||
304 | return buf; | ||
305 | |||
306 | case 'n': /* Playlist Name (without path) */ | ||
307 | *flags |= WPS_REFRESH_STATIC; | ||
308 | return playlist_name(NULL, buf, buf_size); | ||
309 | |||
310 | case 'e': /* Playlist Total Entries */ | ||
311 | *flags |= WPS_REFRESH_STATIC; | ||
312 | snprintf(buf, buf_size, "%d", playlist_amount()); | ||
313 | return buf; | ||
314 | |||
315 | case 'c': /* Current Time in Song */ | ||
316 | *flags |= WPS_REFRESH_DYNAMIC; | ||
317 | gui_wps_format_time(buf, buf_size, | ||
318 | id3->elapsed + wps_state.ff_rewind_count); | ||
319 | return buf; | ||
320 | |||
321 | case 'r': /* Remaining Time in Song */ | ||
322 | *flags |= WPS_REFRESH_DYNAMIC; | ||
323 | gui_wps_format_time(buf, buf_size, | ||
324 | id3->length - id3->elapsed - wps_state.ff_rewind_count); | ||
325 | return buf; | ||
326 | |||
327 | case 't': /* Total Time */ | ||
328 | *flags |= WPS_REFRESH_STATIC; | ||
329 | gui_wps_format_time(buf, buf_size, id3->length); | ||
330 | return buf; | ||
331 | |||
332 | #ifdef HAVE_LCD_BITMAP | ||
333 | case 'm': /* Peak Meter */ | ||
334 | *flags |= WPS_REFRESH_PEAK_METER; | ||
335 | return "\x01"; | ||
336 | #endif | ||
337 | case 's': /* shuffle */ | ||
338 | *flags |= WPS_REFRESH_DYNAMIC; | ||
339 | if ( global_settings.playlist_shuffle ) | ||
340 | return "s"; | ||
341 | else | ||
342 | return NULL; | ||
343 | break; | ||
344 | |||
345 | case 'v': /* volume */ | ||
346 | *flags |= WPS_REFRESH_DYNAMIC; | ||
347 | snprintf(buf, buf_size, "%d%%", global_settings.volume); | ||
348 | *intval = global_settings.volume / 10 + 1; | ||
349 | return buf; | ||
350 | |||
351 | } | ||
352 | break; | ||
353 | |||
354 | case 'm': | ||
355 | switch (tag[1]) | ||
356 | { | ||
357 | case 'm': /* playback repeat mode */ | ||
358 | *flags |= WPS_REFRESH_DYNAMIC; | ||
359 | *intval = global_settings.repeat_mode + 1; | ||
360 | snprintf(buf, buf_size, "%d", *intval); | ||
361 | return buf; | ||
362 | |||
363 | /* playback status */ | ||
364 | case 'p': /* play */ | ||
365 | *flags |= WPS_REFRESH_DYNAMIC; | ||
366 | int status = audio_status(); | ||
367 | *intval = 1; | ||
368 | if (status == AUDIO_STATUS_PLAY && \ | ||
369 | !(status & AUDIO_STATUS_PAUSE)) | ||
370 | *intval = 2; | ||
371 | if (audio_status() & AUDIO_STATUS_PAUSE && \ | ||
372 | (! status_get_ffmode())) | ||
373 | *intval = 3; | ||
374 | if (status_get_ffmode() == STATUS_FASTFORWARD) | ||
375 | *intval = 4; | ||
376 | if (status_get_ffmode() == STATUS_FASTBACKWARD) | ||
377 | *intval = 5; | ||
378 | snprintf(buf, buf_size, "%d", *intval); | ||
379 | return buf; | ||
380 | |||
381 | #if CONFIG_KEYPAD == IRIVER_H100_PAD | ||
382 | case 'h': /* hold */ | ||
383 | *flags |= WPS_REFRESH_DYNAMIC; | ||
384 | if (button_hold()) | ||
385 | return "h"; | ||
386 | else | ||
387 | return NULL; | ||
388 | case 'r': /* remote hold */ | ||
389 | *flags |= WPS_REFRESH_DYNAMIC; | ||
390 | if (remote_button_hold()) | ||
391 | return "r"; | ||
392 | else | ||
393 | return NULL; | ||
394 | #endif | ||
395 | } | ||
396 | break; | ||
397 | |||
398 | case 'b': /* battery info */ | ||
399 | *flags |= WPS_REFRESH_DYNAMIC; | ||
400 | switch (tag[1]) { | ||
401 | case 'l': /* battery level */ | ||
402 | { | ||
403 | int l = battery_level(); | ||
404 | if (l > -1) | ||
405 | { | ||
406 | snprintf(buf, buf_size, "%d%%", l); | ||
407 | *intval = l / 20 + 1; | ||
408 | } | ||
409 | else | ||
410 | { | ||
411 | *intval = 6; | ||
412 | return "?%"; | ||
413 | } | ||
414 | return buf; | ||
415 | } | ||
416 | |||
417 | case 't': /* estimated battery time */ | ||
418 | { | ||
419 | int t = battery_time(); | ||
420 | if (t >= 0) | ||
421 | snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60); | ||
422 | else | ||
423 | strncpy(buf, "?h ?m", buf_size); | ||
424 | return buf; | ||
425 | } | ||
426 | |||
427 | case 'p': /* External power plugged in? */ | ||
428 | { | ||
429 | if(charger_inserted()) | ||
430 | return "p"; | ||
431 | else | ||
432 | return NULL; | ||
433 | } | ||
434 | } | ||
435 | break; | ||
436 | |||
437 | case 'D': /* Directory path information */ | ||
438 | id3 = nid3; /* next song please! */ | ||
439 | *flags |= WPS_REFRESH_DYNAMIC; | ||
440 | if(!id3) | ||
441 | return NULL; /* no such info (yet) */ | ||
442 | /* fall-through */ | ||
443 | case 'd': /* Directory path information */ | ||
444 | { | ||
445 | int level = tag[1] - '0'; | ||
446 | *flags |= WPS_REFRESH_STATIC; | ||
447 | /* d1 through d9 */ | ||
448 | if ((0 < level) && (9 > level)) | ||
449 | { | ||
450 | return get_dir(buf, buf_size, id3->path, level); | ||
451 | } | ||
452 | } | ||
453 | break; | ||
454 | |||
455 | case 't': /* set sub line time multiplier */ | ||
456 | { | ||
457 | int d = 1; | ||
458 | int time_mult = 0; | ||
459 | bool have_point = false; | ||
460 | bool have_tenth = false; | ||
461 | |||
462 | while (((tag[d] >= '0') && | ||
463 | (tag[d] <= '9')) || | ||
464 | (tag[d] == '.')) | ||
465 | { | ||
466 | if (tag[d] != '.') | ||
467 | { | ||
468 | time_mult = time_mult * 10; | ||
469 | time_mult = time_mult + tag[d] - '0'; | ||
470 | if (have_point) | ||
471 | { | ||
472 | have_tenth = true; | ||
473 | d++; | ||
474 | break; | ||
475 | } | ||
476 | } | ||
477 | else | ||
478 | { | ||
479 | have_point = true; | ||
480 | } | ||
481 | d++; | ||
482 | } | ||
483 | |||
484 | if (have_tenth == false) | ||
485 | time_mult *= 10; | ||
486 | |||
487 | *subline_time_mult = time_mult; | ||
488 | *tag_len = d; | ||
489 | |||
490 | buf[0] = 0; | ||
491 | return buf; | ||
492 | } | ||
493 | break; | ||
494 | case 'r': /* Runtime database Information */ | ||
495 | switch(tag[1]) | ||
496 | { | ||
497 | case 'p': /* Playcount */ | ||
498 | *flags |= WPS_REFRESH_STATIC; | ||
499 | snprintf(buf, buf_size, "%ld", cid3->playcount); | ||
500 | return buf; | ||
501 | case 'r': /* Rating */ | ||
502 | *flags |= WPS_REFRESH_STATIC; | ||
503 | *intval = cid3->rating+1; | ||
504 | snprintf(buf, buf_size, "%d", cid3->rating); | ||
505 | return buf; | ||
506 | } | ||
507 | break; | ||
508 | } | ||
509 | return NULL; | ||
510 | } | ||
511 | |||
512 | #ifdef HAVE_LCD_BITMAP | ||
513 | /* clears the area where the image was shown */ | ||
514 | static void clear_image_pos(struct gui_wps *gwps, int n) | ||
515 | { | ||
516 | if(!gwps) return; | ||
517 | struct wps_data *data = gwps->data; | ||
518 | gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); | ||
519 | gwps->display->fillrect(data->img[n].x, data->img[n].y, | ||
520 | data->img[n].w, data->img[n].h); | ||
521 | gwps->display->set_drawmode(DRMODE_SOLID); | ||
522 | } | ||
523 | #endif | ||
524 | /* Skip to the end of the current %? conditional. | ||
525 | * | ||
526 | * fmt - string to skip it. Should point to somewhere after the leading | ||
527 | * "<" char (and before or at the last ">"). | ||
528 | * num - number of |'s to skip, or 0 to skip to the end (the ">"). | ||
529 | * | ||
530 | * Returns the new position in fmt. | ||
531 | */ | ||
532 | static const char* skip_conditional(struct gui_wps *gwps, const char* fmt, int num) | ||
533 | { | ||
534 | int level = 1; | ||
535 | int count = num; | ||
536 | const char *last_alternative = NULL; | ||
537 | #ifdef HAVE_LCD_BITMAP | ||
538 | struct wps_data *data = NULL; | ||
539 | if(gwps) data = gwps->data; | ||
540 | int last_x=-1, last_y=-1, last_w=-1, last_h=-1; | ||
541 | #else | ||
542 | (void)gwps; | ||
543 | #endif | ||
544 | while (*fmt) | ||
545 | { | ||
546 | switch (*fmt++) | ||
547 | { | ||
548 | case '%': | ||
549 | #ifdef HAVE_LCD_BITMAP | ||
550 | if(data && *(fmt) == 'x' && *(fmt+1) == 'd' ) | ||
551 | { | ||
552 | fmt +=2; | ||
553 | int n = *fmt; | ||
554 | if(n >= 'a' && n <= 'z') | ||
555 | n -= 'a'; | ||
556 | if(n >= 'A' && n <= 'Z') | ||
557 | n = n - 'A' + 26; | ||
558 | if(last_x != data->img[n].x || last_y != data->img[n].y | ||
559 | || last_w != data->img[n].w || last_h != data->img[n].h) | ||
560 | { | ||
561 | last_x = data->img[n].x; | ||
562 | last_y = data->img[n].y; | ||
563 | last_w = data->img[n].w; | ||
564 | last_h = data->img[n].h; | ||
565 | clear_image_pos(gwps,n); | ||
566 | } | ||
567 | } | ||
568 | #endif | ||
569 | break; | ||
570 | |||
571 | case '|': | ||
572 | if(1 == level) { | ||
573 | last_alternative = fmt; | ||
574 | if(num) { | ||
575 | count--; | ||
576 | if(count == 0) | ||
577 | return fmt; | ||
578 | continue; | ||
579 | } | ||
580 | } | ||
581 | continue; | ||
582 | |||
583 | case '>': | ||
584 | if (0 == --level) | ||
585 | { | ||
586 | /* We're just skipping to the end */ | ||
587 | if(num == 0) | ||
588 | return fmt; | ||
589 | |||
590 | /* If we are parsing an enum, we'll return the selected | ||
591 | item. If there weren't enough items in the enum, we'll | ||
592 | return the last one found. */ | ||
593 | if(count && last_alternative) | ||
594 | { | ||
595 | return last_alternative; | ||
596 | } | ||
597 | return fmt - 1; | ||
598 | } | ||
599 | continue; | ||
600 | |||
601 | default: | ||
602 | continue; | ||
603 | } | ||
604 | |||
605 | switch (*fmt++) | ||
606 | { | ||
607 | case 0: | ||
608 | case '%': | ||
609 | case '|': | ||
610 | case '<': | ||
611 | case '>': | ||
612 | break; | ||
613 | |||
614 | case '?': | ||
615 | while (*fmt && ('<' != *fmt)) | ||
616 | fmt++; | ||
617 | |||
618 | if ('<' == *fmt) | ||
619 | fmt++; | ||
620 | |||
621 | level++; | ||
622 | break; | ||
623 | |||
624 | default: | ||
625 | break; | ||
626 | } | ||
627 | } | ||
628 | |||
629 | return fmt; | ||
630 | } | ||
631 | |||
632 | /* Generate the display based on id3 information and format string. | ||
633 | * | ||
634 | * buf - char buffer to write the display to. | ||
635 | * buf_size - the size of buffer. | ||
636 | * id3 - the ID3 data to format with. | ||
637 | * nid3 - the ID3 data of the next song (might by NULL) | ||
638 | * fmt - format description. | ||
639 | * flags - returns the type of the line. See constants i wps-display.h | ||
640 | */ | ||
641 | static void format_display(struct gui_wps *gwps, char* buf, | ||
642 | int buf_size, | ||
643 | struct mp3entry* id3, | ||
644 | struct mp3entry* nid3, /* next song's id3 */ | ||
645 | const char* fmt, | ||
646 | struct align_pos* align, | ||
647 | unsigned short* subline_time_mult, | ||
648 | unsigned char* flags) | ||
649 | { | ||
650 | char temp_buf[128]; | ||
651 | char* buf_start = buf; | ||
652 | char* buf_end = buf + buf_size - 1; /* Leave room for end null */ | ||
653 | char* value = NULL; | ||
654 | int level = 0; | ||
655 | unsigned char tag_length; | ||
656 | int intval; | ||
657 | int cur_align; | ||
658 | char* cur_align_start; | ||
659 | #ifdef HAVE_LCD_BITMAP | ||
660 | struct gui_img *img = gwps->data->img; | ||
661 | int n; | ||
662 | #endif | ||
663 | |||
664 | cur_align_start = buf; | ||
665 | cur_align = WPS_ALIGN_LEFT; | ||
666 | *subline_time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER; | ||
667 | |||
668 | align->left = 0; | ||
669 | align->center = 0; | ||
670 | align->right = 0; | ||
671 | |||
672 | while (fmt && *fmt && buf < buf_end) | ||
673 | { | ||
674 | switch (*fmt) | ||
675 | { | ||
676 | case '%': | ||
677 | ++fmt; | ||
678 | break; | ||
679 | |||
680 | case '|': | ||
681 | case '>': | ||
682 | if (level > 0) | ||
683 | { | ||
684 | fmt = skip_conditional(NULL,fmt, 0); | ||
685 | level--; | ||
686 | continue; | ||
687 | } | ||
688 | /* Else fall through */ | ||
689 | |||
690 | default: | ||
691 | *buf++ = *fmt++; | ||
692 | continue; | ||
693 | } | ||
694 | |||
695 | switch (*fmt) | ||
696 | { | ||
697 | case 0: | ||
698 | *buf++ = '%'; | ||
699 | break; | ||
700 | case 'a': | ||
701 | ++fmt; | ||
702 | /* remember where the current aligned text started */ | ||
703 | switch (cur_align) | ||
704 | { | ||
705 | case WPS_ALIGN_LEFT: | ||
706 | align->left = cur_align_start; | ||
707 | break; | ||
708 | |||
709 | case WPS_ALIGN_CENTER: | ||
710 | align->center = cur_align_start; | ||
711 | break; | ||
712 | |||
713 | case WPS_ALIGN_RIGHT: | ||
714 | align->right = cur_align_start; | ||
715 | break; | ||
716 | } | ||
717 | /* start a new alignment */ | ||
718 | switch (*fmt) | ||
719 | { | ||
720 | case 'l': | ||
721 | cur_align = WPS_ALIGN_LEFT; | ||
722 | break; | ||
723 | case 'c': | ||
724 | cur_align = WPS_ALIGN_CENTER; | ||
725 | break; | ||
726 | case 'r': | ||
727 | cur_align = WPS_ALIGN_RIGHT; | ||
728 | break; | ||
729 | } | ||
730 | *buf++=0; | ||
731 | cur_align_start = buf; | ||
732 | ++fmt; | ||
733 | break; | ||
734 | case 's': | ||
735 | *flags |= WPS_REFRESH_SCROLL; | ||
736 | ++fmt; | ||
737 | break; | ||
738 | |||
739 | case 'x': /* image support */ | ||
740 | #ifdef HAVE_LCD_BITMAP | ||
741 | /* skip preload or regular image tag */ | ||
742 | if ('l' == *(fmt+1) || '|' == *(fmt+1)) | ||
743 | { | ||
744 | while (*fmt && *fmt != '\n') | ||
745 | fmt++; | ||
746 | } | ||
747 | else if ('d' == *(fmt+1) ) | ||
748 | { | ||
749 | fmt+=2; | ||
750 | |||
751 | /* get the image ID */ | ||
752 | n = *fmt; | ||
753 | if(n >= 'a' && n <= 'z') | ||
754 | n -= 'a'; | ||
755 | if(n >= 'A' && n <= 'Z') | ||
756 | n = n - 'A' + 26; | ||
757 | if (n >= 0 && n < MAX_IMAGES && img[n].loaded) { | ||
758 | img[n].display = true; | ||
759 | } | ||
760 | } | ||
761 | |||
762 | #endif | ||
763 | fmt++; | ||
764 | break; | ||
765 | |||
766 | |||
767 | case '%': | ||
768 | case '|': | ||
769 | case '<': | ||
770 | case '>': | ||
771 | case ';': | ||
772 | *buf++ = *fmt++; | ||
773 | break; | ||
774 | |||
775 | case '?': | ||
776 | fmt++; | ||
777 | value = get_tag(gwps->data, id3, nid3, fmt, temp_buf, sizeof(temp_buf), | ||
778 | &tag_length, subline_time_mult, flags, | ||
779 | &intval); | ||
780 | |||
781 | while (*fmt && ('<' != *fmt)) | ||
782 | fmt++; | ||
783 | |||
784 | if ('<' == *fmt) | ||
785 | fmt++; | ||
786 | |||
787 | /* No value, so skip to else part, using a sufficiently high | ||
788 | value to "hit" the last part of the conditional */ | ||
789 | if ((!value) || (!strlen(value))) | ||
790 | fmt = skip_conditional(gwps, fmt, 1000); | ||
791 | else | ||
792 | if(intval > 1) /* enum */ | ||
793 | fmt = skip_conditional(gwps, fmt, intval - 1); | ||
794 | |||
795 | level++; | ||
796 | break; | ||
797 | |||
798 | default: | ||
799 | value = get_tag(gwps->data, id3, nid3, fmt, temp_buf, sizeof(temp_buf), | ||
800 | &tag_length, subline_time_mult, flags, | ||
801 | &intval); | ||
802 | fmt += tag_length; | ||
803 | |||
804 | if (value) | ||
805 | { | ||
806 | while (*value && (buf < buf_end)) | ||
807 | *buf++ = *value++; | ||
808 | } | ||
809 | } | ||
810 | } | ||
811 | |||
812 | /* remember where the current aligned text started */ | ||
813 | switch (cur_align) | ||
814 | { | ||
815 | case WPS_ALIGN_LEFT: | ||
816 | align->left = cur_align_start; | ||
817 | break; | ||
818 | |||
819 | case WPS_ALIGN_CENTER: | ||
820 | align->center = cur_align_start; | ||
821 | break; | ||
822 | |||
823 | case WPS_ALIGN_RIGHT: | ||
824 | align->right = cur_align_start; | ||
825 | break; | ||
826 | } | ||
827 | |||
828 | *buf = 0; | ||
829 | |||
830 | /* if resulting line is an empty line, set the subline time to 0 */ | ||
831 | if (buf - buf_start == 0) | ||
832 | *subline_time_mult = 0; | ||
833 | |||
834 | /* If no flags have been set, the line didn't contain any format codes. | ||
835 | We still want to refresh it. */ | ||
836 | if(*flags == 0) | ||
837 | *flags = WPS_REFRESH_STATIC; | ||
838 | } | ||
839 | |||
840 | /* fades the volume */ | ||
841 | void fade(bool fade_in) | ||
842 | { | ||
843 | unsigned fp_global_vol = global_settings.volume << 8; | ||
844 | unsigned fp_step = fp_global_vol / 30; | ||
845 | |||
846 | if (fade_in) { | ||
847 | /* fade in */ | ||
848 | unsigned fp_volume = 0; | ||
849 | |||
850 | /* zero out the sound */ | ||
851 | sound_set_volume(0); | ||
852 | |||
853 | sleep(HZ/10); /* let audio thread run */ | ||
854 | audio_resume(); | ||
855 | |||
856 | while (fp_volume < fp_global_vol) { | ||
857 | fp_volume += fp_step; | ||
858 | sleep(1); | ||
859 | sound_set_volume(fp_volume >> 8); | ||
860 | } | ||
861 | sound_set_volume(global_settings.volume); | ||
862 | } | ||
863 | else { | ||
864 | /* fade out */ | ||
865 | unsigned fp_volume = fp_global_vol; | ||
866 | |||
867 | while (fp_volume > fp_step) { | ||
868 | fp_volume -= fp_step; | ||
869 | sleep(1); | ||
870 | sound_set_volume(fp_volume >> 8); | ||
871 | } | ||
872 | audio_pause(); | ||
873 | #ifndef SIMULATOR | ||
874 | /* let audio thread run and wait for the mas to run out of data */ | ||
875 | while (!mp3_pause_done()) | ||
876 | #endif | ||
877 | sleep(HZ/10); | ||
878 | |||
879 | /* reset volume to what it was before the fade */ | ||
880 | sound_set_volume(global_settings.volume); | ||
881 | } | ||
882 | } | ||
883 | |||
884 | /* Set format string to use for WPS, splitting it into lines */ | ||
885 | void gui_wps_format(struct wps_data *data, const char *bmpdir, size_t bmpdirlen) | ||
886 | { | ||
887 | if(!data) return; | ||
888 | char* buf = data->format_buffer; | ||
889 | char* start_of_line = data->format_buffer; | ||
890 | int line = 0; | ||
891 | int subline; | ||
892 | #ifndef HAVE_LCD_BITMAP | ||
893 | /* no bitmap lcd == no bitmap loading */ | ||
894 | (void)bmpdir; | ||
895 | (void)bmpdirlen; | ||
896 | #else | ||
897 | unsigned char* img_buf_ptr = data->img_buf; /* where are in image buffer? */ | ||
898 | |||
899 | int img_buf_free = IMG_BUFSIZE; /* free space in image buffer */ | ||
900 | #endif | ||
901 | for (line=0; line<WPS_MAX_LINES; line++) | ||
902 | { | ||
903 | for (subline=0; subline<WPS_MAX_SUBLINES; subline++) | ||
904 | { | ||
905 | data->format_lines[line][subline] = 0; | ||
906 | data->time_mult[line][subline] = 0; | ||
907 | } | ||
908 | data->subline_expire_time[line] = 0; | ||
909 | data->curr_subline[line] = SUBLINE_RESET; | ||
910 | } | ||
911 | |||
912 | line = 0; | ||
913 | subline = 0; | ||
914 | data->format_lines[line][subline] = buf; | ||
915 | |||
916 | while ((*buf) && (line < WPS_MAX_LINES)) | ||
917 | { | ||
918 | switch (*buf) | ||
919 | { | ||
920 | /* | ||
921 | * skip % sequences so "%;" doesn't start a new subline | ||
922 | * don't skip %x lines (pre-load bitmaps) | ||
923 | */ | ||
924 | case '%': | ||
925 | if (*(buf+1) != 'x') | ||
926 | buf++; | ||
927 | break; | ||
928 | |||
929 | case '\r': /* CR */ | ||
930 | *buf = 0; | ||
931 | break; | ||
932 | |||
933 | case '\n': /* LF */ | ||
934 | *buf = 0; | ||
935 | |||
936 | if (*start_of_line != '#') /* A comment? */ | ||
937 | line++; | ||
938 | |||
939 | if (line < WPS_MAX_LINES) | ||
940 | { | ||
941 | /* the next line starts on the next byte */ | ||
942 | subline = 0; | ||
943 | data->format_lines[line][subline] = buf+1; | ||
944 | start_of_line = data->format_lines[line][subline]; | ||
945 | } | ||
946 | break; | ||
947 | |||
948 | case ';': /* start a new subline */ | ||
949 | *buf = 0; | ||
950 | subline++; | ||
951 | if (subline < WPS_MAX_SUBLINES) | ||
952 | { | ||
953 | data->format_lines[line][subline] = buf+1; | ||
954 | } | ||
955 | else /* exceeded max sublines, skip rest of line */ | ||
956 | { | ||
957 | while (*(++buf)) | ||
958 | { | ||
959 | if ((*buf == '\r') || (*buf == '\n')) | ||
960 | { | ||
961 | break; | ||
962 | } | ||
963 | } | ||
964 | buf--; | ||
965 | subline = 0; | ||
966 | } | ||
967 | break; | ||
968 | |||
969 | case 'x': | ||
970 | #ifdef HAVE_LCD_BITMAP | ||
971 | /* Preload images so the %xd# tag can display it */ | ||
972 | { | ||
973 | int ret = 0; | ||
974 | int n; | ||
975 | char *ptr = buf+1; | ||
976 | char *pos = NULL; | ||
977 | char imgname[MAX_PATH]; | ||
978 | char qual = *ptr; | ||
979 | if (qual == 'l' || qual == '|') /* format: | ||
980 | %x|n|filename.bmp|x|y| | ||
981 | or | ||
982 | %xl|n|filename.bmp|x|y| | ||
983 | */ | ||
984 | { | ||
985 | ptr = strchr(ptr, '|') + 1; | ||
986 | pos = strchr(ptr, '|'); | ||
987 | if (pos) | ||
988 | { | ||
989 | /* get the image ID */ | ||
990 | n = *ptr; | ||
991 | if(n >= 'a' && n <= 'z') | ||
992 | n -= 'a'; | ||
993 | if(n >= 'A' && n <= 'Z') | ||
994 | n = n - 'A' + 26; | ||
995 | |||
996 | if(n < 0 || n >= MAX_IMAGES) | ||
997 | { | ||
998 | /* Skip the rest of the line */ | ||
999 | while(*buf != '\n') | ||
1000 | buf++; | ||
1001 | break; | ||
1002 | } | ||
1003 | ptr = pos+1; | ||
1004 | |||
1005 | /* check the image number and load state */ | ||
1006 | if (!data->img[n].loaded) | ||
1007 | { | ||
1008 | /* get filename */ | ||
1009 | pos = strchr(ptr, '|'); | ||
1010 | if ((pos - ptr) < | ||
1011 | (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2) | ||
1012 | { | ||
1013 | memcpy(imgname, bmpdir, bmpdirlen); | ||
1014 | imgname[bmpdirlen] = '/'; | ||
1015 | memcpy(&imgname[bmpdirlen+1], | ||
1016 | ptr, pos - ptr); | ||
1017 | imgname[bmpdirlen+1+pos-ptr] = 0; | ||
1018 | } | ||
1019 | else | ||
1020 | /* filename too long */ | ||
1021 | imgname[0] = 0; | ||
1022 | |||
1023 | ptr = pos+1; | ||
1024 | |||
1025 | /* get x-position */ | ||
1026 | pos = strchr(ptr, '|'); | ||
1027 | if (pos) | ||
1028 | data->img[n].x = atoi(ptr); | ||
1029 | else | ||
1030 | { | ||
1031 | /* weird syntax, bail out */ | ||
1032 | buf++; | ||
1033 | break; | ||
1034 | } | ||
1035 | |||
1036 | /* get y-position */ | ||
1037 | ptr = pos+1; | ||
1038 | pos = strchr(ptr, '|'); | ||
1039 | if (pos) | ||
1040 | data->img[n].y = atoi(ptr); | ||
1041 | else | ||
1042 | { | ||
1043 | /* weird syntax, bail out */ | ||
1044 | buf++; | ||
1045 | break; | ||
1046 | } | ||
1047 | |||
1048 | pos++; | ||
1049 | |||
1050 | /* reposition buf pointer to next WPS element */ | ||
1051 | while (*pos && *pos != ';' && | ||
1052 | *pos != '\r' && *pos != '\n') | ||
1053 | pos++; | ||
1054 | |||
1055 | buf = pos; | ||
1056 | |||
1057 | /* load the image */ | ||
1058 | ret = read_bmp_file(imgname, &data->img[n].w, | ||
1059 | &data->img[n].h, img_buf_ptr, | ||
1060 | img_buf_free); | ||
1061 | if (ret > 0) | ||
1062 | { | ||
1063 | data->img[n].ptr = img_buf_ptr; | ||
1064 | img_buf_ptr += ret; | ||
1065 | img_buf_free -= ret; | ||
1066 | data->img[n].loaded = true; | ||
1067 | if(qual == '|') | ||
1068 | data->img[n].always_display = true; | ||
1069 | } | ||
1070 | } | ||
1071 | buf++; | ||
1072 | } | ||
1073 | } | ||
1074 | } | ||
1075 | #endif | ||
1076 | break; | ||
1077 | } | ||
1078 | buf++; | ||
1079 | } | ||
1080 | } | ||
1081 | |||
1082 | #ifdef HAVE_LCD_BITMAP | ||
1083 | /* Display images */ | ||
1084 | static void wps_display_images(struct gui_wps *gwps) | ||
1085 | { | ||
1086 | if(!gwps || !gwps->data || !gwps->display) return; | ||
1087 | int n; | ||
1088 | struct wps_data *data = gwps->data; | ||
1089 | struct screen *display = gwps->display; | ||
1090 | for (n = 0; n < MAX_IMAGES; n++) { | ||
1091 | if (data->img[n].loaded && data->img[n].display) { | ||
1092 | if(data->img[n].always_display) | ||
1093 | display->set_drawmode(DRMODE_FG); | ||
1094 | else | ||
1095 | display->set_drawmode(DRMODE_SOLID); | ||
1096 | |||
1097 | display->mono_bitmap(data->img[n].ptr, data->img[n].x, | ||
1098 | data->img[n].y, data->img[n].w, data->img[n].h); | ||
1099 | display->update_rect(data->img[n].x, data->img[n].y, | ||
1100 | data->img[n].w, data->img[n].h); | ||
1101 | } | ||
1102 | } | ||
1103 | display->set_drawmode(DRMODE_SOLID); | ||
1104 | } | ||
1105 | #endif | ||
1106 | |||
1107 | void gui_wps_reset(struct gui_wps *gui_wps) | ||
1108 | { | ||
1109 | if(!gui_wps || !gui_wps->data) return; | ||
1110 | gui_wps->data->wps_loaded = false; | ||
1111 | memset(&gui_wps->data->format_buffer, 0, sizeof (gui_wps->data->format_buffer)); | ||
1112 | } | ||
1113 | |||
1114 | bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset, | ||
1115 | unsigned char refresh_mode) | ||
1116 | { | ||
1117 | char buf[MAX_PATH]; | ||
1118 | unsigned char flags; | ||
1119 | int i; | ||
1120 | bool update_line; | ||
1121 | bool only_one_subline; | ||
1122 | bool new_subline_refresh; | ||
1123 | int search; | ||
1124 | int search_start; | ||
1125 | struct wps_data *data = gwps->data; | ||
1126 | struct wps_state *state = gwps->state; | ||
1127 | struct screen *display = gwps->display; | ||
1128 | if(!gwps || !data || !state || !display){ | ||
1129 | return false; | ||
1130 | } | ||
1131 | #ifdef HAVE_LCD_BITMAP | ||
1132 | int h = font_get(FONT_UI)->height; | ||
1133 | int offset = global_settings.statusbar ? STATUSBAR_HEIGHT : 0; | ||
1134 | /* to find out wether the peak meter is enabled we | ||
1135 | assume it wasn't until we find a line that contains | ||
1136 | the peak meter. We can't use peak_meter_enabled itself | ||
1137 | because that would mean to turn off the meter thread | ||
1138 | temporarily. (That shouldn't matter unless yield | ||
1139 | or sleep is called but who knows...) | ||
1140 | */ | ||
1141 | bool enable_pm = false; | ||
1142 | |||
1143 | /* Set images to not to be displayed */ | ||
1144 | for (i = 0; i < MAX_IMAGES; i++) { | ||
1145 | data->img[i].display = false; | ||
1146 | } | ||
1147 | #endif | ||
1148 | /* reset to first subline if refresh all flag is set */ | ||
1149 | if (refresh_mode == WPS_REFRESH_ALL) | ||
1150 | { | ||
1151 | for (i=0; i<WPS_MAX_LINES; i++) | ||
1152 | { | ||
1153 | data->curr_subline[i] = SUBLINE_RESET; | ||
1154 | } | ||
1155 | } | ||
1156 | |||
1157 | #ifdef HAVE_LCD_CHARCELLS | ||
1158 | for (i=0; i<8; i++) { | ||
1159 | if (data->wps_progress_pat[i]==0) | ||
1160 | data->wps_progress_pat[i]=display->get_locked_pattern(); | ||
1161 | } | ||
1162 | #endif | ||
1163 | |||
1164 | if (!state->id3) | ||
1165 | { | ||
1166 | display->stop_scroll(); | ||
1167 | return false; | ||
1168 | } | ||
1169 | |||
1170 | state->ff_rewind_count = ffwd_offset; | ||
1171 | |||
1172 | for (i = 0; i < WPS_MAX_LINES; i++) | ||
1173 | { | ||
1174 | new_subline_refresh = false; | ||
1175 | only_one_subline = false; | ||
1176 | |||
1177 | /* if time to advance to next sub-line */ | ||
1178 | if (TIME_AFTER(current_tick, data->subline_expire_time[i] - 1) || | ||
1179 | (data->curr_subline[i] == SUBLINE_RESET)) | ||
1180 | { | ||
1181 | /* search all sublines until the next subline with time > 0 | ||
1182 | is found or we get back to the subline we started with */ | ||
1183 | if (data->curr_subline[i] == SUBLINE_RESET) | ||
1184 | search_start = 0; | ||
1185 | else | ||
1186 | search_start = data->curr_subline[i]; | ||
1187 | for (search=0; search<WPS_MAX_SUBLINES; search++) | ||
1188 | { | ||
1189 | data->curr_subline[i]++; | ||
1190 | |||
1191 | /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */ | ||
1192 | if ((!data->format_lines[i][data->curr_subline[i]]) || | ||
1193 | (data->curr_subline[i] == WPS_MAX_SUBLINES)) | ||
1194 | { | ||
1195 | if (data->curr_subline[i] == 1) | ||
1196 | only_one_subline = true; | ||
1197 | data->curr_subline[i] = 0; | ||
1198 | } | ||
1199 | |||
1200 | /* if back where we started after search or | ||
1201 | only one subline is defined on the line */ | ||
1202 | if (((search > 0) && (data->curr_subline[i] == search_start)) || | ||
1203 | only_one_subline) | ||
1204 | { | ||
1205 | /* no other subline with a time > 0 exists */ | ||
1206 | data->subline_expire_time[i] = current_tick + 100 * HZ; | ||
1207 | break; | ||
1208 | } | ||
1209 | else | ||
1210 | { | ||
1211 | /* get initial time multiplier and | ||
1212 | line type flags for this subline */ | ||
1213 | format_display(gwps, buf, sizeof(buf), | ||
1214 | state->id3, state->nid3, | ||
1215 | data->format_lines[i][data->curr_subline[i]], | ||
1216 | &data->format_align[i][data->curr_subline[i]], | ||
1217 | &data->time_mult[i][data->curr_subline[i]], | ||
1218 | &data->line_type[i][data->curr_subline[i]]); | ||
1219 | |||
1220 | /* only use this subline if subline time > 0 */ | ||
1221 | if (data->time_mult[i][data->curr_subline[i]] > 0) | ||
1222 | { | ||
1223 | new_subline_refresh = true; | ||
1224 | data->subline_expire_time[i] = current_tick + | ||
1225 | BASE_SUBLINE_TIME * data->time_mult[i][data->curr_subline[i]]; | ||
1226 | break; | ||
1227 | } | ||
1228 | } | ||
1229 | } | ||
1230 | |||
1231 | } | ||
1232 | |||
1233 | update_line = false; | ||
1234 | |||
1235 | if ( !data->format_lines[i][data->curr_subline[i]] ) | ||
1236 | break; | ||
1237 | |||
1238 | if ((data->line_type[i][data->curr_subline[i]] & refresh_mode) || | ||
1239 | (refresh_mode == WPS_REFRESH_ALL) || | ||
1240 | new_subline_refresh) | ||
1241 | { | ||
1242 | flags = 0; | ||
1243 | #ifdef HAVE_LCD_BITMAP | ||
1244 | int left_width, left_xpos; | ||
1245 | int center_width, center_xpos; | ||
1246 | int right_width, right_xpos; | ||
1247 | int space_width; | ||
1248 | int string_height; | ||
1249 | int ypos; | ||
1250 | #endif | ||
1251 | |||
1252 | format_display(gwps, buf, sizeof(buf), | ||
1253 | state->id3, state->nid3, | ||
1254 | data->format_lines[i][data->curr_subline[i]], | ||
1255 | &data->format_align[i][data->curr_subline[i]], | ||
1256 | &data->time_mult[i][data->curr_subline[i]], | ||
1257 | &flags); | ||
1258 | data->line_type[i][data->curr_subline[i]] = flags; | ||
1259 | |||
1260 | #ifdef HAVE_LCD_BITMAP | ||
1261 | /* progress */ | ||
1262 | if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) | ||
1263 | { | ||
1264 | #define PROGRESS_BAR_HEIGHT 6 /* this should probably be defined elsewhere; config-*.h perhaps? */ | ||
1265 | int sby = i*h + offset + (h > 7 ? (h - 6) / 2 : 1); | ||
1266 | gui_scrollbar_draw(display, 0, sby, display->width, PROGRESS_BAR_HEIGHT, | ||
1267 | state->id3->length?state->id3->length:1, 0, | ||
1268 | state->id3->length?state->id3->elapsed + state->ff_rewind_count:0, | ||
1269 | HORIZONTAL); | ||
1270 | #ifdef AB_REPEAT_ENABLE | ||
1271 | if ( ab_repeat_mode_enabled() ) | ||
1272 | ab_draw_markers(state->id3->length, 0, sby, LCD_WIDTH, PROGRESS_BAR_HEIGHT); | ||
1273 | #endif | ||
1274 | update_line = true; | ||
1275 | } | ||
1276 | if (flags & refresh_mode & WPS_REFRESH_PEAK_METER && display->height >= LCD_HEIGHT) { | ||
1277 | /* peak meter */ | ||
1278 | int peak_meter_y; | ||
1279 | |||
1280 | update_line = true; | ||
1281 | peak_meter_y = i * h + offset; | ||
1282 | |||
1283 | /* The user might decide to have the peak meter in the last | ||
1284 | line so that it is only displayed if no status bar is | ||
1285 | visible. If so we neither want do draw nor enable the | ||
1286 | peak meter. */ | ||
1287 | if (peak_meter_y + h <= LCD_HEIGHT) { | ||
1288 | /* found a line with a peak meter -> remember that we must | ||
1289 | enable it later */ | ||
1290 | enable_pm = true; | ||
1291 | peak_meter_draw(0, peak_meter_y, LCD_WIDTH, | ||
1292 | MIN(h, LCD_HEIGHT - peak_meter_y)); | ||
1293 | } | ||
1294 | } | ||
1295 | #else | ||
1296 | /* progress */ | ||
1297 | if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) { | ||
1298 | if (data->full_line_progressbar) | ||
1299 | draw_player_fullbar(gwps, buf, sizeof(buf)); | ||
1300 | else | ||
1301 | draw_player_progress(gwps); | ||
1302 | } | ||
1303 | #endif | ||
1304 | #ifdef HAVE_LCD_BITMAP | ||
1305 | /* calculate different string sizes and positions */ | ||
1306 | display->getstringsize(" ", &space_width, &string_height); | ||
1307 | if (data->format_align[i][data->curr_subline[i]].left != 0) { | ||
1308 | display->getstringsize(data->format_align[i][data->curr_subline[i]].left, | ||
1309 | &left_width, &string_height); | ||
1310 | } | ||
1311 | else { | ||
1312 | left_width = 0; | ||
1313 | } | ||
1314 | left_xpos = 0; | ||
1315 | |||
1316 | if (data->format_align[i][data->curr_subline[i]].center != 0) { | ||
1317 | display->getstringsize(data->format_align[i][data->curr_subline[i]].center, | ||
1318 | ¢er_width, &string_height); | ||
1319 | } | ||
1320 | else { | ||
1321 | center_width = 0; | ||
1322 | } | ||
1323 | center_xpos=(display->width - center_width) / 2; | ||
1324 | |||
1325 | if (data->format_align[i][data->curr_subline[i]].right != 0) { | ||
1326 | display->getstringsize(data->format_align[i][data->curr_subline[i]].right, | ||
1327 | &right_width, &string_height); | ||
1328 | } | ||
1329 | else { | ||
1330 | right_width = 0; | ||
1331 | } | ||
1332 | right_xpos = (display->width - right_width); | ||
1333 | |||
1334 | /* Checks for overlapping strings. | ||
1335 | If needed the overlapping strings will be merged, separated by a | ||
1336 | space */ | ||
1337 | |||
1338 | /* CASE 1: left and centered string overlap */ | ||
1339 | /* there is a left string, need to merge left and center */ | ||
1340 | if ((left_width != 0 && center_width != 0) && | ||
1341 | (left_xpos + left_width + space_width > center_xpos)) { | ||
1342 | /* replace the former separator '\0' of left and | ||
1343 | center string with a space */ | ||
1344 | *(--data->format_align[i][data->curr_subline[i]].center) = ' '; | ||
1345 | /* calculate the new width and position of the merged string */ | ||
1346 | left_width = left_width + space_width + center_width; | ||
1347 | left_xpos = 0; | ||
1348 | /* there is no centered string anymore */ | ||
1349 | center_width = 0; | ||
1350 | } | ||
1351 | /* there is no left string, move center to left */ | ||
1352 | if ((left_width == 0 && center_width != 0) && | ||
1353 | (left_xpos + left_width > center_xpos)) { | ||
1354 | /* move the center string to the left string */ | ||
1355 | data->format_align[i][data->curr_subline[i]].left = | ||
1356 | data->format_align[i][data->curr_subline[i]].center; | ||
1357 | /* calculate the new width and position of the string */ | ||
1358 | left_width = center_width; | ||
1359 | left_xpos = 0; | ||
1360 | /* there is no centered string anymore */ | ||
1361 | center_width = 0; | ||
1362 | } | ||
1363 | |||
1364 | /* CASE 2: centered and right string overlap */ | ||
1365 | /* there is a right string, need to merge center and right */ | ||
1366 | if ((center_width != 0 && right_width != 0) && | ||
1367 | (center_xpos + center_width + space_width > right_xpos)) { | ||
1368 | /* replace the former separator '\0' of center and | ||
1369 | right string with a space */ | ||
1370 | *(--data->format_align[i][data->curr_subline[i]].right) = ' '; | ||
1371 | /* move the center string to the right after merge */ | ||
1372 | data->format_align[i][data->curr_subline[i]].right = | ||
1373 | data->format_align[i][data->curr_subline[i]].center; | ||
1374 | /* calculate the new width and position of the merged string */ | ||
1375 | right_width = center_width + space_width + right_width; | ||
1376 | right_xpos = (display->width - right_width); | ||
1377 | /* there is no centered string anymore */ | ||
1378 | center_width = 0; | ||
1379 | } | ||
1380 | /* there is no right string, move center to right */ | ||
1381 | if ((center_width != 0 && right_width == 0) && | ||
1382 | (center_xpos + center_width > right_xpos)) { | ||
1383 | /* move the center string to the right string */ | ||
1384 | data->format_align[i][data->curr_subline[i]].right = | ||
1385 | data->format_align[i][data->curr_subline[i]].center; | ||
1386 | /* calculate the new width and position of the string */ | ||
1387 | right_width = center_width; | ||
1388 | right_xpos = (display->width - right_width); | ||
1389 | /* there is no centered string anymore */ | ||
1390 | center_width = 0; | ||
1391 | } | ||
1392 | |||
1393 | /* CASE 3: left and right overlap | ||
1394 | There is no center string anymore, either there never | ||
1395 | was one or it has been merged in case 1 or 2 */ | ||
1396 | /* there is a left string, need to merge left and right */ | ||
1397 | if ((left_width != 0 && center_width == 0 && right_width != 0) && | ||
1398 | (left_xpos + left_width + space_width > right_xpos)) { | ||
1399 | /* replace the former separator '\0' of left and | ||
1400 | right string with a space */ | ||
1401 | *(--data->format_align[i][data->curr_subline[i]].right) = ' '; | ||
1402 | /* calculate the new width and position of the string */ | ||
1403 | left_width = left_width + space_width + right_width; | ||
1404 | left_xpos = 0; | ||
1405 | /* there is no right string anymore */ | ||
1406 | right_width = 0; | ||
1407 | } | ||
1408 | /* there is no left string, move right to left */ | ||
1409 | if ((left_width == 0 && center_width == 0 && right_width != 0) && | ||
1410 | (left_xpos + left_width > right_xpos)) { | ||
1411 | /* move the right string to the left string */ | ||
1412 | data->format_align[i][data->curr_subline[i]].left = | ||
1413 | data->format_align[i][data->curr_subline[i]].right; | ||
1414 | /* calculate the new width and position of the string */ | ||
1415 | left_width = right_width; | ||
1416 | left_xpos = 0; | ||
1417 | /* there is no right string anymore */ | ||
1418 | right_width = 0; | ||
1419 | } | ||
1420 | |||
1421 | #endif | ||
1422 | |||
1423 | if (flags & WPS_REFRESH_SCROLL) { | ||
1424 | |||
1425 | /* scroll line */ | ||
1426 | if ((refresh_mode & WPS_REFRESH_SCROLL) || | ||
1427 | new_subline_refresh) { | ||
1428 | #ifdef HAVE_LCD_BITMAP | ||
1429 | ypos = (i*string_height)+display->getymargin(); | ||
1430 | update_line = true; | ||
1431 | |||
1432 | if (left_width>display->width) { | ||
1433 | display->puts_scroll(0, i, | ||
1434 | data->format_align[i][data->curr_subline[i]].left); | ||
1435 | } else { | ||
1436 | /* clear the line first */ | ||
1437 | display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); | ||
1438 | display->fillrect(0, ypos, display->width, string_height); | ||
1439 | display->set_drawmode(DRMODE_SOLID); | ||
1440 | |||
1441 | /* Nasty hack: we output an empty scrolling string, | ||
1442 | which will reset the scroller for that line */ | ||
1443 | display->puts_scroll(0, i, ""); | ||
1444 | |||
1445 | /* print aligned strings */ | ||
1446 | if (left_width != 0) | ||
1447 | { | ||
1448 | display->putsxy(left_xpos, ypos, | ||
1449 | data->format_align[i][data->curr_subline[i]].left); | ||
1450 | } | ||
1451 | if (center_width != 0) | ||
1452 | { | ||
1453 | display->putsxy(center_xpos, ypos, | ||
1454 | data->format_align[i][data->curr_subline[i]].center); | ||
1455 | } | ||
1456 | if (right_width != 0) | ||
1457 | { | ||
1458 | display->putsxy(right_xpos, ypos, | ||
1459 | data->format_align[i][data->curr_subline[i]].right); | ||
1460 | } | ||
1461 | } | ||
1462 | #else | ||
1463 | display->puts_scroll(0, i, buf); | ||
1464 | update_line = true; | ||
1465 | #endif | ||
1466 | } | ||
1467 | } | ||
1468 | else if (flags & (WPS_REFRESH_DYNAMIC | WPS_REFRESH_STATIC)) | ||
1469 | { | ||
1470 | /* dynamic / static line */ | ||
1471 | if ((refresh_mode & (WPS_REFRESH_DYNAMIC|WPS_REFRESH_STATIC)) || | ||
1472 | new_subline_refresh) | ||
1473 | { | ||
1474 | #ifdef HAVE_LCD_BITMAP | ||
1475 | ypos = (i*string_height)+display->getymargin(); | ||
1476 | update_line = true; | ||
1477 | |||
1478 | /* clear the line first */ | ||
1479 | display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); | ||
1480 | display->fillrect(0, ypos, display->width, string_height); | ||
1481 | display->set_drawmode(DRMODE_SOLID); | ||
1482 | |||
1483 | /* Nasty hack: we output an empty scrolling string, | ||
1484 | which will reset the scroller for that line */ | ||
1485 | display->puts_scroll(0, i, ""); | ||
1486 | |||
1487 | /* print aligned strings */ | ||
1488 | if (left_width != 0) | ||
1489 | { | ||
1490 | display->putsxy(left_xpos, ypos, | ||
1491 | data->format_align[i][data->curr_subline[i]].left); | ||
1492 | } | ||
1493 | if (center_width != 0) | ||
1494 | { | ||
1495 | display->putsxy(center_xpos, ypos, | ||
1496 | data->format_align[i][data->curr_subline[i]].center); | ||
1497 | } | ||
1498 | if (right_width != 0) | ||
1499 | { | ||
1500 | display->putsxy(right_xpos, ypos, | ||
1501 | data->format_align[i][data->curr_subline[i]].right); | ||
1502 | } | ||
1503 | #else | ||
1504 | update_line = true; | ||
1505 | display->puts(0, i, buf); | ||
1506 | #endif | ||
1507 | } | ||
1508 | } | ||
1509 | } | ||
1510 | #ifdef HAVE_LCD_BITMAP | ||
1511 | if (update_line) { | ||
1512 | display->update_rect(0, i*h + offset, display->width, h); | ||
1513 | } | ||
1514 | #endif | ||
1515 | } | ||
1516 | |||
1517 | #ifdef HAVE_LCD_BITMAP | ||
1518 | /* Display all images */ | ||
1519 | for (i = 0; i < MAX_IMAGES; i++) { | ||
1520 | if(data->img[i].always_display) | ||
1521 | data->img[i].display = data->img[i].always_display; | ||
1522 | } | ||
1523 | wps_display_images(gwps); | ||
1524 | |||
1525 | /* Now we know wether the peak meter is used. | ||
1526 | So we can enable / disable the peak meter thread */ | ||
1527 | data->peak_meter_enabled = enable_pm; | ||
1528 | #endif | ||
1529 | |||
1530 | #if defined(CONFIG_BACKLIGHT) && !defined(SIMULATOR) | ||
1531 | if (global_settings.caption_backlight && state->id3) { | ||
1532 | /* turn on backlight n seconds before track ends, and turn it off n | ||
1533 | seconds into the new track. n == backlight_timeout, or 5s */ | ||
1534 | int n = | ||
1535 | backlight_timeout_value[global_settings.backlight_timeout] * 1000; | ||
1536 | |||
1537 | if ( n < 1000 ) | ||
1538 | n = 5000; /* use 5s if backlight is always on or off */ | ||
1539 | |||
1540 | if ((state->id3->elapsed < 1000) || | ||
1541 | ((state->id3->length - state->id3->elapsed) < (unsigned)n)) | ||
1542 | backlight_on(); | ||
1543 | } | ||
1544 | #endif | ||
1545 | return true; | ||
1546 | } | ||
1547 | |||
1548 | #ifdef HAVE_LCD_CHARCELLS | ||
1549 | static bool draw_player_progress(struct gui_wps *gwps) | ||
1550 | { | ||
1551 | char player_progressbar[7]; | ||
1552 | char binline[36]; | ||
1553 | int songpos = 0; | ||
1554 | int i,j; | ||
1555 | struct wps_state *state = gwps->state; | ||
1556 | if (!state->id3) | ||
1557 | return false; | ||
1558 | |||
1559 | memset(binline, 1, sizeof binline); | ||
1560 | memset(player_progressbar, 1, sizeof player_progressbar); | ||
1561 | |||
1562 | if(state->id3->elapsed >= state->id3->length) | ||
1563 | songpos = 0; | ||
1564 | else | ||
1565 | { | ||
1566 | if(state->wps_time_countup == false) | ||
1567 | songpos = ((state->id3->elapsed - state->ff_rewind_count) * 36) / | ||
1568 | state->id3->length; | ||
1569 | else | ||
1570 | songpos = ((state->id3->elapsed + state->ff_rewind_count) * 36) / | ||
1571 | state->id3->length; | ||
1572 | } | ||
1573 | for (i=0; i < songpos; i++) | ||
1574 | binline[i] = 0; | ||
1575 | |||
1576 | for (i=0; i<=6; i++) { | ||
1577 | for (j=0;j<5;j++) { | ||
1578 | player_progressbar[i] <<= 1; | ||
1579 | player_progressbar[i] += binline[i*5+j]; | ||
1580 | } | ||
1581 | } | ||
1582 | gwps->display->define_pattern(gwps->data->wps_progress_pat[0], player_progressbar); | ||
1583 | return true; | ||
1584 | } | ||
1585 | |||
1586 | static char map_fullbar_char(char ascii_val) | ||
1587 | { | ||
1588 | if (ascii_val >= '0' && ascii_val <= '9') { | ||
1589 | return(ascii_val - '0'); | ||
1590 | } | ||
1591 | else if (ascii_val == ':') { | ||
1592 | return(10); | ||
1593 | } | ||
1594 | else | ||
1595 | return(11); /* anything besides a number or ':' is mapped to <blank> */ | ||
1596 | } | ||
1597 | |||
1598 | static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size) | ||
1599 | { | ||
1600 | int i,j,lcd_char_pos; | ||
1601 | |||
1602 | char player_progressbar[7]; | ||
1603 | char binline[36]; | ||
1604 | static const char numbers[12][4][3]={ | ||
1605 | {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/ | ||
1606 | {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/ | ||
1607 | {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/ | ||
1608 | {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/ | ||
1609 | {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/ | ||
1610 | {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/ | ||
1611 | {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/ | ||
1612 | {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/ | ||
1613 | {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/ | ||
1614 | {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/ | ||
1615 | {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/ | ||
1616 | {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/ | ||
1617 | }; | ||
1618 | |||
1619 | int songpos = 0; | ||
1620 | int digits[6]; | ||
1621 | int time; | ||
1622 | char timestr[7]; | ||
1623 | |||
1624 | struct wps_state *state = gwps->state; | ||
1625 | |||
1626 | for (i=0; i < buf_size; i++) | ||
1627 | buf[i] = ' '; | ||
1628 | |||
1629 | if(state->id3->elapsed >= state->id3->length) | ||
1630 | songpos = 55; | ||
1631 | else { | ||
1632 | if(state->wps_time_countup == false) | ||
1633 | songpos = ((state->id3->elapsed - state->ff_rewind_count) * 55) / | ||
1634 | state->id3->length; | ||
1635 | else | ||
1636 | songpos = ((state->id3->elapsed + state->ff_rewind_count) * 55) / | ||
1637 | state->id3->length; | ||
1638 | } | ||
1639 | |||
1640 | time=(state->id3->elapsed + state->ff_rewind_count); | ||
1641 | |||
1642 | memset(timestr, 0, sizeof(timestr)); | ||
1643 | gui_wps_format_time(timestr, sizeof(timestr), time); | ||
1644 | for(lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) { | ||
1645 | digits[lcd_char_pos] = map_fullbar_char(timestr[lcd_char_pos]); | ||
1646 | } | ||
1647 | |||
1648 | /* build the progressbar-icons */ | ||
1649 | for (lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) { | ||
1650 | memset(binline, 0, sizeof binline); | ||
1651 | memset(player_progressbar, 0, sizeof player_progressbar); | ||
1652 | |||
1653 | /* make the character (progressbar & digit)*/ | ||
1654 | for (i=0; i<7; i++) { | ||
1655 | for (j=0;j<5;j++) { | ||
1656 | /* make the progressbar */ | ||
1657 | if (lcd_char_pos==(songpos/5)) { | ||
1658 | /* partial */ | ||
1659 | if ((j<(songpos%5))&&(i>4)) | ||
1660 | binline[i*5+j] = 1; | ||
1661 | else | ||
1662 | binline[i*5+j] = 0; | ||
1663 | } | ||
1664 | else { | ||
1665 | if (lcd_char_pos<(songpos/5)) { | ||
1666 | /* full character */ | ||
1667 | if (i>4) | ||
1668 | binline[i*5+j] = 1; | ||
1669 | } | ||
1670 | } | ||
1671 | /* insert the digit */ | ||
1672 | if ((j<3)&&(i<4)) { | ||
1673 | if (numbers[digits[lcd_char_pos]][i][j]==1) | ||
1674 | binline[i*5+j] = 1; | ||
1675 | } | ||
1676 | } | ||
1677 | } | ||
1678 | |||
1679 | for (i=0; i<=6; i++) { | ||
1680 | for (j=0;j<5;j++) { | ||
1681 | player_progressbar[i] <<= 1; | ||
1682 | player_progressbar[i] += binline[i*5+j]; | ||
1683 | } | ||
1684 | } | ||
1685 | |||
1686 | gwps->display->define_pattern(gwps->data->wps_progress_pat[lcd_char_pos+1],player_progressbar); | ||
1687 | buf[lcd_char_pos]=gwps->data->wps_progress_pat[lcd_char_pos+1]; | ||
1688 | |||
1689 | } | ||
1690 | |||
1691 | /* make rest of the progressbar if necessary */ | ||
1692 | if (songpos/5>5) { | ||
1693 | |||
1694 | /* set the characters positions that use the full 5 pixel wide bar */ | ||
1695 | for (lcd_char_pos=6; lcd_char_pos < (songpos/5); lcd_char_pos++) | ||
1696 | buf[lcd_char_pos] = 0x86; /* '_' */ | ||
1697 | |||
1698 | /* build the partial bar character for the tail character position */ | ||
1699 | memset(binline, 0, sizeof binline); | ||
1700 | memset(player_progressbar, 0, sizeof player_progressbar); | ||
1701 | |||
1702 | for (i=5; i<7; i++) { | ||
1703 | for (j=0;j<5;j++) { | ||
1704 | if (j<(songpos%5)) { | ||
1705 | binline[i*5+j] = 1; | ||
1706 | } | ||
1707 | } | ||
1708 | } | ||
1709 | |||
1710 | for (i=0; i<7; i++) { | ||
1711 | for (j=0;j<5;j++) { | ||
1712 | player_progressbar[i] <<= 1; | ||
1713 | player_progressbar[i] += binline[i*5+j]; | ||
1714 | } | ||
1715 | } | ||
1716 | |||
1717 | gwps->display->define_pattern(gwps->data->wps_progress_pat[7],player_progressbar); | ||
1718 | |||
1719 | buf[songpos/5]=gwps->data->wps_progress_pat[7]; | ||
1720 | } | ||
1721 | } | ||
1722 | #endif | ||
1723 | |||
1724 | /* set volume | ||
1725 | return true if screen restore is needed | ||
1726 | return false otherwise | ||
1727 | */ | ||
1728 | bool setvol(void) | ||
1729 | { | ||
1730 | if (global_settings.volume < sound_min(SOUND_VOLUME)) | ||
1731 | global_settings.volume = sound_min(SOUND_VOLUME); | ||
1732 | if (global_settings.volume > sound_max(SOUND_VOLUME)) | ||
1733 | global_settings.volume = sound_max(SOUND_VOLUME); | ||
1734 | sound_set_volume(global_settings.volume); | ||
1735 | gui_syncstatusbar_draw(&statusbars, false); | ||
1736 | int i; | ||
1737 | FOR_NB_SCREENS(i) | ||
1738 | gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_NON_STATIC); | ||
1739 | settings_save(); | ||
1740 | #ifdef HAVE_LCD_CHARCELLS | ||
1741 | gui_syncsplash(0, false, "Vol: %d %% ", | ||
1742 | sound_val2phys(SOUND_VOLUME, global_settings.volume)); | ||
1743 | return true; | ||
1744 | #endif | ||
1745 | return false; | ||
1746 | } | ||
1747 | |||
1748 | bool ffwd_rew(int button) | ||
1749 | { | ||
1750 | static const int ff_rew_steps[] = { | ||
1751 | 1000, 2000, 3000, 4000, | ||
1752 | 5000, 6000, 8000, 10000, | ||
1753 | 15000, 20000, 25000, 30000, | ||
1754 | 45000, 60000 | ||
1755 | }; | ||
1756 | |||
1757 | unsigned int step = 0; /* current ff/rewind step */ | ||
1758 | unsigned int max_step = 0; /* maximum ff/rewind step */ | ||
1759 | int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */ | ||
1760 | int direction = -1; /* forward=1 or backward=-1 */ | ||
1761 | long accel_tick = 0; /* next time at which to bump the step size */ | ||
1762 | bool exit = false; | ||
1763 | bool usb = false; | ||
1764 | int i = 0; | ||
1765 | |||
1766 | while (!exit) { | ||
1767 | switch ( button ) { | ||
1768 | case WPS_FFWD: | ||
1769 | #ifdef WPS_RC_FFWD | ||
1770 | case WPS_RC_FFWD: | ||
1771 | #endif | ||
1772 | direction = 1; | ||
1773 | case WPS_REW: | ||
1774 | #ifdef WPS_RC_REW | ||
1775 | case WPS_RC_REW: | ||
1776 | #endif | ||
1777 | if (wps_state.ff_rewind) | ||
1778 | { | ||
1779 | if (direction == 1) | ||
1780 | { | ||
1781 | /* fast forwarding, calc max step relative to end */ | ||
1782 | max_step = | ||
1783 | (wps_state.id3->length - (wps_state.id3->elapsed + ff_rewind_count)) * | ||
1784 | FF_REWIND_MAX_PERCENT / 100; | ||
1785 | } | ||
1786 | else | ||
1787 | { | ||
1788 | /* rewinding, calc max step relative to start */ | ||
1789 | max_step = (wps_state.id3->elapsed + ff_rewind_count) * | ||
1790 | FF_REWIND_MAX_PERCENT / 100; | ||
1791 | } | ||
1792 | |||
1793 | max_step = MAX(max_step, MIN_FF_REWIND_STEP); | ||
1794 | |||
1795 | if (step > max_step) | ||
1796 | step = max_step; | ||
1797 | |||
1798 | ff_rewind_count += step * direction; | ||
1799 | |||
1800 | if (global_settings.ff_rewind_accel != 0 && | ||
1801 | current_tick >= accel_tick) | ||
1802 | { | ||
1803 | step *= 2; | ||
1804 | accel_tick = current_tick + | ||
1805 | global_settings.ff_rewind_accel*HZ; | ||
1806 | } | ||
1807 | } | ||
1808 | else | ||
1809 | { | ||
1810 | if ( (audio_status() & AUDIO_STATUS_PLAY) && | ||
1811 | wps_state.id3 && wps_state.id3->length ) | ||
1812 | { | ||
1813 | if (!wps_state.paused) | ||
1814 | audio_pause(); | ||
1815 | #if CONFIG_KEYPAD == PLAYER_PAD | ||
1816 | FOR_NB_SCREENS(i) | ||
1817 | gui_syncwps.gui_wps[i].display->stop_scroll(); | ||
1818 | #endif | ||
1819 | if (direction > 0) | ||
1820 | status_set_ffmode(STATUS_FASTFORWARD); | ||
1821 | else | ||
1822 | status_set_ffmode(STATUS_FASTBACKWARD); | ||
1823 | |||
1824 | wps_state.ff_rewind = true; | ||
1825 | |||
1826 | step = ff_rew_steps[global_settings.ff_rewind_min_step]; | ||
1827 | |||
1828 | accel_tick = current_tick + | ||
1829 | global_settings.ff_rewind_accel*HZ; | ||
1830 | } | ||
1831 | else | ||
1832 | break; | ||
1833 | } | ||
1834 | |||
1835 | if (direction > 0) { | ||
1836 | if ((wps_state.id3->elapsed + ff_rewind_count) > wps_state.id3->length) | ||
1837 | ff_rewind_count = wps_state.id3->length - wps_state.id3->elapsed; | ||
1838 | } | ||
1839 | else { | ||
1840 | if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0) | ||
1841 | ff_rewind_count = -wps_state.id3->elapsed; | ||
1842 | } | ||
1843 | |||
1844 | if(wps_state.wps_time_countup == false){ | ||
1845 | FOR_NB_SCREENS(i) | ||
1846 | gui_wps_refresh(&gui_syncwps.gui_wps[i], ff_rewind_count, | ||
1847 | WPS_REFRESH_PLAYER_PROGRESS | | ||
1848 | WPS_REFRESH_DYNAMIC); | ||
1849 | } | ||
1850 | else{ | ||
1851 | FOR_NB_SCREENS(i) | ||
1852 | gui_wps_refresh(&gui_syncwps.gui_wps[i], -ff_rewind_count, | ||
1853 | WPS_REFRESH_PLAYER_PROGRESS | | ||
1854 | WPS_REFRESH_DYNAMIC); | ||
1855 | } | ||
1856 | |||
1857 | break; | ||
1858 | |||
1859 | case WPS_PREV: | ||
1860 | case WPS_NEXT: | ||
1861 | #ifdef WPS_RC_PREV | ||
1862 | case WPS_RC_PREV: | ||
1863 | case WPS_RC_NEXT: | ||
1864 | #endif | ||
1865 | audio_ff_rewind(wps_state.id3->elapsed+ff_rewind_count); | ||
1866 | ff_rewind_count = 0; | ||
1867 | wps_state.ff_rewind = false; | ||
1868 | status_set_ffmode(0); | ||
1869 | if (!wps_state.paused) | ||
1870 | audio_resume(); | ||
1871 | #ifdef HAVE_LCD_CHARCELLS | ||
1872 | gui_wps_display(); | ||
1873 | #endif | ||
1874 | exit = true; | ||
1875 | break; | ||
1876 | |||
1877 | default: | ||
1878 | if(default_event_handler(button) == SYS_USB_CONNECTED) { | ||
1879 | status_set_ffmode(0); | ||
1880 | usb = true; | ||
1881 | exit = true; | ||
1882 | } | ||
1883 | break; | ||
1884 | } | ||
1885 | if (!exit) | ||
1886 | button = button_get(true); | ||
1887 | } | ||
1888 | |||
1889 | /* let audio thread update id3->elapsed before calling wps_refresh */ | ||
1890 | yield(); | ||
1891 | FOR_NB_SCREENS(i) | ||
1892 | gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_ALL); | ||
1893 | return usb; | ||
1894 | } | ||
1895 | |||
1896 | bool gui_wps_display(void) | ||
1897 | { | ||
1898 | int i; | ||
1899 | if (!wps_state.id3 && !(audio_status() & AUDIO_STATUS_PLAY)) | ||
1900 | { | ||
1901 | global_settings.resume_index = -1; | ||
1902 | #ifdef HAVE_LCD_CHARCELLS | ||
1903 | gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_PLAYER)); | ||
1904 | #else | ||
1905 | gui_syncstatusbar_draw(&statusbars, true); | ||
1906 | gui_syncsplash(HZ, true, str(LANG_END_PLAYLIST_RECORDER)); | ||
1907 | #endif | ||
1908 | return true; | ||
1909 | } | ||
1910 | else | ||
1911 | { | ||
1912 | FOR_NB_SCREENS(i) | ||
1913 | { | ||
1914 | gui_syncwps.gui_wps[i].display->clear_display(); | ||
1915 | if (!gui_syncwps.gui_wps[i].data->wps_loaded) { | ||
1916 | if ( !gui_syncwps.gui_wps[i].data->format_buffer[0] ) { | ||
1917 | /* set the default wps for the main-screen */ | ||
1918 | if(i == 0){ | ||
1919 | #ifdef HAVE_LCD_BITMAP | ||
1920 | wps_data_load(gui_syncwps.gui_wps[i].data, | ||
1921 | "%s%?it<%?in<%in. |>%it|%fn>\n" | ||
1922 | "%s%?ia<%ia|%?d2<%d2|(root)>>\n" | ||
1923 | "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n" | ||
1924 | "\n" | ||
1925 | "%al%pc/%pt%ar[%pp:%pe]\n" | ||
1926 | "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n" | ||
1927 | "%pb\n" | ||
1928 | "%pm\n", false, false); | ||
1929 | #else | ||
1930 | wps_data_load(gui_syncwps.gui_wps[i].data, | ||
1931 | "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n" | ||
1932 | "%pc%?ps<*|/>%pt\n", false, false); | ||
1933 | #endif | ||
1934 | } | ||
1935 | #if NB_SCREENS == 2 | ||
1936 | /* set the default wps for the remote-screen */ | ||
1937 | else if(i == 1) | ||
1938 | { | ||
1939 | wps_data_load(gui_syncwps.gui_wps[i].data, | ||
1940 | "%s%?ia<%ia|%?d2<%d2|(root)>>\n" | ||
1941 | "%s%?it<%?in<%in. |>%it|%fn>\n" | ||
1942 | "%al%pc/%pt%ar[%pp:%pe]\n" | ||
1943 | "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n" | ||
1944 | "%pb", false, false); | ||
1945 | } | ||
1946 | #endif | ||
1947 | } | ||
1948 | } | ||
1949 | } | ||
1950 | } | ||
1951 | yield(); | ||
1952 | FOR_NB_SCREENS(i) | ||
1953 | gui_wps_refresh(&gui_syncwps.gui_wps[i], 0, WPS_REFRESH_ALL); | ||
1954 | gui_syncstatusbar_draw(&statusbars, true); | ||
1955 | FOR_NB_SCREENS(i) | ||
1956 | { | ||
1957 | #ifdef HAVE_LCD_BITMAP | ||
1958 | wps_display_images(&gui_syncwps.gui_wps[i]); | ||
1959 | gui_syncwps.gui_wps[i].display->update(); | ||
1960 | #endif | ||
1961 | } | ||
1962 | return false; | ||
1963 | } | ||
1964 | |||
1965 | bool update(struct gui_wps *gwps) | ||
1966 | { | ||
1967 | bool track_changed = audio_has_changed_track(); | ||
1968 | bool retcode = false; | ||
1969 | |||
1970 | gwps->state->nid3 = audio_next_track(); | ||
1971 | if (track_changed) | ||
1972 | { | ||
1973 | gwps->display->stop_scroll(); | ||
1974 | gwps->state->id3 = audio_current_track(); | ||
1975 | if (gui_wps_display()) | ||
1976 | retcode = true; | ||
1977 | else{ | ||
1978 | gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL); | ||
1979 | } | ||
1980 | |||
1981 | if (gwps->state->id3) | ||
1982 | memcpy(gwps->state->current_track_path, gwps->state->id3->path, | ||
1983 | sizeof(gwps->state->current_track_path)); | ||
1984 | } | ||
1985 | |||
1986 | if (gwps->state->id3){ | ||
1987 | gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC); | ||
1988 | } | ||
1989 | |||
1990 | gui_syncstatusbar_draw(&statusbars, false); | ||
1991 | |||
1992 | return retcode; | ||
1993 | } | ||
1994 | |||
1995 | #ifdef WPS_KEYLOCK | ||
1996 | void display_keylock_text(bool locked) | ||
1997 | { | ||
1998 | char* s; | ||
1999 | int i; | ||
2000 | FOR_NB_SCREENS(i) | ||
2001 | gui_syncwps.gui_wps[i].display->stop_scroll(); | ||
2002 | |||
2003 | #ifdef HAVE_LCD_CHARCELLS | ||
2004 | if(locked) | ||
2005 | s = str(LANG_KEYLOCK_ON_PLAYER); | ||
2006 | else | ||
2007 | s = str(LANG_KEYLOCK_OFF_PLAYER); | ||
2008 | #else | ||
2009 | if(locked) | ||
2010 | s = str(LANG_KEYLOCK_ON_RECORDER); | ||
2011 | else | ||
2012 | s = str(LANG_KEYLOCK_OFF_RECORDER); | ||
2013 | #endif | ||
2014 | gui_syncsplash(HZ, true, s); | ||
2015 | } | ||
2016 | |||
2017 | void waitfor_nokey(void) | ||
2018 | { | ||
2019 | /* wait until all keys are released */ | ||
2020 | while (button_get(false) != BUTTON_NONE) | ||
2021 | yield(); | ||
2022 | } | ||
2023 | #endif | ||
2024 | |||