summaryrefslogtreecommitdiffstats
path: root/apps/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/2048.c14
-rw-r--r--apps/plugins/CATEGORIES9
-rw-r--r--apps/plugins/SOURCES16
-rw-r--r--apps/plugins/SOURCES.app_build1
-rw-r--r--apps/plugins/SUBDIRS4
-rw-r--r--apps/plugins/SUBDIRS.app_build1
-rw-r--r--apps/plugins/alarmclock.c12
-rw-r--r--apps/plugins/announce_status.c109
-rw-r--r--apps/plugins/battery_bench.c69
-rw-r--r--apps/plugins/bitmaps/native/SOURCES42
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.112x30x1.bmpbin0 -> 542 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.128x40x16.bmpbin0 -> 15414 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.128x42x1.bmpbin0 -> 736 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.128x42x2.bmpbin0 -> 2808 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.132x40x16.bmpbin0 -> 15894 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.138x46x2.bmpbin0 -> 3392 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.160x50x16.bmpbin0 -> 24054 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.160x53x1.bmpbin0 -> 1122 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.160x53x2.bmpbin0 -> 4312 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.176x54x16.bmpbin0 -> 28566 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.220x68x16.bmpbin0 -> 44934 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.240x74x16.bmpbin0 -> 53334 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.320x98x16.bmpbin0 -> 94136 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.480x149x16.bmpbin0 -> 214614 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.640x198x16.bmpbin0 -> 380214 bytes
-rw-r--r--apps/plugins/bitmaps/native/creditslogo.96x30x16.bmpbin0 -> 8694 bytes
-rw-r--r--apps/plugins/bitmaps/remote_native/SOURCES7
-rw-r--r--apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x1.bmpbin0 -> 736 bytes
-rw-r--r--apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x2.bmpbin0 -> 2808 bytes
-rw-r--r--apps/plugins/bounce.c23
-rw-r--r--apps/plugins/brickmania.c25
-rw-r--r--apps/plugins/bubbles.c10
-rw-r--r--apps/plugins/calculator.c4
-rw-r--r--apps/plugins/calendar.c15
-rw-r--r--apps/plugins/chessbox/chessbox.c3
-rw-r--r--apps/plugins/chessbox/chessbox_pgn.c4
-rw-r--r--apps/plugins/chessbox/chessbox_pgn.h2
-rw-r--r--apps/plugins/chopper.c35
-rw-r--r--apps/plugins/clix.c8
-rw-r--r--apps/plugins/clock/clock.c10
-rw-r--r--apps/plugins/clock/clock_menu.c24
-rw-r--r--apps/plugins/codebuster.c7
-rw-r--r--apps/plugins/credits.c79
-rw-r--r--apps/plugins/cube.c12
-rw-r--r--apps/plugins/dart_scorer.c601
-rw-r--r--apps/plugins/demystify.c32
-rw-r--r--apps/plugins/dice.c9
-rw-r--r--apps/plugins/disktidy.c2
-rw-r--r--apps/plugins/doom/d_deh.c3
-rw-r--r--apps/plugins/doom/i_video.c3
-rw-r--r--apps/plugins/doom/rockdoom.c14
-rw-r--r--apps/plugins/fft/fft.c42
-rw-r--r--apps/plugins/fire.c15
-rw-r--r--apps/plugins/fireworks.c29
-rw-r--r--apps/plugins/flipit.c4
-rw-r--r--apps/plugins/fractals/fractal.h2
-rw-r--r--apps/plugins/imageviewer/image_decoder.c5
-rw-r--r--apps/plugins/imageviewer/imageviewer.c70
-rw-r--r--apps/plugins/imageviewer/imageviewer.h14
-rw-r--r--apps/plugins/imageviewer/imageviewer_button.h8
-rw-r--r--apps/plugins/imageviewer/jpeg/yuv2rgb.c2
-rw-r--r--apps/plugins/imageviewer/ppm/ppm_decoder.c2
-rw-r--r--apps/plugins/invadrox.c2
-rw-r--r--apps/plugins/jackpot.c9
-rw-r--r--apps/plugins/jewels.c6
-rw-r--r--apps/plugins/keybox.c3
-rw-r--r--apps/plugins/keyremap.c2228
-rw-r--r--apps/plugins/lamp.c22
-rw-r--r--apps/plugins/lastfm_scrobbler.c938
-rw-r--r--apps/plugins/lastfm_scrobbler_viewer.c1033
-rw-r--r--apps/plugins/lib/SOURCES6
-rw-r--r--apps/plugins/lib/action_helper.h2
-rwxr-xr-xapps/plugins/lib/action_helper.pl2
-rw-r--r--apps/plugins/lib/bmp_smooth_scale.c2
-rw-r--r--apps/plugins/lib/button_helper.h2
-rwxr-xr-xapps/plugins/lib/button_helper.pl5
-rw-r--r--apps/plugins/lib/helper.c48
-rw-r--r--apps/plugins/lib/helper.h20
-rw-r--r--apps/plugins/lib/highscore.c9
-rw-r--r--apps/plugins/lib/id3.c39
-rw-r--r--apps/plugins/lib/id3.h26
-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_bmp.c2
-rw-r--r--apps/plugins/lib/printcell_helper.c299
-rw-r--r--apps/plugins/lib/printcell_helper.h68
-rw-r--r--apps/plugins/lib/wrappers.h1
-rw-r--r--apps/plugins/lib/xlcd_scroll.c4
-rw-r--r--apps/plugins/logo.c14
-rw-r--r--apps/plugins/lrcplayer.c36
-rw-r--r--apps/plugins/lua/include_lua/playlist.lua2
-rwxr-xr-xapps/plugins/lua/rbdefines_helper.pl2
-rw-r--r--apps/plugins/lua/rocklib.c16
-rw-r--r--apps/plugins/lua/rocklib_events.c6
-rw-r--r--apps/plugins/lua/rocklib_img.c2
-rw-r--r--apps/plugins/main_menu_config.c2
-rw-r--r--apps/plugins/matrix.c9
-rw-r--r--apps/plugins/maze.c20
-rw-r--r--apps/plugins/mazezam.c9
-rw-r--r--apps/plugins/metronome.c6
-rw-r--r--apps/plugins/mikmod/mikmod.c21
-rw-r--r--apps/plugins/mosaique.c11
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/idct_arm.S6
-rw-r--r--apps/plugins/mpegplayer/libmpeg2/idct_armv6.S7
-rw-r--r--apps/plugins/mpegplayer/mpeg_misc.c1
-rw-r--r--apps/plugins/mpegplayer/mpeg_misc.h24
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.c22
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c5
-rw-r--r--apps/plugins/multiboot_select.c346
-rw-r--r--apps/plugins/open_plugins.c73
-rw-r--r--apps/plugins/oscilloscope.c16
-rw-r--r--apps/plugins/otp.c17
-rwxr-xr-xapps/plugins/pacbox/pacbox.c18
-rw-r--r--apps/plugins/pdbox/PDa/src/m_glob.c2
-rw-r--r--apps/plugins/pdbox/PDa/src/m_obj.c3
-rw-r--r--apps/plugins/pdbox/pdbox.c2
-rw-r--r--apps/plugins/pegbox.c4
-rw-r--r--apps/plugins/periodic_table.c1
-rw-r--r--apps/plugins/pictureflow/pictureflow.c1171
-rw-r--r--apps/plugins/pitch_detector.c11
-rw-r--r--apps/plugins/plasma.c17
-rw-r--r--apps/plugins/playing_time.c392
-rw-r--r--apps/plugins/plugin_crt0.c3
-rw-r--r--apps/plugins/pong.c7
-rw-r--r--apps/plugins/properties.c439
-rw-r--r--apps/plugins/puzzles/rockbox.c15
-rw-r--r--apps/plugins/random_folder_advance_config.c3
-rw-r--r--apps/plugins/rb_info.c40
-rw-r--r--apps/plugins/resistor.c18
-rw-r--r--apps/plugins/reversi/reversi-gui.c4
-rw-r--r--apps/plugins/robotfindskitten.c6
-rw-r--r--apps/plugins/rockblox.c14
-rw-r--r--apps/plugins/rockblox1d.c7
-rw-r--r--apps/plugins/rockboy/menu.c22
-rw-r--r--apps/plugins/rockboy/rockboy.c5
-rw-r--r--apps/plugins/rocklife.c12
-rw-r--r--apps/plugins/rockpaint.c24
-rw-r--r--apps/plugins/sdl/SDL_mixer/timidity/playmidi.c2
-rw-r--r--apps/plugins/sdl/main.c7
-rw-r--r--apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c38
-rw-r--r--apps/plugins/shopper.c3
-rw-r--r--apps/plugins/shortcuts/shortcuts_view.c67
-rw-r--r--apps/plugins/sliding_puzzle.c8
-rw-r--r--apps/plugins/snake.c4
-rw-r--r--apps/plugins/snake2.c6
-rw-r--r--apps/plugins/snow.c8
-rw-r--r--apps/plugins/sokoban.c2
-rw-r--r--apps/plugins/solitaire.c5
-rw-r--r--apps/plugins/spacerocks.c19
-rw-r--r--apps/plugins/speedread.c24
-rw-r--r--apps/plugins/star.c4
-rw-r--r--apps/plugins/starfield.c18
-rw-r--r--apps/plugins/stats.c10
-rw-r--r--apps/plugins/sudoku/sudoku.c4
-rw-r--r--apps/plugins/superdom.c3
-rw-r--r--apps/plugins/tagcache/SOURCES2
-rw-r--r--apps/plugins/tagcache/tagcache.c1021
-rw-r--r--apps/plugins/tagcache/tagcache.h25
-rw-r--r--apps/plugins/tagcache/tagcache.make30
-rw-r--r--apps/plugins/test_codec.c2
-rw-r--r--apps/plugins/test_disk.c6
-rw-r--r--apps/plugins/test_fps.c8
-rw-r--r--apps/plugins/test_gfx.c8
-rw-r--r--apps/plugins/test_grey.c8
-rw-r--r--apps/plugins/test_kbd.c46
-rw-r--r--apps/plugins/test_sampr.c2
-rw-r--r--apps/plugins/test_usb.c137
-rw-r--r--apps/plugins/test_viewports.c4
-rw-r--r--apps/plugins/text_editor.c3
-rw-r--r--apps/plugins/text_viewer/tv_menu.c92
-rw-r--r--apps/plugins/theme_remove.c2
-rw-r--r--apps/plugins/vbrfix.c4
-rw-r--r--apps/plugins/viewers.config7
-rw-r--r--apps/plugins/vu_meter.c19
-rw-r--r--apps/plugins/wormlet.c21
-rw-r--r--apps/plugins/xobox.c11
-rw-r--r--apps/plugins/xworld/engine.c2
-rw-r--r--apps/plugins/xworld/sys.c24
-rw-r--r--apps/plugins/zxbox/spmain.c12
182 files changed, 9648 insertions, 1405 deletions
diff --git a/apps/plugins/2048.c b/apps/plugins/2048.c
index 2633753071..2f4eb4c001 100644
--- a/apps/plugins/2048.c
+++ b/apps/plugins/2048.c
@@ -91,9 +91,16 @@ static const int BACKGROUND_Y = (BASE_Y-MIN_SPACE);
#define KEY_DOWN PLA_DOWN
#define KEY_LEFT PLA_LEFT
#define KEY_RIGHT PLA_RIGHT
-#define KEY_EXIT PLA_CANCEL
#define KEY_UNDO PLA_SELECT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define KEY_EXIT PLA_SELECT_REPEAT
+#else
+#define KEY_EXIT PLA_CANCEL
+#endif
+
/* notice how "color" is spelled :P */
#ifdef HAVE_LCD_COLOR
@@ -148,9 +155,7 @@ static inline int rand_range(int min, int max)
/* prepares for exit */
static void cleanup(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
}
/* returns 2 or 4 */
@@ -700,9 +705,8 @@ static void init_game(bool newgame)
max_numeral_width = rb->font_get_width(rb->font_get(WHAT_FONT), '0');
#endif
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
draw();
}
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index bb0960f501..5320015772 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -22,6 +22,8 @@ clock,apps
codebuster,games
credits,viewers
cube,demos
+dart_scorer,apps
+db_commit,apps
db_folder_select,viewers
demystify,demos
dice,games
@@ -47,7 +49,10 @@ jackpot,games
jewels,games
jpeg,viewers
keybox,apps
+keyremap,apps
lamp,apps
+lastfm_scrobbler,apps
+lastfm_scrobbler_viewer,viewers
logo,demos
lrcplayer,apps
lua,viewers
@@ -67,6 +72,7 @@ minesweeper,games
mosaique,demos
mp3_encoder,apps
mpegplayer,viewers
+multiboot_select,apps
nim,games
open_plugins,viewers
oscilloscope,demos
@@ -80,6 +86,7 @@ pitch_detector,apps
pitch_screen,viewers
pixel-painter,games
plasma,demos
+playing_time,apps
png,viewers
gif,viewers
pong,games
@@ -170,10 +177,12 @@ test_disk,apps
test_fps,apps
test_grey,apps
test_gfx,apps
+test_kbd,apps
test_resize,apps
test_sampr,apps
test_scanrate,apps
test_touchscreen,apps
+test_usb,apps
test_viewports,apps
test_greylib_bitmap_scale,viewers
text_editor,apps
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index ab77dcde58..bf36d50a40 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -4,17 +4,23 @@ battery_bench.c
#endif
#ifdef HAVE_TAGCACHE
db_folder_select.c
+tagcache/tagcache.c
#endif
chessclock.c
credits.c
cube.c
+dart_scorer.c
dict.c
jackpot.c
keybox.c
+keyremap.c
+lastfm_scrobbler.c
+lastfm_scrobbler_viewer.c
logo.c
lrcplayer.c
mosaique.c
main_menu_config.c
+playing_time.c
properties.c
random_folder_advance_config.c
rb_info.c
@@ -80,8 +86,7 @@ crypt_firmware.c
/* Overlays loaders */
-#if defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
+#if defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE)
#if (PLUGIN_BUFFER_SIZE > 0x14000) && (CONFIG_PLATFORM & (PLATFORM_NATIVE |PLATFORM_HOSTED)) && (defined(CPU_ARM) || defined(CPU_MIPS))
duke3d.c
quake.c
@@ -156,6 +161,11 @@ wormlet.c
announce_status.c
#endif
+/* can't build in the sim for some reason */
+#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR)
+multiboot_select.c
+#endif
+
/* Plugins needing the grayscale lib on low-depth LCDs */
fire.c
@@ -202,6 +212,7 @@ test_core_jpeg.c
test_disk.c
test_fps.c
test_gfx.c
+test_kbd.c
#if LCD_DEPTH < 4 && !defined(SIMULATOR)
test_scanrate.c
#endif
@@ -218,5 +229,6 @@ test_sampr.c
#ifdef HAVE_TOUCHSCREEN
test_touchscreen.c
#endif
+test_usb.c
test_viewports.c
#endif /* HAVE_TEST_PLUGINS */
diff --git a/apps/plugins/SOURCES.app_build b/apps/plugins/SOURCES.app_build
index ac9f01a896..50c79ec016 100644
--- a/apps/plugins/SOURCES.app_build
+++ b/apps/plugins/SOURCES.app_build
@@ -19,6 +19,7 @@ stopwatch.lua
#ifdef HAVE_TEST_PLUGINS /* enable in advanced build options */
+test_kbd.c
test_fps.c
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
test_boost.c
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index 8479e4b3dd..b9c18b5fd9 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -9,8 +9,7 @@ clock
#endif
/* color horizontal-stride LCDs */
-#if defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
+#if defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE)
xworld
/* for duke3d, wolf3d and quake */
@@ -32,6 +31,7 @@ rockboy
#if defined(HAVE_TAGCACHE)
pictureflow
+tagcache
#endif
#if PLUGIN_BUFFER_SIZE > 0x20000
diff --git a/apps/plugins/SUBDIRS.app_build b/apps/plugins/SUBDIRS.app_build
index 1fc283d6c4..fbf83f01da 100644
--- a/apps/plugins/SUBDIRS.app_build
+++ b/apps/plugins/SUBDIRS.app_build
@@ -17,6 +17,7 @@ reversi
#ifdef HAVE_TAGCACHE
pictureflow
+tagcache
#endif
/* For all the swcodec targets */
diff --git a/apps/plugins/alarmclock.c b/apps/plugins/alarmclock.c
index ecafceddc7..535097724f 100644
--- a/apps/plugins/alarmclock.c
+++ b/apps/plugins/alarmclock.c
@@ -136,14 +136,20 @@ enum plugin_status plugin_start(const void* parameter)
pause_audio();
while(!quit) {
+ FOR_NB_SCREENS(i) {
+ draw(rb->screens[i]);
+ }
button = get_button();
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ if (button == PLA_EXIT || button == PLA_CANCEL || button == PLA_UP)
+#else
if (button == PLA_EXIT || button == PLA_CANCEL)
+#endif
quit = true;
- FOR_NB_SCREENS(i) {
- draw(rb->screens[i]);
- }
if (waiting) {
if (rem_seconds() <= 0) {
quit = done = true;
diff --git a/apps/plugins/announce_status.c b/apps/plugins/announce_status.c
index 77e9015000..21518b4d68 100644
--- a/apps/plugins/announce_status.c
+++ b/apps/plugins/announce_status.c
@@ -97,6 +97,7 @@ enum plugin_status plugin_start(const void* parameter); /* entry */
static struct
{
bool exiting; /* signal to the thread that we want to exit */
+ bool resume;
unsigned int id; /* worker thread id */
struct event_queue queue; /* thread event queue */
long stack[THREAD_STACK_SIZE / sizeof(long)];
@@ -382,7 +383,7 @@ static int settings_menu(void)
break;
case 1:
rb->set_option(rb->str(LANG_ANNOUNCE_ON),
- &gAnnounce.announce_on, INT, announce_options, 2, NULL);
+ &gAnnounce.announce_on, RB_INT, announce_options, 2, NULL);
break;
case 2:
rb->set_int(rb->str(LANG_GROUPING), "", 1,
@@ -393,7 +394,7 @@ static int settings_menu(void)
break;
case 4: /*sep*/
continue;
- case 5:
+ case 5: /* quit the plugin */
return -1;
break;
case 6:
@@ -433,7 +434,8 @@ void thread(void)
in_usb = false;
/*fall through*/
case EV_STARTUP:
- rb->beep_play(1500, 100, 1000);
+ if (!gThread.resume)
+ rb->beep_play(1500, 100, 1000);
break;
case EV_EXIT:
return;
@@ -479,17 +481,45 @@ void thread_quit(void)
}
}
+static bool check_user_input(void)
+{
+ int i = 0;
+ rb->button_clear_queue();
+ if (rb->button_get_w_tmo(HZ) > BUTTON_NONE)
+ {
+ while ((rb->button_get(false) & BUTTON_REL) != BUTTON_REL)
+ {
+ if (i & 1)
+ rb->beep_play(800, 100, 1000 - i * (1000 / 15));
+
+ if (++i > 15)
+ {
+ return true;
+ }
+ rb->sleep(HZ / 5);
+ }
+ }
+ return false;
+}
+
/* callback to end the TSR plugin, called before a new one gets loaded */
-static bool exit_tsr(bool reenter)
+static int exit_tsr(bool reenter)
{
if (reenter)
{
rb->queue_post(&gThread.queue, EV_OTHINSTANCE, 0);
- return false; /* dont let it start again */
+
+ /* quit the plugin if user holds a button */
+ if (check_user_input() == true)
+ {
+ if (settings_menu() < 0)
+ return PLUGIN_TSR_TERMINATE; /*kill TSR dont let it start again */
+ }
+ return PLUGIN_TSR_CONTINUE; /* dont let new plugin start*/
}
thread_quit();
- return true;
+ return PLUGIN_TSR_SUSPEND;
}
@@ -497,58 +527,35 @@ static bool exit_tsr(bool reenter)
int plugin_main(const void* parameter)
{
- (void)parameter;
- bool settings = false;
- int i = 0;
-
rb->memset(&gThread, 0, sizeof(gThread));
gAnnounce.index = 0;
gAnnounce.timeout = 0;
-
- rb->splash(HZ / 2, "Announce Status");
-
- if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
- {
- /* If the loading failed, save a new config file */
- config_set_defaults();
- configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
-
- rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
- }
-
- if (gAnnounce.show_prompt)
+ /* Resume plugin ? */
+ if (parameter == rb->plugin_tsr)
{
- if (rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_PLAYING)
- {
- rb->talk_id(LANG_HOLD_FOR_SETTINGS, false);
- }
- rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
+ gThread.resume = true;
}
-
- rb->button_clear_queue();
- if (rb->button_get_w_tmo(HZ) > BUTTON_NONE)
+ else
{
- while ((rb->button_get(false) & BUTTON_REL) != BUTTON_REL)
+ rb->splash(HZ / 2, "Announce Status");
+ if (gAnnounce.show_prompt)
{
- if (i & 1)
- rb->beep_play(800, 100, 1000);
-
- if (++i > 15)
+ if (rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_PLAYING)
{
- settings = true;
- break;
+ rb->talk_id(LANG_HOLD_FOR_SETTINGS, false);
}
- rb->sleep(HZ / 5);
+ rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
}
- }
- if (settings)
- {
- rb->splash(100, ID2P(LANG_SETTINGS));
- int ret = settings_menu();
- if (ret < 0)
- return 0;
+
+ if (check_user_input() == true)
+ {
+ rb->splash(100, ID2P(LANG_SETTINGS));
+ int ret = settings_menu();
+ if (ret < 0)
+ return 0;
+ }
}
gAnnounce.timeout = *rb->current_tick;
@@ -571,6 +578,16 @@ enum plugin_status plugin_start(const void* parameter)
/* now go ahead and have fun! */
if (rb->usb_inserted() == true)
return PLUGIN_USB_CONNECTED;
+
+ config_set_defaults();
+ if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
+ {
+ /* If the loading failed, save a new config file */
+ config_set_defaults();
+ configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+ rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
+ }
+
int ret = plugin_main(parameter);
return (ret==0) ? PLUGIN_OK : PLUGIN_ERROR;
}
diff --git a/apps/plugins/battery_bench.c b/apps/plugins/battery_bench.c
index d8e29d73ca..f258492363 100644
--- a/apps/plugins/battery_bench.c
+++ b/apps/plugins/battery_bench.c
@@ -289,11 +289,11 @@ static struct event_queue thread_q SHAREDBSS_ATTR;
static bool in_usb_mode;
static unsigned int buf_idx;
-static bool exit_tsr(bool reenter)
+static int exit_tsr(bool reenter)
{
- bool is_exit;
+ int exit_status;
long button;
- (void)reenter;
+
rb->lcd_clear_display();
rb->lcd_puts_scroll(0, 0, "Batt.Bench is currently running.");
rb->lcd_puts_scroll(0, 1, "Press " BATTERY_OFF_TXT " to cancel the test");
@@ -313,16 +313,17 @@ static bool exit_tsr(bool reenter)
rb->thread_wait(gThread.id);
/* remove the thread's queue from the broadcast list */
rb->queue_delete(&thread_q);
- is_exit = true;
+ exit_status = (reenter ? PLUGIN_TSR_TERMINATE : PLUGIN_TSR_SUSPEND);
+
}
- else is_exit = false;
+ else exit_status = PLUGIN_TSR_CONTINUE;
break;
}
FOR_NB_SCREENS(idx)
rb->screens[idx]->scroll_stop();
- return is_exit;
+ return exit_status;
}
#define BIT_CHARGER 0x1
@@ -462,6 +463,7 @@ static void thread(void)
in_usb_mode = false;
break;
case SYS_POWEROFF:
+ case SYS_REBOOT:
exit_reason = "power off";
exit = true;
break;
@@ -501,14 +503,19 @@ static void put_centered_str(const char* str, plcdfunc putsxy, int lcd_width, in
enum plugin_status plugin_start(const void* parameter)
{
- (void)parameter;
int button, fd;
+ bool resume = false;
bool on = false;
start_tick = *rb->current_tick;
int i;
const char *msgs[] = { "Battery Benchmark","Check file", BATTERY_LOG,
"for more info", BATTERY_ON_TXT, BATTERY_OFF_TXT " - quit" };
- rb->lcd_clear_display();
+
+ if (parameter == rb->plugin_tsr)
+ {
+ resume = true;
+ on = true;
+ }
rb->lcd_clear_display();
rb->lcd_setfont(FONT_SYSFIXED);
@@ -528,36 +535,38 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_remote_putsxy,LCD_REMOTE_WIDTH,2);
rb->lcd_remote_update();
#endif
-
- do
+ if (!resume)
{
- button = rb->button_get(true);
- switch (button)
+ do
{
- case BATTERY_ON:
-#ifdef BATTERY_RC_ON
- case BATTERY_RC_ON:
-#endif
- on = true;
- break;
- case BATTERY_OFF:
-#ifdef BATTERY_RC_OFF
- case BATTERY_RC_OFF:
-#endif
- return PLUGIN_OK;
-
- default:
- if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
- return PLUGIN_USB_CONNECTED;
- }
- }while(!on);
-
+ button = rb->button_get(true);
+ switch (button)
+ {
+ case BATTERY_ON:
+ #ifdef BATTERY_RC_ON
+ case BATTERY_RC_ON:
+ #endif
+ on = true;
+ break;
+ case BATTERY_OFF:
+ #ifdef BATTERY_RC_OFF
+ case BATTERY_RC_OFF:
+ #endif
+ return PLUGIN_OK;
+
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ return PLUGIN_USB_CONNECTED;
+ }
+ }while(!on);
+ }
fd = rb->open(BATTERY_LOG, O_RDONLY);
if (fd < 0)
{
fd = rb->open(BATTERY_LOG, O_RDWR | O_CREAT, 0666);
if (fd >= 0)
{
+
rb->fdprintf(fd,
"# This plugin will log your battery performance in a\n"
"# file (%s) every minute.\n"
diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES
index f207f358b2..814845dc5b 100644
--- a/apps/plugins/bitmaps/native/SOURCES
+++ b/apps/plugins/bitmaps/native/SOURCES
@@ -548,8 +548,7 @@ pegbox_pieces.9x7x1.bmp
#endif
/* Puzzles */
-#if defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
+#if defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE)
puzzles_cursor.11x16x24.bmp
#endif
@@ -974,6 +973,45 @@ rockboxlogo.91x32x1.bmp
#endif
#endif
+/* Credits logo */
+#if (LCD_DEPTH == 1)
+#if (LCD_WIDTH == 160)
+creditslogo.160x53x1.bmp
+#elif (LCD_WIDTH == 128)
+creditslogo.128x42x1.bmp
+#else
+creditslogo.112x30x1.bmp
+#endif
+#elif (LCD_WIDTH == 96) && (LCD_DEPTH >= 16)
+creditslogo.96x30x16.bmp
+#elif (LCD_WIDTH == 128) && (LCD_DEPTH == 2)
+creditslogo.128x42x2.bmp
+#elif (LCD_WIDTH == 128) && (LCD_DEPTH >= 16)
+creditslogo.128x40x16.bmp
+#elif (LCD_WIDTH == 132) && (LCD_DEPTH >= 16)
+creditslogo.132x40x16.bmp
+#elif (LCD_WIDTH == 138) && (LCD_DEPTH >= 2)
+creditslogo.138x46x2.bmp
+#elif (LCD_WIDTH == 160) && (LCD_DEPTH == 2)
+creditslogo.160x53x2.bmp
+#elif (LCD_WIDTH == 320) && (LCD_DEPTH == 2)
+creditslogo.160x53x2.bmp
+#elif (LCD_WIDTH == 160) && (LCD_DEPTH >= 16)
+creditslogo.160x50x16.bmp
+#elif (LCD_WIDTH == 176) && (LCD_DEPTH >= 16)
+creditslogo.176x54x16.bmp
+#elif (LCD_WIDTH == 220) && (LCD_DEPTH >= 16)
+creditslogo.220x68x16.bmp
+#elif (LCD_WIDTH == 240) && (LCD_DEPTH >= 16)
+creditslogo.240x74x16.bmp
+#elif (LCD_WIDTH >= 320) && (LCD_WIDTH < 480) && (LCD_DEPTH >= 16)
+creditslogo.320x98x16.bmp
+#elif (LCD_WIDTH >= 480) && (LCD_WIDTH < 640) && (LCD_DEPTH >= 16)
+creditslogo.480x149x16.bmp
+#elif (LCD_WIDTH >= 640) && (LCD_DEPTH >= 16)
+creditslogo.640x198x16.bmp
+#endif
+
/* Pitch detector */
/* The following preprocessor condition must match the condition */
/* for pitch detector from plugins/SOURCES */
diff --git a/apps/plugins/bitmaps/native/creditslogo.112x30x1.bmp b/apps/plugins/bitmaps/native/creditslogo.112x30x1.bmp
new file mode 100644
index 0000000000..c414ffb27b
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.112x30x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.128x40x16.bmp b/apps/plugins/bitmaps/native/creditslogo.128x40x16.bmp
new file mode 100644
index 0000000000..228dbddbd5
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.128x40x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.128x42x1.bmp b/apps/plugins/bitmaps/native/creditslogo.128x42x1.bmp
new file mode 100644
index 0000000000..27d8e5fc26
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.128x42x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.128x42x2.bmp b/apps/plugins/bitmaps/native/creditslogo.128x42x2.bmp
new file mode 100644
index 0000000000..fa12aa4bd7
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.128x42x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.132x40x16.bmp b/apps/plugins/bitmaps/native/creditslogo.132x40x16.bmp
new file mode 100644
index 0000000000..865a8ed59d
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.132x40x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.138x46x2.bmp b/apps/plugins/bitmaps/native/creditslogo.138x46x2.bmp
new file mode 100644
index 0000000000..4b5d7beb16
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.138x46x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.160x50x16.bmp b/apps/plugins/bitmaps/native/creditslogo.160x50x16.bmp
new file mode 100644
index 0000000000..59e472def1
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.160x50x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.160x53x1.bmp b/apps/plugins/bitmaps/native/creditslogo.160x53x1.bmp
new file mode 100644
index 0000000000..7e34b23c6b
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.160x53x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.160x53x2.bmp b/apps/plugins/bitmaps/native/creditslogo.160x53x2.bmp
new file mode 100644
index 0000000000..240cf10686
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.160x53x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.176x54x16.bmp b/apps/plugins/bitmaps/native/creditslogo.176x54x16.bmp
new file mode 100644
index 0000000000..bf43a65e42
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.176x54x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.220x68x16.bmp b/apps/plugins/bitmaps/native/creditslogo.220x68x16.bmp
new file mode 100644
index 0000000000..1dc68a09f2
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.220x68x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.240x74x16.bmp b/apps/plugins/bitmaps/native/creditslogo.240x74x16.bmp
new file mode 100644
index 0000000000..fd335b2fb8
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.240x74x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.320x98x16.bmp b/apps/plugins/bitmaps/native/creditslogo.320x98x16.bmp
new file mode 100644
index 0000000000..26dcac89c3
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.320x98x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.480x149x16.bmp b/apps/plugins/bitmaps/native/creditslogo.480x149x16.bmp
new file mode 100644
index 0000000000..c73d9374e4
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.480x149x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.640x198x16.bmp b/apps/plugins/bitmaps/native/creditslogo.640x198x16.bmp
new file mode 100644
index 0000000000..ec6a16da5d
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.640x198x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/creditslogo.96x30x16.bmp b/apps/plugins/bitmaps/native/creditslogo.96x30x16.bmp
new file mode 100644
index 0000000000..e032ebae34
--- /dev/null
+++ b/apps/plugins/bitmaps/native/creditslogo.96x30x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/remote_native/SOURCES b/apps/plugins/bitmaps/remote_native/SOURCES
index b3cc2157ff..2142674c67 100644
--- a/apps/plugins/bitmaps/remote_native/SOURCES
+++ b/apps/plugins/bitmaps/remote_native/SOURCES
@@ -22,3 +22,10 @@ remote_rockboxlogo.91x32x1.bmp
#elif (LCD_REMOTE_DEPTH == 2)
remote_rockboxlogo.91x32x2.bmp
#endif
+
+/* Credits logo */
+#if (LCD_REMOTE_DEPTH == 1)
+remote_creditslogo.128x42x1.bmp
+#elif (LCD_REMOTE_DEPTH == 2)
+remote_creditslogo.128x42x2.bmp
+#endif
diff --git a/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x1.bmp b/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x1.bmp
new file mode 100644
index 0000000000..27d8e5fc26
--- /dev/null
+++ b/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x2.bmp b/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x2.bmp
new file mode 100644
index 0000000000..fa12aa4bd7
--- /dev/null
+++ b/apps/plugins/bitmaps/remote_native/remote_creditslogo.128x42x2.bmp
Binary files differ
diff --git a/apps/plugins/bounce.c b/apps/plugins/bounce.c
index e6f29817d6..a42a0af6ba 100644
--- a/apps/plugins/bounce.c
+++ b/apps/plugins/bounce.c
@@ -34,10 +34,6 @@
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
/* We set button maping with PLA */
-#define BOUNCE_UP PLA_UP
-#define BOUNCE_UP_REPEAT PLA_UP_REPEAT
-#define BOUNCE_DOWN PLA_DOWN
-#define BOUNCE_DOWN_REPEAT PLA_DOWN_REPEAT
#ifdef HAVE_SCROLLWHEEL
#define BOUNCE_LEFT PLA_SCROLL_BACK
@@ -52,7 +48,22 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#endif
#define BOUNCE_QUIT PLA_EXIT
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define BOUNCE_QUIT2 PLA_UP
+#define BOUNCE_DOWN PLA_LEFT
+#define BOUNCE_DOWN_REPEAT PLA_LEFT_REPEAT
+#define BOUNCE_UP PLA_RIGHT
+#define BOUNCE_UP_REPEAT PLA_RIGHT_REPEAT
+#else
#define BOUNCE_QUIT2 PLA_CANCEL
+#define BOUNCE_DOWN PLA_DOWN
+#define BOUNCE_DOWN_REPEAT PLA_DOWN_REPEAT
+#define BOUNCE_UP PLA_UP
+#define BOUNCE_UP_REPEAT PLA_UP_REPEAT
+#endif
#define BOUNCE_MODE PLA_SELECT
#define LETTER_WIDTH 11
@@ -471,6 +482,10 @@ enum plugin_status plugin_start(const void* parameter)
#if (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD) || \
(CONFIG_KEYPAD == SAMSUNG_YH820_PAD)
"[Rew] to stop";
+#elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ "[Menu] to stop";
#else
"[Off] to stop";
#endif
diff --git a/apps/plugins/brickmania.c b/apps/plugins/brickmania.c
index 4983d5a417..a26ff8edd8 100644
--- a/apps/plugins/brickmania.c
+++ b/apps/plugins/brickmania.c
@@ -111,8 +111,7 @@
#elif CONFIG_KEYPAD == SANSA_C200_PAD || \
CONFIG_KEYPAD == SANSA_CLIP_PAD || \
-CONFIG_KEYPAD == SANSA_M200_PAD || \
-CONFIG_KEYPAD == SANSA_CONNECT_PAD
+CONFIG_KEYPAD == SANSA_M200_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
@@ -122,6 +121,18 @@ CONFIG_KEYPAD == SANSA_CONNECT_PAD
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
+#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
+#define QUIT BUTTON_POWER
+#define LEFT BUTTON_LEFT
+#define RIGHT BUTTON_RIGHT
+#define SELECT BUTTON_SELECT
+#define UP BUTTON_UP
+#define DOWN BUTTON_DOWN
+
+#define SCROLL_FWD(x) ((x) & BUTTON_SCROLL_FWD)
+#define SCROLL_BACK(x) ((x) & BUTTON_SCROLL_BACK)
+
+
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
@@ -1554,7 +1565,7 @@ static int brickmania_menu(void)
brickmania_init_game(true);
return 0;
case 2:
- rb->set_option("Difficulty", &difficulty, INT,
+ rb->set_option("Difficulty", &difficulty, RB_INT,
options, 2, NULL);
break;
case 3:
@@ -2522,10 +2533,10 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* now go ahead and have fun! */
rb->srand( *rb->current_tick );
brickmania_loadgame();
@@ -2554,9 +2565,9 @@ enum plugin_status plugin_start(const void* parameter)
configfile_save(CONFIG_FILE_NAME,config,1,0);
/* Restore user's original backlight setting */
rb->lcd_setfont(FONT_UI);
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/bubbles.c b/apps/plugins/bubbles.c
index 88d7228d72..a64093492f 100644
--- a/apps/plugins/bubbles.c
+++ b/apps/plugins/bubbles.c
@@ -79,7 +79,6 @@ enum {
#define ANGLE_STEP_REP 6
#define BUBBLES_QUIT1 PLA_EXIT
-#define BUBBLES_QUIT2 PLA_CANCEL
/* these are better off shooting with up */
#if (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) \
@@ -92,10 +91,19 @@ enum {
#define BUBBLES_FIRE PLA_UP
#define BUBBLES_FIRE_REPEAT PLA_UP_REPEAT
#define BUBBLES_PAUSE PLA_SELECT
+#define BUBBLES_QUIT2 PLA_CANCEL
+#elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define BUBBLES_FIRE PLA_SELECT
+#define BUBBLES_FIRE_REPEAT PLA_SELECT_REPEAT
+#define BUBBLES_PAUSE PLA_DOWN
+#define BUBBLES_QUIT2 PLA_UP
#else
#define BUBBLES_FIRE PLA_SELECT
#define BUBBLES_FIRE_REPEAT PLA_SELECT_REPEAT
#define BUBBLES_PAUSE PLA_UP
+#define BUBBLES_QUIT2 PLA_CANCEL
#endif
/* external bitmaps */
diff --git a/apps/plugins/calculator.c b/apps/plugins/calculator.c
index 5bab15d7ed..743192dab6 100644
--- a/apps/plugins/calculator.c
+++ b/apps/plugins/calculator.c
@@ -732,12 +732,15 @@ static void oneOperand(void);
static void drawLines(void);
static void drawButtons(int group);
+#ifndef _WIN32
double strtod(const char *nptr, char **endptr);
+#endif
long long atoll(const char *nptr);
/* -----------------------------------------------------------------------
Standard library function
----------------------------------------------------------------------- */
+#ifndef _WIN32
double strtod(const char *nptr, char **endptr)
{
double out;
@@ -774,6 +777,7 @@ double strtod(const char *nptr, char **endptr)
*endptr=(char *) nptr;
return out;
}
+#endif
// WARNING Unsafe: Use strtoll instead
long long atoll(const char *nptr)
diff --git a/apps/plugins/calendar.c b/apps/plugins/calendar.c
index 3299a81273..6d1091159f 100644
--- a/apps/plugins/calendar.c
+++ b/apps/plugins/calendar.c
@@ -39,14 +39,12 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define CALENDAR_QUIT (BUTTON_SELECT|BUTTON_MENU)
+#define CALENDAR_QUIT (BUTTON_MENU|BUTTON_REL)
#define CALENDAR_SELECT (BUTTON_SELECT|BUTTON_REL)
#define CALENDAR_NEXT_WEEK BUTTON_SCROLL_FWD
#define CALENDAR_PREV_WEEK BUTTON_SCROLL_BACK
#define CALENDAR_NEXT_DAY BUTTON_RIGHT
#define CALENDAR_PREV_DAY BUTTON_LEFT
-#define CALENDAR_NEXT_MONTH BUTTON_PLAY
-#define CALENDAR_PREV_MONTH (BUTTON_MENU|BUTTON_REL)
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
#define CALENDAR_QUIT BUTTON_POWER
@@ -913,7 +911,7 @@ static bool edit_memo(int change, struct shown *shown)
case 6: /* weekday */
rb->set_option("First Day of Week", &info.first_wday,
- INT, modes, 7, NULL);
+ RB_INT, modes, 7, NULL);
break;
case 7: /* playback control */
@@ -964,7 +962,7 @@ static bool view_events(int selected, struct shown *shown)
while (!exit)
{
button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- rb->gui_synclist_do_button(&gui_memos, &button, LIST_WRAP_UNLESS_HELD);
+ rb->gui_synclist_do_button(&gui_memos, &button);
switch (button)
{
@@ -1095,17 +1093,18 @@ enum plugin_status plugin_start(const void* parameter)
case CALENDAR_QUIT:
exit = true;
break;
-
+#ifdef CALENDAR_NEXT_MONTH
case CALENDAR_NEXT_MONTH:
case CALENDAR_NEXT_MONTH | BUTTON_REPEAT:
next_month(&shown, 0);
break;
-
+#endif
+#ifdef CALENDAR_PREV_MONTH
case CALENDAR_PREV_MONTH:
case CALENDAR_PREV_MONTH | BUTTON_REPEAT:
prev_month(&shown, 0);
break;
-
+#endif
case CALENDAR_NEXT_WEEK:
case CALENDAR_NEXT_WEEK | BUTTON_REPEAT:
next_day(&shown, 7);
diff --git a/apps/plugins/chessbox/chessbox.c b/apps/plugins/chessbox/chessbox.c
index 13df4f177e..089cf7c400 100644
--- a/apps/plugins/chessbox/chessbox.c
+++ b/apps/plugins/chessbox/chessbox.c
@@ -222,6 +222,7 @@ static void cb_wt_callback ( void ) {
button = rb->button_get(false);
switch (button) {
case SYS_POWEROFF:
+ case SYS_REBOOT:
cb_sysevent = button;
#ifdef CB_RC_QUIT
case CB_RC_QUIT:
@@ -585,6 +586,7 @@ static struct cb_command cb_get_viewer_command (void) {
button = rb->button_get(true);
switch (button) {
case SYS_POWEROFF:
+ case SYS_REBOOT:
cb_sysevent = button;
#ifdef CB_RC_QUIT
case CB_RC_QUIT:
@@ -848,6 +850,7 @@ static struct cb_command cb_getcommand (void) {
button = rb->button_get(true);
switch (button) {
case SYS_POWEROFF:
+ case SYS_REBOOT:
cb_sysevent = button;
#ifdef CB_RC_QUIT
case CB_RC_QUIT:
diff --git a/apps/plugins/chessbox/chessbox_pgn.c b/apps/plugins/chessbox/chessbox_pgn.c
index 0d9da441b1..bb35bec726 100644
--- a/apps/plugins/chessbox/chessbox_pgn.c
+++ b/apps/plugins/chessbox/chessbox_pgn.c
@@ -678,7 +678,6 @@ struct pgn_game_node* pgn_show_game_list(struct pgn_game_node* first_game){
if (rb->global_settings->talk_menu)
rb->gui_synclist_set_voice_callback(&games_list, speak_game_selection);
rb->gui_synclist_set_nb_items(&games_list, i);
- rb->gui_synclist_limit_scroll(&games_list, true);
rb->gui_synclist_select_item(&games_list, 0);
rb->gui_synclist_draw(&games_list);
@@ -687,9 +686,8 @@ struct pgn_game_node* pgn_show_game_list(struct pgn_game_node* first_game){
while (true) {
curr_selection = rb->gui_synclist_get_sel_pos(&games_list);
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&games_list,&button,LIST_WRAP_OFF)){
+ if (rb->gui_synclist_do_button(&games_list, &button))
continue;
- }
switch (button) {
case ACTION_STD_OK:
return get_game_info(curr_selection, first_game);
diff --git a/apps/plugins/chessbox/chessbox_pgn.h b/apps/plugins/chessbox/chessbox_pgn.h
index 9d4f369ecc..7d0cb795cf 100644
--- a/apps/plugins/chessbox/chessbox_pgn.h
+++ b/apps/plugins/chessbox/chessbox_pgn.h
@@ -33,7 +33,7 @@
#define CB_PLAY (BUTTON_SELECT | BUTTON_PLAY)
#define CB_LEVEL (BUTTON_SELECT | BUTTON_RIGHT)
#define CB_RESTART (BUTTON_SELECT | BUTTON_LEFT)
-#define CB_MENU (BUTTON_SELECT | BUTTON_MENU)
+#define CB_MENU (BUTTON_SELECT | BUTTON_REPEAT)
#define CB_SCROLL_UP (BUTTON_SCROLL_FWD|BUTTON_REPEAT)
#define CB_SCROLL_DOWN (BUTTON_SCROLL_BACK|BUTTON_REPEAT)
#define CB_SCROLL_LEFT (BUTTON_LEFT|BUTTON_REPEAT)
diff --git a/apps/plugins/chopper.c b/apps/plugins/chopper.c
index 70763a1b67..78cc292147 100644
--- a/apps/plugins/chopper.c
+++ b/apps/plugins/chopper.c
@@ -523,19 +523,18 @@ static void chopAddBlock(int x,int y,int sx,int sy, int indexOverride)
static void chopAddParticle(int x,int y,int sx,int sy)
{
- int i=0;
-
- while(mParticles[i].bIsActive && i < NUMBER_OF_PARTICLES)
- i++;
-
- if(i==NUMBER_OF_PARTICLES)
- return;
-
- mParticles[i].bIsActive = 1;
- mParticles[i].iWorldX = x;
- mParticles[i].iWorldY = y;
- mParticles[i].iSpeedX = sx;
- mParticles[i].iSpeedY = sy;
+ for(int i = 0; i < NUMBER_OF_PARTICLES; ++i)
+ {
+ if(!mParticles[i].bIsActive)
+ {
+ mParticles[i].bIsActive = 1;
+ mParticles[i].iWorldX = x;
+ mParticles[i].iWorldY = y;
+ mParticles[i].iSpeedX = sx;
+ mParticles[i].iSpeedY = sy;
+ return;
+ }
+ }
}
static void chopGenerateBlockIfNeeded(void)
@@ -785,7 +784,7 @@ static int chopMenu(int menunum)
res = -1;
break;
case 2:
- rb->set_option("Level", &iLevelMode, INT, levels, 2, NULL);
+ rb->set_option("Level", &iLevelMode, RB_INT, levels, 2, NULL);
break;
case 3:
playback_control(NULL);
@@ -1084,10 +1083,10 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_set_foreground(LCD_WHITE);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
rb->srand( *rb->current_tick );
@@ -1099,10 +1098,10 @@ enum plugin_status plugin_start(const void* parameter)
configfile_save(CFG_FILE, config, 1, 0);
rb->lcd_setfont(FONT_UI);
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return ret;
}
diff --git a/apps/plugins/clix.c b/apps/plugins/clix.c
index 9cd66a8034..06813f3a4d 100644
--- a/apps/plugins/clix.c
+++ b/apps/plugins/clix.c
@@ -66,9 +66,7 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define CLIX_BUTTON_QUIT (BUTTON_SELECT | BUTTON_MENU)
-#define CLIX_BUTTON_UP BUTTON_MENU
-#define CLIX_BUTTON_DOWN BUTTON_PLAY
+#define CLIX_BUTTON_QUIT BUTTON_MENU
#define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
#define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
#define CLIX_BUTTON_CLICK BUTTON_SELECT
@@ -921,8 +919,10 @@ static int clix_handle_game(struct clix_game_state_t* state)
case CLIX_BUTTON_SCROLL_BACK:
case CLIX_BUTTON_SCROLL_BACK|BUTTON_REPEAT:
#endif
+#ifdef CLIX_BUTTON_UP
case CLIX_BUTTON_UP:
case CLIX_BUTTON_UP|BUTTON_REPEAT:
+#endif
if( state->y == 0 ||
state->board[ XYPOS( state->x, state->y - 1)] ==
CC_BLACK
@@ -938,8 +938,10 @@ static int clix_handle_game(struct clix_game_state_t* state)
case CLIX_BUTTON_SCROLL_FWD:
case CLIX_BUTTON_SCROLL_FWD|BUTTON_REPEAT:
#endif
+#ifdef CLIX_BUTTON_DOWN
case CLIX_BUTTON_DOWN:
case CLIX_BUTTON_DOWN|BUTTON_REPEAT:
+#endif
if( state->y == (BOARD_HEIGHT - 1))
state->y = 0;
else
diff --git a/apps/plugins/clock/clock.c b/apps/plugins/clock/clock.c
index d287c75598..c61b466aba 100644
--- a/apps/plugins/clock/clock.c
+++ b/apps/plugins/clock/clock.c
@@ -44,13 +44,19 @@ const struct button_mapping* plugin_contexts[]={
#define ACTION_COUNTER_TOGGLE PLA_SELECT
#define ACTION_COUNTER_RESET PLA_SELECT_REPEAT
-#define ACTION_MENU PLA_CANCEL
#define ACTION_MODE_NEXT PLA_RIGHT
#define ACTION_MODE_NEXT_REPEAT PLA_RIGHT_REPEAT
#define ACTION_MODE_PREV PLA_LEFT
#define ACTION_MODE_PREV_REPEAT PLA_LEFT_REPEAT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define ACTION_MENU PLA_UP
+#else
+#define ACTION_MENU PLA_CANCEL
#define ACTION_SKIN_NEXT PLA_UP
#define ACTION_SKIN_NEXT_REPEAT PLA_UP_REPEAT
+#endif
#define ACTION_SKIN_PREV PLA_DOWN
#define ACTION_SKIN_PREV_REPEAT PLA_DOWN_REPEAT
@@ -165,10 +171,12 @@ enum plugin_status plugin_start(const void* parameter){
case ACTION_SKIN_PREV:
clock_settings_skin_next(&clock_settings);
break;
+#if defined(ACTION_SKIN_NEXT) && defined(ACTION_SKIN_NEXT_REPEAT)
case ACTION_SKIN_NEXT_REPEAT:
case ACTION_SKIN_NEXT:
clock_settings_skin_previous(&clock_settings);
break;
+#endif
case ACTION_MENU:
clock_draw_restore_colors();
exit_clock=main_menu();
diff --git a/apps/plugins/clock/clock_menu.c b/apps/plugins/clock/clock_menu.c
index a597664f49..8b7a2c82be 100644
--- a/apps/plugins/clock/clock_menu.c
+++ b/apps/plugins/clock/clock_menu.c
@@ -81,17 +81,17 @@ static void menu_analog_settings(void)
switch(result){
case 0:
rb->set_option("Show Date", &clock_settings.analog.show_date,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
case 1:
rb->set_option("Show Second Hand",
&clock_settings.analog.show_seconds,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
case 2:
rb->set_option("Show Border",
&clock_settings.analog.show_border,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
}
}
@@ -112,12 +112,12 @@ static void menu_digital_settings(void){
case 0:
rb->set_option("Show Seconds",
&clock_settings.digital.show_seconds,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
case 1:
rb->set_option("Blinking Colon",
&clock_settings.digital.blinkcolon,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
}
}
@@ -129,7 +129,7 @@ static void menu_digital_settings(void){
static void confirm_reset(void){
int result=0;
- rb->set_option("Reset all settings?", &result, INT, noyes_text, 2, NULL);
+ rb->set_option("Reset all settings?", &result, RB_INT, noyes_text, 2, NULL);
if(result == 1){ /* reset! */
clock_settings_reset(&clock_settings);
@@ -157,16 +157,16 @@ static void menu_general_settings(void){
case 0:
rb->set_option("Hour format",
&clock_settings.general.hour_format,
- INT, hour_format_text, 2, NULL);
+ RB_INT, hour_format_text, 2, NULL);
break;
case 1:
rb->set_option("Date format",
&clock_settings.general.date_format,
- INT, date_format_text, 4, NULL);
+ RB_INT, date_format_text, 4, NULL);
break;
case 2:
rb->set_option("Show Counter", &clock_settings.general.show_counter,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
break;
case 3:
confirm_reset();
@@ -180,7 +180,7 @@ static void menu_general_settings(void){
case 5:
rb->set_option("Save On Exit",
&clock_settings.general.save_settings,
- BOOL, noyes_text, 2, NULL);
+ RB_BOOL, noyes_text, 2, NULL);
/* if we no longer save on exit,
we better save now to remember that */
@@ -190,14 +190,14 @@ static void menu_general_settings(void){
case 6:
rb->set_option("Backlight Settings",
&clock_settings.general.backlight,
- INT, backlight_settings_text, 3, NULL);
+ RB_INT, backlight_settings_text, 3, NULL);
apply_backlight_setting(clock_settings.general.backlight);
break;
case 7:
rb->set_option("Idle Poweroff (temporary)",
&clock_settings.general.idle_poweroff,
- BOOL, idle_poweroff_text, 2, NULL);
+ RB_BOOL, idle_poweroff_text, 2, NULL);
break;
}
}
diff --git a/apps/plugins/codebuster.c b/apps/plugins/codebuster.c
index 956991575d..401ce6085f 100644
--- a/apps/plugins/codebuster.c
+++ b/apps/plugins/codebuster.c
@@ -438,6 +438,13 @@ enum plugin_status plugin_start(const void* parameter) {
if (button == PLA_SELECT)
break;
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ if (button == PLA_UP) /* Menu button */
+ button = PLA_CANCEL;
+#endif
+
switch (button) {
/* Exit */
diff --git a/apps/plugins/credits.c b/apps/plugins/credits.c
index 9e43aab2a7..f4716651fd 100644
--- a/apps/plugins/credits.c
+++ b/apps/plugins/credits.c
@@ -21,7 +21,19 @@
#include "plugin.h"
#include "lib/helper.h"
-
+#ifdef HAVE_REMOTE_LCD
+#define REMOTE_WIDTH LCD_REMOTE_WIDTH
+#define REMOTE_HEIGHT LCD_REMOTE_HEIGHT
+#include "pluginbitmaps/remote_creditslogo.h"
+#define REMOTE_LOGO_WIDTH BMPWIDTH_remote_creditslogo
+#define REMOTE_LOGO_HEIGHT BMPHEIGHT_remote_creditslogo
+#define REMOTE_LOGO (const fb_remote_data*)remote_creditslogo
+#endif /* HAVE_REMOTE_LCD */
+
+#define LOGO (const fb_data*)creditslogo
+#include "pluginbitmaps/creditslogo.h"
+#define LOGO_WIDTH BMPWIDTH_creditslogo
+#define LOGO_HEIGHT BMPHEIGHT_creditslogo
static const char* const credits[] = {
#include "credits.raw" /* generated list of names from docs/CREDITS */
@@ -299,29 +311,78 @@ static void roll_credits(void)
}
}
+int show_logo(void)
+{
+ unsigned char version[32];
+ int font_h, ver_w;
+ rb->snprintf(version, sizeof(version), "Ver. %s", rb->rbversion);
+ ver_w = rb->font_getstringsize(version, NULL, &font_h, FONT_SYSFIXED);
+ rb->lcd_clear_display();
+ rb->lcd_setfont(FONT_SYSFIXED);
+#if defined(SANSA_CLIP) || defined(SANSA_CLIPV2) || defined(SANSA_CLIPPLUS)
+ /* display the logo in the blue area of the screen (bottom 48 pixels) */
+ if (ver_w > LCD_WIDTH)
+ rb->lcd_puts_scroll(0, 0, version);
+ else
+ rb->lcd_putsxy((LCD_WIDTH/2) - (ver_w/2), 0, version);
+ rb->lcd_bitmap(LOGO, (LCD_WIDTH - LOGO_WIDTH) / 2, 16,
+ LOGO_WIDTH, LOGO_HEIGHT);
+#else
+ rb->lcd_bitmap(LOGO, (LCD_WIDTH - LOGO_WIDTH) / 2, 10,
+ LOGO_WIDTH, LOGO_HEIGHT);
+ if (ver_w > LCD_WIDTH)
+ rb->lcd_puts_scroll(0, (LCD_HEIGHT-font_h) / font_h, version);
+ else
+ rb->lcd_putsxy((LCD_WIDTH/2) - (ver_w/2), LCD_HEIGHT-font_h, version);
+#endif
+ rb->lcd_setfont(FONT_UI);
+ rb->lcd_update();
+#ifdef HAVE_REMOTE_LCD
+ rb->lcd_remote_clear_display();
+ rb->lcd_remote_bitmap(REMOTE_LOGO, 0, 10,
+ REMOTE_LOGO_WIDTH, REMOTE_LOGO_HEIGHT);
+ rb->lcd_remote_setfont(FONT_SYSFIXED);
+ if (ver_w > LCD_REMOTE_WIDTH)
+ rb->lcd_remote_puts_scroll(0, (LCD_REMOTE_HEIGHT-font_h) / font_h, version);
+ else
+ rb->lcd_remote_putsxy((LCD_REMOTE_WIDTH/2) - (ver_w/2),
+ LCD_REMOTE_HEIGHT-font_h, version);
+ rb->lcd_remote_setfont(FONT_UI);
+ rb->lcd_remote_update();
+#endif
+
+ return 0;
+}
+
enum plugin_status plugin_start(const void* parameter)
{
(void)parameter;
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
-#if LCD_DEPTH>=16
+#if LCD_DEPTH >= 16
rb->lcd_set_foreground (LCD_WHITE);
rb->lcd_set_background (LCD_BLACK);
#endif
- rb->show_logo();
- /* Show the logo for about 3 secs allowing the user to stop */
- if(!rb->action_userabort(3*HZ))
+ show_logo();
+
+ /* Show the logo for about 5 secs allowing the user to stop */
+ if(!rb->action_userabort(5*HZ))
+ {
+ rb->lcd_scroll_stop();
roll_credits();
+ }
+
+#ifdef HAVE_REMOTE_LCD
+ rb->lcd_remote_scroll_stop();
+#endif
-#ifdef HAVE_BACKLIGHT
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/cube.c b/apps/plugins/cube.c
index dfd7df8951..43318f5aee 100644
--- a/apps/plugins/cube.c
+++ b/apps/plugins/cube.c
@@ -52,12 +52,14 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define CUBE_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define CUBE_QUIT_PRE BUTTON_MENU
+#define CUBE_QUIT (BUTTON_MENU | BUTTON_REL)
#define CUBE_NEXT BUTTON_RIGHT
#define CUBE_PREV BUTTON_LEFT
#define CUBE_INC BUTTON_SCROLL_FWD
#define CUBE_DEC BUTTON_SCROLL_BACK
-#define CUBE_MODE BUTTON_MENU
+#define CUBE_MODE_PRE BUTTON_MENU
+#define CUBE_MODE (BUTTON_MENU | BUTTON_REPEAT)
#define CUBE_PAUSE BUTTON_PLAY
#define CUBE_HIGHSPEED_PRE BUTTON_SELECT
#define CUBE_HIGHSPEED (BUTTON_SELECT | BUTTON_REL)
@@ -729,6 +731,7 @@ enum plugin_status plugin_start(const void* parameter)
int button;
#if defined(CUBE_MODE_PRE) || \
+ defined(CUBE_QUIT_PRE) || \
defined(CUBE_PAUSE_PRE) || \
defined(CUBE_HIGHSPEED_PRE)
int lastbutton = BUTTON_NONE;
@@ -903,6 +906,10 @@ enum plugin_status plugin_start(const void* parameter)
case CUBE_RC_QUIT:
#endif
case CUBE_QUIT:
+#ifdef CUBE_QUIT_PRE
+ if (lastbutton != CUBE_QUIT_PRE)
+ break;
+#endif
quit = true;
break;
@@ -911,6 +918,7 @@ enum plugin_status plugin_start(const void* parameter)
break;
}
#if defined(CUBE_MODE_PRE) || \
+ defined(CUBE_QUIT_PRE) || \
defined(CUBE_PAUSE_PRE) || \
defined(CUBE_HIGHSPEED_PRE)
if (button != BUTTON_NONE)
diff --git a/apps/plugins/dart_scorer.c b/apps/plugins/dart_scorer.c
new file mode 100644
index 0000000000..1e8dd8f37b
--- /dev/null
+++ b/apps/plugins/dart_scorer.c
@@ -0,0 +1,601 @@
+#include "plugin.h"
+#include "lib/display_text.h"
+#include "lib/helper.h"
+#include "lib/playback_control.h"
+#include "lib/pluginlib_exit.h"
+#include "lib/pluginlib_actions.h"
+
+#define BUTTON_ROWS 6
+#define BUTTON_COLS 5
+
+#define REC_HEIGHT (int)(LCD_HEIGHT / (BUTTON_ROWS + 1))
+#define REC_WIDTH (int)(LCD_WIDTH / BUTTON_COLS)
+
+#define Y_7_POS (LCD_HEIGHT) /* Leave room for the border */
+#define Y_6_POS (Y_7_POS - REC_HEIGHT) /* y6 = 63 */
+#define Y_5_POS (Y_6_POS - REC_HEIGHT) /* y5 = 53 */
+#define Y_4_POS (Y_5_POS - REC_HEIGHT) /* y4 = 43 */
+#define Y_3_POS (Y_4_POS - REC_HEIGHT) /* y3 = 33 */
+#define Y_2_POS (Y_3_POS - REC_HEIGHT) /* y2 = 23 */
+#define Y_1_POS (Y_2_POS - REC_HEIGHT) /* y1 = 13 */
+#define Y_0_POS 0 /* y0 = 0 */
+
+#define X_0_POS 0 /* x0 = 0 */
+#define X_1_POS (X_0_POS + REC_WIDTH) /* x1 = 22 */
+#define X_2_POS (X_1_POS + REC_WIDTH) /* x2 = 44 */
+#define X_3_POS (X_2_POS + REC_WIDTH) /* x3 = 66 */
+#define X_4_POS (X_3_POS + REC_WIDTH) /* x4 = 88 */
+#define X_5_POS (X_4_POS + REC_WIDTH) /* x5 = 110, column 111 left blank */
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define DARTS_QUIT PLA_SELECT_REPEAT
+#else
+#define DARTS_QUIT PLA_CANCEL
+#endif
+#define DARTS_SELECT PLA_SELECT
+#define DARTS_RIGHT PLA_RIGHT
+#define DARTS_LEFT PLA_LEFT
+#define DARTS_UP PLA_UP
+#define DARTS_DOWN PLA_DOWN
+#define DARTS_RRIGHT PLA_RIGHT_REPEAT
+#define DARTS_RLEFT PLA_LEFT_REPEAT
+#define DARTS_RUP PLA_UP_REPEAT
+#define DARTS_RDOWN PLA_DOWN_REPEAT
+
+#define RESUME_FILE PLUGIN_GAMES_DATA_DIR "/dart_scorer.save"
+/* leave first line blank on bitmap display, for pause icon */
+#define FIRST_LINE 1
+
+#define NUM_PLAYERS 2
+#define MAX_UNDO 100
+
+static const struct button_mapping *plugin_contexts[] = {pla_main_ctx};
+
+/* game data structures */
+enum game_mode
+{
+ five,
+ three
+};
+static struct settings_struct
+{
+ enum game_mode mode;
+ int scores[2];
+ bool turn;
+ int throws;
+ int history[MAX_UNDO];
+ int history_ptr;
+} settings;
+
+/* temporary data */
+static bool loaded = false; /* has a save been loaded? */
+int btn_row, btn_col; /* current position index for button */
+int prev_btn_row, prev_btn_col; /* previous cursor position */
+unsigned char *buttonChar[6][5] = {
+ {"", "Single", "Double", "Triple", ""},
+ {"1", "2", "3", "4", "5"},
+ {"6", "7", "8", "9", "10"},
+ {"11", "12", "13", "14", "15"},
+ {"16", "17", "18", "19", "20"},
+ {"", "Missed", "Bull", "Undo", ""}};
+int modifier;
+
+static int do_dart_scorer_pause_menu(void);
+static void drawButtons(void);
+
+/* First, increases *dimen1 by dimen1_delta modulo dimen1_modulo.
+ If dimen1 wraps, increases *dimen2 by dimen2_delta modulo dimen2_modulo.
+*/
+static void move_with_wrap_and_shift(
+ int *dimen1, int dimen1_delta, int dimen1_modulo,
+ int *dimen2, int dimen2_delta, int dimen2_modulo)
+{
+ bool wrapped = false;
+
+ *dimen1 += dimen1_delta;
+ if (*dimen1 < 0)
+ {
+ *dimen1 = dimen1_modulo - 1;
+ wrapped = true;
+ }
+ else if (*dimen1 >= dimen1_modulo)
+ {
+ *dimen1 = 0;
+ wrapped = true;
+ }
+
+ if (wrapped)
+ {
+ /* Make the dividend always positive to be sure about the result.
+ Adding dimen2_modulo does not change it since we do it modulo. */
+ *dimen2 = (*dimen2 + dimen2_modulo + dimen2_delta) % dimen2_modulo;
+ }
+}
+
+static void drawButtons()
+{
+ int i, j, w, h;
+ for (i = 0; i <= 5; i++)
+ {
+ for (j = 0; j <= 4; j++)
+ {
+ unsigned char button_text[16];
+ char *selected_prefix = (i == 0 && modifier > 0 && j == modifier) ? "*" : "";
+ rb->snprintf(button_text, sizeof(button_text), "%s%s", selected_prefix, buttonChar[i][j]);
+ rb->lcd_getstringsize(button_text, &w, &h);
+ if (i == btn_row && j == btn_col) /* selected item */
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+ else
+ rb->lcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
+ rb->lcd_fillrect(X_0_POS + j * REC_WIDTH,
+ Y_1_POS + i * REC_HEIGHT,
+ REC_WIDTH, REC_HEIGHT + 1);
+ if (i == btn_row && j == btn_col) /* selected item */
+ rb->lcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
+ else
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+ rb->lcd_putsxy(X_0_POS + j * REC_WIDTH + (REC_WIDTH - w) / 2,
+ Y_1_POS + i * REC_HEIGHT + (REC_HEIGHT - h) / 2 + 1,
+ button_text);
+ }
+ }
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+}
+
+static void draw(void)
+{
+ rb->lcd_clear_display();
+
+ char buf[32];
+
+ int x = 5;
+ int y = 10;
+ for (int i = 0; i < NUM_PLAYERS; ++i, x = x + 95)
+ {
+ char *turn_marker = (i == settings.turn) ? "*" : "";
+ rb->snprintf(buf, sizeof(buf), "%sPlayer %d: %d", turn_marker, i + 1, settings.scores[i]);
+ rb->lcd_putsxy(x, y, buf);
+ }
+ int throws_x = (LCD_WIDTH / 2) - 10;
+ char throws_buf[3];
+ for (int i = 0; i < settings.throws; ++i, throws_x += 5)
+ {
+ rb->strcat(throws_buf, "1");
+ rb->lcd_putsxy(throws_x, y, "|");
+ }
+
+ drawButtons();
+
+ rb->lcd_update();
+}
+
+/*****************************************************************************
+* save_game() saves the current game state.
+******************************************************************************/
+static void save_game(void)
+{
+ int fd = rb->open(RESUME_FILE, O_WRONLY | O_CREAT, 0666);
+ if (fd < 0)
+ return;
+
+ rb->write(fd, &settings, sizeof(struct settings_struct));
+
+ rb->close(fd);
+ rb->lcd_update();
+}
+
+/* load_game() loads the saved game and returns load success.*/
+static bool load_game(void)
+{
+ signed int fd;
+ bool loaded = false;
+
+ /* open game file */
+ fd = rb->open(RESUME_FILE, O_RDONLY);
+ if (fd < 0)
+ return false;
+
+ /* read in saved game */
+ if (rb->read(fd, &settings, sizeof(struct settings_struct)) == (long)sizeof(struct settings_struct))
+ {
+ loaded = true;
+ }
+
+ rb->close(fd);
+
+ return loaded;
+ return false;
+}
+
+/* asks the user if they wish to quit */
+static bool confirm_quit(void)
+{
+ const struct text_message prompt = {(const char *[]){"Are you sure?", "This will clear your current game."}, 2};
+ enum yesno_res response = rb->gui_syncyesno_run(&prompt, NULL, NULL);
+ if (response == YESNO_NO)
+ return false;
+ else
+ return true;
+}
+
+/* displays the help text */
+static bool do_help(void)
+{
+
+#ifdef HAVE_LCD_COLOR
+ rb->lcd_set_foreground(LCD_WHITE);
+ rb->lcd_set_background(LCD_BLACK);
+#endif
+
+ rb->lcd_setfont(FONT_UI);
+
+ static char *help_text[] = {"Dart Scorer", "", "", "Keep score of your darts game."};
+
+ struct style_text style[] = {
+ {0, TEXT_CENTER | TEXT_UNDERLINE},
+ };
+
+ return display_text(ARRAYLEN(help_text), help_text, style, NULL, true);
+}
+
+static void undo(void)
+{
+ if (!settings.history_ptr)
+ {
+ rb->splash(HZ * 2, "Out of undos!");
+ return;
+ }
+
+ /* jumping back to previous player? */
+ int turn = settings.throws == 3 ? !settings.turn : settings.turn;
+ if (turn != settings.turn)
+ {
+ settings.throws = 0;
+ settings.turn ^= true;
+ }
+
+ if (settings.history[settings.history_ptr - 1] >= 0)
+ {
+ settings.scores[turn] += settings.history[--settings.history_ptr];
+ ++settings.throws;
+ }
+ else
+ {
+ /*
+ negative history means we bust. negative filled for all skipped throws
+ from being bust so consume back until no more
+ */
+ for (; settings.throws < 3 && settings.history[settings.history_ptr - 1] < 0; --settings.history_ptr, ++settings.throws)
+ {
+ }
+ }
+}
+
+static void init_game(bool newgame)
+{
+ if (newgame)
+ {
+ /* initialize the game context */
+ modifier = 1;
+ btn_row = 1;
+ btn_col = 0;
+
+ int game_mode = -1;
+ MENUITEM_STRINGLIST(menu, "Game Mode", NULL, "501", "301");
+ while (game_mode < 0)
+ {
+ switch (rb->do_menu(&menu, &game_mode, NULL, false))
+ {
+ case 0:
+ {
+ settings.mode = five;
+ break;
+ }
+ case 1:
+ {
+ settings.mode = three;
+ break;
+ }
+ }
+
+ for (int i = 0; i < NUM_PLAYERS; ++i)
+ {
+ settings.scores[i] = (settings.mode == five) ? 501 : 301;
+ }
+ settings.turn = false;
+ settings.throws = 3;
+ settings.history_ptr = 0;
+ rb->lcd_clear_display();
+ }
+ }
+}
+
+/* main game loop */
+static enum plugin_status do_game(bool newgame)
+{
+ init_game(newgame);
+ draw();
+
+ while (1)
+ {
+ /* wait for button press */
+ int button = pluginlib_getaction(-1, plugin_contexts, ARRAYLEN(plugin_contexts));
+ unsigned char *selected = buttonChar[btn_row][btn_col];
+ switch (button)
+ {
+ case DARTS_SELECT:
+ if ((!rb->strcmp(selected, "")) || (!rb->strcmp(selected, "Single")))
+ modifier = 1;
+ else if (!rb->strcmp(selected, "Double"))
+ modifier = 2;
+ else if (!rb->strcmp(selected, "Triple"))
+ modifier = 3;
+ else if (!rb->strcmp(selected, "Undo"))
+ {
+ undo();
+ continue;
+ }
+ else
+ {
+ /* main logic of score keeping */
+ if (modifier == 0)
+ modifier = 1;
+ int hit = (!rb->strcmp(selected, "Bull")) ? 25 : rb->atoi(selected);
+ if (hit == 25 && modifier == 3)
+ {
+ /* no triple bullseye! */
+ rb->splash(HZ * 2, "Triple Bull... Don't be silly!");
+ continue;
+ }
+ hit *= modifier;
+ if (hit > settings.scores[settings.turn])
+ {
+ rb->splash(HZ * 2, "Bust! End of turn.");
+ for (int i = 0; i < settings.throws; ++i)
+ settings.history[settings.history_ptr++] = -1;
+ settings.throws = 0;
+ }
+ else if (hit == settings.scores[settings.turn] - 1)
+ {
+ rb->splash(HZ * 2, "1 left! Must checkout with a double. End of turn.");
+ for (int i = 0; i < settings.throws; ++i)
+ settings.history[settings.history_ptr++] = -1;
+ settings.throws = 0;
+ }
+ else if (hit == settings.scores[settings.turn] && modifier != 2)
+ {
+ rb->splash(HZ * 2, "Must checkout with a double! End of turn.");
+ for (int i = 0; i < settings.throws; ++i)
+ settings.history[settings.history_ptr++] = -1;
+ settings.throws = 0;
+ }
+ else
+ {
+ settings.scores[settings.turn] -= hit;
+ --settings.throws;
+ settings.history[settings.history_ptr++] = hit;
+ modifier = 1;
+ if (!settings.scores[settings.turn])
+ goto GAMEOVER;
+ }
+
+ if (!settings.throws)
+ {
+ settings.throws = 3;
+ settings.turn ^= true;
+ }
+ }
+ break;
+ case DARTS_LEFT:
+ case DARTS_RLEFT:
+ move_with_wrap_and_shift(
+ &btn_col, -1, BUTTON_COLS,
+ &btn_row, 0, BUTTON_ROWS);
+ break;
+ case DARTS_RIGHT:
+ case DARTS_RRIGHT:
+ move_with_wrap_and_shift(
+ &btn_col, 1, BUTTON_COLS,
+ &btn_row, 0, BUTTON_ROWS);
+ break;
+#ifdef DARTS_UP
+ case DARTS_UP:
+ case DARTS_RUP:
+#ifdef HAVE_SCROLLWHEEL
+ case PLA_SCROLL_BACK:
+ case PLA_SCROLL_BACK_REPEAT:
+#endif
+ move_with_wrap_and_shift(
+ &btn_row, -1, BUTTON_ROWS,
+ &btn_col, 0, BUTTON_COLS);
+ break;
+#endif
+#ifdef DARTS_DOWN
+ case DARTS_DOWN:
+ case DARTS_RDOWN:
+#ifdef HAVE_SCROLLWHEEL
+ case PLA_SCROLL_FWD:
+ case PLA_SCROLL_FWD_REPEAT:
+#endif
+ move_with_wrap_and_shift(
+ &btn_row, 1, BUTTON_ROWS,
+ &btn_col, 0, BUTTON_COLS);
+ break;
+#endif
+ case DARTS_QUIT:
+ switch (do_dart_scorer_pause_menu())
+ {
+ case 0: /* resume */
+ break;
+ case 1:
+ init_game(true);
+ continue;
+ case 2: /* quit w/o saving */
+ rb->remove(RESUME_FILE);
+ return PLUGIN_ERROR;
+ case 3: /* save & quit */
+ save_game();
+ return PLUGIN_ERROR;
+ break;
+ }
+ break;
+ default:
+ {
+ exit_on_usb(button); /* handle poweroff and USB */
+ break;
+ }
+ }
+ draw();
+ }
+
+GAMEOVER:
+ rb->splashf(HZ * 3, "Gameover. Player %d wins!", settings.turn + 1);
+
+ return PLUGIN_OK;
+}
+
+/* decide if this_item should be shown in the main menu */
+/* used to hide resume option when there is no save */
+static int mainmenu_cb(int action,
+ const struct menu_item_ex *this_item,
+ struct gui_synclist *this_list)
+{
+ (void)this_list;
+ int idx = ((intptr_t)this_item);
+ if (action == ACTION_REQUEST_MENUITEM && !loaded && (idx == 0 || idx == 5))
+ return ACTION_EXIT_MENUITEM;
+ return action;
+}
+
+/* show the pause menu */
+static int do_dart_scorer_pause_menu(void)
+{
+ int sel = 0;
+ MENUITEM_STRINGLIST(menu, "Dart Scorer", NULL,
+ "Resume Game",
+ "Start New Game",
+ "Playback Control",
+ "Help",
+ "Quit without Saving",
+ "Quit");
+ while (1)
+ {
+ switch (rb->do_menu(&menu, &sel, NULL, false))
+ {
+ case 0:
+ {
+ rb->splash(HZ * 2, "Resume");
+ return 0;
+ }
+ case 1:
+ {
+ if (!confirm_quit())
+ break;
+ else
+ {
+ rb->splash(HZ * 2, "New Game");
+ return 1;
+ }
+ }
+ case 2:
+ playback_control(NULL);
+ break;
+ case 3:
+ do_help();
+ break;
+ case 4: /* quit w/o saving */
+ {
+ if (!confirm_quit())
+ break;
+ else
+ {
+ return 2;
+ }
+ }
+ case 5:
+ return 3;
+ default:
+ break;
+ }
+ }
+}
+
+/* show the main menu */
+static enum plugin_status do_dart_scorer_menu(void)
+{
+ int sel = 0;
+ loaded = load_game();
+ MENUITEM_STRINGLIST(menu,
+ "Dart Scorer Menu",
+ mainmenu_cb,
+ "Resume Game",
+ "Start New Game",
+ "Playback Control",
+ "Help",
+ "Quit without Saving",
+ "Quit");
+ while (true)
+ {
+ switch (rb->do_menu(&menu, &sel, NULL, false))
+ {
+ case 0: /* Start new game or resume a game */
+ case 1:
+ {
+ if (sel == 1 && loaded)
+ {
+ if (!confirm_quit())
+ break;
+ }
+ enum plugin_status ret = do_game(sel == 1);
+ switch (ret)
+ {
+ case PLUGIN_OK:
+ {
+ loaded = false;
+ rb->remove(RESUME_FILE);
+ break;
+ }
+ case PLUGIN_USB_CONNECTED:
+ save_game();
+ return ret;
+ case PLUGIN_ERROR: /* exit without menu */
+ return PLUGIN_OK;
+ default:
+ break;
+ }
+ break;
+ }
+ case 2:
+ playback_control(NULL);
+ break;
+ case 3:
+ do_help();
+ break;
+ case 4:
+ if (confirm_quit())
+ return PLUGIN_OK;
+ break;
+ case 5:
+ if (loaded)
+ save_game();
+ return PLUGIN_OK;
+ default:
+ break;
+ }
+ }
+}
+
+/* prepares for exit */
+static void cleanup(void)
+{
+ backlight_use_settings();
+}
+
+enum plugin_status plugin_start(const void *parameter)
+{
+ (void)parameter;
+ /* now start the game menu */
+ enum plugin_status ret = do_dart_scorer_menu();
+ cleanup();
+ return ret;
+}
diff --git a/apps/plugins/demystify.c b/apps/plugins/demystify.c
index 74537e94f8..cb8e4ddf72 100644
--- a/apps/plugins/demystify.c
+++ b/apps/plugins/demystify.c
@@ -37,26 +37,42 @@
#define MIN_POLYGONS 1
/* Key assignement */
-#define DEMYSTIFY_QUIT PLA_CANCEL
#ifdef HAVE_SCROLLWHEEL
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define DEMYSTIFY_QUIT PLA_UP
+#else
+#define DEMYSTIFY_QUIT PLA_CANCEL
+#endif
+
#define DEMYSTIFY_INCREASE_SPEED PLA_SCROLL_FWD
#define DEMYSTIFY_DECREASE_SPEED PLA_SCROLL_BACK
#define DEMYSTIFY_INCREASE_SPEED_REPEAT PLA_SCROLL_FWD_REPEAT
#define DEMYSTIFY_DECREASE_SPEED_REPEAT PLA_SCROLL_BACK_REPEAT
+
+#define DEMYSTIFY_ADD_POLYGON PLA_RIGHT
+#define DEMYSTIFY_REMOVE_POLYGON PLA_LEFT
+#define DEMYSTIFY_ADD_POLYGON_REPEAT PLA_RIGHT_REPEAT
+#define DEMYSTIFY_REMOVE_POLYGON_REPEAT PLA_LEFT_REPEAT
+
#else
+
+#define DEMYSTIFY_QUIT PLA_CANCEL
+
#define DEMYSTIFY_INCREASE_SPEED PLA_RIGHT
#define DEMYSTIFY_DECREASE_SPEED PLA_LEFT
#define DEMYSTIFY_INCREASE_SPEED_REPEAT PLA_RIGHT_REPEAT
#define DEMYSTIFY_DECREASE_SPEED_REPEAT PLA_LEFT_REPEAT
-#endif
#define DEMYSTIFY_ADD_POLYGON PLA_UP
#define DEMYSTIFY_REMOVE_POLYGON PLA_DOWN
#define DEMYSTIFY_ADD_POLYGON_REPEAT PLA_UP_REPEAT
#define DEMYSTIFY_REMOVE_POLYGON_REPEAT PLA_DOWN_REPEAT
+#endif
const struct button_mapping *plugin_contexts[]
= {pla_main_ctx,
#if defined(HAVE_REMOTE_LCD)
@@ -262,12 +278,10 @@ static void polygons_draw(struct polygon_fifo * polygons, struct screen * displa
static void cleanup(void)
{
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#ifdef HAVE_REMOTE_LCD
remote_backlight_use_settings();
-#endif
-#endif
+
}
#ifdef HAVE_LCD_COLOR
@@ -438,12 +452,10 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#ifdef HAVE_REMOTE_LCD
remote_backlight_ignore_timeout();
-#endif
-#endif
+
ret = plugin_main();
return ret;
diff --git a/apps/plugins/dice.c b/apps/plugins/dice.c
index 622c58d71d..8f2315cde8 100644
--- a/apps/plugins/dice.c
+++ b/apps/plugins/dice.c
@@ -28,7 +28,14 @@
#define INITIAL_NB_DICES 1
#define INITIAL_NB_SIDES 2 /* corresponds to 6 sides in the array */
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define DICE_QUIT PLA_UP
+#else
#define DICE_QUIT PLA_CANCEL
+#endif
#define DICE_ROLL PLA_SELECT
@@ -194,7 +201,7 @@ static bool dice_menu(struct dices * dice) {
break;
case 2:
- rb->set_option("Number of Sides", &sides_index, INT,
+ rb->set_option("Number of Sides", &sides_index, RB_INT,
nb_sides_option,
sizeof(nb_sides_values)/sizeof(int), NULL);
dice->nb_sides=nb_sides_values[sides_index];
diff --git a/apps/plugins/disktidy.c b/apps/plugins/disktidy.c
index 4f2bf6e992..e018c3f80d 100644
--- a/apps/plugins/disktidy.c
+++ b/apps/plugins/disktidy.c
@@ -176,7 +176,7 @@ static void tidy_load_file(const char* file)
str++;
unsigned i = find_file_string(str, last_group);
- tidy_types[i].remove = rb->strcmp(remove, "yes");
+ tidy_types[i].remove = !rb->strcmp(remove, "yes");
if (i >= tidy_type_count)
{
diff --git a/apps/plugins/doom/d_deh.c b/apps/plugins/doom/d_deh.c
index 1a399e3b49..0a67aa0aad 100644
--- a/apps/plugins/doom/d_deh.c
+++ b/apps/plugins/doom/d_deh.c
@@ -63,7 +63,7 @@ char* strlwr(char* str)
typedef struct {
const byte *inp; // Pointer to string
size_t size; // Bytes remaining in string
- int fd; // Current file descriptor
+ int fd; // Current file descriptor
} DEHFILE;
// killough 10/98: emulate IO whether input really comes from a file or not
@@ -2868,6 +2868,7 @@ boolean deh_GetData(char *s, char *k, uint_64_t *l, char **strval, int fpout)
if (*t == '=') break;
buffer[i] = *t; // copy it
}
+ if (i == 0) i = 1; /* Just in case */
buffer[--i] = '\0'; // terminate the key before the '='
if (!*t) // end of string with no equal sign
{
diff --git a/apps/plugins/doom/i_video.c b/apps/plugins/doom/i_video.c
index f5d07a8354..79f3212467 100644
--- a/apps/plugins/doom/i_video.c
+++ b/apps/plugins/doom/i_video.c
@@ -453,6 +453,9 @@ void I_ShutdownGraphics(void)
#define DOOMBUTTON_MAP BUTTON_BOTTOMRIGHT
#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
+#define DOOMBUTTON_SCROLLWHEEL
+#define DOOMBUTTON_SCROLLWHEEL_CC BUTTON_SCROLL_BACK
+#define DOOMBUTTON_SCROLLWHEEL_CW BUTTON_SCROLL_FWD
#define DOOMBUTTON_UP BUTTON_UP
#define DOOMBUTTON_DOWN BUTTON_DOWN
#define DOOMBUTTON_LEFT BUTTON_LEFT
diff --git a/apps/plugins/doom/rockdoom.c b/apps/plugins/doom/rockdoom.c
index 6594859c08..04817d4722 100644
--- a/apps/plugins/doom/rockdoom.c
+++ b/apps/plugins/doom/rockdoom.c
@@ -490,7 +490,7 @@ int Oset_keys()
else
{
*keys[result]=translatekey(*keys[result]);
- rb->set_option(menu_[result], keys[result], INT, doomkeys, numdoomkeys, NULL );
+ rb->set_option(menu_[result], keys[result], RB_INT, doomkeys, numdoomkeys, NULL );
*keys[result]=translatekey(*keys[result]);
}
}
@@ -540,7 +540,7 @@ static bool Doptions()
if(result==0)
Oset_keys();
else if (result > 0)
- rb->set_option(menu_[result], options[result-1], INT, onoff, 2, NULL );
+ rb->set_option(menu_[result], options[result-1], RB_INT, onoff, 2, NULL );
else
menuquit=1;
}
@@ -620,7 +620,7 @@ int doom_menu()
result = rb->do_menu(&menu, &selected, NULL, false);
switch (result) {
case 0: /* Game picker */
- rb->set_option("Game WAD", &gamever, INT, names, status, NULL );
+ rb->set_option("Game WAD", &gamever, RB_INT, names, status, NULL );
break;
case 1: /* Addon picker */
@@ -722,9 +722,9 @@ enum plugin_status plugin_start(const void* parameter)
systemvol= rb->global_settings->volume-rb->global_settings->volume%mod;
general_translucency = default_translucency; // phares
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
#ifdef RB_PROFILE
rb->profile_thread();
#endif
@@ -738,9 +738,9 @@ enum plugin_status plugin_start(const void* parameter)
#ifdef RB_PROFILE
rb->profstop();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
M_SaveDefaults ();
I_Quit(); // Make SURE everything was closed out right
diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c
index 35498227bf..4d36302ddf 100644
--- a/apps/plugins/fft/fft.c
+++ b/apps/plugins/fft/fft.c
@@ -40,13 +40,22 @@ GREY_INFO_STRUCT
/* this set the context to use with PLA */
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
-#define FFT_PREV_GRAPH PLA_LEFT
-#define FFT_NEXT_GRAPH PLA_RIGHT
-#define FFT_ORIENTATION PLA_CANCEL
-#define FFT_WINDOW PLA_SELECT
-#define FFT_AMP_SCALE PLA_UP
-#define FFT_FREQ_SCALE PLA_DOWN
-#define FFT_QUIT PLA_EXIT
+#define FFT_PREV_GRAPH PLA_LEFT
+#define FFT_NEXT_GRAPH PLA_RIGHT
+#define FFT_ORIENTATION PLA_CANCEL
+#define FFT_WINDOW PLA_SELECT_REL
+#define FFT_AMP_SCALE PLA_SELECT_REPEAT
+#define FFT_AMP_SCALE_PRE PLA_SELECT
+#define FFT_FREQ_SCALE PLA_DOWN
+
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define FFT_QUIT PLA_UP
+#else
+#define FFT_QUIT PLA_EXIT
+#endif
#ifdef HAVE_LCD_COLOR
#include "pluginbitmaps/fft_colors.h"
@@ -1184,9 +1193,8 @@ static void fft_cleanup(void)
#ifndef HAVE_LCD_COLOR
grey_release();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
/* save settings if changed */
if (rb->memcmp(&fft, &fft_disk, sizeof(fft)))
@@ -1237,9 +1245,8 @@ static bool fft_setup(void)
mylcd_clear_display();
myosd_lcd_update();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->trigger_cpu_boost();
@@ -1259,6 +1266,10 @@ enum plugin_status plugin_start(const void* parameter)
if(!fft_setup())
return PLUGIN_ERROR;
+#if defined(FFT_AMP_SCALE_PRE)
+ int lastbutton = BUTTON_NONE;
+#endif
+
while(run)
{
long delay = fft_draw();
@@ -1302,6 +1313,10 @@ enum plugin_status plugin_start(const void* parameter)
break;
case FFT_AMP_SCALE:
+#ifdef FFT_AMP_SCALE_PRE
+ if (lastbutton != FFT_AMP_SCALE_PRE)
+ break;
+#endif
if (++fft.amp_scale >= FFT_MAX_AS)
fft.amp_scale = FFT_MIN_AS;
@@ -1330,6 +1345,11 @@ enum plugin_status plugin_start(const void* parameter)
exit_on_usb(button);
break;
}
+
+#if defined(FFT_AMP_SCALE_PRE)
+ if (button != BUTTON_NONE)
+ lastbutton = button;
+#endif
}
return PLUGIN_OK;
diff --git a/apps/plugins/fire.c b/apps/plugins/fire.c
index f3e6fb35e4..5cdf4a2d3a 100644
--- a/apps/plugins/fire.c
+++ b/apps/plugins/fire.c
@@ -56,10 +56,17 @@ const struct button_mapping* plugin_contexts[]= {
};
#define FIRE_QUIT PLA_CANCEL
-#define FIRE_QUIT2 PLA_EXIT
#define FIRE_SWITCH_FLAMES_TYPE PLA_LEFT
#define FIRE_SWITCH_FLAMES_MOVING PLA_RIGHT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define FIRE_QUIT2 PLA_UP
+#else
+#define FIRE_QUIT2 PLA_EXIT
+#endif
+
#ifdef HAVE_SCROLLWHEEL
#define FIRE_INCREASE_MULT PLA_SCROLL_FWD
#define FIRE_INCREASE_MULT_REP PLA_SCROLL_FWD_REPEAT
@@ -279,10 +286,9 @@ static void cleanup(void *parameter)
#ifndef HAVE_LCD_COLOR
grey_release();
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
}
@@ -371,10 +377,9 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_PAL256);
diff --git a/apps/plugins/fireworks.c b/apps/plugins/fireworks.c
index b7dad0d8ba..5f547ed5e0 100644
--- a/apps/plugins/fireworks.c
+++ b/apps/plugins/fireworks.c
@@ -35,10 +35,17 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
/* We use PLA */
#define BTN_EXIT PLA_EXIT
-#define BTN_MENU PLA_CANCEL
#define BTN_FIRE PLA_SELECT
#define BTN_FIRE_REPEAT PLA_SELECT_REPEAT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define BTN_MENU PLA_UP
+#else
+#define BTN_MENU PLA_CANCEL
+#endif
+
/* The lowdown on source terminology:
* a ROCKET is launched from the LCD bottom.
* FIREWORKs are ejected from the rocket when it explodes. */
@@ -305,32 +312,32 @@ static void fireworks_menu(void)
break;
case 1:
- rb->set_option("Auto-Fire", &autofire_delay, INT,
+ rb->set_option("Auto-Fire", &autofire_delay, RB_INT,
autofire_delay_settings, 15, NULL);
break;
case 2:
rb->set_option("Particles Per Firework", &particles_per_firework,
- INT, particle_settings, 8, NULL);
+ RB_INT, particle_settings, 8, NULL);
break;
case 3:
- rb->set_option("Particle Life", &particle_life, INT,
+ rb->set_option("Particle Life", &particle_life, RB_INT,
particle_life_settings, 9, NULL);
break;
case 4:
- rb->set_option("Gravity", &gravity, INT,
+ rb->set_option("Gravity", &gravity, RB_INT,
gravity_settings, 4, NULL);
break;
case 5:
- rb->set_option("Show Rockets", &show_rockets, INT,
+ rb->set_option("Show Rockets", &show_rockets, RB_INT,
rocket_settings, 3, NULL);
break;
case 6:
- rb->set_option("FPS (Speed)", &frames_per_second, INT,
+ rb->set_option("FPS (Speed)", &frames_per_second, RB_INT,
fps_settings, 9, NULL);
break;
@@ -358,9 +365,9 @@ enum plugin_status plugin_start(const void* parameter)
/* set everything up.. no BL timeout, no backdrop,
white-text-on-black-background. */
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
rb->lcd_set_background(LCD_BLACK);
@@ -524,10 +531,10 @@ enum plugin_status plugin_start(const void* parameter)
break;
}
}
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
diff --git a/apps/plugins/flipit.c b/apps/plugins/flipit.c
index c659cf5ba5..278c77e188 100644
--- a/apps/plugins/flipit.c
+++ b/apps/plugins/flipit.c
@@ -49,7 +49,7 @@
#define FLIPIT_DOWN BUTTON_PLAY
#define FLIPIT_NEXT BUTTON_SCROLL_FWD
#define FLIPIT_PREV BUTTON_SCROLL_BACK
-#define FLIPIT_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define FLIPIT_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
#define FLIPIT_SHUFFLE (BUTTON_SELECT | BUTTON_LEFT)
#define FLIPIT_SOLVE (BUTTON_SELECT | BUTTON_PLAY)
#define FLIPIT_STEP_BY_STEP (BUTTON_SELECT | BUTTON_RIGHT)
@@ -841,7 +841,7 @@ enum plugin_status plugin_start(const void* parameter)
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
- rb->lcd_putsxy(2, 8, "[S-MENU] to stop");
+ rb->lcd_putsxy(2, 8, "Long [SELECT] to stop");
rb->lcd_putsxy(2, 18, "[SELECT] toggle");
rb->lcd_putsxy(2, 28, "[S-LEFT] shuffle");
rb->lcd_putsxy(2, 38, "[S-PLAY] solution");
diff --git a/apps/plugins/fractals/fractal.h b/apps/plugins/fractals/fractal.h
index 8e9446df0d..8432546c1e 100644
--- a/apps/plugins/fractals/fractal.h
+++ b/apps/plugins/fractals/fractal.h
@@ -40,7 +40,7 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define FRACTAL_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define FRACTAL_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
#define FRACTAL_UP BUTTON_MENU
#define FRACTAL_DOWN BUTTON_PLAY
#define FRACTAL_LEFT BUTTON_LEFT
diff --git a/apps/plugins/imageviewer/image_decoder.c b/apps/plugins/imageviewer/image_decoder.c
index eab1c01dbc..0c1776daaa 100644
--- a/apps/plugins/imageviewer/image_decoder.c
+++ b/apps/plugins/imageviewer/image_decoder.c
@@ -155,7 +155,10 @@ const struct image_decoder *load_decoder(struct loader_info *loader_info)
goto error_close;
}
- if (lc_hdr->api_version != IMGDEC_API_VERSION)
+ if (lc_hdr->api_version != IMGDEC_API_VERSION ||
+ hdr->img_api_size > sizeof(struct imgdec_api) ||
+ hdr->plugin_api_version != PLUGIN_API_VERSION ||
+ hdr->plugin_api_size > sizeof(struct plugin_api))
{
rb->splashf(2*HZ, "%s decoder: Incompatible version.", name);
goto error_close;
diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c
index 0dd140d1ab..d1a512c4fd 100644
--- a/apps/plugins/imageviewer/imageviewer.c
+++ b/apps/plugins/imageviewer/imageviewer.c
@@ -127,6 +127,9 @@ static int curfile = -1, direction = DIR_NEXT, entries = 0;
/* list of the supported image files */
static char **file_pt;
+/* progress update tick */
+static long next_progress_tick;
+
static const struct image_decoder *imgdec = NULL;
static enum image_type image_type = IMAGE_UNKNOWN;
@@ -192,7 +195,11 @@ static int change_filename(int direct)
return PLUGIN_ERROR;
}
- rb->strcpy(rb->strrchr(np_file, '/')+1, file_pt[curfile]);
+ size_t np_file_length = rb->strlen(np_file);
+ size_t np_file_name_length = rb->strlen(rb->strrchr(np_file, '/')+1);
+ size_t avail_length = sizeof(np_file) - (np_file_length - np_file_name_length);
+
+ rb->snprintf(rb->strrchr(np_file, '/')+1, avail_length, "%s", file_pt[curfile]);
return PLUGIN_OTHER;
}
@@ -223,15 +230,15 @@ static bool set_option_dithering(void)
[DITHER_DIFFUSION] = { STR(LANG_DIFFUSION) },
};
- rb->set_option(rb->str(LANG_DITHERING), &settings.jpeg_dither_mode, INT,
+ rb->set_option(rb->str(LANG_DITHERING), &settings.jpeg_dither_mode, RB_INT,
dithering, DITHER_NUM_MODES, NULL);
return false;
}
MENUITEM_FUNCTION(grayscale_item, 0, ID2P(LANG_GRAYSCALE),
- set_option_grayscale, NULL, NULL, Icon_NOICON);
+ set_option_grayscale, NULL, Icon_NOICON);
MENUITEM_FUNCTION(dithering_item, 0, ID2P(LANG_DITHERING),
- set_option_dithering, NULL, NULL, Icon_NOICON);
+ set_option_dithering, NULL, Icon_NOICON);
MAKE_MENU(display_menu, "Display Options", NULL, Icon_NOICON,
&grayscale_item, &dithering_item);
@@ -283,7 +290,7 @@ static int show_menu(void) /* return 1 to quit */
case MIID_RETURN:
break;
case MIID_TOGGLE_SS_MODE:
- rb->set_option(rb->str(LANG_SLIDESHOW_MODE), &iv_api.slideshow_enabled, BOOL,
+ rb->set_option(rb->str(LANG_SLIDESHOW_MODE), &iv_api.slideshow_enabled, RB_BOOL,
slideshow , 2, NULL);
break;
case MIID_CHANGE_SS_MODE:
@@ -346,7 +353,7 @@ static int show_menu(void) /* return 1 to quit */
static int ask_and_get_audio_buffer(const char *filename)
{
int button;
-#if defined(IMGVIEW_ZOOM_PRE)
+#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_QUIT_PRE)
int lastbutton = BUTTON_NONE;
#endif
rb->lcd_setfont(FONT_SYSFIXED);
@@ -385,6 +392,10 @@ static int ask_and_get_audio_buffer(const char *filename)
#endif
#ifdef IMGVIEW_QUIT
case IMGVIEW_QUIT:
+#ifdef IMGVIEW_QUIT_PRE
+ if (lastbutton != IMGVIEW_QUIT_PRE)
+ break;
+#endif
#endif
case IMGVIEW_MENU:
return PLUGIN_OK;
@@ -417,7 +428,7 @@ static int ask_and_get_audio_buffer(const char *filename)
== SYS_USB_CONNECTED)
return PLUGIN_USB_CONNECTED;
}
-#if defined(IMGVIEW_ZOOM_PRE)
+#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_QUIT_PRE)
if (button != BUTTON_NONE)
lastbutton = button;
#endif
@@ -428,7 +439,14 @@ static int ask_and_get_audio_buffer(const char *filename)
/* callback updating a progress meter while image decoding */
static void cb_progress(int current, int total)
{
- rb->yield(); /* be nice to the other threads */
+ /* do not yield or update the progress bar if we did so too recently */
+ long now = *rb->current_tick;
+ if(!TIME_AFTER(now, next_progress_tick))
+ return;
+
+ /* limit to 20fps */
+ next_progress_tick = now + HZ/20;
+
#ifndef USEGSLIB
/* in slideshow mode, keep gui interference to a minimum */
const int size = (!iv_api.running_slideshow ? 8 : 4);
@@ -442,6 +460,8 @@ static void cb_progress(int current, int total)
total, 0, current, HORIZONTAL);
rb->lcd_update_rect(0, LCD_HEIGHT-size, LCD_WIDTH, size);
}
+
+ rb->yield(); /* be nice to the other threads */
}
#define VSCROLL (LCD_HEIGHT/8)
@@ -556,14 +576,19 @@ static void pan_view_down(struct image_info *info)
}
/* interactively scroll around the image */
-static int scroll_bmp(struct image_info *info)
+static int scroll_bmp(struct image_info *info, bool initial_frame)
{
static long ss_timeout = 0;
int button;
#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_MENU_PRE) \
- || defined(IMGVIEW_SLIDE_SHOW_PRE)
- int lastbutton = BUTTON_NONE;
+ || defined(IMGVIEW_SLIDE_SHOW_PRE) || defined(IMGVIEW_QUIT_PRE)
+ static int lastbutton;
+ if (initial_frame)
+ lastbutton = BUTTON_NONE;
+
+#else
+ (void) initial_frame;
#endif
if (!ss_timeout && iv_api.slideshow_enabled)
@@ -616,11 +641,19 @@ static int scroll_bmp(struct image_info *info)
case IMGVIEW_UP:
case IMGVIEW_UP | BUTTON_REPEAT:
+#ifdef IMGVIEW_SCROLL_UP
+ case IMGVIEW_SCROLL_UP:
+ case IMGVIEW_SCROLL_UP | BUTTON_REPEAT:
+#endif
pan_view_up(info);
break;
case IMGVIEW_DOWN:
case IMGVIEW_DOWN | BUTTON_REPEAT:
+#ifdef IMGVIEW_SCROLL_DOWN
+ case IMGVIEW_SCROLL_DOWN:
+ case IMGVIEW_SCROLL_DOWN | BUTTON_REPEAT:
+#endif
pan_view_down(info);
break;
@@ -721,6 +754,10 @@ static int scroll_bmp(struct image_info *info)
#ifdef IMGVIEW_QUIT
case IMGVIEW_QUIT:
+#ifdef IMGVIEW_QUIT_PRE
+ if (lastbutton != IMGVIEW_QUIT_PRE)
+ break;
+#endif
return PLUGIN_OK;
break;
#endif
@@ -732,7 +769,8 @@ static int scroll_bmp(struct image_info *info)
break;
} /* switch */
-#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_MENU_PRE) || defined(IMGVIEW_SLIDE_SHOW_PRE)
+#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_MENU_PRE) ||\
+ defined(IMGVIEW_SLIDE_SHOW_PRE) || defined(IMGVIEW_QUIT_PRE)
if (button != BUTTON_NONE)
lastbutton = button;
#endif
@@ -898,6 +936,7 @@ static int load_and_show(char* filename, struct image_info *info)
/* used to loop through subimages in animated gifs */
int frame = 0;
+ bool initial_frame = true;
do /* loop the image prepare and decoding when zoomed */
{
status = imgdec->get_image(info, frame, ds); /* decode or fetch from cache */
@@ -930,7 +969,8 @@ static int load_and_show(char* filename, struct image_info *info)
*/
while (1)
{
- status = scroll_bmp(info);
+ status = scroll_bmp(info, initial_frame);
+ initial_frame = false;
if (status == ZOOM_IN)
{
@@ -1040,10 +1080,8 @@ enum plugin_status plugin_start(const void* parameter)
ARRAYLEN(config), IMGVIEW_SETTINGS_MINVERSION);
rb->memcpy(&old_settings, &settings, sizeof (settings));
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
@@ -1070,10 +1108,8 @@ enum plugin_status plugin_start(const void* parameter)
rb->storage_spindown(rb->global_settings->disk_spindown);
#endif
-#ifdef HAVE_BACKLIGHT
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
#ifdef USEGSLIB
grey_release(); /* deinitialize */
diff --git a/apps/plugins/imageviewer/imageviewer.h b/apps/plugins/imageviewer/imageviewer.h
index 19b5db15bb..ac15df5960 100644
--- a/apps/plugins/imageviewer/imageviewer.h
+++ b/apps/plugins/imageviewer/imageviewer.h
@@ -136,14 +136,17 @@ struct image_decoder {
int x, int y, int width, int height);
};
-#define IMGDEC_API_VERSION (PLUGIN_API_VERSION << 4 | 0)
+#define IMGDEC_API_VERSION 1
/* image decoder header */
struct imgdec_header {
struct lc_header lc_hdr; /* must be the first */
const struct image_decoder *decoder;
const struct plugin_api **api;
+ unsigned short plugin_api_version;
+ size_t plugin_api_size;
const struct imgdec_api **img_api;
+ size_t img_api_size;
};
#ifdef IMGDEC
@@ -157,15 +160,18 @@ extern const struct image_decoder image_decoder;
const struct imgdec_header __header \
__attribute__ ((section (".header")))= { \
{ PLUGIN_MAGIC, TARGET_ID, IMGDEC_API_VERSION, \
- plugin_start_addr, plugin_end_addr }, &image_decoder, &rb, &iv };
+ plugin_start_addr, plugin_end_addr, }, &image_decoder, \
+ &rb, PLUGIN_API_VERSION, sizeof(struct plugin_api), \
+ &iv, sizeof(struct imgdec_api) };
#else /* PLATFORM_HOSTED */
#define IMGDEC_HEADER \
const struct plugin_api *rb DATA_ATTR; \
const struct imgdec_api *iv DATA_ATTR; \
const struct imgdec_header __header \
__attribute__((visibility("default"))) = { \
- { PLUGIN_MAGIC, TARGET_ID, IMGDEC_API_VERSION, \
- NULL, NULL }, &image_decoder, &rb, &iv };
+ { PLUGIN_MAGIC, TARGET_ID, IMGDEC_API_VERSION, NULL, NULL }, \
+ &image_decoder, &rb, PLUGIN_API_VERSION, sizeof(struct plugin_api), \
+ &iv, sizeof(struct imgdec_api), };
#endif /* CONFIG_PLATFORM */
#endif
diff --git a/apps/plugins/imageviewer/imageviewer_button.h b/apps/plugins/imageviewer/imageviewer_button.h
index e6cd2ac089..d324f93292 100644
--- a/apps/plugins/imageviewer/imageviewer_button.h
+++ b/apps/plugins/imageviewer/imageviewer_button.h
@@ -53,8 +53,10 @@
#define IMGVIEW_RIGHT BUTTON_RIGHT
#define IMGVIEW_NEXT (BUTTON_SELECT | BUTTON_RIGHT)
#define IMGVIEW_PREVIOUS (BUTTON_SELECT | BUTTON_LEFT)
-#define IMGVIEW_MENU (BUTTON_SELECT | BUTTON_MENU)
-#define IMGVIEW_QUIT (BUTTON_SELECT | BUTTON_PLAY)
+#define IMGVIEW_MENU_PRE BUTTON_SELECT
+#define IMGVIEW_MENU (BUTTON_SELECT | BUTTON_REPEAT)
+#define IMGVIEW_QUIT_PRE BUTTON_SELECT
+#define IMGVIEW_QUIT (BUTTON_SELECT | BUTTON_REL)
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
#define IMGVIEW_ZOOM_PRE BUTTON_SELECT
@@ -529,7 +531,9 @@
#define IMGVIEW_ZOOM_IN BUTTON_VOL_UP
#define IMGVIEW_ZOOM_OUT BUTTON_VOL_DOWN
#define IMGVIEW_UP BUTTON_UP
+#define IMGVIEW_SCROLL_UP BUTTON_SCROLL_BACK
#define IMGVIEW_DOWN BUTTON_DOWN
+#define IMGVIEW_SCROLL_DOWN BUTTON_SCROLL_FWD
#define IMGVIEW_LEFT BUTTON_LEFT
#define IMGVIEW_RIGHT BUTTON_RIGHT
#define IMGVIEW_NEXT BUTTON_BACK
diff --git a/apps/plugins/imageviewer/jpeg/yuv2rgb.c b/apps/plugins/imageviewer/jpeg/yuv2rgb.c
index 61d7fd6487..3e7f08d8bc 100644
--- a/apps/plugins/imageviewer/jpeg/yuv2rgb.c
+++ b/apps/plugins/imageviewer/jpeg/yuv2rgb.c
@@ -238,7 +238,7 @@ static fb_data (* const pixel_funcs[COLOUR_NUM_MODES][DITHER_NUM_MODES])(void) =
};
/* These defines are used fornormal horizontal strides and vertical strides. */
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define LCDADDR(x, y) (lcd_fb + LCD_HEIGHT*(x) + (y))
#define ROWENDOFFSET (width*LCD_HEIGHT)
#define ROWOFFSET (1)
diff --git a/apps/plugins/imageviewer/ppm/ppm_decoder.c b/apps/plugins/imageviewer/ppm/ppm_decoder.c
index 4a86be1a3a..ccb208b80b 100644
--- a/apps/plugins/imageviewer/ppm/ppm_decoder.c
+++ b/apps/plugins/imageviewer/ppm/ppm_decoder.c
@@ -177,7 +177,7 @@ static int read_ppm_row(int fd, struct ppm_info *ppm, int row)
int col;
int r, g, b;
#ifdef HAVE_LCD_COLOR
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
fb_data *dst = (fb_data *) ppm->buf + row;
const int stride = ppm->x;
#else
diff --git a/apps/plugins/invadrox.c b/apps/plugins/invadrox.c
index a164b95cf0..d130ab6108 100644
--- a/apps/plugins/invadrox.c
+++ b/apps/plugins/invadrox.c
@@ -785,7 +785,7 @@ static fb_data *lcd_fb;
/* No standard get_pixel function yet, use this hack instead */
#if (LCD_DEPTH >= 8)
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
static inline fb_data get_pixel(int x, int y)
{
return lcd_fb[x*LCD_HEIGHT+y];
diff --git a/apps/plugins/jackpot.c b/apps/plugins/jackpot.c
index 78f74568ca..fd878509cf 100644
--- a/apps/plugins/jackpot.c
+++ b/apps/plugins/jackpot.c
@@ -25,6 +25,13 @@
#include "lib/pluginlib_exit.h"
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define JACKPOT_QUIT PLA_UP
+#else
+#define JACKPOT_QUIT PLA_CANCEL
+#endif
const struct button_mapping* plugin_contexts[]={pla_main_ctx};
#define NB_PICTURES 9
@@ -248,7 +255,7 @@ enum plugin_status plugin_start(const void* parameter)
plugin_contexts, ARRAYLEN(plugin_contexts));
switch ( action )
{
- case PLA_CANCEL:
+ case JACKPOT_QUIT:
return PLUGIN_OK;
case PLA_SELECT:
jackpot_play_turn(&game);
diff --git a/apps/plugins/jewels.c b/apps/plugins/jewels.c
index 3f209ae556..3a65e8ba64 100644
--- a/apps/plugins/jewels.c
+++ b/apps/plugins/jewels.c
@@ -49,9 +49,9 @@
#define JEWELS_PREV BUTTON_SCROLL_BACK
#define JEWELS_NEXT BUTTON_SCROLL_FWD
#define JEWELS_SELECT BUTTON_SELECT
-#define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_MENU)
+#define JEWELS_CANCEL (BUTTON_SELECT | BUTTON_REPEAT)
#define HK_SELECT "SELECT"
-#define HK_CANCEL "SEL + MENU"
+#define HK_CANCEL "Long SELECT"
#elif (CONFIG_KEYPAD == IPOD_3G_PAD)
#define JEWELS_LEFT BUTTON_LEFT
@@ -1481,7 +1481,7 @@ static int jewels_game_menu(struct game_context* bj, bool ingame)
jewels_init(bj);
return 0;
case 2:
- rb->set_option("Mode", &bj->tmp_type, INT, mode, 2, NULL);
+ rb->set_option("Mode", &bj->tmp_type, RB_INT, mode, 2, NULL);
break;
case 3:
if(jewels_help())
diff --git a/apps/plugins/keybox.c b/apps/plugins/keybox.c
index f8c6800a4d..cb2e23a94a 100644
--- a/apps/plugins/keybox.c
+++ b/apps/plugins/keybox.c
@@ -567,7 +567,7 @@ static int keybox(void)
{
rb->gui_synclist_draw(&kb_list);
button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&kb_list, &button, LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&kb_list, &button))
continue;
switch (button)
@@ -659,7 +659,6 @@ enum plugin_status plugin_start(const void *parameter)
rb->gui_synclist_set_title(&kb_list, "Keybox", NOICON);
rb->gui_synclist_set_icon_callback(&kb_list, NULL);
rb->gui_synclist_set_nb_items(&kb_list, 0);
- rb->gui_synclist_limit_scroll(&kb_list, false);
rb->gui_synclist_select_item(&kb_list, 0);
init_ll();
diff --git a/apps/plugins/keyremap.c b/apps/plugins/keyremap.c
new file mode 100644
index 0000000000..202d5fcfa4
--- /dev/null
+++ b/apps/plugins/keyremap.c
@@ -0,0 +1,2228 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ /
+ * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) (
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "lang_enum.h"
+
+#include "lib/action_helper.h"
+#include "lib/button_helper.h"
+#include "lib/pluginlib_actions.h"
+#include "lib/printcell_helper.h"
+#include "lib/kbd_helper.h"
+
+#ifdef ROCKBOX_HAS_LOGF
+#define logf rb->logf
+#else
+#define logf(...) do { } while(0)
+#endif
+
+/* CORE_KEYREMAP_FILE */
+#include "../core_keymap.h"
+#define KMFDIR ROCKBOX_DIR
+#define KMFEXT1 ".kmf"
+#define KMFEXT2 ".kmf.old"
+#define KMFUSER "user_keyremap"
+#define MAX_BUTTON_COMBO 5
+#define MAX_BUTTON_NAME 32
+#define MAX_MENU_NAME 64
+#define TEST_COUNTDOWN_MS 1590
+
+struct context_flags {
+ char * name;
+ int flag;
+};
+
+/* flags added to context_name[] */
+static struct context_flags context_flags[] = {
+ {"UNKNOWN", 0},/* index 0 is an Error */
+#ifndef HAS_BUTTON_HOLD
+ {"LOCKED", CONTEXT_LOCKED},
+#endif
+ /*{"PLUGIN", CONTEXT_PLUGIN}, need a custom action list and a way to supply */
+#if BUTTON_REMOTE != 0
+ {"REMOTE", CONTEXT_REMOTE},
+#ifndef HAS_BUTTON_HOLD
+ {"REMOTE_LOCKED", CONTEXT_REMOTE | CONTEXT_LOCKED},
+#endif
+#endif /* BUTTON_REMOTE != 0 */
+};
+
+static struct keyremap_buffer_t {
+ char * buffer;
+ size_t buf_size;
+ char *front;
+ char *end;
+} keyremap_buffer;
+
+
+struct action_mapping_t {
+ int context;
+ int display_pos;
+ struct button_mapping map;
+};
+
+static struct user_context_data_t {
+ struct action_mapping_t *ctx_map;
+ int ctx_count;
+ struct action_mapping_t *act_map;
+ int act_count;
+} ctx_data;
+
+/* set keys keymap */
+static struct setkeys_data_t {
+ /* save state in the set keys action view list */
+ int view_columns;
+ int view_lastcol;
+ uint32_t crc32;
+} keyset;
+
+/* test keys data */
+static struct testkey_data_t {
+ struct button_mapping *keymap;
+ int action;
+ int context;
+ int index;
+ int countdown;
+} keytest;
+
+static struct context_menu_data_t {
+ char *menuid;
+ int ctx_index;
+ int act_index;
+ int act_edit_index;
+ //const char * ctx_fmt;
+ const char * act_fmt;
+} ctx_menu_data;
+#define ACTIONFMT_LV0 "$%s$%s$%s$%s"
+#define ACTVIEW_HEADER "$Context$Action$Button$PreBtn"
+
+#define GOTO_ACTION_DEFAULT_HANDLER (PLUGIN_OK + 1)
+#define MENU_ID(x) (((void*)&mainmenu[x]))
+#define MENU_MAX_DEPTH 4
+/* this enum sets menu order */
+enum {
+ M_ROOT = 0,
+ M_SETKEYS,
+ M_TESTKEYS,
+ M_RESETKEYS,
+ M_EXPORTKEYS,
+ M_IMPORTKEYS,
+ M_SAVEKEYS,
+ M_LOADKEYS,
+ M_DELKEYS,
+ M_TMPCORE,
+ M_SETCORE,
+ M_DELCORE,
+ M_EXIT,
+ M_LAST_MAINITEM, //MAIN MENU ITEM COUNT
+/*Menus not directly accessible from main menu*/
+ M_ACTIONS = M_LAST_MAINITEM,
+ M_BUTTONS,
+ M_CONTEXTS,
+ M_CONTEXT_EDIT,
+ M_LAST_ITEM, //ITEM COUNT
+};
+
+struct mainmenu { const char *name; void *menuid; int index; int items;};
+static struct mainmenu mainmenu[M_LAST_ITEM] = {
+#define MENU_ITEM(ID, NAME, COUNT) [ID]{NAME, MENU_ID(ID), (int)ID, (int)COUNT}
+MENU_ITEM(M_ROOT, "Key Remap Plugin", M_LAST_MAINITEM - 1),
+MENU_ITEM(M_SETKEYS, "Edit Keymap", 1),
+MENU_ITEM(M_TESTKEYS, "Test Keymap", 4),
+MENU_ITEM(M_RESETKEYS, "Reset Keymap", 1),
+MENU_ITEM(M_EXPORTKEYS, "Export Text Keymap", 1),
+MENU_ITEM(M_IMPORTKEYS, "Import Text Keymap", 1),
+MENU_ITEM(M_SAVEKEYS, "Save Native Keymap", 1),
+MENU_ITEM(M_LOADKEYS, "Load Native Keymaps", 1),
+MENU_ITEM(M_DELKEYS, "Delete Keymaps", 1),
+MENU_ITEM(M_TMPCORE, "Temp Core Remap", 1),
+MENU_ITEM(M_SETCORE, "Set Core Remap", 1),
+MENU_ITEM(M_DELCORE, "Remove Core Remap", 1),
+MENU_ITEM(M_EXIT, ID2P(LANG_MENU_QUIT), 0),
+MENU_ITEM(M_ACTIONS, "Actions", LAST_ACTION_PLACEHOLDER),
+MENU_ITEM(M_BUTTONS, "Buttons", -1), /* Set at runtime in plugin_start: */
+MENU_ITEM(M_CONTEXTS, "Contexts", LAST_CONTEXT_PLACEHOLDER * ARRAYLEN(context_flags)),
+MENU_ITEM(M_CONTEXT_EDIT, "", 5),
+#undef MENU_ITEM
+};
+
+DIR* kmffiles_dirp = NULL;
+static int core_savecount = 0;/* so we don't overwrite the last .old file with revisions */
+
+/* global lists, for everything */
+static struct gui_synclist lists;
+
+static void menu_useract_set_positions(void);
+static size_t lang_strlcpy(char * dst, const char *src, size_t len)
+{
+ unsigned char **language_strings = rb->language_strings;
+ return rb->strlcpy(dst, P2STR((unsigned char*)src), len);
+}
+
+/* Menu stack macros */
+static int mlastsel[MENU_MAX_DEPTH * 2 + 1] = {0};
+#define PUSH_MENU(id, item) \
+ if(mlastsel[0] < MENU_MAX_DEPTH * 2) {mlastsel[++mlastsel[0]] = id;mlastsel[++mlastsel[0]] = item;}
+#define POP_MENU(id, item) { item = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]--]:0); \
+ id = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]--]:0); }
+#define PEEK_MENU_ITEM(item) { item = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]]:0);}
+#define PEEK_MENU_ID(id) {id = (mlastsel[0] > 1 ? mlastsel[mlastsel[0]-1]:0);}
+#define PEEK_MENU(id, item) PEEK_MENU_ITEM(item) PEEK_MENU_ID(id)
+#define SET_MENU_ITEM(item) \
+ if(mlastsel[0] > 1) {mlastsel[mlastsel[0]] = item;}
+
+static struct mainmenu *mainitem(int selected_item)
+{
+ static struct mainmenu empty = {0};
+ if (selected_item >= 0 && selected_item < (int) ARRAYLEN(mainmenu))
+ return &mainmenu[selected_item];
+ else
+ return &empty;
+}
+/* Forward Declarations */
+static const char *edit_keymap_name_cb(int selected_item, void* data,char* buf, size_t buf_len);
+static int keyremap_import_file(char *filenamebuf, size_t bufsz);
+static void synclist_set(int id, int selected_item, int items, int sel_size);
+
+static int prompt_filename(char *buf, size_t bufsz)
+{
+#define KBD_LAYOUT "abcdefghijklmnop\nqrstuvwxyz |()[]\n1234567890 /._-+\n\n" \
+ "\nABCDEFGHIJKLMNOP\nQRSTUVWXYZ |()[]\n1234567890 /._-+"
+ unsigned short kbd[sizeof(KBD_LAYOUT) + 10];
+ unsigned short *kbd_p = kbd;
+ if (!kbd_create_layout(KBD_LAYOUT, kbd, sizeof(kbd)))
+ kbd_p = NULL;
+
+#undef KBD_LAYOUT
+ return rb->kbd_input(buf, bufsz, kbd_p) + 1;
+}
+
+static void synclist_set_update(int id, int selected_item, int items, int sel_size)
+{
+ SET_MENU_ITEM(lists.selected_item + 1); /* update selected for previous menu*/
+ synclist_set(id, selected_item, items, sel_size);
+}
+
+/* returns the actual context & index of the flag is passed in *flagidx */
+static int ctx_strip_flagidx(int ctx, int *flagidx)
+{
+ int ctx_out = ctx % (LAST_CONTEXT_PLACEHOLDER);
+ *flagidx = 0;
+ if (ctx_out != ctx)
+ {
+ while (ctx >= LAST_CONTEXT_PLACEHOLDER)
+ {
+ (*flagidx)++;
+ ctx -= LAST_CONTEXT_PLACEHOLDER;
+ }
+ if (*flagidx >= (int)ARRAYLEN(context_flags))
+ *flagidx = 0; /* unknown flag */
+
+ logf("%s ctx: (%d) %s flag idx: (%d) %s\n", __func__,
+ ctx, context_name(ctx), *flagidx, context_flags[*flagidx].name);
+ }
+ return ctx_out;
+}
+
+/* combines context name and flag name using '_' to join them */
+static char *ctx_name_and_flag(int index)
+{
+ static char ctx_namebuf[MAX_MENU_NAME];
+ char *ctx_name = "?";
+ int flagidx;
+ int ctx = ctx_strip_flagidx(index, &flagidx);
+ if (flagidx == 0)
+ {
+ ctx_name = context_name(ctx);
+ }
+ else if (flagidx >= 0 && flagidx < (int)ARRAYLEN(context_flags))
+ {
+ rb->snprintf(ctx_namebuf, sizeof(ctx_namebuf), "%s_%s",
+ context_name(ctx), context_flags[flagidx].name);
+ ctx_name = ctx_namebuf;
+ }
+ return ctx_name;
+}
+
+static int btnval_to_index(unsigned int btnvalue)
+{
+ int index = -1;
+ for (int i = 0; i < available_button_count; i++)
+ {
+ const struct available_button *btn = &available_buttons[i];
+ if (btnvalue == btn->value)
+ {
+ index = i;
+ break;
+ }
+ }
+ return index;
+}
+
+static int btnval_to_name(char *buf, size_t bufsz, unsigned int btnvalue)
+{
+ int index = btnval_to_index(btnvalue);
+ if (index >= 0)
+ {
+ return rb->snprintf(buf, bufsz, "%s", available_buttons[index].name);
+ }
+ else /* this provides names for button combos e.g.(BUTTON_UP|BUTTON_REPEAT) */
+ {
+ int res = get_button_names(buf, bufsz, btnvalue);
+ if (res > 0 && res <= (int)bufsz)
+ return res;
+ }
+ return rb->snprintf(buf, bufsz, "%s", "BUTTON_UNKNOWN");
+}
+
+static int keyremap_check_extension(const char* filename)
+{
+ int found = 0;
+ unsigned int len = rb->strlen(filename);
+ if (len > sizeof(KMFEXT1) &&
+ rb->strcmp(&filename[len - sizeof(KMFEXT1) + 1], KMFEXT1) == 0)
+ {
+ found = 1;
+ }
+ else if (len > sizeof(KMFEXT2) &&
+ rb->strcmp(&filename[len - sizeof(KMFEXT2) + 1], KMFEXT2) == 0)
+ {
+ found = 2;
+ }
+ return found;
+}
+
+static int keyremap_count_files(const char *directory)
+{
+ int nfiles = 0;
+ DIR* kmfdirp = NULL;
+ kmfdirp = rb->opendir(directory);
+ if (kmfdirp != NULL)
+ {
+ struct dirent *entry;
+ while ((entry = rb->readdir(kmfdirp)))
+ {
+ /* skip directories */
+ if ((rb->dir_get_info(kmfdirp, entry).attribute & ATTR_DIRECTORY) != 0)
+ continue;
+ if (keyremap_check_extension(entry->d_name) > 0)
+ nfiles++;
+ }
+ rb->closedir(kmfdirp);
+ }
+ return nfiles;
+}
+
+static void keyremap_reset_buffer(void)
+{
+ keyremap_buffer.front = keyremap_buffer.buffer;
+ keyremap_buffer.end = keyremap_buffer.front + keyremap_buffer.buf_size;
+ rb->memset(keyremap_buffer.front, 0, keyremap_buffer.buf_size);
+ ctx_data.ctx_map = (struct action_mapping_t*)keyremap_buffer.front;
+ ctx_data.ctx_count = 0;
+ ctx_data.act_map = (struct action_mapping_t*) keyremap_buffer.end;
+ ctx_data.act_count = 0;
+
+}
+
+static size_t keyremap_write_entries(int fd, struct button_mapping *data, int entry_count)
+{
+ if (fd < 0 || entry_count <= 0 || !data)
+ goto fail;
+ size_t bytes_req = sizeof(struct button_mapping) * entry_count;
+ size_t bytes = rb->write(fd, data, bytes_req);
+ if (bytes == bytes_req)
+ return bytes_req;
+fail:
+ return 0;
+}
+
+static size_t keyremap_write_header(int fd, int entry_count)
+{
+ if (fd < 0 || entry_count < 0)
+ goto fail;
+ struct button_mapping header = {KEYREMAP_VERSION, KEYREMAP_HEADERID, entry_count};
+ return keyremap_write_entries(fd, &header, 1);
+fail:
+ return 0;
+}
+
+static int keyremap_open_file(const char *filename, int *fd, size_t *fsize)
+{
+ int count = 0;
+
+ while (filename && fd && fsize)
+ {
+ *fsize = 0;
+ *fd = rb->open(filename, O_RDONLY);
+ if (*fd)
+ {
+ *fsize = rb->filesize(*fd);
+
+ count = *fsize / sizeof(struct button_mapping);
+
+ if (count * sizeof(struct button_mapping) != *fsize)
+ {
+ count = -10;
+ break;
+ }
+
+ if (count > 1)
+ {
+ struct button_mapping header = {0};
+ rb->read(*fd, &header, sizeof(struct button_mapping));
+ if (KEYREMAP_VERSION == header.action_code &&
+ KEYREMAP_HEADERID == header.button_code &&
+ header.pre_button_code == count)
+ {
+ count--;
+ *fsize -= sizeof(struct button_mapping);
+ }
+ else /* Header mismatch */
+ {
+ count = -20;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return count;
+}
+
+static int keyremap_map_is_valid(struct action_mapping_t *amap, int context)
+{
+ int ret = (amap->context == context ? 1: 0);
+ struct button_mapping entry = amap->map;
+ if (entry.action_code == (int)CONTEXT_STOPSEARCHING ||
+ entry.button_code == BUTTON_NONE)
+ {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static struct button_mapping *keyremap_create_temp(int *entries)
+{
+ logf("%s()", __func__);
+ struct button_mapping *tempkeymap;
+ int action_offset = 1; /* start of action entries (ctx_count + 1)*/
+ int entry_count = 1;/* (ctx_count + ctx_count + act_count) */
+ int index = 0;
+ int i, j;
+
+ /* count includes a single stop sentinel for the list of contexts */
+ /* and a stop sentinel for each group of action remaps as well */
+ for (i = 0; i < ctx_data.ctx_count; i++)
+ {
+ int actions_this_ctx = 0;
+ int context = ctx_data.ctx_map[i].context;
+ /* how many actions are contained in each context? */
+ for (j = 0; j < ctx_data.act_count; j++)
+ {
+ if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
+ {
+ actions_this_ctx++;
+ }
+ }
+ /* only count contexts with remapped actions */
+ if (actions_this_ctx != 0)
+ {
+ action_offset++;
+ entry_count += actions_this_ctx + 2;
+ }
+ }
+ size_t keymap_bytes = (entry_count) * sizeof(struct button_mapping);
+ *entries = entry_count;
+ logf("%s() entry count: %d", __func__, entry_count);
+ logf("keyremap bytes: %zu, avail: %zu", keymap_bytes,
+ (keyremap_buffer.end - keyremap_buffer.front));
+ if (keyremap_buffer.front + keymap_bytes < keyremap_buffer.end)
+ {
+ tempkeymap = (struct button_mapping *) keyremap_buffer.front;
+ rb->memset(tempkeymap, 0, keymap_bytes);
+ struct button_mapping *entry = &tempkeymap[index++];
+ for (i = 0; i < ctx_data.ctx_count; i++)
+ {
+ int actions_this_ctx = 0;
+ int flagidx;
+ int context = ctx_data.ctx_map[i].context;
+ /* how many actions are contained in each context? */
+ for (j = 0; j < ctx_data.act_count; j++)
+ {
+ if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
+ {
+ actions_this_ctx += 1;
+ }
+ }
+ /*Don't save contexts with no actions */
+ if (actions_this_ctx == 0){ continue; }
+
+ /* convert context x flag to context | flag */
+ context = ctx_strip_flagidx(ctx_data.ctx_map[i].context, &flagidx);
+ context |= context_flags[flagidx].flag;
+
+ entry->action_code = CORE_CONTEXT_REMAP(context);
+ entry->button_code = action_offset; /* offset of first action entry */
+ entry->pre_button_code = actions_this_ctx; /* entries (excluding sentinel) */
+ entry = &tempkeymap[index++];
+ logf("keyremap found context: %d index: %d entries: %d",
+ context, action_offset, actions_this_ctx);
+ action_offset += actions_this_ctx + 1; /* including sentinel */
+ }
+ /* context sentinel */
+ entry->action_code = CONTEXT_STOPSEARCHING;;
+ entry->button_code = BUTTON_NONE;
+ entry->pre_button_code = BUTTON_NONE;
+ entry = &tempkeymap[index++];
+
+ for (i = 0; i < ctx_data.ctx_count; i++)
+ {
+ int actions_this_ctx = 0;
+ int context = ctx_data.ctx_map[i].context;
+
+ for (int j = 0; j < ctx_data.act_count; j++)
+ {
+ if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
+ {
+ struct button_mapping map = ctx_data.act_map[j].map;
+ entry->action_code = map.action_code;
+ entry->button_code = map.button_code;
+ entry->pre_button_code = map.pre_button_code;
+ entry = &tempkeymap[index++];
+ actions_this_ctx++;
+ logf("keyremap: found ctx: %d, act: %d btn: %d pbtn: %d",
+ context, map.action_code, map.button_code, map.pre_button_code);
+ }
+ }
+ /*Don't save sentinel for contexts with no actions */
+ if (actions_this_ctx == 0){ continue; }
+
+ /* action sentinel */
+ entry->action_code = CONTEXT_STOPSEARCHING;;
+ entry->button_code = BUTTON_NONE;
+ entry->pre_button_code = BUTTON_NONE;
+ entry = &tempkeymap[index++];
+ }
+ }
+ else
+ {
+ rb->splashf(HZ *2, "Out of Memory");
+ logf("keyremap: create temp OOM");
+ *entries = 0;
+ tempkeymap = NULL;
+ }
+
+ return tempkeymap;
+}
+
+static int keyremap_save_current(const char *filename)
+{
+ int status = 0;
+ int entry_count;/* (ctx_count + ctx_count + act_count + 1) */
+
+ struct button_mapping *keymap = keyremap_create_temp(&entry_count);
+
+ if (keymap == NULL || entry_count <= 3) /* there should be atleast 4 entries */
+ return status;
+ keyset.crc32 =
+ rb->crc_32(keymap, entry_count * sizeof(struct button_mapping), 0xFFFFFFFF);
+ int keyfd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (keyremap_write_header(keyfd, entry_count + 1) > 0)
+ {
+ if (keyremap_write_entries(keyfd, keymap, entry_count) > 0)
+ status = 1;
+ }
+ rb->close(keyfd);
+
+ return status;
+}
+
+static void keyremap_save_user_keys(bool notify)
+{
+ char buf[MAX_PATH];
+ int i = 0;
+ do
+ {
+ i++;
+ rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", KMFDIR, KMFUSER, i, KMFEXT1);
+ } while (i < 100 && rb->file_exists(buf));
+
+ if (notify && prompt_filename(buf, sizeof(buf)) <= 0)
+ {
+ return;
+ }
+
+ if (keyremap_save_current(buf) == 0)
+ {
+ logf("Error Saving");
+ if(notify)
+ rb->splash(HZ *2, "Error Saving");
+ }
+ else if (notify)
+ rb->splashf(HZ *2, "Saved %s", buf);
+}
+
+static int keyremap_export_current(char *filenamebuf, size_t bufsz)
+{
+ filenamebuf[bufsz - 1] = '\0';
+ int i, j;
+ int ctx_count = 0;
+ size_t entrylen;
+
+ int entry_count = ctx_data.ctx_count + ctx_data.act_count + 1;
+
+ if (entry_count < 3) /* the header is not counted should be at least 3 entries */
+ {
+ logf("%s: Not enough entries", __func__);
+ return 0;
+ }
+ int fd = rb->open(filenamebuf, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+ if (fd < 0)
+ return -1;
+ rb->fdprintf(fd, "# Key Remap\n# Device: %s\n" \
+ "# Entries: %d\n\n", MODEL_NAME, entry_count - 1);
+ rb->fdprintf(fd, "# Each entry should be PROPER_CASE and on its own line\n" \
+ "# Comments run to end of line \n");
+
+ for (i = 0; i <= entry_count; i++)
+ {
+ entrylen = 0;
+ rb->memset(filenamebuf, 0, bufsz);
+ edit_keymap_name_cb(i, MENU_ID(M_EXPORTKEYS), filenamebuf, bufsz);
+ if (i == 0)
+ {
+ ctx_menu_data.act_fmt = " {%s%s, %s, %s},\n\n";
+ continue;
+ }
+ else if (i == entry_count)
+ {
+ rb->strlcpy(filenamebuf, "}\n\n", bufsz);
+ }
+ char last = '\0';
+ for (j = 0; j < (int)bufsz ;j++)
+ {
+ char ch = filenamebuf[j];
+ if (ch == '$' && last == '\0') /*CONTEXT*/
+ {
+ if (ctx_count > 0)
+ rb->fdprintf(fd, "}\n");
+ ctx_count++;
+ filenamebuf[j] = '\n';
+ }
+ if (ch == '\n' && last == '\n')
+ {
+ entrylen = j;
+ break;
+ }
+ else if (ch == '\0')
+ filenamebuf[j] = ',';
+ last = ch;
+ }
+
+ size_t bytes = rb->write(fd, filenamebuf, entrylen);
+ if (bytes != entrylen)
+ {
+ entry_count = -2;
+ goto fail;
+ }
+ }
+
+fail:
+ rb->close(fd);
+
+ return entry_count;
+}
+
+static void keyremap_export_user_keys(void)
+{
+ const bool notify = true;
+ char buf[MAX_PATH];
+ int i = 0;
+ do
+ {
+ i++;
+ rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", "", KMFUSER, i, ".txt");
+ } while (i < 100 && rb->file_exists(buf));
+
+ if (notify && prompt_filename(buf, sizeof(buf)) <= 0)
+ {
+ return;
+ }
+
+ if (keyremap_export_current(buf, sizeof(buf)) <= 0)
+ {
+ if(notify)
+ rb->splash(HZ *2, "Error Saving");
+ }
+ else if (notify)
+ {
+ rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", "", KMFUSER, i, ".txt");
+ rb->splashf(HZ *2, "Saved %s", buf);
+ }
+}
+
+static void keyremap_import_user_keys(void)
+{
+ char buf[MAX_PATH];
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY,
+ .title = "Select Keymap",
+ .icon = Icon_Plugin,
+ .buf = buf,
+ .bufsize = sizeof(buf),
+ .root = "/",
+ };
+
+ if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
+ {
+ int ret = keyremap_import_file(buf, sizeof(buf));
+ if (ret <= 0)
+ {
+ keyremap_reset_buffer();
+ rb->splash(HZ *2, "Error Opening");
+ }
+ else
+ rb->splashf(HZ * 2, "Loaded Text Keymap ");
+ }
+}
+
+static int keymap_add_context_entry(int context)
+{
+ int remap_context = CORE_CONTEXT_REMAP(context);
+ for (int i = 0; i < ctx_data.ctx_count; i++)
+ {
+ if (ctx_data.ctx_map[i].map.action_code == remap_context)
+ goto fail; /* context exists */
+ }
+ if (keyremap_buffer.front + sizeof(struct action_mapping_t) > keyremap_buffer.end)
+ goto fail;
+ keyremap_buffer.front += sizeof(struct action_mapping_t);
+ ctx_data.ctx_map[ctx_data.ctx_count].context = context;
+ ctx_data.ctx_map[ctx_data.ctx_count].display_pos = -1;
+ ctx_data.ctx_map[ctx_data.ctx_count].map.action_code = remap_context;
+ ctx_data.ctx_map[ctx_data.ctx_count].map.button_code = 0;
+ ctx_data.ctx_map[ctx_data.ctx_count].map.pre_button_code = 0;
+ ctx_data.ctx_count++;
+ menu_useract_set_positions();
+ return ctx_data.ctx_count;
+fail:
+ return 0;
+}
+
+static int keymap_add_button_entry(int context, int action_code,
+ int button_code, int pre_button_code)
+{
+ bool hasctx = false;
+ for (int i = 0; i < ctx_data.ctx_count; i++)
+ {
+ if (ctx_data.ctx_map[i].context == context)
+ {
+ hasctx = true;
+ break;
+ }
+ }
+ if (!hasctx || keyremap_buffer.end - sizeof(struct action_mapping_t) < keyremap_buffer.front)
+ goto fail;
+ keyremap_buffer.end -= sizeof(struct action_mapping_t);
+ ctx_data.act_map = (struct action_mapping_t*) keyremap_buffer.end;
+ ctx_data.act_map[0].context = context;
+ ctx_data.act_map[0].display_pos = -1;
+ ctx_data.act_map[0].map.action_code = action_code;
+ ctx_data.act_map[0].map.button_code = button_code;
+ ctx_data.act_map[0].map.pre_button_code = pre_button_code;
+ ctx_data.act_count++;
+ menu_useract_set_positions();
+ return ctx_data.act_count;
+fail:
+ return 0;
+}
+
+static int get_action_from_str(char *pfield, size_t bufsz)
+{
+ int act = -1;
+ for (int i=0;i < LAST_ACTION_PLACEHOLDER; i++)
+ {
+ if (rb->strncasecmp(pfield, action_name(i), bufsz) == 0)
+ {
+ logf("Action Found: %s (%d)", pfield, i);
+ act = i;
+ break;
+ }
+ }
+ return act;
+}
+
+static int get_button_from_str(char *pfield, size_t bufsz)
+{
+ int btn = -1;
+ for (int i=0;i < available_button_count; i++)
+ {
+ const struct available_button* abtn = &available_buttons[i];
+ if (rb->strncasecmp(pfield, abtn->name, bufsz) == 0)
+ {
+ logf("Button Found: %s (%lx)", abtn->name, abtn->value);
+ btn = abtn->value;
+ break;
+ }
+ }
+ if (btn < 0) /* Not Fatal */
+ {
+ logf("Invalid Button %s", pfield);
+ rb->splashf(HZ, "Invalid Button %s", pfield);
+ }
+ return btn;
+}
+
+static int parse_action_import_entry(int context, char * pbuf, size_t bufsz)
+{
+ size_t bufleft;
+ int count = 0;
+ char ch;
+ char *pfirst = NULL;
+ char *pfield;
+ int field = -1;
+ int act = -1;
+ int button = BUTTON_NONE;
+ int prebtn = BUTTON_NONE;
+ while ((ch = *(pbuf)) != '\0')
+ {
+ pfield = NULL; /* Valid names */
+ if ((ch >= 'A' && ch <= 'Z') || ch == '_')
+ {
+ if (pfirst == NULL)
+ pfirst = pbuf;
+ }
+ else if (ch == ',')
+ {
+ if (pfirst != NULL)
+ {
+ field++;
+ pfield = pfirst;
+ pfirst = NULL;
+ *pbuf = '\0';
+ }
+ }
+ else if (ch == ' ' || ch == '|'){;}
+ else
+ pfirst = NULL;
+
+ if (field == 1 && pfirst != NULL && pbuf[1] == '\0')
+ {
+ field++;
+ pfield = pfirst;
+ pfirst = NULL;
+ }
+
+ if (pfield != NULL)
+ {
+ char *pf;
+
+ if (field == 0) /* action */
+ {
+ char *pact = pfield;
+ pf = pfield;
+ while ((ch = *(pf)) != '\0')
+ {
+ if(ch == ' ')
+ *pf = '\0';
+ else
+ pf++;
+ }
+ bufleft = bufsz - (pact - pbuf);
+ act = get_action_from_str(pact, bufleft);
+
+ if (act < 0)
+ {
+ logf("Error Action Expected: %s", pact);
+ return -1;
+ }
+ }
+ else if (field == 1 || field == 2) /* button / pre_btn */
+ {
+ char *pbtn = pfield;
+ pf = pfield;
+ while ((ch = *(pf)) != '\0')
+ {
+ if (pf[1] == '\0') /* last item? */
+ {
+ pf++;
+ ch = '|';
+ pfield = NULL;
+ }
+ else if (ch == ' ' || ch == '|')
+ {
+ *pf = '\0';
+ }
+
+ if(ch == '|')
+ {
+ bufleft = bufsz - (pbtn - pbuf);
+ int btn = get_button_from_str(pbtn, bufleft);
+
+ if (btn > BUTTON_NONE)
+ {
+ if (field == 1)
+ button |= btn;
+ else if (field == 2)
+ prebtn |= btn;
+ }
+
+ if (pfield != NULL)
+ {
+ pf++;
+ while ((ch = *(pf)) != '\0')
+ {
+ if (*pf == ' ')
+ pf++;
+ else
+ break;
+ }
+ pbtn = pf;
+ }
+ }
+ else
+ pf++;
+ }
+
+ if (act < 0)
+ {
+ logf("Error Action Expected: %s", pfield);
+ return -1;
+ }
+ }
+
+ pfield = NULL;
+ }
+
+ pbuf++;
+ }
+ if (field == 2)
+ {
+ count = keymap_add_button_entry(context, act, button, prebtn);
+ if (count > 0)
+ {
+ logf("Added: Ctx: %d, Act: %d, Btn: %d PBtn: %d",
+ context, act, button, prebtn);
+ }
+ }
+ return count;
+}
+
+static int keyremap_import_file(char *filenamebuf, size_t bufsz)
+{
+ size_t bufleft;
+ int count = 0;
+ int line = 0;
+ filenamebuf[bufsz - 1] = '\0';
+ logf("keyremap: import %s", filenamebuf);
+ int fd = rb->open(filenamebuf, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ char ch;
+ char *pbuf;
+ char *pfirst;
+
+ char *pctx = NULL;
+ char *pact;
+ int ctx = -1;
+
+ keyremap_reset_buffer();
+next_line:
+ while (rb->read_line(fd, filenamebuf, (int) bufsz) > 0)
+ {
+ line++;
+
+
+ pbuf = filenamebuf;
+ pfirst = NULL;
+ pact = NULL;
+ char *pcomment = rb->strchr(pbuf, '#');
+ if (pcomment != NULL)
+ {
+ logf("ln: %d: Skipped Comment: %s", line, pcomment);
+ *pcomment = '\0';
+ }
+
+ while ((ch = *(pbuf)) != '\0')
+ {
+ /* PARSE CONTEXT = { */
+ if ((ch >= 'A' && ch <= 'Z') || ch == '_')
+ {
+ if (pfirst == NULL)
+ pfirst = pbuf;
+ }
+ else if (ch == ' ')
+ {
+ if (ctx < 0 && pfirst != NULL)
+ {
+ *pbuf = '\0';
+ pctx = pfirst;
+ pfirst = NULL;
+ }
+ }
+ else if (ch == '=')
+ {
+ if (ctx < 0 && pfirst != NULL)
+ {
+ *pbuf = '\0';
+ pbuf++;
+ pctx = pfirst;
+ pfirst = NULL;
+ }
+ while ((ch = *(pbuf)) != '\0')
+ {
+ if (ch == '{')
+ break;
+ pbuf++;
+ }
+ if (ch == '{' && pctx != NULL)
+ {
+ bufleft = bufsz - (pctx - filenamebuf);
+ ctx = -1;
+ int ctx_x_flag_count = (LAST_CONTEXT_PLACEHOLDER
+ * ARRAYLEN(context_flags));
+
+ for (int i=0;i < ctx_x_flag_count ;i++)
+ {
+ /* context x flag */
+ if (rb->strncasecmp(pctx, ctx_name_and_flag(i), bufleft) == 0)
+ {
+ logf("ln: %d: Context Found: %s (%d)", line, pctx, i);
+ if (keymap_add_context_entry(i) <= 0)
+ logf("ln: %d: Context Exists: %s (%d)", line, pctx, i);
+ ctx = i;
+ goto next_line;
+
+ }
+ }
+ logf("ln: %d: ERROR { Context Expected got: %s", line, pctx);
+ goto fail;
+ }
+ }
+ else if (ch == '}')
+ {
+ if (ctx >= 0)
+ ctx = -1;
+ else
+ {
+ logf("ln: %d: ERROR no context, unexpected close {", line);
+ goto fail;
+ }
+ }
+ else if (ch == '{') /* PARSE FIELDS { ACTION, BUTTON, PREBTN } */
+ {
+ int res = 0;
+ if (ctx >= 0)
+ {
+ pfirst = pbuf;
+
+ while ((ch = *(pbuf)) != '\0')
+ {
+ if (ch == '}')
+ {
+ pact = pfirst + 1;
+ pfirst = NULL;
+ *pbuf = '\0';
+ pbuf = "";
+ continue;
+ }
+ pbuf++;
+ }
+ if (pact != NULL)
+ {
+ bufleft = bufsz - (pact - filenamebuf);
+ logf("ln: %d: Entry Found: {%s} (%d)", line, pact, 0);
+ res = parse_action_import_entry(ctx, pact, bufleft);
+ }
+ }
+ if (res <= 0)
+ {
+ logf("ln: %d: ERROR action entry expected", line);
+ goto fail;
+ }
+ else
+ {
+ pbuf = "";
+ continue;
+ }
+ }
+ else
+ pfirst = NULL;
+ pbuf++;
+ }
+
+ }
+ rb->close(fd);
+ count = ctx_data.ctx_count + ctx_data.act_count;
+ return count;
+
+fail:
+ rb->close(fd);
+ rb->splashf(HZ * 2, "Error @ line %d", line);
+ return 0;
+}
+
+static int keyremap_load_file(const char *filename)
+{
+ logf("keyremap: load %s", filename);
+ int fd = -1;
+ size_t fsize = 0;
+ size_t bytes;
+ struct button_mapping entry;
+ int count = keyremap_open_file(filename, &fd, &fsize);
+ logf("keyremap: entries %d", count);
+ /* actions are indexed from the first entry after the header save this pos */
+ off_t firstpos = rb->lseek(fd, 0, SEEK_CUR);
+ off_t ctxpos = firstpos;
+
+ if (count > 0)
+ {
+ keyremap_reset_buffer();
+ while(--count > 0)
+ {
+ rb->lseek(fd, ctxpos, SEEK_SET); /* next context remap entry */
+ bytes = rb->read(fd, &entry, sizeof(struct button_mapping));
+ ctxpos = rb->lseek(fd, 0, SEEK_CUR);
+ if (bytes != sizeof(struct button_mapping))
+ {
+ count = -10;
+ goto fail;
+ }
+ if (entry.action_code == (int)CONTEXT_STOPSEARCHING)
+ {
+ logf("keyremap: end of context entries ");
+ break;
+ }
+ if ((entry.action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
+ {
+ int context = (entry.action_code & ~CONTEXT_REMAPPED);
+ for (int i = ARRAYLEN(context_flags) - 1; i > 0; i--) /* don't check idx 0*/
+ {
+ /* convert context | flag to context x flag */
+ if ((context & context_flags[i].flag) == context_flags[i].flag)
+ {
+ logf("found ctx flag %s", context_flags[i].name);
+ context &= ~context_flags[i].flag;
+ context += i * LAST_CONTEXT_PLACEHOLDER;
+ }
+ }
+ int offset = entry.button_code;
+ int entries = entry.pre_button_code;
+ if (offset == 0 || entries <= 0)
+ {
+ logf("keyremap: error reading offset");
+ count = -15;
+ goto fail;
+ }
+ logf("keyremap found context: %d file offset: %d entries: %d",
+ context, offset, entries);
+
+ keymap_add_context_entry(context);
+
+ off_t entrypos = firstpos + (offset * sizeof(struct button_mapping));
+ rb->lseek(fd, entrypos, SEEK_SET);
+ for (int i = 0; i < entries; i++)
+ {
+ bytes = rb->read(fd, &entry, sizeof(struct button_mapping));
+ if (bytes == sizeof(struct button_mapping))
+ {
+ int act = entry.action_code;
+ int button = entry.button_code;
+ int prebtn = entry.pre_button_code;
+
+ if (act == (int)CONTEXT_STOPSEARCHING || button == BUTTON_NONE)
+ {
+ logf("keyremap: entry invalid");
+ goto fail;
+ }
+ logf("keyremap: found ctx: %d, act: %d btn: %d pbtn: %d",
+ context, act, button, prebtn);
+ keymap_add_button_entry(context, act, button, prebtn);
+ }
+ else
+ goto fail;
+ }
+ }
+ else
+ {
+ logf("keyremap: Invalid context entry");
+ keyremap_reset_buffer();
+ count = -20;
+ goto fail;
+ }
+ }
+ }
+
+ int entries = 0;
+ struct button_mapping *keymap = keyremap_create_temp(&entries);
+ if (keymap != NULL)
+ {
+ keyset.crc32 =
+ rb->crc_32(keymap, entries * sizeof(struct button_mapping), 0xFFFFFFFF);
+ }
+fail:
+ rb->close(fd);
+ if (count <= 0)
+ rb->splashf(HZ * 2, "Error Loading %sz", filename);
+ return count;
+}
+
+static const struct button_mapping* test_get_context_map(int context)
+{
+ (void)context;
+
+ if (keytest.keymap != NULL && keytest.context >= 0)
+ {
+ int mapidx = keytest.keymap[keytest.index].button_code;
+ int mapcnt = keytest.keymap[keytest.index].pre_button_code;
+ /* make fallthrough to the test context*/
+ keytest.keymap[mapidx + mapcnt].action_code = keytest.context;
+ static const struct button_mapping *testctx[] = { NULL };
+ testctx[0] = &keytest.keymap[mapidx];
+ return testctx[0];
+ }
+ else
+ return NULL;
+}
+
+static const char *kmffiles_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ /* found kmf filenames returned by each call kmffiles_dirp keeps state
+ * selected_item = 0 resets state */
+
+ (void)data;
+ buf[0] = '\0';
+ if (selected_item == 0)
+ {
+ rb->closedir(kmffiles_dirp);
+ kmffiles_dirp = rb->opendir(KMFDIR);
+ }
+ if (kmffiles_dirp != NULL)
+ {
+ struct dirent *entry;
+ while ((entry = rb->readdir(kmffiles_dirp)))
+ {
+ /* skip directories */
+ if ((rb->dir_get_info(kmffiles_dirp, entry).attribute & ATTR_DIRECTORY) != 0)
+ continue;
+ if (keyremap_check_extension(entry->d_name) > 0)
+ {
+ rb->snprintf(buf, buf_len, "%s", entry->d_name);
+ return buf;
+ }
+ }
+ }
+ return "Error!";
+}
+
+static void menu_useract_set_positions(void)
+{
+ /* responsible for item ordering to display action edit interface */
+ int display_pos = 0; /* start at item 0*/
+ int i, j;
+ for (i = 0; i < ctx_data.ctx_count; i++)
+ {
+ int context = ctx_data.ctx_map[i].context;
+ ctx_data.ctx_map[i].display_pos = display_pos++;
+ /* how many actions are contained in this context? */
+ for (j = 0; j < ctx_data.act_count; j++)
+ {
+ if (ctx_data.act_map[j].context == context)
+ {
+ ctx_data.act_map[j].display_pos = display_pos++;
+ }
+ }
+ }
+}
+
+static const char *menu_useract_items_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ static char buf_button[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
+ static char buf_prebtn[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
+ int i;
+ int szbtn = sizeof("BUTTON");
+ int szctx = sizeof("CONTEXT");
+ int szact = sizeof("ACTION");
+ const char* ctxfmt = "%s";
+
+ if (data == MENU_ID(M_EXPORTKEYS))
+ {
+ szbtn = 0;
+ szctx = 0;
+ szact = 0;
+ ctxfmt = "$%s = {\n\n";
+ }
+ buf[0] = '\0';
+ for(i = 0; i < ctx_data.ctx_count; i ++)
+ {
+ if (ctx_data.ctx_map[i].display_pos == selected_item)
+ {
+ if (ctx_data.act_count == 0)
+ rb->snprintf(buf, buf_len, "%s$%s",
+ ctx_name_and_flag(ctx_data.ctx_map[i].context),
+ "Select$to add$actions");
+ else
+ rb->snprintf(buf, buf_len, ctxfmt, ctx_name_and_flag(ctx_data.ctx_map[i].context));
+ return buf;
+ }
+ }
+ for(i = 0; i < ctx_data.act_count; i ++)
+ {
+ if (ctx_data.act_map[i].display_pos == selected_item)
+ {
+ int context = ctx_data.act_map[i].context;
+ char ctxbuf[action_helper_maxbuffer];
+ char *pctxbuf = "\0";
+ char *pactname;
+ if (data != MENU_ID(M_EXPORTKEYS))
+ {
+ pctxbuf = ctxbuf;
+ rb->snprintf(ctxbuf, sizeof(ctxbuf), ctxfmt, ctx_name_and_flag(context));
+ pctxbuf += szctx;//sizeof("CONTEXT")
+ }
+ struct button_mapping * bm = &ctx_data.act_map[i].map;
+ pactname = action_name(bm->action_code);
+ pactname += szact;//sizeof("ACTION")
+ /* BUTTON & PRE_BUTTON */
+ btnval_to_name(buf_button, sizeof(buf_button), bm->button_code);
+ btnval_to_name(buf_prebtn, sizeof(buf_prebtn), bm->pre_button_code);
+
+ rb->snprintf(buf, buf_len, ctx_menu_data.act_fmt, pctxbuf,
+ pactname, buf_button + szbtn, buf_prebtn + szbtn);
+ return buf;
+ }
+ }
+ return "Error!";
+}
+
+static const char *edit_keymap_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ buf[0] = '\0';
+ if (selected_item == 0)
+ {
+ rb->snprintf(buf, buf_len, "Add Context");
+ ctx_menu_data.act_index = -1;
+ ctx_menu_data.ctx_index = -1;
+ ctx_menu_data.act_fmt = ACTIONFMT_LV0;
+ }
+ else if (ctx_data.ctx_count > 0)
+ {
+ return menu_useract_items_cb(selected_item - 1, data, buf, buf_len);
+ }
+ return buf;
+}
+
+static const char *test_keymap_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ (void)data;
+ buf[0] = '\0';
+ if (keytest.context >= 0)
+ {
+ if (selected_item == 0)
+ rb->snprintf(buf, buf_len, "< %s >", ctx_name_and_flag(keytest.context));
+ else if (selected_item == 1)
+ {
+ if (keytest.countdown >= 10)
+ rb->snprintf(buf, buf_len, "Testing %d", keytest.countdown / 10);
+ else
+ rb->snprintf(buf, buf_len, "Start test");
+ }
+ else if (selected_item == 2)
+ rb->snprintf(buf, buf_len, "%s", action_name(keytest.action));
+ }
+ return buf;
+}
+
+static const char* list_get_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ buf[0] = '\0';
+ const struct mainmenu *cur = (struct mainmenu *) data;
+ if (data == MENU_ID(M_ROOT))
+ return mainitem(selected_item + 1)->name;
+ else if (selected_item >= cur->items - 1)
+ {
+ return ID2P(LANG_BACK);
+ }
+ if (data == MENU_ID(M_SETKEYS))
+ {
+ return edit_keymap_name_cb(selected_item, data, buf, buf_len);
+ }
+ else if (data == MENU_ID(M_BUTTONS))
+ {
+ const struct available_button *btn = &available_buttons[selected_item];
+ rb->snprintf(buf, buf_len, "%s: [0x%X] ", btn->name, (unsigned int) btn->value);
+ return buf;
+ }
+ else if (data == MENU_ID(M_ACTIONS))
+ {
+ return action_name(selected_item);
+ }
+ else if (data == MENU_ID(M_CONTEXTS))
+ {
+ return ctx_name_and_flag(selected_item);
+ }
+ else if (data == MENU_ID(M_DELKEYS) || data == MENU_ID(M_LOADKEYS))
+ {
+ /* need to iterate the callback for the items off screen to
+ * keep ordering, this limits the menu to only the main screen :( */
+ int start_item = lists.start_item[SCREEN_MAIN];
+ if (start_item != 0 && start_item == selected_item)
+ {
+ for (int i = 0; i < start_item; i++)
+ kmffiles_name_cb(i, data, buf, buf_len);
+ }
+ return kmffiles_name_cb(selected_item, data, buf, buf_len);
+ }
+ else if (data == MENU_ID(M_TESTKEYS))
+ {
+ return test_keymap_name_cb(selected_item, data, buf, buf_len);
+ }
+ return buf;
+}
+
+static int list_voice_cb(int list_index, void* data)
+{
+ if (!rb->global_settings->talk_menu)
+ return -1;
+
+ if (data == MENU_ID(M_ROOT))
+ {
+ const char * name = mainitem(list_index)->name;
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ else if(data == MENU_ID(M_SETKEYS))
+ {
+ char buf[MAX_MENU_NAME];
+ int selcol = printcell_get_column_selected();
+ const char* name = printcell_get_column_text(selcol, buf, sizeof(buf));
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ else
+ {
+ char buf[MAX_MENU_NAME];
+ const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf));
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ return 0;
+}
+
+int menu_action_root(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+#ifdef ROCKBOX_HAS_LOGF
+ char logfnamebuf[64];
+#endif
+
+ if (*action == ACTION_STD_OK)
+ {
+ struct mainmenu *cur = mainitem(selected_item + 1);
+ if (cur != NULL)
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ lang_strlcpy(logfnamebuf, cur->name, sizeof(logfnamebuf));
+ logf("Root select menu -> %s(%d) ", logfnamebuf, cur->index);
+#endif
+ if (cur->menuid == MENU_ID(M_SETKEYS))
+ {
+ keyset.view_lastcol = -1;
+ }
+ else if (cur->menuid == MENU_ID(M_TMPCORE))
+ {
+ int entry_count;/* (ctx_count + ctx_count + act_count + 1) */
+ struct button_mapping *keymap = keyremap_create_temp(&entry_count);
+ if (rb->core_set_keyremap(keymap, entry_count) >= 0)
+ rb->splash(HZ *2, "Keymap Applied");
+ else
+ rb->splash(HZ *2, "Error Applying");
+
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_SETCORE))
+ {
+ if (rb->file_exists(CORE_KEYREMAP_FILE) && 0 == core_savecount++)
+ {
+ rb->rename(CORE_KEYREMAP_FILE, CORE_KEYREMAP_FILE".old"); /* make a backup */
+ }
+ if (keyremap_save_current(CORE_KEYREMAP_FILE) == 0)
+ rb->splash(HZ *2, "Error Saving");
+ else
+ {
+ rb->splash(HZ *2, "Saved");
+ int entry_count;/* (ctx_count + ctx_count + act_count + 1) */
+ struct button_mapping *keymap = keyremap_create_temp(&entry_count);
+ rb->core_set_keyremap(keymap, entry_count);
+ }
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_DELCORE))
+ {
+ rb->core_set_keyremap(NULL, -1);
+ if (rb->file_exists(CORE_KEYREMAP_FILE))
+ {
+ rb->rename(CORE_KEYREMAP_FILE, KMFDIR "/core_deleted" KMFEXT2);
+ rb->splash(HZ *2, "Removed");
+ }
+ else
+ rb->splash(HZ *2, "Error Removing");
+
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_SAVEKEYS))
+ {
+ keyremap_save_user_keys(true);
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_EXPORTKEYS))
+ {
+ keyremap_export_user_keys();
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_IMPORTKEYS))
+ {
+ keyremap_import_user_keys();
+ goto default_handler;
+ }
+ else if (cur->menuid == MENU_ID(M_DELKEYS) ||
+ cur->menuid == MENU_ID(M_LOADKEYS))
+ {
+ cur->items = keyremap_count_files(KMFDIR) + 1;
+ }
+ else if (cur->menuid == MENU_ID(M_TESTKEYS))
+ {
+ int entries = 0;
+ keytest.keymap = keyremap_create_temp(&entries);
+ if (entries > 0)
+ {
+ struct button_mapping *entry = &keytest.keymap[0];
+ if (entry->action_code != (int)CONTEXT_STOPSEARCHING
+ && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
+ {
+ keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
+ }
+ else
+ keytest.context = -1;
+ }
+ else
+ {
+ keytest.keymap = NULL;
+ keytest.context = -1;
+ }
+ keytest.action = ACTION_NONE;
+ keytest.countdown = 0;
+ }
+ else if (cur->menuid == MENU_ID(M_RESETKEYS))
+ {
+ if (rb->yesno_pop("Delete Current Entries?") == true)
+ {
+ keyremap_reset_buffer();
+ }
+ goto default_handler;
+ }
+ }
+
+ if (cur->menuid == NULL || cur->menuid == MENU_ID(M_EXIT))
+ {
+ logf("Root menu %s", (cur->menuid) == NULL ? "NULL":"Exit");
+ *action = ACTION_STD_CANCEL;
+ *exit = true;
+ }
+ else
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ lang_strlcpy(logfnamebuf, cur->name, sizeof(logfnamebuf));
+ logf("Root load menu -> %s(%d) ", logfnamebuf, cur->index);
+#endif
+ synclist_set_update(cur->index, 0, cur->items, 1);
+ rb->gui_synclist_draw(lists);
+ *action = ACTION_NONE;
+ }
+ }
+
+ return PLUGIN_OK;
+default_handler:
+ return GOTO_ACTION_DEFAULT_HANDLER;
+}
+
+int menu_action_setkeys(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ (void) exit;
+ int i;
+ struct mainmenu *cur = (struct mainmenu *)lists->data;
+ if (*action == ACTION_STD_OK)
+ {
+ if (selected_item == 0) /*add_context*/
+ {
+ const struct mainmenu *mainm = &mainmenu[M_CONTEXTS];
+ synclist_set_update(mainm->index, 0, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ goto default_handler;
+ }
+ else if (selected_item < lists->nb_items - 1)/* not back*/
+ {
+ bool add_action = false;
+ for (i = 0; i < ctx_data.ctx_count; i++)
+ {
+ if (ctx_data.ctx_map[i].display_pos == selected_item - 1)
+ {
+ add_action = true;
+ break;
+ }
+ }
+
+ if (add_action)
+ {
+ keymap_add_button_entry(ctx_data.ctx_map[i].context, ACTION_NONE, BUTTON_NONE, BUTTON_NONE);
+ cur->items++;
+ lists->nb_items++;
+ goto default_handler;
+ }
+ else
+ {
+ keyset.view_lastcol = printcell_increment_column(1, true);
+ *action = ACTION_NONE;
+ }
+ }
+ }
+ else if (*action == ACTION_STD_CANCEL)
+ {
+ keyset.view_lastcol = printcell_increment_column(-1, true);
+ if (keyset.view_lastcol != keyset.view_columns - 1)
+ {
+ *action = ACTION_NONE;
+ }
+ }
+ else if (*action == ACTION_STD_CONTEXT)
+ {
+ int col = keyset.view_lastcol;
+ for (i = 0; i < ctx_data.act_count; i++)
+ {
+ if (ctx_data.act_map[i].display_pos == selected_item - 1)
+ {
+ int context = ctx_data.act_map[i].context;
+ int act = ctx_data.act_map[i].map.action_code;
+ int button = ctx_data.act_map[i].map.button_code;
+ int prebtn = ctx_data.act_map[i].map.pre_button_code;
+
+ if (col < 0)
+ {
+ rb->splashf(HZ, "short press increments columns");
+
+ }
+ else if (col == 0) /* Context */
+ {
+ const struct mainmenu *mainm = &mainmenu[M_CONTEXTS];
+ synclist_set_update(mainm->index, context, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ goto default_handler;
+
+ }
+ else if (col == 1) /* Action */
+ {
+ const struct mainmenu *mainm = &mainmenu[M_ACTIONS];
+ synclist_set_update(mainm->index, act, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ goto default_handler;
+ }
+ else if (col == 2) /* Button */
+ {
+ const struct mainmenu *mainm = &mainmenu[M_BUTTONS];
+ int btnidx = btnval_to_index(button);
+ synclist_set_update(mainm->index, btnidx, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ goto default_handler;
+ }
+ else if (col == 3) /* PreBtn */
+ {
+ const struct mainmenu *mainm = &mainmenu[M_BUTTONS];
+ int pbtnidx = btnval_to_index(prebtn);
+ synclist_set_update(mainm->index, pbtnidx, mainm->items, 1);
+ rb->gui_synclist_draw(lists);
+ goto default_handler;
+ }
+ }
+ }
+ }
+ return PLUGIN_OK;
+default_handler:
+ return GOTO_ACTION_DEFAULT_HANDLER;
+}
+
+int menu_action_testkeys(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ (void) exit;
+ (void) lists;
+ static int last_count = 0;
+ if (keytest.keymap != NULL && keytest.countdown >= 10 && keytest.context >= 0)
+ {
+ int newact = rb->get_custom_action(CONTEXT_PLUGIN, HZ / 10, test_get_context_map);
+ if (newact == ACTION_NONE)
+ {
+ keytest.countdown--;
+ if (last_count - keytest.countdown > 10)
+ keytest.action = newact;
+ }
+ else
+ {
+ last_count = keytest.countdown;
+ keytest.action = newact;
+ }
+ *action = ACTION_REDRAW;
+ goto default_handler;
+ }
+
+ if (*action == ACTION_STD_CANCEL && selected_item == 0 && keytest.keymap != NULL)
+ {
+ keytest.index -= 2;
+ if (keytest.index < 0)
+ keytest.index = 0;
+ *action = ACTION_STD_OK;
+ }
+
+ if (*action == ACTION_STD_OK)
+ {
+ if (selected_item == 0)
+ {
+ if (keytest.keymap != NULL)
+ {
+ struct button_mapping *entry = &keytest.keymap[++keytest.index];
+ if (entry->action_code != (int) CONTEXT_STOPSEARCHING
+ && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
+ {
+ keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
+ }
+ else
+ {
+ entry = &keytest.keymap[0];
+ if (entry->action_code != (int)CONTEXT_STOPSEARCHING
+ && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
+ {
+ keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
+ keytest.index = 0;
+ }
+ else
+ {
+ keytest.keymap = NULL;
+ keytest.context = -1;
+ keytest.index = -1;
+ keytest.action = ACTION_NONE;
+ keytest.countdown = 0;
+ }
+ }
+ }
+ }
+ else if (selected_item == 1)
+ {
+ keytest.countdown = TEST_COUNTDOWN_MS / 10;
+ keytest.action = ACTION_NONE;
+ }
+ }
+
+ return PLUGIN_OK;
+default_handler:
+ return GOTO_ACTION_DEFAULT_HANDLER;
+}
+
+int menu_action_listfiles(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ (void) exit;
+ int i;
+ if (*action == ACTION_STD_OK)
+ {
+ struct mainmenu *cur = (struct mainmenu *)lists->data;
+ if (selected_item >= (cur->items) - 1)/*back*/
+ {
+ *action = ACTION_STD_CANCEL;
+ }
+ else
+ {
+ /* iterate to the desired file */
+ char buf[MAX_PATH];
+ int len = rb->snprintf(buf, sizeof(buf), "%s/", KMFDIR);
+ if (len < (int) sizeof(buf))
+ {
+ char *name = &buf[len];
+ size_t bufleft = sizeof(buf) - len;
+ list_get_name_cb(0, lists->data, name, bufleft);
+ for (i = 1; i <= selected_item; i++)
+ {
+ list_get_name_cb(i, lists->data, name, bufleft);
+ }
+
+ if (lists->data == MENU_ID(M_DELKEYS))
+ {
+ const char *lines[] = {ID2P(LANG_DELETE), buf};
+ const struct text_message message={lines, 2};
+ if (rb->gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES)
+ {
+ rb->remove(buf);
+ cur->items--;
+ lists->nb_items--;
+ *action = ACTION_NONE;
+ }
+ }
+ else if (lists->data == MENU_ID(M_LOADKEYS))
+ {
+ if (keyremap_load_file(buf) > 0)
+ {
+ rb->splashf(HZ * 2, "Loaded %s ", buf);
+ *action = ACTION_STD_CANCEL;
+ }
+ }
+ }
+ }
+ }
+ return PLUGIN_OK;
+}
+
+int menu_action_submenus(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+#ifdef ROCKBOX_HAS_LOGF
+ char logfnamebuf[64];
+#endif
+ (void) exit;
+ if (*action == ACTION_STD_OK)
+ {
+ struct mainmenu *cur = (struct mainmenu *)lists->data;
+ if (selected_item >= (cur->items) - 1)/*back*/
+ {
+ *action = ACTION_STD_CANCEL;
+ }
+ else if (lists->data == MENU_ID(M_ACTIONS))
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
+ logf("ACT %s %d (0x%X)", name, selected_item, selected_item);
+#endif
+ int id, item;
+ POP_MENU(id, item);
+ POP_MENU(id, item);
+ const struct mainmenu *lastm = &mainmenu[id];
+
+ if (id == M_SETKEYS)
+ {
+ for (int i = 0; i < ctx_data.act_count; i++)
+ {
+ if (ctx_data.act_map[i].display_pos == item - 2)
+ {
+ int col = keyset.view_lastcol;
+ struct button_mapping * bm = &ctx_data.act_map[i].map;
+ if (col == 1) /* Action */
+ {
+ bm->action_code = selected_item;
+ }
+ break;
+ }
+ }
+ }
+ synclist_set(lastm->index, item-1, lastm->items, 1);
+ rb->gui_synclist_draw(lists);
+ }
+ else if (lists->data == MENU_ID(M_CONTEXTS))
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
+ logf("CTX %s %d (0x%X)", name, selected_item, selected_item);
+#endif
+ int id, item;
+ POP_MENU(id, item);
+ POP_MENU(id, item);
+ struct mainmenu *lastm = &mainmenu[id];
+ if (id == M_SETKEYS)
+ {
+ bool found = false;
+ int newctx = selected_item;
+
+ for (int i = 0; i < ctx_data.act_count; i++)
+ {
+ if (ctx_data.act_map[i].display_pos == item - 2)
+ {
+ int col = keyset.view_lastcol;
+ if (col == 0) /* Context */
+ {
+ ctx_data.act_map[i].context = newctx;
+ /* check if this context exists (if not create it) */
+ for (int j = 0; j < ctx_data.ctx_count; j++)
+ {
+ if (ctx_data.ctx_map[j].context == newctx)
+ {
+ found = true;;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ if (found == false)
+ {
+ keymap_add_context_entry(newctx);
+ lastm->items++;
+ }
+ }
+ synclist_set(lastm->index, item-1, lastm->items, 1);
+ rb->gui_synclist_draw(lists);
+ }
+ else if (lists->data == MENU_ID(M_BUTTONS))
+ {
+#ifdef ROCKBOX_HAS_LOGF
+ const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
+ logf("BTN %s", name);
+#endif
+ int id, item;
+ POP_MENU(id, item);
+ POP_MENU(id, item);
+ const struct mainmenu *lastm = &mainmenu[id];
+
+ if (id == M_SETKEYS)
+ {
+ for (int i = 0; i < ctx_data.act_count; i++)
+ {
+ if (ctx_data.act_map[i].display_pos == item - 2)
+ {
+ int col = keyset.view_lastcol;
+ struct button_mapping * bm = &ctx_data.act_map[i].map;
+ const struct available_button *btn = &available_buttons[selected_item];
+ if (col == 2) /* BUTTON*/
+ {
+ if (btn->value == BUTTON_NONE)
+ bm->button_code = btn->value;
+ else
+ bm->button_code |= btn->value;
+ }
+ else if (col == 3) /* PREBTN */
+ {
+ if (btn->value == BUTTON_NONE)
+ bm->pre_button_code = btn->value;
+ else
+ bm->pre_button_code |= btn->value;
+ }
+ break;
+ }
+ }
+ }
+ synclist_set(lastm->index, item-1, lastm->items, 1);
+ rb->gui_synclist_draw(lists);
+ }
+ }
+ return PLUGIN_OK;
+}
+
+static void cleanup(void *parameter)
+{
+ (void)parameter;
+ keyremap_save_user_keys(false);
+}
+
+int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ int status = PLUGIN_OK;
+ /* Top Level Menu Actions */
+ if (lists->data == MENU_ID(M_ROOT))
+ {
+ status = menu_action_root(action, selected_item, exit, lists);
+ }
+ else if (lists->data == MENU_ID(M_SETKEYS))
+ {
+ status = menu_action_setkeys(action, selected_item, exit, lists);
+ }
+ else if (lists->data == MENU_ID(M_TESTKEYS))
+ {
+ status = menu_action_testkeys(action, selected_item, exit, lists);
+ }
+ else if (lists->data == MENU_ID(M_DELKEYS))
+ {
+ status = menu_action_listfiles(action, selected_item, exit, lists);
+ }
+ else if (lists->data == MENU_ID(M_LOADKEYS))
+ {
+ status = menu_action_listfiles(action, selected_item, exit, lists);
+ }
+
+ if (status == GOTO_ACTION_DEFAULT_HANDLER)
+ goto default_handler;
+ /* Submenu Actions */
+ menu_action_submenus(action, selected_item, exit, lists);
+
+ /* Global cancel */
+ if (*action == ACTION_STD_CANCEL)
+ {
+ logf("CANCEL BUTTON");
+ if (lists->data == MENU_ID(M_TESTKEYS))
+ {
+ keytest.keymap = NULL;
+ keytest.context = -1;
+ }
+
+ if (lists->data != MENU_ID(M_ROOT))
+ {
+ int id, item;
+ POP_MENU(id, item);
+ POP_MENU(id, item);
+ const struct mainmenu *lastm = &mainmenu[id];
+ synclist_set(lastm->index, item-1, lastm->items, 1);
+ rb->gui_synclist_draw(lists);
+ }
+ else
+ {
+ /* save changes? */
+ if (ctx_data.act_count + ctx_data.ctx_count >= 2)
+ {
+ int entries = 0;
+ struct button_mapping *keymap = keyremap_create_temp(&entries);
+ if (keymap != NULL && keyset.crc32 != rb->crc_32(keymap,
+ entries * sizeof(struct button_mapping), 0xFFFFFFFF))
+ {
+ if (rb->yesno_pop("Save Keymap?") == true)
+ {
+ keyremap_save_user_keys(true);
+ goto default_handler;
+ }
+ }
+ }
+ *exit = true;
+ }
+ }
+default_handler:
+ if (rb->default_event_handler_ex(*action, cleanup, NULL) == SYS_USB_CONNECTED)
+ {
+ *exit = true;
+ return PLUGIN_USB_CONNECTED;
+ }
+ return PLUGIN_OK;
+}
+
+static void synclist_set(int id, int selected_item, int items, int sel_size)
+{
+ void* menu_id = MENU_ID(id);
+ static char menu_title[64]; /* title is used by every menu */
+ PUSH_MENU(id, selected_item);
+
+ if (items <= 0)
+ return;
+ if (selected_item < 0)
+ selected_item = 0;
+
+ list_voice_cb(0, menu_id);
+ rb->gui_synclist_init(&lists,list_get_name_cb,
+ menu_id, false, sel_size, NULL);
+
+ rb->gui_synclist_set_icon_callback(&lists,NULL);
+ rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
+ rb->gui_synclist_set_nb_items(&lists,items);
+ rb->gui_synclist_select_item(&lists, selected_item);
+ printcell_enable(false);
+
+ if (menu_id == MENU_ID(M_ROOT))
+ {
+ lang_strlcpy(menu_title, mainmenu[M_ROOT].name, sizeof(menu_title));
+ rb->gui_synclist_set_title(&lists, menu_title, Icon_Plugin);
+ }
+ else if (menu_id == MENU_ID(M_SETKEYS))
+ {
+ keyset.view_columns = printcell_set_columns(&lists, NULL,
+ ACTVIEW_HEADER, Icon_Rockbox);
+ printcell_enable(true);
+ int curcol = printcell_get_column_selected();
+ if (keyset.view_lastcol >= keyset.view_columns)
+ keyset.view_lastcol = -1;
+ /* restore column position */
+ while (keyset.view_lastcol > -1 && curcol != keyset.view_lastcol)
+ {
+ curcol = printcell_increment_column(1, true);
+ }
+ keyset.view_lastcol = curcol;
+ }
+ else
+ {
+ int id;
+ PEEK_MENU_ID(id);
+ lang_strlcpy(menu_title, mainitem(id)->name, sizeof(menu_title));
+ rb->gui_synclist_set_title(&lists, menu_title, Icon_Submenu_Entered);
+ /* if (menu_title[0] == '$'){ printcell_enable(true); } */
+ }
+}
+
+static void keyremap_set_buffer(void* buffer, size_t buf_size)
+{
+ /* set up the keyremap action buffer
+ * contexts start at the front and work towards the back
+ * actions start at the back and work towards front
+ * if they meet buffer is out of space (checked by ctx & btn add entry fns)
+ */
+ keyremap_buffer.buffer = buffer;
+ keyremap_buffer.buf_size = buf_size;
+ keyremap_reset_buffer();
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ int ret = PLUGIN_OK;
+ int selected_item = -1;
+ int action;
+ bool redraw = true;
+ bool exit = false;
+ if (parameter)
+ {
+ //
+ }
+
+ size_t buf_size;
+ void* buffer = rb->plugin_get_buffer(&buf_size);
+ keyremap_set_buffer(buffer, buf_size);
+
+ mainmenu[M_BUTTONS].items = available_button_count;
+ /* add back item to each submenu */
+ for (int i = 1; i < M_LAST_ITEM; i++)
+ mainmenu[i].items += 1;
+
+#if 0
+ keymap_add_context_entry(CONTEXT_WPS);
+ keymap_add_button_entry(CONTEXT_WPS, ACTION_WPS_PLAY, BUTTON_VOL_UP, BUTTON_NONE);
+ keymap_add_button_entry(CONTEXT_WPS, ACTION_WPS_STOP, BUTTON_VOL_DOWN | BUTTON_REPEAT, BUTTON_NONE);
+
+ keymap_add_context_entry(CONTEXT_MAINMENU);
+ keymap_add_button_entry(CONTEXT_MAINMENU, ACTION_STD_PREV, BUTTON_VOL_UP, BUTTON_NONE);
+ keymap_add_button_entry(CONTEXT_MAINMENU, ACTION_STD_NEXT, BUTTON_VOL_DOWN, BUTTON_NONE);
+
+ keymap_add_context_entry(CONTEXT_STD);
+ keymap_add_button_entry(CONTEXT_STD, ACTION_STD_OK, BUTTON_VOL_UP, BUTTON_NONE);
+ keymap_add_button_entry(CONTEXT_STD, ACTION_STD_OK, BUTTON_VOL_DOWN, BUTTON_NONE);
+#endif
+
+ keyset.crc32 = 0;
+ keyset.view_lastcol = -1;
+ if (!exit)
+ {
+ const struct mainmenu *mainm = &mainmenu[M_ROOT];
+ synclist_set(mainm->index, 0, mainm->items, 1);
+ rb->gui_synclist_draw(&lists);
+
+ while (!exit)
+ {
+ action = rb->get_action(CONTEXT_LIST, HZ / 10);
+
+ if (action == ACTION_NONE)
+ {
+ if (redraw)
+ {
+ action = ACTION_REDRAW;
+ redraw = false;
+ }
+ }
+ else
+ redraw = true;
+
+ ret = menu_action_cb(&action, selected_item, &exit, &lists);
+ if (rb->gui_synclist_do_button(&lists, &action))
+ continue;
+ selected_item = rb->gui_synclist_get_sel_pos(&lists);
+
+ mainmenu[M_SETKEYS].items = ctx_data.ctx_count + ctx_data.act_count + 2;
+ }
+ }
+ rb->closedir(kmffiles_dirp);
+
+ return ret;
+}
+
+
+
+#if 0 /* Alt Example */
+static int write_keyremap(const char*filename, struct button_mapping *remap_data, int count)
+{
+ size_t res = 0;
+ if (!filename || !remap_data || count <= 0)
+ goto fail;
+ int fd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ goto fail;
+ if (keyremap_write_header(fd, count + 1) > 0)
+ {
+ res = keyremap_write_entries(fd, remap_data, count);
+ res += sizeof(struct button_mapping); /*for header */
+ }
+fail:
+ rb->close(fd);
+ return res;
+}
+
+void alt_example(void)
+{
+ struct button_mapping kmap[8];
+
+ kmap[0].action_code = CORE_CONTEXT_REMAP(CONTEXT_WPS);
+ kmap[0].button_code = 3; /*OFFSET*/
+ kmap[0].pre_button_code = 1; /*COUNT*/
+
+ kmap[1].action_code = CORE_CONTEXT_REMAP(CONTEXT_MAINMENU);
+ kmap[1].button_code = 5; /*OFFSET*/
+ kmap[1].pre_button_code = 2; /*COUNT*/
+
+ kmap[2].action_code = CONTEXT_STOPSEARCHING;
+ kmap[2].button_code = BUTTON_NONE;
+ kmap[2].pre_button_code = BUTTON_NONE;
+
+ kmap[3].action_code = ACTION_WPS_PLAY;
+ kmap[3].button_code = BUTTON_VOL_UP;
+ kmap[3].pre_button_code = BUTTON_NONE;
+
+ kmap[4].action_code = CONTEXT_STOPSEARCHING;
+ kmap[4].button_code = BUTTON_NONE;
+ kmap[4].pre_button_code = BUTTON_NONE;
+
+ kmap[5].action_code = ACTION_STD_NEXT;
+ kmap[5].button_code = BUTTON_VOL_DOWN;
+ kmap[5].pre_button_code = BUTTON_NONE;
+
+ kmap[6].action_code = ACTION_STD_PREV;
+ kmap[6].button_code = BUTTON_VOL_UP;
+ kmap[6].pre_button_code = BUTTON_NONE;
+
+ kmap[7].action_code = CONTEXT_STOPSEARCHING;
+ kmap[7].button_code = BUTTON_NONE;
+ kmap[7].pre_button_code = BUTTON_NONE;
+
+ write_keyremap(CORE_KEYREMAP_FILE, kmap, 8);
+
+ return ret;
+}
+#endif
diff --git a/apps/plugins/lamp.c b/apps/plugins/lamp.c
index 6c9ae6626d..5aa06f4d2d 100644
--- a/apps/plugins/lamp.c
+++ b/apps/plugins/lamp.c
@@ -58,7 +58,14 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#define LAMP_EXIT PLA_EXIT
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define LAMP_EXIT2 PLA_UP
+#else
#define LAMP_EXIT2 PLA_CANCEL
+#endif
#ifdef HAVE_LCD_COLOR
@@ -103,9 +110,8 @@ enum plugin_status plugin_start(const void* parameter)
int current_brightness = MAX_BRIGHTNESS_SETTING;
backlight_brightness_set(MAX_BRIGHTNESS_SETTING);
#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
+
buttonlight_brightness_set(MAX_BRIGHTNESS_SETTING);
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#ifdef HAVE_LCD_INVERT
#ifdef HAVE_NEGATIVE_LCD
@@ -116,9 +122,8 @@ enum plugin_status plugin_start(const void* parameter)
#endif /* HAVE_LCD_INVERT */
backlight_force_on();
-#ifdef HAVE_BUTTON_LIGHT
buttonlight_force_on();
-#endif /* HAVE_BUTTON_LIGHT */
+
rb->lcd_clear_display();
rb->lcd_update();
@@ -207,20 +212,17 @@ enum plugin_status plugin_start(const void* parameter)
/* restore */
backlight_use_settings();
-#ifdef HAVE_BUTTON_LIGHT
+
buttonlight_use_settings();
-#endif /* HAVE_BUTTON_LIGHT */
#ifdef HAVE_LCD_INVERT
rb->lcd_set_invert_display(rb->global_settings->invert);
#endif /* HAVE_LCD_INVERT */
-#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+
backlight_brightness_use_setting();
-#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
+
buttonlight_brightness_use_setting();
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#if LCD_DEPTH > 1
rb->lcd_set_background(bg_color);
diff --git a/apps/plugins/lastfm_scrobbler.c b/apps/plugins/lastfm_scrobbler.c
new file mode 100644
index 0000000000..2c0597656c
--- /dev/null
+++ b/apps/plugins/lastfm_scrobbler.c
@@ -0,0 +1,938 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 Robert Keevil
+ * Converted to Plugin
+ * Copyright (C) 2022 William Wilgus
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+/* Scrobbler Plugin
+Audioscrobbler spec at: (use wayback machine)
+http://www.audioscrobbler.net/wiki/Portable_Player_Logging
+* EXCERPT:
+* The first lines of .scrobbler.log should be header lines, indicated by the leading '#' character:
+
+#AUDIOSCROBBLER/1.1
+#TZ/[UNKNOWN|UTC]
+#CLIENT/<IDENTIFICATION STRING>
+
+Where 1.1 is the version for this file format
+
+ If the device knows what timezone it is in,
+ it must convert all logged times to UTC (aka GMT+0)
+ eg: #TZ/UTC
+ If the device knows the time, but not the timezone
+ eg: #TZ/UNKNOWN
+
+<IDENTIFICATION STRING> should be replaced by the name/model of the hardware device
+ and the revision of the software producing the log file.
+
+After the header lines, simply append one line of text for every song
+ that is played or skipped.
+
+The following fields comprise each line, and are tab (\t)
+ separated (strip any tab characters from the data):
+
+ - artist name
+ - album name (optional)
+ - track name
+ - track position on album (optional)
+ - song duration in seconds
+ - rating (L if listened at least 50% or S if skipped)
+ - unix timestamp when song started playing
+ - MusicBrainz Track ID (optional)
+lines should be terminated with \n
+Example
+(listened to enter sandman, skipped cowboys, listened to the pusher) :
+ #AUDIOSCROBBLER/1.0
+ #TZ/UTC
+ #CLIENT/Rockbox h3xx 1.1
+ Metallica Metallica Enter Sandman 1 365 L 1143374412 62c2e20a?-559e-422f-a44c-9afa7882f0c4?
+ Portishead Roseland NYC Live Cowboys 2 312 S 1143374777 db45ed76-f5bf-430f-a19f-fbe3cd1c77d3
+ Steppenwolf Live The Pusher 12 350 L 1143374779 58ddd581-0fcc-45ed-9352-25255bf80bfb?
+ If the data for optional fields is not available to you, leave the field blank (\t\t).
+ All strings should be written as UTF-8, although the file does not use a BOM.
+ All fields except those marked (optional) above are required.
+*/
+
+#include "plugin.h"
+#include "lib/configfile.h"
+
+#ifndef UNTAGGED
+ #define UNTAGGED "<UNTAGGED>"
+#endif
+
+#ifdef ROCKBOX_HAS_LOGF
+#define logf rb->logf
+#else
+#define logf(...) do { } while(0)
+#endif
+
+/****************** constants ******************/
+#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF)
+#define EV_FLUSHCACHE MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFE)
+#define EV_USER_ERROR MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFD)
+#define EV_STARTUP MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x01)
+#define EV_TRACKCHANGE MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x02)
+#define EV_TRACKFINISH MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x03)
+
+#define ERR_NONE (0)
+#define ERR_WRITING_FILE (-1)
+#define ERR_ENTRY_LENGTH (-2)
+#define ERR_WRITING_DATA (-3)
+
+/* increment this on any code change that effects output */
+#define SCROBBLER_VERSION "1.1"
+
+#define SCROBBLER_REVISION " $Revision$"
+
+#define SCROBBLER_BAD_ENTRY "# FAILED - "
+
+/* longest entry I've had is 323, add a safety margin */
+#define SCROBBLER_CACHE_LEN (512)
+#define SCROBBLER_MAX_CACHE (32 * SCROBBLER_CACHE_LEN)
+
+#define SCROBBLER_MAX_TRACK_MRU (32) /* list of hashes to detect repeats */
+
+#define ITEM_HDR "#ARTIST #ALBUM #TITLE #TRACKNUM #LENGTH #RATING #TIMESTAMP #MUSICBRAINZ_TRACKID\n"
+
+#define CFG_FILE "/lastfm_scrobbler.cfg"
+#define CFG_VER 1
+
+#if CONFIG_RTC
+static time_t timestamp;
+#define BASE_FILENAME HOME_DIR "/.scrobbler.log"
+#define HDR_STR_TIMELESS
+#define get_timestamp() ((long)timestamp)
+#define record_timestamp() ((void)(timestamp = rb->mktime(rb->get_time())))
+#else /* !CONFIG_RTC */
+#define HDR_STR_TIMELESS " Timeless"
+#define BASE_FILENAME HOME_DIR "/.scrobbler-timeless.log"
+#define get_timestamp() (0l)
+#define record_timestamp() ({})
+#endif /* CONFIG_RTC */
+
+#define THREAD_STACK_SIZE 4*DEFAULT_STACK_SIZE
+
+/****************** prototypes ******************/
+enum plugin_status plugin_start(const void* parameter); /* entry */
+void play_tone(unsigned int frequency, unsigned int duration);
+/****************** globals ******************/
+/* communication to the worker thread */
+static struct
+{
+ bool exiting; /* signal to the thread that we want to exit */
+ bool hide_reentry; /* we may return on WPS fail, hide next invocation */
+ unsigned int id; /* worker thread id */
+ struct event_queue queue; /* thread event queue */
+ struct queue_sender_list queue_send;
+ long stack[THREAD_STACK_SIZE / sizeof(long)];
+} gThread;
+
+struct cache_entry
+{
+ size_t len;
+ uint32_t crc;
+ char buf[ ];
+};
+
+static struct scrobbler_cache
+{
+ int entries;
+ char *buf;
+ size_t pos;
+ size_t size;
+ bool pending;
+ bool force_flush;
+ struct mutex mtx;
+} gCache;
+
+static struct scrobbler_cfg
+{
+ int uniqct;
+ int savepct;
+ int minms;
+ int beeplvl;
+ bool playback;
+ bool verbose;
+} gConfig;
+
+static struct configdata config[] =
+{
+ #define MAX_MRU (SCROBBLER_MAX_TRACK_MRU)
+ {TYPE_INT, 0, MAX_MRU, { .int_p = &gConfig.uniqct }, "UniqCt", NULL},
+ {TYPE_INT, 0, 100, { .int_p = &gConfig.savepct }, "SavePct", NULL},
+ {TYPE_INT, 0, 10000, { .int_p = &gConfig.minms }, "MinMs", NULL},
+ {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.playback }, "Playback", NULL},
+ {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.verbose }, "Verbose", NULL},
+ {TYPE_INT, 0, 10, { .int_p = &gConfig.beeplvl }, "BeepLvl", NULL},
+ #undef MAX_MRU
+};
+const int gCfg_sz = sizeof(config)/sizeof(*config);
+
+/****************** config functions *****************/
+static void config_set_defaults(void)
+{
+ gConfig.uniqct = SCROBBLER_MAX_TRACK_MRU;
+ gConfig.savepct = 50;
+ gConfig.minms = 500;
+ gConfig.playback = false;
+ gConfig.verbose = true;
+ gConfig.beeplvl = 10;
+}
+
+static int config_settings_menu(void)
+{
+ int selection = 0;
+
+ static uint32_t crc = 0;
+
+ struct viewport parentvp[NB_SCREENS];
+ FOR_NB_SCREENS(l)
+ {
+ rb->viewport_set_defaults(&parentvp[l], l);
+ rb->viewport_set_fullscreen(&parentvp[l], l);
+ }
+
+ #define MENUITEM_STRINGLIST_CUSTOM(name, str, callback, ... ) \
+ static const char *name##_[] = {__VA_ARGS__}; \
+ static const struct menu_callback_with_desc name##__ = \
+ {callback,str, Icon_NOICON}; \
+ struct menu_item_ex name = \
+ {MT_RETURN_ID|MENU_HAS_DESC| \
+ MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
+ { .strings = name##_},{.callback_and_desc = & name##__}};
+
+ MENUITEM_STRINGLIST_CUSTOM(settings_menu, ID2P(LANG_SETTINGS), NULL,
+ ID2P(LANG_RESUME_PLAYBACK),
+ "Save Threshold",
+ "Minimum Elapsed",
+ "Verbose",
+ "Beep Level",
+ "Unique Track MRU",
+ ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS),
+ ID2P(VOICE_BLANK),
+ ID2P(LANG_CANCEL_0),
+ ID2P(LANG_SAVE_EXIT));
+
+ #undef MENUITEM_STRINGLIST_CUSTOM
+
+ const int items = MENU_GET_COUNT(settings_menu.flags);
+ const unsigned int flags = settings_menu.flags & (~MENU_ITEM_COUNT(MENU_COUNT_MASK));
+ if (crc == 0)
+ {
+ crc = rb->crc_32(&gConfig, sizeof(struct scrobbler_cfg), 0xFFFFFFFF);
+ }
+
+ do {
+ if (crc == rb->crc_32(&gConfig, sizeof(struct scrobbler_cfg), 0xFFFFFFFF))
+ {
+ /* hide save item -- there are no changes to save */
+ settings_menu.flags = flags|MENU_ITEM_COUNT((items - 1));
+ }
+ else
+ {
+ settings_menu.flags = flags|MENU_ITEM_COUNT(items);
+ }
+ selection=rb->do_menu(&settings_menu,&selection, parentvp, true);
+ switch(selection) {
+
+ case 0: /* resume playback on plugin start */
+ rb->set_bool(rb->str(LANG_RESUME_PLAYBACK), &gConfig.playback);
+ break;
+ case 1: /* % of track played to indicate listened status */
+ rb->set_int("Save Threshold", "%", UNIT_PERCENT,
+ &gConfig.savepct, NULL, 10, 0, 100, NULL );
+ break;
+ case 2: /* tracks played less than this will not be logged */
+ rb->set_int("Minimum Elapsed", "ms", UNIT_MS,
+ &gConfig.minms, NULL, 100, 0, 10000, NULL );
+ break;
+ case 3: /* suppress non-error messages */
+ rb->set_bool("Verbose", &gConfig.verbose);
+ break;
+ case 4: /* set volume of start-up beep */
+ rb->set_int("Beep Level", "", UNIT_INT,
+ &gConfig.beeplvl, NULL, 1, 0, 10, NULL);
+ play_tone(1500, 100);
+ break;
+ case 5: /* keep a list of tracks to prevent repeat [Skipped] entries */
+ rb->set_int("Unique Track MRU Size", "", UNIT_INT,
+ &gConfig.uniqct, NULL, 1, 0, SCROBBLER_MAX_TRACK_MRU, NULL);
+ break;
+ case 6: /* set defaults */
+ {
+ const struct text_message prompt = {
+ (const char*[]){ ID2P(LANG_AUDIOSCROBBLER),
+ ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS)}, 2};
+ if(rb->gui_syncyesno_run(&prompt, NULL, NULL) == YESNO_YES)
+ {
+ config_set_defaults();
+ if (gConfig.verbose)
+ rb->splash(HZ, ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS));
+ }
+ break;
+ }
+ case 7: /*sep*/
+ continue;
+ case 8: /* Cancel */
+ return -1;
+ break;
+ case 9: /* Save & exit */
+ {
+ int res = configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+ if (res >= 0)
+ {
+ crc = rb->crc_32(&gConfig, sizeof(struct scrobbler_cfg), 0xFFFFFFFF);
+ logf("SCROBBLER: cfg saved %s %d bytes", CFG_FILE, gCfg_sz);
+ return PLUGIN_OK;
+ }
+ logf("SCROBBLER: cfg FAILED (%d) %s", res, CFG_FILE);
+ return PLUGIN_ERROR;
+ }
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ return PLUGIN_OK;
+ }
+ } while ( selection >= 0 );
+ return 0;
+}
+
+/****************** helper fuctions ******************/
+void play_tone(unsigned int frequency, unsigned int duration)
+{
+ if (gConfig.beeplvl > 0)
+ rb->beep_play(frequency, duration, 100 * gConfig.beeplvl);
+}
+
+int scrobbler_init_cache(void)
+{
+ memset(&gCache, 0, sizeof(struct scrobbler_cache));
+ gCache.buf = rb->plugin_get_buffer(&gCache.size);
+
+ /* we need to reserve the space we want for our use in TSR plugins since
+ * someone else could call plugin_get_buffer() and corrupt our memory */
+ size_t reqsz = SCROBBLER_MAX_CACHE;
+ gCache.size = PLUGIN_BUFFER_SIZE - rb->plugin_reserve_buffer(reqsz);
+
+ if (gCache.size < reqsz)
+ {
+ logf("SCROBBLER: OOM , %ld < req:%ld", gCache.size, reqsz);
+ return -1;
+ }
+ gCache.force_flush = true;
+ rb->mutex_init(&gCache.mtx);
+ logf("SCROBBLER: Initialized");
+ return 1;
+}
+
+static inline size_t cache_get_entry_size(int str_len)
+{
+ /* entry_sz consists of the cache entry + str_len + \0NULL terminator */
+ return ALIGN_UP(str_len + 1 + sizeof(struct cache_entry), alignof(struct cache_entry));
+}
+
+static inline const char* str_chk_valid(const char *s, const char *alt)
+{
+ return (s != NULL ? s : alt);
+}
+
+static bool track_is_unique(uint32_t hash1, uint32_t hash2)
+{
+ bool is_unique = false;
+ static uint8_t mru_len = 0;
+
+ struct hash64 { uint32_t hash1; uint32_t hash2; };
+
+ static struct hash64 hash_mru[SCROBBLER_MAX_TRACK_MRU];
+ struct hash64 i = {0};
+ struct hash64 itmp;
+ uint8_t mru;
+
+ if (mru_len > gConfig.uniqct)
+ mru_len = gConfig.uniqct;
+
+ if (gConfig.uniqct < 1)
+ return true;
+
+ /* Search in MRU */
+ for (mru = 0; mru < mru_len; mru++)
+ {
+ /* Items shifted >> 1 */
+ itmp = i;
+ i = hash_mru[mru];
+ hash_mru[mru] = itmp;
+
+ /* Found in MRU */
+ if ((i.hash1 == hash1) && (i.hash2 == hash2))
+ {
+ logf("SCROBBLER: hash [%x, %x] found in MRU @ %d", i.hash1, i.hash2, mru);
+ goto Found;
+ }
+ }
+
+ /* Add MRU entry */
+ is_unique = true;
+ if (mru_len < SCROBBLER_MAX_TRACK_MRU && mru_len < gConfig.uniqct)
+ {
+ hash_mru[mru_len] = i;
+ mru_len++;
+ }
+ else
+ {
+ logf("SCROBBLER: hash [%x, %x] evicted from MRU", i.hash1, i.hash2);
+ }
+
+ i = (struct hash64){.hash1 = hash1, .hash2 = hash2};
+ logf("SCROBBLER: hash [%x, %x] added to MRU[%d]", i.hash1, i.hash2, mru_len);
+
+Found:
+
+ /* Promote MRU item to top of MRU */
+ hash_mru[0] = i;
+
+ return is_unique;
+}
+
+static void get_scrobbler_filename(char *path, size_t size)
+{
+ int used;
+
+ used = rb->snprintf(path, size, "/%s", BASE_FILENAME);
+
+ if (used >= (int)size)
+ {
+ logf("%s: not enough buffer space for log filename", __func__);
+ rb->memset(path, 0, size);
+ }
+}
+
+static void scrobbler_write_cache(void)
+{
+ int i;
+ int fd;
+ logf("%s", __func__);
+ char scrobbler_file[MAX_PATH];
+
+ rb->mutex_lock(&gCache.mtx);
+
+ get_scrobbler_filename(scrobbler_file, sizeof(scrobbler_file));
+
+ /* If the file doesn't exist, create it.
+ Check at each write since file may be deleted at any time */
+ if(!rb->file_exists(scrobbler_file))
+ {
+ fd = rb->open(scrobbler_file, O_RDWR | O_CREAT, 0666);
+ if(fd >= 0)
+ {
+ /* write file header */
+ rb->fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n"
+ "#TZ/UNKNOWN\n" "#CLIENT/Rockbox "
+ TARGET_NAME SCROBBLER_REVISION
+ HDR_STR_TIMELESS "\n");
+ rb->fdprintf(fd, ITEM_HDR);
+
+ rb->close(fd);
+ }
+ else
+ {
+ logf("SCROBBLER: cannot create log file (%s)", scrobbler_file);
+ }
+ }
+
+ int entries = gCache.entries;
+ size_t used = gCache.pos;
+ size_t pos = 0;
+ /* clear even if unsuccessful - we don't want to overflow the buffer */
+ gCache.pos = 0;
+ gCache.entries = 0;
+
+ /* write the cache entries */
+ fd = rb->open(scrobbler_file, O_WRONLY | O_APPEND);
+ if(fd >= 0)
+ {
+ logf("SCROBBLER: writing %d entries", entries);
+ /* copy cached data to storage */
+ uint32_t prev_crc = 0x0;
+ uint32_t crc;
+ size_t entry_sz, len;
+ bool err = false;
+
+ for (i = 0; i < entries && pos < used; i++)
+ {
+ logf("SCROBBLER: write %d read pos [%ld]", i, pos);
+
+ struct cache_entry *entry = (struct cache_entry*)&gCache.buf[pos];
+
+ entry_sz = cache_get_entry_size(entry->len);
+ crc = rb->crc_32(entry->buf, entry->len, 0xFFFFFFFF) ^ prev_crc;
+ prev_crc = crc;
+
+ len = rb->strlen(entry->buf);
+ logf("SCROBBLER: write entry %d sz [%ld] len [%ld]", i, entry_sz, len);
+
+ if (len != entry->len || crc != entry->crc) /* the entry is corrupted */
+ {
+ rb->write(fd, SCROBBLER_BAD_ENTRY, sizeof(SCROBBLER_BAD_ENTRY)-1);
+ logf("SCROBBLER: Bad entry %d", i);
+ if(!err)
+ {
+ rb->queue_post(&gThread.queue, EV_USER_ERROR, ERR_WRITING_DATA);
+ err = true;
+ }
+ }
+
+ logf("SCROBBLER: writing %s", entry->buf);
+
+ if (rb->write(fd, entry->buf, len) != (ssize_t)len)
+ break;
+
+ if (entry->buf[len - 1] != '\n')
+ rb->write(fd, "\n", 1); /* ensure newline termination */
+
+ pos += entry_sz;
+ }
+ rb->close(fd);
+ }
+ else
+ {
+ logf("SCROBBLER: error writing file");
+ rb->queue_post(&gThread.queue, EV_USER_ERROR, ERR_WRITING_FILE);
+ }
+ rb->mutex_unlock(&gCache.mtx);
+}
+
+#if USING_STORAGE_CALLBACK
+static void scrobbler_flush_callback(void)
+{
+ if(gCache.pos == 0)
+ return;
+#if (CONFIG_STORAGE & STORAGE_ATA)
+ else
+#else
+ if ((gCache.pos >= SCROBBLER_MAX_CACHE / 2) || gCache.force_flush == true)
+#endif
+ {
+ gCache.force_flush = false;
+ logf("%s", __func__);
+ scrobbler_write_cache();
+ }
+}
+#endif
+
+static unsigned long scrobbler_get_threshold(unsigned long length)
+{
+ /* length is assumed to be in miliseconds */
+ return length / 100 * gConfig.savepct;
+}
+
+static int create_log_entry(const struct mp3entry *id,
+ struct cache_entry *entry, int *trk_info_len)
+{
+ #define SEP "\t"
+ #define EOL "\n"
+ char* artist = id->artist ? id->artist : id->albumartist;
+ char rating = 'S'; /* Skipped */
+ if (id->elapsed >= scrobbler_get_threshold(id->length))
+ rating = 'L'; /* Listened */
+
+ char tracknum[11] = { "" };
+
+ if (id->tracknum > 0)
+ rb->snprintf(tracknum, sizeof (tracknum), "%d", id->tracknum);
+
+ int ret = rb->snprintf(entry->buf,
+ SCROBBLER_CACHE_LEN,
+ "%s"SEP"%s"SEP"%s"SEP"%s"SEP"%d%n"SEP"%c"SEP"%ld"SEP"%s"EOL"",
+ str_chk_valid(artist, UNTAGGED),
+ str_chk_valid(id->album, ""),
+ str_chk_valid(id->title, id->path),
+ tracknum,
+ (int)(id->length / 1000),
+ trk_info_len, /* receives len of the string written so far */
+ rating,
+ get_timestamp(),
+ str_chk_valid(id->mb_track_id, ""));
+
+ #undef SEP
+ #undef EOL
+ return ret;
+}
+
+static void scrobbler_add_to_cache(const struct mp3entry *id)
+{
+ logf("%s", __func__);
+ int trk_info_len = 0;
+
+ if (id->elapsed < (unsigned long) gConfig.minms)
+ {
+ logf("SCROBBLER: skipping entry < %d ms: %s", gConfig.minms, id->path);
+ return;
+ }
+
+ rb->mutex_lock(&gCache.mtx);
+
+ /* not enough room left to guarantee next entry will fit so flush the cache */
+ if ( gCache.pos > SCROBBLER_MAX_CACHE - SCROBBLER_CACHE_LEN )
+ scrobbler_write_cache();
+
+ logf("SCROBBLER: add_to_cache[%d] write pos[%ld]", gCache.entries, gCache.pos);
+ /* use prev_crc to allow whole buffer to be checked for consistency */
+ static uint32_t prev_crc = 0x0;
+ if (gCache.pos == 0)
+ prev_crc = 0x0;
+
+ void *buf = &gCache.buf[gCache.pos];
+ memset(buf, 0, SCROBBLER_CACHE_LEN);
+
+ struct cache_entry *entry = buf;
+
+ int ret = create_log_entry(id, entry, &trk_info_len);
+
+ if (ret <= 0 || (size_t) ret >= SCROBBLER_CACHE_LEN)
+ {
+ logf("SCROBBLER: entry too long:");
+ logf("SCROBBLER: %s", id->path);
+ rb->queue_post(&gThread.queue, EV_USER_ERROR, ERR_ENTRY_LENGTH);
+ }
+ else if (ret > 0)
+ {
+ /* first generate a crc over the static portion of the track info data
+ this and a crc of the filename will be used to detect repeat entries
+ */
+ static uint32_t last_crc = 0;
+ uint32_t crc_entry = rb->crc_32(entry->buf, trk_info_len, 0xFFFFFFFF);
+ uint32_t crc_path = rb->crc_32(id->path, rb->strlen(id->path), 0xFFFFFFFF);
+ bool is_unique = track_is_unique(crc_entry, crc_path);
+ bool is_listened = (id->elapsed >= scrobbler_get_threshold(id->length));
+
+ if (is_unique || is_listened)
+ {
+ /* finish calculating the CRC of the whole entry */
+ const void *src = entry->buf + trk_info_len;
+ entry->crc = rb->crc_32(src, ret - trk_info_len, crc_entry) ^ prev_crc;
+ prev_crc = entry->crc;
+ entry->len = ret;
+
+ /* since Listened entries are written regardless
+ make sure this isn't a direct repeat */
+ if ((entry->crc ^ crc_path) != last_crc)
+ {
+
+ if (is_listened)
+ last_crc = (entry->crc ^ crc_path);
+ else
+ last_crc = 0;
+
+ size_t entry_sz = cache_get_entry_size(ret);
+
+ logf("SCROBBLER: Added (#%d) sz[%ld] len[%d], %s",
+ gCache.entries, entry_sz, ret, entry->buf);
+
+ gCache.entries++;
+ /* increase pos by string len + null terminator + sizeof entry */
+ gCache.pos += entry_sz;
+
+#if USING_STORAGE_CALLBACK
+ rb->register_storage_idle_func(scrobbler_flush_callback);
+#endif
+ }
+ }
+ else
+ logf("SCROBBLER: skipping repeat entry: %s", id->path);
+ }
+ rb->mutex_unlock(&gCache.mtx);
+}
+
+static void scrobbler_flush_cache(void)
+{
+ logf("%s", __func__);
+ /* Add any pending entries to the cache */
+ if (gCache.pending)
+ {
+ logf("SCROBBLER: pending entry");
+ gCache.pending = false;
+ if (rb->audio_status())
+ scrobbler_add_to_cache(rb->audio_current_track());
+ }
+
+ /* Write the cache to disk if needed */
+ if (gCache.pos > 0)
+ {
+ scrobbler_write_cache();
+ }
+}
+
+static void track_change_event(unsigned short id, void *ev_data)
+{
+ (void)id;
+ logf("%s", __func__);
+ struct mp3entry *id3 = ((struct track_event *)ev_data)->id3;
+
+ /* check if track was resumed > %threshold played ( likely got saved ) */
+ if ((id3->elapsed > scrobbler_get_threshold(id3->length)))
+ {
+ gCache.pending = false;
+ logf("SCROBBLER: skipping file %s", id3->path);
+ }
+ else
+ {
+ logf("SCROBBLER: add pending %s",id3->path);
+ record_timestamp();
+ gCache.pending = true;
+ }
+}
+
+#ifdef ROCKBOX_HAS_LOGF
+static const char* track_event_info(struct track_event* te)
+{
+
+ static const char *strflags[] = {"TEF_NONE", "TEF_CURRENT",
+ "TEF_AUTOSKIP", "TEF_CUR|ASKIP",
+ "TEF_REWIND", "TEF_CUR|REW",
+ "TEF_ASKIP|REW", "TEF_CUR|ASKIP|REW"};
+/*TEF_NONE = 0x0, no flags are set
+* TEF_CURRENT = 0x1, event is for the current track
+* TEF_AUTO_SKIP = 0x2, event is sent in context of auto skip
+* TEF_REWIND = 0x4, interpret as rewind, id3->elapsed is the
+ position before the seek back to 0
+*/
+ logf("SCROBBLER: flag %d", te->flags);
+ return strflags[te->flags&0x7];
+}
+#endif
+
+static void track_finish_event(unsigned short id, void *ev_data)
+{
+ (void)id;
+ struct track_event *te = ((struct track_event *)ev_data);
+ logf("%s %s %s", __func__, gCache.pending?"True":"False", track_event_info(te));
+ /* add entry using the currently ending track */
+ if (gCache.pending && (te->flags & TEF_CURRENT) && !(te->flags & TEF_REWIND))
+ {
+ gCache.pending = false;
+
+ scrobbler_add_to_cache(te->id3);
+ }
+}
+
+/****************** main thread + helpers ******************/
+static void events_unregister(void)
+{
+ /* we don't want any more events */
+ rb->remove_event(PLAYBACK_EVENT_TRACK_CHANGE, track_change_event);
+ rb->remove_event(PLAYBACK_EVENT_TRACK_FINISH, track_finish_event);
+}
+
+static void events_register(void)
+{
+ rb->add_event(PLAYBACK_EVENT_TRACK_CHANGE, track_change_event);
+ rb->add_event(PLAYBACK_EVENT_TRACK_FINISH, track_finish_event);
+}
+
+void thread(void)
+{
+ bool in_usb = false;
+
+ struct queue_event ev;
+ while (!gThread.exiting)
+ {
+ rb->queue_wait(&gThread.queue, &ev);
+
+ switch (ev.id)
+ {
+ case SYS_USB_CONNECTED:
+ scrobbler_flush_cache();
+ rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ in_usb = true;
+ break;
+ case SYS_USB_DISCONNECTED:
+ in_usb = false;
+ /*fall through*/
+ case EV_STARTUP:
+ logf("SCROBBLER: Thread Started");
+ events_register();
+ play_tone(1500, 100);
+ break;
+ case SYS_POWEROFF:
+ logf("SYS_POWEROFF");
+ /*fall through*/
+ case SYS_REBOOT:
+ gCache.force_flush = true;
+ /*fall through*/
+ case EV_EXIT:
+#if USING_STORAGE_CALLBACK
+ rb->unregister_storage_idle_func(scrobbler_flush_callback, false);
+#endif
+ if (!in_usb)
+ scrobbler_flush_cache();
+
+ events_unregister();
+ return;
+ case EV_FLUSHCACHE:
+ scrobbler_flush_cache();
+ rb->queue_reply(&gThread.queue, 0);
+ break;
+ case EV_USER_ERROR:
+ if (!in_usb)
+ {
+ if (ev.data == ERR_WRITING_FILE)
+ rb->splash(HZ, "SCROBBLER: error writing log");
+ else if (ev.data == ERR_ENTRY_LENGTH)
+ rb->splash(HZ, "SCROBBLER: error entry too long");
+ else if (ev.data == ERR_WRITING_DATA)
+ rb->splash(HZ, "SCROBBLER: error bad entry data");
+ }
+ break;
+ default:
+ logf("default %ld", ev.id);
+ break;
+ }
+ }
+}
+
+void thread_create(void)
+{
+ /* put the thread's queue in the broadcast list */
+ rb->queue_init(&gThread.queue, true);
+ gThread.id = rb->create_thread(thread, gThread.stack, sizeof(gThread.stack),
+ 0, "Last.Fm_TSR"
+ IF_PRIO(, PRIORITY_BACKGROUND)
+ IF_COP(, CPU));
+ rb->queue_enable_queue_send(&gThread.queue, &gThread.queue_send, gThread.id);
+ rb->queue_post(&gThread.queue, EV_STARTUP, 0);
+ rb->yield();
+}
+
+void thread_quit(void)
+{
+ if (!gThread.exiting) {
+ gThread.exiting = true;
+ rb->queue_post(&gThread.queue, EV_EXIT, 0);
+ rb->thread_wait(gThread.id);
+ /* remove the thread's queue from the broadcast list */
+ rb->queue_delete(&gThread.queue);
+ }
+}
+
+/* callback to end the TSR plugin, called before a new plugin gets loaded */
+static int plugin_exit_tsr(bool reenter)
+{
+ MENUITEM_STRINGLIST(menu, ID2P(LANG_AUDIOSCROBBLER), NULL, ID2P(LANG_SETTINGS),
+ "Flush Cache", "Exit Plugin", ID2P(LANG_BACK));
+
+ const struct text_message quit_prompt = {
+ (const char*[]){ ID2P(LANG_AUDIOSCROBBLER),
+ "is currently running.",
+ "Quit scrobbler?" }, 3
+ };
+
+ if (gThread.hide_reentry &&
+ (rb->audio_status() & (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE)) == 0)
+ {
+ gThread.hide_reentry = false;
+ return PLUGIN_TSR_CONTINUE;
+ }
+
+ while(true)
+ {
+ int result = reenter ? rb->do_menu(&menu, NULL, NULL, false) : 2;
+ switch(result)
+ {
+ case 0: /* settings */
+ config_settings_menu();
+ break;
+ case 1: /* flush cache */
+ if (gCache.entries > 0)
+ {
+ rb->queue_send(&gThread.queue, EV_FLUSHCACHE, 0);
+ if (gConfig.verbose)
+ rb->splashf(2*HZ, "%s Cache Flushed", rb->str(LANG_AUDIOSCROBBLER));
+ }
+ break;
+
+ case 2: /* exit plugin - quit */
+ if(rb->gui_syncyesno_run(&quit_prompt, NULL, NULL) == YESNO_YES)
+ {
+ scrobbler_flush_cache();
+ thread_quit();
+ return (reenter ? PLUGIN_TSR_TERMINATE : PLUGIN_TSR_SUSPEND);
+ }
+ /* Fall Through */
+ case 3: /* back to menu */
+ return PLUGIN_TSR_CONTINUE;
+ }
+ }
+}
+
+/****************** main ******************/
+static int plugin_main(const void* parameter)
+{
+ struct scrobbler_cfg cfg;
+ rb->memcpy(&cfg, &gConfig, sizeof(struct scrobbler_cfg)); /* store settings */
+
+ /* Resume plugin ? -- silences startup */
+ if (parameter == rb->plugin_tsr)
+ {
+ gConfig.beeplvl = 0;
+ gConfig.playback = false;
+ gConfig.verbose = false;
+ }
+
+ rb->memset(&gThread, 0, sizeof(gThread));
+ if (gConfig.verbose)
+ rb->splashf(HZ / 2, "%s Started",rb->str(LANG_AUDIOSCROBBLER));
+ logf("%s: %s Started", __func__, rb->str(LANG_AUDIOSCROBBLER));
+
+ rb->plugin_tsr(plugin_exit_tsr); /* stay resident */
+
+ thread_create();
+ rb->memcpy(&gConfig, &cfg, sizeof(struct scrobbler_cfg)); /*restore settings */
+
+ if (gConfig.playback)
+ {
+ gThread.hide_reentry = true;
+ return PLUGIN_GOTO_WPS;
+ }
+ return PLUGIN_OK;
+}
+
+/***************** Plugin Entry Point *****************/
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ /* now go ahead and have fun! */
+ if (rb->usb_inserted() == true)
+ return PLUGIN_USB_CONNECTED;
+
+ if (scrobbler_init_cache() < 0)
+ return PLUGIN_ERROR;
+
+ config_set_defaults();
+
+ if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
+ {
+ /* If the loading failed, save a new config file */
+ configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+ if (gConfig.verbose)
+ rb->splash(HZ, ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS));
+ }
+
+ int ret = plugin_main(parameter);
+ return ret;
+}
diff --git a/apps/plugins/lastfm_scrobbler_viewer.c b/apps/plugins/lastfm_scrobbler_viewer.c
new file mode 100644
index 0000000000..c35ba64918
--- /dev/null
+++ b/apps/plugins/lastfm_scrobbler_viewer.c
@@ -0,0 +1,1033 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ /
+ * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) (
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 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 "lang_enum.h"
+
+#include "lib/printcell_helper.h"
+
+#include "lib/configfile.h"
+#define CFG_FILE "/lastfm_scrobbler_viewer.cfg"
+#define TMP_FILE ""PLUGIN_DATA_DIR "/lscrobbler_viewer_%d.tmp"
+#define CFG_VER 1
+
+#ifdef ROCKBOX_HAS_LOGF
+#define logf rb->logf
+#else
+#define logf(...) do { } while(0)
+#endif
+
+/*#ARTIST #ALBUM #TITLE #TRACKNUM #LENGTH #RATING #TIMESTAMP #MUSICBRAINZ_TRACKID*/
+
+#define SCROBBLE_HDR_FMT "$*%d$%s$*%d$%s$*%d$%s$Track#$Length$Rating$TimeStamp$TrackId"
+//#define SCROBBLE_HDR "$*128$Artist$*128$Album$*128$Title$Track#$Length$Rating$TimeStamp$TrackId"
+
+#define SCROBBLER_MIN_COLUMNS (6) /* a valid scrobbler file should have at least this many columns */
+
+#define GOTO_ACTION_DEFAULT_HANDLER (PLUGIN_OK + 1)
+#define CANCEL_CONTEXT_MENU (PLUGIN_OK + 2)
+#define QUIT_CONTEXT_MENU (PLUGIN_OK + 3)
+/* global lists, for everything */
+static struct gui_synclist lists;
+
+/* printcell data for the current file */
+struct printcell_data_t {
+ int view_columns;
+ int view_lastcol;
+
+ int items_buffered;
+ int items_total;
+ int fd_cur;
+ char *filename;
+
+ char *buf;
+ size_t buf_size;
+ off_t buf_used;
+ char header[PRINTCELL_MAXLINELEN];
+
+};
+
+enum e_find_type {
+ FIND_ALL = 0,
+ FIND_EXCLUDE,
+ FIND_EXCLUDE_CASE,
+ FIND_EXCLUDE_ANY,
+ FIND_INCLUDE,
+ FIND_INCLUDE_CASE,
+ FIND_INCLUDE_ANY,
+ FIND_CUSTOM,
+};
+
+static void synclist_set(int selected_item, int items, int sel_size, struct printcell_data_t *pc_data);
+static void pc_data_set_header(struct printcell_data_t *pc_data);
+
+static void browse_file(char *buf, size_t bufsz)
+{
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY,
+ .title = "Select a scrobbler log file",
+ .icon = Icon_Playlist,
+ .buf = buf,
+ .bufsize = bufsz,
+ .root = "/",
+ };
+
+ if (rb->rockbox_browse(&browse) != GO_TO_PREVIOUS)
+ {
+ buf[0] = '\0';
+ }
+}
+
+static struct plugin_config
+{
+ bool separator;
+ bool talk;
+ int col_width;
+ int hidecol_flags;
+} gConfig;
+
+static struct configdata config[] =
+{
+ {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.separator }, "Cell Separator", NULL},
+ {TYPE_BOOL, 0, 1, { .bool_p = &gConfig.talk }, "Voice", NULL},
+ {TYPE_INT, 32, LCD_WIDTH, { .int_p = &gConfig.col_width }, "Cell Width", NULL},
+ {TYPE_INT, INT_MIN, INT_MAX, { .int_p = &gConfig.hidecol_flags }, "Hidden Columns", NULL},
+};
+const int gCfg_sz = sizeof(config)/sizeof(*config);
+/****************** config functions *****************/
+static void config_set_defaults(void)
+{
+ gConfig.col_width = MIN(LCD_WIDTH, 128);
+ gConfig.hidecol_flags = 0;
+ gConfig.separator = true;
+ gConfig.talk = rb->global_settings->talk_menu;
+}
+
+static void config_save(void)
+{
+ configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+}
+
+static int config_settings_menu(struct printcell_data_t *pc_data)
+{
+ int selection = 0;
+
+ struct viewport parentvp[NB_SCREENS];
+ FOR_NB_SCREENS(l)
+ {
+ rb->viewport_set_defaults(&parentvp[l], l);
+ rb->viewport_set_fullscreen(&parentvp[l], l);
+ }
+
+ MENUITEM_STRINGLIST(settings_menu, ID2P(LANG_SETTINGS), NULL,
+ ID2P(LANG_LIST_SEPARATOR),
+ "Cell Width",
+ ID2P(LANG_VOICE),
+ ID2P(VOICE_BLANK),
+ ID2P(VOICE_BLANK),
+ ID2P(LANG_CANCEL_0),
+ ID2P(LANG_SAVE_EXIT));
+
+ do {
+ selection=rb->do_menu(&settings_menu,&selection, parentvp, true);
+ switch(selection) {
+
+ case 0:
+ rb->set_bool(rb->str(LANG_LIST_SEPARATOR), &gConfig.separator);
+ break;
+ case 1:
+ rb->set_int("Cell Width", "", UNIT_INT,
+ &gConfig.col_width, NULL, 1, 32, LCD_WIDTH, NULL );
+ break;
+ case 2:
+ rb->set_bool(rb->str(LANG_VOICE), &gConfig.talk);
+ break;
+ case 3:
+ continue;
+ case 4: /*sep*/
+ continue;
+ case 5:
+ return -1;
+ break;
+ case 6:
+ {
+ pc_data_set_header(pc_data);
+ synclist_set(0, pc_data->items_total, 1, pc_data);
+ int res = configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+ if (res >= 0)
+ {
+ logf("Cfg saved %s %d bytes", CFG_FILE, gCfg_sz);
+ return PLUGIN_OK;
+ }
+ logf("Cfg FAILED (%d) %s", res, CFG_FILE);
+ return PLUGIN_ERROR;
+ }
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ return PLUGIN_OK;
+ }
+ } while ( selection >= 0 );
+ return 0;
+}
+
+/* open file pass back file descriptor and return file size */
+static size_t file_open(const char *filename, int *fd)
+{
+ size_t fsize = 0;
+
+ if (filename && fd)
+ {
+ *fd = rb->open(filename, O_RDONLY);
+ if (*fd >= 0)
+ {
+ fsize = rb->filesize(*fd);
+ }
+ }
+ return fsize;
+}
+
+static const char* list_get_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ const int slush_pos = 32; /* entries around the current entry to keep buffered */
+ /* keep track of the last position in the buffer */
+ static int buf_line_num = 0;
+ static off_t buf_last_pos = 0;
+ /* keep track of the last position in the file */
+ static int file_line_num = 0;
+ static off_t file_last_seek = 0;
+
+ if (selected_item < 0)
+ {
+ logf("[%s] Reset positions", __func__);
+ buf_line_num = 0;
+ buf_last_pos = 0;
+ file_line_num = 0;
+ file_last_seek = 0;
+ return "";
+ }
+
+ int line_num;
+ struct printcell_data_t *pc_data = (struct printcell_data_t*) data;
+ bool found = false;
+
+ if (pc_data->buf && selected_item < pc_data->items_buffered)
+ {
+ int buf_pos;
+
+ if (buf_line_num > selected_item || buf_last_pos >= pc_data->buf_used
+ || buf_line_num < 0)
+ {
+ logf("[%s] Set pos buffer 0", __func__);
+ buf_line_num = 0;
+ buf_last_pos = 0;
+ }
+ buf_pos = buf_last_pos;
+ line_num = buf_line_num;
+
+ while (buf_pos < pc_data->buf_used
+ && line_num < pc_data->items_buffered)
+ {
+ size_t len = rb->strlen(&pc_data->buf[buf_pos]);
+ if(line_num == selected_item)
+ {
+ rb->strlcpy(buf, &pc_data->buf[buf_pos], MIN(buf_len, len));
+ logf("(%d) in buffer: %s", line_num, buf);
+ found = true;
+ break;
+ }
+ else
+ {
+ buf_pos += len + 1; /* need to go past the NULL terminator */
+ line_num++;
+
+ if (buf_line_num + slush_pos < selected_item)
+ {
+ logf("[%s] Set pos buffer %d", __func__, line_num);
+ buf_line_num = line_num;
+ buf_last_pos = buf_pos;
+ }
+ }
+ }
+ }
+
+ /* didn't find the item try the file */
+ if(!found && pc_data->fd_cur >= 0)
+ {
+ int fd = pc_data->fd_cur;
+
+ if (file_line_num < 0 || file_line_num > selected_item)
+ {
+ logf("[%s] Set seek file 0", __func__);
+ file_line_num = 0;
+ file_last_seek = 0;
+ }
+
+ rb->lseek(fd, file_last_seek, SEEK_SET);
+ line_num = file_line_num;
+
+ while ((rb->read_line(fd, buf, buf_len)) > 0)
+ {
+ if(buf[0] == '#')
+ continue;
+ if(line_num == selected_item)
+ {
+ logf("(%d) in file: %s", line_num, buf);
+ found = true;
+ break;
+ }
+ else
+ {
+ line_num++;
+
+ if (file_line_num + slush_pos < selected_item)
+ {
+ logf("[%s] Set seek file %d", __func__, line_num);
+ file_line_num = line_num;
+ file_last_seek = rb->lseek(fd, 0, SEEK_CUR);
+ }
+ }
+ }
+ }
+
+ if(!found)
+ {
+ logf("(%d) Not Found!", selected_item);
+ buf_line_num = -1;
+ file_line_num = -1;
+ buf[0] = '\0';
+ }
+ return buf;
+}
+
+static int list_voice_cb(int list_index, void* data)
+{
+ (void) list_index;
+ struct printcell_data_t *pc_data = (struct printcell_data_t*) data;
+ if (!gConfig.talk)
+ return -1;
+
+ int selcol = printcell_get_column_selected();
+ char buf[MAX_PATH];
+ char* name;
+ long id;
+
+ if (pc_data->view_lastcol != selcol)
+ {
+ name = printcell_get_title_text(selcol, buf, sizeof(buf));
+
+ id = P2ID((const unsigned char *)name);
+
+ if(id>=0)
+ rb->talk_id(id, true);
+ else if (selcol >= 0)
+ {
+ switch (selcol)
+ {
+ case 0:
+ rb->talk_id(LANG_ID3_ARTIST, true);
+ break;
+ case 1:
+ rb->talk_id(LANG_ID3_ALBUM, true);
+ break;
+ case 2:
+ rb->talk_id(LANG_ID3_TITLE, true);
+ break;
+ case 3:
+ rb->talk_id(LANG_ID3_TRACKNUM, true);
+ break;
+ case 4:
+ rb->talk_id(LANG_ID3_LENGTH, true);
+ break;
+
+ default:
+ rb->talk_spell(name, true);
+ break;
+ }
+ }
+ else
+ rb->talk_id(LANG_ALL, true);
+
+ rb->talk_id(VOICE_PAUSE, true);
+ }
+
+ name = printcell_get_column_text(selcol, buf, sizeof(buf));
+
+ id = P2ID((const unsigned char *)name);
+
+ if(id>=0)
+ rb->talk_id(id, true);
+ else if (selcol >= 0)
+ rb->talk_spell(name, true);
+
+ return 0;
+}
+
+static enum themable_icons list_icon_cb(int selected_item, void *data)
+{
+ struct printcell_data_t *pc_data = (struct printcell_data_t*) data;
+ if (lists.selected_item == selected_item)
+ {
+ if (pc_data->view_lastcol < 0)
+ return Icon_Config;
+ return Icon_Audio;
+ }
+ return Icon_NOICON;
+}
+
+/* load file entries into pc_data buffer, file should already be opened
+ * and will be closed if the whole file was buffered */
+static int file_load_entries(struct printcell_data_t *pc_data)
+{
+ int items = 0;
+ int count = 0;
+ int buffered = 0;
+ unsigned int pos = 0;
+ bool comment = false;
+ char ch;
+ int fd = pc_data->fd_cur;
+ if (fd < 0)
+ return 0;
+
+ rb->lseek(fd, 0, SEEK_SET);
+
+ while (rb->read(fd, &ch, 1) > 0)
+ {
+ if (count++ == 0 && ch == '#') /* skip comments */
+ comment = true;
+ else if (!comment && ch != '\r' && pc_data->buf_size > pos)
+ pc_data->buf[pos++] = ch;
+
+ if (items == 0 && pos > PRINTCELL_MAXLINELEN * 2)
+ break;
+
+ if (ch == '\n')
+ {
+ if (!comment)
+ {
+ pc_data->buf[pos] = '\0';
+ if (pc_data->buf_size > pos)
+ {
+ pos++;
+ buffered++;
+ }
+ items++;
+ }
+ comment = false;
+ count = 0;
+ rb->yield();
+ }
+ }
+
+ logf("[%s] items: %d buffered: %d", __func__, items, buffered);
+
+ pc_data->items_total = items;
+ pc_data->items_buffered = buffered;
+ pc_data->buf[pos] = '\0';
+ pc_data->buf_used = pos;
+
+ if (items == buffered) /* whole file fit into buffer; close file */
+ {
+ rb->close(pc_data->fd_cur);
+ pc_data->fd_cur = -1;
+ }
+
+ list_get_name_cb(-1, NULL, NULL, 0); /* prime name cb */
+ return items;
+}
+
+static int filter_items(struct printcell_data_t *pc_data,
+ enum e_find_type find_type, int col)
+{
+ /* saves filtered items to a temp file and loads it */
+ int fd;
+ bool reload = false;
+ char buf[PRINTCELL_MAXLINELEN];
+ char find_exclude_buf[PRINTCELL_MAXLINELEN];
+ int selcol = printcell_get_column_selected();
+ char *find_exclude = printcell_get_column_text(selcol, find_exclude_buf,
+ sizeof(find_exclude_buf));
+ const char colsep = '\t';
+ int find_len = rb->strlen(find_exclude);
+
+ if (find_type == FIND_CUSTOM || find_len == 0)
+ {
+ int option = 0;
+ struct opt_items find_types[] = {
+ {"Exclude", -1}, {"Exclude Case Sensitive", -1},
+ {"Exclude Any", -1}, {"Include", -1},
+ {"Include Case Sensitive", -1}, {"Include Any", -1}
+ };
+ if (rb->set_option("Find Type", &option, RB_INT,
+ find_types, 6, NULL))
+ {
+ return 0;
+ }
+ switch (option)
+ {
+ case 0:
+ find_type = FIND_EXCLUDE;
+ break;
+ case 1:
+ find_type = FIND_EXCLUDE_CASE;
+ break;
+ case 2:
+ find_type = FIND_EXCLUDE_ANY;
+ break;
+ case 3:
+ find_type = FIND_INCLUDE;
+ break;
+ case 4:
+ find_type = FIND_INCLUDE_CASE;
+ break;
+ case 5:
+ find_type = FIND_INCLUDE_ANY;
+ break;
+ default:
+ find_type = FIND_ALL;
+ break;
+ }
+
+ /* copy data to beginning of buf */
+ rb->memmove(find_exclude_buf, find_exclude, find_len);
+ find_exclude_buf[find_len] = '\0';
+
+ if (rb->kbd_input(find_exclude_buf, sizeof(find_exclude_buf), NULL) < 0)
+ return -1;
+ find_exclude = find_exclude_buf;
+ find_len = rb->strlen(find_exclude);
+ }
+
+ char tmp_filename[MAX_PATH];
+ static int tmp_num = 0;
+ rb->snprintf(tmp_filename, sizeof(tmp_filename), TMP_FILE, tmp_num);
+ tmp_num++;
+
+ fd = rb->open(tmp_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ if(fd >= 0)
+ {
+ rb->splash_progress_set_delay(HZ * 5);
+ for (int i = 0; i < pc_data->items_total; i++)
+ {
+ rb->splash_progress(i, pc_data->items_total, "Filtering...");
+ const char * data = list_get_name_cb(i, pc_data, buf, sizeof(buf));
+
+ if (find_type != FIND_ALL)
+ {
+ int index = col;
+
+ if (index < 0)
+ index = 0;
+ if (find_len > 0)
+ {
+ char *bcol = buf;
+ while (*bcol != '\0' && index > 0)
+ {
+ if (*bcol == colsep)
+ index--;
+ bcol++;
+ }
+ if (index > 0)
+ continue;
+
+ if (find_type == FIND_EXCLUDE)
+ {
+ if (rb->strncasecmp(bcol, find_exclude, find_len) == 0)
+ {
+ logf("[%s] exclude [%s]", find_exclude, bcol);
+ continue;
+ }
+ }
+ else if (find_type == FIND_INCLUDE)
+ {
+ if (rb->strncasecmp(bcol, find_exclude, find_len) != 0)
+ {
+ logf("%s include %s", find_exclude, bcol);
+ continue;
+ }
+ }
+ else if (find_type == FIND_EXCLUDE_CASE)
+ {
+ if (rb->strncmp(bcol, find_exclude, find_len) == 0)
+ {
+ logf("[%s] exclude case [%s]", find_exclude, bcol);
+ continue;
+ }
+ }
+ else if (find_type == FIND_INCLUDE_CASE)
+ {
+ if (rb->strncmp(bcol, find_exclude, find_len) != 0)
+ {
+ logf("%s include case %s", find_exclude, bcol);
+ continue;
+ }
+ }
+ else if (find_type == FIND_EXCLUDE_ANY)
+ {
+ bool found = false;
+ while (*bcol != '\0' && *bcol != colsep)
+ {
+ if (rb->strncasecmp(bcol, find_exclude, find_len) == 0)
+ {
+ logf("[%s] exclude any [%s]", find_exclude, bcol);
+ found = true;
+ break;
+ }
+ bcol++;
+ }
+ if (found)
+ continue;
+ }
+ else if (find_type == FIND_INCLUDE_ANY)
+ {
+ bool found = false;
+ while (*bcol != '\0' && *bcol != colsep)
+ {
+ if (rb->strncasecmp(bcol, find_exclude, find_len) == 0)
+ {
+ found = true;
+ logf("[%s] include any [%s]", find_exclude, bcol);
+ break;
+ }
+ bcol++;
+ }
+ if (!found)
+ continue;
+ }
+ }
+ }
+ int len = rb->strlen(data);
+ if (len > 0)
+ {
+ logf("writing [%d bytes][%s]", len + 1, data);
+ rb->write(fd, data, len);
+ rb->write(fd, "\n", 1);
+ }
+ }
+ reload = true;
+ }
+
+ if (reload)
+ {
+ if (pc_data->fd_cur >= 0)
+ rb->close(pc_data->fd_cur);
+
+ pc_data->fd_cur = fd;
+ int items = file_load_entries(pc_data);
+ if (items >= 0)
+ {
+ lists.selected_item = 0;
+ rb->gui_synclist_set_nb_items(&lists, items);
+ }
+ }
+ logf("Col text '%s'", find_exclude);
+ logf("text '%s'", find_exclude_buf);
+
+ return pc_data->items_total;
+}
+
+static int scrobbler_context_menu(struct printcell_data_t *pc_data)
+{
+ struct viewport parentvp[NB_SCREENS];
+ FOR_NB_SCREENS(l)
+ {
+ rb->viewport_set_defaults(&parentvp[l], l);
+ rb->viewport_set_fullscreen(&parentvp[l], l);
+ }
+
+ int col = printcell_get_column_selected();
+ int selection = 0;
+ int items = 0;
+ uint32_t visible = printcell_get_column_visibility(-1);
+ bool hide_col = PRINTCELL_COLUMN_IS_VISIBLE(visible, col);
+
+ char namebuf[PRINTCELL_MAXLINELEN];
+ enum e_find_type find_type = FIND_ALL;
+
+ char *colname = pc_data->filename;
+ if (col >= 0)
+ colname = printcell_get_title_text(col, namebuf, sizeof(namebuf));
+
+#define MENUITEM_STRINGLIST_CUSTOM(name, str, callback, ... ) \
+ const char *name##_[] = {__VA_ARGS__}; \
+ const struct menu_callback_with_desc name##__ = \
+ {callback,str, Icon_NOICON}; \
+ const struct menu_item_ex name = \
+ {MT_RETURN_ID|MENU_HAS_DESC| \
+ MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \
+ { .strings = name##_},{.callback_and_desc = & name##__}};
+
+ const char *menu_item[4];
+
+ menu_item[0]= hide_col ? "Hide":"Show";
+ menu_item[1]= "Exclude";
+ menu_item[2]= "Include";
+ menu_item[3]= "Custom Filter";
+
+ if (col == -1)
+ {
+ menu_item[0]= hide_col ? "Hide All":"Show All";
+ menu_item[1]= "Open";
+ menu_item[2]= "Reload";
+ menu_item[3]= ID2P(LANG_SETTINGS);
+ }
+
+ MENUITEM_STRINGLIST_CUSTOM(context_menu, colname, NULL,
+ menu_item[0],
+ menu_item[1],
+ menu_item[2],
+ menu_item[3],
+ ID2P(VOICE_BLANK),
+ ID2P(LANG_CANCEL_0),
+ ID2P(LANG_MENU_QUIT));
+
+#undef MENUITEM_STRINGLIST_CUSTOM
+ do {
+ selection=rb->do_menu(&context_menu,&selection, parentvp, true);
+ switch(selection) {
+
+ case 0:
+ {
+ printcell_set_column_visible(col, !hide_col);
+ if (hide_col)
+ {
+ do
+ {
+ col = printcell_increment_column(1, true);
+ } while (col >= 0 && printcell_get_column_visibility(col) == 1);
+ pc_data->view_lastcol = col;
+ }
+ gConfig.hidecol_flags = printcell_get_column_visibility(-1);
+ break;
+ }
+ case 1: /* Exclude / Open */
+ {
+ if (col == -1)/*Open*/
+ {
+ char buf[MAX_PATH];
+ browse_file(buf, sizeof(buf));
+ if (rb->file_exists(buf))
+ {
+ rb->strlcpy(pc_data->filename, buf, MAX_PATH);
+ if (pc_data->fd_cur >= 0)
+ rb->close(pc_data->fd_cur);
+ if (file_open(pc_data->filename, &pc_data->fd_cur) > 0)
+ items = file_load_entries(pc_data);
+ if (items >= 0)
+ synclist_set(0, items, 1, pc_data);
+ }
+ else
+ rb->splash(HZ *2, "Error Opening");
+
+ return CANCEL_CONTEXT_MENU;
+ }
+
+ find_type = FIND_EXCLUDE;
+ }
+ /* fall-through */
+ case 2: /* Include / Reload */
+ {
+ if (col == -1) /*Reload*/
+ {
+ if (pc_data->fd_cur >= 0)
+ rb->close(pc_data->fd_cur);
+ if (file_open(pc_data->filename, &pc_data->fd_cur) > 0)
+ items = file_load_entries(pc_data);
+ if (items >= 0)
+ rb->gui_synclist_set_nb_items(&lists, items);
+ return CANCEL_CONTEXT_MENU;
+ }
+
+ if (find_type == FIND_ALL)
+ find_type = FIND_INCLUDE;
+ /* fall-through */
+ }
+ case 3: /*Custom Filter / Settings */
+ {
+ if (col == -1)/*Settings*/
+ return config_settings_menu(pc_data);
+
+ if (find_type == FIND_ALL)
+ find_type = FIND_CUSTOM;
+ items = filter_items(pc_data, find_type, col);
+
+ if (items >= 0)
+ rb->gui_synclist_set_nb_items(&lists, items);
+ break;
+ }
+ case 4: /*sep*/
+ continue;
+ case 5:
+ return CANCEL_CONTEXT_MENU;
+ break;
+ case 6: /* Quit */
+ return QUIT_CONTEXT_MENU;
+ break;
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ return PLUGIN_OK;
+ }
+ } while ( selection < 0 );
+ return 0;
+}
+
+static void cleanup(void *parameter)
+{
+ struct printcell_data_t *pc_data = (struct printcell_data_t*) parameter;
+ if (pc_data->fd_cur >= 0)
+ rb->close(pc_data->fd_cur);
+ pc_data->fd_cur = -1;
+}
+
+static void menu_action_printcell(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+ (void) exit;
+ struct printcell_data_t *pc_data = (struct printcell_data_t*) lists->data;
+ if (*action == ACTION_STD_OK)
+ {
+ if (selected_item < lists->nb_items)
+ {
+ pc_data->view_lastcol = printcell_increment_column(1, true);
+ *action = ACTION_NONE;
+ }
+ }
+ else if (*action == ACTION_STD_CANCEL)
+ {
+ pc_data->view_lastcol = printcell_increment_column(-1, true);
+ if (pc_data->view_lastcol != pc_data->view_columns - 1)
+ {
+ *action = ACTION_NONE;
+ }
+ }
+ else if (*action == ACTION_STD_CONTEXT)
+ {
+ int ctxret = scrobbler_context_menu(pc_data);
+ if (ctxret == QUIT_CONTEXT_MENU)
+ *exit = true;
+ }
+}
+
+int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
+{
+
+ menu_action_printcell(action, selected_item, exit, lists);
+
+ if (rb->default_event_handler_ex(*action, cleanup, lists->data) == SYS_USB_CONNECTED)
+ {
+ *exit = true;
+ return PLUGIN_USB_CONNECTED;
+ }
+ return PLUGIN_OK;
+}
+
+static int count_max_columns(int items, char delimeter,
+ int expected_cols, struct printcell_data_t *pc_data)
+{
+ int max_cols = 0;
+ int cols = 0;
+ char buf[PRINTCELL_MAXLINELEN];
+ for (int i = 0; i < items; i++)
+ {
+ const char *txt = list_get_name_cb(i, pc_data, buf, sizeof(buf));
+ while (*txt != '\0')
+ {
+ if (*txt == delimeter)
+ {
+ cols++;
+ if (cols == expected_cols)
+ {
+ max_cols = cols;
+ break;
+ }
+ }
+ txt++;
+ }
+
+ if(max_cols < expected_cols && i > 32)
+ break;
+
+ if (cols > max_cols)
+ max_cols = cols;
+ cols = 0;
+ }
+ return max_cols;
+}
+
+static void synclist_set(int selected_item, int items, int sel_size, struct printcell_data_t *pc_data)
+{
+ if (items <= 0)
+ return;
+ if (selected_item < 0)
+ selected_item = 0;
+
+ rb->gui_synclist_init(&lists,list_get_name_cb,
+ pc_data, false, sel_size, NULL);
+
+ rb->gui_synclist_set_icon_callback(&lists, list_icon_cb);
+ rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
+ rb->gui_synclist_set_nb_items(&lists,items);
+ rb->gui_synclist_select_item(&lists, selected_item);
+
+ struct printcell_settings pcs = {.cell_separator = gConfig.separator,
+ .title_delimeter = '$',
+ .text_delimeter = '\t',
+ .hidecol_flags = gConfig.hidecol_flags};
+
+ pc_data->view_columns = printcell_set_columns(&lists, &pcs,
+ pc_data->header, Icon_Rockbox);
+ printcell_enable(true);
+
+
+ int max_cols = count_max_columns(items, pcs.text_delimeter,
+ SCROBBLER_MIN_COLUMNS, pc_data);
+ if (max_cols < SCROBBLER_MIN_COLUMNS) /* not a scrobbler file? */
+ {
+ rb->gui_synclist_set_voice_callback(&lists, NULL);
+ pc_data->view_columns = printcell_set_columns(&lists, NULL,
+ "$*512$", Icon_Questionmark);
+ }
+
+ int curcol = printcell_get_column_selected();
+ while (curcol >= 0)
+ curcol = printcell_increment_column(-1, false);
+
+ if (pc_data->view_lastcol >= pc_data->view_columns)
+ pc_data->view_lastcol = -1;
+
+ /* restore column position */
+ while (pc_data->view_lastcol > -1 && curcol != pc_data->view_lastcol)
+ {
+ curcol = printcell_increment_column(1, true);
+ }
+ pc_data->view_lastcol = curcol;
+ list_voice_cb(0, pc_data);
+}
+
+static void pc_data_set_header(struct printcell_data_t *pc_data)
+{
+ int col_w = gConfig.col_width;
+ rb->snprintf(pc_data->header, PRINTCELL_MAXLINELEN,
+ SCROBBLE_HDR_FMT, col_w, rb->str(LANG_ID3_ARTIST),
+ col_w, rb->str(LANG_ID3_ALBUM),
+ col_w, rb->str(LANG_ID3_TITLE));
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ int ret = PLUGIN_OK;
+ int selected_item = -1;
+ int action;
+ int items = 0;
+#if CONFIG_RTC
+ static char filename[MAX_PATH] = HOME_DIR "/.scrobbler.log";
+#else /* !CONFIG_RTC */
+ static char filename[MAX_PATH] = HOME_DIR "/.scrobbler-timeless.log";
+#endif /* CONFIG_RTC */
+ bool redraw = true;
+ bool exit = false;
+
+ static struct printcell_data_t printcell_data;
+
+ if (parameter)
+ {
+ rb->strlcpy(filename, (const char*)parameter, MAX_PATH);
+ filename[MAX_PATH - 1] = '\0';
+ }
+
+ if (!rb->file_exists(filename))
+ {
+ browse_file(filename, sizeof(filename));
+ if (!rb->file_exists(filename))
+ {
+ rb->splash(HZ, "No Scrobbler file Goodbye.");
+ return PLUGIN_ERROR;
+ }
+
+ }
+
+ config_set_defaults();
+ if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
+ {
+ /* If the loading failed, save a new config file */
+ config_set_defaults();
+ configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
+
+ rb->splash(HZ, ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS));
+ }
+
+ rb->memset(&printcell_data, 0, sizeof (struct printcell_data_t));
+ printcell_data.fd_cur = -1;
+ printcell_data.view_lastcol = -1;
+
+ if (rb->file_exists(filename))
+ {
+ if (file_open(filename, &printcell_data.fd_cur) == 0)
+ printcell_data.fd_cur = -1;
+ else
+ {
+ size_t buf_size;
+ printcell_data.buf = rb->plugin_get_buffer(&buf_size);
+ printcell_data.buf_size = buf_size;
+ printcell_data.buf_used = 0;
+ printcell_data.filename = filename;
+ items = file_load_entries(&printcell_data);
+ }
+ }
+ int col_w = gConfig.col_width;
+ rb->snprintf(printcell_data.header, PRINTCELL_MAXLINELEN,
+ SCROBBLE_HDR_FMT, col_w, rb->str(LANG_ID3_ARTIST),
+ col_w, rb->str(LANG_ID3_ALBUM),
+ col_w, rb->str(LANG_ID3_TITLE));
+
+ if (!exit && items > 0)
+ {
+ synclist_set(0, items, 1, &printcell_data);
+ rb->gui_synclist_draw(&lists);
+
+ while (!exit)
+ {
+ action = rb->get_action(CONTEXT_LIST, HZ / 10);
+
+ if (action == ACTION_NONE)
+ {
+ if (redraw)
+ {
+ action = ACTION_REDRAW;
+ redraw = false;
+ }
+ }
+ else
+ redraw = true;
+
+ ret = menu_action_cb(&action, selected_item, &exit, &lists);
+ if (rb->gui_synclist_do_button(&lists, &action))
+ continue;
+ selected_item = rb->gui_synclist_get_sel_pos(&lists);
+
+ }
+ }
+ config_save();
+ cleanup(&printcell_data);
+ return ret;
+}
diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES
index bff9017404..09a8b1c5ed 100644
--- a/apps/plugins/lib/SOURCES
+++ b/apps/plugins/lib/SOURCES
@@ -69,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.h b/apps/plugins/lib/action_helper.h
index 58d9c6c303..53f5c840f8 100644
--- a/apps/plugins/lib/action_helper.h
+++ b/apps/plugins/lib/action_helper.h
@@ -27,7 +27,7 @@
*/
#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);
diff --git a/apps/plugins/lib/action_helper.pl b/apps/plugins/lib/action_helper.pl
index 1dfdcfd070..742419e23b 100755
--- a/apps/plugins/lib/action_helper.pl
+++ b/apps/plugins/lib/action_helper.pl
@@ -140,10 +140,12 @@ 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
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.h b/apps/plugins/lib/button_helper.h
index 1197b172b0..4087ba898a 100644
--- a/apps/plugins/lib/button_helper.h
+++ b/apps/plugins/lib/button_helper.h
@@ -32,7 +32,9 @@ struct available_button
* 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
index 45c3fd9073..192df18d7f 100755
--- a/apps/plugins/lib/button_helper.pl
+++ b/apps/plugins/lib/button_helper.pl
@@ -26,12 +26,15 @@ 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/)
{
@@ -53,6 +56,8 @@ print <<EOF
#include "button.h"
#include "button_helper.h"
+const size_t button_helper_maxbuffer = $len_max_button;
+
static const struct available_button buttons[$count] = {
EOF
;
diff --git a/apps/plugins/lib/helper.c b/apps/plugins/lib/helper.c
index 018c1616c8..92d9ec905e 100644
--- a/apps/plugins/lib/helper.c
+++ b/apps/plugins/lib/helper.c
@@ -58,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;
@@ -73,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 */
@@ -106,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 */
@@ -133,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)
@@ -145,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)
@@ -157,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 00ad8ac087..6aee4dc581 100644
--- a/apps/plugins/lib/helper.h
+++ b/apps/plugins/lib/helper.h
@@ -23,6 +23,16 @@
#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);
/**
@@ -32,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/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/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_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
index 328f74e318..48b8b2c9d2 100644
--- a/apps/plugins/lib/printcell_helper.c
+++ b/apps/plugins/lib/printcell_helper.c
@@ -22,10 +22,6 @@
#include "plugin.h"
#include "lib/printcell_helper.h"
-#ifndef PRINTCELL_MAX_COLUMNS
-#define PRINTCELL_MAX_COLUMNS 16
-#endif
-
#define COLUMN_ENDLEN 3
#define TITLE_FLAG 0xFF
#define SELECTED_FLAG 0x1
@@ -37,48 +33,54 @@
#endif
struct printcell_info_t {
- int offw[NB_SCREENS];
- int iconw[NB_SCREENS];
- int selcol_offw[NB_SCREENS];
- int totalcolw[NB_SCREENS];
- int firstcolxw[NB_SCREENS];
- uint16_t colw[NB_SCREENS][PRINTCELL_MAX_COLUMNS];
- int ncols;
- int selcol;
- int selcol_index;
- char title[PRINTCELL_MAXLINELEN];
- bool separator;
+ 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(int ncols, const char *dsp_text, char* buffer, uint16_t *sidx)
+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 '$'
+ the buffer of null terminated strings, splits on 'splitchr'
_assumptions_:
dsp_text[len - 1] = \0,
- buffer[PRINTCELL_MAXLINELEN],
sidx[PRINTCELL_MAX_COLUMNS]
*/
int i = 0;
- int j = 0;
- int ch = '$'; /* first column $ is optional */
- if (*dsp_text == '$')
+ 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 == '$')
+ if (ch == splitchr)
{
- sidx[i] = j;
- if (*dsp_text == '$') /* $$ escaped user must want to display $*/
- buffer[j++] = *dsp_text++;
- while (*dsp_text != '\0' && *dsp_text != '$' && j < PRINTCELL_MAXLINELEN - 1)
+ 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 >= (PRINTCELL_MAXLINELEN - 1))
+ if (i >= ncols || j >= (bufsz - 1))
break;
}
ch = *dsp_text++;
@@ -146,23 +148,39 @@ static inline void set_cell_width(struct viewport *vp, int max_w, int new_w)
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, bool scroll)
+ 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 ncols = printcell.ncols;
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 < ncols; i++)
+ 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 && x < vp_w)
+
+ if (nx > 0 && nw > offw && x < vp_w)
{
set_cell_width(vp, vp_w, nx);
@@ -173,11 +191,15 @@ static inline int printcells(struct screen *display, char* buffer,
}
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, ny, linedes, "$t", buftext);
+ 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);
}
@@ -192,12 +214,18 @@ static inline int calcvisible(int screen, int vp_w, int text_offset, int sbwidth
uint16_t *screencolwidth = printcell.colw[screen];
int screenicnwidth = printcell.iconw[screen];
int offset = 0;
- int selcellw = screencolwidth[printcell.selcol] + text_offset;
+ 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)
@@ -234,7 +262,7 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info)
bool show_cursor = list_info->show_cursor;
bool have_icons = list_info->have_icons;
struct line_desc *linedes = list_info->linedes;
- char *dsp_text = list_info->dsp_text;
+ const char *dsp_text = list_info->dsp_text;
struct viewport *vp = list_info->vp;
int line = list_info->line;
@@ -255,15 +283,30 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info)
separator = 1;
int nx = x;
+ int last_col = printcell.ncols - 1;
+ int hidden_w = 0;
int nw, colxw;
-
+ char *buftext;
printcell_buffer[0] = '\0';
- parse_dsptext(printcell.ncols, dsp_text, printcell_buffer, sidx);
- char *buftext = &printcell_buffer[sidx[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;
@@ -276,6 +319,9 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info)
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;
@@ -299,16 +345,21 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info)
}
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)
{
- printcell.selcol_index = sidx[printcell.selcol]; /* save the item offset*/
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 */
@@ -358,7 +409,7 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info)
nx += nw;
/* display remaining cells */
printcells(display, printcell_buffer, sidx, linedes, vp, vp_w, separator,
- nx, y, col_offset_width, selected_flag, scroll_items);
+ 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)
@@ -371,14 +422,17 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info)
vp->width = vp_w;
}
-void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator)
+void printcell_enable(bool enable)
{
- printcell.separator = separator;
+ 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)
{
- list_sep_color = rb->global_settings->list_separator_color;
+ 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;
}
@@ -398,8 +452,11 @@ void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator
}
-int printcell_increment_column(struct gui_synclist *gui_list, int increment, bool wrap)
+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;
@@ -419,36 +476,94 @@ int printcell_increment_column(struct gui_synclist *gui_list, int increment, boo
FOR_NB_SCREENS(n) /* offset needs recalculated */
printcell.selcol_offw[n] = 0;
printcell.selcol = item;
- printcell.selcol_index = 0;
+
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,
- char * title, enum themable_icons icon)
+ 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;
- int j = 0;
- int ch = '$'; /* first column $ is optional */
-
+ 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 == '$')
+ if (*title == printcell.titlesep)
title++;
do
{
- if (ch == '$')
+ if (ch == printcell.titlesep)
{
printcell.title[j++] = ch;
user_minwidth = 0;
@@ -460,7 +575,7 @@ int printcell_set_columns(struct gui_synclist *gui_list,
user_minwidth = 10*user_minwidth + *title - '0';
title++;
}
- if (*title != '$') /* user forgot $ or wants to display '*' */
+ if (*title != printcell.titlesep) /* user forgot titlesep or wants to display '*' */
{
title = dspst;
user_minwidth = 0;
@@ -470,17 +585,25 @@ int printcell_set_columns(struct gui_synclist *gui_list,
}
sidx[i] = j;
- if (*title == '$') /* $$ escaped user must want to display $*/
- printcell.title[j++] = *title++;
- while (*title != '\0' && *title != '$' && j < PRINTCELL_MAXLINELEN - 1)
+ 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);
+ 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;
}
@@ -492,37 +615,67 @@ int printcell_set_columns(struct gui_synclist *gui_list,
printcell.ncols = i;
printcell.title[j] = '\0';
printcell.selcol = -1;
- printcell.selcol_index = 0;
rb->gui_synclist_set_title(gui_list, printcell.title, icon);
return printcell.ncols;
}
-char *printcell_get_selected_column_text(struct gui_synclist *gui_list, char *buf, size_t bufsz)
+char *printcell_get_title_text(int selcol, char *buf, size_t bufsz)
{
- int selected = gui_list->selected_item;
- int index = printcell.selcol_index - 1;
+ /* 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];
+}
- if (index < 0)
- index = 0;
+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->callback_get_item_name(selected, gui_list->data, buf, bufsz) == buf)
+ if (gui_list && gui_list->callback_draw_item == printcell_listdraw_fn)
{
- bpos = &buf[index];
- if (printcell.selcol < 0) /* return entire string incld col formatting '$'*/
- return bpos;
- while(bpos < &buf[bufsz - 1])
+ 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)
{
- if (*bpos == '$' || *bpos == '\0')
- goto success;
- bpos++;
+ 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;
+ bpos = buf;
+ index = 0;
success:
- *bpos = '\0';
- return &buf[index];
+ *bpos = '\0';
+ return &buf[index];
}
diff --git a/apps/plugins/lib/printcell_helper.h b/apps/plugins/lib/printcell_helper.h
index adc98e5a5f..f58e73c0a5 100644
--- a/apps/plugins/lib/printcell_helper.h
+++ b/apps/plugins/lib/printcell_helper.h
@@ -21,25 +21,67 @@
#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))
-/* sets the printcell function enabled */
-void printcell_enable(struct gui_synclist *gui_list, bool enable, bool separator);
+struct printcell_settings
+{
+ bool cell_separator;
+ char title_delimeter;
+ char text_delimeter;
+ uint32_t hidecol_flags;
+};
-/* sets title and calculates cell widths 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
- returns number of columns
+/* 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,
- char * title, enum themable_icons icon);
+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
+/* Increments the current selected column negative increment is allowed
+ returns the selected column
range: -1(no selection) to ncols - 1 */
-int printcell_increment_column(struct gui_synclist *gui_list, int increment, bool wrap);
+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 buffer should be sized
+/* 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_selected_column_text(struct gui_synclist *gui_list, char *buf, size_t bufsz);
+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 */
diff --git a/apps/plugins/logo.c b/apps/plugins/logo.c
index 984a65aa34..9df73a9d0b 100644
--- a/apps/plugins/logo.c
+++ b/apps/plugins/logo.c
@@ -49,15 +49,27 @@ static const struct button_mapping *plugin_contexts[]
/* We use PLA */
#define LP_QUIT PLA_EXIT
-#define LP_QUIT2 PLA_CANCEL
#define LP_DEC_X PLA_LEFT
#define LP_DEC_X_REPEAT PLA_LEFT_REPEAT
#define LP_INC_X PLA_RIGHT
#define LP_INC_X_REPEAT PLA_RIGHT_REPEAT
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define LP_QUIT2 PLA_UP
+#define LP_DEC_Y PLA_SCROLL_BACK
+#define LP_DEC_Y_REPEAT PLA_SCROLL_BACK_REPEAT
+#define LP_INC_Y PLA_SCROLL_FWD
+#define LP_INC_Y_REPEAT PLA_SCROLL_FWD_REPEAT
+#else
+#define LP_QUIT2 PLA_CANCEL
#define LP_DEC_Y PLA_DOWN
#define LP_DEC_Y_REPEAT PLA_DOWN_REPEAT
#define LP_INC_Y PLA_UP
#define LP_INC_Y_REPEAT PLA_UP_REPEAT
+#endif
+
enum plugin_status plugin_start(const void* parameter) {
int button;
diff --git a/apps/plugins/lrcplayer.c b/apps/plugins/lrcplayer.c
index f42b96b5b3..d341e6b7a5 100644
--- a/apps/plugins/lrcplayer.c
+++ b/apps/plugins/lrcplayer.c
@@ -643,22 +643,6 @@ static void init_time_tag(void)
* /ddd.lrc
*/
-/* taken from apps/recorder/albumart.c */
-static void fix_filename(char* name)
-{
- static const char invalid_chars[] = "*/:<>?\\|";
-
- while (1)
- {
- if (*name == 0)
- return;
- if (*name == '"')
- *name = '\'';
- else if (rb->strchr(invalid_chars, *name))
- *name = '_';
- name++;
- }
-}
static bool find_lrc_file_helper(const char *base_dir)
{
char fname[MAX_PATH];
@@ -678,7 +662,7 @@ static bool find_lrc_file_helper(const char *base_dir)
if (current.id3->title && rb->strcmp(names[0], current.id3->title))
{
rb->strlcpy(fname, current.id3->title, sizeof(fname));
- fix_filename(fname);
+ rb->fix_path_part(fname, 0, sizeof(fname) - 1);
names[1] = fname;
}
@@ -2078,8 +2062,7 @@ static int timetag_editor(void)
while (!exit)
{
button = rb->get_action(CONTEXT_TREE, TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&gui_editor, &button,
- LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&gui_editor, &button))
continue;
switch (button)
@@ -2305,7 +2288,7 @@ static bool lrc_display_menu(void)
usb = rb->set_bool("Wipe", &prefs.wipe);
break;
case LRC_MENU_ALIGN:
- usb = rb->set_option("Alignment", &prefs.align, INT,
+ usb = rb->set_option("Alignment", &prefs.align, RB_INT,
align_names, 3, NULL);
break;
case LRC_MENU_LINE_MODE:
@@ -2362,7 +2345,7 @@ static bool lrc_lyrics_menu(void)
case LRC_MENU_ENCODING:
prefs.encoding++;
old_val = prefs.encoding;
- usb = rb->set_option("Encoding", &prefs.encoding, INT,
+ usb = rb->set_option("Encoding", &prefs.encoding, RB_INT,
cp_names, NUM_CODEPAGES+1, NULL);
if (prefs.encoding != old_val)
{
@@ -2437,7 +2420,6 @@ static bool lrc_debug_menu(void)
{
struct simplelist_info info;
rb->simplelist_info_init(&info, "Debug Menu", 6, NULL);
- info.hide_selection = true;
info.scroll_all = true;
info.get_name = lrc_debug_data;
return rb->simplelist_show_list(&info);
@@ -2643,16 +2625,10 @@ static int handle_button(void)
ff_rewind(0, false);
break;
case ACTION_WPS_VOLDOWN:
- limit = rb->sound_min(SOUND_VOLUME);
- if (--rb->global_settings->volume < limit)
- rb->global_settings->volume = limit;
- rb->sound_set(SOUND_VOLUME, rb->global_settings->volume);
+ rb->adjust_volume(-1);
break;
case ACTION_WPS_VOLUP:
- limit = rb->sound_max(SOUND_VOLUME);
- if (++rb->global_settings->volume > limit)
- rb->global_settings->volume = limit;
- rb->sound_set(SOUND_VOLUME, rb->global_settings->volume);
+ rb->adjust_volume(1);
break;
case ACTION_WPS_CONTEXT:
ret = LRC_GOTO_EDITOR;
diff --git a/apps/plugins/lua/include_lua/playlist.lua b/apps/plugins/lua/include_lua/playlist.lua
index e11b30fe7d..dfa839aab1 100644
--- a/apps/plugins/lua/include_lua/playlist.lua
+++ b/apps/plugins/lua/include_lua/playlist.lua
@@ -28,7 +28,7 @@ rb.playlist_amount = function()
return rb.playlist("amount")
end
rb.playlist_add = function (filename)
- return rb.playlist("add", filename)
+ return rb.playlist("insert_track", filename, rb.PLAYLIST_INSERT_LAST, false, true)
end
rb.playlist_create = function(dir, filename)
return rb.playlist("create", dir, filename)
diff --git a/apps/plugins/lua/rbdefines_helper.pl b/apps/plugins/lua/rbdefines_helper.pl
index 5fb0946a6a..095ec55515 100755
--- a/apps/plugins/lua/rbdefines_helper.pl
+++ b/apps/plugins/lua/rbdefines_helper.pl
@@ -55,6 +55,8 @@ if ($def_type eq "rb_defines") {
'^PLUGIN(_APPS_|_GAMES_|_)DATA_DIR$',
'^ROCKBOX_DIR$',
'^STYLE_(NONE|DEFAULT|INVERT|COLORBAR|GRADIENT|COLORED)',
+ '^CORE_KEYMAP_FILE$',
+ 'CONTEXT_(STOPSEARCHING|REMOTE|CUSTOM|CUSTOM2|PLUGIN|REMAPPED)$',
'^VIEWERS_DATA_DIR$');
}
elsif ($def_type eq "sound_defines") {
diff --git a/apps/plugins/lua/rocklib.c b/apps/plugins/lua/rocklib.c
index 6131a479db..070fdb4991 100644
--- a/apps/plugins/lua/rocklib.c
+++ b/apps/plugins/lua/rocklib.c
@@ -317,13 +317,13 @@ RB_WRAP(splash_scroller)
RB_WRAP(playlist)
{
/* just passes NULL to work with the current playlist */
- enum e_playlist {PLAYL_AMOUNT = 0, PLAYL_ADD, PLAYL_CREATE,
+ enum e_playlist {PLAYL_AMOUNT = 0, PLAYL_CREATE,
PLAYL_START, PLAYL_RESUMETRACK, PLAYL_RESUME,
PLAYL_SHUFFLE, PLAYL_SYNC, PLAYL_REMOVEALLTRACKS,
PLAYL_INSERTTRACK, PLAYL_INSERTDIRECTORY, PLAYL_INSERTPLAYL,
PLAYL_ECOUNT};
- const char *playlist_option[] = {"amount", "add", "create", "start", "resume_track",
+ const char *playlist_option[] = {"amount", "create", "start", "resume_track",
"resume", "shuffle", "sync", "remove_all_tracks",
"insert_track", "insert_directory", "insert_playlist", NULL};
@@ -339,10 +339,6 @@ RB_WRAP(playlist)
case PLAYL_AMOUNT:
result = rb->playlist_amount();
break;
- case PLAYL_ADD:
- filename = luaL_checkstring(L, 2);
- result = rb->playlist_add(filename);
- break;
case PLAYL_CREATE:
dir = luaL_checkstring(L, 2);
filename = luaL_checkstring(L, 3);
@@ -930,12 +926,6 @@ RB_WRAP(restart_lua)
return -1;
}
-RB_WRAP(show_logo)
-{
- rb->show_logo();
- return 0;
-}
-
RB_WRAP(mem_stats)
{
/* used, allocd, free = rb.mem_stats() */
@@ -1032,7 +1022,6 @@ static const luaL_Reg rocklib[] =
/* MISC */
RB_FUNC(restart_lua),
- RB_FUNC(show_logo),
RB_FUNC(mem_stats),
{NULL, NULL}
@@ -1090,6 +1079,7 @@ LUALIB_API int luaopen_rock(lua_State *L)
RB_CONSTANT(SYS_USB_DISCONNECTED),
RB_CONSTANT(SYS_TIMEOUT),
RB_CONSTANT(SYS_POWEROFF),
+ RB_CONSTANT(SYS_REBOOT),
RB_CONSTANT(SYS_CHARGER_CONNECTED),
RB_CONSTANT(SYS_CHARGER_DISCONNECTED),
diff --git a/apps/plugins/lua/rocklib_events.c b/apps/plugins/lua/rocklib_events.c
index 1c13a6758f..52e87f3d61 100644
--- a/apps/plugins/lua/rocklib_events.c
+++ b/apps/plugins/lua/rocklib_events.c
@@ -253,7 +253,9 @@ static int lua_rev_callback(lua_State *L, struct cb_data *evt)
lua_pushlightuserdata(L, evt->data);
lua_status = lua_resume(L, 2); /* call the saved function */
- if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */
+ if (lua_status == LUA_SUCCESS)
+ lua_settop(L, 0); /* eat any value(s) returned */
+ else if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */
luaL_where(L, 1); /* push error string on stack */
return lua_status;
@@ -421,7 +423,7 @@ static void init_event_thread(bool init, struct event_data *ev_data)
0,
EVENT_THREAD
IF_PRIO(, PRIORITY_SYSTEM)
- IF_COP(, COP));
+ IF_COP(, CPU));
/* Timer is used to poll waiting events */
if (!rb->timer_register(1, NULL, EV_TIMER_FREQ, rev_timer_isr IF_COP(, CPU)))
diff --git a/apps/plugins/lua/rocklib_img.c b/apps/plugins/lua/rocklib_img.c
index b0ca769ca4..68e5325ce0 100644
--- a/apps/plugins/lua/rocklib_img.c
+++ b/apps/plugins/lua/rocklib_img.c
@@ -380,7 +380,7 @@ static inline fb_data* rli_get_element(struct rocklua_image* img, int x, int y)
pixel_to_native(x, y, &x, &y);
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
/* column major address */
size_t data_address = (stride * (x - 1)) + (y - 1);
diff --git a/apps/plugins/main_menu_config.c b/apps/plugins/main_menu_config.c
index 9f651094b1..a5488ed2c0 100644
--- a/apps/plugins/main_menu_config.c
+++ b/apps/plugins/main_menu_config.c
@@ -188,7 +188,7 @@ enum plugin_status plugin_start(const void* parameter)
{
cur_sel = rb->gui_synclist_get_sel_pos(&list);
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&list,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&list, &action))
continue;
switch (action)
diff --git a/apps/plugins/matrix.c b/apps/plugins/matrix.c
index 1b2f6d465a..6e96aae10c 100644
--- a/apps/plugins/matrix.c
+++ b/apps/plugins/matrix.c
@@ -52,6 +52,14 @@
/* this set the context to use with PLA */
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define MATRIX_EXIT2 PLA_UP
+#else
+#define MATRIX_EXIT2 PLA_CANCEL
+#endif
+
#ifdef HAVE_SCROLLWHEEL
#define MATRIX_SLEEP_MORE PLA_SCROLL_BACK
#define MATRIX_SLEEP_MORE_REPEAT PLA_SCROLL_BACK_REPEAT
@@ -65,7 +73,6 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#endif /* HAVE_SCROLLWHEEL */
#define MATRIX_PAUSE PLA_SELECT
#define MATRIX_EXIT PLA_EXIT
-#define MATRIX_EXIT2 PLA_CANCEL
#define SLEEP HZ/50
diff --git a/apps/plugins/maze.c b/apps/plugins/maze.c
index 20d5c82495..ebb83ab15c 100644
--- a/apps/plugins/maze.c
+++ b/apps/plugins/maze.c
@@ -37,11 +37,14 @@
/* key assignments */
-#if (CONFIG_KEYPAD == IPOD_3G_PAD)
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
# define MAZE_NEW (BUTTON_SELECT | BUTTON_REPEAT)
# define MAZE_NEW_PRE BUTTON_SELECT
# define MAZE_QUIT BUTTON_MENU
-# define MAZE_SOLVE (BUTTON_SELECT | BUTTON_PLAY)
+# define MAZE_SOLVE (BUTTON_SELECT | BUTTON_REL)
+# define MAZE_SOLVE_PRE BUTTON_SELECT
# define MAZE_RIGHT BUTTON_RIGHT
# define MAZE_RIGHT_REPEAT BUTTON_RIGHT|BUTTON_REPEAT
# define MAZE_LEFT BUTTON_LEFT
@@ -491,17 +494,15 @@ static void maze_move_player_left(struct maze* maze)
enum plugin_status plugin_start(const void* parameter)
{
int button;
-#ifdef MAZE_NEW_PRE
+#if defined(MAZE_NEW_PRE) || defined(MAZE_SOLVE_PRE)
int lastbutton = BUTTON_NONE;
#endif
int quit = 0;
struct maze maze;
(void)parameter;
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
/* Seed the RNG */
rb->srand(*rb->current_tick);
@@ -546,6 +547,10 @@ enum plugin_status plugin_start(const void* parameter)
maze_draw(&maze, rb->screens[i]);
break;
case MAZE_SOLVE:
+#ifdef MAZE_SOLVE_PRE
+ if(lastbutton != MAZE_SOLVE_PRE)
+ break;
+#endif
maze_solve(&maze);
FOR_NB_SCREENS(i)
maze_draw(&maze, rb->screens[i]);
@@ -585,14 +590,13 @@ enum plugin_status plugin_start(const void* parameter)
}
break;
}
-#ifdef MAZE_NEW_PRE
+#if defined(MAZE_NEW_PRE) || defined(MAZE_SOLVE_PRE)
if( button != BUTTON_NONE )
lastbutton = button;
#endif
}
/* Turn on backlight timeout (revert to settings) */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
return ((quit == 1) ? PLUGIN_OK : PLUGIN_USB_CONNECTED);
}
diff --git a/apps/plugins/mazezam.c b/apps/plugins/mazezam.c
index cd7b6e22a8..1183f8f502 100644
--- a/apps/plugins/mazezam.c
+++ b/apps/plugins/mazezam.c
@@ -27,7 +27,9 @@
/* Include standard plugin macro */
-#if (CONFIG_KEYPAD == IPOD_3G_PAD)
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
# define MAZEZAM_MENU BUTTON_MENU
# define MAZEZAM_RIGHT BUTTON_RIGHT
# define MAZEZAM_LEFT BUTTON_LEFT
@@ -256,9 +258,7 @@ static void store_lcd_settings(void)
******************************************************************************/
static void restore_lcd_settings(void) {
/* Turn on backlight timeout (revert to settings) */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
/* Restore the old settings */
#if LCD_DEPTH > 1
@@ -272,10 +272,9 @@ static void restore_lcd_settings(void) {
* Adjust the LCD settings to suit MazezaM levels
******************************************************************************/
static void plugin_lcd_settings(void) {
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* Set the new settings */
#ifdef HAVE_LCD_COLOR
rb->lcd_set_background(MAZEZAM_BG_COLOR);
diff --git a/apps/plugins/metronome.c b/apps/plugins/metronome.c
index 157d116ff9..9d61c067fd 100644
--- a/apps/plugins/metronome.c
+++ b/apps/plugins/metronome.c
@@ -82,7 +82,13 @@ enum metronome_errors
#define MET_SYNC
#endif
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define METRONOME_QUIT PLA_UP
+#else
#define METRONOME_QUIT PLA_EXIT
+#endif
#ifdef HAVE_SCROLLWHEEL
#define METRONOME_VOL_UP PLA_SCROLL_FWD
diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c
index 6622b5fdb6..5179848549 100644
--- a/apps/plugins/mikmod/mikmod.c
+++ b/apps/plugins/mikmod/mikmod.c
@@ -623,7 +623,7 @@ static int settings_menu(void)
break;
case 6:
- rb->set_option(rb->str(LANG_MIKMOD_SAMPLERATE), &(settings.sample_rate), INT, sr_names,
+ rb->set_option(rb->str(LANG_MIKMOD_SAMPLERATE), &(settings.sample_rate), RB_INT, sr_names,
HW_NUM_FREQ, NULL);
applysettings();
break;
@@ -710,7 +710,6 @@ static void mm_errorhandler(void)
static int playfile(char* filename)
{
- int vol = 0;
int button;
int retval = PLUGIN_OK;
bool changingpos = false;
@@ -789,13 +788,8 @@ static int playfile(char* filename)
}
break;
}
- vol = rb->global_settings->volume;
- if (vol < rb->sound_max(SOUND_VOLUME))
- {
- vol++;
- rb->sound_set(SOUND_VOLUME, vol);
- rb->global_settings->volume = vol;
- }
+
+ rb->adjust_volume(1);
break;
case ACTION_WPS_VOLDOWN:
@@ -808,13 +802,8 @@ static int playfile(char* filename)
}
break;
}
- vol = rb->global_settings->volume;
- if (vol > rb->sound_min(SOUND_VOLUME))
- {
- vol--;
- rb->sound_set(SOUND_VOLUME, vol);
- rb->global_settings->volume = vol;
- }
+
+ rb->adjust_volume(-1);
break;
case ACTION_WPS_SKIPPREV:
diff --git a/apps/plugins/mosaique.c b/apps/plugins/mosaique.c
index caf5346dc5..cf3f42521a 100644
--- a/apps/plugins/mosaique.c
+++ b/apps/plugins/mosaique.c
@@ -37,10 +37,17 @@
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#define MOSAIQUE_QUIT PLA_EXIT
-#define MOSAIQUE_QUIT2 PLA_CANCEL
-#define MOSAIQUE_SPEED PLA_UP
+#define MOSAIQUE_SPEED PLA_RIGHT
#define MOSAIQUE_RESTART PLA_SELECT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define MOSAIQUE_QUIT2 PLA_UP
+#else
+#define MOSAIQUE_QUIT2 PLA_CANCEL
+#endif
+
enum plugin_status plugin_start(const void* parameter)
{
int button;
diff --git a/apps/plugins/mpegplayer/libmpeg2/idct_arm.S b/apps/plugins/mpegplayer/libmpeg2/idct_arm.S
index 97a87a8b59..90eb5031c7 100644
--- a/apps/plugins/mpegplayer/libmpeg2/idct_arm.S
+++ b/apps/plugins/mpegplayer/libmpeg2/idct_arm.S
@@ -43,8 +43,8 @@
ldrsh r7, [r0, #12] /* d2 */
ldrsh r8, [r0, #14] /* d3 */
orrs r9, r2, r3
- orreqs r9, r4, r5
- orreqs r9, r6, r7
+ orrseq r9, r4, r5
+ orrseq r9, r6, r7
cmpeq r8, #0
bne 2f
mov r1, r1, asl #15
@@ -320,7 +320,7 @@ mpeg2_idct_copy:
mpeg2_idct_add:
cmp r0, #129
mov r0, r1
- ldreqsh r1, [r0, #0]
+ ldrsheq r1, [r0, #0]
bne 1f
and r1, r1, #0x70
cmp r1, #0x40
diff --git a/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S b/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S
index dc53cbd7bd..a259721410 100644
--- a/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S
+++ b/apps/plugins/mpegplayer/libmpeg2/idct_armv6.S
@@ -19,6 +19,7 @@
*
****************************************************************************/
+#include "config.h"
.global mpeg2_idct_copy
.type mpeg2_idct_copy, %function
@@ -228,7 +229,7 @@ mpeg2_idct_copy:
mpeg2_idct_add:
cmp r0, #129
mov r0, r1
- ldreqsh r1, [r0, #0]
+ ldrsheq r1, [r0, #0]
bne 1f
and r1, r1, #0x70
cmp r1, #0x40
@@ -260,7 +261,7 @@ mpeg2_idct_add:
strd r4, [r1] @ r4, r5
add r1, r1, r2
cmp r0, r3
- ldrlod r8, [r1] @ r8, r9
+ ldrdlo r8, [r1] @ r8, r9
blo 2b
ldmfd sp!, {r4-r11, pc}
@@ -291,7 +292,7 @@ mpeg2_idct_add:
strd r0, [r2] @ r0, r1
add r2, r2, r3
cmp r2, r12
- ldrlod r0, [r2] @ r0, r1
+ ldrdlo r0, [r2] @ r0, r1
blo 4b
ldmfd sp!, {r4, pc}
diff --git a/apps/plugins/mpegplayer/mpeg_misc.c b/apps/plugins/mpegplayer/mpeg_misc.c
index c85285f4f8..31f0644212 100644
--- a/apps/plugins/mpegplayer/mpeg_misc.c
+++ b/apps/plugins/mpegplayer/mpeg_misc.c
@@ -192,6 +192,7 @@ int mpeg_sysevent_callback(int btn,
{
case SYS_USB_CONNECTED:
case SYS_POWEROFF:
+ case SYS_REBOOT:
mpeg_sysevent_id = btn;
return ACTION_STD_CANCEL;
}
diff --git a/apps/plugins/mpegplayer/mpeg_misc.h b/apps/plugins/mpegplayer/mpeg_misc.h
index e04db0e19d..233b815493 100644
--- a/apps/plugins/mpegplayer/mpeg_misc.h
+++ b/apps/plugins/mpegplayer/mpeg_misc.h
@@ -53,12 +53,14 @@ enum state_enum
#define CMP_3_CONST(_a, _b) \
({ int _x; \
asm volatile ( \
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED \
"ldrb %[x], [%[a], #0] \n" \
"eors %[x], %[x], %[b0] \n" \
- "ldreqb %[x], [%[a], #1] \n" \
- "eoreqs %[x], %[x], %[b1] \n" \
- "ldreqb %[x], [%[a], #2] \n" \
- "eoreqs %[x], %[x], %[b2] \n" \
+ "ldrbeq %[x], [%[a], #1] \n" \
+ "eorseq %[x], %[x], %[b1] \n" \
+ "ldrbeq %[x], [%[a], #2] \n" \
+ "eorseq %[x], %[x], %[b2] \n" \
+ END_ARM_ASM_SYNTAX_UNIFIED \
: [x]"=&r"(_x) \
: [a]"r"(_a), \
[b0]"i"(((_b) >> 24) & 0xff), \
@@ -70,14 +72,16 @@ enum state_enum
#define CMP_4_CONST(_a, _b) \
({ int _x; \
asm volatile ( \
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED \
"ldrb %[x], [%[a], #0] \n" \
"eors %[x], %[x], %[b0] \n" \
- "ldreqb %[x], [%[a], #1] \n" \
- "eoreqs %[x], %[x], %[b1] \n" \
- "ldreqb %[x], [%[a], #2] \n" \
- "eoreqs %[x], %[x], %[b2] \n" \
- "ldreqb %[x], [%[a], #3] \n" \
- "eoreqs %[x], %[x], %[b3] \n" \
+ "ldrbeq %[x], [%[a], #1] \n" \
+ "eorseq %[x], %[x], %[b1] \n" \
+ "ldrbeq %[x], [%[a], #2] \n" \
+ "eorseq %[x], %[x], %[b2] \n" \
+ "ldrbeq %[x], [%[a], #3] \n" \
+ "eorseq %[x], %[x], %[b3] \n" \
+ END_ARM_ASM_SYNTAX_UNIFIED \
: [x]"=&r"(_x) \
: [a]"r"(_a), \
[b0]"i"(((_b) >> 24) & 0xff), \
diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c
index c904de466d..6464f37217 100644
--- a/apps/plugins/mpegplayer/mpeg_settings.c
+++ b/apps/plugins/mpegplayer/mpeg_settings.c
@@ -1199,7 +1199,7 @@ static void display_options(void)
#if MPEG_OPTION_DITHERING_ENABLED
case MPEG_OPTION_DITHERING:
result = (settings.displayoptions & LCD_YUV_DITHER) ? 1 : 0;
- mpeg_set_option(rb->str(LANG_DITHERING), &result, INT, noyes, 2, NULL);
+ mpeg_set_option(rb->str(LANG_DITHERING), &result, RB_INT, noyes, 2, NULL);
settings.displayoptions =
(settings.displayoptions & ~LCD_YUV_DITHER)
| ((result != 0) ? LCD_YUV_DITHER : 0);
@@ -1208,17 +1208,17 @@ static void display_options(void)
#endif /* MPEG_OPTION_DITHERING_ENABLED */
case MPEG_OPTION_DISPLAY_FPS:
- mpeg_set_option(rb->str(LANG_DISPLAY_FPS), &settings.showfps, INT,
+ mpeg_set_option(rb->str(LANG_DISPLAY_FPS), &settings.showfps, RB_INT,
noyes, 2, NULL);
break;
case MPEG_OPTION_LIMIT_FPS:
- mpeg_set_option(rb->str(LANG_LIMIT_FPS), &settings.limitfps, INT,
+ mpeg_set_option(rb->str(LANG_LIMIT_FPS), &settings.limitfps, RB_INT,
noyes, 2, NULL);
break;
case MPEG_OPTION_SKIP_FRAMES:
- mpeg_set_option(rb->str(LANG_SKIP_FRAMES), &settings.skipframes, INT,
+ mpeg_set_option(rb->str(LANG_SKIP_FRAMES), &settings.skipframes, RB_INT,
noyes, 2, NULL);
break;
@@ -1269,31 +1269,31 @@ static void audio_options(void)
switch (result)
{
case MPEG_AUDIO_TONE_CONTROLS:
- mpeg_set_option(rb->str(LANG_TONE_CONTROLS), &settings.tone_controls, INT,
+ mpeg_set_option(rb->str(LANG_TONE_CONTROLS), &settings.tone_controls, RB_INT,
globaloff, 2, NULL);
sync_audio_setting(result, false);
break;
case MPEG_AUDIO_CHANNEL_MODES:
mpeg_set_option(rb->str(LANG_CHANNEL_CONFIGURATION), &settings.channel_modes,
- INT, globaloff, 2, NULL);
+ RB_INT, globaloff, 2, NULL);
sync_audio_setting(result, false);
break;
case MPEG_AUDIO_CROSSFEED:
- mpeg_set_option(rb->str(LANG_CROSSFEED), &settings.crossfeed, INT,
+ mpeg_set_option(rb->str(LANG_CROSSFEED), &settings.crossfeed, RB_INT,
globaloff, 2, NULL);
sync_audio_setting(result, false);
break;
case MPEG_AUDIO_EQUALIZER:
- mpeg_set_option(rb->str(LANG_EQUALIZER), &settings.equalizer, INT,
+ mpeg_set_option(rb->str(LANG_EQUALIZER), &settings.equalizer, RB_INT,
globaloff, 2, NULL);
sync_audio_setting(result, false);
break;
case MPEG_AUDIO_DITHERING:
- mpeg_set_option(rb->str(LANG_DITHERING), &settings.dithering, INT,
+ mpeg_set_option(rb->str(LANG_DITHERING), &settings.dithering, RB_INT,
globaloff, 2, NULL);
sync_audio_setting(result, false);
break;
@@ -1322,7 +1322,7 @@ static void resume_options(void)
};
mpeg_set_option(rb->str(LANG_MENU_RESUME_OPTIONS), &settings.resume_options,
- INT, items, MPEG_RESUME_NUM_OPTIONS, NULL);
+ RB_INT, items, MPEG_RESUME_NUM_OPTIONS, NULL);
}
static void clear_resume_count(void)
@@ -1369,7 +1369,7 @@ static void mpeg_settings(void)
case MPEG_SETTING_PLAY_MODE:
mpeg_set_option(rb->str(LANG_MENU_PLAY_MODE), &settings.play_mode,
- INT, singleall, 2, NULL);
+ RB_INT, singleall, 2, NULL);
break;
case MPEG_SETTING_CLEAR_RESUMES:
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index e66b4df146..654a348959 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -1215,10 +1215,9 @@ static void osd_lcd_enable_hook(unsigned short id, void* param)
static void osdbacklight_hw_on_video_mode(bool video_on)
{
if (video_on) {
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
#endif
@@ -1226,10 +1225,8 @@ static void osdbacklight_hw_on_video_mode(bool video_on)
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
rb->add_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
#endif
-#ifdef HAVE_BACKLIGHT
/* Revert to user's backlight settings */
backlight_use_settings();
-#endif
}
}
diff --git a/apps/plugins/multiboot_select.c b/apps/plugins/multiboot_select.c
new file mode 100644
index 0000000000..63babbb2c1
--- /dev/null
+++ b/apps/plugins/multiboot_select.c
@@ -0,0 +1,346 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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"
+
+/* should be more than enough */
+#define MAX_ROOTS 128
+
+static enum plugin_status plugin_status = PLUGIN_OK;
+static char tmpbuf[MAX_PATH+1];
+static char tmpbuf2[MAX_PATH+1];
+static char cur_root[MAX_PATH];
+static char roots[MAX_ROOTS][MAX_PATH];
+static int nroots;
+
+/* Read a redirect file and return the path */
+static char* read_redirect_file(const char* filename)
+{
+ int fd = rb->open(filename, O_RDONLY);
+ if(fd < 0)
+ return NULL;
+
+ ssize_t ret = rb->read(fd, tmpbuf, sizeof(tmpbuf));
+ rb->close(fd);
+ if(ret < 0 || (size_t)ret >= sizeof(tmpbuf))
+ return NULL;
+
+ /* relative paths are ignored */
+ if(tmpbuf[0] != '/')
+ ret = 0;
+
+ /* remove trailing control chars (newlines etc.) */
+ for(ssize_t i = ret - 1; i >= 0 && tmpbuf[i] < 0x20; --i)
+ tmpbuf[i] = '\0';
+
+ return tmpbuf;
+}
+
+/* Search for a redirect file, like get_redirect_dir() */
+static const char* read_redirect(void)
+{
+ for(int vol = NUM_VOLUMES-1; vol >= MULTIBOOT_MIN_VOLUME; --vol) {
+ rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>/%s", vol, BOOT_REDIR);
+ const char* path = read_redirect_file(tmpbuf);
+ if(path && path[0] == '/') {
+ /* prepend the volume because that's what we expect */
+ rb->snprintf(tmpbuf2, sizeof(tmpbuf2), "/<%d>%s", vol, path);
+ return tmpbuf2;
+ }
+ }
+
+ tmpbuf[0] = '\0';
+ return tmpbuf;
+}
+
+/* Remove all redirect files except for the one on volume 'exclude_vol' */
+static int clear_redirect(int exclude_vol)
+{
+ int ret = 0;
+ for(int vol = MULTIBOOT_MIN_VOLUME; vol < NUM_VOLUMES; ++vol) {
+ if(vol == exclude_vol)
+ continue;
+
+ rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>/%s", vol, BOOT_REDIR);
+ if(rb->file_exists(tmpbuf) && rb->remove(tmpbuf) < 0)
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/* Save a path to the redirect file */
+static int write_redirect(const char* abspath)
+{
+ /* get the volume (required) */
+ const char* path = abspath;
+ int vol = rb->path_strip_volume(abspath, &path, false);
+ if(path == abspath)
+ return -1;
+
+ /* remove all other redirect files */
+ if(clear_redirect(vol))
+ return -1;
+
+ /* open the redirect file on the same volume */
+ rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>/%s", vol, BOOT_REDIR);
+ int fd = rb->open(tmpbuf, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if(fd < 0)
+ return fd;
+
+ size_t pathlen = rb->strlen(path);
+ ssize_t ret = rb->write(fd, path, pathlen);
+ if(ret < 0 || (size_t)ret != pathlen) {
+ rb->close(fd);
+ return -1;
+ }
+
+ ret = rb->write(fd, "\n", 1);
+ rb->close(fd);
+ if(ret != 1)
+ return -1;
+
+ return 0;
+}
+
+/* Check if the firmware file is valid
+ * TODO: this should at least check model number or something */
+static bool check_firmware(const char* path)
+{
+ return rb->file_exists(path);
+}
+
+static int root_compare(const void* a, const void* b)
+{
+ const char* as = a;
+ const char* bs = b;
+ return rb->strcmp(as, bs);
+}
+
+/* Scan the filesystem for possible redirect targets. To prevent this from
+ * taking too long we only check the directories in the root of each volume
+ * look check for a rockbox firmware binary underneath /dir/.rockbox. If it
+ * exists then we report /<vol#>/dir as a root. */
+static int find_roots(void)
+{
+ const char* bootdir = *BOOTDIR == '/' ? &BOOTDIR[1] : BOOTDIR;
+ nroots = 0;
+
+ for(int vol = MULTIBOOT_MIN_VOLUME; vol < NUM_VOLUMES; ++vol) {
+ rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>", vol);
+
+ /* try to open the volume root; ignore failures since they'll
+ * occur if the volume is unmounted */
+ DIR* dir = rb->opendir(tmpbuf);
+ if(!dir)
+ continue;
+
+ struct dirent* ent;
+ while((ent = rb->readdir(dir))) {
+ int r = rb->snprintf(tmpbuf, sizeof(tmpbuf), "/<%d>/%s/%s/%s",
+ vol, ent->d_name, bootdir, BOOTFILE);
+ if(r < 0 || (size_t)r >= sizeof(tmpbuf))
+ continue;
+
+ if(check_firmware(tmpbuf)) {
+ rb->snprintf(roots[nroots], MAX_PATH, "/<%d>/%s",
+ vol, ent->d_name);
+ nroots += 1;
+
+ /* quit if we hit the maximum */
+ if(nroots == MAX_ROOTS) {
+ vol = NUM_VOLUMES;
+ break;
+ }
+ }
+ }
+
+ rb->closedir(dir);
+ }
+
+ rb->qsort(roots, nroots, MAX_PATH, root_compare);
+ return nroots;
+}
+
+static const char* picker_get_name_cb(int selected, void* data,
+ char* buffer, size_t buffer_len)
+{
+ (void)data;
+ (void)buffer;
+ (void)buffer_len;
+ return roots[selected];
+}
+
+static int picker_action_cb(int action, struct gui_synclist* lists)
+{
+ (void)lists;
+ if(action == ACTION_STD_OK)
+ action = ACTION_STD_CANCEL;
+ return action;
+}
+
+static bool show_picker_menu(int* selection)
+{
+ struct simplelist_info info;
+ rb->simplelist_info_init(&info, "Select new root", nroots, NULL);
+ info.selection = *selection;
+ info.get_name = picker_get_name_cb;
+ info.action_callback = picker_action_cb;
+ if(rb->simplelist_show_list(&info))
+ return true;
+
+ if(0 <= info.selection && info.selection < nroots)
+ *selection = info.selection;
+
+ return false;
+}
+
+enum {
+ MB_SELECT_ROOT,
+ MB_CLEAR_REDIRECT,
+ MB_CURRENT_ROOT,
+ MB_SAVE_AND_EXIT,
+ MB_SAVE_AND_REBOOT,
+ MB_EXIT,
+ MB_NUM_ITEMS,
+};
+
+static const char* menu_get_name_cb(int selected, void* data,
+ char* buffer, size_t buffer_len)
+{
+ (void)data;
+
+ switch(selected) {
+ case MB_SELECT_ROOT:
+ return "Select root";
+
+ case MB_CLEAR_REDIRECT:
+ return "Clear redirect";
+
+ case MB_CURRENT_ROOT:
+ if(cur_root[0]) {
+ rb->snprintf(buffer, buffer_len, "Using root: %s", cur_root);
+ return buffer;
+ }
+
+ return "Using default root";
+
+ case MB_SAVE_AND_EXIT:
+ return "Save and Exit";
+
+ case MB_SAVE_AND_REBOOT:
+ return "Save and Reboot";
+
+ case MB_EXIT:
+ default:
+ return "Exit";
+ }
+}
+
+static int menu_action_cb(int action, struct gui_synclist* lists)
+{
+ int selected = rb->gui_synclist_get_sel_pos(lists);
+
+ if(action != ACTION_STD_OK)
+ return action;
+
+ switch(selected) {
+ case MB_SELECT_ROOT: {
+ if(find_roots() <= 0) {
+ rb->splashf(3*HZ, "No roots found");
+ break;
+ }
+
+ int root_sel = nroots-1;
+ for(; root_sel > 0; --root_sel)
+ if(!rb->strcmp(roots[root_sel], cur_root))
+ break;
+
+ if(show_picker_menu(&root_sel))
+ return SYS_USB_CONNECTED;
+
+ rb->strcpy(cur_root, roots[root_sel]);
+ action = ACTION_REDRAW;
+ } break;
+
+ case MB_CLEAR_REDIRECT:
+ if(cur_root[0]) {
+ memset(cur_root, 0, sizeof(cur_root));
+ rb->splashf(HZ, "Cleared redirect");
+ }
+
+ action = ACTION_REDRAW;
+ break;
+
+ case MB_SAVE_AND_REBOOT:
+ case MB_SAVE_AND_EXIT: {
+ int ret;
+ if(cur_root[0])
+ ret = write_redirect(cur_root);
+ else
+ ret = clear_redirect(-1);
+
+ if(ret < 0)
+ rb->splashf(3*HZ, "Couldn't save settings");
+ else
+ rb->splashf(HZ, "Settings saved");
+
+ action = ACTION_STD_CANCEL;
+ plugin_status = (ret < 0) ? PLUGIN_ERROR : PLUGIN_OK;
+
+ if(ret >= 0 && selected == MB_SAVE_AND_REBOOT)
+ rb->sys_reboot();
+ } break;
+
+ case MB_EXIT:
+ return ACTION_STD_CANCEL;
+
+ default:
+ action = ACTION_REDRAW;
+ break;
+ }
+
+ return action;
+}
+
+static bool show_menu(void)
+{
+ struct simplelist_info info;
+ rb->simplelist_info_init(&info, "Multiboot Settings", MB_NUM_ITEMS, NULL);
+ info.get_name = menu_get_name_cb;
+ info.action_callback = menu_action_cb;
+ return rb->simplelist_show_list(&info);
+}
+
+enum plugin_status plugin_start(const void* param)
+{
+ (void)param;
+
+ /* load the current root */
+ const char* myroot = read_redirect();
+ rb->strcpy(cur_root, myroot);
+
+ /* display the menu */
+ if(show_menu())
+ return PLUGIN_USB_CONNECTED;
+ else
+ return plugin_status;
+}
diff --git a/apps/plugins/open_plugins.c b/apps/plugins/open_plugins.c
index 3a0c34d8d6..b608aff789 100644
--- a/apps/plugins/open_plugins.c
+++ b/apps/plugins/open_plugins.c
@@ -87,14 +87,23 @@ static size_t pathbasename(const char *name, const char **nameptr)
*nameptr = q;
return r - q;
}
+static int op_entry_checksum(void)
+{
+ if (op_entry.checksum != open_plugin_csum +
+ (op_entry.lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY))
+ {
+ return 0;
+ }
+ return 1;
+}
static bool op_entry_read(int fd, int selected_item, off_t data_sz)
{
rb->memset(&op_entry, 0, op_entry_sz);
op_entry.lang_id = -1;
- return ((selected_item >= 0) &&
+ return ((selected_item >= 0) && (fd >= 0) &&
(rb->lseek(fd, selected_item * op_entry_sz, SEEK_SET) >= 0) &&
- (rb->read(fd, &op_entry, data_sz) == data_sz));
+ (rb->read(fd, &op_entry, data_sz) == data_sz) && op_entry_checksum() > 0);
}
static bool op_entry_read_name(int fd, int selected_item)
@@ -102,15 +111,6 @@ static bool op_entry_read_name(int fd, int selected_item)
return op_entry_read(fd, selected_item, op_name_sz);
}
-static int op_entry_checksum(void)
-{
- if (op_entry.checksum != open_plugin_csum)
- {
- return 0;
- }
- return 1;
-}
-
static int op_entry_read_opx(const char *path)
{
int ret = -1;
@@ -174,7 +174,8 @@ failure:
static void op_entry_set_checksum(void)
{
- op_entry.checksum = open_plugin_csum;
+ op_entry.checksum = open_plugin_csum +
+ (op_entry.lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY);
}
static void op_entry_set_name(void)
@@ -188,17 +189,20 @@ static void op_entry_set_name(void)
static int op_entry_set_path(void)
{
int ret = 0;
- struct browse_context browse;
char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
if (op_entry.path[0] == '\0')
rb->strcpy(op_entry.path, PLUGIN_DIR"/");
- rb->browse_context_init(&browse, SHOW_ALL, BROWSE_SELECTONLY, rb->str(LANG_ADD),
- Icon_Plugin, op_entry.path, NULL);
-
- browse.buf = tmp_buf;
- browse.bufsize = OPEN_PLUGIN_BUFSZ;
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER,
+ .title = rb->str(LANG_ADD),
+ .icon = Icon_Plugin,
+ .root = op_entry.path,
+ .buf = tmp_buf,
+ .bufsize = sizeof(tmp_buf),
+ };
if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
{
@@ -212,7 +216,6 @@ static int op_entry_set_path(void)
static int op_entry_set_param_path(void)
{
int ret = 0;
- struct browse_context browse;
char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
if (op_entry.param[0] == '\0')
@@ -220,11 +223,15 @@ static int op_entry_set_param_path(void)
else
rb->strcpy(tmp_buf, op_entry.param);
- rb->browse_context_init(&browse, SHOW_ALL, BROWSE_SELECTONLY, "",
- Icon_Plugin, tmp_buf, NULL);
-
- browse.buf = tmp_buf;
- browse.bufsize = OPEN_PLUGIN_BUFSZ;
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER,
+ .title = rb->str(LANG_PARAMETER),
+ .icon = Icon_Plugin,
+ .root = tmp_buf,
+ .buf = tmp_buf,
+ .bufsize = sizeof(tmp_buf),
+ };
if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
{
@@ -405,6 +412,7 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
{
rb->close(fd_tmp);
rb->close(fd_dat);
+ fd_dat = -1;
rb->remove(OPEN_PLUGIN_DAT);
rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT);
}
@@ -461,12 +469,12 @@ static void op_entry_remove(int selection)
static void op_entry_remove_empty(void)
{
bool resave = false;
- if (fd_dat && rb->lseek(fd_dat, 0, SEEK_SET) == 0)
+ if (fd_dat >= 0 && rb->lseek(fd_dat, 0, SEEK_SET) == 0)
{
while (resave == false &&
rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz)
{
- if (op_entry.hash == 0)
+ if (op_entry.hash == 0 || !op_entry_checksum())
resave = true;
}
}
@@ -482,6 +490,7 @@ static void op_entry_remove_empty(void)
{
rb->close(fd_tmp);
rb->close(fd_dat);
+ fd_dat = -1;
rb->remove(OPEN_PLUGIN_DAT);
rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT);
}
@@ -623,7 +632,6 @@ static void synclist_set(char* menu_id, int selection, int items, int sel_size)
rb->gui_synclist_set_icon_callback(&lists,NULL);
rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
rb->gui_synclist_set_nb_items(&lists,items);
- rb->gui_synclist_limit_scroll(&lists,true);
rb->gui_synclist_select_item(&lists, selection);
list_voice_cb(selection, menu_id);
}
@@ -682,7 +690,7 @@ static void edit_menu(int selection)
{
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &action))
continue;
selected_item = rb->gui_synclist_get_sel_pos(&lists);
switch (action)
@@ -840,6 +848,12 @@ reopen_datfile:
}/* OP_EXT */
}
+ for (int i = items - 1; i > 0 && !exit; i--)
+ {
+ if (!op_entry_read(fd_dat, i, op_entry_sz))
+ items--;
+ }
+
if (items < 1 && !exit)
{
char* cur_filename = rb->plugin_get_current_filename();
@@ -865,7 +879,7 @@ reopen_datfile:
{
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &action))
continue;
selection = rb->gui_synclist_get_sel_pos(&lists);
switch (action)
@@ -892,6 +906,7 @@ reopen_datfile:
}
break;
case ACTION_STD_CANCEL:
+ case ACTION_STD_MENU:
{
selection = -2;
exit = true;
diff --git a/apps/plugins/oscilloscope.c b/apps/plugins/oscilloscope.c
index ae84e14f7f..00d03fb03e 100644
--- a/apps/plugins/oscilloscope.c
+++ b/apps/plugins/oscilloscope.c
@@ -47,12 +47,14 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define OSCILLOSCOPE_QUIT (BUTTON_SELECT | BUTTON_MENU)
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_PLAY)
+#define OSCILLOSCOPE_QUIT BUTTON_MENU
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_SELECT | BUTTON_REL)
+#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REPEAT)
#define OSCILLOSCOPE_ADVMODE (BUTTON_SELECT | BUTTON_RIGHT)
#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
-#define OSCILLOSCOPE_GRAPHMODE BUTTON_MENU
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY | BUTTON_REL
#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_FWD
@@ -1939,10 +1941,9 @@ static void osc_cleanup(void)
rb->lcd_set_foreground(LCD_DEFAULT_FG);
rb->lcd_set_background(LCD_DEFAULT_BG);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
/* save settings if changed */
if (rb->memcmp(&osc, &osc_disk, sizeof(osc)))
@@ -1975,10 +1976,9 @@ static void osc_setup(void)
mixer_sampr = rb->mixer_get_frequency();
#endif
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
graphmode_setup();
}
diff --git a/apps/plugins/otp.c b/apps/plugins/otp.c
index 4d302563fb..356e1e5eb6 100644
--- a/apps/plugins/otp.c
+++ b/apps/plugins/otp.c
@@ -208,17 +208,16 @@ static int base32_encode(const uint8_t *data, int length, uint8_t *result,
static bool browse( char *dst, int dst_size, const char *start )
{
- struct browse_context browse;
-
- rb->browse_context_init(&browse, SHOW_ALL,
- BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
- NULL, NOICON, start, NULL);
-
- browse.buf = dst;
- browse.bufsize = dst_size;
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU,
+ .icon = Icon_NOICON,
+ .root = start,
+ .buf = dst,
+ .bufsize = dst_size,
+ };
rb->rockbox_browse(&browse);
-
return (browse.flags & BROWSE_SELECTED);
}
diff --git a/apps/plugins/pacbox/pacbox.c b/apps/plugins/pacbox/pacbox.c
index 71c9751cad..b306503862 100755
--- a/apps/plugins/pacbox/pacbox.c
+++ b/apps/plugins/pacbox/pacbox.c
@@ -266,7 +266,7 @@ static bool pacbox_menu(void)
{
case PBMI_DIFFICULTY:
new_setting=settings.difficulty;
- rb->set_option("Difficulty", &new_setting, INT,
+ rb->set_option("Difficulty", &new_setting, RB_INT,
difficulty_options , 2, NULL);
if (new_setting != settings.difficulty) {
settings.difficulty=new_setting;
@@ -275,7 +275,7 @@ static bool pacbox_menu(void)
break;
case PBMI_PACMEN_PER_GAME:
new_setting=settings.numlives;
- rb->set_option("Pacmen Per Game", &new_setting, INT,
+ rb->set_option("Pacmen Per Game", &new_setting, RB_INT,
numlives_options , 4, NULL);
if (new_setting != settings.numlives) {
settings.numlives=new_setting;
@@ -284,7 +284,7 @@ static bool pacbox_menu(void)
break;
case PBMI_BONUS_LIFE:
new_setting=settings.bonus;
- rb->set_option("Bonus Life", &new_setting, INT,
+ rb->set_option("Bonus Life", &new_setting, RB_INT,
bonus_options , 4, NULL);
if (new_setting != settings.bonus) {
settings.bonus=new_setting;
@@ -293,7 +293,7 @@ static bool pacbox_menu(void)
break;
case PBMI_GHOST_NAMES:
new_setting=settings.ghostnames;
- rb->set_option("Ghost Names", &new_setting, INT,
+ rb->set_option("Ghost Names", &new_setting, RB_INT,
ghostname_options , 2, NULL);
if (new_setting != settings.ghostnames) {
settings.ghostnames=new_setting;
@@ -301,16 +301,16 @@ static bool pacbox_menu(void)
}
break;
case PBMI_DISPLAY_FPS:
- rb->set_option("Display FPS",&settings.showfps,INT,
+ rb->set_option("Display FPS",&settings.showfps, RB_INT,
noyes, 2, NULL);
break;
case PBMI_SOUND:
- rb->set_option("Sound",&settings.sound, INT,
+ rb->set_option("Sound",&settings.sound, RB_INT,
noyes, 2, NULL);
break;
#ifdef AI
case PBMI_AI:
- rb->set_option("AI",&settings.ai, INT,
+ rb->set_option("AI",&settings.ai, RB_INT,
noyes, 2, NULL);
break;
#endif
@@ -809,8 +809,8 @@ enum plugin_status plugin_start(const void* parameter)
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/pdbox/PDa/src/m_glob.c b/apps/plugins/pdbox/PDa/src/m_glob.c
index 9a036a984e..43f7ca8510 100644
--- a/apps/plugins/pdbox/PDa/src/m_glob.c
+++ b/apps/plugins/pdbox/PDa/src/m_glob.c
@@ -16,7 +16,7 @@ void glob_quit(void *dummy);
void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv);
void glob_meters(void *dummy, t_floatarg f);
void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av);
-void glob_audiostatus(void *dummy);
+void glob_audiostatus(void);
void glob_finderror(t_pd *dummy);
void glob_audio_properties(t_pd *dummy, t_floatarg flongform);
void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv);
diff --git a/apps/plugins/pdbox/PDa/src/m_obj.c b/apps/plugins/pdbox/PDa/src/m_obj.c
index d06caa12fd..8d4947f202 100644
--- a/apps/plugins/pdbox/PDa/src/m_obj.c
+++ b/apps/plugins/pdbox/PDa/src/m_obj.c
@@ -272,6 +272,9 @@ static int outlet_eventno;
recursion */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
+#if __GNUC__ >= 13
+#pragma GCC diagnostic ignored "-Wdangling-pointer"
+#endif
void outlet_setstacklim(void)
{
char c;
diff --git a/apps/plugins/pdbox/pdbox.c b/apps/plugins/pdbox/pdbox.c
index f21913788b..08236bfeba 100644
--- a/apps/plugins/pdbox/pdbox.c
+++ b/apps/plugins/pdbox/pdbox.c
@@ -229,7 +229,7 @@ enum plugin_status plugin_start(const void* parameter)
0, /* FIXME Which flags? */
"PD core"
IF_PRIO(, PRIORITY_REALTIME)
- IF_COP(, COP));
+ IF_COP(, CPU));
gui_thread_id =
rb->create_thread(&gui_thread,
diff --git a/apps/plugins/pegbox.c b/apps/plugins/pegbox.c
index cb6361547d..8b88aad052 100644
--- a/apps/plugins/pegbox.c
+++ b/apps/plugins/pegbox.c
@@ -65,7 +65,7 @@
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define PEGBOX_SELECT (BUTTON_SELECT|BUTTON_RIGHT)
-#define PEGBOX_QUIT (BUTTON_SELECT|BUTTON_PLAY)
+#define PEGBOX_QUIT (BUTTON_SELECT|BUTTON_REPEAT)
#define PEGBOX_RESTART (BUTTON_SELECT|BUTTON_LEFT)
#define PEGBOX_LVL_UP (BUTTON_SELECT|BUTTON_MENU)
#define PEGBOX_UP BUTTON_MENU
@@ -74,7 +74,7 @@
#define PEGBOX_LEFT BUTTON_LEFT
#define SELECT_TEXT "SELECT+RIGHT"
-#define QUIT_TEXT "SELECT+PLAY"
+#define QUIT_TEXT "Long SELECT"
#define RESTART_TEXT "SELECT+LEFT"
#define LVL_UP_TEXT "SELECT+MENU"
#define LVL_DOWN_TEXT "-"
diff --git a/apps/plugins/periodic_table.c b/apps/plugins/periodic_table.c
index b77dd07432..2dd84baacd 100644
--- a/apps/plugins/periodic_table.c
+++ b/apps/plugins/periodic_table.c
@@ -619,7 +619,6 @@ enum plugin_status plugin_start(const void* parameter)
switch (button)
{
case PERIODIC_KEY_SELECT:
- break;
case PERIODIC_KEY_MENU:
return PLUGIN_OK;
break;
diff --git a/apps/plugins/pictureflow/pictureflow.c b/apps/plugins/pictureflow/pictureflow.c
index bee1f580f7..87ad1a403f 100644
--- a/apps/plugins/pictureflow/pictureflow.c
+++ b/apps/plugins/pictureflow/pictureflow.c
@@ -33,6 +33,7 @@
#include "lib/grey.h"
#include "lib/mylcd.h"
#include "lib/feature_wrappers.h"
+#include "lib/id3.h"
/******************************* Globals ***********************************/
static fb_data *lcd_fb;
@@ -44,6 +45,7 @@ static fb_data *lcd_fb;
#if PF_PLAYBACK_CAPABLE
#include "lib/playback_control.h"
+#include "lib/mul_id3.h"
#endif
#define PF_PREV ACTION_STD_PREV
@@ -60,6 +62,8 @@ static fb_data *lcd_fb;
#define PF_QUIT (LAST_ACTION_PLACEHOLDER + 1)
#define PF_TRACKLIST (LAST_ACTION_PLACEHOLDER + 2)
+#define PF_SORTING_NEXT (LAST_ACTION_PLACEHOLDER + 3)
+#define PF_SORTING_PREV (LAST_ACTION_PLACEHOLDER + 4)
#if defined(HAVE_SCROLLWHEEL) || CONFIG_KEYPAD == IRIVER_H10_PAD || \
CONFIG_KEYPAD == MPIO_HD300_PAD
@@ -149,12 +153,12 @@ const struct button_mapping pf_context_buttons[] =
{PF_QUIT, BUTTON_POWER, BUTTON_NONE},
#elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
|| (CONFIG_KEYPAD == IPOD_3G_PAD) \
- || (CONFIG_KEYPAD == IPOD_4G_PAD) \
- || (CONFIG_KEYPAD == MPIO_HD300_PAD)
- {PF_JMP_PREV, BUTTON_LEFT, BUTTON_NONE},
- {PF_JMP_PREV, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
- {PF_JMP, BUTTON_RIGHT, BUTTON_NONE},
- {PF_JMP, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ {PF_MENU, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
+ {PF_QUIT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
+ {PF_SORTING_NEXT, BUTTON_SELECT|BUTTON_MENU, BUTTON_NONE},
+ {PF_SORTING_PREV, BUTTON_SELECT|BUTTON_PLAY, BUTTON_NONE},
+#elif CONFIG_KEYPAD == MPIO_HD300_PAD
{PF_QUIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
{PF_QUIT, BUTTON_RC_REC, BUTTON_NONE},
@@ -177,6 +181,8 @@ const struct button_mapping pf_context_buttons[] =
{PF_JMP, BUTTON_RIGHT, BUTTON_NONE},
{PF_JMP, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{PF_MENU, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
+ {PF_SORTING_NEXT, BUTTON_VOL_UP, BUTTON_NONE},
+ {PF_SORTING_PREV, BUTTON_VOL_DOWN, BUTTON_NONE},
{PF_QUIT, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER},
{PF_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
{PF_TRACKLIST, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
@@ -268,7 +274,6 @@ typedef fb_data pix_t;
/* some magic numbers for cache_version. */
#define CACHE_REBUILD 0
-#define CACHE_UPDATE 1
/* Error return values */
#define SUCCESS 0
@@ -278,7 +283,7 @@ typedef fb_data pix_t;
#define ERROR_USER_ABORT -4
/* current version for cover cache */
-#define CACHE_VERSION 3
+#define CACHE_VERSION 4
#define CONFIG_VERSION 1
#define CONFIG_FILE "pictureflow.cfg"
#define INDEX_HDR "PFID"
@@ -300,8 +305,14 @@ struct pf_config_t
int cache_version;
int show_album_name;
+ int sort_albums_by;
+ int year_sort_order;
+ bool show_year;
+
bool resize;
bool show_fps;
+
+ bool update_albumart;
};
struct pf_index_t {
@@ -368,6 +379,7 @@ struct slide_cache {
struct album_data {
int name_idx; /* offset to the album name */
int artist_idx; /* offset to the artist name */
+ int year; /* album year */
long artist_seek; /* artist taglist position */
long seek; /* album taglist position */
};
@@ -451,6 +463,31 @@ static char* show_album_name_conf[] =
"both bottom",
};
+enum sort_albums_by_values {
+ SORT_BY_ARTIST_AND_NAME = 0,
+ SORT_BY_ARTIST_AND_YEAR,
+ SORT_BY_YEAR,
+ SORT_BY_NAME,
+
+ SORT_VALUES_SIZE
+};
+static char* sort_albums_by_conf[] =
+{
+ "artist + name",
+ "artist + year",
+ "year",
+ "name"
+};
+enum year_sort_order_values {
+ ASCENDING = 0,
+ DESCENDING
+};
+static char* year_sort_order_conf[] =
+{
+ "ascending",
+ "descending"
+};
+
#define MAX_SPACING 40
#define MAX_MARGIN 80
@@ -475,7 +512,13 @@ static struct configdata config[] =
{ TYPE_INT, 0, 999999, { .int_p = &pf_cfg.last_album }, "last album", NULL },
{ TYPE_INT, 0, 1, { .int_p = &pf_cfg.backlight_mode }, "backlight", NULL },
{ TYPE_INT, 0, 999999, { .int_p = &aa_cache.idx }, "art cache pos", NULL },
- { TYPE_INT, 0, 999999, { .int_p = &aa_cache.inspected }, "art cache inspected", NULL }
+ { TYPE_INT, 0, 999999, { .int_p = &aa_cache.inspected }, "art cache inspected", NULL },
+ { TYPE_ENUM, 0, 4, { .int_p = &pf_cfg.sort_albums_by }, "sort albums by",
+ sort_albums_by_conf },
+ { TYPE_ENUM, 0, 2, { .int_p = &pf_cfg.year_sort_order }, "year order",
+ year_sort_order_conf },
+ { TYPE_BOOL, 0, 1, { .bool_p = &pf_cfg.show_year }, "show year", NULL },
+ { TYPE_BOOL, 0, 1, { .bool_p = &pf_cfg.update_albumart }, "update albumart", NULL }
};
#define CONFIG_NUM_ITEMS (sizeof(config) / sizeof(struct configdata))
@@ -496,6 +539,8 @@ static int itilt;
static PFreal offsetX;
static PFreal offsetY;
static int number_of_slides;
+static bool is_initial_slide = true;
+static bool show_tracks_while_browsing = false;
static struct pf_slide_cache pf_sldcache;
@@ -516,6 +561,8 @@ static struct pf_index_t pf_idx;
static struct pf_track_t pf_tracks;
+static struct mp3entry id3;
+
void reset_track_list(void);
static bool thread_is_running;
@@ -588,7 +635,7 @@ static inline void buf_ctx_unlock(void)
buf_ctx_locked = false;
}
-static bool check_database(bool prompt)
+static bool check_database(void)
{
bool needwarn = true;
int spin = 5;
@@ -606,9 +653,7 @@ static bool check_database(bool prompt)
needwarn = false;
rb->splash(0, ID2P(LANG_TAGCACHE_BUSY));
}
- else if (!prompt)
- return false;
- else if (rb->action_userabort(HZ/5))
+ else
return false;
rb->yield();
@@ -631,9 +676,10 @@ static bool confirm_quit(void)
return true;
}
-static void config_save(int cache_version)
+static void config_save(int cache_version, bool update_albumart)
{
pf_cfg.cache_version = cache_version;
+ pf_cfg.update_albumart = update_albumart;
configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION);
}
@@ -648,9 +694,13 @@ static void config_set_defaults(struct pf_config_t *cfg)
cfg->last_album = 0;
cfg->backlight_mode = 0;
cfg->resize = true;
- cfg->cache_version = 0;
+ cfg->cache_version = CACHE_REBUILD;
cfg->show_album_name = (LCD_HEIGHT > 100)
- ? ALBUM_NAME_TOP : ALBUM_NAME_BOTTOM;
+ ? ALBUM_AND_ARTIST_BOTTOM : ALBUM_NAME_BOTTOM;
+ cfg->sort_albums_by = SORT_BY_ARTIST_AND_NAME;
+ cfg->year_sort_order = ASCENDING;
+ cfg->show_year = false;
+ cfg->update_albumart = false;
}
static inline PFreal fmul(PFreal a, PFreal b)
@@ -920,6 +970,7 @@ const struct custom_format format_transposed = {
static const struct button_mapping* get_context_map(int context)
{
+ context &= ~CONTEXT_LOCKED;
return pf_contexts[context & ~CONTEXT_PLUGIN];
}
@@ -1001,6 +1052,51 @@ static void init_reflect_table(void)
(5 * REFLECT_HEIGHT);
}
+
+static int compare_albums (const void *a_v, const void *b_v)
+{
+ uint32_t artist_a = ((struct album_data *)a_v)->artist_idx;
+ uint32_t artist_b = ((struct album_data *)b_v)->artist_idx;
+
+ uint32_t album_a = ((struct album_data *)a_v)->name_idx;
+ uint32_t album_b = ((struct album_data *)b_v)->name_idx;
+
+ int year_a = ((struct album_data *)a_v)->year;
+ int year_b = ((struct album_data *)b_v)->year;
+
+ switch (pf_cfg.sort_albums_by)
+ {
+ case SORT_BY_ARTIST_AND_NAME:
+ if (artist_a - artist_b == 0)
+ return (int)(album_a - album_b);
+ break;
+ case SORT_BY_ARTIST_AND_YEAR:
+ if (artist_a - artist_b == 0)
+ {
+ if (pf_cfg.year_sort_order == ASCENDING)
+ return year_a - year_b;
+ else
+ return year_b - year_a;
+ }
+ break;
+ case SORT_BY_YEAR:
+ if (year_a - year_b != 0)
+ {
+ if (pf_cfg.year_sort_order == ASCENDING)
+ return year_a - year_b;
+ else
+ return year_b - year_a;
+ }
+ break;
+ case SORT_BY_NAME:
+ if (album_a - album_b != 0)
+ return (int)(album_a - album_b);
+ break;
+ }
+
+ return (int)(artist_a - artist_b);
+}
+
static int compare_album_artists (const void *a_v, const void *b_v)
{
uint32_t a = ((struct album_data *)a_v)->artist_idx;
@@ -1015,6 +1111,7 @@ static void write_album_index(int idx, int name_idx,
pf_idx.album_index[idx].seek = album_seek;
pf_idx.album_index[idx].artist_idx = artist_idx;
pf_idx.album_index[idx].artist_seek = artist_seek;
+ pf_idx.album_index[idx].year = 0;
}
static inline void write_album_entry(struct tagcache_search *tcs,
@@ -1044,6 +1141,8 @@ static void write_artist_entry(struct tagcache_search *tcs,
static int get_tcs_search_res(int type, struct tagcache_search *tcs,
void **buf, size_t *bufsz)
{
+ char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
int ret = SUCCESS;
unsigned int l, name_idx = 0;
void (*writefn)(struct tagcache_search *, int, unsigned int);
@@ -1059,7 +1158,7 @@ static int get_tcs_search_res(int type, struct tagcache_search *tcs,
data_size = sizeof(struct album_data);
}
- while (rb->tagcache_get_next(tcs))
+ while (rb->tagcache_get_next(tcs, tcs_buf, tcs_bufsz))
{
if (rb->button_get(false) > BUTTON_NONE)
{
@@ -1099,6 +1198,8 @@ static int get_tcs_search_res(int type, struct tagcache_search *tcs,
static int create_album_untagged(struct tagcache_search *tcs,
void **buf, size_t *bufsz)
{
+ static char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
int ret = SUCCESS;
int album_count = pf_idx.album_ct; /* store existing count */
int total_count = pf_idx.album_ct + pf_idx.artist_ct * 2;
@@ -1113,7 +1214,7 @@ static int create_album_untagged(struct tagcache_search *tcs,
{
rb->tagcache_search_add_filter(tcs, tag_album, pf_idx.album_untagged_seek);
- while (rb->tagcache_get_next(tcs))
+ while (rb->tagcache_get_next(tcs, tcs_buf, tcs_bufsz))
{
if (rb->button_get(false) > BUTTON_NONE) {
if (confirm_quit())
@@ -1218,9 +1319,8 @@ static int build_artist_index(struct tagcache_search *tcs,
if (res < SUCCESS)
return res;
- ALIGN_BUFFER(*buf, *bufsz, 4);
-
/* finalize the artist index */
+ ALIGN_BUFFER(*buf, *bufsz, alignof(struct artist_data));
tmp_artist = (struct artist_data*)*buf;
for (i = pf_idx.artist_ct - 1; i >= 0; i--)
tmp_artist[i] = pf_idx.artist_index[-i];
@@ -1228,7 +1328,6 @@ static int build_artist_index(struct tagcache_search *tcs,
pf_idx.artist_index = tmp_artist;
/* move buf ptr to end of artist_index */
*buf = pf_idx.artist_index + pf_idx.artist_ct;
- ALIGN_BUFFER(*buf, *bufsz, 4);
if (res == SUCCESS)
{
@@ -1242,12 +1341,59 @@ static int build_artist_index(struct tagcache_search *tcs,
}
+static int assign_album_year(void)
+{
+ char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
+ draw_progressbar(0, pf_idx.album_ct, "Assigning Album Year");
+ for (int album_idx = 0; album_idx < pf_idx.album_ct; album_idx++)
+ {
+ /* Prevent idle poweroff */
+ rb->reset_poweroff_timer();
+
+ if (rb->button_get(false) > BUTTON_NONE)
+ {
+ if (confirm_quit())
+ return ERROR_USER_ABORT;
+ else
+ {
+ rb->lcd_clear_display();
+ draw_progressbar(album_idx, pf_idx.album_ct, "Assigning Album Year");
+ }
+ }
+ draw_progressbar(album_idx, pf_idx.album_ct, NULL);
+ int album_year = 0;
+
+ if (rb->tagcache_search(&tcs, tag_year))
+ {
+ rb->tagcache_search_add_filter(&tcs, tag_album,
+ pf_idx.album_index[album_idx].seek);
+
+ if (pf_idx.album_index[album_idx].artist_idx >= 0)
+ rb->tagcache_search_add_filter(&tcs, tag_albumartist,
+ pf_idx.album_index[album_idx].artist_seek);
+
+ while (rb->tagcache_get_next(&tcs, tcs_buf, tcs_bufsz)) {
+ int track_year = rb->tagcache_get_numeric(&tcs, tag_year);
+ if (track_year > album_year)
+ album_year = track_year;
+ }
+ }
+ rb->tagcache_search_finish(&tcs);
+
+ pf_idx.album_index[album_idx].year = album_year;
+ }
+ return SUCCESS;
+}
+
/**
Create an index of all artists and albums from the database.
Also store the artists and album names so we can access them later.
*/
static int create_album_index(void)
{
+ static char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
void *buf = pf_idx.buf;
size_t buf_size = pf_idx.buf_sz;
@@ -1256,14 +1402,14 @@ static int create_album_index(void)
int i, j, last, final, retry, res;
draw_splashscreen(buf, buf_size);
- ALIGN_BUFFER(buf, buf_size, 4);
+ ALIGN_BUFFER(buf, buf_size, sizeof(long));
-/* Artists */
+ /* Artists */
res = build_artist_index(&tcs, &buf, &buf_size);
if (res < SUCCESS)
return res;
-/* Albums */
+ /* Albums */
pf_idx.album_ct = 0;
pf_idx.album_len =0;
pf_idx.album_untagged_idx = 0;
@@ -1279,7 +1425,6 @@ static int create_album_index(void)
rb->tagcache_search_finish(&tcs);
if (res < SUCCESS)
return res;
- ALIGN_BUFFER(buf, buf_size, 4);
/* Build artist list for untagged albums */
res = create_album_untagged(&tcs, &buf, &buf_size);
@@ -1287,9 +1432,8 @@ static int create_album_index(void)
if (res < SUCCESS)
return res;
- ALIGN_BUFFER(buf, buf_size, 4);
-
/* finalize the album index */
+ ALIGN_BUFFER(buf, buf_size, alignof(struct album_data));
tmp_album = (struct album_data*)buf;
for (i = pf_idx.album_ct - 1; i >= 0; i--)
tmp_album[i] = pf_idx.album_index[-i];
@@ -1297,9 +1441,8 @@ static int create_album_index(void)
pf_idx.album_index = tmp_album;
/* move buf ptr to end of album_index */
buf = pf_idx.album_index + pf_idx.album_ct;
- ALIGN_BUFFER(buf, buf_size, 4);
-/* Assign indices */
+ /* Assign indices */
draw_splashscreen(buf, buf_size);
draw_progressbar(0, pf_idx.album_ct, "Assigning Albums");
for (j = 0; j < pf_idx.album_ct; j++)
@@ -1328,7 +1471,7 @@ static int create_album_index(void)
last = 0;
final = pf_idx.artist_ct;
retry = 0;
- if (rb->tagcache_get_next(&tcs))
+ if (rb->tagcache_get_next(&tcs, tcs_buf, tcs_bufsz))
{
retry_artist_lookup:
@@ -1356,6 +1499,14 @@ retry_artist_lookup:
}
rb->tagcache_search_finish(&tcs);
}
+
+ draw_splashscreen(buf, buf_size);
+
+ res = assign_album_year();
+
+ if (res < SUCCESS)
+ return res;
+
/* sort list order to find duplicates */
rb->qsort(pf_idx.album_index, pf_idx.album_ct,
sizeof(struct album_data), compare_album_artists);
@@ -1405,11 +1556,13 @@ retry_artist_lookup:
}
}
- ALIGN_BUFFER(buf, buf_size, 4);
pf_idx.buf = buf;
pf_idx.buf_sz = buf_size;
pf_idx.artist_index = 0;
+ rb->qsort(pf_idx.album_index, pf_idx.album_ct,
+ sizeof(struct album_data), compare_albums);
+
return (pf_idx.album_ct > 0) ? 0 : ERROR_NO_ALBUMS;
}
@@ -1479,7 +1632,6 @@ static int load_album_index(void){
//rb->lseek(fr, sizeof(data) + 1, SEEK_SET);
/* artist names */
- ALIGN_BUFFER(buf, buf_size, 4);
if (read2buf(fr, buf, data.artist_len) == 0)
goto failure;
@@ -1488,7 +1640,6 @@ static int load_album_index(void){
buf_size -= data.artist_len;
/* album names */
- ALIGN_BUFFER(buf, buf_size, 4);
if (read2buf(fr, buf, data.album_len) == 0)
goto failure;
@@ -1497,7 +1648,7 @@ static int load_album_index(void){
buf_size -= data.album_len;
/* index of album names */
- ALIGN_BUFFER(buf, buf_size, 4);
+ ALIGN_BUFFER(buf, buf_size, alignof(struct album_data));
if (read2buf(fr, buf, album_idx_sz) == 0)
goto failure;
@@ -1523,6 +1674,9 @@ static int load_album_index(void){
pf_idx.buf = buf;
pf_idx.buf_sz = buf_size;
+ rb->qsort(pf_idx.album_index, pf_idx.album_ct,
+ sizeof(struct album_data), compare_albums);
+
return 0;
}
}
@@ -1576,6 +1730,14 @@ static char* get_album_artist(const int slide_index)
}
+static char* get_slide_name(const int slide_index, bool artist)
+{
+ if (artist)
+ return get_album_artist(slide_index);
+
+ return get_album_name(slide_index);
+}
+
/**
Return a pointer to the track name of the active album
create_track_index has to be called first.
@@ -1597,29 +1759,91 @@ static char* get_track_filename(const int track_index)
-static int get_album_artist_alpha_prev_index(void)
+static int jmp_idx_prev(void)
{
- char* current_album_artist = get_album_artist(center_index);
- for (int i = center_index - 1; i > 0; i-- )
+ if (aa_cache.inspected < pf_idx.album_ct)
+ {
+#ifdef USEGSLIB
+ grey_show(false);
+ rb->lcd_clear_display();
+ rb->lcd_update();
+#endif
+ rb->splash(HZ*2, rb->str(LANG_WAIT_FOR_CACHE));
+#ifdef USEGSLIB
+ grey_show(true);
+#endif
+ return center_index;
+ }
+
+ if (pf_cfg.sort_albums_by == SORT_BY_YEAR)
{
- int artist_idx = pf_idx.album_index[i].artist_idx;
- if(rb->strncmp(pf_idx.artist_names + artist_idx, current_album_artist, 1))
- current_album_artist = pf_idx.artist_names + artist_idx;
- while (i > 0 && !rb->strncmp(pf_idx.artist_names + pf_idx.album_index[i-1].artist_idx, current_album_artist, 1))
- i--;
- return i;
+ int current_year = pf_idx.album_index[center_index].year;
+
+ for (int i = center_index - 1; i > 0; i-- )
+ {
+ if(pf_idx.album_index[i].year != current_year)
+ current_year = pf_idx.album_index[i].year;
+ while (i > 0)
+ {
+ if (pf_idx.album_index[i-1].year != current_year)
+ break;
+ i--;
+ }
+ return i;
+ }
}
+ else
+ {
+ bool by_artist = pf_cfg.sort_albums_by != SORT_BY_NAME;
+ char *current_selection = get_slide_name(center_index, by_artist);
+
+ for (int i = center_index - 1; i > 0; i-- )
+ {
+ if(rb->strncmp(get_slide_name(i, by_artist), current_selection, 1))
+ current_selection = get_slide_name(i, by_artist);
+ while (i > 0)
+ {
+ if (rb->strncmp(get_slide_name(i-1, by_artist), current_selection, 1))
+ break;
+ i--;
+ }
+ return i;
+ }
+ }
+
return 0;
}
-static int get_album_artist_alpha_next_index(void)
+static int jmp_idx_next(void)
{
- char* current_album_artist = get_album_artist(center_index);
- for (int i = center_index + 1; i < pf_idx.album_ct; i++ )
+ if (aa_cache.inspected < pf_idx.album_ct)
{
- int artist_idx = pf_idx.album_index[i].artist_idx;
- if(rb->strncmp(pf_idx.artist_names + artist_idx, current_album_artist, 1))
- return i;
+#ifdef USEGSLIB
+ grey_show(false);
+ rb->lcd_clear_display();
+ rb->lcd_update();
+#endif
+ rb->splash(HZ*2, rb->str(LANG_WAIT_FOR_CACHE));
+#ifdef USEGSLIB
+ grey_show(true);
+#endif
+ return center_index;
+ }
+
+ if (pf_cfg.sort_albums_by == SORT_BY_YEAR)
+ {
+ int current_year = pf_idx.album_index[center_index].year;
+ for (int i = center_index + 1; i < pf_idx.album_ct; i++ )
+ if(pf_idx.album_index[i].year != current_year)
+ return i;
+ }
+ else
+ {
+ bool by_artist = pf_cfg.sort_albums_by != SORT_BY_NAME;
+ char *current_selection = get_slide_name(center_index, by_artist);
+ for (int i = center_index + 1; i < pf_idx.album_ct; i++ )
+ if(rb->strncmp(get_slide_name(i, by_artist), current_selection, 1))
+ return i;
}
return pf_idx.album_ct - 1;
}
@@ -1719,17 +1943,10 @@ static int pf_tcs_retrieve_track_title(int string_index, int disc_num, int track
if (rb->strcmp(UNTAGGED, tcs.result) == 0)
{
/* show filename instead of <untaggged> */
- if (!rb->tagcache_retrieve(&tcs, tcs.idx_id, tag_filename,
+ if (!rb->tagcache_retrieve(&tcs, tcs.idx_id, tag_virt_basename,
file_name, MAX_PATH))
return 0;
track_title = file_name;
- if (track_title)
- {
- /* if filename remove the '/' */
- track_title = rb->strrchr(track_title, PATH_SEPCH);
- if (track_title)
- track_title++;
- }
}
if (!track_title)
@@ -1772,6 +1989,8 @@ static int pf_tcs_retrieve_file_name(int fn_idx)
*/
static void create_track_index(const int slide_index)
{
+ char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
buf_ctx_lock();
if ( slide_index == pf_tracks.cur_idx )
return;
@@ -1789,7 +2008,7 @@ static void create_track_index(const int slide_index)
int string_index = 0;
pf_tracks.count = 0;
- while (rb->tagcache_get_next(&tcs))
+ while (rb->tagcache_get_next(&tcs, tcs_buf, tcs_bufsz))
{
int disc_num = rb->tagcache_get_numeric(&tcs, tag_discnumber);
int track_num = rb->tagcache_get_numeric(&tcs, tag_tracknumber);
@@ -1863,15 +2082,12 @@ static inline void free_borrowed_tracks(void)
static bool get_albumart_for_index_from_db(const int slide_index, char *buf,
int buflen)
{
- if ( slide_index == -1 )
- {
- rb->strlcpy( buf, EMPTY_SLIDE, buflen );
- }
-
+ bool ret;
+ char tcs_buf[TAGCACHE_BUFSZ];
+ const long tcs_bufsz = sizeof(tcs_buf);
if (tcs.valid || !rb->tagcache_search(&tcs, tag_filename))
return false;
- bool result;
/* find the first track of the album */
rb->tagcache_search_add_filter(&tcs, tag_album,
pf_idx.album_index[slide_index].seek);
@@ -1879,36 +2095,12 @@ static bool get_albumart_for_index_from_db(const int slide_index, char *buf,
rb->tagcache_search_add_filter(&tcs, tag_albumartist,
pf_idx.album_index[slide_index].artist_seek);
- if ( rb->tagcache_get_next(&tcs) ) {
- struct mp3entry id3;
- int fd;
-
-#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
- if (rb->tagcache_fill_tags(&id3, tcs.result))
- {
- rb->strlcpy(id3.path, tcs.result, sizeof(id3.path));
- }
- else
-#endif
- {
- fd = rb->open(tcs.result, O_RDONLY);
- if (fd) {
- rb->get_metadata(&id3, fd, tcs.result);
- rb->close(fd);
- }
- }
+ ret = rb->tagcache_get_next(&tcs, tcs_buf, tcs_bufsz) &&
+ retrieve_id3(&id3, tcs.result) &&
+ search_albumart_files(&id3, ":", buf, buflen);
- if ( search_albumart_files(&id3, ":", buf, buflen) )
- result = true;
- else
- result = false;
- }
- else {
- /* did not find a matching track */
- result = false;
- }
rb->tagcache_search_finish(&tcs);
- return result;
+ return ret;
}
/**
@@ -2005,6 +2197,9 @@ 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;
@@ -2026,13 +2221,7 @@ static bool save_pfraw(char* filename, struct bitmap *bm)
int fh = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
if( fh < 0 ) return false;
rb->write( fh, &bmph, sizeof( struct pfraw_header ) );
- pix_t *data = (pix_t*)( bm->data );
- int y;
- for( y = 0; y < bm->height; y++ )
- {
- rb->write( fh, data , sizeof( pix_t ) * bm->width );
- data += bm->width;
- }
+ rb->write( fh, bm->data , sizeof( pix_t ) * bm->width * bm->height );
rb->close( fh );
return true;
}
@@ -2052,8 +2241,6 @@ static bool incremental_albumart_cache(bool verbose)
unsigned int hash_artist, hash_album;
unsigned int format = FORMAT_NATIVE;
- bool update = (pf_cfg.cache_version == CACHE_UPDATE);
-
if (pf_cfg.resize)
format |= FORMAT_RESIZE|FORMAT_KEEP_ASPECT;
@@ -2065,8 +2252,6 @@ static bool incremental_albumart_cache(bool verbose)
aa_cache.inspected++;
if (aa_cache.idx >= pf_idx.album_ct) { aa_cache.idx = 0; } /* Rollover */
- if (!get_albumart_for_index_from_db(idx, aa_cache.file, sizeof(aa_cache.file)))
- goto aa_failure; //rb->strcpy(aa_cache.file, EMPTY_SLIDE_BMP);
hash_artist = mfnv(get_album_artist(idx));
hash_album = mfnv(get_album_name(idx));
@@ -2074,13 +2259,15 @@ static bool incremental_albumart_cache(bool verbose)
rb->snprintf(aa_cache.pfraw_file, sizeof(aa_cache.pfraw_file),
CACHE_PREFIX "/%x%x.pfraw", hash_album, hash_artist);
- if(rb->file_exists(aa_cache.pfraw_file)) {
- if(update) {
- aa_cache.slides++;
- goto aa_success;
- }
+ if(pf_cfg.update_albumart && rb->file_exists(aa_cache.pfraw_file)) {
+ aa_cache.slides++;
+ goto aa_success;
}
+ if (!get_albumart_for_index_from_db(idx, aa_cache.file, sizeof(aa_cache.file)))
+ goto aa_failure; //rb->strcpy(aa_cache.file, EMPTY_SLIDE_BMP);
+
+
aa_cache.input_bmp.data = aa_cache.buf;
aa_cache.input_bmp.width = DISPLAY_WIDTH;
aa_cache.input_bmp.height = DISPLAY_HEIGHT;
@@ -2118,6 +2305,8 @@ aa_success:
configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS,
CONFIG_VERSION);
free_all_slide_prio(0);
+ if (pf_state == pf_idle)
+ rb->queue_post(&thread_q, EV_WAKEUP, 0);
}
if(verbose)/* direct interaction with user */
@@ -2195,12 +2384,15 @@ static void thread(void)
/* we just woke up */
break;
}
- if(ev.id != SYS_TIMEOUT)
- while ( load_new_slide() ) {
- rb->yield();
- switch (ev.id) {
- case EV_EXIT:
- return;
+
+ if(ev.id != SYS_TIMEOUT) {
+ while ( rb->queue_empty(&thread_q) ) {
+ buf_ctx_lock();
+ bool slide_loaded = load_new_slide();
+ buf_ctx_unlock();
+ if (!slide_loaded)
+ break;
+ rb->yield();
}
}
}
@@ -2527,7 +2719,6 @@ static inline bool load_and_prepare_surface(const int slide_index,
*/
bool load_new_slide(void)
{
- buf_ctx_lock();
if (wants_to_quit)
return false;
@@ -2581,7 +2772,6 @@ bool load_new_slide(void)
pf_sldcache.center_idx = i;
pf_sldcache.left_idx = i;
pf_sldcache.right_idx = i;
- buf_ctx_unlock();
return true;
}
}
@@ -2615,7 +2805,6 @@ bool load_new_slide(void)
{
if (pf_sldcache.free == -1 && !free_slide_prio(prio_l))
{
- buf_ctx_unlock();
return false;
}
@@ -2624,14 +2813,12 @@ bool load_new_slide(void)
{
lla_insert_before(&pf_sldcache.used, i, pf_sldcache.left_idx);
pf_sldcache.left_idx = i;
- buf_ctx_unlock();
return true;
}
} else if(right < number_of_slides - 1)
{
if (pf_sldcache.free == -1 && !free_slide_prio(prio_r))
{
- buf_ctx_unlock();
return false;
}
@@ -2640,7 +2827,6 @@ bool load_new_slide(void)
{
lla_insert_after(i, pf_sldcache.right_idx);
pf_sldcache.right_idx = i;
- buf_ctx_unlock();
return true;
}
}
@@ -2655,7 +2841,6 @@ insert_first_slide:
pf_sldcache.left_idx = i;
pf_sldcache.right_idx = i;
pf_sldcache.used = i;
- buf_ctx_unlock();
return true;
}
}
@@ -2664,12 +2849,10 @@ fail_and_refree:
{
lla_insert_tail(&pf_sldcache.free, i);
}
- buf_ctx_unlock();
return false;
fatal_fail:
free_all_slide_prio(0);
initialize_slide_cache();
- buf_ctx_unlock();
return false;
}
@@ -2704,13 +2887,19 @@ static inline struct dim *surface(const int slide_index)
{
int j = 0;
do {
- if (pf_sldcache.cache[i].index == slide_index)
+ if (pf_sldcache.cache[i].index == slide_index) {
+ if (is_initial_slide && slide_index == center_index)
+ is_initial_slide = false;
return get_slide(pf_sldcache.cache[i].hid);
+ }
i = pf_sldcache.cache[i].next;
j++;
} while (i != pf_sldcache.used && j < SLIDE_CACHE_SIZE);
}
- return get_slide(empty_slide_hid);
+ if (is_initial_slide && slide_index == center_index)
+ return NULL;
+ else
+ return get_slide(empty_slide_hid);
}
/**
@@ -2898,7 +3087,7 @@ static void render_slide(struct slide_data *slide, const int alpha)
const pix_t *ptr = &src[column * bmp->height];
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define PIXELSTEP_Y 1
#define LCDADDR(x, y) (&buffer[BUFFER_HEIGHT*(x) + (y)])
#else
@@ -2971,17 +3160,106 @@ static inline void set_current_slide(const int slide_index)
{
int old_center_index = center_index;
step = 0;
- center_index = fbound(slide_index, 0, number_of_slides - 1);
+ center_index = fbound(0, slide_index, number_of_slides - 1);
if (old_center_index != center_index)
{
rb->queue_remove_from_head(&thread_q, EV_WAKEUP);
rb->queue_post(&thread_q, EV_WAKEUP, 0);
}
target = center_index;
- slide_frame = slide_index << 16;
+ slide_frame = center_index << 16;
reset_slides();
}
+
+static void skip_animation_to_idle_state(void);
+static bool sort_albums(int new_sorting, bool from_settings)
+{
+ int i, album_idx, artist_idx;
+ char* current_album_name = NULL;
+ char* current_album_artist = NULL;
+ static const char* sort_options[] = {
+ ID2P(LANG_ARTIST_PLUS_NAME),
+ ID2P(LANG_ARTIST_PLUS_YEAR),
+ ID2P(LANG_ID3_YEAR),
+ ID2P(LANG_NAME)
+ };
+
+ /* Only change sorting once artwork has been inspected */
+ if (aa_cache.inspected < pf_idx.album_ct)
+ {
+#ifdef USEGSLIB
+ if (!from_settings)
+ grey_show(false);
+#endif
+ rb->splash(HZ*2, rb->str(LANG_WAIT_FOR_CACHE));
+#ifdef USEGSLIB
+ if (!from_settings)
+ grey_show(true);
+#endif
+ return false;
+ }
+
+ /* set idle state */
+ if (pf_state == pf_show_tracks)
+ free_borrowed_tracks();
+ if (pf_state == pf_show_tracks ||
+ pf_state == pf_cover_in ||
+ pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
+ else if (pf_state == pf_scrolling)
+ set_current_slide(target);
+ pf_state = pf_idle;
+
+ pf_cfg.sort_albums_by = new_sorting;
+ if (!from_settings)
+ {
+#ifdef USEGSLIB
+ grey_show(false);
+#if LCD_DEPTH > 1
+ rb->lcd_set_background(N_BRIGHT(0));
+ rb->lcd_set_foreground(N_BRIGHT(255));
+#endif
+ rb->lcd_clear_display();
+ rb->lcd_update();
+#endif
+ rb->splash(HZ, sort_options[pf_cfg.sort_albums_by]);
+#ifdef USEGSLIB
+ grey_show(true);
+#endif
+ }
+
+ current_album_artist = get_album_artist(center_index);
+ current_album_name = get_album_name(center_index);
+
+ end_pf_thread(); /* stop loading of covers */
+
+ rb->qsort(pf_idx.album_index, pf_idx.album_ct,
+ sizeof(struct album_data), compare_albums);
+
+ /* Empty cache and restart cover loading thread */
+ rb->buflib_init(&buf_ctx, (void *)pf_idx.buf, pf_idx.buf_sz);
+ empty_slide_hid = read_pfraw(EMPTY_SLIDE, 0);
+ initialize_slide_cache();
+ is_initial_slide = true;
+ create_pf_thread();
+
+ /* Go to previously selected slide */
+ for (i = 0; i < pf_idx.album_ct; i++ )
+ {
+ album_idx = pf_idx.album_index[i].name_idx;
+ artist_idx = pf_idx.album_index[i].artist_idx;
+
+ if(!rb->strcmp(pf_idx.album_names + album_idx, current_album_name) &&
+ !rb->strcmp(pf_idx.artist_names + artist_idx, current_album_artist))
+ {
+ set_current_slide(i);
+ pf_cfg.last_album = i;
+ }
+ }
+ return true;
+}
+
/**
Start the animation for changing slides
*/
@@ -2991,6 +3269,8 @@ static void start_animation(void)
pf_state = pf_scrolling;
}
+static void update_scroll_animation(void);
+
/**
Go to the previous slide
*/
@@ -3004,6 +3284,8 @@ static void show_previous_slide(void)
} else if ( step > 0 ) {
target = center_index;
step = (target <= center_slide.slide_index) ? -1 : 1;
+ if (step < 0)
+ update_scroll_animation();
} else {
target = fmax(0, center_index - 2);
}
@@ -3023,6 +3305,8 @@ static void show_next_slide(void)
} else if ( step < 0 ) {
target = center_index;
step = (target < center_slide.slide_index) ? -1 : 1;
+ if (step > 0)
+ update_scroll_animation();
} else {
target = fmin(center_index + 2, number_of_slides - 1);
}
@@ -3199,31 +3483,47 @@ static void cleanup(void)
rb->cpu_boost(false);
#endif
end_pf_thread();
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
#ifdef USEGSLIB
grey_release();
#endif
}
+static void skip_animation_to_show_tracks(void);
+static void adjust_album_display_for_setting(int old_val, int new_val)
+{
+ if (old_val == new_val)
+ return;
+
+ reset_track_list();
+ recalc_offsets();
+ reset_slides();
+
+ if (pf_state == pf_show_tracks)
+ skip_animation_to_show_tracks();
+}
+
/**
Shows the settings menu
*/
static int settings_menu(void)
{
int selection = 0;
- bool old_val;
+ int old_val;
MENUITEM_STRINGLIST(settings_menu, "PictureFlow Settings", NULL,
+ ID2P(LANG_SHOW_ALBUM_TITLE),
+ ID2P(LANG_SHOW_YEAR_IN_ALBUM_TITLE),
+ ID2P(LANG_SORT_ALBUMS_BY),
+ ID2P(LANG_YEAR_SORT_ORDER),
ID2P(LANG_DISPLAY_FPS),
ID2P(LANG_SPACING),
ID2P(LANG_CENTRE_MARGIN),
ID2P(LANG_NUMBER_OF_SLIDES),
ID2P(LANG_ZOOM),
- ID2P(LANG_SHOW_ALBUM_TITLE),
ID2P(LANG_RESIZE_COVERS),
ID2P(LANG_REBUILD_CACHE),
ID2P(LANG_UPDATE_CACHE),
@@ -3237,6 +3537,16 @@ static int settings_menu(void)
{ STR(LANG_SHOW_ALL_AT_THE_TOP) },
{ STR(LANG_SHOW_ALL_AT_THE_BOTTOM) },
};
+ static const struct opt_items sort_options[] = {
+ { STR(LANG_ARTIST_PLUS_NAME) },
+ { STR(LANG_ARTIST_PLUS_YEAR) },
+ { STR(LANG_ID3_YEAR) },
+ { STR(LANG_NAME) }
+ };
+ static const struct opt_items year_sort_order_options[] = {
+ { STR(LANG_ASCENDING) },
+ { STR(LANG_DESCENDING) }
+ };
static const struct opt_items wps_options[] = {
{ STR(LANG_OFF) },
{ STR(LANG_DIRECT) },
@@ -3251,73 +3561,96 @@ static int settings_menu(void)
selection=rb->do_menu(&settings_menu,&selection, NULL, false);
switch(selection) {
case 0:
+ old_val = pf_cfg.show_album_name;
+ rb->set_option(rb->str(LANG_SHOW_ALBUM_TITLE),
+ &pf_cfg.show_album_name, RB_INT, album_name_options, 5, NULL);
+ adjust_album_display_for_setting(old_val, pf_cfg.show_album_name);
+ break;
+ case 1:
+ rb->set_bool(rb->str(LANG_SHOW_YEAR_IN_ALBUM_TITLE), &pf_cfg.show_year);
+ break;
+ case 2:
+ old_val = pf_cfg.sort_albums_by;
+ rb->set_option(rb->str(LANG_SORT_ALBUMS_BY),
+ &pf_cfg.sort_albums_by, RB_INT, sort_options, 4, NULL);
+ if (old_val != pf_cfg.sort_albums_by &&
+ !sort_albums(pf_cfg.sort_albums_by, true))
+ pf_cfg.sort_albums_by = old_val;
+ break;
+ case 3:
+ old_val = pf_cfg.year_sort_order;
+ rb->set_option(rb->str(LANG_YEAR_SORT_ORDER),
+ &pf_cfg.year_sort_order, RB_INT, year_sort_order_options, 2, NULL);
+ if (old_val != pf_cfg.year_sort_order &&
+ !sort_albums(pf_cfg.sort_albums_by, true))
+ pf_cfg.year_sort_order = old_val;
+ break;
+ case 4:
+ old_val = pf_cfg.show_fps;
rb->set_bool(rb->str(LANG_DISPLAY_FPS), &pf_cfg.show_fps);
- reset_track_list();
+ if (old_val != pf_cfg.show_fps)
+ reset_track_list();
break;
- case 1:
+ case 5:
+ old_val = pf_cfg.slide_spacing;
rb->set_int(rb->str(LANG_SPACING), "", 1,
&pf_cfg.slide_spacing,
NULL, 1, 0, 100, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.slide_spacing);
break;
- case 2:
+ case 6:
+ old_val = pf_cfg.center_margin;
rb->set_int(rb->str(LANG_CENTRE_MARGIN), "", 1,
&pf_cfg.center_margin,
NULL, 1, 0, 80, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.center_margin);
break;
- case 3:
+ case 7:
+ old_val = pf_cfg.num_slides;
rb->set_int(rb->str(LANG_NUMBER_OF_SLIDES), "", 1,
&pf_cfg.num_slides, NULL, 1, 1, MAX_SLIDES_COUNT, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.num_slides);
break;
- case 4:
+ case 8:
+ old_val = pf_cfg.zoom;
rb->set_int(rb->str(LANG_ZOOM), "", 1, &pf_cfg.zoom,
NULL, 1, 10, 300, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.zoom);
break;
- case 5:
- rb->set_option(rb->str(LANG_SHOW_ALBUM_TITLE),
- &pf_cfg.show_album_name, INT, album_name_options, 5, NULL);
- reset_track_list();
- recalc_offsets();
- reset_slides();
- break;
- case 6:
+
+ case 9:
old_val = pf_cfg.resize;
rb->set_bool(rb->str(LANG_RESIZE_COVERS), &pf_cfg.resize);
if (old_val == pf_cfg.resize) /* changed? */
break;
/* fallthrough if changed, since cache needs to be rebuilt */
- case 7:
+ case 10:
+ pf_cfg.update_albumart = false;
pf_cfg.cache_version = CACHE_REBUILD;
rb->remove(EMPTY_SLIDE);
configfile_save(CONFIG_FILE, config,
CONFIG_NUM_ITEMS, CONFIG_VERSION);
rb->splash(HZ, ID2P(LANG_CACHE_REBUILT_NEXT_RESTART));
break;
- case 8:
- pf_cfg.cache_version = CACHE_UPDATE;
+ case 11:
+ pf_cfg.update_albumart = true;
+ pf_cfg.cache_version = CACHE_REBUILD;
rb->remove(EMPTY_SLIDE);
configfile_save(CONFIG_FILE, config,
CONFIG_NUM_ITEMS, CONFIG_VERSION);
rb->splash(HZ, ID2P(LANG_CACHE_REBUILT_NEXT_RESTART));
break;
- case 9:
+ case 12:
rb->set_option(rb->str(LANG_WPS_INTEGRATION),
- &pf_cfg.auto_wps, INT, wps_options, 3, NULL);
+ &pf_cfg.auto_wps, RB_INT, wps_options, 3, NULL);
break;
- case 10:
+ case 13:
rb->set_option(rb->str(LANG_BACKLIGHT),
- &pf_cfg.backlight_mode, INT, backlight_options, 2, NULL);
+ &pf_cfg.backlight_mode, RB_INT, backlight_options, 2, NULL);
break;
case MENU_ATTACHED_USB:
@@ -3331,45 +3664,67 @@ static int settings_menu(void)
Show the main menu
*/
enum {
+ PF_SHOW_TRACKS_WHILE_BROWSING,
+ PF_GOTO_LAST_ALBUM,
PF_GOTO_WPS,
#if PF_PLAYBACK_CAPABLE
- PF_MENU_CLEAR_PLAYLIST,
PF_MENU_PLAYBACK_CONTROL,
#endif
PF_MENU_SETTINGS,
- PF_MENU_RETURN,
PF_MENU_QUIT,
};
static int main_menu(void)
{
int selection = 0;
- int result;
+ int result, curr_album;
#if LCD_DEPTH > 1
rb->lcd_set_foreground(N_BRIGHT(255));
#endif
MENUITEM_STRINGLIST(main_menu, "PictureFlow Main Menu", NULL,
+ ID2P(LANG_SHOW_TRACKS_WHILE_BROWSING),
+ ID2P(LANG_GOTO_LAST_ALBUM),
ID2P(LANG_GOTO_WPS),
#if PF_PLAYBACK_CAPABLE
- ID2P(LANG_CLEAR_PLAYLIST),
ID2P(LANG_PLAYBACK_CONTROL),
#endif
ID2P(LANG_SETTINGS),
- ID2P(LANG_RETURN),
ID2P(LANG_MENU_QUIT));
while (1) {
switch (rb->do_menu(&main_menu,&selection, NULL, false)) {
+ case PF_SHOW_TRACKS_WHILE_BROWSING:
+ if (pf_state != pf_show_tracks)
+ {
+ if (pf_state == pf_scrolling)
+ set_current_slide(target);
+
+ skip_animation_to_show_tracks();
+ }
+ show_tracks_while_browsing = true;
+ return 0;
+ case PF_GOTO_LAST_ALBUM:
+ if (pf_state == pf_scrolling)
+ curr_album = target;
+ else
+ curr_album = center_index;
+
+ if (pf_state == pf_show_tracks)
+ free_borrowed_tracks();
+ if (pf_state == pf_show_tracks ||
+ pf_state == pf_cover_in ||
+ pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
+
+ set_current_slide(pf_cfg.last_album);
+ pf_cfg.last_album = curr_album;
+
+ pf_state = pf_idle;
+ return 0;
case PF_GOTO_WPS: /* WPS */
return -2;
#if PF_PLAYBACK_CAPABLE
- case PF_MENU_CLEAR_PLAYLIST:
- if(rb->warn_on_pl_erase() && rb->playlist_remove_all_tracks(NULL) == 0) {
- rb->playlist_create(NULL, NULL);
- rb->splash(HZ*2, ID2P(LANG_PLAYLIST_CLEARED));
- }
- break;
case PF_MENU_PLAYBACK_CONTROL: /* Playback Control */
playback_control(NULL);
break;
@@ -3378,8 +3733,6 @@ static int main_menu(void)
result = settings_menu();
if ( result != 0 ) return result;
break;
- case PF_MENU_RETURN:
- return 0;
case PF_MENU_QUIT:
return -1;
@@ -3392,21 +3745,33 @@ static int main_menu(void)
}
}
+#define ZOOMIN_FRAME_COUNT 19
+#define ZOOMIN_FRAME_DIST -5
+#define ZOOMIN_FRAME_ANGLE 1
+#define ZOOMIN_FRAME_FADE 13
+
+#define ROTATE_FRAME_COUNT 15
+#define ROTATE_FRAME_ANGLE 16
+
+#define KEYFRAME_COUNT ZOOMIN_FRAME_COUNT + ROTATE_FRAME_COUNT
+
/**
Animation step for zooming into the current cover
*/
static void update_cover_in_animation(void)
{
cover_animation_keyframe++;
- if( cover_animation_keyframe < 20 ) {
- center_slide.distance-=5;
- center_slide.angle+=1;
- extra_fade += 13;
- }
- else if( cover_animation_keyframe < 35 ) {
- center_slide.angle+=16;
+
+ if(cover_animation_keyframe <= ZOOMIN_FRAME_COUNT)
+ {
+ center_slide.distance += ZOOMIN_FRAME_DIST;
+ center_slide.angle += ZOOMIN_FRAME_ANGLE;
+ extra_fade += ZOOMIN_FRAME_FADE;
}
- else {
+ else if(cover_animation_keyframe <= KEYFRAME_COUNT)
+ center_slide.angle += ROTATE_FRAME_ANGLE;
+ else
+ {
cover_animation_keyframe = 0;
pf_state = pf_show_tracks;
}
@@ -3418,36 +3783,40 @@ static void update_cover_in_animation(void)
static void update_cover_out_animation(void)
{
cover_animation_keyframe++;
- if( cover_animation_keyframe <= 15 ) {
- center_slide.angle-=16;
- }
- else if( cover_animation_keyframe < 35 ) {
- center_slide.distance+=5;
- center_slide.angle-=1;
- extra_fade -= 13;
+
+ if(cover_animation_keyframe <= ROTATE_FRAME_COUNT)
+ center_slide.angle -= ROTATE_FRAME_ANGLE;
+ else if(cover_animation_keyframe <= KEYFRAME_COUNT)
+ {
+ center_slide.distance -= ZOOMIN_FRAME_DIST;
+ center_slide.angle -= ZOOMIN_FRAME_ANGLE;
+ extra_fade -= ZOOMIN_FRAME_FADE;
}
- else {
+ else
+ {
cover_animation_keyframe = 0;
pf_state = pf_idle;
}
}
/**
- Skip steps for zooming into the current cover
+ Immediately show tracks and skip any animation frames
*/
-static void interrupt_cover_in_animation(void)
+static void skip_animation_to_show_tracks(void)
{
pf_state = pf_show_tracks;
cover_animation_keyframe = 0;
- extra_fade = 13 * 19;
- center_slide.distance = -5 * 19;
- center_slide.angle = 19 + (15 * 16);
+
+ extra_fade = ZOOMIN_FRAME_COUNT * ZOOMIN_FRAME_FADE;
+ center_slide.distance = ZOOMIN_FRAME_COUNT * ZOOMIN_FRAME_DIST;
+ center_slide.angle = (ZOOMIN_FRAME_COUNT * ZOOMIN_FRAME_ANGLE) +
+ (ROTATE_FRAME_COUNT * ROTATE_FRAME_ANGLE);
}
/**
- Skip steps for zooming out the current cover
+ Immediately transition to idle state and skip any animation frames
*/
-static void interrupt_cover_out_animation(void)
+static void skip_animation_to_idle_state(void)
{
pf_state = pf_idle;
cover_animation_keyframe = 0;
@@ -3456,21 +3825,12 @@ static void interrupt_cover_out_animation(void)
}
/**
- Stop zooming out the current cover and start zooming in
+ Change direction during cover in/out animation
*/
-static void revert_cover_out_animation(void)
+static void reverse_animation(void)
{
- pf_state = pf_cover_in;
- cover_animation_keyframe = 34 - cover_animation_keyframe;
-}
-
-/**
- Stop zooming into the current cover and start zooming out
-*/
-static void revert_cover_in_animation(void)
-{
- pf_state = pf_cover_out;
- cover_animation_keyframe = 34 - cover_animation_keyframe;
+ pf_state = pf_state == pf_cover_out ? pf_cover_in : pf_cover_out;
+ cover_animation_keyframe = KEYFRAME_COUNT - cover_animation_keyframe;
}
/**
@@ -3577,7 +3937,10 @@ static void show_track_list(void)
{
mylcd_clear_display();
if ( center_slide.slide_index != pf_tracks.cur_idx ) {
- show_track_list_loading();
+#ifdef HAVE_TC_RAMCACHE
+ if (!rb->tagcache_is_in_ram())
+#endif
+ show_track_list_loading();
create_track_index(center_slide.slide_index);
if (pf_tracks.count == 0)
{
@@ -3598,7 +3961,7 @@ static void show_track_list(void)
for (; track_i < pf_tracks.list_visible + pf_tracks.list_start; track_i++)
{
char *trackname = get_track_name(track_i);
- if ( track_i == pf_tracks.sel ) {
+ if (track_i == pf_tracks.sel && !show_tracks_while_browsing) {
if (pf_tracks.sel != pf_tracks.last_sel) {
set_scroll_line(trackname, PF_SCROLL_TRACK);
pf_tracks.last_sel = pf_tracks.sel;
@@ -3650,7 +4013,7 @@ static void select_next_album(void)
free_borrowed_tracks();
target = center_index + 1;
set_current_slide(target);
- interrupt_cover_in_animation();
+ skip_animation_to_show_tracks();
}
}
@@ -3660,13 +4023,50 @@ static void select_prev_album(void)
free_borrowed_tracks();
target = center_index - 1;
set_current_slide(target);
- interrupt_cover_in_animation();
+ skip_animation_to_show_tracks();
}
}
#if PF_PLAYBACK_CAPABLE
+static int show_id3_info(const char *selected_file)
+{
+ int i;
+ unsigned long last_tick;
+ const char *file_name;
+ bool is_multiple_tracks = insert_whole_album && pf_tracks.count > 1;
+
+ last_tick = *(rb->current_tick) + HZ/2;
+ rb->splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
+ i = 0;
+ do {
+ file_name = i == 0 ? selected_file : get_track_filename(i);
+ if (rb->mp3info(&id3, file_name))
+ return 0;
+
+ if (is_multiple_tracks)
+ {
+ rb->splash_progress(i, pf_tracks.count,
+ "%s (%s)", rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
+ if (TIME_AFTER(*(rb->current_tick), last_tick + HZ/4))
+ {
+ if (rb->action_userabort(TIMEOUT_NOBLOCK))
+ return 0;
+ last_tick = *(rb->current_tick);
+ }
+
+ collect_id3(&id3, i == 0);
+ rb->yield();
+ }
+ } while (++i < pf_tracks.count && is_multiple_tracks);
+
+ if (is_multiple_tracks)
+ finalize_id3(&id3);
+
+ return rb->browse_id3(&id3, 0, 0, NULL, i) ? PLUGIN_USB_CONNECTED : 0;
+}
+
-static bool playlist_insert(int position, bool queue, bool create_new)
+static bool pf_current_playlist_insert(int position, bool queue, bool create_new)
{
if (position == PLAYLIST_REPLACE)
{
@@ -3697,11 +4097,52 @@ static bool playlist_insert(int position, bool queue, bool create_new)
return true;
}
+
+static int pf_add_to_playlist(const char* playlist, bool new_playlist)
+{
+ int fd;
+ int result = 0;
+
+ if (new_playlist)
+ fd = rb->open_utf8(playlist, O_CREAT|O_WRONLY|O_TRUNC);
+ else
+ fd = rb->open(playlist, O_CREAT|O_WRONLY|O_APPEND, 0666);
+
+ if(fd < 0)
+ return -1;
+
+ rb->reload_directory();
+
+ if (!insert_whole_album)
+ {
+ if (rb->fdprintf(fd, "%s\n", get_track_filename(pf_tracks.sel)) <= 0)
+ result = -1;
+ }
+ else
+ {
+ int i = 0;
+ do {
+ if (rb->fdprintf(fd, "%s\n", get_track_filename(i)) <= 0)
+ {
+ result = -1;
+ break;
+ }
+ rb->yield();
+ } while(++i < pf_tracks.count);
+ }
+ rb->close(fd);
+ return result;
+}
+
+
static bool track_list_ready(void)
{
if (pf_state != pf_show_tracks)
{
- rb->splash(0, ID2P(LANG_WAIT));
+#ifdef HAVE_TC_RAMCACHE
+ if (!rb->tagcache_is_in_ram())
+#endif
+ rb->splash(0, ID2P(LANG_WAIT));
create_track_index(center_slide.slide_index);
if (pf_tracks.count == 0)
{
@@ -3713,14 +4154,8 @@ static bool track_list_ready(void)
return true;
}
-/**
- Brings up "Current Playlist" menu with first
- track of selection.
- Onplay menu code calls back playlist_insert for
- adding all of the tracks.
-*/
-static void show_current_playlist_menu(void)
+static bool context_menu_ready(void)
{
#ifdef USEGSLIB
grey_show(false);
@@ -3732,16 +4167,26 @@ static void show_current_playlist_menu(void)
#ifdef USEGSLIB
grey_show(true);
#endif
- return;
+ return false;
}
- insert_whole_album = pf_state != pf_show_tracks;
+#if LCD_DEPTH > 1
+#ifdef USEGSLIB
+ rb->lcd_set_foreground(N_BRIGHT(0));
+ rb->lcd_set_background(N_BRIGHT(255));
+#endif
+#endif
+ insert_whole_album = (pf_state != pf_show_tracks) || show_tracks_while_browsing;
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_enable(i, true, NULL);
- rb->onplay_show_playlist_menu(get_track_filename(pf_tracks.sel),
- &playlist_insert);
+
+ return true;
+}
+
+static void context_menu_cleanup(void)
+{
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_undo(i, false);
- if (insert_whole_album)
+ if (pf_state != pf_show_tracks)
free_borrowed_tracks();
#ifdef USEGSLIB
grey_show(true);
@@ -3750,6 +4195,60 @@ static void show_current_playlist_menu(void)
}
+static int context_menu(void)
+{
+ char album_name[MAX_PATH];
+ char *file_name = get_track_filename(show_tracks_while_browsing ? 0 : pf_tracks.sel);
+ int attr = FILE_ATTR_AUDIO;
+
+ enum {
+ PF_CURRENT_PLAYLIST = 0,
+ PF_CATALOG,
+ PF_ID3_INFO
+ };
+ MENUITEM_STRINGLIST(context_menu, ID2P(LANG_ONPLAY_MENU_TITLE), NULL,
+ ID2P(LANG_PLAYING_NEXT),
+ ID2P(LANG_ADD_TO_PL),
+ ID2P(LANG_MENU_SHOW_ID3_INFO));
+
+ while (1) {
+ switch (rb->do_menu(&context_menu,
+ NULL, NULL, false)) {
+
+ case PF_CURRENT_PLAYLIST:
+ if (insert_whole_album && pf_tracks.count > 1)
+ {
+ attr = ATTR_DIRECTORY;
+ file_name = NULL;
+ }
+ rb->onplay_show_playlist_menu(file_name, attr, &pf_current_playlist_insert);
+ return 0;
+ case PF_CATALOG:
+ if (insert_whole_album)
+ {
+ /* add a leading slash so that catalog_add_to_a_playlist
+ later prefills the name when creating a new playlist */
+ rb->snprintf(album_name, MAX_PATH, "/%s", get_album_name(center_index));
+ rb->fix_path_part(album_name, 1, sizeof(album_name) - 2);
+ file_name = album_name;
+ attr = ATTR_DIRECTORY;
+ }
+
+ rb->onplay_show_playlist_cat_menu(file_name, attr, &pf_add_to_playlist);
+ return 0;
+ case PF_ID3_INFO:
+ return show_id3_info(file_name);
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ return 0;
+
+ }
+ }
+}
+
+
+
/*
* Puts selected album's tracks into a newly created playlist and starts playing
*/
@@ -3763,7 +4262,9 @@ static bool start_playback(bool return_to_WPS)
#endif
rb->lcd_clear_display();
rb->lcd_update();
-#endif /* USEGSLIB */
+#else /* if !USEGSLIB */
+ (void) return_to_WPS;
+#endif
if (!rb->warn_on_pl_erase() || !track_list_ready())
{
@@ -3780,7 +4281,7 @@ static bool start_playback(bool return_to_WPS)
if (shuffle || center_slide.slide_index != old_playlist
|| (old_shuffle != shuffle))
{
- if (!playlist_insert(PLAYLIST_REPLACE, false, true))
+ if (!pf_current_playlist_insert(PLAYLIST_REPLACE, false, true))
{
#ifdef USEGSLIB
grey_show(true);
@@ -3791,12 +4292,9 @@ static bool start_playback(bool return_to_WPS)
start_index = rb->playlist_shuffle(*rb->current_tick, pf_tracks.sel);
}
rb->playlist_start(start_index, 0, 0);
- rb->playlist_get_current()->num_inserted_tracks = 0; /* prevent warn_on_pl_erase */
old_shuffle = shuffle;
- if (return_to_WPS)
- pf_cfg.last_album = center_index;
#ifdef USEGSLIB
- else
+ if (!return_to_WPS)
grey_show(true);
#endif
return true;
@@ -3808,10 +4306,13 @@ static bool start_playback(bool return_to_WPS)
*/
static void draw_album_text(void)
{
+ char album_and_year[MAX_PATH];
+
if (pf_cfg.show_album_name == ALBUM_NAME_HIDE)
return;
static int prev_albumtxt_index = -1;
+ static bool prev_show_year = false;
int albumtxt_index;
int char_height;
int albumtxt_x, albumtxt_y, artisttxt_x;
@@ -3839,10 +4340,18 @@ static void draw_album_text(void)
}
albumtxt = get_album_name_idx(albumtxt_index, &album_idx);
+ if (pf_cfg.show_year && pf_idx.album_index[albumtxt_index].year > 0)
+ {
+ rb->snprintf(album_and_year, sizeof(album_and_year), "%s – %d",
+ albumtxt, pf_idx.album_index[albumtxt_index].year);
+ } else
+ rb->snprintf(album_and_year, sizeof(album_and_year), "%s", albumtxt);
+
mylcd_set_foreground(G_BRIGHT(c));
- if (albumtxt_index != prev_albumtxt_index) {
- set_scroll_line(albumtxt, PF_SCROLL_ALBUM);
+ if (albumtxt_index != prev_albumtxt_index || pf_cfg.show_year != prev_show_year) {
+ set_scroll_line(album_and_year, PF_SCROLL_ALBUM);
prev_albumtxt_index = albumtxt_index;
+ prev_show_year = pf_cfg.show_year;
}
char_height = rb->screens[SCREEN_MAIN]->getcharheight();
@@ -3867,7 +4376,7 @@ static void draw_album_text(void)
|| (pf_cfg.show_album_name == ALBUM_AND_ARTIST_BOTTOM)){
if (album_idx != (int) pf_idx.album_untagged_idx)
- mylcd_putsxy(albumtxt_x, albumtxt_y, albumtxt);
+ mylcd_putsxy(albumtxt_x, albumtxt_y, album_and_year);
artisttxt = get_album_artist(albumtxt_index);
set_scroll_line(artisttxt, PF_SCROLL_ARTIST);
@@ -3875,37 +4384,22 @@ static void draw_album_text(void)
int y_offset = char_height + char_height/2;
mylcd_putsxy(artisttxt_x, albumtxt_y + y_offset, artisttxt);
} else {
- mylcd_putsxy(albumtxt_x, albumtxt_y, albumtxt);
+ mylcd_putsxy(albumtxt_x, albumtxt_y, album_and_year);
}
}
static void set_initial_slide(const char* selected_file)
{
- if (selected_file == NULL)
- set_current_slide(id3_get_index(rb->audio_current_track()));
+ if (selected_file)
+ set_current_slide(retrieve_id3(&id3, selected_file) ?
+ id3_get_index(&id3) :
+ pf_cfg.last_album);
else
- {
- struct mp3entry id3;
-#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
- if (rb->tagcache_fill_tags(&id3, selected_file))
- set_current_slide(id3_get_index(&id3));
- else
-#endif
- {
- int fd = rb->open(selected_file, O_RDONLY);
- if (fd >= 0)
- {
- if (rb->get_metadata(&id3, fd, selected_file))
- set_current_slide(id3_get_index(&id3));
- else
- set_current_slide(pf_cfg.last_album);
- rb->close(fd);
- }
- else
- set_current_slide(pf_cfg.last_album);
- }
- }
+ set_current_slide(rb->audio_status() ?
+ id3_get_index(rb->audio_current_track()) :
+ pf_cfg.last_album);
+
}
/**
@@ -3940,22 +4434,17 @@ static int pictureflow_main(const char* selected_file)
config_set_defaults(&pf_cfg);
configfile_load(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION);
- if(pf_cfg.auto_wps == 0)
- draw_splashscreen(pf_idx.buf, pf_idx.buf_sz);
- if(pf_cfg.backlight_mode == 0) {
- /* Turn off backlight timeout */
+
#ifdef HAVE_BACKLIGHT
+ if(pf_cfg.backlight_mode == 0)
backlight_ignore_timeout();
#endif
- }
rb->mutex_init(&buf_ctx_mutex);
init_scroll_lines();
init_reflect_table();
- ALIGN_BUFFER(pf_idx.buf, pf_idx.buf_sz, 4);
-
/*Scan will trigger when no file is found or the option was activated*/
if ((pf_cfg.cache_version != CACHE_VERSION)||(load_album_index() < 0)){
rb->splash(HZ/2,"Creating index, please wait");
@@ -3980,32 +4469,30 @@ static int pictureflow_main(const char* selected_file)
return PLUGIN_ERROR;
}
- ALIGN_BUFFER(pf_idx.buf, pf_idx.buf_sz, 4);
- number_of_slides = pf_idx.album_ct;
-
- size_t aa_bufsz = ALIGN_DOWN(pf_idx.buf_sz / 4, 0x4);
+ number_of_slides = pf_idx.album_ct;
+ size_t aa_bufsz = ALIGN_DOWN(pf_idx.buf_sz / 4, sizeof(long));
if (aa_bufsz < DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(pix_t))
{
error_wait("Not enough memory for album art cache");
return PLUGIN_ERROR;
}
- pf_idx.buf_sz -= aa_bufsz;
- ALIGN_BUFFER(pf_idx.buf, pf_idx.buf_sz, 4);
+ ALIGN_BUFFER(pf_idx.buf, pf_idx.buf_sz, sizeof(long));
aa_cache.buf = (char*) pf_idx.buf;
aa_cache.buf_sz = aa_bufsz;
+
pf_idx.buf += aa_bufsz;
- ALIGN_BUFFER(pf_idx.buf, pf_idx.buf_sz, 4);
+ pf_idx.buf_sz -= aa_bufsz;
if (!create_empty_slide(pf_cfg.cache_version != CACHE_VERSION)) {
- config_save(CACHE_REBUILD);
+ config_save(CACHE_REBUILD, false);
error_wait("Could not load the empty slide");
return PLUGIN_ERROR;
}
if ((pf_cfg.cache_version != CACHE_VERSION) && !create_albumart_cache()) {
- config_save(CACHE_REBUILD);
+ config_save(CACHE_REBUILD, false);
error_wait("Could not create album art cache");
} else if(aa_cache.inspected < pf_idx.album_ct) {
rb->splash(HZ * 2, "Updating album art cache in background");
@@ -4013,7 +4500,7 @@ static int pictureflow_main(const char* selected_file)
if (pf_cfg.cache_version != CACHE_VERSION)
{
- config_save(CACHE_VERSION);
+ config_save(CACHE_VERSION, pf_cfg.update_albumart);
}
rb->buflib_init(&buf_ctx, (void *)pf_idx.buf, pf_idx.buf_sz);
@@ -4087,6 +4574,7 @@ static int pictureflow_main(const char* selected_file)
instant_update = true;
break;
case pf_cover_out:
+ show_tracks_while_browsing = false;
update_cover_out_animation();
render_all_slides();
instant_update = true;
@@ -4095,8 +4583,14 @@ static int pictureflow_main(const char* selected_file)
show_track_list();
break;
case pf_idle:
+ show_tracks_while_browsing = false;
render_all_slides();
- incremental_albumart_cache(false);
+ if (aa_cache.inspected < pf_idx.album_ct)
+ {
+ buf_ctx_lock();
+ incremental_albumart_cache(false);
+ buf_ctx_unlock();
+ }
break;
}
@@ -4122,7 +4616,8 @@ static int pictureflow_main(const char* selected_file)
rb->snprintf(fpstxt, sizeof(fpstxt), "%d %%", progress_pct);
}
- if (pf_cfg.show_album_name == ALBUM_NAME_TOP)
+ if (pf_cfg.show_album_name == ALBUM_NAME_TOP ||
+ pf_cfg.show_album_name == ALBUM_AND_ARTIST_TOP)
fpstxt_y = LCD_HEIGHT -
rb->screens[SCREEN_MAIN]->getcharheight();
else
@@ -4133,7 +4628,8 @@ static int pictureflow_main(const char* selected_file)
/* Copy offscreen buffer to LCD and give time to other threads */
- mylcd_update();
+ if (is_initial_slide == false)
+ mylcd_update();
rb->yield();
/*/ Handle buttons */
@@ -4150,15 +4646,17 @@ static int pictureflow_main(const char* selected_file)
case PF_WPS:
return PLUGIN_GOTO_WPS;
case PF_BACK:
- if ( pf_state == pf_show_tracks )
+ if (show_tracks_while_browsing)
+ show_tracks_while_browsing = false;
+ else if (pf_state == pf_show_tracks)
{
pf_state = pf_cover_out;
free_borrowed_tracks();
}
else if (pf_state == pf_cover_in)
- revert_cover_in_animation();
+ reverse_animation();
else if (pf_state == pf_cover_out)
- interrupt_cover_out_animation();
+ skip_animation_to_idle_state();
else if (pf_state == pf_idle || pf_state == pf_scrolling)
return PLUGIN_OK;
break;
@@ -4183,11 +4681,16 @@ static int pictureflow_main(const char* selected_file)
case PF_NEXT:
case PF_NEXT_REPEAT:
if ( pf_state == pf_show_tracks )
- select_next_track();
+ {
+ if (show_tracks_while_browsing)
+ select_next_album();
+ else
+ select_next_track();
+ }
else if (pf_state == pf_cover_in)
- interrupt_cover_in_animation();
+ skip_animation_to_show_tracks();
else if (pf_state == pf_cover_out)
- interrupt_cover_out_animation();
+ skip_animation_to_idle_state();
if ( pf_state == pf_idle || pf_state == pf_scrolling )
show_next_slide();
@@ -4196,47 +4699,75 @@ static int pictureflow_main(const char* selected_file)
case PF_PREV:
case PF_PREV_REPEAT:
if ( pf_state == pf_show_tracks )
- select_prev_track();
+ {
+ if (show_tracks_while_browsing)
+ select_prev_album();
+ else
+ select_prev_track();
+ }
else if (pf_state == pf_cover_in)
- interrupt_cover_in_animation();
+ skip_animation_to_show_tracks();
else if (pf_state == pf_cover_out)
- interrupt_cover_out_animation();
+ skip_animation_to_idle_state();
if ( pf_state == pf_idle || pf_state == pf_scrolling )
show_previous_slide();
break;
+ case PF_SORTING_NEXT:
+ sort_albums((pf_cfg.sort_albums_by + 1) % SORT_VALUES_SIZE, false);
+ break;
+ case PF_SORTING_PREV:
+ sort_albums((pf_cfg.sort_albums_by + (SORT_VALUES_SIZE - 1)) % SORT_VALUES_SIZE, false);
+ break;
case PF_JMP:
- if (pf_state == pf_idle || pf_state == pf_scrolling)
+ if (pf_state == pf_idle || pf_state == pf_scrolling)
+ {
+ int new_idx = jmp_idx_next();
+ if (new_idx != center_index)
{
pf_state = pf_idle;
- set_current_slide(get_album_artist_alpha_next_index());
+ set_current_slide(new_idx);
}
- else if ( pf_state == pf_show_tracks )
- select_next_album();
- break;
+ }
+ else if ( pf_state == pf_show_tracks )
+ select_next_album();
+ break;
case PF_JMP_PREV:
- if (pf_state == pf_idle || pf_state == pf_scrolling)
+ if (pf_state == pf_idle || pf_state == pf_scrolling)
+ {
+ int new_idx = jmp_idx_prev();
+ if (new_idx != center_index)
{
pf_state = pf_idle;
- set_current_slide(get_album_artist_alpha_prev_index());
+ set_current_slide(new_idx);
}
- else if ( pf_state == pf_show_tracks )
- select_prev_album();
- break;
+ }
+ else if ( pf_state == pf_show_tracks )
+ select_prev_album();
+ else if (pf_state == pf_cover_in)
+ reverse_animation();
+ else if (pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
+ break;
#if PF_PLAYBACK_CAPABLE
case PF_CONTEXT:
- if (pf_cfg.auto_wps != 0 &&
- (pf_state == pf_idle || pf_state == pf_scrolling ||
- pf_state == pf_show_tracks || pf_state == pf_cover_out)) {
-
+ if (pf_state == pf_idle || pf_state == pf_scrolling ||
+ pf_state == pf_show_tracks || pf_state == pf_cover_out)
+ {
if ( pf_state == pf_scrolling)
{
set_current_slide(target);
pf_state = pf_idle;
- } else if (pf_state == pf_cover_out)
- interrupt_cover_out_animation();
+ }
+ else if (pf_state == pf_cover_out)
+ skip_animation_to_idle_state();
- show_current_playlist_menu();
+ if (context_menu_ready())
+ {
+ ret = context_menu();
+ context_menu_cleanup();
+ if ( ret != 0 ) return ret;
+ }
}
break;
#endif
@@ -4259,19 +4790,22 @@ static int pictureflow_main(const char* selected_file)
pf_state = pf_cover_in;
}
else if (pf_state == pf_cover_out)
- revert_cover_out_animation();
+ reverse_animation();
else if (pf_state == pf_cover_in)
- interrupt_cover_in_animation();
+ skip_animation_to_show_tracks();
+ else if (pf_state == pf_show_tracks)
+ {
+ if (show_tracks_while_browsing)
+ show_tracks_while_browsing = false;
#if PF_PLAYBACK_CAPABLE
- else if (pf_state == pf_show_tracks) {
- if(pf_cfg.auto_wps != 0) {
+ else if(pf_cfg.auto_wps != 0) {
if (start_playback(true))
return PLUGIN_GOTO_WPS;
}
else
start_playback(false);
- }
#endif
+ }
break;
default:
exit_on_usb(button);
@@ -4292,17 +4826,12 @@ enum plugin_status plugin_start(const void *parameter)
void * buf;
size_t buf_size;
- bool prompt = (parameter && (((char *) parameter)[0] == ACTIVITY_MAINMENU));
bool file_id3 = (parameter && (((char *) parameter)[0] == '/'));
- if (!check_database(prompt))
+ if (!check_database())
{
- if (prompt)
- return PLUGIN_OK;
- else
- error_wait("Please enable database");
-
- return PLUGIN_ERROR;
+ error_wait("Please enable database");
+ return PLUGIN_OK;
}
atexit(cleanup);
@@ -4342,6 +4871,10 @@ enum plugin_status plugin_start(const void *parameter)
ret = file_id3 ? pictureflow_main(file) : pictureflow_main(NULL);
if ( ret == PLUGIN_OK || ret == PLUGIN_GOTO_WPS) {
+ if (pf_state == pf_scrolling)
+ pf_cfg.last_album = target;
+ else
+ pf_cfg.last_album = center_index;
if (configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS,
CONFIG_VERSION))
{
diff --git a/apps/plugins/pitch_detector.c b/apps/plugins/pitch_detector.c
index 4ae43b3236..f7d1219445 100644
--- a/apps/plugins/pitch_detector.c
+++ b/apps/plugins/pitch_detector.c
@@ -503,19 +503,19 @@ static bool main_menu(void)
rb->set_option(
"Algorithm Pickiness (Lower -> more discriminating)",
&settings.yin_threshold,
- INT, yin_threshold_text,
+ RB_INT, yin_threshold_text,
sizeof(yin_threshold_text) / sizeof(yin_threshold_text[0]),
NULL);
break;
case 5:
rb->set_option("Display Accidentals As",
&settings.use_sharps,
- BOOL, accidental_text, 2, NULL);
+ RB_BOOL, accidental_text, 2, NULL);
break;
case 6:
rb->set_option("Key Transposition",
&settings.key_transposition,
- INT, transpose_text, 12, NULL);
+ RB_INT, transpose_text, 12, NULL);
break;
case 7:
rb->set_bool("Display Frequency (Hz)",
@@ -1013,6 +1013,11 @@ static void record_and_get_pitch(void)
break;
case PLA_CANCEL:
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ case PLA_UP:
+#endif
rb->pcm_stop_recording();
quit = main_menu();
if(!quit)
diff --git a/apps/plugins/plasma.c b/apps/plugins/plasma.c
index f944d3d775..cdb6e66569 100644
--- a/apps/plugins/plasma.c
+++ b/apps/plugins/plasma.c
@@ -140,10 +140,10 @@ static void cleanup(void)
#ifndef HAVE_LCD_COLOR
grey_release();
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_RGB565);
#endif
@@ -268,6 +268,11 @@ int main(void)
switch(action)
{
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ case PLA_UP:
+#endif
case PLA_EXIT:
case PLA_CANCEL:
return PLUGIN_OK;
@@ -277,7 +282,11 @@ int main(void)
case PLA_SCROLL_FWD:
case PLA_SCROLL_FWD_REPEAT:
#endif
+#if (CONFIG_KEYPAD != IPOD_1G2G_PAD) \
+ && (CONFIG_KEYPAD != IPOD_3G_PAD) \
+ && (CONFIG_KEYPAD != IPOD_4G_PAD)
case PLA_UP:
+#endif
case PLA_UP_REPEAT:
++plasma_frequency;
wave_table_generate();
@@ -321,10 +330,10 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_PAL256);
#endif
diff --git a/apps/plugins/playing_time.c b/apps/plugins/playing_time.c
new file mode 100644
index 0000000000..354c5a3e06
--- /dev/null
+++ b/apps/plugins/playing_time.c
@@ -0,0 +1,392 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ *
+ * 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"
+
+#define rb_talk_ids(enqueue, ids...) rb->talk_idarray(TALK_IDARRAY(ids), enqueue)
+
+/* units used with output_dyn_value */
+const unsigned char * const byte_units[] =
+{
+ ID2P(LANG_BYTE),
+ ID2P(LANG_KIBIBYTE),
+ ID2P(LANG_MEBIBYTE),
+ ID2P(LANG_GIBIBYTE)
+};
+
+const unsigned char * const * const kibyte_units = &byte_units[1];
+
+enum ePT_SECS {
+ ePT_SECS_TTL = 0,
+ ePT_SECS_BEF,
+ ePT_SECS_AFT,
+ ePT_SECS_COUNT
+};
+
+enum ePT_KBS {
+ /* Note: Order matters (voicing order of LANG_PLAYTIME_STORAGE) */
+ ePT_KBS_TTL = 0,
+ ePT_KBS_BEF,
+ ePT_KBS_AFT,
+ ePT_KBS_COUNT
+};
+
+/* playing_time screen context */
+struct playing_time_info {
+ int curr_index; /* index of currently playing track in playlist */
+ int curr_display_index; /* display index of currently playing track in playlist */
+ int nb_tracks; /* how many tracks in playlist */
+
+ /* seconds total, before, and after current position. Datatype
+ allows for values up to 68years. If I had kept it in ms
+ though, it would have overflowed at 24days, which takes
+ something like 8.5GB at 32kbps, and so we could conceivably
+ have playlists lasting longer than that. */
+ long secs[ePT_SECS_COUNT];
+ long trk_secs[ePT_SECS_COUNT];
+
+ /* kilobytes played total, before, and after current pos.
+ Kilobytes because bytes would overflow. Data type range is up
+ to 2TB. */
+ long kbs[ePT_KBS_COUNT];
+};
+
+/* list callback for playing_time screen */
+static const char * playing_time_get_or_speak_info(int selected_item, void * data,
+ char *buf, size_t buffer_len,
+ bool say_it)
+{
+ long elapsed_pct; /* percentage of duration elapsed */
+ struct playing_time_info *pti = (struct playing_time_info *)data;
+ switch(selected_item) {
+ case 0: { /* elapsed and total time */
+ char timestr1[25], timestr2[25];
+ rb->format_time_auto(timestr1, sizeof(timestr1),
+ pti->secs[ePT_SECS_BEF], UNIT_SEC, false);
+
+ rb->format_time_auto(timestr2, sizeof(timestr2),
+ pti->secs[ePT_SECS_TTL], UNIT_SEC, false);
+
+ if (pti->secs[ePT_SECS_TTL] == 0)
+ elapsed_pct = 0;
+ else if (pti->secs[ePT_SECS_TTL] <= 0xFFFFFF)
+ {
+ elapsed_pct = (pti->secs[ePT_SECS_BEF] * 100
+ / pti->secs[ePT_SECS_TTL]);
+ }
+ else /* sacrifice some precision to avoid overflow */
+ {
+ elapsed_pct = (pti->secs[ePT_SECS_BEF] >> 7) * 100
+ / (pti->secs[ePT_SECS_TTL] >> 7);
+ }
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_ELAPSED),
+ timestr1, timestr2, elapsed_pct);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_ELAPSED,
+ TALK_ID(pti->secs[ePT_SECS_BEF], UNIT_TIME),
+ VOICE_OF,
+ TALK_ID(pti->secs[ePT_SECS_TTL], UNIT_TIME),
+ VOICE_PAUSE,
+ TALK_ID(elapsed_pct, UNIT_PERCENT));
+ break;
+ }
+ case 1: { /* playlist remaining time */
+ char timestr[25];
+ rb->format_time_auto(timestr, sizeof(timestr), pti->secs[ePT_SECS_AFT],
+ UNIT_SEC, false);
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_REMAINING), timestr);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_REMAINING,
+ TALK_ID(pti->secs[ePT_SECS_AFT], UNIT_TIME));
+ break;
+ }
+ case 2: { /* track elapsed and duration */
+ char timestr1[25], timestr2[25];
+
+ rb->format_time_auto(timestr1, sizeof(timestr1), pti->trk_secs[ePT_SECS_BEF],
+ UNIT_SEC, false);
+ rb->format_time_auto(timestr2, sizeof(timestr2), pti->trk_secs[ePT_SECS_TTL],
+ UNIT_SEC, false);
+
+ if (pti->trk_secs[ePT_SECS_TTL] == 0)
+ elapsed_pct = 0;
+ else if (pti->trk_secs[ePT_SECS_TTL] <= 0xFFFFFF)
+ {
+ elapsed_pct = (pti->trk_secs[ePT_SECS_BEF] * 100
+ / pti->trk_secs[ePT_SECS_TTL]);
+ }
+ else /* sacrifice some precision to avoid overflow */
+ {
+ elapsed_pct = (pti->trk_secs[ePT_SECS_BEF] >> 7) * 100
+ / (pti->trk_secs[ePT_SECS_TTL] >> 7);
+ }
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_TRK_ELAPSED),
+ timestr1, timestr2, elapsed_pct);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_TRK_ELAPSED,
+ TALK_ID(pti->trk_secs[ePT_SECS_BEF], UNIT_TIME),
+ VOICE_OF,
+ TALK_ID(pti->trk_secs[ePT_SECS_TTL], UNIT_TIME),
+ VOICE_PAUSE,
+ TALK_ID(elapsed_pct, UNIT_PERCENT));
+ break;
+ }
+ case 3: { /* track remaining time */
+ char timestr[25];
+ rb->format_time_auto(timestr, sizeof(timestr), pti->trk_secs[ePT_SECS_AFT],
+ UNIT_SEC, false);
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_TRK_REMAINING), timestr);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_TRK_REMAINING,
+ TALK_ID(pti->trk_secs[ePT_SECS_AFT], UNIT_TIME));
+ break;
+ }
+ case 4: { /* track index */
+ int track_pct = pti->curr_display_index * 100 / pti->nb_tracks;
+
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_TRACK),
+ pti->curr_display_index, pti->nb_tracks, track_pct);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_TRACK,
+ TALK_ID(pti->curr_display_index, UNIT_INT),
+ VOICE_OF,
+ TALK_ID(pti->nb_tracks, UNIT_INT),
+ VOICE_PAUSE,
+ TALK_ID(track_pct, UNIT_PERCENT));
+ break;
+ }
+ case 5: { /* storage size */
+ int i;
+ char kbstr[ePT_KBS_COUNT][10];
+
+ for (i = 0; i < ePT_KBS_COUNT; i++) {
+ rb->output_dyn_value(kbstr[i], sizeof(kbstr[i]),
+ pti->kbs[i], kibyte_units, 3, true);
+ }
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_STORAGE),
+ kbstr[ePT_KBS_TTL], kbstr[ePT_KBS_BEF],kbstr[ePT_KBS_AFT]);
+
+ if (say_it) {
+ int32_t voice_ids[ePT_KBS_COUNT];
+ voice_ids[ePT_KBS_TTL] = LANG_PLAYTIME_STORAGE;
+ voice_ids[ePT_KBS_BEF] = VOICE_PLAYTIME_DONE;
+ voice_ids[ePT_KBS_AFT] = LANG_PLAYTIME_REMAINING;
+
+ for (i = 0; i < ePT_KBS_COUNT; i++) {
+ rb_talk_ids(i > 0, VOICE_PAUSE, voice_ids[i]);
+ rb->output_dyn_value(NULL, 0, pti->kbs[i], kibyte_units, 3, true);
+ }
+ }
+ break;
+ }
+ case 6: { /* Average track file size */
+ char str[10];
+ long avg_track_size = pti->kbs[ePT_KBS_TTL] / pti->nb_tracks;
+ rb->output_dyn_value(str, sizeof(str), avg_track_size, kibyte_units, 3, true);
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_AVG_TRACK_SIZE), str);
+
+ if (say_it) {
+ rb->talk_id(LANG_PLAYTIME_AVG_TRACK_SIZE, false);
+ rb->output_dyn_value(NULL, 0, avg_track_size, kibyte_units, 3, true);
+ }
+ break;
+ }
+ case 7: { /* Average bitrate */
+ /* Convert power of 2 kilobytes to power of 10 kilobits */
+ long avg_bitrate = (pti->kbs[ePT_KBS_TTL] / pti->secs[ePT_SECS_TTL]
+ * 1024 * 8 / 1000);
+ rb->snprintf(buf, buffer_len, rb->str(LANG_PLAYTIME_AVG_BITRATE), avg_bitrate);
+
+ if (say_it)
+ rb_talk_ids(false, LANG_PLAYTIME_AVG_BITRATE,
+ TALK_ID(avg_bitrate, UNIT_KBIT));
+ break;
+ }
+ }
+ return buf;
+}
+
+static const char * playing_time_get_info(int selected_item, void * data,
+ char *buffer, size_t buffer_len)
+{
+ return playing_time_get_or_speak_info(selected_item, data,
+ buffer, buffer_len, false);
+}
+
+static int playing_time_speak_info(int selected_item, void * data)
+{
+ static char buffer[MAX_PATH];
+ playing_time_get_or_speak_info(selected_item, data,
+ buffer, MAX_PATH, true);
+ return 0;
+}
+
+/* playing time screen: shows total and elapsed playlist duration and
+ other stats */
+static bool playing_time(void)
+{
+ int error_count = 0;
+ unsigned long talked_tick = *rb->current_tick;
+ struct playing_time_info pti;
+ struct playlist_track_info pltrack;
+ struct mp3entry id3;
+ int i, index, fd;
+
+ pti.nb_tracks = rb->playlist_amount();
+ rb->playlist_get_resume_info(&pti.curr_index);
+ struct mp3entry *curr_id3 = rb->audio_current_track();
+ if (pti.curr_index == -1 || !curr_id3)
+ return false;
+ pti.curr_display_index = rb->playlist_get_display_index();
+
+ pti.secs[ePT_SECS_BEF] = pti.trk_secs[ePT_SECS_BEF] = curr_id3->elapsed / 1000;
+ pti.secs[ePT_SECS_AFT] = pti.trk_secs[ePT_SECS_AFT]
+ = (curr_id3->length -curr_id3->elapsed) / 1000;
+ pti.kbs[ePT_KBS_BEF] = curr_id3->offset / 1024;
+ pti.kbs[ePT_KBS_AFT] = (curr_id3->filesize -curr_id3->offset) / 1024;
+
+ rb->splash(0, ID2P(LANG_WAIT));
+ rb->splash_progress_set_delay(5 * HZ);
+ /* Go through each file in the playlist and get its stats. For
+ huge playlists this can take a while... The reference position
+ is the position at the moment this function was invoked,
+ although playback continues forward. */
+ index = rb->playlist_get_first_index(NULL);
+ for (i = 0; i < pti.nb_tracks; i++, index++) {
+
+ if (index == pti.nb_tracks)
+ index = 0;
+
+ /* Show a splash while we are loading. */
+ rb->splash_progress(i, pti.nb_tracks,
+ "%s (%s)", rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT));
+
+ /* Voice equivalent */
+ if (TIME_AFTER(*rb->current_tick, talked_tick + 5 * HZ)) {
+ talked_tick = *rb->current_tick;
+ rb_talk_ids(false, LANG_LOADING_PERCENT,
+ TALK_ID(i * 100 / pti.nb_tracks, UNIT_PERCENT));
+ }
+ if (rb->action_userabort(TIMEOUT_NOBLOCK))
+ goto exit;
+
+ if (index == pti.curr_index)
+ continue;
+
+ if (rb->playlist_get_track_info(NULL, index, &pltrack) >= 0)
+ {
+ bool ret = false;
+ if ((fd = rb->open(pltrack.filename, O_RDONLY)) >= 0)
+ {
+ ret = rb->get_metadata(&id3, fd, pltrack.filename);
+ rb->close(fd);
+ if (ret)
+ {
+ if (pltrack.display_index < pti.curr_display_index) {
+ pti.secs[ePT_SECS_BEF] += id3.length / 1000;
+ pti.kbs[ePT_KBS_BEF] += id3.filesize / 1024;
+ } else {
+ pti.secs[ePT_SECS_AFT] += id3.length / 1000;
+ pti.kbs[ePT_KBS_AFT] += id3.filesize / 1024;
+ }
+ }
+ }
+
+ if (!ret)
+ {
+ error_count++;
+ continue;
+ }
+ }
+ else
+ {
+ error_count++;
+ break;
+ }
+ }
+
+ if (error_count > 0)
+ {
+ rb->splash(HZ, ID2P(LANG_PLAYTIME_ERROR));
+ }
+
+ pti.nb_tracks -= error_count;
+ pti.secs[ePT_SECS_TTL] = pti.secs[ePT_SECS_BEF] + pti.secs[ePT_SECS_AFT];
+ pti.trk_secs[ePT_SECS_TTL] = pti.trk_secs[ePT_SECS_BEF] + pti.trk_secs[ePT_SECS_AFT];
+ pti.kbs[ePT_KBS_TTL] = pti.kbs[ePT_KBS_BEF] + pti.kbs[ePT_KBS_AFT];
+
+ struct gui_synclist pt_lists;
+ int key;
+
+ rb->gui_synclist_init(&pt_lists, &playing_time_get_info, &pti, true, 1, NULL);
+ if (rb->global_settings->talk_menu)
+ rb->gui_synclist_set_voice_callback(&pt_lists, playing_time_speak_info);
+ rb->gui_synclist_set_nb_items(&pt_lists, 8);
+ rb->gui_synclist_set_title(&pt_lists, rb->str(LANG_PLAYING_TIME), NOICON);
+ rb->gui_synclist_draw(&pt_lists);
+ rb->gui_synclist_speak_item(&pt_lists);
+ while (true) {
+ if (rb->list_do_action(CONTEXT_LIST, HZ/2, &pt_lists, &key) == 0
+ && key!=ACTION_NONE && key!=ACTION_UNKNOWN)
+ {
+ bool usb = rb->default_event_handler(key) == SYS_USB_CONNECTED;
+
+ if (!usb && IS_SYSEVENT(key))
+ continue;
+
+ rb->talk_force_shutup();
+ return usb;
+ }
+
+ }
+
+ exit:
+ return false;
+}
+
+/* this is the plugin entry point */
+enum plugin_status plugin_start(const void* parameter)
+{
+ enum plugin_status status = PLUGIN_OK;
+
+ (void)parameter;
+
+ if (!rb->audio_status())
+ {
+ rb->splash(HZ*2, "Nothing Playing");
+ return status;
+ }
+
+ FOR_NB_SCREENS(i)
+ rb->viewportmanager_theme_enable(i, true, NULL);
+
+ if (playing_time())
+ status = PLUGIN_USB_CONNECTED;
+
+ FOR_NB_SCREENS(i)
+ rb->viewportmanager_theme_undo(i, false);
+
+ return status;
+}
diff --git a/apps/plugins/plugin_crt0.c b/apps/plugins/plugin_crt0.c
index 680bb0723d..5564c5575f 100644
--- a/apps/plugins/plugin_crt0.c
+++ b/apps/plugins/plugin_crt0.c
@@ -141,6 +141,7 @@ void exit_on_usb(int button)
long result = rb->default_event_handler_ex(button, cleanup_wrapper, NULL);
if (result == SYS_USB_CONNECTED)
_exit(PLUGIN_USB_CONNECTED);
- else if (result == SYS_POWEROFF)
+ else if (result == SYS_POWEROFF || result == SYS_REBOOT)
+ /* note: nobody actually pays attention to this exit code */
_exit(PLUGIN_POWEROFF);
}
diff --git a/apps/plugins/pong.c b/apps/plugins/pong.c
index b49fec2459..17e6e6ed73 100644
--- a/apps/plugins/pong.c
+++ b/apps/plugins/pong.c
@@ -750,10 +750,9 @@ enum plugin_status plugin_start(const void* parameter)
this to avoid the compiler warning about it */
(void)parameter;
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* Clear screen */
rb->lcd_clear_display();
@@ -792,9 +791,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_clear_display();
}
}
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return (game == 0) ? PLUGIN_OK : PLUGIN_USB_CONNECTED;
}
diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c
index fa3517726a..ce3c03694c 100644
--- a/apps/plugins/properties.c
+++ b/apps/plugins/properties.c
@@ -20,39 +20,50 @@
****************************************************************************/
#include "plugin.h"
+#ifdef HAVE_TAGCACHE
+#include "lib/mul_id3.h"
+#endif
+
+#if !defined(ARRAY_SIZE)
+ #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
+#endif
+
+struct dir_stats {
+ char dirname[MAX_PATH];
+ int len;
+ unsigned int dir_count;
+ unsigned int file_count;
+ unsigned long long byte_count;
+};
+enum props_types {
+ PROPS_FILE = 0,
+ PROPS_ID3,
+ PROPS_MUL_ID3,
+ PROPS_DIR
+};
-bool its_a_dir = false;
-
-char str_filename[MAX_PATH];
-char str_dirname[MAX_PATH];
-char str_size[64];
-char str_dircount[64];
-char str_filecount[64];
-char str_date[64];
-char str_time[64];
-
-char str_title[MAX_PATH];
-char str_composer[MAX_PATH];
-char str_artist[MAX_PATH];
-char str_albumartist[MAX_PATH];
-char str_album[MAX_PATH];
-char str_genre[MAX_PATH];
-char str_comment[MAX_PATH];
-char str_year[MAX_PATH];
-char str_discnum[MAX_PATH];
-char str_tracknum[MAX_PATH];
-char str_duration[32];
-char str_bitrate[32];
-char str_frequency[32];
-
-unsigned nseconds;
-unsigned long nsize;
-int32_t size_unit;
-struct tm tm;
-
-int num_properties;
+static int props_type = PROPS_FILE;
+static struct mp3entry id3;
+#ifdef HAVE_TAGCACHE
+static int mul_id3_count;
+static int skipped_count;
+#endif
+
+static char str_filename[MAX_PATH];
+static char str_dirname[MAX_PATH];
+static char str_size[64];
+static char str_dircount[64];
+static char str_filecount[64];
+static char str_date[64];
+static char str_time[64];
+
+static unsigned long nsize;
+static int32_t size_unit;
+static struct tm tm;
+
+#define NUM_FILE_PROPERTIES 5
static const unsigned char* const props_file[] =
{
ID2P(LANG_PROPERTIES_PATH), str_dirname,
@@ -60,20 +71,9 @@ static const unsigned char* const props_file[] =
ID2P(LANG_PROPERTIES_SIZE), str_size,
ID2P(LANG_PROPERTIES_DATE), str_date,
ID2P(LANG_PROPERTIES_TIME), str_time,
- ID2P(LANG_PROPERTIES_COMPOSER), str_composer,
- ID2P(LANG_PROPERTIES_ARTIST), str_artist,
- ID2P(LANG_PROPERTIES_ALBUMARTIST), str_albumartist,
- ID2P(LANG_PROPERTIES_TITLE), str_title,
- ID2P(LANG_PROPERTIES_ALBUM), str_album,
- ID2P(LANG_PROPERTIES_GENRE), str_genre,
- ID2P(LANG_PROPERTIES_COMMENT), str_comment,
- ID2P(LANG_PROPERTIES_YEAR), str_year,
- ID2P(LANG_PROPERTIES_DISCNUM), str_discnum,
- ID2P(LANG_PROPERTIES_TRACKNUM), str_tracknum,
- ID2P(LANG_PROPERTIES_DURATION), str_duration,
- ID2P(LANG_PROPERTIES_BITRATE), str_bitrate,
- ID2P(LANG_PROPERTIES_FREQUENCY), str_frequency,
};
+
+#define NUM_DIR_PROPERTIES 4
static const unsigned char* const props_dir[] =
{
ID2P(LANG_PROPERTIES_PATH), str_dirname,
@@ -107,7 +107,6 @@ static bool file_properties(const char* selected_file)
bool found = false;
DIR* dir;
struct dirent* entry;
- static struct mp3entry id3;
dir = rb->opendir(str_dirname);
if (dir)
@@ -128,81 +127,8 @@ static bool file_properties(const char* selected_file)
rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d",
tm.tm_hour, tm.tm_min, tm.tm_sec);
- num_properties = 5;
-
- int fd = rb->open(selected_file, O_RDONLY);
- if (fd >= 0 &&
- rb->get_metadata(&id3, fd, selected_file))
- {
- long dur = id3.length / 1000; /* seconds */
- rb->snprintf(str_composer, sizeof str_composer,
- "%s", id3.composer ? id3.composer : "");
- rb->snprintf(str_artist, sizeof str_artist,
- "%s", id3.artist ? id3.artist : "");
- rb->snprintf(str_albumartist, sizeof str_albumartist,
- "%s", id3.albumartist ? id3.albumartist : "");
- rb->snprintf(str_title, sizeof str_title,
- "%s", id3.title ? id3.title : "");
- rb->snprintf(str_album, sizeof str_album,
- "%s", id3.album ? id3.album : "");
- rb->snprintf(str_genre, sizeof str_genre,
- "%s", id3.genre_string ? id3.genre_string : "");
- rb->snprintf(str_comment, sizeof str_comment,
- "%s", id3.comment ? id3.comment : "");
-
- if (id3.year_string)
- rb->snprintf(str_year, sizeof str_year,
- "%s", id3.year_string);
- else if (id3.year)
- rb->snprintf(str_year, sizeof str_year,
- "%d", id3.year);
- else
- rb->snprintf(str_year, sizeof str_year,
- "%s", "");
-
- if (id3.disc_string)
- rb->snprintf(str_discnum, sizeof str_discnum,
- "%s", id3.disc_string);
- else if (id3.discnum)
- rb->snprintf(str_discnum, sizeof str_discnum,
- "%d", id3.discnum);
- else
- rb->snprintf(str_discnum, sizeof str_discnum,
- "%s", "");
-
- if (id3.track_string)
- rb->snprintf(str_tracknum, sizeof str_tracknum,
- "%s", id3.track_string);
- else if(id3.tracknum)
- rb->snprintf(str_tracknum, sizeof str_tracknum,
- "%d", id3.tracknum);
- else
- rb->snprintf(str_tracknum, sizeof str_tracknum,
- "%s", "");
-
- rb->snprintf(str_bitrate, sizeof str_bitrate,
- "%d kbps", id3.bitrate ? : 0);
- rb->snprintf(str_frequency, sizeof str_frequency,
- "%ld Hz", id3.frequency ? : 0);
- num_properties += 12;
-
- if (dur > 0)
- {
- nseconds = dur;
- if (dur < 3600)
- rb->snprintf(str_duration, sizeof str_duration,
- "%d:%02d", (int)(dur / 60),
- (int)(dur % 60));
- else
- rb->snprintf(str_duration, sizeof str_duration,
- "%d:%02d:%02d",
- (int)(dur / 3600),
- (int)(dur % 3600 / 60),
- (int)(dur % 60));
- num_properties++;
- }
- }
- rb->close(fd);
+ if (!rb->mp3info(&id3, selected_file))
+ props_type = PROPS_ID3;
found = true;
break;
}
@@ -212,15 +138,7 @@ static bool file_properties(const char* selected_file)
return found;
}
-typedef struct {
- char dirname[MAX_PATH];
- int len;
- unsigned int dc;
- unsigned int fc;
- unsigned long long bc;
-} DPS;
-
-static bool _dir_properties(DPS *dps)
+static bool _dir_properties(struct dir_stats *stats)
{
/* recursively scan directories in search of files
and informs the user of the progress */
@@ -231,11 +149,11 @@ static bool _dir_properties(DPS *dps)
struct dirent* entry;
result = true;
- dirlen = rb->strlen(dps->dirname);
- dir = rb->opendir(dps->dirname);
+ dirlen = rb->strlen(stats->dirname);
+ dir = rb->opendir(stats->dirname);
if (!dir)
{
- rb->splashf(HZ*2, "%s", dps->dirname);
+ rb->splashf(HZ*2, "%s", stats->dirname);
return false; /* open error */
}
@@ -244,7 +162,7 @@ static bool _dir_properties(DPS *dps)
{
struct dirinfo info = rb->dir_get_info(dir, entry);
/* append name to current directory */
- rb->snprintf(dps->dirname+dirlen, dps->len-dirlen, "/%s",
+ rb->snprintf(stats->dirname+dirlen, stats->len-dirlen, "/%s",
entry->d_name);
if (info.attribute & ATTR_DIRECTORY)
@@ -253,29 +171,30 @@ static bool _dir_properties(DPS *dps)
!rb->strcmp((char *)entry->d_name, ".."))
continue; /* skip these */
- dps->dc++; /* new directory */
+ stats->dir_count++; /* new directory */
if (*rb->current_tick - lasttick > (HZ/8))
{
unsigned log;
lasttick = *rb->current_tick;
rb->lcd_clear_display();
rb->lcd_puts(0,0,"SCANNING...");
- rb->lcd_puts(0,1,dps->dirname);
- rb->lcd_putsf(0,2,"Directories: %d", dps->dc);
- rb->lcd_putsf(0,3,"Files: %d", dps->fc);
- log = human_size_log(dps->bc);
- rb->lcd_putsf(0,4,"Size: %lu %cB", (unsigned long)(dps->bc >> (10*log)),
- rb->str(units[log]));
+ rb->lcd_puts(0,1,stats->dirname);
+ rb->lcd_putsf(0,2,"Directories: %d", stats->dir_count);
+ rb->lcd_putsf(0,3,"Files: %d", stats->file_count);
+ log = human_size_log(stats->byte_count);
+ rb->lcd_putsf(0,4,"Size: %lu %s",
+ (unsigned long)(stats->byte_count >> (10*log)),
+ rb->str(units[log]));
rb->lcd_update();
}
/* recursion */
- result = _dir_properties(dps);
+ result = _dir_properties(stats);
}
else
{
- dps->fc++; /* new file */
- dps->bc += info.size;
+ stats->file_count++; /* new file */
+ stats->byte_count += info.size;
}
if(ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD,TIMEOUT_NOBLOCK))
result = false;
@@ -285,17 +204,17 @@ static bool _dir_properties(DPS *dps)
return result;
}
-static bool dir_properties(const char* selected_file, DPS *dps)
+static bool dir_properties(const char* selected_file, struct dir_stats *stats)
{
unsigned log;
- rb->strlcpy(dps->dirname, selected_file, MAX_PATH);
+ rb->strlcpy(stats->dirname, selected_file, MAX_PATH);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true);
#endif
- if (!_dir_properties(dps))
+ if (!_dir_properties(stats))
{
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
@@ -308,13 +227,12 @@ static bool dir_properties(const char* selected_file, DPS *dps)
#endif
rb->strlcpy(str_dirname, selected_file, MAX_PATH);
- rb->snprintf(str_dircount, sizeof str_dircount, "%d", dps->dc);
- rb->snprintf(str_filecount, sizeof str_filecount, "%d", dps->fc);
- log = human_size_log(dps->bc);
- nsize = (long) (dps->bc >> (log*10));
+ rb->snprintf(str_dircount, sizeof str_dircount, "%d", stats->dir_count);
+ rb->snprintf(str_filecount, sizeof str_filecount, "%d", stats->file_count);
+ log = human_size_log(stats->byte_count);
+ nsize = (long) (stats->byte_count >> (log*10));
size_unit = units[log];
rb->snprintf(str_size, sizeof str_size, "%ld %s", nsize, rb->str(size_unit));
- num_properties = 4;
return true;
}
@@ -328,35 +246,20 @@ static const char * get_props(int selected_item, void* data,
char *buffer, size_t buffer_len)
{
(void)data;
- if(its_a_dir)
- {
- if(selected_item >= (int)(sizeof(props_dir) / sizeof(props_dir[0])))
- {
- rb->strlcpy(buffer, "ERROR", buffer_len);
- }
- else
- {
- rb->strlcpy(buffer, p2str(props_dir[selected_item]), buffer_len);
- }
- }
- else
- {
- if(selected_item >= (int)(sizeof(props_file) / sizeof(props_file[0])))
- {
- rb->strlcpy(buffer, "ERROR", buffer_len);
- }
- else
- {
- rb->strlcpy(buffer, p2str(props_file[selected_item]), buffer_len);
- }
- }
+ if (PROPS_DIR == props_type)
+ rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_dir)) ? "ERROR" :
+ (char *) p2str(props_dir[selected_item]), buffer_len);
+ else if (PROPS_FILE == props_type)
+ rb->strlcpy(buffer, selected_item >= (int)(ARRAY_SIZE(props_file)) ? "ERROR" :
+ (char *) p2str(props_file[selected_item]), buffer_len);
+
return buffer;
}
static int speak_property_selection(int selected_item, void *data)
{
- DPS *dps = data;
- int32_t id = P2ID((its_a_dir ? props_dir : props_file)[selected_item]);
+ struct dir_stats *stats = data;
+ int32_t id = P2ID((props_type == PROPS_DIR ? props_dir : props_file)[selected_item]);
rb->talk_id(id, false);
switch (id)
{
@@ -394,14 +297,11 @@ static int speak_property_selection(int selected_item, void *data)
case LANG_PROPERTIES_TIME:
rb->talk_time(&tm, true);
break;
- case LANG_PROPERTIES_DURATION:
- rb->talk_value_decimal(nseconds, UNIT_TIME, 0, true);
- break;
case LANG_PROPERTIES_SUBDIRS:
- rb->talk_number(dps->dc, true);
+ rb->talk_number(stats->dir_count, true);
break;
case LANG_PROPERTIES_FILES:
- rb->talk_number(dps->fc, true);
+ rb->talk_number(stats->file_count, true);
break;
default:
rb->talk_spell(props_file[selected_item + 1], true);
@@ -410,33 +310,49 @@ static int speak_property_selection(int selected_item, void *data)
return 0;
}
-enum plugin_status plugin_start(const void* parameter)
+static int browse_file_or_dir(struct dir_stats *stats)
{
struct gui_synclist properties_lists;
int button;
- bool quit = false, usb = false;
- const char *file = parameter;
- if(!parameter) return PLUGIN_ERROR;
-#ifdef HAVE_TOUCHSCREEN
- rb->touchscreen_set_mode(rb->global_settings->touch_mode);
-#endif
- static DPS dps = {
- .len = MAX_PATH,
- .dc = 0,
- .fc = 0,
- .bc = 0,
- };
+ rb->gui_synclist_init(&properties_lists, &get_props, stats, false, 2, NULL);
+ rb->gui_synclist_set_title(&properties_lists,
+ rb->str(props_type == PROPS_DIR ?
+ LANG_PROPERTIES_DIRECTORY_PROPERTIES :
+ LANG_PROPERTIES_FILE_PROPERTIES),
+ NOICON);
+ rb->gui_synclist_set_icon_callback(&properties_lists, NULL);
+ if (rb->global_settings->talk_menu)
+ rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection);
+ rb->gui_synclist_set_nb_items(&properties_lists,
+ 2 * (props_type == PROPS_FILE ? NUM_FILE_PROPERTIES :
+ NUM_DIR_PROPERTIES));
+ rb->gui_synclist_select_item(&properties_lists, 0);
+ rb->gui_synclist_draw(&properties_lists);
+ rb->gui_synclist_speak_item(&properties_lists);
- /* determine if it's a file or a directory */
- bool found = false;
+ while(true)
+ {
+ button = rb->get_action(CONTEXT_LIST, HZ);
+ /* HZ so the status bar redraws corectly */
+ if (rb->gui_synclist_do_button(&properties_lists,&button))
+ continue;
+ switch(button)
+ {
+ case ACTION_STD_CANCEL:
+ return false;
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ return true;
+ break;
+ }
+ }
+}
+
+static bool determine_file_or_dir(void)
+{
DIR* dir;
struct dirent* entry;
- char* ptr = rb->strrchr(file, '/') + 1;
- int dirlen = (ptr - file);
-
- rb->strlcpy(str_dirname, file, dirlen + 1);
- rb->snprintf(str_filename, sizeof str_filename, "%s", file+dirlen);
dir = rb->opendir(str_dirname);
if (dir)
@@ -446,69 +362,104 @@ enum plugin_status plugin_start(const void* parameter)
if(!rb->strcmp(entry->d_name, str_filename))
{
struct dirinfo info = rb->dir_get_info(dir, entry);
- its_a_dir = info.attribute & ATTR_DIRECTORY ? true : false;
- found = true;
- break;
+ props_type = info.attribute & ATTR_DIRECTORY ? PROPS_DIR : PROPS_FILE;
+ rb->closedir(dir);
+ return true;
}
}
rb->closedir(dir);
}
- /* now we know if it's a file or a dir or maybe something failed */
+ return false;
+}
- if(!found)
+#ifdef HAVE_TAGCACHE
+bool mul_id3_add(const char *file_name)
+{
+ if (!file_name || rb->mp3info(&id3, file_name))
+ skipped_count++;
+ else
{
- /* weird: we couldn't find the entry. This Should Never Happen (TM) */
- rb->splashf(0, "File/Dir not found: %s", file);
- rb->action_userabort(TIMEOUT_BLOCK);
- return PLUGIN_OK;
+ collect_id3(&id3, mul_id3_count == 0);
+ mul_id3_count++;
}
- /* get the info depending on its_a_dir */
- if(!(its_a_dir ? dir_properties(file, &dps) : file_properties(file)))
+ return true;
+}
+#endif
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ static struct dir_stats stats =
{
- /* something went wrong (to do: tell user what it was (nesting,...) */
- rb->splash(0, ID2P(LANG_PROPERTIES_FAIL));
- rb->action_userabort(TIMEOUT_BLOCK);
- return PLUGIN_OK;
- }
+ .len = MAX_PATH,
+ .dir_count = 0,
+ .file_count = 0,
+ .byte_count = 0,
+ };
- FOR_NB_SCREENS(i)
- rb->viewportmanager_theme_enable(i, true, NULL);
+ const char *file = parameter;
+ if(!parameter)
+ return PLUGIN_ERROR;
- rb->gui_synclist_init(&properties_lists, &get_props, &dps, false, 2, NULL);
- rb->gui_synclist_set_title(&properties_lists, rb->str(its_a_dir ? LANG_PROPERTIES_DIRECTORY_PROPERTIES : LANG_PROPERTIES_FILE_PROPERTIES), NOICON);
- rb->gui_synclist_set_icon_callback(&properties_lists, NULL);
- if (rb->global_settings->talk_menu)
- rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection);
- rb->gui_synclist_set_nb_items(&properties_lists, num_properties * 2);
- rb->gui_synclist_limit_scroll(&properties_lists, true);
- rb->gui_synclist_select_item(&properties_lists, 0);
- rb->gui_synclist_draw(&properties_lists);
- rb->gui_synclist_speak_item(&properties_lists);
+#ifdef HAVE_TOUCHSCREEN
+ rb->touchscreen_set_mode(rb->global_settings->touch_mode);
+#endif
- while(!quit)
+#ifdef HAVE_TAGCACHE
+ if (!rb->strcmp(file, MAKE_ACT_STR(ACTIVITY_DATABASEBROWSER))) /* db table selected */
{
- button = rb->get_action(CONTEXT_LIST, HZ);
- /* HZ so the status bar redraws corectly */
- if (rb->gui_synclist_do_button(&properties_lists,&button,LIST_WRAP_UNLESS_HELD))
- continue;
- switch(button)
+ props_type = PROPS_MUL_ID3;
+ mul_id3_count = skipped_count = 0;
+
+ if (!rb->tagtree_subentries_do_action(&mul_id3_add) || mul_id3_count == 0)
+ return PLUGIN_ERROR;
+ else if (mul_id3_count > 1) /* otherwise, the retrieved id3 can be used as-is */
+ finalize_id3(&id3);
+
+ if (skipped_count > 0)
+ rb->splashf(HZ*2, "Skipped %d", skipped_count);
+ }
+ else
+#endif
+ if (file[0] == '/') /* single track selected */
+ {
+ const char* file_name = rb->strrchr(file, '/') + 1;
+ int dirlen = (file_name - file);
+
+ rb->strlcpy(str_dirname, file, dirlen + 1);
+ rb->snprintf(str_filename, sizeof str_filename, "%s", file+dirlen);
+
+ if(!determine_file_or_dir())
{
- case ACTION_STD_CANCEL:
- quit = true;
- break;
- default:
- if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
- {
- quit = true;
- usb = true;
- }
- break;
+ /* weird: we couldn't find the entry. This Should Never Happen (TM) */
+ rb->splashf(0, "File/Dir not found: %s", file);
+ rb->action_userabort(TIMEOUT_BLOCK);
+ return PLUGIN_OK;
+ }
+
+ /* get the info depending on its_a_dir */
+ if(!(props_type == PROPS_DIR ? dir_properties(file, &stats) : file_properties(file)))
+ {
+ /* something went wrong (to do: tell user what it was (nesting,...) */
+ rb->splash(0, ID2P(LANG_PROPERTIES_FAIL));
+ rb->action_userabort(TIMEOUT_BLOCK);
+ return PLUGIN_OK;
}
}
+ else
+ return PLUGIN_ERROR;
+
+ FOR_NB_SCREENS(i)
+ rb->viewportmanager_theme_enable(i, true, NULL);
+
+ bool usb = props_type == PROPS_ID3 ? rb->browse_id3(&id3, 0, 0, &tm, 1) :
+#ifdef HAVE_TAGCACHE
+ props_type == PROPS_MUL_ID3 ? rb->browse_id3(&id3, 0, 0, NULL, mul_id3_count) :
+#endif
+ browse_file_or_dir(&stats);
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_undo(i, false);
- return usb? PLUGIN_USB_CONNECTED: PLUGIN_OK;
+ return usb ? PLUGIN_USB_CONNECTED : PLUGIN_OK;
}
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c
index 6e34adb1db..e175429075 100644
--- a/apps/plugins/puzzles/rockbox.c
+++ b/apps/plugins/puzzles/rockbox.c
@@ -1465,7 +1465,7 @@ static void rb_blitter_free(void *handle, blitter *bl)
static void rb_blitter_save(void *handle, blitter *bl, int x, int y)
{
/* no viewport offset */
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#error no vertical stride
#else
if(bl && bl->bmp.data)
@@ -2450,7 +2450,6 @@ static int list_choose(const char *list_str, const char *title, int sel)
rb->gui_synclist_init(&list, &config_choices_formatter, (void*)list_str, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&list, NULL);
rb->gui_synclist_set_nb_items(&list, n);
- rb->gui_synclist_limit_scroll(&list, false);
rb->gui_synclist_select_item(&list, sel);
@@ -2459,7 +2458,7 @@ static int list_choose(const char *list_str, const char *title, int sel)
{
rb->gui_synclist_draw(&list);
int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_UNLESS_HELD))
+ if(rb->gui_synclist_do_button(&list, &button))
continue;
switch(button)
{
@@ -2664,7 +2663,6 @@ static bool config_menu(void)
rb->gui_synclist_init(&list, &config_formatter, config, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&list, NULL);
rb->gui_synclist_set_nb_items(&list, n);
- rb->gui_synclist_limit_scroll(&list, false);
rb->gui_synclist_select_item(&list, 0);
@@ -2674,7 +2672,7 @@ static bool config_menu(void)
{
rb->gui_synclist_draw(&list);
int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_UNLESS_HELD))
+ if(rb->gui_synclist_do_button(&list, &button))
continue;
switch(button)
{
@@ -2750,7 +2748,6 @@ static int do_preset_menu(struct preset_menu *menu, char *title, int selected)
rb->gui_synclist_init(&list, &preset_formatter, menu, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&list, NULL);
rb->gui_synclist_set_nb_items(&list, menu->n_entries);
- rb->gui_synclist_limit_scroll(&list, false);
rb->gui_synclist_select_item(&list, selected);
@@ -2760,7 +2757,7 @@ static int do_preset_menu(struct preset_menu *menu, char *title, int selected)
{
rb->gui_synclist_draw(&list);
int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_UNLESS_HELD))
+ if(rb->gui_synclist_do_button(&list, &button))
continue;
switch(button)
{
@@ -3393,9 +3390,7 @@ static void shutdown_tlsf(void)
static void exit_handler(void)
{
-#ifdef HAVE_SW_POWEROFF
sw_poweroff_restore();
-#endif
unload_fonts();
shutdown_tlsf();
@@ -3655,9 +3650,7 @@ static void puzzles_main(void)
{
rb_atexit(exit_handler);
-#ifdef HAVE_SW_POWEROFF
sw_poweroff_disable();
-#endif
init_default_settings();
init_fonttab();
diff --git a/apps/plugins/random_folder_advance_config.c b/apps/plugins/random_folder_advance_config.c
index 2c9fb411ac..5688ff93d3 100644
--- a/apps/plugins/random_folder_advance_config.c
+++ b/apps/plugins/random_folder_advance_config.c
@@ -311,14 +311,13 @@ static int edit_list(void)
rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&lists,NULL);
rb->gui_synclist_set_nb_items(&lists,list->count);
- rb->gui_synclist_limit_scroll(&lists,true);
rb->gui_synclist_select_item(&lists, 0);
while (!exit)
{
rb->gui_synclist_draw(&lists);
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &button))
continue;
selection = rb->gui_synclist_get_sel_pos(&lists);
switch (button)
diff --git a/apps/plugins/rb_info.c b/apps/plugins/rb_info.c
index e0ec117dfb..a89cc658cc 100644
--- a/apps/plugins/rb_info.c
+++ b/apps/plugins/rb_info.c
@@ -335,6 +335,17 @@ static int list_voice_cb(int list_index, void* data)
rb->talk_spell(name, true);
}
}
+ else if (data == MENU_ID(M_TESTPUT))
+ {
+ char buf[64];
+ const char* name = printcell_get_column_text(printcell_get_column_selected(),
+ buf, sizeof(buf));
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
else
{
char buf[64];
@@ -354,12 +365,12 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli
{
if (*action == ACTION_STD_OK)
{
- printcell_increment_column(lists, 1, true);
+ printcell_increment_column(1, true);
*action = ACTION_NONE;
}
else if (*action == ACTION_STD_CANCEL)
{
- if (printcell_increment_column(lists, -1, true) != testput_cols - 1)
+ if (printcell_increment_column(-1, true) != testput_cols - 1)
{
*action = ACTION_NONE;
}
@@ -368,7 +379,8 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli
{
char buf[PRINTCELL_MAXLINELEN];
char* bufp = buf;
- bufp = printcell_get_selected_column_text(lists, bufp, PRINTCELL_MAXLINELEN);
+ int selcol = printcell_get_column_selected();
+ bufp = printcell_get_column_text(selcol, bufp, PRINTCELL_MAXLINELEN);
rb->splashf(HZ * 2, "Item: %s", bufp);
}
}
@@ -428,20 +440,13 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli
if (cur->menuid == MENU_ID(M_TESTPUT))
{
- //rb->gui_list_screen_scroll_out_of_view(true);
synclist_set(cur->menuid, 0, cur->items, 1);
-#if LCD_DEPTH > 1
- /* If line sep is set to automatic then outline cells */
- bool showlinesep = (rb->global_settings->list_separator_height < 0);
-#else
- bool showlinesep = (rb->global_settings->cursor_style == 0);
-#endif
- printcell_enable(lists, true, showlinesep);
+ printcell_enable(true);
//lists->callback_draw_item = test_listdraw_fn;
}
else
{
- printcell_enable(lists, false, false);
+ printcell_enable(false);
synclist_set(cur->menuid, 1, cur->items, 1);
}
rb->gui_synclist_draw(lists);
@@ -473,9 +478,8 @@ int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_syncli
{
if (lists->data == MENU_ID(M_TESTPUT))
{
- //rb->gui_list_screen_scroll_out_of_view(false);
//lists->callback_draw_item = NULL;
- printcell_enable(lists, false, false);
+ printcell_enable(false);
}
if (lists->data != MENU_ID(M_ROOT))
{
@@ -507,7 +511,8 @@ static void synclist_set(char* menu_id, int selected_item, int items, int sel_si
menu_id, false, sel_size, NULL);
if (menu_id == MENU_ID(M_TESTPUT))
{
- testput_cols = printcell_set_columns(&lists, TESTPUT_HEADER, Icon_Rockbox);
+ testput_cols = printcell_set_columns(&lists, NULL,
+ TESTPUT_HEADER, Icon_Rockbox);
}
else
{
@@ -516,7 +521,6 @@ static void synclist_set(char* menu_id, int selected_item, int items, int sel_si
rb->gui_synclist_set_icon_callback(&lists,NULL);
rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
rb->gui_synclist_set_nb_items(&lists,items);
- rb->gui_synclist_limit_scroll(&lists,true);
rb->gui_synclist_select_item(&lists, selected_item);
}
@@ -560,11 +564,11 @@ enum plugin_status plugin_start(const void* parameter)
else
redraw = true;
ret = menu_action_cb(&action, selected_item, &exit, &lists);
- if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &action))
continue;
selected_item = rb->gui_synclist_get_sel_pos(&lists);
}
}
-
+ printcell_enable(false);
return ret;
}
diff --git a/apps/plugins/resistor.c b/apps/plugins/resistor.c
index d32ac3fad9..4461dc0dea 100644
--- a/apps/plugins/resistor.c
+++ b/apps/plugins/resistor.c
@@ -574,9 +574,8 @@ static void display_helpfile(void)
static void led_resistance_calc(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
int voltage_menu_selection, button_press, j, k, l, foreward_current = 0;
int fwd_current_selection = 0;
bool quit = false;
@@ -769,9 +768,8 @@ static void led_resistance_calc(void)
default:
quit = true;
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
break;
}
}
@@ -784,9 +782,8 @@ static void led_resistance_calc(void)
static void resistance_to_color(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
int menu_selection;
int menu_selection_tol;
int button_press;
@@ -910,9 +907,9 @@ static void resistance_to_color(void)
break;
default:
quit = true;
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
break;
}
}
@@ -924,9 +921,8 @@ static void resistance_to_color(void)
static void color_to_resistance(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
bool quit = false;
int button_input = 0;
@@ -995,9 +991,7 @@ static void color_to_resistance(void)
case PLA_SELECT:
default:
quit = true;
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
break;
}
}
diff --git a/apps/plugins/reversi/reversi-gui.c b/apps/plugins/reversi/reversi-gui.c
index 74dd98b676..e4bb232a26 100644
--- a/apps/plugins/reversi/reversi-gui.c
+++ b/apps/plugins/reversi/reversi-gui.c
@@ -400,7 +400,7 @@ static bool reversi_gui_choose_strategy(
}
result =
- rb->set_option(prompt, &index, INT, strategy_settings, num_items, NULL);
+ rb->set_option(prompt, &index, RB_INT, strategy_settings, num_items, NULL);
(*player) = strategy_values[index];
@@ -450,7 +450,7 @@ static bool reversi_gui_menu(void) {
break;
}
}
- rb->set_option(MENU_TEXT_WRAP_MODE, &index, INT,
+ rb->set_option(MENU_TEXT_WRAP_MODE, &index, RB_INT,
cursor_wrap_mode_settings, 3, NULL);
cursor_wrap_mode = cursor_wrap_mode_values[index];
break;
diff --git a/apps/plugins/robotfindskitten.c b/apps/plugins/robotfindskitten.c
index 4f228423b6..31c419e33d 100644
--- a/apps/plugins/robotfindskitten.c
+++ b/apps/plugins/robotfindskitten.c
@@ -469,7 +469,13 @@ static char* messages[] =
#define RFK_VERSION "v1.4142135.406"
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+# define RFK_QUIT PLA_SELECT_REPEAT
+#else
# define RFK_QUIT PLA_CANCEL
+#endif
# define RFK_RIGHT PLA_RIGHT
# define RFK_LEFT PLA_LEFT
# define RFK_UP PLA_UP
diff --git a/apps/plugins/rockblox.c b/apps/plugins/rockblox.c
index 927710b37b..a0105a1ffb 100644
--- a/apps/plugins/rockblox.c
+++ b/apps/plugins/rockblox.c
@@ -33,7 +33,7 @@
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define ROCKBLOX_OFF (BUTTON_MENU | BUTTON_SELECT)
+#define ROCKBLOX_OFF (BUTTON_SELECT | BUTTON_REPEAT)
#define ROCKBLOX_ROTATE_CCW BUTTON_SCROLL_BACK
#define ROCKBLOX_ROTATE_CCW2 (BUTTON_MENU | BUTTON_REL)
#define ROCKBLOX_ROTATE_CW BUTTON_SCROLL_FWD
@@ -41,6 +41,7 @@
#define ROCKBLOX_RIGHT BUTTON_RIGHT
#define ROCKBLOX_DOWN BUTTON_PLAY
#define ROCKBLOX_RESTART (BUTTON_SELECT | BUTTON_PLAY)
+#define ROCKBLOX_DROP_PRE BUTTON_SELECT
#define ROCKBLOX_DROP (BUTTON_SELECT | BUTTON_REL)
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
@@ -1498,16 +1499,13 @@ static int rockblox_loop (void)
#ifdef HAS_BUTTON_HOLD
if (rb->button_hold ()) {
/* Turn on backlight timeout (revert to settings) */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
rb->splash(0, "Paused");
while (rb->button_hold ())
rb->sleep(HZ/10);
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* get rid of the splash text */
rb->lcd_bitmap (rockblox_background, 0, 0, LCD_WIDTH, LCD_HEIGHT);
show_details ();
@@ -1677,10 +1675,9 @@ enum plugin_status plugin_start (const void *parameter)
rb->lcd_setfont (FONT_SYSFIXED);
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
load_game();
resume_file = resume;
@@ -1728,9 +1725,8 @@ enum plugin_status plugin_start (const void *parameter)
/* Save user's HighScore */
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
-#ifdef HAVE_BACKLIGNT
+
backlight_use_settings();
-#endif
return PLUGIN_OK;
}
diff --git a/apps/plugins/rockblox1d.c b/apps/plugins/rockblox1d.c
index 6a2b013c44..6d535bbcd7 100644
--- a/apps/plugins/rockblox1d.c
+++ b/apps/plugins/rockblox1d.c
@@ -28,7 +28,14 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#define ONEDROCKBLOX_DOWN PLA_DOWN
#define ONEDROCKBLOX_DOWN_REPEAT PLA_DOWN_REPEAT
#define ONEDROCKBLOX_QUIT PLA_EXIT
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define ONEDROCKBLOX_QUIT2 PLA_UP
+#else
#define ONEDROCKBLOX_QUIT2 PLA_CANCEL
+#endif
#define mrand(max) (short)(rb->rand()%max)
diff --git a/apps/plugins/rockboy/menu.c b/apps/plugins/rockboy/menu.c
index 6fafdc11a6..870ea389fb 100644
--- a/apps/plugins/rockboy/menu.c
+++ b/apps/plugins/rockboy/menu.c
@@ -83,9 +83,8 @@ int do_user_menu(void) {
rb->lcd_set_mode(LCD_MODE_RGB565);
#endif
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
/* Clean out the button Queue */
while (rb->button_get(false) != BUTTON_NONE)
rb->yield();
@@ -139,10 +138,9 @@ int do_user_menu(void) {
rb->lcd_set_mode(LCD_MODE_PAL256);
#endif
-#ifdef HAVE_BACKLIGHT
/* ignore backlight time out */
backlight_ignore_timeout();
-#endif
+
return ret;
}
@@ -430,7 +428,7 @@ static void do_opt_menu(void)
options.dirty=1; /* Assume that the settings have been changed */
struct viewport *parentvp = NULL;
- const struct settings_list* vol = rb->find_setting(&rb->global_settings->volume, NULL);
+ const struct settings_list* vol = rb->find_setting(&rb->global_settings->volume);
while(!done)
{
@@ -439,39 +437,39 @@ static void do_opt_menu(void)
switch (result)
{
case 0: /* Frameskip */
- rb->set_option("Max Frameskip", &options.maxskip, INT, frameskip,
+ rb->set_option("Max Frameskip", &options.maxskip, RB_INT, frameskip,
sizeof(frameskip)/sizeof(*frameskip), NULL );
break;
case 1: /* Autosave */
- rb->set_option("Autosave", &options.autosave, INT, onoff, 2, NULL );
+ rb->set_option("Autosave", &options.autosave, RB_INT, onoff, 2, NULL );
break;
case 2: /* Sound */
if(options.sound>1) options.sound=1;
- rb->set_option("Sound", &options.sound, INT, onoff, 2, NULL );
+ rb->set_option("Sound", &options.sound, RB_INT, onoff, 2, NULL );
if(options.sound) sound_dirty();
break;
case 3: /* Volume */
rb->option_screen((struct settings_list*)vol, parentvp, false, "Volume");
break;
case 4: /* Stats */
- rb->set_option("Stats", &options.showstats, INT, stats, 3, NULL );
+ rb->set_option("Stats", &options.showstats, RB_INT, stats, 3, NULL );
break;
case 5: /* Keys */
setupkeys();
break;
#ifdef HAVE_LCD_COLOR
case 6: /* Screen Size */
- rb->set_option("Screen Size", &options.scaling, INT, scaling,
+ rb->set_option("Screen Size", &options.scaling, RB_INT, scaling,
sizeof(scaling)/sizeof(*scaling), NULL );
setvidmode();
break;
case 7: /* Screen rotate */
- rb->set_option("Screen Rotate", &options.rotate, INT, rotate,
+ rb->set_option("Screen Rotate", &options.rotate, RB_INT, rotate,
sizeof(rotate)/sizeof(*rotate), NULL );
setvidmode();
break;
case 8: /* Palette */
- rb->set_option("Set Palette", &options.pal, INT, palette, 17, NULL );
+ rb->set_option("Set Palette", &options.pal, RB_INT, palette, 17, NULL );
set_pal();
break;
#endif
diff --git a/apps/plugins/rockboy/rockboy.c b/apps/plugins/rockboy/rockboy.c
index 2c5c6e4dbf..2d0c349507 100644
--- a/apps/plugins/rockboy/rockboy.c
+++ b/apps/plugins/rockboy/rockboy.c
@@ -602,10 +602,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_set_mode(LCD_MODE_PAL256);
#endif
-#ifdef HAVE_BACKLIGHT
/* ignore backlight time out */
backlight_ignore_timeout();
-#endif
+
gnuboy_main(parameter);
#ifdef HAVE_WHEEL_POSITION
@@ -616,9 +615,7 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_set_mode(LCD_MODE_RGB565);
#endif
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
if(!rb->audio_status())
rockboy_pcm_close();
diff --git a/apps/plugins/rocklife.c b/apps/plugins/rocklife.c
index 99297abd0f..c4c7842fe0 100644
--- a/apps/plugins/rocklife.c
+++ b/apps/plugins/rocklife.c
@@ -70,7 +70,13 @@
#define ROCKLIFE_INIT PLA_DOWN
#define ROCKLIFE_NEXT PLA_RIGHT
#define ROCKLIFE_NEXT_REP PLA_RIGHT_REPEAT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define ROCKLIFE_QUIT PLA_UP
+#else
#define ROCKLIFE_QUIT PLA_CANCEL
+#endif
#define ROCKLIFE_STATUS PLA_LEFT
#define PATTERN_RANDOM 0
@@ -474,9 +480,8 @@ enum plugin_status plugin_start(const void* parameter)
char *ptemp;
(void)(parameter);
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#ifdef HAVE_LCD_COLOR
@@ -580,8 +585,7 @@ enum plugin_status plugin_start(const void* parameter)
rb->yield();
}
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
return usb? PLUGIN_USB_CONNECTED: PLUGIN_OK;
}
diff --git a/apps/plugins/rockpaint.c b/apps/plugins/rockpaint.c
index 09fa2c8c5f..52281edfb0 100644
--- a/apps/plugins/rockpaint.c
+++ b/apps/plugins/rockpaint.c
@@ -1084,15 +1084,15 @@ static bool callback_show_item(char *name, int attr, struct tree_context *tc)
static bool browse( char *dst, int dst_size, const char *start )
{
- struct browse_context browse;
-
- rb->browse_context_init(&browse, SHOW_ALL,
- BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
- NULL, NOICON, start, NULL);
-
- browse.callback_show_item = callback_show_item;
- browse.buf = dst;
- browse.bufsize = dst_size;
+ struct browse_context browse = {
+ .dirfilter = SHOW_ALL,
+ .flags = BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU,
+ .icon = Icon_NOICON,
+ .root = start,
+ .buf = dst,
+ .bufsize = dst_size,
+ .callback_show_item = callback_show_item,
+ };
rb->rockbox_browse(&browse);
@@ -2869,7 +2869,7 @@ static void goto_menu(void)
case MAIN_MENU_BRUSH_SIZE:
for(multi = 0; multi<4; multi++)
if(bsize == times_list[multi]) break;
- rb->set_option( "Brush Size", &multi, INT, times_options, 4, NULL );
+ rb->set_option( "Brush Size", &multi, RB_INT, times_options, 4, NULL );
if( multi >= 0 )
bsize = times_list[multi];
break;
@@ -2877,7 +2877,7 @@ static void goto_menu(void)
case MAIN_MENU_BRUSH_SPEED:
for(multi = 0; multi<3; multi++)
if(bspeed == times_list[multi]) break;
- rb->set_option( "Brush Speed", &multi, INT, times_options, 3, NULL );
+ rb->set_option( "Brush Speed", &multi, RB_INT, times_options, 3, NULL );
if( multi >= 0 ) {
bspeed = times_list[multi];
incdec_x.step[0] = bspeed;
@@ -2894,7 +2894,7 @@ static void goto_menu(void)
case MAIN_MENU_GRID_SIZE:
for(multi = 0; multi<4; multi++)
if(gridsize == gridsize_list[multi]) break;
- rb->set_option( "Grid Size", &multi, INT, gridsize_options, 4, NULL );
+ rb->set_option( "Grid Size", &multi, RB_INT, gridsize_options, 4, NULL );
if( multi >= 0 )
gridsize = gridsize_list[multi];
break;
diff --git a/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c b/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c
index 1638732dc5..38f7109b13 100644
--- a/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c
+++ b/apps/plugins/sdl/SDL_mixer/timidity/playmidi.c
@@ -21,6 +21,8 @@
#include "tables.h"
+/* ROCKBOX HACK: avoid a conflict with adjust_volume() in misc.h */
+#define adjust_volume adjust_midi_volume
static int opt_expression_curve = 2;
static int opt_volume_curve = 2;
diff --git a/apps/plugins/sdl/main.c b/apps/plugins/sdl/main.c
index 6efb072faf..7220c7cfd9 100644
--- a/apps/plugins/sdl/main.c
+++ b/apps/plugins/sdl/main.c
@@ -64,9 +64,8 @@ void cleanup(void)
if(audiobuf)
memset(audiobuf, 0, 4); /* clear */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
@@ -219,9 +218,9 @@ enum plugin_status plugin_start(const void *param)
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
/* set the real exit handler */
#undef rb_atexit
rb_atexit(cleanup);
diff --git a/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c b/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c
index cb72687d48..ac268b4a19 100644
--- a/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c
+++ b/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c
@@ -132,38 +132,42 @@ static void ROCKBOXAUD_WaitAudio(_THIS)
}
/* when this is called, SDL wants us to play the samples in mixbuf */
-static void ROCKBOXAUD_PlayAudio(_THIS)
+static void
+ROCKBOXAUD_PlayAudio(_THIS)
{
/* There are two cases in which we should be called:
* - There is an empty buffer (marked with status = 0)
* - There are more than two buffers marked as playing, meaning at least one is stale.
*/
- int idx = -1;
/* Find the next empty or stale buffer and fill. */
- for(int i = 1; i < this->hidden->n_buffers; ++i)
+ for(int i = 1; i <= this->hidden->n_buffers; ++i)
{
- idx = (this->hidden->current_playing + i) % this->hidden->n_buffers;
+ int idx = (this->hidden->current_playing + i) % this->hidden->n_buffers;
/* Empty or stale. */
if(this->hidden->status[idx] == 0 ||
- this->hidden->status[idx] == 2)
- break;
- }
- if(idx < 0)
- return;
+ this->hidden->status[idx] == 2) {
+
+ LOGF("found empty buffer: %d (status: %d)", idx, this->hidden->status[idx]);
+
+ /* probably premature optimization here */
+ char *dst = (char*)this->hidden->rb_buf[idx], *src = this->hidden->mixbuf;
+ int size = this->spec.size / 2;
+ memcpy(dst, src, size);
- /* probably premature optimization here */
- char *dst = (char*)this->hidden->rb_buf[idx], *src = this->hidden->mixbuf;
- int size = this->hidden->mixlen / 2;
- memcpy(dst, src, size);
+ this->hidden->status[idx] = 1;
+ rb->yield();
- this->hidden->status[idx] = 1;
- rb->yield();
+ memcpy(dst + size, src + size, this->spec.size - size);
- memcpy(dst + size, src + size, this->hidden->mixlen - size);
+ LOGF("filled buffer %d (status %d %d %d %d)", idx, this->hidden->status[0], this->hidden->status[1], this->hidden->status[2], this->hidden->status[3]);
+
+ return;
+ }
+ }
- //LOGF("filled buffer %d (status %d %d %d)", idx, this->hidden->status[0], this->hidden->status[1], this->hidden->status[2]);
+ LOGF("WARNING: PlayDevice could not find buffer to fill; DROPPING SAMPLES!");
}
static SDL_AudioDevice *ROCKBOXAUD_CreateDevice(int devindex)
diff --git a/apps/plugins/shopper.c b/apps/plugins/shopper.c
index 7129291c10..25a484a31e 100644
--- a/apps/plugins/shopper.c
+++ b/apps/plugins/shopper.c
@@ -304,7 +304,6 @@ enum plugin_status plugin_start(const void* parameter)
/* now dump it in the list */
rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&lists, list_get_icon_cb);
- rb->gui_synclist_limit_scroll(&lists,true);
create_view(&lists);
rb->gui_synclist_set_nb_items(&lists,view_item_count);
rb->gui_synclist_select_item(&lists, 0);
@@ -316,7 +315,7 @@ enum plugin_status plugin_start(const void* parameter)
rb->gui_synclist_draw(&lists);
cur_sel = rb->gui_synclist_get_sel_pos(&lists);
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &button))
continue;
switch (button)
{
diff --git a/apps/plugins/shortcuts/shortcuts_view.c b/apps/plugins/shortcuts/shortcuts_view.c
index f4c4b58bc1..e0146f3174 100644
--- a/apps/plugins/shortcuts/shortcuts_view.c
+++ b/apps/plugins/shortcuts/shortcuts_view.c
@@ -32,7 +32,7 @@ enum sc_list_action_type
SCLA_USB,
};
-
+static size_t root_len;
static char *link_filename;
static bool user_file;
@@ -59,13 +59,8 @@ enum sc_list_action_type draw_sc_list(struct gui_synclist *gui_sc)
/* user input */
button = rb->get_action(CONTEXT_LIST, HZ);
/* HZ so the status bar redraws corectly */
- if (rb->gui_synclist_do_button(gui_sc, &button,
- LIST_WRAP_UNLESS_HELD)) {
- /* automatic handling of user input.
- * _UNLESS_HELD can be _ON or _OFF also
- * selection changed, so redraw */
+ if (rb->gui_synclist_do_button(gui_sc, &button))
continue;
- }
switch (button) { /* process the user input */
case ACTION_STD_OK:
return SCLA_SELECT;
@@ -115,7 +110,6 @@ int list_sc(void)
rb->gui_synclist_set_title(&gui_sc,
(user_file?"Shortcuts (sealed)":"Shortcuts (editable)"), NOICON);
rb->gui_synclist_set_nb_items(&gui_sc, sc_file.entry_cnt);
- rb->gui_synclist_limit_scroll(&gui_sc, false);
rb->gui_synclist_select_item(&gui_sc, 0);
/* Draw the prepared widget to the LCD now */
@@ -181,6 +175,42 @@ bool goto_entry(char *file_or_dir)
}
#endif
+static bool callback_show_item(char *name, int attr, struct tree_context *tc)
+{
+ (void)name;
+ if(attr & ATTR_DIRECTORY)
+ {
+ if ((tc->browse->flags & BROWSE_SELECTED) == 0 &&
+ rb->strlen(tc->currdir) < root_len)
+ {
+ tc->is_browsing = false; /* exit immediately */
+ }
+ }
+
+ return true;
+}
+
+bool open_browse(char *path, char *buf, size_t bufsz)
+{
+ struct browse_context browse = {
+ .dirfilter = rb->global_settings->dirfilter,
+ .flags = BROWSE_DIRFILTER| BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU,
+ .title = path,
+ .icon = Icon_Plugin,
+ .root = path,
+ .buf = buf,
+ .bufsize = bufsz,
+ .callback_show_item = callback_show_item,
+ };
+ root_len = 0;
+ char *name = rb->strrchr(path, '/');
+ if (name)
+ root_len = name - path;
+ rb->rockbox_browse(&browse);
+
+ return (browse.flags & BROWSE_SELECTED);
+}
+
int goto_entry(char *file_or_dir)
{
DEBUGF("Trying to go to '%s'...\n", file_or_dir);
@@ -208,14 +238,19 @@ int goto_entry(char *file_or_dir)
}
else
{
- /* Set the browsers dirfilter to the global setting
- * This is required in case the plugin was launched
- * from the plugins browser, in which case the
- * dirfilter is set to only display .rock files */
- rb->set_dirfilter(rb->global_settings->dirfilter);
-
- /* Change directory to the entry selected by the user */
- rb->set_current_file(file_or_dir);
+ if (!is_dir)
+ {
+ rb->set_current_file(file_or_dir);
+ return LOOP_EXIT;
+ }
+ char tmp_buf[MAX_PATH];
+ if (open_browse(file_or_dir, tmp_buf, sizeof(tmp_buf)))
+ {
+ DEBUGF("Trying to load '%s'...\n", tmp_buf);
+ rb->set_dirfilter(rb->global_settings->dirfilter);
+ rb->set_current_file(tmp_buf);
+ return LOOP_EXIT;
+ }
}
return PLUGIN_OK;
}
diff --git a/apps/plugins/sliding_puzzle.c b/apps/plugins/sliding_puzzle.c
index a34cb77669..33d2bb68f6 100644
--- a/apps/plugins/sliding_puzzle.c
+++ b/apps/plugins/sliding_puzzle.c
@@ -36,7 +36,7 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define PUZZLE_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define PUZZLE_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
#define PUZZLE_LEFT BUTTON_LEFT
#define PUZZLE_RIGHT BUTTON_RIGHT
#define PUZZLE_UP BUTTON_MENU
@@ -468,8 +468,8 @@ static const char * initial_bmp_path=NULL;
static const char * get_albumart_bmp_path(void)
{
struct mp3entry* track = rb->audio_current_track();
-
- if (!track || !track->path || track->path[0] == '\0')
+ /* Note rb->audio_current_track->path should never be null */
+ if (!track || track->path[0] == '\0')
return NULL;
if (!rb->search_albumart_files(track, "", albumart_path, MAX_PATH ) )
@@ -848,7 +848,7 @@ enum plugin_status plugin_start(
#if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
- rb->lcd_putsxy(0, 18, "[S-MENU] to stop");
+ rb->lcd_putsxy(0, 18, "Long [SELECT] to stop");
rb->lcd_putsxy(0, 28, "[S-LEFT] shuffle");
rb->lcd_putsxy(0, 38, "[S-RIGHT] change pic");
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
diff --git a/apps/plugins/snake.c b/apps/plugins/snake.c
index 25c89b264b..359077c9fa 100644
--- a/apps/plugins/snake.c
+++ b/apps/plugins/snake.c
@@ -55,12 +55,12 @@ dir is the current direction of the snake - 0=up, 1=right, 2=down, 3=left;
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define SNAKE_QUIT (BUTTON_SELECT|BUTTON_MENU)
+#define SNAKE_QUIT (BUTTON_SELECT|BUTTON_REPEAT)
#define SNAKE_LEFT BUTTON_LEFT
#define SNAKE_RIGHT BUTTON_RIGHT
#define SNAKE_UP BUTTON_MENU
#define SNAKE_DOWN BUTTON_PLAY
-#define SNAKE_PLAYPAUSE BUTTON_SELECT
+#define SNAKE_PLAYPAUSE (BUTTON_SELECT|BUTTON_REL)
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
#define SNAKE_QUIT BUTTON_POWER
diff --git a/apps/plugins/snake2.c b/apps/plugins/snake2.c
index 094fd854eb..c71fa7f247 100644
--- a/apps/plugins/snake2.c
+++ b/apps/plugins/snake2.c
@@ -181,8 +181,8 @@ Head and Tail are stored
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_MENU
#define SNAKE2_DOWN BUTTON_PLAY
-#define SNAKE2_QUIT (BUTTON_SELECT | BUTTON_MENU)
-#define SNAKE2_PLAYPAUSE BUTTON_SELECT
+#define SNAKE2_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
+#define SNAKE2_PLAYPAUSE (BUTTON_SELECT | BUTTON_REL)
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
@@ -1599,7 +1599,7 @@ static void game_init(void)
speed = level*20;
return;
case 1:
- rb->set_option("Game Type", &game_type, INT,
+ rb->set_option("Game Type", &game_type, RB_INT,
type_options, 2, NULL);
break;
case 2:
diff --git a/apps/plugins/snow.c b/apps/plugins/snow.c
index 10b41c972b..c7d7ad31d8 100644
--- a/apps/plugins/snow.c
+++ b/apps/plugins/snow.c
@@ -30,8 +30,14 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
/* PLA definitions */
#define SNOW_QUIT PLA_EXIT
-#define SNOW_QUIT2 PLA_CANCEL
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define SNOW_QUIT2 PLA_UP
+#else
+#define SNOW_QUIT2 PLA_CANCEL
+#endif
static short particles[NUM_PARTICLES][2];
#if LCD_WIDTH >= 160
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c
index 247663a5c2..bf61db7d88 100644
--- a/apps/plugins/sokoban.c
+++ b/apps/plugins/sokoban.c
@@ -125,7 +125,7 @@
#define SOKOBAN_RIGHT BUTTON_RIGHT
#define SOKOBAN_UP BUTTON_MENU
#define SOKOBAN_DOWN BUTTON_PLAY
-#define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_MENU)
+#define SOKOBAN_MENU (BUTTON_SELECT | BUTTON_REPEAT)
#define SOKOBAN_UNDO_PRE BUTTON_SELECT
#define SOKOBAN_UNDO (BUTTON_SELECT | BUTTON_REL)
#define SOKOBAN_REDO (BUTTON_SELECT | BUTTON_PLAY)
diff --git a/apps/plugins/solitaire.c b/apps/plugins/solitaire.c
index b1ede16f90..fde3d04a0b 100644
--- a/apps/plugins/solitaire.c
+++ b/apps/plugins/solitaire.c
@@ -56,7 +56,7 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-# define SOL_QUIT (BUTTON_SELECT | BUTTON_MENU)
+# define SOL_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
# define SOL_UP BUTTON_SCROLL_BACK
# define SOL_DOWN BUTTON_SCROLL_FWD
# define SOL_LEFT_PRE BUTTON_LEFT
@@ -1088,7 +1088,7 @@ static int solitaire_menu(bool in_game)
case 2:
if (rb->set_option("Draw Cards Option", &sol.draw_type,
- INT, drawcards, 2, NULL))
+ RB_INT, drawcards, 2, NULL))
result = MENU_USB;
break;
@@ -2149,6 +2149,7 @@ static int solitaire( int skipmenu )
break;
case SYS_POWEROFF:
+ case SYS_REBOOT:
return SOLITAIRE_SAVE_AND_QUIT;
default:
diff --git a/apps/plugins/spacerocks.c b/apps/plugins/spacerocks.c
index 8203fad612..36729f8453 100644
--- a/apps/plugins/spacerocks.c
+++ b/apps/plugins/spacerocks.c
@@ -51,10 +51,10 @@
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define AST_PAUSE (BUTTON_SELECT | BUTTON_PLAY)
-#define AST_QUIT (BUTTON_SELECT | BUTTON_MENU)
-#define AST_THRUST BUTTON_MENU
-#define AST_HYPERSPACE BUTTON_PLAY
+#define AST_PAUSE BUTTON_PLAY
+#define AST_QUIT BUTTON_MENU
+#define AST_THRUST BUTTON_RIGHT
+#define AST_HYPERSPACE BUTTON_LEFT
#define AST_LEFT BUTTON_SCROLL_BACK
#define AST_RIGHT BUTTON_SCROLL_FWD
#define AST_FIRE BUTTON_SELECT
@@ -259,8 +259,8 @@
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_UP
#define AST_HYPERSPACE BUTTON_DOWN
-#define AST_LEFT BUTTON_LEFT
-#define AST_RIGHT BUTTON_RIGHT
+#define AST_LEFT BUTTON_SCROLL_BACK
+#define AST_RIGHT BUTTON_SCROLL_FWD
#define AST_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
@@ -2128,10 +2128,10 @@ enum plugin_status plugin_start(const void* parameter)
#endif
/* universal font */
rb->lcd_setfont(FONT_SYSFIXED);
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
highscore_load(SCORE_FILE, highscores, NUM_SCORES);
rb->srand(*rb->current_tick);
@@ -2143,10 +2143,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_setfont(FONT_UI);
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
return ret;
}
diff --git a/apps/plugins/speedread.c b/apps/plugins/speedread.c
index 42634fb536..7a9ab61e7c 100644
--- a/apps/plugins/speedread.c
+++ b/apps/plugins/speedread.c
@@ -141,9 +141,9 @@ static void cleanup(void)
{
if(custom_font != FONT_UI)
rb->font_unload(custom_font);
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
}
/* returns height of drawn area */
@@ -302,9 +302,8 @@ static void begin_anim(void)
static void init_drawing(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
atexit(cleanup);
rb->lcd_set_background(OUTSIDE_COLOR);
@@ -483,16 +482,19 @@ static void load_font(void)
static void font_menu(void)
{
/* taken from text_viewer */
- struct browse_context browse;
char font[MAX_PATH], name[MAX_FILENAME+10];
-
rb->snprintf(name, sizeof(name), "%s.fnt", rb->global_settings->font_file);
- rb->browse_context_init(&browse, SHOW_FONT,
- BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
- "Font", Icon_Menu_setting, FONT_DIR, name);
- browse.buf = font;
- browse.bufsize = sizeof(font);
+ struct browse_context browse = {
+ .dirfilter = SHOW_FONT,
+ .flags = BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU,
+ .title = rb->str(LANG_CUSTOM_FONT),
+ .icon = Icon_Menu_setting,
+ .root = FONT_DIR,
+ .selected = name,
+ .buf = font,
+ .bufsize = sizeof(font),
+ };
rb->rockbox_browse(&browse);
diff --git a/apps/plugins/star.c b/apps/plugins/star.c
index 874afc1cf1..59cefa2c15 100644
--- a/apps/plugins/star.c
+++ b/apps/plugins/star.c
@@ -80,7 +80,7 @@
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define STAR_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define STAR_QUIT (BUTTON_SELECT | BUTTON_REPEAT)
#define STAR_LEFT BUTTON_LEFT
#define STAR_RIGHT BUTTON_RIGHT
#define STAR_UP BUTTON_MENU
@@ -91,7 +91,7 @@
#define STAR_LEVEL_DOWN (BUTTON_SELECT | BUTTON_LEFT)
#define STAR_LEVEL_REPEAT (BUTTON_SELECT | BUTTON_PLAY)
#define STAR_TOGGLE_CONTROL_NAME "SELECT"
-#define STAR_QUIT_NAME "S + MENU"
+#define STAR_QUIT_NAME "Long SELECT"
#define STAR_LEVEL_UP_NAME "S >"
#define STAR_LEVEL_DOWN_NAME "S <"
#define STAR_LEVEL_REPEAT_NAME "S + PLAY"
diff --git a/apps/plugins/starfield.c b/apps/plugins/starfield.c
index 7fc400d0ee..239b7c1396 100644
--- a/apps/plugins/starfield.c
+++ b/apps/plugins/starfield.c
@@ -27,11 +27,21 @@ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
/* Key assignement */
#define STARFIELD_QUIT PLA_EXIT
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define STARFIELD_QUIT2 PLA_UP
+#define STARFIELD_INCREASE_ZMOVE PLA_SCROLL_FWD
+#define STARFIELD_INCREASE_ZMOVE_REPEAT PLA_SCROLL_FWD_REPEAT
+#define STARFIELD_DECREASE_ZMOVE PLA_SCROLL_BACK
+#define STARFIELD_DECREASE_ZMOVE_REPEAT PLA_SCROLL_BACK_REPEAT
+#else
#define STARFIELD_QUIT2 PLA_CANCEL
#define STARFIELD_INCREASE_ZMOVE PLA_UP
#define STARFIELD_INCREASE_ZMOVE_REPEAT PLA_UP_REPEAT
#define STARFIELD_DECREASE_ZMOVE PLA_DOWN
#define STARFIELD_DECREASE_ZMOVE_REPEAT PLA_DOWN_REPEAT
+#endif
#define STARFIELD_INCREASE_NB_STARS PLA_RIGHT
#define STARFIELD_INCREASE_NB_STARS_REPEAT PLA_RIGHT_REPEAT
#define STARFIELD_DECREASE_NB_STARS PLA_LEFT
@@ -324,14 +334,14 @@ enum plugin_status plugin_start(const void* parameter)
int ret;
(void)parameter;
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
ret = plugin_main();
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return ret;
}
diff --git a/apps/plugins/stats.c b/apps/plugins/stats.c
index 19ccd9f452..b48259a0e4 100644
--- a/apps/plugins/stats.c
+++ b/apps/plugins/stats.c
@@ -29,7 +29,15 @@ static bool cancel;
/* we use PLA */
#define STATS_STOP PLA_EXIT
+
+#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_3G_PAD) \
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+#define STATS_STOP2 PLA_UP
+#else
#define STATS_STOP2 PLA_CANCEL
+#endif
+
/* this set the context to use with PLA */
static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
@@ -149,7 +157,7 @@ static void traversedir(char* location, char* name)
lasttick = *rb->current_tick;
button = pluginlib_getaction(TIMEOUT_NOBLOCK, plugin_contexts,
ARRAYLEN(plugin_contexts));
- if (button == STATS_STOP) {
+ if (button == STATS_STOP || button == STATS_STOP2) {
cancel = true;
break;
}
diff --git a/apps/plugins/sudoku/sudoku.c b/apps/plugins/sudoku/sudoku.c
index 34a1f6dd07..3ede4e8af6 100644
--- a/apps/plugins/sudoku/sudoku.c
+++ b/apps/plugins/sudoku/sudoku.c
@@ -867,7 +867,7 @@ static bool numdisplay_setting(void)
{"Coloured", -1},
};
- return rb->set_option("Number Display", &sudcfg.number_display, INT, names,
+ return rb->set_option("Number Display", &sudcfg.number_display, RB_INT, names,
sizeof(names) / sizeof(names[0]), NULL);
}
#endif
@@ -880,7 +880,7 @@ static bool showmarkings_setting(void)
{"Show", -1},
};
- return rb->set_option("Show Markings", &sudcfg.show_markings, INT, names,
+ return rb->set_option("Show Markings", &sudcfg.show_markings, RB_INT, names,
sizeof(names) / sizeof(names[0]), NULL);
}
#endif
diff --git a/apps/plugins/superdom.c b/apps/plugins/superdom.c
index 50027a30c6..79a6d1a8f2 100644
--- a/apps/plugins/superdom.c
+++ b/apps/plugins/superdom.c
@@ -583,7 +583,7 @@ static int settings_menu(void)
};
static int sel=1;
rb->set_option("Computer difficulty", &sel,
- INT, difficulty_options, 3, NULL);
+ RB_INT, difficulty_options, 3, NULL);
superdom_settings.compdiff=sel+1;
break;
}
@@ -1450,7 +1450,6 @@ static int show_inventory(void)
{
struct simplelist_info info;
rb->simplelist_info_init(&info, "Inventory", 9, NULL);
- info.hide_selection = true;
info.get_name = inventory_data;
if(rb->simplelist_show_list(&info))
{
diff --git a/apps/plugins/tagcache/SOURCES b/apps/plugins/tagcache/SOURCES
new file mode 100644
index 0000000000..2541f3e87c
--- /dev/null
+++ b/apps/plugins/tagcache/SOURCES
@@ -0,0 +1,2 @@
+tagcache.c
+
diff --git a/apps/plugins/tagcache/tagcache.c b/apps/plugins/tagcache/tagcache.c
new file mode 100644
index 0000000000..cce9efbed9
--- /dev/null
+++ b/apps/plugins/tagcache/tagcache.c
@@ -0,0 +1,1021 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 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.
+ *
+ ****************************************************************************/
+
+/*Plugin Includes*/
+
+#include "plugin.h"
+#include "errno.h"
+
+/* Redefinitons of ANSI C functions. */
+#include "lib/wrappers.h"
+#include "lib/helper.h"
+
+static void thread_create(void);
+static void thread(void); /* the thread running commit*/
+static void allocate_tempbuf(void);
+static void free_tempbuf(void);
+static bool do_timed_yield(void);
+static void _log(const char *fmt, ...);
+static bool logdump(bool append);
+/*Aliases*/
+#if 0
+#ifdef ROCKBOX_HAS_LOGF
+ #define logf rb->logf
+#else
+ #define logf(...) {}
+#endif
+#endif
+
+#define logf _log
+#define sleep rb->sleep
+#define qsort rb->qsort
+
+#define write(x,y,z) rb->write(x,y,z)
+#define ftruncate rb->ftruncate
+#define remove rb->remove
+#define rename rb->rename
+
+#define vsnprintf rb->vsnprintf
+#define mkdir rb->mkdir
+#define filesize rb->filesize
+
+#define strtok_r rb->strtok_r
+#define strncasecmp rb->strncasecmp
+#define strcasecmp rb->strcasecmp
+
+#define current_tick (*rb->current_tick)
+#define crc_32(x,y,z) rb->crc_32(x,y,z)
+#define plugin_get_buffer rb->plugin_get_buffer
+
+#define MAX_LOG_SIZE 16384
+
+#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF)
+#define EV_STARTUP MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x01)
+
+#define BACKUP_DIRECTORY PLUGIN_APPS_DATA_DIR "/db_commit"
+
+#define RC_SUCCESS 0
+#define RC_USER_CANCEL 2
+
+enum fcpy_op_flags
+{
+ FCPY_MOVE = 0x00, /* Is a move operation (default) */
+ FCPY_COPY = 0x01, /* Is a copy operation */
+ FCPY_OVERWRITE = 0x02, /* Overwrite destination */
+ FCPY_EXDEV = 0x04, /* Actually copy/move across volumes */
+};
+
+/* communication to the worker thread */
+static struct
+{
+ int user_index;
+ bool exiting; /* signal to the thread that we want to exit */
+ bool resume;
+ unsigned int id; /* worker thread id */
+ struct event_queue queue; /* thread event queue */
+ long last_useraction_tick;
+} gThread;
+
+/* status of db and commit */
+static struct
+{
+ long last_check;
+ bool do_commit;
+ bool auto_commit;
+ bool commit_ready;
+ bool db_exists;
+ bool have_backup;
+ bool bu_exists;
+} gStatus;
+
+static unsigned char logbuffer[MAX_LOG_SIZE + 1];
+static int log_font_h = -1;
+static int logindex;
+static bool logwrap;
+static bool logenabled = true;
+
+/*Support Fns*/
+/* open but with a builtin printf for assembling the path */
+int open_pathfmt(char *buf, size_t size, int oflag, const char *pathfmt, ...)
+{
+ va_list ap;
+ va_start(ap, pathfmt);
+ vsnprintf(buf, size, pathfmt, ap);
+ va_end(ap);
+ if ((oflag & O_PATH) == O_PATH)
+ return -1;
+ int handle = open(buf, oflag, 0666);
+ //logf("Open: %s %d flag: %x", buf, handle, oflag);
+ return handle;
+}
+
+static void sleep_yield(void)
+{
+ sleep(1);
+ #undef yield
+ rb->yield();
+ #define yield sleep_yield
+}
+
+/* make sure tag can be displayed by font pf*/
+static bool text_is_displayable(struct font *pf, unsigned char *src)
+{
+ unsigned short code;
+ const unsigned char *ptr = src;
+ while(*ptr)
+ {
+ ptr = rb->utf8decode(ptr, &code);
+
+ if(!rb->font_get_bits(pf, code))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* callback for each tag if false returned tag will not be added */
+bool user_check_tag(int index_type, char* build_idx_buf)
+{
+ static struct font *pf = NULL;
+ if (!pf)
+ pf = rb->font_get(FONT_UI);
+
+ if (index_type == tag_artist || index_type == tag_album ||
+ index_type == tag_genre || index_type == tag_title ||
+ index_type == tag_composer || index_type == tag_comment ||
+ index_type == tag_albumartist || index_type == tag_virt_canonicalartist)
+ {
+ /* this could be expanded with more rules / transformations */
+ if (rb->utf8length(build_idx_buf) != strlen(build_idx_buf))
+ {
+ if (!text_is_displayable(pf, build_idx_buf))
+ {
+ logf("Can't display (%d) %s", index_type, build_idx_buf);
+ }
+ }
+ }
+ return true;
+}
+
+/* undef features we don't need */
+#undef HAVE_DIRCACHE
+#undef HAVE_TC_RAMCACHE
+#undef HAVE_EEPROM_SETTINGS
+/* paste the whole tagcache.c file */
+#include "../tagcache.c"
+
+static void check_logindex(void)
+{
+ if(logindex >= MAX_LOG_SIZE)
+ {
+ logdump(true);
+ //logwrap = true;
+ logindex = 0;
+ }
+}
+
+static int log_push(void *userp, int c)
+{
+ (void)userp;
+
+ logbuffer[logindex++] = c;
+ check_logindex();
+
+ return 1;
+}
+
+/* our logf function */
+static void _log(const char *fmt, ...)
+{
+ if (!logenabled)
+ {
+ rb->splash(10, "log not enabled");
+ return;
+ }
+
+ #ifdef USB_ENABLE_SERIAL
+ int old_logindex = logindex;
+ #endif
+ va_list ap;
+
+ va_start(ap, fmt);
+
+#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
+ char buf[1024];
+ vsnprintf(buf, sizeof buf, fmt, ap);
+ DEBUGF("%s\n", buf);
+ /* restart va_list otherwise the result if undefined when vuprintf is called */
+ va_end(ap);
+ va_start(ap, fmt);
+#endif
+
+ rb->vuprintf(log_push, NULL, fmt, ap);
+ va_end(ap);
+
+ /* add trailing zero */
+ log_push(NULL, '\0');
+}
+
+
+int compute_nb_lines(int w, struct font* font)
+{
+ int i, nb_lines;
+ int cur_x, delta_x;
+
+ if(logindex>= MAX_LOG_SIZE || (logindex == 0 && !logwrap))
+ return 0;
+
+ if(logwrap)
+ i = logindex;
+ else
+ i = 0;
+
+ cur_x = 0;
+ nb_lines = 0;
+
+ do {
+ if(logbuffer[i] == '\0')
+ {
+ cur_x = 0;
+ nb_lines++;
+ }
+ else
+ {
+ /* does character fit on this line ? */
+ delta_x = rb->font_get_width(font, logbuffer[i]);
+
+ if(cur_x + delta_x > w)
+ {
+ cur_x = 0;
+ nb_lines++;
+ }
+
+ /* update pointer */
+ cur_x += delta_x;
+ }
+
+ i++;
+ if(i >= MAX_LOG_SIZE)
+ i = 0;
+ } while(i != logindex);
+
+ return nb_lines;
+}
+
+static bool logdisplay(void)
+{
+
+ int w, h, i, index;
+ int fontnr;
+ int cur_x, cur_y, delta_x;
+ struct font* font;
+
+ char buf[2];
+
+ fontnr = FONT_FIRSTUSERFONT;
+ font = rb->font_get(fontnr);
+ buf[1] = '\0';
+ w = LCD_WIDTH;
+ h = LCD_HEIGHT;
+
+ if (log_font_h < 0) /* init, get the horizontal size of each line */
+ {
+ rb->font_getstringsize("A", NULL, &log_font_h, fontnr);
+ /* start at the end of the log */
+ gThread.user_index = compute_nb_lines(w, font) - h/log_font_h -1;
+ /* user_index will be number of the first line to display (warning: line!=log entry) */
+ /* if negative, will be set 0 to zero later */
+ }
+
+ rb->lcd_clear_display();
+
+ if(gThread.user_index < 0 || gThread.user_index >= MAX_LOG_SIZE)
+ gThread.user_index = 0;
+
+ if(logwrap)
+ i = logindex;
+ else
+ i = 0;
+
+ index = 0;
+ cur_x = 0;
+ cur_y = 0;
+
+ /* nothing to print ? */
+ if(logindex == 0 && !logwrap)
+ goto end_print;
+
+ do {
+ if(logbuffer[i] == '\0')
+ {
+ /* should be display a newline ? */
+ if(index >= gThread.user_index)
+ cur_y += log_font_h;
+ cur_x = 0;
+ index++;
+ }
+ else
+ {
+ /* does character fit on this line ? */
+ delta_x = rb->font_get_width(font, logbuffer[i]);
+
+ if(cur_x + delta_x > w)
+ {
+ /* should be display a newline ? */
+ if(index >= gThread.user_index)
+ cur_y += log_font_h;
+ cur_x = 0;
+ index++;
+ }
+
+ /* should we print character ? */
+ if(index >= gThread.user_index)
+ {
+ buf[0] = logbuffer[i];
+ rb->lcd_putsxy(cur_x, cur_y, buf);
+ }
+
+ /* update pointer */
+ cur_x += delta_x;
+ }
+ i++;
+ /* did we fill the screen ? */
+ if(cur_y > h - log_font_h)
+ {
+ if (TIME_AFTER(current_tick, gThread.last_useraction_tick + HZ))
+ gThread.user_index++;
+ break;
+ }
+
+ if(i >= MAX_LOG_SIZE)
+ i = 0;
+ } while(i != logindex);
+
+ end_print:
+ rb->lcd_update();
+
+ return false;
+}
+
+static bool logdump(bool append)
+{
+ int fd;
+ int flags = O_CREAT|O_WRONLY|O_TRUNC;
+ /* nothing to print ? */
+ if(!logenabled || (logindex == 0 && !logwrap))
+ {
+ /* nothing is logged just yet */
+ return false;
+ }
+ if (append)
+ {
+ flags = O_CREAT|O_WRONLY|O_APPEND;
+ }
+
+ fd = open(ROCKBOX_DIR "/db_commit_log.txt", flags, 0666);
+ logenabled = false;
+ if(-1 != fd) {
+ int i;
+
+ if(logwrap)
+ i = logindex;
+ else
+ i = 0;
+
+ do {
+ if(logbuffer[i]=='\0')
+ rb->fdprintf(fd, "\n");
+ else
+ rb->fdprintf(fd, "%c", logbuffer[i]);
+
+ i++;
+ if(i >= MAX_LOG_SIZE)
+ i = 0;
+ } while(i != logindex);
+
+ close(fd);
+ }
+
+ logenabled = true;
+
+ return false;
+}
+
+static void allocate_tempbuf(void)
+{
+ tempbuf_size = 0;
+ tempbuf = rb->plugin_get_audio_buffer(&tempbuf_size);
+ tempbuf_size &= ~0x03;
+
+}
+
+static void free_tempbuf(void)
+{
+ if (tempbuf_size == 0)
+ return ;
+
+ rb->plugin_release_audio_buffer();
+ tempbuf = NULL;
+ tempbuf_size = 0;
+}
+
+static bool do_timed_yield(void)
+{
+ /* Sorting can lock up for quite a while, so yield occasionally */
+ static long wakeup_tick = 0;
+ if (TIME_AFTER(current_tick, wakeup_tick))
+ {
+ yield();
+
+ wakeup_tick = current_tick + (HZ/5);
+ return true;
+ }
+ return false;
+}
+
+/* copy/move a file */
+static int fcpy(const char *src, const char *target,
+ unsigned int flags, bool (*poll_cancel)(const char *))
+{
+ int rc = -1;
+
+ while (!(flags & (FCPY_COPY | FCPY_EXDEV))) {
+ if ((flags & FCPY_OVERWRITE) || !file_exists(target)) {
+ /* Rename and possibly overwrite the file */
+ if (poll_cancel && poll_cancel(src)) {
+ rc = RC_USER_CANCEL;
+ } else {
+ rc = rename(src, target);
+ }
+
+ #ifdef HAVE_MULTIVOLUME
+ if (rc < 0 && errno == EXDEV) {
+ /* Failed because cross volume rename doesn't work; force
+ a move instead */
+ flags |= FCPY_EXDEV;
+ break;
+ }
+ #endif /* HAVE_MULTIVOLUME */
+ }
+
+ return rc;
+ }
+
+ /* See if we can get the plugin buffer for the file copy buffer */
+ size_t buffersize;
+ char *buffer = (char *) plugin_get_buffer(&buffersize);
+ if (buffer == NULL || buffersize < 512) {
+ /* Not large enough, try for a disk sector worth of stack
+ instead */
+ buffersize = 512;
+ buffer = (char *)alloca(buffersize);
+ }
+
+ if (buffer == NULL) {
+ return -1;
+ }
+
+ buffersize &= ~0x1ff; /* Round buffer size to multiple of sector
+ size */
+
+ int src_fd = open(src, O_RDONLY);
+ if (src_fd >= 0) {
+ int oflag = O_WRONLY|O_CREAT;
+
+ if (!(flags & FCPY_OVERWRITE)) {
+ oflag |= O_EXCL;
+ }
+
+ int target_fd = open(target, oflag, 0666);
+ if (target_fd >= 0) {
+ off_t total_size = 0;
+ off_t next_cancel_test = 0; /* No excessive button polling */
+
+ rc = RC_SUCCESS;
+
+ while (rc == RC_SUCCESS) {
+ if (total_size >= next_cancel_test) {
+ next_cancel_test = total_size + 0x10000;
+ if (poll_cancel && poll_cancel(src)) {
+ rc = RC_USER_CANCEL;
+ break;
+ }
+ }
+
+ ssize_t bytesread = read(src_fd, buffer, buffersize);
+ if (bytesread <= 0) {
+ if (bytesread < 0) {
+ rc = -1;
+ }
+ /* else eof on buffer boundary; nothing to write */
+ break;
+ }
+
+ ssize_t byteswritten = write(target_fd, buffer, bytesread);
+ if (byteswritten < bytesread) {
+ /* Some I/O error */
+ rc = -1;
+ break;
+ }
+
+ total_size += byteswritten;
+
+ if (bytesread < (ssize_t)buffersize) {
+ /* EOF with trailing bytes */
+ break;
+ }
+ }
+
+ if (rc == RC_SUCCESS) {
+ /* If overwriting, set the correct length if original was
+ longer */
+ rc = ftruncate(target_fd, total_size);
+ }
+
+ close(target_fd);
+
+ if (rc != RC_SUCCESS) {
+ /* Copy failed. Cleanup. */
+ remove(target);
+ }
+ }
+
+ close(src_fd);
+ }
+
+ if (rc == RC_SUCCESS && !(flags & FCPY_COPY)) {
+ /* Remove the source file */
+ rc = remove(src);
+ }
+
+ return rc;
+}
+
+static bool backup_restore_tagcache(bool backup)
+{
+ struct master_header tcmh;
+ char path[MAX_PATH];
+ char bu_path[MAX_PATH];
+ int fd;
+ int rc;
+
+ if (backup)
+ {
+ if (!rb->dir_exists(BACKUP_DIRECTORY))
+ mkdir(BACKUP_DIRECTORY);
+ snprintf(path, sizeof(path), "%s/"TAGCACHE_FILE_MASTER, tc_stat.db_path);
+ snprintf(bu_path, sizeof(bu_path), "%s/"TAGCACHE_FILE_MASTER, BACKUP_DIRECTORY);
+ }
+ else
+ {
+ if (!rb->dir_exists(BACKUP_DIRECTORY))
+ return false;
+ snprintf(path, sizeof(path), "%s/"TAGCACHE_FILE_MASTER, BACKUP_DIRECTORY);
+ snprintf(bu_path, sizeof(bu_path), "%s/"TAGCACHE_FILE_MASTER, tc_stat.db_path);
+ }
+
+ fd = open(path, O_RDONLY, 0666);
+
+ if (fd >= 0)
+ {
+ rc = read(fd, &tcmh, sizeof(struct master_header));
+ close(fd);
+ if (rc != sizeof(struct master_header))
+ {
+ logf("master file read failed");
+ return false;
+ }
+ int entries = tcmh.tch.entry_count;
+
+ logf("master file %d entries", entries);
+ if (backup)
+ logf("backup db to %s", BACKUP_DIRECTORY);
+ else
+ logf("restore db to %s", tc_stat.db_path);
+
+ if (entries > 0)
+ {
+ logf("%s", path);
+ fcpy(path, bu_path, FCPY_COPY|FCPY_OVERWRITE, NULL);
+
+ for (int i = 0; i < TAG_COUNT; i++)
+ {
+ if (TAGCACHE_IS_NUMERIC(i))
+ continue;
+ snprintf(path, sizeof(path),
+ "%s/"TAGCACHE_FILE_INDEX, tc_stat.db_path, i);
+
+ snprintf(bu_path, sizeof(bu_path),
+ "%s/"TAGCACHE_FILE_INDEX, BACKUP_DIRECTORY, i);
+ /* Note: above we swapped paths in the snprintf call here we swap variables */
+ if (backup)
+ {
+ logf("%s", path);
+ if (fcpy(path, bu_path, FCPY_COPY|FCPY_OVERWRITE, NULL) < 0)
+ goto failed;
+ gStatus.have_backup = true;
+ }
+ else
+ {
+ logf("%s", bu_path);
+ if (fcpy(bu_path, path, FCPY_COPY|FCPY_OVERWRITE, NULL) < 0)
+ goto failed;
+ }
+ }
+ }
+ return true;
+ }
+failed:
+ if (backup)
+ {
+ logf("failed backup");
+ }
+
+ return false;
+}
+
+/* asks the user if they wish to quit */
+static bool confirm_quit(void)
+{
+ const struct text_message prompt =
+ { (const char*[]) {"Are you sure?", "This will abort commit."}, 2};
+ enum yesno_res response = rb->gui_syncyesno_run(&prompt, NULL, NULL);
+ return (response == YESNO_YES);
+}
+
+/* asks the user if they wish to backup/restore */
+static bool prompt_backup_restore(bool backup)
+{
+ const struct text_message bu_prompt = { (const char*[]) {"Backup database?"}, 1};
+ const struct text_message re_prompt =
+ { (const char*[]) {"Error committing,", "Restore database?"}, 2};
+ enum yesno_res response =
+ rb->gui_syncyesno_run(backup ? &bu_prompt:&re_prompt, NULL, NULL);
+ if(response == YESNO_YES)
+ return backup_restore_tagcache(backup);
+ return true;
+}
+
+static const char* list_get_name_cb(int selected_item, void* data,
+ char* buf, size_t buf_len)
+{
+ (void) data;
+ (void) buf;
+ (void) buf_len;
+
+ /* buf supplied isn't used so lets use it for a filename buffer */
+ if (TIME_AFTER(current_tick, gStatus.last_check))
+ {
+ snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_NOCOMMIT, tc_stat.db_path);
+ gStatus.auto_commit = !file_exists(buf);
+ snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_TEMP, tc_stat.db_path);
+ gStatus.commit_ready = file_exists(buf);
+ snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_MASTER, tc_stat.db_path);
+ gStatus.db_exists = file_exists(buf);
+ snprintf(buf, buf_len, "%s/"TAGCACHE_FILE_MASTER, BACKUP_DIRECTORY);
+ gStatus.bu_exists = file_exists(buf);
+ gStatus.last_check = current_tick + HZ;
+ buf[0] = '\0';
+ }
+
+ switch(selected_item)
+ {
+
+ case 0: /* exit */
+ return ID2P(LANG_MENU_QUIT);
+ case 1: /*sep*/
+ return ID2P(VOICE_BLANK);
+ case 2: /*backup*/
+ if (!gStatus.db_exists)
+ return ID2P(VOICE_BLANK);
+ return "Backup";
+ case 3: /*restore*/
+ if (!gStatus.bu_exists)
+ return ID2P(VOICE_BLANK);
+ return "Restore";
+ case 4: /*sep*/
+ return ID2P(VOICE_BLANK);
+ case 5: /*auto commit*/
+ if (gStatus.auto_commit)
+ return "Disable auto commit";
+ else
+ return "Enable auto commit";
+ case 6: /*destroy*/
+ if (gStatus.db_exists)
+ return "Delete database";
+ /*fall through*/
+ case 7: /*sep*/
+ return ID2P(VOICE_BLANK);
+ case 8: /*commit*/
+ if (gStatus.commit_ready)
+ return "Commit";
+ else
+ return "Nothing to commit";
+ default:
+ return "?";
+ }
+}
+
+static int list_voice_cb(int list_index, void* data)
+{
+ #define MAX_MENU_NAME 32
+ if (!rb->global_settings->talk_menu)
+ return -1;
+ else
+ {
+ char buf[MAX_MENU_NAME];
+ const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf));
+ long id = P2ID((const unsigned char *)name);
+ if(id>=0)
+ rb->talk_id(id, true);
+ else
+ rb->talk_spell(name, true);
+ }
+ return 0;
+}
+
+static int commit_menu(void)
+{
+ struct gui_synclist lists;
+ bool exit = false;
+ int button,i;
+ int selection, ret = 0;
+
+ rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
+ rb->gui_synclist_set_icon_callback(&lists, NULL);
+ rb->gui_synclist_set_nb_items(&lists, 9);
+ rb->gui_synclist_select_item(&lists, 0);
+
+ while (!exit)
+ {
+ rb->gui_synclist_draw(&lists);
+ button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
+ if (rb->gui_synclist_do_button(&lists, &button))
+ continue;
+ selection = rb->gui_synclist_get_sel_pos(&lists);
+
+ if (button == ACTION_STD_CANCEL)
+ return 0;
+ else if (button == ACTION_STD_OK)
+ {
+ switch(selection)
+ {
+ case 0: /* exit */
+ exit = true;
+ break;
+ case 1: /*sep*/
+ continue;
+ case 2: /*backup*/
+ if (!gStatus.db_exists)
+ break;
+ if (!backup_restore_tagcache(true))
+ rb->splash(HZ, "Backup failed!");
+ else
+ {
+ rb->splash(HZ, "Backup success!");
+ gStatus.bu_exists = true;
+ }
+ break;
+ case 3: /*restore*/
+ if (!gStatus.bu_exists)
+ break;
+ if (!backup_restore_tagcache(false))
+ rb->splash(HZ, "Restore failed!");
+ else
+ rb->splash(HZ, "Restore success!");
+ break;
+ case 4: /*sep*/
+ continue;
+ case 5: /*auto commit*/
+ {
+ /* build_idx_buf supplied by tagcache.c isn't being used
+ * so lets use it for a filename buffer */
+ snprintf(build_idx_buf, build_idx_bufsz,
+ "%s/" TAGCACHE_FILE_NOCOMMIT, tc_stat.db_path);
+ if(gStatus.auto_commit)
+ close(open(build_idx_buf, O_WRONLY | O_CREAT | O_TRUNC, 0666));
+ else
+ remove(build_idx_buf);
+ gStatus.auto_commit = !file_exists(build_idx_buf);
+ break;
+ }
+ case 6: /*destroy*/
+ {
+ if (!gStatus.db_exists)
+ break;
+ const struct text_message prompt =
+ { (const char*[]) {"Are you sure?", "This will destroy database."}, 2};
+ if (rb->gui_syncyesno_run(&prompt, NULL, NULL) == YESNO_YES)
+ remove_files();
+ break;
+ }
+ case 7: /*sep*/
+ continue;
+ case 8: /*commit*/
+ if (gStatus.commit_ready)
+ {
+ gStatus.do_commit = true;
+ exit = true;
+ }
+ break;
+
+ case MENU_ATTACHED_USB:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ return 0;
+ }
+ }
+ } /*while*/
+ return ret;
+}
+
+/*-----------------------------------------------------------------------------*/
+/******* plugin_start ******* */
+/*-----------------------------------------------------------------------------*/
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ (void) parameter;
+
+ /* Turn off backlight timeout */
+ backlight_ignore_timeout();
+
+ memset(&gThread, 0, sizeof(gThread));
+ memset(&gStatus, 0, sizeof(gStatus));
+ memset(&tc_stat, 0, sizeof(struct tagcache_stat));
+ memset(&current_tcmh, 0, sizeof(struct master_header));
+ filenametag_fd = -1;
+
+ strlcpy(tc_stat.db_path, rb->global_settings->tagcache_db_path, sizeof(tc_stat.db_path));
+ if (!rb->dir_exists(tc_stat.db_path)) /* on fail use default DB path */
+ strlcpy(tc_stat.db_path, ROCKBOX_DIR, sizeof(tc_stat.db_path));
+ tc_stat.initialized = true;
+ tc_stat.commit_step = -1;
+
+ logf("started");
+
+ int result = commit_menu();
+
+ if (!gStatus.do_commit)
+ {
+ /* Turn on backlight timeout (revert to settings) */
+ backlight_use_settings();
+ return PLUGIN_OK;
+ }
+
+ logdump(false);
+ allocate_tempbuf();
+ if (gStatus.db_exists && !gStatus.have_backup && !prompt_backup_restore(true))
+ rb->splash(HZ, "Backup failed!");
+ thread_create();
+ gThread.user_index = 0;
+ logdisplay(); /* get something on the screen while user waits */
+
+ while (!gThread.exiting)
+ {
+ logdisplay();
+
+ int action = rb->get_action(CONTEXT_STD, HZ/20);
+
+ switch( action )
+ {
+ case ACTION_NONE:
+ break;
+ case ACTION_STD_NEXT:
+ case ACTION_STD_NEXTREPEAT:
+ gThread.last_useraction_tick = current_tick;
+ gThread.user_index++;
+ break;
+ case ACTION_STD_PREV:
+ case ACTION_STD_PREVREPEAT:
+ gThread.last_useraction_tick = current_tick;
+ gThread.user_index--;
+ break;
+ case ACTION_STD_OK:
+ gThread.last_useraction_tick = current_tick;
+ gThread.user_index = 0;
+ break;
+ case SYS_POWEROFF:
+ case ACTION_STD_CANCEL:
+ if (tc_stat.commit_step >= 0 && !tc_stat.ready)
+ {
+ if (!confirm_quit())
+ break;
+ tc_stat.commit_delayed = true; /* Cancel the commit */
+ }
+ rb->queue_remove_from_head(&gThread.queue, EV_EXIT);
+ rb->queue_post(&gThread.queue, EV_EXIT, 0);
+ break;
+#ifdef HAVE_TOUCHSCREEN
+ case ACTION_TOUCHSCREEN:
+ {
+ gThread.last_useraction_tick = current_tick;
+ short x, y;
+ static int prev_y;
+
+ action = rb->action_get_touchscreen_press(&x, &y);
+
+ if(action & BUTTON_REL)
+ prev_y = 0;
+ else
+ {
+ if(prev_y != 0)
+ gThread.user_index += (prev_y - y) / log_font_h;
+
+ prev_y = y;
+ }
+ }
+#endif
+ default:
+ break;
+ }
+ yield();
+ }
+
+ rb->thread_wait(gThread.id);
+ rb->queue_delete(&gThread.queue);
+ free_tempbuf();
+
+ if (tc_stat.commit_delayed || !tc_stat.ready)
+ {
+ remove_files();
+ if (gStatus.bu_exists && !prompt_backup_restore(false))
+ rb->splash(HZ, "Restore failed!");
+ }
+
+ if (tc_stat.ready)
+ rb->tagcache_commit_finalize();
+
+ /* Turn on backlight timeout (revert to settings) */
+ backlight_use_settings();
+ return PLUGIN_OK;
+}
+
+/****************** main thread + helper ******************/
+static void thread(void)
+{
+ struct queue_event ev;
+ while (!gThread.exiting)
+ {
+ rb->queue_wait_w_tmo(&gThread.queue, &ev, 1);
+ switch (ev.id)
+ {
+ case SYS_USB_CONNECTED:
+ rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ logenabled = false;
+ break;
+ case SYS_USB_DISCONNECTED:
+ logenabled = true;
+ /*fall through*/
+ case EV_STARTUP:
+ logf("Thread Started");
+ cpu_boost(true);
+ if (commit())
+ tc_stat.ready = true;
+ cpu_boost(false);
+ logdump(true);
+ gThread.user_index++;
+ logdisplay();
+ break;
+ case EV_EXIT:
+ gThread.exiting = true;
+ return;
+ default:
+ break;
+ }
+ yield();
+ }
+}
+
+static void thread_create(void)
+{
+ /* put the thread's queue in the bcast list */
+ rb->queue_init(&gThread.queue, true);
+ /*Note: tagcache_stack is defined in apps/tagcache.c */
+ gThread.last_useraction_tick = current_tick;
+ gThread.id = rb->create_thread(thread, tagcache_stack, sizeof(tagcache_stack),
+ 0, "db_commit"
+ IF_PRIO(, PRIORITY_USER_INTERFACE)
+ IF_COP(, CPU));
+ rb->queue_post(&gThread.queue, EV_STARTUP, 0);
+ yield();
+}
diff --git a/apps/plugins/tagcache/tagcache.h b/apps/plugins/tagcache/tagcache.h
new file mode 100644
index 0000000000..e416289741
--- /dev/null
+++ b/apps/plugins/tagcache/tagcache.h
@@ -0,0 +1,25 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C)
+ *
+ * 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 _TC_PLUGIN_
+#define _TC_PLUGIN_
+#endif /*_TC_PLUGIN_ */
+
+
diff --git a/apps/plugins/tagcache/tagcache.make b/apps/plugins/tagcache/tagcache.make
new file mode 100644
index 0000000000..5d6d65cb0e
--- /dev/null
+++ b/apps/plugins/tagcache/tagcache.make
@@ -0,0 +1,30 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+TCPLUG_SRCDIR := $(APPSDIR)/plugins/tagcache
+TCPLUG_BUILDDIR := $(BUILDDIR)/apps/plugins/tagcache
+
+ROCKS += $(TCPLUG_BUILDDIR)/db_commit.rock
+
+TCPLUG_FLAGS = $(PLUGINFLAGS) -fno-strict-aliasing -Wno-unused \
+ -I$(TCPLUG_SRCDIR) -ffunction-sections \
+ -fdata-sections -Wl,--gc-sections
+TCPLUG_SRC := $(call preprocess, $(TCPLUG_SRCDIR)/SOURCES)
+TCPLUG_OBJ := $(call c2obj, $(TCPLUG_SRC))
+
+# add source files to OTHER_SRC to get automatic dependencies
+OTHER_SRC += $(APPSDIR)/tagcache.c $(TCPLUG_SRC)
+
+$(TCPLUG_BUILDDIR)/db_commit.rock: $(TCPLUG_OBJ)
+
+# special pattern rule for compiling with extra flags
+$(TCPLUG_BUILDDIR)/%.o: $(TCPLUG_SRCDIR)/%.c $(TCPLUG_SRCDIR)/tagcache.make
+ $(SILENT)mkdir -p $(dir $@)
+ $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(TCPLUG_FLAGS) -c $< -o $@
+
diff --git a/apps/plugins/test_codec.c b/apps/plugins/test_codec.c
index ac0dbf1633..91599bfc5d 100644
--- a/apps/plugins/test_codec.c
+++ b/apps/plugins/test_codec.c
@@ -915,7 +915,7 @@ menu:
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
if (result == BOOST)
{
- rb->set_option("Boosting", &boost, INT,
+ rb->set_option("Boosting", &boost, RB_INT,
boost_settings, 2, NULL);
goto menu;
}
diff --git a/apps/plugins/test_disk.c b/apps/plugins/test_disk.c
index 1429668556..fee6c4d0b0 100644
--- a/apps/plugins/test_disk.c
+++ b/apps/plugins/test_disk.c
@@ -467,10 +467,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->srand(*rb->current_tick);
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
while(!quit)
{
@@ -489,9 +488,8 @@ enum plugin_status plugin_start(const void* parameter)
}
/* Turn on backlight timeout (revert to settings) */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
rb->rmdir(testbasedir);
return PLUGIN_OK;
diff --git a/apps/plugins/test_fps.c b/apps/plugins/test_fps.c
index ddf938ac25..2f4e9bb13e 100644
--- a/apps/plugins/test_fps.c
+++ b/apps/plugins/test_fps.c
@@ -401,9 +401,9 @@ enum plugin_status plugin_start(const void* parameter)
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
cpu_freq = *rb->cpu_frequency; /* remember CPU frequency */
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
time_main_update();
rb->sleep(HZ);
#if defined(HAVE_LCD_COLOR) && (MEMORYSIZE > 2)
@@ -424,9 +424,9 @@ enum plugin_status plugin_start(const void* parameter)
(cpu_freq + 500000) / 1000000);
log_text(str);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
/* wait until user closes plugin */
plugin_quit();
diff --git a/apps/plugins/test_gfx.c b/apps/plugins/test_gfx.c
index 7734179159..60e2836463 100644
--- a/apps/plugins/test_gfx.c
+++ b/apps/plugins/test_gfx.c
@@ -495,9 +495,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_set_backdrop(NULL);
rb->lcd_clear_display();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
rb->splashf(0, "LCD driver performance test, please wait %d sec",
7*4*DURATION/HZ);
init_rand_table();
@@ -522,9 +522,9 @@ enum plugin_status plugin_start(const void* parameter)
(cpu_freq + 500000) / 1000000);
#endif
rb->close(log_fd);
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
#ifdef TEST_GREYLIB
grey_release();
#endif
diff --git a/apps/plugins/test_grey.c b/apps/plugins/test_grey.c
index 121cbad051..07fda1b9c6 100644
--- a/apps/plugins/test_grey.c
+++ b/apps/plugins/test_grey.c
@@ -123,9 +123,9 @@ enum plugin_status plugin_start(const void* parameter)
}
for (i = 0; i <= STEPS; i++)
input_levels[i] = lcd_levels[i] = (255 * i + (STEPS/2)) / STEPS;
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
grey_set_background(0); /* set background to black */
grey_clear_display();
grey_show(true);
@@ -221,8 +221,8 @@ enum plugin_status plugin_start(const void* parameter)
}
grey_release();
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/test_kbd.c b/apps/plugins/test_kbd.c
new file mode 100644
index 0000000000..a40aa4c76a
--- /dev/null
+++ b/apps/plugins/test_kbd.c
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Björn Stenberg
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/* welcome to the example rockbox plugin */
+
+/* mandatory include for all plugins */
+#include "plugin.h"
+
+/* this is the plugin entry point */
+enum plugin_status plugin_start(const void* parameter)
+{
+ /* if you don't use the parameter, you can do like
+ this to avoid the compiler warning about it */
+ (void)parameter;
+
+ /* "rb->" marks a plugin api call. Rockbox offers many of its built-in
+ * functions to plugins */
+ /* now go ahead and have fun! */
+ char buffer[MAX_PATH];
+ rb->snprintf(buffer, sizeof(buffer), "Keyboard test; Current plugin filename: '%s'",
+ rb->plugin_get_current_filename());
+
+ if (rb->kbd_input(buffer, sizeof(buffer), NULL) == 0)
+ rb->splash(HZ*2, buffer);
+
+ /* tell Rockbox that we have completed successfully */
+ return PLUGIN_OK;
+}
diff --git a/apps/plugins/test_sampr.c b/apps/plugins/test_sampr.c
index c13392f069..3006faeea5 100644
--- a/apps/plugins/test_sampr.c
+++ b/apps/plugins/test_sampr.c
@@ -252,7 +252,7 @@ static void play_tone(bool volume_set)
else
#endif /* HAVE_VOLUME_IN_LIST */
{
- rb->set_option("Sample Rate", &freq, INT, names,
+ rb->set_option("Sample Rate", &freq, RB_INT, names,
HW_NUM_FREQ, set_frequency);
(void)volume_set;
}
diff --git a/apps/plugins/test_usb.c b/apps/plugins/test_usb.c
new file mode 100644
index 0000000000..28ef8f7e5f
--- /dev/null
+++ b/apps/plugins/test_usb.c
@@ -0,0 +1,137 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 "logf.h"
+
+#undef DEBUGF
+#define DEBUGF(...)
+//#define DEBUGF printf
+
+#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF)
+
+unsigned char stack[DEFAULT_STACK_SIZE];
+struct event_queue queue;
+int thread_id;
+const char* state = "none";
+const char* prev_state = "none";
+
+static void main_loop(void)
+{
+ bool exiting = false;
+ struct queue_event ev;
+
+ while(true) {
+ rb->queue_wait(&queue, &ev);
+
+ /* events that are handled whether exiting or not */
+ switch(ev.id) {
+ case EV_EXIT:
+ return;
+ }
+
+ if(exiting)
+ continue;
+
+ /* events handled only when not exiting */
+ switch(ev.id) {
+ case SYS_USB_CONNECTED:
+ prev_state = state;
+ state = "connected";
+ logf("test_usb: connect ack %ld", *rb->current_tick);
+ DEBUGF("test_usb: connect ack %ld\n", *rb->current_tick);
+ rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ break;
+
+ case SYS_USB_DISCONNECTED:
+ prev_state = state;
+ state = "disconnected";
+ logf("test_usb: disconnect %ld", *rb->current_tick);
+ DEBUGF("test_usb: disconnect %ld\n", *rb->current_tick);
+ break;
+
+ case SYS_POWEROFF:
+ case SYS_REBOOT:
+ prev_state = state;
+ state = "exiting";
+ exiting = true;
+ break;
+ }
+ }
+}
+
+static void kill_tsr(void)
+{
+ rb->queue_post(&queue, EV_EXIT, 0);
+ rb->thread_wait(thread_id);
+ rb->queue_delete(&queue);
+}
+
+static int exit_tsr(bool reenter)
+{
+ MENUITEM_STRINGLIST(menu, "USB test menu", NULL,
+ "Status", "Stop plugin", "Back");
+
+ while(true) {
+ int result = reenter ? rb->do_menu(&menu, NULL, NULL, false) : 1;
+ switch(result) {
+ case 0:
+ rb->splashf(HZ, "State: %s", state);
+ rb->splashf(HZ, "Prev: %s", prev_state);
+ break;
+ case 1:
+ rb->splashf(HZ, "Stopping USB test thread");
+ kill_tsr();
+ return (reenter ? PLUGIN_TSR_TERMINATE : PLUGIN_TSR_SUSPEND);
+ case 2:
+ return PLUGIN_TSR_CONTINUE;
+ }
+ }
+}
+
+static void run_tsr(void)
+{
+ rb->queue_init(&queue, true);
+ thread_id = rb->create_thread(main_loop, stack, sizeof(stack),
+ 0, "test_usb TSR"
+ IF_PRIO(, PRIORITY_BACKGROUND)
+ IF_COP(, CPU));
+ rb->plugin_tsr(exit_tsr);
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ bool resume = (parameter == rb->plugin_tsr);
+
+ MENUITEM_STRINGLIST(menu, "USB test menu", NULL,
+ "Start", "Quit");
+
+ switch(!resume ? rb->do_menu(&menu, NULL, NULL, false) : 0) {
+ case 0:
+ run_tsr();
+ rb->splashf(HZ, "USB test thread started");
+ return PLUGIN_OK;
+ case 1:
+ return PLUGIN_OK;
+ default:
+ return PLUGIN_ERROR;
+ }
+}
diff --git a/apps/plugins/test_viewports.c b/apps/plugins/test_viewports.c
index 2bada01f79..6eff0249a2 100644
--- a/apps/plugins/test_viewports.c
+++ b/apps/plugins/test_viewports.c
@@ -130,7 +130,7 @@ static void *test_address_fn(int x, int y)
struct frame_buffer_t *fb = vp0.buffer;
/* LCD_STRIDEFORMAT & LCD_NATIVE_STRIDE macros allow Horiz screens to work with RB */
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCD_NATIVE_STRIDE(fb->stride)) + y;
#else
size_t element = (y * LCD_NATIVE_STRIDE(fb->stride)) + x;
@@ -249,9 +249,11 @@ enum plugin_status plugin_start(const void* parameter)
rb->button_get(true);
+ rb->screens[SCREEN_MAIN]->scroll_stop();
/* Restore the default viewport */
rb->screens[SCREEN_MAIN]->set_viewport(NULL);
#ifdef HAVE_REMOTE_LCD
+ rb->screens[SCREEN_REMOTE]->scroll_stop();
rb->screens[SCREEN_REMOTE]->set_viewport(NULL);
#endif
diff --git a/apps/plugins/text_editor.c b/apps/plugins/text_editor.c
index 0cbb61c774..8740606c58 100644
--- a/apps/plugins/text_editor.c
+++ b/apps/plugins/text_editor.c
@@ -214,7 +214,6 @@ static void setup_lists(struct gui_synclist *lists, int sel)
rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL);
rb->gui_synclist_set_icon_callback(lists,NULL);
rb->gui_synclist_set_nb_items(lists,line_count);
- rb->gui_synclist_limit_scroll(lists,true);
rb->gui_synclist_select_item(lists, sel);
rb->gui_synclist_draw(lists);
}
@@ -395,7 +394,7 @@ enum plugin_status plugin_start(const void* parameter)
rb->gui_synclist_draw(&lists);
cur_sel = rb->gui_synclist_get_sel_pos(&lists);
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &button))
continue;
switch (button)
{
diff --git a/apps/plugins/text_viewer/tv_menu.c b/apps/plugins/text_viewer/tv_menu.c
index 53e0adaf67..74a964ba98 100644
--- a/apps/plugins/text_viewer/tv_menu.c
+++ b/apps/plugins/text_viewer/tv_menu.c
@@ -46,15 +46,14 @@ static bool tv_horizontal_scroll_mode_setting(void)
{"Scroll by Column", -1},
};
- return rb->set_option("Scroll Mode", &new_prefs.horizontal_scroll_mode, INT,
+ return rb->set_option("Scroll Mode", &new_prefs.horizontal_scroll_mode, RB_INT,
names, 2, NULL);
}
MENUITEM_FUNCTION(horizontal_scrollbar_item, 0, "Scrollbar",
- tv_horizontal_scrollbar_setting,
- NULL, NULL, Icon_NOICON);
+ tv_horizontal_scrollbar_setting, NULL, Icon_NOICON);
MENUITEM_FUNCTION(horizontal_scroll_mode_item, 0, "Scroll Mode",
- tv_horizontal_scroll_mode_setting, NULL, NULL, Icon_NOICON);
+ tv_horizontal_scroll_mode_setting, NULL, Icon_NOICON);
MAKE_MENU(horizontal_scroll_menu, "Horizontal", NULL, Icon_NOICON,
&horizontal_scrollbar_item,
@@ -76,7 +75,7 @@ static bool tv_vertical_scroll_mode_setting(void)
{"Scroll by Line", -1},
};
- return rb->set_option("Scroll Mode", &new_prefs.vertical_scroll_mode, INT,
+ return rb->set_option("Scroll Mode", &new_prefs.vertical_scroll_mode, RB_INT,
names, 2, NULL);
}
@@ -98,21 +97,19 @@ static bool tv_narrow_mode_setting(void)
{"Top/Bottom Page", -1},
};
- return rb->set_option("Left/Right Key", &new_prefs.narrow_mode, INT,
+ return rb->set_option("Left/Right Key", &new_prefs.narrow_mode, RB_INT,
names, 2, NULL);
}
MENUITEM_FUNCTION(vertical_scrollbar_item, 0, "Scrollbar",
- tv_vertical_scrollbar_setting,
- NULL, NULL, Icon_NOICON);
+ tv_vertical_scrollbar_setting, NULL, Icon_NOICON);
MENUITEM_FUNCTION(vertical_scroll_mode_item, 0, "Scroll Mode",
- tv_vertical_scroll_mode_setting, NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(overlap_page_mode_item, 0, "Overlap Pages", tv_overlap_page_mode_setting,
- NULL, NULL, Icon_NOICON);
+ tv_vertical_scroll_mode_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(overlap_page_mode_item, 0, "Overlap Pages", tv_overlap_page_mode_setting, NULL, Icon_NOICON);
MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
- tv_autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
+ tv_autoscroll_speed_setting, NULL, Icon_NOICON);
MENUITEM_FUNCTION(narrow_mode_item, 0, "Left/Right Key (Narrow mode)",
- tv_narrow_mode_setting, NULL, NULL, Icon_NOICON);
+ tv_narrow_mode_setting, NULL, Icon_NOICON);
MAKE_MENU(vertical_scroll_menu, "Vertical", NULL, Icon_NOICON,
&vertical_scrollbar_item,
@@ -141,7 +138,7 @@ static bool tv_encoding_setting(void)
names[idx].voice_id = -1;
}
- return rb->set_option("Encoding", &new_prefs.encoding, INT, names,
+ return rb->set_option("Encoding", &new_prefs.encoding, RB_INT, names,
sizeof(names) / sizeof(names[0]), NULL);
}
@@ -152,7 +149,7 @@ static bool tv_word_wrap_setting(void)
{"Off (Chop Words)", -1},
};
- return rb->set_option("Word Wrap", &new_prefs.word_mode, INT,
+ return rb->set_option("Word Wrap", &new_prefs.word_mode, RB_INT,
names, 2, NULL);
}
@@ -165,7 +162,7 @@ static bool tv_line_mode_setting(void)
{"Reflow Lines", -1},
};
- return rb->set_option("Line Mode", &new_prefs.line_mode, INT, names,
+ return rb->set_option("Line Mode", &new_prefs.line_mode, RB_INT, names,
sizeof(names) / sizeof(names[0]), NULL);
}
@@ -182,7 +179,7 @@ static bool tv_alignment_setting(void)
{"Right", -1},
};
- return rb->set_option("Alignment", &new_prefs.alignment, INT,
+ return rb->set_option("Alignment", &new_prefs.alignment, RB_INT,
names , 2, NULL);
}
@@ -203,16 +200,19 @@ static bool tv_statusbar_setting(void)
static bool tv_font_setting(void)
{
- struct browse_context browse;
char font[MAX_PATH], name[MAX_FILENAME+10];
-
rb->snprintf(name, sizeof(name), "%s.fnt", new_prefs.font_name);
- rb->browse_context_init(&browse, SHOW_FONT,
- BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
- "Font", Icon_Menu_setting, FONT_DIR, name);
- browse.buf = font;
- browse.bufsize = sizeof(font);
+ struct browse_context browse = {
+ .dirfilter = SHOW_FONT,
+ .flags = BROWSE_SELECTONLY | BROWSE_NO_CONTEXT_MENU,
+ .title = "Font", /* XXX: Translate? */
+ .icon = Icon_Menu_setting,
+ .root = FONT_DIR,
+ .selected = name,
+ .buf = font,
+ .bufsize = sizeof(font),
+ };
rb->rockbox_browse(&browse);
@@ -240,29 +240,29 @@ static bool tv_night_mode_setting(void)
}
#endif
-MENUITEM_FUNCTION(encoding_item, 0, "Encoding", tv_encoding_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", tv_word_wrap_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", tv_line_mode_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(windows_item, 0, "Screens Per Page", tv_windows_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(alignment_item, 0, "Alignment", tv_alignment_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(header_item, 0, "Show Header", tv_header_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(footer_item, 0, "Show Footer", tv_footer_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(statusbar_item, 0, "Show Statusbar", tv_statusbar_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(font_item, 0, "Font", tv_font_setting,
- NULL, NULL, Icon_NOICON);
-MENUITEM_FUNCTION(indent_spaces_item, 0, "Indent Spaces", tv_indent_spaces_setting,
- NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(encoding_item, 0, "Encoding",
+ tv_encoding_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap",
+ tv_word_wrap_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode",
+ tv_line_mode_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(windows_item, 0, "Screens Per Page",
+ tv_windows_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(alignment_item, 0, "Alignment",
+ tv_alignment_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(header_item, 0, "Show Header",
+ tv_header_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(footer_item, 0, "Show Footer",
+ tv_footer_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(statusbar_item, 0, "Show Statusbar",
+ tv_statusbar_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(font_item, 0, "Font",
+ tv_font_setting, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(indent_spaces_item, 0, "Indent Spaces",
+ tv_indent_spaces_setting, NULL, Icon_NOICON);
#ifdef HAVE_LCD_COLOR
-MENUITEM_FUNCTION(night_mode_item, 0, "Night Mode", tv_night_mode_setting,
- NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(night_mode_item, 0, "Night Mode",
+ tv_night_mode_setting, NULL, Icon_NOICON);
#endif
MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
diff --git a/apps/plugins/theme_remove.c b/apps/plugins/theme_remove.c
index f39d224868..6f5353f3f6 100644
--- a/apps/plugins/theme_remove.c
+++ b/apps/plugins/theme_remove.c
@@ -589,7 +589,7 @@ static bool option_menu(void)
{
struct remove_setting *setting = &remove_list[result];
int prev_option = setting->option;
- if (rb->set_option(option_menu_[result], &setting->option, INT,
+ if (rb->set_option(option_menu_[result], &setting->option, RB_INT,
remove_names, NUM_REMOVE_OPTION, NULL))
return true;
if (prev_option != setting->option)
diff --git a/apps/plugins/vbrfix.c b/apps/plugins/vbrfix.c
index 88f0a6579e..79fa134499 100644
--- a/apps/plugins/vbrfix.c
+++ b/apps/plugins/vbrfix.c
@@ -150,7 +150,7 @@ static bool vbr_fix(const char *selected_file)
xingupdate(0);
rc = rb->mp3info(&entry, selected_file);
- if(rc < 0) {
+ if(rc) {
fileerror(rc);
return true;
}
@@ -258,6 +258,8 @@ static bool vbr_fix(const char *selected_file)
}
else
{
+ rb->close(fd);
+
/* Not a VBR file */
DEBUGF("Not a VBR file\n");
rb->splash(HZ*2, ID2P(LANG_NOT_A_VBR_FILE));
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 8aa0ac370a..45bbfeef0b 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -3,6 +3,10 @@ txt,viewers/text_viewer,1
txt,apps/text_editor,2
txt,viewers/sort,-
txt,viewers/speedread,1
+md,viewers/text_viewer,1
+md,apps/text_editor,2
+md,viewers/sort,-
+md,viewers/speedread,1
nfo,viewers/text_viewer,1
bmp,viewers/imageviewer,2
bmp,apps/rockpaint,11
@@ -105,4 +109,5 @@ shopper,viewers/shopper,1
lnk,viewers/windows_lnk,-
#ifdef HAVE_TAGCACHE
*,demos/pictureflow,-
-#endif \ No newline at end of file
+log,viewers/lastfm_scrobbler_viewer,4
+#endif
diff --git a/apps/plugins/vu_meter.c b/apps/plugins/vu_meter.c
index 356a7fdd93..0dafd05845 100644
--- a/apps/plugins/vu_meter.c
+++ b/apps/plugins/vu_meter.c
@@ -20,6 +20,8 @@
#include "plugin.h"
#include "fixedpoint.h"
#include "lib/playback_control.h"
+#include "lib/helper.h"
+#include "lib/pluginlib_exit.h"
@@ -666,7 +668,7 @@ static bool vu_meter_menu(void)
switch(rb->do_menu(&menu, &selection, NULL, false))
{
case 0:
- rb->set_option("Meter Type", &vumeter_settings.meter_type, INT,
+ rb->set_option("Meter Type", &vumeter_settings.meter_type, RB_INT,
meter_type_option, 2, NULL);
break;
@@ -699,12 +701,12 @@ static bool vu_meter_menu(void)
case 3:
if(vumeter_settings.meter_type==ANALOG)
{
- rb->set_option("Decay Speed", &vumeter_settings.analog_decay, INT,
+ rb->set_option("Decay Speed", &vumeter_settings.analog_decay, RB_INT,
decay_speed_option, 7, NULL);
}
else
{
- rb->set_option("Decay Speed", &vumeter_settings.digital_decay, INT,
+ rb->set_option("Decay Speed", &vumeter_settings.digital_decay, RB_INT,
decay_speed_option, 7, NULL);
}
break;
@@ -910,6 +912,12 @@ static void digital_meter(void) {
rb->lcd_hline(0,LCD_WIDTH-1,half_height+3);
}
+static void vu_meter_cleanup(void)
+{
+ /* Turn on backlight timeout (revert to settings) */
+ backlight_use_settings();
+}
+
enum plugin_status plugin_start(const void* parameter) {
int button;
#if defined(VUMETER_HELP_PRE) || defined(VUMETER_MENU_PRE)
@@ -920,12 +928,17 @@ enum plugin_status plugin_start(const void* parameter) {
calc_scales();
+ atexit(vu_meter_cleanup);
+
load_settings();
rb->lcd_setfont(FONT_SYSFIXED);
#ifdef HAVE_LCD_COLOR
screen_foreground = rb->lcd_get_foreground();
#endif
+ /* Turn off backlight timeout */
+ backlight_ignore_timeout();
+
while (1)
{
rb->lcd_clear_display();
diff --git a/apps/plugins/wormlet.c b/apps/plugins/wormlet.c
index 162cea6208..7dd814bc08 100644
--- a/apps/plugins/wormlet.c
+++ b/apps/plugins/wormlet.c
@@ -54,7 +54,7 @@ static long max_cycle;
#define BTN_DIR_LEFT BUTTON_LEFT
#define BTN_DIR_RIGHT BUTTON_RIGHT
#define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
-#define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)
+#define BTN_QUIT (BUTTON_SELECT|BUTTON_REPEAT)
#define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)
#elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
@@ -2428,10 +2428,9 @@ static bool launch_wormlet(void)
rb->lcd_clear_display();
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* start the game */
while (game_result == 1)
game_result = run();
@@ -2439,10 +2438,10 @@ static bool launch_wormlet(void)
switch (game_result)
{
case 2:
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return false;
break;
}
@@ -2563,24 +2562,24 @@ enum plugin_status plugin_start(const void* parameter)
case 3:
switch(players) {
case 0:
- rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote,INT,
+ rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT,
nokey_option, 1, NULL);
break;
case 1:
- rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote,INT,
+ rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT,
key24_option, 2, NULL);
break;
case 2:
#ifdef REMOTE
- rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote,INT,
+ rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT,
remote_option, 2, NULL);
#else
- rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote,INT,
+ rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT,
key2_option, 1, NULL);
#endif
break;
case 3:
- rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote,INT,
+ rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT,
remoteonly_option, 1, NULL);
break;
}
@@ -2609,7 +2608,7 @@ enum plugin_status plugin_start(const void* parameter)
break;
case 9:
new_setting = 0;
- rb->set_option(rb->str(LANG_RESET), &new_setting, INT, noyes , 2, NULL);
+ rb->set_option(rb->str(LANG_RESET), &new_setting, RB_INT, noyes , 2, NULL);
if (new_setting == 1)
default_settings();
break;
diff --git a/apps/plugins/xobox.c b/apps/plugins/xobox.c
index b8b1964db4..336d92bd4e 100644
--- a/apps/plugins/xobox.c
+++ b/apps/plugins/xobox.c
@@ -42,10 +42,10 @@
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define QUIT (BUTTON_SELECT | BUTTON_REPEAT)
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
-#define PAUSE BUTTON_SELECT
+#define PAUSE (BUTTON_SELECT | BUTTON_REL)
#define MENU_UP BUTTON_SCROLL_FWD
#define MENU_DOWN BUTTON_SCROLL_BACK
#define UP BUTTON_MENU
@@ -1297,10 +1297,9 @@ enum plugin_status plugin_start (const void *parameter)
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
highscore_load(SCORE_FILE, highscores, NUM_SCORES);
if (!load_game()) {
@@ -1310,10 +1309,10 @@ enum plugin_status plugin_start (const void *parameter)
randomize ();
ret = xobox_loop ();
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
rb->lcd_setfont (FONT_UI);
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
diff --git a/apps/plugins/xworld/engine.c b/apps/plugins/xworld/engine.c
index b09e320078..d99d9df6c6 100644
--- a/apps/plugins/xworld/engine.c
+++ b/apps/plugins/xworld/engine.c
@@ -293,7 +293,7 @@ void engine_processInput(struct Engine* e) {
e->sys->input.save = false;
}
if (e->sys->input.fastMode) {
- e->vm._fastMode = !&e->vm._fastMode;
+ e->vm._fastMode = !e->vm._fastMode;
e->sys->input.fastMode = false;
}
if (e->sys->input.stateSlot != 0) {
diff --git a/apps/plugins/xworld/sys.c b/apps/plugins/xworld/sys.c
index c57da9456b..4a80152b70 100644
--- a/apps/plugins/xworld/sys.c
+++ b/apps/plugins/xworld/sys.c
@@ -122,9 +122,8 @@ void exit_handler(void)
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
}
static bool sys_do_help(void)
@@ -207,7 +206,7 @@ static void do_video_settings(struct System* sys)
#else
case 2:
#endif
- rb->set_option("Scaling", &sys->settings.scaling_quality, INT, scaling_settings,
+ rb->set_option("Scaling", &sys->settings.scaling_quality, RB_INT, scaling_settings,
#ifdef HAVE_LCD_COLOR
3
#else
@@ -226,7 +225,7 @@ static void do_video_settings(struct System* sys)
#else
case 3:
#endif
- rb->set_option("Rotation", &sys->settings.rotation_option, INT, rotation_settings, 3, NULL);
+ rb->set_option("Rotation", &sys->settings.rotation_option, RB_INT, rotation_settings, 3, NULL);
if(sys->settings.rotation_option &&
sys->settings.zoom)
{
@@ -287,7 +286,7 @@ static void do_sound_settings(struct System* sys)
case 2:
{
const struct settings_list* vol =
- rb->find_setting(&rb->global_settings->volume, NULL);
+ rb->find_setting(&rb->global_settings->volume);
rb->option_screen((struct settings_list*)vol, NULL, false, "Volume");
break;
}
@@ -428,9 +427,8 @@ void sys_menu(struct System* sys)
void sys_init(struct System* sys, const char* title)
{
(void) title;
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
rb_atexit(exit_handler);
save_sys = sys;
rb->memset(&sys->input, 0, sizeof(sys->input));
@@ -496,7 +494,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
for (int i = 0; i < w / 2; ++i) {
uint8_t pix1 = *(buf + i) >> 4;
uint8_t pix2 = *(buf + i) & 0xF;
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
framebuffer[( (h * 2) ) * 320 + i] = sys->palette[pix1];
framebuffer[( (h * 2) + 1) * 320 + i] = sys->palette[pix2];
#else
@@ -515,7 +513,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
for (int i = 0; i < w / 2; ++i) {
uint8_t pix1 = *(buf + i) >> 4;
uint8_t pix2 = *(buf + i) & 0xF;
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
framebuffer[(200 - h * 2 ) * 320 + ( 320 - i )] = sys->palette[pix1];
framebuffer[(200 - h * 2 - 1) * 320 + ( 320 - i )] = sys->palette[pix2];
#else
@@ -531,7 +529,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
else
{
int next = 0;
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
for(int x = 0; x < w / 2; ++x)
{
for(int y = 0; y < h; ++y)
@@ -565,7 +563,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
struct bitmap in_bmp;
if(sys->settings.rotation_option)
{
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
in_bmp.width = 320;
in_bmp.height = 200;
#else
@@ -575,7 +573,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
}
else
{
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
in_bmp.width = 200;
in_bmp.height = 320;
#else
@@ -600,7 +598,7 @@ void sys_copyRect(struct System* sys, uint16_t x, uint16_t y, uint16_t w, uint16
}
else
{
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
for(int x = 0; x < 320; ++x)
{
for(int y = 0; y < 200; ++y)
diff --git a/apps/plugins/zxbox/spmain.c b/apps/plugins/zxbox/spmain.c
index 6c6b59c3fb..38cc175a85 100644
--- a/apps/plugins/zxbox/spmain.c
+++ b/apps/plugins/zxbox/spmain.c
@@ -257,21 +257,21 @@ static void options_menu(void){
{
case 0:
new_setting=settings.kempston;
- rb->set_option("Map Keys to kempston",&new_setting,INT,
+ rb->set_option("Map Keys to kempston",&new_setting, RB_INT,
no_yes, 2, NULL);
if (new_setting != settings.kempston )
settings.kempston=new_setting;
break;
case 1:
new_setting = settings.showfps;
- rb->set_option("Display Speed",&new_setting,INT,
+ rb->set_option("Display Speed",&new_setting, RB_INT,
no_yes, 2, NULL);
if (new_setting != settings.showfps )
settings.showfps=new_setting;
break;
case 2:
new_setting = settings.invert_colors;
- rb->set_option("Invert Colors",&new_setting,INT,
+ rb->set_option("Invert Colors",&new_setting, RB_INT,
no_yes, 2, NULL);
if (new_setting != settings.invert_colors )
settings.invert_colors=new_setting;
@@ -279,14 +279,14 @@ static void options_menu(void){
break;
case 3:
new_setting = settings.frameskip;
- rb->set_option("Frameskip",&new_setting,INT,
+ rb->set_option("Frameskip",&new_setting, RB_INT,
frameskip_items, 10, NULL);
if (new_setting != settings.frameskip )
settings.frameskip=new_setting;
break;
case 4:
new_setting = settings.sound;
- rb->set_option("Sound",&new_setting,INT,
+ rb->set_option("Sound",&new_setting, RB_INT,
no_yes, 2, NULL);
if (new_setting != settings.sound )
settings.sound=new_setting;
@@ -296,7 +296,7 @@ static void options_menu(void){
break;
case 5:
new_setting = 9 - settings.volume;
- rb->set_option("Volume",&new_setting,INT,
+ rb->set_option("Volume",&new_setting, RB_INT,
frameskip_items, 10, NULL);
new_setting = 9 - new_setting;
if (new_setting != settings.volume )