Rockbox Development > Feature Ideas

more advanced sleep timer

<< < (4/5) > >>

Bilgus:
I've got a plugin that implements it in progress but I still haven't finished it yet but I will eventually get interested in it again


--- Code: ---/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2017 William Wilgus
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "plugin.h"
#include "lib/pluginlib_actions.h"
#define settings_filename PLUGIN_APPS_DIR "/fade2sleep_settings.f2s"

#define MSTR_CT(x) (sizeof(x)/sizeof(0[x]))
#define MFMT_PLAYLIST       "%s %s %s;"
#define MFMT_TIME           "%s %s;"
#define MFMT_VOL            "%s %d %s;"
#define MFMT_STR            "%s;"
#define MFMT_SEPARATOR      " ;"
#define MENU_SETTINGS_BEGIN "",

#define MENUSETTING_VOLUME(item) setting_strings[item],settings->table[item],\
                                  rb->sound_unit(SOUND_VOLUME)
#define MENUSETTING_TRIGGER(item) setting_strings[item],\
                                   trigger_strings[settings->table[item]]
#define MENUSETTING_ACTION(item) action_strings[settings->table[item]]
#define MENUSETTING_PLAY(item)   setting_strings[item],\
                                   play_strings[settings->table[item]],\
                                   (settings->table[item])? settings->play : "\0"
#define WATCHED_EV(flag) ((watched_events & flag) == flag) ? flag : 0
#define THREAD_STACK_SIZE DEFAULT_STACK_SIZE
/* use long for aligning */
static unsigned long thread_stack[THREAD_STACK_SIZE/sizeof(long)];
#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 1)
#define TIMEOUT_VOL_CHANGE HZ/2
static unsigned int thread_id;
static struct event_queue thread_q SHAREDBSS_ATTR;
const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
//#define MAX_MENU_ENTRIES 10 /* menu string pointers statically allocated */
//const char* menu_strings[MAX_MENU_ENTRIES];// * sizeof(const char*)
static unsigned int triggered_events = 0;
static unsigned int watched_events = 0;
static bool quit = false, in_usb_mode = false;

/*Time setting formatting (NOT RTC)*/
#define MS_IN_TICK (1000U/HZ)
enum e_fmt_time_auto_idx
{
    UNIT_IDX_HR = 0,
    UNIT_IDX_MIN,
    UNIT_IDX_SEC,
    UNIT_IDX_MS,
    UNIT_IDX_TIME_COUNT,
};
/* format_time_auto */

#define UNIT_IDX_MASK       0x01FFU /*Return only Unit_IDX*/
#define UNIT_TRIM_ZERO      0x0200U /*Don't show leading zero on max_idx*/
#define UNIT_LOCK_HR        0x0400U /*Don't Auto Range below this field*/
#define UNIT_LOCK_MIN       0x0800U /*Don't Auto Range below this field*/
#define UNIT_LOCK_SEC       0x1000U /*Don't Auto Range below this field*/

static const char *unit_strings[] =
{
    [UNIT_INT] = "",    [UNIT_MS]  = "ms",
    [UNIT_SEC] = "s",   [UNIT_MIN] = "min",
    [UNIT_HOUR]= "hr",  [UNIT_KHZ] = "kHz",
    [UNIT_DB]  = "dB",  [UNIT_PERCENT] = "%",
    [UNIT_MAH] = "mAh", [UNIT_PIXEL] = "px",
    [UNIT_PER_SEC] = "per sec",
    [UNIT_HERTZ] = "Hz",
    [UNIT_MB]  = "MB",  [UNIT_KBIT]  = "kb/s",
    [UNIT_PM_TICK] = "units/10ms",
};
enum e_self_triggered_events
{   /* TRIGGERED WITHIN THIS PLUGIN */
    SELF_EVENT_CHECK_TRIGGERED  = 0,
    SELF_EVENT_TIMEOUT_REACHED  = 1,
    SELF_EVENT_FINALVOL_REACHED = 2,
};

enum e_triggered_events
{
    //TRIG_EVENT_=0x0001,
    TRIG_EVENT_START_PLAYBACK = 0x0002,
    TRIG_EVENT_TRACK_SKIP     = 0x0004,
    TRIG_EVENT_TRACK_CHANGE   = 0x0008,
    TRIG_EVENT_TRACK_FINISH   = 0x0010,
    TRIG_EVENT_VOL_CHANGED    = 0x0020,
    TRIG_EVENT_SCREEN_CHANGED = 0x0040,
    TRIG_EVENT_SCREEN_ON      = 0x0080,
    TRIG_EVENT_VOL_FINAL      = 0x0100,
    TRIG_EVENT_PAUSE          = 0x0200,
    TRIG_EVENT_RESUME         = 0x0400,
    TRIG_EVENT_PLAY           = 0x0800,
    TRIG_EVENT_STOP           = 0x1000,
    TRIG_EVENT_TIMEOUT        = 0x2000,
    //TRIG_EVENT_=0x4000,
    //TRIG_EVENT_=0x8000,
};
enum e_play_str
{
    PLAY_USECURRENT = 0,
    PLAY_LOAD,
};
static const char * play_strings[] =
{
    [PLAY_USECURRENT]"Use Current",
    [PLAY_LOAD]      "Load",
};

enum e_save_str
{
    SAV_SAVE = 0,
    SAV_SAVED,
};
static const char * saved_strings[] =
{
    [SAV_SAVE] "Save",
    [SAV_SAVED]"Saved",
};

