int main (int argc, char **argv) { static BarApp_t app; memset (&app, 0, sizeof (app)); /* save terminal attributes, before disabling echoing */ BarTermInit (); /* signals */ signal (SIGPIPE, SIG_IGN); /* init some things */ gcry_check_version (NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); BarPlayerInit (); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); PianoReturn_t pret; if ((pret = PianoInit (&app.ph, app.settings.partnerUser, app.settings.partnerPassword, app.settings.device, app.settings.inkey, app.settings.outkey)) != PIANO_RET_OK) { BarUiMsg (&app.settings, MSG_ERR, "Initialization failed:" " %s\n", PianoErrorToStr (pret)); return 0; } BarUiMsg (&app.settings, MSG_NONE, "Welcome to " PACKAGE " (" VERSION ")! "); if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) { BarUiMsg (&app.settings, MSG_NONE, "\n"); } else { BarUiMsg (&app.settings, MSG_NONE, "Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); } curl_global_init (CURL_GLOBAL_DEFAULT); app.http = curl_easy_init (); assert (app.http != NULL); /* init fds */ FD_ZERO(&app.input.set); int fifo_uses_this_fd = 0; app.input.fds[0] = STDIN_FILENO; if (isatty(fileno(stdin))) { fifo_uses_this_fd = 1; FD_SET(app.input.fds[0], &app.input.set); } /* open fifo read/write so it won't EOF if nobody writes to it */ assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2); app.input.fds[fifo_uses_this_fd] = open (app.settings.fifo, O_RDWR); if (app.input.fds[fifo_uses_this_fd] != -1) { struct stat s; /* check for file type, must be fifo */ fstat (app.input.fds[fifo_uses_this_fd], &s); if (!S_ISFIFO (s.st_mode)) { BarUiMsg (&app.settings, MSG_ERR, "File at %s is not a fifo\n", app.settings.fifo); close (app.input.fds[fifo_uses_this_fd]); app.input.fds[fifo_uses_this_fd] = -1; } else { FD_SET(app.input.fds[fifo_uses_this_fd], &app.input.set); BarUiMsg (&app.settings, MSG_INFO, "Control fifo at %s opened\n", app.settings.fifo); } } app.input.maxfd = app.input.fds[0]; if (fifo_uses_this_fd>0) app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] : app.input.fds[1]; ++app.input.maxfd; BarMainLoop (&app); if (app.input.fds[fifo_uses_this_fd] != -1) { close (app.input.fds[fifo_uses_this_fd]); } /* write statefile */ BarSettingsWrite (app.curStation, &app.settings); PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); curl_easy_cleanup (app.http); curl_global_cleanup (); BarPlayerDestroy (); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (); return 0; }
int main (int argc, char **argv) { static BarApp_t app; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; memset (&app, 0, sizeof (app)); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* signals */ signal (SIGPIPE, SIG_IGN); /* init some things */ ao_initialize (); gcry_check_version (NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); gnutls_global_init (); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); PianoInit (&app.ph, app.settings.partnerUser, app.settings.partnerPassword, app.settings.device, app.settings.inkey, app.settings.outkey); BarUiMsg (&app.settings, MSG_NONE, "Welcome to " PACKAGE " (" VERSION ")! "); if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) { BarUiMsg (&app.settings, MSG_NONE, "\n"); } else { BarUiMsg (&app.settings, MSG_NONE, "Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); } WaitressInit (&app.waith); app.waith.url.host = app.settings.rpcHost; app.waith.tlsFingerprint = app.settings.tlsFingerprint; /* init fds */ FD_ZERO(&app.input.set); app.input.fds[0] = STDIN_FILENO; FD_SET(app.input.fds[0], &app.input.set); /* open fifo read/write so it won't EOF if nobody writes to it */ assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2); app.input.fds[1] = open (app.settings.fifo, O_RDWR); if (app.input.fds[1] != -1) { struct stat s; /* check for file type, must be fifo */ fstat (app.input.fds[1], &s); if (!S_ISFIFO (s.st_mode)) { BarUiMsg (&app.settings, MSG_ERR, "File at %s is not a fifo\n", app.settings.fifo); close (app.input.fds[1]); app.input.fds[1] = -1; } else { FD_SET(app.input.fds[1], &app.input.set); BarUiMsg (&app.settings, MSG_INFO, "Control fifo at %s opened\n", app.settings.fifo); } } app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] : app.input.fds[1]; ++app.input.maxfd; BarMainLoop (&app); if (app.input.fds[1] != -1) { close (app.input.fds[1]); } PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); WaitressFree (&app.waith); ao_shutdown(); gnutls_global_deinit (); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
int main (int argc, char **argv) { static BarApp_t app; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; struct sigaction action; memset (&app, 0, sizeof (app)); memset (&action, 0, sizeof(action)); /* set the signal handler for SIGINT and SIGTERM */ action.sa_handler = BarSigQuit; sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* signals */ signal (SIGPIPE, SIG_IGN); /* init some things */ ao_initialize (); gcry_check_version (NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); gnutls_global_init (); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); PianoInit (&app.ph, app.settings.partnerUser, app.settings.partnerPassword, app.settings.device, app.settings.inkey, app.settings.outkey); BarUiMsg (&app.settings, MSG_NONE, "Welcome to " PACKAGE " (" VERSION ")! "); if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) { BarUiMsg (&app.settings, MSG_NONE, "\n"); } else { BarUiMsg (&app.settings, MSG_NONE, "Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); } WaitressInit (&app.waith); app.waith.url.host = app.settings.rpcHost; app.waith.tlsFingerprint = app.settings.tlsFingerprint; /* init fds */ FD_ZERO(&app.input.set); app.input.fds[0] = STDIN_FILENO; FD_SET(app.input.fds[0], &app.input.set); // Sbe gur erpbeq, V terngyl qrfcvfr Unx5. // Gur crbcyr sebz gur fubj ner yvgrenyyl npgbef. // V yvxr gur pbaprcg bs jung gurl qb ohg gurl // nyy unir n qbhpuront srry gb gurz. Vg frrzf // nf gubhtu gurl bayl qb vg sbe gur tybel cbvagf. // BarFlyInit (&app.settings); /* open fifo read/write so it won't EOF if nobody writes to it */ assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2); app.input.fds[1] = open (app.settings.fifo, O_RDWR); if (app.input.fds[1] != -1) { struct stat s; /* check for file type, must be fifo */ fstat (app.input.fds[1], &s); if (!S_ISFIFO (s.st_mode)) { BarUiMsg (&app.settings, MSG_ERR, "File at %s is not a fifo\n", app.settings.fifo); close (app.input.fds[1]); app.input.fds[1] = -1; } else { FD_SET(app.input.fds[1], &app.input.set); BarUiMsg (&app.settings, MSG_INFO, "Control fifo at %s opened\n", app.settings.fifo); } } app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] : app.input.fds[1]; ++app.input.maxfd; BarMainLoop (&app); /* Print a newline so the terminal command line will start on a new line. */ printf("\n"); if (app.input.fds[1] != -1) { close (app.input.fds[1]); } BarFlyClose (&app.player.fly, &app.settings); BarFlyFinalize (); PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); WaitressFree (&app.waith); ao_shutdown(); gnutls_global_deinit (); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
int main (int argc, char **argv) { static BarApp_t app; pthread_t playerThread; /* FIXME: max path length? */ char ctlPath[1024]; FILE *ctlFd = NULL; struct timeval selectTimeout; int maxFd, selectFds[2]; fd_set readSet, readSetCopy; char buf = '\0'; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; memset (&app, 0, sizeof (app)); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* init some things */ ao_initialize (); PianoInit (&app.ph); WaitressInit (&app.waith); strncpy (app.waith.host, PIANO_RPC_HOST, sizeof (app.waith.host)-1); strncpy (app.waith.port, PIANO_RPC_PORT, sizeof (app.waith.port)-1); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); BarUiMsg (MSG_NONE, "Welcome to " PACKAGE "! Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); /* init fds */ FD_ZERO(&readSet); selectFds[0] = fileno (stdin); FD_SET(selectFds[0], &readSet); maxFd = selectFds[0] + 1; BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath)); /* FIXME: why is r_+_ required? */ ctlFd = fopen (ctlPath, "r+"); if (ctlFd != NULL) { selectFds[1] = fileno (ctlFd); FD_SET(selectFds[1], &readSet); /* assuming ctlFd is always > stdin */ maxFd = selectFds[1] + 1; BarUiMsg (MSG_INFO, "Control fifo at %s opened\n", ctlPath); } if (app.settings.username == NULL) { char nameBuf[100]; BarUiMsg (MSG_QUESTION, "Username: "******"Password: "******"Login... "); if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_LOGIN, &app.waith, &reqData, &pRet, &wRet)) { BarTermRestore (&termOrig); return 0; } } { PianoReturn_t pRet; WaitressReturn_t wRet; BarUiMsg (MSG_INFO, "Get stations... "); if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_GET_STATIONS, &app.waith, NULL, &pRet, &wRet)) { BarTermRestore (&termOrig); return 0; } } /* try to get autostart station */ if (app.settings.autostartStation != NULL) { app.curStation = PianoFindStationById (app.ph.stations, app.settings.autostartStation); if (app.curStation == NULL) { BarUiMsg (MSG_ERR, "Error: Autostart station not found.\n"); } } /* no autostart? ask the user */ if (app.curStation == NULL) { app.curStation = BarUiSelectStation (&app.ph, "Select station: ", app.settings.sortOrder, stdin); } if (app.curStation != NULL) { BarUiPrintStation (app.curStation); } /* little hack, needed to signal: hey! we need a playlist, but don't * free anything (there is nothing to be freed yet) */ memset (&app.player, 0, sizeof (app.player)); while (!app.doQuit) { /* song finished playing, clean up things/scrobble song */ if (app.player.mode == PLAYER_FINISHED_PLAYBACK) { BarUiStartEventCmd (&app.settings, "songfinish", app.curStation, app.playlist, &app.player, PIANO_RET_OK, WAITRESS_RET_OK); /* FIXME: pthread_join blocks everything if network connection * is hung up e.g. */ void *threadRet; pthread_join (playerThread, &threadRet); /* don't continue playback if thread reports error */ if (threadRet != (void *) PLAYER_RET_OK) { app.curStation = NULL; } memset (&app.player, 0, sizeof (app.player)); } /* check whether player finished playing and start playing new * song */ if (app.player.mode >= PLAYER_FINISHED_PLAYBACK || app.player.mode == PLAYER_FREED) { if (app.curStation != NULL) { /* what's next? */ if (app.playlist != NULL) { if (app.settings.history != 0) { /* prepend song to history list */ PianoSong_t *tmpSong = app.songHistory; app.songHistory = app.playlist; /* select next song */ app.playlist = app.playlist->next; app.songHistory->next = tmpSong; /* limit history's length */ /* start with 1, so we're stopping at n-1 and have the * chance to set ->next = NULL */ unsigned int i = 1; tmpSong = app.songHistory; while (i < app.settings.history && tmpSong != NULL) { tmpSong = tmpSong->next; ++i; } /* if too many songs in history... */ if (tmpSong != NULL) { PianoSong_t *delSong = tmpSong->next; tmpSong->next = NULL; if (delSong != NULL) { PianoDestroyPlaylist (delSong); } } } else { /* don't keep history */ app.playlist = app.playlist->next; } } if (app.playlist == NULL) { PianoReturn_t pRet; WaitressReturn_t wRet; PianoRequestDataGetPlaylist_t reqData; reqData.station = app.curStation; reqData.format = app.settings.audioFormat; BarUiMsg (MSG_INFO, "Receiving new playlist... "); if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_GET_PLAYLIST, &app.waith, &reqData, &pRet, &wRet)) { app.curStation = NULL; } else { app.playlist = reqData.retPlaylist; if (app.playlist == NULL) { BarUiMsg (MSG_INFO, "No tracks left.\n"); app.curStation = NULL; } } BarUiStartEventCmd (&app.settings, "stationfetchplaylist", app.curStation, app.playlist, &app.player, pRet, wRet); } /* song ready to play */ if (app.playlist != NULL) { BarUiPrintSong (app.playlist, app.curStation->isQuickMix ? PianoFindStationById (app.ph.stations, app.playlist->stationId) : NULL); if (app.playlist->audioUrl == NULL) { BarUiMsg (MSG_ERR, "Invalid song url.\n"); } else { /* setup player */ memset (&app.player, 0, sizeof (app.player)); WaitressInit (&app.player.waith); WaitressSetUrl (&app.player.waith, app.playlist->audioUrl); /* set up global proxy, player is NULLed on songfinish */ if (app.settings.proxy != NULL) { char tmpPath[2]; WaitressSplitUrl (app.settings.proxy, app.player.waith.proxyHost, sizeof (app.player.waith.proxyHost), app.player.waith.proxyPort, sizeof (app.player.waith.proxyPort), tmpPath, sizeof (tmpPath)); } app.player.gain = app.playlist->fileGain; app.player.audioFormat = app.playlist->audioFormat; /* throw event */ BarUiStartEventCmd (&app.settings, "songstart", app.curStation, app.playlist, &app.player, PIANO_RET_OK, WAITRESS_RET_OK); /* prevent race condition, mode must _not_ be FREED if * thread has been started */ app.player.mode = PLAYER_STARTING; /* start player */ pthread_create (&playerThread, NULL, BarPlayerThread, &app.player); } /* end if audioUrl == NULL */ } /* end if playlist != NULL */ } /* end if curStation != NULL */ } /* select modifies its arguments => copy the set */ memcpy (&readSetCopy, &readSet, sizeof (readSet)); selectTimeout.tv_sec = 1; selectTimeout.tv_usec = 0; /* in the meantime: wait for user actions */ if (select (maxFd, &readSetCopy, NULL, NULL, &selectTimeout) > 0) { FILE *curFd = NULL; if (FD_ISSET(selectFds[0], &readSetCopy)) { curFd = stdin; } else if (FD_ISSET(selectFds[1], &readSetCopy)) { curFd = ctlFd; } buf = fgetc (curFd); size_t i; for (i = 0; i < BAR_KS_COUNT; i++) { if (app.settings.keys[i] == buf) { static const BarKeyShortcutFunc_t idToF[] = {BarUiActHelp, BarUiActLoveSong, BarUiActBanSong, BarUiActAddMusic, BarUiActCreateStation, BarUiActDeleteStation, BarUiActExplain, BarUiActStationFromGenre, BarUiActHistory, BarUiActSongInfo, BarUiActAddSharedStation, BarUiActMoveSong, BarUiActSkipSong, BarUiActPause, BarUiActQuit, BarUiActRenameStation, BarUiActSelectStation, BarUiActTempBanSong, BarUiActPrintUpcoming, BarUiActSelectQuickMix, BarUiActDebug, BarUiActBookmark}; idToF[i] (&app, curFd); break; } } } /* show time */ if (app.player.mode >= PLAYER_SAMPLESIZE_INITIALIZED && app.player.mode < PLAYER_FINISHED_PLAYBACK) { /* Ugly: songDuration is unsigned _long_ int! Lets hope this won't * overflow */ int songRemaining = (signed long int) (app.player.songDuration - app.player.songPlayed) / BAR_PLAYER_MS_TO_S_FACTOR; char pos = 0; if (songRemaining < 0) { /* Use plus sign if song is longer than expected */ pos = 1; songRemaining = -songRemaining; } BarUiMsg (MSG_TIME, "%c%02i:%02i/%02i:%02i\r", (pos ? '+' : '-'), songRemaining / 60, songRemaining % 60, app.player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR / 60, app.player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR % 60); } } /* destroy everything (including the world...) */ if (app.player.mode != PLAYER_FREED) { pthread_join (playerThread, NULL); } if (ctlFd != NULL) { fclose (ctlFd); } PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); ao_shutdown(); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
int main (int argc, char **argv) { static BarApp_t app; char ctlPath[PATH_MAX]; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; memset (&app, 0, sizeof (app)); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* init some things */ ao_initialize (); PianoInit (&app.ph); WaitressInit (&app.waith); app.waith.url.host = strdup (PIANO_RPC_HOST); app.waith.url.port = strdup (PIANO_RPC_PORT); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); BarUiMsg (&app.settings, MSG_NONE, "Welcome to " PACKAGE " (" VERSION ")! "); if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) { BarUiMsg (&app.settings, MSG_NONE, "\n"); } else { BarUiMsg (&app.settings, MSG_NONE, "Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); } /* init fds */ FD_ZERO(&app.input.set); app.input.fds[0] = STDIN_FILENO; FD_SET(app.input.fds[0], &app.input.set); BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath)); /* open fifo read/write so it won't EOF if nobody writes to it */ assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2); app.input.fds[1] = open (ctlPath, O_RDWR); if (app.input.fds[1] != -1) { FD_SET(app.input.fds[1], &app.input.set); BarUiMsg (&app.settings, MSG_INFO, "Control fifo at %s opened\n", ctlPath); } app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] : app.input.fds[1]; ++app.input.maxfd; BarMainLoop (&app); if (app.input.fds[1] != -1) { close (app.input.fds[1]); } PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); ao_shutdown(); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
int main (int argc, char **argv) { /* handles */ PianoHandle_t ph; static struct audioPlayer player; BarSettings_t settings; pthread_t playerThread; /* playlist; first item is current song */ PianoSong_t *playlist = NULL; PianoSong_t *songHistory = NULL; PianoStation_t *curStation = NULL; char doQuit = 0; /* FIXME: max path length? */ char ctlPath[1024]; FILE *ctlFd = NULL; struct timeval selectTimeout; int maxFd, selectFds[2]; fd_set readSet, readSetCopy; char buf = '\0'; /* terminal attributes _before_ we started messing around with ~ECHO */ struct termios termOrig; BarUiMsg (MSG_NONE, "Welcome to " PACKAGE "!\n"); /* save terminal attributes, before disabling echoing */ BarTermSave (&termOrig); BarTermSetEcho (0); BarTermSetBuffer (0); /* init some things */ ao_initialize (); PianoInit (&ph); BarSettingsInit (&settings); BarSettingsRead (&settings); /* init fds */ FD_ZERO(&readSet); selectFds[0] = fileno (stdin); FD_SET(selectFds[0], &readSet); maxFd = selectFds[0] + 1; BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath)); /* FIXME: why is r_+_ required? */ ctlFd = fopen (ctlPath, "r+"); if (ctlFd != NULL) { selectFds[1] = fileno (ctlFd); FD_SET(selectFds[1], &readSet); /* assuming ctlFd is always > stdin */ maxFd = selectFds[1] + 1; BarUiMsg (MSG_INFO, "Control fifo at %s opened\n", ctlPath); } if (settings.username == NULL) { char nameBuf[100]; BarUiMsg (MSG_QUESTION, "Username: "******"Password: "******"Login... "); if (BarUiPrintPianoStatus (PianoConnect (&ph, settings.username, settings.password)) != PIANO_RET_OK) { BarTermRestore (&termOrig); return 0; } BarUiMsg (MSG_INFO, "Get stations... "); if (BarUiPrintPianoStatus (PianoGetStations (&ph)) != PIANO_RET_OK) { BarTermRestore (&termOrig); return 0; } /* try to get autostart station */ if (settings.autostartStation != NULL) { curStation = PianoFindStationById (ph.stations, settings.autostartStation); if (curStation == NULL) { BarUiMsg (MSG_ERR, "Error: Autostart station not found.\n"); } } /* no autostart? ask the user */ if (curStation == NULL) { curStation = BarUiSelectStation (&ph, "Select station: ", stdin); } if (curStation != NULL) { BarUiPrintStation (curStation); } /* little hack, needed to signal: hey! we need a playlist, but don't * free anything (there is nothing to be freed yet) */ memset (&player, 0, sizeof (player)); while (!doQuit) { /* song finished playing, clean up things/scrobble song */ if (player.mode == PLAYER_FINISHED_PLAYBACK) { BarUiStartEventCmd (&settings, "songfinish", curStation, playlist, &player, PIANO_RET_OK); /* FIXME: pthread_join blocks everything if network connection * is hung up e.g. */ void *threadRet; pthread_join (playerThread, &threadRet); /* don't continue playback if thread reports error */ if (threadRet != NULL) { curStation = NULL; } memset (&player, 0, sizeof (player)); } /* check whether player finished playing and start playing new * song */ if (player.mode >= PLAYER_FINISHED_PLAYBACK || player.mode == PLAYER_FREED) { if (curStation != NULL) { /* what's next? */ if (playlist != NULL) { if (settings.history != 0) { /* prepend song to history list */ PianoSong_t *tmpSong = songHistory; songHistory = playlist; /* select next song */ playlist = playlist->next; songHistory->next = tmpSong; /* limit history's length */ /* start with 1, so we're stopping at n-1 and have the * chance to set ->next = NULL */ unsigned int i = 1; tmpSong = songHistory; while (i < settings.history && tmpSong != NULL) { tmpSong = tmpSong->next; ++i; } /* if too many songs in history... */ if (tmpSong != NULL) { PianoSong_t *delSong = tmpSong->next; tmpSong->next = NULL; if (delSong != NULL) { PianoDestroyPlaylist (delSong); } } } else { /* don't keep history */ playlist = playlist->next; } } if (playlist == NULL) { PianoReturn_t pRet = PIANO_RET_ERR; BarUiMsg (MSG_INFO, "Receiving new playlist... "); if ((pRet = BarUiPrintPianoStatus (PianoGetPlaylist (&ph, curStation->id, settings.audioFormat, &playlist))) != PIANO_RET_OK) { curStation = NULL; } else { if (playlist == NULL) { BarUiMsg (MSG_INFO, "No tracks left.\n"); curStation = NULL; } } BarUiStartEventCmd (&settings, "stationfetchplaylist", curStation, playlist, &player, pRet); } /* song ready to play */ if (playlist != NULL) { BarUiPrintSong (playlist, curStation->isQuickMix ? PianoFindStationById (ph.stations, playlist->stationId) : NULL); if (playlist->audioUrl == NULL) { BarUiMsg (MSG_ERR, "Invalid song url.\n"); } else { /* setup player */ memset (&player, 0, sizeof (player)); WaitressInit (&player.waith); WaitressSetUrl (&player.waith, playlist->audioUrl); player.gain = playlist->fileGain; player.audioFormat = playlist->audioFormat; /* Setup dump directories. */ prepare_dump_name(player.dump_filename, playlist); /* Setup dump handle. If we can read the file, * then it already exists so don't re-write. */ if (access(player.dump_filename, R_OK) != 0) { player.dump_handle = fopen(player.dump_filename, "w"); BarUiMsg(MSG_INFO, "Will dump song...\n"); } else { player.dump_handle = NULL; BarUiMsg(MSG_INFO, "Dump file found, will not dump!\n"); } /* throw event */ BarUiStartEventCmd (&settings, "songstart", curStation, playlist, &player, PIANO_RET_OK); /* start player */ pthread_create (&playerThread, NULL, BarPlayerThread, &player); } /* end if audioUrl == NULL */ } /* end if playlist != NULL */ } /* end if curStation != NULL */ } /* select modifies its arguments => copy the set */ memcpy (&readSetCopy, &readSet, sizeof (readSet)); selectTimeout.tv_sec = 1; selectTimeout.tv_usec = 0; /* in the meantime: wait for user actions */ if (select (maxFd, &readSetCopy, NULL, NULL, &selectTimeout) > 0) { FILE *curFd = NULL; if (FD_ISSET(selectFds[0], &readSetCopy)) { curFd = stdin; } else if (FD_ISSET(selectFds[1], &readSetCopy)) { curFd = ctlFd; } buf = fgetc (curFd); size_t i; for (i = 0; i < BAR_KS_COUNT; i++) { if (settings.keys[i] == buf) { BarKeyShortcutFunc_t idToF[] = {BarUiActHelp, BarUiActLoveSong, BarUiActBanSong, BarUiActAddMusic, BarUiActCreateStation, BarUiActDeleteStation, BarUiActExplain, BarUiActStationFromGenre, BarUiActHistory, BarUiActSongInfo, BarUiActAddSharedStation, BarUiActMoveSong, BarUiActSkipSong, BarUiActPause, BarUiActQuit, BarUiActRenameStation, BarUiActSelectStation, BarUiActTempBanSong, BarUiActPrintUpcoming, BarUiActSelectQuickMix, BarUiActDebug, BarUiActBookmark}; idToF[i] (&ph, &player, &settings, &playlist, &curStation, &songHistory, &doQuit, curFd); break; } } } /* show time */ if (player.mode >= PLAYER_SAMPLESIZE_INITIALIZED && player.mode < PLAYER_FINISHED_PLAYBACK) { /* Ugly: songDuration is unsigned _long_ int! Lets hope this won't * overflow */ int songRemaining = (signed long int) (player.songDuration - player.songPlayed) / BAR_PLAYER_MS_TO_S_FACTOR; char pos = 0; if (songRemaining < 0) { /* Use plus sign if song is longer than expected */ pos = 1; songRemaining = -songRemaining; } BarUiMsg (MSG_TIME, "%c%02i:%02i/%02i:%02i\r", (pos ? '+' : '-'), songRemaining / 60, songRemaining % 60, player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR / 60, player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR % 60); } } /* destroy everything (including the world...) */ if (player.mode != PLAYER_FREED) { pthread_join (playerThread, NULL); } if (ctlFd != NULL) { fclose (ctlFd); } PianoDestroy (&ph); PianoDestroyPlaylist (songHistory); PianoDestroyPlaylist (playlist); ao_shutdown(); BarSettingsDestroy (&settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }