⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 userif.c

📁 Internet Radio Internet Radio Internet Radio Internet Radio Internet Radio
💻 C
字号:
/*
 * Copyright (C) 2003-2007 by egnite Software GmbH. All rights reserved.
 * Copyright (C) 2008 by egnite GmbH. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * For additional information see http://www.ethernut.de/
 *
 */

/*!
 * \file userif.c
 * \brief User interface.
 *
 * \verbatim
 *
 * $Log$
 *
 * \endverbatim
 */

#include <cfg/lcd.h>

#include <stdio.h>
#include <io.h>
#include <stdlib.h>
#include <string.h>

#include <dev/term.h>
#include <dev/vscodec.h>

#include <sys/thread.h>
#include <sys/timer.h>
#include <sys/event.h>

#include <arpa/inet.h>

#include "config.h"
#include "logmsg.h"
#include "favlist.h"
#include "webradio.h"
#include "shoutcast.h"
#include "xmlserv.h"
#include "buttons.h"
#include "userif.h"

#ifndef UI_THREAD_STACK
#ifdef AT91SAM7X_EK
#define UI_THREAD_STACK 512
#else
#define UI_THREAD_STACK 2048
#endif
#endif

#ifndef UI_REFRESH_RATE
#define UI_REFRESH_RATE 300
#endif

/*!
 * \def LCD_VCOLS
 * \brief Number of virtual columns.
 *
 * Contents will be scrolled, if it doesn't fit on a single LCD line.
 */
#ifndef LCD_VCOLS
#ifdef AT91SAM7X_EK
#define LCD_VCOLS    40
#else
#define LCD_VCOLS    160
#endif
#endif

typedef struct {
    /*! \brief Status text. */
    char dln_status[LCD_VCOLS + 1];
    /*! \brief Message text. */
    char dln_msg[LCD_VCOLS + 1];
#if USE_DISPLAY || USE_TERM
    u_int dln_step;
    u_int dln_len;
#endif
    u_int dln_msgticks;
} DISPLAYLINE;

typedef struct {
#if USE_DISPLAY || USE_TERM
    /*! \brief Stream handle of LCD device. */
    FILE *dpi_stream;
    /*! \brief General buffer. */
    char dpi_buff[LCD_COLS + 1];
#endif
    DISPLAYLINE dpi_line[LCD_ROWS];
} DISPLAYINFO;

static DISPLAYINFO display;


#if USE_DISPLAY || USE_TERM
#ifndef LCD_SCROLL_GAP
#define LCD_SCROLL_GAP  (LCD_COLS / 2)
#endif
#endif

/*!
 * \brief Refresh the LCD.
 */
static void DisplayRefresh(void)
{
#if USE_DISPLAY || USE_TERM
    u_int ln;
    u_int cn;
    DISPLAYLINE *dlp;
    char *line;

    for (ln = 0; ln < LCD_ROWS; ln++) {
        dlp = &display.dpi_line[ln];

        /*
         * Determine if we display a message or just the current status.
         */
        if (dlp->dln_msgticks) {
            /* Displaying a message. */
            line = dlp->dln_msg;
            dlp->dln_msgticks--;
        } else {
            /* Displaying the status. */
            line = dlp->dln_status;
        }

        /* 
         * Re-initialize scrolling, if the line length changed.
         */
        if (dlp->dln_len != strlen(line)) {
            dlp->dln_len = strlen(line);
            dlp->dln_step = 0;
        }

        /* Position the cursor. */
        fprintf(display.dpi_stream, ESC_POS "%c" "\x20", ln + 32);

        if (dlp->dln_len <= LCD_COLS) {
            /* No scrolling required. */
            fputs(line, display.dpi_stream);
            if (dlp->dln_len < LCD_COLS) {
                fputs(ESC_CLREOL, display.dpi_stream);
            }
        }
        else {
            for (cn = 0; cn < LCD_COLS; cn++) {
                if (dlp->dln_step + cn < dlp->dln_len) {
                    display.dpi_buff[cn] = line[dlp->dln_step + cn];
                }
                else if (dlp->dln_step + cn < dlp->dln_len + LCD_SCROLL_GAP) {
                    display.dpi_buff[cn] = ' ';
                }
                else {
                    display.dpi_buff[cn] = line[dlp->dln_step + cn - (dlp->dln_len + LCD_SCROLL_GAP)];
                }
            }
            fputs(display.dpi_buff, display.dpi_stream);

            /* Update the scroll position counter. */
            dlp->dln_step++;
            if (dlp->dln_step >= dlp->dln_len + LCD_SCROLL_GAP) {
                dlp->dln_step = 0;
            }
        }
    }
#endif /* USE_DISPLAY */
}