enum e_trigger_str
{
    TRIG_NONE = 0,
    TRIG_TIMEOUT,
    TRIG_FINALVOL,
    TRIG_PAUSE,
    TRIG_VOLCHANGE,
    TRIG_TRACKCHANGE,
    TRIG_ANY,
};
static const char * trigger_strings[] =
{
    [TRIG_NONE]       "None",
    [TRIG_TIMEOUT]    "Timeout",
    [TRIG_FINALVOL]   "Final Volume",
    [TRIG_PAUSE]      "Pause",
    [TRIG_VOLCHANGE]  "Volume Change",
    [TRIG_TRACKCHANGE]"Track Change",
    [TRIG_ANY]        "Any Action",
};

enum e_delay_str
{
    DLY_NONE = 0,
    DLY_TIMEOUT,
    DLY_TRACKCHANGE,
    DLY_PAUSERESUME,
    DLY_PLAYSTOP,
};
static const char * delay_strings[] =
{
    [DLY_NONE]       "None",
    [DLY_TIMEOUT]    "Timeout",
    [DLY_TRACKCHANGE]"Track Change",
    [DLY_PAUSERESUME]"Pause/Resume",
    [DLY_PLAYSTOP]   "Play/Stop",
};

enum e_curvol_str
{
    CURVOL_NONE = 0,
    CURVOL_START,
    CURVOL_END,
};
static const char *  cur_vol_strings[] =
{
    [CURVOL_NONE] "None",
    [CURVOL_START]"Start",
    [CURVOL_END]  "End",
};

enum e_action_str
{
    ACT_NONE = 0,
    ACT_SLEEP,
    ACT_STARTOVER,
    ACT_INVERTFADE,
    ACT_EXIT,
};
static const char * action_strings[] =
{
    [ACT_NONE]      "None",
    [ACT_SLEEP]     "Sleep",
    [ACT_STARTOVER] "Start Over",
    [ACT_INVERTFADE]"Invert Fade",
    [ACT_EXIT]      "Exit",
};

enum e_ny_str
{
    NY_NO = 0,
    NY_YES,
};
static const char * ny_strings[] =
{
    [NY_NO] "No",
    [NY_YES]"Yes",
};

enum enum_menu
{ /*same order as they will be displayed in the menu*/
    M_PLAY = 0,
    M_DELAY,
    M_ACTDELAY,
    M_USECURVOL,
    M_STARTVOL,
    M_ENDVOL,
    M_TIMEOUT,
    M_TRIG1,
    M_ACT1,
    M_TRIG2,
    M_ACT2,
    M_TRIG3,
    M_ACT3,
    M_TSR,
    M_SEP,
    M_SAVE,
    M_RUN,
    M_EXIT,
    M_ITEM_COUNT,
};

static const char * setting_strings[M_ITEM_COUNT] =
{//[]"",
    [M_PLAY]  "Play",
    [M_DELAY]     "Delay",
    [M_ACTDELAY]  "After",
    [M_USECURVOL] "Use Cur Volume",
    [M_STARTVOL]  "Fade from :",
    [M_ENDVOL]    "to :",
    [M_TIMEOUT]   "Timeout",
    [M_TRIG1]     "After",
    [M_ACT1]      "Action1",
    [M_TRIG2]     "After",
    [M_ACT2]      "Action2",
    [M_TRIG3]     "After",
    [M_ACT3]      "Action3",
    [M_TSR]       "In Background",
    /*[M_SEP]*/
    [M_SAVE]      "Save",
    [M_RUN]       "Run",
    [M_EXIT]      "Exit",

};

static const int setting_icons[M_ITEM_COUNT] =
{//Icon_NOICON
[M_DELAY]Icon_Menu_setting,
[M_ACTDELAY]Icon_Playback_menu,
[M_USECURVOL]Icon_Questionmark,
[M_STARTVOL]Icon_EQ,
[M_ENDVOL]Icon_Audio,
[M_TIMEOUT]Icon_Config,
[M_TRIG1]Icon_Menu_setting,
[M_ACT1]Icon_Menu_functioncall,
[M_TRIG2]Icon_NOICON,
[M_ACT2]Icon_NOICON,
[M_TRIG3]Icon_NOICON,
[M_ACT3]Icon_NOICON,
[M_SEP]Icon_NOICON,
[M_SAVE]Icon_Submenu,
[M_EXIT]Icon_Reverse_Cursor,
};

//static int setting_table[M_ITEM_COUNT]={0};
static struct viewport viewport[NB_SCREENS];

static struct fade_settings
{
    int  vol_orig;
    int  vol_min;
    int  vol_max;
    int  vol_step;
    int  vol_cur;
    bool fade_out;
    long timeout_ticks;
    long ticks_remaining;

    int  audio_status;
    int  duration;
    int  delay_duration;
    int  vol_init;
    bool init_use_cur;
    int  vol_final;
    bool final_use_cur;
    int  actions;
    int  sleep;
    bool tsr;
} fade_set;

static struct settings
{
    uint32_t  crc;
    char      play[MAX_PATH];
    int       table[M_ITEM_COUNT];

} settings;

static const char * dyn_menu_get_entry(int selected_item, void * data,
                                       char * buffer, size_t buffer_len)
{
    //(void)data;
    (void)buffer;
    (void)buffer_len;
    char * menustr = ((char**) data)[selected_item];
    return menustr;
}

