Something went wrong. Try again.
A modern, network-enabled music player platform built on Rockbox technology. rockboxd.tsiry-sandratraina.com
rust deno navidrome airplay libadwaita zig mpris snapcast mpd rockbox audio subsonic
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * * Copyright (C) 2006 Jonathan Gordon * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/#include "plugin.h"#include "lib/playback_control.h"
#if defined(DEBUG) || defined(SIMULATOR) #define logf(...) rb->debugf(__VA_ARGS__); rb->debugf("\n")#elif defined(ROCKBOX_HAS_LOGF) #define logf rb->logf#else #define logf(...) do { } while(0)#endif
#define MAX_LINE_LEN 2048
static unsigned char *buffer;static size_t buffer_size;static size_t char_count = 0;static int line_count = 0;static int last_action_line = 0;static int last_char_index = 0;static bool audio_buf = false;
static char temp_line[MAX_LINE_LEN];static char copy_buffer[MAX_LINE_LEN];static char filename[MAX_PATH];static char eol[3];static bool newfile;static struct gui_synclist lists;
#define ACTION_INSERT 0#define ACTION_GET 1#define ACTION_REMOVE 2#define ACTION_UPDATE 3#define ACTION_CONCAT 4
static void cpuboost(int onoff){#ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost((onoff == 1) ? true : false);#else (void)onoff;#endif}
static char* _do_action(int action, char* str, int line){ int len, lennew; int i=0,c=0; if (line>=last_action_line) { i = last_action_line; c = last_char_index; } while (i<line && i<line_count) { c += rb->strlen(&buffer[c])+1; i++; } switch (action) { case ACTION_INSERT: len = rb->strlen(str)+1; if ( char_count+ len > buffer_size ) return NULL; rb->memmove(&buffer[c+len],&buffer[c],char_count-c); rb->strcpy(&buffer[c],str); char_count += len; line_count++; break; case ACTION_GET: if (line > line_count) return &buffer[0]; break; case ACTION_REMOVE: if (line > line_count) return NULL; len = rb->strlen(&buffer[c])+1; rb->memmove(&buffer[c],&buffer[c+len],char_count-c-len); char_count -= len; line_count--; break; case ACTION_UPDATE: if (line > line_count) return NULL; len = rb->strlen(&buffer[c])+1; lennew = rb->strlen(str)+1; if ( char_count+ lennew-len > buffer_size ) return NULL; rb->memmove(&buffer[c+lennew],&buffer[c+len],char_count-c-len); rb->strcpy(&buffer[c],str); char_count += lennew-len; break; case ACTION_CONCAT: if (line > line_count) return NULL; rb->memmove(&buffer[c-1],&buffer[c],char_count-c); char_count--; line_count--; break; default: return NULL; } last_action_line = i; last_char_index = c; return &buffer[c];}
static char* do_action(int action, char* str, int line){ char *r; cpuboost(1); r = _do_action(action,str,line); cpuboost(0); return r;}
static const char* list_get_name_cb(int selected_item, void* data, char* buf, size_t buf_len){ (void)data; char *b = do_action(ACTION_GET, 0, selected_item); /* strlcpy(dst, src, siz) returns strlen(src) */ if (rb->strlcpy(buf, b, buf_len) >= buf_len) { rb->strcpy(&buf[buf_len-10], " ..."); } return buf;}
static void get_eol_string(char* fn){ int fd; char t;
/* assume LF first */ rb->strcpy(eol,"\n");
if (!fn || !fn[0]) return; fd = rb->open(fn,O_RDONLY); if (fd<0) return;
while (1) { if (!rb->read(fd,&t,1) || t == '\n') { break; } if (t == '\r') { if (rb->read(fd,&t,1) && t=='\n') rb->strcpy(eol,"\r\n"); else rb->strcpy(eol,"\r"); break; } } rb->close(fd); return;}
static bool save_changes(int overwrite){ int fd; int i;
if (newfile || !overwrite) { if(rb->kbd_input(filename,MAX_PATH, NULL) < 0) { newfile = true; return false; } }
fd = rb->open(filename,O_WRONLY|O_CREAT|O_TRUNC, 0666); if (fd < 0) { newfile = true; rb->splash(HZ*2, "Changes NOT saved"); return false; }
rb->lcd_clear_display();
cpuboost(1); for (i=0;i<line_count - 1;i++) { rb->fdprintf(fd,"%s%s", do_action(ACTION_GET, 0, i), eol); } if (line_count > 0) /* No EOL for last item */ rb->fdprintf(fd,"%s", do_action(ACTION_GET, 0, line_count - 1)); cpuboost(0);
rb->close(fd);
if (newfile || !overwrite) /* current directory may have changed */ rb->reload_directory();
newfile = false; return true;}
static void setup_lists(struct gui_synclist *lists, int sel){ rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL); rb->gui_synclist_set_nb_items(lists,line_count); rb->gui_synclist_select_item(lists, sel); rb->gui_synclist_draw(lists);}
enum { MENU_RET_SAVE = -1, MENU_RET_NO_UPDATE, MENU_RET_UPDATE,};static int do_item_menu(int cur_sel){ int ret = MENU_RET_NO_UPDATE; MENUITEM_STRINGLIST(menu, "Line Options", NULL, "Find", "Cut/Delete", "Copy", "Insert Above", "Insert Below", "Concat To Above", "Save", "Playback Control");
switch (rb->do_menu(&menu, NULL, NULL, false)) { case 0: /* find*/ { static char find_str[32] = ""; if (rb->kbd_input(find_str, sizeof(find_str), NULL) != 0) break;
unsigned long last_tick; int i = rb->gui_synclist_get_sel_pos(&lists) + 1; rb->gui_synclist_draw(&lists);
rb->splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */ last_tick = *(rb->current_tick) + HZ/4;
cpuboost(1);
for(; i < line_count; i++) { char *t = _do_action(ACTION_GET, 0, i); //logf("find '%s' ln %d %s\n", find_str, i, t);
if (TIME_AFTER(*(rb->current_tick), last_tick + HZ/4)) { rb->splash_progress(i, line_count - i, "%s (%s)", rb->str(LANG_SEARCH), rb->str(LANG_OFF_ABORT)); if (rb->action_userabort(TIMEOUT_NOBLOCK)) break; last_tick = *(rb->current_tick); }
if (rb->strcasestr(t, find_str) != NULL) { logf("found '%s' @ ln %d\n'%s'\n", find_str, i, t); break; } } cpuboost(0); if (i >= line_count) rb->splash(HZ * 2, ID2P(LANG_FAILED)); else rb->gui_synclist_select_item(&lists, i); break; } case 1: /* cut */ rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel), MAX_LINE_LEN); do_action(ACTION_REMOVE, 0, cur_sel); ret = MENU_RET_UPDATE; break; case 2: /* copy */ rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel), MAX_LINE_LEN); ret = MENU_RET_NO_UPDATE; break; case 3: /* insert above */ if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN, NULL)) { do_action(ACTION_INSERT,copy_buffer,cur_sel); copy_buffer[0]='\0'; ret = MENU_RET_UPDATE; } break; case 4: /* insert below */ if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN, NULL)) { do_action(ACTION_INSERT,copy_buffer,cur_sel+1); copy_buffer[0]='\0'; ret = MENU_RET_UPDATE; } break; case 5: /* cat to above */ if (cur_sel>0) { do_action(ACTION_CONCAT,0,cur_sel); ret = MENU_RET_UPDATE; } break; case 6: /* save */ ret = MENU_RET_SAVE; break; case 7: /* playback menu */ if (!audio_buf) playback_control(NULL); else rb->splash(HZ, "Cannot restart playback"); ret = MENU_RET_NO_UPDATE; break; default: ret = MENU_RET_NO_UPDATE; break; } return ret;}
#ifdef HAVE_LCD_COLOR
#define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \ (toupper(c)) - 'A' + 10)
static int my_hex_to_rgb(const char* hex, int* color){ int ok = 1; int i; int red, green, blue;
if (rb->strlen(hex) == 6) { for (i=0; i < 6; i++ ) { if (!isxdigit(hex[i])) { ok=0; break; } }
if (ok) { red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]); green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]); blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]); *color = LCD_RGBPACK(red,green,blue); return 0; } }
return -1;}#endif /* HAVE_LCD_COLOR */
/* this is the plugin entry point */enum plugin_status plugin_start(const void* parameter){ int fd;
bool exit = false; int button; bool changed = false; int cur_sel=0;#ifdef HAVE_LCD_COLOR bool edit_colors_file = false;#endif
copy_buffer[0]='\0';
#if LCD_DEPTH > 1 rb->lcd_set_backdrop(NULL);#endif buffer = rb->plugin_get_buffer(&buffer_size);
cpuboost(1);
if (parameter) {#ifdef HAVE_LCD_COLOR char *c = NULL;#endif rb->strlcpy(filename, (char*)parameter, MAX_PATH); get_eol_string(filename); fd = rb->open(filename,O_RDONLY); if (fd<0) { rb->splashf(HZ*2, "Couldnt open file: %s", filename); return PLUGIN_ERROR; }#ifdef HAVE_LCD_COLOR c = rb->strrchr(filename, '.'); if (c && !rb->strcmp(c, ".colours")) edit_colors_file = true;#endif if (buffer_size <= (size_t)rb->ffilesize(fd) + 0x400) { buffer = rb->plugin_get_audio_buffer(&buffer_size); audio_buf = true; } /* read in the file */ while (rb->read_line(fd,temp_line,MAX_LINE_LEN) > 0) { if (!do_action(ACTION_INSERT,temp_line,line_count)) { rb->splashf(HZ*2,"Error reading file: %s",(char*)parameter); rb->close(fd); return PLUGIN_ERROR; } } rb->close(fd); newfile = false; } else { rb->strcpy(filename,"/"); rb->strcpy(eol,"\n"); newfile = true; }
cpuboost(0);
/* now dump it in the list */ setup_lists(&lists,0); rb->lcd_update(); while (!exit) { rb->gui_synclist_draw(&lists); cur_sel = rb->gui_synclist_get_sel_pos(&lists); button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK); if (rb->gui_synclist_do_button(&lists, &button)) continue; switch (button) { case ACTION_STD_OK: { if (line_count) rb->strlcpy(temp_line, do_action(ACTION_GET, 0, cur_sel), MAX_LINE_LEN);#ifdef HAVE_LCD_COLOR if (edit_colors_file && line_count) { char *name = temp_line, *value = NULL; char extension[16]; int color, old_color; bool temp_changed = false;
MENUITEM_STRINGLIST(menu, "Edit What?", NULL, "Extension", "Colour");
rb->settings_parseline(temp_line, &name, &value); rb->strlcpy(extension, name, sizeof(extension)); if (value) my_hex_to_rgb(value, &color); else color = 0;
switch (rb->do_menu(&menu, NULL, NULL, false)) { case 0: temp_changed = !rb->kbd_input(extension, sizeof(extension), NULL); break; case 1: old_color = color; rb->set_color(rb->screens[SCREEN_MAIN], name, &color, -1); temp_changed = (value == NULL) || (color != old_color); break; }
if (temp_changed) { rb->snprintf(temp_line, MAX_LINE_LEN, "%s: %02X%02X%02X", extension, RGB_UNPACK_RED(color), RGB_UNPACK_GREEN(color), RGB_UNPACK_BLUE(color)); do_action(ACTION_UPDATE, temp_line, cur_sel); changed = true; } } else#endif if (!rb->kbd_input(temp_line,MAX_LINE_LEN, NULL)) { if (line_count) do_action(ACTION_UPDATE,temp_line,cur_sel); else do_action(ACTION_INSERT,temp_line,cur_sel); changed = true; } } break; case ACTION_STD_CONTEXT:/* These targets have unintuitive STD_MENU keymaps, so we use context keymap instead; We don't need the "delete line" action, since this can be done via the menu. */#if (CONFIG_KEYPAD != SAMSUNG_YH92X_PAD) && (CONFIG_KEYPAD != SAMSUNG_YH820_PAD) if (!line_count) break; rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel), MAX_LINE_LEN); do_action(ACTION_REMOVE, 0, cur_sel); changed = true; break; case ACTION_STD_MENU:#endif { rb->gui_synclist_scroll_stop(&lists); /* do the item menu */ switch (do_item_menu(cur_sel)) { case MENU_RET_SAVE: if(save_changes(1)) changed = false; break; case MENU_RET_UPDATE: changed = true; break; case MENU_RET_NO_UPDATE: break; } } break; case ACTION_STD_CANCEL: if (changed) { rb->gui_synclist_scroll_stop(&lists); MENUITEM_STRINGLIST(menu, "Text Editor", NULL, "Return", "Playback Control", "Save Changes", "Save As...", "Save and Exit", "Ignore Changes and Exit"); switch (rb->do_menu(&menu, NULL, NULL, false)) { case 0: break; case 1: if (!audio_buf) playback_control(NULL); else rb->splash(HZ, "Cannot restart playback"); break; case 2: //save to disk if(save_changes(1)) changed = 0; break; case 3: if(save_changes(0)) changed = 0; break; case 4: if(save_changes(1)) exit=1; break; case 5: exit=1; break; } } else exit=1; break; } rb->gui_synclist_set_nb_items(&lists,line_count); if(line_count > 0 && line_count <= cur_sel) rb->gui_synclist_select_item(&lists,line_count-1); } return PLUGIN_OK;}