A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) Daniel Stenberg (2002)
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "stdarg.h"
22#include "string.h"
23#include "rbunicode.h"
24#include "stdio.h"
25#include "kernel.h"
26#include "screen_access.h"
27#include "lang.h"
28#include "settings.h"
29#include "talk.h"
30#include "splash.h"
31#include "viewport.h"
32#include "strptokspn_r.h"
33#include "scrollbar.h"
34#include "font.h"
35#ifndef BOOTLOADER
36#include "misc.h" /* get_current_activity */
37#endif
38
39static long progress_next_tick, talked_tick;
40
41#define MAXLINES (LCD_HEIGHT/6)
42#define MAXBUFFER 512
43#define RECT_SPACING 3
44#define SPLASH_MEMORY_INTERVAL (HZ)
45
46static bool splash_internal(struct screen * screen, const char *fmt, va_list ap,
47 struct viewport *vp, int addl_lines)
48{
49 static int max_width[NB_SCREENS] = {2*RECT_SPACING};
50#ifndef BOOTLOADER
51 static enum current_activity last_act = ACTIVITY_UNKNOWN;
52 enum current_activity act = get_current_activity();
53
54 if (last_act != act) /* changed activities reset max_width */
55 {
56 FOR_NB_SCREENS(i)
57 max_width[i] = 2*RECT_SPACING;
58 last_act = act;
59 }
60#endif
61 /* prevent screen artifacts by keeping the max width seen */
62 int min_width = max_width[screen->screen_type];
63 char splash_buf[MAXBUFFER];
64 struct splash_lines {
65 const char *str;
66 size_t len;
67 } lines[MAXLINES];
68 const char *next;
69 const char *lastbreak = NULL;
70 const char *store = NULL;
71 int line = 0;
72 int x = 0;
73 int y, i;
74 int space_w, w, chr_h;
75 int width, height;
76 int maxw = min_width - 2*RECT_SPACING;
77 int fontnum = vp->font;
78
79 char lastbrkchr;
80 size_t len, next_len;
81 const char matchstr[] = "\r\n\f\v\t ";
82 font_getstringsize(" ", &space_w, &chr_h, fontnum);
83 y = chr_h + (addl_lines * chr_h);
84
85 vsnprintf(splash_buf, sizeof(splash_buf), fmt, ap);
86 va_end(ap);
87
88 /* break splash string into display lines, doing proper word wrap */
89 next = strptokspn_r(splash_buf, matchstr, &next_len, &store);
90 if (!next)
91 return false; /* nothing to display */
92
93 lines[line].len = next_len;
94 lines[line].str = next;
95 while (true)
96 {
97 w = font_getstringnsize(next, next_len, NULL, NULL, fontnum);
98 if (lastbreak)
99 {
100 len = next - lastbreak;
101 int next_w = len * space_w;
102 if (x + next_w + w > vp->width - RECT_SPACING*2 || lastbrkchr != ' ')
103 { /* too wide, or control character wrap */
104 if (x > maxw)
105 maxw = x;
106 if ((y + chr_h * 2 > vp->height) || (line >= (MAXLINES-1)))
107 break; /* screen full or out of lines */
108 x = 0;
109 y += chr_h;
110
111 /* split when it fits since we didn't find a valid token to break on */
112 size_t nl = next_len;
113 while (w > vp->width && --nl > 0)
114 w = font_getstringnsize(next, nl, NULL, NULL, fontnum);
115
116 if (nl > 1 && nl != next_len)
117 {
118 next_len = nl;
119 store = next + nl; /* move the start pos for the next token read */
120 }
121
122 lines[++line].len = next_len;
123 lines[line].str = next;
124 }
125 else
126 {
127 /* restore & calculate spacing */
128 lines[line].len += next_len + 1;
129 x += next_w;
130 }
131 }
132 x += w;
133
134 lastbreak = next + next_len;
135 lastbrkchr = *lastbreak;
136
137 next = strptokspn_r(NULL, matchstr, &next_len, &store);
138
139 if (!next)
140 { /* no more words */
141 if (x > maxw)
142 maxw = x;
143 break;
144 }
145 }
146
147 /* prepare viewport
148 * First boundaries, then the background filling, then the border and finally
149 * the text*/
150
151 screen->scroll_stop();
152
153 width = maxw + 2*RECT_SPACING;
154 height = y + 2*RECT_SPACING;
155
156 if (width > vp->width)
157 width = vp->width;
158 if (height > vp->height)
159 height = vp->height;
160
161 vp->x += (vp->width - width) / 2;
162 vp->y += (vp->height - height) / 2;
163 vp->width = width;
164 vp->height = height;
165
166 /* prevent artifacts by locking to max width observed on repeated calls */
167 max_width[screen->screen_type] = width;
168
169 vp->flags |= VP_FLAG_ALIGN_CENTER;
170#if LCD_DEPTH > 1
171 unsigned fg = 0, bg = 0;
172 bool broken = false;
173
174 if (screen->depth > 1)
175 {
176 fg = screen->get_foreground();
177 bg = screen->get_background();
178
179 broken = (fg == bg) ||
180 (bg == 63422 && fg == 65535); /* -> iPod reFresh themes from '22 */
181
182 vp->drawmode = DRMODE_FG;
183 /* can't do vp->fg_pattern here, since set_foreground does a bit more on
184 * greyscale */
185 screen->set_foreground(broken ? SCREEN_COLOR_TO_NATIVE(screen, LCD_LIGHTGRAY) :
186 bg); /* gray as fallback for broken themes */
187 }
188 else
189#endif
190 vp->drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
191
192 screen->fill_viewport();
193
194#if LCD_DEPTH > 1
195 if (screen->depth > 1)
196 /* can't do vp->fg_pattern here, since set_foreground does a bit more on
197 * greyscale */
198 screen->set_foreground(broken ? SCREEN_COLOR_TO_NATIVE(screen, LCD_BLACK) :
199 fg); /* black as fallback for broken themes */
200 else
201#endif
202 vp->drawmode = DRMODE_SOLID;
203
204 screen->draw_border_viewport();
205
206 /* print the message to screen */
207 for(i = 0, y = RECT_SPACING; i <= line; i++, y+= chr_h)
208 {
209 screen->putsxyf(0, y, "%.*s", lines[i].len, lines[i].str);
210 }
211 return true; /* needs update */
212}
213
214void splashf(int ticks, const char *fmt, ...)
215{
216 va_list ap;
217
218 /* fmt may be a so called virtual pointer. See settings.h. */
219 long id;
220 if((id = P2ID((const unsigned char*)fmt)) >= 0)
221 /* If fmt specifies a voicefont ID, and voice menus are
222 enabled, then speak it. */
223 cond_talk_ids_fq(id);
224
225 /* If fmt is a lang ID then get the corresponding string (which
226 still might contain % place holders). */
227 fmt = P2STR((unsigned char *)fmt);
228 FOR_NB_SCREENS(i)
229 {
230 struct screen * screen = &(screens[i]);
231 struct viewport vp;
232 viewport_set_defaults(&vp, screen->screen_type);
233 struct viewport *last_vp = screen->set_viewport(&vp);
234
235 va_start(ap, fmt);
236 if (splash_internal(screen, fmt, ap, &vp, 0))
237 screen->update_viewport();
238 va_end(ap);
239
240 screen->set_viewport(last_vp);
241 }
242 if (ticks)
243 sleep(ticks);
244}
245
246/* set delay before progress meter is shown */
247void splash_progress_set_delay(long delay_ticks)
248{
249 progress_next_tick = current_tick + delay_ticks;
250 talked_tick = 0;
251}
252
253/* splash a progress meter */
254void splash_progress(int current, int total, const char *fmt, ...)
255{
256 va_list ap;
257 int vp_flag = VP_FLAG_VP_DIRTY;
258 /* progress update tick */
259 long now = current_tick;
260
261 if (current < total)
262 {
263 if(TIME_BEFORE(now, progress_next_tick))
264 return;
265 /* limit to 20fps */
266 progress_next_tick = now + HZ/20;
267 vp_flag = 0; /* don't mark vp dirty to prevent flashing */
268 }
269
270 if (global_settings.talk_menu &&
271 total > 0 &&
272 TIME_AFTER(current_tick, talked_tick + HZ*5))
273 {
274 talked_tick = current_tick;
275 talk_ids(false, LANG_LOADING_PERCENT,
276 TALK_ID(current * 100 / total, UNIT_PERCENT));
277 }
278
279 /* If fmt is a lang ID then get the corresponding string (which
280 still might contain % place holders). */
281 fmt = P2STR((unsigned char *)fmt);
282 FOR_NB_SCREENS(i)
283 {
284 struct screen * screen = &(screens[i]);
285 struct viewport vp;
286 viewport_set_defaults(&vp, screen->screen_type);
287 struct viewport *last_vp = screen->set_viewport_ex(&vp, vp_flag);
288
289 va_start(ap, fmt);
290 if (splash_internal(screen, fmt, ap, &vp, 1))
291 {
292 int size = screen->getcharheight();
293 int x = RECT_SPACING;
294 int y = vp.height - size - RECT_SPACING;
295 int w = vp.width - RECT_SPACING * 2;
296 int h = size;
297#ifdef HAVE_LCD_COLOR
298 const int sb_flags = HORIZONTAL | FOREGROUND;
299#else
300 const int sb_flags = HORIZONTAL;
301#endif
302 gui_scrollbar_draw(screen, x, y, w, h, total, 0, current, sb_flags);
303
304 screen->update_viewport();
305 }
306 va_end(ap);
307
308 screen->set_viewport(last_vp);
309 }
310}