static enum themable_icons menu_get_icon(int selected_item, void * data)
{
    (void)data;
    if ((unsigned) selected_item >= M_ITEM_COUNT)
        return Icon_NOICON;
    return setting_icons[selected_item];//data[selected_item];// : Icon_NOICON;
}

static int do_dyn_menu(const char* title,
                       char* strentry, const char ** entrylist, int entries,
                       int *cur_selection, int *list_start,
                       list_get_icon *icon_callback,
                       struct viewport parent[NB_SCREENS], bool hide_theme)
{

/*
    Creates a menu, can use non constant strings by supplying a comma
    separated list of menu items via strentry ex. "item1;item2;item3;item4"
    strentry may be a buffer filled by any means (sprintf) or a string literal
    but NOT const since every separator ';' will be replaced by a '\0'
    ALTERNATIVELY can use a list of const strings via entrylist
    ex. static const char * menu_str[] ={"item1", "item2", "item3"};

do_dyn_menu(titlestr, menustr, entrylist, entries, start, hidetheme)
titlestr   - title for the menu
menustr    - comma separated list of menu entries
entrylist  - ALTERNATIVE to menustr pointer to const string list
entries    - number of entries in menustr if > MAX_MENU_ENTRIES will be truncated.
cur_sel    - sets menu item selected, returns users selection
list_start - sets menu entry at top of list, keeps position between calls
hide_theme - hides theme elements such as the status bar and background
returns selected item index, -2 if canceled, <0 on other error
*/
    enum{MAX_DYN_MENU_ENTRIES=M_ITEM_COUNT +1 };
    static const char* menu_strings[MAX_DYN_MENU_ENTRIES];
    char *remain, *token = NULL;
    const char * const sep= ";";

    struct gui_synclist list;
    int result = 0;
    int action;
    entries = MIN(MAX_DYN_MENU_ENTRIES, entries);
    if(menu_strings != NULL && entrylist == NULL)
    {
        for(int i=0; i < entries; i++)
        {
            token = rb->strtok_r(strentry, sep, &remain);
            strentry = NULL; /*strtok will continue with sequence*/
            menu_strings[i] = (token != NULL)? token : "???";
        }
        entrylist = menu_strings;
    }
   
    rb->gui_synclist_init(&list, dyn_menu_get_entry, entrylist, true, 1, parent);

    rb->gui_synclist_set_title(&list, (unsigned char*) title, Icon_Rockbox);
    if (icon_callback)
        rb->gui_synclist_set_icon_callback(&list, *icon_callback);
    rb->gui_synclist_set_nb_items(&list, entries);
    rb->gui_synclist_select_item(&list, (cur_selection) ? *cur_selection : 0);

    FOR_NB_SCREENS(i)
    {   
        if (list_start)
            list.start_item[i] = *list_start;
        rb->viewportmanager_theme_enable(i, !hide_theme, parent);
    }
 
    rb->gui_synclist_draw(&list);

    while (result == 0)
    {
        action = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
        if (rb->gui_synclist_do_button(&list, &action, LIST_WRAP_UNLESS_HELD))
            continue;


        switch (action)
        {
            case ACTION_STD_OK:
            {
                result = 1;
                break;
            }
            case ACTION_STD_CANCEL:
            {
                result = -1;
                break;
            }
        }
    }

    if (cur_selection)
        *cur_selection = rb->gui_synclist_get_sel_pos(&list);

    FOR_NB_SCREENS(i)
    {
        rb->viewportmanager_theme_undo(i, false);
        if (list_start)
        {
            if (i == 0)
                *list_start = 0;
            *list_start |= list.start_item[i];
        }   
    }


return result;
}

/*
unsigned int ms_to_ticks(unsigned int ms)
{
    return ms/MS_IN_TICK;
}

unsigned int ticks_to_ms(unsigned int ticks)
{
    return ticks * MS_IN_TICK;
}
*/
/*  time_split_units()
    split time values depending on base unit
    unit_idx: UNIT_HOUR, UNIT_MIN, UNIT_SEC, UNIT_MS
    abs_value: absolute time value
    units_in: array of unsigned ints with UNIT_IDX_TIME_COUNT fields
*/
static unsigned int time_split_units(int unit_idx, unsigned long abs_val,
                              unsigned int (*units_in)[UNIT_IDX_TIME_COUNT])
{
    unsigned int base_idx = UNIT_IDX_HR;
    int hours;
    int minutes  = 0;
    int seconds  = 0;
    int millisec = 0;

    switch (unit_idx & UNIT_IDX_MASK) /*Mask off upper bits*/
    {
            case UNIT_MS:
                base_idx = UNIT_IDX_MS;
                millisec = abs_val;
                abs_val  = abs_val  /  1000U;
                millisec = millisec - (1000U * abs_val);
                /* fallthrough and calculate the rest of the units */
            case UNIT_SEC:
                if (base_idx == UNIT_IDX_HR)
                    base_idx = UNIT_IDX_SEC;
                seconds  = abs_val;
                abs_val  = abs_val  / 60U;
                seconds  = seconds - (60U * abs_val);
                /* fallthrough and calculate the rest of the units */
            case UNIT_MIN:
                if (base_idx == UNIT_IDX_HR)
                    base_idx = UNIT_IDX_MIN;
                minutes  = abs_val;
                abs_val  = abs_val / 60U;
                minutes  = minutes -(60U * abs_val);
                /* fallthrough and calculate the rest of the units */
            case UNIT_HOUR:
            default:
                hours    = abs_val;
                break;
    }

    (*units_in)[UNIT_IDX_HR]  = hours;
    (*units_in)[UNIT_IDX_MIN] = minutes;
    (*units_in)[UNIT_IDX_SEC] = seconds;
    (*units_in)[UNIT_IDX_MS]  = millisec;

    return base_idx;
}

/* format_time_auto - return an auto ranged time string;
   buffer:  needs to be at least 64 characters

   unit_idx: specifies lowest or base index of the value
   add | UNIT_LOCK_ to prevent autorange below this index
   add | UNIT_TRIM_ZERO to supress leading zero on the largest unit

   value: should be passed in the same form as unit_idx

   supress_unit: if true unit string is NOT printed

   idx_pos[2]: (if !NULL) [0] specifies an index of interest,
   the offset and width for that index will be returned.
   In field [0] offset, field [1] length
   Ex: given 12:34:56.78 if you pass the idx_pos UNIT_IDX_MIN
   idx_pos returns -> {3,2}.. offset(3) and length(2) = '34'
*/
static const char *format_time_auto(char *buffer, int buf_len, const long value,
                                  int unit_idx, bool supress_unit,
                                  unsigned char (*idx_pos)[2])
{
    const char * const sign        = &"-"[value < 0 ? 0 : 1];   
    bool               is_rtl      = rb->lang_is_rtl();
    unsigned int       timebuf_len = 30; /* -2147483648:00:00.00\0 */
    char               *timebuf;/*timebuf[24]; shared with buffer instead*/
    int                len, left_offset;
    unsigned char      base_idx, max_idx;

    unsigned int       units_in[UNIT_IDX_TIME_COUNT];
    char               fwidth[UNIT_IDX_TIME_COUNT] =
                       {
                            [UNIT_IDX_HR]  = 0, /* hr is variable length */
                            [UNIT_IDX_MIN] = 2,
                            [UNIT_IDX_SEC] = 2,
                            [UNIT_IDX_MS]  = 3,
                       }; /* {0,2,2,3}; Field Widths*/
    unsigned char      offsets[UNIT_IDX_TIME_COUNT] =
                       {
                            [UNIT_IDX_HR]  = 10,/* ?:59:59.999 Std offsets */
                            [UNIT_IDX_MIN] = 7, /*0?:+1:+4.+7 need calculated */
                            [UNIT_IDX_SEC] = 4,/* 999.59:59:0  RTL offsets */
                            [UNIT_IDX_MS]  = 0,/* 0  .4 :7 :10 won't change */
                       }; /* {10,7,4,0}; Offsets*/
    static const int   unitlock[UNIT_IDX_TIME_COUNT] =
                       {
                            [UNIT_IDX_HR]  = UNIT_LOCK_HR,
                            [UNIT_IDX_MIN] = UNIT_LOCK_MIN,
                            [UNIT_IDX_SEC] = UNIT_LOCK_SEC,
                            [UNIT_IDX_MS]  = 0,
                       }; /* unitlock*/
    static const int   units[UNIT_IDX_TIME_COUNT] =
                       {
                            [UNIT_IDX_HR]  = UNIT_HOUR,
                            [UNIT_IDX_MIN] = UNIT_MIN,
                            [UNIT_IDX_SEC] = UNIT_SEC,
                            [UNIT_IDX_MS]  = UNIT_MS,
                       }; /* units*/

    buf_len = buf_len - (timebuf_len + 2);
    timebuf = &buffer[buf_len + 1]; /* use part of the supplied buffer */
    if (buf_len < 32)
        return buffer;

    if (idx_pos != NULL)
    {
        (*idx_pos)[0] = MIN((*idx_pos)[0], UNIT_IDX_TIME_COUNT - 1);
        unit_idx |= unitlock[(*idx_pos)[0]];
    }

    base_idx = time_split_units(unit_idx, abs(value), &units_in);

    if (units_in[UNIT_IDX_HR] || (unit_idx & unitlock[UNIT_IDX_HR]))
        max_idx = UNIT_IDX_HR;
    else if (units_in[UNIT_IDX_MIN] || (unit_idx & unitlock[UNIT_IDX_MIN]))
        max_idx = UNIT_IDX_MIN;
    else if (units_in[UNIT_IDX_SEC] || (unit_idx & unitlock[UNIT_IDX_SEC]))
        max_idx = UNIT_IDX_SEC;
    else if (units_in[UNIT_IDX_MS])
        max_idx = UNIT_IDX_MS;
    else /* value is 0*/
        max_idx = base_idx;

    if (!is_rtl)
    {
        len = snprintf(timebuf, timebuf_len,
                       "%02d:%02d:%02d.%03d",
                       units_in[UNIT_IDX_HR],
                       units_in[UNIT_IDX_MIN],
                       units_in[UNIT_IDX_SEC],
                       units_in[UNIT_IDX_MS]);

        fwidth[UNIT_IDX_HR]   = len - offsets[UNIT_IDX_HR];

        offsets[UNIT_IDX_MS]  = fwidth[UNIT_IDX_HR] + offsets[UNIT_IDX_MIN];
        offsets[UNIT_IDX_SEC] = fwidth[UNIT_IDX_HR] + offsets[UNIT_IDX_SEC];
        offsets[UNIT_IDX_MIN] = fwidth[UNIT_IDX_HR] + 1;
        offsets[UNIT_IDX_HR]  = 0;

        timebuf[offsets[base_idx] + fwidth[base_idx]] = '\0';

        left_offset  = -(offsets[max_idx]);
        left_offset += rb->strlcpy(buffer, sign, buf_len);

        /* trim leading zero on the max_idx */
        if ((unit_idx & UNIT_TRIM_ZERO) == UNIT_TRIM_ZERO &&
            timebuf[offsets[max_idx]] == '0')
        {
            offsets[max_idx]++;
        }

        rb->strlcat(buffer, &timebuf[offsets[max_idx]], buf_len);

        if (!supress_unit)
        {
            rb->strlcat(buffer, " ", buf_len);
            rb->strlcat(buffer, unit_strings[units[max_idx]], buf_len);
        }

    }
    else /*RTL Languages*/
    {
        len = snprintf(timebuf, timebuf_len,
                       "%03d.%02d:%02d:%02d",
                       units_in[UNIT_IDX_MS],
                       units_in[UNIT_IDX_SEC],
                       units_in[UNIT_IDX_MIN],
                       units_in[UNIT_IDX_HR]);

        fwidth[UNIT_IDX_HR] = len - offsets[UNIT_IDX_HR];

        left_offset = -(offsets[base_idx]);

        /* trim leading zero on the max_idx */
        if ((unit_idx & UNIT_TRIM_ZERO) == UNIT_TRIM_ZERO &&
            timebuf[offsets[max_idx]] == '0')
        {
            timebuf[offsets[max_idx]] = timebuf[offsets[max_idx]+1];
            fwidth[max_idx]--;
        }

        timebuf[offsets[max_idx] + fwidth[max_idx]] = '\0';

        if (!supress_unit)
        {
            rb->strlcpy(buffer, unit_strings[units[max_idx]], buf_len);
            left_offset += rb->strlcat(buffer, " ", buf_len);
            rb->strlcat(buffer, &timebuf[offsets[base_idx]], buf_len);
        }
        else
            rb->strlcpy(buffer, &timebuf[offsets[base_idx]], buf_len);

        rb->strlcat(buffer, sign, buf_len);
    }

    if (idx_pos != NULL)
    {
        (*idx_pos)[1]= fwidth[*(idx_pos)[0]];
        (*idx_pos)[0]= left_offset + offsets[(*idx_pos)[0]];
    }
    return buffer;
}


static void draw_horiz_scrollbar(struct screen * display, int top, int height, int cur_pct)
{
    rb->gui_scrollbar_draw(display,                  /* screen */
                           5,                /* x */
                           top,                    /* y */
                           LCD_WIDTH-10, /* width */
                           height ,              /* height */
                           100,                 /* items */
                           0,                  /* min_shown */
                           cur_pct,           /* max_shown */
                           HORIZONTAL);      /* flags */
}

static inline int get_button(void)
{
    return pluginlib_getaction(HZ/10, plugin_contexts,
                               ARRAYLEN(plugin_contexts));
}

static bool can_play(void)
{
    int audio_status = rb->audio_status();
    if ((!audio_status && rb->global_status->resume_index != -1)
        && (rb->playlist_resume() != -1)) {
        return true;
    }
    else if (audio_status & AUDIO_STATUS_PLAY)
        return true;

    return false;
}

static void resume_audio(bool from_stop)
{
    int audio_status = rb->audio_status();
    //rb->playlist_resume()
    if (from_stop && !audio_status && rb->global_status->resume_index != -1) {
        if (rb->playlist_resume() != -1) {
            rb->playlist_resume_track(rb->global_status->resume_index,
                rb->global_status->resume_crc32,
                rb->global_status->resume_elapsed,
                rb->global_status->resume_offset);
        }
    }
    else if (audio_status & AUDIO_STATUS_PLAY)
        rb->audio_resume();
}

static void pause_audio(void)
{
    if (rb->audio_status() & AUDIO_STATUS_PLAY)
        rb->audio_pause();
}

int value_to_pct(int value, int min, int max)
{
    int pct = 0;
    pct = ((value * 100 - min *100)/(abs(max-min)));

    return pct;
}

static int clamp_int(int value, int min, int max)
{
    int tmp = 0;
    if (min > max)
    { /* Swap max and min if needed */
        tmp = max;
        max = min;
        min = tmp;
    }

    if (value < min)
        value = min;
    else if (value > max)
        value = max;

    return value;
}

static int do_select_clamped(int v1, int v2, bool use_v2, int v_min, int v_max)
{
    /*
        selects val1 or val2 by bool use_v2
        false = val1, true = val2
        0     = val1  1    = val2
        returns selected clamped to v_min/v_max
    */
    int value;
    if (use_v2)
        value = v2;
    else
        value = v1;

    return clamp_int(value, v_min, v_max);
}

static int clamp_int_rollover(int value, int min, int max)
{
    int tmp = 0;
    if (min > max)
    { /* Swap max and min if needed */
        tmp = max;
        max = min;
        min = tmp;
    }

    if (value < min)
        value = max;
    else if (value > max)
        value = min;

    return value;
}

static int do_volume_change(int vol_step, int vol_min, int vol_max)
{
    /*
        Negative numbers lower volume
        Positive numbers raise volume
        clamps to vol_min/volmax
        returns current volume
    */
    rb->button_clear_queue(); //clear any pending buttons
    int vol = rb->sound_get_current(SOUND_VOLUME);

    vol = clamp_int(vol + vol_step, vol_min, vol_max);

    rb->sound_set_current(SOUND_VOLUME, vol);

    return vol;
}

