summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/gui/line.c349
-rw-r--r--apps/gui/line.h117
-rw-r--r--apps/plugin.h1
-rw-r--r--apps/screen_access.c20
-rw-r--r--apps/screen_access.h2
6 files changed, 490 insertions, 0 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index b6bb82fa31..918ee1e4fc 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -77,6 +77,7 @@ gui/buttonbar.c
gui/icon.c
#endif
gui/list.c
+gui/line.c
#ifdef HAVE_LCD_BITMAP
gui/bitmap/list.c
gui/bitmap/list-skinned.c
diff --git a/apps/gui/line.c b/apps/gui/line.c
new file mode 100644
index 0000000000..9374279b60
--- /dev/null
+++ b/apps/gui/line.c
@@ -0,0 +1,349 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2013 Thomas Martitz
+ *
+ * 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 <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "scroll_engine.h"
+#include "system.h"
+#include "line.h"
+#include "gcc_extensions.h"
+#include "icon.h"
+#include "screens.h"
+#include "settings.h"
+#include "debug.h"
+
+#ifdef HAVE_REMOTE_LCD
+#define MAX_LINES (LCD_SCROLLABLE_LINES + LCD_REMOTE_SCROLLABLE_LINES)
+#else
+#define MAX_LINES LCD_SCROLLABLE_LINES
+#endif
+
+
+#ifdef HAVE_LCD_CHARCELLS
+#define style_line(d, x, y, l)
+#else
+static void style_line(struct screen *display, int x, int y, struct line_desc *line);
+#endif
+
+static void put_text(struct screen *display, int x, int y, struct line_desc *line,
+ const char *text, bool prevent_scroll, int text_skip_pixels);
+
+
+static struct line_desc *get_line_desc(void)
+{
+ static struct line_desc lines[MAX_LINES];
+ static unsigned line_index;
+ struct line_desc *ret;
+
+ ret = &lines[line_index++];
+ if (line_index >= ARRAYLEN(lines))
+ line_index = 0;
+
+ return ret;
+}
+
+static void scroller(struct scrollinfo *s, struct screen *display)
+{
+ /* style_line() expects the entire line rect, including padding, to
+ * draw selector properly across the text+padding. however struct scrollinfo
+ * has only the rect for the text itself, which is off depending on the
+ * line padding. this needs to be corrected for calling style_line().
+ * The alternative would be to really redraw only the text area,
+ * but that would complicate the code a lot */
+ struct line_desc *line = s->userdata;
+ style_line(display, s->x, s->y - (line->height/2 - display->getcharheight()/2), line);
+ put_text(display, s->x, s->y, line, s->line, true, s->offset);
+}
+
+static void scroller_main(struct scrollinfo *s)
+{
+ scroller(s, &screens[SCREEN_MAIN]);
+}
+
+#ifdef HAVE_REMOTE_LCD
+static void scroller_remote(struct scrollinfo *s)
+{
+ scroller(s, &screens[SCREEN_REMOTE]);
+}
+#endif
+
+static void (*scrollers[NB_SCREENS])(struct scrollinfo *s) = {
+ scroller_main,
+#ifdef HAVE_REMOTE_LCD
+ scroller_remote,
+#endif
+};
+
+static void put_icon(struct screen *display, int x, int y,
+ struct line_desc *line,
+ enum themable_icons icon)
+{
+ unsigned drmode = DRMODE_FG;
+ /* Need to change the drawmode:
+ * mono icons should behave like text, inverted on the selector bar
+ * native (colored) icons should be drawn as-is */
+ if (!get_icon_format(display->screen_type) == FORMAT_MONO && (line->style & STYLE_INVERT))
+ drmode = DRMODE_SOLID | DRMODE_INVERSEVID;
+
+ display->set_drawmode(drmode);
+ screen_put_iconxy(display, x, y, icon);
+}
+
+
+static void put_text(struct screen *display,
+ int x, int y, struct line_desc *line,
+ const char *text, bool prevent_scroll,
+ int text_skip_pixels)
+{
+ /* set drawmode because put_icon() might have changed it */
+ unsigned drmode = DRMODE_FG;
+ if (line->style & STYLE_INVERT)
+ drmode = DRMODE_SOLID | DRMODE_INVERSEVID;
+
+ display->set_drawmode(drmode);
+
+ if (line->scroll && !prevent_scroll)
+ {
+ struct line_desc *line_data = get_line_desc();
+ *line_data = *line;
+ /* precalculate to avoid doing it in the scroller, it's save to
+ * do this on the copy of the original line_desc*/
+ if (line_data->height == -1)
+ line_data->height = display->getcharheight();
+ display->putsxy_scroll_func(x, y, text,
+ scrollers[display->screen_type], line_data, text_skip_pixels);
+ }
+ else
+ display->putsxy_scroll_func(x, y, text, NULL, NULL, text_skip_pixels);
+}
+
+/* A line consists of:
+ * |[Ss]|[i]|[Ss]|[t]|, where s is empty space (pixels), S is empty space
+ * (n space characters), i is an icon and t is the text.
+ *
+ * All components are optional. However, even if none are specified the whole
+ * line will be cleared and redrawn.
+ *
+ * For empty space with the width of an icon use i and pass Icon_NOICON as
+ * corresponding argument.
+ */
+static void print_line(struct screen *display,
+ int x, int y, struct line_desc *line,
+ const char *fmt, va_list ap)
+{
+ const char *str;
+ bool num_is_valid;
+ int ch, num, height;
+ int xpos = x;
+ int icon_y, icon_h, icon_w;
+ enum themable_icons icon;
+ char tempbuf[128];
+ int tempbuf_idx;
+
+ height = line->height == -1 ? display->getcharheight() : line->height;
+ icon_h = get_icon_height(display->screen_type);
+ icon_w = get_icon_width(display->screen_type);
+ tempbuf_idx = 0;
+ /* vertically center string on the line
+ * x/2 - y/2 rounds up compared to (x-y)/2 if one of x and y is odd */
+ icon_y = y + height/2 - icon_h/2;
+ y += height/2 - display->getcharheight()/2;
+
+ /* parse format string */
+ while (1)
+ {
+ ch = *fmt++;
+ /* need to check for escaped '$' */
+ if (ch == '$' && *fmt != '$')
+ {
+ /* extra flag as num == 0 can be valid */
+ num_is_valid = false;
+ num = 0;
+ if (tempbuf_idx)
+ { /* flush pending inline text */
+ tempbuf_idx = tempbuf[tempbuf_idx] = 0;
+ put_text(display, xpos, y, line, tempbuf, false, 0);
+ xpos += display->getstringsize(tempbuf, NULL, NULL);
+ }
+next:
+ ch = *fmt++;
+ switch(ch)
+ {
+ case '*': /* num from parameter list */
+ num = va_arg(ap, int);
+ num_is_valid = true;
+ goto next;
+
+ case 'i': /* icon (without pad) */
+ case 'I': /* icon with pad */
+ if (ch == 'i')
+ num = 0;
+ else /* 'I' */
+ if (!num_is_valid)
+ num = 1;
+ icon = va_arg(ap, int);
+ /* draw it, then skip over */
+ if (icon != Icon_NOICON)
+ put_icon(display, xpos + num, icon_y, line, icon);
+ xpos += icon_w + num*2;
+ break;
+
+ case 'S':
+ if (!num_is_valid)
+ num = 1;
+ xpos += num * display->getcharwidth();
+ break;
+
+ case 's':
+ if (!num_is_valid)
+ num = 1;
+ xpos += num;
+ break;
+
+ case 't':
+ str = va_arg(ap, const char *);
+ put_text(display, xpos, y, line, str, false, num);
+ xpos += display->getstringsize(str, NULL, NULL);
+ break;
+
+ default:
+ if (LIKELY(isdigit(ch)))
+ {
+ num_is_valid = true;
+ num = 10*num + ch - '0';
+ goto next;
+ }
+ else
+ {
+ /* any other character here is an erroneous format string */
+ snprintf(tempbuf, sizeof(tempbuf), "<E:%c>", ch);
+ display->putsxy(xpos, y, tempbuf);
+ /* Don't consider going forward, fix the caller */
+ return;
+ }
+ }
+ }
+ else
+ { /* handle string constant in format string */
+ tempbuf[tempbuf_idx++] = ch;
+ if (!ch)
+ { /* end of string. put it online */
+ put_text(display, xpos, y, line, tempbuf, false, 0);
+ return;
+ }
+ else if (ch == '$')
+ fmt++; /* escaped '$', display just once */
+ }
+ }
+}
+
+#ifdef HAVE_LCD_BITMAP
+static void style_line(struct screen *display,
+ int x, int y, struct line_desc *line)
+{
+ int style = line->style;
+ int width = display->getwidth();
+ int height = line->height == -1 ? display->getcharheight() : line->height;
+ unsigned mask = STYLE_MODE_MASK & ~STYLE_COLORED;
+
+ /* mask out gradient and colorbar styles for non-color displays */
+ if (display->depth < 16)
+ {
+ if (style & (STYLE_COLORBAR|STYLE_GRADIENT))
+ {
+ style &= ~(STYLE_COLORBAR|STYLE_GRADIENT);
+ style |= STYLE_INVERT;
+ }
+ style &= ~STYLE_COLORED;
+ }
+
+ switch (style & mask)
+ {
+#if (LCD_DEPTH > 1 || (defined(LCD_REMOTE_DEPTH) && LCD_REMOTE_DEPTH > 1))
+ case STYLE_GRADIENT:
+ display->set_drawmode(DRMODE_FG);
+ display->gradient_fillrect_part(x, y, width, height,
+ line->line_color,
+ line->line_end_color,
+ height*line->nlines,
+ height*line->line);
+ break;
+ case STYLE_COLORBAR:
+ display->set_drawmode(DRMODE_FG);
+ display->set_foreground(line->line_color);
+ display->fillrect(x, y, width - x, height);
+ break;
+#endif
+ case STYLE_INVERT:
+ display->set_drawmode(DRMODE_FG);
+ display->fillrect(x, y, width - x, height);
+ break;
+ case STYLE_DEFAULT: default:
+ display->set_drawmode(DRMODE_BG | DRMODE_INVERSEVID);
+ display->fillrect(x, y, width - x, height);
+ break;
+ case STYLE_NONE:
+ break;
+ }
+#if (LCD_DEPTH > 1 || (defined(LCD_REMOTE_DEPTH) && LCD_REMOTE_DEPTH > 1))
+ /* fg color and bg color are left as-is for text drawing */
+ if (display->depth > 1)
+ {
+ if (style & STYLE_COLORED)
+ {
+ if (style & STYLE_INVERT)
+ display->set_background(line->text_color);
+ else
+ display->set_foreground(line->text_color);
+ }
+ else if (style & (STYLE_GRADIENT|STYLE_COLORBAR))
+ display->set_foreground(line->text_color);
+ else
+ display->set_foreground(global_settings.fg_color);
+ }
+#endif
+}
+#endif /* HAVE_LCD_BITMAP */
+
+void vput_line(struct screen *display,
+ int x, int y, struct line_desc *line,
+ const char *fmt, va_list ap)
+{
+ style_line(display, x, y, line);
+ print_line(display, x, y, line, fmt, ap);
+#if (LCD_DEPTH > 1 || (defined(LCD_REMOTE_DEPTH) && LCD_REMOTE_DEPTH > 1))
+ if (display->depth > 1)
+ display->set_foreground(global_settings.fg_color);
+#endif
+ display->set_drawmode(DRMODE_SOLID);
+}
+
+void put_line(struct screen *display,
+ int x, int y, struct line_desc *line,
+ const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vput_line(display, x, y, line, fmt, ap);
+ va_end(ap);
+}
diff --git a/apps/gui/line.h b/apps/gui/line.h
new file mode 100644
index 0000000000..d5350eb410
--- /dev/null
+++ b/apps/gui/line.h
@@ -0,0 +1,117 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2013 Thomas Martitz
+ *
+ * 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 __LINE_H__
+#define __LINE_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+
+#include "lcd.h"
+#include "screens.h"
+
+struct line_desc {
+ /* height of the line (in pixels). -1 to inherit the height
+ * from the font. The text will be centered if the height is larger,
+ * but the decorations will span the entire height */
+ int height;
+ /* multiline support: For some decorations (e.g. gradient) to work
+ * across multiple lines (e.g. to draw a line selector across 2 lines)
+ * the line index and line count must be known. For normal, single
+ * lines specify nlines=1 and line=0 */
+ /* line count of a group */
+ int16_t nlines;
+ /* index of the line in the group */
+ int16_t line;
+ /* line text color if STYLE_COLORED is specified, in native
+ * lcd format (convert with LCD_RGBPACK() if necessary) */
+ fb_data text_color;
+ /* line color if STYLE_COLORBAR or STYLE_GRADIENT is specified, in native
+ * lcd format (convert with LCD_RGBPACK() if necessary) */
+ fb_data line_color, line_end_color;
+ /* line decorations, see STYLE_DEFAULT etc. */
+ int style;
+ /* whether the line can scroll */
+ bool scroll;
+};
+
+/* default initializer, can be used for static initialitation also.
+ * This initializer will result in single lines without style that don't scroll */
+#define LINE_DESC_DEFINIT { .style = STYLE_DEFAULT, .height = -1, .line = 0, .nlines = 1, .scroll = false }
+
+/**
+ * Print a line at a given pixel postion, using decoration information from
+ * line and content information from the format specifier. The format specifier
+ * can include tags that depend on further parameters given to the function
+ * (similar to the well-known printf()).
+ *
+ * Tags start with the $ sign. Below is a list of the currently supported tags:
+ * $s - insert a column (1px wide) of empty space.
+ * $S - insert a column (1 char wide) of empty space.
+ * $i - insert an icon. put_line() expects a corresponding parameter of the
+ * type 'enum themable_icons'. If Icon_NOICON is passed, then empty
+ * space (icon-wide) will be inserted.
+ * $I - insert an icon with padding. Works like $i but additionally
+ * adds a column (1px wide) of empty space on either side of the icon.
+ * $t - insert text. put_line() expects a corresponding parameter of the type
+ * 'const char *'
+ * $$ - insert a '$' char, use it to escape $.
+ *
+ * $I, $s and $S support the following two forms:
+ * $n[IsS] - inserts n columns (pixels/chars respectively) of empty space
+ * $*[IsS] - inserts n columns (pixels/chars respectively) of empty space. put_line()
+ * expects a correspinding paramter of the type 'int' that specifies n.
+ *
+ * $t supports the following two forms:
+ * $nt - skips the first n pixels when displaying the string
+ * $*t - skips the first n pixels when displaying the string. put_line()
+ * expects a correspinding paramter of the type 'int' that specifies n.
+ *
+ * Inline text will be printed as is and can be freely intermixed with tags,
+ * except when the line can scroll. Due to limitations of the scroll engine
+ * only the last piece of text (whether inline or via $t) can scroll.
+ *
+ * x, y - pixel postion of the line.
+ * line - holds information for the line decorations
+ * fmt - holds the text and optionally format tags
+ * ... - additional paramters for the format tags
+ *
+ */
+void put_line(struct screen *display,
+ int x, int y, struct line_desc *line,
+ const char *fmt, ...);
+
+
+/**
+ * Print a line at a given pixel postion, using decoration information from
+ * line and content information from the format specifier. The format specifier
+ * can include tags that depend on further parameters given to the function
+ * (similar to the well-known vprintf()).
+ *
+ * For details, see put_line(). This function is equivalent, except for
+ * accepting a va_list instead of a variable paramter list.
+ */
+void vput_line(struct screen *display,
+ int x, int y, struct line_desc *line,
+ const char *fmt, va_list ap);
+
+#endif /* __LINE_H__*/
diff --git a/apps/plugin.h b/apps/plugin.h
index 38d8889d9e..4e6f4a8c4e 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -113,6 +113,7 @@ void* plugin_get_buffer(size_t *buffer_size);
#include "crc32.h"
#include "rbpaths.h"
#include "core_alloc.h"
+#include "screen_access.h"
#ifdef HAVE_ALBUMART
#include "albumart.h"
diff --git a/apps/screen_access.c b/apps/screen_access.c
index 7f44cf5305..81bb6bae8f 100644
--- a/apps/screen_access.c
+++ b/apps/screen_access.c
@@ -100,6 +100,15 @@ static void screen_helper_set_drawmode(int mode)
#endif
}
+static void screen_helper_put_line(int x, int y, struct line_desc *line,
+ const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vput_line(&screens[0], x, y, line, fmt, ap);
+ va_end(ap);
+}
+
#if NB_SCREENS == 2
static int screen_helper_remote_getcharwidth(void)
{
@@ -156,6 +165,15 @@ static void screen_helper_remote_setuifont(int font)
#endif
}
+static void screen_helper_remote_put_line(int x, int y, struct line_desc *line,
+ const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vput_line(&screens[0], x, y, line, fmt, ap);
+ va_end(ap);
+}
+
#endif
struct screen screens[NB_SCREENS] =
@@ -280,6 +298,7 @@ struct screen screens[NB_SCREENS] =
.gradient_fillrect_part = lcd_gradient_fillrect_part,
#endif
#endif
+ .put_line = screen_helper_put_line,
},
#if NB_SCREENS == 2
{
@@ -380,6 +399,7 @@ struct screen screens[NB_SCREENS] =
#if defined(HAVE_LCD_BITMAP)
.set_framebuffer = (void*)lcd_remote_set_framebuffer,
#endif
+ .put_line = screen_helper_remote_put_line,
}
#endif /* NB_SCREENS == 2 */
};
diff --git a/apps/screen_access.h b/apps/screen_access.h
index 26c9977bf2..7bc9c35237 100644
--- a/apps/screen_access.h
+++ b/apps/screen_access.h
@@ -26,6 +26,7 @@
#include "buttonbar.h"
#include "scroll_engine.h"
#include "backdrop.h"
+#include "line.h"
#if defined(HAVE_REMOTE_LCD) && !defined (ROCKBOX_HAS_LOGF)
#define NB_SCREENS 2
@@ -177,6 +178,7 @@ struct screen
void (*nine_segment_bmp)(const struct bitmap* bm, int x, int y,
int width, int height);
#endif
+ void (*put_line)(int x, int y, struct line_desc *line, const char *fmt, ...);
};
#if defined(HAVE_LCD_BITMAP) || defined(HAVE_REMOTE_LCD)