/*!
 * \brief Displays configured information about a station.
 *
 * \param scp Pointer to the configuration table entry.
 */
static void UserIfShowStationConf(RADIOSTATION * scp)
{
    display.dpi_line[0].dln_status[0] = 0;
    if (scp && scp->rs_name && scp->rs_name[0]) {
        strncpy(display.dpi_line[0].dln_status, scp->rs_name, LCD_VCOLS);
    }
}

/*!
 * \brief Displays station information.
 *
 * Called during connect.
 *
 * \param sip Information structure of the connected station.
 */
static void UserIfShowStationInfo(STATIONINFO * sip)
{
    display.dpi_line[0].dln_status[0] = 0;
    if (sip) {
        if (sip->si_name && sip->si_name[0]) {
            strncpy(display.dpi_line[0].dln_status, sip->si_name, LCD_VCOLS);
        } else {
            UserIfShowStationConf(sip->si_scp);
        }
    }
}

#if USE_DISPLAY || USE_TERM
/*!
 * \brief Main menu processing.
 *
 */
static void UserIfMainMenu(void)
{
    char key;
    u_int tmocnt = 0;
    int want = 0;

    for (;;) {
        UserIfShowStationConf(&favlist[want]);
        strcpy(display.dpi_line[1].dln_status, "Prev Select Next");
        DisplayRefresh();

        /* Read the next button key code. Timeout after 5 seconds. */
        if ((key = ButtonRead(UI_REFRESH_RATE)) == 0) {
            /* Button timeout. Keep the current station and return from 
               main menu. */
            if (++tmocnt > 20) {
                want = -1;
            }
        }
        else {
            tmocnt = 0;
            if (key == KEYCODE_DOWN) {
                /* User pressed the DOWN button. Show the preceding list item. */
                want = FavListSearch(want, -1);
            }
            else if (key == KEYCODE_UP) {
                /* User pressed the UP button. Show the next list item. */
                want = FavListSearch(want, 1);
            }
            else if (key == KEYCODE_SELECT) {
                /* User pressed the SELECT key. */
                break;
            }
        }
    }
    if (want > 0) {
        if (FavListCopy(want, TOP_FAVORITE) == 0) {
            UserIfShowMessage(1, 5, "Please wait");
        }
    }
}
#endif

/*!
 * \brief Background thread for user interface.
 *
 * \param arg Unused. Should be NULL.
 */
THREAD(UserIfThread, arg)
{
    char key;

#if USE_DISPLAY || USE_TERM
    fputs(ESC_CURSOROFF, display.dpi_stream);
#endif
    NutThreadSetPriority(128);
    
    for (;;) {
        key = ButtonRead(UI_REFRESH_RATE);
        if (key == KEYCODE_SELECT) {
#if USE_DISPLAY || USE_TERM
            UserIfMainMenu();
#endif
        }
        else {
            if (key) {
                if (key == KEYCODE_DOWN) {
                    if (webradio.wr_gain > AUDIO_DAC_MIN_GAIN) {
                        webradio.wr_gain--;
                    }
                }
                else if (key == KEYCODE_UP) {
                    if (webradio.wr_gain < AUDIO_DAC_MAX_GAIN) {
                        webradio.wr_gain++;
                    }
                }
                UserIfShowMessage(1, 2, "Volume %d dB", webradio.wr_gain);                webradio.wr_cfgupd = 10;
                if (webradio.wr_rip == NULL || /* */
                    webradio.wr_rip->ri_decoder == -1 || /* */
                    _ioctl(webradio.wr_rip->ri_decoder, AUDIO_SET_PLAYGAIN, &webradio.wr_gain) != 0) {
                        LogMsg(LOG_ERROR, "No volume control\n");
                }
                XmlRefresh();
            }
            DisplayRefresh();
        }
    }
}

/*!
 * \brief Display a specified text.
 *
 * \param row  Row position of the message.
 * \param secs Number of seconds to display the message. Set to 0 for
 *             permanent display.
 * \param fmt  Format string containing conversion specifications.
 */