static void do_set_fade(struct fade_settings * fade_set)
{

    int duration = fade_set->duration;
    int vol_min = rb->sound_min(SOUND_VOLUME);
    int vol_max = rb->sound_max(SOUND_VOLUME);
    int vol_orig = rb->sound_get_current(SOUND_VOLUME); /* get current volume */
    int vol_cur; /*vol_orig is the global volume not necessarily current volume*/
    int vol_init;
    int vol_final;
    int vol_step;
    int vol_range;
    bool fade_out;
    long ticks_remaining;
    long timeout_ticks;

    vol_init = do_select_clamped(vol_orig, fade_set->vol_init,
                                 !fade_set->init_use_cur, vol_min, vol_max);
    fade_set->vol_init = vol_init;
    vol_cur = vol_init;

    vol_final = do_select_clamped(vol_orig, fade_set->vol_final,
                                  !fade_set->final_use_cur, vol_min, vol_max);
    fade_set->vol_final = vol_final;

    if (vol_init > vol_final)
    {
        fade_out = true;
        vol_range = vol_init - vol_final; //abs()
        vol_step = -1;
    }
    else
    {
        fade_out = false;
        vol_range = vol_final - vol_init;//abs()
        vol_step = 1;
    }

    fade_set->fade_out = fade_out;

    fade_set->vol_step = vol_step;
    ticks_remaining = HZ * duration;
    if (ticks_remaining < HZ)
        ticks_remaining = HZ;

    fade_set->ticks_remaining = ticks_remaining;

    timeout_ticks = (ticks_remaining / vol_range);
    if (timeout_ticks < HZ/10)
        timeout_ticks = HZ/10;

    fade_set->timeout_ticks = timeout_ticks;

    fade_set->vol_min = vol_min;

    fade_set->vol_max = vol_max;

    fade_set->vol_cur = vol_cur;
    fade_set->vol_orig = vol_orig;
}

static bool do_fade(struct fade_settings * fade_set)
{
    int vol_min  = fade_set->vol_init;
    int vol_max  = fade_set->vol_final;
    int vol_step = fade_set->vol_step;
    int vol_cur  = fade_set->vol_cur;
    int ret = true;

    vol_cur = do_volume_change(vol_step, vol_min, vol_max);
    if (fade_set->vol_cur == vol_cur)
    {
        rb->splashf(HZ, "usr = %d", vol_cur);
    }
    if (vol_cur == vol_max)
    {
        ret = false;
        rb->splashf(HZ, "v = %d", vol_cur);
    }
    fade_set->vol_cur = vol_cur;
    return ret;
}

static int get_button_dpad(int *value, int *h_pos, int * v_pos, unsigned int multiplier)
{
/*  Allow user to use left,right, up, down, and (scroll) buttons to set a value
    single button press increments/decrements value by 1 * multiplier
    repeat button press accelerates inc/dec by multiplier * 2
    if h_pos is not NULL then left decrements h_pos, right increments
    if v_pos is not NULL then down decrements v_pos, up increments
    h_pos/v_pos being defined blocks actions for respective
    single press inc/dec of value
    Returns 0 if no button matched,
            -1 if canceled,
            1 if value selected
*/
    int button;
    int exit_val = 0;


        button = get_button();
        switch (button)
        {
            case PLA_RIGHT:
                if (h_pos) { *h_pos += 1; break; }
            case PLA_UP:
                if (v_pos) { *v_pos += 1; break; }
                *value+=1 * multiplier;
                break;
            case PLA_RIGHT_REPEAT:
            case PLA_UP_REPEAT:
                *value+=2 * multiplier;
                break;
#ifdef HAVE_SCROLLWHEEL
            case PLA_SCROLL_FWD:
                *value+=1 * multiplier;
                break;
            case PLA_SCROLL_FWD_REPEAT:
                *value+=2 * multiplier;
                break;
#endif
            case PLA_LEFT:
                if (h_pos) { *h_pos -= 1; break; }
            case PLA_DOWN:
                if (v_pos) { *v_pos -= 1; break; }
                *value-=1 * multiplier;
                break;
            case PLA_LEFT_REPEAT:
            case PLA_DOWN_REPEAT:
                *value-=2 * multiplier;
                break;
#ifdef HAVE_SCROLLWHEEL
            case PLA_SCROLL_BACK:
                *value-=1 * multiplier;
                break;
            case PLA_SCROLL_BACK_REPEAT:
                *value -=2 * multiplier;
                break;
#endif
            case PLA_SELECT:
            case PLA_SELECT_REPEAT:
                exit_val = 1;
                break;
            case PLA_EXIT:
            case PLA_CANCEL:
                exit_val  = -1;
                break;

            default:
                exit_val  = 0;
                break;
    }


    return exit_val;
}
void pop_up_frame(struct screen * display, int x, int y, int width, int height)
{
/*
|___________|
|           |
|           |
|___________|
*/

    //int title_height = display->getcharheight() + 5;
    //display->puts_scroll(1, 0, title_buf);
//void (*lcd_fillrect)(int x, int y, int width, int height);
    display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
    display->fillrect(x, y, width, height);
    display->set_drawmode(DRMODE_SOLID);
    display->hline(0, width, height/3);
    display->hline(1, width-1, height);
    display->vline(x, 0, height-1);
    display->vline(width-x, 0, height-1);
   

}
int do_set_value(const char * title, int val, int val_min, int val_max, const char * val_lbl)
{
    struct screen * display = rb->screens[SCREEN_MAIN];
    int title_height = display->getcharheight() + 5;
    char title_buf[48]= "\0";
    int initial_val = val;
    int ret = 0;

    FOR_NB_SCREENS(i)   
        rb->viewportmanager_theme_enable(i, false, NULL);
    //rb->lcd_clear_display();
        pop_up_frame(display, 0, 0, LCD_WIDTH-1, title_height * 3);
    while(ret == 0)
    {
        ret = get_button_dpad(&val, NULL, NULL, 1); /* allow user to set value*/

        val = clamp_int_rollover(val, val_min, val_max);

        draw_horiz_scrollbar(display, title_height * 2, title_height/2,
                             value_to_pct(val, val_min, val_max));

        rb->snprintf(title_buf, sizeof(title_buf), "%s %d %s   ",
                                                   title, val, val_lbl);

        display->puts_scroll(1, 0, title_buf);
        display->update();
        rb->sleep(HZ/100);
    }
    if (ret == -1)
    {
        val = initial_val;
        rb->splash(HZ/2, "Canceled");
    }
    display->clear_display();
    FOR_NB_SCREENS(i)
        rb->viewportmanager_theme_undo(i, true);
   
    //display->update();
    return val;
}

