📄 userif.c
字号:
/*
* Copyright (C) 2003-2007 by egnite Software 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 EGNITE SOFTWARE GMBH 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 EGNITE
* SOFTWARE GMBH 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 config.c
* \brief User interface.
*
* \verbatim
*
* $Log$
*
* \endverbatim
*/
#include <cfg/lcd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dev/term.h>
#include <sys/thread.h>
#include <sys/timer.h>
#include <sys/event.h>
#include <arpa/inet.h>
#include "config.h"
#include "webradio.h"
#include "shoutcast.h"
#include "mp3player.h"
#include "tlv320dac.h"
#include "buttons.h"
#include "userif.h"
/*!
* \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 80
#endif
#endif
#ifndef LCD_SCROLL_GAP
#define LCD_SCROLL_GAP (LCD_COLS / 2)
#endif
#ifndef UI_REFRESH_RATE
#define UI_REFRESH_RATE 300
#endif
#ifndef UI_THREAD_STACK
#ifdef AT91SAM7X_EK
#define UI_THREAD_STACK 512
#else
#define UI_THREAD_STACK 2048
#endif
#endif
typedef struct {
char dln_status[LCD_VCOLS + 1];
char dln_msg[LCD_VCOLS + 1];
u_int dln_step;
u_int dln_len;
u_int dln_msgticks;
} DISPLAYLINE;
typedef struct {
FILE *dpi_stream;
u_int dpi_scrolling; /*!< . */
char *dpi_sptr;
char dpi_buff[LCD_COLS + 1];
DISPLAYLINE dpi_line[LCD_ROWS];
} DISPLAYINFO;
static DISPLAYINFO display;
/*!
* \brief Refresh the LCD.
*/
static void DisplayRefresh(void)
{
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;
}
}
}
}
/*!
* \brief Displays configured information about a station.
*
* \param scp Pointer to the configuration table entry.
*/
static void UserIfShowStationConf(STATIONCONF * scp)
{
if (scp->rs_port) {
if (scp->rs_symbol && scp->rs_symbol[0]) {
strncpy(display.dpi_line[0].dln_status, scp->rs_symbol, LCD_VCOLS);
} else {
strncpy(display.dpi_line[0].dln_status, inet_ntoa(scp->rs_ip), LCD_VCOLS);
}
} else {
display.dpi_line[0].dln_status[0] = 0;
}
}
/*!
* \brief Displays station information.
*
* Called during connect.
*
* \param scp Pointer to the configuration table entry.
*/
static void UserIfShowStationInfo(STATIONINFO * sip)
{
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);
}
} else {
display.dpi_line[0].dln_status[0] = 0;
}
}
/*!
* \brief Main menu processing.
*
*/
void UserIfMainMenu(void)
{
char key;
u_int tmocnt = 0;
u_char want = radio.rc_cstation;
for (;;) {
UserIfShowStationConf(&station[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 = radio.rc_cstation;
break;
}
}
else {
tmocnt = 0;
if (key == KEYCODE_DOWN) {
/* User pressed the DOWN button. Show the preceding list item. */
want = StationSelect(want, -1);
}
else if (key == KEYCODE_UP) {
/* User pressed the UP button. Show the next list item. */
want = StationSelect(want, 1);
}
else if (key == KEYCODE_SELECT) {
/* User pressed the SELECT key. */
break;
}
}
}
if (want != radio.rc_cstation) {
UserIfShowMessage(1, 5, "Please wait");
radio.rc_rstation = want;
}
}
/*!
* \brief Background thread for user interface.
*
* \param arg Unused. Should be NULL.
*/
THREAD(UserIfThread, arg)
{
char key;
fputs(ESC_CURSOROFF, display.dpi_stream);
NutThreadSetPriority(128);
for (;;) {
key = ButtonRead(UI_REFRESH_RATE);
if (key == KEYCODE_SELECT) {
UserIfMainMenu();
}
else {
if (key) {
if (key == KEYCODE_DOWN) {
if (radio.rc_rvolume > DAC_MIN_VOLUME) {
radio.rc_rvolume--;
}
}
else if (key == KEYCODE_UP) {
if (radio.rc_rvolume < DAC_MAX_VOLUME) {
radio.rc_rvolume++;
}
}
UserIfShowMessage(1, 2, "Volume %d dB", radio.rc_rvolume); }
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 (radio.rc_cstatus != status) {
if (status == DIST_FORCE)
status = radio.rc_cstatus;
else
radio.rc_cstatus = 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(&station[radio.rc_cstation]);
UserIfShowMessage(1, 2, "not available");
} else if (status == DIST_CONNECTING) {
UserIfShowStationConf(&station[radio.rc_cstation]);
strcpy(display.dpi_line[1].dln_status, "Connecting...");
} else if (status == DIST_CONNECTED) {
UserIfShowStationInfo(radio.rc_sip);
if (radio.rc_rip) {
SHOUTCASTINFO *sci = (SHOUTCASTINFO *) radio.rc_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 (radio.rc_sip && radio.rc_sip->si_genre && radio.rc_sip->si_genre[0]) {
strncpy(display.dpi_line[1].dln_status, radio.rc_sip->si_genre, LCD_VCOLS);
}
}
}
}
}
}
/*!
* \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 ((display.dpi_stream = fopen(name, "w")) == 0) {
return -1;
}
display.dpi_scrolling = LCD_ROWS;
if (NutThreadCreate("displ", UserIfThread, 0, UI_THREAD_STACK) == 0) {
fclose(display.dpi_stream);
display.dpi_stream = NULL;
return -1;
}
radio.rc_cstatus = DIST_NONE;
UserIfShowStatus(DIST_FORCE);
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -