diff options
Diffstat (limited to 'apps')
390 files changed, 33104 insertions, 16975 deletions
diff --git a/apps/SOURCES b/apps/SOURCES index 1628524805..444951bbcb 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -7,6 +7,7 @@ alarm_menu.c #endif abrepeat.c bookmark.c +core_keymap.c debug_menu.c filetypes.c language.c @@ -57,7 +58,6 @@ tree.c tagtree.c #endif filetree.c -scrobbler.c #ifdef IPOD_ACCESSORY_PROTOCOL iap/iap-core.c iap/iap-lingo0.c diff --git a/apps/abrepeat.h b/apps/abrepeat.h index f7ee65247c..4d2c4ea001 100644 --- a/apps/abrepeat.h +++ b/apps/abrepeat.h @@ -20,9 +20,38 @@ ****************************************************************************/ #ifndef _ABREPEAT_H_ #define _ABREPEAT_H_ - -#ifdef AB_REPEAT_ENABLE #include <stdbool.h> + +#ifndef AB_REPEAT_ENABLE /* Dummy functions */ +static inline bool ab_repeat_mode_enabled(void) +{ + return false; +} +static inline bool ab_bool_dummy_marker(unsigned int song_position) +{ + (void) song_position; + return false; +} +static inline void ab_void_dummy_marker(unsigned int song_position) +{ + (void) song_position; +} +static inline void ab_dummy_voidfn(void){} + +#define ab_repeat_init ab_dummy_voidfn +#define ab_before_A_marker ab_bool_dummy_marker +#define ab_after_A_marker ab_bool_dummy_marker +#define ab_jump_to_A_marker ab_dummy_voidfn +#define ab_reset_markers ab_dummy_voidfn +#define ab_set_A_marker ab_void_dummy_marker +#define ab_set_B_marker ab_void_dummy_marker +#define ab_get_A_marker ab_bool_dummy_marker +#define ab_get_B_marker ab_bool_dummy_marker +#define ab_end_of_track_report ab_dummy_voidfn +#define ab_reached_B_marker ab_bool_dummy_marker +#define ab_position_report ab_void_dummy_marker + +#else /*def AB_REPEAT_ENABLE*/ #include "audio.h" #include "kernel.h" /* needed for HZ */ diff --git a/apps/action.c b/apps/action.c index b31c4fa927..208fea4a97 100644 --- a/apps/action.c +++ b/apps/action.c @@ -34,6 +34,7 @@ #include "button.h" #include "action.h" #include "kernel.h" +#include "core_alloc.h" #include "splash.h" #include "settings.h" @@ -69,6 +70,10 @@ static action_last_t action_last = .tick = 0, .wait_for_release = false, +#ifndef DISABLE_ACTION_REMAP + .key_remap = 0, +#endif + #ifdef HAVE_TOUCHSCREEN .ts_data = 0, .ts_short_press = false, @@ -120,7 +125,7 @@ static bool is_action_filtered(int action, unsigned int mask, int context) { case ACTION_NONE: break; -/*Actions that are not mapped will not turn on the backlight option NOUNMAPPED*/ + /* Actions that are not mapped will not turn on the backlight */ case ACTION_UNKNOWN: match = has_flag(mask, SEL_ACTION_NOUNMAPPED); break; @@ -128,15 +133,15 @@ static bool is_action_filtered(int action, unsigned int mask, int context) case ACTION_FM_PLAY: match = has_flag(mask, SEL_ACTION_PLAY); break; - //case ACTION_STD_PREVREPEAT: // seek not exempted outside of WPS - //case ACTION_STD_NEXTREPEAT: + /* case ACTION_STD_PREVREPEAT:*/ /* seek not exempted outside of WPS */ + /* case ACTION_STD_NEXTREPEAT: */ case ACTION_WPS_SEEKBACK: case ACTION_WPS_SEEKFWD: case ACTION_WPS_STOPSEEK: match = has_flag(mask, SEL_ACTION_SEEK); break; - //case ACTION_STD_PREV: // skip/scrollwheel not exempted outside of WPS - //case ACTION_STD_NEXT: + /* case ACTION_STD_PREV: */ /* skip/scrollwheel not */ + /* case ACTION_STD_NEXT: */ /* exempted outside of WPS */ case ACTION_WPS_SKIPNEXT: case ACTION_WPS_SKIPPREV: case ACTION_FM_NEXT_PRESET: @@ -144,8 +149,8 @@ static bool is_action_filtered(int action, unsigned int mask, int context) match = has_flag(mask, SEL_ACTION_SKIP); break; #ifdef HAVE_VOLUME_IN_LIST - case ACTION_LIST_VOLUP: // volume exempted outside of WPS if the device supports it - case ACTION_LIST_VOLDOWN: + case ACTION_LIST_VOLUP: /* volume exempted outside of WPS */ + case ACTION_LIST_VOLDOWN: /* ( if the device supports it )*/ #endif case ACTION_WPS_VOLUP: case ACTION_WPS_VOLDOWN: @@ -499,7 +504,7 @@ static inline int action_code_worker(action_last_t *last, int *end ) { int ret = ACTION_UNKNOWN; - int i = 0; + int i = *end; unsigned int found = 0; while (cur->items[i].button_code != BUTTON_NONE) { @@ -583,21 +588,52 @@ static inline int get_next_context(const struct button_mapping *items, int i) * for a more in-depth explanation * places action into current_action */ + static inline void action_code_lookup(action_last_t *last, action_cur_t *cur) { - int action = ACTION_NONE; + int action, i; int context = cur->context; - int i = 0; - cur->is_prebutton = false; -#ifdef HAVE_LOCKED_ACTIONS +#if !defined(HAS_BUTTON_HOLD) && !defined(BOOTLOADER) /* This only applies to the first context, to allow locked contexts to * specify a fall through to their non-locked version */ if (is_keys_locked()) context |= CONTEXT_LOCKED; #endif +#ifndef DISABLE_ACTION_REMAP + /* attempt to look up the button in user supplied remap */ + if(last->key_remap && (context & CONTEXT_PLUGIN) == 0) + { + if ((cur->button & BUTTON_REMOTE) != 0) + { + context |= CONTEXT_REMOTE; + } + cur->items = core_get_data(last->key_remap); + i = 0; + action = ACTION_UNKNOWN; + /* check the lut at the beginning for the desired context */ + while (cur->items[i].button_code != BUTTON_NONE) + { + if (cur->items[i].action_code == CORE_CONTEXT_REMAP(context)) + { + i = cur->items[i].button_code; + action = action_code_worker(last, cur, &i); + if (action != ACTION_UNKNOWN) + { + cur->action = action; + return; + } + } + i++; + } + } +#endif + + i = 0; + action = ACTION_NONE; + /* attempt to look up the button in the in-built keymaps */ for(;;) { /* logf("context = %x",context); */ @@ -609,9 +645,13 @@ static inline void action_code_lookup(action_last_t *last, action_cur_t *cur) #endif if ((context & CONTEXT_PLUGIN) && cur->get_context_map) + { cur->items = cur->get_context_map(context); + } else + { cur->items = get_context_mapping(context); + } if (cur->items != NULL) { @@ -960,7 +1000,8 @@ static inline int do_backlight(action_last_t *last, action_cur_t *cur, int actio && power_input_present()); #endif /* skip if backlight on | incorrect context | SEL_ACTION_NOEXT + ext pwr */ - if ((cur->context == CONTEXT_FM || cur->context == CONTEXT_WPS) && bl_is_off) + if (bl_is_off && (cur->context == CONTEXT_FM || cur->context == CONTEXT_WPS || + cur->context == CONTEXT_MAINMENU)) { filtered = is_action_filtered(action, last->backlight_mask, cur->context); bl_activate = !is_action_discarded(cur, filtered, &last->bl_filter_tick); @@ -1150,6 +1191,92 @@ int get_action(int context, int timeout) return action; } +int action_set_keymap(struct button_mapping* core_keymap, int count) +{ +#ifdef DISABLE_ACTION_REMAP + (void)core_keymap; + (void)count; + return -1; +#else + if (count <= 0 || core_keymap == NULL) + return action_set_keymap_handle(0, 0); + + size_t keyremap_buf_size = count * sizeof(struct button_mapping); + int handle = core_alloc(keyremap_buf_size); + if (handle < 0) + return -6; + + memcpy(core_get_data(handle), core_keymap, keyremap_buf_size); + return action_set_keymap_handle(handle, count); +#endif +} + +int action_set_keymap_handle(int handle, int count) +{ +#ifdef DISABLE_ACTION_REMAP + (void)core_keymap; + (void)count; + return -1; +#else + /* free an existing remap */ + action_last.key_remap = core_free(action_last.key_remap); + + /* if clearing the remap, we're done */ + if (count <= 0 || handle <= 0) + return 0; + + /* validate the keymap */ + struct button_mapping* core_keymap = core_get_data(handle); + struct button_mapping* entry = &core_keymap[count - 1]; + if (entry->action_code != (int) CONTEXT_STOPSEARCHING || + entry->button_code != BUTTON_NONE) /* check for sentinel at end*/ + { + /* missing sentinel entry */ + return -1; + } + + /* check the lut at the beginning for invalid offsets */ + for (int i = 0; i < count; ++i) + { + entry = &core_keymap[i]; + if (entry->action_code == (int)CONTEXT_STOPSEARCHING) + break; + + if ((entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED) + { + int firstbtn = entry->button_code; + int endpos = firstbtn + entry->pre_button_code; + if (firstbtn > count || firstbtn < i || endpos > count) + { + /* offset out of bounds */ + return -2; + } + + if (core_keymap[endpos].button_code != BUTTON_NONE) + { + /* stop sentinel is not at end of action lut */ + return -3; + } + } + else + { + /* something other than a context remap in the lut */ + return -4; + } + + if (i+1 >= count) + { + /* no sentinel in the lut */ + return -5; + } + } + + /* success */ + action_last.key_remap = handle; + return count; +#endif +} + int get_custom_action(int context,int timeout, const struct button_mapping* (*get_context_map)(int)) { diff --git a/apps/action.h b/apps/action.h index ad91f31535..6e1278b33c 100644 --- a/apps/action.h +++ b/apps/action.h @@ -28,13 +28,15 @@ #define TIMEOUT_NOBLOCK 0 #define CONTEXT_STOPSEARCHING 0xFFFFFFFF + #define CONTEXT_REMOTE 0x80000000 /* | this against another context to get remote buttons for that context */ #define CONTEXT_CUSTOM 0x40000000 /* | this against anything to get your context number */ #define CONTEXT_CUSTOM2 0x20000000 /* as above */ #define CONTEXT_PLUGIN 0x10000000 /* for plugins using get_custom_action */ -#ifdef HAVE_LOCKED_ACTIONS +#define CONTEXT_REMAPPED 0x08000000 /* marker for key remap context table */ +#define CORE_CONTEXT_REMAP(context) (CONTEXT_REMAPPED | context) #define CONTEXT_LOCKED 0x04000000 /* flag to use alternate keymap when screen is locked */ -#endif + #define LAST_ITEM_IN_LIST { CONTEXT_STOPSEARCHING, BUTTON_NONE, BUTTON_NONE } #define LAST_ITEM_IN_LIST__NEXTLIST(a) { a, BUTTON_NONE, BUTTON_NONE } @@ -92,7 +94,7 @@ void set_selective_backlight_actions(bool selective, unsigned int mask, enum { CONTEXT_STD = 0, /* These CONTEXT_ values were here before me, - there values may have significance, so dont touch! */ + their values may have significance, so dont touch! */ CONTEXT_WPS = 1, CONTEXT_TREE = 2, CONTEXT_RECORD = 3, @@ -129,6 +131,7 @@ enum { CONTEXT_USB_HID_MODE_PRESENTATION, CONTEXT_USB_HID_MODE_BROWSER, CONTEXT_USB_HID_MODE_MOUSE, + LAST_CONTEXT_PLACEHOLDER, }; @@ -244,7 +247,6 @@ enum { ACTION_SETTINGS_DEC, ACTION_SETTINGS_DECREPEAT, ACTION_SETTINGS_DECBIGSTEP, - ACTION_SETTINGS_RESET, ACTION_SETTINGS_SET, /* Used by touchscreen targets */ /* bookmark screen */ @@ -415,6 +417,10 @@ typedef struct bool repeated; bool wait_for_release; +#ifndef DISABLE_ACTION_REMAP + int key_remap; +#endif + #ifdef HAVE_TOUCHSCREEN bool ts_short_press; int ts_data; @@ -441,6 +447,11 @@ bool action_userabort(int timeout); /* no other code should need this apart from action.c */ const struct button_mapping* get_context_mapping(int context); +/* load a key map to allow buttons for actions to be remapped see: core_keymap */ +int action_set_keymap(struct button_mapping* core_keymap, int count); +/* load keymap in a handle: takes ownership of the handle on success */ +int action_set_keymap_handle(int handle, int count); + /* returns the status code variable from action.c for the button just pressed If button != NULL it will be set to the actual button code */ #define ACTION_REMOTE 0x1 /* remote was pressed */ diff --git a/apps/alarm_menu.c b/apps/alarm_menu.c index 62b54a84bb..67f8d1e8dd 100644 --- a/apps/alarm_menu.c +++ b/apps/alarm_menu.c @@ -38,161 +38,56 @@ #include "splash.h" #include "viewport.h" -static void speak_time(int hours, int minutes, bool speak_hours, bool enqueue) -{ - if (global_settings.talk_menu){ - if(speak_hours) { - talk_value(hours, UNIT_HOUR, enqueue); - talk_value(minutes, UNIT_MIN, true); - } else { - talk_value(minutes, UNIT_MIN, enqueue); - } - } -} - int alarm_screen(void) { - int h, m; - bool done = false; - struct tm *tm; - int togo; - int button; - bool update = true; - bool hour_wrapped = false; - struct viewport vp[NB_SCREENS]; - struct viewport * last_vp; - - rtc_get_alarm(&h, &m); + bool usb, update; + struct tm *now = get_time(); + struct tm atm; + memcpy(&atm, now, sizeof(struct tm)); + rtc_get_alarm(&atm.tm_hour, &atm.tm_min); /* After a battery change the RTC values are out of range */ - if (m > 60 || h > 24) { - m = 0; - h = 12; - } else { - m = m / 5 * 5; /* 5 min accuracy should be enough */ - } - FOR_NB_SCREENS(i) - { - viewport_set_defaults(&vp[i], i); - } + if (!valid_time(&atm)) + memcpy(&atm, now, sizeof(struct tm)); + atm.tm_sec = 0; - while(!done) { - if(update) - { - FOR_NB_SCREENS(i) - { - screens[i].set_viewport(&vp[i]); - screens[i].clear_viewport(); - screens[i].puts(0, 4, str(LANG_ALARM_MOD_KEYS)); - } - /* Talk when entering the wakeup screen */ - speak_time(h, m, true, true); - update = false; - } + usb = set_time_screen(str(LANG_ALARM_MOD_TIME), &atm, false); + update = valid_time(&atm); /* set_time returns invalid time if canceled */ - FOR_NB_SCREENS(i) - { - last_vp = screens[i].set_viewport(&vp[i]); - screens[i].putsf(0, 1, str(LANG_ALARM_MOD_TIME)); - screens[i].putsf(0, 2, "%02d:%02d", h, m); - screens[i].update_viewport(); - screens[i].set_viewport(last_vp); - } - button = get_action(CONTEXT_SETTINGS,HZ); + if (!usb && update) + { - switch(button) { - case ACTION_STD_OK: + now = get_time(); + int nmins = now->tm_min + (now->tm_hour * 60); + int amins = atm.tm_min + (atm.tm_hour * 60); + int mins_togo = (amins - nmins + 1440) % 1440; /* prevent that an alarm occurs in the shutdown procedure */ /* accept alarms only if they are in 2 minutes or more */ - tm = get_time(); - togo = (m + h * 60 - tm->tm_min - tm->tm_hour * 60 + 1440) % 1440; - - if (togo > 1) { + if (mins_togo > 1) { rtc_init(); - rtc_set_alarm(h,m); + rtc_set_alarm(atm.tm_hour,atm.tm_min); rtc_enable_alarm(true); if (global_settings.talk_menu) { talk_id(LANG_ALARM_MOD_TIME_TO_GO, true); - talk_value(togo / 60, UNIT_HOUR, true); - talk_value(togo % 60, UNIT_MIN, true); + talk_value(mins_togo / 60, UNIT_HOUR, true); + talk_value(mins_togo % 60, UNIT_MIN, true); talk_force_enqueue_next(); } splashf(HZ*2, str(LANG_ALARM_MOD_TIME_TO_GO), - togo / 60, togo % 60); - done = true; + mins_togo / 60, mins_togo % 60); } else { splash(HZ, ID2P(LANG_ALARM_MOD_ERROR)); - update = true; - } - break; - - /* inc(m) */ - case ACTION_SETTINGS_INC: - case ACTION_SETTINGS_INCREPEAT: - m += 5; - if (m == 60) { - h += 1; - m = 0; - hour_wrapped = true; + update = false; } - if (h == 24) - h = 0; - - speak_time(h, m, hour_wrapped, false); - break; - - /* dec(m) */ - case ACTION_SETTINGS_DEC: - case ACTION_SETTINGS_DECREPEAT: - m -= 5; - if (m == -5) { - h -= 1; - m = 55; - hour_wrapped = true; - } - if (h == -1) - h = 23; - - speak_time(h, m, hour_wrapped, false); - break; - - /* inc(h) */ - case ACTION_STD_NEXT: - case ACTION_STD_NEXTREPEAT: - h = (h+1) % 24; - - if (global_settings.talk_menu) - talk_value(h, UNIT_HOUR, false); - break; - - /* dec(h) */ - case ACTION_STD_PREV: - case ACTION_STD_PREVREPEAT: - h = (h+23) % 24; - - if (global_settings.talk_menu) - talk_value(h, UNIT_HOUR, false); - break; + } - case ACTION_STD_CANCEL: - rtc_enable_alarm(false); + if (usb || !update) + { + if (!usb) splash(HZ*2, ID2P(LANG_ALARM_MOD_DISABLE)); - done = true; - break; - - case ACTION_NONE: - hour_wrapped = false; - break; - - default: - if(default_event_handler(button) == SYS_USB_CONNECTED) - { - rtc_enable_alarm(false); - return 1; - } - break; - } + rtc_enable_alarm(false); + return 1; } return 0; } diff --git a/apps/apps.make b/apps/apps.make index 6afcd12b5c..47b015bc92 100644 --- a/apps/apps.make +++ b/apps/apps.make @@ -24,7 +24,7 @@ $(BUILDDIR)/apps/features: $(APPSDIR)/features.txt $(BUILDDIR)/firmware/common/ $(call PRINTS,PP $(<F)) $(SILENT)$(CC) $(PPCFLAGS) \ -E -P -imacros "config.h" -imacros "button.h" -x c $< | \ - grep -v "^\#" | grep -v "^ *$$" > $(BUILDDIR)/apps/features; \ + grep -v "^#" | grep -v "^ *$$" > $(BUILDDIR)/apps/features; \ $(BUILDDIR)/apps/genlang-features: $(BUILDDIR)/apps/features $(call PRINTS,GEN $(subst $(BUILDDIR)/,,$@))tr \\n : < $< > $@ diff --git a/apps/bookmark.c b/apps/bookmark.c index 70dbd8075d..68c10b36e7 100644 --- a/apps/bookmark.c +++ b/apps/bookmark.c @@ -43,6 +43,9 @@ #include "file.h" #include "pathfuncs.h" +/*#define LOGF_ENABLE*/ +#include "logf.h" + #define MAX_BOOKMARKS 10 #define MAX_BOOKMARK_SIZE 350 #define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark" @@ -66,286 +69,289 @@ struct bookmark_list #define BM_SPEED 0x02 /* bookmark values */ -static struct { +struct resume_info{ + const struct mp3entry *id3; int resume_index; unsigned long resume_offset; int resume_seed; - long resume_time; + long resume_elapsed; int repeat_mode; bool shuffle; /* optional values */ int pitch; int speed; -} bm; - -static bool add_bookmark(const char* bookmark_file_name, const char* bookmark, - bool most_recent); -static char* create_bookmark(void); -static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id); -static void say_bookmark(const char* bookmark, - int bookmark_id, bool show_playlist_name); -static bool play_bookmark(const char* bookmark); -static bool generate_bookmark_file_name(const char *in); -static bool parse_bookmark(const char *bookmark, const bool get_filenames, const bool strip_dir); -static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line); -static const char* get_bookmark_info(int list_index, - void* data, - char *buffer, - size_t buffer_len); -static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume, char** selected_bookmark); -static bool write_bookmark(bool create_bookmark_file, const char *bookmark); -static int get_bookmark_count(const char* bookmark_file_name); +}; -#define TEMP_BUF_SIZE (MAX_PATH + 1) +/* Temp buffer used for reading, create_bookmark and filename creation */ +#define TEMP_BUF_SIZE (MAX(MAX_BOOKMARK_SIZE, MAX_PATH + 1)) static char global_temp_buffer[TEMP_BUF_SIZE]; -/* File name created by generate_bookmark_file_name */ -static char global_bookmark_file_name[MAX_PATH]; -static char global_read_buffer[MAX_BOOKMARK_SIZE]; -/* Bookmark created by create_bookmark*/ -static char global_bookmark[MAX_BOOKMARK_SIZE]; -/* Filename from parsed bookmark (can be made local where needed) */ -static char global_filename[MAX_PATH]; -/* ----------------------------------------------------------------------- */ -/* This is an interface function from the context menu. */ -/* Returns true on successful bookmark creation. */ -/* ----------------------------------------------------------------------- */ -bool bookmark_create_menu(void) +static inline void get_hash(const char *key, uint32_t *hash, int len) { - return write_bookmark(true, create_bookmark()); + *hash = crc_32(key, len, *hash); /* this is probably sufficient */ } -/* ----------------------------------------------------------------------- */ -/* This function acts as the load interface from the context menu. */ -/* This function determines the bookmark file name and then loads that file*/ -/* for the user. The user can then select or delete previous bookmarks. */ -/* This function returns BOOKMARK_SUCCESS on the selection of a track to */ -/* resume, BOOKMARK_FAIL if the menu is exited without a selection and */ -/* BOOKMARK_USB_CONNECTED if the menu is forced to exit due to a USB */ -/* connection. */ -/* ----------------------------------------------------------------------- */ -int bookmark_load_menu(void) +static const char* skip_tokens(const char* s, int ntokens) { - char* bookmark; - int ret = BOOKMARK_FAIL; - - push_current_activity(ACTIVITY_BOOKMARKSLIST); - - char* name = playlist_get_name(NULL, global_temp_buffer, - sizeof(global_temp_buffer)); - if (generate_bookmark_file_name(name)) + for (int i = 0; i < ntokens; i++) { - ret = select_bookmark(global_bookmark_file_name, false, &bookmark); - if (bookmark != NULL) + while (*s && *s != ';') { - ret = play_bookmark(bookmark) ? BOOKMARK_SUCCESS : BOOKMARK_FAIL; + s++; + } + + if (*s) + { + s++; } } + return s; +} - pop_current_activity(); +static int int_token(const char **s) +{ + int ret = atoi(*s); + *s = skip_tokens(*s, 1); return ret; } -/* ----------------------------------------------------------------------- */ -/* Gives the user a list of the Most Recent Bookmarks. This is an */ -/* interface function */ -/* Returns true on the successful selection of a recent bookmark. */ -/* ----------------------------------------------------------------------- */ -bool bookmark_mrb_load() +static long long_token(const char **s) { - char* bookmark; - bool ret = false; + /* Should be atol, but we don't have it. */ + return int_token(s); +} - push_current_activity(ACTIVITY_BOOKMARKSLIST); - select_bookmark(RECENT_BOOKMARK_FILE, false, &bookmark); - if (bookmark != NULL) +/*-------------------------------------------------------------------------*/ +/* Get the name of the playlist and the name of the track from a bookmark. */ +/* Returns true iff both were extracted. */ +/*-------------------------------------------------------------------------*/ +static bool bookmark_get_playlist_and_track_hash(const char *bookmark, + uint32_t *pl_hash, + uint32_t *track_hash) +{ + *pl_hash = 0; + *track_hash = 0; + int pl_len; + const char *pl_start, *pl_end, *track; + + logf("%s", __func__); + + pl_start = strchr(bookmark,'/'); + if (!(pl_start)) + return false; + + pl_end = skip_tokens(pl_start, 1) - 1; + pl_len = pl_end - pl_start; + + track = pl_end + 1; + get_hash(pl_start, pl_hash, pl_len); + + if (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK) { - ret = play_bookmark(bookmark); + get_hash(track, track_hash, strlen(track)); } - pop_current_activity(); - return ret; + + return true; } /* ----------------------------------------------------------------------- */ -/* This function handles an autobookmark creation. This is an interface */ -/* function. */ -/* Returns true on successful bookmark creation. */ +/* This function takes a bookmark and parses it. This function also */ +/* validates the bookmark. Valid filenamebuf indicates whether */ +/* the filename tokens are to be extracted. */ +/* Returns true on successful bookmark parse. */ /* ----------------------------------------------------------------------- */ -bool bookmark_autobookmark(bool prompt_ok) +static bool parse_bookmark(char *filenamebuf, + size_t filenamebufsz, + const char *bookmark, + struct resume_info *resume_info, + const bool strip_dir) { - char* bookmark; - bool update; - - if (!bookmark_is_bookmarkable_state()) - return false; + const char* s = bookmark; + const char* end; - audio_pause(); /* first pause playback */ - update = (global_settings.autoupdatebookmark && bookmark_exists()); - bookmark = create_bookmark(); +#define GET_INT_TOKEN(var) var = int_token(&s) +#define GET_LONG_TOKEN(var) var = long_token(&s) +#define GET_BOOL_TOKEN(var) var = (int_token(&s) != 0) - if (update) - return write_bookmark(true, bookmark); + /* if new format bookmark, extract the optional content flags, + otherwise treat as an original format bookmark */ + int opt_flags = 0; + int opt_pitch = 0; + int opt_speed = 0; + int old_format = ((strchr(s, '>') == s) ? 0 : 1); + if (old_format == 0) /* this is a new format bookmark */ + { + s++; + GET_INT_TOKEN(opt_flags); + opt_pitch = (opt_flags & BM_PITCH) ? 1:0; + opt_speed = (opt_flags & BM_SPEED) ? 1:0; + } - switch (global_settings.autocreatebookmark) + /* extract all original bookmark tokens */ + if (resume_info) { - case BOOKMARK_YES: - return write_bookmark(true, bookmark); + GET_INT_TOKEN(resume_info->resume_index); + GET_LONG_TOKEN(resume_info->resume_offset); + GET_INT_TOKEN(resume_info->resume_seed); - case BOOKMARK_NO: - return false; + s = skip_tokens(s, old_format); /* skip deprecated token */ - case BOOKMARK_RECENT_ONLY_YES: - return write_bookmark(false, bookmark); - } - const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)}; - const struct text_message message={lines, 1}; + GET_LONG_TOKEN(resume_info->resume_elapsed); + GET_INT_TOKEN(resume_info->repeat_mode); + GET_BOOL_TOKEN(resume_info->shuffle); - if(prompt_ok && gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES) + /* extract all optional bookmark tokens */ + if (opt_pitch != 0) + GET_INT_TOKEN(resume_info->pitch); + if (opt_speed != 0) + GET_INT_TOKEN(resume_info->speed); + } + else /* no resume info we just want the file name strings */ { - if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK) - return write_bookmark(false, bookmark); - else - return write_bookmark(true, bookmark); + #define DEFAULT_BM_TOKENS 6 + int skipct = DEFAULT_BM_TOKENS + old_format + opt_pitch + opt_speed; + s = skip_tokens(s, skipct); + #undef DEFAULT_BM_TOKENS } - return false; -} -/* ----------------------------------------------------------------------- */ -/* This function takes the current current resume information and writes */ -/* that to the beginning of the bookmark file. */ -/* This file will contain N number of bookmarks in the following format: */ -/* resume_index*resume_offset*resume_seed*resume_first_index* */ -/* resume_file*milliseconds*MP3 Title* */ -/* Returns true on successful bookmark write. */ -/* Returns false if any part of the bookmarking process fails. It is */ -/* possible that a bookmark is successfully added to the most recent */ -/* bookmark list but fails to be added to the bookmark file or vice versa. */ -/* ------------------------------------------------------------------------*/ -static bool write_bookmark(bool create_bookmark_file, const char *bookmark) -{ - bool ret=true; - - if (!bookmark) + if (*s == 0) { - ret = false; /* something didn't happen correctly, do nothing */ + return false; } - else - { - if (global_settings.usemrb) - ret = add_bookmark(RECENT_BOOKMARK_FILE, bookmark, true); + end = strchr(s, ';'); - /* writing the bookmark */ - if (create_bookmark_file) + /* extract file names */ + if(filenamebuf) + { + size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s); + len = MIN(TEMP_BUF_SIZE - 1, len); + strmemccpy(global_temp_buffer, s, len + 1); + + if (end != NULL) { - char* name = playlist_get_name(NULL, global_temp_buffer, - sizeof(global_temp_buffer)); - if (generate_bookmark_file_name(name)) - { - ret = ret & add_bookmark(global_bookmark_file_name, bookmark, false); - } - else + end++; + if (strip_dir) { - ret = false; /* generating bookmark file failed */ + s = strrchr(end, '/'); + if (s) + { + end = s; + end++; + } } + strmemccpy(filenamebuf, end, filenamebufsz); } - } - - splash(HZ, ret ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS) - : ID2P(LANG_BOOKMARK_CREATE_FAILURE)); + } - return ret; + return true; } -/* Get the name of the playlist and the name of the track from a bookmark. */ -/* Returns true iff both were extracted. */ -static bool get_playlist_and_track(const char *bookmark, char **pl_start, - char **pl_end, char **track) +/* ------------------------------------------------------------------------- */ +/* This function takes a filename and appends .tmp. This function also opens */ +/* the resulting file based on oflags, filename will be in buf on return */ +/* Returns file descriptor */ +/* --------------------------------------------------------------------------*/ +static int open_temp_bookmark(char *buf, + size_t bufsz, + int oflags, + const char* filename) { - *pl_start = strchr(bookmark,'/'); - if (!(*pl_start)) - return false; - *pl_end = strrchr(bookmark,';'); - *track = *pl_end + 1; - return true; + if(filename[0] == '/') + filename++; + /* Opening up a temp bookmark file */ + int fd = open_pathfmt(buf, bufsz, oflags, "/%s.tmp", filename); +#ifdef LOGF_ENABLE + if (oflags & O_PATH) + logf("tempfile path %s", buf); + else + logf("opening tempfile %s", buf); +#endif + return fd; } /* ----------------------------------------------------------------------- */ /* This function adds a bookmark to a file. */ /* Returns true on successful bookmark add. */ /* ------------------------------------------------------------------------*/ -static bool add_bookmark(const char* bookmark_file_name, const char* bookmark, +static bool add_bookmark(const char* bookmark_file_name, + const char* bookmark, bool most_recent) { - int temp_bookmark_file = 0; - int bookmark_file = 0; - int bookmark_count = 0; - char *pl_start = NULL, *bm_pl_start; - char *pl_end = NULL, *bm_pl_end; - int pl_len = 0, bm_pl_len; - char *track = NULL, *bm_track; - bool comp_playlist = false; - bool comp_track = false; - bool equal; + char fnamebuf[MAX_PATH]; + int temp_bookmark_file = 0; + int bookmark_file = 0; + int bookmark_count = 0; + bool comp_playlist = false; + bool comp_track = false; + bool equal; + uint32_t pl_hash, pl_track_hash; + uint32_t bm_pl_hash, bm_pl_track_hash; /* Opening up a temp bookmark file */ - snprintf(global_temp_buffer, sizeof(global_temp_buffer), - "%s.tmp", bookmark_file_name); - temp_bookmark_file = open(global_temp_buffer, - O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (temp_bookmark_file < 0) - return false; /* can't open the temp file */ + temp_bookmark_file = open_temp_bookmark(fnamebuf, + sizeof(fnamebuf), + O_WRONLY | O_CREAT | O_TRUNC, + bookmark_file_name); + + if (temp_bookmark_file < 0 || !bookmark) + return false; /* can't open the temp file or no bookmark */ if (most_recent && ((global_settings.usemrb == BOOKMARK_ONE_PER_PLAYLIST) || (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK))) { - if (get_playlist_and_track(bookmark, &pl_start, &pl_end, &track)) + + if (bookmark_get_playlist_and_track_hash(bookmark, &pl_hash, &pl_track_hash)) { comp_playlist = true; - pl_len = pl_end - pl_start; - if (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK) - comp_track = true; + comp_track = (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK); } } + logf("adding bookmark to %s [%s]", fnamebuf, bookmark); /* Writing the new bookmark to the begining of the temp file */ write(temp_bookmark_file, bookmark, strlen(bookmark)); write(temp_bookmark_file, "\n", 1); bookmark_count++; + /* WARNING underlying buffer to *bookmrk gets overwritten after this point! */ + /* Reading in the previous bookmarks and writing them to the temp file */ + logf("opening old bookmark %s", bookmark_file_name); bookmark_file = open(bookmark_file_name, O_RDONLY); if (bookmark_file >= 0) { - while (read_line(bookmark_file, global_read_buffer, - sizeof(global_read_buffer)) > 0) + while (read_line(bookmark_file, global_temp_buffer, + sizeof(global_temp_buffer)) > 0) { /* The MRB has a max of MAX_BOOKMARKS in it */ /* This keeps it from getting too large */ if (most_recent && (bookmark_count >= MAX_BOOKMARKS)) break; - if (!parse_bookmark(global_read_buffer, false, false)) + if (!parse_bookmark(NULL, 0, global_temp_buffer, NULL, false)) break; equal = false; if (comp_playlist) { - if (get_playlist_and_track(global_read_buffer, &bm_pl_start, - &bm_pl_end, &bm_track)) + if (bookmark_get_playlist_and_track_hash(global_temp_buffer, + &bm_pl_hash, &bm_pl_track_hash)) { - bm_pl_len = bm_pl_end - bm_pl_start; - equal = (pl_len == bm_pl_len) && !strncmp(pl_start, bm_pl_start, pl_len); + equal = (pl_hash == bm_pl_hash); if (equal && comp_track) - equal = !strcmp(track, bm_track); + { + equal = (pl_track_hash == bm_pl_track_hash); + } } } if (!equal) { bookmark_count++; - write(temp_bookmark_file, global_read_buffer, - strlen(global_read_buffer)); + /*logf("copying old bookmark [%s]", global_temp_buffer);*/ + write(temp_bookmark_file, global_temp_buffer, + strlen(global_temp_buffer)); write(temp_bookmark_file, "\n", 1); } } @@ -354,180 +360,233 @@ static bool add_bookmark(const char* bookmark_file_name, const char* bookmark, close(temp_bookmark_file); remove(bookmark_file_name); - rename(global_temp_buffer, bookmark_file_name); + rename(fnamebuf, bookmark_file_name); return true; } +/* ----------------------------------------------------------------------- */ +/* This function is used by multiple functions and is used to generate a */ +/* bookmark named based off of the input. */ +/* Changing this function could result in how the bookmarks are stored. */ +/* it would be here that the centralized/decentralized bookmark code */ +/* could be placed. */ +/* Returns true if the file name is generated, false if it was too long */ +/* ----------------------------------------------------------------------- */ +static bool generate_bookmark_file_name(char *filenamebuf, + size_t filenamebufsz, + const char *bmarknamein, + size_t bmarknamelen) +{ + /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */ + /* otherwise, name it based on the bmarknamein variable */ + if (!strcmp("/", bmarknamein)) + strmemccpy(filenamebuf, "/root_dir.bmark", filenamebufsz); + else + { + size_t buflen, len; + /* strmemccpy considers the NULL so bmarknamelen is one off */ + buflen = MIN(filenamebufsz -1 , bmarknamelen); + if (buflen >= filenamebufsz) + return false; + + strmemccpy(filenamebuf, bmarknamein, buflen + 1); + + len = strlen(filenamebuf); + +#ifdef HAVE_MULTIVOLUME + /* The "root" of an extra volume need special handling too. */ + const char *filename; + path_strip_volume(filenamebuf, &filename, true); + bool volume_root = *filename == '\0'; +#endif + if(filenamebuf[len-1] == '/') { + filenamebuf[len-1] = '\0'; + } + + const char *name = ".bmark"; +#ifdef HAVE_MULTIVOLUME + if (volume_root) + name = "/volume_dir.bmark"; +#endif + len = strlcat(filenamebuf, name, filenamebufsz); + + if(len >= filenamebufsz) + return false; + } + logf ("generated name '%s' from '%.*s'", + filenamebuf, (int)bmarknamelen, bmarknamein); + return true; +} + /* GCC 7 and up complain about the snprintf in create_bookmark() when compiled with -D_FORTIFY_SOURCE or -Wformat-truncation This is a false positive, so disable it here only */ -#if __GNUC__ >= 7 +/* SHOULD NO LONGER BE NEEDED --Bilgus 11-2022 */ +#if 0 /* __GNUC__ >= 7 */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-truncation" #endif /* ----------------------------------------------------------------------- */ /* This function takes the system resume data and formats it into a valid */ /* bookmark. */ -/* Returns not NULL on successful bookmark format. */ +/* playlist name and name len are passed back through the name/namelen */ +/* Return is not NULL on successful bookmark format. */ /* ----------------------------------------------------------------------- */ -static char* create_bookmark() +static char* create_bookmark(char **name, + size_t *namelen, + struct resume_info *resume_info) { - int resume_index = 0; - char *file; - - if (!bookmark_is_bookmarkable_state()) - return NULL; /* something didn't happen correctly, do nothing */ + const char *file; + char *buf = global_temp_buffer; + size_t bufsz = sizeof(global_temp_buffer); - /* grab the currently playing track */ - struct mp3entry *id3 = audio_current_track(); - if(!id3) - return NULL; - - /* Get some basic resume information */ - /* queue_resume and queue_resume_index are not used and can be ignored.*/ - playlist_get_resume_info(&resume_index); - - /* Get the currently playing file minus the path */ - /* This is used when displaying the available bookmarks */ - file = strrchr(id3->path,'/'); - if(NULL == file) + if(!resume_info->id3) return NULL; - /* create the bookmark */ - playlist_get_name(NULL, global_temp_buffer, sizeof(global_temp_buffer)); - if (global_temp_buffer[strlen(global_temp_buffer) - 1] != '/') - file = id3->path; - else file++; - snprintf(global_bookmark, sizeof(global_bookmark), - /* new optional bookmark token descriptors should be inserted - just before the "%s;%s" in this line... */ + size_t bmarksz= snprintf(buf, bufsz, + /* new optional bookmark token descriptors should + be inserted just after ';"' in this line... */ #if defined(HAVE_PITCHCONTROL) - ">%d;%d;%ld;%d;%ld;%d;%d;%ld;%ld;%s;%s", + ">%d;%d;%ld;%d;%ld;%d;%d;%ld;%ld;", #else - ">%d;%d;%ld;%d;%ld;%d;%d;%s;%s", + ">%d;%d;%ld;%d;%ld;%d;%d;", #endif - /* ... their flags should go here ... */ + /* ... their flags should go here ... */ #if defined(HAVE_PITCHCONTROL) - BM_PITCH | BM_SPEED, + BM_PITCH | BM_SPEED, #else - 0, + 0, #endif - resume_index, - id3->offset, - playlist_get_seed(NULL), - id3->elapsed, - global_settings.repeat_mode, - global_settings.playlist_shuffle, - /* ...and their values should go here */ + resume_info->resume_index, + resume_info->id3->offset, + resume_info->resume_seed, + resume_info->id3->elapsed, + resume_info->repeat_mode, + resume_info->shuffle, + /* ...and their values should go here */ #if defined(HAVE_PITCHCONTROL) - (long)sound_get_pitch(), - (long)dsp_get_timestretch(), + (long)resume_info->pitch, + (long)resume_info->speed #endif - /* more mandatory tokens */ - global_temp_buffer, - file); + ); /*sprintf*/ +/* mandatory tokens */ + if (bmarksz >= bufsz) /* include NULL*/ + return NULL; + buf += bmarksz; + bufsz -= bmarksz; + + /* create the bookmark */ + playlist_get_name(NULL, buf, bufsz); + bmarksz = strlen(buf); + + if (bmarksz == 0 || (bmarksz + 1) >= bufsz) /* include the separator & NULL*/ + return NULL; + + *name = buf; /* return the playlist name through the *pointer */ + *namelen = bmarksz; /* return the name length through the pointer */ + + /* Get the currently playing file minus the path */ + /* This is used when displaying the available bookmarks */ + file = strrchr(resume_info->id3->path,'/'); + if(NULL == file) + return NULL; + + if (buf[bmarksz - 1] != '/') + file = resume_info->id3->path; + else file++; + + buf += bmarksz; + bufsz -= (bmarksz + 1); + buf[0] = ';'; + buf[1] = '\0'; + + strlcat(buf, file, bufsz); + logf("%s [%s]", __func__, global_temp_buffer); /* checking to see if the bookmark is valid */ - if (parse_bookmark(global_bookmark, false, false)) - return global_bookmark; + if (parse_bookmark(NULL, 0, global_temp_buffer, NULL, false)) + return global_temp_buffer; else return NULL; } -#if __GNUC__ >= 7 +#if 0/* __GNUC__ >= 7*/ #pragma GCC diagnostic pop /* -Wformat-truncation */ #endif /* ----------------------------------------------------------------------- */ -/* This function will determine if an autoload is necessary. This is an */ -/* interface function. */ -/* Returns true on bookmark load or bookmark selection. */ -/* ------------------------------------------------------------------------*/ -bool bookmark_autoload(const char* file) +/* This function gets some basic resume information for the current song */ +/* from rockbox, */ +/* ----------------------------------------------------------------------- */ +static void get_track_resume_info(struct resume_info *resume_info) { - char* bookmark; - - if(global_settings.autoloadbookmark == BOOKMARK_NO) - return false; - - /*Checking to see if a bookmark file exists.*/ - if(!generate_bookmark_file_name(file)) - { - return false; - } - - if(!file_exists(global_bookmark_file_name)) - return false; - - if(global_settings.autoloadbookmark == BOOKMARK_YES) - { - return bookmark_load(global_bookmark_file_name, true); - } - else - { - int ret = select_bookmark(global_bookmark_file_name, true, &bookmark); - - if (bookmark != NULL) - { - if (!play_bookmark(bookmark)) - { - /* Selected bookmark not found. */ - splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); - } - - /* Act as if autoload was done even if it failed, since the - * user did make an active selection. - */ - return true; - } - - return ret != BOOKMARK_SUCCESS; - } + playlist_get_resume_info(&(resume_info->resume_index)); + resume_info->resume_seed = playlist_get_seed(NULL); + resume_info->id3 = audio_current_track(); + resume_info->repeat_mode = global_settings.repeat_mode; + resume_info->shuffle = global_settings.playlist_shuffle; +#if defined(HAVE_PITCHCONTROL) + resume_info->pitch = sound_get_pitch(); + resume_info->speed = dsp_get_timestretch(); +#endif } /* ----------------------------------------------------------------------- */ -/* This function loads the bookmark information into the resume memory. */ -/* This is an interface function. */ -/* Returns true on successful bookmark load. */ +/* This function takes the current current resume information and writes */ +/* that to the beginning of the bookmark file. */ +/* This file will contain N number of bookmarks in the following format: */ +/* resume_index*resume_offset*resume_seed*resume_first_index* */ +/* resume_file*milliseconds*MP3 Title* */ +/* Returns true on successful bookmark write. */ +/* Returns false if any part of the bookmarking process fails. It is */ +/* possible that a bookmark is successfully added to the most recent */ +/* bookmark list but fails to be added to the bookmark file or vice versa. */ /* ------------------------------------------------------------------------*/ -bool bookmark_load(const char* file, bool autoload) +static bool write_bookmark(bool create_bookmark_file) { - int fd; - char* bookmark = NULL; + logf("%s", __func__); + char bm_filename[MAX_PATH]; + bool ret=true; - if(autoload) + char *name = NULL; + size_t namelen = 0; + char* bm; + struct resume_info resume_info; + + if (bookmark_is_bookmarkable_state()) { - fd = open(file, O_RDONLY); - if(fd >= 0) + get_track_resume_info(&resume_info); + /* writing the most recent bookmark */ + if (global_settings.usemrb) { - if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)) > 0) - bookmark=global_read_buffer; - close(fd); + /* since we use the same buffer bookmark needs created each time */ + bm = create_bookmark(&name, &namelen, &resume_info); + ret = add_bookmark(RECENT_BOOKMARK_FILE, bm, true); } - } - else - { - /* This is not an auto-load, so list the bookmarks */ - select_bookmark(file, false, &bookmark); - } - if (bookmark != NULL) - { - if (!play_bookmark(bookmark)) + /* writing the directory bookmark */ + if (create_bookmark_file) { - /* Selected bookmark not found. */ - if (!autoload) + bm = create_bookmark(&name, &namelen, &resume_info); + if (generate_bookmark_file_name(bm_filename, + sizeof(bm_filename), name, namelen)) { - splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); + ret &= add_bookmark(bm_filename, bm, false); + } + else + { + ret = false; /* generating bookmark file failed */ } - - return false; } } + else + ret = false; - return true; -} + splash(HZ, ret ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS) + : ID2P(LANG_BOOKMARK_CREATE_FAILURE)); + return ret; +} static int get_bookmark_count(const char* bookmark_file_name) { @@ -537,7 +596,7 @@ static int get_bookmark_count(const char* bookmark_file_name) if(file < 0) return -1; - while(read_line(file, global_read_buffer, sizeof(global_read_buffer)) > 0) + while(read_line(file, global_temp_buffer, sizeof(global_temp_buffer)) > 0) { read_count++; } @@ -568,13 +627,13 @@ static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line) bookmarks->count = 0; bookmarks->reload = false; - while(read_line(file, global_read_buffer, sizeof(global_read_buffer)) > 0) + while(read_line(file, global_temp_buffer, sizeof(global_temp_buffer)) > 0) { read_count++; if (read_count >= first_line) { - dest -= strlen(global_read_buffer) + 1; + dest -= strlen(global_temp_buffer) + 1; if (dest < ((char*) bookmarks) + sizeof(*bookmarks) + (sizeof(char*) * (bookmarks->count + 1))) @@ -582,7 +641,7 @@ static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line) break; } - strcpy(dest, global_read_buffer); + strcpy(dest, global_temp_buffer); bookmarks->items[bookmarks->count] = dest; bookmarks->count++; } @@ -597,6 +656,8 @@ static const char* get_bookmark_info(int list_index, char *buffer, size_t buffer_len) { + char fnamebuf[MAX_PATH]; + struct resume_info resume_info; struct bookmark_list* bookmarks = (struct bookmark_list*) data; int index = list_index / 2; @@ -652,7 +713,8 @@ static const char* get_bookmark_info(int list_index, } } - if (!parse_bookmark(bookmarks->items[index - bookmarks->start], true, true)) + if (!parse_bookmark(fnamebuf, sizeof(fnamebuf), + bookmarks->items[index - bookmarks->start], &resume_info, true)) { return list_index % 2 == 0 ? (char*) str(LANG_BOOKMARK_INVALID) : " "; } @@ -686,25 +748,73 @@ static const char* get_bookmark_info(int list_index, } else { - name = global_filename; + name = fnamebuf; format = "%s"; } - strrsplt(global_filename, '.'); - snprintf(buffer, buffer_len, format, name, global_filename); + strrsplt(fnamebuf, '.'); + snprintf(buffer, buffer_len, format, name, fnamebuf); return buffer; } else { char time_buf[32]; - format_time(time_buf, sizeof(time_buf), bm.resume_time); - snprintf(buffer, buffer_len, "%s, %d%s", time_buf, bm.resume_index + 1, - bm.shuffle ? (char*) str(LANG_BOOKMARK_SHUFFLE) : ""); + format_time(time_buf, sizeof(time_buf), resume_info.resume_elapsed); + snprintf(buffer, buffer_len, "%s, %d%s", time_buf, + resume_info.resume_index + 1, + resume_info.shuffle ? (char*) str(LANG_BOOKMARK_SHUFFLE) : ""); return buffer; } } +/* ----------------------------------------------------------------------- */ +/* This function parses a bookmark, says the voice UI part of it. */ +/* ------------------------------------------------------------------------*/ +static void say_bookmark(const char* bookmark, + int bookmark_id, + bool show_playlist_name) +{ + char fnamebuf[MAX_PATH]; + struct resume_info resume_info; + if (!parse_bookmark(fnamebuf, sizeof(fnamebuf), bookmark, &resume_info, false)) + { + talk_id(LANG_BOOKMARK_INVALID, false); + return; + } + + talk_number(bookmark_id + 1, false); + + bool is_dir = (global_temp_buffer[0] + && global_temp_buffer[strlen(global_temp_buffer)-1] == '/'); + + /* HWCODEC cannot enqueue voice file entries and .talk thumbnails + together, because there is no guarantee that the same mp3 + parameters are used. */ + if(show_playlist_name) + { /* It's useful to know which playlist this is */ + if(is_dir) + talk_dir_or_spell(global_temp_buffer, + TALK_IDARRAY(VOICE_DIR), true); + else talk_file_or_spell(NULL, global_temp_buffer, + TALK_IDARRAY(LANG_PLAYLIST), true); + } + + if(resume_info.shuffle) + talk_id(LANG_SHUFFLE, true); + + talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true); + talk_number(resume_info.resume_index + 1, true); + talk_id(LANG_TIME, true); + talk_value(resume_info.resume_elapsed / 1000, UNIT_TIME, true); + + /* Track filename */ + if(!is_dir) + global_temp_buffer[0] = 0; + talk_file_or_spell(global_temp_buffer, fnamebuf, + TALK_IDARRAY(VOICE_FILE), true); +} + static int bookmark_list_voice_cb(int list_index, void* data) { struct bookmark_list* bookmarks = (struct bookmark_list*) data; @@ -722,6 +832,57 @@ static int bookmark_list_voice_cb(int list_index, void* data) } /* ----------------------------------------------------------------------- */ +/* This function takes a location in a bookmark file and deletes that */ +/* bookmark. */ +/* Returns true on successful bookmark deletion. */ +/* ------------------------------------------------------------------------*/ +static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id) +{ + int temp_bookmark_file = 0; + int bookmark_file = 0; + int bookmark_count = 0; + + /* Opening up a temp bookmark file */ + temp_bookmark_file = open_temp_bookmark(global_temp_buffer, + sizeof(global_temp_buffer), + O_WRONLY | O_CREAT | O_TRUNC, + bookmark_file_name); + + if (temp_bookmark_file < 0) + return false; /* can't open the temp file */ + + /* Reading in the previous bookmarks and writing them to the temp file */ + bookmark_file = open(bookmark_file_name, O_RDONLY); + if (bookmark_file >= 0) + { + while (read_line(bookmark_file, global_temp_buffer, + sizeof(global_temp_buffer)) > 0) + { + if (bookmark_id != bookmark_count) + { + write(temp_bookmark_file, global_temp_buffer, + strlen(global_temp_buffer)); + write(temp_bookmark_file, "\n", 1); + } + bookmark_count++; + } + close(bookmark_file); + } + close(temp_bookmark_file); + + /* only retrieve the path*/ + open_temp_bookmark(global_temp_buffer, + sizeof(global_temp_buffer), + O_PATH, + bookmark_file_name); + + remove(bookmark_file_name); + rename(global_temp_buffer, bookmark_file_name); + + return true; +} + +/* ----------------------------------------------------------------------- */ /* This displays the bookmarks in a file and allows the user to */ /* select one to play. */ /* *selected_bookmark contains a non NULL value on successful bookmark */ @@ -730,7 +891,9 @@ static int bookmark_list_voice_cb(int list_index, void* data) /* if no selection was made and BOOKMARK_USB_CONNECTED if the selection */ /* menu is forced to exit due to a USB connection. */ /* ------------------------------------------------------------------------*/ -static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume, char** selected_bookmark) +static int select_bookmark(const char* bookmark_file_name, + bool show_dont_resume, + char** selected_bookmark) { struct bookmark_list* bookmarks; struct gui_synclist list; @@ -747,12 +910,13 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume bookmarks->filename = bookmark_file_name; bookmarks->start = 0; bookmarks->show_playlist_name - = strcmp(bookmark_file_name, RECENT_BOOKMARK_FILE) == 0; - gui_synclist_init(&list, &get_bookmark_info, (void*) bookmarks, false, 2, NULL); + = (strcmp(bookmark_file_name, RECENT_BOOKMARK_FILE) == 0); + + gui_synclist_init(&list, &get_bookmark_info, + (void*) bookmarks, false, 2, NULL); + if(global_settings.talk_menu) gui_synclist_set_voice_callback(&list, bookmark_list_voice_cb); - gui_synclist_set_title(&list, str(LANG_BOOKMARK_SELECT_BOOKMARK), - Icon_Bookmark); while (!exit) { @@ -787,14 +951,15 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume } buffer_bookmarks(bookmarks, bookmarks->start); + gui_synclist_set_title(&list, str(LANG_BOOKMARK_SELECT_BOOKMARK), + Icon_Bookmark); gui_synclist_draw(&list); cond_talk_ids_fq(VOICE_EXT_BMARK); gui_synclist_speak_item(&list); refresh = false; } - list_do_action(CONTEXT_BOOKMARKSCREEN, HZ / 2, - &list, &action, LIST_WRAP_UNLESS_HELD); + list_do_action(CONTEXT_BOOKMARKSCREEN, HZ / 2, &list, &action); item = gui_synclist_get_sel_pos(&list) / 2; if (bookmarks->show_dont_resume) @@ -843,17 +1008,7 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume case ACTION_BMS_DELETE: if (item >= 0) { - const char *lines[]={ - ID2P(LANG_REALLY_DELETE) - }; - const char *yes_lines[]={ - ID2P(LANG_DELETING) - }; - - const struct text_message message={lines, 1}; - const struct text_message yes_message={yes_lines, 1}; - - if(gui_syncyesno_run(&message, &yes_message, NULL)==YESNO_YES) + if (confirm_delete_yesno("") == YESNO_YES) { delete_bookmark(bookmark_file_name, item); bookmarks->reload = true; @@ -879,264 +1034,247 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume } /* ----------------------------------------------------------------------- */ -/* This function takes a location in a bookmark file and deletes that */ -/* bookmark. */ -/* Returns true on successful bookmark deletion. */ +/* This function parses a bookmark and then plays it. */ +/* Returns true on successful bookmark play. */ /* ------------------------------------------------------------------------*/ -static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id) +static bool play_bookmark(const char* bookmark) { - int temp_bookmark_file = 0; - int bookmark_file = 0; - int bookmark_count = 0; - - /* Opening up a temp bookmark file */ - snprintf(global_temp_buffer, sizeof(global_temp_buffer), - "%s.tmp", bookmark_file_name); - temp_bookmark_file = open(global_temp_buffer, - O_WRONLY | O_CREAT | O_TRUNC, 0666); - - if (temp_bookmark_file < 0) - return false; /* can't open the temp file */ + char fnamebuf[MAX_PATH]; + struct resume_info resume_info; +#if defined(HAVE_PITCHCONTROL) + /* preset pitch and speed to 100% in case bookmark doesn't have info */ + resume_info.pitch = sound_get_pitch(); + resume_info.speed = dsp_get_timestretch(); +#endif - /* Reading in the previous bookmarks and writing them to the temp file */ - bookmark_file = open(bookmark_file_name, O_RDONLY); - if (bookmark_file >= 0) + if (parse_bookmark(fnamebuf, sizeof(fnamebuf), bookmark, &resume_info, true)) { - while (read_line(bookmark_file, global_read_buffer, - sizeof(global_read_buffer)) > 0) + global_settings.repeat_mode = resume_info.repeat_mode; + global_settings.playlist_shuffle = resume_info.shuffle; +#if defined(HAVE_PITCHCONTROL) + sound_set_pitch(resume_info.pitch); + dsp_set_timestretch(resume_info.speed); +#endif + if (!warn_on_pl_erase()) + return false; + bool success = bookmark_play(global_temp_buffer, resume_info.resume_index, + resume_info.resume_elapsed, resume_info.resume_offset, + resume_info.resume_seed, fnamebuf); + if (success) /* verify we loaded the correct track */ { - if (bookmark_id != bookmark_count) + const struct mp3entry *id3 = audio_current_track(); + if (id3) { - write(temp_bookmark_file, global_read_buffer, - strlen(global_read_buffer)); - write(temp_bookmark_file, "\n", 1); + const char *path; + const char *track; + path_basename(id3->path, &path); + path_basename(fnamebuf, &track); + if (strcmp(path, track) == 0) + { + return true; + } } - bookmark_count++; + audio_stop(); } - close(bookmark_file); } - close(temp_bookmark_file); - - remove(bookmark_file_name); - rename(global_temp_buffer, bookmark_file_name); - return true; + return false; } +/*-------------------------------------------------------------------------*/ +/* PUBLIC INTERFACE -------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ + + /* ----------------------------------------------------------------------- */ -/* This function parses a bookmark, says the voice UI part of it. */ -/* ------------------------------------------------------------------------*/ -static void say_bookmark(const char* bookmark, - int bookmark_id, bool show_playlist_name) +/* This is an interface function from the context menu. */ +/* Returns true on successful bookmark creation. */ +/* ----------------------------------------------------------------------- */ +bool bookmark_create_menu(void) { - if (!parse_bookmark(bookmark, true, false)) - { - talk_id(LANG_BOOKMARK_INVALID, false); - return; - } - - talk_number(bookmark_id + 1, false); + return write_bookmark(true); +} +/* ----------------------------------------------------------------------- */ +/* This function acts as the load interface from the context menu. */ +/* This function determines the bookmark file name and then loads that file*/ +/* for the user. The user can then select or delete previous bookmarks. */ +/* This function returns BOOKMARK_SUCCESS on the selection of a track to */ +/* resume, BOOKMARK_FAIL if the menu is exited without a selection and */ +/* BOOKMARK_USB_CONNECTED if the menu is forced to exit due to a USB */ +/* connection. */ +/* ----------------------------------------------------------------------- */ +int bookmark_load_menu(void) +{ + char bm_filename[MAX_PATH]; + char* bookmark; + int ret = BOOKMARK_FAIL; - bool is_dir = (global_temp_buffer[0] - && global_temp_buffer[strlen(global_temp_buffer)-1] == '/'); + push_current_activity(ACTIVITY_BOOKMARKSLIST); - /* HWCODEC cannot enqueue voice file entries and .talk thumbnails - together, because there is no guarantee that the same mp3 - parameters are used. */ - if(show_playlist_name) - { /* It's useful to know which playlist this is */ - if(is_dir) - talk_dir_or_spell(global_temp_buffer, - TALK_IDARRAY(VOICE_DIR), true); - else talk_file_or_spell(NULL, global_temp_buffer, - TALK_IDARRAY(LANG_PLAYLIST), true); + char* name = playlist_get_name(NULL, global_temp_buffer, + sizeof(global_temp_buffer)); + if (generate_bookmark_file_name(bm_filename, sizeof(bm_filename), name, -1)) + { + ret = select_bookmark(bm_filename, false, &bookmark); + if (bookmark != NULL) + { + ret = play_bookmark(bookmark) ? BOOKMARK_SUCCESS : BOOKMARK_FAIL; + } } - if(bm.shuffle) - talk_id(LANG_SHUFFLE, true); - - talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true); - talk_number(bm.resume_index + 1, true); - talk_id(LANG_TIME, true); - talk_value(bm.resume_time / 1000, UNIT_TIME, true); - - /* Track filename */ - if(!is_dir) - global_temp_buffer[0] = 0; - talk_file_or_spell(global_temp_buffer, global_filename, - TALK_IDARRAY(VOICE_FILE), true); + pop_current_activity(); + return ret; } /* ----------------------------------------------------------------------- */ -/* This function parses a bookmark and then plays it. */ -/* Returns true on successful bookmark play. */ -/* ------------------------------------------------------------------------*/ -static bool play_bookmark(const char* bookmark) +/* Gives the user a list of the Most Recent Bookmarks. This is an */ +/* interface function */ +/* Returns true on the successful selection of a recent bookmark. */ +/* ----------------------------------------------------------------------- */ +bool bookmark_mrb_load() { -#if defined(HAVE_PITCHCONTROL) - /* preset pitch and speed to 100% in case bookmark doesn't have info */ - bm.pitch = sound_get_pitch(); - bm.speed = dsp_get_timestretch(); -#endif + char* bookmark; + bool ret = false; - if (parse_bookmark(bookmark, true, true)) + push_current_activity(ACTIVITY_BOOKMARKSLIST); + select_bookmark(RECENT_BOOKMARK_FILE, false, &bookmark); + if (bookmark != NULL) { - global_settings.repeat_mode = bm.repeat_mode; - global_settings.playlist_shuffle = bm.shuffle; -#if defined(HAVE_PITCHCONTROL) - sound_set_pitch(bm.pitch); - dsp_set_timestretch(bm.speed); -#endif - if (!warn_on_pl_erase()) - return false; - return bookmark_play(global_temp_buffer, bm.resume_index, - bm.resume_time, bm.resume_offset, bm.resume_seed, global_filename); + ret = play_bookmark(bookmark); } - return false; + pop_current_activity(); + return ret; } -static const char* skip_token(const char* s) +/* ----------------------------------------------------------------------- */ +/* This function handles an autobookmark creation. This is an interface */ +/* function. */ +/* Returns true on successful bookmark creation. */ +/* ----------------------------------------------------------------------- */ +bool bookmark_autobookmark(bool prompt_ok) { - while (*s && *s != ';') - { - s++; - } + logf("%s", __func__); + bool update; + + if (!bookmark_is_bookmarkable_state()) + return false; + + audio_pause(); /* first pause playback */ + update = (global_settings.autoupdatebookmark && bookmark_exists()); - if (*s) + if (update) + return write_bookmark(true); + + switch (global_settings.autocreatebookmark) { - s++; - } + case BOOKMARK_YES: + return write_bookmark(true); - return s; -} + case BOOKMARK_NO: + return false; -static const char* int_token(const char* s, int* dest) -{ - *dest = atoi(s); - return skip_token(s); -} + case BOOKMARK_RECENT_ONLY_YES: + return write_bookmark(false); + } + const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)}; + const struct text_message message={lines, 1}; -static const char* long_token(const char* s, long* dest) -{ - *dest = atoi(s); /* Should be atol, but we don't have it. */ - return skip_token(s); + if(prompt_ok && gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES) + { + if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK) + return write_bookmark(false); + else + return write_bookmark(true); + } + return false; } /* ----------------------------------------------------------------------- */ -/* This function takes a bookmark and parses it. This function also */ -/* validates the bookmark. The parse_filenames flag indicates whether */ -/* the filename tokens are to be extracted. */ -/* Returns true on successful bookmark parse. */ -/* ----------------------------------------------------------------------- */ -static bool parse_bookmark(const char *bookmark, const bool parse_filenames, const bool strip_dir) +/* This function will determine if an autoload is necessary. This is an */ +/* interface function. */ +/* Returns */ +/* BOOKMARK_DO_RESUME on bookmark load or bookmark selection. */ +/* BOOKMARK_DONT_RESUME if we're not going to resume */ +/* BOOKMARK_CANCEL if user canceled */ +/* ------------------------------------------------------------------------*/ +int bookmark_autoload(const char* file) { - const char* s = bookmark; - const char* end; + logf("%s", __func__); + char bm_filename[MAX_PATH]; + char* bookmark; -#define GET_INT_TOKEN(var) s = int_token(s, &var) -#define GET_LONG_TOKEN(var) s = long_token(s, &var) -#define GET_BOOL_TOKEN(var) var = (atoi(s)!=0); s = skip_token(s) + if(global_settings.autoloadbookmark == BOOKMARK_NO) + return BOOKMARK_DONT_RESUME; - /* if new format bookmark, extract the optional content flags, - otherwise treat as an original format bookmark */ - int opt_flags = 0; - bool new_format = (strchr(s, '>') == s); - if (new_format) + /*Checking to see if a bookmark file exists.*/ + if(!generate_bookmark_file_name(bm_filename, sizeof(bm_filename), file, -1)) { - s++; - GET_INT_TOKEN(opt_flags); + return BOOKMARK_DONT_RESUME; } - /* extract all original bookmark tokens */ - GET_INT_TOKEN(bm.resume_index); - GET_LONG_TOKEN(bm.resume_offset); - GET_INT_TOKEN(bm.resume_seed); - if (!new_format) /* skip deprecated token */ - s = skip_token(s); - GET_LONG_TOKEN(bm.resume_time); - GET_INT_TOKEN(bm.repeat_mode); - GET_BOOL_TOKEN(bm.shuffle); - - /* extract all optional bookmark tokens */ - if (opt_flags & BM_PITCH) - GET_INT_TOKEN(bm.pitch); - if (opt_flags & BM_SPEED) - GET_INT_TOKEN(bm.speed); + if(!file_exists(bm_filename)) + return BOOKMARK_DONT_RESUME; - if (*s == 0) + if(global_settings.autoloadbookmark == BOOKMARK_YES) { - return false; + return (bookmark_load(bm_filename, true) + ? BOOKMARK_DO_RESUME : BOOKMARK_DONT_RESUME); } - - end = strchr(s, ';'); - - /* extract file names */ - if (parse_filenames) + else { - size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s); - len = MIN(TEMP_BUF_SIZE - 1, len); - strlcpy(global_temp_buffer, s, len + 1); + int ret = select_bookmark(bm_filename, true, &bookmark); - if (end != NULL) + if (bookmark != NULL) { - end++; - if (strip_dir) - { - s = strrchr(end, '/'); - if (s) - { - end = s; - end++; - } - } - strlcpy(global_filename, end, MAX_PATH); + if (!play_bookmark(bookmark)) + return BOOKMARK_CANCEL; + return BOOKMARK_DO_RESUME; } - } - return true; + return (ret != BOOKMARK_SUCCESS) ? BOOKMARK_CANCEL : BOOKMARK_DONT_RESUME; + } } /* ----------------------------------------------------------------------- */ -/* This function is used by multiple functions and is used to generate a */ -/* bookmark named based off of the input. */ -/* Changing this function could result in how the bookmarks are stored. */ -/* it would be here that the centralized/decentralized bookmark code */ -/* could be placed. */ -/* Returns true if the file name is generated, false if it was too long */ -/* ----------------------------------------------------------------------- */ -static bool generate_bookmark_file_name(const char *in) +/* This function loads the bookmark information into the resume memory. */ +/* This is an interface function. */ +/* Returns true on successful bookmark load. */ +/* ------------------------------------------------------------------------*/ +bool bookmark_load(const char* file, bool autoload) { - /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */ - /* otherwise, name it based on the in variable */ - if (!strcmp("/", in)) - strcpy(global_bookmark_file_name, "/root_dir.bmark"); - else - { -#ifdef HAVE_MULTIVOLUME - /* The "root" of an extra volume need special handling too. */ - const char *filename; - path_strip_volume(in, &filename, true); - bool volume_root = *filename == '\0'; -#endif - size_t len = strlcpy(global_bookmark_file_name, in, MAX_PATH); - if(len >= MAX_PATH) - return false; + logf("%s", __func__); + int fd; + char* bookmark = NULL; - if(global_bookmark_file_name[len-1] == '/') { - global_bookmark_file_name[len-1] = '\0'; - len--; + if(autoload) + { + fd = open(file, O_RDONLY); + if(fd >= 0) + { + if(read_line(fd, global_temp_buffer, sizeof(global_temp_buffer)) > 0) + bookmark=global_temp_buffer; + close(fd); } + } + else + { + /* This is not an auto-load, so list the bookmarks */ + select_bookmark(file, false, &bookmark); + } -#ifdef HAVE_MULTIVOLUME - if (volume_root) - len = strlcat(global_bookmark_file_name, "/volume_dir.bmark", MAX_PATH); - else -#endif - len = strlcat(global_bookmark_file_name, ".bmark", MAX_PATH); + if (bookmark != NULL) + { + if (!play_bookmark(bookmark)) + { + /* Selected bookmark not found. */ + if (!autoload) + { + splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); + } - if(len >= MAX_PATH) return false; + } } return true; @@ -1148,13 +1286,14 @@ static bool generate_bookmark_file_name(const char *in) /* ----------------------------------------------------------------------- */ bool bookmark_exists(void) { + char bm_filename[MAX_PATH]; bool exist=false; char* name = playlist_get_name(NULL, global_temp_buffer, sizeof(global_temp_buffer)); - if (generate_bookmark_file_name(name)) + if (generate_bookmark_file_name(bm_filename, sizeof(bm_filename), name, -1)) { - exist = file_exists(global_bookmark_file_name); + exist = file_exists(bm_filename); } return exist; } @@ -1180,4 +1319,3 @@ bool bookmark_is_bookmarkable_state(void) return true; } - diff --git a/apps/bookmark.h b/apps/bookmark.h index ff7b87c1bf..192e577ce6 100644 --- a/apps/bookmark.h +++ b/apps/bookmark.h @@ -29,11 +29,17 @@ enum { BOOKMARK_USB_CONNECTED = 1 }; +enum { + BOOKMARK_CANCEL, + BOOKMARK_DONT_RESUME, + BOOKMARK_DO_RESUME +}; + int bookmark_load_menu(void); bool bookmark_autobookmark(bool prompt_ok); bool bookmark_create_menu(void); bool bookmark_mrb_load(void); -bool bookmark_autoload(const char* file); +int bookmark_autoload(const char* file); bool bookmark_load(const char* file, bool autoload); bool bookmark_exists(void); bool bookmark_is_bookmarkable_state(void); diff --git a/apps/buffering.c b/apps/buffering.c index 3adbc4a6b9..81b861ccf1 100644 --- a/apps/buffering.c +++ b/apps/buffering.c @@ -20,7 +20,6 @@ ****************************************************************************/ #include "config.h" #include <string.h> -#include "strlcpy.h" #include "system.h" #include "storage.h" #include "thread.h" @@ -71,8 +70,6 @@ /* amount of data to read in one read() call */ #define BUFFERING_DEFAULT_FILECHUNK (1024*32) -#define BUF_HANDLE_MASK 0x7FFFFFFF - enum handle_flags { H_CANWRAP = 0x1, /* Handle data may wrap in buffer */ @@ -295,12 +292,11 @@ static int next_handle_id(void) { static int cur_handle_id = 0; - /* Wrap signed int is safe and 0 doesn't happen */ - int next_hid = (cur_handle_id + 1) & BUF_HANDLE_MASK; - if (next_hid == 0) - next_hid = 1; - - cur_handle_id = next_hid; + int next_hid = cur_handle_id + 1; + if (next_hid == INT_MAX) + cur_handle_id = 0; /* next would overflow; reset the counter */ + else + cur_handle_id = next_hid; return next_hid; } @@ -420,7 +416,8 @@ add_handle(unsigned int flags, size_t data_size, const char *path, h->signaled = 0; /* Data can be waited for */ /* Save the provided path */ - memcpy(h->path, path, pathsize); + if (path) + memcpy(h->path, path, pathsize); /* Return the start of the data area */ *data_out = ringbuf_add(index, handlesize); @@ -851,8 +848,9 @@ static bool fill_buffer(void) Return value is the total size (struct + data). */ static int load_image(int fd, const char *path, struct bufopen_bitmap_data *data, - size_t bufidx) + size_t bufidx, size_t max_size) { + (void)path; int rc; struct bitmap *bmp = ringbuf_ptr(bufidx); struct dim *dim = data->dim; @@ -866,25 +864,20 @@ static int load_image(int fd, const char *path, #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) bmp->maskdata = NULL; #endif - int free = (int)MIN(buffer_len - bytes_used(), buffer_len - bufidx) - - sizeof(struct bitmap); - + const int format = FORMAT_NATIVE | FORMAT_DITHER | + FORMAT_RESIZE | FORMAT_KEEP_ASPECT; #ifdef HAVE_JPEG if (aa != NULL) { lseek(fd, aa->pos, SEEK_SET); - rc = clip_jpeg_fd(fd, aa->size, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| - FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL); + rc = clip_jpeg_fd(fd, aa->size, bmp, (int)max_size, format, NULL); } else if (strcmp(path + strlen(path) - 4, ".bmp")) - rc = read_jpeg_fd(fd, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| - FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL); + rc = read_jpeg_fd(fd, bmp, (int)max_size, format, NULL); else #endif - rc = read_bmp_fd(fd, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| - FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL); + rc = read_bmp_fd(fd, bmp, (int)max_size, format, NULL); return rc + (rc > 0 ? sizeof(struct bitmap) : 0); - (void)path; } #endif /* HAVE_ALBUMART */ @@ -970,11 +963,18 @@ int bufopen(const char *file, off_t offset, enum data_type type, size_t size = 0; #ifdef HAVE_ALBUMART if (type == TYPE_BITMAP) { - /* If albumart is embedded, the complete file is not buffered, - * but only the jpeg part; filesize() would be wrong */ + /* Bitmaps are resized to the requested dimensions when loaded, + * so the file size should not be used as it may be too large + * or too small */ struct bufopen_bitmap_data *aa = user_data; - if (aa->embedded_albumart) - size = aa->embedded_albumart->size; + size = BM_SIZE(aa->dim->width, aa->dim->height, FORMAT_NATIVE, false); + size += sizeof(struct bitmap); + +#ifdef HAVE_JPEG + /* JPEG loading requires extra memory + * TODO: don't add unncessary overhead for .bmp images! */ + size += JPEG_DECODE_OVERHEAD; +#endif } #endif @@ -983,7 +983,10 @@ int bufopen(const char *file, off_t offset, enum data_type type, unsigned int hflags = 0; if (type == TYPE_PACKET_AUDIO || type == TYPE_CODEC) - hflags = H_CANWRAP; + hflags |= H_CANWRAP; + /* Bitmaps need their space allocated up front */ + if (type == TYPE_BITMAP) + hflags |= H_ALLOCALL; size_t adjusted_offset = offset; if (adjusted_offset > size) @@ -1034,7 +1037,7 @@ int bufopen(const char *file, off_t offset, enum data_type type, #ifdef HAVE_ALBUMART if (type == TYPE_BITMAP) { /* Bitmap file: we load the data instead of the file */ - int rc = load_image(fd, file, user_data, data); + int rc = load_image(fd, file, user_data, data, padded_size); if (rc <= 0) { handle_id = ERR_FILE_ERROR; } else { @@ -1433,62 +1436,6 @@ ssize_t bufgetdata(int handle_id, size_t size, void **data) return size; } -ssize_t bufgettail(int handle_id, size_t size, void **data) -{ - if (thread_self() != buffering_thread_id) - return ERR_WRONG_THREAD; /* only from buffering thread */ - - /* We don't support tail requests of > guardbuf_size, for simplicity */ - if (size > GUARD_BUFSIZE) - return ERR_INVALID_VALUE; - - const struct memory_handle *h = find_handle(handle_id); - if (!h) - return ERR_HANDLE_NOT_FOUND; - - if (h->end >= h->filesize) { - size_t tidx = ringbuf_sub_empty(h->widx, size); - - if (tidx + size > buffer_len) { - size_t copy_n = tidx + size - buffer_len; - memcpy(guard_buffer, ringbuf_ptr(0), copy_n); - } - - *data = ringbuf_ptr(tidx); - } - else { - size = ERR_HANDLE_NOT_DONE; - } - - return size; -} - -ssize_t bufcuttail(int handle_id, size_t size) -{ - if (thread_self() != buffering_thread_id) - return ERR_WRONG_THREAD; /* only from buffering thread */ - - struct memory_handle *h = find_handle(handle_id); - if (!h) - return ERR_HANDLE_NOT_FOUND; - - if (h->end >= h->filesize) { - /* Cannot trim to before read position */ - size_t available = h->end - MAX(h->start, h->pos); - if (available < size) - size = available; - - h->widx = ringbuf_sub_empty(h->widx, size); - h->filesize -= size; - h->end -= size; - } else { - size = ERR_HANDLE_NOT_DONE; - } - - return size; -} - - /* SECONDARY EXPORTED FUNCTIONS ============================ diff --git a/apps/buffering.h b/apps/buffering.h index 1a75d865ae..bc47d6b1a1 100644 --- a/apps/buffering.h +++ b/apps/buffering.h @@ -46,8 +46,7 @@ enum data_type { #define ERR_FILE_ERROR -4 #define ERR_HANDLE_NOT_DONE -5 #define ERR_UNSUPPORTED_TYPE -6 -#define ERR_WRONG_THREAD -7 -#define ERR_BITMAP_TOO_LARGE -8 +#define ERR_BITMAP_TOO_LARGE -7 /* Initialise the buffering subsystem */ void buffering_init(void) INIT_ATTR; @@ -68,8 +67,6 @@ bool buffering_reset(char *buf, size_t buflen); * bufftell : Return the handle's file read position * bufread : Copy data from a handle to a buffer * bufgetdata: Obtain a pointer for linear access to a "size" amount of data - * bufgettail: Out-of-band get the last size bytes of a handle. - * bufcuttail: Out-of-band remove the trailing 'size' bytes of a handle. * * NOTE: bufread and bufgetdata will block the caller until the requested * amount of data is ready (unless EOF is reached). @@ -85,8 +82,6 @@ int bufadvance(int handle_id, off_t offset); off_t bufftell(int handle_id); ssize_t bufread(int handle_id, size_t size, void *dest); ssize_t bufgetdata(int handle_id, size_t size, void **data); -ssize_t bufgettail(int handle_id, size_t size, void **data); -ssize_t bufcuttail(int handle_id, size_t size); /*************************************************************************** * SECONDARY FUNCTIONS diff --git a/apps/codecs.c b/apps/codecs.c index 4d2dd34ce0..9f34d26e14 100644 --- a/apps/codecs.c +++ b/apps/codecs.c @@ -203,8 +203,9 @@ static int codec_load_ram(struct codec_api *api) return CODEC_ERROR; } - if (hdr->api_version > CODEC_API_VERSION - || hdr->api_version < CODEC_MIN_API_VERSION) { + if (hdr->api_version != CODEC_API_VERSION || + c_hdr->api_size > sizeof(struct codec_api)) + { logf("codec api version error"); lc_close(curr_handle); curr_handle = NULL; diff --git a/apps/core_keymap.c b/apps/core_keymap.c new file mode 100644 index 0000000000..89e7913c33 --- /dev/null +++ b/apps/core_keymap.c @@ -0,0 +1,95 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2020 by William Wilgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "action.h" +#include "core_alloc.h" +#include "core_keymap.h" + +/*#define LOGF_ENABLE*/ +#include "logf.h" + +#if !defined(__PCTOOL__) || defined(CHECKWPS) +int core_set_keyremap(struct button_mapping* core_keymap, int count) +{ + return action_set_keymap(core_keymap, count); +} + +static int open_key_remap(const char *filename, int *countp) +{ + int fd = open(filename, O_RDONLY); + if (fd < 0) + return fd; + + size_t fsize = filesize(fd); + int count = fsize / sizeof(struct button_mapping); + if (count == 0 || (size_t)(count * sizeof(struct button_mapping)) != fsize) + { + logf("core_keyremap: bad filesize %d / %lu", count, (unsigned long)fsize); + goto error; + } + + struct button_mapping header; + if(read(fd, &header, sizeof(header)) != (ssize_t)sizeof(header)) + { + logf("core_keyremap: read error"); + goto error; + } + + if (header.action_code != KEYREMAP_VERSION || + header.button_code != KEYREMAP_HEADERID || + header.pre_button_code != count) + { + logf("core_keyremap: bad header %d", count); + goto error; + } + + *countp = count - 1; + return fd; + + error: + close(fd); + return -1; +} + +int core_load_key_remap(const char *filename) +{ + int count = 0; /* gcc falsely believes this may be used uninitialized */ + int fd = open_key_remap(filename, &count); + if (fd < 0) + return -1; + + size_t bufsize = count * sizeof(struct button_mapping); + int handle = core_alloc(bufsize); + if (handle > 0) + { + void *data = core_get_data_pinned(handle); + + if (read(fd, data, bufsize) == (ssize_t)bufsize) + count = action_set_keymap_handle(handle, count); + + core_put_data_pinned(data); + } + + close(fd); + return count; +} + +#endif /* !defined(__PCTOOL__) */ diff --git a/apps/core_keymap.h b/apps/core_keymap.h new file mode 100644 index 0000000000..2077daa685 --- /dev/null +++ b/apps/core_keymap.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2020 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. + * + ****************************************************************************/ +#ifndef CORE_KEYMAP_H +#define CORE_KEYMAP_H + +#include <stdbool.h> +#include <inttypes.h> +#include "config.h" +#include "action.h" +#define KEYREMAP_VERSION 1 +#define KEYREMAP_HEADERID (LAST_ACTION_PLACEHOLDER | (TARGET_ID << 8)) + +/* If exists remap file will be loaded at startup */ +#define CORE_KEYREMAP_FILE ROCKBOX_DIR "/keyremap.kmf" + +/* Allocates core buffer, copies keymap to allow buttons for actions to be remapped*/ +int core_set_keyremap(struct button_mapping* core_keymap, int count); + +/* load a remap file to allow buttons for actions to be remapped */ +int core_load_key_remap(const char *filename); + +/* + * entries consist of 3 int [action, button, prebtn] + * the header (VERSION, LAST_DEFINED_ACTION, count) is stripped by open_key_remap + * + * context look up table is at the beginning + * action_code contains (context | CONTEXT_REMAPPED) + * button_code contains index of first remapped action for the matched context + * prebtn_code contains count of actions in this remapped context + * [-1] REMAP_VERSION, REMAP_HEADERID, entry count(9) / DISCARDED AFTER LOAD + * [0] CORE_CONTEXT_REMAP(ctx1), offset1=(3), count=(1) + * [1] CORE_CONTEXT_REMAP(ctx2, offset2=(5), count=(2) + * [2] sentinel, 0, 0 + * [3] act0, btn, 0 + * [4] sentinel 0, 0 + * [5] act1, btn, 0 + * [6] act2, btn1 + * [7] sentinel, 0, 0 + * + * Note: + * last entry of each group is always the sentinel [CONTEXT_STOPSEARCHING, BUTTON_NONE, BUTTON_NONE] + * contexts must match exactly -- re-mapped contexts run before the built in w/ fall through contexts + * ie. you can't remap std_context and expect it to match std_context actions from the WPS context. + */ + +#endif /* CORE_KEYMAP_H */ + diff --git a/apps/cuesheet.c b/apps/cuesheet.c index be89ef96cf..263fed154d 100644 --- a/apps/cuesheet.c +++ b/apps/cuesheet.c @@ -58,28 +58,28 @@ static bool search_for_cuesheet(const char *path, struct cuesheet_file *cue_file slash_cuepath = &cuepath[slash - path]; dot = strrchr(slash_cuepath, '.'); if (dot) - strlcpy(dot, ".cue", MAX_PATH - (dot-cuepath)); + strmemccpy(dot, ".cue", MAX_PATH - (dot-cuepath)); if (!dot || !file_exists(cuepath)) { strcpy(cuepath, CUE_DIR); if (strlcat(cuepath, slash, MAX_PATH) >= MAX_PATH) goto skip; /* overflow */ - char *dot = strrchr(cuepath, '.'); + dot = strrchr(cuepath, '.'); strcpy(dot, ".cue"); if (!file_exists(cuepath)) { skip: if ((len+4) >= MAX_PATH) return false; - strlcpy(cuepath, path, MAX_PATH); + strmemccpy(cuepath, path, MAX_PATH); strlcat(cuepath, ".cue", MAX_PATH); if (!file_exists(cuepath)) return false; } } - strlcpy(cue_file->path, cuepath, MAX_PATH); + strmemccpy(cue_file->path, cuepath, MAX_PATH); return true; } @@ -91,7 +91,7 @@ bool look_for_cuesheet_file(struct mp3entry *track_id3, struct cuesheet_file *cu cue_file->pos = track_id3->embedded_cuesheet.pos; cue_file->size = track_id3->embedded_cuesheet.size; cue_file->encoding = track_id3->embedded_cuesheet.encoding; - strlcpy(cue_file->path, track_id3->path, MAX_PATH); + strmemccpy(cue_file->path, track_id3->path, MAX_PATH); return true; } @@ -123,6 +123,7 @@ static unsigned long parse_cue_index(const char *line) /* assumes strncmp(line, "INDEX 01", 8) & NULL terminated string */ /* INDEX 01 MM:SS:FF\0 (00:00:00\0 - 99:99:99\0)*/ const unsigned field_m[3] = {60 * 1000, 1000, 13}; /* MM:SS:~FF*/ + const unsigned field_max[3] = {30000, 59, 74}; /* MM, SS, FF */ const char f_sep = ':'; int field = -1; unsigned long offset = 0; /* ms from start of track */ @@ -138,7 +139,7 @@ static unsigned long parse_cue_index(const char *line) while (isdigit(*line)) { value = 10 * value + (*line - '0'); - if (value > 99) /* Sanity check bail early */ + if (field >= 0 && value > field_max[field]) /* Sanity check bail early */ return 0; line++; } @@ -165,6 +166,38 @@ static unsigned long parse_cue_index(const char *line) return offset; } +enum eCS_SUPPORTED_TAGS { + eCS_TRACK = 0, eCS_INDEX_01, eCS_TITLE, + eCS_PERFORMER, eCS_SONGWRITER, eCS_FILE, + eCS_COUNT_TAGS_COUNT, eCS_NOTFOUND = -1 +}; + +static enum eCS_SUPPORTED_TAGS cuesheet_tag_get_option(const char *option) +{ + #define CS_OPTN(str) {str, sizeof(str)-1} + static const struct cs_option_t { + const char *str; + const int len; + } cs_options[eCS_COUNT_TAGS_COUNT + 1] = { + [eCS_TRACK] = CS_OPTN("TRACK"), + [eCS_INDEX_01] = CS_OPTN("INDEX 01"), + [eCS_TITLE] =CS_OPTN("TITLE"), + [eCS_PERFORMER] =CS_OPTN("PERFORMER"), + [eCS_SONGWRITER] =CS_OPTN("SONGWRITER"), + [eCS_FILE] =CS_OPTN("FILE"), + [eCS_COUNT_TAGS_COUNT] = {NULL, 0} /*SENTINEL*/ + }; + + const struct cs_option_t *op; + for (int i=0; ((op=&cs_options[i]))->str != NULL; i++) + { + if (strncmp(option, op->str, op->len) == 0) + return i; + } + return eCS_NOTFOUND; +#undef CS_OPTN +} + /* parse cuesheet "cue_file" and store the information in "cue" */ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) { @@ -190,22 +223,25 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) /* Look for a Unicode BOM */ unsigned char bom_read = 0; - read(fd, line, BOM_UTF_8_SIZE); - if(!memcmp(line, BOM_UTF_8, BOM_UTF_8_SIZE)) - { - char_enc = CHAR_ENC_UTF_8; - bom_read = BOM_UTF_8_SIZE; - } - else if(!memcmp(line, BOM_UTF_16_LE, BOM_UTF_16_SIZE)) - { - char_enc = CHAR_ENC_UTF_16_LE; - bom_read = BOM_UTF_16_SIZE; - } - else if(!memcmp(line, BOM_UTF_16_BE, BOM_UTF_16_SIZE)) + if (read(fd, line, BOM_UTF_8_SIZE) > 0) { - char_enc = CHAR_ENC_UTF_16_BE; - bom_read = BOM_UTF_16_SIZE; + if(!memcmp(line, BOM_UTF_8, BOM_UTF_8_SIZE)) + { + char_enc = CHAR_ENC_UTF_8; + bom_read = BOM_UTF_8_SIZE; + } + else if(!memcmp(line, BOM_UTF_16_LE, BOM_UTF_16_SIZE)) + { + char_enc = CHAR_ENC_UTF_16_LE; + bom_read = BOM_UTF_16_SIZE; + } + else if(!memcmp(line, BOM_UTF_16_BE, BOM_UTF_16_SIZE)) + { + char_enc = CHAR_ENC_UTF_16_BE; + bom_read = BOM_UTF_16_SIZE; + } } + if (bom_read < BOM_UTF_8_SIZE) lseek(fd, cue_file->pos + bom_read, SEEK_SET); if (is_embedded) @@ -245,11 +281,16 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) } s = skip_whitespace(line); - if (!strncmp(s, "TRACK", 5)) +/* RECOGNIZED TAGS *********************** +* eCS_TRACK = 0, eCS_INDEX_01, eCS_TITLE, +* eCS_PERFORMER, eCS_SONGWRITER, eCS_FILE, +*/ + enum eCS_SUPPORTED_TAGS option = cuesheet_tag_get_option(s); + if (option == eCS_TRACK) { cue->track_count++; } - else if (!strncmp(s, "INDEX 01", 8)) + else if (option == eCS_INDEX_01) { #if 0 s = strchr(s,' '); @@ -265,10 +306,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) cue->tracks[cue->track_count-1].offset = parse_cue_index(s); #endif } - else if (!strncmp(s, "TITLE", 5) - || !strncmp(s, "PERFORMER", 9) - || !strncmp(s, "SONGWRITER", 10) - || !strncmp(s, "FILE", 4)) + else if (option != eCS_NOTFOUND) { char *dest = NULL; char *string = get_string(s); @@ -278,24 +316,24 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) size_t count = MAX_NAME*3 + 1; size_t count8859 = MAX_NAME; - switch (*s) + switch (option) { - case 'T': /* TITLE */ + case eCS_TITLE: /* TITLE */ dest = (cue->track_count <= 0) ? cue->title : cue->tracks[cue->track_count-1].title; break; - case 'P': /* PERFORMER */ + case eCS_PERFORMER: /* PERFORMER */ dest = (cue->track_count <= 0) ? cue->performer : cue->tracks[cue->track_count-1].performer; break; - case 'S': /* SONGWRITER */ + case eCS_SONGWRITER: /* SONGWRITER */ dest = (cue->track_count <= 0) ? cue->songwriter : cue->tracks[cue->track_count-1].songwriter; break; - case 'F': /* FILE */ + case eCS_FILE: /* FILE */ if (is_embedded || cue->track_count > 0) break; @@ -303,6 +341,16 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) count = MAX_PATH; count8859 = MAX_PATH/3; break; + case eCS_TRACK: + /*Fall-Through*/ + case eCS_INDEX_01: + /*Fall-Through*/ + case eCS_COUNT_TAGS_COUNT: + /*Fall-Through*/ + case eCS_NOTFOUND: /*Shouldn't happen*/ + logf(HZ * 2, "Bad Tag %d @ %s", (int) option, __func__); + dest = NULL; + break; } if (dest) @@ -315,10 +363,11 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) } else { - strlcpy(dest, string, count); + strmemccpy(dest, string, count); } } } + if (is_embedded) { bytes_left -= line_len; @@ -337,7 +386,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) strcpy(cue->file, cue->path); char *slash = strrchr(cue->file, '/'); if (!slash++) slash = cue->file; - strlcpy(slash, line, MAX_PATH - (slash - cue->file)); + strmemccpy(slash, line, MAX_PATH - (slash - cue->file)); } /* If some songs don't have performer info, we copy the cuesheet performer */ @@ -345,10 +394,10 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) for (i = 0; i < cue->track_count; i++) { if (*(cue->tracks[i].performer) == '\0') - strlcpy(cue->tracks[i].performer, cue->performer, MAX_NAME*3); + strmemccpy(cue->tracks[i].performer, cue->performer, MAX_NAME*3); if (*(cue->tracks[i].songwriter) == '\0') - strlcpy(cue->tracks[i].songwriter, cue->songwriter, MAX_NAME*3); + strmemccpy(cue->tracks[i].songwriter, cue->songwriter, MAX_NAME*3); } return true; @@ -392,7 +441,7 @@ static const char* list_get_name_cb(int selected_item, struct cuesheet *cue = (struct cuesheet *)data; if (selected_item & 1) - strlcpy(buffer, cue->tracks[selected_item/2].title, buffer_len); + strmemccpy(buffer, cue->tracks[selected_item/2].title, buffer_len); else snprintf(buffer, buffer_len, "%02d. %s", selected_item/2+1, cue->tracks[selected_item/2].performer); @@ -432,7 +481,7 @@ void browse_cuesheet(struct cuesheet *cue) { gui_synclist_draw(&lists); action = get_action(CONTEXT_LIST,TIMEOUT_BLOCK); - if (gui_synclist_do_button(&lists, &action, LIST_WRAP_UNLESS_HELD)) + if (gui_synclist_do_button(&lists, &action)) continue; switch (action) { @@ -459,7 +508,7 @@ void browse_cuesheet(struct cuesheet *cue) /* check that this cue is the same one that would be found by a search from playback */ char file[MAX_PATH]; - strlcpy(file, cue->file, MAX_PATH); + strmemccpy(file, cue->file, MAX_PATH); if (!strcmp(cue->path, file) || /* if embedded */ (search_for_cuesheet(file, &cue_file) && @@ -486,7 +535,7 @@ bool display_cuesheet_content(char* filename) if (!cue || bufsize < sizeof(struct cuesheet)) return false; - strlcpy(cue_file.path, filename, MAX_PATH); + strmemccpy(cue_file.path, filename, MAX_PATH); cue_file.pos = 0; cue_file.size = 0; @@ -516,12 +565,12 @@ bool curr_cuesheet_skip(struct cuesheet *cue, int direction, unsigned long curr_ if (!(direction <= 0 && track == 0)) { /* If skipping forward, skip to next cuesheet segment. If skipping - backward before DEFAULT_SKIP_TRESH milliseconds have elapsed, skip + backward before DEFAULT_SKIP_THRESH milliseconds have elapsed, skip to previous cuesheet segment. If skipping backward after - DEFAULT_SKIP_TRESH seconds have elapsed, skip to the start of the + DEFAULT_SKIP_THRESH seconds have elapsed, skip to the start of the current cuesheet segment */ if (direction == 1 || - ((curr_pos - cue->tracks[track].offset) < DEFAULT_SKIP_TRESH)) + ((curr_pos - cue->tracks[track].offset) < DEFAULT_SKIP_THRESH)) { track += direction; } diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 33970da581..7fc93b315b 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -23,7 +23,7 @@ #include <stdlib.h> #include <stdio.h> #include <stdbool.h> -#include <string.h> +#include <string-extra.h> #include "lcd.h" #include "lang.h" #include "menu.h" @@ -39,7 +39,6 @@ #include "audio.h" #include "settings.h" #include "list.h" -#include "statusbar.h" #include "dir.h" #include "panic.h" #include "screens.h" @@ -128,12 +127,18 @@ #if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) #include "bootdata.h" +#include "multiboot.h" +#include "rbpaths.h" +#include "pathfuncs.h" +#include "rb-loader.h" #endif +#define SCREEN_MAX_CHARS (LCD_WIDTH / SYSFONT_WIDTH) + static const char* threads_getname(int selected_item, void *data, char *buffer, size_t buffer_len) { - (void)data; + int *x_offset = (int*) data; #if NUM_CORES > 1 if (selected_item < (int)NUM_CORES) @@ -153,36 +158,76 @@ static const char* threads_getname(int selected_item, void *data, struct thread_debug_info threadinfo; if (thread_get_debug_info(selected_item, &threadinfo) > 0) { - fmtstr = "%2d:" IF_COP(" (%d)") " %s" IF_PRIO(" %d %d") + fmtstr = "%2d:" IF_COP(" (%d)") " %s%n" IF_PRIO(" %d %d") IFN_SDL(" %2d%%") " %s"; } - - snprintf(buffer, buffer_len, fmtstr, + int status_len; + size_t len = snprintf(buffer, buffer_len, fmtstr, selected_item, IF_COP(threadinfo.core,) threadinfo.statusstr, + &status_len, IF_PRIO(threadinfo.base_priority, threadinfo.current_priority,) IFN_SDL(threadinfo.stack_usage,) threadinfo.name); - return buffer; + int start = 0; +#if LCD_WIDTH <= 128 + if (len >= SCREEN_MAX_CHARS) + { + int ch_offset = (*x_offset)%(len-1); + int rem = SCREEN_MAX_CHARS - (len - ch_offset); + if (rem > 0) + ch_offset -= rem; + + if (ch_offset > 0) + { + /* don't scroll the # and status */ + status_len++; + if ((unsigned int)ch_offset + status_len < buffer_len) + memmove(&buffer[ch_offset], &buffer[0], status_len); + start = ch_offset; + } + } +#else + (void) x_offset; + (void) len; +#endif + return &buffer[start]; } static int dbg_threads_action_callback(int action, struct gui_synclist *lists) { - (void)lists; + if (action == ACTION_NONE) + { + return ACTION_REDRAW; + } +#if LCD_WIDTH <= 128 + int *x_offset = ((int*) lists->data); + if (action == ACTION_STD_OK) + { + *x_offset += 1; action = ACTION_REDRAW; + } + else if (action != ACTION_UNKNOWN) + { + *x_offset = 0; + } +#else + (void) lists; +#endif return action; } /* Test code!!! */ static bool dbg_os(void) { struct simplelist_info info; + int xoffset = 0; + simplelist_info_init(&info, IF_COP("Core and ") "Stack usage:", - MAXTHREADS IF_COP( + NUM_CORES ), NULL); - info.hide_selection = true; - info.scroll_all = true; + MAXTHREADS IF_COP( + NUM_CORES ), &xoffset); + info.scroll_all = false; info.action_callback = dbg_threads_action_callback; info.get_name = threads_getname; return simplelist_show_list(&info); @@ -295,7 +340,6 @@ static bool dbg_cpuinfo(void) info.get_name = get_cpuinfo; info.action_callback = cpuinfo_cb; info.timeout = HZ; - info.hide_selection = true; info.scroll_all = true; return simplelist_show_list(&info); } @@ -330,6 +374,13 @@ static bool dbg_buffering_thread(void) struct buffering_debug d; size_t filebuflen = audio_get_filebuflen(); /* This is a size_t, but call it a long so it puts a - when it's bad. */ +#if LCD_WIDTH > 96 + #define STR_DATAREM "data_rem" + const char * const fmt_used = "%s: %6ld/%ld"; +#else /* clipzip, ?*/ + #define STR_DATAREM "remain" + const char * const fmt_used = "%s:%ld/%ld"; +#endif #ifndef CPU_MULTI_FREQUENCY boost_ticks = 0; @@ -366,13 +417,13 @@ static bool dbg_buffering_thread(void) screens[i].clear_display(); - screens[i].putsf(0, line++, "pcm: %6ld/%ld", (long) bufused, (long) bufsize); + screens[i].putsf(0, line++, fmt_used, "pcm", (long) bufused, (long) bufsize); gui_scrollbar_draw(&screens[i],0, line*8, screens[i].lcdwidth, 6, bufsize, 0, bufused, HORIZONTAL); line++; - screens[i].putsf(0, line++, "alloc: %6ld/%ld", audio_filebufused(), + screens[i].putsf(0, line++, fmt_used, "alloc", audio_filebufused(), (long) filebuflen); #if LCD_HEIGHT > 80 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_HEIGHT > 80) @@ -382,7 +433,7 @@ static bool dbg_buffering_thread(void) filebuflen, 0, audio_filebufused(), HORIZONTAL); line++; - screens[i].putsf(0, line++, "real: %6ld/%ld", (long)d.buffered_data, + screens[i].putsf(0, line++, fmt_used, "real", (long)d.buffered_data, (long)filebuflen); gui_scrollbar_draw(&screens[i],0, line*8, screens[i].lcdwidth, 6, @@ -391,7 +442,7 @@ static bool dbg_buffering_thread(void) } #endif - screens[i].putsf(0, line++, "usefl: %6ld/%ld", (long)(d.useful_data), + screens[i].putsf(0, line++, fmt_used, "usefl", (long)(d.useful_data), (long)filebuflen); #if LCD_HEIGHT > 80 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_HEIGHT > 80) @@ -403,7 +454,7 @@ static bool dbg_buffering_thread(void) } #endif - screens[i].putsf(0, line++, "data_rem: %ld", (long)d.data_rem); + screens[i].putsf(0, line++, "%s: %ld", STR_DATAREM, (long)d.data_rem); screens[i].putsf(0, line++, "track count: %2u", audio_track_count()); @@ -442,8 +493,10 @@ static bool dbg_buffering_thread(void) screens[i].setfont(FONT_UI); return false; +#undef STR_DATAREM } +#ifdef BUFLIB_DEBUG_PRINT static const char* bf_getname(int selected_item, void *data, char *buffer, size_t buffer_len) { @@ -463,13 +516,12 @@ static int bf_action_cb(int action, struct gui_synclist* list) else { splash(HZ/1, "Attempting a 64k allocation"); - int handle = core_alloc("test", 64<<10); + int handle = core_alloc(64<<10); splash(HZ/2, (handle > 0) ? "Success":"Fail"); /* for some reason simplelist doesn't allow adding items here if * info.get_name is given, so use normal list api */ gui_synclist_set_nb_items(list, core_get_num_blocks()); - if (handle > 0) - core_free(handle); + core_free(handle); } action = ACTION_REDRAW; } @@ -485,6 +537,7 @@ static bool dbg_buflib_allocs(void) info.timeout = HZ; return simplelist_show_list(&info); } +#endif /* BUFLIB_DEBUG_PRINT */ #if (CONFIG_PLATFORM & PLATFORM_NATIVE) static const char* dbg_partitions_getname(int selected_item, void *data, @@ -514,7 +567,6 @@ static bool dbg_partitions(void) struct simplelist_info info; simplelist_info_init(&info, "Partition Info", NUM_DRIVES * 4, NULL); info.selection_size = 2; - info.hide_selection = true; info.scroll_all = true; info.get_name = dbg_partitions_getname; return simplelist_show_list(&info); @@ -816,8 +868,7 @@ static int tsc2100debug_action_callback(int action, struct gui_synclist *lists) if (action == ACTION_STD_OK) { *page = (*page+1)%3; - snprintf(lists->title, 32, - "tsc2100 registers - Page %d", *page); + snprintf((char*)lists->title, 32, "tsc2100 registers - Page %d", *page); return ACTION_REDRAW; } return action; @@ -825,7 +876,8 @@ static int tsc2100debug_action_callback(int action, struct gui_synclist *lists) static bool tsc2100_debug(void) { int page = 0; - char title[32] = "tsc2100 registers - Page 0"; + char title[32]; + snprintf(title, 32, "tsc2100 registers - Page %d", page); struct simplelist_info info; simplelist_info_init(&info, title, 32, &page); info.timeout = HZ/100; @@ -876,7 +928,7 @@ static bool view_battery(void) else grid = 5; - lcd_putsf(0, 0, "battery %d.%03dV", power_history[0] / 1000, + lcd_putsf(0, 0, "%s %d.%03dV", "Battery", power_history[0] / 1000, power_history[0] % 1000); lcd_putsf(0, 1, "%d.%03d-%d.%03dV (%2dmV)", minv / 1000, minv % 1000, maxv / 1000, maxv % 1000, @@ -887,7 +939,7 @@ static bool view_battery(void) grid = 10; else grid = 1; - lcd_putsf(0, 0, "battery %d%%", power_history[0]); + lcd_putsf(0, 0, "%s %d%%", "Battery", power_history[0]); lcd_putsf(0, 1, "%d%%-%d%% (%d %%)", minv, maxv, grid); #endif @@ -942,16 +994,16 @@ static bool view_battery(void) lcd_putsf(0, 0, "Pwr status: %s", charging_state() ? "charging" : "discharging"); #else - lcd_puts(0, 0, "Power status: unknown"); + lcd_putsf(0, 0, "Pwr status: %s", "unknown"); #endif battery_read_info(&y, &z); if (y > 0) - lcd_putsf(0, 1, "Battery: %d.%03d V (%d %%)", y / 1000, y % 1000, z); + lcd_putsf(0, 1, "%s: %d.%03d V (%d %%)", "Battery", y / 1000, y % 1000, z); else if (z > 0) - lcd_putsf(0, 1, "Battery: %d %%", z); + lcd_putsf(0, 1, "%s: %d %%", "Battery", z); #ifdef ADC_EXT_POWER y = (adc_read(ADC_EXT_POWER) * EXT_SCALE_FACTOR) / 1000; - lcd_putsf(0, 2, "External: %d.%03d V", y / 1000, y % 1000); + lcd_putsf(0, 2, "%s: %d.%03d V", "External", y / 1000, y % 1000); #endif #if CONFIG_CHARGING #if defined IPOD_NANO || defined IPOD_VIDEO @@ -965,7 +1017,7 @@ static bool view_battery(void) usb_pwr ? "present" : "absent"); lcd_putsf(0, 4, "EXT pwr: %s", ext_pwr ? "present" : "absent"); - lcd_putsf(0, 5, "Battery: %s", + lcd_putsf(0, 5, "%s: %s", "Battery", charging ? "charging" : (usb_pwr||ext_pwr) ? "charged" : "discharging"); lcd_putsf(0, 6, "Dock mode: %s", dock ? "enabled" : "disabled"); @@ -1019,7 +1071,7 @@ static bool view_battery(void) lcd_putsf(0, line++, "State: %s", chrgstate_strings[y]); - lcd_putsf(0, line++, "Battery Switch: %s", + lcd_putsf(0, line++, "%s Switch: %s", "Battery", (st & POWER_INPUT_BATTERY) ? "On" : "Off"); y = chrgraw_adc_voltage(); @@ -1042,11 +1094,11 @@ static bool view_battery(void) y = battery_adc_temp(); if (y != INT_MIN) { - lcd_putsf(0, line++, "T Battery: %d\u00b0C (%d\u00b0F)", y, - (9*y + 160) / 5); + lcd_putsf(0, line++, "T %s: %d\u00b0C (%d\u00b0F)", + "Battery", y, (9*y + 160) / 5); } else { /* Conversion disabled */ - lcd_puts(0, line++, "T Battery: ?"); + lcd_putsf(0, line++, "T %s: ?", "Battery"); } #elif defined(HAVE_AS3514) && CONFIG_CHARGING static const char * const chrgstate_strings[] = @@ -1073,7 +1125,7 @@ static bool view_battery(void) y = pmu_read_battery_voltage(); lcd_putsf(17, 1, "RAW: %d.%03d V", y / 1000, y % 1000); y = pmu_read_battery_current(); - lcd_putsf(0, 2, "Battery current: %d mA", y); + lcd_putsf(0, 2, "%s current: %d mA", "Battery", y); lcd_putsf(0, 3, "PWRCON: %08x %08x", PWRCON, PWRCONEXT); lcd_putsf(0, 4, "CLKCON: %08x %03x %03x", CLKCON, CLKCON2, CLKCON3); lcd_putsf(0, 5, "PLL: %06x %06x %06x", PLL0PMS, PLL1PMS, PLL2PMS); @@ -1099,9 +1151,9 @@ static bool view_battery(void) lcd_putsf(0, 3, "Charger: %s", charger_inserted() ? "present" : "absent"); x = (avr_hid_hdq_read_short(HDQ_REG_TEMP) / 4) - 273; - lcd_putsf(0, 4, "Battery temperature: %d C", x); + lcd_putsf(0, 4, "%s temperature: %d C", "Battery", x); x = (avr_hid_hdq_read_short(HDQ_REG_AI) * 357) / 200; - lcd_putsf(0, 5, "Battery current: %d.%01d mA", x / 10, x % 10); + lcd_putsf(0, 5, "%s current: %d.%01d mA", "Battery", x / 10, x % 10); x = (avr_hid_hdq_read_short(HDQ_REG_AP) * 292) / 20; lcd_putsf(0, 6, "Discharge power: %d.%01d mW", x / 10, x % 10); x = (avr_hid_hdq_read_short(HDQ_REG_SAE) * 292) / 2; @@ -1139,13 +1191,17 @@ static bool view_battery(void) power_history[0] % 1000); #endif - lcd_putsf(0, 6, "battery level: %d%%", battery_level()); + lcd_putsf(0, 6, "%s level: %d%%", "Battery", battery_level()); int time_left = battery_time(); if (time_left >= 0) lcd_putsf(0, 7, "Est. remain: %d m", time_left); else lcd_puts(0, 7, "Estimation n/a"); + +#if (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE) + lcd_putsf(0, 8, "%s current: %d mA", "Battery", battery_current()); +#endif break; } @@ -1189,7 +1245,8 @@ static int disk_callback(int btn, struct gui_synclist *lists) int *cardnum = (int*)lists->data; unsigned char card_name[6]; unsigned char pbuf[32]; - char *title = lists->title; + /* Casting away const is safe; the buffer is defined as non-const. */ + char *title = (char *)lists->title; static const unsigned char i_vmin[] = { 0, 1, 5, 10, 25, 35, 60, 100 }; static const unsigned char i_vmax[] = { 1, 5, 10, 25, 35, 45, 80, 200 }; static const unsigned char * const kbit_units[] = { "kBit/s", "MBit/s", "GBit/s" }; @@ -1219,7 +1276,7 @@ static int disk_callback(int btn, struct gui_synclist *lists) { card_name[i] = card_extract_bits(card->cid, (103-8*i), 8); } - strlcpy(card_name, card_name, sizeof(card_name)); + strmemccpy(card_name, card_name, sizeof(card_name)); simplelist_addline( "%s Rev %d.%d", card_name, (int) card_extract_bits(card->cid, 63, 4), @@ -1572,7 +1629,8 @@ static int ata_smart_attr_to_string( if (len >= name_sz) len = name_sz-1; slen += len; } - snprintf(str+slen, size-slen, "%s", buf); + + strmemccpy(str+slen, buf, size-slen); } return 1; /* ok */ @@ -1663,7 +1721,6 @@ static bool dbg_ata_smart(void) struct simplelist_info info; simplelist_info_init(&info, "S.M.A.R.T. Data [CONTEXT to dump]", 1, NULL); info.action_callback = ata_smart_callback; - info.hide_selection = true; info.scroll_all = true; return simplelist_show_list(&info); } @@ -1717,7 +1774,6 @@ static bool dbg_disk_info(void) info.title = title; #endif info.action_callback = disk_callback; - info.hide_selection = true; info.scroll_all = true; return simplelist_show_list(&info); } @@ -1786,7 +1842,6 @@ static bool dbg_dircache_info(void) int syncbuild = 0; simplelist_info_init(&info, "Dircache Info", 8, &syncbuild); info.action_callback = dircache_callback; - info.hide_selection = true; info.scroll_all = true; return simplelist_show_list(&info); } @@ -1799,6 +1854,7 @@ static int database_callback(int btn, struct gui_synclist *lists) (void)lists; struct tagcache_stat *stat = tagcache_get_stat(); static bool synced = false; + static int update_entries = 0; simplelist_set_line_count(0); @@ -1810,6 +1866,8 @@ static int database_callback(int btn, struct gui_synclist *lists) stat->ramcache ? "Yes" : "No"); simplelist_addline("RAM: %d/%d B", stat->ramcache_used, stat->ramcache_allocated); + simplelist_addline("Total entries: %d", + stat->total_entries); simplelist_addline("Progress: %d%% (%d entries)", stat->progress, stat->processed_entries); simplelist_addline("Curfile: %s", @@ -1831,12 +1889,19 @@ static int database_callback(int btn, struct gui_synclist *lists) if (!btn && stat->curentry) { synced = true; - return ACTION_REDRAW; + if (update_entries <= stat->processed_entries) + { + update_entries = stat->processed_entries + 100; + return ACTION_REDRAW; + } + return ACTION_NONE; } if (btn == ACTION_STD_CANCEL) + { + update_entries = 0; tagcache_screensync_enable(false); - + } return btn; } static bool dbg_tagcache_info(void) @@ -1844,7 +1909,6 @@ static bool dbg_tagcache_info(void) struct simplelist_info info; simplelist_info_init(&info, "Database Info", 8, NULL); info.action_callback = database_callback; - info.hide_selection = true; info.scroll_all = true; /* Don't do nonblock here, must give enough processing time @@ -2026,12 +2090,12 @@ static int radio_callback(int btn, struct gui_synclist *lists) tea5767_dbg_info(&nfo); simplelist_addline("Philips regs:"); simplelist_addline( - " Read: %02X %02X %02X %02X %02X", + " %s: %02X %02X %02X %02X %02X", "Read", (unsigned)nfo.read_regs[0], (unsigned)nfo.read_regs[1], (unsigned)nfo.read_regs[2], (unsigned)nfo.read_regs[3], (unsigned)nfo.read_regs[4]); simplelist_addline( - " Write: %02X %02X %02X %02X %02X", + " %s: %02X %02X %02X %02X %02X", "Write", (unsigned)nfo.write_regs[0], (unsigned)nfo.write_regs[1], (unsigned)nfo.write_regs[2], (unsigned)nfo.write_regs[3], (unsigned)nfo.write_regs[4]); @@ -2097,7 +2161,7 @@ static int radio_callback(int btn, struct gui_synclist *lists) struct tm* time = gmtime(&seconds); simplelist_addline( - "CT:%4d-%02d-%02d %02d:%02d", + "CT:%4d-%02d-%02d %02d:%02d:%02d", time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); } @@ -2117,7 +2181,6 @@ static bool dbg_fm_radio(void) radio_hardware_present() ? "yes" : "no"); info.action_callback = radio_hardware_present()?radio_callback : NULL; - info.hide_selection = true; return simplelist_show_list(&info); } #endif /* CONFIG_TUNER */ @@ -2246,11 +2309,6 @@ static bool cpu_boost_log(void) static bool cpu_boost_log_dump(void) { int fd; -#if CONFIG_RTC - struct tm *nowtm; - char fname[MAX_PATH]; -#endif - int count = cpu_boost_log_getcount(); char *str = cpu_boost_log_getlog_first(); @@ -2261,11 +2319,12 @@ static bool cpu_boost_log_dump(void) return false; #if CONFIG_RTC - nowtm = get_time(); - snprintf(fname, MAX_PATH, "%s/boostlog_%04d%02d%02d%02d%02d%02d.txt", ROCKBOX_DIR, - nowtm->tm_year + 1900, nowtm->tm_mon + 1, nowtm->tm_mday, - nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec); - fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC); + char fname[MAX_PATH]; + struct tm *nowtm = get_time(); + fd = open_pathfmt(fname, sizeof(fname), O_CREAT|O_WRONLY|O_TRUNC, + "%s/boostlog_%04d%02d%02d%02d%02d%02d.txt", ROCKBOX_DIR, + nowtm->tm_year + 1900, nowtm->tm_mon + 1, nowtm->tm_mday, + nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec); #else fd = open(ROCKBOX_DIR "/boostlog.txt", O_CREAT|O_WRONLY|O_TRUNC, 0666); #endif @@ -2389,7 +2448,6 @@ static bool dbg_talk(void) else simplelist_info_init(&list, "Voice Information:", 2, &data); list.scroll_all = true; - list.hide_selection = true; list.timeout = HZ; list.get_name = dbg_talk_get_name; @@ -2429,7 +2487,6 @@ static bool dbg_isp1583(void) isp1583.scroll_all = true; simplelist_info_init(&isp1583, "ISP1583", dbg_usb_num_items(), NULL); isp1583.timeout = HZ/100; - isp1583.hide_selection = true; isp1583.get_name = dbg_usb_item; isp1583.action_callback = isp1583_action_callback; return simplelist_show_list(&isp1583); @@ -2455,83 +2512,34 @@ static bool dbg_pic(void) pic.scroll_all = true; simplelist_info_init(&pic, "PIC", pic_dbg_num_items(), NULL); pic.timeout = HZ/100; - pic.hide_selection = true; pic.get_name = pic_dbg_item; pic.action_callback = pic_action_callback; return simplelist_show_list(&pic); } #endif -static bool dbg_skin_engine(void) -{ - struct simplelist_info info; - int i, total = 0; -#if defined(HAVE_BACKDROP_IMAGE) - int ref_count; - char *path; - size_t bytes; - int path_prefix_len = strlen(ROCKBOX_DIR "/wps/"); -#endif - simplelist_info_init(&info, "Skin engine usage", 0, NULL); - simplelist_set_line_count(0); - info.hide_selection = true; - FOR_NB_SCREENS(j) { -#if NB_SCREENS > 1 - simplelist_addline("%s display:", - j == 0 ? "Main" : "Remote"); -#endif - for (i = 0; i < skin_get_num_skins(); i++) { - struct skin_stats *stats = skin_get_stats(i, j); - if (stats->buflib_handles) - { - simplelist_addline("Skin ID: %d, %d allocations", - i, stats->buflib_handles); - simplelist_addline("\tskin: %d bytes", - stats->tree_size); - simplelist_addline("\tImages: %d bytes", - stats->images_size); - simplelist_addline("\tTotal: %d bytes", - stats->tree_size + stats->images_size); - total += stats->tree_size + stats->images_size; - } - } - } - simplelist_addline("Skin total usage: %d bytes", total); -#if defined(HAVE_BACKDROP_IMAGE) - simplelist_addline("Backdrop Images:"); - i = 0; - while (skin_backdrop_get_debug(i++, &path, &ref_count, &bytes)) { - if (ref_count > 0) { - - if (!strncasecmp(path, ROCKBOX_DIR "/wps/", path_prefix_len)) - path += path_prefix_len; - simplelist_addline("%s", path); - simplelist_addline("\tref_count: %d", ref_count); - simplelist_addline("\tsize: %d", bytes); - total += bytes; - } - } - simplelist_addline("Total usage: %d bytes", total); -#endif - return simplelist_show_list(&info); -} - #if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) static bool dbg_boot_data(void) { - unsigned int crc = 0; + unsigned int crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff); struct simplelist_info info; info.scroll_all = true; simplelist_info_init(&info, "Boot data", 1, NULL); simplelist_set_line_count(0); - crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff); + #if defined(HAVE_MULTIBOOT) + char rootpath[MAX_PATH / 2] = RB_ROOT_CONTENTS_DIR; int boot_volume = 0; if(crc == boot_data.crc) { boot_volume = boot_data.boot_volume; /* boot volume contained in uint8_t payload */ + int rtlen = get_redirect_dir(rootpath, sizeof(rootpath), boot_volume, "", ""); + while (rtlen > 0 && rootpath[--rtlen] == PATH_SEPCH) /* remove extra separators */ + rootpath[rtlen] = '\0'; } simplelist_addline("Boot Volume: <%lu>", boot_volume); + simplelist_addline("Root:"); + simplelist_addline("%s", rootpath); simplelist_addline(""); #endif simplelist_addline("Bootdata RAW:"); @@ -2547,7 +2555,6 @@ static bool dbg_boot_data(void) boot_data.payload[i+1], boot_data.payload[i+2], boot_data.payload[i+3]); } - info.hide_selection = true; return simplelist_show_list(&info); } #endif /* defined(HAVE_BOOTDATA) && !defined(SIMULATOR) */ @@ -2624,7 +2631,9 @@ static const struct { #ifdef PM_DEBUG { "pm histogram", peak_meter_histogram}, #endif /* PM_DEBUG */ +#ifdef BUFLIB_DEBUG_PRINT { "View buflib allocs", dbg_buflib_allocs }, +#endif #ifndef SIMULATOR #if CONFIG_TUNER { "FM Radio", dbg_fm_radio }, @@ -2691,6 +2700,24 @@ static const char* menu_get_name(int item, void * data, return menuitems[item].desc; } +static int menu_get_talk(int item, void *data) +{ + (void)data; + if (global_settings.talk_menu && menuitems[item].desc) + { + talk_number(item + 1, true); + talk_id(VOICE_PAUSE, true); +#if 0 /* no debug items currently have lang ids */ + long id = P2ID((const unsigned char *)(menuitems[item].desc)); + if(id>=0) + talk_id(id, true); + else +#endif + talk_spell(menuitems[item].desc, true); + } + return 0; +} + int debug_menu(void) { struct simplelist_info info; @@ -2698,6 +2725,7 @@ int debug_menu(void) simplelist_info_init(&info, "Debug Menu", ARRAYLEN(menuitems), NULL); info.action_callback = menu_action_callback; info.get_name = menu_get_name; + info.get_talk = menu_get_talk; return (simplelist_show_list(&info)) ? 1 : 0; } diff --git a/apps/enc_config.c b/apps/enc_config.c index 65ef65667a..b70ac03888 100644 --- a/apps/enc_config.c +++ b/apps/enc_config.c @@ -193,9 +193,9 @@ static bool mp3_enc_bitrate(struct menucallback_data *data) } /* mp3_enc_bitrate */ /* mp3_enc configuration menu */ -MENUITEM_FUNCTION(mp3_bitrate, MENU_FUNC_USEPARAM, ID2P(LANG_BITRATE), - mp3_enc_bitrate, - &menu_callback_data, enc_menuitem_callback, Icon_NOICON); +MENUITEM_FUNCTION_W_PARAM(mp3_bitrate, 0, ID2P(LANG_BITRATE), + mp3_enc_bitrate, &menu_callback_data, + enc_menuitem_callback, Icon_NOICON); MAKE_MENU( mp3_enc_menu, ID2P(LANG_ENCODER_SETTINGS), enc_menuitem_enteritem, Icon_NOICON, &mp3_bitrate); @@ -286,7 +286,7 @@ static int enc_menuitem_callback(int action, { (void)this_list; struct menucallback_data *data = - (struct menucallback_data*)this_item->function->param; + (struct menucallback_data*)this_item->function_param->param; if (action == ACTION_EXIT_MENUITEM) { diff --git a/apps/features.txt b/apps/features.txt index 83c3f0a65f..bafaa11599 100644 --- a/apps/features.txt +++ b/apps/features.txt @@ -105,6 +105,10 @@ radio_remote #endif #endif +#if defined(HAVE_RDS_CAP) +rds +#endif + #if defined(HAVE_RECORDING) recording #if defined(HAVE_LINE_IN) @@ -262,6 +266,8 @@ recording_digital #if MEMORYSIZE <= 2 lowmem +#elif MEMORYSIZE > 8 +himem #endif #if defined(HAVE_HARDWARE_CLICK) @@ -283,3 +289,14 @@ multi_boot #if defined(HIBY_LINUX) hibylinux #endif + +#if defined(BUTTON_REC) || \ + (CONFIG_KEYPAD == GIGABEAT_PAD) || \ + (CONFIG_KEYPAD == IPOD_4G_PAD) || \ + (CONFIG_KEYPAD == IRIVER_H10_PAD) +clear_settings_on_hold +#endif + +#if defined(HAVE_PERCEPTUAL_VOLUME) +perceptual_volume +#endif diff --git a/apps/filetree.c b/apps/filetree.c index efe5e80a0f..b652b6515b 100644 --- a/apps/filetree.c +++ b/apps/filetree.c @@ -51,11 +51,28 @@ #endif #include "wps.h" -static int compare_sort_dir; /* qsort key for sorting directories */ +static struct compare_data +{ + int sort_dir; /* qsort key for sorting directories */ + int(*_compar)(const char*, const char*, size_t); +} cmp_data; + +/* dummmy functions to allow compatibility with strncmp & strncasecmp */ +static int strnatcmp_n(const char *a, const char *b, size_t n) +{ + (void)n; + return strnatcmp(a, b); +} +static int strnatcasecmp_n(const char *a, const char *b, size_t n) +{ + (void)n; + return strnatcasecmp(a, b); +} int ft_build_playlist(struct tree_context* c, int start_index) { int i; + int res = 0; int start=start_index; tree_lock_cache(c); @@ -65,7 +82,8 @@ int ft_build_playlist(struct tree_context* c, int start_index) { if((entries[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) { - if (playlist_add(entries[i].name) < 0) + res = playlist_add(entries[i].name); + if (res < 0) break; } else @@ -77,7 +95,6 @@ int ft_build_playlist(struct tree_context* c, int start_index) } tree_unlock_cache(c); - return start_index; } @@ -89,7 +106,8 @@ int ft_build_playlist(struct tree_context* c, int start_index) * avoid allocating yet another path buffer on the stack (and save some * code; the caller typically needs to create the full pathname anyway)... */ -bool ft_play_playlist(char* pathname, char* dirname, char* filename, bool skip_dyn_warning) +bool ft_play_playlist(char* pathname, char* dirname, + char* filename, bool skip_warn_and_bookmarks) { if (global_settings.party_mode && audio_status()) { @@ -97,22 +115,15 @@ bool ft_play_playlist(char* pathname, char* dirname, char* filename, bool skip_d return false; } - if (bookmark_autoload(pathname)) + if (!skip_warn_and_bookmarks) { - return false; + int res = bookmark_autoload(pathname); + if (res == BOOKMARK_CANCEL || res == BOOKMARK_DO_RESUME || !warn_on_pl_erase()) + return false; } splash(0, ID2P(LANG_WAIT)); - /* about to create a new current playlist... - * allow user to cancel the operation. - * Do not show if skip_dyn_warning is true */ - if (!skip_dyn_warning) - { - if (!warn_on_pl_erase()) - return false; - } - if (playlist_create(dirname, filename) != -1) { if (global_settings.playlist_shuffle) @@ -198,7 +209,7 @@ static int compare(const void* p1, const void* p2) if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY) { /* two directories */ - criteria = compare_sort_dir; + criteria = cmp_data.sort_dir; #ifdef HAVE_MULTIVOLUME if (e1->attr & ATTR_VOLUME || e2->attr & ATTR_VOLUME) @@ -233,41 +244,23 @@ static int compare(const void* p1, const void* p2) if (t1 != t2) /* if different */ return (t1 - t2) * (criteria == SORT_TYPE_REVERSED ? -1 : 1); - /* else fall through to alphabetical sorting */ + /* else alphabetical sorting */ + return cmp_data._compar(e1->name, e2->name, MAX_PATH); } case SORT_DATE: case SORT_DATE_REVERSED: - /* Ignore SORT_TYPE */ - if (criteria == SORT_DATE || criteria == SORT_DATE_REVERSED) - { - if (e1->time_write != e2->time_write) - return (e1->time_write - e2->time_write) - * (criteria == SORT_DATE_REVERSED ? -1 : 1); - /* else fall through to alphabetical sorting */ - } - + { + if (e1->time_write != e2->time_write) + return (e1->time_write - e2->time_write) + * (criteria == SORT_DATE_REVERSED ? -1 : 1); + /* else fall through to alphabetical sorting */ + } case SORT_ALPHA: case SORT_ALPHA_REVERSED: { - if (global_settings.sort_case) - { - if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER) - return strnatcmp(e1->name, e2->name) - * (criteria == SORT_ALPHA_REVERSED ? -1 : 1); - else - return strncmp(e1->name, e2->name, MAX_PATH) - * (criteria == SORT_ALPHA_REVERSED ? -1 : 1); - } - else - { - if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER) - return strnatcasecmp(e1->name, e2->name) - * (criteria == SORT_ALPHA_REVERSED ? -1 : 1); - else - return strncasecmp(e1->name, e2->name, MAX_PATH) - * (criteria == SORT_ALPHA_REVERSED ? -1 : 1); - } + return cmp_data._compar(e1->name, e2->name, MAX_PATH) * + (criteria == SORT_ALPHA_REVERSED ? -1 : 1); } } @@ -330,42 +323,43 @@ int ft_load(struct tree_context* c, const char* tempdir) } dptr->attr = info.attribute; + int dir_attr = (dptr->attr & ATTR_DIRECTORY); /* check for known file types */ - if ( !(dptr->attr & ATTR_DIRECTORY) ) + if ( !(dir_attr) ) dptr->attr |= filetype_get_attr((char *)entry->d_name); + int file_attr = (dptr->attr & FILE_ATTR_MASK); + /* filter out non-visible files */ - if ((!(dptr->attr & ATTR_DIRECTORY) && ( - (*c->dirfilter == SHOW_PLAYLIST && - (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) || - ((*c->dirfilter == SHOW_MUSIC && - (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) && - (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) || + if ((!(dir_attr) && ((*c->dirfilter == SHOW_PLAYLIST && + file_attr != FILE_ATTR_M3U) || + ((*c->dirfilter == SHOW_MUSIC && file_attr != FILE_ATTR_AUDIO) && + file_attr != FILE_ATTR_M3U) || (*c->dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)))) || - (*c->dirfilter == SHOW_WPS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_WPS) || - (*c->dirfilter == SHOW_FONT && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FONT) || - (*c->dirfilter == SHOW_SBS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_SBS) || + (*c->dirfilter == SHOW_WPS && file_attr != FILE_ATTR_WPS) || + (*c->dirfilter == SHOW_FONT && file_attr != FILE_ATTR_FONT) || + (*c->dirfilter == SHOW_SBS && file_attr != FILE_ATTR_SBS) || #if CONFIG_TUNER - (*c->dirfilter == SHOW_FMS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FMS) || + (*c->dirfilter == SHOW_FMS && file_attr != FILE_ATTR_FMS) || #endif #ifdef HAVE_REMOTE_LCD - (*c->dirfilter == SHOW_RWPS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RWPS) || - (*c->dirfilter == SHOW_RSBS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RSBS) || + (*c->dirfilter == SHOW_RWPS && file_attr != FILE_ATTR_RWPS) || + (*c->dirfilter == SHOW_RSBS && file_attr != FILE_ATTR_RSBS) || #if CONFIG_TUNER - (*c->dirfilter == SHOW_RFMS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RFMS) || + (*c->dirfilter == SHOW_RFMS && file_attr != FILE_ATTR_RFMS) || #endif #endif #if CONFIG_TUNER - (*c->dirfilter == SHOW_FMR && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FMR) || + (*c->dirfilter == SHOW_FMR && file_attr != FILE_ATTR_FMR) || #endif - (*c->dirfilter == SHOW_M3U && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) || - (*c->dirfilter == SHOW_CFG && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_CFG) || - (*c->dirfilter == SHOW_LNG && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_LNG) || - (*c->dirfilter == SHOW_MOD && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_MOD) || - (*c->dirfilter == SHOW_PLUGINS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_ROCK && - (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_LUA && - (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_OPX) || + (*c->dirfilter == SHOW_M3U && file_attr != FILE_ATTR_M3U) || + (*c->dirfilter == SHOW_CFG && file_attr != FILE_ATTR_CFG) || + (*c->dirfilter == SHOW_LNG && file_attr != FILE_ATTR_LNG) || + (*c->dirfilter == SHOW_MOD && file_attr != FILE_ATTR_MOD) || + (*c->dirfilter == SHOW_PLUGINS && file_attr != FILE_ATTR_ROCK && + file_attr != FILE_ATTR_LUA && + file_attr != FILE_ATTR_OPX) || (callback_show_item && !callback_show_item(entry->d_name, dptr->attr, c))) { continue; @@ -384,14 +378,29 @@ int ft_load(struct tree_context* c, const char* tempdir) strcpy(dptr->name, (char *)entry->d_name); name_buffer_used += len + 1; - if (dptr->attr & ATTR_DIRECTORY) /* count the remaining dirs */ + if (dir_attr) /* count the remaining dirs */ c->dirsindir++; } c->filesindir = files_in_dir; c->dirlength = files_in_dir; closedir(dir); - compare_sort_dir = c->sort_dir; + cmp_data.sort_dir = c->sort_dir; + if (global_settings.sort_case) + { + if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER) + cmp_data._compar = strnatcmp_n; + else + cmp_data._compar = strncmp; + } + else + { + if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER) + cmp_data._compar = strnatcasecmp_n; + else + cmp_data._compar = strncasecmp; + } + qsort(tree_get_entries(c), files_in_dir, sizeof(struct entry), compare); /* If thumbnail talking is enabled, make an extra run to mark files with @@ -432,10 +441,17 @@ static void ft_load_font(char *file) viewportmanager_theme_changed(THEME_UI_VIEWPORT); } +static void ft_apply_skin_file(char *buf, char *file, const int maxlen) +{ + splash(0, ID2P(LANG_WAIT)); + set_file(buf, file, maxlen); + settings_apply_skins(); +} + int ft_enter(struct tree_context* c) { int rc = GO_TO_PREVIOUS; - static char buf[MAX_PATH]; + char buf[MAX_PATH]; struct entry* file = tree_get_entry_at(c, c->selected_item); if (!file) @@ -480,7 +496,9 @@ int ft_enter(struct tree_context* c) break; case FILE_ATTR_AUDIO: - if (bookmark_autoload(c->currdir)) + { + int res = bookmark_autoload(c->currdir); + if (res == BOOKMARK_CANCEL || res == BOOKMARK_DO_RESUME) break; splash(0, ID2P(LANG_WAIT)); @@ -514,7 +532,7 @@ int ft_enter(struct tree_context* c) play = true; } break; - + } #if CONFIG_TUNER /* fmr preset file */ case FILE_ATTR_FMR: @@ -539,49 +557,32 @@ int ft_enter(struct tree_context* c) break; case FILE_ATTR_FMS: - splash(0, ID2P(LANG_WAIT)); - set_file(buf, (char *)global_settings.fms_file, MAX_FILENAME); - settings_apply_skins(); + ft_apply_skin_file(buf, global_settings.fms_file, MAX_FILENAME); break; #ifdef HAVE_REMOTE_LCD case FILE_ATTR_RFMS: - splash(0, ID2P(LANG_WAIT)); - set_file(buf, (char *)global_settings.rfms_file, MAX_FILENAME); - settings_apply_skins(); + ft_apply_skin_file(buf, global_settings.rfms_file, MAX_FILENAME); break; #endif #endif - case FILE_ATTR_SBS: - splash(0, ID2P(LANG_WAIT)); - set_file(buf, (char *)global_settings.sbs_file, MAX_FILENAME); - settings_apply_skins(); + ft_apply_skin_file(buf, global_settings.sbs_file, MAX_FILENAME); break; #ifdef HAVE_REMOTE_LCD case FILE_ATTR_RSBS: - splash(0, ID2P(LANG_WAIT)); - set_file(buf, (char *)global_settings.rsbs_file, MAX_FILENAME); - settings_apply_skins(); + ft_apply_skin_file(buf, global_settings.rsbs_file, MAX_FILENAME); break; #endif /* wps config file */ case FILE_ATTR_WPS: - splash(0, ID2P(LANG_WAIT)); - set_file(buf, (char *)global_settings.wps_file, - MAX_FILENAME); - settings_apply_skins(); + ft_apply_skin_file(buf, global_settings.wps_file, MAX_FILENAME); break; - #if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1) /* remote-wps config file */ case FILE_ATTR_RWPS: - splash(0, ID2P(LANG_WAIT)); - set_file(buf, (char *)global_settings.rwps_file, - MAX_FILENAME); - settings_apply_skins(); + ft_apply_skin_file(buf, global_settings.rwps_file, MAX_FILENAME); break; #endif - case FILE_ATTR_CFG: splash(0, ID2P(LANG_WAIT)); if (!settings_load_config(buf,true)) @@ -641,6 +642,8 @@ int ft_enter(struct tree_context* c) splash(HZ, ID2P(LANG_PARTY_MODE)); break; } + +#ifdef PLUGINS_RUN_IN_BROWSER /* Stay in the filetree to run a plugin */ switch (plugin_load(plugin, argument)) { case PLUGIN_GOTO_WPS: @@ -664,6 +667,10 @@ int ft_enter(struct tree_context* c) default: break; } +#else /* Exit the filetree to run a plugin */ + plugin_open(plugin, argument); + rc = GO_TO_PLUGIN; +#endif break; } @@ -677,7 +684,7 @@ int ft_enter(struct tree_context* c) break; } - struct entry* file = tree_get_entry_at(c, c->selected_item); + file = tree_get_entry_at(c, c->selected_item); if (!file) { splashf(HZ, str(LANG_READ_FAILED), str(LANG_UNKNOWN)); @@ -687,6 +694,7 @@ int ft_enter(struct tree_context* c) plugin = filetype_get_plugin(file, plugin_path, sizeof(plugin_path)); if (plugin) { +#ifdef PLUGINS_RUN_IN_BROWSER /* Stay in the filetree to run a plugin */ switch (plugin_load(plugin, argument)) { case PLUGIN_USB_CONNECTED: @@ -705,6 +713,10 @@ int ft_enter(struct tree_context* c) default: break; } +#else /* Exit the filetree to run a plugin */ + plugin_open(plugin, argument); + rc = GO_TO_PLUGIN; +#endif } break; } diff --git a/apps/filetree.h b/apps/filetree.h index 178ba0e973..7931c3c454 100644 --- a/apps/filetree.h +++ b/apps/filetree.h @@ -26,6 +26,7 @@ int ft_load(struct tree_context* c, const char* tempdir); int ft_enter(struct tree_context* c); int ft_exit(struct tree_context* c); int ft_build_playlist(struct tree_context* c, int start_index); -bool ft_play_playlist(char* pathname, char* dirname, char* filename, bool skip_dyn_warning); +bool ft_play_playlist(char* pathname, char* dirname, + char* filename, bool skip_warn_and_bookmarks); #endif diff --git a/apps/filetypes.c b/apps/filetypes.c index d68bab3daa..1944ee9383 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -38,6 +38,7 @@ #include "splash.h" #include "core_alloc.h" #include "icons.h" +/*#define LOGF_ENABLE*/ #include "logf.h" /* max filetypes (plugins & icons stored here) */ @@ -45,111 +46,171 @@ /* max viewer plugins */ #define MAX_VIEWERS 56 +static void read_builtin_types_init(void) INIT_ATTR; +static void read_viewers_config_init(void) INIT_ATTR; +static void read_config_init(int fd) INIT_ATTR; + /* a table for the known file types */ static const struct filetype inbuilt_filetypes[] = { - { "mp3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "mp2", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "mpa", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "mp1", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "ogg", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "oga", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "wma", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "wmv", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "asf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "wav", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "flac",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "ac3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "a52", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "mpc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "wv", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "m4a", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "m4b", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "mp4", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "mod", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "shn", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "aif", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "aiff",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "spx" ,FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "opus",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "sid", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "adx", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "nsf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "nsfe",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "spc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "ape", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "mac", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "sap" ,FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "rm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "ra", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "rmvb",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "cmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "cm3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "cmr", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "cms", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "dmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "dlt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "mpt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "mpd", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "rmt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "tmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "tm8", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "tm2", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "oma", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "aa3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "at3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "mmf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "au", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "snd", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "vox", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "w64", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "tta", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "ay", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "vtx", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "gbs", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "hes", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "sgc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "vgm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "vgz", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "kss", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "aac", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, - { "m3u", FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, - { "m3u8",FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, - { "cfg", FILE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG }, - { "wps", FILE_ATTR_WPS, Icon_Wps, VOICE_EXT_WPS }, + { "mp3", FILE_ATTR_AUDIO }, + { "mp2", FILE_ATTR_AUDIO }, + { "mpa", FILE_ATTR_AUDIO }, + { "mp1", FILE_ATTR_AUDIO }, + { "ogg", FILE_ATTR_AUDIO }, + { "oga", FILE_ATTR_AUDIO }, + { "wma", FILE_ATTR_AUDIO }, + { "wmv", FILE_ATTR_AUDIO }, + { "asf", FILE_ATTR_AUDIO }, + { "wav", FILE_ATTR_AUDIO }, + { "flac", FILE_ATTR_AUDIO }, + { "ac3", FILE_ATTR_AUDIO }, + { "a52", FILE_ATTR_AUDIO }, + { "mpc", FILE_ATTR_AUDIO }, + { "wv", FILE_ATTR_AUDIO }, + { "m4a", FILE_ATTR_AUDIO }, + { "m4b", FILE_ATTR_AUDIO }, + { "mp4", FILE_ATTR_AUDIO }, + { "mod", FILE_ATTR_AUDIO }, + { "mpga", FILE_ATTR_AUDIO }, + { "shn", FILE_ATTR_AUDIO }, + { "aif", FILE_ATTR_AUDIO }, + { "aiff", FILE_ATTR_AUDIO }, + { "spx" , FILE_ATTR_AUDIO }, + { "opus", FILE_ATTR_AUDIO }, + { "sid", FILE_ATTR_AUDIO }, + { "adx", FILE_ATTR_AUDIO }, + { "nsf", FILE_ATTR_AUDIO }, + { "nsfe", FILE_ATTR_AUDIO }, + { "spc", FILE_ATTR_AUDIO }, + { "ape", FILE_ATTR_AUDIO }, + { "mac", FILE_ATTR_AUDIO }, + { "sap" , FILE_ATTR_AUDIO }, + { "rm", FILE_ATTR_AUDIO }, + { "ra", FILE_ATTR_AUDIO }, + { "rmvb", FILE_ATTR_AUDIO }, + { "cmc", FILE_ATTR_AUDIO }, + { "cm3", FILE_ATTR_AUDIO }, + { "cmr", FILE_ATTR_AUDIO }, + { "cms", FILE_ATTR_AUDIO }, + { "dmc", FILE_ATTR_AUDIO }, + { "dlt", FILE_ATTR_AUDIO }, + { "mpt", FILE_ATTR_AUDIO }, + { "mpd", FILE_ATTR_AUDIO }, + { "rmt", FILE_ATTR_AUDIO }, + { "tmc", FILE_ATTR_AUDIO }, + { "tm8", FILE_ATTR_AUDIO }, + { "tm2", FILE_ATTR_AUDIO }, + { "oma", FILE_ATTR_AUDIO }, + { "aa3", FILE_ATTR_AUDIO }, + { "at3", FILE_ATTR_AUDIO }, + { "mmf", FILE_ATTR_AUDIO }, + { "au", FILE_ATTR_AUDIO }, + { "snd", FILE_ATTR_AUDIO }, + { "vox", FILE_ATTR_AUDIO }, + { "w64", FILE_ATTR_AUDIO }, + { "tta", FILE_ATTR_AUDIO }, + { "ay", FILE_ATTR_AUDIO }, + { "vtx", FILE_ATTR_AUDIO }, + { "gbs", FILE_ATTR_AUDIO }, + { "hes", FILE_ATTR_AUDIO }, + { "sgc", FILE_ATTR_AUDIO }, + { "vgm", FILE_ATTR_AUDIO }, + { "vgz", FILE_ATTR_AUDIO }, + { "kss", FILE_ATTR_AUDIO }, + { "aac", FILE_ATTR_AUDIO }, + { "m3u", FILE_ATTR_M3U }, + { "m3u8", FILE_ATTR_M3U }, + { "cfg", FILE_ATTR_CFG }, + { "wps", FILE_ATTR_WPS }, #ifdef HAVE_REMOTE_LCD - { "rwps",FILE_ATTR_RWPS, Icon_Wps, VOICE_EXT_RWPS }, + { "rwps", FILE_ATTR_RWPS }, #endif #if CONFIG_TUNER - { "fmr", FILE_ATTR_FMR, Icon_Preset, LANG_FMR }, - { "fms", FILE_ATTR_FMS, Icon_Wps, VOICE_EXT_FMS }, + { "fmr", FILE_ATTR_FMR }, + { "fms", FILE_ATTR_FMS }, #endif - { "lng", FILE_ATTR_LNG, Icon_Language, LANG_LANGUAGE }, - { "rock",FILE_ATTR_ROCK,Icon_Plugin, VOICE_EXT_ROCK }, - { "lua", FILE_ATTR_LUA, Icon_Plugin, VOICE_EXT_ROCK }, - { "opx", FILE_ATTR_OPX, Icon_Plugin, VOICE_EXT_ROCK }, - { "fnt", FILE_ATTR_FONT,Icon_Font, VOICE_EXT_FONT }, - { "kbd", FILE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD }, - { "bmark",FILE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK }, - { "cue", FILE_ATTR_CUE, Icon_Bookmark, VOICE_EXT_CUESHEET }, - { "sbs", FILE_ATTR_SBS, Icon_Wps, VOICE_EXT_SBS }, + { "lng", FILE_ATTR_LNG }, + { "rock", FILE_ATTR_ROCK }, + { "lua", FILE_ATTR_LUA }, + { "opx", FILE_ATTR_OPX }, + { "fnt", FILE_ATTR_FONT }, + { "kbd", FILE_ATTR_KBD }, + { "bmark",FILE_ATTR_BMARK }, + { "cue", FILE_ATTR_CUE }, + { "sbs", FILE_ATTR_SBS }, #ifdef HAVE_REMOTE_LCD - { "rsbs", FILE_ATTR_RSBS, Icon_Wps, VOICE_EXT_RSBS }, + { "rsbs", FILE_ATTR_RSBS }, #if CONFIG_TUNER - { "rfms", FILE_ATTR_RFMS, Icon_Wps, VOICE_EXT_RFMS }, + { "rfms", FILE_ATTR_RFMS }, #endif #endif #ifdef BOOTFILE_EXT - { BOOTFILE_EXT, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, + { BOOTFILE_EXT, FILE_ATTR_MOD }, #endif #ifdef BOOTFILE_EXT2 - { BOOTFILE_EXT2, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, + { BOOTFILE_EXT2, FILE_ATTR_MOD }, +#endif +}; + +struct fileattr_icon_voice { + int tree_attr; + uint16_t icon; + uint16_t voiceclip; +}; + +/* a table for the known file types icons & voice clips */ +static const struct fileattr_icon_voice inbuilt_attr_icons_voices[] = { + { FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, + { FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, + { FILE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG }, + { FILE_ATTR_WPS, Icon_Wps, VOICE_EXT_WPS }, +#ifdef HAVE_REMOTE_LCD + {FILE_ATTR_RWPS, Icon_Wps, VOICE_EXT_RWPS }, +#endif +#if CONFIG_TUNER + { FILE_ATTR_FMR, Icon_Preset, LANG_FMR }, + { FILE_ATTR_FMS, Icon_Wps, VOICE_EXT_FMS }, +#endif + { FILE_ATTR_LNG, Icon_Language, LANG_LANGUAGE }, + { FILE_ATTR_ROCK, Icon_Plugin, VOICE_EXT_ROCK }, + { FILE_ATTR_LUA, Icon_Plugin, VOICE_EXT_ROCK }, + { FILE_ATTR_OPX, Icon_Plugin, VOICE_EXT_ROCK }, + { FILE_ATTR_FONT, Icon_Font, VOICE_EXT_FONT }, + { FILE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD }, + { FILE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK }, + { FILE_ATTR_CUE, Icon_Bookmark, VOICE_EXT_CUESHEET }, + { FILE_ATTR_SBS, Icon_Wps, VOICE_EXT_SBS }, +#ifdef HAVE_REMOTE_LCD + { FILE_ATTR_RSBS, Icon_Wps, VOICE_EXT_RSBS }, +#if CONFIG_TUNER + { FILE_ATTR_RFMS, Icon_Wps, VOICE_EXT_RFMS }, +#endif +#endif +#if defined(BOOTFILE_EXT) || defined(BOOTFILE_EXT2) + { FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, #endif }; -void tree_get_filetypes(const struct filetype** types, int* count) +long tree_get_filetype_voiceclip(int attr) { - *types = inbuilt_filetypes; - *count = sizeof(inbuilt_filetypes) / sizeof(*inbuilt_filetypes); + if (global_settings.talk_filetype) + { + size_t count = ARRAY_SIZE(inbuilt_attr_icons_voices); + /* try to find a voice ID for the extension, if known */ + attr &= FILE_ATTR_MASK; /* file type */ + + for (size_t i = count - 1; i < count; i--) + { + if (attr == inbuilt_attr_icons_voices[i].tree_attr) + { + logf("%s found attr %d id %d", __func__, attr, + inbuilt_attr_icons_voices[i].voiceclip); + return inbuilt_attr_icons_voices[i].voiceclip; + } + } + } + logf("%s not found attr %d", __func__, attr); + return -1; } #define ROCK_EXTENSION "rock" @@ -243,8 +304,6 @@ static int find_extension(const char* extension) return -1; } -static void read_builtin_types(void); -static void read_config(int fd); #ifdef HAVE_LCD_COLOR /* Colors file format is similar to icons: * ext:hex_color @@ -261,9 +320,9 @@ void read_color_theme_file(void) { unknown_file.color = -1; if (!global_settings.colors_file[0] || global_settings.colors_file[0] == '-') return; - snprintf(buffer, MAX_PATH, THEME_DIR "/%s.colours", - global_settings.colors_file); - fd = open(buffer, O_RDONLY); + + fd = open_pathfmt(buffer, sizeof(buffer), O_RDONLY, + THEME_DIR "/%s.colours", global_settings.colors_file); if (fd < 0) return; while (read_line(fd, buffer, MAX_PATH) > 0) @@ -301,12 +360,12 @@ void read_viewer_theme_file(void) { custom_filetype_icons[i] = filetypes[i].icon; } - - snprintf(buffer, MAX_PATH, "%s/%s.icons", ICON_DIR, - global_settings.viewers_icon_file); - fd = open(buffer, O_RDONLY); + + fd = open_pathfmt(buffer, sizeof(buffer), O_RDONLY, + ICON_DIR "/%s.icons", global_settings.viewers_icon_file); if (fd < 0) return; + while (read_line(fd, buffer, MAX_PATH) > 0) { if (!settings_parseline(buffer, &ext, &icon)) @@ -338,7 +397,7 @@ void read_viewer_theme_file(void) custom_icons_loaded = true; } -static void read_viewers_config(void) +static void read_viewers_config_init(void) { int fd = open(VIEWERS_CONFIG, O_RDONLY); if(fd < 0) @@ -350,18 +409,18 @@ static void read_viewers_config(void) /* estimate bufsize with the filesize, will not be larger */ strdup_bufsize = (size_t)filesz; - strdup_handle = core_alloc_ex("filetypes", strdup_bufsize, &ops); + strdup_handle = core_alloc_ex(strdup_bufsize, &ops); if(strdup_handle <= 0) goto out; - read_config(fd); - core_shrink(strdup_handle, core_get_data(strdup_handle), strdup_cur_idx); + read_config_init(fd); + core_shrink(strdup_handle, NULL, strdup_cur_idx); out: close(fd); } -void filetype_init(void) +void filetype_init(void) { /* set the directory item first */ filetypes[0].extension = NULL; @@ -372,8 +431,8 @@ void filetype_init(void) viewer_count = 0; filetype_count = 1; - read_builtin_types(); - read_viewers_config(); + read_builtin_types_init(); + read_viewers_config_init(); read_viewer_theme_file(); #ifdef HAVE_LCD_COLOR read_color_theme_file(); @@ -396,22 +455,35 @@ static void rm_whitespaces(char* str) *s = '\0'; } -static void read_builtin_types(void) +static void read_builtin_types_init(void) { - int count = sizeof(inbuilt_filetypes)/sizeof(*inbuilt_filetypes), i; - for(i=0; i<count && (filetype_count < MAX_FILETYPES); i++) + int tree_attr; + size_t count = ARRAY_SIZE(inbuilt_filetypes); + size_t icon_count = ARRAY_SIZE(inbuilt_attr_icons_voices); + for(size_t i = 0; (i < count) && (filetype_count < MAX_FILETYPES); i++) { filetypes[filetype_count].extension = inbuilt_filetypes[i].extension; filetypes[filetype_count].plugin = NULL; - filetypes[filetype_count].attr = inbuilt_filetypes[i].tree_attr>>8; + + tree_attr = inbuilt_filetypes[i].tree_attr; + filetypes[filetype_count].attr = tree_attr>>8; if (filetypes[filetype_count].attr > highest_attr) highest_attr = filetypes[filetype_count].attr; - filetypes[filetype_count].icon = inbuilt_filetypes[i].icon; + + filetypes[filetype_count].icon = unknown_file.icon; + for (size_t j = icon_count - 1; j < icon_count; j--) + { + if (tree_attr == inbuilt_attr_icons_voices[j].tree_attr) + { + filetypes[filetype_count].icon = inbuilt_attr_icons_voices[j].icon; + break; + } + } filetype_count++; } } -static void read_config(int fd) +static void read_config_init(int fd) { char line[64], *s, *e; char *extension, *plugin; @@ -617,7 +689,7 @@ int filetype_list_viewers(const char* current_file) int i = viewers[info.selection]; snprintf(plugin, MAX_PATH, "%s/%s." ROCK_EXTENSION, PLUGIN_DIR, filetypes[i].plugin); - plugin_load(plugin, current_file); + ret = plugin_load(plugin, current_file); } return ret; } diff --git a/apps/filetypes.h b/apps/filetypes.h index efe9f3f5df..5aae772a9c 100644 --- a/apps/filetypes.h +++ b/apps/filetypes.h @@ -53,14 +53,14 @@ struct filetype { char* extension; int tree_attr; - enum themable_icons icon; - int voiceclip; }; -void tree_get_filetypes(const struct filetype**, int*) INIT_ATTR; + +long tree_get_filetype_voiceclip(int attr); /* init the filetypes structs. uses audio buffer for storage, so call early in init... */ void filetype_init(void) INIT_ATTR; + void read_viewer_theme_file(void); #ifdef HAVE_LCD_COLOR void read_color_theme_file(void); diff --git a/apps/gui/bitmap/list-skinned.c b/apps/gui/bitmap/list-skinned.c index a67ac8cb0a..bebff821f8 100644 --- a/apps/gui/bitmap/list-skinned.c +++ b/apps/gui/bitmap/list-skinned.c @@ -213,8 +213,7 @@ bool skinlist_draw(struct screen *display, struct gui_synclist *list) if (list_start_item+cur_line+1 > list->nb_items) break; current_drawing_line = list_start_item+cur_line; - is_selected = list->show_selection_marker && - list_start_item+cur_line == list->selected_item; + is_selected = list_start_item+cur_line == list->selected_item; for (viewport = SKINOFFSETTOPTR(get_skin_buffer(wps.data), listcfg[screen]->data->tree); viewport; diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c index ff0f5a29c1..1c9b73a5fe 100644 --- a/apps/gui/bitmap/list.c +++ b/apps/gui/bitmap/list.c @@ -90,11 +90,57 @@ static int list_icon_width(enum screen_type screen) return get_icon_width(screen) + ICON_PADDING * 2; } -static bool draw_title(struct screen *display, struct gui_synclist *list) +static void _default_listdraw_fn(struct list_putlineinfo_t *list_info) +{ + struct screen *display = list_info->display; + int x = list_info->x; + int y = list_info->y; + int item_indent = list_info->item_indent; + int item_offset = list_info->item_offset; + int icon = list_info->icon; + bool is_selected = list_info->is_selected; + bool is_title = list_info->is_title; + bool show_cursor = list_info->show_cursor; + bool have_icons = list_info->have_icons; + struct line_desc *linedes = list_info->linedes; + const char *dsp_text = list_info->dsp_text; + + if (is_title) + { + if (have_icons) + display->put_line(x, y, linedes, "$"ICON_PADDING_S"I$t", + icon, dsp_text); + else + display->put_line(x, y, linedes, "$t", dsp_text); + } + else if (show_cursor && have_icons) + { + /* the list can have both, one of or neither of cursor and item icons, + * if both don't apply icon padding twice between the icons */ + display->put_line(x, y, + linedes, "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t", + item_indent, is_selected ? Icon_Cursor : Icon_NOICON, + icon, item_offset, dsp_text); + } + else if (show_cursor || have_icons) + { + display->put_line(x, y, linedes, "$*s$"ICON_PADDING_S"I$*t", item_indent, + show_cursor ? (is_selected ? Icon_Cursor:Icon_NOICON):icon, + item_offset, dsp_text); + } + else + { + display->put_line(x, y, linedes, "$*s$*t", item_indent, item_offset, dsp_text); + } +} + +static bool draw_title(struct screen *display, + struct gui_synclist *list, + list_draw_item *callback_draw_item) { const int screen = display->screen_type; struct viewport *title_text_vp = &title_text[screen]; - struct line_desc line = LINE_DESC_DEFINIT; + struct line_desc linedes = LINE_DESC_DEFINIT; if (sb_set_title_text(list->title, list->title_icon, screen)) return false; /* the sbs is handling the title */ @@ -102,29 +148,41 @@ static bool draw_title(struct screen *display, struct gui_synclist *list) if (!list_display_title(list, screen)) return false; *title_text_vp = *(list->parent[screen]); - line.height = list->line_height[screen]; - title_text_vp->height = line.height; + linedes.height = list->line_height[screen]; + title_text_vp->height = linedes.height; #if LCD_DEPTH > 1 /* XXX: Do we want to support the separator on remote displays? */ if (display->screen_type == SCREEN_MAIN && global_settings.list_separator_height != 0) - line.separator_height = abs(global_settings.list_separator_height) + linedes.separator_height = abs(global_settings.list_separator_height) + (lcd_get_dpi() > 200 ? 2 : 1); #endif #ifdef HAVE_LCD_COLOR if (list->title_color >= 0) - line.style |= (STYLE_COLORED|list->title_color); + linedes.style |= (STYLE_COLORED|list->title_color); #endif - line.scroll = true; + linedes.scroll = true; display->set_viewport(title_text_vp); + int icon = list->title_icon; + int icon_w = list_icon_width(display->screen_type); + bool have_icons = false; + if (icon != Icon_NOICON && list->show_icons) + { + have_icons = true; + } - if (list->title_icon != Icon_NOICON && global_settings.show_icons) - put_line(display, 0, 0, &line, "$"ICON_PADDING_S"I$t", - list->title_icon, list->title); - else - put_line(display, 0, 0, &line, "$t", list->title); + struct list_putlineinfo_t list_info = + { + .x = 0, .y = 0, .item_indent = 0, .item_offset = 0, + .line = -1, .icon = icon, .icon_width = icon_w, + .display = display, .vp = title_text_vp, .linedes = &linedes, .list = list, + .dsp_text = list->title, + .is_selected = false, .is_title = true, .show_cursor = false, + .have_icons = have_icons + }; + callback_draw_item(&list_info); return true; } @@ -133,23 +191,30 @@ void list_draw(struct screen *display, struct gui_synclist *list) { int start, end, item_offset, i; const int screen = display->screen_type; + list_draw_item *callback_draw_item; + const int list_start_item = list->start_item[screen]; - const bool scrollbar_in_left = (global_settings.scrollbar == SCROLLBAR_LEFT); - const bool scrollbar_in_right = (global_settings.scrollbar == SCROLLBAR_RIGHT); - const bool show_cursor = !global_settings.cursor_style && - list->show_selection_marker; - const bool have_icons = global_settings.show_icons && list->callback_get_item_icon; + const bool scrollbar_in_left = (list->scrollbar == SCROLLBAR_LEFT); + const bool scrollbar_in_right = (list->scrollbar == SCROLLBAR_RIGHT); + const bool show_cursor = (list->cursor_style == SYNCLIST_CURSOR_NOSTYLE); + const bool have_icons = list->callback_get_item_icon && list->show_icons; + struct viewport *parent = (list->parent[screen]); struct line_desc linedes = LINE_DESC_DEFINIT; bool show_title; struct viewport *list_text_vp = &list_text[screen]; int indent = 0; + if (list->callback_draw_item != NULL) + callback_draw_item = list->callback_draw_item; + else + callback_draw_item = _default_listdraw_fn; + struct viewport * last_vp = display->set_viewport(parent); display->clear_viewport(); display->scroll_stop_viewport(list_text_vp); *list_text_vp = *parent; - if ((show_title = draw_title(display, list))) + if ((show_title = draw_title(display, list, callback_draw_item))) { int title_height = title_text[screen].height; list_text_vp->y += title_height; @@ -169,6 +234,12 @@ void list_draw(struct screen *display, struct gui_synclist *list) end = start + nb_lines; #ifdef HAVE_TOUCHSCREEN + /* y_pos needs to be clamped now since it can overflow the maximum + * in some cases, and we have no easy way to prevent this beforehand */ + int max_y_pos = list->nb_items * linedes.height - list_text[screen].height; + if (max_y_pos > 0 && list->y_pos > max_y_pos) + list->y_pos = max_y_pos; + int draw_offset = list_start_item * linedes.height - list->y_pos; /* draw some extra items to not have empty lines at the top and bottom */ if (draw_offset > 0) @@ -179,31 +250,55 @@ void list_draw(struct screen *display, struct gui_synclist *list) if (start > 0) start--; } - else if (draw_offset < 0) - end++; + else if (draw_offset < 0) { + if(end < list->nb_items) + end++; + } + + /* If the viewport is not an exact multiple of the line height, then + * there will be space for one more partial line. */ + int spare_space = list_text_vp->height - linedes.height * nb_lines; + if(nb_lines < list->nb_items && spare_space > 0 && end < list->nb_items) + if(end < list->nb_items) + end++; #else #define draw_offset 0 #endif /* draw the scrollbar if its needed */ - if (global_settings.scrollbar != SCROLLBAR_OFF) + if (list->scrollbar != SCROLLBAR_OFF) { /* if the scrollbar is shown the text viewport needs to shrink */ if (nb_lines < list->nb_items) { struct viewport vp = *list_text_vp; vp.width = SCROLLBAR_WIDTH; +#ifndef HAVE_TOUCHSCREEN + /* touchscreens must use full viewport height + * due to pixelwise rendering */ vp.height = linedes.height * nb_lines; +#endif list_text_vp->width -= SCROLLBAR_WIDTH; if (scrollbar_in_right) vp.x += list_text_vp->width; else /* left */ list_text_vp->x += SCROLLBAR_WIDTH; struct viewport *last = display->set_viewport(&vp); + +#ifndef HAVE_TOUCHSCREEN + /* button targets go itemwise */ + int scrollbar_items = list->nb_items; + int scrollbar_min = list_start_item; + int scrollbar_max = list_start_item + nb_lines; +#else + /* touchscreens use pixelwise scrolling */ + int scrollbar_items = list->nb_items * linedes.height; + int scrollbar_min = list->y_pos; + int scrollbar_max = list->y_pos + list_text_vp->height; +#endif gui_scrollbar_draw(display, (scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height, - list->nb_items, list_start_item, list_start_item + nb_lines, - VERTICAL); + scrollbar_items, scrollbar_min, scrollbar_max, VERTICAL); display->set_viewport(last); } /* shift everything a bit in relation to the title */ @@ -214,6 +309,16 @@ void list_draw(struct screen *display, struct gui_synclist *list) } display->set_viewport(list_text_vp); + int icon_w = list_icon_width(screen); + int character_width = display->getcharwidth(); + + struct list_putlineinfo_t list_info = + { + .x = 0, .y = 0, .vp = list_text_vp, .list = list, + .icon_width = icon_w, .is_title = false, .show_cursor = show_cursor, + .have_icons = have_icons, .linedes = &linedes, .display = display + }; + for (i=start; i<end && i<list->nb_items; i++) { /* do the text */ @@ -221,7 +326,6 @@ void list_draw(struct screen *display, struct gui_synclist *list) unsigned const char *s; char entry_buffer[MAX_PATH]; unsigned char *entry_name; - int text_pos = 0; int line = i - start; int line_indent = 0; int style = STYLE_DEFAULT; @@ -237,17 +341,17 @@ void list_draw(struct screen *display, struct gui_synclist *list) } if (line_indent) { - if (global_settings.show_icons) - line_indent *= list_icon_width(screen); + if (list->show_icons) + line_indent *= icon_w; else - line_indent *= display->getcharwidth(); + line_indent *= character_width; } line_indent += indent; /* position the string at the correct offset place */ int item_width,h; display->getstringsize(entry_name, &item_width, &h); - item_offset = gui_list_get_item_offset(list, item_width, text_pos, + item_offset = gui_list_get_item_offset(list, item_width, indent + (list->show_icons ? icon_w : 0), display, list_text_vp); /* draw the selected line */ @@ -257,8 +361,7 @@ void list_draw(struct screen *display, struct gui_synclist *list) !hide_selection && #endif i >= list->selected_item - && i < list->selected_item + list->selected_size - && list->show_selection_marker) + && i < list->selected_item + list->selected_size) {/* The selected item must be displayed scrolling */ #ifdef HAVE_LCD_COLOR if (list->selection_color) @@ -271,12 +374,12 @@ void list_draw(struct screen *display, struct gui_synclist *list) } else #endif - if (global_settings.cursor_style == 1 + if (list->cursor_style == SYNCLIST_CURSOR_INVERT #ifdef HAVE_REMOTE_LCD /* the global_settings.cursor_style check is here to make * sure if they want the cursor instead of bar it will work */ - || (display->depth < 16 && global_settings.cursor_style) + || (display->depth < 16 && list->cursor_style) #endif ) { @@ -284,14 +387,14 @@ void list_draw(struct screen *display, struct gui_synclist *list) style = STYLE_INVERT; } #ifdef HAVE_LCD_COLOR - else if (global_settings.cursor_style == 2) + else if (list->cursor_style == SYNCLIST_CURSOR_COLOR) { /* Display colour line selector */ style = STYLE_COLORBAR; linedes.text_color = global_settings.lst_color; linedes.line_color = global_settings.lss_color; } - else if (global_settings.cursor_style == 3) + else if (list->cursor_style == SYNCLIST_CURSOR_GRADIENT) { /* Display gradient line selector */ style = STYLE_GRADIENT; @@ -315,27 +418,22 @@ void list_draw(struct screen *display, struct gui_synclist *list) } } #endif - linedes.style = style; linedes.scroll = is_selected ? true : list->scroll_all; linedes.line = i % list->selected_size; icon = list->callback_get_item_icon ? list->callback_get_item_icon(i, list->data) : Icon_NOICON; - /* the list can have both, one of or neither of cursor and item icons, - * if both don't apply icon padding twice between the icons */ - if (show_cursor && have_icons) - put_line(display, 0, line * linedes.height + draw_offset, - &linedes, "$*s$"ICON_PADDING_S"I$i$"ICON_PADDING_S"s$*t", - line_indent, is_selected ? Icon_Cursor : Icon_NOICON, - icon, item_offset, entry_name); - else if (show_cursor || have_icons) - put_line(display, 0, line * linedes.height + draw_offset, - &linedes, "$*s$"ICON_PADDING_S"I$*t", line_indent, - show_cursor ? (is_selected ? Icon_Cursor:Icon_NOICON):icon, - item_offset, entry_name); - else - put_line(display, 0, line * linedes.height + draw_offset, - &linedes, "$*s$*t", line_indent, item_offset, entry_name); + + + list_info.y = line * linedes.height + draw_offset; + list_info.is_selected = is_selected; + list_info.item_indent = line_indent; + list_info.line = i; + list_info.icon = icon; + list_info.dsp_text = entry_name; + list_info.item_offset = item_offset; + + callback_draw_item(&list_info); } display->set_viewport(parent); display->update_viewport(); @@ -360,21 +458,28 @@ static int scrollbar_scroll(struct gui_synclist * gui_list, int y) const int screen = screens[SCREEN_MAIN].screen_type; const int nb_lines = list_get_nb_lines(gui_list, screen); - if (nb_lines < gui_list->nb_items) + if (nb_lines < gui_list->nb_items) { - /* scrollbar scrolling is still line based */ - int scrollbar_size = nb_lines * gui_list->line_height[screen]; - int actual_y = y - list_text[screen].y; - int new_selection = (actual_y * gui_list->nb_items) / scrollbar_size; + const int line_height = gui_list->line_height[screen]; - int start_item = new_selection - nb_lines/2; - if(start_item < 0) - start_item = 0; - else if(start_item > gui_list->nb_items - nb_lines) - start_item = gui_list->nb_items - nb_lines; + /* try to position the center of the scrollbar at the touch point */ + int scrollbar_size = list_text[screen].height; + int actual_y = y - list_text[screen].y; + int new_y_pos = (actual_y * gui_list->nb_items * line_height) / scrollbar_size; + int new_start = (actual_y * gui_list->nb_items) / scrollbar_size; + + new_start -= nb_lines / 2; + new_y_pos -= (nb_lines * line_height) / 2; + if(new_start < 0) { + new_start = 0; + new_y_pos = 0; + } else if(new_start > gui_list->nb_items - nb_lines) { + new_start = gui_list->nb_items - nb_lines; + new_y_pos = new_start * line_height; + } - gui_list->start_item[screen] = start_item; - gui_list->y_pos = start_item * gui_list->line_height[screen]; + gui_list->start_item[screen] = new_start; + gui_list->y_pos = new_y_pos; return ACTION_REDRAW; } @@ -509,6 +614,7 @@ static bool swipe_scroll(struct gui_synclist * gui_list, int difference) const int old_start = gui_list->start_item[screen]; int new_start_item = -1; int line_diff = 0; + int max_y_pos = gui_list->nb_items * line_height - list_text[screen].height; /* Track whether we hit the end of the list for sake of kinetic scroll */ bool hit_end = true; @@ -517,8 +623,8 @@ static bool swipe_scroll(struct gui_synclist * gui_list, int difference) gui_list->y_pos -= difference; if(gui_list->y_pos < 0) gui_list->y_pos = 0; - else if(gui_list->y_pos > (gui_list->nb_items - nb_lines) * line_height) - gui_list->y_pos = (gui_list->nb_items - nb_lines) * line_height; + else if(gui_list->y_pos > max_y_pos) + gui_list->y_pos = max_y_pos; else hit_end = false; @@ -647,7 +753,7 @@ static int get_click_location(struct gui_synclist *list, int x, int y) if (viewport_point_within_vp(title, x, y)) retval = TITLE_TEXT; /* check the icon too */ - if (list->title_icon != Icon_NOICON && global_settings.show_icons) + if (list->title_icon != Icon_NOICON && list->show_icons) { int width = list_icon_width(screen); struct viewport vp = *title; @@ -665,14 +771,19 @@ static int get_click_location(struct gui_synclist *list, int x, int y) { bool on_scrollbar_clicked; int adj_x = x - parent->x; - switch (global_settings.scrollbar) + switch (list->scrollbar) { + case SCROLLBAR_OFF: + /*fall-through*/ + default: + on_scrollbar_clicked = false; + break; case SCROLLBAR_LEFT: - on_scrollbar_clicked = adj_x <= SCROLLBAR_WIDTH; break; + on_scrollbar_clicked = adj_x <= SCROLLBAR_WIDTH; + break; case SCROLLBAR_RIGHT: - on_scrollbar_clicked = adj_x > (title->x + title->width - SCROLLBAR_WIDTH); break; - default: - on_scrollbar_clicked = false; break; + on_scrollbar_clicked = adj_x > (title->x + title->width - SCROLLBAR_WIDTH); + break; } if (on_scrollbar_clicked) retval = SCROLLBAR; diff --git a/apps/gui/color_picker.c b/apps/gui/color_picker.c index a32f1ee179..ef17c0a230 100644 --- a/apps/gui/color_picker.c +++ b/apps/gui/color_picker.c @@ -154,7 +154,6 @@ static void draw_screen(struct screen *display, char *title, { unsigned text_color = LCD_BLACK; unsigned background_color = LCD_WHITE; - char buf[32]; int i, char_height, line_height; int max_label_width; int text_x, text_top; @@ -253,17 +252,16 @@ static void draw_screen(struct screen *display, char *title, set_drawinfo(display, mode, fg, bg); /* Draw label */ - buf[0] = str(LANG_COLOR_RGB_LABELS)[i]; - buf[1] = '\0'; vp.flags &= ~VP_FLAG_ALIGNMENT_MASK; - display->putsxy(text_x, text_top, buf); + display->putsxyf(text_x, text_top, "%c", str(LANG_COLOR_RGB_LABELS)[i]); /* Draw color value */ + vp.flags |= VP_FLAG_ALIGN_RIGHT; if (display->depth >= 24) - snprintf(buf, 4, "%03d", rgb->rgb_val[i] & 0xFF); + display->putsxyf(text_x, text_top, "%03d", rgb->rgb_val[i] & 0xFF); else - snprintf(buf, 3, "%02d", rgb->rgb_val[i] & 0x3F); - vp.flags |= VP_FLAG_ALIGN_RIGHT; - display->putsxy(text_x, text_top, buf); + display->putsxyf(text_x, text_top, "%02d", rgb->rgb_val[i] & 0x3F); + + /* Draw scrollbar */ gui_scrollbar_draw(display, /* screen */ @@ -280,9 +278,6 @@ static void draw_screen(struct screen *display, char *title, text_top += line_height; } /* end for */ - /* Format RGB: #rrggbb */ - snprintf(buf, sizeof(buf), str(LANG_COLOR_RGB_VALUE), - rgb->red, rgb->green, rgb->blue); vp.flags |= VP_FLAG_ALIGN_CENTER; if (display->depth >= 16) { @@ -301,8 +296,9 @@ static void draw_screen(struct screen *display, char *title, /* Draw RGB: #rrggbb in middle of swatch */ set_drawinfo(display, DRMODE_FG, get_black_or_white(rgb), background_color); - - display->putsxy(0, top + (height - char_height) / 2, buf); + /* Format RGB: #rrggbb */ + display->putsxyf(0, top + (height - char_height) / 2, + str(LANG_COLOR_RGB_VALUE), rgb->red, rgb->green, rgb->blue); /* Draw border around the rect */ set_drawinfo(display, DRMODE_SOLID, text_color, background_color); @@ -318,7 +314,9 @@ static void draw_screen(struct screen *display, char *title, if (height >= char_height) { set_drawinfo(display, DRMODE_SOLID, text_color, background_color); - display->putsxy(0, top + (height - char_height) / 2, buf); + /* Format RGB: #rrggbb */ + display->putsxyf(0, top + (height - char_height) / 2, + str(LANG_COLOR_RGB_VALUE), rgb->red, rgb->green, rgb->blue); } } diff --git a/apps/gui/folder_select.c b/apps/gui/folder_select.c index 706b166941..a76d77562b 100644 --- a/apps/gui/folder_select.c +++ b/apps/gui/folder_select.c @@ -8,6 +8,7 @@ * * Copyright (C) 2012 Jonathan Gordon * Copyright (C) 2012 Thomas Martitz +* * Copyright (C) 2021 William Wilgus * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,7 +31,11 @@ #include "language.h" #include "list.h" #include "plugin.h" +#include "splash.h" +/* Define LOGF_ENABLE to enable logf output in this file */ +//#define LOGF_ENABLE +#include "logf.h" /* * Order for changing child states: @@ -56,18 +61,31 @@ struct child { struct folder { char *name; struct child *children; - int children_count; - int depth; - struct folder* previous; + uint16_t children_count; + uint16_t depth; }; static char *buffer_front, *buffer_end; + +static struct +{ + int32_t len; /* keeps count versus maxlen to give buffer full notification */ + uint32_t val; /* hash of all selected items */ + char buf[3];/* address used as identifier -- only \0 written to it */ + char maxlen_exceeded; /*0,1*/ +} hashed; + +static inline void get_hash(const char *key, uint32_t *hash, int len) +{ + *hash = crc_32(key, len, *hash); +} + static char* folder_alloc(size_t size) { char* retval; /* 32-bit aligned */ - size = (size + 3) & ~3; + size = ALIGN_UP(size, 4); if (buffer_front + size > buffer_end) { return NULL; @@ -86,32 +104,57 @@ static char* folder_alloc_from_end(size_t size) buffer_end -= size; return buffer_end; } - -static void get_full_path_r(struct folder *start, char* dst) +#if 0 +/* returns the buffer size required to store the path + \0 */ +static int get_full_pathsz(struct folder *start) { - if (start->previous) - get_full_path_r(start->previous, dst); - - if (start->name && start->name[0] && strcmp(start->name, "/")) + int reql = 0; + struct folder *next = start; + do { - strlcat(dst, "/", MAX_PATH); - strlcat(dst, start->name, MAX_PATH); - } + reql += strlen(next->name) + 1; + } while ((next = next->previous)); + + if (start->name[0] != '/') reql--; + if (--reql < 0) reql = 0; + return reql; } +#endif -static char* get_full_path(struct folder *start) +static size_t get_full_path(struct folder *start, char *dst, size_t dst_sz) { - static char buffer[MAX_PATH]; - - if (strcmp(start->name, "/")) + size_t pos = 0; + struct folder *prev, *cur = NULL, *next = start; + dst[0] = '\0'; /* for strlcat to do its thing */ + /* First traversal R->L mutate nodes->previous to point at child */ + while (next->previous != NULL) /* stop at the root */ { - buffer[0] = 0; - get_full_path_r(start, buffer); +#define PATHMUTATE() \ + ({ \ + prev = cur; \ + cur = next; \ + next = cur->previous;\ + cur->previous = prev; \ + }) + PATHMUTATE(); } - else /* get_full_path_r() does the wrong thing for / */ - return "/"; - - return buffer; + /*swap the next and cur nodes to reverse direction */ + prev = next; + next = cur; + cur = prev; + /* Second traversal L->R mutate nodes->previous to point back at parent + * copy strings to buf as they go by */ + while (next != NULL) + { + PATHMUTATE(); + pos = strlcat(dst, cur->name, dst_sz); + /* do not append slash to paths starting with slash */ + if (cur->name[0] != '/') + pos = strlcat(dst, "/", dst_sz); + } + logf("get_full_path: (%d)[%s]", (int)pos, dst); + return pos; +#undef PATHMUTATE } /* support function for qsort() */ @@ -125,49 +168,52 @@ static int compare(const void* p1, const void* p2) static struct folder* load_folder(struct folder* parent, char *folder) { DIR *dir; - char* path = get_full_path(parent); char fullpath[MAX_PATH]; + struct dirent *entry; - struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder)); int child_count = 0; char *first_child = NULL; + size_t len = 0; - if (!strcmp(folder,"/")) - strlcpy(fullpath, folder, 2); - else - snprintf(fullpath, MAX_PATH, "%s/%s", parent ? path : "", folder); + struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder)); + if (this == NULL) + goto fail; + + if (parent) + { + len = get_full_path(parent, fullpath, sizeof(fullpath)); + if (len >= sizeof(fullpath)) + goto fail; + } + strmemccpy(&fullpath[len], folder, sizeof(fullpath) - len); + logf("load_folder: [%s]", fullpath); - if (!this) - return NULL; dir = opendir(fullpath); - if (!dir) - return NULL; + if (dir == NULL) + goto fail; this->previous = parent; this->name = folder; this->children = NULL; this->children_count = 0; - this->depth = parent ? parent->depth + 1 : 0; + if (parent) + this->depth = parent->depth + 1; while ((entry = readdir(dir))) { - int len = strlen((char *)entry->d_name); - struct dirinfo info; - - info = dir_get_info(dir, entry); - /* skip anything not a directory */ - if ((info.attribute & ATTR_DIRECTORY) == 0) { + if ((dir_get_info(dir, entry).attribute & ATTR_DIRECTORY) == 0) { continue; } - /* skip directories . and .. */ - if ((!strcmp((char *)entry->d_name, ".")) || - (!strcmp((char *)entry->d_name, ".."))) { + /* skip . and .. */ + char *dn = entry->d_name; + if ((dn[0] == '.') && (dn[1] == '\0' || (dn[1] == '.' && dn[2] == '\0'))) continue; - } - char *name = folder_alloc_from_end(len+1); - if (!name) + /* copy entry name to end of buffer, save pointer */ + len = strlen((char *)entry->d_name); + char *name = folder_alloc_from_end(len+1); /*for NULL*/ + if (name == NULL) { closedir(dir); - return NULL; + goto fail; } memcpy(name, (char *)entry->d_name, len+1); child_count++; @@ -177,26 +223,29 @@ static struct folder* load_folder(struct folder* parent, char *folder) /* now put the names in the array */ this->children = (struct child*)folder_alloc(sizeof(struct child) * child_count); - if (!this->children) - return NULL; + if (this->children == NULL) + goto fail; + while (child_count) { - this->children[this->children_count].name = first_child; - this->children[this->children_count].folder = NULL; - this->children[this->children_count].state = COLLAPSED; - this->children_count++; - first_child += strlen(first_child) + 1; + struct child *child = &this->children[this->children_count++]; + child->name = first_child; + child->folder = NULL; + child->state = COLLAPSED; + while(*first_child++ != '\0'){};/* move to next name entry */ child_count--; } qsort(this->children, this->children_count, sizeof(struct child), compare); return this; +fail: + return NULL; } struct folder* load_root(void) { static struct child root_child; - + /* reset the root for each call */ root_child.name = "/"; root_child.folder = NULL; root_child.state = COLLAPSED; @@ -205,7 +254,7 @@ struct folder* load_root(void) .name = "", .children = &root_child, .children_count = 1, - .depth = -1, + .depth = 0, .previous = NULL, }; @@ -230,7 +279,6 @@ static int count_items(struct folder *start) static struct child* find_index(struct folder *start, int index, struct folder **parent) { int i = 0; - *parent = NULL; while (i < start->children_count) @@ -262,22 +310,22 @@ static const char * folder_get_name(int selected_item, void * data, struct folder *parent; struct child *this = find_index(root, selected_item , &parent); - buffer[0] = '\0'; - - if (parent->depth >= 0) - for(int i = 0; i <= parent->depth; i++) - strcat(buffer, "\t"); - + char *buf = buffer; + if ((int)buffer_len > parent->depth) + { + int i = parent->depth; + while(--i > 0) /* don't indent the parent /folders */ + *buf++ = '\t'; + } + *buf = '\0'; strlcat(buffer, this->name, buffer_len); if (this->state == EACCESS) { /* append error message to the entry if unaccessible */ - size_t len = strlcat(buffer, " (", buffer_len); + size_t len = strlcat(buffer, " ( ", buffer_len); if (buffer_len > len) { - snprintf(&buffer[len], buffer_len - len, str(LANG_READ_FAILED), - this->name); - strlcat(buffer, ")", buffer_len); + snprintf(&buffer[len], buffer_len - len, str(LANG_READ_FAILED), ")"); } } @@ -304,6 +352,23 @@ static enum themable_icons folder_get_icon(int selected_item, void * data) return Icon_NOICON; } +static int child_set_state_expand(struct child *this, struct folder *parent) +{ + int newstate = EACCESS; + if (this->folder == NULL) + this->folder = load_folder(parent, this->name); + + if (this->folder != NULL) + { + if(this->folder->children_count == 0) + newstate = SELECTED; + else + newstate = EXPANDED; + } + this->state = newstate; + return newstate; +} + static int folder_action_callback(int action, struct gui_synclist *list) { struct folder *root = (struct folder*)list->data; @@ -322,17 +387,13 @@ static int folder_action_callback(int action, struct gui_synclist *list) this->state = COLLAPSED; break; case COLLAPSED: - if (this->folder == NULL) - this->folder = load_folder(parent, this->name); - this->state = this->folder ? (this->folder->children_count == 0 ? - SELECTED : EXPANDED) : EACCESS; + child_set_state_expand(this, parent); break; case EACCESS: /* cannot open, do nothing */ return action; } - list->nb_items = count_items(root); - return ACTION_REDRAW; + action = ACTION_REDRAW; } else if (action == ACTION_STD_CONTEXT) { @@ -342,140 +403,198 @@ static int folder_action_callback(int action, struct gui_synclist *list) for (i = 0; i < this->folder->children_count; i++) { child = &this->folder->children[i]; - if (child->state == SELECTED || - child->state == EXPANDED) - child->state = COLLAPSED; - else if (child->state == COLLAPSED) - child->state = SELECTED; + switch (child->state) + { + case SELECTED: + case EXPANDED: + child->state = COLLAPSED; + break; + case COLLAPSED: + child->state = SELECTED; + break; + case EACCESS: + break; + } } break; case SELECTED: case COLLAPSED: - if (this->folder == NULL) - this->folder = load_folder(parent, this->name); - this->state = this->folder ? (this->folder->children_count == 0 ? - SELECTED : EXPANDED) : EACCESS; - if (this->state == EACCESS) - break; - for (i = 0; i < this->folder->children_count; i++) + if (child_set_state_expand(this, parent) != EACCESS) { - child = &this->folder->children[i]; - child->state = SELECTED; + for (i = 0; i < (this->folder->children_count); i++) + { + child = &this->folder->children[i]; + child->state = SELECTED; + } } break; case EACCESS: /* cannot open, do nothing */ return action; } - list->nb_items = count_items(root); - return ACTION_REDRAW; + action = ACTION_REDRAW; } - - + if (action == ACTION_REDRAW) + list->nb_items = count_items(root); return action; } -static struct child* find_from_filename(char* filename, struct folder *root) +static struct child* find_from_filename(const char* filename, struct folder *root) { - char *slash = strchr(filename, '/'); - int i = 0; - if (slash) - *slash = '\0'; if (!root) return NULL; - + const char *slash = strchr(filename, '/'); struct child *this; /* filenames beginning with a / are specially treated as the * loop below can't handle them. they can only occur on the first, * and not recursive, calls to this function.*/ - if (slash == filename) + if (filename[0] == '/') /* in the loop nothing starts with '/' */ { + logf("find_from_filename [%s]", filename); /* filename begins with /. in this case root must be the * top level folder */ this = &root->children[0]; - if (!slash[1]) + if (filename[1] == '\0') { /* filename == "/" */ return this; } - else - { - /* filename == "/XXX/YYY". cascade down */ - if (!this->folder) - this->folder = load_folder(root, this->name); - this->state = EXPANDED; - /* recurse with XXX/YYY */ - return find_from_filename(slash+1, this->folder); - } + else /* filename == "/XXX/YYY". cascade down */ + goto cascade; } - while (i < root->children_count) + for (int i = 0; i < root->children_count; i++) { this = &root->children[i]; - if (!strcasecmp(this->name, filename)) + /* when slash == NULL n will be really large but \0 stops the compare */ + if (strncasecmp(this->name, filename, slash - filename) == 0) { - if (!slash) + if (slash == NULL) { /* filename == XXX */ return this; } else - { - /* filename == XXX/YYY. cascade down */ - if (!this->folder) - this->folder = load_folder(root, this->name); - this->state = EXPANDED; - return find_from_filename(slash+1, this->folder); - } + goto cascade; } - i++; } return NULL; + +cascade: + /* filename == XXX/YYY. cascade down */ + child_set_state_expand(this, root); + while (slash[0] == '/') slash++; /* eat slashes */ + return find_from_filename(slash, this->folder); } -/* _modifies_ buf */ -int select_paths(struct folder* root, char* buf) +static int select_paths(struct folder* root, const char* filenames) { - struct child *item = find_from_filename(buf, root); - if (item) - item->state = SELECTED; + /* Takes a list of filenames in a ':' delimited string + splits filenames at the ':' character loads each into buffer + selects each file in the folder list + + if last item or only item the rest of the string is copied to the buffer + *End the last item WITHOUT the ':' character /.rockbox/eqs:/.rockbox/wps\0* + */ + char buf[MAX_PATH]; + const int buflen = sizeof(buf); + + const char *fnp = filenames; + const char *lastfnp = fnp; + const char *sstr; + off_t len; + + while (fnp) + { + fnp = strchr(fnp, ':'); + if (fnp) + { + len = fnp - lastfnp; + fnp++; + } + else /* no ':' get the rest of the string */ + len = strlen(lastfnp); + + sstr = lastfnp; + lastfnp = fnp; + if (len <= 0 || len + 1 >= buflen) + continue; + strmemccpy(buf, sstr, len + 1); + struct child *item = find_from_filename(buf, root); + if (item) + item->state = SELECTED; + } + return 0; } -static void save_folders_r(struct folder *root, char* dst, size_t maxlen) +static void save_folders_r(struct folder *root, char* dst, size_t maxlen, size_t buflen) { - int i = 0; + size_t len; + struct folder *curfolder; + char* name; - while (i < root->children_count) + for (int i = 0; i < root->children_count; i++) { struct child *this = &root->children[i]; if (this->state == SELECTED) { - if (this->folder) - snprintf(buffer_front, buffer_end - buffer_front, - "%s:", get_full_path(this->folder)); + if (this->folder == NULL) + { + curfolder = root; + name = this->name; + logf("save_folders_r: this->name[%s]", name); + } else { - char *p = get_full_path(root); - snprintf(buffer_front, buffer_end - buffer_front, - "%s/%s:", strcmp(p, "/") ? p : "", - strcmp(this->name, "/") ? this->name : ""); + curfolder = this->folder->previous; + name = this->folder->name; + logf("save_folders_r: this->folder->name[%s]", name); + } + + len = get_full_path(curfolder, buffer_front, buflen); + + if (len + 2 >= buflen) + continue; + + len += snprintf(&buffer_front[len], buflen - len, "%s:", name); + logf("save_folders_r: [%s]", buffer_front); + if (dst != hashed.buf) + { + int dlen = strlen(dst); + if (dlen + len >= maxlen) + continue; + strmemccpy(&dst[dlen], buffer_front, maxlen - dlen); + } + else + { + if (hashed.len + len >= maxlen) + { + hashed.maxlen_exceeded = 1; + continue; + } + get_hash(buffer_front, &hashed.val, len); + hashed.len += len; } - strlcat(dst, buffer_front, maxlen); } else if (this->state == EXPANDED) - save_folders_r(this->folder, dst, maxlen); - i++; + save_folders_r(this->folder, dst, maxlen, buflen); } } -static void save_folders(struct folder *root, char* dst, size_t maxlen) +static uint32_t save_folders(struct folder *root, char* dst, size_t maxlen) { - int len; + hashed.len = 0; + hashed.val = 0; + hashed.maxlen_exceeded = 0; + size_t len = buffer_end - buffer_front; dst[0] = '\0'; - save_folders_r(root, dst, maxlen); + save_folders_r(root, dst, maxlen, len); len = strlen(dst); /* fix trailing ':' */ if (len > 1) dst[len-1] = '\0'; + /*Notify - user will probably not see save dialog if nothing new got added*/ + if (hashed.maxlen_exceeded > 0) splash(HZ *2, ID2P(LANG_SHOWDIR_BUFFER_FULL)); + return hashed.val; } bool folder_select(char* setting, int setting_len) @@ -483,40 +602,32 @@ bool folder_select(char* setting, int setting_len) struct folder *root; struct simplelist_info info; size_t buf_size; - /* 32 separate folders should be Enough For Everybody(TM) */ - char *vect[32]; - char copy[setting_len]; - int nb_items; - - /* copy onto stack as split_string() modifies it */ - strlcpy(copy, setting, setting_len); - nb_items = split_string(copy, ':', vect, ARRAYLEN(vect)); buffer_front = plugin_get_buffer(&buf_size); buffer_end = buffer_front + buf_size; + logf("%d bytes free", (int)(buffer_end - buffer_front)); root = load_root(); - if (nb_items > 0) - { - for(int i = 0; i < nb_items; i++) - select_paths(root, vect[i]); - } - + logf("folders in: %s", setting); + /* Load previous selection(s) */ + select_paths(root, setting); + /* get current hash to check for changes later */ + uint32_t hash = save_folders(root, hashed.buf, setting_len); simplelist_info_init(&info, str(LANG_SELECT_FOLDER), count_items(root), root); info.get_name = folder_get_name; info.action_callback = folder_action_callback; info.get_icon = folder_get_icon; simplelist_show_list(&info); - + logf("%d bytes free", (int)(buffer_end - buffer_front)); /* done editing. check for changes */ - save_folders(root, copy, setting_len); - if (strcmp(copy, setting)) - { /* prompt for saving changes and commit if yes */ + if (hash != save_folders(root, hashed.buf, setting_len)) + { /* prompt for saving changes and commit if yes */ if (yesno_pop(ID2P(LANG_SAVE_CHANGES))) { - strcpy(setting, copy); + save_folders(root, setting, setting_len); settings_save(); + logf("folders out: %s", setting); return true; } } diff --git a/apps/gui/icon.c b/apps/gui/icon.c index 9fe7090f4a..7a59a72151 100644 --- a/apps/gui/icon.c +++ b/apps/gui/icon.c @@ -32,6 +32,7 @@ #include "bmp.h" #include "filetypes.h" #include "language.h" +#include "misc.h" #include "bitmaps/default_icons.h" #if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1) @@ -63,7 +64,6 @@ static struct iconset { struct bitmap bmp; bool loaded; int handle; - int handle_locked; } iconsets[Iconset_Count][NB_SCREENS]; #define ICON_HEIGHT(screen) (!iconsets[Iconset_user][screen].loaded ? \ @@ -160,8 +160,6 @@ static int buflib_move_callback(int handle, void* current, void* new) struct iconset *set = &iconsets[i][j]; if (set->bmp.data == current) { - if (set->handle_locked > 0) - return BUFLIB_CB_CANNOT_MOVE; set->bmp.data = new; return BUFLIB_CB_OK; } @@ -169,59 +167,27 @@ static int buflib_move_callback(int handle, void* current, void* new) } return BUFLIB_CB_OK; } -static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL}; static void load_icons(const char* filename, enum Iconset iconset, enum screen_type screen) { - ssize_t size_read; - ssize_t buf_size; - int fd; - int bmpformat = (FORMAT_ANY|FORMAT_DITHER|FORMAT_TRANSPARENT); + static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL}; + const int bmpformat = (FORMAT_ANY|FORMAT_DITHER|FORMAT_TRANSPARENT); struct iconset *ic = &iconsets[iconset][screen]; + ssize_t buf_reqd; ic->loaded = false; - ic->handle = 0; + ic->handle = CLB_ALOC_ERR; if (filename[0] && filename[0] != '-') { - char path[MAX_PATH]; - - snprintf(path, sizeof(path), ICON_DIR "/%s.bmp", filename); - fd = open(path, O_RDONLY); - if (fd < 0) - return; - buf_size = read_bmp_fd(fd, &ic->bmp, 0, - bmpformat|FORMAT_RETURN_SIZE, NULL); - if (buf_size > 0) - ic->handle = core_alloc_ex(filename, (size_t) buf_size, &buflib_ops); - - if (ic->handle <= 0) - { - /* error */ - goto finished; - } - lseek(fd, 0, SEEK_SET); - ic->bmp.data = core_get_data(ic->handle); - - ic->handle_locked = 1; - size_read = read_bmp_fd(fd, &ic->bmp, buf_size, bmpformat, NULL); - ic->handle_locked = 0; - - if (size_read < 0) + char fname[MAX_PATH]; + snprintf(fname, sizeof(fname), ICON_DIR "/%s.bmp", filename); + ic->handle = core_load_bmp(fname, &ic->bmp, bmpformat, &buf_reqd, &buflib_ops); + if (ic->handle != CLB_ALOC_ERR) { - /* error */ - size_read = 0; - } - /* free unused alpha channel, if any */ - core_shrink(ic->handle, ic->bmp.data, size_read); - - if (size_read == 0) - ic->handle = core_free(ic->handle); - else + ic->bmp.data = core_get_data(ic->handle); ic->loaded = true; -finished: - close(fd); - return; + } } } diff --git a/apps/gui/line.c b/apps/gui/line.c index d319ff3c51..7e84aa7b31 100644 --- a/apps/gui/line.c +++ b/apps/gui/line.c @@ -264,8 +264,7 @@ next: else { /* any other character here is an erroneous format string */ - snprintf(tempbuf, sizeof(tempbuf), "<E:%c>", ch); - display->putsxy(xpos, y, tempbuf); + display->putsxyf(xpos, y, "<E:%c>", ch); /* Don't consider going forward, fix the caller */ return; } diff --git a/apps/gui/list.c b/apps/gui/list.c index 13a850bd7b..652279a9de 100644 --- a/apps/gui/list.c +++ b/apps/gui/list.c @@ -47,12 +47,6 @@ */ #define FRAMEDROP_TRIGGER 6 -static int offset_step = 16; /* pixels per screen scroll step */ -/* should lines scroll out of the screen */ -static bool offset_out_of_view = false; - -static void gui_list_select_at_offset(struct gui_synclist * gui_list, - int offset); void list_draw(struct screen *display, struct gui_synclist *list); static long last_dirty_tick; @@ -129,6 +123,18 @@ void list_init_item_height(struct gui_synclist *list, enum screen_type screen) #endif } +static void gui_synclist_init_display_settings(struct gui_synclist * list) +{ + struct user_settings *gs = &global_settings; + list->scrollbar = gs->scrollbar; + list->show_icons = gs->show_icons; + list->scroll_paginated = gs->scroll_paginated; + list->keyclick = gs->keyclick; + list->talk_menu = gs->talk_menu; + list->wraparound = gs->list_wraparound; + list->cursor_style = gs->cursor_style; +} + /* * Initializes a scrolling list * - gui_list : the list structure to initialize @@ -150,8 +156,11 @@ void gui_synclist_init(struct gui_synclist * gui_list, gui_list->callback_get_item_icon = NULL; gui_list->callback_get_item_name = callback_get_item_name; gui_list->callback_speak_item = NULL; + gui_list->callback_draw_item = NULL; gui_list->nb_items = 0; gui_list->selected_item = 0; + gui_synclist_init_display_settings(gui_list); + #ifdef HAVE_TOUCHSCREEN gui_list->y_pos = 0; #endif @@ -167,7 +176,6 @@ void gui_synclist_init(struct gui_synclist * gui_list, list_init_viewports(gui_list); FOR_NB_SCREENS(i) list_init_item_height(gui_list, i); - gui_list->limit_scroll = false; gui_list->data = data; gui_list->scroll_all = scroll_all; gui_list->selected_size = selected_size; @@ -176,7 +184,6 @@ void gui_synclist_init(struct gui_synclist * gui_list, gui_list->scheduled_talk_tick = gui_list->last_talked_tick = 0; gui_list->dirty_tick = current_tick; - gui_list->show_selection_marker = true; #ifdef HAVE_LCD_COLOR gui_list->title_color = -1; @@ -185,13 +192,6 @@ void gui_synclist_init(struct gui_synclist * gui_list, #endif } -/* this toggles the selection bar or cursor */ -void gui_synclist_hide_selection_marker(struct gui_synclist * lists, bool hide) -{ - lists->show_selection_marker = !hide; -} - - int gui_list_get_item_offset(struct gui_synclist * gui_list, int item_width, int text_pos, @@ -200,7 +200,7 @@ int gui_list_get_item_offset(struct gui_synclist * gui_list, { int item_offset; - if (offset_out_of_view) + if (global_settings.offset_out_of_view) { item_offset = gui_list->offset_position[display->screen_type]; } @@ -254,15 +254,11 @@ static void gui_list_put_selection_on_screen(struct gui_synclist * gui_list, const int scroll_limit_up = (nb_lines < gui_list->selected_size+2 ? 0:1); const int scroll_limit_down = (scroll_limit_up+gui_list->selected_size); - if (gui_list->show_selection_marker == false) - { - new_start_item = gui_list->selected_item; - } - else if (gui_list->selected_size >= nb_lines) + if (gui_list->selected_size >= nb_lines) { new_start_item = gui_list->selected_item; } - else if (global_settings.scroll_paginated) + else if (gui_list->scroll_paginated) { nb_lines -= nb_lines%gui_list->selected_size; if (difference < 0 || difference >= nb_lines) @@ -292,7 +288,7 @@ static void gui_list_put_selection_on_screen(struct gui_synclist * gui_list, static void edge_beep(struct gui_synclist * gui_list, bool wrap) { - if (global_settings.keyclick) + if (gui_list->keyclick) { list_speak_item *cb = gui_list->callback_speak_item; if (!wrap) /* a bounce */ @@ -355,7 +351,7 @@ static void _gui_synclist_speak_item(struct gui_synclist *lists) void gui_synclist_speak_item(struct gui_synclist *lists) { - if (global_settings.talk_menu) + if (lists->talk_menu) { if (lists->nb_items == 0) talk_id(VOICE_EMPTY_LIST, true); @@ -383,7 +379,7 @@ void gui_synclist_select_item(struct gui_synclist * gui_list, int item_number) } static void gui_list_select_at_offset(struct gui_synclist * gui_list, - int offset) + int offset, bool allow_wrap) { int new_selection; if (gui_list->selected_size > 1) @@ -395,41 +391,15 @@ static void gui_list_select_at_offset(struct gui_synclist * gui_list, if (new_selection >= gui_list->nb_items) { - new_selection = gui_list->limit_scroll ? - gui_list->nb_items - gui_list->selected_size : 0; - edge_beep(gui_list, !gui_list->limit_scroll); + new_selection = allow_wrap ? 0 : gui_list->nb_items - gui_list->selected_size; + edge_beep(gui_list, allow_wrap); } else if (new_selection < 0) { - new_selection = gui_list->limit_scroll ? - 0 : gui_list->nb_items - gui_list->selected_size; - edge_beep(gui_list, !gui_list->limit_scroll); + new_selection = allow_wrap ? gui_list->nb_items - gui_list->selected_size : 0; + edge_beep(gui_list, allow_wrap); } - else if (gui_list->show_selection_marker == false) - { - FOR_NB_SCREENS(i) - { - int nb_lines = list_get_nb_lines(gui_list, i); - if (offset > 0) - { - int screen_top = MAX(0, gui_list->nb_items - nb_lines); - gui_list->start_item[i] = MIN(screen_top, gui_list->start_item[i] + - gui_list->selected_size); - gui_list->selected_item = gui_list->start_item[i]; - } - else - { - gui_list->start_item[i] = MAX(0, gui_list->start_item[i] - - gui_list->selected_size); - gui_list->selected_item = gui_list->start_item[i] + nb_lines; - } -#ifdef HAVE_TOUCHSCREEN - gui_list->y_pos = gui_list->start_item[SCREEN_MAIN] * gui_list->line_height[SCREEN_MAIN]; -#endif - } - return; - } gui_synclist_select_item(gui_list, new_selection); } @@ -460,22 +430,12 @@ void gui_synclist_del_item(struct gui_synclist * gui_list) } } -void gui_list_screen_scroll_step(int ofs) -{ - offset_step = ofs; -} - -void gui_list_screen_scroll_out_of_view(bool enable) -{ - offset_out_of_view = enable; -} - /* * Set the title and title icon of the list. Setting title to NULL disables * both the title and icon. Use NOICON if there is no icon. */ void gui_synclist_set_title(struct gui_synclist * gui_list, - char * title, enum themable_icons icon) + const char * title, enum themable_icons icon) { gui_list->title = title; gui_list->title_icon = icon; @@ -543,26 +503,25 @@ void gui_synclist_set_sel_color(struct gui_synclist * lists, #endif static void gui_synclist_select_next_page(struct gui_synclist * lists, - enum screen_type screen) + enum screen_type screen, + bool allow_wrap) { int nb_lines = list_get_nb_lines(lists, screen); if (lists->selected_size > 1) nb_lines = MAX(1, nb_lines/lists->selected_size); - gui_list_select_at_offset(lists, nb_lines); + + gui_list_select_at_offset(lists, nb_lines, allow_wrap); } static void gui_synclist_select_previous_page(struct gui_synclist * lists, - enum screen_type screen) + enum screen_type screen, + bool allow_wrap) { int nb_lines = list_get_nb_lines(lists, screen); if (lists->selected_size > 1) nb_lines = MAX(1, nb_lines/lists->selected_size); - gui_list_select_at_offset(lists, -nb_lines); -} -void gui_synclist_limit_scroll(struct gui_synclist * lists, bool scroll) -{ - lists->limit_scroll = scroll; + gui_list_select_at_offset(lists, -nb_lines, allow_wrap); } /* @@ -577,7 +536,7 @@ static void gui_synclist_scroll_right(struct gui_synclist * lists) /* FIXME: This is a fake right boundry limiter. there should be some * callback function to find the longest item on the list in pixels, * to stop the list from scrolling past that point */ - lists->offset_position[i] += offset_step; + lists->offset_position[i] += global_settings.screen_scroll_step; if (lists->offset_position[i] > 1000) lists->offset_position[i] = 1000; } @@ -591,7 +550,7 @@ static void gui_synclist_scroll_left(struct gui_synclist * lists) { FOR_NB_SCREENS(i) { - lists->offset_position[i] -= offset_step; + lists->offset_position[i] -= global_settings.screen_scroll_step; if (lists->offset_position[i] < 0) lists->offset_position[i] = 0; } @@ -601,16 +560,22 @@ bool gui_synclist_keyclick_callback(int action, void* data) { struct gui_synclist *lists = (struct gui_synclist *)data; - /* block the beep if we are at the end of the list and we are not wrapping. - * CAVEAT: mosts lists don't set limit_scroll untill it sees a repeat - * press at the end of the list so this can cause an extra beep. - */ - if (lists->limit_scroll == false) - return true; + /* Block the beep if we're at the end of the list and we're not wrapping. */ if (lists->selected_item == 0) - return (action != ACTION_STD_PREV && action != ACTION_STD_PREVREPEAT); + { + if (action == ACTION_STD_PREVREPEAT) + return false; + if (action == ACTION_STD_PREV && !lists->wraparound) + return false; + } + if (lists->selected_item == lists->nb_items - lists->selected_size) - return (action != ACTION_STD_NEXT && action != ACTION_STD_NEXTREPEAT); + { + if (action == ACTION_STD_NEXTREPEAT) + return false; + if (action == ACTION_STD_NEXT && !lists->wraparound) + return false; + } return action != ACTION_NONE; } @@ -635,8 +600,7 @@ static void _lists_uiviewport_update_callback(unsigned short id, void *data) gui_synclist_draw(current_lists); } -bool gui_synclist_do_button(struct gui_synclist * lists, - int *actionptr, enum list_wrap wrap) +bool gui_synclist_do_button(struct gui_synclist * lists, int *actionptr) { int action = *actionptr; static bool pgleft_allow_cancel = false; @@ -683,23 +647,13 @@ bool gui_synclist_do_button(struct gui_synclist * lists, /* Disable the skin redraw callback */ current_lists = NULL; - switch (wrap) - { - case LIST_WRAP_ON: - gui_synclist_limit_scroll(lists, false); - break; - case LIST_WRAP_OFF: - gui_synclist_limit_scroll(lists, true); - break; - case LIST_WRAP_UNLESS_HELD: - if (action == ACTION_STD_PREVREPEAT || - action == ACTION_STD_NEXTREPEAT || - action == ACTION_LISTTREE_PGUP || - action == ACTION_LISTTREE_PGDOWN) - gui_synclist_limit_scroll(lists, true); - else gui_synclist_limit_scroll(lists, false); - break; - }; + /* Prevent list wraparound by repeating actions */ + bool allow_wrap = lists->wraparound; + if (action == ACTION_STD_PREVREPEAT || + action == ACTION_STD_NEXTREPEAT || + action == ACTION_LISTTREE_PGUP || + action == ACTION_LISTTREE_PGDOWN) + allow_wrap = false; switch (action) { @@ -709,17 +663,15 @@ bool gui_synclist_do_button(struct gui_synclist * lists, #ifdef HAVE_VOLUME_IN_LIST case ACTION_LIST_VOLUP: - global_settings.volume += sound_steps(SOUND_VOLUME); - setvol(); + adjust_volume(1); return true; case ACTION_LIST_VOLDOWN: - global_settings.volume -= sound_steps(SOUND_VOLUME); - setvol(); + adjust_volume(-1); return true; #endif case ACTION_STD_PREV: case ACTION_STD_PREVREPEAT: - gui_list_select_at_offset(lists, -next_item_modifier); + gui_list_select_at_offset(lists, -next_item_modifier, allow_wrap); #ifndef HAVE_WHEEL_ACCELERATION if (button_queue_count() < FRAMEDROP_TRIGGER) #endif @@ -730,7 +682,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists, case ACTION_STD_NEXT: case ACTION_STD_NEXTREPEAT: - gui_list_select_at_offset(lists, next_item_modifier); + gui_list_select_at_offset(lists, next_item_modifier, allow_wrap); #ifndef HAVE_WHEEL_ACCELERATION if (button_queue_count() < FRAMEDROP_TRIGGER) #endif @@ -754,7 +706,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists, if (lists->offset_position[0] == 0) { pgleft_allow_cancel = true; - *actionptr = ACTION_STD_CANCEL; + *actionptr = ACTION_STD_MENU; return true; } *actionptr = ACTION_TREE_PGLEFT; @@ -781,7 +733,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists, SCREEN_REMOTE : #endif SCREEN_MAIN; - gui_synclist_select_previous_page(lists, screen); + gui_synclist_select_previous_page(lists, screen, allow_wrap); gui_synclist_draw(lists); yield(); *actionptr = ACTION_STD_NEXT; @@ -796,7 +748,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists, SCREEN_REMOTE : #endif SCREEN_MAIN; - gui_synclist_select_next_page(lists, screen); + gui_synclist_select_next_page(lists, screen, allow_wrap); gui_synclist_draw(lists); yield(); *actionptr = ACTION_STD_PREV; @@ -835,8 +787,7 @@ int list_do_action_timeout(struct gui_synclist *lists, int timeout) } bool list_do_action(int context, int timeout, - struct gui_synclist *lists, int *action, - enum list_wrap wrap) + struct gui_synclist *lists, int *action) /* Combines the get_action() (with possibly overridden timeout) and gui_synclist_do_button() calls. Returns the list action from do_button, and places the action from get_action in *action. */ @@ -844,14 +795,7 @@ bool list_do_action(int context, int timeout, timeout = list_do_action_timeout(lists, timeout); keyclick_set_callback(gui_synclist_keyclick_callback, lists); *action = get_action(context, timeout); - return gui_synclist_do_button(lists, action, wrap); -} - -bool gui_synclist_item_is_onscreen(struct gui_synclist *lists, - enum screen_type screen, int item) -{ - int nb_lines = list_get_nb_lines(lists, screen); - return (unsigned)(item - lists->start_item[screen]) < (unsigned) nb_lines; + return gui_synclist_do_button(lists, action); } /* Simple use list implementation */ @@ -911,7 +855,6 @@ bool simplelist_show_list(struct simplelist_info *info) struct gui_synclist lists; int action, old_line_count = simplelist_line_count; list_get_name *getname; - int wrap = LIST_WRAP_UNLESS_HELD; if (info->get_name) getname = info->get_name; else @@ -936,12 +879,6 @@ bool simplelist_show_list(struct simplelist_info *info) gui_synclist_set_sel_color(&lists, info->selection_color); #endif - if (info->hide_selection) - { - gui_synclist_hide_selection_marker(&lists, true); - wrap = LIST_WRAP_OFF; - } - if (info->action_callback) info->action_callback(ACTION_REDRAW, &lists); @@ -960,8 +897,7 @@ bool simplelist_show_list(struct simplelist_info *info) while(1) { - list_do_action(CONTEXT_LIST, info->timeout, - &lists, &action, wrap); + list_do_action(CONTEXT_LIST, info->timeout, &lists, &action); /* We must yield in this case or no other thread can run */ if (info->timeout == TIMEOUT_NOBLOCK) @@ -1025,7 +961,6 @@ void simplelist_info_init(struct simplelist_info *info, char* title, info->title = title; info->count = count; info->selection_size = 1; - info->hide_selection = false; info->scroll_all = false; info->hide_theme = false; info->speak_onshow = true; diff --git a/apps/gui/list.h b/apps/gui/list.h index 64ff3e3fdd..40a27d1061 100644 --- a/apps/gui/list.h +++ b/apps/gui/list.h @@ -30,10 +30,12 @@ #define SCROLLBAR_WIDTH global_settings.scrollbar_width -enum list_wrap { - LIST_WRAP_ON = 0, - LIST_WRAP_OFF, - LIST_WRAP_UNLESS_HELD, +enum synclist_cursor +{ + SYNCLIST_CURSOR_NOSTYLE = 0, + SYNCLIST_CURSOR_INVERT, + SYNCLIST_CURSOR_COLOR, + SYNCLIST_CURSOR_GRADIENT, }; /* @@ -69,6 +71,35 @@ typedef enum themable_icons list_get_icon(int selected_item, void * data); typedef const char * list_get_name(int selected_item, void * data, char * buffer, size_t buffer_len); /* + * Draw callback + * - display : functions supplied depends on the screen call originated from (typ: MAIN) + * - list_info : a pointer to an internal struct containing item display information + */ +/* owner drawn lists need to know this info */ +struct list_putlineinfo_t { + int x; + int y; + int item_indent; + int item_offset; + int line; + + int icon; + int icon_width; + + struct screen *display; + struct viewport *vp; + struct line_desc *linedes; + struct gui_synclist * list; + const char *dsp_text; + + bool is_selected; + bool is_title; + bool show_cursor; + bool have_icons; +}; + +typedef void list_draw_item(struct list_putlineinfo_t *list_info); +/* * Voice callback * - selected_item : an integer that tells the number of the item to speak * - data : a void pointer to the data you gave to the list when you @@ -110,14 +141,20 @@ struct list_selection_color struct gui_synclist { - /* defines wether the list should stop when reaching the top/bottom - * or should continue (by going to bottom/top) */ - bool limit_scroll; - /* wether the text of the whole items of the list have to be + /*flags to hold settings show: icons, scrollbar etc..*/ + int scrollbar; + int cursor_style; + bool show_icons; + bool keyclick; + bool talk_menu; + bool wraparound; + bool scroll_paginated; + /* whether the text of the whole items of the list have to be * scrolled or only for the selected item */ bool scroll_all; int nb_items; int selected_item; + #ifdef HAVE_TOUCHSCREEN /* absolute Y coordinate, used for smooth scrolling */ int y_pos; @@ -133,14 +170,14 @@ struct gui_synclist list_get_icon *callback_get_item_icon; list_get_name *callback_get_item_name; list_speak_item *callback_speak_item; + list_draw_item *callback_draw_item; /* The data that will be passed to the callback function YOU implement */ void * data; /* The optional title, set to NULL for none */ - char * title; + const char * title; /* Optional title icon */ enum themable_icons title_icon; - bool show_selection_marker; /* set to true by default */ #ifdef HAVE_LCD_COLOR int title_color; @@ -152,11 +189,6 @@ struct gui_synclist extern void list_init(void); -/* parse global setting to static int */ -extern void gui_list_screen_scroll_step(int ofs); - -/* parse global setting to static bool */ -extern void gui_list_screen_scroll_out_of_view(bool enable); extern void gui_synclist_init( struct gui_synclist * lists, @@ -185,13 +217,8 @@ extern void gui_synclist_select_item(struct gui_synclist * lists, int item_number); extern void gui_synclist_add_item(struct gui_synclist * lists); extern void gui_synclist_del_item(struct gui_synclist * lists); -extern void gui_synclist_limit_scroll(struct gui_synclist * lists, bool scroll); -extern void gui_synclist_set_title(struct gui_synclist * lists, char * title, +extern void gui_synclist_set_title(struct gui_synclist * lists, const char * title, enum themable_icons icon); -extern void gui_synclist_hide_selection_marker(struct gui_synclist *lists, - bool hide); -extern bool gui_synclist_item_is_onscreen(struct gui_synclist *lists, - enum screen_type screen, int item); extern bool gui_synclist_keyclick_callback(int action, void* data); /* @@ -199,20 +226,9 @@ extern bool gui_synclist_keyclick_callback(int action, void* data); * returns true if the action was handled. * NOTE: *action may be changed regardless of return value */ -extern bool gui_synclist_do_button(struct gui_synclist * lists, - int *action, - enum list_wrap); +extern bool gui_synclist_do_button(struct gui_synclist * lists, int *action); #if !defined(PLUGIN) -struct listitem_viewport_cfg { - struct wps_data *data; - OFFSETTYPE(char *) label; - int width; - int height; - int xmargin; - int ymargin; - bool tile; - struct skin_viewport selected_item_vp; -}; +struct listitem_viewport_cfg; bool skinlist_get_item(struct screen *display, struct gui_synclist *list, int x, int y, int *item); bool skinlist_draw(struct screen *display, struct gui_synclist *list); @@ -244,8 +260,7 @@ extern int list_do_action_timeout(struct gui_synclist *lists, int timeout); list_do_action_timeout) with the gui_synclist_do_button call, for convenience. */ extern bool list_do_action(int context, int timeout, - struct gui_synclist *lists, int *action, - enum list_wrap wrap); + struct gui_synclist *lists, int *action); /** Simplelist implementation. @@ -254,10 +269,9 @@ extern bool list_do_action(int context, int timeout, **/ struct simplelist_info { - char *title; /* title to show on the list */ + const char *title; /* title to show on the list */ int count; /* number of items in the list, each item is selection_size high */ int selection_size; /* list selection size, usually 1 */ - bool hide_selection; bool scroll_all; bool hide_theme; bool speak_onshow; /* list speaks first item or 'empty list' */ @@ -303,7 +317,6 @@ void simplelist_addline(const char *fmt, ...); /* setup the info struct. members not setup in this function need to be assigned manually members set in this function: info.selection_size = 1; - info.hide_selection = false; info.scroll_all = false; info.hide_theme = false; info.speak_onshow = true; diff --git a/apps/gui/option_select.c b/apps/gui/option_select.c index 9f1f0a64e3..8839f42e42 100644 --- a/apps/gui/option_select.c +++ b/apps/gui/option_select.c @@ -68,8 +68,8 @@ const char *option_get_valuestring(const struct settings_list *setting, if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING) { bool val = (bool)temp_var; - strlcpy(buffer, str(val? setting->bool_setting->lang_yes : - setting->bool_setting->lang_no), buf_len); + strmemccpy(buffer, str(val? setting->bool_setting->lang_yes : + setting->bool_setting->lang_no), buf_len); } #if 0 /* probably dont need this one */ else if ((setting->flags & F_FILENAME) == F_FILENAME) @@ -117,23 +117,21 @@ const char *option_get_valuestring(const struct settings_list *setting, { if (setting->flags & F_CHOICETALKS) { - int setting_id; const struct choice_setting *info = setting->choice_setting; if (info->talks[(int)temp_var] < LANG_LAST_INDEX_IN_ARRAY) { - strlcpy(buffer, str(info->talks[(int)temp_var]), buf_len); + strmemccpy(buffer, str(info->talks[(int)temp_var]), buf_len); } else { - find_setting(setting->setting, &setting_id); - cfg_int_to_string(setting_id, (int)temp_var, buffer, buf_len); + cfg_int_to_string(setting, (int)temp_var, buffer, buf_len); } } else { int value = (int)temp_var; char *val = P2STR(setting->choice_setting->desc[value]); - strlcpy(buffer, val, buf_len); + strmemccpy(buffer, val, buf_len); } } return str; @@ -334,29 +332,35 @@ static int selection_to_val(const struct settings_list *setting, int selection) else if ((setting->flags & F_T_SOUND) == F_T_SOUND) { int setting_id = setting->sound_setting->setting; -#ifndef ASCENDING_INT_SETTINGS - step = sound_steps(setting_id); - max = (setting_id == SOUND_VOLUME) ? - global_settings.volume_limit : sound_max(setting_id); - /* min = sound_min(setting_id); */ -#else - step = -sound_steps(setting_id); - /* min = sound_max(setting_id); */ - max = sound_min(setting_id); -#endif + if(global_settings.list_order == LIST_ORDER_DESCENDING) + { + step = sound_steps(setting_id); + max = (setting_id == SOUND_VOLUME) ? + global_settings.volume_limit : sound_max(setting_id); + /* min = sound_min(setting_id); */ + } + else + { + step = -sound_steps(setting_id); + /* min = sound_max(setting_id); */ + max = sound_min(setting_id); + } } else if ((setting->flags & F_INT_SETTING) == F_INT_SETTING) { const struct int_setting *info = setting->int_setting; -#ifndef ASCENDING_INT_SETTINGS - /* min = info->min; */ - max = info->max; - step = info->step; -#else - max = info->min; - /* min = info->max; */ - step = -info->step; -#endif + if(global_settings.list_order == LIST_ORDER_DESCENDING) + { + /* min = info->min; */ + max = info->max; + step = info->step; + } + else + { + max = info->min; + /* min = info->max; */ + step = -info->step; + } } return max- (selection * step); } @@ -424,11 +428,10 @@ static void val_to_selection(const struct settings_list *setting, int oldvalue, int max = (setting_id == SOUND_VOLUME) ? global_settings.volume_limit : sound_max(setting_id); *nb_items = (max-min)/steps + 1; -#ifndef ASCENDING_INT_SETTINGS - *selected = (max - oldvalue) / steps; -#else - *selected = (oldvalue - min) / steps; -#endif + if (global_settings.list_order == LIST_ORDER_DESCENDING) + *selected = (max - oldvalue) / steps; + else + *selected = (oldvalue - min) / steps; *function = sound_get_fn(setting_id); } else @@ -439,11 +442,10 @@ static void val_to_selection(const struct settings_list *setting, int oldvalue, min = info->min; step = info->step; *nb_items = (max-min)/step + 1; -#ifndef ASCENDING_INT_SETTINGS - *selected = (max - oldvalue) / step; -#else - *selected = (oldvalue - min) / step; -#endif + if(global_settings.list_order == LIST_ORDER_DESCENDING) + *selected = (max - oldvalue) / step; + else + *selected = (oldvalue - min) / step; *function = info->option_callback; } } @@ -502,15 +504,18 @@ bool option_screen(const struct settings_list *setting, gui_synclist_set_nb_items(&lists, nb_items); gui_synclist_select_item(&lists, selected); - gui_synclist_limit_scroll(&lists, true); gui_synclist_draw(&lists); /* talk the item */ gui_synclist_speak_item(&lists); while (!done) { + /* override user wraparound setting; used mainly by EQ settings. + * Not sure this is justified? */ + if (!allow_wrap) + lists.wraparound = false; + if (list_do_action(CONTEXT_LIST, HZ, /* HZ so the status bar redraws */ - &lists, &action, - allow_wrap? LIST_WRAP_UNLESS_HELD: LIST_WRAP_OFF)) + &lists, &action)) { /* setting changed */ selected = gui_synclist_get_sel_pos(&lists); @@ -520,7 +525,7 @@ bool option_screen(const struct settings_list *setting, } else if (action == ACTION_NONE) continue; - else if (action == ACTION_STD_CANCEL) + else if (action == ACTION_STD_CANCEL || action == ACTION_STD_MENU) { /* setting canceled, restore old value if changed */ if (*variable != oldvalue) @@ -560,7 +565,10 @@ bool option_screen(const struct settings_list *setting, function(*variable); } else if(default_event_handler(action) == SYS_USB_CONNECTED) + { + pop_current_activity(); return true; + } /* callback */ if (function && !cb_on_select_only) function(*variable); @@ -573,9 +581,8 @@ bool option_screen(const struct settings_list *setting, return false; } -int get_setting_info_for_bar(int setting_id, int *count, int *val) +int get_setting_info_for_bar(const struct settings_list *setting, int *count, int *val) { - const struct settings_list *setting = &settings[setting_id]; int var_type = setting->flags&F_T_MASK; void (*function)(int) = NULL; int oldvalue; @@ -600,9 +607,8 @@ int get_setting_info_for_bar(int setting_id, int *count, int *val) } #ifdef HAVE_TOUCHSCREEN -void update_setting_value_from_touch(int setting_id, int selection) +void update_setting_value_from_touch(const struct settings_list *setting, int selection) { - const struct settings_list *setting = &settings[setting_id]; int new_val = selection_to_val(setting, selection); int var_type = setting->flags&F_T_MASK; diff --git a/apps/gui/option_select.h b/apps/gui/option_select.h index 476e7b81bd..eabe5825e7 100644 --- a/apps/gui/option_select.h +++ b/apps/gui/option_select.h @@ -25,11 +25,10 @@ #include "screen_access.h" #include "settings.h" -#if defined (HAVE_SCROLLWHEEL) && !defined(FIIO_M3K) -/* Define this if your target makes sense to have - smaller values at the top of the list increasing down the list */ -#define ASCENDING_INT_SETTINGS -#endif +enum { + LIST_ORDER_DESCENDING = 0, + LIST_ORDER_ASCENDING = 1, +}; bool option_screen(const struct settings_list *setting, struct viewport parent[NB_SCREENS], @@ -47,9 +46,9 @@ void option_talk_value(const struct settings_list *setting, int value, bool enqu /* only use this for int and bool settings */ int option_value_as_int(const struct settings_list *setting); -int get_setting_info_for_bar(int setting_id, int *count, int *val); +int get_setting_info_for_bar(const struct settings_list *setting, int *count, int *val); #ifdef HAVE_TOUCHSCREEN -void update_setting_value_from_touch(int setting_id, int selection); +void update_setting_value_from_touch(const struct settings_list *setting, int selection); #endif #endif /* _GUI_OPTION_SELECT_H_ */ diff --git a/apps/gui/pitchscreen.c b/apps/gui/pitchscreen.c index 871921a10f..9f42aedb5d 100644 --- a/apps/gui/pitchscreen.c +++ b/apps/gui/pitchscreen.c @@ -18,1059 +18,8 @@ * KIND, either express or implied. * ****************************************************************************/ - -#include <stdbool.h> -#include <string.h> -#include <stdio.h> -#include <math.h> -#include <stdlib.h> /* for abs() */ -#include "config.h" -#include "action.h" -#include "sound.h" -#include "pcmbuf.h" -#include "lang.h" -#include "icons.h" -#include "screens.h" -#include "talk.h" -#include "viewport.h" -#include "font.h" -#include "system.h" -#include "misc.h" -#include "pitchscreen.h" -#include "settings.h" -#include "tdspeed.h" - -#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */ - /* on both sides when drawing */ - -#define PITCH_MAX (200 * PITCH_SPEED_PRECISION) -#define PITCH_MIN (50 * PITCH_SPEED_PRECISION) -#define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */ -#define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */ -#define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */ - -#define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */ -#define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */ - -#define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */ -#define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */ - -enum -{ - PITCH_TOP = 0, - PITCH_MID, - PITCH_BOTTOM, - PITCH_ITEM_COUNT, -}; - - -/* This is a table of semitone percentage values of the appropriate - precision (based on PITCH_SPEED_PRECISION). Note that these are - all constant expressions, which will be evaluated at compile time, - so no need to worry about how complex the expressions look. - That's just to get the precision right. - - I calculated these values, starting from 50, as - - x(n) = 50 * 2^(n/12) - - All that math in each entry simply converts the float constant - to an integer equal to PITCH_SPEED_PRECISION times the float value, - with as little precision loss as possible (i.e. correctly rounding - the last digit). -*/ -#define TO_INT_WITH_PRECISION(x) \ - ( (unsigned short)(((x) * PITCH_SPEED_PRECISION * 10 + 5) / 10) ) - -static const unsigned short semitone_table[] = -{ - TO_INT_WITH_PRECISION(50.00000000), /* Octave lower */ - TO_INT_WITH_PRECISION(52.97315472), - TO_INT_WITH_PRECISION(56.12310242), - TO_INT_WITH_PRECISION(59.46035575), - TO_INT_WITH_PRECISION(62.99605249), - TO_INT_WITH_PRECISION(66.74199271), - TO_INT_WITH_PRECISION(70.71067812), - TO_INT_WITH_PRECISION(74.91535384), - TO_INT_WITH_PRECISION(79.37005260), - TO_INT_WITH_PRECISION(84.08964153), - TO_INT_WITH_PRECISION(89.08987181), - TO_INT_WITH_PRECISION(94.38743127), - TO_INT_WITH_PRECISION(100.0000000), /* Normal sound */ - TO_INT_WITH_PRECISION(105.9463094), - TO_INT_WITH_PRECISION(112.2462048), - TO_INT_WITH_PRECISION(118.9207115), - TO_INT_WITH_PRECISION(125.9921049), - TO_INT_WITH_PRECISION(133.4839854), - TO_INT_WITH_PRECISION(141.4213562), - TO_INT_WITH_PRECISION(149.8307077), - TO_INT_WITH_PRECISION(158.7401052), - TO_INT_WITH_PRECISION(168.1792831), - TO_INT_WITH_PRECISION(178.1797436), - TO_INT_WITH_PRECISION(188.7748625), - TO_INT_WITH_PRECISION(200.0000000) /* Octave higher */ -}; - -#define NUM_SEMITONES ((int)(sizeof(semitone_table)/sizeof(semitone_table[0]))) -#define SEMITONE_END (NUM_SEMITONES/2) -#define SEMITONE_START (-SEMITONE_END) - -/* A table of values for approximating the cent curve with - linear interpolation. Multipy the next lowest semitone - by this much to find the corresponding cent percentage. - - These values were calculated as - x(n) = 100 * 2^(n * 20/1200) -*/ - -static const unsigned short cent_interp[] = -{ - TO_INT_WITH_PRECISION(100.0000000), - TO_INT_WITH_PRECISION(101.1619440), - TO_INT_WITH_PRECISION(102.3373892), - TO_INT_WITH_PRECISION(103.5264924), - TO_INT_WITH_PRECISION(104.7294123), - /* this one's the next semitone but we have it here for convenience */ - TO_INT_WITH_PRECISION(105.9463094), -}; - -/* Number of cents between entries in the cent_interp table */ -#define CENT_INTERP_INTERVAL 20 -#define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(cent_interp[0]))) - -/* This stores whether the pitch and speed are at their own limits */ -/* or that of the timestretching algorithm */ -static bool at_limit = false; - -/* - * - * The pitchscreen is divided into 3 viewports (each row is a viewport) - * Then each viewport is again divided into 3 colums, each showsing some infos - * Additionally, on touchscreen, each cell represents a button - * - * Below a sketch describing what each cell will show (what's drawn on it) - * -------------------------- - * | | | | <-- pitch up in the middle (text and button) - * | | | | <-- arrows for mode toggling on the sides for touchscreen - * |------------------------| - * | | | | <-- semitone/speed up/down on the sides - * | | | | <-- reset pitch&speed in the middle - * |------------------------| - * | | | | <-- pitch down in the middle - * | | | | <-- Two "OK" for exit on the sides for touchscreen - * |------------------------| - * - * - */ - -static void speak_pitch_mode(bool enqueue) -{ - bool timestretch_mode = global_settings.pitch_mode_timestretch && dsp_timestretch_available(); - if (timestretch_mode) - talk_id(VOICE_PITCH_TIMESTRETCH_MODE, enqueue); - if (global_settings.pitch_mode_semitone) - talk_id(VOICE_PITCH_SEMITONE_MODE, timestretch_mode ? true : enqueue); - else - talk_id(VOICE_PITCH_ABSOLUTE_MODE, timestretch_mode ? true : enqueue); - return; -} - -/* - * Fixes the viewports so they represent the 3 rows, and adds a little margin - * on all sides for the icons (which are drawn outside of the grid - * - * The modified viewports need to be passed to the touchscreen handling function - **/ -static void pitchscreen_fix_viewports(struct viewport *parent, - struct viewport pitch_viewports[PITCH_ITEM_COUNT]) -{ - int i, font_height; - font_height = font_get(parent->font)->height; - for (i = 0; i < PITCH_ITEM_COUNT; i++) - { - pitch_viewports[i] = *parent; - pitch_viewports[i].height = parent->height / PITCH_ITEM_COUNT; - pitch_viewports[i].x += ICON_BORDER; - pitch_viewports[i].width -= 2*ICON_BORDER; - } - pitch_viewports[PITCH_TOP].y += ICON_BORDER; - pitch_viewports[PITCH_TOP].height -= ICON_BORDER; - - if(pitch_viewports[PITCH_MID].height < font_height * 2) - pitch_viewports[PITCH_MID].height = font_height * 2; - - pitch_viewports[PITCH_MID].y = pitch_viewports[PITCH_TOP].y - + pitch_viewports[PITCH_TOP].height; - - pitch_viewports[PITCH_BOTTOM].y = pitch_viewports[PITCH_MID].y - + pitch_viewports[PITCH_MID].height; - - pitch_viewports[PITCH_BOTTOM].height -= ICON_BORDER; -} - -/* must be called before pitchscreen_draw, or within - * since it neither clears nor updates the display */ -static void pitchscreen_draw_icons(struct screen *display, - struct viewport *parent) -{ - display->set_viewport(parent); - display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow], - parent->width/2 - 3, - 2, 7, 8); - display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow], - parent->width /2 - 3, - parent->height - 10, 7, 8); - display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward], - parent->width - 10, - parent->height /2 - 4, 7, 8); - display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward], - 2, - parent->height /2 - 4, 7, 8); - display->update_viewport(); -} - -static void pitchscreen_draw(struct screen *display, int max_lines, - struct viewport pitch_viewports[PITCH_ITEM_COUNT], - int32_t pitch, int32_t semitone - ,int32_t speed - ) -{ - const char* ptr; - char buf[32]; - int w, h; - bool show_lang_pitch; - struct viewport *last_vp = NULL; - - /* "Pitch up/Pitch down" - hide for a small screen, - * the text is drawn centered automatically - * - * note: this assumes 5 lines always fit on a touchscreen (should be - * reasonable) */ - if (max_lines >= 5) - { - int w, h; - struct viewport *vp = &pitch_viewports[PITCH_TOP]; - last_vp = display->set_viewport(vp); - display->clear_viewport(); -#ifdef HAVE_TOUCHSCREEN - /* two arrows in the top row, left and right column */ - char *arrows[] = { "<", ">"}; - display->getstringsize(arrows[0], &w, &h); - display->putsxy(0, vp->height/2 - h/2, arrows[0]); - display->putsxy(vp->width - w, vp->height/2 - h/2, arrows[1]); -#endif - /* UP: Pitch Up */ - if (global_settings.pitch_mode_semitone) - ptr = str(LANG_PITCH_UP_SEMITONE); - else - ptr = str(LANG_PITCH_UP); - - display->getstringsize(ptr, &w, NULL); - /* draw text */ - display->putsxy(vp->width/2 - w/2, 0, ptr); - display->update_viewport(); - - /* DOWN: Pitch Down */ - vp = &pitch_viewports[PITCH_BOTTOM]; - display->set_viewport(vp); - display->clear_viewport(); - -#ifdef HAVE_TOUCHSCREEN - ptr = str(LANG_KBD_OK); - display->getstringsize(ptr, &w, &h); - /* one OK in the middle first column of the vp (at half height) */ - display->putsxy(vp->width/6 - w/2, vp->height/2 - h/2, ptr); - /* one OK in the middle of the last column of the vp (at half height) */ - display->putsxy(5*vp->width/6 - w/2, vp->height/2 - h/2, ptr); -#endif - if (global_settings.pitch_mode_semitone) - ptr = str(LANG_PITCH_DOWN_SEMITONE); - else - ptr = str(LANG_PITCH_DOWN); - display->getstringsize(ptr, &w, &h); - /* draw text */ - display->putsxy(vp->width/2 - w/2, vp->height - h, ptr); - display->update_viewport(); - } - - /* Middle section */ - display->set_viewport(&pitch_viewports[PITCH_MID]); - display->clear_viewport(); - int width_used = 0; - - /* Middle section upper line - hide for a small screen */ - if ((show_lang_pitch = (max_lines >= 3))) - { - if(global_settings.pitch_mode_timestretch) - { - /* Pitch:XXX.X% */ - if(global_settings.pitch_mode_semitone) - { - snprintf(buf, sizeof(buf), "%s: %s%d.%02d", str(LANG_PITCH), - semitone >= 0 ? "+" : "-", - abs(semitone / PITCH_SPEED_PRECISION), - abs((semitone % PITCH_SPEED_PRECISION) / - (PITCH_SPEED_PRECISION / 100)) - ); - } - else - { - snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH), - pitch / PITCH_SPEED_PRECISION, - (pitch % PITCH_SPEED_PRECISION) / - (PITCH_SPEED_PRECISION / 10)); - } - } - else - { - /* Rate */ - snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE)); - } - display->getstringsize(buf, &w, &h); - display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2), - (pitch_viewports[PITCH_MID].height / 2) - h, buf); - if (w > width_used) - width_used = w; - } - - /* Middle section lower line */ - /* "Speed:XXX%" */ - if(global_settings.pitch_mode_timestretch) - { - snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED), - speed / PITCH_SPEED_PRECISION, - (speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10)); - } - else - { - if(global_settings.pitch_mode_semitone) - { - snprintf(buf, sizeof(buf), "%s%d.%02d", - semitone >= 0 ? "+" : "-", - abs(semitone / PITCH_SPEED_PRECISION), - abs((semitone % PITCH_SPEED_PRECISION) / - (PITCH_SPEED_PRECISION / 100)) - ); - } - else - { - snprintf(buf, sizeof(buf), "%ld.%ld%%", - pitch / PITCH_SPEED_PRECISION, - (pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10)); - } - } - - display->getstringsize(buf, &w, &h); - display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2), - show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) : - (pitch_viewports[PITCH_MID].height / 2) - (h / 2), - buf); - if (w > width_used) - width_used = w; - - /* "limit" and "timestretch" labels */ - if (max_lines >= 7) - { - if(at_limit) - { - const char * const p = str(LANG_STRETCH_LIMIT); - display->getstringsize(p, &w, &h); - display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2), - (pitch_viewports[PITCH_MID].height / 2) + h, p); - if (w > width_used) - width_used = w; - } - } - - /* Middle section left/right labels */ - const char *leftlabel = "-2%"; - const char *rightlabel = "+2%"; - if (global_settings.pitch_mode_timestretch) - { - leftlabel = "<<"; - rightlabel = ">>"; - } - - /* Only display if they fit */ - display->getstringsize(leftlabel, &w, &h); - width_used += w; - display->getstringsize(rightlabel, &w, &h); - width_used += w; - - if (width_used <= pitch_viewports[PITCH_MID].width) - { - display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2), - leftlabel); - display->putsxy((pitch_viewports[PITCH_MID].width - w), - (pitch_viewports[PITCH_MID].height / 2) - (h / 2), - rightlabel); - } - display->update_viewport(); - display->set_viewport(last_vp); -} - -static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff - /* need this to maintain correct pitch/speed caps */ - , int32_t speed - ) -{ - int32_t new_pitch; - int32_t new_stretch; - at_limit = false; - - if (pitch_delta < 0) - { - /* for large jumps, snap up to whole numbers */ - if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION && - (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0) - { - pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION); - } - - new_pitch = pitch + pitch_delta; - - if (new_pitch < PITCH_MIN) - { - if (!allow_cutoff) - { - return pitch; - } - new_pitch = PITCH_MIN; - at_limit = true; - } - } - else if (pitch_delta > 0) - { - /* for large jumps, snap down to whole numbers */ - if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION && - (pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0) - { - pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION; - } - - new_pitch = pitch + pitch_delta; - - if (new_pitch > PITCH_MAX) - { - if (!allow_cutoff) - return pitch; - new_pitch = PITCH_MAX; - at_limit = true; - } - } - else - { - /* pitch_delta == 0 -> no real change */ - return pitch; - } - if (dsp_timestretch_available()) - { - /* increase the multiple to increase precision of this calculation */ - new_stretch = GET_STRETCH(new_pitch, speed); - if(new_stretch < STRETCH_MIN) - { - /* we have to ignore allow_cutoff, because we can't have the */ - /* stretch go higher than STRETCH_MAX */ - new_pitch = GET_PITCH(speed, STRETCH_MIN); - } - else if(new_stretch > STRETCH_MAX) - { - /* we have to ignore allow_cutoff, because we can't have the */ - /* stretch go higher than STRETCH_MAX */ - new_pitch = GET_PITCH(speed, STRETCH_MAX); - } - - if(new_stretch >= STRETCH_MAX || - new_stretch <= STRETCH_MIN) - { - at_limit = true; - } - } - - sound_set_pitch(new_pitch); - - return new_pitch; -} - -static int32_t get_semitone_from_pitch(int32_t pitch) -{ - int semitone = 0; - int32_t fractional_index = 0; - - while(semitone < NUM_SEMITONES - 1 && - pitch >= semitone_table[semitone + 1]) - { - semitone++; - } - - - /* now find the fractional part */ - while(pitch > (cent_interp[fractional_index + 1] * - semitone_table[semitone] / PITCH_SPEED_100)) - { - /* Check to make sure fractional_index isn't too big */ - /* This should never happen. */ - if(fractional_index >= CENT_INTERP_NUM - 1) - { - break; - } - fractional_index++; - } - - int32_t semitone_pitch_a = cent_interp[fractional_index] * - semitone_table[semitone] / - PITCH_SPEED_100; - int32_t semitone_pitch_b = cent_interp[fractional_index + 1] * - semitone_table[semitone] / - PITCH_SPEED_100; - /* this will be the integer offset from the cent_interp entry */ - int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL / - (semitone_pitch_b - semitone_pitch_a); - semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION + - fractional_index * CENT_INTERP_INTERVAL + - semitone_frac_ofs; - - return semitone; -} - -static int32_t get_pitch_from_semitone(int32_t semitone) -{ - int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION; - - /* Find the index into the semitone table */ - int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION); - - /* set pitch to the semitone's integer part value */ - int32_t pitch = semitone_table[semitone_index]; - /* get the range of the cent modification for future calculation */ - int32_t pitch_mod_a = - cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) / - CENT_INTERP_INTERVAL]; - int32_t pitch_mod_b = - cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) / - CENT_INTERP_INTERVAL + 1]; - /* figure out the cent mod amount based on the semitone fractional value */ - int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) * - (adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL; - - /* modify pitch based on the mod amount we just calculated */ - return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100; -} - -static int32_t pitch_increase_semitone(int32_t pitch, - int32_t current_semitone, - int32_t semitone_delta - , int32_t speed - ) -{ - int32_t new_semitone = current_semitone; - - /* snap to the delta interval */ - if(current_semitone % semitone_delta != 0) - { - if(current_semitone > 0 && semitone_delta > 0) - new_semitone += semitone_delta; - else if(current_semitone < 0 && semitone_delta < 0) - new_semitone += semitone_delta; - - new_semitone -= new_semitone % semitone_delta; - } - else - new_semitone += semitone_delta; - - /* clamp the pitch so it doesn't go beyond the pitch limits */ - if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION)) - { - new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION; - at_limit = true; - } - else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION)) - { - new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION; - at_limit = true; - } - - int32_t new_pitch = get_pitch_from_semitone(new_semitone); - - int32_t new_stretch = GET_STRETCH(new_pitch, speed); - - /* clamp the pitch so it doesn't go beyond the stretch limits */ - if( new_stretch > STRETCH_MAX) - { - new_pitch = GET_PITCH(speed, STRETCH_MAX); - new_semitone = get_semitone_from_pitch(new_pitch); - at_limit = true; - } - else if (new_stretch < STRETCH_MIN) - { - new_pitch = GET_PITCH(speed, STRETCH_MIN); - new_semitone = get_semitone_from_pitch(new_pitch); - at_limit = true; - } - - pitch_increase(pitch, new_pitch - pitch, false - , speed - ); - - return new_semitone; -} - -#ifdef HAVE_TOUCHSCREEN -/* - * Check for touchscreen presses as per sketch above in this file - * - * goes through each row of the, checks whether the touchscreen - * was pressed in it. Then it looks the columns of each row for specific actions - */ -static int pitchscreen_do_touchscreen(struct viewport vps[]) -{ - short x, y; - struct viewport *this_vp = &vps[PITCH_TOP]; - int ret; - static bool wait_for_release = false; - ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp); - - /* top row */ - if (ret > ACTION_UNKNOWN) - { /* press on top row, left or right column - * only toggle mode if released */ - int column = this_vp->width / 3; - if ((x < column || x > (2*column)) && (ret == BUTTON_REL)) - return ACTION_PS_TOGGLE_MODE; - - - else if (x >= column && x <= (2*column)) - { /* center column pressed */ - if (ret == BUTTON_REPEAT) - return ACTION_PS_INC_BIG; - else if (ret & BUTTON_REL) - return ACTION_PS_INC_SMALL; - } - return ACTION_NONE; - } - - /* now the center row */ - this_vp = &vps[PITCH_MID]; - ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp); - - if (ret > ACTION_UNKNOWN) - { - int column = this_vp->width / 3; - - if (x < column) - { /* left column */ - if (ret & BUTTON_REL) - { - wait_for_release = false; - return ACTION_PS_NUDGE_LEFTOFF; - } - else if (ret & BUTTON_REPEAT) - return ACTION_PS_SLOWER; - if (!wait_for_release) - { - wait_for_release = true; - return ACTION_PS_NUDGE_LEFT; - } - } - else if (x > (2*column)) - { /* right column */ - if (ret & BUTTON_REL) - { - wait_for_release = false; - return ACTION_PS_NUDGE_RIGHTOFF; - } - else if (ret & BUTTON_REPEAT) - return ACTION_PS_FASTER; - if (!wait_for_release) - { - wait_for_release = true; - return ACTION_PS_NUDGE_RIGHT; - } - } - else - /* center column was pressed */ - return ACTION_PS_RESET; - } - - /* now the bottom row */ - this_vp = &vps[PITCH_BOTTOM]; - ret = action_get_touchscreen_press_in_vp(&x, &y, this_vp); - - if (ret > ACTION_UNKNOWN) - { - int column = this_vp->width / 3; - - /* left or right column is exit */ - if ((x < column || x > (2*column)) && (ret == BUTTON_REL)) - return ACTION_PS_EXIT; - else if (x >= column && x <= (2*column)) - { /* center column was pressed */ - if (ret & BUTTON_REPEAT) - return ACTION_PS_DEC_BIG; - else if (ret & BUTTON_REL) - return ACTION_PS_DEC_SMALL; - } - return ACTION_NONE; - } - return ACTION_NONE; -} - -#endif -/* - returns: - 0 on exit - 1 if USB was connected -*/ - +#include "plugin.h" int gui_syncpitchscreen_run(void) { - int button; - int32_t pitch = sound_get_pitch(); - int32_t semitone; - - int32_t new_pitch; - int32_t pitch_delta; - bool nudged = false; - int i, updated = 4, decimals = 0; - bool exit = false; - /* should maybe be passed per parameter later, not needed for now */ - struct viewport parent[NB_SCREENS]; - struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT]; - int max_lines[NB_SCREENS]; - - push_current_activity(ACTIVITY_PITCHSCREEN); - - int32_t new_speed = 0, new_stretch; - - /* the speed variable holds the apparent speed of the playback */ - int32_t speed; - if (dsp_timestretch_available()) - { - speed = GET_SPEED(pitch, dsp_get_timestretch()); - } - else - { - speed = pitch; - } - - /* Figure out whether to be in timestretch mode */ - if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available()) - { - global_settings.pitch_mode_timestretch = false; - settings_save(); - } - - /* Count decimals for speaking */ - for (i = PITCH_SPEED_PRECISION; i >= 10; i /= 10) - decimals++; - - /* set the semitone index based on the current pitch */ - semitone = get_semitone_from_pitch(pitch); - - /* initialize pitchscreen vps */ - FOR_NB_SCREENS(i) - { - viewport_set_defaults(&parent[i], i); - max_lines[i] = viewport_get_nb_lines(&parent[i]); - pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]); - screens[i].set_viewport(&parent[i]); - screens[i].clear_viewport(); - - /* also, draw the icons now, it's only needed once */ - pitchscreen_draw_icons(&screens[i], &parent[i]); - } - pcmbuf_set_low_latency(true); - - while (!exit) - { - FOR_NB_SCREENS(i) - pitchscreen_draw(&screens[i], max_lines[i], - pitch_viewports[i], pitch, semitone - , speed - ); - pitch_delta = 0; - new_speed = 0; - - if (global_settings.talk_menu && updated) - { - talk_shutup(); - switch (updated) - { - case 1: - if (global_settings.pitch_mode_semitone) - talk_value_decimal(semitone, UNIT_SIGNED, decimals, false); - else - talk_value_decimal(pitch, UNIT_PERCENT, decimals, false); - break; - case 2: - talk_value_decimal(speed, UNIT_PERCENT, decimals, false); - break; - case 3: - speak_pitch_mode(false); - break; - case 4: - if (global_settings.pitch_mode_timestretch && dsp_timestretch_available()) - talk_id(LANG_PITCH, false); - else - talk_id(LANG_PLAYBACK_RATE, false); - talk_value_decimal(pitch, UNIT_PERCENT, decimals, true); - if (global_settings.pitch_mode_timestretch && dsp_timestretch_available()) - { - talk_id(LANG_SPEED, true); - talk_value_decimal(speed, UNIT_PERCENT, decimals, true); - } - speak_pitch_mode(true); - break; - default: - break; - } - } - updated = 0; - - button = get_action(CONTEXT_PITCHSCREEN, HZ); - -#ifdef HAVE_TOUCHSCREEN - if (button == ACTION_TOUCHSCREEN) - { - FOR_NB_SCREENS(i) - button = pitchscreen_do_touchscreen(pitch_viewports[i]); - } -#endif - switch (button) - { - case ACTION_PS_INC_SMALL: - if(global_settings.pitch_mode_semitone) - pitch_delta = SEMITONE_SMALL_DELTA; - else - pitch_delta = PITCH_SMALL_DELTA; - updated = 1; - break; - - case ACTION_PS_INC_BIG: - if(global_settings.pitch_mode_semitone) - pitch_delta = SEMITONE_BIG_DELTA; - else - pitch_delta = PITCH_BIG_DELTA; - updated = 1; - break; - - case ACTION_PS_DEC_SMALL: - if(global_settings.pitch_mode_semitone) - pitch_delta = -SEMITONE_SMALL_DELTA; - else - pitch_delta = -PITCH_SMALL_DELTA; - updated = 1; - break; - - case ACTION_PS_DEC_BIG: - if(global_settings.pitch_mode_semitone) - pitch_delta = -SEMITONE_BIG_DELTA; - else - pitch_delta = -PITCH_BIG_DELTA; - updated = 1; - break; - - case ACTION_PS_NUDGE_RIGHT: - if (!global_settings.pitch_mode_timestretch) - { - new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false - , speed - ); - nudged = (new_pitch != pitch); - pitch = new_pitch; - semitone = get_semitone_from_pitch(pitch); - speed = pitch; - updated = nudged ? 1 : 0; - break; - } - else - { - new_speed = speed + SPEED_SMALL_DELTA; - at_limit = false; - updated = 2; - } - break; - - case ACTION_PS_FASTER: - if (global_settings.pitch_mode_timestretch) - { - new_speed = speed + SPEED_BIG_DELTA; - /* snap to whole numbers */ - if(new_speed % PITCH_SPEED_PRECISION != 0) - new_speed -= new_speed % PITCH_SPEED_PRECISION; - at_limit = false; - updated = 2; - } - break; - - case ACTION_PS_NUDGE_RIGHTOFF: - if (nudged) - { - pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false - , speed - ); - speed = pitch; - semitone = get_semitone_from_pitch(pitch); - nudged = false; - updated = 1; - } - break; - - case ACTION_PS_NUDGE_LEFT: - if (!global_settings.pitch_mode_timestretch) - { - new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false - , speed - ); - nudged = (new_pitch != pitch); - pitch = new_pitch; - semitone = get_semitone_from_pitch(pitch); - speed = pitch; - updated = nudged ? 1 : 0; - break; - } - else - { - new_speed = speed - SPEED_SMALL_DELTA; - at_limit = false; - updated = 2; - } - break; - - case ACTION_PS_SLOWER: - if (global_settings.pitch_mode_timestretch) - { - new_speed = speed - SPEED_BIG_DELTA; - /* snap to whole numbers */ - if(new_speed % PITCH_SPEED_PRECISION != 0) - new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION; - at_limit = false; - updated = 2; - } - break; - - case ACTION_PS_NUDGE_LEFTOFF: - if (nudged) - { - pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false - , speed - ); - speed = pitch; - semitone = get_semitone_from_pitch(pitch); - nudged = false; - updated = 1; - } - break; - - case ACTION_PS_RESET: - pitch = PITCH_SPEED_100; - sound_set_pitch(pitch); - speed = PITCH_SPEED_100; - if (dsp_timestretch_available()) - { - dsp_set_timestretch(PITCH_SPEED_100); - at_limit = false; - } - semitone = get_semitone_from_pitch(pitch); - updated = 4; - break; - - case ACTION_PS_TOGGLE_MODE: - global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone; - - if (dsp_timestretch_available() && !global_settings.pitch_mode_semitone) - { - global_settings.pitch_mode_timestretch = !global_settings.pitch_mode_timestretch; - if(!global_settings.pitch_mode_timestretch) - { - /* no longer in timestretch mode. Reset speed */ - speed = pitch; - dsp_set_timestretch(PITCH_SPEED_100); - } - } - settings_save(); - updated = 3; - break; - - case ACTION_PS_EXIT: - exit = true; - break; - - default: - if (default_event_handler(button) == SYS_USB_CONNECTED) - return 1; - break; - } - if (pitch_delta) - { - if (global_settings.pitch_mode_semitone) - { - semitone = pitch_increase_semitone(pitch, semitone, pitch_delta - , speed - ); - pitch = get_pitch_from_semitone(semitone); - } - else - { - pitch = pitch_increase(pitch, pitch_delta, true - , speed - ); - semitone = get_semitone_from_pitch(pitch); - } - if (global_settings.pitch_mode_timestretch) - { - /* do this to make sure we properly obey the stretch limits */ - new_speed = speed; - } - else - { - speed = pitch; - } - } - - if(new_speed) - { - new_stretch = GET_STRETCH(pitch, new_speed); - - /* limit the amount of stretch */ - if(new_stretch > STRETCH_MAX) - { - new_stretch = STRETCH_MAX; - new_speed = GET_SPEED(pitch, new_stretch); - } - else if(new_stretch < STRETCH_MIN) - { - new_stretch = STRETCH_MIN; - new_speed = GET_SPEED(pitch, new_stretch); - } - - new_stretch = GET_STRETCH(pitch, new_speed); - if(new_stretch >= STRETCH_MAX || - new_stretch <= STRETCH_MIN) - { - at_limit = true; - } - - /* set the amount of stretch */ - dsp_set_timestretch(new_stretch); - - /* update the speed variable with the new speed */ - speed = new_speed; - - /* Reset new_speed so we only call dsp_set_timestretch */ - /* when needed */ - new_speed = 0; - } - } - - pcmbuf_set_low_latency(false); - pop_current_activity(); - - /* Clean up */ - FOR_NB_SCREENS(i) - { - screens[i].set_viewport(NULL); - } - - return 0; + return (plugin_load(VIEWERS_DIR"/pitch_screen.rock", NULL) == PLUGIN_USB_CONNECTED); } diff --git a/apps/gui/quickscreen.c b/apps/gui/quickscreen.c index f8bf98d4ee..221dfe3111 100644 --- a/apps/gui/quickscreen.c +++ b/apps/gui/quickscreen.c @@ -39,6 +39,9 @@ #include "option_select.h" #include "debug.h" #include "shortcuts.h" +#ifdef HAVE_ALBUMART +#include "playback.h" +#endif /* 1 top, 1 bottom, 2 on either side, 1 for the icons * if enough space, top and bottom have 2 lines */ @@ -248,20 +251,21 @@ static void talk_qs_option(const struct settings_list *opt, bool enqueue) static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button) { int item; - bool invert = false; + bool previous = false; switch(button) { case ACTION_QS_TOP: - invert = true; item = QUICKSCREEN_TOP; break; + case ACTION_QS_LEFT: - invert = true; item = QUICKSCREEN_LEFT; + previous = true; break; case ACTION_QS_DOWN: item = QUICKSCREEN_BOTTOM; + previous = true; break; case ACTION_QS_RIGHT: @@ -271,47 +275,58 @@ static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button) default: return false; } + if (qs->items[item] == NULL) return false; -#ifdef ASCENDING_INT_SETTINGS - if (((qs->items[item]->flags & F_INT_SETTING) == F_INT_SETTING) && - ( button == ACTION_QS_DOWN || button == ACTION_QS_TOP)) - { - invert = !invert; - } -#endif - option_select_next_val(qs->items[item], invert, true); + + option_select_next_val(qs->items[item], previous, true); talk_qs_option(qs->items[item], false); return true; } #ifdef HAVE_TOUCHSCREEN -static int quickscreen_touchscreen_button(const struct viewport - vps[QUICKSCREEN_ITEM_COUNT]) +static int quickscreen_touchscreen_button(void) { short x,y; - /* only hitting the text counts, everything else is exit */ if (action_get_touchscreen_press(&x, &y) != BUTTON_REL) return ACTION_NONE; - else if (viewport_point_within_vp(&vps[QUICKSCREEN_TOP], x, y)) + + enum { left=1, right=2, top=4, bottom=8 }; + + int bits = 0; + + if(x < LCD_WIDTH/3) + bits |= left; + else if(x > 2*LCD_WIDTH/3) + bits |= right; + + if(y < LCD_HEIGHT/3) + bits |= top; + else if(y > 2*LCD_HEIGHT/3) + bits |= bottom; + + switch(bits) { + case top: return ACTION_QS_TOP; - else if (viewport_point_within_vp(&vps[QUICKSCREEN_BOTTOM], x, y)) + case bottom: return ACTION_QS_DOWN; - else if (viewport_point_within_vp(&vps[QUICKSCREEN_LEFT], x, y)) + case left: return ACTION_QS_LEFT; - else if (viewport_point_within_vp(&vps[QUICKSCREEN_RIGHT], x, y)) + case right: return ACTION_QS_RIGHT; - return ACTION_STD_CANCEL; + default: + return ACTION_STD_CANCEL; + } } #endif -static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter, bool *usb) +static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter, bool *usb) { int button; struct viewport parent[NB_SCREENS]; struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT]; struct viewport vp_icons[NB_SCREENS]; - bool changed = false; + int ret = QUICKSCREEN_OK; /* To quit we need either : * - a second press on the button that made us enter * - an action taken while pressing the enter button, @@ -343,7 +358,7 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente button = get_action(CONTEXT_QUICKSCREEN, HZ/5); #ifdef HAVE_TOUCHSCREEN if (button == ACTION_TOUCHSCREEN) - button = quickscreen_touchscreen_button(vps[SCREEN_MAIN]); + button = quickscreen_touchscreen_button(); #endif if (default_event_handler(button) == SYS_USB_CONNECTED) { @@ -352,7 +367,7 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente } if (gui_quickscreen_do_button(qs, button)) { - changed = true; + ret |= QUICKSCREEN_CHANGED; can_quit = true; FOR_NB_SCREENS(i) gui_quickscreen_draw(qs, &screens[i], &parent[i], @@ -363,17 +378,20 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente else if (button == button_enter) can_quit = true; else if (button == ACTION_QS_VOLUP) { - global_settings.volume += sound_steps(SOUND_VOLUME); - setvol(); + adjust_volume(1); FOR_NB_SCREENS(i) skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_NON_STATIC); } else if (button == ACTION_QS_VOLDOWN) { - global_settings.volume -= sound_steps(SOUND_VOLUME); - setvol(); + adjust_volume(-1); FOR_NB_SCREENS(i) skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_NON_STATIC); } + else if (button == ACTION_STD_CONTEXT) + { + ret |= QUICKSCREEN_GOTO_SHORTCUTS_MENU; + break; + } if ((button == button_enter) && can_quit) break; @@ -386,20 +404,15 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente { /* stop scrolling before exiting */ for (int j = 0; j < QUICKSCREEN_ITEM_COUNT; j++) screens[i].scroll_stop_viewport(&vps[i][j]); - viewportmanager_theme_undo(i, true); + viewportmanager_theme_undo(i, !(ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU)); } - pop_current_activity(); - return changed; -} + if (ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU) /* Eliminate flashing of parent during */ + pop_current_activity_without_refresh(); /* transition to Shortcuts */ + else + pop_current_activity(); -static const struct settings_list *get_setting(int gs_value, - const struct settings_list *defaultval) -{ - if (gs_value != -1 && gs_value < nb_settings && - is_setting_quickscreenable(&settings[gs_value])) - return &settings[gs_value]; - return defaultval; + return ret; } int quick_screen_quick(int button_enter) @@ -407,24 +420,22 @@ int quick_screen_quick(int button_enter) struct gui_quickscreen qs; bool oldshuffle = global_settings.playlist_shuffle; int oldrepeat = global_settings.repeat_mode; +#ifdef HAVE_ALBUMART + int old_album_art = global_settings.album_art; +#endif bool usb = false; - if (global_settings.shortcuts_replaces_qs) - return do_shortcut_menu(NULL); + for (int i = 0; i < 4; ++i) + { + qs.items[i] = global_settings.qs_items[i]; - qs.items[QUICKSCREEN_TOP] = - get_setting(global_settings.qs_items[QUICKSCREEN_TOP], NULL); - qs.items[QUICKSCREEN_LEFT] = - get_setting(global_settings.qs_items[QUICKSCREEN_LEFT], - find_setting(&global_settings.playlist_shuffle, NULL)); - qs.items[QUICKSCREEN_RIGHT] = - get_setting(global_settings.qs_items[QUICKSCREEN_RIGHT], - find_setting(&global_settings.repeat_mode, NULL)); - qs.items[QUICKSCREEN_BOTTOM] = - get_setting(global_settings.qs_items[QUICKSCREEN_BOTTOM], NULL); + if (!is_setting_quickscreenable(qs.items[i])) + qs.items[i] = NULL; + } qs.callback = NULL; - if (gui_syncquickscreen_run(&qs, button_enter, &usb)) + int ret = gui_syncquickscreen_run(&qs, button_enter, &usb); + if (ret & QUICKSCREEN_CHANGED) { settings_save(); /* make sure repeat/shuffle/any other nasty ones get updated */ @@ -442,17 +453,28 @@ int quick_screen_quick(int button_enter) else playlist_sort(NULL, true); } +#ifdef HAVE_ALBUMART + if (old_album_art != global_settings.album_art) + set_albumart_mode(global_settings.album_art); +#endif } - return (usb ? 1:0); + if (usb) + return QUICKSCREEN_IN_USB; + return ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU ? QUICKSCREEN_GOTO_SHORTCUTS_MENU : + QUICKSCREEN_OK; } /* stuff to make the quickscreen configurable */ bool is_setting_quickscreenable(const struct settings_list *setting) { + if (!setting) + return true; + /* to keep things simple, only settings which have a lang_id set are ok */ - if (setting->lang_id < 0 || (setting->flags&F_BANFROMQS)) + if (setting->lang_id < 0 || (setting->flags & F_BANFROMQS)) return false; - switch (setting->flags&F_T_MASK) + + switch (setting->flags & F_T_MASK) { case F_T_BOOL: return true; @@ -463,16 +485,3 @@ bool is_setting_quickscreenable(const struct settings_list *setting) return false; } } - -void set_as_qs_item(const struct settings_list *setting, - enum quickscreen_item item) -{ - int i; - for (i = 0; i < nb_settings; i++) - { - if (&settings[i] == setting) - break; - } - - global_settings.qs_items[item] = i; -} diff --git a/apps/gui/quickscreen.h b/apps/gui/quickscreen.h index 015928ee8a..bd8008bd34 100644 --- a/apps/gui/quickscreen.h +++ b/apps/gui/quickscreen.h @@ -36,6 +36,13 @@ enum quickscreen_item { QUICKSCREEN_ITEM_COUNT, }; +enum quickscreen_return { + QUICKSCREEN_OK = 0, + QUICKSCREEN_IN_USB = 0x1, + QUICKSCREEN_GOTO_SHORTCUTS_MENU = 0x2, + QUICKSCREEN_CHANGED = 0x4, +}; + struct gui_quickscreen { const struct settings_list *items[QUICKSCREEN_ITEM_COUNT]; @@ -46,7 +53,6 @@ struct gui_quickscreen extern int quick_screen_quick(int button_enter); int quickscreen_set_option(void *data); bool is_setting_quickscreenable(const struct settings_list *setting); -void set_as_qs_item(const struct settings_list *setting, - enum quickscreen_item item); + #endif /*_GUI_QUICK_SCREEN_H_*/ #endif /* HAVE_QUICKSCREEN */ diff --git a/apps/gui/skin_engine/skin_backdrops.c b/apps/gui/skin_engine/skin_backdrops.c index 41de1e1f76..eecf5b0433 100644 --- a/apps/gui/skin_engine/skin_backdrops.c +++ b/apps/gui/skin_engine/skin_backdrops.c @@ -41,7 +41,6 @@ static struct skin_backdrop { } backdrops[NB_BDROPS]; #define NB_BDROPS SKINNABLE_SCREENS_COUNT*NB_SCREENS -static int handle_being_loaded; static int current_lcd_backdrop[NB_SCREENS]; bool skin_backdrop_get_debug(int index, char **path, int *ref_count, size_t *size) @@ -65,8 +64,8 @@ bool skin_backdrop_get_debug(int index, char **path, int *ref_count, size_t *siz static int buflib_move_callback(int handle, void* current, void* new) { - if (handle == handle_being_loaded) - return BUFLIB_CB_CANNOT_MOVE; + (void)handle; + for (int i=0; i<NB_BDROPS; i++) { if (backdrops[i].buffer == current) @@ -81,8 +80,9 @@ static int buflib_move_callback(int handle, void* current, void* new) } static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL}; static bool first_go = true; -void skin_backdrop_init(void) +bool skin_backdrop_init(void) { + bool go_status = first_go; if (first_go) { for (int i=0; i<NB_BDROPS; i++) @@ -95,9 +95,9 @@ void skin_backdrop_init(void) } FOR_NB_SCREENS(i) current_lcd_backdrop[i] = -1; - handle_being_loaded = -1; first_go = false; } + return go_status; } int skin_backdrop_assign(char* backdrop, char *bmpdir, @@ -137,7 +137,7 @@ int skin_backdrop_assign(char* backdrop, char *bmpdir, } if (free >= 0) { - strlcpy(backdrops[free].name, filename, MAX_PATH); + strmemccpy(backdrops[free].name, filename, MAX_PATH); backdrops[free].buffer = NULL; backdrops[free].screen = screen; backdrops[free].ref_count = 1; @@ -174,22 +174,22 @@ bool skin_backdrops_preload(void) } if (*filename && *filename != '-') { - backdrops[i].buflib_handle = core_alloc_ex(filename, buf_size, &buflib_ops); + backdrops[i].buflib_handle = core_alloc_ex(buf_size, &buflib_ops); if (backdrops[i].buflib_handle > 0) { backdrops[i].buffer = core_get_data(backdrops[i].buflib_handle); if (strcmp(filename, BACKDROP_BUFFERNAME)) { - handle_being_loaded = backdrops[i].buflib_handle; + core_pin(backdrops[i].buflib_handle); backdrops[i].loaded = screens[screen].backdrop_load(filename, backdrops[i].buffer); + core_unpin(backdrops[i].buflib_handle); if (!backdrops[i].loaded) { core_free(backdrops[i].buflib_handle); backdrops[i].buflib_handle = -1; retval = false; } - handle_being_loaded = -1; } else backdrops[i].loaded = true; @@ -265,8 +265,7 @@ void skin_backdrop_unload(int backdrop_id) backdrops[backdrop_id].ref_count--; if (backdrops[backdrop_id].ref_count <= 0) { - if (backdrops[backdrop_id].buflib_handle > 0) - core_free(backdrops[backdrop_id].buflib_handle); + core_free(backdrops[backdrop_id].buflib_handle); backdrops[backdrop_id].buffer = NULL; backdrops[backdrop_id].buflib_handle = -1; backdrops[backdrop_id].loaded = false; @@ -288,18 +287,17 @@ void skin_backdrop_load_setting(void) if (backdrops[i].buflib_handle <= 0) { backdrops[i].buflib_handle = - core_alloc_ex(global_settings.backdrop_file, - LCD_BACKDROP_BYTES, &buflib_ops); + core_alloc_ex(LCD_BACKDROP_BYTES, &buflib_ops); if (backdrops[i].buflib_handle <= 0) return; } bool loaded; + core_pin(backdrops[i].buflib_handle); backdrops[i].buffer = core_get_data(backdrops[i].buflib_handle); - handle_being_loaded = backdrops[i].buflib_handle; loaded = screens[SCREEN_MAIN].backdrop_load( global_settings.backdrop_file, backdrops[i].buffer); - handle_being_loaded = -1; + core_unpin(backdrops[i].buflib_handle); backdrops[i].name[2] = loaded ? '.' : '\0'; backdrops[i].loaded = loaded; return; @@ -331,8 +329,20 @@ void skin_backdrop_unload(int backdrop_id) (void)backdrop_id; } #else +static bool first_go = true; +bool skin_backdrop_init(void) +{ + bool go_status = first_go; + first_go = false; + return go_status; +} -void skin_backdrop_init(void) +void skin_backdrop_load_setting(void) +{ +} + +void skin_backdrop_show(int backdrop_id) { + (void) backdrop_id; } #endif diff --git a/apps/gui/skin_engine/skin_display.c b/apps/gui/skin_engine/skin_display.c index 4a3dcc2177..913bdcfbc4 100644 --- a/apps/gui/skin_engine/skin_display.c +++ b/apps/gui/skin_engine/skin_display.c @@ -46,6 +46,7 @@ #include "tagcache.h" #include "list.h" #include "option_select.h" +#include "buffering.h" #include "peakmeter.h" /* Image stuff */ @@ -64,7 +65,7 @@ #endif #include "root_menu.h" - +#include "wps.h" #include "wps_internals.h" #include "skin_engine.h" #include "statusbar-skinned.h" @@ -82,7 +83,7 @@ void skin_update(enum skinnable_screens skin, enum screen_type screen, struct gui_wps *gwps = skin_get_gwps(skin, screen); /* This maybe shouldnt be here, * This is also safe for skined screen which dont use the id3 */ - struct mp3entry *id3 = skin_get_global_state()->id3; + struct mp3entry *id3 = get_wps_state()->id3; bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false); if (cuesheet_update) skin_request_full_update(skin); @@ -157,11 +158,12 @@ void ab_draw_markers(struct screen * screen, int capacity, #endif -void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb) +void draw_progressbar(struct gui_wps *gwps, struct skin_viewport* skin_viewport, + int line, struct progressbar *pb) { struct screen *display = gwps->display; - struct viewport *vp = SKINOFFSETTOPTR(get_skin_buffer(gwps->data), pb->vp); - struct wps_state *state = skin_get_global_state(); + struct viewport *vp = &skin_viewport->vp; + struct wps_state *state = get_wps_state(); struct mp3entry *id3 = state->id3; int x = pb->x, y = pb->y, width = pb->width, height = pb->height; unsigned long length, end; @@ -183,8 +185,13 @@ void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb) { int minvol = sound_min(SOUND_VOLUME); int maxvol = sound_max(SOUND_VOLUME); - length = maxvol-minvol; - end = global_settings.volume-minvol; +#if defined(HAVE_PERCEPTUAL_VOLUME) || defined(HAVE_TOUCHSCREEN) + length = 1000; + end = to_normalized_volume(global_settings.volume, minvol, maxvol, length); +#else + length = maxvol - minvol; + end = global_settings.volume - minvol; +#endif } else if (pb->type == SKIN_TOKEN_BATTERY_PERCENTBAR) { @@ -210,7 +217,7 @@ void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb) else if (pb->type == SKIN_TOKEN_SETTINGBAR) { int val, count; - get_setting_info_for_bar(pb->setting_id, &count, &val); + get_setting_info_for_bar(pb->setting, &count, &val); length = count - 1; end = val; } @@ -411,8 +418,7 @@ void wps_display_images(struct gui_wps *gwps, struct viewport* vp) #ifdef HAVE_ALBUMART /* now draw the AA */ struct skin_albumart *aa = SKINOFFSETTOPTR(get_skin_buffer(data), data->albumart); - if (aa && SKINOFFSETTOPTR(get_skin_buffer(data), aa->vp) == vp - && aa->draw_handle >= 0) + if (aa && aa->draw_handle >= 0) { draw_album_art(gwps, aa->draw_handle, false); aa->draw_handle = -1; @@ -499,78 +505,82 @@ void write_line(struct screen *display, struct align_pos *format_align, /* CASE 1: left and centered string overlap */ /* there is a left string, need to merge left and center */ - if ((left_width != 0 && center_width != 0) && - (left_width + space_width > center_xpos)) { - /* replace the former separator '\0' of left and - center string with a space */ - *(--format_align->center) = ' '; - /* calculate the new width and position of the merged string */ - left_width = left_width + space_width + center_width; - /* there is no centered string anymore */ - center_width = 0; - } - /* there is no left string, move center to left */ - if ((left_width == 0 && center_width != 0) && - (left_width > center_xpos)) { - /* move the center string to the left string */ - format_align->left = format_align->center; - /* calculate the new width and position of the string */ - left_width = center_width; - /* there is no centered string anymore */ - center_width = 0; - } + if (center_width != 0) + { + if (left_width != 0 && left_width + space_width > center_xpos) { + /* replace the former separator '\0' of left and + center string with a space */ + *(--format_align->center) = ' '; + /* calculate the new width and position of the merged string */ + left_width = left_width + space_width + center_width; + /* there is no centered string anymore */ + center_width = 0; + } + /* there is no left string, move center to left */ + else if (left_width == 0 && center_xpos < 0) { + /* move the center string to the left string */ + format_align->left = format_align->center; + /* calculate the new width and position of the string */ + left_width = center_width; + /* there is no centered string anymore */ + center_width = 0; + } + } /*(center_width != 0)*/ /* CASE 2: centered and right string overlap */ /* there is a right string, need to merge center and right */ - if ((center_width != 0 && right_width != 0) && - (center_xpos + center_width + space_width > right_xpos)) { - /* replace the former separator '\0' of center and - right string with a space */ - *(--format_align->right) = ' '; - /* move the center string to the right after merge */ - format_align->right = format_align->center; - /* calculate the new width and position of the merged string */ - right_width = center_width + space_width + right_width; - right_xpos = (viewport_width - right_width); - /* there is no centered string anymore */ - center_width = 0; - } - /* there is no right string, move center to right */ - if ((center_width != 0 && right_width == 0) && - (center_xpos + center_width > right_xpos)) { - /* move the center string to the right string */ - format_align->right = format_align->center; - /* calculate the new width and position of the string */ - right_width = center_width; - right_xpos = (viewport_width - right_width); - /* there is no centered string anymore */ - center_width = 0; - } + if (center_width != 0) + { + int center_left_x = center_xpos + center_width; + if (right_width != 0 && center_left_x + space_width > right_xpos) { + /* replace the former separator '\0' of center and + right string with a space */ + *(--format_align->right) = ' '; + /* move the center string to the right after merge */ + format_align->right = format_align->center; + /* calculate the new width and position of the merged string */ + right_width = center_width + space_width + right_width; + right_xpos = (viewport_width - right_width); + /* there is no centered string anymore */ + center_width = 0; + } + /* there is no right string, move center to right */ + else if (right_width == 0 && center_left_x > right_xpos) { + /* move the center string to the right string */ + format_align->right = format_align->center; + /* calculate the new width and position of the string */ + right_width = center_width; + right_xpos = (viewport_width - right_width); + /* there is no centered string anymore */ + center_width = 0; + } + } /*(center_width != 0)*/ /* CASE 3: left and right overlap There is no center string anymore, either there never was one or it has been merged in case 1 or 2 */ /* there is a left string, need to merge left and right */ - if ((left_width != 0 && center_width == 0 && right_width != 0) && - (left_width + space_width > right_xpos)) { - /* replace the former separator '\0' of left and - right string with a space */ - *(--format_align->right) = ' '; - /* calculate the new width and position of the string */ - left_width = left_width + space_width + right_width; - /* there is no right string anymore */ - right_width = 0; - } - /* there is no left string, move right to left */ - if ((left_width == 0 && center_width == 0 && right_width != 0) && - (left_width > right_xpos)) { - /* move the right string to the left string */ - format_align->left = format_align->right; - /* calculate the new width and position of the string */ - left_width = right_width; - /* there is no right string anymore */ - right_width = 0; - } + if (center_width == 0 && right_width != 0) + { + if (left_width != 0 && left_width + space_width > right_xpos) { + /* replace the former separator '\0' of left and + right string with a space */ + *(--format_align->right) = ' '; + /* calculate the new width and position of the string */ + left_width = left_width + space_width + right_width; + /* there is no right string anymore */ + right_width = 0; + } + /* there is no left string, move right to left */ + else if (left_width == 0 && right_xpos < 0) { + /* move the right string to the left string */ + format_align->left = format_align->right; + /* calculate the new width and position of the string */ + left_width = right_width; + /* there is no right string anymore */ + right_width = 0; + } + } /* (center_width == 0 && right_width != 0)*/ if (scroll && ((left_width > scroll_width) || (center_width > scroll_width) || @@ -601,9 +611,9 @@ void write_line(struct screen *display, struct align_pos *format_align, char *center = format_align->center ?: ""; char *right = format_align->right ?: ""; - display->put_line(0, line, linedes, "$t$*s$t$*s$t", left, - center_xpos - left_width, center, - right_xpos - (center_xpos + center_width), right); + display->put_line(0, line, linedes, "$t$*s$t$*s$t", left_width == 0 ? "" : left , + center_xpos - left_width, center_width == 0 ? "" : center, + right_xpos - center_xpos - center_width, right_width == 0 ? "" : right); } } @@ -632,14 +642,86 @@ void draw_peakmeters(struct gui_wps *gwps, int line_number, } } -bool skin_has_sbs(enum screen_type screen, struct wps_data *data) +#ifdef HAVE_ALBUMART +/* Draw the album art bitmap from the given handle ID onto the given WPS. + Call with clear = true to clear the bitmap instead of drawing it. */ +void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear) +{ + if (!gwps || !gwps->data || !gwps->display || handle_id < 0) + return; + + struct wps_data *data = gwps->data; + struct skin_albumart *aa = SKINOFFSETTOPTR(get_skin_buffer(data), data->albumart); + + if (!aa) + return; + + struct bitmap *bmp; + if (bufgetdata(handle_id, 0, (void *)&bmp) <= 0) + return; + + short x = aa->x; + short y = aa->y; + short width = bmp->width; + short height = bmp->height; + + if (aa->width > 0) + { + /* Crop if the bitmap is too wide */ + width = MIN(bmp->width, aa->width); + + /* Align */ + if (aa->xalign & WPS_ALBUMART_ALIGN_RIGHT) + x += aa->width - width; + else if (aa->xalign & WPS_ALBUMART_ALIGN_CENTER) + x += (aa->width - width) / 2; + } + + if (aa->height > 0) + { + /* Crop if the bitmap is too high */ + height = MIN(bmp->height, aa->height); + + /* Align */ + if (aa->yalign & WPS_ALBUMART_ALIGN_BOTTOM) + y += aa->height - height; + else if (aa->yalign & WPS_ALBUMART_ALIGN_CENTER) + y += (aa->height - height) / 2; + } + + if (!clear) + { + /* Draw the bitmap */ + gwps->display->bitmap_part((fb_data*)bmp->data, 0, 0, + STRIDE(gwps->display->screen_type, + bmp->width, bmp->height), + x, y, width, height); +#ifdef HAVE_LCD_INVERT + if (global_settings.invert) { + gwps->display->set_drawmode(DRMODE_COMPLEMENT); + gwps->display->fillrect(x, y, width, height); + gwps->display->set_drawmode(DRMODE_SOLID); + } +#endif + } + else + { + /* Clear the bitmap */ + gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); + gwps->display->fillrect(x, y, width, height); + gwps->display->set_drawmode(DRMODE_SOLID); + } +} +#endif + +bool skin_has_sbs(struct gui_wps *gwps) { - (void)screen; - (void)data; + struct wps_data *data = gwps->data; + bool draw = false; if (data->wps_sb_tag) draw = data->show_sb_on_wps; - else if (statusbar_position(screen) != STATUSBAR_OFF) + else if (statusbar_position(gwps->display->screen_type) != STATUSBAR_OFF) draw = true; return draw; } diff --git a/apps/gui/skin_engine/skin_display.h b/apps/gui/skin_engine/skin_display.h index de1b0b20b5..56af12aa03 100644 --- a/apps/gui/skin_engine/skin_display.h +++ b/apps/gui/skin_engine/skin_display.h @@ -29,7 +29,8 @@ #define _SKIN_DISPLAY_H_ -void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb); +void draw_progressbar(struct gui_wps *gwps, struct skin_viewport* skin_viewport, + int line, struct progressbar *pb); void draw_playlist_viewer_list(struct gui_wps *gwps, struct playlistviewer *viewer); /* clears the area where the image was shown */ void clear_image_pos(struct gui_wps *gwps, struct gui_img *img); @@ -54,4 +55,10 @@ void write_line(struct screen *display, struct align_pos *format_align, int line, bool scroll, struct line_desc *line_desc); void draw_peakmeters(struct gui_wps *gwps, int line_number, struct viewport *viewport); +#ifdef HAVE_ALBUMART +/* Draw the album art bitmap from the given handle ID onto the given Skin. + Call with clear = true to clear the bitmap instead of drawing it. */ +void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear); +#endif + #endif diff --git a/apps/gui/skin_engine/skin_engine.c b/apps/gui/skin_engine/skin_engine.c index ce3401f41c..c007ed35d4 100644 --- a/apps/gui/skin_engine/skin_engine.c +++ b/apps/gui/skin_engine/skin_engine.c @@ -34,9 +34,11 @@ #if CONFIG_TUNER #include "radio.h" #endif +#include "gui/list.h" #include "skin_engine.h" #include "skin_buffer.h" #include "statusbar-skinned.h" +#include "wps_internals.h" #define FAILSAFENAME "rockbox_failsafe" @@ -51,7 +53,6 @@ static bool skins_initialised = false; static char* get_skin_filename(char *buf, size_t buf_size, enum skinnable_screens skin, enum screen_type screen); -struct wps_state wps_state = { .id3 = NULL }; static struct gui_skin_helper { int (*preproccess)(enum screen_type screen, struct wps_data *data); int (*postproccess)(enum screen_type screen, struct wps_data *data); @@ -149,13 +150,28 @@ void skin_unload_all(void) gui_sync_skin_init(); } +static void skin_reset_buffers(int item, int screen) +{ + skin_data_free_buflib_allocs(&skins[item][screen].data); +#ifdef HAVE_ALBUMART + if (skins[item][screen].data.playback_aa_slot >= 0) + playback_release_aa_slot(skins[item][screen].data.playback_aa_slot); +#endif +#ifdef HAVE_BACKDROP_IMAGE + if (skins[item][screen].data.backdrop_id >= 0) + skin_backdrop_unload(skins[item][screen].data.backdrop_id); +#endif +} + void settings_apply_skins(void) { int i; char filename[MAX_PATH]; - static bool first_run = true; - skin_backdrop_init(); + if (audio_status() & AUDIO_STATUS_PLAY) + audio_stop(); + + bool first_run = skin_backdrop_init(); skins_initialised = true; /* Make sure each skin is loaded */ @@ -167,15 +183,7 @@ void settings_apply_skins(void) if (!first_run) { - skin_data_free_buflib_allocs(&skins[i][j].data); -#ifdef HAVE_ALBUMART - if (skins[i][j].data.playback_aa_slot >= 0) - playback_release_aa_slot(skins[i][j].data.playback_aa_slot); -#endif -#ifdef HAVE_BACKDROP_IMAGE - if (skins[i][j].data.backdrop_id >= 0) - skin_backdrop_unload(skins[i][j].data.backdrop_id); -#endif + skin_reset_buffers(i, j); } gui_skin_reset(&skins[i][j]); skins[i][j].gui_wps.display = &screens[j]; @@ -183,17 +191,14 @@ void settings_apply_skins(void) skin_get_gwps(i, j); } } - first_run = false; -#ifdef HAVE_BACKDROP_IMAGE + /* any backdrop that was loaded with "-" has to be reloaded because * the setting may have changed */ skin_backdrop_load_setting(); -#endif viewportmanager_theme_changed(THEME_STATUSBAR); -#ifdef HAVE_BACKDROP_IMAGE + FOR_NB_SCREENS(i) skin_backdrop_show(sb_get_backdrop(i)); -#endif } void skin_load(enum skinnable_screens skin, enum screen_type screen, @@ -304,11 +309,6 @@ struct gui_wps *skin_get_gwps(enum skinnable_screens skin, enum screen_type scre return &skins[skin][screen].gui_wps; } -struct wps_state *skin_get_global_state(void) -{ - return &wps_state; -} - /* This is called to find out if we the screen needs a full update. * if true you MUST do a full update as the next call will return false */ bool skin_do_full_update(enum skinnable_screens skin, @@ -330,3 +330,56 @@ void skin_request_full_update(enum skinnable_screens skin) FOR_NB_SCREENS(i) skins[skin][i].needs_full_update = true; } + +bool dbg_skin_engine(void) +{ + struct simplelist_info info; + int i, total = 0; +#if defined(HAVE_BACKDROP_IMAGE) + int ref_count; + char *path; + size_t bytes; + int path_prefix_len = strlen(ROCKBOX_DIR "/wps/"); +#endif + simplelist_info_init(&info, "Skin engine usage", 0, NULL); + simplelist_set_line_count(0); + FOR_NB_SCREENS(j) { +#if NB_SCREENS > 1 + simplelist_addline("%s display:", + j == 0 ? "Main" : "Remote"); +#endif + for (i = 0; i < skin_get_num_skins(); i++) { + struct skin_stats *stats = skin_get_stats(i, j); + if (stats->buflib_handles) + { + simplelist_addline("Skin ID: %d, %d allocations", + i, stats->buflib_handles); + simplelist_addline("\tskin: %d bytes", + stats->tree_size); + simplelist_addline("\tImages: %d bytes", + stats->images_size); + simplelist_addline("\tTotal: %d bytes", + stats->tree_size + stats->images_size); + total += stats->tree_size + stats->images_size; + } + } + } + simplelist_addline("Skin total usage: %d bytes", total); +#if defined(HAVE_BACKDROP_IMAGE) + simplelist_addline("Backdrop Images:"); + i = 0; + while (skin_backdrop_get_debug(i++, &path, &ref_count, &bytes)) { + if (ref_count > 0) { + + if (!strncasecmp(path, ROCKBOX_DIR "/wps/", path_prefix_len)) + path += path_prefix_len; + simplelist_addline("%s", path); + simplelist_addline("\tref_count: %d", ref_count); + simplelist_addline("\tsize: %d", bytes); + total += bytes; + } + } + simplelist_addline("Total usage: %d bytes", total); +#endif + return simplelist_show_list(&info); +} diff --git a/apps/gui/skin_engine/skin_engine.h b/apps/gui/skin_engine/skin_engine.h index 3b757a5f8b..d04c873e84 100644 --- a/apps/gui/skin_engine/skin_engine.h +++ b/apps/gui/skin_engine/skin_engine.h @@ -26,8 +26,7 @@ #ifndef PLUGIN #include "tag_table.h" - -#include "wps_internals.h" /* TODO: remove this line.. shoudlnt be needed */ +#include "screen_access.h" enum skinnable_screens { CUSTOM_STATUSBAR, @@ -39,30 +38,26 @@ enum skinnable_screens { SKINNABLE_SCREENS_COUNT }; +struct skin_stats; +struct skin_viewport; +struct gui_wps; + #ifdef HAVE_TOUCHSCREEN -int skin_get_touchaction(struct wps_data *data, int* edge_offset, - struct touchregion **retregion); -void skin_disarm_touchregions(struct wps_data *data); +int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset); +void skin_disarm_touchregions(struct gui_wps *gwps); #endif /* Do a update_type update of the skinned screen */ void skin_update(enum skinnable_screens skin, enum screen_type screen, unsigned int update_type); -/* - * setup up the skin-data from a format-buffer (isfile = false) - * or from a skinfile (isfile = true) - */ -bool skin_data_load(enum screen_type screen, struct wps_data *wps_data, - const char *buf, bool isfile, struct skin_stats *stats); - -bool skin_has_sbs(enum screen_type screen, struct wps_data *data); +bool skin_has_sbs(struct gui_wps *gwps); /* load a backdrop into the skin buffer. * reuse buffers if the file is already loaded */ char* skin_backdrop_load(char* backdrop, char *bmpdir, enum screen_type screen); -void skin_backdrop_init(void); +bool skin_backdrop_init(void); int skin_backdrop_assign(char* backdrop, char *bmpdir, enum screen_type screen); bool skin_backdrops_preload(void); @@ -81,7 +76,6 @@ int skin_wait_for_action(enum skinnable_screens skin, int context, int timeout); void skin_load(enum skinnable_screens skin, enum screen_type screen, const char *buf, bool isfile); struct gui_wps *skin_get_gwps(enum skinnable_screens skin, enum screen_type screen); -struct wps_state *skin_get_global_state(void); void gui_sync_skin_init(void); void skin_unload_all(void); @@ -89,5 +83,7 @@ void skin_unload_all(void); bool skin_do_full_update(enum skinnable_screens skin, enum screen_type screen); void skin_request_full_update(enum skinnable_screens skin); +bool dbg_skin_engine(void); + #endif /* !PLUGIN */ #endif diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c index 4ebdcab722..6cc3c596b0 100644 --- a/apps/gui/skin_engine/skin_parser.c +++ b/apps/gui/skin_engine/skin_parser.c @@ -343,11 +343,12 @@ static int parse_image_display(struct skin_element *element, if (element->params_count > 1) { - if (get_param(element, 1)->type == CODE) + struct skin_tag_parameter *param1 = get_param(element, 1); + if (param1->type == CODE) id->token = get_param_code(element, 1)->data; /* specify a number. 1 being the first subimage (i.e top) NOT 0 */ - else if (get_param(element, 1)->type == INTEGER) - id->subimage = get_param(element, 1)->data.number - 1; + else if (param1->type == INTEGER) + id->subimage = param1->data.number - 1; if (element->params_count > 2) id->offset = get_param(element, 2)->data.number; } @@ -391,14 +392,16 @@ static int parse_image_load(struct skin_element *element, subimages = get_param(element, 2)->data.number; else if (element->params_count > 3) { - if (get_param(element, 2)->type == PERCENT) - x = get_param(element, 2)->data.number * curr_vp->vp.width / 1000; + struct skin_tag_parameter *param2 = get_param(element, 2); + struct skin_tag_parameter *param3 = get_param(element, 3); + if (param2->type == PERCENT) + x = param2->data.number * curr_vp->vp.width / 1000; else - x = get_param(element, 2)->data.number; - if (get_param(element, 3)->type == PERCENT) - y = get_param(element, 3)->data.number * curr_vp->vp.height / 1000; + x = param2->data.number; + if (param3->type == PERCENT) + y = param3->data.number * curr_vp->vp.height / 1000; else - y = get_param(element, 3)->data.number; + y = param3->data.number; if (element->params_count == 5) subimages = get_param(element, 4)->data.number; @@ -424,9 +427,6 @@ static int parse_image_load(struct skin_element *element, img->is_9_segment = false; img->loaded = false; - /* save current viewport */ - img->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp); - if (token->type == SKIN_TOKEN_IMAGE_DISPLAY) token->value.data = PTRTOSKINOFFSET(skin_buffer, img); @@ -494,7 +494,6 @@ static int parse_playlistview(struct skin_element *element, struct playlistviewer *viewer = skin_buffer_alloc(sizeof(*viewer)); if (!viewer) return WPS_ERROR_INVALID_PARAM; - viewer->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp); viewer->show_icons = true; viewer->start_offset = get_param(element, 0)->data.number; viewer->line = PTRTOSKINOFFSET(skin_buffer, get_param_code(element, 1)); @@ -561,6 +560,7 @@ static int parse_listitemviewport(struct skin_element *element, struct wps_data *wps_data) { #ifndef __PCTOOL__ + struct skin_tag_parameter *param; struct listitem_viewport_cfg *cfg = skin_buffer_alloc(sizeof(*cfg)); if (!cfg) return -1; @@ -569,10 +569,15 @@ static int parse_listitemviewport(struct skin_element *element, cfg->label = PTRTOSKINOFFSET(skin_buffer, get_param_text(element, 0)); cfg->width = -1; cfg->height = -1; - if (!isdefault(get_param(element, 1))) - cfg->width = get_param(element, 1)->data.number; - if (!isdefault(get_param(element, 2))) - cfg->height = get_param(element, 2)->data.number; + + param = get_param(element, 1); + if (!isdefault(param)) + cfg->width = param->data.number; + + param = get_param(element, 2); + if (!isdefault(param)) + cfg->height = param->data.number; + if (element->params_count > 3 && !strcmp(get_param_text(element, 3), "tile")) cfg->tile = true; @@ -592,20 +597,26 @@ static int parse_viewporttextstyle(struct skin_element *element, *line = (struct line_desc)LINE_DESC_DEFINIT; unsigned colour; - if (!strcmp(mode, "invert")) + static const char * const vp_options[] = { "invert", "color", "colour", + "clear", "gradient", NULL}; + + int vp_op = string_option(mode, vp_options, false); + + if (vp_op == 0) /*invert*/ { line->style = STYLE_INVERT; } - else if (!strcmp(mode, "colour") || !strcmp(mode, "color")) + else if (vp_op == 1 || vp_op == 2) /*color/colour*/ { if (element->params_count < 2 || !parse_color(curr_screen, get_param_text(element, 1), &colour)) return 1; - line->style = STYLE_COLORED; + /* STYLE_COLORED is only a modifier and can't be used on its own */ + line->style = STYLE_COLORED | STYLE_DEFAULT; line->text_color = colour; } #ifdef HAVE_LCD_COLOR - else if (!strcmp(mode, "gradient")) + else if (vp_op == 4) /*gradient*/ { int num_lines; if (element->params_count < 2) @@ -618,7 +629,7 @@ static int parse_viewporttextstyle(struct skin_element *element, line->nlines = num_lines; } #endif - else if (!strcmp(mode, "clear")) + else if (vp_op == 3) /*clear*/ { line->style = STYLE_DEFAULT; } @@ -634,34 +645,39 @@ static int parse_drawrectangle( struct skin_element *element, struct wps_data *wps_data) { (void)wps_data; + struct skin_tag_parameter *param; struct draw_rectangle *rect = skin_buffer_alloc(sizeof(*rect)); if (!rect) return -1; - if (get_param(element, 0)->type == PERCENT) - rect->x = get_param(element, 0)->data.number * curr_vp->vp.width / 1000; + param = get_param(element, 0); + if (param->type == PERCENT) + rect->x = param->data.number * curr_vp->vp.width / 1000; else - rect->x = get_param(element, 0)->data.number; + rect->x = param->data.number; - if (get_param(element, 1)->type == PERCENT) - rect->y = get_param(element, 1)->data.number * curr_vp->vp.height / 1000; + param = get_param(element, 1); + if (param->type == PERCENT) + rect->y = param->data.number * curr_vp->vp.height / 1000; else - rect->y = get_param(element, 1)->data.number; + rect->y = param->data.number; - if (isdefault(get_param(element, 2))) + param = get_param(element, 2); + if (isdefault(param)) rect->width = curr_vp->vp.width - rect->x; - else if (get_param(element, 2)->type == PERCENT) - rect->width = get_param(element, 2)->data.number * curr_vp->vp.width / 1000; + else if (param->type == PERCENT) + rect->width = param->data.number * curr_vp->vp.width / 1000; else - rect->width = get_param(element, 2)->data.number; + rect->width = param->data.number; - if (isdefault(get_param(element, 3))) + param = get_param(element, 3); + if (isdefault(param)) rect->height = curr_vp->vp.height - rect->y; - else if (get_param(element, 3)->type == PERCENT) - rect->height = get_param(element, 3)->data.number * curr_vp->vp.height / 1000; + else if (param->type == PERCENT) + rect->height = param->data.number * curr_vp->vp.height / 1000; else - rect->height = get_param(element, 3)->data.number; + rect->height = param->data.number; rect->start_colour = curr_vp->vp.fg_pattern; rect->end_colour = curr_vp->vp.fg_pattern; @@ -695,8 +711,35 @@ static int parse_viewportcolour(struct skin_element *element, return -1; if (isdefault(param)) { - colour->colour = get_viewport_default_colour(curr_screen, - token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR); + unsigned int fg_color; + unsigned int bg_color; + + switch (curr_screen) + { +#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1 + case SCREEN_REMOTE: + fg_color = LCD_REMOTE_DEFAULT_FG; + bg_color = LCD_REMOTE_DEFAULT_BG; + break; +#endif + default: +#if defined(HAVE_LCD_COLOR) + fg_color = global_settings.fg_color; + bg_color = global_settings.bg_color; +#elif LCD_DEPTH > 1 + fg_color = LCD_DEFAULT_FG; + bg_color = LCD_DEFAULT_BG; +#else + fg_color = 0; + bg_color = 0; +#endif + break; + } + + if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR) + colour->colour = fg_color; + else + colour->colour = bg_color; } else { @@ -704,7 +747,6 @@ static int parse_viewportcolour(struct skin_element *element, &colour->colour)) return -1; } - colour->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp); token->value.data = PTRTOSKINOFFSET(skin_buffer, colour); if (element->line == curr_viewport_element->line) { @@ -761,14 +803,14 @@ static int parse_setting_and_lang(struct skin_element *element, */ (void)wps_data; char *temp = get_param_text(element, 0); - int i; if (token->type == SKIN_TOKEN_TRANSLATEDSTRING) { #ifndef __PCTOOL__ - i = lang_english_to_id(temp); + int i = lang_english_to_id(temp); if (i < 0) i = LANG_LAST_INDEX_IN_ARRAY; + token->value.i = i; #endif } else if (element->params_count > 1) @@ -781,12 +823,13 @@ static int parse_setting_and_lang(struct skin_element *element, else { #ifndef __PCTOOL__ - if (find_setting_by_cfgname(temp, &i) == NULL) + const struct settings_list *setting = find_setting_by_cfgname(temp); + if (!setting) return WPS_ERROR_INVALID_PARAM; + + token->value.xdata = (void *)setting; #endif } - /* Store the setting number */ - token->value.i = i; return 0; } @@ -811,19 +854,38 @@ static int parse_logical_if(struct skin_element *element, token->value.data = PTRTOSKINOFFSET(skin_buffer, lif); lif->token = get_param_code(element, 0)->data; - if (!strncmp(op, "=", 1)) - lif->op = IF_EQUALS; - else if (!strncmp(op, "!=", 2)) - lif->op = IF_NOTEQUALS; - else if (!strncmp(op, ">=", 2)) - lif->op = IF_GREATERTHAN_EQ; - else if (!strncmp(op, "<=", 2)) - lif->op = IF_LESSTHAN_EQ; - else if (!strncmp(op, ">", 2)) - lif->op = IF_GREATERTHAN; - else if (!strncmp(op, "<", 1)) - lif->op = IF_LESSTHAN; - + /* one or two operator conditionals */ + #define OPS2VAL(op1, op2) ((int)op1 << 8 | (int)op2) + #define CLAUSE(op1, op2, symbol) {OPS2VAL(op1, op2), symbol } + + struct clause_symbol {int value;int symbol;}; + static const struct clause_symbol get_clause_match[] = + { + CLAUSE('=', '=', IF_EQUALS), + CLAUSE('!', '=', IF_NOTEQUALS), + CLAUSE('>', '=', IF_GREATERTHAN_EQ), + CLAUSE('<', '=', IF_LESSTHAN_EQ), + /*All Single value items @ end */ + CLAUSE('>', 0, IF_GREATERTHAN), + CLAUSE('<', 0, IF_LESSTHAN), + CLAUSE('=', 0, IF_EQUALS), + }; + + int val1 = OPS2VAL(op[0], 0); + int val2; + if (val1 != 0) /* Empty string ?*/ + { + val2 = OPS2VAL(op[0], op[1]); + for (unsigned int i = 0; i < ARRAYLEN(get_clause_match); i++) + { + const struct clause_symbol *sym = &get_clause_match[i]; + if(sym->value == val1 || sym->value == val2) + { + lif->op = sym->symbol; + break; + } + } + } memcpy(&lif->operand, get_param(element, 2), sizeof(lif->operand)); if (element->params_count > 3) lif->num_options = get_param(element, 3)->data.number; @@ -904,7 +966,6 @@ static int parse_progressbar_tag(struct skin_element* element, if (!pb) return WPS_ERROR_INVALID_PARAM; - pb->vp = PTRTOSKINOFFSET(skin_buffer, vp); pb->follow_lang_direction = follow_lang_direction > 0; pb->nofill = false; pb->noborder = false; @@ -912,7 +973,7 @@ static int parse_progressbar_tag(struct skin_element* element, pb->image = PTRTOSKINOFFSET(skin_buffer, NULL); pb->slider = PTRTOSKINOFFSET(skin_buffer, NULL); pb->backdrop = PTRTOSKINOFFSET(skin_buffer, NULL); - pb->setting_id = -1; + pb->setting = NULL; pb->invert_fill_direction = false; pb->horizontal = true; @@ -1009,20 +1070,37 @@ static int parse_progressbar_tag(struct skin_element* element, } pb->horizontal = pb->width > pb->height; + + enum + { + eINVERT = 0, eNOFILL, eNOBORDER, eNOBAR, eSLIDER, eIMAGE, + eBACKDROP, eVERTICAL, eHORIZONTAL, eNOTOUCH, eSETTING, + e_PB_TAG_COUNT + }; + + static const char *pb_options[e_PB_TAG_COUNT + 1] = {[eINVERT] = "invert", + [eNOFILL] = "nofill", [eNOBORDER] = "noborder", [eNOBAR] = "nobar", + [eSLIDER] = "slider", [eIMAGE] = "image", [eBACKDROP] = "backdrop", + [eVERTICAL] = "vertical", [eHORIZONTAL] = "horizontal", + [eNOTOUCH] = "notouch", [eSETTING] = "setting", [e_PB_TAG_COUNT] = NULL}; + int pb_op; + while (curr_param < element->params_count) { char* text; param++; text = SKINOFFSETTOPTR(skin_buffer, param->data.text); - if (!strcmp(text, "invert")) + + pb_op = string_option(text, pb_options, false); + if (pb_op == eINVERT) pb->invert_fill_direction = true; - else if (!strcmp(text, "nofill")) + else if (pb_op == eNOFILL) pb->nofill = true; - else if (!strcmp(text, "noborder")) + else if (pb_op == eNOBORDER) pb->noborder = true; - else if (!strcmp(text, "nobar")) + else if (pb_op == eNOBAR) pb->nobar = true; - else if (!strcmp(text, "slider")) + else if (pb_op == eSLIDER) { if (curr_param+1 < element->params_count) { @@ -1035,7 +1113,7 @@ static int parse_progressbar_tag(struct skin_element* element, else /* option needs the next param */ return -1; } - else if (!strcmp(text, "image")) + else if (pb_op == eIMAGE) { if (curr_param+1 < element->params_count) { @@ -1046,7 +1124,7 @@ static int parse_progressbar_tag(struct skin_element* element, else /* option needs the next param */ return -1; } - else if (!strcmp(text, "backdrop")) + else if (pb_op == eBACKDROP) { if (curr_param+1 < element->params_count) { @@ -1060,31 +1138,32 @@ static int parse_progressbar_tag(struct skin_element* element, else /* option needs the next param */ return -1; } - else if (!strcmp(text, "vertical")) + else if (pb_op == eVERTICAL) { pb->horizontal = false; if (isdefault(get_param(element, 3))) pb->height = vp->height - pb->y; } - else if (!strcmp(text, "horizontal")) + else if (pb_op == eHORIZONTAL) pb->horizontal = true; #ifdef HAVE_TOUCHSCREEN - else if (!strcmp(text, "notouch")) + else if (pb_op == eNOTOUCH) suppress_touchregion = true; #endif - else if (token->type == SKIN_TOKEN_SETTING && !strcmp(text, "setting")) - { + else if (token->type == SKIN_TOKEN_SETTING && pb_op == eSETTING) + { if (curr_param+1 < element->params_count) { curr_param++; param++; text = SKINOFFSETTOPTR(skin_buffer, param->data.text); #ifndef __PCTOOL__ - if (find_setting_by_cfgname(text, &pb->setting_id) == NULL) - return WPS_ERROR_INVALID_PARAM; + pb->setting = find_setting_by_cfgname(text); + if (!pb->setting) + return WPS_ERROR_INVALID_PARAM; #endif - } - } + } + } else if (curr_param == 4) image_filename = text; @@ -1114,7 +1193,6 @@ static int parse_progressbar_tag(struct skin_element* element, img->display = -1; img->using_preloaded_icons = false; img->buflib_handle = -1; - img->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp); img->loaded = false; struct skin_token_list *item = new_skin_token_list_item(NULL, img); if (!item) @@ -1187,7 +1265,7 @@ static int parse_progressbar_tag(struct skin_element* element, region->reverse_bar = false; region->allow_while_locked = false; region->press_length = PRESS; - region->last_press = 0xffff; + region->last_press = -1; region->armed = false; region->bar = PTRTOSKINOFFSET(skin_buffer, pb); @@ -1219,24 +1297,28 @@ static int parse_albumart_load(struct skin_element* element, aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */ aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */ - aa->x = get_param(element, 0)->data.number; - aa->y = get_param(element, 1)->data.number; - aa->width = get_param(element, 2)->data.number; - aa->height = get_param(element, 3)->data.number; + struct skin_tag_parameter *param0 = get_param(element, 0); + struct skin_tag_parameter *param1 = get_param(element, 1); + struct skin_tag_parameter *param2 = get_param(element, 2); + struct skin_tag_parameter *param3 = get_param(element, 3); + + aa->x = param0->data.number; + aa->y = param1->data.number; + aa->width = param2->data.number; + aa->height = param3->data.number; - if (!isdefault(get_param(element, 0)) && get_param(element, 0)->type == PERCENT) - aa->x = get_param(element, 0)->data.number * curr_vp->vp.width / 1000; + if (!isdefault(param0) && param0->type == PERCENT) + aa->x = param0->data.number * curr_vp->vp.width / 1000; - if (!isdefault(get_param(element, 1)) && get_param(element, 1)->type == PERCENT) - aa->y = get_param(element, 1)->data.number * curr_vp->vp.height / 1000; + if (!isdefault(param1) && param1->type == PERCENT) + aa->y = param1->data.number * curr_vp->vp.height / 1000; - if (!isdefault(get_param(element, 2)) && get_param(element, 2)->type == PERCENT) - aa->width = get_param(element, 2)->data.number * curr_vp->vp.width / 1000; + if (!isdefault(param2) && param2->type == PERCENT) + aa->width = param2->data.number * curr_vp->vp.width / 1000; - if (!isdefault(get_param(element, 3)) && get_param(element, 3)->type == PERCENT) - aa->height = get_param(element, 3)->data.number * curr_vp->vp.height / 1000; + if (!isdefault(param3) && param3->type == PERCENT) + aa->height = param3->data.number * curr_vp->vp.height / 1000; - aa->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp); aa->draw_handle = -1; /* if we got here, we parsed everything ok .. ! */ @@ -1341,27 +1423,32 @@ static int parse_skinvar( struct skin_element *element, return 0; case SKIN_TOKEN_VAR_SET: { + static const char * const sv_options[] = {"touch", "set", "inc", "dec", NULL}; + struct skin_var_changer *data = skin_buffer_alloc(sizeof(*data)); if (!data) return WPS_ERROR_INVALID_PARAM; data->var = PTRTOSKINOFFSET(skin_buffer, var); + char *text_param1 = get_param_text(element, 1); + int sv_op = string_option(text_param1, sv_options, false); + if (!isdefault(get_param(element, 2))) data->newval = get_param(element, 2)->data.number; - else if (strcmp(get_param_text(element, 1), "touch")) + else if (sv_op != 0) /*!touch*/ return WPS_ERROR_INVALID_PARAM; data->max = 0; - if (!strcmp(get_param_text(element, 1), "set")) + if (sv_op == 1) /*set*/ data->direct = true; - else if (!strcmp(get_param_text(element, 1), "inc")) + else if (sv_op == 2) /*inc*/ { data->direct = false; } - else if (!strcmp(get_param_text(element, 1), "dec")) + else if (sv_op == 3) /*dec*/ { data->direct = false; data->newval *= -1; } - else if (!strcmp(get_param_text(element, 1), "touch")) + else if (sv_op == 0) /*touch*/ { data->direct = false; data->newval = 0; @@ -1403,12 +1490,12 @@ static int parse_lasttouch(struct skin_element *element, for (i=0; i<element->params_count; i++) { - if (get_param(element, i)->type == STRING) + struct skin_tag_parameter *param = get_param(element, i); + if (param->type == STRING) region = skin_find_item(get_param_text(element, i), SKIN_FIND_TOUCHREGION, wps_data); - else if (get_param(element, i)->type == INTEGER || - get_param(element, i)->type == DECIMAL) - data->timeout = get_param(element, i)->data.number; + else if (param->type == INTEGER || param->type == DECIMAL) + data->timeout = param->data.number; } data->region = PTRTOSKINOFFSET(skin_buffer, region); @@ -1461,48 +1548,48 @@ static int touchregion_setup_setting(struct skin_element *element, int param_no, #ifndef __PCTOOL__ int p = param_no; char *name = get_param_text(element, p++); - int j; - - region->setting_data.setting = find_setting_by_cfgname(name, &j); - if (region->setting_data.setting == NULL) + const struct settings_list *setting = find_setting_by_cfgname(name); + if (!setting) return WPS_ERROR_INVALID_PARAM; + region->setting_data.setting = setting; + if (region->action == ACTION_SETTINGS_SET) { char* text; int temp; - struct touchsetting *setting = + struct touchsetting *touchsetting = ®ion->setting_data; if (element->params_count < p+1) return -1; text = get_param_text(element, p++); - switch (settings[j].flags&F_T_MASK) + switch (setting->flags & F_T_MASK) { case F_T_CUSTOM: - setting->value.text = PTRTOSKINOFFSET(skin_buffer, text); + touchsetting->value.text = PTRTOSKINOFFSET(skin_buffer, text); break; case F_T_INT: case F_T_UINT: - if (settings[j].cfg_vals == NULL) + if (setting->cfg_vals == NULL) { - setting->value.number = atoi(text); + touchsetting->value.number = atoi(text); } - else if (cfg_string_to_int(j, &temp, text)) + else if (cfg_string_to_int(setting, &temp, text)) { - if (settings[j].flags&F_TABLE_SETTING) - setting->value.number = - settings[j].table_setting->values[temp]; + if (setting->flags & F_TABLE_SETTING) + touchsetting->value.number = + setting->table_setting->values[temp]; else - setting->value.number = temp; + touchsetting->value.number = temp; } else return -1; break; case F_T_BOOL: - if (cfg_string_to_int(j, &temp, text)) + if (cfg_string_to_int(setting, &temp, text)) { - setting->value.number = temp; + touchsetting->value.number = temp; } else return -1; @@ -1602,7 +1689,7 @@ static int parse_touchregion(struct skin_element *element, region->armed = false; region->reverse_bar = false; region->value = 0; - region->last_press = 0xffff; + region->last_press = -1; region->press_length = PRESS; region->allow_while_locked = false; region->bar = PTRTOSKINOFFSET(skin_buffer, NULL); @@ -1640,16 +1727,21 @@ static int parse_touchregion(struct skin_element *element, if (region->action == ACTION_NONE) return WPS_ERROR_INVALID_PARAM; } + static const char * const pm_options[] = {"allow_while_locked", "reverse_bar", + "repeat_press", "long_press", NULL}; + int pm_op; + while (p < element->params_count) { char* param = get_param_text(element, p++); - if (!strcmp(param, "allow_while_locked")) + pm_op = string_option(param, pm_options, false); + if (pm_op == 0) region->allow_while_locked = true; - else if (!strcmp(param, "reverse_bar")) + else if (pm_op == 1) region->reverse_bar = true; - else if (!strcmp(param, "repeat_press")) + else if (pm_op == 2) region->press_length = REPEAT; - else if (!strcmp(param, "long_press")) + else if (pm_op == 3) region->press_length = LONG_PRESS; } struct skin_token_list *item = new_skin_token_list_item(NULL, region); @@ -1758,9 +1850,7 @@ void skin_data_free_buflib_allocs(struct wps_data *wps_data) abort: wps_data->font_ids = PTRTOSKINOFFSET(skin_buffer, NULL); /* Safe if skin_buffer is NULL */ wps_data->images = PTRTOSKINOFFSET(skin_buffer, NULL); - if (wps_data->buflib_handle > 0) - core_free(wps_data->buflib_handle); - wps_data->buflib_handle = -1; + wps_data->buflib_handle = core_free(wps_data->buflib_handle); #endif } @@ -1801,13 +1891,11 @@ static void skin_data_reset(struct wps_data *wps_data) } #ifndef __PCTOOL__ -static int currently_loading_handle = -1; static int buflib_move_callback(int handle, void* current, void* new) { + (void)handle; (void)current; (void)new; - if (handle == currently_loading_handle) - return BUFLIB_CB_CANNOT_MOVE; /* Any active skins may be scrolling - which means using viewports which * will be moved after this callback returns. This is a hammer to make that * safe. TODO: use a screwdriver instead. @@ -1820,85 +1908,61 @@ static int buflib_move_callback(int handle, void* current, void* new) return BUFLIB_CB_OK; } -static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL}; -static void lock_handle(int handle) -{ - currently_loading_handle = handle; -} -static void unlock_handle(void) -{ - currently_loading_handle = -1; -} #endif static int load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir) { + (void)wps_data; /* only needed for remote targets */ char img_path[MAX_PATH]; - int fd; - int handle; + get_image_filename(bitmap->data, bmpdir, img_path, sizeof(img_path)); - /* load the image */ - int format; -#ifdef HAVE_REMOTE_LCD - if (curr_screen == SCREEN_REMOTE) - format = FORMAT_ANY|FORMAT_REMOTE; - else -#endif - format = FORMAT_ANY|FORMAT_TRANSPARENT; - - fd = open(img_path, O_RDONLY); +#ifdef __PCTOOL__ /* just check if image exists */ + int fd = open(img_path, O_RDONLY); if (fd < 0) { DEBUGF("Couldn't open %s\n", img_path); return fd; } -#ifndef __PCTOOL__ - int buf_size = read_bmp_fd(fd, bitmap, 0, - format|FORMAT_RETURN_SIZE, NULL); - if(buf_size < 0) - { - close(fd); - return buf_size; - } + close(fd); + return 1; +#else /* load the image */ + static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL}; + int handle; + int bmpformat; + ssize_t buf_reqd; +#ifdef HAVE_REMOTE_LCD + if (curr_screen == SCREEN_REMOTE) + bmpformat = FORMAT_ANY|FORMAT_REMOTE; + else +#endif + bmpformat = FORMAT_ANY|FORMAT_TRANSPARENT; - handle = core_alloc_ex(bitmap->data, buf_size, &buflib_ops); - if (handle <= 0) + handle = core_load_bmp(img_path, bitmap, bmpformat, &buf_reqd, &buflib_ops); + if (handle != CLB_ALOC_ERR) { - DEBUGF("Not enough skin buffer: need %zd more.\n", - buf_size - skin_buffer_freespace()); - close(fd); + /* NOTE!: bitmap->data == NULL to force a crash later if the + caller doesnt call core_get_data() */ + _stats->buflib_handles++; + _stats->images_size += buf_reqd; return handle; } - _stats->buflib_handles++; - _stats->images_size += buf_size; - lseek(fd, 0, SEEK_SET); - lock_handle(handle); - bitmap->data = core_get_data(handle); - int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL); - bitmap->data = NULL; /* do this to force a crash later if the - caller doesnt call core_get_data() */ - unlock_handle(); - close(fd); - if (ret > 0) + + if (buf_reqd == CLB_READ_ERR) { - /* free unused alpha channel, if any */ - core_shrink(handle, core_get_data(handle), ret); - return handle; + /* Abort if we can't load an image */ + DEBUGF("Couldn't load '%s' (%ld)\n", img_path, buf_reqd); } else { - /* Abort if we can't load an image */ - DEBUGF("Couldn't load '%s'\n", img_path); - core_free(handle); - return -1; + DEBUGF("Not enough skin buffer: need %zd more.\n", + buf_reqd - skin_buffer_freespace()); } -#else /* !__PCTOOL__ */ - close(fd); - return 1; -#endif + + return -1; +#endif/* !__PCTOOL__ */ } static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir) @@ -1928,7 +1992,7 @@ static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir) strcpy(path, img->bm.data); handle = load_skin_bmp(wps_data, &img->bm, bmpdir); img->buflib_handle = handle; - img->loaded = img->buflib_handle >= 0; + img->loaded = img->buflib_handle > 0; if (img->loaded) { @@ -2326,13 +2390,6 @@ static int skin_element_callback(struct skin_element* element, void* data) break; #endif #ifdef HAVE_ALBUMART - case SKIN_TOKEN_ALBUMART_DISPLAY: - if (SKINOFFSETTOPTR(skin_buffer, wps_data->albumart)) - { - struct skin_albumart *aa = SKINOFFSETTOPTR(skin_buffer, wps_data->albumart); - aa->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp); - } - break; case SKIN_TOKEN_ALBUMART_LOAD: function = parse_albumart_load; break; @@ -2468,8 +2525,9 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data, skin_buffer = wps_buffer; wps_buffer = (char*)buf; } - skin_buffer = ALIGN_UP(skin_buffer, 4); /* align on 4-byte boundary */ - buffersize -= 3; + + /* align to long */ + ALIGN_BUFFER(skin_buffer, buffersize, sizeof(long)); #ifdef HAVE_BACKDROP_IMAGE backdrop_filename = "-"; wps_data->backdrop_id = -1; @@ -2492,7 +2550,7 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data, { /* get the bitmap dir */ char *dot = strrchr(buf, '.'); - strlcpy(bmpdir, buf, dot - buf + 1); + strmemccpy(bmpdir, buf, dot - buf + 1); } else { @@ -2516,8 +2574,7 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data, } #endif #ifndef __PCTOOL__ - wps_data->buflib_handle = core_alloc(isfile ? buf : "failsafe skin", - skin_buffer_usage()); + wps_data->buflib_handle = core_alloc(skin_buffer_usage()); if (wps_data->buflib_handle > 0) { wps_data->wps_loaded = true; diff --git a/apps/gui/skin_engine/skin_render.c b/apps/gui/skin_engine/skin_render.c index 1f777b6672..06f7d9798d 100644 --- a/apps/gui/skin_engine/skin_render.c +++ b/apps/gui/skin_engine/skin_render.c @@ -50,6 +50,7 @@ #include "root_menu.h" #include "misc.h" #include "list.h" +#include "wps.h" #define MAX_LINE 1024 @@ -66,7 +67,6 @@ struct skin_draw_info { bool no_line_break; bool line_scrolls; bool force_redraw; - bool viewport_change; char *buf; size_t buf_size; @@ -74,6 +74,8 @@ struct skin_draw_info { int offset; /* used by the playlist viewer */ }; +extern void sb_set_info_vp(enum screen_type screen, OFFSETTYPE(char*) label); + typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info); bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info); @@ -93,11 +95,11 @@ get_child(OFFSETTYPE(struct skin_element**) children, int child) static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info, - struct skin_element *element, struct skin_viewport* skin_vp) + struct skin_element *element) { struct wps_token *token = (struct wps_token *)SKINOFFSETTOPTR(skin_buffer, element->data); if (!token) return false; - struct viewport *vp = &skin_vp->vp; + struct skin_viewport *skin_vp = info->skin_vp; struct wps_data *data = gwps->data; bool do_refresh = (element->tag->flags & info->refresh_type) > 0; @@ -108,9 +110,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info, { struct viewport_colour *col = SKINOFFSETTOPTR(skin_buffer, token->value.data); if (!col) return false; - struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, col->vp); - if (!vp) return false; - vp->fg_pattern = col->colour; + skin_vp->vp.fg_pattern = col->colour; skin_vp->fgbg_changed = true; } break; @@ -118,9 +118,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info, { struct viewport_colour *col = SKINOFFSETTOPTR(skin_buffer, token->value.data); if (!col) return false; - struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, col->vp); - if (!vp) return false; - vp->bg_pattern = col->colour; + skin_vp->vp.bg_pattern = col->colour; skin_vp->fgbg_changed = true; } break; @@ -196,7 +194,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info, case SKIN_TOKEN_PEAKMETER: data->peak_meter_enabled = true; if (do_refresh) - draw_peakmeters(gwps, info->line_number, vp); + draw_peakmeters(gwps, info->line_number, &skin_vp->vp); break; case SKIN_TOKEN_DRAWRECTANGLE: if (do_refresh) @@ -215,13 +213,13 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info, #endif { #if LCD_DEPTH > 1 - unsigned backup = vp->fg_pattern; - vp->fg_pattern = rect->start_colour; + unsigned backup = skin_vp->vp.fg_pattern; + skin_vp->vp.fg_pattern = rect->start_colour; #endif gwps->display->fillrect(rect->x, rect->y, rect->width, rect->height); #if LCD_DEPTH > 1 - vp->fg_pattern = backup; + skin_vp->vp.fg_pattern = backup; #endif } } @@ -239,7 +237,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info, { struct progressbar *bar = (struct progressbar*)SKINOFFSETTOPTR(skin_buffer, token->value.data); if (do_refresh) - draw_progressbar(gwps, info->line_number, bar); + draw_progressbar(gwps, info->skin_vp, info->line_number, bar); } break; case SKIN_TOKEN_IMAGE_DISPLAY: @@ -582,7 +580,7 @@ static bool skin_render_line(struct skin_element* line, struct skin_draw_info *i { break; } - if (!do_non_text_tags(info->gwps, info, child, info->skin_vp)) + if (!do_non_text_tags(info->gwps, info, child)) { static char tempbuf[128]; const char *valuestr = get_token_value(info->gwps, SKINOFFSETTOPTR(skin_buffer, child->data), @@ -590,10 +588,6 @@ static bool skin_render_line(struct skin_element* line, struct skin_draw_info *i sizeof(tempbuf), NULL); if (valuestr) { -#if defined(ONDA_VX747) || defined(ONDA_VX747P) - /* Doesn't redraw (in sim at least) */ - needs_update = true; -#endif #if CONFIG_RTC if (child->tag->flags&SKIN_RTC_REFRESH) needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC; @@ -606,10 +600,6 @@ static bool skin_render_line(struct skin_element* line, struct skin_draw_info *i } break; case TEXT: -#if defined(ONDA_VX747) || defined(ONDA_VX747P) - /* Doesn't redraw (in sim at least) */ - needs_update = true; -#endif strlcat(info->cur_align_start, SKINOFFSETTOPTR(skin_buffer, child->data), info->buf_size - (info->cur_align_start-info->buf)); needs_update = needs_update || @@ -943,7 +933,7 @@ void skin_render_playlistviewer(struct playlistviewer* viewer, struct align_pos * align = &info.align; bool needs_update; int cur_pos, start_item, max; - int nb_lines = viewport_get_nb_lines(SKINOFFSETTOPTR(skin_buffer, viewer->vp)); + int nb_lines = viewport_get_nb_lines(&skin_viewport->vp); #if CONFIG_TUNER if (get_current_activity() == ACTIVITY_FM) { @@ -954,8 +944,8 @@ void skin_render_playlistviewer(struct playlistviewer* viewer, else #endif { - struct cuesheet *cue = skin_get_global_state()->id3 ? - skin_get_global_state()->id3->cuesheet : NULL; + struct wps_state *state = get_wps_state(); + struct cuesheet *cue = state->id3 ? state->id3->cuesheet : NULL; cur_pos = playlist_get_display_index(); max = playlist_amount()+1; if (cue) @@ -989,7 +979,7 @@ void skin_render_playlistviewer(struct playlistviewer* viewer, /* only update if the line needs to be, and there is something to write */ if (refresh_type && needs_update) { - struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, viewer->vp); + struct viewport *vp = &skin_viewport->vp; if (!info.force_redraw) display->scroll_stop_viewport_rect(vp, 0, info.line_number*display->getcharheight(), diff --git a/apps/gui/skin_engine/skin_tokens.c b/apps/gui/skin_engine/skin_tokens.c index a4c9af7539..f6c166b140 100644 --- a/apps/gui/skin_engine/skin_tokens.c +++ b/apps/gui/skin_engine/skin_tokens.c @@ -66,11 +66,10 @@ #include "fixedpoint.h" #endif #include "list.h" +#include "wps.h" #define NOINLINE __attribute__ ((noinline)) -extern struct wps_state wps_state; - static const char* get_codectype(const struct mp3entry* id3) { if (id3 && id3->codectype < AFMT_NUM_CODECS) { @@ -115,7 +114,7 @@ char* get_dir(char* buf, int buf_size, const char* path, int level) return NULL; len = MIN(last_sep - sep, buf_size - 1); - strlcpy(buf, sep + 1, len + 1); + strmemccpy(buf, sep + 1, len + 1); return buf; } @@ -228,7 +227,7 @@ static const char* get_filename_token(struct wps_token *token, char* filename, const char *get_id3_token(struct wps_token *token, struct mp3entry *id3, char *filename, char *buf, int buf_size, int limit, int *intval) { - struct wps_state *state = &wps_state; + struct wps_state *state = get_wps_state(); if (id3) { unsigned long length = id3->length; @@ -540,7 +539,7 @@ const char *get_radio_token(struct wps_token *token, int preset_offset, static struct mp3entry* get_mp3entry_from_offset(int offset, char **filename) { struct mp3entry* pid3 = NULL; - struct wps_state *state = skin_get_global_state(); + struct wps_state *state = get_wps_state(); struct cuesheet *cue = state->id3 ? state->id3->cuesheet : NULL; const char *fname = NULL; if (cue && cue->curr_track_idx + offset < cue->track_count) @@ -678,7 +677,7 @@ const char *get_token_value(struct gui_wps *gwps, return NULL; struct wps_data *data = gwps->data; - struct wps_state *state = skin_get_global_state(); + struct wps_state *state = get_wps_state(); struct mp3entry *id3; /* Think very carefully about using this. maybe get_id3_token() is the better place? */ const char *out_text = NULL; @@ -1040,37 +1039,19 @@ const char *get_token_value(struct gui_wps *gwps, case SKIN_TOKEN_PLAYBACK_STATUS: { int status = current_playmode(); - /* music */ - int mode = 1; /* stop */ - if (status == STATUS_PLAY) - mode = 2; /* play */ - if (state->is_fading || - (status == STATUS_PAUSE && !status_get_ffmode())) - mode = 3; /* pause */ - else - { /* ff / rwd */ - if (status_get_ffmode() == STATUS_FASTFORWARD) - mode = 4; - if (status_get_ffmode() == STATUS_FASTBACKWARD) - mode = 5; + switch (status) { + case STATUS_STOP: + numeric_ret = 1; + break; + case STATUS_PLAY: + numeric_ret = 2; + break; + default: + numeric_ret = status + 1; + break; } -#ifdef HAVE_RECORDING - /* recording */ - if (status == STATUS_RECORD) - mode = 6; - else if (status == STATUS_RECORD_PAUSE) - mode = 7; -#endif -#if CONFIG_TUNER - /* radio */ - if (status == STATUS_RADIO) - mode = 8; - else if (status == STATUS_RADIO_PAUSE) - mode = 9; -#endif - numeric_ret = mode; - snprintf(buf, buf_size, "%d", mode-1); + snprintf(buf, buf_size, "%d", numeric_ret-1); numeric_buf = buf; goto gtv_ret_numeric_tag_info; } @@ -1367,7 +1348,7 @@ const char *get_token_value(struct gui_wps *gwps, case SKIN_TOKEN_LASTTOUCH: { #ifdef HAVE_TOUCHSCREEN - unsigned int last_touch = touchscreen_last_touch(); + long last_touch = touchscreen_last_touch(); char *skin_base = get_skin_buffer(data); struct touchregion_lastpress *data = SKINOFFSETTOPTR(skin_base, token->value.data); if (!data) return NULL; @@ -1375,7 +1356,7 @@ const char *get_token_value(struct gui_wps *gwps, if (region) last_touch = region->last_press; - if (last_touch != 0xffff && + if (last_touch != -1 && TIME_BEFORE(current_tick, data->timeout + last_touch)) return "t"; #endif @@ -1390,7 +1371,7 @@ const char *get_token_value(struct gui_wps *gwps, case SKIN_TOKEN_SETTING: { - const struct settings_list *s = settings+token->value.i; + const struct settings_list *s = token->value.xdata; if (intval) { /* Handle contionals */ @@ -1461,7 +1442,7 @@ const char *get_token_value(struct gui_wps *gwps, goto gtv_ret_numeric_tag_info; } } - cfg_to_string(token->value.i,buf,buf_size); + cfg_to_string(s, buf, buf_size); numeric_buf = buf; goto gtv_ret_numeric_tag_info; } diff --git a/apps/gui/skin_engine/skin_touchsupport.c b/apps/gui/skin_engine/skin_touchsupport.c index 045bc809c8..b952709562 100644 --- a/apps/gui/skin_engine/skin_touchsupport.c +++ b/apps/gui/skin_engine/skin_touchsupport.c @@ -35,8 +35,9 @@ #include "dsp_misc.h" /** Disarms all touchregions. */ -void skin_disarm_touchregions(struct wps_data *data) +void skin_disarm_touchregions(struct gui_wps *gwps) { + struct wps_data *data = gwps->data; char* skin_buffer = get_skin_buffer(data); struct skin_token_list *regions = SKINOFFSETTOPTR(skin_buffer, data->touchregions); while (regions) @@ -52,9 +53,9 @@ void skin_disarm_touchregions(struct wps_data *data) * egde_offset is a percentage value for the position of the touch * inside the bar for regions which arnt WPS_TOUCHREGION_ACTION type. */ -int skin_get_touchaction(struct wps_data *data, int* edge_offset, - struct touchregion **retregion) +int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset) { + struct wps_data *data = gwps->data; int returncode = ACTION_NONE; short x,y; short vx, vy; @@ -162,9 +163,7 @@ int skin_get_touchaction(struct wps_data *data, int* edge_offset, /* On release, all regions are disarmed. */ if (released) - skin_disarm_touchregions(data); - if (retregion && temp) - *retregion = temp; + skin_disarm_touchregions(gwps); if (temp && temp->press_length == LONG_PRESS) temp->armed = false; @@ -301,7 +300,7 @@ int skin_get_touchaction(struct wps_data *data, int* edge_offset, case ACTION_TOUCH_REPMODE: /* cycle the repeat mode setting */ { const struct settings_list *rep_setting = - find_setting(&global_settings.repeat_mode, NULL); + find_setting(&global_settings.repeat_mode); option_select_next_val(rep_setting, false, true); audio_flush_and_reload_tracks(); returncode = ACTION_REDRAW; @@ -314,9 +313,9 @@ int skin_get_touchaction(struct wps_data *data, int* edge_offset, if (bar && edge_offset) { int val, count; - get_setting_info_for_bar(bar->setting_id, &count, &val); + get_setting_info_for_bar(bar->setting, &count, &val); val = *edge_offset * count / 1000; - update_setting_value_from_touch(bar->setting_id, val); + update_setting_value_from_touch(bar->setting, val); } } break; diff --git a/apps/gui/skin_engine/wps_internals.h b/apps/gui/skin_engine/wps_internals.h index bf368bc4f3..6b9719282e 100644 --- a/apps/gui/skin_engine/wps_internals.h +++ b/apps/gui/skin_engine/wps_internals.h @@ -31,6 +31,8 @@ #include "core_alloc.h" #endif +struct wps_data; + struct skin_stats { size_t buflib_handles; size_t tree_size; @@ -42,6 +44,13 @@ struct skin_stats *skin_get_stats(int number, int screen); #define skin_clear_stats(stats) memset(stats, 0, sizeof(struct skin_stats)) bool skin_backdrop_get_debug(int index, char **path, int *ref_count, size_t *size); +/* + * setup up the skin-data from a format-buffer (isfile = false) + * or from a skinfile (isfile = true) + */ +bool skin_data_load(enum screen_type screen, struct wps_data *wps_data, + const char *buf, bool isfile, struct skin_stats *stats); + /* Timeout unit expressed in HZ. In WPS, all timeouts are given in seconds (possibly with a decimal fraction) but stored as integer values. E.g. 2.5 is stored as 25. This means 25 tenth of a second, i.e. 25 units. @@ -64,6 +73,7 @@ struct wps_token { unsigned short i; long l; OFFSETTYPE(void*) data; + void *xdata; } value; enum skin_token_type type; /* enough to store the token type */ @@ -81,7 +91,6 @@ struct skin_token_list { }; struct gui_img { - OFFSETTYPE(struct viewport*) vp; /* The viewport to display this image in */ short int x; /* x-pos */ short int y; /* y-pos */ short int num_subimages; /* number of sub-images */ @@ -104,7 +113,6 @@ struct image_display { struct progressbar { enum skin_token_type type; - OFFSETTYPE(struct viewport *) vp; /* regular pb */ short x; /* >=0: explicitly set in the tag -> y-coord within the viewport @@ -124,7 +132,7 @@ struct progressbar { OFFSETTYPE(struct gui_img *) slider; bool horizontal; OFFSETTYPE(struct gui_img *) backdrop; - int setting_id; /* for the setting bar type */ + const struct settings_list *setting; }; @@ -189,7 +197,6 @@ struct skin_viewport { #endif }; struct viewport_colour { - OFFSETTYPE(struct viewport *) vp; unsigned colour; }; @@ -236,7 +243,6 @@ struct touchregion_lastpress { #endif struct playlistviewer { - OFFSETTYPE(struct viewport *) vp; bool show_icons; int start_offset; OFFSETTYPE(struct skin_element *) line; @@ -267,7 +273,6 @@ struct skin_albumart { unsigned char yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */ unsigned char state; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */ - OFFSETTYPE(struct viewport *) vp; int draw_handle; }; #endif @@ -313,6 +318,17 @@ struct listitem { short offset; }; +struct listitem_viewport_cfg { + struct wps_data *data; + OFFSETTYPE(char *) label; + int width; + int height; + int xmargin; + int ymargin; + bool tile; + struct skin_viewport selected_item_vp; +}; + #ifdef HAVE_SKIN_VARIABLES struct skin_var { OFFSETTYPE(const char *) label; @@ -372,7 +388,7 @@ struct wps_data #ifndef __PCTOOL__ static inline char* get_skin_buffer(struct wps_data* data) { - if (data->buflib_handle >= 0) + if (data->buflib_handle > 0) return core_get_data(data->buflib_handle); return NULL; } @@ -382,30 +398,6 @@ static inline char* get_skin_buffer(struct wps_data* data) /* wps_data end */ -/* wps_state - holds the data which belongs to the current played track, - the track which will be played afterwards, current path to the track - and some status infos */ -struct wps_state -{ - struct mp3entry* id3; - struct mp3entry* nid3; - int ff_rewind_count; - bool ff_rewind; - bool paused; - bool is_fading; -}; - -/* change the ff/rew-status - if ff_rew = true then we are in skipping mode - else we are in normal mode */ -/* void wps_state_update_ff_rew(bool ff_rew); Currently unused */ - -/* change the tag-information of the current played track - and the following track */ -/* void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3); Currently unused */ -/* wps_state end*/ - /* gui_wps defines a wps with its data, state, and the screen on which the wps-content should be drawn */ diff --git a/apps/gui/splash.c b/apps/gui/splash.c index b85e4693aa..d0f1fbb67c 100644 --- a/apps/gui/splash.c +++ b/apps/gui/splash.c @@ -29,71 +29,84 @@ #include "talk.h" #include "splash.h" #include "viewport.h" -#include "strtok_r.h" +#include "strptokspn_r.h" +#include "scrollbar.h" +#include "font.h" + +static long progress_next_tick = 0; #define MAXLINES (LCD_HEIGHT/6) #define MAXBUFFER 512 #define RECT_SPACING 2 #define SPLASH_MEMORY_INTERVAL (HZ) -static void splash_internal(struct screen * screen, const char *fmt, va_list ap) +static bool splash_internal(struct screen * screen, const char *fmt, va_list ap, + struct viewport *vp, int addl_lines) { char splash_buf[MAXBUFFER]; - char *lines[MAXLINES]; - - char *next; - char *lastbreak = NULL; - char *store = NULL; + struct splash_lines { + const char *str; + size_t len; + } lines[MAXLINES]; + const char *next; + const char *lastbreak = NULL; + const char *store = NULL; int line = 0; int x = 0; int y, i; - int space_w, w, h; - struct viewport vp; + int space_w, w, chr_h; int width, height; int maxw = 0; + int fontnum = vp->font; - viewport_set_defaults(&vp, screen->screen_type); - struct viewport *last_vp = screen->set_viewport(&vp); - - screen->getstringsize(" ", &space_w, &h); - y = h; + char lastbrkchr; + size_t len, next_len; + const char matchstr[] = "\r\n\f\v\t "; + font_getstringsize(" ", &space_w, &chr_h, fontnum); + y = chr_h + (addl_lines * chr_h); vsnprintf(splash_buf, sizeof(splash_buf), fmt, ap); va_end(ap); /* break splash string into display lines, doing proper word wrap */ - - next = strtok_r(splash_buf, " ", &store); + next = strptokspn_r(splash_buf, matchstr, &next_len, &store); if (!next) - goto end; /* nothing to display */ + return false; /* nothing to display */ - lines[0] = next; + lines[line].len = next_len; + lines[line].str = next; while (true) { - screen->getstringsize(next, &w, NULL); + w = font_getstringnsize(next, next_len, NULL, NULL, fontnum); if (lastbreak) { - if (x + (next - lastbreak) * space_w + w - > vp.width - RECT_SPACING*2) - { /* too wide, wrap */ + len = next - lastbreak; + int next_w = len * space_w; + if (x + next_w + w > vp->width - RECT_SPACING*2 || lastbrkchr != ' ') + { /* too wide, or control character wrap */ if (x > maxw) maxw = x; - if ((y + h > vp.height) || (line >= (MAXLINES-1))) + if ((y + chr_h * 2 > vp->height) || (line >= (MAXLINES-1))) break; /* screen full or out of lines */ x = 0; - y += h; - lines[++line] = next; + y += chr_h; + lines[++line].len = next_len; + lines[line].str = next; } else { /* restore & calculate spacing */ - *lastbreak = ' '; - x += (next - lastbreak) * space_w; + lines[line].len += next_len + 1; + x += next_w; } } x += w; - lastbreak = next + strlen(next); - next = strtok_r(NULL, " ", &store); + + lastbreak = next + next_len; + lastbrkchr = *lastbreak; + + next = strptokspn_r(NULL, matchstr, &next_len, &store); + if (!next) { /* no more words */ if (x > maxw) @@ -111,53 +124,48 @@ static void splash_internal(struct screen * screen, const char *fmt, va_list ap) width = maxw + 2*RECT_SPACING; height = y + 2*RECT_SPACING; - if (width > vp.width) - width = vp.width; - if (height > vp.height) - height = vp.height; + if (width > vp->width) + width = vp->width; + if (height > vp->height) + height = vp->height; - vp.x += (vp.width - width) / 2; - vp.y += (vp.height - height) / 2; - vp.width = width; - vp.height = height; + vp->x += (vp->width - width) / 2; + vp->y += (vp->height - height) / 2; + vp->width = width; + vp->height = height; - vp.flags |= VP_FLAG_ALIGN_CENTER; + vp->flags |= VP_FLAG_ALIGN_CENTER; #if LCD_DEPTH > 1 if (screen->depth > 1) { - vp.drawmode = DRMODE_FG; - /* can't do vp.fg_pattern here, since set_foreground does a bit more on + vp->drawmode = DRMODE_FG; + /* can't do vp->fg_pattern here, since set_foreground does a bit more on * greyscale */ screen->set_foreground(SCREEN_COLOR_TO_NATIVE(screen, LCD_LIGHTGRAY)); } else #endif - vp.drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID); + vp->drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID); screen->fill_viewport(); #if LCD_DEPTH > 1 if (screen->depth > 1) - /* can't do vp.fg_pattern here, since set_foreground does a bit more on + /* can't do vp->fg_pattern here, since set_foreground does a bit more on * greyscale */ screen->set_foreground(SCREEN_COLOR_TO_NATIVE(screen, LCD_BLACK)); else #endif - vp.drawmode = DRMODE_SOLID; + vp->drawmode = DRMODE_SOLID; screen->draw_border_viewport(); - /* prepare putting the text */ - y = RECT_SPACING; - /* print the message to screen */ - for (i = 0; i <= line; i++, y+=h) + for(i = 0, y = RECT_SPACING; i <= line; i++, y+= chr_h) { - screen->putsxy(0, y, lines[i]); + screen->putsxyf(0, y, "%.*s", lines[i].len, lines[i].str); } - screen->update_viewport(); -end: - screen->set_viewport(last_vp); + return true; /* needs update */ } void splashf(int ticks, const char *fmt, ...) @@ -169,9 +177,17 @@ void splashf(int ticks, const char *fmt, ...) fmt = P2STR((unsigned char *)fmt); FOR_NB_SCREENS(i) { + struct screen * screen = &(screens[i]); + struct viewport vp; + viewport_set_defaults(&vp, screen->screen_type); + struct viewport *last_vp = screen->set_viewport(&vp); + va_start(ap, fmt); - splash_internal(&(screens[i]), fmt, ap); + if (splash_internal(screen, fmt, ap, &vp, 0)) + screen->update_viewport(); va_end(ap); + + screen->set_viewport(last_vp); } if (ticks) sleep(ticks); @@ -189,3 +205,59 @@ void splash(int ticks, const char *str) #endif splashf(ticks, "%s", P2STR((const unsigned char*)str)); } + +/* set delay before progress meter is shown */ +void splash_progress_set_delay(long delay_ticks) +{ + progress_next_tick = current_tick + delay_ticks; +} + +/* splash a progress meter */ +void splash_progress(int current, int total, const char *fmt, ...) +{ + va_list ap; + int vp_flag = VP_FLAG_VP_DIRTY; + /* progress update tick */ + long now = current_tick; + + if (current < total) + { + if(TIME_BEFORE(now, progress_next_tick)) + return; + /* limit to 20fps */ + progress_next_tick = now + HZ/20; + vp_flag = 0; /* don't mark vp dirty to prevent flashing */ + } + + /* If fmt is a lang ID then get the corresponding string (which + still might contain % place holders). */ + fmt = P2STR((unsigned char *)fmt); + FOR_NB_SCREENS(i) + { + struct screen * screen = &(screens[i]); + struct viewport vp; + viewport_set_defaults(&vp, screen->screen_type); + struct viewport *last_vp = screen->set_viewport_ex(&vp, vp_flag); + + va_start(ap, fmt); + if (splash_internal(screen, fmt, ap, &vp, 1)) + { + int size = screen->getcharheight(); + int x = RECT_SPACING; + int y = vp.height - size - RECT_SPACING; + int w = vp.width - RECT_SPACING * 2; + int h = size; +#ifdef HAVE_LCD_COLOR + const int sb_flags = HORIZONTAL | FOREGROUND; +#else + const int sb_flags = HORIZONTAL; +#endif + gui_scrollbar_draw(screen, x, y, w, h, total, 0, current, sb_flags); + + screen->update_viewport(); + } + va_end(ap); + + screen->set_viewport(last_vp); + } +} diff --git a/apps/gui/splash.h b/apps/gui/splash.h index 76b4c16d0c..f7ff44e00b 100644 --- a/apps/gui/splash.h +++ b/apps/gui/splash.h @@ -39,4 +39,15 @@ extern void splashf(int ticks, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3); * it will be voiced */ extern void splash(int ticks, const char *str); + +/* set a delay before displaying the progress meter the first time */ +extern void splash_progress_set_delay(long delay_ticks); +/* + * Puts a splash message centered on all the screens with a progressbar + * - current : current progress increment + * - total : total increments + * - fmt : what to say *printf style + * updates limited internally to 20 fps - call repeatedly to update progress + */ +extern void splash_progress(int current, int total, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4); #endif /* _GUI_ICON_H_ */ diff --git a/apps/gui/statusbar-skinned.c b/apps/gui/statusbar-skinned.c index 63f3197faa..8dd66641dd 100644 --- a/apps/gui/statusbar-skinned.c +++ b/apps/gui/statusbar-skinned.c @@ -27,7 +27,6 @@ #include "appevents.h" #include "screens.h" #include "screen_access.h" -#include "strlcpy.h" #include "skin_parser.h" #include "skin_buffer.h" #include "skin_engine/skin_engine.h" @@ -49,11 +48,13 @@ static int update_delay = DEFAULT_UPDATE_DELAY; static bool sbs_has_title[NB_SCREENS]; -static char* sbs_title[NB_SCREENS]; +static const char* sbs_title[NB_SCREENS]; static enum themable_icons sbs_icon[NB_SCREENS]; static bool sbs_loaded[NB_SCREENS] = { false }; -bool sb_set_title_text(char* title, enum themable_icons icon, enum screen_type screen) +void sb_set_info_vp(enum screen_type screen, OFFSETTYPE(char*) label); + +bool sb_set_title_text(const char* title, enum themable_icons icon, enum screen_type screen) { sbs_title[screen] = title; /* Icon_NOICON == -1 which the skin engine wants at position 1, so + 2 */ @@ -156,7 +157,12 @@ int sb_get_backdrop(enum screen_type screen) else return -1; } - +#else +int sb_get_backdrop(enum screen_type screen) +{ + (void) screen; + return -1; +} #endif static bool force_waiting = false; void sb_skin_update(enum screen_type screen, bool force) @@ -300,18 +306,17 @@ void sb_bypass_touchregions(bool enable) int sb_touch_to_button(int context) { - struct touchregion *region; static int last_context = -1; int button, offset; if (bypass_sb_touchregions) return ACTION_TOUCHSCREEN; - + + struct gui_wps *gwps = skin_get_gwps(CUSTOM_STATUSBAR, SCREEN_MAIN); if (last_context != context) - skin_disarm_touchregions(skin_get_gwps(CUSTOM_STATUSBAR, SCREEN_MAIN)->data); + skin_disarm_touchregions(gwps); last_context = context; - button = skin_get_touchaction(skin_get_gwps(CUSTOM_STATUSBAR, SCREEN_MAIN)->data, - &offset, ®ion); - + + button = skin_get_touchaction(gwps, &offset); switch (button) { #ifdef HAVE_VOLUME_IN_LIST diff --git a/apps/gui/statusbar-skinned.h b/apps/gui/statusbar-skinned.h index ad102bef47..e8fa14e676 100644 --- a/apps/gui/statusbar-skinned.h +++ b/apps/gui/statusbar-skinned.h @@ -30,16 +30,15 @@ #include "icon.h" #include "skin_engine/skin_engine.h" -void sb_skin_data_load(enum screen_type screen, const char *buf, bool isfile); +struct wps_data; char* sb_create_from_settings(enum screen_type screen); void sb_skin_init(void) INIT_ATTR; -void sb_set_info_vp(enum screen_type screen, OFFSETTYPE(char*) label); struct viewport *sb_skin_get_info_vp(enum screen_type screen); void sb_skin_update(enum screen_type screen, bool force); void sb_skin_set_update_delay(int delay); -bool sb_set_title_text(char* title, enum themable_icons icon, enum screen_type screen); +bool sb_set_title_text(const char* title, enum themable_icons icon, enum screen_type screen); void sb_skin_has_title(enum screen_type screen); const char* sb_get_title(enum screen_type screen); enum themable_icons sb_get_icon(enum screen_type screen); @@ -49,9 +48,7 @@ void sb_bypass_touchregions(bool enable); int sb_touch_to_button(int context); #endif -#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) int sb_get_backdrop(enum screen_type screen); -#endif int sb_preproccess(enum screen_type screen, struct wps_data *data); int sb_postproccess(enum screen_type screen, struct wps_data *data); diff --git a/apps/gui/statusbar.c b/apps/gui/statusbar.c index a961d191b2..74da14bd3f 100644 --- a/apps/gui/statusbar.c +++ b/apps/gui/statusbar.c @@ -725,18 +725,6 @@ void gui_syncstatusbar_init(struct gui_syncstatusbar * bars) } } -void gui_syncstatusbar_draw(struct gui_syncstatusbar * bars, - bool force_redraw) -{ - if(!global_settings.statusbar) - return; - struct viewport viewport; - FOR_NB_SCREENS(i) { - GET_RECT(viewport,statusbar_position(i),&screens[i]); - gui_statusbar_draw( &(bars->statusbars[i]), force_redraw, &viewport ); - } -} - #ifdef HAVE_REMOTE_LCD enum statusbar_values statusbar_position(int screen) diff --git a/apps/gui/statusbar.h b/apps/gui/statusbar.h index a676c7b143..c62434155d 100644 --- a/apps/gui/statusbar.h +++ b/apps/gui/statusbar.h @@ -99,8 +99,7 @@ struct gui_syncstatusbar }; extern void gui_syncstatusbar_init(struct gui_syncstatusbar * bars) INIT_ATTR; -extern void gui_syncstatusbar_draw(struct gui_syncstatusbar * bars, - bool force_redraw); + #if !defined(HAVE_REMOTE_LCD) || defined(__PCTOOL__) #include "settings.h" #define statusbar_position(a) ((enum statusbar_values)global_settings.statusbar) diff --git a/apps/gui/usb_screen.c b/apps/gui/usb_screen.c index 31321ec005..fb59f820b6 100644 --- a/apps/gui/usb_screen.c +++ b/apps/gui/usb_screen.c @@ -41,6 +41,7 @@ #include "skin_engine/skin_engine.h" #include "playlist.h" #include "misc.h" +#include "icons.h" #include "bitmaps/usblogo.h" @@ -155,7 +156,24 @@ static void usb_screen_fix_viewports(struct screen *screen, *logo = *parent; logo->x = parent->x + parent->width - logo_width; +#ifdef HAVE_LCD_SPLIT + switch (statusbar_position(screen)) + { + /* start beyond split */ + case STATUSBAR_OFF: + logo->y = parent->y + LCD_SPLIT_POS; + break; + case STATUSBAR_TOP: + logo->y = parent->y + LCD_SPLIT_POS - STATUSBAR_HEIGHT; + break; + /* start at the top for maximum space */ + default: + logo->y = parent->y; + break; + } +#else logo->y = parent->y + (parent->height - logo_height) / 2; +#endif logo->width = logo_width; logo->height = logo_height; diff --git a/apps/gui/viewport.c b/apps/gui/viewport.c index 3dd8bca979..e90426a132 100644 --- a/apps/gui/viewport.c +++ b/apps/gui/viewport.c @@ -233,7 +233,7 @@ static void viewportmanager_redraw(unsigned short id, void* data) } } -void viewportmanager_init() +void viewportmanager_init(void) { FOR_NB_SCREENS(i) { @@ -332,7 +332,7 @@ void viewport_set_defaults(struct viewport *vp, const enum screen_type screen) { vp->buffer = NULL; /* use default frame_buffer */ - + vp->flags = VP_DEFAULT_FLAGS; #if !defined(__PCTOOL__) struct viewport *sbs_area = NULL; if (!is_theme_enabled(screen)) @@ -348,41 +348,3 @@ void viewport_set_defaults(struct viewport *vp, #endif /* !__PCTOOL__ */ viewport_set_fullscreen(vp, screen); } - - -int get_viewport_default_colour(enum screen_type screen, bool fgcolour) -{ - (void)screen; (void)fgcolour; -#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) - int colour; - if (fgcolour) - { -#if (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) - if (screen == SCREEN_REMOTE) - colour = REMOTE_FG_FALLBACK; - else -#endif -#if defined(HAVE_LCD_COLOR) - colour = global_settings.fg_color; -#else - colour = FG_FALLBACK; -#endif - } - else - { -#if (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1) - if (screen == SCREEN_REMOTE) - colour = REMOTE_BG_FALLBACK; - else -#endif -#if defined(HAVE_LCD_COLOR) - colour = global_settings.bg_color; -#else - colour = BG_FALLBACK; -#endif - } - return colour; -#else - return 0; -#endif /* LCD_DEPTH > 1 || LCD_REMOTE_DEPTH > 1 */ -} diff --git a/apps/gui/viewport.h b/apps/gui/viewport.h index 2810be2ac3..c57a58b232 100644 --- a/apps/gui/viewport.h +++ b/apps/gui/viewport.h @@ -43,7 +43,6 @@ void viewport_set_defaults(struct viewport *vp, const enum screen_type screen); void viewport_set_fullscreen(struct viewport *vp, const enum screen_type screen); -int get_viewport_default_colour(enum screen_type screen, bool fgcolour); #ifndef __PCTOOL__ diff --git a/apps/gui/wps.c b/apps/gui/wps.c index cdb34ab447..d35a5ac6d2 100644 --- a/apps/gui/wps.c +++ b/apps/gui/wps.c @@ -53,6 +53,7 @@ #include "root_menu.h" #include "backdrop.h" #include "quickscreen.h" +#include "shortcuts.h" #include "pitchscreen.h" #include "appevents.h" #include "viewport.h" @@ -61,14 +62,15 @@ #include "playlist_viewer.h" #include "wps.h" #include "statusbar-skinned.h" - -#define RESTORE_WPS_INSTANTLY 0l -#define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick)) +#include "skin_engine/wps_internals.h" +#include "open_plugin.h" #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ /* 3% of 30min file == 54s step size */ #define MIN_FF_REWIND_STEP 500 +static struct wps_state wps_state; + /* initial setup of wps_data */ static void wps_state_init(void); static void track_info_callback(unsigned short id, void *param); @@ -114,7 +116,7 @@ static void update_non_static(void) skin_update(WPS, i, SKIN_REFRESH_NON_STATIC); } -void pause_action(bool may_fade, bool updatewps) +void pause_action(bool updatewps) { /* Do audio first, then update, unless skin were to use its local status in which case, reverse it */ @@ -131,11 +133,9 @@ void pause_action(bool may_fade, bool updatewps) - global_settings.pause_rewind * 1000; audio_ff_rewind(newpos > 0 ? newpos : 0); } - - (void)may_fade; } -void unpause_action(bool may_fade, bool updatewps) +void unpause_action(bool updatewps) { /* Do audio first, then update, unless skin were to use its local status in which case, reverse it */ @@ -143,24 +143,15 @@ void unpause_action(bool may_fade, bool updatewps) if (updatewps) update_non_static(); - - (void)may_fade; -} - -static bool update_onvol_change(enum screen_type screen) -{ - skin_update(WPS, screen, SKIN_REFRESH_NON_STATIC); - - return false; } - #ifdef HAVE_TOUCHSCREEN -static int skintouch_to_wps(struct wps_data *data) +static int skintouch_to_wps(void) { int offset = 0; - struct touchregion *region; - int button = skin_get_touchaction(data, &offset, ®ion); + struct wps_state *gstate = get_wps_state(); + struct gui_wps *gwps = skin_get_gwps(WPS, SCREEN_MAIN); + int button = skin_get_touchaction(gwps, &offset); switch (button) { case ACTION_STD_PREV: @@ -182,17 +173,17 @@ static int skintouch_to_wps(struct wps_data *data) return ACTION_WPS_HOTKEY; #endif case ACTION_TOUCH_SCROLLBAR: - skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->length*offset/1000; + gstate->id3->elapsed = gstate->id3->length*offset/1000; audio_pre_ff_rewind(); - audio_ff_rewind(skin_get_global_state()->id3->elapsed); + audio_ff_rewind(gstate->id3->elapsed); return ACTION_TOUCHSCREEN; case ACTION_TOUCH_VOLUME: { const int min_vol = sound_min(SOUND_VOLUME); const int max_vol = sound_max(SOUND_VOLUME); const int step_vol = sound_steps(SOUND_VOLUME); - global_settings.volume = (offset * (max_vol - min_vol)) / 1000; - global_settings.volume += min_vol; + + global_settings.volume = from_normalized_volume(offset, min_vol, max_vol, 1000); global_settings.volume -= (global_settings.volume % step_vol); setvol(); } @@ -202,7 +193,7 @@ static int skintouch_to_wps(struct wps_data *data) } #endif /* HAVE_TOUCHSCREEN */ -bool ffwd_rew(int button) +static bool ffwd_rew(int button, bool seek_from_end) { unsigned int step = 0; /* current ff/rewind step */ unsigned int max_step = 0; /* maximum ff/rewind step */ @@ -210,7 +201,10 @@ bool ffwd_rew(int button) int direction = -1; /* forward=1 or backward=-1 */ bool exit = false; bool usb = false; + bool ff_rewind = false; const long ff_rw_accel = (global_settings.ff_rewind_accel + 3); + struct wps_state *gstate = get_wps_state(); + struct mp3entry *old_id3 = gstate->id3; if (button == ACTION_NONE) { @@ -219,27 +213,37 @@ bool ffwd_rew(int button) } while (!exit) { + struct mp3entry *id3 = gstate->id3; + if (id3 != old_id3) + { + ff_rewind = false; + ff_rewind_count = 0; + old_id3 = id3; + } + if (id3 && seek_from_end) + id3->elapsed = id3->length; + switch ( button ) { case ACTION_WPS_SEEKFWD: direction = 1; /* Fallthrough */ case ACTION_WPS_SEEKBACK: - if (skin_get_global_state()->ff_rewind) + if (ff_rewind) { if (direction == 1) { /* fast forwarding, calc max step relative to end */ - max_step = (skin_get_global_state()->id3->length - - (skin_get_global_state()->id3->elapsed + + max_step = (id3->length - + (id3->elapsed + ff_rewind_count)) * FF_REWIND_MAX_PERCENT / 100; } else { /* rewinding, calc max step relative to start */ - max_step = (skin_get_global_state()->id3->elapsed + ff_rewind_count) * - FF_REWIND_MAX_PERCENT / 100; + max_step = (id3->elapsed + ff_rewind_count) * + FF_REWIND_MAX_PERCENT / 100; } max_step = MAX(max_step, MIN_FF_REWIND_STEP); @@ -254,8 +258,7 @@ bool ffwd_rew(int button) } else { - if ( (audio_status() & AUDIO_STATUS_PLAY) && - skin_get_global_state()->id3 && skin_get_global_state()->id3->length ) + if ((audio_status() & AUDIO_STATUS_PLAY) && id3 && id3->length ) { audio_pre_ff_rewind(); if (direction > 0) @@ -263,7 +266,7 @@ bool ffwd_rew(int button) else status_set_ffmode(STATUS_FASTBACKWARD); - skin_get_global_state()->ff_rewind = true; + ff_rewind = true; step = 1000 * global_settings.ff_rewind_min_step; } @@ -272,19 +275,17 @@ bool ffwd_rew(int button) } if (direction > 0) { - if ((skin_get_global_state()->id3->elapsed + ff_rewind_count) > - skin_get_global_state()->id3->length) - ff_rewind_count = skin_get_global_state()->id3->length - - skin_get_global_state()->id3->elapsed; + if ((id3->elapsed + ff_rewind_count) > id3->length) + ff_rewind_count = id3->length - id3->elapsed; } else { - if ((int)(skin_get_global_state()->id3->elapsed + ff_rewind_count) < 0) - ff_rewind_count = -skin_get_global_state()->id3->elapsed; + if ((int)(id3->elapsed + ff_rewind_count) < 0) + ff_rewind_count = -id3->elapsed; } /* set the wps state ff_rewind_count so the progess info displays corectly */ - skin_get_global_state()->ff_rewind_count = ff_rewind_count; + gstate->ff_rewind_count = ff_rewind_count; FOR_NB_SCREENS(i) { @@ -296,10 +297,10 @@ bool ffwd_rew(int button) break; case ACTION_WPS_STOPSEEK: - skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->elapsed+ff_rewind_count; - audio_ff_rewind(skin_get_global_state()->id3->elapsed); - skin_get_global_state()->ff_rewind_count = 0; - skin_get_global_state()->ff_rewind = false; + id3->elapsed = id3->elapsed + ff_rewind_count; + audio_ff_rewind(id3->elapsed); + gstate->ff_rewind_count = 0; + ff_rewind = false; status_set_ffmode(0); exit = true; break; @@ -317,20 +318,21 @@ bool ffwd_rew(int button) button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_BLOCK); #ifdef HAVE_TOUCHSCREEN if (button == ACTION_TOUCHSCREEN) - button = skintouch_to_wps(skin_get_gwps(WPS, SCREEN_MAIN)->data); + button = skintouch_to_wps(); #endif - if (button != ACTION_WPS_SEEKFWD && - button != ACTION_WPS_SEEKBACK) + if (button != ACTION_WPS_SEEKFWD + && button != ACTION_WPS_SEEKBACK + && button != 0) button = ACTION_WPS_STOPSEEK; } } return usb; } -#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) static void gwps_caption_backlight(struct wps_state *state) { - if (state && state->id3) +#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) + if (state->id3) { #ifdef HAVE_BACKLIGHT if (global_settings.caption_backlight) @@ -366,9 +368,10 @@ static void gwps_caption_backlight(struct wps_state *state) } #endif } +#else + (void) state; +#endif /* def HAVE_BACKLIGHT || def HAVE_REMOTE_LCD */ } -#endif - static void change_dir(int direction) { @@ -385,7 +388,7 @@ static void change_dir(int direction) static void prev_track(unsigned long skip_thresh) { - struct wps_state *state = skin_get_global_state(); + struct wps_state *state = get_wps_state(); if (state->id3->elapsed < skip_thresh) { audio_prev(); @@ -406,7 +409,7 @@ static void prev_track(unsigned long skip_thresh) static void next_track(void) { - struct wps_state *state = skin_get_global_state(); + struct wps_state *state = get_wps_state(); /* take care of if we're playing a cuesheet */ if (state->id3->cuesheet) { @@ -423,7 +426,7 @@ static void next_track(void) static void play_hop(int direction) { - struct wps_state *state = skin_get_global_state(); + struct wps_state *state = get_wps_state(); struct cuesheet *cue = state->id3->cuesheet; long step = global_settings.skip_length*1000; long elapsed = state->id3->elapsed; @@ -442,26 +445,26 @@ static void play_hop(int direction) { if (direction < 0) { - prev_track(DEFAULT_SKIP_TRESH); + prev_track(DEFAULT_SKIP_THRESH); return; } - else if (remaining < DEFAULT_SKIP_TRESH*2) + else if (remaining < DEFAULT_SKIP_THRESH*2) { next_track(); return; } else - elapsed += (remaining - DEFAULT_SKIP_TRESH*2); + elapsed += (remaining - DEFAULT_SKIP_THRESH*2); } else if (!global_settings.prevent_skip && (!step || (direction > 0 && step >= remaining) || - (direction < 0 && elapsed < DEFAULT_SKIP_TRESH))) + (direction < 0 && elapsed < DEFAULT_SKIP_THRESH))) { /* Do normal track skipping */ if (direction > 0) next_track(); else if (direction < 0) - prev_track(DEFAULT_SKIP_TRESH); + prev_track(DEFAULT_SKIP_THRESH); return; } else if (direction == 1 && step >= remaining) @@ -485,7 +488,6 @@ static void play_hop(int direction) audio_ff_rewind(elapsed); } - #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) /* * If the user is unable to see the wps, because the display is deactivated, @@ -502,16 +504,19 @@ static void wps_lcd_activation_hook(unsigned short id, void *param) } #endif -static void gwps_leave_wps(void) +static void gwps_leave_wps(bool theme_enabled) { FOR_NB_SCREENS(i) { - skin_get_gwps(WPS, i)->display->scroll_stop(); + struct gui_wps *gwps = skin_get_gwps(WPS, i); + gwps->display->scroll_stop(); + if (theme_enabled) + { #ifdef HAVE_BACKDROP_IMAGE - skin_backdrop_show(sb_get_backdrop(i)); + skin_backdrop_show(sb_get_backdrop(i)); #endif - viewportmanager_theme_undo(i, skin_has_sbs(i, skin_get_gwps(WPS, i)->data)); - + viewportmanager_theme_undo(i, skin_has_sbs(gwps)); + } } #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) @@ -525,19 +530,30 @@ static void gwps_leave_wps(void) #endif } +static void restore_theme(void) +{ + FOR_NB_SCREENS(i) + { + struct gui_wps *gwps = skin_get_gwps(WPS, i); + struct screen *display = gwps->display; + display->scroll_stop(); + viewportmanager_theme_enable(i, skin_has_sbs(gwps), NULL); + } +} + /* * display the wps on entering or restoring */ -static void gwps_enter_wps(void) +static void gwps_enter_wps(bool theme_enabled) { struct gui_wps *gwps; struct screen *display; + if (theme_enabled) + restore_theme(); FOR_NB_SCREENS(i) { gwps = skin_get_gwps(WPS, i); display = gwps->display; display->scroll_stop(); - viewportmanager_theme_enable(i, skin_has_sbs(i, skin_get_gwps(WPS, i)->data), NULL); - /* Update the values in the first (default) viewport - in case the user has modified the statusbar or colour settings */ #if LCD_DEPTH > 1 @@ -563,7 +579,7 @@ static void gwps_enter_wps(void) } #ifdef HAVE_TOUCHSCREEN gwps = skin_get_gwps(WPS, SCREEN_MAIN); - skin_disarm_touchregions(gwps->data); + skin_disarm_touchregions(gwps); if (gwps->data->touchregions < 0) touchscreen_set_mode(TOUCHSCREEN_BUTTON); #endif @@ -573,16 +589,16 @@ static void gwps_enter_wps(void) void wps_do_playpause(bool updatewps) { - struct wps_state *state = skin_get_global_state(); + struct wps_state *state = get_wps_state(); if ( state->paused ) { state->paused = false; - unpause_action(true, updatewps); + unpause_action(updatewps); } else { state->paused = true; - pause_action(true, updatewps); + pause_action(updatewps); settings_save(); #if !defined(HAVE_SW_POWEROFF) call_storage_idle_notifys(true); /* make sure resume info is saved */ @@ -590,6 +606,57 @@ void wps_do_playpause(bool updatewps) } } +static long do_wps_exit(long action, bool bookmark) +{ + audio_pause(); + update_non_static(); + if (bookmark) + bookmark_autobookmark(true); + audio_stop(); + + ab_reset_markers(); + + gwps_leave_wps(true); +#ifdef HAVE_RECORDING + if (action == ACTION_WPS_REC) + return GO_TO_RECSCREEN; +#else + (void)action; +#endif + if (global_settings.browse_current) + return GO_TO_PREVIOUS_BROWSER; + return GO_TO_PREVIOUS; +} + +static long do_party_mode(long action) +{ + if (global_settings.party_mode) + { + switch (action) + { +#ifdef ACTION_WPSAB_SINGLE + case ACTION_WPSAB_SINGLE: + if (!ab_repeat_mode_enabled()) + break; + /* Note: currently all targets use ACTION_WPS_BROWSE + * if mapped to any of below actions this will cause problems */ +#endif + case ACTION_WPS_PLAY: + case ACTION_WPS_SEEKFWD: + case ACTION_WPS_SEEKBACK: + case ACTION_WPS_SKIPPREV: + case ACTION_WPS_SKIPNEXT: + case ACTION_WPS_ABSETB_NEXTDIR: + case ACTION_WPS_ABSETA_PREVDIR: + case ACTION_WPS_STOP: + return ACTION_NONE; + break; + default: + break; + } + } + return action; +} /* The WPS can be left in two ways: * a) call a function, which draws over the wps. In this case, the wps @@ -597,31 +664,28 @@ void wps_do_playpause(bool updatewps) * b) return with a value evaluated by root_menu.c, in this case the wps * is really left, and root_menu will handle the next screen * - * In either way, call gwps_leave_wps(), in order to restore the correct + * In either way, call gwps_leave_wps(true), in order to restore the correct * "main screen" backdrops and statusbars */ long gui_wps_show(void) { long button = 0; bool restore = true; - long restoretimer = RESTORE_WPS_INSTANTLY; /* timer to delay screen redraw temporarily */ bool exit = false; bool bookmark = false; bool update = false; - bool vol_changed = false; + bool theme_enabled = true; long last_left = 0, last_right = 0; - struct wps_state *state = skin_get_global_state(); + struct wps_state *state = get_wps_state(); -#ifdef AB_REPEAT_ENABLE ab_repeat_init(); ab_reset_markers(); -#endif - wps_state_init(); + wps_state_init(); while ( 1 ) { + bool hotkey = false; bool audio_paused = (audio_status() & AUDIO_STATUS_PAUSE)?true:false; - /* did someone else (i.e power thread) change audio pause mode? */ if (state->paused != audio_paused) { state->paused = audio_paused; @@ -635,8 +699,53 @@ long gui_wps_show(void) #endif } } - button = skin_wait_for_action(WPS, CONTEXT_WPS|ALLOW_SOFTLOCK, - restore ? 1 : HZ/5); + + if (restore) + { + restore = false; +#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) + add_event(LCD_EVENT_ACTIVATION, wps_lcd_activation_hook); +#endif + /* we remove the update delay since it's not very usable in the wps, + * e.g. during volume changing or ffwd/rewind */ + sb_skin_set_update_delay(0); + skin_request_full_update(WPS); + update = true; + gwps_enter_wps(theme_enabled); + theme_enabled = true; + } + else + { + gwps_caption_backlight(state); + + FOR_NB_SCREENS(i) + { +#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) + /* currently, all remotes are readable without backlight + * so still update those */ + if (lcd_active() || (i != SCREEN_MAIN)) +#endif + { + bool full_update = skin_do_full_update(WPS, i); + if (update || full_update) + { + skin_update(WPS, i, full_update ? + SKIN_REFRESH_ALL : SKIN_REFRESH_NON_STATIC); + } + } + } + update = false; + } + + if (exit) + { + return do_wps_exit(button, bookmark); + } + + if (button && !IS_SYSEVENT(button) ) + storage_spin(); + + button = skin_wait_for_action(WPS, CONTEXT_WPS|ALLOW_SOFTLOCK, HZ/5); /* Exit if audio has stopped playing. This happens e.g. at end of playlist or if using the sleep timer. */ @@ -644,46 +753,64 @@ long gui_wps_show(void) exit = true; #ifdef HAVE_TOUCHSCREEN if (button == ACTION_TOUCHSCREEN) - button = skintouch_to_wps(skin_get_gwps(WPS, SCREEN_MAIN)->data); + button = skintouch_to_wps(); #endif + button = do_party_mode(button); /* block select actions in party mode */ + /* The iPods/X5/M5 use a single button for the A-B mode markers, defined as ACTION_WPSAB_SINGLE in their config files. */ #ifdef ACTION_WPSAB_SINGLE - if (!global_settings.party_mode && ab_repeat_mode_enabled()) + static int wps_ab_state = 0; + if (button == ACTION_WPSAB_SINGLE && ab_repeat_mode_enabled()) { - static int wps_ab_state = 0; - if (button == ACTION_WPSAB_SINGLE) + switch (wps_ab_state) { - switch (wps_ab_state) - { - case 0: /* set the A spot */ - button = ACTION_WPS_ABSETA_PREVDIR; - break; - case 1: /* set the B spot */ - button = ACTION_WPS_ABSETB_NEXTDIR; - break; - case 2: - button = ACTION_WPS_ABRESET; - break; - } - wps_ab_state = (wps_ab_state+1) % 3; + case 0: /* set the A spot */ + button = ACTION_WPS_ABSETA_PREVDIR; + break; + case 1: /* set the B spot */ + button = ACTION_WPS_ABSETB_NEXTDIR; + break; + case 2: + button = ACTION_WPS_ABRESET; + break; } + wps_ab_state = (wps_ab_state+1) % 3; } -#endif +#endif /* def ACTION_WPSAB_SINGLE */ + switch(button) { #ifdef HAVE_HOTKEY case ACTION_WPS_HOTKEY: + { + hotkey = true; if (!global_settings.hotkey_wps) break; - /* fall through */ -#endif + if (get_hotkey(global_settings.hotkey_wps)->flags & HOTKEY_FLAG_NOSBS) + { + /* leave WPS without re-enabling theme */ + theme_enabled = false; + gwps_leave_wps(theme_enabled); + onplay(state->id3->path, + FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey); + if (!audio_status()) + { + /* re-enable theme since we're returning to SBS */ + gwps_leave_wps(true); + return GO_TO_ROOT; + } + restore = true; + break; + } + } + /* fall through */ +#endif /* def HAVE_HOTKEY */ case ACTION_WPS_CONTEXT: { - bool hotkey = button == ACTION_WPS_HOTKEY; - gwps_leave_wps(); + gwps_leave_wps(true); int retval = onplay(state->id3->path, - FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey); + FILE_ATTR_AUDIO, CONTEXT_WPS, hotkey); /* if music is stopped in the context menu we want to exit the wps */ if (retval == ONPLAY_MAINMENU || !audio_status()) @@ -691,39 +818,46 @@ long gui_wps_show(void) else if (retval == ONPLAY_PLAYLIST) return GO_TO_PLAYLIST_VIEWER; else if (retval == ONPLAY_PLUGIN) - return GO_TO_PLUGIN; + { + restore_theme(); + theme_enabled = false; + open_plugin_run(ID2P(LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN)); + } + restore = true; } break; case ACTION_WPS_BROWSE: - gwps_leave_wps(); + gwps_leave_wps(true); return GO_TO_PREVIOUS_BROWSER; break; /* play/pause */ case ACTION_WPS_PLAY: - if (global_settings.party_mode) - break; wps_do_playpause(true); break; - case ACTION_WPS_VOLUP: - global_settings.volume += sound_steps(SOUND_VOLUME); - vol_changed = true; - break; + case ACTION_WPS_VOLUP: /* fall through */ case ACTION_WPS_VOLDOWN: - global_settings.volume -= sound_steps(SOUND_VOLUME); - vol_changed = true; + if (button == ACTION_WPS_VOLUP) + adjust_volume(1); + else + adjust_volume(-1); + + setvol(); + FOR_NB_SCREENS(i) + { + skin_update(WPS, i, SKIN_REFRESH_NON_STATIC); + } + update = false; break; /* fast forward OR next dir if this is straight after ACTION_WPS_SKIPNEXT */ case ACTION_WPS_SEEKFWD: - if (global_settings.party_mode) - break; if (current_tick -last_right < HZ) { - if (state->id3->cuesheet) + if (state->id3->cuesheet && playlist_check(1)) { audio_next(); } @@ -733,37 +867,42 @@ long gui_wps_show(void) } } else - ffwd_rew(ACTION_WPS_SEEKFWD); + ffwd_rew(ACTION_WPS_SEEKFWD, false); last_right = last_left = 0; break; /* fast rewind OR prev dir if this is straight after ACTION_WPS_SKIPPREV,*/ case ACTION_WPS_SEEKBACK: - if (global_settings.party_mode) - break; - if (current_tick -last_left < HZ) + if (current_tick - last_left < HZ) { - if (state->id3->cuesheet) + if (state->id3->cuesheet && playlist_check(-1)) { - audio_pre_ff_rewind(); - audio_ff_rewind(0); + audio_prev(); } else { change_dir(-1); } + } else if (global_settings.rewind_across_tracks + && get_wps_state()->id3->elapsed < DEFAULT_SKIP_THRESH + && playlist_check(-1)) + { + if (!audio_paused) + audio_pause(); + audio_prev(); + ffwd_rew(ACTION_WPS_SEEKBACK, true); + if (!audio_paused) + audio_resume(); } else - ffwd_rew(ACTION_WPS_SEEKBACK); + ffwd_rew(ACTION_WPS_SEEKBACK, false); last_left = last_right = 0; break; /* prev / restart */ case ACTION_WPS_SKIPPREV: - if (global_settings.party_mode) - break; last_left = current_tick; -#ifdef AB_REPEAT_ENABLE + /* if we're in A/B repeat mode and the current position is past the A marker, jump back to the A marker... */ if ( ab_repeat_mode_enabled() && ab_after_A_marker(state->id3->elapsed) ) @@ -771,19 +910,15 @@ long gui_wps_show(void) ab_jump_to_A_marker(); break; } - else - /* ...otherwise, do it normally */ -#endif + else /* ...otherwise, do it normally */ play_hop(-1); break; /* next OR if skip length set, hop by predetermined amount. */ case ACTION_WPS_SKIPNEXT: - if (global_settings.party_mode) - break; last_right = current_tick; -#ifdef AB_REPEAT_ENABLE + /* if we're in A/B repeat mode and the current position is before the A marker, jump to the A marker... */ if ( ab_repeat_mode_enabled() ) @@ -794,43 +929,33 @@ long gui_wps_show(void) break; } } - else - /* ...otherwise, do it normally */ -#endif + else /* ...otherwise, do it normally */ play_hop(1); break; /* next / prev directories */ /* and set A-B markers if in a-b mode */ case ACTION_WPS_ABSETB_NEXTDIR: - if (global_settings.party_mode) - break; -#if defined(AB_REPEAT_ENABLE) if (ab_repeat_mode_enabled()) { ab_set_B_marker(state->id3->elapsed); ab_jump_to_A_marker(); } else -#endif { change_dir(1); } break; case ACTION_WPS_ABSETA_PREVDIR: - if (global_settings.party_mode) - break; -#if defined(AB_REPEAT_ENABLE) if (ab_repeat_mode_enabled()) ab_set_A_marker(state->id3->elapsed); else -#endif { change_dir(-1); } break; /* menu key functions */ case ACTION_WPS_MENU: - gwps_leave_wps(); + gwps_leave_wps(true); return GO_TO_ROOT; break; @@ -838,16 +963,25 @@ long gui_wps_show(void) #ifdef HAVE_QUICKSCREEN case ACTION_WPS_QUICKSCREEN: { - gwps_leave_wps(); - if (global_settings.shortcuts_replaces_qs) + gwps_leave_wps(true); + bool enter_shortcuts_menu = global_settings.shortcuts_replaces_qs; + if (!enter_shortcuts_menu) { - global_status.last_screen = GO_TO_SHORTCUTMENU; int ret = quick_screen_quick(button); + if (ret == QUICKSCREEN_IN_USB) + return GO_TO_ROOT; + else if (ret == QUICKSCREEN_GOTO_SHORTCUTS_MENU) + enter_shortcuts_menu = true; + else + restore = true; + } + + if (enter_shortcuts_menu) /* enter_shortcuts_menu */ + { + global_status.last_screen = GO_TO_SHORTCUTMENU; + int ret = do_shortcut_menu(NULL); return (ret == GO_TO_PREVIOUS ? GO_TO_WPS : ret); } - else if (quick_screen_quick(button) > 0) - return GO_TO_ROOT; - restore = true; } break; #endif /* HAVE_QUICKSCREEN */ @@ -858,7 +992,7 @@ long gui_wps_show(void) #ifdef HAVE_PITCHCONTROL case ACTION_WPS_PITCHSCREEN: { - gwps_leave_wps(); + gwps_leave_wps(true); if (1 == gui_syncpitchscreen_run()) return GO_TO_ROOT; restore = true; @@ -866,7 +1000,6 @@ long gui_wps_show(void) break; #endif /* HAVE_PITCHCONTROL */ -#ifdef AB_REPEAT_ENABLE /* reset A&B markers */ case ACTION_WPS_ABRESET: if (ab_repeat_mode_enabled()) @@ -875,18 +1008,15 @@ long gui_wps_show(void) update = true; } break; -#endif /* AB_REPEAT_ENABLE */ /* stop and exit wps */ case ACTION_WPS_STOP: - if (global_settings.party_mode) - break; bookmark = true; exit = true; break; case ACTION_WPS_LIST_BOOKMARKS: - gwps_leave_wps(); + gwps_leave_wps(true); if (bookmark_load_menu() == BOOKMARK_USB_CONNECTED) { return GO_TO_ROOT; @@ -895,15 +1025,17 @@ long gui_wps_show(void) break; case ACTION_WPS_CREATE_BOOKMARK: - gwps_leave_wps(); + gwps_leave_wps(true); bookmark_create_menu(); restore = true; break; case ACTION_WPS_ID3SCREEN: { - gwps_leave_wps(); - if (browse_id3()) + gwps_leave_wps(true); + if (browse_id3(audio_current_track(), + playlist_get_display_index(), + playlist_amount(), NULL)) return GO_TO_ROOT; restore = true; } @@ -915,7 +1047,7 @@ long gui_wps_show(void) break; case ACTION_NONE: /* Timeout, do a partial update */ update = true; - ffwd_rew(button); /* hopefully fix the ffw/rwd bug */ + ffwd_rew(button, false); /* hopefully fix the ffw/rwd bug */ break; #ifdef HAVE_RECORDING case ACTION_WPS_REC: @@ -923,7 +1055,7 @@ long gui_wps_show(void) break; #endif case ACTION_WPS_VIEW_PLAYLIST: - gwps_leave_wps(); + gwps_leave_wps(true); return GO_TO_PLAYLIST_VIEWER; break; default: @@ -932,99 +1064,25 @@ long gui_wps_show(void) case SYS_USB_CONNECTED: case SYS_CALL_INCOMING: case BUTTON_MULTIMEDIA_STOP: - gwps_leave_wps(); + gwps_leave_wps(true); return GO_TO_ROOT; } update = true; break; } - - if (vol_changed) - { - bool res = false; - vol_changed = false; - setvol(); - FOR_NB_SCREENS(i) - { - if(update_onvol_change(i)) - res = true; - } - if (res) { - restore = true; - restoretimer = RESTORE_WPS_NEXT_SECOND; - } - } - - - if (restore && - ((restoretimer == RESTORE_WPS_INSTANTLY) || - TIME_AFTER(current_tick, restoretimer))) - { - restore = false; - restoretimer = RESTORE_WPS_INSTANTLY; -#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) - add_event(LCD_EVENT_ACTIVATION, wps_lcd_activation_hook); -#endif - /* we remove the update delay since it's not very usable in the wps, - * e.g. during volume changing or ffwd/rewind */ - sb_skin_set_update_delay(0); - skin_request_full_update(WPS); - update = true; - gwps_enter_wps(); - } - else - { -#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD) - gwps_caption_backlight(state); -#endif - FOR_NB_SCREENS(i) - { -#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) - /* currently, all remotes are readable without backlight - * so still update those */ - if (lcd_active() || (i != SCREEN_MAIN)) -#endif - { - bool full_update = skin_do_full_update(WPS, i); - if (update || full_update) - { - skin_update(WPS, i, full_update ? - SKIN_REFRESH_ALL : SKIN_REFRESH_NON_STATIC); - } - } - } - update = false; - } - - if (exit) { - audio_pause(); - update_non_static(); - if (bookmark) - bookmark_autobookmark(true); - audio_stop(); -#ifdef AB_REPEAT_ENABLE - ab_reset_markers(); -#endif - gwps_leave_wps(); -#ifdef HAVE_RECORDING - if (button == ACTION_WPS_REC) - return GO_TO_RECSCREEN; -#endif - if (global_settings.browse_current) - return GO_TO_PREVIOUS_BROWSER; - return GO_TO_PREVIOUS; - } - - if (button && !IS_SYSEVENT(button) ) - storage_spin(); } return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */ } +struct wps_state *get_wps_state(void) +{ + return &wps_state; +} + /* this is called from the playback thread so NO DRAWING! */ static void track_info_callback(unsigned short id, void *param) { - struct wps_state *state = skin_get_global_state(); + struct wps_state *state = get_wps_state(); if (id == PLAYBACK_EVENT_TRACK_CHANGE || id == PLAYBACK_EVENT_CUR_TRACK_READY) { @@ -1040,14 +1098,13 @@ static void track_info_callback(unsigned short id, void *param) state->id3 = audio_current_track(); } #endif - skin_get_global_state()->nid3 = audio_next_track(); + state->nid3 = audio_next_track(); skin_request_full_update(WPS); } static void wps_state_init(void) { - struct wps_state *state = skin_get_global_state(); - state->ff_rewind = false; + struct wps_state *state = get_wps_state(); state->paused = false; if(audio_status() & AUDIO_STATUS_PLAY) { @@ -1071,16 +1128,3 @@ static void wps_state_init(void) add_event(PLAYBACK_EVENT_TRACK_SKIP, track_info_callback); #endif } - - -#ifdef IPOD_ACCESSORY_PROTOCOL -bool is_wps_fading(void) -{ - return skin_get_global_state()->is_fading; -} - -int wps_get_ff_rewind_count(void) -{ - return skin_get_global_state()->ff_rewind_count; -} -#endif diff --git a/apps/gui/wps.h b/apps/gui/wps.h index a463b0e9bb..001c112a4d 100644 --- a/apps/gui/wps.h +++ b/apps/gui/wps.h @@ -20,35 +20,30 @@ ****************************************************************************/ #ifndef _WPS_H_ #define _WPS_H_ + #include <stdbool.h> -#include "config.h" -#include "screen_access.h" - -long gui_wps_show(void); - -/* wrapper for the wps to load the skin (.wps/.rwps) files */ -void wps_data_load(enum screen_type, const char *, bool); -void gui_sync_wps_init(void) INIT_ATTR; +struct mp3entry; -/* fade (if enabled) and pause the audio, optionally rewind a little */ -void pause_action(bool may_fade, bool updatewps); -void unpause_action(bool may_fade, bool updatewps); +/* Please don't add anything else to here... */ +struct wps_state +{ + struct mp3entry *id3; + struct mp3entry *nid3; + int ff_rewind_count; + bool paused; +}; -/* fades the volume, e.g. on pause or stop */ -void fade(bool fade_in, bool updatewps); +long gui_wps_show(void); -bool ffwd_rew(int button); +/* fade (if enabled) and pause the audio, optionally rewind a little */ +void pause_action(bool updatewps); +void unpause_action(bool updatewps); void wps_do_playpause(bool updatewps); -#ifdef IPOD_ACCESSORY_PROTOCOL -/* whether the wps is fading the volume due to pausing/stopping */ -bool is_wps_fading(void); -/* return length of the current ff or rewin action, IAP needs this */ -int wps_get_ff_rewind_count(void); -#endif /* IPOD_ACCESSORY_PROTOCOL */ +struct wps_state *get_wps_state(void); /* in milliseconds */ -#define DEFAULT_SKIP_TRESH 3000l +#define DEFAULT_SKIP_THRESH 3000l #endif /* _WPS_H_ */ diff --git a/apps/gui/yesno.c b/apps/gui/yesno.c index a79b8ae644..c763753cd7 100644 --- a/apps/gui/yesno.c +++ b/apps/gui/yesno.c @@ -253,14 +253,15 @@ enum yesno_res gui_syncyesno_run(const struct text_message * main_message, if(result_displayed) sleep(HZ); + exit: + remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, gui_yesno_ui_update, &yn[0]); + FOR_NB_SCREENS(i) { screens[i].scroll_stop_viewport(yn[i].vp); viewportmanager_theme_undo(i, true); } - exit: - remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, gui_yesno_ui_update, &yn[0]); #ifdef HAVE_TOUCHSCREEN touchscreen_set_mode(old_mode); #endif diff --git a/apps/hosted/android/keyboard.c b/apps/hosted/android/keyboard.c index eda951a7c9..b74f67e782 100644 --- a/apps/hosted/android/keyboard.c +++ b/apps/hosted/android/keyboard.c @@ -100,7 +100,7 @@ int kbd_input(char* text, int buflen, unsigned short *kbd) if (accepted) { utf8_string = e->GetStringUTFChars(env_ptr, new_string, 0); - strlcpy(text, utf8_string, buflen); + strmemccpy(text, utf8_string, buflen); e->ReleaseStringUTFChars(env_ptr, new_string, utf8_string); e->DeleteGlobalRef(env_ptr, new_string); } diff --git a/apps/hosted/android/notification.c b/apps/hosted/android/notification.c index 39c8b07737..a5b60c1013 100644 --- a/apps/hosted/android/notification.c +++ b/apps/hosted/android/notification.c @@ -29,6 +29,7 @@ #include "misc.h" #include "thread.h" #include "debug.h" +#include "audio.h" extern JNIEnv *env_ptr; extern jclass RockboxService_class; diff --git a/apps/iap/iap-core.c b/apps/iap/iap-core.c index 885ba2c188..da04a67311 100644 --- a/apps/iap/iap-core.c +++ b/apps/iap/iap-core.c @@ -19,8 +19,8 @@ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> -#include <string.h> +#include "string-extra.h" #include "panic.h" #include "iap-core.h" #include "iap-lingo.h" @@ -364,6 +364,7 @@ static void iap_thread(void) /* Handle poweroff message */ case SYS_POWEROFF: + case SYS_REBOOT: { iap_shutdown = true; break; @@ -452,7 +453,7 @@ static void iap_malloc(void) return; #ifdef IAP_MALLOC_DYNAMIC - iap_buffer_handle = core_alloc_ex("iap", IAP_MALLOC_SIZE, &iap_buflib_callbacks); + iap_buffer_handle = core_alloc_ex(IAP_MALLOC_SIZE, &iap_buflib_callbacks); if (iap_buffer_handle < 0) panicf("Could not allocate buffer memory"); iap_buffers = core_get_data(iap_buffer_handle); diff --git a/apps/iap/iap-lingo4.c b/apps/iap/iap-lingo4.c index 4ec5c462a1..eb629407f2 100644 --- a/apps/iap/iap-lingo4.c +++ b/apps/iap/iap-lingo4.c @@ -24,6 +24,7 @@ #include "filetree.h" #include "wps.h" #include "playback.h" +#include "string-extra.h" /* * This macro is meant to be used inside an IAP mode message handler. @@ -87,7 +88,7 @@ static void get_playlist_name(unsigned char *dest, } } if (playlist_file != NULL) { - strlcpy(dest, playlist_file->d_name, max_length); + strmemccpy(dest, playlist_file->d_name, max_length); } closedir(dp); } @@ -1218,7 +1219,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf) { memcpy(cur_dbrecord, buf + 3, 5); - int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE)); + int paused = !!(audio_status() & AUDIO_STATUS_PAUSE); uint32_t index; uint32_t trackcount; index = get_u32(&cur_dbrecord[1]); @@ -1430,7 +1431,6 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf) unsigned int number_of_playlists = nbr_total_playlists(); uint32_t trackcount; trackcount = playlist_amount(); - size_t len; if ((buf[3] == 0x05) && ((start_index + read_count ) > trackcount)) { @@ -1465,22 +1465,21 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf) switch(buf[3]) { case 0x05: - len = strlcpy((char *)&data[7], id3.title,64); + strmemccpy((char *)&data[7], id3.title,64); break; case 0x02: - len = strlcpy((char *)&data[7], id3.artist,64); + strmemccpy((char *)&data[7], id3.artist,64); break; case 0x03: - len = strlcpy((char *)&data[7], id3.album,64); + strmemccpy((char *)&data[7], id3.album,64); break; case 0x04: case 0x06: - len = strlcpy((char *)&data[7], "Not Supported",14); + strmemccpy((char *)&data[7], "Not Supported",14); break; } break; } - (void)len; /* Shut up, compiler */ put_u32(&data[3], start_index+counter); iap_send_pkt(data, 7 + strlen(data+7) + 1); yield(); @@ -2003,7 +2002,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf) * */ { - int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE)); + int paused = !!(audio_status() & AUDIO_STATUS_PAUSE); uint32_t index; uint32_t trackcount; index = get_u32(&buf[3]); @@ -2821,7 +2820,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf) * */ { - int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE)); + int paused = !!(audio_status() & AUDIO_STATUS_PAUSE); long tracknum = get_u32(&buf[3]); audio_pause(); @@ -2977,7 +2976,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf) { memcpy(cur_dbrecord, buf + 3, 5); - int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE)); + int paused = !!(audio_status() & AUDIO_STATUS_PAUSE); unsigned int number_of_playlists = nbr_total_playlists(); uint32_t index; uint32_t trackcount; diff --git a/apps/keymaps/keymap-agptekrocker.c b/apps/keymaps/keymap-agptekrocker.c index 4d7139ffd5..5ca834ab51 100644 --- a/apps/keymaps/keymap-agptekrocker.c +++ b/apps/keymaps/keymap-agptekrocker.c @@ -96,7 +96,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_SETTINGS_DEC, BUTTON_VOLDOWN, BUTTON_NONE }, { ACTION_SETTINGS_DECREPEAT, BUTTON_VOLDOWN|BUTTON_REPEAT, BUTTON_NONE }, /* ACTION_SETTINGS_DECBIGSTEP */ - { ACTION_SETTINGS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT }, /* ACTION_SETTINGS_SET, Used by touchscreen targets */ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), }; /* button_context_settings */ @@ -140,15 +139,16 @@ static const struct button_mapping button_context_yesno[] = { }; /* button_context_settings_yesno */ static const struct button_mapping button_context_quickscreen[] = { - { ACTION_QS_TOP, BUTTON_UP|BUTTON_REL, BUTTON_NONE }, - { ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE }, - { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE }, - { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE }, - { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE }, + { ACTION_QS_TOP, BUTTON_UP|BUTTON_REL, BUTTON_NONE }, + { ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE }, + { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE }, + { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE }, + { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT }, + { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT }, LAST_ITEM_IN_LIST }; /* button_context_quickscreen */ @@ -221,7 +221,7 @@ static const struct button_mapping button_context_bmark[] = { /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; diff --git a/apps/keymaps/keymap-c200.c b/apps/keymaps/keymap-c200.c index 2e26f424cc..6ca1702e5f 100644 --- a/apps/keymaps/keymap-c200.c +++ b/apps/keymaps/keymap-c200.c @@ -88,7 +88,6 @@ static const struct button_mapping button_context_wps[] = { static const struct button_mapping button_context_settings[] = { { ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE }, { ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE }, { ACTION_SETTINGS_INCREPEAT,BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, diff --git a/apps/keymaps/keymap-clip.c b/apps/keymaps/keymap-clip.c index 7c8fb69471..494c9a734c 100644 --- a/apps/keymaps/keymap-clip.c +++ b/apps/keymaps/keymap-clip.c @@ -98,7 +98,6 @@ static const struct button_mapping button_context_wps[] = { static const struct button_mapping button_context_settings[] = { { ACTION_STD_OK, BUTTON_HOME|BUTTON_REL, BUTTON_HOME }, { ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE }, { ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE }, { ACTION_SETTINGS_INCREPEAT,BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, @@ -180,7 +179,7 @@ static const struct button_mapping button_context_quickscreen[] = { { ACTION_NONE, BUTTON_LEFT, BUTTON_NONE }, { ACTION_STD_CANCEL, BUTTON_POWER|BUTTON_REL, BUTTON_NONE }, { ACTION_STD_CANCEL, BUTTON_HOME, BUTTON_NONE }, - { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE }, + { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT }, { ACTION_QS_TOP, BUTTON_UP|BUTTON_REL, BUTTON_NONE }, { ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE }, @@ -213,9 +212,11 @@ static const struct button_mapping button_context_pitchscreen[] = { { ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE }, { ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE }, - { ACTION_PS_TOGGLE_MODE, BUTTON_HOME|BUTTON_REL, BUTTON_HOME }, - { ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE }, + { ACTION_PS_TOGGLE_MODE, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT }, + { ACTION_PS_RESET, BUTTON_HOME|BUTTON_REPEAT, BUTTON_HOME }, + { ACTION_PS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT }, { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE }, + { ACTION_PS_EXIT, BUTTON_HOME|BUTTON_REL, BUTTON_HOME }, { ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), @@ -398,7 +399,7 @@ static const struct button_mapping button_context_usb_hid_mode_mouse[] = { /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; diff --git a/apps/keymaps/keymap-creativezv.c b/apps/keymaps/keymap-creativezv.c index f2bcfd80f7..3242e8924f 100644 --- a/apps/keymaps/keymap-creativezv.c +++ b/apps/keymaps/keymap-creativezv.c @@ -134,7 +134,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_LEFT }, { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_RIGHT }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -147,7 +146,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_BACK, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-creativezvm.c b/apps/keymaps/keymap-creativezvm.c index 7a0cb5965a..47b24bf8b3 100644 --- a/apps/keymaps/keymap-creativezvm.c +++ b/apps/keymaps/keymap-creativezvm.c @@ -133,7 +133,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_LEFT }, { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_RIGHT }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -146,7 +145,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_BACK, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-e200.c b/apps/keymaps/keymap-e200.c index 538cd7cb8a..20cc16f2dc 100644 --- a/apps/keymaps/keymap-e200.c +++ b/apps/keymaps/keymap-e200.c @@ -100,7 +100,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP }, { ACTION_STD_NEXT, BUTTON_DOWN|BUTTON_REL, BUTTON_DOWN }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_DOWN }, - { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), }; /* button_context_settings */ diff --git a/apps/keymaps/keymap-erosq.c b/apps/keymaps/keymap-erosq.c index 59b70d24ca..054da96201 100644 --- a/apps/keymaps/keymap-erosq.c +++ b/apps/keymaps/keymap-erosq.c @@ -87,7 +87,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE }, { ACTION_SETTINGS_DEC, BUTTON_SCROLL_BACK, BUTTON_NONE }, { ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), }; /* button_context_settings */ @@ -131,6 +130,7 @@ static const struct button_mapping button_context_quickscreen[] = { { ACTION_QS_DOWN, BUTTON_NEXT|BUTTON_REL, BUTTON_NONE }, { ACTION_QS_DOWN, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE }, + { ACTION_STD_CONTEXT,BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU }, LAST_ITEM_IN_LIST }; /* button_context_quickscreen */ @@ -194,7 +194,7 @@ static const struct button_mapping button_context_bmark[] = { /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; @@ -212,7 +212,8 @@ const struct button_mapping* get_context_mapping(int context) return button_context_list; case CONTEXT_SETTINGS: - case CONTEXT_SETTINGS_EQ: + case CONTEXT_SETTINGS_EQ: + case CONTEXT_SETTINGS_COLOURCHOOSER: return button_context_settings; case CONTEXT_SETTINGS_TIME: diff --git a/apps/keymaps/keymap-fiiom3k.c b/apps/keymaps/keymap-fiiom3k.c index 4cd2691d33..01fe3a689c 100644 --- a/apps/keymaps/keymap-fiiom3k.c +++ b/apps/keymaps/keymap-fiiom3k.c @@ -40,39 +40,41 @@ static const struct button_mapping button_context_standard[] = { {ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT}, {ACTION_STD_CANCEL, BUTTON_BACK|BUTTON_REL, BUTTON_BACK}, {ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT}, - {ACTION_STD_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU}, - {ACTION_STD_MENU, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK}, - {ACTION_STD_QUICKSCREEN, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU}, + {ACTION_STD_QUICKSCREEN, BUTTON_MENU|BUTTON_REL, BUTTON_MENU}, + {ACTION_STD_MENU, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU}, {ACTION_STD_KEYLOCK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER}, - {ACTION_STD_HOTKEY, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY}, LAST_ITEM_IN_LIST }; /* button_context_standard */ static const struct button_mapping button_context_wps[] = { {ACTION_WPS_PLAY, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY}, - {ACTION_WPS_PLAY, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT}, + {ACTION_WPS_VIEW_PLAYLIST, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT}, {ACTION_WPS_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER}, {ACTION_WPS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE}, {ACTION_WPS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE}, {ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_WPS_VOLUP, BUTTON_SCROLL_BACK, BUTTON_NONE}, + {ACTION_WPS_VOLUP, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_WPS_VOLDOWN, BUTTON_SCROLL_FWD, BUTTON_NONE}, + {ACTION_WPS_VOLDOWN, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_WPS_VOLUP, BUTTON_UP|BUTTON_REL, BUTTON_UP}, + {ACTION_WPS_VOLDOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_DOWN}, {ACTION_WPS_SKIPNEXT, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT}, {ACTION_WPS_SKIPPREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT}, {ACTION_WPS_SEEKFWD, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_WPS_STOPSEEK, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT|BUTTON_REPEAT}, {ACTION_WPS_SEEKBACK, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_WPS_STOPSEEK, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT|BUTTON_REPEAT}, - {ACTION_WPS_BROWSE, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK}, - {ACTION_WPS_MENU, BUTTON_BACK|BUTTON_REL, BUTTON_BACK}, - {ACTION_WPS_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU}, - {ACTION_WPS_QUICKSCREEN, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU}, + {ACTION_WPS_BROWSE, BUTTON_BACK|BUTTON_REL, BUTTON_BACK}, + {ACTION_WPS_QUICKSCREEN, BUTTON_MENU|BUTTON_REL, BUTTON_MENU}, + {ACTION_WPS_MENU, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU}, {ACTION_STD_KEYLOCK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER}, {ACTION_WPS_HOTKEY, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY}, - {ACTION_WPS_VIEW_PLAYLIST, BUTTON_SCROLL_FWD, BUTTON_NONE}, - {ACTION_WPS_VIEW_PLAYLIST, BUTTON_SCROLL_BACK, BUTTON_NONE}, + {ACTION_WPS_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT}, {ACTION_WPS_ABSETA_PREVDIR, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP}, {ACTION_WPS_ABSETB_NEXTDIR, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_DOWN}, - {ACTION_WPS_ABRESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT}, + {ACTION_WPS_ABRESET, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK}, LAST_ITEM_IN_LIST }; /* button_context_wps */ @@ -87,11 +89,20 @@ static const struct button_mapping button_context_wps_locked[] = { }; /* button_context_wps_locked */ static const struct button_mapping button_context_tree[] = { - {ACTION_TREE_STOP, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY}, - {ACTION_TREE_WPS, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK}, + {ACTION_TREE_WPS, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY }, + {ACTION_TREE_HOTKEY, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY}, + {ACTION_TREE_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER}, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST) }; /* button_context_tree */ + +static const struct button_mapping button_context_tree_scroll_lr[] = { + { ACTION_TREE_ROOT_INIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU }, + { ACTION_TREE_PGLEFT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_TREE_PGRIGHT, BUTTON_BACK|BUTTON_REPEAT, BUTTON_NONE }, + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM|CONTEXT_TREE), +}; /* button_context_tree_scroll_lr */ + static const struct button_mapping button_context_bmark[] = { {ACTION_BMS_DELETE, BUTTON_PLAY, BUTTON_NONE}, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_TREE), @@ -120,7 +131,6 @@ static const struct button_mapping button_context_settings[] = { {ACTION_SETTINGS_DEC, BUTTON_SCROLL_FWD, BUTTON_NONE}, {ACTION_SETTINGS_DECREPEAT, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE}, - {ACTION_SETTINGS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT}, {ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE}, {ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE}, @@ -128,6 +138,16 @@ static const struct button_mapping button_context_settings[] = { LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ +static const struct button_mapping button_context_settings_rectrigger[] = { + {ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE}, + {ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE}, + {ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE}, + {ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE}, + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) +}; /* button_context_settings_rectrigger */ + static const struct button_mapping button_context_settings_eq[] = { {ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE}, {ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, @@ -140,14 +160,26 @@ static const struct button_mapping button_context_settings_eq[] = { static const struct button_mapping button_context_quickscreen[] = { {ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE}, + {ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_QS_TOP, BUTTON_SCROLL_BACK, BUTTON_NONE}, + {ACTION_QS_TOP, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_QS_DOWN, BUTTON_DOWN, BUTTON_NONE}, + {ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_QS_DOWN, BUTTON_SCROLL_FWD, BUTTON_NONE}, + {ACTION_QS_DOWN, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_QS_LEFT, BUTTON_LEFT, BUTTON_NONE}, + {ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE}, + {ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_QS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE}, + {ACTION_QS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE}, - {ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE}, + {ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT}, + {ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT}, {ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE}, {ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_MENU, BUTTON_NONE}, LAST_ITEM_IN_LIST }; /* button_context_quickscreen */ @@ -172,7 +204,7 @@ static const struct button_mapping button_context_pitchscreen[] = { static const struct button_mapping button_context_yesnoscreen[] = { {ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE}, - {ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE}, {ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE}, {ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE}, {ACTION_STD_CANCEL, BUTTON_RIGHT, BUTTON_NONE}, @@ -182,12 +214,43 @@ static const struct button_mapping button_context_yesnoscreen[] = { LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_yesnoscreen */ +static const struct button_mapping button_context_recscreen[] = { + {ACTION_REC_PAUSE, BUTTON_SELECT, BUTTON_NONE}, + {ACTION_REC_PAUSE, BUTTON_PLAY, BUTTON_NONE}, + {ACTION_REC_NEWFILE, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT}, + {ACTION_REC_NEWFILE, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY}, + {ACTION_STD_MENU, BUTTON_MENU, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE}, + {ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE}, + {ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE}, + {ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE}, + {ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_STD_PREV, BUTTON_SCROLL_BACK, BUTTON_NONE}, + {ACTION_STD_PREVREPEAT, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_STD_NEXT, BUTTON_SCROLL_FWD, BUTTON_NONE}, + {ACTION_STD_NEXTREPEAT, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_SETTINGS_INC, BUTTON_VOL_UP, BUTTON_NONE}, + {ACTION_SETTINGS_INCREPEAT, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_SETTINGS_DEC, BUTTON_VOL_DOWN, BUTTON_NONE}, + {ACTION_SETTINGS_DECREPEAT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE}, + {ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE}, + {ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, + LAST_ITEM_IN_LIST +}; /* button_context_recscreen */ + static const struct button_mapping button_context_keyboard[] = { {ACTION_KBD_UP, BUTTON_UP, BUTTON_NONE}, {ACTION_KBD_DOWN, BUTTON_DOWN, BUTTON_NONE}, {ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE}, {ACTION_KBD_RIGHT, BUTTON_RIGHT, BUTTON_NONE}, {ACTION_KBD_UP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_KBD_UP, BUTTON_SCROLL_BACK, BUTTON_NONE}, + {ACTION_KBD_UP, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE}, + {ACTION_KBD_DOWN, BUTTON_SCROLL_FWD, BUTTON_NONE}, + {ACTION_KBD_DOWN, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_KBD_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_KBD_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, @@ -261,24 +324,21 @@ static const struct button_mapping button_context_usb_hid_mode_browser[] = { const struct button_mapping* get_context_mapping(int context) { - switch (context) - { - case CONTEXT_WPS|CONTEXT_LOCKED: - return button_context_wps_locked; - default: - context &= ~CONTEXT_LOCKED; - break; - } - - switch (context) + switch (context & ~CONTEXT_LOCKED) { default: case CONTEXT_STD: return button_context_standard; case CONTEXT_WPS: + if (context & CONTEXT_LOCKED) + return button_context_wps_locked; return button_context_wps; case CONTEXT_TREE: case CONTEXT_MAINMENU: + if (global_settings.hold_lr_for_scroll_in_list) + return button_context_tree_scroll_lr; + /* else fall through to CUSTOM|CONTEXT_TREE */ + case CONTEXT_CUSTOM|CONTEXT_TREE: return button_context_tree; case CONTEXT_BOOKMARKSCREEN: return button_context_bmark; @@ -286,8 +346,9 @@ const struct button_mapping* get_context_mapping(int context) return button_context_list; case CONTEXT_SETTINGS: case CONTEXT_SETTINGS_TIME: - case CONTEXT_SETTINGS_RECTRIGGER: return button_context_settings; + case CONTEXT_SETTINGS_RECTRIGGER: + return button_context_settings_rectrigger; case CONTEXT_SETTINGS_EQ: case CONTEXT_SETTINGS_COLOURCHOOSER: return button_context_settings_eq; @@ -297,6 +358,8 @@ const struct button_mapping* get_context_mapping(int context) return button_context_pitchscreen; case CONTEXT_YESNOSCREEN: return button_context_yesnoscreen; + case CONTEXT_RECSCREEN: + return button_context_recscreen; case CONTEXT_KEYBOARD: return button_context_keyboard; case CONTEXT_USB_HID: diff --git a/apps/keymaps/keymap-fiiom3klinux.c b/apps/keymaps/keymap-fiiom3klinux.c index 7769e2da04..79a7687501 100644 --- a/apps/keymaps/keymap-fiiom3klinux.c +++ b/apps/keymaps/keymap-fiiom3klinux.c @@ -150,7 +150,6 @@ static const struct button_mapping button_context_quickscreen[] = { /** Settings - General Mappings **/ static const struct button_mapping button_context_settings[] = { - { ACTION_SETTINGS_RESET, BUTTON_POWER|BUTTON_REL, BUTTON_POWER }, { ACTION_STD_PREV, BUTTON_PREV, BUTTON_NONE }, { ACTION_STD_PREVREPEAT, BUTTON_PREV|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_NEXT, BUTTON_NONE }, @@ -193,7 +192,7 @@ static const struct button_mapping button_context_yesnoscreen[] = { /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_LIST: return button_context_list; diff --git a/apps/keymaps/keymap-fuze.c b/apps/keymaps/keymap-fuze.c index c5538a6c49..ef0bed1099 100644 --- a/apps/keymaps/keymap-fuze.c +++ b/apps/keymaps/keymap-fuze.c @@ -99,7 +99,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP }, { ACTION_STD_NEXT, BUTTON_DOWN|BUTTON_REL, BUTTON_DOWN }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_DOWN }, - { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), }; /* button_context_settings */ diff --git a/apps/keymaps/keymap-fuzeplus.c b/apps/keymaps/keymap-fuzeplus.c index 0933916b4a..052f11085e 100644 --- a/apps/keymaps/keymap-fuzeplus.c +++ b/apps/keymaps/keymap-fuzeplus.c @@ -128,7 +128,8 @@ static const struct button_mapping button_context_keyboard[] = { }; /* button_context_keyboard */ static const struct button_mapping button_context_quickscreen[] = { - { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE }, + { ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT }, + { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT }, { ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE }, { ACTION_STD_CANCEL, BUTTON_PLAYPAUSE, BUTTON_NONE }, { ACTION_STD_CANCEL, BUTTON_BOTTOMRIGHT, BUTTON_NONE }, @@ -225,7 +226,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_SETTINGS_DECBIGSTEP, BUTTON_BOTTOMLEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT }, - { ACTION_SETTINGS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE }, @@ -407,7 +407,7 @@ static const struct button_mapping button_context_usb_hid_mode_mouse[] = { /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; diff --git a/apps/keymaps/keymap-gigabeat-s.c b/apps/keymaps/keymap-gigabeat-s.c index 4cd76184ff..35cecb7476 100644 --- a/apps/keymaps/keymap-gigabeat-s.c +++ b/apps/keymaps/keymap-gigabeat-s.c @@ -167,7 +167,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_BACK, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -181,7 +180,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_BACK, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-gigabeat.c b/apps/keymaps/keymap-gigabeat.c index 31fd0eaf0d..913763af18 100644 --- a/apps/keymaps/keymap-gigabeat.c +++ b/apps/keymaps/keymap-gigabeat.c @@ -160,7 +160,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_A, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -174,7 +173,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_A, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-h10.c b/apps/keymaps/keymap-h10.c index 825590e6f4..7df2b61d05 100644 --- a/apps/keymaps/keymap-h10.c +++ b/apps/keymaps/keymap-h10.c @@ -124,7 +124,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_FF|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_REW, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_REW|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), }; /* button_context_settings */ diff --git a/apps/keymaps/keymap-h1x0_h3x0.c b/apps/keymaps/keymap-h1x0_h3x0.c index d4f098ce1e..422af7502c 100644 --- a/apps/keymaps/keymap-h1x0_h3x0.c +++ b/apps/keymaps/keymap-h1x0_h3x0.c @@ -145,7 +145,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_ON, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -159,7 +158,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_ON, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-hdd1630.c b/apps/keymaps/keymap-hdd1630.c index ca55f7fc26..7afcaeec6d 100644 --- a/apps/keymaps/keymap-hdd1630.c +++ b/apps/keymaps/keymap-hdd1630.c @@ -160,7 +160,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_VIEW, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -174,7 +173,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_VIEW, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-hdd6330.c b/apps/keymaps/keymap-hdd6330.c index ed197af41c..18ef2b7c8f 100644 --- a/apps/keymaps/keymap-hdd6330.c +++ b/apps/keymaps/keymap-hdd6330.c @@ -142,7 +142,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_VIEW, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -156,7 +155,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_VIEW, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-hm60x.c b/apps/keymaps/keymap-hm60x.c index 2e1f3dd516..fa8783de6d 100644 --- a/apps/keymaps/keymap-hm60x.c +++ b/apps/keymaps/keymap-hm60x.c @@ -128,7 +128,6 @@ static const struct button_mapping button_context_quickscreen[] = { /** Settings - General Mappings **/ static const struct button_mapping button_context_settings[] = { - { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE }, { ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE }, { ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE }, diff --git a/apps/keymaps/keymap-hm801.c b/apps/keymaps/keymap-hm801.c index 1f0a49c90c..210f521178 100644 --- a/apps/keymaps/keymap-hm801.c +++ b/apps/keymaps/keymap-hm801.c @@ -154,7 +154,6 @@ static const struct button_mapping button_context_quickscreen[] = { /** Settings - General Mappings **/ static const struct button_mapping button_context_settings[] = { { ACTION_STD_CANCEL, BUTTON_POWER|BUTTON_REL, BUTTON_POWER}, - { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE }, { ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE }, { ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE }, diff --git a/apps/keymaps/keymap-ihifi.c b/apps/keymaps/keymap-ihifi.c index 147c2f5f62..0c28f812f1 100644 --- a/apps/keymaps/keymap-ihifi.c +++ b/apps/keymaps/keymap-ihifi.c @@ -127,7 +127,6 @@ static const struct button_mapping button_context_quickscreen[] = { /** Settings - General Mappings **/ static const struct button_mapping button_context_settings[] = { - { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE }, { ACTION_SETTINGS_INC, BUTTON_BWD, BUTTON_NONE }, { ACTION_SETTINGS_INCREPEAT, BUTTON_BWD|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_SETTINGS_DEC, BUTTON_FWD, BUTTON_NONE }, diff --git a/apps/keymaps/keymap-ihifi770.c b/apps/keymaps/keymap-ihifi770.c index c5671bd650..a5f37cdb7d 100644 --- a/apps/keymaps/keymap-ihifi770.c +++ b/apps/keymaps/keymap-ihifi770.c @@ -139,7 +139,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_NEXT, BUTTON_NEXT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_OK, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ diff --git a/apps/keymaps/keymap-ihifi800.c b/apps/keymaps/keymap-ihifi800.c index b56bd018eb..4dfceff965 100644 --- a/apps/keymaps/keymap-ihifi800.c +++ b/apps/keymaps/keymap-ihifi800.c @@ -143,7 +143,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_NEXTREPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_OK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER }, { ACTION_STD_OK, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ diff --git a/apps/keymaps/keymap-ipod.c b/apps/keymaps/keymap-ipod.c index e3a17fffba..6d06bd0244 100644 --- a/apps/keymaps/keymap-ipod.c +++ b/apps/keymaps/keymap-ipod.c @@ -132,7 +132,7 @@ static const struct button_mapping button_context_quickscreen[] = { { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE }, + { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT }, { ACTION_QS_VOLDOWN, BUTTON_SCROLL_BACK, BUTTON_NONE }, { ACTION_QS_VOLDOWN, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_QS_VOLUP, BUTTON_SCROLL_FWD, BUTTON_NONE }, diff --git a/apps/keymaps/keymap-m200.c b/apps/keymaps/keymap-m200.c index f055d648ef..be202d21e6 100644 --- a/apps/keymaps/keymap-m200.c +++ b/apps/keymaps/keymap-m200.c @@ -89,7 +89,6 @@ static const struct button_mapping button_context_wps[] = { static const struct button_mapping button_context_settings[] = { { ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_SELECT, BUTTON_NONE }, { ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE }, { ACTION_SETTINGS_INCREPEAT,BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, diff --git a/apps/keymaps/keymap-ma.c b/apps/keymaps/keymap-ma.c index 28220d9f50..aca006d071 100644 --- a/apps/keymaps/keymap-ma.c +++ b/apps/keymaps/keymap-ma.c @@ -135,7 +135,6 @@ static const struct button_mapping button_context_quickscreen[] = { /** Settings - General Mappings **/ static const struct button_mapping button_context_settings[] = { - { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE }, { ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE }, { ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE }, @@ -189,7 +188,7 @@ static const struct button_mapping button_context_yesnoscreen[] = { /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { /* anything that uses button_context_standard */ case CONTEXT_LIST: diff --git a/apps/keymaps/keymap-meizu-m6sl.c b/apps/keymaps/keymap-meizu-m6sl.c index 35e44da905..31dbc88432 100644 --- a/apps/keymaps/keymap-meizu-m6sl.c +++ b/apps/keymaps/keymap-meizu-m6sl.c @@ -147,7 +147,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -161,7 +160,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-mini2440.c b/apps/keymaps/keymap-mini2440.c index 70f63aef72..3fe0698fa0 100644 --- a/apps/keymaps/keymap-mini2440.c +++ b/apps/keymaps/keymap-mini2440.c @@ -162,7 +162,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_A, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -176,7 +175,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_A, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-mr100.c b/apps/keymaps/keymap-mr100.c index d551d434be..0a677efc00 100644 --- a/apps/keymaps/keymap-mr100.c +++ b/apps/keymaps/keymap-mr100.c @@ -153,7 +153,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_DISPLAY, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -167,7 +166,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_DISPLAY, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-mr500.c b/apps/keymaps/keymap-mr500.c index b35c9c1dfc..41e6d21827 100644 --- a/apps/keymaps/keymap-mr500.c +++ b/apps/keymaps/keymap-mr500.c @@ -118,7 +118,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_PREVREPEAT, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_RC_FF, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_RC_MODE, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -132,7 +131,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_STD_PREVREPEAT, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_RC_DOWN, BUTTON_NONE }, { ACTION_STD_NEXTREPEAT, BUTTON_RC_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_RC_MODE, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settingsgraphical */ diff --git a/apps/keymaps/keymap-nwz.c b/apps/keymaps/keymap-nwz.c index 66bdd1b0db..36675566d3 100644 --- a/apps/keymaps/keymap-nwz.c +++ b/apps/keymaps/keymap-nwz.c @@ -112,7 +112,8 @@ static const struct button_mapping button_context_keyboard[] = { }; /* button_context_keyboard */ static const struct button_mapping button_context_quickscreen[] = { - { ACTION_STD_CANCEL, BUTTON_PLAY, BUTTON_NONE }, + { ACTION_STD_CONTEXT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY }, + { ACTION_STD_CANCEL, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY }, { ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE }, { ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE }, { ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE }, @@ -199,7 +200,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_OK, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ @@ -357,7 +357,7 @@ static const struct button_mapping button_context_usb_hid_mode_mouse[] = { /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; diff --git a/apps/keymaps/keymap-nwza860.c b/apps/keymaps/keymap-nwza860.c index f1a3bb6f85..899796552e 100644 --- a/apps/keymaps/keymap-nwza860.c +++ b/apps/keymaps/keymap-nwza860.c @@ -74,7 +74,8 @@ static const struct button_mapping button_context_keyboard[] = { }; /* button_context_keyboard */ static const struct button_mapping button_context_quickscreen[] = { - { ACTION_STD_CANCEL, BUTTON_PLAY, BUTTON_NONE }, + { ACTION_STD_CONTEXT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY }, + { ACTION_STD_CANCEL, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY }, { ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE }, LAST_ITEM_IN_LIST @@ -125,8 +126,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_SETTINGS_DEC, BUTTON_VOL_DOWN, BUTTON_NONE }, { ACTION_SETTINGS_DECREPEAT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE }, - LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ diff --git a/apps/keymaps/keymap-ondavx777.c b/apps/keymaps/keymap-ondavx777.c index ed23350666..c4b68f1ef2 100644 --- a/apps/keymaps/keymap-ondavx777.c +++ b/apps/keymaps/keymap-ondavx777.c @@ -127,7 +127,7 @@ static const struct button_mapping button_context_usb_hid[] = { const struct button_mapping* target_get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; diff --git a/apps/keymaps/keymap-rk27xx-generic.c b/apps/keymaps/keymap-rk27xx-generic.c index d837c608fc..4c07384810 100644 --- a/apps/keymaps/keymap-rk27xx-generic.c +++ b/apps/keymaps/keymap-rk27xx-generic.c @@ -164,7 +164,7 @@ static const struct button_mapping button_context_usb_hid_mode_mouse[] = { /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; diff --git a/apps/keymaps/keymap-sa9200.c b/apps/keymaps/keymap-sa9200.c index d29557e222..2dcf301f0f 100644 --- a/apps/keymaps/keymap-sa9200.c +++ b/apps/keymaps/keymap-sa9200.c @@ -155,7 +155,6 @@ static const struct button_mapping button_context_listtree_scroll_without_combo[ static const struct button_mapping button_context_settings[] = { { ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY, BUTTON_NONE }, { ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE }, { ACTION_SETTINGS_INCREPEAT,BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, diff --git a/apps/keymaps/keymap-shanlingq1.c b/apps/keymaps/keymap-shanlingq1.c index 4caaa36fd2..f8fefe5ff3 100644 --- a/apps/keymaps/keymap-shanlingq1.c +++ b/apps/keymaps/keymap-shanlingq1.c @@ -69,7 +69,7 @@ static const struct button_mapping button_context_yesno[] = { const struct button_mapping* target_get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { default: case CONTEXT_STD: diff --git a/apps/keymaps/keymap-touchscreen.c b/apps/keymaps/keymap-touchscreen.c index c70aea2651..07710be78d 100644 --- a/apps/keymaps/keymap-touchscreen.c +++ b/apps/keymaps/keymap-touchscreen.c @@ -146,7 +146,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_SETTINGS_INCREPEAT, BUTTON_TOPMIDDLE|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_SETTINGS_DEC, BUTTON_BOTTOMMIDDLE, BUTTON_NONE }, { ACTION_SETTINGS_DECREPEAT, BUTTON_BOTTOMMIDDLE|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_CENTER, BUTTON_NONE }, { ACTION_STD_PREV, BUTTON_MIDLEFT, BUTTON_NONE }, { ACTION_STD_PREVREPEAT, BUTTON_MIDLEFT|BUTTON_REPEAT, BUTTON_NONE }, @@ -164,7 +163,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_SETTINGS_INCREPEAT, BUTTON_MIDRIGHT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_SETTINGS_DEC, BUTTON_MIDLEFT, BUTTON_NONE }, { ACTION_SETTINGS_DECREPEAT, BUTTON_MIDLEFT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_CENTER, BUTTON_NONE }, { ACTION_STD_CANCEL, BUTTON_TOPLEFT, BUTTON_NONE }, @@ -385,7 +383,7 @@ const struct button_mapping* get_context_mapping(int context) return target_get_context_mapping(context & ~CONTEXT_CUSTOM2); } - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; diff --git a/apps/keymaps/keymap-xduoox20.c b/apps/keymaps/keymap-xduoox20.c index c49410cd03..fa4076e27d 100644 --- a/apps/keymaps/keymap-xduoox20.c +++ b/apps/keymaps/keymap-xduoox20.c @@ -145,7 +145,6 @@ static const struct button_mapping button_context_quickscreen[] = { /** Settings - General Mappings **/ static const struct button_mapping button_context_settings[] = { - { ACTION_SETTINGS_RESET, BUTTON_POWER|BUTTON_REL, BUTTON_POWER }, { ACTION_STD_PREV, BUTTON_NEXT, BUTTON_NONE }, { ACTION_STD_PREVREPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_PREV, BUTTON_NONE }, @@ -185,7 +184,7 @@ static const struct button_mapping button_context_yesnoscreen[] = { /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_LIST: return button_context_list; diff --git a/apps/keymaps/keymap-xduoox3.c b/apps/keymaps/keymap-xduoox3.c index 124ca06c9d..bf588411eb 100644 --- a/apps/keymaps/keymap-xduoox3.c +++ b/apps/keymaps/keymap-xduoox3.c @@ -86,10 +86,12 @@ static const struct button_mapping button_context_wps[] = { { ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_WPS_BROWSE, BUTTON_HOME|BUTTON_REL, BUTTON_HOME }, { ACTION_WPS_CONTEXT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY }, - { ACTION_WPS_MENU, BUTTON_OPTION|BUTTON_REL, BUTTON_OPTION }, - { ACTION_WPS_QUICKSCREEN, BUTTON_OPTION|BUTTON_REPEAT, BUTTON_OPTION }, { ACTION_WPS_HOTKEY, BUTTON_HOME|BUTTON_REPEAT, BUTTON_HOME }, + { ACTION_WPS_MENU, BUTTON_OPTION|BUTTON_REL, BUTTON_OPTION }, + { ACTION_WPS_QUICKSCREEN, BUTTON_OPTION|BUTTON_REPEAT, BUTTON_OPTION }, + { ACTION_WPS_PITCHSCREEN, BUTTON_OPTION|BUTTON_HOME|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_WPS_ABSETB_NEXTDIR, BUTTON_PWRALT|BUTTON_NEXT, BUTTON_POWER }, { ACTION_WPS_ABSETA_PREVDIR, BUTTON_PWRALT|BUTTON_PREV, BUTTON_POWER }, { ACTION_WPS_ABRESET, BUTTON_PWRALT|BUTTON_PLAY, BUTTON_POWER }, @@ -172,7 +174,6 @@ static const struct button_mapping button_context_quickscreen[] = { /** Settings - General Mappings **/ static const struct button_mapping button_context_settings[] = { - { ACTION_SETTINGS_RESET, BUTTON_POWER|BUTTON_REL, BUTTON_POWER }, { ACTION_STD_PREV, BUTTON_PREV, BUTTON_NONE }, { ACTION_STD_PREVREPEAT, BUTTON_PREV|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_NEXT, BUTTON_NONE }, diff --git a/apps/keymaps/keymap-xduoox3ii.c b/apps/keymaps/keymap-xduoox3ii.c index c8be8ce13e..c8724ec1f0 100644 --- a/apps/keymaps/keymap-xduoox3ii.c +++ b/apps/keymaps/keymap-xduoox3ii.c @@ -145,7 +145,6 @@ static const struct button_mapping button_context_quickscreen[] = { /** Settings - General Mappings **/ static const struct button_mapping button_context_settings[] = { - { ACTION_SETTINGS_RESET, BUTTON_POWER|BUTTON_REL, BUTTON_POWER }, { ACTION_STD_PREV, BUTTON_NEXT, BUTTON_NONE }, { ACTION_STD_PREVREPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_NEXT, BUTTON_PREV, BUTTON_NONE }, @@ -185,7 +184,7 @@ static const struct button_mapping button_context_yesnoscreen[] = { /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_LIST: return button_context_list; diff --git a/apps/keymaps/keymap-yh8xx_yh9xx.c b/apps/keymaps/keymap-yh8xx_yh9xx.c index 32623e1a72..9332c9fe98 100644 --- a/apps/keymaps/keymap-yh8xx_yh9xx.c +++ b/apps/keymaps/keymap-yh8xx_yh9xx.c @@ -201,7 +201,6 @@ static const struct button_mapping button_context_settings_right_is_inc[] = { { ACTION_SETTINGS_INCREPEAT,BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE }, { ACTION_SETTINGS_DECREPEAT,BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_SETTINGS_RESET, BUTTON_REW|BUTTON_REL, BUTTON_REW }, { ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE }, { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, diff --git a/apps/keymaps/keymap-ypr0.c b/apps/keymaps/keymap-ypr0.c index 2d6be5b7ec..6e493ddf61 100644 --- a/apps/keymaps/keymap-ypr0.c +++ b/apps/keymaps/keymap-ypr0.c @@ -255,7 +255,7 @@ static const struct button_mapping button_context_radio[] = { const struct button_mapping* get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; diff --git a/apps/keymaps/keymap-ypr1.c b/apps/keymaps/keymap-ypr1.c index d47047912b..67f42f07c6 100644 --- a/apps/keymaps/keymap-ypr1.c +++ b/apps/keymaps/keymap-ypr1.c @@ -132,7 +132,7 @@ static const struct button_mapping button_context_radio[] = { const struct button_mapping* target_get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; diff --git a/apps/keymaps/keymap-zen.c b/apps/keymaps/keymap-zen.c index ab0f32edb4..04b65b3980 100644 --- a/apps/keymaps/keymap-zen.c +++ b/apps/keymaps/keymap-zen.c @@ -145,7 +145,8 @@ static const struct button_mapping button_context_keyboard[] = { }; /* button_context_keyboard */ static const struct button_mapping button_context_quickscreen[] = { - { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE }, + { ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT }, + { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT }, { ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE }, { ACTION_STD_CANCEL, BUTTON_PLAYPAUSE, BUTTON_NONE }, { ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE }, @@ -235,7 +236,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT }, - { ACTION_SETTINGS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ diff --git a/apps/keymaps/keymap-zenxfi2.c b/apps/keymaps/keymap-zenxfi2.c index c98a4a8405..ba25592246 100644 --- a/apps/keymaps/keymap-zenxfi2.c +++ b/apps/keymaps/keymap-zenxfi2.c @@ -137,7 +137,7 @@ static const struct button_mapping button_context_radio[] = { const struct button_mapping* target_get_context_mapping(int context) { - switch (context) + switch (context & ~CONTEXT_LOCKED) { case CONTEXT_STD: return button_context_standard; diff --git a/apps/keymaps/keymap-zenxfi3.c b/apps/keymaps/keymap-zenxfi3.c index 191b36a1ca..d19a407b78 100644 --- a/apps/keymaps/keymap-zenxfi3.c +++ b/apps/keymaps/keymap-zenxfi3.c @@ -163,7 +163,6 @@ static const struct button_mapping button_context_settings[] = { { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, { ACTION_STD_OK, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY }, - { ACTION_SETTINGS_RESET, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE }, LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_settings */ diff --git a/apps/lang/arabic.lang b/apps/lang/arabic.lang index ecf8f8fb1f..c9b295956b 100644 --- a/apps/lang/arabic.lang +++ b/apps/lang/arabic.lang @@ -14,6 +14,8 @@ # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY # KIND, either express or implied. # +# LANGUAGE_IS_RTL +# # Arabic language file, translated by: # - Mohamed Tarek # - Raafat Akkad @@ -764,20 +766,17 @@ </voice> </phrase> <phrase> - id: LANG_RECORDING_FORMAT - desc: audio format item in recording menu + id: LANG_FORMAT + desc: audio format user: core <source> - *: none - recording: "Format" + *: "Format" </source> <dest> - *: none - recording: "الصيغة" + *: "الصيغة" </dest> <voice> - *: none - recording: "Format" + *: "Format" </voice> </phrase> <phrase> @@ -4043,15 +4042,15 @@ user: core <source> *: none - gigabeatfx,sansafuzeplus: "Touchpad Sensitivity" + fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity" </source> <dest> *: none - gigabeatfx,sansafuzeplus: "حساسية اللمس" + fiiom3k,gigabeatfx,sansafuzeplus: "حساسية اللمس" </dest> <voice> *: none - gigabeatfx,sansafuzeplus: "Touchpad Sensitivity" + fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity" </voice> </phrase> <phrase> diff --git a/apps/lang/basque.lang b/apps/lang/basque.lang index 59ab84b04e..05fae692e8 100644 --- a/apps/lang/basque.lang +++ b/apps/lang/basque.lang @@ -5263,20 +5263,17 @@ </voice> </phrase> <phrase> - id: LANG_RECORDING_FORMAT - desc: audio format item in recording menu + id: LANG_FORMAT + desc: audio format user: core <source> - *: none - recording: "Format" + *: "Format" </source> <dest> - *: none - recording: "Formatua" + *: "Formatua" </dest> <voice> - *: none - recording: "Formatua" + *: "Formatua" </voice> </phrase> <phrase> @@ -10311,15 +10308,15 @@ user: core <source> *: none - gigabeatfx,sansafuzeplus: "Touchpad Sensitivity" + fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity" </source> <dest> *: none - gigabeatfx,sansafuzeplus: "Taktilaren Sentikortasuna" + fiiom3k,gigabeatfx,sansafuzeplus: "Taktilaren Sentikortasuna" </dest> <voice> *: none - gigabeatfx,sansafuzeplus: "Taktilaren Sentikortasuna" + fiiom3k,gigabeatfx,sansafuzeplus: "Taktilaren Sentikortasuna" </voice> </phrase> <phrase> diff --git a/apps/lang/bulgarian.lang b/apps/lang/bulgarian.lang index a66df1ba51..77f2d58361 100644 --- a/apps/lang/bulgarian.lang +++ b/apps/lang/bulgarian.lang @@ -252,7 +252,7 @@ *: "PLAY = Yes" archosplayer: "(PLAY/STOP)" cowond2*: "MENU, or top-right = Yes" - creativezen*: "Select = Yes" + creativezen*: "SELECT = Yes" gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes" iriverh100,iriverh120,iriverh300: "NAVI = Yes" mrobe500: "PLAY, POWER, or top-right = Yes" @@ -262,7 +262,7 @@ *: "PLAY = Да" archosplayer: "(PLAY/STOP)" cowond2*: "MENU или горе ляво = Да" - creativezen*: "Select = Да" + creativezen*: "SELECT = Да" gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Да" iriverh100,iriverh120,iriverh300: "NAVI = Да" mrobe500: "PLAY, POWER или горе дясно = Да" @@ -5268,20 +5268,17 @@ </voice> </phrase> <phrase> - id: LANG_RECORDING_FORMAT - desc: audio format item in recording menu + id: LANG_FORMAT + desc: audio format user: core <source> - *: none - recording: "Format" + *: "Format" </source> <dest> - *: none - recording: "Формат" + *: "Формат" </dest> <voice> - *: none - recording: "Формат" + *: "Формат" </voice> </phrase> <phrase> @@ -10316,15 +10313,15 @@ user: core <source> *: none - gigabeatfx,sansafuzeplus: "Touchpad Sensitivity" + fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity" </source> <dest> *: none - gigabeatfx,sansafuzeplus: "Чувствителност на тъчпада" + fiiom3k,gigabeatfx,sansafuzeplus: "Чувствителност на тъчпада" </dest> <voice> *: none - gigabeatfx,sansafuzeplus: "Чувствителност на тъчпада" + fiiom3k,gigabeatfx,sansafuzeplus: "Чувствителност на тъчпада" </voice> </phrase> <phrase> diff --git a/apps/lang/catala.lang b/apps/lang/catala.lang index 9c29513ca5..38fd6d0644 100644 --- a/apps/lang/catala.lang +++ b/apps/lang/catala.lang @@ -5257,20 +5257,17 @@ </voice> </phrase> <phrase> - id: LANG_RECORDING_FORMAT - desc: audio format item in recording menu + id: LANG_FORMAT + desc: audio format user: core <source> - *: none - recording: "Format" + *: "Format" </source> <dest> - *: none - recording: "Format" + *: "Format" </dest> <voice> - *: none - recording: "Format" + *: "Format" </voice> </phrase> <phrase> @@ -10305,15 +10302,15 @@ user: core <source> *: none - gigabeatfx,sansafuzeplus: "Touchpad Sensitivity" + fiiom3k,gigabeatfx,sansafuzeplus: "Touchpad Sensitivity" </source> <dest> *: none - gigabeatfx,sansafuzeplus: "Sensibilitat del touchpad" + fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilitat del touchpad" </dest> <voice> *: none - gigabeatfx,sansafuzeplus: "Sensibilitat del Touchpad" + fiiom3k,gigabeatfx,sansafuzeplus: "Sensibilitat del Touchpad" </voice> </phrase> <phrase> diff --git a/apps/lang/chinese-simp.lang b/apps/lang/chinese-simp.lang index c1829298be..3d1d0ecaa3 100644 --- a/apps/lang/chinese-simp.lang +++ b/apps/lang/chinese-simp.lang @@ -19,6 +19,35 @@ # - Harry Tu # - Jun Gu # - Purling Nayuki +#------ +<phrase> + id: LANG_LIST_SEPARATOR + desc: line between lines in lists + user: core + <source> + *: "Line Separator" + </source> + <dest> + *: "行分隔线" + </dest> + <voice> + *: "行分隔线" + </voice> +</phrase> +<phrase> + id: LANG_LIST_SEPARATOR_COLOR + desc: line between lines in lists + user: core + <source> + *: "Line Separator Colour" + </source> + <dest> + *: "行分隔线颜色" + </dest> + <voice> + *: "Color of Line Separator" + </voice> +</phrase> <phrase> id: LANG_SET_BOOL_YES desc: bool true representation @@ -27,7 +56,7 @@ *: "Yes" </source> <dest> - *: "√" + *: "是" </dest> <voice> *: "是" @@ -41,7 +70,7 @@ *: "No" </source> <dest> - *: "×" + *: "否" </dest> <voice> *: "否" @@ -104,23 +133,6 @@ </voice> </phrase> <phrase> - id: LANG_CONFIRM_SHUTDOWN - desc: in shutdown screen - user: core - <source> - *: none - soft_shutdown: "Press OFF to shut down" - </source> - <dest> - *: none - soft_shutdown: "按下OFF关机" - </dest> - <voice> - *: none - soft_shutdown: "" - </voice> -</phrase> -<phrase> id: LANG_SHUTTINGDOWN desc: in main menu user: core @@ -128,27 +140,10 @@ *: "Shutting down..." </source> <dest> - *: "关机中..." - </dest> - <voice> - *: "关机中" - </voice> -</phrase> -<phrase> - id: LANG_REMOVE_MMC - desc: before acknowledging usb in case an MMC is inserted (Ondio) - user: core - <source> - *: none - archosondio*: "Please remove inserted MMC" - </source> - <dest> - *: none - archosondio*: "请取出MMC卡" + *: "关闭中..." </dest> <voice> - *: none - archosondio*: "请取出MMC卡" + *: "关闭中" </voice> </phrase> <phrase> @@ -201,7 +196,7 @@ *: "General Settings" </source> <dest> - *: "一般设置" + *: "常规设置" </dest> <voice> *: "一般设置" @@ -262,11 +257,11 @@ </source> <dest> *: none - recording: "录音界面" + recording: "录音" </dest> <voice> *: none - recording: "录音界面" + recording: "录音" </voice> </phrase> <phrase> @@ -284,23 +279,6 @@ </voice> </phrase> <phrase> - id: LANG_SHUTDOWN - desc: in main menu - user: core - <source> - *: none - soft_shutdown: "Shut down" - </source> - <dest> - *: none - soft_shutdown: "关机" - </dest> - <voice> - *: none - soft_shutdown: "关机" - </voice> -</phrase> -<phrase> id: LANG_VOLUME desc: in sound_settings user: core @@ -455,159 +433,6 @@ </voice> </phrase> <phrase> - id: LANG_LOUDNESS - desc: in sound_settings - user: core - <source> - *: none - masf: "Loudness" - </source> - <dest> - *: none - masf: "响度" - </dest> - <voice> - *: none - masf: "响度" - </voice> -</phrase> -<phrase> - id: LANG_AUTOVOL - desc: in sound_settings - user: core - <source> - *: none - masf: "Auto Volume" - </source> - <dest> - *: none - masf: "自动音量调整" - </dest> - <voice> - *: none - masf: "自动音量调整" - </voice> -</phrase> -<phrase> - id: LANG_DECAY - desc: in sound_settings - user: core - <source> - *: none - masf: "AV Decay Time" - </source> - <dest> - *: none - masf: "自动音量调整衰減时间" - </dest> - <voice> - *: none - masf: "" - </voice> -</phrase> -<phrase> - id: LANG_SUPERBASS - desc: in sound settings - user: core - <source> - *: none - masf: "Super Bass" - </source> - <dest> - *: none - masf: "超低音" - </dest> - <voice> - *: none - masf: "超低音" - </voice> -</phrase> -<phrase> - id: LANG_MDB_ENABLE - desc: in sound settings - user: core - <source> - *: none - masf: "MDB Enable" - </source> - <dest> - *: none - masf: "启用MDB" - </dest> - <voice> - *: none - masf: "启用MDB" - </voice> -</phrase> -<phrase> - id: LANG_MDB_STRENGTH - desc: in sound settings - user: core - <source> - *: none - masf: "MDB Strength" - </source> - <dest> - *: none - masf: "MDB强度" - </dest> - <voice> - *: none - masf: "MDB的强度" - </voice> -</phrase> -<phrase> - id: LANG_MDB_HARMONICS - desc: in sound settings - user: core - <source> - *: none - masf: "MDB Harmonics" - </source> - <dest> - *: none - masf: "MDB泛音" - </dest> - <voice> - *: none - masf: "MDB的泛音" - </voice> -</phrase> -<phrase> - id: LANG_MDB_CENTER - desc: in sound settings - user: core - <source> - *: none - masf: "MDB Centre Frequency" - </source> - <dest> - *: none - masf: "MDB中心频率" - </dest> - <voice> - *: none - masf: "MDB的中心频率" - </voice> -</phrase> -<phrase> - id: LANG_MDB_SHAPE - desc: in sound settings - user: core - <source> - *: none - masf: "MDB Shape" - </source> - <dest> - *: none - masf: "MDB形状" - </dest> - <voice> - *: none - masf: "MDB的形状" - </voice> -</phrase> -<phrase> id: LANG_CROSSFEED desc: in sound settings user: core @@ -929,7 +754,7 @@ *: "%dHz频段增益" </dest> <voice> - *: "赫兹频段增益" + *: "%d赫兹频段增益" </voice> </phrase> <phrase> @@ -957,7 +782,7 @@ *: "带通滤波器%d" </dest> <voice> - *: "带通滤波器" + *: "带通滤波器%d" </voice> </phrase> <phrase> @@ -1726,15 +1551,12 @@ user: core <source> *: "Peak Meter" - masd: none </source> <dest> *: "峰值电平表" - masd: none </dest> <voice> *: "峰值电平表" - masd: none </voice> </phrase> <phrase> @@ -2142,20 +1964,6 @@ </voice> </phrase> <phrase> - id: LANG_BOOKMARK_SETTINGS_UNIQUE_ONLY - desc: Save only on bookmark for each playlist in recent bookmarks - user: core - <source> - *: "Unique only" - </source> - <dest> - *: "只保存唯一一份" - </dest> - <voice> - *: "只保存唯一一份" - </voice> -</phrase> -<phrase> id: LANG_VOICE_MENU desc: item of voice menu, enable/disable the voice UI user: core @@ -2240,23 +2048,6 @@ </voice> </phrase> <phrase> - id: LANG_RECORDING_QUALITY - desc: in the recording settings - user: core - <source> - *: none - recording_hwcodec: "Quality" - </source> - <dest> - *: none - recording_hwcodec: "品质" - </dest> - <voice> - *: none - recording_hwcodec: "品质" - </voice> -</phrase> -<phrase> id: LANG_FREQUENCY desc: in recording and playback settings user: core @@ -2328,23 +2119,6 @@ </voice> </phrase> <phrase> - id: LANG_RECORDING_EDITABLE - desc: Editable recordings setting - user: core - <source> - *: none - recording_hwcodec: "Independent Frames" - </source> - <dest> - *: none - recording_hwcodec: "独立框架" - </dest> - <voice> - *: none - recording_hwcodec: "独立框架" - </voice> -</phrase> -<phrase> id: LANG_RECORD_TIMESPLIT desc: Record split menu user: core @@ -3182,23 +2956,6 @@ </voice> </phrase> <phrase> - id: LANG_BUTTON_BAR - desc: in settings menu - user: core - <source> - *: none - recorder_pad: "Button Bar" - </source> - <dest> - *: none - recorder_pad: "按键条" - </dest> - <voice> - *: none - recorder_pad: "按键条" - </voice> -</phrase> -<phrase> id: LANG_VOLUME_DISPLAY desc: Volume type title user: core @@ -3260,15 +3017,12 @@ user: core <source> *: "Peak Release" - masd: none </source> <dest> *: "峰值释放速度" - masd: none </dest> <voice> *: "峰值释放速度" - masd: none </voice> </phrase> <phrase> @@ -3277,15 +3031,12 @@ user: core <source> *: "Peak Hold Time" - masd: none </source> <dest> *: "峰值保持时长" - masd: none </dest> <voice> *: "峰值保持时长" - masd: none </voice> </phrase> <phrase> @@ -3294,15 +3045,12 @@ user: core <source> *: "Clip Hold Time" - masd: none </source> <dest> *: "削波保持时长" - masd: none </dest> <voice> *: "削波保持时长" - masd: none </voice> </phrase> <phrase> @@ -3311,15 +3059,12 @@ user: core <source> *: "Eternal" - masd: none </source> <dest> *: "外部" - masd: none </dest> <voice> *: "外部" - masd: none </voice> </phrase> <phrase> @@ -3328,15 +3073,12 @@ user: core <source> *: "Scale" - masd: none </source> <dest> *: "标尺类型" - masd: none </dest> <voice> *: "标尺类型" - masd: none </voice> </phrase> <phrase> @@ -3345,15 +3087,12 @@ user: core <source> *: "Logarithmic (dB)" - masd: none </source> <dest> *: "对数(dB)坐标" - masd: none </dest> <voice> *: "对数坐标" - masd: none </voice> </phrase> <phrase> @@ -3362,15 +3101,12 @@ user: core <source> *: "Linear (%)" - masd: none </source> <dest> *: "线性(%)坐标" - masd: none </dest> <voice> *: "线性坐标" - masd: none </voice> </phrase> <phrase> @@ -3379,15 +3115,12 @@ user: core <source> *: "Minimum Of Range" - masd: none </source> <dest> *: "量程最小值" - masd: none </dest> <voice> *: "量程最小值" - masd: none </voice> </phrase> <phrase> @@ -3396,15 +3129,12 @@ user: core <source> *: "Maximum Of Range" - masd: none </source> <dest> *: "量程最大值" - masd: none </dest> <voice> *: "量程最大值" - masd: none </voice> </phrase> <phrase> @@ -3999,7 +3729,7 @@ *: "搜索中...已找到%d个(%s)" </dest> <voice> - *: "" + *: "搜索中...已找到%d个(%s)" </voice> </phrase> <phrase> @@ -4062,23 +3792,6 @@ </voice> </phrase> <phrase> - id: LANG_BATTERY_TOPOFF_CHARGE - desc: in info display, shows that top off charge is running Only for V1 archosrecorder - user: core - <source> - *: none - archosrecorder: "Battery: Top-Off Chg" - </source> - <dest> - *: none - archosrecorder: "电池:恒流快速充电" - </dest> - <voice> - *: none - archosrecorder: "电池 恒流快速充电" - </voice> -</phrase> -<phrase> id: LANG_BATTERY_TRICKLE_CHARGE desc: in info display, shows that trickle charge is running user: core @@ -4102,12 +3815,10 @@ <source> *: "Battery: %d%% %dh %dm" ipodmini1g,ipodmini2g,iriverh10: "Batt: %d%% %dh %dm" - iriverifp7xx: "%d%% %dh %dm" </source> <dest> *: "剩余电量:%d%% %dh %dm" ipodmini1g,ipodmini2g,iriverh10: "电量:%d%% %dh %dm" - iriverifp7xx: "%d%% %dh %dm" </dest> <voice> *: "剩余电量" @@ -4147,38 +3858,38 @@ user: core <source> *: "Int:" + hibylinux: "mSD:" xduoox3: "mSD1:" </source> <dest> *: "内部:" + hibylinux: "mSD:" xduoox3: "mSD1:" </dest> <voice> *: "内部大小为" + hibylinux: "micro S D" xduoox3: "mSD卡1" </voice> </phrase> <phrase> id: LANG_DISK_NAME_MMC - desc: in info menu; name for external disk with multivolume (Ondio; keep short!) + desc: in info menu; name for external disk with multivolume (keep short!) user: core <source> *: none - archosondio*: "MMC:" multivolume: "HD1" sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:" xduoox3: "mSD2:" </source> <dest> *: none - archosondio*: "MMC:" multivolume: "磁盘1" sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:" xduoox3: "mSD2:" </dest> <voice> *: none - archosondio*: "MMC卡" multivolume: "磁盘1" sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD卡" xduoox3: "mSD卡2" @@ -4339,7 +4050,8 @@ vibe500: "CANCEL:取消" </dest> <voice> - *: none + *: "" + gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,samsungyh*,sansac200*,sansae200*: "" </voice> </phrase> <phrase> @@ -4398,6 +4110,7 @@ </dest> <voice> *: none + iaudiom5,iaudiox5,iriverh100,iriverh120,iriverh300,recording,samsungyh*,sansac200*,sansae200*,vibe500: "" </voice> </phrase> <phrase> @@ -4516,7 +4229,7 @@ </dest> <voice> *: none - alarm: "离闹钟响起还有" + alarm: "离闹钟响起还有%d:%02d" </voice> </phrase> <phrase> @@ -4579,7 +4292,7 @@ </dest> <voice> *: none - alarm,gigabeats,ipod*,iriverh10,iriverh10_5gb: "" + alarm,ipod*: "" </voice> </phrase> <phrase> @@ -4787,7 +4500,7 @@ *: "(VBR)" </dest> <voice> - *: "VBR" + *: "(VBR)" </voice> </phrase> <phrase> @@ -4857,7 +4570,7 @@ *: "<无信息>" </dest> <voice> - *: "无信息" + *: "<无信息>" </voice> </phrase> <phrase> @@ -5834,48 +5547,6 @@ </voice> </phrase> <phrase> - id: LANG_KILOBYTE - desc: a unit postfix, also voiced - user: core - <source> - *: "KB" - </source> - <dest> - *: "KB" - </dest> - <voice> - *: "B" - </voice> -</phrase> -<phrase> - id: LANG_MEGABYTE - desc: a unit postfix, also voiced - user: core - <source> - *: "MB" - </source> - <dest> - *: "MB" - </dest> - <voice> - *: "MB" - </voice> -</phrase> -<phrase> - id: LANG_GIGABYTE - desc: a unit postfix, also voiced - user: core - <source> - *: "GB" - </source> - <dest> - *: "GB" - </dest> - <voice> - *: "GB" - </voice> -</phrase> -<phrase> id: LANG_POINT desc: decimal separator for composing numbers user: core @@ -6477,7 +6148,7 @@ *: "插入了%d首曲目(%s)" </dest> <voice> - *: "首曲目 插入了" + *: "插入了%d首曲目%s" </voice> </phrase> <phrase> @@ -6491,7 +6162,7 @@ *: "列入了%d首曲目(%s)" </dest> <voice> - *: "首曲目 列入了" + *: "列入了%d首曲目%s" </voice> </phrase> <phrase> @@ -6505,7 +6176,7 @@ *: "保存了%d首曲目(%s)" </dest> <voice> - *: "首曲目 保存了" + *: "保存了%d首曲目%s" </voice> </phrase> <phrase> @@ -6723,91 +6394,6 @@ </voice> </phrase> <phrase> - id: LANG_BUTTONBAR_MENU - desc: in button bar - user: core - <source> - *: none - radio_screen_button_bar: "Menu" - </source> - <dest> - *: none - radio_screen_button_bar: "选单" - </dest> - <voice> - *: none - radio_screen_button_bar: "" - </voice> -</phrase> -<phrase> - id: LANG_FM_BUTTONBAR_EXIT - desc: in radio screen - user: core - <source> - *: none - radio_screen_button_bar: "Exit" - </source> - <dest> - *: none - radio_screen_button_bar: "退出" - </dest> - <voice> - *: none - radio_screen_button_bar: "" - </voice> -</phrase> -<phrase> - id: LANG_FM_BUTTONBAR_ACTION - desc: in radio screen - user: core - <source> - *: none - radio_screen_button_bar: "Action" - </source> - <dest> - *: none - radio_screen_button_bar: "动作" - </dest> - <voice> - *: none - radio_screen_button_bar: "" - </voice> -</phrase> -<phrase> - id: LANG_FM_BUTTONBAR_ADD - desc: in radio screen - user: core - <source> - *: none - radio_screen_button_bar: "Add" - </source> - <dest> - *: none - radio_screen_button_bar: "增加" - </dest> - <voice> - *: none - radio_screen_button_bar: "" - </voice> -</phrase> -<phrase> - id: LANG_FM_BUTTONBAR_RECORD - desc: in radio screen - user: core - <source> - *: none - radio_screen_button_bar: "Record" - </source> - <dest> - *: none - radio_screen_button_bar: "录音" - </dest> - <voice> - *: none - radio_screen_button_bar: "" - </voice> -</phrase> -<phrase> id: LANG_FM_MONO_MODE desc: in radio screen user: core @@ -7012,7 +6598,7 @@ </phrase> <phrase> id: LANG_OFF_ABORT - desc: Used on archosrecorder models + desc: Used on many models user: core <source> *: "OFF to abort" @@ -7031,6 +6617,7 @@ ipod*: "按PLAY/PAUSE键取消" iriverh10,iriverh10_5gb,sansac200*,sansae200*,vibe500: "按PREV键取消" iriverh100,iriverh120,iriverh300: "按STOP键取消" + sansafuzeplus: "按BACK键取消" </dest> <voice> *: "" @@ -7178,7 +6765,7 @@ </phrase> <phrase> id: LANG_PLUGIN_WRONG_MODEL - desc: The plugin is not compatible with the archos model trying to run it + desc: The plugin is not compatible with the player model trying to run it user: core <source> *: "Incompatible model" @@ -7187,7 +6774,7 @@ *: "不兼容型号的插件" </dest> <voice> - *: "" + *: "不兼容型号的插件" </voice> </phrase> <phrase> @@ -7201,7 +6788,7 @@ *: "不兼容版本的插件" </dest> <voice> - *: "" + *: "不兼容版本的插件" </voice> </phrase> <phrase> @@ -7215,7 +6802,7 @@ *: "插件返回错误" </dest> <voice> - *: "" + *: "插件返回错误" </voice> </phrase> <phrase> @@ -7475,159 +7062,6 @@ </voice> </phrase> <phrase> - id: LANG_SYSFONT_CHANNEL_STEREO - desc: in sound_settings - user: core - <source> - *: none - recording: "Stereo" - </source> - <dest> - *: none - recording: "Stereo" - </dest> - <voice> - *: none - recording: "Stereo" - </voice> -</phrase> -<phrase> - id: LANG_SYSFONT_CHANNEL_MONO - desc: in sound_settings - user: core - <source> - *: none - recording: "Mono" - </source> - <dest> - *: none - recording: "Mono" - </dest> - <voice> - *: none - recording: "Mono" - </voice> -</phrase> -<phrase> - id: LANG_SYSFONT_RECORDING_SOURCE - desc: in the recording settings - user: core - <source> - *: none - recording: "Source" - </source> - <dest> - *: none - recording: "来源" - </dest> - <voice> - *: none - recording: "来源" - </voice> -</phrase> -<phrase> - id: LANG_SYSFONT_RECORDING_SRC_MIC - desc: in the recording settings - user: core - <source> - *: none - recording: "Int. Mic" - </source> - <dest> - *: none - recording: "内部麦克风" - </dest> - <voice> - *: none - recording: "内部麦克风" - </voice> -</phrase> -<phrase> - id: LANG_SYSFONT_RECORDING_SRC_DIGITAL - desc: in the recording settings - user: core - <source> - *: none - recording: "Digital" - </source> - <dest> - *: none - recording: "数码" - </dest> - <voice> - *: none - recording: "数码" - </voice> -</phrase> -<phrase> - id: LANG_SYSFONT_RECORD_TRIGGER - desc: in recording settings_menu - user: core - <source> - *: none - recording: "Trigger" - </source> - <dest> - *: none - recording: "触发" - </dest> - <voice> - *: none - recording: "触发" - </voice> -</phrase> -<phrase> - id: LANG_SYSFONT_DIRBROWSE_F1 - desc: in dir browser, F1 button bar text - user: core - <source> - *: none - recorder_pad: "Menu" - </source> - <dest> - *: none - recorder_pad: "选单" - </dest> - <voice> - *: none - recorder_pad: "" - </voice> -</phrase> -<phrase> - id: LANG_SYSFONT_DIRBROWSE_F2 - desc: in dir browser, F2 button bar text - user: core - <source> - *: none - recorder_pad: "Option" - </source> - <dest> - *: none - recorder_pad: "选项" - </dest> - <voice> - *: none - recorder_pad: "" - </voice> -</phrase> -<phrase> - id: LANG_SYSFONT_DIRBROWSE_F3 - desc: in dir browser, F3 button bar text - user: core - <source> - *: none - recorder_pad: "LCD" - </source> - <dest> - *: none - recorder_pad: "屏幕" - </dest> - <voice> - *: none - recorder_pad: "" - </voice> -</phrase> -<phrase> id: LANG_AFMT_PCM_WAV desc: audio format description user: core @@ -7641,7 +7075,7 @@ </dest> <voice> *: none - recording: "PCM Wave" + recording: "PCM Wave(.wav)" </voice> </phrase> <phrase> @@ -7677,33 +7111,13 @@ desc: in lcd settings user: core <source> - *: none - lcd_sleep: "Never" + *: "Never" </source> <dest> - *: none - lcd_sleep: "从不" + *: "从不" </dest> <voice> - *: none - lcd_sleep: "从不" - </voice> -</phrase> -<phrase> - id: LANG_SYSFONT_LINE_IN - desc: in the recording settings - user: core - <source> - *: none - recording: "Line In" - </source> - <dest> - *: none - recording: "线路输入" - </dest> - <voice> - *: none - recording: "线路输入" + *: "从不" </voice> </phrase> <phrase> @@ -7712,7 +7126,6 @@ user: core <source> *: "Building database... %d found (OFF to return)" - archosplayer: "Building DB %d found" gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "Building database... %d found (LEFT to return)" gogearsa9200: "Building database... %d found (REW to return)" ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "Building database... %d found (PREV to return)" @@ -7720,7 +7133,6 @@ </source> <dest> *: "数据库更新中...已找到%d个(按OFF键返回)" - archosplayer: "数据库更新中...已找到%d个" gigabeat*,iaudiom5,iaudiox5,mrobe100,samsungyh*: "数据库更新中...已找到%d个(按LEFT键返回)" gogearsa9200: "数据库更新中...已找到%d个(按REW键返回)" ipod*,iriverh10,iriverh10_5gb,sansac200*,sansae200*,sansafuze*,vibe500: "数据库更新中...已找到%d个(按PREV键返回)" @@ -8075,20 +7487,17 @@ </voice> </phrase> <phrase> - id: LANG_RECORDING_FORMAT - desc: audio format item in recording menu + id: LANG_FORMAT + desc: audio format user: core <source> - *: none - recording: "Format" + *: "Format" </source> <dest> - *: none - recording: "格式" + *: "格式" </dest> <voice> - *: none - recording: "格式" + *: "格式" </voice> </phrase> <phrase> @@ -8142,11 +7551,9 @@ user: core <source> *: "Buffer:" - archosplayer: "Buf:" </source> <dest> *: "缓冲区:" - archosplayer: "Buf:" </dest> <voice> *: "缓冲区大小为" @@ -8206,8 +7613,8 @@ user: core <source> *: "PLAY = Yes" - archosplayer: "(PLAY/STOP)" cowond2*: "MENU, or top-right = Yes" + creativezen*: "SELECT = Yes" gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Yes" iriverh100,iriverh120,iriverh300: "NAVI = Yes" mrobe500: "PLAY, POWER, or top-right = Yes" @@ -8215,9 +7622,8 @@ </source> <dest> *: "PLAY:是" - archosplayer: "(PLAY/STOP)" cowond2*: "MENU或TOP-RIGHT:是" - gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT:是" + creativezen*,gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT:是" iriverh100,iriverh120,iriverh300: "NAVI=是" mrobe500: "PLAY,POWER或TOP-RIGHT:是" vibe500: "OK:是" @@ -8252,6 +7658,7 @@ </dest> <voice> *: none + gigabeat*,gogearsa9200,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh100,iriverh10_5gb,iriverh120,iriverh300,mrobe100,samsungyh*,sansac200*,sansae200*: "" </voice> </phrase> <phrase> @@ -8260,11 +7667,9 @@ user: core <source> *: "Any Other = No" - archosplayer: none </source> <dest> *: "Any Other=否" - archosplayer: none </dest> <voice> *: "" @@ -8427,7 +7832,7 @@ *: "Main Menu" </source> <dest> - *: "主选单" + *: "主菜单" </dest> <voice> *: "主选单" @@ -8453,15 +7858,12 @@ user: core <source> *: "End of Song List" - archosplayer: "End of List" </source> <dest> *: "曲目列表结束" - archosplayer: "列表结束" </dest> |