int do_set_time(const char * title, int sec, int sec_min, int sec_max)
{
    char time_buf[64]= "\0";
    const int time_buf_sz = sizeof(time_buf);
    struct screen *display = rb->screens[SCREEN_MAIN];

    int title_height = display->getcharheight() + 5;
    int is_rtl = rb->lang_is_rtl();

    const int initial_sec = sec; /* on cancel original time returned*/
    int ret = 0;

    enum {FIELD_ST = 0, FIELD_CT = 3};
    unsigned char idx_pos[2];
    int field_pos = (is_rtl? FIELD_ST : FIELD_CT - 1);
    int rel_field; /* If lang is RTL then fields/cols are reversed */

    static const unsigned char field_cols[FIELD_CT] =
                               {UNIT_IDX_HR, UNIT_IDX_MIN, UNIT_IDX_SEC};

    static const unsigned int  multiplier_cols[FIELD_CT] = {3600, 60, 1};

    int offset = 1;
    const int lcd_chr_width = (LCD_WIDTH/rb->lcd_getstringsize("W", NULL, NULL));

    //rb->lcd_clear_display();

    pop_up_frame(display, 0, 0, LCD_WIDTH-1, title_height * 3);
    while(ret == 0)
    {

        field_pos = clamp_int_rollover(field_pos, FIELD_ST, FIELD_CT - 1);
        /* if lang is RTL then columns flip */
        rel_field = (is_rtl? FIELD_CT - field_pos - 1 : field_pos);

        idx_pos[0] = field_cols[rel_field];

        ret = get_button_dpad(&sec, &field_pos, NULL, multiplier_cols[rel_field]);
        /* allow user to set value*/

        sec = clamp_int_rollover(sec, sec_min, sec_max);

        rb->lcd_puts_scroll(offset, 2, "             ");

        format_time_auto(time_buf, time_buf_sz, sec, UNIT_SEC, false, &idx_pos);

        offset = (lcd_chr_width - rb->strlen(time_buf))/2;
        rb->lcd_puts_scroll(offset, 2, time_buf);

        rb->lcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
        time_buf[idx_pos[0] + idx_pos[1]] = '\0';
        /*draw selected position inverse*/
        rb->lcd_puts_scroll(offset + idx_pos[0], 2, &time_buf[idx_pos[0]]);
        rb->lcd_set_drawmode(DRMODE_SOLID);

        rb->lcd_puts_scroll(1,0,title);
        rb->lcd_update();
        rb->sleep(HZ/100);
    }
    if (ret == -1)
    {
        sec = initial_sec;
        rb->splash(HZ/2, "Canceled");
    }
    rb->lcd_clear_display();
    rb->lcd_update();

    return sec;
}

uint32_t crc_settings(struct settings * set)
{
    //struct settings set_tmp

    /* some fields are ignored in crc calculation */
    uint32_t current_crc = set->crc;
    set->crc = 0;

    int vol_start = set->table[M_STARTVOL];
    int vol_end = set->table[M_ENDVOL];

    if (set->table[M_USECURVOL] == CURVOL_START)
        set->table[M_STARTVOL] = 0;
    if (set->table[M_USECURVOL] == CURVOL_END)
        set->table[M_ENDVOL] = 0;
   
    set->table[M_SAVE] = 0;

    uint32_t crc = rb->crc_32(set, sizeof(*set), 0xffffffff);

    /* put back ignored fields */
    set->crc = current_crc;
    set->table[M_STARTVOL] = vol_start;
    set->table[M_ENDVOL] = vol_end;
    return crc;
}

void load_settings(struct settings * settings, char* filename)
{
    int fh = rb->open(filename, O_RDONLY);
    struct settings settings_load;

    /* defaults */
    settings->play[0] = '\0';

    if(fh >= 0)
    { /* does file exist? */
        /* basic consistency check */
        if(rb->filesize(fh) == sizeof(*settings))
        {
            rb->read(fh, &settings_load, sizeof(struct settings));
            if (settings_load.crc == crc_settings(&settings_load))
            {
                rb->memcpy(settings, &settings_load, sizeof(struct settings));
            }
            else
                rb->splashf(HZ * 5, "CRC Check Failed %s", filename);
        }
        else
            rb->splashf(HZ * 5, "Incorrect File Size %s", filename);
        rb->close(fh);
    }

}

void save_settings(struct settings * settings, char* filename)
{
    int fd = rb->creat(filename, 0666);
    settings->crc = crc_settings(settings);
    if(fd >= 0)
    { /* does file exist? */
        rb->write (fd, settings, sizeof(*settings));
        rb->close(fd);
    }
}


void scroll_table_rollover(int * p_sel, int entries, int direction)
{
    int sel = *p_sel;
    if (direction < 0)
        (sel)--;
    else
        (sel)++;

    *p_sel = clamp_int_rollover(sel, 0, entries - 1);
}

static bool play_load(const char* filename)
{
    struct browse_context browse;
    if (rb->file_exists(filename))
    {
        rb->browse_context_init(&browse, 0, BROWSE_RUNFILE, "", 0, filename, "");
        rb->rockbox_browse(&browse);
        return true;
    }
    else
        rb->splashf(HZ * 2, "Error: %s Does Not Exist", filename);
    return false;
}

static bool play_picker(struct settings *settings)
{
    bool ret = false;
    struct browse_context browse;
    rb->browse_context_init(&browse, SHOW_PLAYLIST,
                            BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
                            "Play", Icon_Menu_setting, "/", "");

    browse.buf = settings->play;
    browse.bufsize = sizeof(settings->play);

    rb->rockbox_browse(&browse);

    if (browse.flags & BROWSE_SELECTED)
    {
        rb->splashf(HZ * 2, "%s", settings->play);
        if (rb->file_exists(settings->play))
            ret = true;
        //play_load("/test.txt");
    }
    return ret;
}

static void do_set_volume_table_values(struct settings *set)
{
/* user can elect to use the current volume level as the start or end volume */
    int vol = rb->sound_get_current(SOUND_VOLUME);
    int vol_min = rb->sound_min(SOUND_VOLUME);
    int vol_max = rb->sound_max(SOUND_VOLUME);
    set->table[M_STARTVOL] = do_select_clamped(set->table[M_STARTVOL], vol,
                                        set->table[M_USECURVOL] == CURVOL_START,
                                                               vol_min, vol_max);

    set->table[M_ENDVOL] = do_select_clamped(set->table[M_ENDVOL], vol,
                                           set->table[M_USECURVOL] == CURVOL_END,
                                                                vol_min, vol_max);
}

static void do_set_action_table_values(struct settings *set)
{
    if (set->table[M_TRIG1] == TRIG_NONE)
        set->table[M_ACT1] = ACT_NONE;
    if (set->table[M_TRIG2] == TRIG_NONE)
        set->table[M_ACT2] = ACT_NONE;
    if (set->table[M_TRIG3] == TRIG_NONE)
        set->table[M_ACT3] = ACT_NONE;
}

static void init_table_settings(struct settings *set)
{
    do_set_volume_table_values(set);
    do_set_action_table_values(set);

    set->table[M_SAVE] = (set->crc == crc_settings(set))? SAV_SAVED : SAV_SAVE;
}

static void event_callback(unsigned short id, void *param)
{

    static int last_audio = AUDIO_STATUS_ERROR;
    static int last_screen = 0;
    static bool backlight_on = true;
    int audio_status;
    int screen;
    int last_vol_change;
    if (((struct track_event *)param)->flags & TEF_REWIND)//TEF_AUTO_SKIP
        return; /* Not a true track end */

    switch(id)
    {
        case PLAYBACK_EVENT_START_PLAYBACK:
            triggered_events |= WATCHED_EV(TRIG_EVENT_START_PLAYBACK);
            break;
        case PLAYBACK_EVENT_TRACK_SKIP:
            if (!(((struct track_event *)param)->flags & TEF_AUTO_SKIP))
                triggered_events |= WATCHED_EV(TRIG_EVENT_TRACK_SKIP);
            break;
        case PLAYBACK_EVENT_TRACK_CHANGE:
            triggered_events |= WATCHED_EV(TRIG_EVENT_TRACK_CHANGE);
            break;
        case PLAYBACK_EVENT_TRACK_FINISH:
            if (!(((struct track_event *)param)->flags & TEF_REWIND))//TEF_AUTO_SKIP
                triggered_events |= WATCHED_EV(TRIG_EVENT_TRACK_FINISH);
            break;
        case SELF_EVENT_FINALVOL_REACHED:
            triggered_events |= WATCHED_EV(TRIG_EVENT_VOL_FINAL);
            break;
        case SELF_EVENT_TIMEOUT_REACHED:
            triggered_events |= WATCHED_EV(TRIG_EVENT_TIMEOUT);
            break;
        case SELF_EVENT_CHECK_TRIGGERED:
            last_vol_change = rb->global_status->last_volume_change;
            if (last_vol_change + TIMEOUT_VOL_CHANGE < *rb->current_tick)
                 triggered_events |= WATCHED_EV(TRIG_EVENT_VOL_CHANGED
--- End code ---

tdb:
Okay, thanks for your answer - there is no rush (it is not a bug and with the lua script there is already something that works good enough).

Still it is great to see new developments, not everyone is reading the forum so it would be nice to have it out of the box especially for these users.
As mentioned before script is working fine for me (and continuous to be incredibly helpful)

I was just curious about the status - maybe I will celebrate existence of the script with a yearly  friendly status update request ;)   



   


Bilgus:
fade to sleep plugin can now be found in plugins/demos/lua_scripts

tdb:
Haven't checked this thread for a while, but nice to see the script included. Still using it - almost daily.

Bilgus:
I'm pretty sure I edited it to include the new sleep timer functionality and ability to read the starting volume level

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version