diff --git a/apps/gui/splash.c b/apps/gui/splash.c index efb4b1cda2..3f361f5a04 100644 --- a/apps/gui/splash.c +++ b/apps/gui/splash.c @@ -29,8 +29,9 @@ #include "talk.h" #include "splash.h" #include "viewport.h" -#include "strtok_r.h" +#include "strptokspn_r.h" #include "scrollbar.h" +#include "font.h" static long progress_next_tick = 0; @@ -43,57 +44,68 @@ static bool splash_internal(struct screen * screen, const char *fmt, va_list ap, struct viewport *vp, int addl_lines) { char splash_buf[MAXBUFFER]; - char *lines[MAXLINES]; + struct splash_lines { + const char *str; + size_t len; + } lines[MAXLINES]; char *next; char *lastbreak = NULL; char *store = NULL; int line = 0; int x = 0; int y, i; - int space_w, w, h; + int space_w, w, chr_h; int width, height; int maxw = 0; + int fontnum = vp->font; - screen->getstringsize(" ", &space_w, &h); - y = h + (addl_lines * h); + char lastbrkchr; + size_t len, next_len; + const char matchstr[] = "\r\n\f\v\t "; + font_getstringsize(" ", &space_w, &chr_h, fontnum); + y = chr_h + (addl_lines * chr_h); vsnprintf(splash_buf, sizeof(splash_buf), fmt, ap); va_end(ap); /* break splash string into display lines, doing proper word wrap */ - - next = strtok_r(splash_buf, " ", &store); + next = strptokspn_r(splash_buf, matchstr, &next_len, &store); if (!next) return false; /* nothing to display */ - lines[0] = next; + lines[line].len = next_len + 1; + lines[line].str = next; while (true) { - screen->getstringsize(next, &w, NULL); + w = font_getstringnsize(next, next_len + 1, NULL, NULL, fontnum); if (lastbreak) { - int next_w = (next - lastbreak) * space_w; - - if (x + next_w + w > vp->width - RECT_SPACING*2) - { /* too wide, wrap */ + len = next - lastbreak; + int next_w = len * space_w; + if (x + next_w + w > vp->width - RECT_SPACING*2 || lastbrkchr != ' ') + { /* too wide, or control character wrap */ if (x > maxw) maxw = x; - if ((y + h > vp->height) || (line >= (MAXLINES-1))) + if ((y + chr_h * 2 > vp->height) || (line >= (MAXLINES-1))) break; /* screen full or out of lines */ x = 0; - y += h; - lines[++line] = next; + y += chr_h; + lines[++line].len = next_len + len; + lines[line].str = next; } else { /* restore & calculate spacing */ - *lastbreak = ' '; + lines[line].len += next_len + len + 1; x += next_w; } } x += w; - lastbreak = next + strlen(next); - next = strtok_r(NULL, " ", &store); + lastbreak = next + next_len + 1; + lastbrkchr = *lastbreak; + + next = strptokspn_r(NULL, matchstr, &next_len, &store); + if (!next) { /* no more words */ if (x > maxw) @@ -147,13 +159,10 @@ static bool splash_internal(struct screen * screen, const char *fmt, va_list ap, screen->draw_border_viewport(); - /* prepare putting the text */ - y = RECT_SPACING; - /* print the message to screen */ - for (i = 0; i <= line; i++, y+=h) + for(i = 0, y = RECT_SPACING; i <= line; i++, y+= chr_h) { - screen->putsxy(0, y, lines[i]); + screen->putsxyf(0, y, "%.*s", lines[i].len, lines[i].str); } return true; /* needs update */ } diff --git a/apps/screen_access.c b/apps/screen_access.c index a7b902918c..1909ef277c 100644 --- a/apps/screen_access.c +++ b/apps/screen_access.c @@ -217,6 +217,7 @@ struct screen screens[NB_SCREENS] = .putsxy=&lcd_putsxy, .puts=&lcd_puts, .putsf=&lcd_putsf, + .putsxyf=&lcd_putsxyf, .puts_scroll=&lcd_puts_scroll, .putsxy_scroll_func=&lcd_putsxy_scroll_func, .scroll_speed=&lcd_scroll_speed, @@ -305,6 +306,7 @@ struct screen screens[NB_SCREENS] = .putsxy=&lcd_remote_putsxy, .puts=&lcd_remote_puts, .putsf=&lcd_remote_putsf, + .putsxyf=&lcd_remote_putsxyf, .puts_scroll=&lcd_remote_puts_scroll, .putsxy_scroll_func=&lcd_remote_putsxy_scroll_func, .scroll_speed=&lcd_remote_scroll_speed, diff --git a/apps/screen_access.h b/apps/screen_access.h index 94c0a19670..3e24306636 100644 --- a/apps/screen_access.h +++ b/apps/screen_access.h @@ -109,6 +109,7 @@ struct screen void (*putsxy)(int x, int y, const unsigned char *str); void (*puts)(int x, int y, const unsigned char *str); void (*putsf)(int x, int y, const unsigned char *str, ...); + void (*putsxyf)(int x, int y, const unsigned char *fmt, ...); bool (*puts_scroll)(int x, int y, const unsigned char *string); bool (*putsxy_scroll_func)(int x, int y, const unsigned char *string, void (*scroll_func)(struct scrollinfo *), diff --git a/firmware/SOURCES b/firmware/SOURCES index bbd67631a9..4aa7c38daf 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -205,39 +205,10 @@ target/hosted/samsungypr/ypr1/wmcodec-ypr1.c target/hosted/maemo/maemo-thread.c #endif -/* Standard library */ -#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(__MINGW32__) || defined(__CYGWIN__) -libc/strtok.c -#endif /* PLATFORM_NATIVE || __MINGW32__ || __CYGWIN__ */ -#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(HAVE_ROCKBOX_C_LIBRARY) -libc/atoi.c -libc/errno.c -#if (CONFIG_PLATFORM & PLATFORM_NATIVE) -/* our ctype.[ch] comes from newlib and is incompitble with most desktop's ctype */ -libc/ctype.c -/* alsa on linux requires a more advanced sprintf, i.e. not ours */ -libc/sprintf.c -#endif - -libc/memchr.c -libc/memcmp.c - -libc/qsort.c -libc/random.c -libc/strcat.c -libc/strchr.c -libc/strcmp.c -libc/strcpy.c - -libc/strncmp.c -libc/strrchr.c -libc/strstr.c -libc/mktime.c -libc/gmtime.c -#endif /* CONFIG_PLATFORM || HAVE_ROCKBOX_C_LIBRARY */ - /* Common */ #ifndef BOOTLOADER +common/strptokspn.c +#define HAVE_STRTOK_R common/ap_int.c #endif common/version.c @@ -277,6 +248,37 @@ common/zip.c common/adler32.c common/inflate.c +/* Standard library */ +#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(__MINGW32__) || defined(__CYGWIN__) +libc/strtok.c +#endif /* PLATFORM_NATIVE || __MINGW32__ || __CYGWIN__ */ +#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(HAVE_ROCKBOX_C_LIBRARY) +libc/atoi.c +libc/errno.c +#if (CONFIG_PLATFORM & PLATFORM_NATIVE) +/* our ctype.[ch] comes from newlib and is incompitble with most desktop's ctype */ +libc/ctype.c +/* alsa on linux requires a more advanced sprintf, i.e. not ours */ +libc/sprintf.c +#endif + +libc/memchr.c +libc/memcmp.c + +libc/qsort.c +libc/random.c +libc/strcat.c +libc/strchr.c +libc/strcmp.c +libc/strcpy.c + +libc/strncmp.c +libc/strrchr.c +libc/strstr.c +libc/mktime.c +libc/gmtime.c +#endif /* CONFIG_PLATFORM || HAVE_ROCKBOX_C_LIBRARY */ + /* Display */ scroll_engine.c diff --git a/firmware/common/strptokspn.c b/firmware/common/strptokspn.c new file mode 100644 index 0000000000..f4b92c0712 --- /dev/null +++ b/firmware/common/strptokspn.c @@ -0,0 +1,88 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2022 by William WIlgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. * + ****************************************************************************/ + +#include "config.h" + +#include +#include +#include "strtok_r.h" +/* strptokspn_r is a custom implementation of strtok_r that does NOT modify + * the source string. + * + * strptokspn_r reads ptr as a series of zero or more tokens, + * and sep as delimiters of the tokens + * The tokens can be separated by one or more of the delimiters + * first call searches for the first token skipping over any leading delimiters. + * Returns pointer to first token + * Pointer *len contains the span to the first delimeter + * (this would be the resulting strlen had token actually been NULL terminated) + * Pointer **end contains pointer to first character after the last delimeter + * + * When strptokspn_r is called with a ptr == NULL, the next token is read from + * Pointer **end + * + * Note the returned token is NOT NULL terminated by the function as in strtok_r + * However the caller can use ret[len+1] = '\0'; to emulate a call to strtok_r +*/ + +const char *strptokspn_r(const char *ptr, const char *sep, size_t *len, const char **end) +{ + *len = 0; + if (!ptr) + /* we got NULL input so then we get our last position instead */ + ptr = *end; + + /* pass all letters that are including in the separator string */ + while (*ptr && strchr(sep, *ptr)) + ++ptr; + + if (*ptr) { + /* so this is where the next piece of string starts */ + const char *start = ptr; + + /* set the end pointer to the first byte after the start */ + *end = start + 1; + + /* scan through the string to find where it ends, it ends on a + null byte or a character that exists in the separator string */ + while (**end && !strchr(sep, **end)) + ++*end; + *len = (*end - start) - 1; /* this would be the string len if there actually was a NULL */ + if (**end) { /* the end is not a null byte */ + ++*end; /* advance last pointer to beyond the match */ + } + + return start; /* return the position where the string starts */ + } + + /* we ended up on a null byte, there are no more strings to find! */ + return NULL; +} + +#if !defined(HAVE_STRTOK_R) +char * strtok_r(char *ptr, const char *sep, char **end) +{ + size_t len; + char * ret = (char*) strptokspn_r((const char*)ptr, sep, &len, (const char**) end); + if (ret) + ret[len + 1] = '\0'; + return ret; +} +#endif diff --git a/firmware/export/font.h b/firmware/export/font.h index 067c67e43d..38e30187b3 100644 --- a/firmware/export/font.h +++ b/firmware/export/font.h @@ -132,7 +132,7 @@ void font_disable_all(void); void font_enable_all(void); struct font* font_get(int font); - +int font_getstringnsize(const unsigned char *str, size_t maxbytes, int *w, int *h, int fontnumber); int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber); int font_get_width(struct font* ft, unsigned short ch); const unsigned char * font_get_bits(struct font* ft, unsigned short ch); diff --git a/firmware/font.c b/firmware/font.c index 7ce64ed47d..97a15221fc 100644 --- a/firmware/font.c +++ b/firmware/font.c @@ -1046,16 +1046,20 @@ const unsigned char* font_get_bits(struct font* pf, unsigned short char_code) #endif /* BOOTLOADER */ /* - * Returns the stringsize of a given string. + * Returns the stringsize of a given NULL terminated string + * stops after maxbytes or NULL (\0) whichever occurs first. + * maxbytes = -1 ignores maxbytes and relies on NULL terminator (\0) + * to terminate the string */ -int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber) +int font_getstringnsize(const unsigned char *str, size_t maxbytes, int *w, int *h, int fontnum) { - struct font* pf = font_get(fontnumber); + struct font* pf = font_get(fontnum); + font_lock( fontnum, true ); unsigned short ch; int width = 0; + size_t b = maxbytes - 1; - font_lock( fontnumber, true ); - for (str = utf8decode(str, &ch); ch != 0 ; str = utf8decode(str, &ch)) + for (str = utf8decode(str, &ch); ch != 0 && b < maxbytes; str = utf8decode(str, &ch), b--) { if (is_diacritic(ch, NULL)) continue; @@ -1067,10 +1071,18 @@ int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber) *w = width; if ( h ) *h = pf->height; - font_lock( fontnumber, false ); + font_lock( fontnum, false ); return width; } +/* + * Returns the stringsize of a given NULL terminated string. + */ +int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber) +{ + return font_getstringnsize(str, -1, w, h, fontnumber); +} + /* ----------------------------------------------------------------- * vim: et sw=4 ts=8 sts=4 tw=78 */ diff --git a/firmware/include/strptokspn_r.h b/firmware/include/strptokspn_r.h new file mode 100644 index 0000000000..d565118190 --- /dev/null +++ b/firmware/include/strptokspn_r.h @@ -0,0 +1,26 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2022 William Wilgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + +#ifndef __STRPTOKSPN_R_H__ +#define __STRPTOKSPN_R_H__ +const char *strptokspn_r(const char *ptr, const char *sep, size_t *len, const char **end); +#endif