📄 userif.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 + -