summaryrefslogtreecommitdiffstats
path: root/apps/plugins/lib
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/lib')
-rw-r--r--apps/plugins/lib/SOURCES11
-rw-r--r--apps/plugins/lib/action_helper.c1
-rw-r--r--apps/plugins/lib/action_helper.h34
-rwxr-xr-xapps/plugins/lib/action_helper.pl211
-rw-r--r--apps/plugins/lib/arg_helper.c312
-rw-r--r--apps/plugins/lib/arg_helper.h60
-rw-r--r--apps/plugins/lib/bmp_smooth_scale.c2
-rw-r--r--apps/plugins/lib/button_helper.c1
-rw-r--r--apps/plugins/lib/button_helper.h40
-rwxr-xr-xapps/plugins/lib/button_helper.pl103
-rw-r--r--apps/plugins/lib/helper.c54
-rw-r--r--apps/plugins/lib/helper.h22
-rw-r--r--apps/plugins/lib/highscore.c9
-rw-r--r--apps/plugins/lib/icon_helper.c48
-rw-r--r--apps/plugins/lib/icon_helper.h26
-rw-r--r--apps/plugins/lib/id3.c39
-rw-r--r--apps/plugins/lib/id3.h26
-rw-r--r--apps/plugins/lib/kbd_helper.c8
-rw-r--r--apps/plugins/lib/kbd_helper.h2
-rw-r--r--apps/plugins/lib/keymaps.h12
-rw-r--r--apps/plugins/lib/mul_id3.c174
-rw-r--r--apps/plugins/lib/mul_id3.h27
-rw-r--r--apps/plugins/lib/osd.c8
-rw-r--r--apps/plugins/lib/overlay.c5
-rw-r--r--apps/plugins/lib/playback_control.c20
-rw-r--r--apps/plugins/lib/pluginlib_actions.c2
-rw-r--r--apps/plugins/lib/pluginlib_bmp.c2
-rw-r--r--apps/plugins/lib/printcell_helper.c681
-rw-r--r--apps/plugins/lib/printcell_helper.h87
-rw-r--r--apps/plugins/lib/wrappers.h1
-rw-r--r--apps/plugins/lib/xlcd_scroll.c4
31 files changed, 1989 insertions, 43 deletions
diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES
index 811771e0ca..09a8b1c5ed 100644
--- a/apps/plugins/lib/SOURCES
+++ b/apps/plugins/lib/SOURCES
@@ -1,7 +1,11 @@
sha1.c
gcc-support.c
pluginlib_actions.c
+action_helper.c
+button_helper.c
helper.c
+icon_helper.c
+arg_helper.c
md5.c
jhash.c
configfile.c
@@ -10,6 +14,7 @@ rgb_hsv.c
highscore.c
simple_viewer.c
display_text.c
+printcell_helper.c
strncpy.c
stdio_compat.c
@@ -64,3 +69,9 @@ kbd_helper.c
#ifdef HAVE_TOUCHSCREEN
pluginlib_touchscreen.c
#endif
+
+id3.c
+
+#ifdef HAVE_TAGCACHE
+mul_id3.c
+#endif
diff --git a/apps/plugins/lib/action_helper.c b/apps/plugins/lib/action_helper.c
new file mode 100644
index 0000000000..906051c1ea
--- /dev/null
+++ b/apps/plugins/lib/action_helper.c
@@ -0,0 +1 @@
+/*DUMMY_FILE_DONT_CHANGEME*/
diff --git a/apps/plugins/lib/action_helper.h b/apps/plugins/lib/action_helper.h
new file mode 100644
index 0000000000..53f5c840f8
--- /dev/null
+++ b/apps/plugins/lib/action_helper.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Copyright (C) 2021 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.
+*
+****************************************************************************/
+/* action_helper provides a way to turn numeric action/context into strings
+* the file action_helper.c is generated at compile time
+* ACTION_ and CONTEXT_ are stripped from the strings and replaced when
+* action_name and context_name are called,
+* NOTE: both share the same static buffer sized as the largest string possible
+*/
+#ifndef _ACTION_HELPER_H_
+#define _ACTION_HELPER_H_
+extern const size_t action_helper_maxbuffer;
+char* action_name(int action);
+char* context_name(int context);
+
+#endif /* _ACTION_HELPER_H_ */
diff --git a/apps/plugins/lib/action_helper.pl b/apps/plugins/lib/action_helper.pl
new file mode 100755
index 0000000000..742419e23b
--- /dev/null
+++ b/apps/plugins/lib/action_helper.pl
@@ -0,0 +1,211 @@
+#!/usr/bin/env perl
+############################################################################
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $action_helper$
+#
+# Copyright (C) 2021 William Wilgus
+#
+# All files in this archive are subject to the GNU General Public License.
+# See the file COPYING in the source tree root for full license agreement.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+############################################################################
+#expects -E source input on STDIN
+use strict;
+use warnings;
+
+my @actions = ();
+my @contexts = ();
+my @action_offset = ();
+my @context_offset = ();
+my $action_ct = 0;
+my $context_ct = 0;
+my $len_max_action = 0;
+my $len_max_context = 0;
+my $len_min_action = -1;
+my $len_min_context = -1;
+while(my $line = <STDIN>)
+{
+ chomp($line);
+ if($line =~ /^\s*(ACTION_[^\s]+)(\s*=.*)?,\s*$/)
+ {
+ $actions[$action_ct] = $1;
+ $action_ct++;
+ }
+ elsif($line =~ /^\s*(LAST_ACTION_PLACEHOLDER)(\s*=.*)?,\s*$/)
+ { #special case don't save actual name
+ $actions[$action_ct] = "";
+ $action_ct++;
+ }
+ elsif($line =~ /^\s*(PLA_[^\s]+)(\s*=.*)?,\s*$/)
+ {
+ $actions[$action_ct] = $1;
+ $action_ct++;
+ }
+ elsif($line =~ /^\s*(CONTEXT_[^\s]+)(\s*=.*)?,\s*$/)
+ {
+ $contexts[$context_ct] = $1;
+ $context_ct++;
+ }
+}
+
+print <<EOF
+/* Don't change this file! */
+/* It is automatically generated of action.h */
+#include "plugin.h"
+#include "action_helper.h"
+EOF
+;
+#dump actions
+my $offset = 0;
+print "static const char action_names[]= \n";
+for(my $i = 0; $i < $action_ct; $i++){
+ my $act = $actions[$i];
+ $act =~ s/ACTION_USB_HID_/%s/ig; # strip the common part
+ $act =~ s/ACTION_/%s/ig; # strip the common part
+ my $actlen = length($act);
+ if ($actlen < $len_min_action or $len_min_action == -1){
+ $len_min_action = $actlen;
+ }
+ if ($actions[$i] ne $act){
+ printf "/*%s*/\"%s\\0\"\n", substr($actions[$i], 0, -($actlen - 2)), $act;
+ } else {
+ print "\"$act\\0\" \n";
+ }
+ my $slen = length($actions[$i]) + 1; #NULL terminator
+ if ($slen > $len_max_action) { $len_max_action = $slen; }
+ push(@action_offset, {'name' => $actions[$i], 'offset' => $offset});
+ $offset += length($act) + 1; # NULL terminator
+}
+printf "\"\";/* %d + \\0 */\n\n", $offset;
+@actions = ();
+
+#dump contexts
+$offset = 0;
+print "static const char context_names[]= \n";
+for(my $i = 0; $i < $context_ct; $i++){
+ my $ctx = $contexts[$i];
+ $ctx =~ s/CONTEXT_/%s/ig; # strip the common part
+ my $ctxlen = length($ctx);
+
+ if ($ctxlen < 5){
+ $ctx = $contexts[$i];
+ $ctxlen = length($ctx);
+ }
+
+ if ($ctxlen < $len_min_context or $len_min_context == -1){
+ $len_min_context = $ctxlen;
+ }
+ if ($contexts[$i] ne $ctx){
+ printf "/*%s*/\"%s\\0\"\n", substr($contexts[$i], 0, -($ctxlen - 2)), $ctx;
+ } else {
+ print "\"$ctx\\0\" \n";
+ }
+ my $slen = length($contexts[$i]) + 1; # NULL terminator
+ if ($slen > $len_max_context) { $len_max_context = $slen; }
+ push(@context_offset, {'name' => $contexts[$i], 'offset' => $offset});
+ $offset += length($ctx) + 1; # NULL terminator
+}
+printf "\"\";/* %d + \\0 */\n\n", $offset;
+@contexts = ();
+
+printf "#define ACTION_CT %d\n", $action_ct;
+print "static const uint16_t action_offsets[ACTION_CT] = {\n";
+foreach my $define (@action_offset)
+{
+ printf("%d, /*%s*/\n", @$define{'offset'}, @$define{'name'});
+}
+print "};\n\n";
+@action_offset = ();
+
+printf "#define CONTEXT_CT %d\n", $context_ct;
+print "#if 0 /* context_names is small enough to walk the string instead */\n";
+print "static const uint16_t context_offsets[CONTEXT_CT] = {\n";
+foreach my $define (@context_offset)
+{
+ printf("%d, /*%s*/\n", @$define{'offset'}, @$define{'name'});
+}
+print "};\n#endif\n\n";
+@context_offset = ();
+
+printf "#define ACTIONBUFSZ %d\n", $len_max_action;
+printf "#define CONTEXTBUFSZ %d\n\n", $len_max_context;
+
+if ($len_max_action > $len_max_context)
+{
+ print "const size_t action_helper_maxbuffer = ACTIONBUFSZ;\n";
+ print "static char name_buf[ACTIONBUFSZ];\n";
+}
+else
+{
+ print "const size_t action_helper_maxbuffer = CONTEXTBUFSZ;\n";
+ print "static char name_buf[CONTEXTBUFSZ];\n";
+}
+print <<EOF
+
+char* action_name(int action)
+{
+ if (action >= 0 && action < ACTION_CT)
+ {
+ uint16_t offset = action_offsets[action];
+ const char *act = &action_names[offset];
+ if (action < ACTION_USB_HID_FIRST)
+ rb->snprintf(name_buf, ACTIONBUFSZ, act, "ACTION_");
+ else
+ rb->snprintf(name_buf, ACTIONBUFSZ, act, "ACTION_USB_HID_");
+ }
+ else
+ rb->snprintf(name_buf, ACTIONBUFSZ, "ACTION_UNKNOWN");
+ return name_buf;
+}
+
+/* walk string increment offset for each NULL if desired offset found, return */
+static const char *context_getoffset(int offset)
+{
+ const char *names = context_names;
+ const size_t len = sizeof(context_names) - 1;
+ int current = 0;
+ if (offset > 0)
+ {
+ const char *pos = names;
+ const char *end = names + len;
+ while (pos < end)
+ {
+ if (*pos++ == '\\0')
+ {
+ current++;
+ if (offset == current)
+ return pos;
+ pos += $len_min_context; /* each string is at least this long */
+ }
+ }
+ }
+ return names;
+}
+
+char* context_name(int context)
+{
+ const char *ctx;
+ if (context >= 0 && context < CONTEXT_CT)
+ {
+#if 0
+ uint16_t offset = context_offsets[context];
+ ctx = &context_names[offset];
+#else
+ ctx = context_getoffset(context);
+#endif
+ }
+ else
+ ctx = "%sUNKNOWN";
+ rb->snprintf(name_buf, CONTEXTBUFSZ, ctx, "CONTEXT_");
+ return name_buf;
+}
+EOF
+;
diff --git a/apps/plugins/lib/arg_helper.c b/apps/plugins/lib/arg_helper.c
new file mode 100644
index 0000000000..3ea5ba714d
--- /dev/null
+++ b/apps/plugins/lib/arg_helper.c
@@ -0,0 +1,312 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 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 "plugin.h"
+#include "arg_helper.h"
+
+#ifndef logf
+#define logf(...) {}
+#endif
+
+#define SWCHAR '-'
+#define DECSEPCHAR '.'
+#ifdef PLUGIN
+ #define strchr rb->strchr
+#endif
+int string_parse(const char **parameter, char* buf, size_t buf_sz)
+{
+/* fills buf with a string upto buf_sz, null terminates the buffer
+ * strings break on WS by default but can be enclosed in single or double quotes
+ * opening and closing quotes will not be included in the buffer but will be counted
+ * use alternating quotes if you really want them included '"text"' or "'text'"
+ * failure to close the string will result in eating all remaining args till \0
+ * If buffer full remaining chars are discarded till stopchar or \0 is reached */
+
+ char stopchar = ' ';
+ char stopchars[] = "\'\"";
+ int skipped = 0;
+ int found = 0;
+ const char* start = *parameter;
+
+ if (strchr(stopchars, *start))
+ {
+ logf("stop char %c\n", *start);
+ stopchar = *start;
+ skipped++;
+ start++;
+ }
+ while (*start && *start != stopchar)
+ {
+ if (buf_sz > 1)
+ {
+ *buf++ = *start;
+ buf_sz--;
+ }
+ found++;
+ start++;
+ }
+ if (*start == stopchar && skipped)
+ {
+ start++;
+ skipped++;
+ }
+
+ *buf = '\0';
+
+ if (found > 0)
+ *parameter = start;
+ else
+ skipped = 0;
+
+ return found + skipped;
+}
+
+int char_parse(const char **parameter, char* character)
+{
+/* passes *character a single character eats remaining non-WS characters */
+ char buf[2];
+ int ret = string_parse(parameter, buf, sizeof(buf));
+ if (ret && character)
+ *character = buf[0];
+ return ret;
+
+}
+
+int bool_parse(const char **parameter, bool *choice)
+{
+/* determine true false using the first character the rest are skipped/ignored */
+ int found = 0;
+ const char tf_val[]="fn0ty1";/* false chars on left f/t should be balanced fffttt */
+ const char* start = *parameter;
+
+
+ char c = tolower(*start);
+ const char *tfval = strchr(tf_val, c);
+ while(isalnum(*++start)) {;}
+
+ if (tfval)
+ {
+ found = start - (*parameter);
+ *parameter = start;
+ }
+
+ if (choice)
+ *choice = (tfval - tf_val) > (signed int) (sizeof(tf_val) / 2) - 1;
+
+ return found;
+}
+
+int longnum_parse(const char **parameter, long *number, long *decimal)
+{
+/* passes number and or decimal portion of number base 10 only..
+ fractional portion is scaled by ARGPARSE_FRAC_DEC_MULTIPLIER
+ Example (if ARGPARSE_FRAC_DEC_MULTIPLIER = 10 000)
+ meaning .0009 returns 9 , 9 / 10000 = .0009
+ .009 returns 90
+ .099 returns 990
+ .09 returns 900
+ .9 returns 9000
+ .9999 returns 9999
+*/
+
+ long num = 0;
+ long dec = 0;
+ int found = 0;
+ int neg = 0;
+ int digits = 0;
+ //logf ("n: %s\n", *parameter);
+ const char *start = *parameter;
+
+ if (*start == '-')
+ {
+ neg = 1;
+ start++;
+ }
+ while (isdigit(*start))
+ {
+ found++;
+ num = num *10 + *start - '0';
+ start++;
+ }
+
+ if (*start == DECSEPCHAR)
+ {
+ start++;
+ while(*start == '0')
+ {
+ digits++;
+ start++;
+ }
+ while (isdigit(*start))
+ {
+ dec = dec *10 + *start - '0';
+ digits++;
+ start++;
+ }
+ if (decimal && digits <= ARGPARSE_MAX_FRAC_DIGITS)
+ {
+ if(digits < ARGPARSE_MAX_FRAC_DIGITS)
+ {
+ digits = ARGPARSE_MAX_FRAC_DIGITS - digits;
+ while (digits--)
+ dec *= 10;
+ }
+ }
+ else
+ dec = -1; /* error */
+ }
+
+ if (found > 0)
+ {
+ found = start - (*parameter);
+ *parameter = start;
+ }
+
+ if(number)
+ *number = neg ? -num : num;
+
+ if (decimal)
+ *decimal = dec;
+
+ return found;
+}
+
+int num_parse(const char **parameter, int *number, int *decimal)
+{
+ long num, dec;
+ int ret = longnum_parse(parameter, &num, &dec);
+ if(number)
+ *number = num;
+ if (decimal)
+ *decimal = dec;
+ return ret;
+}
+
+/*
+*argparse(const char *parameter, int parameter_len,
+* int (*arg_callback)(char argchar, const char **parameter))
+* parameter : constant char string of arguments
+* parameter_len : may be set to -1 if your parameter string is NULL (\0) terminated
+* arg_callback : function gets called for each SWCHAR found in the parameter string
+* Note: WS at beginning is stripped, **parameter starts at the first NON WS char
+* return 0 for arg_callback to quit parsing immediately
+*/
+void argparse(const char *parameter, int parameter_len, int (*arg_callback)(char argchar, const char **parameter))
+{
+ bool lastchr;
+ char argchar;
+ const char *start = parameter;
+ while (parameter_len < 0 || (parameter - start) < parameter_len)
+ {
+ switch (*parameter++)
+ {
+ case SWCHAR:
+ {
+ if ((*parameter) == '\0')
+ return;
+ logf ("%s\n",parameter);
+ argchar = *parameter;
+ lastchr = (*(parameter + 1) == '\0');
+ while (*++parameter || lastchr)
+ {
+ lastchr = false;
+ if (isspace(*parameter))
+ continue; /* eat spaces at beginning */
+ if (!arg_callback(argchar, &parameter))
+ return;
+ break;
+ }
+ break;
+ }
+ case '\0':
+ {
+ if (parameter_len <= 0)
+ return;
+ }
+ }
+ }
+}
+
+/* EXAMPLE USAGE
+argparse("-n 42 -N 9.9 -n -78.9009 -f -P /rockbox/path/f -s 'Yestest' -B false -B 0 -B true -b n -by -b 1-c ops -c s -k", -1, &arg_callback);
+
+int arg_callback(char argchar, const char **parameter)
+{
+ int ret;
+ int num, dec;
+ char c;
+ char buf[32];
+ bool bret;
+ logf ("Arg: %c\n", argchar);
+ switch (tolower(argchar))
+ {
+ case 'k' :
+ logf("Option K!");
+ break;
+ case 'c' :
+ ret = char_parse(parameter, &c);
+ if (ret)
+ {
+ logf ("Val: %c\n", c);
+ logf("ate %d chars\n", ret);
+ }
+ break;
+
+ case 'n' :
+ ret = num_parse(parameter, &num, &dec);
+ if (ret)
+ {
+ logf ("Val: %d.%d\n", num, dec);
+ logf("ate %d chars\n", ret);
+ }
+ break;
+ case 's' :
+ ret = string_parse(parameter, buf, sizeof(buf));
+ if (ret)
+ {
+ logf ("Val: %s\n", buf);
+ logf("ate %d chars\n", ret);
+ }
+ break;
+ case 'p' :
+ ret = string_parse(parameter, buf, sizeof(buf));
+ if (ret)
+ {
+ logf ("Path: %s\n", buf);
+ logf("ate %d chars\n", ret);
+ }
+ break;
+ case 'b' :
+ ret = bool_parse(parameter, &bret);
+ if (ret)
+ {
+ logf ("Val: %s\n", bret ? "true" : "false");
+ logf("ate %d chars\n", ret);
+ }
+ break;
+ default :
+ logf ("Unknown switch '%c'\n",argchar);
+ //return 0;
+ }
+ return 1;
+}
+*/
+
diff --git a/apps/plugins/lib/arg_helper.h b/apps/plugins/lib/arg_helper.h
new file mode 100644
index 0000000000..2cf94ba1dd
--- /dev/null
+++ b/apps/plugins/lib/arg_helper.h
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 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 _LIB_ARG_HELPER_H_
+#define _LIB_ARG_HELPER_H_
+
+#include "plugin.h"
+
+#define ARGPARSE_MAX_FRAC_DIGITS 9 /* Uses 30 bits max (0.999999999) */
+
+#define ARGP_EXP(a, b) (a ##E## b)
+#define ARGP_FRAC_DEC_MULTIPLIER(n) ARGP_EXP(1,n) /*1x10^n*/
+#define ARGPARSE_FRAC_DEC_MULTIPLIER \
+ (long)ARGP_FRAC_DEC_MULTIPLIER(ARGPARSE_MAX_FRAC_DIGITS)
+
+/* fills buf with a string upto buf_sz, null terminates the buffer
+ * strings break on WS by default but can be enclosed in single or double quotes
+ * opening and closing quotes will not be included in the buffer but will be counted
+ * use alternating quotes if you really want them included '"text"' or "'text'"
+ * failure to close the string will result in eating all remaining args till \0
+ * If buffer full remaining chars are discarded till stopchar or \0 is reached */
+int string_parse(const char **parameter, char* buf, size_t buf_sz);
+/* passes *character a single character eats remaining non-WS characters */
+int char_parse(const char **parameter, char* character);
+/* determine true false using the first character the rest are skipped/ignored */
+int bool_parse(const char **parameter, bool *choice);
+/* passes number and or decimal portion of number base 10 only.. */
+int longnum_parse(const char **parameter, long *number, long *decimal);
+int num_parse(const char **parameter, int *number, int *decimal);
+
+/*
+*argparse(const char *parameter, int parameter_len,
+* int (*arg_callback)(char argchar, const char **parameter))
+* parameter : constant char string of arguments
+* parameter_len : may be set to -1 if your parameter string is NULL (\0) terminated
+* arg_callback : function gets called for each SWCHAR found in the parameter string
+* Note: WS at beginning is stripped, **parameter starts at the first NON WS char
+* return 0 for arg_callback to quit parsing immediately
+*/
+void argparse(const char *parameter, int parameter_len,
+ int (*arg_callback)(char argchar, const char **parameter));
+
+#endif /* _LIB_ARG_HELPER_H_ */
diff --git a/apps/plugins/lib/bmp_smooth_scale.c b/apps/plugins/lib/bmp_smooth_scale.c
index c5f258cdbf..378ff96448 100644
--- a/apps/plugins/lib/bmp_smooth_scale.c
+++ b/apps/plugins/lib/bmp_smooth_scale.c
@@ -78,7 +78,7 @@ void smooth_resize_bitmap(struct bitmap *src_bmp, struct bitmap *dest_bmp)
fb_data *sptr, *dptr;
int x, y, end;
int val_y = 0, val_x;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
const int sw = src_bmp->height;
const int sh = src_bmp->width;
const int dw = dest_bmp->height;
diff --git a/apps/plugins/lib/button_helper.c b/apps/plugins/lib/button_helper.c
new file mode 100644
index 0000000000..906051c1ea
--- /dev/null
+++ b/apps/plugins/lib/button_helper.c
@@ -0,0 +1 @@
+/*DUMMY_FILE_DONT_CHANGEME*/
diff --git a/apps/plugins/lib/button_helper.h b/apps/plugins/lib/button_helper.h
new file mode 100644
index 0000000000..4087ba898a
--- /dev/null
+++ b/apps/plugins/lib/button_helper.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Copyright (C) 2021 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 _BUTTON_HELPER_H_
+#define _BUTTON_HELPER_H_
+struct available_button
+{
+ const char* name;
+ unsigned long value;
+};
+
+/* *available_buttons is holding a pointer to the first element of an array
+ * of struct available_button it is set up in such a way due to the file being
+ * generated at compile time you can still call it as such though
+* eg available_buttons[0] or available_buttons[available_button_count] (NULL SENTINEL, 0)*/
+
+extern const size_t button_helper_maxbuffer;
+extern const struct available_button * const available_buttons;
+extern const int available_button_count;
+
+int get_button_names(char *buf, size_t bufsz, unsigned long button);
+#endif /* _BUTTON_HELPER_H_ */
diff --git a/apps/plugins/lib/button_helper.pl b/apps/plugins/lib/button_helper.pl
new file mode 100755
index 0000000000..192df18d7f
--- /dev/null
+++ b/apps/plugins/lib/button_helper.pl
@@ -0,0 +1,103 @@
+#!/usr/bin/env perl
+############################################################################
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+# Copyright (C) 2009 by Maurus Cuelenaere
+# Copyright (C) 2021 by William Wilgus
+#
+# All files in this archive are subject to the GNU General Public License.
+# See the file COPYING in the source tree root for full license agreement.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+############################################################################
+#expects -dM -E source input on STDIN
+use strict;
+use warnings;
+my $svnrev = '$Revision$';
+my @buttons = ();
+my $count = 1; #null sentinel
+my $val;
+my $def;
+my $len_max_button = 0;
+while(my $line = <STDIN>)
+{
+ chomp($line);
+ if($line =~ /^#define (BUTTON_[^\s]+) (.+)$/)
+ {
+ $def = "{\"$1\", $2},\n";
+ my $slen = length($1) + 1; # NULL terminator
+ if ($slen > $len_max_button) { $len_max_button = $slen; }
+ $val = $2;
+ if($val =~ /^0/)
+ {
+ $val = oct($val)
+ }
+ else
+ {
+ $val = 0xFFFFFFFF; #only used for sorting
+ }
+ push(@buttons, {'name' => $1, 'value' => $val, 'def' => $def});
+ $count = $count + 1;
+ }
+}
+my @sorted = sort { @$a{'value'} <=> @$b{'value'} } @buttons;
+print <<EOF
+/* Don't change this file! */
+/* It is automatically generated of button.h */
+#include "plugin.h"
+#include "button.h"
+#include "button_helper.h"
+
+const size_t button_helper_maxbuffer = $len_max_button;
+
+static const struct available_button buttons[$count] = {
+EOF
+;
+$count--; # don't count the sentinel
+foreach my $button (@sorted)
+{
+ printf " %s", @$button{'def'};
+}
+
+print <<EOF
+ {"\\0", 0} /* sentinel */
+};
+const int available_button_count = $count;
+const struct available_button * const available_buttons = buttons;
+
+int get_button_names(char *buf, size_t bufsz, unsigned long button)
+{
+ int len = 0;
+ buf[0] = '\\0';
+ const struct available_button *btn = buttons;
+ while(btn->name[0] != '\\0')
+ {
+ if(btn->value == 0)
+ {
+ if (button == 0)
+ {
+ buf[0] = '\\0';
+ len = rb->strlcat(buf, btn->name, bufsz);
+ return len;
+ }
+ }
+ else if ((button & btn->value) == btn->value)
+ {
+ if (len > 0)
+ rb->strlcat(buf, " | ", bufsz);
+ len = rb->strlcat(buf, btn->name, bufsz);
+ }
+ btn++;
+ }
+ return len;
+}
+EOF
+;
diff --git a/apps/plugins/lib/helper.c b/apps/plugins/lib/helper.c
index f36c01b23e..92d9ec905e 100644
--- a/apps/plugins/lib/helper.c
+++ b/apps/plugins/lib/helper.c
@@ -22,6 +22,12 @@
#include "plugin.h"
#include "helper.h"
+int talk_val(long n, int unit, bool enqueue)
+{
+ #define NODECIMALS 0
+ return rb->talk_value_decimal(n, unit, NODECIMALS, enqueue);
+}
+
#ifdef HAVE_BACKLIGHT
/* Force the backlight on */
void backlight_force_on(void)
@@ -52,7 +58,12 @@ void backlight_use_settings(void)
backlight_timeout_plugged);
#endif /* CONFIG_CHARGING */
}
-#endif /* HAVE_BACKLIGHT */
+#else /* HAVE_BACKLIGHT */
+/* DUMMY FUNCTIONS */
+void backlight_force_on(void){}
+void backlight_ignore_timeout(void){}
+void backlight_use_settings(void){}
+#endif /* !HAVE_BACKLIGHT */
#ifdef HAVE_SW_POWEROFF
static bool original_sw_poweroff_state = true;
@@ -67,7 +78,11 @@ void sw_poweroff_restore(void)
{
rb->button_set_sw_poweroff_state(original_sw_poweroff_state);
}
-#endif
+#else /* HAVE_SW_POWEROFF */
+/* DUMMY FUNCTIONS */
+void sw_poweroff_disable(void){}
+void sw_poweroff_restore(void){}
+#endif /* !HAVE_SW_POWEROFF */
#ifdef HAVE_REMOTE_LCD
/* Force the backlight on */
@@ -100,7 +115,12 @@ void remote_backlight_use_settings(void)
remote_backlight_timeout_plugged);
#endif /* CONFIG_CHARGING */
}
-#endif /* HAVE_REMOTE_LCD */
+#else /* HAVE_REMOTE_LCD */
+/* DUMMY FUNCTIONS */
+void remote_backlight_force_on(void){}
+void remote_backlight_ignore_timeout(void){}
+void remote_backlight_use_settings(void){}
+#endif /* !HAVE_REMOTE_LCD */
#ifdef HAVE_BUTTON_LIGHT
/* Force the buttonlight on */
@@ -127,7 +147,13 @@ void buttonlight_use_settings(void)
{
rb->buttonlight_set_timeout(rb->global_settings->buttonlight_timeout);
}
-#endif /* HAVE_BUTTON_LIGHT */
+#else /* HAVE_BUTTON_LIGHT */
+/* DUMMY FUNCTIONS */
+void buttonlight_force_on(void){}
+void buttonlight_force_off(void){}
+void buttonlight_ignore_timeout(void){}
+void buttonlight_use_settings(void){}
+#endif /* !HAVE_BUTTON_LIGHT */
#ifdef HAVE_BACKLIGHT_BRIGHTNESS
void backlight_brightness_set(int brightness)
@@ -139,7 +165,15 @@ void backlight_brightness_use_setting(void)
{
rb->backlight_set_brightness(rb->global_settings->brightness);
}
-#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
+#else /* HAVE_BACKLIGHT_BRIGHTNESS */
+/* DUMMY FUNCTIONS */
+void backlight_brightness_set(int brightness)
+{
+ (void)brightness;
+}
+void backlight_brightness_use_setting(void){}
+
+#endif /* !HAVE_BACKLIGHT_BRIGHTNESS */
#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
void buttonlight_brightness_set(int brightness)
@@ -151,4 +185,12 @@ void buttonlight_brightness_use_setting(void)
{
rb->buttonlight_set_brightness(rb->global_settings->buttonlight_brightness);
}
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
+#else /* HAVE_BUTTONLIGHT_BRIGHTNESS */
+/* DUMMY FUNCTIONS */
+void buttonlight_brightness_set(int brightness)
+{
+ (void)brightness;
+}
+
+void buttonlight_brightness_use_setting(void){}
+#endif /* !HAVE_BUTTONLIGHT_BRIGHTNESS */
diff --git a/apps/plugins/lib/helper.h b/apps/plugins/lib/helper.h
index f2e9187a96..6aee4dc581 100644
--- a/apps/plugins/lib/helper.h
+++ b/apps/plugins/lib/helper.h
@@ -23,6 +23,18 @@
#include "plugin.h"
+#ifndef MAX_BRIGHTNESS_SETTING
+#define MAX_BRIGHTNESS_SETTING 0
+#endif
+#ifndef MIN_BRIGHTNESS_SETTING
+#define MIN_BRIGHTNESS_SETTING 0
+#endif
+#ifndef DEFAULT_BRIGHTNESS_SETTING
+#define DEFAULT_BRIGHTNESS_SETTING 0
+#endif
+
+int talk_val(long n, int unit, bool enqueue);
+
/**
* Backlight on/off operations
*/
@@ -30,39 +42,29 @@ void backlight_force_on(void);
void backlight_ignore_timeout(void);
void backlight_use_settings(void);
-#ifdef HAVE_SW_POWEROFF
/**
* Disable and restore software poweroff (i.e. holding PLAY on iPods).
* Only call _restore() if _disable() was called earlier!
*/
void sw_poweroff_disable(void);
void sw_poweroff_restore(void);
-#endif
-#ifdef HAVE_REMOTE_LCD
void remote_backlight_force_on(void);
void remote_backlight_ignore_timeout(void);
void remote_backlight_use_settings(void);
-#endif
-#ifdef HAVE_BUTTON_LIGHT
void buttonlight_force_on(void);
void buttonlight_force_off(void);
void buttonlight_ignore_timeout(void);
void buttonlight_use_settings(void);
-#endif
/**
* Backlight brightness adjustment settings
*/
-#ifdef HAVE_BACKLIGHT_BRIGHTNESS
void backlight_brightness_set(int brightness);
void backlight_brightness_use_setting(void);
-#endif
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
void buttonlight_brightness_set(int brightness);
void buttonlight_brightness_use_setting(void);
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#endif /* _LIB_HELPER_H_ */
diff --git a/apps/plugins/lib/highscore.c b/apps/plugins/lib/highscore.c
index 3aae955bfc..04faf27891 100644
--- a/apps/plugins/lib/highscore.c
+++ b/apps/plugins/lib/highscore.c
@@ -124,11 +124,16 @@ void highscore_show(int position, struct highscore *scores, int num_scores,
bool show_level)
{
int i, w, h;
-#ifdef HAVE_LCD_COLOR
+#if defined(HAVE_LCD_COLOR) || LCD_DEPTH >= 2
unsigned bgcolor = rb->lcd_get_background();
unsigned fgcolor = rb->lcd_get_foreground();
+#ifdef HAVE_LCD_COLOR
rb->lcd_set_background(LCD_BLACK);
rb->lcd_set_foreground(LCD_WHITE);
+#else
+ rb->lcd_set_background(LCD_WHITE);
+ rb->lcd_set_foreground(LCD_BLACK);
+#endif
#endif
rb->lcd_clear_display();
@@ -173,7 +178,7 @@ void highscore_show(int position, struct highscore *scores, int num_scores,
rb->button_clear_queue();
rb->button_get(true);
rb->lcd_setfont(FONT_SYSFIXED);
-#ifdef HAVE_LCD_COLOR
+#if defined(HAVE_LCD_COLOR) || LCD_DEPTH >= 2
rb->lcd_set_background(bgcolor);
rb->lcd_set_foreground(fgcolor);
#endif
diff --git a/apps/plugins/lib/icon_helper.c b/apps/plugins/lib/icon_helper.c
new file mode 100644
index 0000000000..857bddb128
--- /dev/null
+++ b/apps/plugins/lib/icon_helper.c
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 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 "plugin.h"
+#include "icon_helper.h"
+
+const unsigned char* cbmp_get_icon(unsigned int cbmp_fmt, unsigned int index, int *width, int *height)
+{
+ const unsigned char* bmp = NULL;
+ while (cbmp_fmt < CBMP_BitmapFormatLast)
+ {
+ const struct cbmp_bitmap_info_entry *cbmp = &rb->core_bitmaps[cbmp_fmt];
+ if (index > cbmp->count)
+ break;
+ int w = cbmp->width;
+ int h = cbmp->height;
+ /* ((height/CHAR_BIT) Should always be 1 thus far */
+
+ off_t offset = (((unsigned)h/CHAR_BIT) * (index * w));
+ bmp = cbmp->pbmp + offset;
+
+ if (width)
+ *width = w;
+ if (height)
+ *height = h;
+ break;
+ }
+
+ return bmp;
+}
diff --git a/apps/plugins/lib/icon_helper.h b/apps/plugins/lib/icon_helper.h
new file mode 100644
index 0000000000..e30a607a3f
--- /dev/null
+++ b/apps/plugins/lib/icon_helper.h
@@ -0,0 +1,26 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 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 _LIB_ICON_HELPER_H_
+#define _LIB_ICON_HELPER_H_
+
+#include "plugin.h"
+const unsigned char* cbmp_get_icon(unsigned int cbmp_fmt, unsigned int index, int *width, int *height);
+#endif /* _LIB_ICON_HELPER_H_ */
diff --git a/apps/plugins/lib/id3.c b/apps/plugins/lib/id3.c
new file mode 100644
index 0000000000..b0202b1d9c
--- /dev/null
+++ b/apps/plugins/lib/id3.c
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Christian Soffke
+ *
+ * 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 "plugin.h"
+
+/* Fills mp3entry with metadata retrieved from RAM, if possible, or by reading from
+ * the file directly. Note that the tagcache only stores a subset of metadata and
+ * will thus not return certain properties of the file, such as frequency, size, or
+ * codec.
+ */
+bool retrieve_id3(struct mp3entry *id3, const char* file)
+{
+#if defined (HAVE_TAGCACHE) && defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
+ if (rb->tagcache_fill_tags(id3, file))
+ {
+ rb->strlcpy(id3->path, file, sizeof(id3->path));
+ return true;
+ }
+#endif
+
+ return !rb->mp3info(id3, file);
+}
diff --git a/apps/plugins/lib/id3.h b/apps/plugins/lib/id3.h
new file mode 100644
index 0000000000..6ae1688798
--- /dev/null
+++ b/apps/plugins/lib/id3.h
@@ -0,0 +1,26 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Christian Soffke
+ *
+ * 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 ID3_H
+#define ID3_H
+
+bool retrieve_id3(struct mp3entry *id3, const char* file);
+
+#endif /* ID3_H */
diff --git a/apps/plugins/lib/kbd_helper.c b/apps/plugins/lib/kbd_helper.c
index e3a844a993..f99282575d 100644
--- a/apps/plugins/lib/kbd_helper.c
+++ b/apps/plugins/lib/kbd_helper.c
@@ -34,11 +34,12 @@
* success returns size of buffer used
* failure returns 0
*/
-int kbd_create_layout(char *layout, unsigned short *buf, int bufsz)
+int kbd_create_layout(const char *layout, unsigned short *buf, int bufsz)
{
unsigned short *pbuf;
const unsigned char *p = layout;
int len = 0;
+ int total_len = 0;
pbuf = buf;
while (*p && (pbuf - buf + (ptrdiff_t) sizeof(unsigned short)) < bufsz)
{
@@ -47,6 +48,7 @@ int kbd_create_layout(char *layout, unsigned short *buf, int bufsz)
{
*pbuf = len;
pbuf += len+1;
+ total_len += len + 1;
len = 0;
}
else
@@ -57,7 +59,9 @@ int kbd_create_layout(char *layout, unsigned short *buf, int bufsz)
{
*pbuf = len;
pbuf[len+1] = 0xFEFF; /* mark end of characters */
- return len + 1;
+ total_len += len + 1;
+ return total_len * sizeof(unsigned short);
}
+
return 0;
}
diff --git a/apps/plugins/lib/kbd_helper.h b/apps/plugins/lib/kbd_helper.h
index 90443cbf3f..ee2ce7551c 100644
--- a/apps/plugins/lib/kbd_helper.h
+++ b/apps/plugins/lib/kbd_helper.h
@@ -22,6 +22,6 @@
#define KBD_HELPER_H
/* create a custom keyboard layout for kbd_input */
-int kbd_create_layout(char *layout, unsigned short *buf, int bufsz);
+int kbd_create_layout(const char *layout, unsigned short *buf, int bufsz);
#endif /* KBD_HELPER_H */
diff --git a/apps/plugins/lib/keymaps.h b/apps/plugins/lib/keymaps.h
index b660d4d85e..2cbca9e5ad 100644
--- a/apps/plugins/lib/keymaps.h
+++ b/apps/plugins/lib/keymaps.h
@@ -255,6 +255,15 @@
#define BTN_FIRE BUTTON_SELECT
#define BTN_PAUSE BUTTON_POWER
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define BTN_FIRE BUTTON_CENTER
+#define BTN_PAUSE BUTTON_POWER
+#define BTN_HAVE_DIAGONAL
+#define BTN_DOWN_LEFT BUTTON_BOTTOMLEFT
+#define BTN_DOWN_RIGHT BUTTON_BOTTOMRIGHT
+#define BTN_UP_LEFT BUTTON_TOPLEFT
+#define BTN_UP_RIGHT BUTTON_TOPRIGHT
+
#else
#error Unsupported keypad
#endif
@@ -272,7 +281,8 @@
#elif (CONFIG_KEYPAD != COWON_D2_PAD) && \
(CONFIG_KEYPAD != DX50_PAD) && \
(CONFIG_KEYPAD != ONDAVX777_PAD) && \
- (CONFIG_KEYPAD != CREATIVE_ZENXFI2_PAD)
+ (CONFIG_KEYPAD != CREATIVE_ZENXFI2_PAD) && \
+ (CONFIG_KEYPAD != SHANLING_Q1_PAD)
#define BTN_FIRE BUTTON_BOTTOMLEFT
#define BTN_PAUSE BUTTON_TOPLEFT
#endif
diff --git a/apps/plugins/lib/mul_id3.c b/apps/plugins/lib/mul_id3.c
new file mode 100644
index 0000000000..edf44f7282
--- /dev/null
+++ b/apps/plugins/lib/mul_id3.c
@@ -0,0 +1,174 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Christian Soffke
+ *
+ * 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 "plugin.h"
+#include "mul_id3.h"
+
+struct multiple_tracks_id3 {
+ unsigned long long filesize;
+ unsigned long long length;
+ unsigned long frequency;
+ unsigned int artist_hash;
+ unsigned int composer_hash;
+ unsigned int albumartist_hash;
+ unsigned int grouping_hash;
+ unsigned int comment_hash;
+ unsigned int album_hash;
+ unsigned int genre_hash;
+ unsigned int codectype;
+ unsigned int bitrate;
+ int year;
+ bool vbr;
+};
+
+static struct multiple_tracks_id3 mul_id3;
+
+
+/* Calculate modified FNV hash of string
+ * has good avalanche behaviour and uniform distribution
+ * see http://home.comcast.net/~bretm/hash/ */
+static unsigned int mfnv(char *str)
+{
+ const unsigned int p = 16777619;
+ unsigned int hash = 0x811C9DC5; // 2166136261;
+
+ if (!str)
+ return 0;
+
+ while(*str)
+ hash = (hash ^ *str++) * p;
+ hash += hash << 13;
+ hash ^= hash >> 7;
+ hash += hash << 3;
+ hash ^= hash >> 17;
+ hash += hash << 5;
+ return hash;
+}
+
+static void init_mul_id3(void)
+{
+ mul_id3.artist_hash = 0;
+ mul_id3.album_hash = 0;
+ mul_id3.genre_hash = 0;
+ mul_id3.composer_hash = 0;
+ mul_id3.albumartist_hash = 0;
+ mul_id3.grouping_hash = 0;
+ mul_id3.comment_hash = 0;
+ mul_id3.codectype = 0;
+ mul_id3.vbr = false;
+ mul_id3.bitrate = 0;
+ mul_id3.frequency = 0;
+ mul_id3.length = 0;
+ mul_id3.filesize = 0;
+ mul_id3.year = 0;
+}
+
+void collect_id3(struct mp3entry *id3, bool is_first_track)
+{
+ if (is_first_track)
+ {
+ init_mul_id3();
+ mul_id3.artist_hash = mfnv(id3->artist);
+ mul_id3.album_hash = mfnv(id3->album);
+ mul_id3.genre_hash = mfnv(id3->genre_string);
+ mul_id3.composer_hash = mfnv(id3->composer);
+ mul_id3.albumartist_hash = mfnv(id3->albumartist);
+ mul_id3.grouping_hash = mfnv(id3->grouping);
+ mul_id3.comment_hash = mfnv(id3->comment);
+ mul_id3.codectype = id3->codectype;
+ mul_id3.vbr = id3->vbr;
+ mul_id3.bitrate = id3->bitrate;
+ mul_id3.frequency = id3->frequency;
+ mul_id3.year = id3->year;
+ }
+ else
+ {
+ if (mul_id3.artist_hash && (mfnv(id3->artist) != mul_id3.artist_hash))
+ mul_id3.artist_hash = 0;
+ if (mul_id3.album_hash && (mfnv(id3->album) != mul_id3.album_hash))
+ mul_id3.album_hash = 0;
+ if (mul_id3.genre_hash && (mfnv(id3->genre_string) != mul_id3.genre_hash))
+ mul_id3.genre_hash = 0;
+ if (mul_id3.composer_hash && (mfnv(id3->composer) != mul_id3.composer_hash))
+ mul_id3.composer_hash = 0;
+ if (mul_id3.albumartist_hash && (mfnv(id3->albumartist) !=
+ mul_id3.albumartist_hash))
+ mul_id3.albumartist_hash = 0;
+ if (mul_id3.grouping_hash && (mfnv(id3->grouping) != mul_id3.grouping_hash))
+ mul_id3.grouping_hash = 0;
+ if (mul_id3.comment_hash && (mfnv(id3->comment) != mul_id3.comment_hash))
+ mul_id3.comment_hash = 0;
+
+ if (mul_id3.codectype && (id3->codectype != mul_id3.codectype))
+ mul_id3.codectype = AFMT_UNKNOWN;
+ if (mul_id3.bitrate && (id3->bitrate != mul_id3.bitrate ||
+ id3->vbr != mul_id3.vbr))
+ mul_id3.bitrate = 0;
+ if (mul_id3.frequency && (id3->frequency != mul_id3.frequency))
+ mul_id3.frequency = 0;
+ if (mul_id3.year && (id3->year != mul_id3.year))
+ mul_id3.year = 0;
+ }
+ mul_id3.length += id3->length;
+ mul_id3.filesize += id3->filesize;
+}
+
+/* (!) Note scale factor applied to returned metadata:
+ * - Unit for filesize will be KiB instead of Bytes
+ * - Unit for length will be s instead of ms
+ *
+ * Use result only as input for browse_id3,
+ * with the track_ct parameter set to > 1.
+ */
+void finalize_id3(struct mp3entry *id3)
+{
+ id3->path[0] = '\0';
+ id3->title = NULL;
+ if (!mul_id3.artist_hash)
+ id3->artist = NULL;
+ if (!mul_id3.album_hash)
+ id3->album = NULL;
+ if (!mul_id3.genre_hash)
+ id3->genre_string = NULL;
+ if (!mul_id3.composer_hash)
+ id3->composer = NULL;
+ if (!mul_id3.albumartist_hash)
+ id3->albumartist = NULL;
+ if (!mul_id3.grouping_hash)
+ id3->grouping = NULL;
+ if (!mul_id3.comment_hash)
+ id3->comment = NULL;
+ id3->disc_string = NULL;
+ id3->track_string = NULL;
+ id3->year_string = NULL;
+ id3->year = mul_id3.year;
+ mul_id3.length /= 1000;
+ mul_id3.filesize >>= 10;
+ id3->length = mul_id3.length > ULONG_MAX ? 0 : mul_id3.length;
+ id3->filesize = mul_id3.filesize > INT_MAX ? 0 : mul_id3.filesize;
+ id3->frequency = mul_id3.frequency;
+ id3->bitrate = mul_id3.bitrate;
+ id3->codectype = mul_id3.codectype;
+ id3->vbr = mul_id3.vbr;
+ id3->discnum = 0;
+ id3->tracknum = 0;
+ id3->track_level = 0;
+ id3->album_level = 0;
+}
diff --git a/apps/plugins/lib/mul_id3.h b/apps/plugins/lib/mul_id3.h
new file mode 100644
index 0000000000..d08095de5c
--- /dev/null
+++ b/apps/plugins/lib/mul_id3.h
@@ -0,0 +1,27 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Christian Soffke
+ *
+ * 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 MUL_ID3_H
+#define MUL_ID3_H
+
+void collect_id3(struct mp3entry *id3, bool is_first_track);
+void finalize_id3(struct mp3entry *id3);
+
+#endif /* MUL_ID3_H */
diff --git a/apps/plugins/lib/osd.c b/apps/plugins/lib/osd.c
index 7d6e10a410..99f77da7dc 100644
--- a/apps/plugins/lib/osd.c
+++ b/apps/plugins/lib/osd.c
@@ -106,10 +106,10 @@ static struct osd grey_osd;
# error Unknown 2-bit format; please define macros
# endif /* LCD_PIXELFORMAT */
#elif LCD_DEPTH == 16
-# if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+# if LCD_STRIDEFORMAT == VERTICAL_STRIDE
# define _OSD_HEIGHT2BYTES(h) ((h)*2)
# define _OSD_BYTES2HEIGHT(b) ((b)/2)
-# else /* !defined(LCD_STRIDEFORMAT) || LCD_STRIDEFORMAT != VERTICAL_STRIDE */
+# else /* LCD_STRIDEFORMAT != VERTICAL_STRIDE */
# define _OSD_WIDTH2BYTES(w) ((w)*2)
# define _OSD_BYTES2WIDTH(b) ((b)/2)
# endif /* end stride type selection */
@@ -160,7 +160,7 @@ static void * _osd_lcd_init_buffers(struct osd *osd, unsigned flags,
rb->viewport_set_fullscreen(&osd->vp, SCREEN_MAIN);
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
int colbytes = _OSD_HEIGHT2BYTES(LCD_HEIGHT);
int bytecols = *bufsize / colbytes;
int w = _OSD_BYTES2WIDTH(bytecols);
@@ -193,7 +193,7 @@ static void * _osd_lcd_init_buffers(struct osd *osd, unsigned flags,
w = _OSD_BYTES2WIDTH(_OSD_WIDTH2BYTES(w));
osd->lcd_bitmap_stride = _OSD_BYTES2HEIGHT(_OSD_HEIGHT2BYTES(LCD_HEIGHT));
osd->back_bitmap_stride = h;
-#else /* !defined(LCD_STRIDEFORMAT) || LCD_STRIDEFORMAT != VERTICAL_STRIDE */
+#else /* LCD_STRIDEFORMAT != VERTICAL_STRIDE */
int rowbytes = _OSD_WIDTH2BYTES(LCD_WIDTH);
int byterows = *bufsize / rowbytes;
int w = _OSD_BYTES2WIDTH(rowbytes);
diff --git a/apps/plugins/lib/overlay.c b/apps/plugins/lib/overlay.c
index 0ecc1bf3e7..065273534e 100644
--- a/apps/plugins/lib/overlay.c
+++ b/apps/plugins/lib/overlay.c
@@ -83,9 +83,8 @@ enum plugin_status run_overlay(const void* parameter,
goto error_close;
}
-
- if (hdr->api_version > PLUGIN_API_VERSION
- || hdr->api_version < PLUGIN_MIN_API_VERSION)
+ if (hdr->api_version != PLUGIN_API_VERSION ||
+ p_hdr->api_size > sizeof(struct plugin_api))
{
rb->splashf(2*HZ, "%s overlay: Incompatible version.", name);
goto error_close;
diff --git a/apps/plugins/lib/playback_control.c b/apps/plugins/lib/playback_control.c
index 363033b1f2..3e97916400 100644
--- a/apps/plugins/lib/playback_control.c
+++ b/apps/plugins/lib/playback_control.c
@@ -65,21 +65,21 @@ static bool nexttrack(void)
static bool volume(void)
{
const struct settings_list* vol =
- rb->find_setting(&rb->global_settings->volume, NULL);
+ rb->find_setting(&rb->global_settings->volume);
return rb->option_screen((struct settings_list*)vol, parentvp, false, "Volume");
}
static bool shuffle(void)
{
const struct settings_list* shuffle =
- rb->find_setting(&rb->global_settings->playlist_shuffle, NULL);
+ rb->find_setting(&rb->global_settings->playlist_shuffle);
return rb->option_screen((struct settings_list*)shuffle, parentvp, false, "Shuffle");
}
static bool repeat_mode(void)
{
const struct settings_list* repeat =
- rb->find_setting(&rb->global_settings->repeat_mode, NULL);
+ rb->find_setting(&rb->global_settings->repeat_mode);
int old_repeat = rb->global_settings->repeat_mode;
rb->option_screen((struct settings_list*)repeat, parentvp, false, "Repeat");
@@ -91,19 +91,19 @@ static bool repeat_mode(void)
return false;
}
MENUITEM_FUNCTION(prevtrack_item, 0, ID2P(LANG_PREVTRACK),
- prevtrack, NULL, NULL, Icon_NOICON);
+ prevtrack, NULL, Icon_NOICON);
MENUITEM_FUNCTION(playpause_item, 0, ID2P(LANG_PLAYPAUSE),
- play, NULL, NULL, Icon_NOICON);
+ play, NULL, Icon_NOICON);
MENUITEM_FUNCTION(stop_item, 0, ID2P(LANG_STOP_PLAYBACK),
- stop, NULL, NULL, Icon_NOICON);
+ stop, NULL, Icon_NOICON);
MENUITEM_FUNCTION(nexttrack_item, 0, ID2P(LANG_NEXTTRACK),
- nexttrack, NULL, NULL, Icon_NOICON);
+ nexttrack, NULL, Icon_NOICON);
MENUITEM_FUNCTION(volume_item, 0, ID2P(LANG_CHANGE_VOLUME),
- volume, NULL, NULL, Icon_NOICON);
+ volume, NULL, Icon_NOICON);
MENUITEM_FUNCTION(shuffle_item, 0, ID2P(LANG_CHANGE_SHUFFLE_MODE),
- shuffle, NULL, NULL, Icon_NOICON);
+ shuffle, NULL, Icon_NOICON);
MENUITEM_FUNCTION(repeat_mode_item, 0, ID2P(LANG_CHANGE_REPEAT_MODE),
- repeat_mode, NULL, NULL, Icon_NOICON);
+ repeat_mode, NULL, Icon_NOICON);
MAKE_MENU(playback_control_menu, ID2P(LANG_PLAYBACK_CONTROL), NULL, Icon_NOICON,
&prevtrack_item, &playpause_item, &stop_item, &nexttrack_item,
&volume_item, &shuffle_item, &repeat_mode_item);
diff --git a/apps/plugins/lib/pluginlib_actions.c b/apps/plugins/lib/pluginlib_actions.c
index 028472d9a8..4115177eaa 100644
--- a/apps/plugins/lib/pluginlib_actions.c
+++ b/apps/plugins/lib/pluginlib_actions.c
@@ -499,6 +499,8 @@ const struct button_mapping pla_main_ctx[] =
{PLA_SELECT, BUTTON_SELECT, BUTTON_NONE},
{PLA_SELECT_REL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{PLA_SELECT_REPEAT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE},
+#elif (CONFIG_KEYPAD == SHANLING_Q1_PAD)
+ {PLA_EXIT, BUTTON_POWER, BUTTON_NONE},
#else
# ifndef HAVE_TOUCHSCREEN
# error pluginlib_actions: No actions defined
diff --git a/apps/plugins/lib/pluginlib_bmp.c b/apps/plugins/lib/pluginlib_bmp.c
index f3edfbf425..82f84b05af 100644
--- a/apps/plugins/lib/pluginlib_bmp.c
+++ b/apps/plugins/lib/pluginlib_bmp.c
@@ -94,7 +94,7 @@ int save_bmp_file( char* filename, struct bitmap *bm )
*/
void simple_resize_bitmap(struct bitmap *src, struct bitmap *dst)
{
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
const int srcw = src->height;
const int srch = src->width;
const int dstw = dst->height;
diff --git a/apps/plugins/lib/printcell_helper.c b/apps/plugins/lib/printcell_helper.c
new file mode 100644
index 0000000000..48b8b2c9d2
--- /dev/null
+++ b/apps/plugins/lib/printcell_helper.c
@@ -0,0 +1,681 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 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.
+ *
+ ****************************************************************************/
+/* spreadsheet cells for rockbox lists */
+#include "plugin.h"
+#include "lib/printcell_helper.h"
+
+#define COLUMN_ENDLEN 3
+#define TITLE_FLAG 0xFF
+#define SELECTED_FLAG 0x1
+
+#if LCD_DEPTH == 1
+#define BAR_WIDTH (1)
+#else
+#define BAR_WIDTH (COLUMN_ENDLEN)
+#endif
+
+struct printcell_info_t {
+ struct gui_synclist *gui_list; /* list to display */
+ int offw[NB_SCREENS]; /* padding between column boundries and text */
+ int iconw[NB_SCREENS]; /* width of an icon */
+ int selcol_offw[NB_SCREENS]; /* offset width calculated for selected item */
+ int totalcolw[NB_SCREENS]; /* total width of all columns */
+ int firstcolxw[NB_SCREENS]; /* first column x + width, save recalculating */
+ int ncols; /* number of columns */
+ int selcol; /* selected column (-1 to ncols-1) */
+ uint32_t hidecol_flags; /*bits 0-31 set bit to 1 to hide a column (1<<col#) */
+ uint16_t colw[NB_SCREENS][PRINTCELL_MAX_COLUMNS]; /* width of title text
+ or MIN(or user defined width / screen width) */
+ char title[PRINTCELL_MAXLINELEN]; /* title buffer */
+ char titlesep; /* character that separates title column items (ex '$') */
+ char colsep; /* character that separates text column items (ex ',') */
+ bool separator; /* draw grid */
+};
+
+static struct printcell_info_t printcell;
+
+static void parse_dsptext(char splitchr, int ncols, const char *dsp_text,
+ char* buffer, size_t bufsz, uint16_t *sidx)
+{
+ /*Internal function loads sidx with split offsets indexing
+ the buffer of null terminated strings, splits on 'splitchr'
+ _assumptions_:
+ dsp_text[len - 1] = \0,
+ sidx[PRINTCELL_MAX_COLUMNS]
+ */
+ int i = 0;
+ size_t j = 0;
+ int ch = splitchr; /* first column $ is optional */
+ if (*dsp_text == splitchr)
+ dsp_text++;
+ /* add null to the start of the text buffer */
+ buffer[j++] = '\0';
+ do
+ {
+ if (ch == splitchr)
+ {
+ sidx[i] = j; /* save start index and copy next column to the buffer */
+ while (*dsp_text != '\0' && *dsp_text != splitchr && j < (bufsz - 1))
+ {
+ buffer[j++] = *dsp_text++;
+ }
+ buffer[j++] = '\0';
+
+ i++;
+ if (i >= ncols || j >= (bufsz - 1))
+ break;
+ }
+ ch = *dsp_text++;
+ } while (ch != '\0');
+ while (i < ncols)
+ sidx[i++] = 0; /* point to null */
+}
+
+static void draw_selector(struct screen *display, struct line_desc *linedes,
+ int selected_flag, int selected_col,
+ int separator_height, int x, int y, int w, int h)
+{
+ /* Internal function draws the currently selected items row & column styling */
+ if (!(separator_height > 0 || (selected_flag & SELECTED_FLAG)))
+ return;
+ y--;
+ h++;
+ int linestyle = linedes->style & _STYLE_DECO_MASK;
+ bool invert = (selected_flag == SELECTED_FLAG && linestyle >= STYLE_COLORBAR);
+ if (invert || (linestyle & STYLE_INVERT) == STYLE_INVERT)
+ {
+ display->set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
+ }
+
+ if (selected_col == printcell.selcol)
+ {
+ if (selected_flag & SELECTED_FLAG)
+ {
+ /* expand left and right bars to show selected column */
+ display->fillrect(x, y, BAR_WIDTH, h);
+ display->fillrect(x + w - BAR_WIDTH + 1, y, BAR_WIDTH, h);
+ display->set_drawmode(DRMODE_FG);
+ }
+ else
+ {
+ /* only draw left and right bars */
+ display->drawrect(x + 1, y, 1, h);
+ display->drawrect(x + w - 1, y, 1, h);
+ return;
+ }
+ }
+ else if (printcell.selcol < 0)
+ {
+ if (selected_flag == SELECTED_FLAG)
+ {
+ if (selected_col > 0)
+ x--;
+ w++;
+ }
+ }
+ /* draw whole rect outline */
+ display->drawrect(x + 1, y, w - 1, h);
+}
+
+static inline void set_cell_width(struct viewport *vp, int max_w, int new_w)
+{
+ /* Internal function sets cell width if less than the max width */
+ if (new_w > max_w)
+ vp->width = max_w;
+ else
+ vp->width = new_w;
+ vp->width -= COLUMN_ENDLEN;
+}
+
+static inline int printcells(struct screen *display, char* buffer,
+ uint16_t *sidx, struct line_desc *linedes,
+ struct viewport *vp, int vp_w, int separator,
+ int x, int y, int offw, int selected_flag, int last_col,
+ bool scroll, bool is_title)
+{
+ /* Internal function prints remaining cells */
+ int text_offset = offw + offw;
+ int screen = display->screen_type;
+ int height = linedes->height;
+ int selsep = (selected_flag == 0) ? 0: separator;
+ uint16_t *screencolwidth = printcell.colw[screen];
+
+ for(int i = 1; i <= last_col; i++)
+ {
+ int ny = y;
+ int nw = screencolwidth[i] + text_offset;
+ int offx = 0;
+
+ if (i == last_col || x + nw >= vp_w - offw + 1)
+ { /* not enough space for next column use up excess */
+ if (nw < (vp_w - x))
+ {
+ if (is_title)
+ offx = ((vp_w - x) - nw) / 2;
+ nw = vp_w - x;
+ }
+ }
+
+ if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i))
+ nw = 0;
+
+ int nx = x + nw;
+ char *buftext;
+
+ if (nx > 0 && nw > offw && x < vp_w)
+ {
+ set_cell_width(vp, vp_w, nx);
+
+ if (i == printcell.selcol)
+ {
+ linedes->scroll = (selected_flag > 0);
+ linedes->separator_height = selsep;
+ }
+ else
+ {
+ if (vp_w < x + text_offset)
+ {
+ scroll = false;
+ }
+ linedes->scroll = scroll;
+ linedes->separator_height = separator;
+ }
+ buftext = &buffer[sidx[i]];
+ display->put_line(x + offw + offx, ny, linedes, "$t", buftext);
+ vp->width += COLUMN_ENDLEN + 1;
+ draw_selector(display, linedes, selected_flag, i, separator, x, ny, nw, height);
+ }
+ x = nx;
+ }
+ return x;
+}
+
+static inline int calcvisible(int screen, int vp_w, int text_offset, int sbwidth)
+{
+ /* Internal function determine how many of the previous colums can be shown */
+ uint16_t *screencolwidth = printcell.colw[screen];
+ int screenicnwidth = printcell.iconw[screen];
+ int offset = 0;
+ int selcellw = 0;
+ if (printcell.selcol >= 0)
+ selcellw = screencolwidth[printcell.selcol] + text_offset;
+ int maxw = vp_w - (sbwidth + selcellw + 1);
+
+ for (int i = printcell.selcol - 1; i >= 0; i--)
+ {
+ int cw = screencolwidth[i] + text_offset;
+
+ if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i))
+ cw = 0;
+
+ if (i == 0)
+ cw += screenicnwidth;
+ if (offset > 0 || cw > maxw)
+ offset += cw; /* can not display this cell -- everything left goes here too */
+ else
+ maxw -= cw; /* can display this cell subtract from the max width */
+ }
+ return offset;
+}
+
+static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info)
+{
+/* Internal function callback from the list, draws items as they are requested */
+#define ICON_PADDING 1
+#define ICON_PADDING_S "1"
+ struct screen *display = list_info->display;
+ int screen = display->screen_type;
+ int col_offset_width = printcell.offw[screen];
+ int text_offset = col_offset_width + col_offset_width;
+
+ static char printcell_buffer[PRINTCELL_MAXLINELEN];
+ static uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /*indexes zero terminated strings in buffer*/
+
+ struct gui_synclist *list = list_info->list;
+ int offset_pos = list_info->list->offset_position[screen];
+ int x = list_info->x - offset_pos;
+ int y = list_info->y;
+ int line_indent = list_info->item_indent;
+ int item_offset = list_info->item_offset;
+ int icon = list_info->icon;
+ int icon_w = list_info->icon_width;
+ bool is_title = list_info->is_title;
+ bool is_selected = list_info->is_selected;
+ bool show_cursor = list_info->show_cursor;
+ bool have_icons = list_info->have_icons;
+ struct line_desc *linedes = list_info->linedes;
+ const char *dsp_text = list_info->dsp_text;
+ struct viewport *vp = list_info->vp;
+ int line = list_info->line;
+
+ int selected_flag = ((is_selected || is_title) ?
+ (is_title ? TITLE_FLAG : SELECTED_FLAG) : 0);
+ bool scroll_items = ((selected_flag == TITLE_FLAG) ||
+ (printcell.selcol < 0 && selected_flag > 0));
+
+ /* save for restore */
+ int vp_w = vp->width;
+ int saved_separator_height = linedes->separator_height;
+ bool saved_scroll = linedes->scroll;
+
+ linedes->separator_height = 0;
+ int separator = saved_separator_height;
+
+ if (printcell.separator || (selected_flag & SELECTED_FLAG))
+ separator = 1;
+
+ int nx = x;
+ int last_col = printcell.ncols - 1;
+ int hidden_w = 0;
+ int nw, colxw;
+ char *buftext;
+ printcell_buffer[0] = '\0';
+
+ uint16_t *screencolwidth = printcell.colw[screen];
+ if (printcell.hidecol_flags > 0)
+ {
+ for (int i = 0; i < printcell.ncols; i++)
+ {
+ if (PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, i))
+ last_col = i;
+ else
+ hidden_w += (screencolwidth[i] + text_offset);
+ }
+ }
+
+ if (is_title)
+ {
+ parse_dsptext(printcell.titlesep, printcell.ncols, dsp_text,
+ printcell_buffer, sizeof(printcell_buffer), sidx);
+
+ buftext = &printcell_buffer[sidx[0]]; /* set to first column text */
+ int sbwidth = 0;
+ if (rb->global_settings->scrollbar == SCROLLBAR_LEFT)
+ sbwidth = rb->global_settings->scrollbar_width;
+
+ printcell.iconw[screen] = have_icons ? ICON_PADDING + icon_w : 0;
+
+ if (printcell.selcol_offw[screen] == 0 && printcell.selcol > 0)
+ printcell.selcol_offw[screen] = calcvisible(screen, vp_w, text_offset, sbwidth);
+
+ nx -= printcell.selcol_offw[screen];
+
+ nw = screencolwidth[0] + printcell.iconw[screen] + text_offset;
+
+ if (!PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, 0))
+ nw = printcell.iconw[screen] - 1;
+ nw += sbwidth;
+
+ colxw = nx + nw;
+ printcell.firstcolxw[screen] = colxw; /* save position of first column for subsequent items */
+
+ if (colxw > 0)
+ {
+ set_cell_width(vp, vp_w, colxw);
+ linedes->separator_height = separator;
+
+ if (have_icons)
+ {
+ display->put_line(nx + (COLUMN_ENDLEN/2), y, linedes,
+ "$"ICON_PADDING_S"I$t", icon, buftext);
+ }
+ else
+ {
+ display->put_line(nx + col_offset_width, y, linedes, "$t", buftext);
+ }
+ }
+ }
+ else
+ {
+ parse_dsptext(printcell.colsep, printcell.ncols, dsp_text,
+ printcell_buffer, sizeof(printcell_buffer), sidx);
+
+ buftext = &printcell_buffer[sidx[0]]; /* set to first column text */
+ int cursor = Icon_NOICON;
+ nx -= printcell.selcol_offw[screen];
+
+ if (selected_flag & SELECTED_FLAG)
+ {
+ cursor = Icon_Cursor;
+ /* limit length of selection if columns don't reach end */
+ int maxw = nx + printcell.totalcolw[screen] + printcell.iconw[screen];
+ maxw += text_offset * printcell.ncols;
+ maxw -= hidden_w;
+
+ if (vp_w > maxw)
+ vp->width = maxw;
+ /* display a blank line first to draw selector across all cells */
+ display->put_line(x, y, linedes, "$t", "");
+ }
+
+ //nw = screencolwidth[0] + printcell.iconw[screen] + text_offset;
+ colxw = printcell.firstcolxw[screen] - vp->x; /* match title spacing */
+ nw = colxw - nx;
+ if (colxw > 0)
+ {
+ set_cell_width(vp, vp_w, colxw);
+ if (printcell.selcol == 0 && selected_flag == 0)
+ linedes->separator_height = 0;
+ else
+ {
+ linedes->scroll = printcell.selcol == 0 || scroll_items;
+ linedes->separator_height = separator;
+ }
+ if (show_cursor && have_icons)
+ {
+ /* the list can have both, one of or neither of cursor and item icons,
+ * if both don't apply icon padding twice between the icons */
+ display->put_line(nx, y,
+ linedes, "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t",
+ line_indent, cursor, icon, item_offset, buftext);
+ }
+ else if (show_cursor || have_icons)
+ {
+ display->put_line(nx, y, linedes, "$*s$"ICON_PADDING_S"I$*t", line_indent,
+ show_cursor ? cursor:icon, item_offset, buftext);
+ }
+ else
+ {
+ display->put_line(nx + col_offset_width, y, linedes,
+ "$*s$*t", line_indent, item_offset, buftext);
+ }
+ }
+ }
+
+ if (colxw > 0) /* draw selector for first column (title or items) */
+ {
+ vp->width += COLUMN_ENDLEN + 1;
+ draw_selector(display, linedes, selected_flag, 0,
+ separator, nx, y, nw, linedes->height);
+ }
+ nx += nw;
+ /* display remaining cells */
+ printcells(display, printcell_buffer, sidx, linedes, vp, vp_w, separator,
+ nx, y, col_offset_width, selected_flag, last_col, scroll_items, is_title);
+
+ /* draw a line at the bottom of the list */
+ if (separator > 0 && line == list->nb_items - 1)
+ display->hline(x, LCD_WIDTH, y + linedes->height - 1);
+
+ /* restore settings */
+ linedes->scroll = saved_scroll;
+ linedes->separator_height = saved_separator_height;
+ display->set_drawmode(DRMODE_FG);
+ vp->width = vp_w;
+}
+
+void printcell_enable(bool enable)
+{
+ if (!printcell.gui_list)
+ return;
+ struct gui_synclist *gui_list = printcell.gui_list;
+#ifdef HAVE_LCD_COLOR
+ static int list_sep_color = INT_MIN;
+ if (enable)
+ {
+ if (list_sep_color == INT_MIN)
+ list_sep_color = rb->global_settings->list_separator_color;
+ rb->global_settings->list_separator_color = rb->global_settings->fg_color;
+ gui_list->callback_draw_item = printcell_listdraw_fn;
+ }
+ else
+ {
+ gui_list->callback_draw_item = NULL;
+ if (list_sep_color != INT_MIN)
+ rb->global_settings->list_separator_color = list_sep_color;
+ list_sep_color = INT_MIN;
+ }
+#else
+ if (enable)
+ gui_list->callback_draw_item = printcell_listdraw_fn;
+ else
+ gui_list->callback_draw_item = NULL;
+#endif
+
+}
+
+int printcell_increment_column(int increment, bool wrap)
+{
+ if (!printcell.gui_list)
+ return -1;
+ struct gui_synclist *gui_list = printcell.gui_list;
+ int item = printcell.selcol + increment;
+ int imin = -1;
+ int imax = printcell.ncols - 1;
+ if(wrap)
+ {
+ imin = imax;
+ imax = -1;
+ }
+
+ if (item < -1)
+ item = imin;
+ else if (item >= printcell.ncols)
+ item = imax;
+
+ if (item != printcell.selcol)
+ {
+ FOR_NB_SCREENS(n) /* offset needs recalculated */
+ printcell.selcol_offw[n] = 0;
+ printcell.selcol = item;
+
+ rb->gui_synclist_draw(gui_list);
+ rb->gui_synclist_speak_item(gui_list);
+ }
+ return item;
+}
+
+int printcell_get_column_selected(void)
+{
+ if (!printcell.gui_list)
+ return -1;
+ return printcell.selcol;
+}
+
+uint32_t printcell_get_column_visibility(int col)
+{
+ if (col >= 0)
+ return (PRINTCELL_COLUMN_IS_VISIBLE(printcell.hidecol_flags, col) ? 0:1);
+ else /* return flag of all columns */
+ return printcell.hidecol_flags;
+}
+
+void printcell_set_column_visible(int col, bool visible)
+{
+ /* visible columns have 0 for the column bit hidden columns have the bit set */
+ if (col >= 0)
+ {
+ if (visible)
+ printcell.hidecol_flags &= ~(PRINTCELL_COLUMN_FLAG(col));
+ else
+ printcell.hidecol_flags |= PRINTCELL_COLUMN_FLAG(col);
+ }
+ else
+ {
+ if (visible) /* set to everything visible */
+ printcell.hidecol_flags = 0;
+ else /* set to everything hidden */
+ printcell.hidecol_flags = ((uint32_t)-1);
+ }
+}
+
+int printcell_set_columns(struct gui_synclist *gui_list,
+ struct printcell_settings * pcs,
+ char * title, enum themable_icons icon)
+{
+
+ if (title == NULL)
+ title = "$PRINTCELL NOT SETUP";
+
+ if (pcs == NULL) /* DEFAULTS */
+ {
+#if LCD_DEPTH > 1
+ /* If line sep is set to automatic then outline cells */
+ bool sep = (rb->global_settings->list_separator_height < 0);
+#else
+ bool sep = (rb->global_settings->cursor_style == 0);
+#endif
+ pcs = &(struct printcell_settings){ .cell_separator = sep,
+ .title_delimeter = '$',
+ .text_delimeter = '$',
+ .hidecol_flags = 0};
+ }
+
+ uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /* starting position of column in title string */
+ int width, height, user_minwidth;
+ int i = 0;
+ size_t j = 0;
+ rb->memset(&printcell, 0, sizeof(struct printcell_info_t));
+
+ printcell.gui_list = gui_list;
+ printcell.separator = pcs->cell_separator;
+ printcell.titlesep = pcs->title_delimeter;
+ printcell.colsep = pcs->text_delimeter;
+ printcell.hidecol_flags = pcs->hidecol_flags;
+
+ int ch = printcell.titlesep; /* first column $ is optional */
+
+ FOR_NB_SCREENS(n)
+ {
+ rb->screens[n]->getstringsize("W", &width, &height);
+ printcell.offw[n] = width; /* set column text offset */
+ }
+
+ if (*title == printcell.titlesep)
+ title++;
+ do
+ {
+ if (ch == printcell.titlesep)
+ {
+ printcell.title[j++] = ch;
+ user_minwidth = 0;
+ if (*title == '*')/* user wants a minimum size for this column */
+ {
+ char *dspst = title++; /* store starting position in case this is wrong */
+ while(isdigit(*title))
+ {
+ user_minwidth = 10*user_minwidth + *title - '0';
+ title++;
+ }
+ if (*title != printcell.titlesep) /* user forgot titlesep or wants to display '*' */
+ {
+ title = dspst;
+ user_minwidth = 0;
+ }
+ else
+ title++;
+ }
+
+ sidx[i] = j;
+
+ while (*title != '\0'
+ && *title != printcell.titlesep
+ && j < PRINTCELL_MAXLINELEN - 1)
+ {
+ printcell.title[j++] = *title++;
+ }
+
+ FOR_NB_SCREENS(n)
+ {
+ rb->screens[n]->getstringsize(&printcell.title[sidx[i]],
+ &width, &height);
+
+ if (width < user_minwidth)
+ width = user_minwidth;
+
+ if (width > LCD_WIDTH)
+ width = LCD_WIDTH;
+
+ printcell.colw[n][i] = width;
+ printcell.totalcolw[n] += width;
+ }
+ if (++i >= PRINTCELL_MAX_COLUMNS - 1)
+ break;
+ }
+ ch = *title++;
+ } while (ch != '\0');
+ printcell.ncols = i;
+ printcell.title[j] = '\0';
+ printcell.selcol = -1;
+
+ rb->gui_synclist_set_title(gui_list, printcell.title, icon);
+ return printcell.ncols;
+}
+
+char *printcell_get_title_text(int selcol, char *buf, size_t bufsz)
+{
+ /* note offsets are calculated everytime this function is called
+ * shouldn't be used in hot code paths */
+ int index = 0;
+ buf[0] = '\0';
+ if (selcol < 0) /* return entire string incld col formatting '$'*/
+ return printcell.title;
+
+ if (selcol < printcell.ncols)
+ {
+ uint16_t sidx[PRINTCELL_MAX_COLUMNS]; /*indexes zero terminated strings in buffer*/
+ parse_dsptext(printcell.titlesep, selcol + 1, printcell.title, buf, bufsz, sidx);
+ index = sidx[selcol];
+ }
+ return &buf[index];
+}
+
+char *printcell_get_column_text(int selcol, char *buf, size_t bufsz)
+{
+ int index = 0;
+ char *bpos;
+ struct gui_synclist *gui_list = printcell.gui_list;
+
+ if (gui_list && gui_list->callback_draw_item == printcell_listdraw_fn)
+ {
+ int col = selcol;
+ int item = gui_list->selected_item;
+ void *data = gui_list->data;
+
+ if (col < printcell.ncols
+ && gui_list->callback_get_item_name(item, data, buf, bufsz) == buf)
+ {
+ bpos = buf;
+ if (col < 0) /* return entire string incld col formatting '$'*/
+ {
+ return bpos;
+ }
+ bpos++; /* Skip sep/NULL */
+
+ while(bpos < &buf[bufsz - 1])
+ {
+ if (*bpos == printcell.colsep || *bpos == '\0')
+ {
+ if (col-- == 0)
+ goto success;
+ index = bpos - buf + 1; /* Skip sep/NULL */
+ }
+ bpos++;
+ }
+ }
+ }
+/*failure*/
+ bpos = buf;
+ index = 0;
+success:
+ *bpos = '\0';
+ return &buf[index];
+}
diff --git a/apps/plugins/lib/printcell_helper.h b/apps/plugins/lib/printcell_helper.h
new file mode 100644
index 0000000000..f58e73c0a5
--- /dev/null
+++ b/apps/plugins/lib/printcell_helper.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 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 _PRINTCELL_LIST_H_
+#define _PRINTCELL_LIST_H_
+#ifndef PRINTCELL_MAX_COLUMNS
+#define PRINTCELL_MAX_COLUMNS 16 /* Max 32 (hidecol_flags)*/
+#endif
+
+#define PRINTCELL_MAXLINELEN MAX_PATH
+#define PC_COL_FLAG(col) ((uint32_t)(col >= 0 \
+ && col < PRINTCELL_MAX_COLUMNS) ? 1u<<col : -1u)
+
+#define PRINTCELL_COLUMN_IS_VISIBLE(flag, col) ((flag & PC_COL_FLAG(col)) == 0)
+#define PRINTCELL_COLUMN_FLAG(col) (PC_COL_FLAG(col))
+
+struct printcell_settings
+{
+ bool cell_separator;
+ char title_delimeter;
+ char text_delimeter;
+ uint32_t hidecol_flags;
+};
+
+/* Printcell initialization - Sets title and calculates cell widths
+* by default each column is identified by '$' character
+* ex 3 columns title = "Col1$Col2$Col3" also accepts $*WIDTH$
+* ex 3 columns varying width title = "$*64$Col1$*128$Col2$Col3
+* supplying struct printcell_settings pcs allows changing default settings
+* supply NULL to use defaults
+*
+* Returns number of columns
+*/
+int printcell_set_columns(struct gui_synclist *gui_list,
+ struct printcell_settings * pcs,
+ char * title, enum themable_icons icon);
+
+/* Sets the printcell function enabled (use after initializing with set_column)
+ * Note you should call printcell_enable(false) if the list might be reused */
+void printcell_enable(bool enable);
+
+/* Increments the current selected column negative increment is allowed
+ returns the selected column
+ range: -1(no selection) to ncols - 1 */
+int printcell_increment_column(int increment, bool wrap);
+
+/* Return index of the currently selected column (-1 to ncols - 1) */
+int printcell_get_column_selected(void);
+
+/* Return the text of currently selected column buffer should be sized
+ * for max item len, buf[PRINTCELL_MAXLINELEN] is a safe bet */
+char *printcell_get_column_text(int selcol, char *buf, size_t bufsz);
+
+/* Return the text of currently selected column title should be sized
+ * for max item len, buf[PRINTCELL_MAXLINELEN] is a safe bet */
+char *printcell_get_title_text(int selcol, char *buf, size_t bufsz);
+
+
+/* Hide or show a specified column - supply col = -1 to affect all columns */
+void printcell_set_column_visible(int col, bool visible);
+
+/* Return visibility of a specified column
+* returns (1 visible or 0 hidden)
+* if supply col == -1 a flag with visibility of all columns will be returned
+* NOTE: flag denotes a hidden column by a 1 in the column bit (1 << col#)
+* PRINTCELL_COLUMN_IS_VISIBLE(flag,col) macro will convert to bool
+*/
+uint32_t printcell_get_column_visibility(int col);
+#endif /*_PRINTCELL_LIST_H_*/
diff --git a/apps/plugins/lib/wrappers.h b/apps/plugins/lib/wrappers.h
index b6fbd51a39..761854fa05 100644
--- a/apps/plugins/lib/wrappers.h
+++ b/apps/plugins/lib/wrappers.h
@@ -53,6 +53,7 @@
#define strlen rb->strlen
#define strlcpy rb->strlcpy
#define strrchr rb->strrchr
+#define fix_path_part rb->fix_path_part
#endif
diff --git a/apps/plugins/lib/xlcd_scroll.c b/apps/plugins/lib/xlcd_scroll.c
index 5ac4a366e8..906f4eaae1 100644
--- a/apps/plugins/lib/xlcd_scroll.c
+++ b/apps/plugins/lib/xlcd_scroll.c
@@ -30,7 +30,7 @@
static const unsigned short patterns[4] = {0xFFFF, 0xFF00, 0x00FF, 0x0000};
#endif
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
void xlcd_scroll_left(int count)
{
/*size_t dst_stride;*/
@@ -668,4 +668,4 @@ void xlcd_scroll_down(int count)
}
#endif /* LCD_PIXELFORMAT, LCD_DEPTH */
-#endif /* defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE */
+#endif /* LCD_STRIDEFORMAT == VERTICAL_STRIDE */