rockbox/apps/plugins/oscilloscope.c
Kevin Ferrare 0e027bd90c To avoid having to use the main unit when accidentally launching a plugin from the remote, plugins now support remote quit
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10157 a1c6a512-1295-4272-9138-f99709370657
2006-06-30 16:43:47 +00:00

687 lines
21 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Oscilloscope, with many different modes of operation.
*
* Copyright (C) 2004-2006 Jens Arnold
*
* 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.
*
****************************************************************************/
#include "plugin.h"
#ifdef HAVE_LCD_BITMAP
#include "xlcd.h"
#include "configfile.h"
PLUGIN_HEADER
/* variable button definitions */
#if CONFIG_KEYPAD == RECORDER_PAD
#define OSCILLOSCOPE_QUIT BUTTON_OFF
#define OSCILLOSCOPE_DRAWMODE BUTTON_F1
#define OSCILLOSCOPE_ADVMODE BUTTON_F2
#define OSCILLOSCOPE_ORIENTATION BUTTON_F3
#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
#define OSCILLOSCOPE_VOL_UP BUTTON_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
#elif CONFIG_KEYPAD == ONDIO_PAD
#define OSCILLOSCOPE_QUIT BUTTON_OFF
#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_MENU
#define OSCILLOSCOPE_DRAWMODE (BUTTON_MENU | BUTTON_REL)
#define OSCILLOSCOPE_ADVMODE (BUTTON_MENU | BUTTON_RIGHT)
#define OSCILLOSCOPE_ORIENTATION (BUTTON_MENU | BUTTON_LEFT)
#define OSCILLOSCOPE_PAUSE (BUTTON_MENU | BUTTON_OFF)
#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
#define OSCILLOSCOPE_VOL_UP BUTTON_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
#define OSCILLOSCOPE_QUIT BUTTON_OFF
#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
#define OSCILLOSCOPE_ADVMODE BUTTON_MODE
#define OSCILLOSCOPE_ORIENTATION BUTTON_REC
#define OSCILLOSCOPE_PAUSE BUTTON_ON
#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
#define OSCILLOSCOPE_VOL_UP BUTTON_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
#define OSCILLOSCOPE_RC_QUIT BUTTON_RC_STOP
#elif (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD)
#define OSCILLOSCOPE_QUIT (BUTTON_SELECT | BUTTON_MENU)
#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_PLAY)
#define OSCILLOSCOPE_ADVMODE (BUTTON_SELECT | BUTTON_RIGHT)
#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_FWD
#define OSCILLOSCOPE_VOL_DOWN BUTTON_SCROLL_BACK
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
#define OSCILLOSCOPE_QUIT BUTTON_POWER
#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
#define OSCILLOSCOPE_PAUSE BUTTON_A
#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
#elif CONFIG_KEYPAD == IAUDIO_X5_PAD
#define OSCILLOSCOPE_QUIT BUTTON_POWER
#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_SELECT
#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REL)
#define OSCILLOSCOPE_ADVMODE BUTTON_REC
#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_REPEAT)
#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
#define OSCILLOSCOPE_VOL_UP BUTTON_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
#endif
/* colours */
#if LCD_DEPTH > 1
#ifdef HAVE_LCD_COLOR
#define BACKG_COLOR LCD_BLACK
#define GRAPH_COLOR LCD_RGBPACK(128, 255, 0)
#define CURSOR_COLOR LCD_RGBPACK(255, 0, 0)
#else
#define BACKG_COLOR LCD_WHITE
#define GRAPH_COLOR LCD_BLACK
#define CURSOR_COLOR LCD_DARKGRAY
#endif
#endif
enum { DRAW_FILLED, DRAW_LINE, DRAW_PIXEL, MAX_DRAW };
enum { ADV_SCROLL, ADV_WRAP, MAX_ADV };
enum { OSC_HORIZ, OSC_VERT, MAX_OSC };
#define CFGFILE_VERSION 0 /* Current config file version */
#define CFGFILE_MINVERSION 0 /* Minimum config file version to accept */
#define MAX_PEAK 0x8000
#if defined(SIMULATOR) && (CONFIG_CODEC != SWCODEC)
#define mas_codec_readreg(x) rand()%MAX_PEAK
#endif
/* global variables */
struct plugin_api* rb; /* global api struct pointer */
/* settings */
struct osc_config {
int delay; /* in ticks */
int draw;
int advance;
int orientation;
};
struct osc_config osc_disk = { 2, DRAW_FILLED, ADV_SCROLL, OSC_HORIZ };
struct osc_config osc; /* running config */
static const char cfg_filename[] = "oscilloscope.cfg";
static char *draw_str[3] = { "filled", "line", "pixel" };
static char *advance_str[2] = { "scroll", "wrap" };
static char *orientation_str[2] = { "horizontal", "vertical" };
struct configdata disk_config[] = {
{ TYPE_INT, 1, 99, &osc_disk.delay, "delay", NULL, NULL },
{ TYPE_ENUM, 0, MAX_DRAW, &osc_disk.draw, "draw", draw_str, NULL },
{ TYPE_ENUM, 0, MAX_ADV, &osc_disk.advance, "advance", advance_str, NULL },
{ TYPE_ENUM, 0, MAX_OSC, &osc_disk.orientation, "orientation", orientation_str, NULL }
};
long last_tick = 0; /* time of last drawing */
int last_pos = 0; /* last x or y drawing position. Reset for aspect switch. */
int last_left; /* last channel values */
int last_right;
unsigned char message[16]; /* message to display */
bool displaymsg = false;
int font_height = 8;
/* implementation */
void anim_horizontal(int cur_left, int cur_right)
{
int cur_x, x;
int left, right, dl, dr;
long cur_tick = *rb->current_tick;
long d = (cur_tick - last_tick) / osc.delay;
bool full_update = false;
if (d == 0) /* too early, bail out */
return;
last_tick = cur_tick;
if (d > HZ) /* first call or too much delay, (re)start */
{
last_left = cur_left;
last_right = cur_right;
return;
}
cur_x = last_pos + d;
if (cur_x >= LCD_WIDTH)
{
if (osc.advance == ADV_SCROLL)
{
int shift = cur_x - (LCD_WIDTH-1);
xlcd_scroll_left(shift);
full_update = true;
cur_x -= shift;
last_pos -= shift;
}
else
{
cur_x -= LCD_WIDTH;
}
}
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
if (cur_x > last_pos)
{
rb->lcd_fillrect(last_pos + 1, 0, d, LCD_HEIGHT);
}
else
{
rb->lcd_fillrect(last_pos + 1, 0, LCD_WIDTH - last_pos, LCD_HEIGHT);
rb->lcd_fillrect(0, 0, cur_x + 1, LCD_HEIGHT);
}
rb->lcd_set_drawmode(DRMODE_SOLID);
switch (osc.draw)
{
case DRAW_FILLED:
left = last_left;
right = last_right;
dl = (cur_left - left) / d;
dr = (cur_right - right) / d;
for (x = last_pos + 1; d > 0; x++, d--)
{
if (x == LCD_WIDTH)
x = 0;
left += dl;
right += dr;
rb->lcd_vline(x, LCD_HEIGHT/2-1,
LCD_HEIGHT/2-1 - (((LCD_HEIGHT-2) * left) >> 16));
rb->lcd_vline(x, LCD_HEIGHT/2+1,
LCD_HEIGHT/2+1 + (((LCD_HEIGHT-2) * right) >> 16));
}
break;
case DRAW_LINE:
if (cur_x > last_pos)
{
rb->lcd_drawline(
last_pos, LCD_HEIGHT/2-1 - (((LCD_HEIGHT-2) * last_left) >> 16),
cur_x, LCD_HEIGHT/2-1 - (((LCD_HEIGHT-2) * cur_left) >> 16)
);
rb->lcd_drawline(
last_pos, LCD_HEIGHT/2+1 + (((LCD_HEIGHT-2) * last_right) >> 16),
cur_x, LCD_HEIGHT/2+1 + (((LCD_HEIGHT-2) * cur_right) >> 16)
);
}
else
{
left = last_left
+ (LCD_WIDTH - last_pos) * (last_left - cur_left) / d;
right = last_right
+ (LCD_WIDTH - last_pos) * (last_right - cur_right) / d;
rb->lcd_drawline(
last_pos, LCD_HEIGHT/2-1 - (((LCD_HEIGHT-2) * last_left) >> 16),
LCD_WIDTH, LCD_HEIGHT/2-1 - (((LCD_HEIGHT-2) * left) >> 16)
);
rb->lcd_drawline(
last_pos, LCD_HEIGHT/2+1 + (((LCD_HEIGHT-2) * last_right) >> 16),
LCD_WIDTH, LCD_HEIGHT/2+1 + (((LCD_HEIGHT-2) * right) >> 16)
);
if (cur_x > 0)
{
rb->lcd_drawline(
0, LCD_HEIGHT/2-1 - (((LCD_HEIGHT-2) * left) >> 16),
cur_x, LCD_HEIGHT/2-1 - (((LCD_HEIGHT-2) * cur_left) >> 16)
);
rb->lcd_drawline(
0, LCD_HEIGHT/2+1 + (((LCD_HEIGHT-2) * right) >> 16),
cur_x, LCD_HEIGHT/2+1 + (((LCD_HEIGHT-2) * cur_right) >> 16)
);
}
}
break;
case DRAW_PIXEL:
left = last_left;
right = last_right;
dl = (cur_left - left) / d;
dr = (cur_right - right) / d;
for (x = last_pos + 1; d > 0; x++, d--)
{
if (x == LCD_WIDTH)
x = 0;
left += dl;
right += dr;
rb->lcd_drawpixel(x, LCD_HEIGHT/2-1 - (((LCD_HEIGHT-2) * left) >> 16));
rb->lcd_drawpixel(x, LCD_HEIGHT/2+1 + (((LCD_HEIGHT-2) * right) >> 16));
}
break;
}
last_left = cur_left;
last_right = cur_right;
if (displaymsg)
{
int width;
rb->lcd_getstringsize(message, &width, NULL);
last_pos -= width - 1;
rb->lcd_putsxy(last_pos, 0, message);
displaymsg = false;
if (last_pos < 0)
last_pos = 0;
}
if (full_update)
{
rb->lcd_update();
}
else
{
#if LCD_DEPTH > 1 /* cursor bar */
rb->lcd_set_foreground(CURSOR_COLOR);
rb->lcd_vline(cur_x + 1, 0, LCD_HEIGHT-1);
rb->lcd_set_foreground(GRAPH_COLOR);
#else
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
rb->lcd_vline(cur_x + 1, 0, LCD_HEIGHT-1);
rb->lcd_set_drawmode(DRMODE_SOLID);
#endif
if (cur_x > last_pos)
{
rb->lcd_update_rect(last_pos, 0, cur_x - last_pos + 2, LCD_HEIGHT);
}
else
{
rb->lcd_update_rect(last_pos, 0, LCD_WIDTH - last_pos, LCD_HEIGHT);
rb->lcd_update_rect(0, 0, cur_x + 2, LCD_HEIGHT);
}
}
last_pos = cur_x;
}
void anim_vertical(int cur_left, int cur_right)
{
int cur_y, y;
int left, right, dl, dr;
long cur_tick = *rb->current_tick;
long d = (cur_tick - last_tick) / osc.delay;
bool full_update = false;
if (d == 0) /* too early, bail out */
return;
last_tick = cur_tick;
if (d > HZ) /* first call or too much delay, (re)start */
{
last_left = cur_left;
last_right = cur_right;
return;
}
cur_y = last_pos + d;
if (cur_y >= LCD_HEIGHT)
{
if (osc.advance == ADV_SCROLL)
{
int shift = cur_y - (LCD_HEIGHT-1);
xlcd_scroll_up(shift);
full_update = true;
cur_y -= shift;
last_pos -= shift;
}
else
{
cur_y -= LCD_HEIGHT;
}
}
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
if (cur_y > last_pos)
{
rb->lcd_fillrect(0, last_pos + 1, LCD_WIDTH, d);
}
else
{
rb->lcd_fillrect(0, last_pos + 1, LCD_WIDTH, LCD_HEIGHT - last_pos);
rb->lcd_fillrect(0, 0, LCD_WIDTH, cur_y + 1);
}
rb->lcd_set_drawmode(DRMODE_SOLID);
switch (osc.draw)
{
case DRAW_FILLED:
left = last_left;
right = last_right;
dl = (cur_left - left) / d;
dr = (cur_right - right) / d;
for (y = last_pos + 1; d > 0; y++, d--)
{
if (y == LCD_HEIGHT)
y = 0;
left += dl;
right += dr;
rb->lcd_hline(LCD_WIDTH/2-1,
LCD_WIDTH/2-1 - (((LCD_WIDTH-2) * left) >> 16), y);
rb->lcd_hline(LCD_WIDTH/2+1,
LCD_WIDTH/2+1 + (((LCD_WIDTH-2) * right) >> 16), y);
}
break;
case DRAW_LINE:
if (cur_y > last_pos)
{
rb->lcd_drawline(
LCD_WIDTH/2-1 - (((LCD_WIDTH-2) * last_left) >> 16), last_pos,
LCD_WIDTH/2-1 - (((LCD_WIDTH-2) * cur_left) >> 16), cur_y
);
rb->lcd_drawline(
LCD_WIDTH/2+1 + (((LCD_WIDTH-2) * last_right) >> 16), last_pos,
LCD_WIDTH/2+1 + (((LCD_WIDTH-2) * cur_right) >> 16), cur_y
);
}
else
{
left = last_left
+ (LCD_HEIGHT - last_pos) * (last_left - cur_left) / d;
right = last_right
+ (LCD_HEIGHT - last_pos) * (last_right - cur_right) / d;
rb->lcd_drawline(
LCD_WIDTH/2-1 - (((LCD_WIDTH-2) * last_left) >> 16), last_pos,
LCD_WIDTH/2-1 - (((LCD_WIDTH-2) * left) >> 16), LCD_HEIGHT
);
rb->lcd_drawline(
LCD_WIDTH/2+1 + (((LCD_WIDTH-2) * last_right) >> 16), last_pos,
LCD_WIDTH/2+1 + (((LCD_WIDTH-2) * right) >> 16), LCD_HEIGHT
);
if (cur_y > 0)
{
rb->lcd_drawline(
LCD_WIDTH/2-1 - (((LCD_WIDTH-2) * left) >> 16), 0,
LCD_WIDTH/2-1 - (((LCD_WIDTH-2) * cur_left) >> 16), cur_y
);
rb->lcd_drawline(
LCD_WIDTH/2+1 + (((LCD_WIDTH-2) * right) >> 16), 0,
LCD_WIDTH/2+1 + (((LCD_WIDTH-2) * cur_right) >> 16), cur_y
);
}
}
break;
case DRAW_PIXEL:
left = last_left;
right = last_right;
dl = (cur_left - left) / d;
dr = (cur_right - right) / d;
for (y = last_pos + 1; d > 0; y++, d--)
{
if (y == LCD_HEIGHT)
y = 0;
left += dl;
right += dr;
rb->lcd_drawpixel(LCD_WIDTH/2-1 - (((LCD_WIDTH-2) * left) >> 16), y);
rb->lcd_drawpixel(LCD_WIDTH/2+1 + (((LCD_WIDTH-2) * right) >> 16), y);
}
break;
}
last_left = cur_left;
last_right = cur_right;
if (displaymsg)
{
last_pos -= font_height - 1;
rb->lcd_putsxy(0, last_pos, message);
displaymsg = false;
if (last_pos < 0)
last_pos = 0;
}
if (full_update)
{
rb->lcd_update();
}
else
{
#if LCD_DEPTH > 1 /* cursor bar */
rb->lcd_set_foreground(CURSOR_COLOR);
rb->lcd_hline(0, LCD_WIDTH-1, cur_y + 1);
rb->lcd_set_foreground(GRAPH_COLOR);
#else
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
rb->lcd_hline(0, LCD_WIDTH-1, cur_y + 1);
rb->lcd_set_drawmode(DRMODE_SOLID);
#endif
if (cur_y > last_pos)
{
rb->lcd_update_rect(0, last_pos, LCD_WIDTH, cur_y - last_pos + 2);
}
else
{
rb->lcd_update_rect(0, last_pos, LCD_WIDTH, LCD_HEIGHT - last_pos);
rb->lcd_update_rect(0, 0, LCD_WIDTH, cur_y + 2);
}
}
last_pos = cur_y;
}
void cleanup(void *parameter)
{
(void)parameter;
#if LCD_DEPTH > 1
rb->lcd_set_foreground(LCD_DEFAULT_FG);
rb->lcd_set_background(LCD_DEFAULT_BG);
#endif
#ifdef HAVE_LCD_COLOR
rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
#endif
}
enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
{
int button, vol;
int lastbutton = BUTTON_NONE;
bool exit = false;
bool paused = false;
bool tell_speed;
(void)parameter;
rb = api;
xlcd_init(rb);
configfile_init(rb);
configfile_load(cfg_filename, disk_config,
sizeof(disk_config) / sizeof(disk_config[0]),
CFGFILE_MINVERSION);
rb->memcpy(&osc, &osc_disk, sizeof(osc)); /* copy to running config */
#if LCD_DEPTH > 1
rb->lcd_set_foreground(GRAPH_COLOR);
rb->lcd_set_background(BACKG_COLOR);
rb->lcd_clear_display();
rb->lcd_update();
#endif
#ifdef HAVE_LCD_COLOR
rb->backlight_set_timeout(1); /* keep the light on */
#endif
rb->lcd_getstringsize("A", NULL, &font_height);
while (!exit)
{
if (!paused)
{
int left, right;
rb->sleep(MAX(last_tick + osc.delay - *rb->current_tick - 1, 0));
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
left = rb->mas_codec_readreg(0xC);
right = rb->mas_codec_readreg(0xD);
#elif (CONFIG_CODEC == SWCODEC)
rb->pcm_calculate_peaks(&left, &right);
#endif
if (osc.orientation == OSC_HORIZ)
anim_horizontal(left, right);
else
anim_vertical(left, right);
}
tell_speed = false;
button = rb->button_get(paused);
switch (button)
{
#ifdef OSCILLOSCOPE_RC_QUIT
case OSCILLOSCOPE_RC_QUIT:
#endif
case OSCILLOSCOPE_QUIT:
exit = true;
break;
case OSCILLOSCOPE_ADVMODE:
if (++osc.advance >= MAX_ADV)
osc.advance = 0;
break;
case OSCILLOSCOPE_DRAWMODE:
#ifdef OSCILLOSCOPE_DRAWMODE_PRE
if (lastbutton != OSCILLOSCOPE_DRAWMODE_PRE)
break;
#endif
if (++osc.draw >= MAX_DRAW)
osc.draw = 0;
break;
case OSCILLOSCOPE_ORIENTATION:
if (++osc.orientation >= MAX_OSC)
osc.orientation = 0;
last_pos = 0;
last_tick = 0;
displaymsg = false;
rb->lcd_clear_display();
rb->lcd_update();
break;
case OSCILLOSCOPE_PAUSE:
paused = !paused;
last_tick = 0;
break;
case OSCILLOSCOPE_SPEED_UP:
case OSCILLOSCOPE_SPEED_UP | BUTTON_REPEAT:
if (osc.delay > 1)
{
osc.delay--;
tell_speed = true;
}
break;
case OSCILLOSCOPE_SPEED_DOWN:
case OSCILLOSCOPE_SPEED_DOWN | BUTTON_REPEAT:
osc.delay++;
tell_speed = true;
break;
case OSCILLOSCOPE_VOL_UP:
case OSCILLOSCOPE_VOL_UP | BUTTON_REPEAT:
vol = rb->global_settings->volume;
if (vol < rb->sound_max(SOUND_VOLUME))
{
vol++;
rb->sound_set(SOUND_VOLUME, vol);
rb->global_settings->volume = vol;
}
break;
case OSCILLOSCOPE_VOL_DOWN:
case OSCILLOSCOPE_VOL_DOWN | BUTTON_REPEAT:
vol = rb->global_settings->volume;
if (vol > rb->sound_min(SOUND_VOLUME))
{
vol--;
rb->sound_set(SOUND_VOLUME, vol);
rb->global_settings->volume = vol;
}
break;
default:
if (rb->default_event_handler_ex(button, cleanup, NULL)
== SYS_USB_CONNECTED)
return PLUGIN_USB_CONNECTED;
break;
}
if (button != BUTTON_NONE)
lastbutton = button;
if (tell_speed)
{
rb->snprintf(message, sizeof(message), "%s%d",
(osc.orientation == OSC_VERT) ? "Speed: " : "",
100 / osc.delay);
displaymsg = true;
}
}
cleanup(NULL);
if (rb->memcmp(&osc, &osc_disk, sizeof(osc))) /* save settings if changed */
{
rb->memcpy(&osc_disk, &osc, sizeof(osc));
configfile_save(cfg_filename, disk_config,
sizeof(disk_config) / sizeof(disk_config[0]),
CFGFILE_VERSION);
}
return PLUGIN_OK;
}
#endif /* HAVE_LCD_BITMAP */