A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 310 lines 9.5 kB view raw
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}