ncurses new_item() 失败
ncurses new_item() failure
我对 ncurses 很陌生,我一直在尝试按照文档 here 来实现滚动菜单。我的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ncurses.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <menu.h>
int NUMBER_OF_TRACKS = 0;
struct Track {
char *title;
char *pathName;
}; typedef struct Track Track;
/*
* playSong is responsible for playing the selected track.
* This function utilizes fork and execvp to accomplish this.
* could maybe use popen again?
*/
void playSong(char *title) {
FILE *fp;
char cmdBuffer[1024];
sprintf(cmdBuffer, "cd ~/Music/iTunes/\"iTunes Media\"/Music/ && afplay %s", title);
fp = popen(cmdBuffer, "r");
if(fp == NULL) {
printf("Unable to play track.\n");
exit(1);
}
// close the pipe
if(pclose(fp) == -1) {
printf("Error while closing pipe.\n");
exit(1);
}
}
/*
* escapeString takes in a path name for a track, and adds escape characters
* to it so that it later be played by the system.
*/
char * escapeString(char *pathName) {
char * escape = (char *)malloc(1024 * sizeof(char));
int index = 0;
for(int i = 0; pathName[i] != '\n'; i++) {
if(pathName[i] == '/') {
if(i == 1) {
// we don't need to doubly pad the first '/' with quotes
escape[index++] = pathName[i];
escape[index++] = '\"';
}
else {
escape[index++] = '\"';
escape[index++] = pathName[i];
escape[index++] = '\"';
}
}
else {
escape[index++] = pathName[i];
}
}
// don't forget to add last quote to end the song
escape[index++] = '\"';
// don't forget null!
escape[index] = '[=11=]';
return escape;
}
/*
* getTrackTitle parses the escaped pathame and returns only the name of the
* song.
*/
char * getTrackTitle(char *pathName) {
char *title = (char *)malloc(1024*sizeof(char));
int slashCount = 0;
int index = 0;
for(int i = 0; pathName[i] != '[=11=]'; i++) {
if(slashCount == 3) {
title[index++] = pathName[i];
}
else if(pathName[i] == '/') {
slashCount++;
}
}
title[index] = '[=11=]';
return title;
}
Track ** getUserTracks() {
FILE *fp;
char pathName[300]; // may need to be longer?
// use fp with popen
char *cmd = "cd ~/Music/iTunes/\"iTunes Media\"/Music/ && find . -name \"*.m4a\"";
fp = popen(cmd, "r");
if(fp == NULL) {
printf("Unable to find your tracks!\n");
exit(1);
}
// create our list
Track **trackList = (Track **)malloc(1024 * sizeof(Track *));
// read the output (i.e) the tracks
while(fgets(pathName, sizeof(pathName), fp) != NULL) {
// allocate memory for track
trackList[NUMBER_OF_TRACKS] = (Track *)malloc(sizeof(Track));
// get the escaped path name
char *escapedPath = escapeString(pathName);
// store values in struct
trackList[NUMBER_OF_TRACKS]->pathName = escapedPath;
trackList[NUMBER_OF_TRACKS]->title = getTrackTitle(escapedPath);
// update number of tracks
NUMBER_OF_TRACKS++;
}
// null terminate list
trackList[NUMBER_OF_TRACKS] = (Track *)NULL;
// close the pipe
if(pclose(fp) == -1) {
printf("Error while closing pipe.\n");
exit(1);
}
// return our list
return trackList;
}
int main(int argc, char **argv) {
/* START NCURSES */
initscr(); // create screen
curs_set(0); // hide cursor
noecho(); // don't show the characters the user is typing
cbreak(); // allow user to quit with ctrl-c; may want to remove this later
// get screen size
int yMax, xMax;
getmaxyx(stdscr, yMax, xMax);
ITEM **userTracks;
MENU *trackMenu;
WINDOW *trackMenuWindow;
// create items for our menu
Track **choices = getUserTracks();
userTracks = (ITEM **)calloc(NUMBER_OF_TRACKS+1, sizeof(ITEM *));
for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
ITEM *trackItem = new_item(choices[i]->title, "");
userTracks[i] = trackItem;
}
userTracks[NUMBER_OF_TRACKS] = (ITEM *)NULL;
/* MENU STUFF */
// create the menu
trackMenu = new_menu((ITEM **)userTracks);
// printf("Item count: %d\n", item_count(trackMenu));
/* create the window associated with the menu */
trackMenuWindow = newwin(yMax, xMax, 0, 0);
box(trackMenuWindow, 0, 0); // adds border to window
keypad(trackMenuWindow, TRUE); // enables keypd use on menu
/* set main window and set sub windows */
set_menu_win(trackMenu, trackMenuWindow);
set_menu_sub(trackMenu, derwin(trackMenuWindow, yMax - 1, xMax - 1, 1, 1));
set_menu_format(trackMenu, 5, 1); // sets number of items to be displayed
/* Set menu mark to the string " * " */
set_menu_mark(trackMenu, NULL);
/* Post the menu */
post_menu(trackMenu);
wrefresh(trackMenuWindow);
int keyPressed;
while((keyPressed = wgetch(trackMenuWindow)) != 'q') {
switch(keyPressed) {
case KEY_DOWN:
menu_driver(trackMenu, REQ_DOWN_ITEM);
break;
case KEY_UP:
menu_driver(trackMenu, REQ_UP_ITEM);
break;
case KEY_NPAGE:
menu_driver(trackMenu, REQ_SCR_DPAGE);
break;
case KEY_PPAGE:
menu_driver(trackMenu, REQ_SCR_UPAGE);
break;
}
wrefresh(trackMenuWindow);
}
unpost_menu(trackMenu);
free_menu(trackMenu);
for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
free_item(userTracks[i]);
}
endwin();
/* END NCURSES */
return 0;
}
基本上,我有兴趣在他们的机器上获取所有用户 (iTunes) 歌曲的列表,并将它们显示在(可滚动的)菜单中。
我能够检索所有歌曲并将它们存储在我的Track **choices
数组中,但是,我不能能够从中创建任何菜单项。我推断我的问题出在以下片段中:
// create items for our menu
Track **choices = getUserTracks();
userTracks = (ITEM **)calloc(NUMBER_OF_TRACKS+1, sizeof(ITEM *));
for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
ITEM *trackItem = new_item(choices[i]->title, "");
userTracks[i] = trackItem;
}
userTracks[NUMBER_OF_TRACKS] = (ITEM *)NULL;
特别是行 ITEM *trackItem = new_item(choices[i]->title, "");
似乎 new_item()
在每次迭代中都返回 NULL
。此外,errno
被设置为 E_BAD_ARGUMENT
暗示 choices[i]->title
不是有效字符串,但我无法想象为什么会这样。
如有任何帮助,我们将不胜感激!
您可以将上面的代码编译如下:gcc -I/usr/local/opt/ncurses/include -L/usr/local/opt/ncurses/lib <source_file>.c -lmenu -lncurses
您也许可以排除 -I/usr/local/opt/ncurses/include -L/usr/local/opt/ncurses/lib
但不幸的是这对我不起作用。
公平警告,由于文件结构和 iTunes 要求,此程序可能无法在 Windows/Linux 环境中运行,但我尚未对其进行测试。
原来我的结构中的字符串是无意义的。我需要在 getUserTracks()
函数中为它们在堆上分配内存。
我对 ncurses 很陌生,我一直在尝试按照文档 here 来实现滚动菜单。我的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ncurses.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <menu.h>
int NUMBER_OF_TRACKS = 0;
struct Track {
char *title;
char *pathName;
}; typedef struct Track Track;
/*
* playSong is responsible for playing the selected track.
* This function utilizes fork and execvp to accomplish this.
* could maybe use popen again?
*/
void playSong(char *title) {
FILE *fp;
char cmdBuffer[1024];
sprintf(cmdBuffer, "cd ~/Music/iTunes/\"iTunes Media\"/Music/ && afplay %s", title);
fp = popen(cmdBuffer, "r");
if(fp == NULL) {
printf("Unable to play track.\n");
exit(1);
}
// close the pipe
if(pclose(fp) == -1) {
printf("Error while closing pipe.\n");
exit(1);
}
}
/*
* escapeString takes in a path name for a track, and adds escape characters
* to it so that it later be played by the system.
*/
char * escapeString(char *pathName) {
char * escape = (char *)malloc(1024 * sizeof(char));
int index = 0;
for(int i = 0; pathName[i] != '\n'; i++) {
if(pathName[i] == '/') {
if(i == 1) {
// we don't need to doubly pad the first '/' with quotes
escape[index++] = pathName[i];
escape[index++] = '\"';
}
else {
escape[index++] = '\"';
escape[index++] = pathName[i];
escape[index++] = '\"';
}
}
else {
escape[index++] = pathName[i];
}
}
// don't forget to add last quote to end the song
escape[index++] = '\"';
// don't forget null!
escape[index] = '[=11=]';
return escape;
}
/*
* getTrackTitle parses the escaped pathame and returns only the name of the
* song.
*/
char * getTrackTitle(char *pathName) {
char *title = (char *)malloc(1024*sizeof(char));
int slashCount = 0;
int index = 0;
for(int i = 0; pathName[i] != '[=11=]'; i++) {
if(slashCount == 3) {
title[index++] = pathName[i];
}
else if(pathName[i] == '/') {
slashCount++;
}
}
title[index] = '[=11=]';
return title;
}
Track ** getUserTracks() {
FILE *fp;
char pathName[300]; // may need to be longer?
// use fp with popen
char *cmd = "cd ~/Music/iTunes/\"iTunes Media\"/Music/ && find . -name \"*.m4a\"";
fp = popen(cmd, "r");
if(fp == NULL) {
printf("Unable to find your tracks!\n");
exit(1);
}
// create our list
Track **trackList = (Track **)malloc(1024 * sizeof(Track *));
// read the output (i.e) the tracks
while(fgets(pathName, sizeof(pathName), fp) != NULL) {
// allocate memory for track
trackList[NUMBER_OF_TRACKS] = (Track *)malloc(sizeof(Track));
// get the escaped path name
char *escapedPath = escapeString(pathName);
// store values in struct
trackList[NUMBER_OF_TRACKS]->pathName = escapedPath;
trackList[NUMBER_OF_TRACKS]->title = getTrackTitle(escapedPath);
// update number of tracks
NUMBER_OF_TRACKS++;
}
// null terminate list
trackList[NUMBER_OF_TRACKS] = (Track *)NULL;
// close the pipe
if(pclose(fp) == -1) {
printf("Error while closing pipe.\n");
exit(1);
}
// return our list
return trackList;
}
int main(int argc, char **argv) {
/* START NCURSES */
initscr(); // create screen
curs_set(0); // hide cursor
noecho(); // don't show the characters the user is typing
cbreak(); // allow user to quit with ctrl-c; may want to remove this later
// get screen size
int yMax, xMax;
getmaxyx(stdscr, yMax, xMax);
ITEM **userTracks;
MENU *trackMenu;
WINDOW *trackMenuWindow;
// create items for our menu
Track **choices = getUserTracks();
userTracks = (ITEM **)calloc(NUMBER_OF_TRACKS+1, sizeof(ITEM *));
for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
ITEM *trackItem = new_item(choices[i]->title, "");
userTracks[i] = trackItem;
}
userTracks[NUMBER_OF_TRACKS] = (ITEM *)NULL;
/* MENU STUFF */
// create the menu
trackMenu = new_menu((ITEM **)userTracks);
// printf("Item count: %d\n", item_count(trackMenu));
/* create the window associated with the menu */
trackMenuWindow = newwin(yMax, xMax, 0, 0);
box(trackMenuWindow, 0, 0); // adds border to window
keypad(trackMenuWindow, TRUE); // enables keypd use on menu
/* set main window and set sub windows */
set_menu_win(trackMenu, trackMenuWindow);
set_menu_sub(trackMenu, derwin(trackMenuWindow, yMax - 1, xMax - 1, 1, 1));
set_menu_format(trackMenu, 5, 1); // sets number of items to be displayed
/* Set menu mark to the string " * " */
set_menu_mark(trackMenu, NULL);
/* Post the menu */
post_menu(trackMenu);
wrefresh(trackMenuWindow);
int keyPressed;
while((keyPressed = wgetch(trackMenuWindow)) != 'q') {
switch(keyPressed) {
case KEY_DOWN:
menu_driver(trackMenu, REQ_DOWN_ITEM);
break;
case KEY_UP:
menu_driver(trackMenu, REQ_UP_ITEM);
break;
case KEY_NPAGE:
menu_driver(trackMenu, REQ_SCR_DPAGE);
break;
case KEY_PPAGE:
menu_driver(trackMenu, REQ_SCR_UPAGE);
break;
}
wrefresh(trackMenuWindow);
}
unpost_menu(trackMenu);
free_menu(trackMenu);
for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
free_item(userTracks[i]);
}
endwin();
/* END NCURSES */
return 0;
}
基本上,我有兴趣在他们的机器上获取所有用户 (iTunes) 歌曲的列表,并将它们显示在(可滚动的)菜单中。
我能够检索所有歌曲并将它们存储在我的Track **choices
数组中,但是,我不能能够从中创建任何菜单项。我推断我的问题出在以下片段中:
// create items for our menu
Track **choices = getUserTracks();
userTracks = (ITEM **)calloc(NUMBER_OF_TRACKS+1, sizeof(ITEM *));
for(int i = 0; i < NUMBER_OF_TRACKS; i++) {
ITEM *trackItem = new_item(choices[i]->title, "");
userTracks[i] = trackItem;
}
userTracks[NUMBER_OF_TRACKS] = (ITEM *)NULL;
特别是行 ITEM *trackItem = new_item(choices[i]->title, "");
似乎 new_item()
在每次迭代中都返回 NULL
。此外,errno
被设置为 E_BAD_ARGUMENT
暗示 choices[i]->title
不是有效字符串,但我无法想象为什么会这样。
如有任何帮助,我们将不胜感激!
您可以将上面的代码编译如下:gcc -I/usr/local/opt/ncurses/include -L/usr/local/opt/ncurses/lib <source_file>.c -lmenu -lncurses
您也许可以排除 -I/usr/local/opt/ncurses/include -L/usr/local/opt/ncurses/lib
但不幸的是这对我不起作用。
公平警告,由于文件结构和 iTunes 要求,此程序可能无法在 Windows/Linux 环境中运行,但我尚未对其进行测试。
原来我的结构中的字符串是无意义的。我需要在 getUserTracks()
函数中为它们在堆上分配内存。