void UserIfShowMessage(u_char row, u_char secs, CONST char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    if (secs) {
        vsprintf(display.dpi_line[row].dln_msg, fmt, ap);
        display.dpi_line[row].dln_msgticks = secs * (1000 / UI_REFRESH_RATE);
    } else
        vsprintf(display.dpi_line[row].dln_status, fmt, ap);
    va_end(ap);
}

/*!
 * \brief Display a specified status.
 *
 * \param status Status to be displayed.
 */
void UserIfShowStatus(u_char status)
{
    if (webradio.wr_status != status) {
        if (status == DIST_FORCE)
            status = webradio.wr_status;
        else
            webradio.wr_status = status;

        if (status == DIST_NONE) {
            strcpy(display.dpi_line[0].dln_status, "Internet Radio");
            strcpy(display.dpi_line[1].dln_status, "Version ");
            strcat(display.dpi_line[1].dln_status, VERSION);
        } else if (status == DIST_DEAD) {
            UserIfShowStationConf(&favlist[LAST_FAVORITE]);
            UserIfShowMessage(1, 2, "Not available");
        } else if (status == DIST_CONNECTING) {
            UserIfShowStationConf(&favlist[LAST_FAVORITE]);
            strcpy(display.dpi_line[1].dln_status, "Connecting...");
        } else if (status == DIST_CONNECTED) {
            UserIfShowStationInfo(webradio.wr_sip);
            display.dpi_line[1].dln_status[0] = '\0';
            if (webradio.wr_rip) {
                SHOUTCASTINFO *sci = (SHOUTCASTINFO *) webradio.wr_rip->ri_bcast;
                if (sci) {
                    if (sci->sci_metatitle && sci->sci_metatitle[0]) {
                        strncpy(display.dpi_line[1].dln_status, sci->sci_metatitle, LCD_VCOLS);
                    }
                    else if (webradio.wr_sip && webradio.wr_sip->si_genre) {
                        strncpy(display.dpi_line[1].dln_status, webradio.wr_sip->si_genre, LCD_VCOLS);
                    }
                }
            }
        }
        LogMsg(LOG_USERIF, "Display %d.0 '%s'\n", status, display.dpi_line[0].dln_status);
        LogMsg(LOG_USERIF, "Display %d.1 '%s'\n", status, display.dpi_line[1].dln_status);
        XmlRefresh();
    }
}

/*!
 * \brief Retrieve the current display text.
 *
 * \param buff The text is store here.
 * \param siz  Size of the buffer.
 *
 * \return Pointer to the buffer.
 */
char * UserIfGetDisplayText(char *buff, size_t siz)
{
    if (siz) {
        siz--;
        buff[siz] = '\0';
        if (siz) {
            if (display.dpi_line[1].dln_msgticks) {
#if (USE_DISPLAY == 0) && (USE_TERM == 0)
                display.dpi_line[1].dln_msgticks = 0;
#endif
                strncpy(buff, display.dpi_line[1].dln_msg, siz);
            } else {
                strncpy(buff, display.dpi_line[1].dln_status, siz);
                siz -= strlen(buff);
                if (siz > 4 && display.dpi_line[0].dln_status[0]) {
                    strcat(buff, "    ");
                    strncpy(buff + strlen(buff), display.dpi_line[0].dln_status, siz - 4);
                }
            }
        }
    }
    return buff;
}

/*!
 * \brief Start background thread for display updates.
 *
 * \param name Display device name.
 *
 * \return 0 on success or -1 in case of a failure.
 */
int UserIfInit(char *name)
{
    /* Initialize button interface. */
    ButtonInit();

#if USE_DISPLAY
    if ((display.dpi_stream = fopen(name, "w")) == 0) {
        return -1;
    }
#elif USE_TERM
    display.dpi_stream = stdout;
#endif

    if (NutThreadCreate("displ", UserIfThread, 0, UI_THREAD_STACK) == 0) {
#if USE_DISPLAY
        fclose(display.dpi_stream);
#endif
#if USE_DISPLAY || USE_TERM
        display.dpi_stream = NULL;
#endif
        return -1;
    }
    webradio.wr_status = DIST_NONE;
    UserIfShowStatus(DIST_FORCE);

    return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -