summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/console.c419
-rw-r--r--src/console.h (renamed from src/terminal.h)29
-rw-r--r--src/main.c278
-rw-r--r--src/main.h6
-rw-r--r--src/player.c451
-rw-r--r--src/player.h91
-rw-r--r--src/player2.c364
-rw-r--r--src/player2.h (renamed from src/terminal.c)62
-rw-r--r--src/settings.c75
-rw-r--r--src/settings.h6
-rw-r--r--src/ui.c218
-rw-r--r--src/ui.h6
-rw-r--r--src/ui_act.c77
-rw-r--r--src/ui_readline.c396
-rw-r--r--src/ui_readline.h20
15 files changed, 1378 insertions, 1120 deletions
diff --git a/src/console.c b/src/console.c
new file mode 100644
index 0000000..09d3116
--- /dev/null
+++ b/src/console.c
@@ -0,0 +1,419 @@
+/*
+Copyright (c) 2015
+ Michał Cichoń <thedmd@interia.pl>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "console.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <io.h>
+#include <pthread.h>
+#include "vtparse/vtparse.h"
+
+enum { READ = 0, WRITE };
+
+# define BAR_IN_CAPACITY 1024
+# define BAR_OUT_CAPACITY 1024
+
+static const int BarTerminalToAttibColor[8] = {
+ 0 /* black */, 4 /* red */, 2 /* green */, 6 /* yellow */,
+ 1 /* blue */, 5 /* magenta */, 3 /* cyan */, 7 /* white */
+};
+
+static struct BarConsoleState {
+ HANDLE StdIn;
+ HANDLE StdOut;
+
+ int Pipes[2];
+ int OriginalStdOut;
+ int OriginalStdErr;
+
+ WORD DefaultAttributes;
+ WORD CurrentAttributes;
+
+ pthread_t ConsoleThread;
+ bool Spawned;
+ bool Terminate;
+
+ vtparse_t Parser;
+
+ char InBuffer[BAR_IN_CAPACITY];
+ size_t InBufferSize;
+
+ char OutBuffer[BAR_OUT_CAPACITY];
+ size_t OutBufferSize;
+} g_BarConsole;
+
+static inline void BarOutSetAttributes(WORD attributes) {
+ g_BarConsole.CurrentAttributes = attributes;
+ SetConsoleTextAttribute(CONSOLE_REAL_OUTPUT_HANDLE, g_BarConsole.CurrentAttributes);
+}
+
+static inline void BarOutFlush() {
+ if (g_BarConsole.OutBufferSize == 0)
+ return;
+
+ _write(g_BarConsole.OriginalStdOut, g_BarConsole.OutBuffer, g_BarConsole.OutBufferSize);
+
+ g_BarConsole.OutBufferSize = 0;
+}
+
+static inline void BarOutPut(char c) {
+ if (g_BarConsole.OutBufferSize >= BAR_OUT_CAPACITY)
+ BarOutFlush();
+
+ g_BarConsole.OutBuffer[g_BarConsole.OutBufferSize++] = c;
+}
+
+static inline void BarOutPuts(const char* c) {
+ size_t length = strlen(c);
+ size_t i;
+
+ for (i = 0; i < length; ++i)
+ BarOutPut(c[i]);
+}
+
+static void BarParseCallback(struct vtparse* parser, vtparse_action_t action, unsigned char ch) {
+ if (action == VTPARSE_ACTION_PRINT || action == VTPARSE_ACTION_EXECUTE)
+ {
+ BarOutPut(ch);
+ if (action == VTPARSE_ACTION_EXECUTE)
+ BarOutFlush();
+ }
+
+ if (action == VTPARSE_ACTION_CSI_DISPATCH)
+ {
+ WORD attribute = g_BarConsole.CurrentAttributes;
+ int i;
+
+ switch (ch)
+ {
+ case 'K':
+ BarConsoleEraseLine(parser->num_params > 0 ? parser->params[0] : 0);
+ break;
+
+ case 'm':
+ for (i = 0; i < parser->num_params; ++i) {
+ int p = parser->params[i];
+ if (p == 0)
+ attribute = g_BarConsole.DefaultAttributes;
+ //else if (p == 1)
+ // attribute |= FOREGROUND_INTENSITY;
+ else if (p == 4)
+ attribute |= COMMON_LVB_UNDERSCORE;
+ else if (p == 7)
+ attribute |= COMMON_LVB_REVERSE_VIDEO;
+ //else if (p == 21)
+ // attribute &= ~FOREGROUND_INTENSITY;
+ else if (p == 24)
+ attribute &= ~COMMON_LVB_UNDERSCORE;
+ else if (p == 27)
+ attribute &= ~COMMON_LVB_REVERSE_VIDEO;
+ else if (p >= 30 && p <= 37)
+ attribute = (attribute & ~0x07) | BarTerminalToAttibColor[p - 30];
+ else if (p >= 40 && p <= 47)
+ attribute = (attribute & ~0x70) | (BarTerminalToAttibColor[p - 40] << 4);
+ else if (p >= 90 && p <= 97)
+ attribute = (attribute & ~0x07) | BarTerminalToAttibColor[p - 90] | FOREGROUND_INTENSITY;
+ else if (p >= 100 && p <= 107)
+ attribute = ((attribute & ~0x70) | (BarTerminalToAttibColor[p - 100] << 4)) | BACKGROUND_INTENSITY;
+ }
+ BarOutFlush();
+ BarOutSetAttributes(attribute);
+ break;
+ }
+ }
+}
+
+static void* BarConsoleThread(void* args) {
+
+ while (!g_BarConsole.Terminate) {
+
+ int bytes = _read(g_BarConsole.Pipes[READ], g_BarConsole.InBuffer, BAR_IN_CAPACITY);
+
+ if (bytes > 0)
+ vtparse(&g_BarConsole.Parser, g_BarConsole.InBuffer, bytes);
+
+ BarOutFlush();
+ }
+
+ return NULL;
+}
+
+void BarConsoleInit() {
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ memset(&g_BarConsole, 0, sizeof(g_BarConsole));
+
+ SetConsoleCP(CP_UTF8);
+ SetConsoleOutputCP(CP_UTF8);
+
+ g_BarConsole.StdIn = GetStdHandle(STD_INPUT_HANDLE);
+ g_BarConsole.StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ GetConsoleScreenBufferInfo(g_BarConsole.StdOut, &csbi);
+
+ g_BarConsole.DefaultAttributes = csbi.wAttributes;
+ g_BarConsole.CurrentAttributes = csbi.wAttributes;
+
+ if (_pipe(g_BarConsole.Pipes, 65536, O_BINARY) != -1) {
+ g_BarConsole.OriginalStdOut = _dup(_fileno(stdout));
+ g_BarConsole.OriginalStdErr = _dup(_fileno(stderr));
+
+ fflush(stdout);
+ fflush(stderr);
+
+ dup2(g_BarConsole.Pipes[WRITE], fileno(stdout));
+ dup2(g_BarConsole.Pipes[WRITE], fileno(stderr));
+
+ g_BarConsole.Spawned = pthread_create(&g_BarConsole.ConsoleThread, NULL, BarConsoleThread, NULL) == 0;
+ if (!g_BarConsole.Spawned)
+ BarConsoleDestroy();
+ }
+
+ vtparse_init(&g_BarConsole.Parser, BarParseCallback);
+}
+
+void BarConsoleDestroy() {
+ if (g_BarConsole.Spawned) {
+ g_BarConsole.Terminate = true;
+ fputs(" ", stderr);
+ fputs(" ", stdout);
+
+ fflush(stdout);
+ fflush(stderr);
+
+ pthread_join(g_BarConsole.ConsoleThread, NULL);
+ }
+
+ if (g_BarConsole.OriginalStdErr > 0) {
+ fflush(stderr);
+ dup2(g_BarConsole.OriginalStdErr, fileno(stderr));
+ _close(g_BarConsole.OriginalStdErr);
+ g_BarConsole.OriginalStdErr = 0;
+ }
+ if (g_BarConsole.OriginalStdOut > 0) {
+ fflush(stdout);
+ dup2(g_BarConsole.OriginalStdOut, fileno(stdout));
+ _close(g_BarConsole.OriginalStdOut);
+ g_BarConsole.OriginalStdOut = 0;
+ }
+ if (g_BarConsole.Pipes[0] > 0) {
+ _close(g_BarConsole.Pipes[0]);
+ g_BarConsole.Pipes[0] = 0;
+ }
+ if (g_BarConsole.Pipes[1] > 0) {
+ _close(g_BarConsole.Pipes[1]);
+ g_BarConsole.Pipes[1] = 0;
+ }
+}
+
+HANDLE BarConsoleGetStdIn () {
+ return g_BarConsole.StdIn;
+}
+
+HANDLE BarConsoleGetStdOut () {
+ return GetStdHandle(STD_OUTPUT_HANDLE);//g_BarConsole.StdOut;
+}
+
+void BarConsoleSetTitle (const char* title) {
+ size_t len = MultiByteToWideChar (CP_UTF8, 0, title, -1, NULL, 0);
+
+ TCHAR* wTitle = malloc((len + 1) * sizeof(TCHAR));
+ if (NULL != wTitle)
+ {
+ MultiByteToWideChar (CP_UTF8, 0, title, -1, wTitle, len);
+ SetConsoleTitleW (wTitle);
+
+ free(wTitle);
+ }
+ else
+ SetConsoleTitleA (title);
+}
+
+void BarConsoleSetSize (int width, int height) {
+ HANDLE handle;
+ SMALL_RECT r;
+ COORD c, s;
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ handle = CONSOLE_REAL_OUTPUT_HANDLE;
+
+ if (!GetConsoleScreenBufferInfo(handle, &csbi))
+ return;
+
+ s.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
+ s.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
+
+ if (s.X > width && s.Y > height)
+ return;
+
+ c.X = width;
+ c.Y = height;
+
+ if (s.X > c.X)
+ c.X = s.X;
+ if (s.Y > c.Y)
+ c.Y = s.Y;
+
+ SetConsoleScreenBufferSize(handle, c);
+
+ r.Left = 0;
+ r.Top = 0;
+ r.Right = c.X - 1;
+ r.Bottom = c.Y - 1;
+ SetConsoleWindowInfo(handle, TRUE, &r);
+}
+
+void BarConsoleSetCursorPosition (COORD position) {
+ SetConsoleCursorPosition(CONSOLE_REAL_OUTPUT_HANDLE, position);
+}
+
+COORD BarConsoleGetCursorPosition () {
+ CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
+ COORD result = { -1, -1 };
+
+ if (GetConsoleScreenBufferInfo(CONSOLE_REAL_OUTPUT_HANDLE, &consoleInfo))
+ result = consoleInfo.dwCursorPosition;
+
+ return result;
+}
+
+COORD BarConsoleMoveCursor (int xoffset) {
+ COORD position;
+
+ position = BarConsoleGetCursorPosition();
+ position.X += xoffset;
+ BarConsoleSetCursorPosition(position);
+
+ return position;
+}
+
+void BarConsoleEraseCharacter () {
+ TCHAR buffer[256];
+ WORD buffer2[256];
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ COORD coords;
+ HANDLE handle = CONSOLE_REAL_OUTPUT_HANDLE;
+ int length, read, write;
+
+ if (!GetConsoleScreenBufferInfo(handle, &csbiInfo))
+ return;
+
+ length = csbiInfo.dwSize.X - csbiInfo.dwCursorPosition.X - 1;
+ read = csbiInfo.dwCursorPosition.X + 1;
+ write = csbiInfo.dwCursorPosition.X;
+
+ while (length >= 0) {
+ int size = min(length, 256);
+ DWORD chRead = 0, chWritten = 0;
+ coords = csbiInfo.dwCursorPosition;
+ coords.X = read;
+ ReadConsoleOutputAttribute(handle, buffer2, size, coords, &chRead);
+ ReadConsoleOutputCharacter(handle, buffer, size, coords, &chRead);
+
+ if (chRead == 0)
+ break;
+
+ coords.X = write;
+ WriteConsoleOutputAttribute(handle, buffer2, chRead, coords, &chWritten);
+ WriteConsoleOutputCharacter(handle, buffer, chRead, coords, &chWritten);
+
+ read += chRead;
+ write += chRead;
+ length -= chRead;
+ }
+}
+
+void BarConsoleEraseLine (int mode) {
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ DWORD writen, length;
+ COORD coords;
+
+ HANDLE handle = CONSOLE_REAL_OUTPUT_HANDLE;
+
+ if (!GetConsoleScreenBufferInfo(handle, &csbiInfo))
+ return;
+
+ writen = 0;
+ coords.X = 0;
+ coords.Y = csbiInfo.dwCursorPosition.Y;
+
+ switch (mode) {
+ default:
+ case 0: /* from cursor */
+ coords.X = BarConsoleGetCursorPosition().X;
+ length = csbiInfo.dwSize.X - coords.X;
+ break;
+
+ case 1: /* to cursor */
+ coords.X = 0;
+ length = BarConsoleGetCursorPosition().X;
+ break;
+
+ case 2: /* whole line */
+ coords.X = 0;
+ length = csbiInfo.dwSize.X;
+ break;
+ }
+
+ FillConsoleOutputCharacter(handle, ' ', length, coords, &writen);
+ FillConsoleOutputAttribute(handle, csbiInfo.wAttributes, csbiInfo.dwSize.X, coords, &writen);
+
+ SetConsoleCursorPosition(handle, coords);
+}
+
+void BarConsoleSetClipboard(const char* text) {
+ WCHAR* wideString;
+ HANDLE stringHandle;
+ size_t wideSize;
+
+ wideSize = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
+ wideString = calloc(1, (wideSize + 1) * sizeof(WCHAR));
+ if (!wideString)
+ return;
+
+ MultiByteToWideChar(CP_UTF8, 0, text, -1, wideString, wideSize);
+
+ stringHandle = GlobalAlloc(GMEM_MOVEABLE, wideSize);
+ if (!stringHandle) {
+ free(wideString);
+ return;
+ }
+
+ memcpy(GlobalLock(stringHandle), wideString, wideSize);
+
+ GlobalUnlock(stringHandle);
+
+ if (!OpenClipboard(NULL)) {
+ GlobalFree(stringHandle);
+ free(wideString);
+ return;
+ }
+
+ EmptyClipboard();
+
+ SetClipboardData(CF_UNICODETEXT, stringHandle);
+
+ CloseClipboard();
+
+ free(wideString);
+} \ No newline at end of file
diff --git a/src/terminal.h b/src/console.h
index c43d01b..4b3a073 100644
--- a/src/terminal.h
+++ b/src/console.h
@@ -1,6 +1,6 @@
/*
-Copyright (c) 2008-2010
- Lars-Dominik Braun <lars@6xq.net>
+Copyright (c) 2015
+ Michał Cichoń <thedmd@interia.pl>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -21,10 +21,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_TERMINAL_H_WY8F3MNH
-#define SRC_TERMINAL_H_WY8F3MNH
+#ifndef SRC_CONSOLE_H_WY8F3MNH
+#define SRC_CONSOLE_H_WY8F3MNH
-void BarTermInit ();
-void BarTermRestore ();
+#include "config.h"
-#endif /* SRC_TERMINAL_H_WY8F3MNH */
+#include <windows.h>
+
+void BarConsoleInit ();
+void BarConsoleDestroy ();
+HANDLE BarConsoleGetStdIn ();
+HANDLE BarConsoleGetStdOut ();
+void BarConsoleSetTitle (const char* title);
+void BarConsoleSetSize (int width, int height);
+void BarConsoleSetCursorPosition (COORD position);
+COORD BarConsoleGetCursorPosition ();
+COORD BarConsoleMoveCursor (int xoffset);
+void BarConsoleEraseCharacter ();
+void BarConsoleEraseLine (int mode); // 0 - from cursor, 1 - to cursor, 2 - entire line
+
+void BarConsoleSetClipboard (const char*);
+
+#endif /* SRC_CONSOLE_H_WY8F3MNH */
diff --git a/src/main.c b/src/main.c
index b113f4e..4800d2d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -23,35 +23,11 @@ THE SOFTWARE.
#include "config.h"
-/* system includes */
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-/* fork () */
-#include <unistd.h>
-#include <sys/select.h>
-#include <time.h>
-#include <ctype.h>
-/* open () */
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-/* tcset/getattr () */
-#include <termios.h>
-#include <pthread.h>
#include <assert.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <signal.h>
-/* waitpid () */
-#include <sys/types.h>
-#include <sys/wait.h>
-
-/* pandora.com library */
#include <piano.h>
#include "main.h"
-#include "terminal.h"
+#include "console.h"
#include "ui.h"
#include "ui_dispatch.h"
#include "ui_readline.h"
@@ -79,14 +55,14 @@ static bool BarMainLoginUser (BarApp_t *app) {
/* ask for username/password if none were provided in settings
*/
static bool BarMainGetLoginCredentials (BarSettings_t *settings,
- BarReadlineFds_t *input) {
+ BarReadline_t rl) {
bool usernameFromConfig = true;
if (settings->username == NULL) {
char nameBuf[100];
BarUiMsg (settings, MSG_QUESTION, "Email: ");
- BarReadlineStr (nameBuf, sizeof (nameBuf), input, BAR_RL_DEFAULT);
+ BarReadlineStr (nameBuf, sizeof (nameBuf), rl, BAR_RL_DEFAULT);
settings->username = strdup (nameBuf);
usernameFromConfig = false;
}
@@ -100,58 +76,59 @@ static bool BarMainGetLoginCredentials (BarSettings_t *settings,
if (settings->passwordCmd == NULL) {
BarUiMsg (settings, MSG_QUESTION, "Password: ");
- BarReadlineStr (passBuf, sizeof (passBuf), input, BAR_RL_NOECHO);
+ BarReadlineStr (passBuf, sizeof (passBuf), rl, BAR_RL_NOECHO);
/* write missing newline */
puts ("");
settings->password = strdup (passBuf);
} else {
- pid_t chld;
- int pipeFd[2];
-
- BarUiMsg (settings, MSG_INFO, "Requesting password from external helper... ");
-
- if (pipe (pipeFd) == -1) {
- BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
- return false;
- }
-
- chld = fork ();
- if (chld == 0) {
- /* child */
- close (pipeFd[0]);
- dup2 (pipeFd[1], fileno (stdout));
- execl ("/bin/sh", "/bin/sh", "-c", settings->passwordCmd, (char *) NULL);
- BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
- close (pipeFd[1]);
- exit (1);
- } else if (chld == -1) {
- BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
- return false;
- } else {
- /* parent */
- int status;
-
- close (pipeFd[1]);
- memset (passBuf, 0, sizeof (passBuf));
- read (pipeFd[0], passBuf, sizeof (passBuf)-1);
- close (pipeFd[0]);
-
- /* drop trailing newlines */
- ssize_t len = strlen (passBuf)-1;
- while (len >= 0 && passBuf[len] == '\n') {
- passBuf[len] = '\0';
- --len;
- }
-
- waitpid (chld, &status, 0);
- if (WEXITSTATUS (status) == 0) {
- settings->password = strdup (passBuf);
- BarUiMsg (settings, MSG_NONE, "Ok.\n");
- } else {
- BarUiMsg (settings, MSG_NONE, "Error: Exit status %i.\n", WEXITSTATUS (status));
- return false;
- }
- }
+ //pid_t chld;
+ //int pipeFd[2];
+
+ //BarUiMsg (settings, MSG_INFO, "Requesting password from external helper... ");
+
+ //if (pipe (pipeFd) == -1) {
+ // BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
+ // return false;
+ //}
+
+ //chld = fork ();
+ //if (chld == 0) {
+ // /* child */
+ // close (pipeFd[0]);
+ // dup2 (pipeFd[1], fileno (stdout));
+ // execl ("/bin/sh", "/bin/sh", "-c", settings->passwordCmd, (char *) NULL);
+ // BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
+ // close (pipeFd[1]);
+ // exit (1);
+ //} else if (chld == -1) {
+ // BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
+ // return false;
+ //} else {
+ // /* parent */
+ // int status;
+
+ // close (pipeFd[1]);
+ // memset (passBuf, 0, sizeof (passBuf));
+ // read (pipeFd[0], passBuf, sizeof (passBuf)-1);
+ // close (pipeFd[0]);
+
+ // /* drop trailing newlines */
+ // ssize_t len = strlen (passBuf)-1;
+ // while (len >= 0 && passBuf[len] == '\n') {
+ // passBuf[len] = '\0';
+ // --len;
+ // }
+
+ // waitpid (chld, &status, 0);
+ // if (WEXITSTATUS (status) == 0) {
+ // settings->password = strdup (passBuf);
+ // BarUiMsg (settings, MSG_NONE, "Ok.\n");
+ // } else {
+ // BarUiMsg (settings, MSG_NONE, "Error: Exit status %i.\n", WEXITSTATUS (status));
+ // return false;
+ // }
+ //}
+ return false;
} /* end else passwordCmd */
}
@@ -172,7 +149,7 @@ static bool BarMainGetStations (BarApp_t *app) {
return ret;
}
-/* get initial station from autostart setting or user input
+/* get initial station from autostart setting or user rl
*/
static void BarMainGetInitialStation (BarApp_t *app) {
/* try to get autostart station */
@@ -194,11 +171,11 @@ static void BarMainGetInitialStation (BarApp_t *app) {
}
}
-/* wait for user input
+/* wait for user rl
*/
static void BarMainHandleUserInput (BarApp_t *app) {
char buf[2];
- if (BarReadline (buf, sizeof (buf), NULL, &app->input,
+ if (BarReadline (buf, sizeof (buf), NULL, app->rl,
BAR_RL_FULLRETURN | BAR_RL_NOECHO, 1) > 0) {
BarUiDispatch (app, buf[0], app->curStation, app->playlist, true,
BAR_DC_GLOBAL);
@@ -232,9 +209,8 @@ static void BarMainGetPlaylist (BarApp_t *app) {
/* start new player thread
*/
-static void BarMainStartPlayback (BarApp_t *app, pthread_t *playerThread) {
+static void BarMainStartPlayback (BarApp_t *app) {
assert (app != NULL);
- assert (playerThread != NULL);
const PianoSong_t * const curSong = app->playlist;
assert (curSong != NULL);
@@ -249,86 +225,66 @@ static void BarMainStartPlayback (BarApp_t *app, pthread_t *playerThread) {
strncmp (curSong->audioUrl, httpPrefix, strlen (httpPrefix)) != 0) {
BarUiMsg (&app->settings, MSG_ERR, "Invalid song url.\n");
} else {
- /* setup player */
- memset (&app->player, 0, sizeof (app->player));
-
- app->player.url = curSong->audioUrl;
- app->player.gain = curSong->fileGain;
- app->player.settings = &app->settings;
- app->player.songDuration = curSong->length;
- pthread_mutex_init (&app->player.pauseMutex, NULL);
- pthread_cond_init (&app->player.pauseCond, NULL);
+ BarPlayer2Open(app->player, curSong->audioUrl);
+ BarPlayer2SetGain(app->player, curSong->fileGain);
/* throw event */
BarUiStartEventCmd (&app->settings, "songstart",
app->curStation, curSong, &app->player, app->ph.stations,
PIANO_RET_OK, CURLE_OK);
- /* prevent race condition, mode must _not_ be DEAD if
- * thread has been started */
- app->player.mode = PLAYER_WAITING;
- /* start player */
- pthread_create (playerThread, NULL, BarPlayerThread,
- &app->player);
+ BarPlayer2Play(app->player);
}
}
/* player is done, clean up
*/
-static void BarMainPlayerCleanup (BarApp_t *app, pthread_t *playerThread) {
- void *threadRet;
-
+static void BarMainPlayerCleanup (BarApp_t *app) {
BarUiStartEventCmd (&app->settings, "songfinish", app->curStation,
app->playlist, &app->player, app->ph.stations, PIANO_RET_OK,
CURLE_OK);
- /* FIXME: pthread_join blocks everything if network connection
- * is hung up e.g. */
- pthread_join (*playerThread, &threadRet);
- pthread_cond_destroy (&app->player.pauseCond);
- pthread_mutex_destroy (&app->player.pauseMutex);
-
- if (threadRet == (void *) PLAYER_RET_OK) {
- app->playerErrors = 0;
- } else if (threadRet == (void *) PLAYER_RET_SOFTFAIL) {
- ++app->playerErrors;
- if (app->playerErrors >= app->settings.maxPlayerErrors) {
- /* don't continue playback if thread reports too many error */
- app->curStation = NULL;
- }
- } else {
- app->curStation = NULL;
- }
-
- memset (&app->player, 0, sizeof (app->player));
+ BarPlayer2Finish(app->player);
+
+ //if (threadRet == (void *) PLAYER_RET_OK) {
+ // app->playerErrors = 0;
+ //} else if (threadRet == (void *) PLAYER_RET_SOFTFAIL) {
+ // ++app->playerErrors;
+ // if (app->playerErrors >= app->settings.maxPlayerErrors) {
+ // /* don't continue playback if thread reports too many error */
+ // app->curStation = NULL;
+ // }
+ //} else {
+ // app->curStation = NULL;
+ //}
}
/* print song duration
*/
static void BarMainPrintTime (BarApp_t *app) {
- unsigned int songRemaining;
+ double songPlayed, songDuration, songRemaining;
char sign;
- if (app->player.songPlayed <= app->player.songDuration) {
- songRemaining = app->player.songDuration - app->player.songPlayed;
+ songDuration = BarPlayer2GetDuration(app->player);
+ songPlayed = BarPlayer2GetTime(app->player);
+
+ if (songPlayed <= songDuration) {
+ songRemaining = songDuration - songPlayed;
sign = '-';
} else {
/* longer than expected */
- songRemaining = app->player.songPlayed - app->player.songDuration;
+ songRemaining = songPlayed - songDuration;
sign = '+';
}
BarUiMsg (&app->settings, MSG_TIME, "%c%02u:%02u/%02u:%02u\r",
- sign, songRemaining / 60, songRemaining % 60,
- app->player.songDuration / 60,
- app->player.songDuration % 60);
+ sign, (int)songRemaining / 60, (int)songRemaining % 60,
+ (int)songDuration / 60, (int)songDuration % 60);
}
/* main loop
*/
static void BarMainLoop (BarApp_t *app) {
- pthread_t playerThread;
-
- if (!BarMainGetLoginCredentials (&app->settings, &app->input)) {
+ if (!BarMainGetLoginCredentials (&app->settings, app->rl)) {
return;
}
@@ -342,19 +298,15 @@ static void BarMainLoop (BarApp_t *app) {
BarMainGetInitialStation (app);
- /* 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) {
- BarMainPlayerCleanup (app, &playerThread);
+ if (BarPlayer2IsStopped(app->player)) {
+ BarMainPlayerCleanup (app);
}
/* check whether player finished playing and start playing new
* song */
- if (app->player.mode == PLAYER_DEAD && app->curStation != NULL) {
+ if (BarPlayer2IsFinished(app->player) && app->curStation != NULL) {
/* what's next? */
if (app->playlist != NULL) {
PianoSong_t *histsong = app->playlist;
@@ -367,21 +319,17 @@ static void BarMainLoop (BarApp_t *app) {
}
/* song ready to play */
if (app->playlist != NULL) {
- BarMainStartPlayback (app, &playerThread);
+ BarMainStartPlayback (app);
}
}
BarMainHandleUserInput (app);
/* show time */
- if (app->player.mode == PLAYER_PLAYING) {
+ if (BarPlayer2IsPlaying(app->player) || BarPlayer2IsPaused(app->player)) {
BarMainPrintTime (app);
}
}
-
- if (app->player.mode != PLAYER_DEAD) {
- pthread_join (playerThread, NULL);
- }
}
int main (int argc, char **argv) {
@@ -389,17 +337,10 @@ int main (int argc, char **argv) {
memset (&app, 0, sizeof (app));
- /* save terminal attributes, before disabling echoing */
- BarTermInit ();
-
- /* signals */
- signal (SIGPIPE, SIG_IGN);
+ BarConsoleInit ();
/* init some things */
- gcry_check_version (NULL);
- gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
- gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
- BarPlayerInit ();
+ BarPlayer2Init (&app.player);
BarSettingsInit (&app.settings);
BarSettingsRead (&app.settings);
@@ -414,7 +355,7 @@ int main (int argc, char **argv) {
}
BarUiMsg (&app.settings, MSG_NONE,
- "Welcome to " PACKAGE " (" VERSION ")! ");
+ "Welcome to " PACKAGE " (" VERSION ")!\n");
if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) {
BarUiMsg (&app.settings, MSG_NONE, "\n");
} else {
@@ -427,38 +368,11 @@ int main (int argc, char **argv) {
app.http = curl_easy_init ();
assert (app.http != NULL);
- /* 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;
+ BarReadlineInit (&app.rl);
BarMainLoop (&app);
- if (app.input.fds[1] != -1) {
- close (app.input.fds[1]);
- }
+ BarReadlineDestroy (app.rl);
/* write statefile */
BarSettingsWrite (app.curStation, &app.settings);
@@ -468,11 +382,9 @@ int main (int argc, char **argv) {
PianoDestroyPlaylist (app.playlist);
curl_easy_cleanup (app.http);
curl_global_cleanup ();
- BarPlayerDestroy ();
+ BarPlayer2Destroy (app.player);
BarSettingsDestroy (&app.settings);
-
- /* restore terminal attributes, zsh doesn't need this, bash does... */
- BarTermRestore ();
+ BarConsoleDestroy ();
return 0;
}
diff --git a/src/main.h b/src/main.h
index c8134ae..41413d9 100644
--- a/src/main.h
+++ b/src/main.h
@@ -28,21 +28,21 @@ THE SOFTWARE.
#include <piano.h>
-#include "player.h"
+#include "player2.h"
#include "settings.h"
#include "ui_readline.h"
typedef struct {
PianoHandle_t ph;
CURL *http;
- player_t player;
+ player2_t player;
BarSettings_t settings;
/* first item is current song */
PianoSong_t *playlist;
PianoSong_t *songHistory;
PianoStation_t *curStation;
char doQuit;
- BarReadlineFds_t input;
+ BarReadline_t rl;
unsigned int playerErrors;
} BarApp_t;
diff --git a/src/player.c b/src/player.c
deleted file mode 100644
index 0ba0f36..0000000
--- a/src/player.c
+++ /dev/null
@@ -1,451 +0,0 @@
-/*
-Copyright (c) 2008-2014
- Lars-Dominik Braun <lars@6xq.net>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
-/* receive/play audio stream */
-
-#include "config.h"
-
-#include <unistd.h>
-#include <string.h>
-#include <math.h>
-#include <stdint.h>
-#include <limits.h>
-#include <assert.h>
-#include <arpa/inet.h>
-
-#include <libavcodec/avcodec.h>
-#include <libavutil/avutil.h>
-#include <libavfilter/avfilter.h>
-#include <libavfilter/avfiltergraph.h>
-#include <libavfilter/buffersink.h>
-#include <libavfilter/buffersrc.h>
-#ifdef HAVE_LIBAVFILTER_AVCODEC_H
-/* required by ffmpeg1.2 for avfilter_copy_buf_props */
-#include <libavfilter/avcodec.h>
-#endif
-#include <libavutil/channel_layout.h>
-#include <libavutil/opt.h>
-#ifndef HAVE_AV_TIMEOUT
-#include <libavutil/time.h>
-#endif
-
-#include "player.h"
-#include "ui.h"
-#include "ui_types.h"
-
-/* default sample format */
-const enum AVSampleFormat avformat = AV_SAMPLE_FMT_S16;
-
-static void printError (const BarSettings_t * const settings,
- const char * const msg, int ret) {
- char avmsg[128];
- av_strerror (ret, avmsg, sizeof (avmsg));
- BarUiMsg (settings, MSG_ERR, "%s (%s)\n", msg, avmsg);
-}
-
-/* global initialization
- *
- * XXX: in theory we can select the filters/formats we want to support, but
- * this does not work in practise.
- */
-void BarPlayerInit () {
- ao_initialize ();
- av_register_all ();
- avfilter_register_all ();
- avformat_network_init ();
-}
-
-void BarPlayerDestroy () {
- avformat_network_deinit ();
- avfilter_uninit ();
- ao_shutdown ();
-}
-
-/* Update volume filter
- */
-void BarPlayerSetVolume (player_t * const player) {
- assert (player != NULL);
-
- if (player->mode != PLAYER_PLAYING) {
- return;
- }
-
- int ret;
-#ifdef HAVE_AVFILTER_GRAPH_SEND_COMMAND
- /* ffmpeg and libav disagree on the type of this option (string vs. double)
- * -> print to string and let them parse it again */
- char strbuf[16];
- snprintf (strbuf, sizeof (strbuf), "%fdB",
- player->settings->volume + player->gain);
- assert (player->fgraph != NULL);
- if ((ret = avfilter_graph_send_command (player->fgraph, "volume", "volume",
- strbuf, NULL, 0, 0)) < 0) {
-#else
- /* convert from decibel */
- const double volume = pow (10, (player->settings->volume + player->gain) / 20);
- /* libav does not provide other means to set this right now. it might not
- * even work everywhere. */
- assert (player->fvolume != NULL);
- if ((ret = av_opt_set_double (player->fvolume->priv, "volume", volume,
- 0)) != 0) {
-#endif
- printError (player->settings, "Cannot set volume", ret);
- }
-}
-
-#define softfail(msg) \
- printError (player->settings, msg, ret); \
- return false;
-
-#ifndef HAVE_AV_TIMEOUT
-/* interrupt callback for libav, which lacks a timeout option
- *
- * obviously calling ping() a lot of times and then calling av_gettime here
- * again is rather inefficient.
- */
-static int intCb (void * const data) {
- player_t * const player = data;
- assert (player != NULL);
- /* 10 seconds timeout (usec) */
- return (av_gettime () - player->ping) > 10*1000000;
-}
-
-#define ping() player->ping = av_gettime ()
-#else
-#define ping()
-#endif
-
-static bool openStream (player_t * const player) {
- assert (player != NULL);
- /* no leak? */
- assert (player->fctx == NULL);
-
- int ret;
-
- /* stream setup */
- AVDictionary *options = NULL;
-#ifdef HAVE_AV_TIMEOUT
- /* 10 seconds timeout on TCP r/w */
- av_dict_set (&options, "timeout", "10000000", 0);
-#else
- /* libav does not support the timeout option above. the workaround stores
- * the current time with ping() now and then, registers an interrupt
- * callback (below) and compares saved/current time in this callback. it’s
- * not bullet-proof, but seems to work fine for av_read_frame. */
- player->fctx = avformat_alloc_context ();
- player->fctx->interrupt_callback.callback = intCb;
- player->fctx->interrupt_callback.opaque = player;
-#endif
-
- assert (player->url != NULL);
- ping ();
- if ((ret = avformat_open_input (&player->fctx, player->url, NULL, &options)) < 0) {
- softfail ("Unable to open audio file");
- }
-
- ping ();
- if ((ret = avformat_find_stream_info (player->fctx, NULL)) < 0) {
- softfail ("find_stream_info");
- }
-
- /* ignore all streams, undone for audio stream below */
- for (size_t i = 0; i < player->fctx->nb_streams; i++) {
- player->fctx->streams[i]->discard = AVDISCARD_ALL;
- }
-
- ping ();
- player->streamIdx = av_find_best_stream (player->fctx, AVMEDIA_TYPE_AUDIO,
- -1, -1, NULL, 0);
- if (player->streamIdx < 0) {
- softfail ("find_best_stream");
- }
-
- player->st = player->fctx->streams[player->streamIdx];
- AVCodecContext * const cctx = player->st->codec;
- player->st->discard = AVDISCARD_DEFAULT;
-
- /* decoder setup */
- AVCodec * const decoder = avcodec_find_decoder (cctx->codec_id);
- if (decoder == NULL) {
- softfail ("find_decoder");
- }
-
- if ((ret = avcodec_open2 (cctx, decoder, NULL)) < 0) {
- softfail ("codec_open2");
- }
-
- if (player->lastTimestamp > 0) {
- ping ();
- av_seek_frame (player->fctx, player->streamIdx, player->lastTimestamp, 0);
- }
-
- player->songPlayed = 0;
- player->songDuration = av_q2d (player->st->time_base) *
- (double) player->st->duration;
-
- return true;
-}
-
-/* setup filter chain
- */
-static bool openFilter (player_t * const player) {
- /* filter setup */
- char strbuf[256];
- int ret = 0;
- AVCodecContext * const cctx = player->st->codec;
-
- if ((player->fgraph = avfilter_graph_alloc ()) == NULL) {
- softfail ("graph_alloc");
- }
-
- /* abuffer */
- AVRational time_base = player->st->time_base;
-
- /* Workaround for a bug in libav-11, which reports an invalid channel
- * layout mp3 files */
- if (cctx->channel_layout == 0) {
- cctx->channel_layout = av_get_default_channel_layout (cctx->channels);
- }
-
- snprintf (strbuf, sizeof (strbuf),
- "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64,
- time_base.num, time_base.den, cctx->sample_rate,
- av_get_sample_fmt_name (cctx->sample_fmt),
- cctx->channel_layout);
- if ((ret = avfilter_graph_create_filter (&player->fabuf,
- avfilter_get_by_name ("abuffer"), NULL, strbuf, NULL,
- player->fgraph)) < 0) {
- softfail ("create_filter abuffer");
- }
-
- /* volume */
- if ((ret = avfilter_graph_create_filter (&player->fvolume,
- avfilter_get_by_name ("volume"), NULL, NULL, NULL,
- player->fgraph)) < 0) {
- softfail ("create_filter volume");
- }
-
- /* aformat: convert float samples into something more usable */
- AVFilterContext *fafmt = NULL;
- snprintf (strbuf, sizeof (strbuf), "sample_fmts=%s",
- av_get_sample_fmt_name (avformat));
- if ((ret = avfilter_graph_create_filter (&fafmt,
- avfilter_get_by_name ("aformat"), NULL, strbuf, NULL,
- player->fgraph)) < 0) {
- softfail ("create_filter aformat");
- }
-
- /* abuffersink */
- if ((ret = avfilter_graph_create_filter (&player->fbufsink,
- avfilter_get_by_name ("abuffersink"), NULL, NULL, NULL,
- player->fgraph)) < 0) {
- softfail ("create_filter abuffersink");
- }
-
- /* connect filter: abuffer -> volume -> aformat -> abuffersink */
- if (avfilter_link (player->fabuf, 0, player->fvolume, 0) != 0 ||
- avfilter_link (player->fvolume, 0, fafmt, 0) != 0 ||
- avfilter_link (fafmt, 0, player->fbufsink, 0) != 0) {
- softfail ("filter_link");
- }
-
- if ((ret = avfilter_graph_config (player->fgraph, NULL)) < 0) {
- softfail ("graph_config");
- }
-
- return true;
-}
-
-/* setup libao
- */
-static bool openDevice (player_t * const player) {
- AVCodecContext * const cctx = player->st->codec;
-
- ao_sample_format aoFmt;
- memset (&aoFmt, 0, sizeof (aoFmt));
- aoFmt.bits = av_get_bytes_per_sample (avformat) * 8;
- assert (aoFmt.bits > 0);
- aoFmt.channels = cctx->channels;
- aoFmt.rate = cctx->sample_rate;
- aoFmt.byte_format = AO_FMT_NATIVE;
-
- int driver = ao_default_driver_id ();
- if ((player->aoDev = ao_open_live (driver, &aoFmt, NULL)) == NULL) {
- BarUiMsg (player->settings, MSG_ERR, "Cannot open audio device.\n");
- return false;
- }
-
- return true;
-}
-
-/* decode and play stream. returns 0 or av error code.
- */
-static int play (player_t * const player) {
- assert (player != NULL);
-
- AVPacket pkt;
- av_init_packet (&pkt);
- pkt.data = NULL;
- pkt.size = 0;
-
- AVFrame *frame = NULL, *filteredFrame = NULL;
- frame = avcodec_alloc_frame ();
- assert (frame != NULL);
- filteredFrame = avcodec_alloc_frame ();
- assert (filteredFrame != NULL);
-
- while (!player->doQuit) {
- ping ();
- int ret = av_read_frame (player->fctx, &pkt);
- if (ret < 0) {
- av_free_packet (&pkt);
- return ret;
- } else if (pkt.stream_index != player->streamIdx) {
- av_free_packet (&pkt);
- continue;
- }
-
- AVPacket pkt_orig = pkt;
-
- /* pausing */
- pthread_mutex_lock (&player->pauseMutex);
- if (player->doPause) {
- av_read_pause (player->fctx);
- do {
- pthread_cond_wait (&player->pauseCond, &player->pauseMutex);
- } while (player->doPause);
- av_read_play (player->fctx);
- }
- pthread_mutex_unlock (&player->pauseMutex);
-
- while (pkt.size > 0 && !player->doQuit) {
- int got_frame = 0;
-
- const int decoded = avcodec_decode_audio4 (player->st->codec,
- frame, &got_frame, &pkt);
- if (decoded < 0) {
- /* skip this one */
- break;
- }
-
- if (got_frame != 0) {
- /* XXX: suppresses warning from resample filter */
- if (frame->pts == (int64_t) AV_NOPTS_VALUE) {
- frame->pts = 0;
- }
- ret = av_buffersrc_write_frame (player->fabuf, frame);
- assert (ret >= 0);
-
- while (true) {
- AVFilterBufferRef *audioref = NULL;
-#ifdef HAVE_AV_BUFFERSINK_GET_BUFFER_REF
- /* ffmpeg’s compatibility layer is broken in some releases */
- if (av_buffersink_get_buffer_ref (player->fbufsink,
- &audioref, 0) < 0) {
-#else
- if (av_buffersink_read (player->fbufsink, &audioref) < 0) {
-#endif
- /* try again next frame */
- break;
- }
-
- ret = avfilter_copy_buf_props (filteredFrame, audioref);
- assert (ret >= 0);
-
- const int numChannels = av_get_channel_layout_nb_channels (
- filteredFrame->channel_layout);
- const int bps = av_get_bytes_per_sample(filteredFrame->format);
- ao_play (player->aoDev, (char *) filteredFrame->data[0],
- filteredFrame->nb_samples * numChannels * bps);
-
- avfilter_unref_bufferp (&audioref);
- }
- }
-
- pkt.data += decoded;
- pkt.size -= decoded;
- };
-
- av_free_packet (&pkt_orig);
-
- player->songPlayed = av_q2d (player->st->time_base) * (double) pkt.pts;
- player->lastTimestamp = pkt.pts;
- }
-
- avcodec_free_frame (&filteredFrame);
- avcodec_free_frame (&frame);
-
- return 0;
-}
-
-static void finish (player_t * const player) {
- ao_close (player->aoDev);
- player->aoDev = NULL;
- if (player->fgraph != NULL) {
- avfilter_graph_free (&player->fgraph);
- player->fgraph = NULL;
- }
- if (player->st != NULL && player->st->codec != NULL) {
- avcodec_close (player->st->codec);
- player->st = NULL;
- }
- if (player->fctx != NULL) {
- avformat_close_input (&player->fctx);
- }
-}
-
-/* player thread; for every song a new thread is started
- * @param audioPlayer structure
- * @return PLAYER_RET_*
- */
-void *BarPlayerThread (void *data) {
- assert (data != NULL);
-
- player_t * const player = data;
- intptr_t pret = PLAYER_RET_OK;
-
- bool retry;
- do {
- retry = false;
- if (openStream (player)) {
- if (openFilter (player) && openDevice (player)) {
- player->mode = PLAYER_PLAYING;
- BarPlayerSetVolume (player);
- retry = play (player) == AVERROR_INVALIDDATA;
- } else {
- /* filter missing or audio device busy */
- pret = PLAYER_RET_HARDFAIL;
- }
- } else {
- /* stream not found */
- pret = PLAYER_RET_SOFTFAIL;
- }
- player->mode = PLAYER_WAITING;
- finish (player);
- } while (retry);
-
- player->mode = PLAYER_FINISHED;
-
- return (void *) pret;
-}
-
diff --git a/src/player.h b/src/player.h
deleted file mode 100644
index e4d9f5b..0000000
--- a/src/player.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
-Copyright (c) 2008-2014
- Lars-Dominik Braun <lars@6xq.net>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
-#ifndef SRC_PLAYER_H_CN979RE9
-#define SRC_PLAYER_H_CN979RE9
-
-#include "config.h"
-
-/* required for freebsd */
-#include <sys/types.h>
-#include <pthread.h>
-#include <stdint.h>
-
-#include <ao/ao.h>
-#include <libavformat/avformat.h>
-#include <libavfilter/avfilter.h>
-#include <libavfilter/avfiltergraph.h>
-#include <piano.h>
-
-#include "settings.h"
-
-typedef struct {
- /* protected by pauseMutex */
- volatile bool doQuit;
- volatile bool doPause;
- pthread_mutex_t pauseMutex;
- pthread_cond_t pauseCond;
-
- enum {
- /* not running */
- PLAYER_DEAD = 0,
- /* running, but not ready to play music yet */
- PLAYER_WAITING,
- /* currently playing a song */
- PLAYER_PLAYING,
- /* finished playing a song */
- PLAYER_FINISHED,
- } mode;
-
- /* libav */
- AVFilterContext *fvolume;
- AVFilterGraph *fgraph;
- AVFormatContext *fctx;
- AVStream *st;
- AVFilterContext *fbufsink, *fabuf;
- int streamIdx;
- int64_t lastTimestamp;
-#ifndef HAVE_AV_TIMEOUT
- int64_t ping;
-#endif
-
- ao_device *aoDev;
-
- /* settings */
- double gain;
- char *url;
- const BarSettings_t *settings;
-
- /* measured in seconds */
- volatile unsigned int songDuration;
- volatile unsigned int songPlayed;
-} player_t;
-
-enum {PLAYER_RET_OK = 0, PLAYER_RET_HARDFAIL = 1, PLAYER_RET_SOFTFAIL = 2};
-
-void *BarPlayerThread (void *data);
-void BarPlayerSetVolume (player_t * const player);
-void BarPlayerInit ();
-void BarPlayerDestroy ();
-
-#endif /* SRC_PLAYER_H_CN979RE9 */
diff --git a/src/player2.c b/src/player2.c
new file mode 100644
index 0000000..906fa39
--- /dev/null
+++ b/src/player2.c
@@ -0,0 +1,364 @@
+/*
+Copyright (c) 2008-2014
+ Lars-Dominik Braun <lars@6xq.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/* receive/play audio stream */
+
+#include "config.h"
+#include "player2.h"
+#define COBJMACROS
+#define INITGUID
+#include <objbase.h>
+#include <dshow.h>
+#pragma comment(lib, "strmiids.lib")
+
+# define WM_GRAPH_EVENT (WM_APP + 1)
+
+enum { NO_GRAPH, RUNNING, PAUSED, STOPPED };
+
+struct _player_t {
+ int state;
+ IGraphBuilder* graph;
+ IMediaControl* control;
+ IMediaEventEx* event;
+ IBasicAudio* audio;
+ IMediaSeeking* media;
+ float volume; // dB
+ float gain; // dB
+};
+
+static void BarPlayer2ApplyVolume(player2_t player) {
+ long v = (long)((player->volume + player->gain) * 100.0f);
+
+ if (!player->audio)
+ return;
+
+ if (v < -10000)
+ v = -10000;
+ if (v > 0)
+ v = 0;
+
+ IBasicAudio_put_Volume(player->audio, v);
+}
+
+static void BarPlayer2TearDown(player2_t player) {
+ /* TODO: send final event */
+
+ if (player->graph) {
+ IGraphBuilder_Release(player->graph);
+ player->graph = NULL;
+ }
+
+ if (player->control) {
+ IMediaControl_Release(player->control);
+ player->control = NULL;
+ }
+
+ if (player->event) {
+ IMediaEventEx_Release(player->event);
+ player->event = NULL;
+ }
+
+ if (player->audio) {
+ IBasicAudio_Release(player->audio);
+ player->audio = NULL;
+ }
+
+ if (player->media) {
+ IMediaSeeking_Release(player->media);
+ player->media = NULL;
+ }
+
+ player->state = NO_GRAPH;
+}
+
+static HRESULT BarPlayer2Build(player2_t player) {
+ HRESULT hr;
+
+ BarPlayer2TearDown(player);
+
+ hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, &player->graph);
+ if (FAILED(hr))
+ return hr;
+
+ hr = IGraphBuilder_QueryInterface(player->graph, &IID_IMediaControl, &player->control);
+ if (FAILED(hr))
+ return hr;
+
+ hr = IGraphBuilder_QueryInterface(player->graph, &IID_IMediaEventEx, &player->event);
+ if (FAILED(hr))
+ return hr;
+
+ hr = IGraphBuilder_QueryInterface(player->graph, &IID_IBasicAudio, &player->audio);
+ if (FAILED(hr))
+ return hr;
+
+ hr = IGraphBuilder_QueryInterface(player->graph, &IID_IMediaSeeking, &player->media);
+ if (FAILED(hr))
+ return hr;
+
+ hr = IMediaEventEx_SetNotifyWindow(player->event, (OAHWND)NULL, WM_GRAPH_EVENT, (LONG_PTR)player);
+ if (FAILED(hr))
+ return hr;
+
+ player->state = STOPPED;
+
+ return S_OK;
+}
+
+static HRESULT BarPlayer2AddFilterByCLSID(IGraphBuilder *pGraph, REFGUID clsid, IBaseFilter **ppF, LPCWSTR wszName) {
+ IBaseFilter *pFilter = NULL;
+ HRESULT hr;
+ *ppF = 0;
+
+ hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, &pFilter);
+ if (FAILED(hr))
+ goto done;
+
+ hr = IGraphBuilder_AddFilter(pGraph, pFilter, wszName);
+ if (FAILED(hr))
+ goto done;
+
+ *ppF = pFilter;
+
+ IBaseFilter_AddRef(*ppF);
+
+done:
+ if (pFilter)
+ IBaseFilter_Release(pFilter);
+
+ return hr;
+}
+
+static HRESULT BarPlayer2Render(player2_t player, IBaseFilter* source) {
+ BOOL bRenderedAnyPin = FALSE;
+
+ IPin* pin = NULL;
+ IEnumPins *enumPins = NULL;
+ IBaseFilter *audioRenderer = NULL;
+ IFilterGraph2* filter = NULL;
+
+ HRESULT hr;
+
+ hr = IGraphBuilder_QueryInterface(player->graph, &IID_IFilterGraph2, &filter);
+ if (FAILED(hr))
+ return hr;
+
+ hr = BarPlayer2AddFilterByCLSID(player->graph, &CLSID_DSoundRender, &audioRenderer, L"Audio Renderer");
+ if (FAILED(hr))
+ goto done;
+
+ hr = IBaseFilter_EnumPins(source, &enumPins);
+ if (FAILED(hr))
+ goto done;
+
+ while (S_OK == IEnumPins_Next(enumPins, 1, &pin, NULL))
+ {
+ HRESULT hr2 = IFilterGraph2_RenderEx(filter, pin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);
+
+ IPin_Release(pin);
+ if (SUCCEEDED(hr2))
+ bRenderedAnyPin = TRUE;
+ }
+
+done:
+ if (enumPins)
+ IEnumPins_Release(enumPins);
+ if (enumPins)
+ IBaseFilter_Release(audioRenderer);
+ if (enumPins)
+ IFilterGraph2_Release(filter);
+
+ if (SUCCEEDED(hr) && !bRenderedAnyPin)
+ hr = VFW_E_CANNOT_RENDER;
+
+ return hr;
+}
+
+bool BarPlayer2Init(player2_t* player) {
+
+ if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
+ return false;
+
+ player2_t out = malloc(sizeof(struct _player_t));
+ if (!out)
+ return false;
+
+ memset(out, 0, sizeof(struct _player_t));
+
+ *player = out;
+
+ return true;
+}
+
+void BarPlayer2Destroy(player2_t player) {
+ BarPlayer2TearDown(player);
+ free(player);
+}
+
+void BarPlayer2SetVolume(player2_t player, float volume) {
+ player->volume = volume;
+ BarPlayer2ApplyVolume(player);
+}
+
+float BarPlayer2GetVolume(player2_t player) {
+ return player->volume;
+}
+
+void BarPlayer2SetGain(player2_t player, float gain) {
+ player->gain = gain;
+ BarPlayer2ApplyVolume(player);
+}
+
+float BarPlayer2GetGain(player2_t player) {
+ return player->gain;
+}
+
+double BarPlayer2GetDuration(player2_t player) {
+ LONGLONG time;
+ if (SUCCEEDED(IMediaSeeking_GetDuration(player->media, &time)))
+ return time / 10000000.0;
+ else
+ return 0;
+}
+
+double BarPlayer2GetTime(player2_t player) {
+ LONGLONG time;
+ if (SUCCEEDED(IMediaSeeking_GetCurrentPosition(player->media, &time)))
+ return time / 10000000.0;
+ else
+ return 0;
+}
+
+bool BarPlayer2Open(player2_t player, const char* url) {
+ IBaseFilter* source = NULL;
+ HRESULT hr;
+ wchar_t* wideUrl = NULL;
+ size_t urlSize;
+ int result;
+
+ hr = BarPlayer2Build(player);
+ if (FAILED(hr))
+ goto done;
+
+ urlSize = strlen(url);
+ result = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, url, urlSize, NULL, 0);
+ wideUrl = malloc((result + 1) * sizeof(wchar_t));
+ if (!wideUrl) {
+ hr = E_OUTOFMEMORY;
+ goto done;
+ }
+ memset(wideUrl, 0, (result + 1) * sizeof(wchar_t));
+
+ MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, url, urlSize, wideUrl, result);
+
+ hr = IGraphBuilder_AddSourceFilter(player->graph, wideUrl, NULL, &source);
+ if (FAILED(hr))
+ goto done;
+
+ hr = BarPlayer2Render(player, source);
+
+ BarPlayer2ApplyVolume(player);
+
+done:
+ if (wideUrl)
+ free(wideUrl);
+ if (FAILED(hr))
+ BarPlayer2TearDown(player);
+ if (source)
+ IBaseFilter_Release(source);
+
+ return SUCCEEDED(hr);
+}
+
+bool BarPlayer2Play(player2_t player) {
+ HRESULT hr;
+
+ if (player->state != PAUSED && player->state != STOPPED)
+ return false; /* wrong state */
+
+ hr = IMediaControl_Run(player->control);
+ if (SUCCEEDED(hr))
+ player->state = RUNNING;
+
+ return SUCCEEDED(hr);
+}
+
+bool BarPlayer2Pause(player2_t player) {
+ HRESULT hr;
+
+ if (player->state != RUNNING)
+ return false; /* wrong state */
+
+ hr = IMediaControl_Pause(player->control);
+ if (SUCCEEDED(hr))
+ player->state = PAUSED;
+
+ return SUCCEEDED(hr);
+}
+
+bool BarPlayer2Stop(player2_t player) {
+ HRESULT hr;
+
+ if (player->state != RUNNING && player->state != PAUSED)
+ return false; /* wrong state */
+
+ hr = IMediaControl_Stop(player->control);
+ if (SUCCEEDED(hr))
+ player->state = STOPPED;
+
+ return SUCCEEDED(hr);
+}
+
+bool BarPlayer2Finish(player2_t player) {
+ if (!player->control)
+ return false;
+
+ BarPlayer2TearDown(player);
+ return true;
+}
+
+bool BarPlayer2IsPlaying(player2_t player) {
+ return player->state == RUNNING;
+}
+
+bool BarPlayer2IsPaused(player2_t player) {
+ return player->state == PAUSED;
+}
+
+bool BarPlayer2IsStopped(player2_t player) {
+ return player->state == STOPPED;
+}
+
+bool BarPlayer2IsFinished(player2_t player) {
+ LONGLONG time;
+ LONGLONG duration;
+
+ if (!player->media)
+ return true;
+
+ if (FAILED(IMediaSeeking_GetDuration(player->media, &duration)) ||
+ FAILED(IMediaSeeking_GetCurrentPosition(player->media, &time)))
+ return true;
+
+ return time >= duration;
+}
diff --git a/src/terminal.c b/src/player2.h
index 2715d89..ac40eb4 100644
--- a/src/terminal.c
+++ b/src/player2.h
@@ -21,38 +21,32 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#include <termios.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <signal.h>
-
-#include "terminal.h"
-
-/* need a global variable here, since these functions get called from a signal
- * handler */
-static struct termios restore;
-
-/* init terminal attributes when continuing, assuming the shell modified them;
- * tcget/setattr and signal are async signal safe */
-static void BarTermHandleCont (int sig) {
- BarTermInit ();
-}
-
-void BarTermInit () {
- struct termios newopt;
-
- tcgetattr (STDIN_FILENO, &restore);
- memcpy (&newopt, &restore, sizeof (newopt));
-
- /* disable echoing and line buffer */
- newopt.c_lflag &= ~(ECHO | ICANON);
- tcsetattr (STDIN_FILENO, TCSANOW, &newopt);
-
- signal (SIGCONT, BarTermHandleCont);
-}
-
-void BarTermRestore () {
- tcsetattr (STDIN_FILENO, TCSANOW, &restore);
-}
+#ifndef SRC_PLAYER2_H_CN979RE9
+#define SRC_PLAYER2_H_CN979RE9
+
+#include "config.h"
+
+#include <stdbool.h>
+
+typedef struct _player_t *player2_t;
+
+bool BarPlayer2Init (player2_t*);
+void BarPlayer2Destroy (player2_t);
+void BarPlayer2SetVolume (player2_t,float);
+float BarPlayer2GetVolume (player2_t);
+void BarPlayer2SetGain (player2_t, float);
+float BarPlayer2GetGain (player2_t);
+double BarPlayer2GetDuration (player2_t);
+double BarPlayer2GetTime (player2_t);
+bool BarPlayer2Open (player2_t, const char*);
+bool BarPlayer2Play (player2_t);
+bool BarPlayer2Pause (player2_t);
+bool BarPlayer2Stop (player2_t);
+bool BarPlayer2Finish (player2_t);
+bool BarPlayer2IsPlaying (player2_t);
+bool BarPlayer2IsPaused (player2_t);
+bool BarPlayer2IsStopped (player2_t);
+bool BarPlayer2IsFinished (player2_t);
+
+#endif /* SRC_PLAYER2_H_CN979RE9 */
diff --git a/src/settings.c b/src/settings.c
index 11a2982..7ed10f5 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -25,40 +25,48 @@ THE SOFTWARE.
#include "config.h"
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <limits.h>
-#include <assert.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <unistd.h>
-
-#include <piano.h>
-
#include "settings.h"
#include "config.h"
#include "ui_dispatch.h"
+#include <stdlib.h>
+#include <assert.h>
+
+#define PACKAGE_CONFIG PACKAGE ".cfg"
+#define PACKAGE_STATE PACKAGE ".state"
+#define PACKAGE_PIPE PACKAGE ".ctrl"
#define streq(a, b) (strcmp (a, b) == 0)
-/* Get current user’s home directory
- */
-static char *BarSettingsGetHome () {
- char *home;
+static char* strndup(const char *s, size_t n)
+{
+ char *result;
+ const char* end = memchr(s, 0, n);
+ size_t len = end ? (size_t)(end - s) : n;
- /* try environment variable */
- if ((home = getenv ("HOME")) != NULL && strlen (home) > 0) {
- return strdup (home);
- }
+ if (len < n)
+ n = len;
- /* try passwd mechanism */
- struct passwd * const pw = getpwuid (getuid ());
- if (pw != NULL && pw->pw_dir != NULL && strlen (pw->pw_dir) > 0) {
- return strdup (pw->pw_dir);
- }
+ result = (char *)malloc(n + 1);
+ if (!result)
+ return 0;
- return NULL;
+ result[n] = '\0';
+
+ return (char*)memcpy(result, s, n);
+}
+
+/* Get current user’s home directory
+ */
+static char *BarSettingsGetHome () {
+ char* exec = NULL;
+ char* delimiter = NULL;
+
+ _get_pgmptr (&exec);
+ delimiter = strrchr (exec, '\\');
+ if (delimiter)
+ return strndup (exec, delimiter - exec);
+ else
+ return NULL;
}
/* Get XDG config directory, which is set by BarSettingsRead (if not set)
@@ -73,7 +81,7 @@ static char *BarGetXdgConfigDir (const char * const filename) {
const size_t len = (strlen (xdgConfigDir) + 1 +
strlen (filename) + 1);
char * const concat = malloc (len * sizeof (*concat));
- snprintf (concat, len, "%s/%s", xdgConfigDir, filename);
+ snprintf (concat, len, "%s\\%s", xdgConfigDir, filename);
return concat;
}
@@ -140,13 +148,13 @@ void BarSettingsDestroy (BarSettings_t *settings) {
* @return nothing yet
*/
void BarSettingsRead (BarSettings_t *settings) {
- char * const configfiles[] = {PACKAGE "/state", PACKAGE "/config"};
+ char * const configfiles[] = { PACKAGE_STATE, PACKAGE_CONFIG };
char * const userhome = BarSettingsGetHome ();
assert (userhome != NULL);
/* set xdg config path (if not set) */
- char * const defaultxdg = malloc (strlen (userhome) + strlen ("/.config") + 1);
- sprintf (defaultxdg, "%s/.config", userhome);
- setenv ("XDG_CONFIG_HOME", defaultxdg, 0);
+ char * const defaultxdg = malloc (strlen ("XDG_CONFIG_HOME=") + strlen (userhome) + 1);
+ sprintf (defaultxdg, "XDG_CONFIG_HOME=%s", userhome);
+ _putenv (defaultxdg);
free (defaultxdg);
assert (sizeof (settings->keys) / sizeof (*settings->keys) ==
@@ -172,7 +180,7 @@ void BarSettingsRead (BarSettings_t *settings) {
settings->device = strdup ("android-generic");
settings->inkey = strdup ("R=U!LH$O2B#");
settings->outkey = strdup ("6#26FRL$ZWD");
- settings->fifo = BarGetXdgConfigDir (PACKAGE "/ctl");
+ settings->fifo = BarGetXdgConfigDir (PACKAGE_PIPE);
assert (settings->fifo != NULL);
settings->msgFormat[MSG_NONE].prefix = NULL;
@@ -365,11 +373,6 @@ void BarSettingsRead (BarSettings_t *settings) {
}
}
- /* ffmpeg does not support setting an http proxy explicitly */
- if (settings->proxy != NULL) {
- setenv ("http_proxy", settings->proxy, 1);
- }
-
free (userhome);
}
diff --git a/src/settings.h b/src/settings.h
index d35a64c..d0fdbfa 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -28,6 +28,8 @@ THE SOFTWARE.
#include <piano.h>
+#include "ui_types.h"
+
/* update structure in ui_dispatch.h if you add shortcuts here */
typedef enum {
BAR_KS_HELP = 0,
@@ -76,8 +78,6 @@ typedef enum {
BAR_SORT_COUNT = 6,
} BarStationSorting_t;
-#include "ui_types.h"
-
typedef struct {
char *prefix;
char *postfix;
@@ -107,8 +107,6 @@ typedef struct {
BarMsgFormatStr_t msgFormat[MSG_COUNT];
} BarSettings_t;
-#include <piano.h>
-
void BarSettingsInit (BarSettings_t *);
void BarSettingsDestroy (BarSettings_t *);
void BarSettingsRead (BarSettings_t *);
diff --git a/src/ui.c b/src/ui.c
index 779dbf8..962f94c 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -25,22 +25,9 @@ THE SOFTWARE.
#include "config.h"
-#include <stdio.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <strings.h>
-#include <assert.h>
-#include <ctype.h> /* tolower() */
-
-/* waitpid () */
-#include <sys/types.h>
-#include <sys/wait.h>
-
#include "ui.h"
#include "ui_readline.h"
+#include <assert.h>
typedef int (*BarSortFunc_t) (const void *, const void *);
@@ -105,6 +92,7 @@ void BarUiMsg (const BarSettings_t *settings, const BarUiMsg_t type,
case MSG_QUESTION:
case MSG_LIST:
/* print ANSI clear line */
+
fputs ("\033[2K", stdout);
break;
@@ -438,7 +426,7 @@ PianoStation_t *BarUiSelectStation (BarApp_t *app, PianoStation_t *stations,
BarUiMsg (&app->settings, MSG_NONE, "%zi\n", lastDisplayed);
retStation = sortedStations[lastDisplayed];
} else {
- if (BarReadlineStr (buf, sizeof (buf), &app->input,
+ if (BarReadlineStr (buf, sizeof (buf), app->rl,
BAR_RL_DEFAULT) == 0) {
break;
}
@@ -468,7 +456,7 @@ PianoStation_t *BarUiSelectStation (BarApp_t *app, PianoStation_t *stations,
* @return pointer to selected item in song list or NULL
*/
PianoSong_t *BarUiSelectSong (const BarSettings_t *settings,
- PianoSong_t *startSong, BarReadlineFds_t *input) {
+ PianoSong_t *startSong, BarReadline_t rl) {
PianoSong_t *tmpSong = NULL;
char buf[100];
@@ -478,7 +466,7 @@ PianoSong_t *BarUiSelectSong (const BarSettings_t *settings,
BarUiListSongs (settings, startSong, buf);
BarUiMsg (settings, MSG_QUESTION, "Select song: ");
- if (BarReadlineStr (buf, sizeof (buf), input, BAR_RL_DEFAULT) == 0) {
+ if (BarReadlineStr (buf, sizeof (buf), rl, BAR_RL_DEFAULT) == 0) {
return NULL;
}
@@ -516,7 +504,7 @@ PianoArtist_t *BarUiSelectArtist (BarApp_t *app, PianoArtist_t *startArtist) {
}
BarUiMsg (&app->settings, MSG_QUESTION, "Select artist: ");
- if (BarReadlineStr (buf, sizeof (buf), &app->input,
+ if (BarReadlineStr (buf, sizeof (buf), app->rl,
BAR_RL_DEFAULT) == 0) {
return NULL;
}
@@ -546,7 +534,7 @@ char *BarUiSelectMusicId (BarApp_t *app, PianoStation_t *station,
PianoSong_t *tmpSong;
BarUiMsg (&app->settings, MSG_QUESTION, "%s", msg);
- if (BarReadlineStr (lineBuf, sizeof (lineBuf), &app->input,
+ if (BarReadlineStr (lineBuf, sizeof (lineBuf), app->rl,
BAR_RL_DEFAULT) > 0) {
PianoReturn_t pRet;
CURLcode wRet;
@@ -566,7 +554,7 @@ char *BarUiSelectMusicId (BarApp_t *app, PianoStation_t *station,
searchResult.artists != NULL) {
/* songs and artists found */
BarUiMsg (&app->settings, MSG_QUESTION, "Is this an [a]rtist or [t]rack name? ");
- BarReadline (selectBuf, sizeof (selectBuf), "at", &app->input,
+ BarReadline (selectBuf, sizeof (selectBuf), "at", app->rl,
BAR_RL_FULLRETURN, -1);
if (*selectBuf == 'a') {
tmpArtist = BarUiSelectArtist (app, searchResult.artists);
@@ -575,7 +563,7 @@ char *BarUiSelectMusicId (BarApp_t *app, PianoStation_t *station,
}
} else if (*selectBuf == 't') {
tmpSong = BarUiSelectSong (&app->settings, searchResult.songs,
- &app->input);
+ app->rl);
if (tmpSong != NULL) {
musicId = strdup (tmpSong->musicId);
}
@@ -583,7 +571,7 @@ char *BarUiSelectMusicId (BarApp_t *app, PianoStation_t *station,
} else if (searchResult.songs != NULL) {
/* songs found */
tmpSong = BarUiSelectSong (&app->settings, searchResult.songs,
- &app->input);
+ app->rl);
if (tmpSong != NULL) {
musicId = strdup (tmpSong->musicId);
}
@@ -751,103 +739,103 @@ size_t BarUiListSongs (const BarSettings_t *settings,
*/
void BarUiStartEventCmd (const BarSettings_t *settings, const char *type,
const PianoStation_t *curStation, const PianoSong_t *curSong,
- const player_t * const player, PianoStation_t *stations,
+ const player2_t * const player, PianoStation_t *stations,
PianoReturn_t pRet, CURLcode wRet) {
- pid_t chld;
- int pipeFd[2];
+ //pid_t chld;
+ //int pipeFd[2];
- if (settings->eventCmd == NULL) {
+ //if (settings->eventCmd == NULL) {
/* nothing to do... */
return;
- }
-
- if (pipe (pipeFd) == -1) {
- BarUiMsg (settings, MSG_ERR, "Cannot create eventcmd pipe. (%s)\n", strerror (errno));
- return;
- }
-
- chld = fork ();
- if (chld == 0) {
- /* child */
- close (pipeFd[1]);
- dup2 (pipeFd[0], fileno (stdin));
- execl (settings->eventCmd, settings->eventCmd, type, (char *) NULL);
- BarUiMsg (settings, MSG_ERR, "Cannot start eventcmd. (%s)\n", strerror (errno));
- close (pipeFd[0]);
- exit (1);
- } else if (chld == -1) {
- BarUiMsg (settings, MSG_ERR, "Cannot fork eventcmd. (%s)\n", strerror (errno));
- } else {
- /* parent */
- int status;
- PianoStation_t *songStation = NULL;
- FILE *pipeWriteFd;
-
- close (pipeFd[0]);
-
- pipeWriteFd = fdopen (pipeFd[1], "w");
-
- if (curSong != NULL && stations != NULL && curStation->isQuickMix) {
- songStation = PianoFindStationById (stations, curSong->stationId);
- }
-
- fprintf (pipeWriteFd,
- "artist=%s\n"
- "title=%s\n"
- "album=%s\n"
- "coverArt=%s\n"
- "stationName=%s\n"
- "songStationName=%s\n"
- "pRet=%i\n"
- "pRetStr=%s\n"
- "wRet=%i\n"
- "wRetStr=%s\n"
- "songDuration=%u\n"
- "songPlayed=%u\n"
- "rating=%i\n"
- "detailUrl=%s\n",
- curSong == NULL ? "" : curSong->artist,
- curSong == NULL ? "" : curSong->title,
- curSong == NULL ? "" : curSong->album,
- curSong == NULL ? "" : curSong->coverArt,
- curStation == NULL ? "" : curStation->name,
- songStation == NULL ? "" : songStation->name,
- pRet,
- PianoErrorToStr (pRet),
- wRet,
- curl_easy_strerror (wRet),
- player->songDuration,
- player->songPlayed,
- curSong == NULL ? PIANO_RATE_NONE : curSong->rating,
- curSong == NULL ? "" : curSong->detailUrl
- );
-
- if (stations != NULL) {
- /* send station list */
- PianoStation_t **sortedStations = NULL;
- size_t stationCount;
- sortedStations = BarSortedStations (stations, &stationCount,
- settings->sortOrder);
- assert (sortedStations != NULL);
-
- fprintf (pipeWriteFd, "stationCount=%zd\n", stationCount);
-
- for (size_t i = 0; i < stationCount; i++) {
- const PianoStation_t *currStation = sortedStations[i];
- fprintf (pipeWriteFd, "station%zd=%s\n", i,
- currStation->name);
- }
- free (sortedStations);
- } else {
- const char * const msg = "stationCount=0\n";
- fwrite (msg, sizeof (*msg), strlen (msg), pipeWriteFd);
- }
-
- /* closes pipeFd[1] as well */
- fclose (pipeWriteFd);
- /* wait to get rid of the zombie */
- waitpid (chld, &status, 0);
- }
+ //}
+
+ //if (pipe (pipeFd) == -1) {
+ // BarUiMsg (settings, MSG_ERR, "Cannot create eventcmd pipe. (%s)\n", strerror (errno));
+ // return;
+ //}
+
+ //chld = fork ();
+ //if (chld == 0) {
+ // /* child */
+ // close (pipeFd[1]);
+ // dup2 (pipeFd[0], fileno (stdin));
+ // execl (settings->eventCmd, settings->eventCmd, type, (char *) NULL);
+ // BarUiMsg (settings, MSG_ERR, "Cannot start eventcmd. (%s)\n", strerror (errno));
+ // close (pipeFd[0]);
+ // exit (1);
+ //} else if (chld == -1) {
+ // BarUiMsg (settings, MSG_ERR, "Cannot fork eventcmd. (%s)\n", strerror (errno));
+ //} else {
+ // /* parent */
+ // int status;
+ // PianoStation_t *songStation = NULL;
+ // FILE *pipeWriteFd;
+
+ // close (pipeFd[0]);
+
+ // pipeWriteFd = fdopen (pipeFd[1], "w");
+
+ // if (curSong != NULL && stations != NULL && curStation->isQuickMix) {
+ // songStation = PianoFindStationById (stations, curSong->stationId);
+ // }
+
+ // fprintf (pipeWriteFd,
+ // "artist=%s\n"
+ // "title=%s\n"
+ // "album=%s\n"
+ // "coverArt=%s\n"
+ // "stationName=%s\n"
+ // "songStationName=%s\n"
+ // "pRet=%i\n"
+ // "pRetStr=%s\n"
+ // "wRet=%i\n"
+ // "wRetStr=%s\n"
+ // "songDuration=%u\n"
+ // "songPlayed=%u\n"
+ // "rating=%i\n"
+ // "detailUrl=%s\n",
+ // curSong == NULL ? "" : curSong->artist,
+ // curSong == NULL ? "" : curSong->title,
+ // curSong == NULL ? "" : curSong->album,
+ // curSong == NULL ? "" : curSong->coverArt,
+ // curStation == NULL ? "" : curStation->name,
+ // songStation == NULL ? "" : songStation->name,
+ // pRet,
+ // PianoErrorToStr (pRet),
+ // wRet,
+ // curl_easy_strerror (wRet),
+ // player->songDuration,
+ // player->songPlayed,
+ // curSong == NULL ? PIANO_RATE_NONE : curSong->rating,
+ // curSong == NULL ? "" : curSong->detailUrl
+ // );
+
+ // if (stations != NULL) {
+ // /* send station list */
+ // PianoStation_t **sortedStations = NULL;
+ // size_t stationCount;
+ // sortedStations = BarSortedStations (stations, &stationCount,
+ // settings->sortOrder);
+ // assert (sortedStations != NULL);
+
+ // fprintf (pipeWriteFd, "stationCount=%zd\n", stationCount);
+
+ // for (size_t i = 0; i < stationCount; i++) {
+ // const PianoStation_t *currStation = sortedStations[i];
+ // fprintf (pipeWriteFd, "station%zd=%s\n", i,
+ // currStation->name);
+ // }
+ // free (sortedStations);
+ // } else {
+ // const char * const msg = "stationCount=0\n";
+ // fwrite (msg, sizeof (*msg), strlen (msg), pipeWriteFd);
+ // }
+ //
+ // /* closes pipeFd[1] as well */
+ // fclose (pipeWriteFd);
+ // /* wait to get rid of the zombie */
+ // waitpid (chld, &status, 0);
+ //}
}
/* prepend song to history
diff --git a/src/ui.h b/src/ui.h
index f49ec62..67b6155 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -29,7 +29,7 @@ THE SOFTWARE.
#include <piano.h>
#include "settings.h"
-#include "player.h"
+#include "player2.h"
#include "main.h"
#include "ui_readline.h"
#include "ui_types.h"
@@ -40,7 +40,7 @@ void BarUiMsg (const BarSettings_t *, const BarUiMsg_t, const char *, ...) __att
PianoStation_t *BarUiSelectStation (BarApp_t *, PianoStation_t *, const char *,
BarUiSelectStationCallback_t, bool);
PianoSong_t *BarUiSelectSong (const BarSettings_t *, PianoSong_t *,
- BarReadlineFds_t *);
+ BarReadline_t);
PianoArtist_t *BarUiSelectArtist (BarApp_t *, PianoArtist_t *);
char *BarUiSelectMusicId (BarApp_t *, PianoStation_t *, const char *);
void BarUiPrintStation (const BarSettings_t *, PianoStation_t *);
@@ -48,7 +48,7 @@ void BarUiPrintSong (const BarSettings_t *, const PianoSong_t *,
const PianoStation_t *);
size_t BarUiListSongs (const BarSettings_t *, const PianoSong_t *, const char *);
void BarUiStartEventCmd (const BarSettings_t *, const char *,
- const PianoStation_t *, const PianoSong_t *, const player_t *,
+ const PianoStation_t *, const PianoSong_t *, const player2_t * const,
PianoStation_t *, PianoReturn_t, CURLcode);
int BarUiPianoCall (BarApp_t * const, PianoRequestType_t,
void *, PianoReturn_t *, CURLcode *);
diff --git a/src/ui_act.c b/src/ui_act.c
index e8452e6..920ca6b 100644
--- a/src/ui_act.c
+++ b/src/ui_act.c
@@ -49,14 +49,10 @@ THE SOFTWARE.
/* helper to _really_ skip a song (unlock mutex, quit player)
* @param player handle
*/
-static inline void BarUiDoSkipSong (player_t * const player) {
+static inline void BarUiDoSkipSong (player2_t player) {
assert (player != NULL);
- pthread_mutex_lock (&player->pauseMutex);
- player->doQuit = true;
- player->doPause = false;
- pthread_cond_broadcast (&player->pauseCond);
- pthread_mutex_unlock (&player->pauseMutex);
+ BarPlayer2Stop(player);
}
/* transform station if necessary to allow changes like rename, rate, ...
@@ -148,7 +144,7 @@ BarUiActCallback(BarUiActBanSong) {
BarUiMsg (&app->settings, MSG_INFO, "Banning song... ");
if (BarUiActDefaultPianoCall (PIANO_REQUEST_RATE_SONG, &reqData) &&
selSong == app->playlist) {
- BarUiDoSkipSong (&app->player);
+ BarUiDoSkipSong (app->player);
}
BarUiActDefaultEventcmd ("songban");
}
@@ -183,7 +179,7 @@ BarUiActCallback(BarUiActCreateStationFromSong) {
reqData.type = PIANO_MUSICTYPE_INVALID;
BarUiMsg (&app->settings, MSG_QUESTION, "Create station from [s]ong or [a]rtist? ");
- BarReadline (selectBuf, sizeof (selectBuf), "sa", &app->input,
+ BarReadline (selectBuf, sizeof (selectBuf), "sa", app->rl,
BAR_RL_FULLRETURN, -1);
switch (selectBuf[0]) {
case 's':
@@ -213,7 +209,7 @@ BarUiActCallback(BarUiActAddSharedStation) {
reqData.type = PIANO_MUSICTYPE_INVALID;
BarUiMsg (&app->settings, MSG_QUESTION, "Station id: ");
- if (BarReadline (stationId, sizeof (stationId), "0123456789", &app->input,
+ if (BarReadline (stationId, sizeof (stationId), "0123456789", app->rl,
BAR_RL_DEFAULT, -1) > 0) {
BarUiMsg (&app->settings, MSG_INFO, "Adding shared station... ");
BarUiActDefaultPianoCall (PIANO_REQUEST_CREATE_STATION, &reqData);
@@ -231,11 +227,11 @@ BarUiActCallback(BarUiActDeleteStation) {
BarUiMsg (&app->settings, MSG_QUESTION, "Really delete \"%s\"? [yN] ",
selStation->name);
- if (BarReadlineYesNo (false, &app->input)) {
+ if (BarReadlineYesNo (false, app->rl)) {
BarUiMsg (&app->settings, MSG_INFO, "Deleting station... ");
if (BarUiActDefaultPianoCall (PIANO_REQUEST_DELETE_STATION,
selStation) && selStation == app->curStation) {
- BarUiDoSkipSong (&app->player);
+ BarUiDoSkipSong (app->player);
PianoDestroyPlaylist (PianoListNextP (app->playlist));
app->playlist->head.next = NULL;
BarUiHistoryPrepend (app, app->playlist);
@@ -296,7 +292,7 @@ BarUiActCallback(BarUiActStationFromGenre) {
do {
/* select category or exit */
BarUiMsg (&app->settings, MSG_QUESTION, "Select category: ");
- if (BarReadlineInt (&i, &app->input) == 0) {
+ if (BarReadlineInt (&i, app->rl) == 0) {
return;
}
curCat = PianoListGetP (app->ph.genreStations, i);
@@ -312,7 +308,7 @@ BarUiActCallback(BarUiActStationFromGenre) {
do {
BarUiMsg (&app->settings, MSG_QUESTION, "Select genre: ");
- if (BarReadlineInt (&i, &app->input) == 0) {
+ if (BarReadlineInt (&i, app->rl) == 0) {
return;
}
curGenre = PianoListGetP (curCat->genres, i);
@@ -349,6 +345,7 @@ BarUiActCallback(BarUiActDebug) {
/* print debug-alike infos */
BarUiMsg (&app->settings, MSG_NONE,
+ "\033[2K"
"album:\t%s\n"
"artist:\t%s\n"
"audioFormat:\t%i\n"
@@ -407,34 +404,28 @@ BarUiActCallback(BarUiActLoveSong) {
/* skip song
*/
BarUiActCallback(BarUiActSkipSong) {
- BarUiDoSkipSong (&app->player);
+ BarUiDoSkipSong (app->player);
}
/* play
*/
BarUiActCallback(BarUiActPlay) {
- pthread_mutex_lock (&app->player.pauseMutex);
- app->player.doPause = false;
- pthread_cond_broadcast (&app->player.pauseCond);
- pthread_mutex_unlock (&app->player.pauseMutex);
+ BarPlayer2Play(app->player);
}
/* pause
*/
BarUiActCallback(BarUiActPause) {
- pthread_mutex_lock (&app->player.pauseMutex);
- app->player.doPause = true;
- pthread_cond_broadcast (&app->player.pauseCond);
- pthread_mutex_unlock (&app->player.pauseMutex);
+ BarPlayer2Pause(app->player);
}
/* toggle pause
*/
BarUiActCallback(BarUiActTogglePause) {
- pthread_mutex_lock (&app->player.pauseMutex);
- app->player.doPause = !app->player.doPause;
- pthread_cond_broadcast (&app->player.pauseCond);
- pthread_mutex_unlock (&app->player.pauseMutex);
+ if (BarPlayer2IsPlaying(app->player))
+ BarPlayer2Play(app->player);
+ else
+ BarPlayer2Pause(app->player);
}
/* rename current station
@@ -447,7 +438,7 @@ BarUiActCallback(BarUiActRenameStation) {
assert (selStation != NULL);
BarUiMsg (&app->settings, MSG_QUESTION, "New name: ");
- if (BarReadlineStr (lineBuf, sizeof (lineBuf), &app->input, BAR_RL_DEFAULT) > 0) {
+ if (BarReadlineStr (lineBuf, sizeof (lineBuf), app->rl, BAR_RL_DEFAULT) > 0) {
PianoRequestDataRenameStation_t reqData;
if (!BarTransformIfShared (app, selStation)) {
return;
@@ -470,7 +461,7 @@ BarUiActCallback(BarUiActSelectStation) {
if (newStation != NULL) {
app->curStation = newStation;
BarUiPrintStation (&app->settings, app->curStation);
- BarUiDoSkipSong (&app->player);
+ BarUiDoSkipSong (app->player);
if (app->playlist != NULL) {
PianoDestroyPlaylist (PianoListNextP (app->playlist));
app->playlist->head.next = NULL;
@@ -491,7 +482,7 @@ BarUiActCallback(BarUiActTempBanSong) {
BarUiMsg (&app->settings, MSG_INFO, "Putting song on shelf... ");
if (BarUiActDefaultPianoCall (PIANO_REQUEST_ADD_TIRED_SONG, selSong) &&
selSong == app->playlist) {
- BarUiDoSkipSong (&app->player);
+ BarUiDoSkipSong (app->player);
}
BarUiActDefaultEventcmd ("songshelf");
}
@@ -573,7 +564,7 @@ BarUiActCallback(BarUiActSelectQuickMix) {
*/
BarUiActCallback(BarUiActQuit) {
app->doQuit = true;
- BarUiDoSkipSong (&app->player);
+ BarUiDoSkipSong (app->player);
}
/* song history
@@ -584,7 +575,7 @@ BarUiActCallback(BarUiActHistory) {
if (app->songHistory != NULL) {
histSong = BarUiSelectSong (&app->settings, app->songHistory,
- &app->input);
+ app->rl);
if (histSong != NULL) {
BarKeyShortcutId_t action;
PianoStation_t *songStation = PianoFindStationById (app->ph.stations,
@@ -600,7 +591,7 @@ BarUiActCallback(BarUiActHistory) {
BarUiMsg (&app->settings, MSG_QUESTION, "What to do with this song? ");
- if (BarReadline (buf, sizeof (buf), NULL, &app->input,
+ if (BarReadline (buf, sizeof (buf), NULL, app->rl,
BAR_RL_FULLRETURN, -1) > 0) {
/* actions assume that selStation is the song's original
* station */
@@ -625,7 +616,7 @@ BarUiActCallback(BarUiActBookmark) {
assert (selSong != NULL);
BarUiMsg (&app->settings, MSG_QUESTION, "Bookmark [s]ong or [a]rtist? ");
- BarReadline (selectBuf, sizeof (selectBuf), "sa", &app->input,
+ BarReadline (selectBuf, sizeof (selectBuf), "sa", app->rl,
BAR_RL_FULLRETURN, -1);
if (selectBuf[0] == 's') {
BarUiMsg (&app->settings, MSG_INFO, "Bookmarking song... ");
@@ -642,21 +633,21 @@ BarUiActCallback(BarUiActBookmark) {
*/
BarUiActCallback(BarUiActVolDown) {
--app->settings.volume;
- BarPlayerSetVolume (&app->player);
+ BarPlayer2SetVolume (app->player, (float)app->settings.volume);
}
/* increase volume
*/
BarUiActCallback(BarUiActVolUp) {
++app->settings.volume;
- BarPlayerSetVolume (&app->player);
+ BarPlayer2SetVolume (app->player, (float)app->settings.volume);
}
/* reset volume
*/
BarUiActCallback(BarUiActVolReset) {
app->settings.volume = 0;
- BarPlayerSetVolume (&app->player);
+ BarPlayer2SetVolume (app->player, (float)app->settings.volume);
}
static const char *boolToYesNo (const bool value) {
@@ -691,7 +682,7 @@ BarUiActCallback(BarUiActSettings) {
int val;
BarUiMsg (&app->settings, MSG_QUESTION, "Change setting: ");
- if (BarReadlineInt (&val, &app->input) == 0) {
+ if (BarReadlineInt (&val, app->rl) == 0) {
break;
}
@@ -700,7 +691,7 @@ BarUiActCallback(BarUiActSettings) {
/* username */
char buf[80];
BarUiMsg (&app->settings, MSG_QUESTION, "New username: ");
- if (BarReadlineStr (buf, sizeof (buf), &app->input,
+ if (BarReadlineStr (buf, sizeof (buf), app->rl,
BAR_RL_DEFAULT) > 0) {
reqData.newUsername = strdup (buf);
modified = true;
@@ -712,7 +703,7 @@ BarUiActCallback(BarUiActSettings) {
/* password */
char buf[80];
BarUiMsg (&app->settings, MSG_QUESTION, "New password: ");
- if (BarReadlineStr (buf, sizeof (buf), &app->input,
+ if (BarReadlineStr (buf, sizeof (buf), app->rl,
BAR_RL_NOECHO) > 0) {
reqData.newPassword = strdup (buf);
modified = true;
@@ -728,7 +719,7 @@ BarUiActCallback(BarUiActSettings) {
"Enable explicit content filter? [yn] ");
reqData.explicitContentFilter =
BarReadlineYesNo (settings.explicitContentFilter,
- &app->input) ? PIANO_TRUE : PIANO_FALSE;
+ app->rl) ? PIANO_TRUE : PIANO_FALSE;
modified = true;
break;
}
@@ -822,7 +813,7 @@ BarUiActCallback(BarUiActManageStation) {
}
BarUiMsg (&app->settings, MSG_QUESTION, "%s", question);
- if (BarReadline (selectBuf, sizeof (selectBuf), allowedActions, &app->input,
+ if (BarReadline (selectBuf, sizeof (selectBuf), allowedActions, app->rl,
BAR_RL_FULLRETURN, -1)) {
if (selectBuf[0] == 'a') {
PianoArtist_t *artist = BarUiSelectArtist (app,
@@ -839,7 +830,7 @@ BarUiActCallback(BarUiActManageStation) {
}
} else if (selectBuf[0] == 's') {
PianoSong_t *song = BarUiSelectSong (&app->settings,
- reqData.info.songSeeds, &app->input);
+ reqData.info.songSeeds, app->rl);
if (song != NULL) {
PianoRequestDataDeleteSeed_t subReqData;
@@ -866,7 +857,7 @@ BarUiActCallback(BarUiActManageStation) {
}
} else if (selectBuf[0] == 'f') {
PianoSong_t *song = BarUiSelectSong (&app->settings,
- reqData.info.feedback, &app->input);
+ reqData.info.feedback, app->rl);
if (song != NULL) {
BarUiMsg (&app->settings, MSG_INFO, "Deleting feedback... ");
BarUiActDefaultPianoCall (PIANO_REQUEST_DELETE_FEEDBACK, song);
diff --git a/src/ui_readline.c b/src/ui_readline.c
index eeb5c12..12f4ac8 100644
--- a/src/ui_readline.c
+++ b/src/ui_readline.c
@@ -21,13 +21,75 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#include <stdlib.h>
+//#include <stdlib.h>
+//#include <stdio.h>
+//#include <string.h>
+//#include <unistd.h>
+//#include <assert.h>
+
+#include "ui_readline.h"
+#include "console.h"
#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
#include <assert.h>
-#include "ui_readline.h"
+static inline char* BarReadlineNextUtf8 (char* ptr) {
+ if ((*ptr & 0x80) == 0)
+ return ptr + 1;
+ else if ((*ptr & 0xE0) == 0xC0)
+ return ptr + 2;
+ else if ((*ptr & 0xF0) == 0xE0)
+ return ptr + 3;
+ else if ((*ptr & 0xF8) == 0xF0)
+ return ptr + 4;
+ else
+ return ptr;
+}
+
+static inline char* BarReadlinePriorUtf8 (char* ptr) {
+ while ((*(--ptr) & 0xC0) == 0x80)
+ /*continue*/;
+
+ return ptr;
+}
+
+static inline int BarReadlineEncodeUtf8 (int codePoint, char* utf8) {
+ if (codePoint < 0x80) {
+ utf8[0] = (char)codePoint;
+ return 1;
+ }
+ else if (codePoint < 0x0800) {
+ utf8[0] = (char)(0xC0 | ((codePoint >> 6) & 0x1F));
+ utf8[1] = (char)(0x80 | ((codePoint >> 0) & 0x3F));
+ return 2;
+ }
+ else if (codePoint < 0x10000) {
+ utf8[0] = (char)(0xE0 | ((codePoint >> 12) & 0x0F));
+ utf8[1] = (char)(0x80 | ((codePoint >> 6) & 0x3F));
+ utf8[2] = (char)(0x80 | ((codePoint >> 0) & 0x3F));
+ return 3;
+ }
+ else if (codePoint < 0x110000) {
+ utf8[0] = (char)(0xF0 | ((codePoint >> 18) & 0x07));
+ utf8[1] = (char)(0x80 | ((codePoint >> 12) & 0x3F));
+ utf8[2] = (char)(0x80 | ((codePoint >> 6) & 0x3F));
+ utf8[2] = (char)(0x80 | ((codePoint >> 0) & 0x3F));
+ return 4;
+ }
+ else
+ return 0;
+}
+
+struct _BarReadline_t {
+ DWORD DefaultAttr;
+};
+
+void BarReadlineInit(BarReadline_t* rl) {
+ static struct _BarReadline_t instance;
+ *rl = &instance;
+}
+
+void BarReadlineDestroy(BarReadline_t rl) {
+}
/* return size of previous UTF-8 character
*/
@@ -46,152 +108,206 @@ static size_t BarReadlinePrevUtf8 (char *ptr) {
* @param buffer
* @param buffer size
* @param accept these characters
- * @param input fds
+ * @param readline
* @param flags
* @param timeout (seconds) or -1 (no timeout)
* @return number of bytes read from stdin
*/
size_t BarReadline (char *buf, const size_t bufSize, const char *mask,
- BarReadlineFds_t *input, const BarReadlineFlags_t flags, int timeout) {
- size_t bufLen = 0;
- unsigned char escapeState = 0;
- fd_set set;
- const bool echo = !(flags & BAR_RL_NOECHO);
-
- assert (buf != NULL);
- assert (bufSize > 0);
- assert (input != NULL);
-
- memset (buf, 0, bufSize);
-
- /* if fd is a fifo fgetc will always return EOF if nobody writes to
- * it, stdin will block */
- while (1) {
- int curFd = -1;
- unsigned char chr;
- struct timeval timeoutstruct;
-
- /* select modifies set and timeout */
- memcpy (&set, &input->set, sizeof (set));
- timeoutstruct.tv_sec = timeout;
- timeoutstruct.tv_usec = 0;
-
- if (select (input->maxfd, &set, NULL, NULL,
- (timeout == -1) ? NULL : &timeoutstruct) <= 0) {
- /* fail or timeout */
- break;
- }
+ BarReadline_t input, const BarReadlineFlags_t flags, int timeout) {
+ HANDLE handle = BarConsoleGetStdIn();
+ DWORD timeStamp, waitResult;
+ int bufPos = 0, bufLen = 0;
+ char* bufOut = buf;
- assert (sizeof (input->fds) / sizeof (*input->fds) == 2);
- if (FD_ISSET(input->fds[0], &set)) {
- curFd = input->fds[0];
- } else if (input->fds[1] != -1 && FD_ISSET(input->fds[1], &set)) {
- curFd = input->fds[1];
- }
- if (read (curFd, &chr, sizeof (chr)) <= 0) {
- /* select() is going wild if fdset contains EOFed stdin, only check
- * for stdin, fifo is "reopened" as soon as another writer is
- * available
- * FIXME: ugly */
- if (curFd == STDIN_FILENO) {
- FD_CLR (curFd, &input->set);
+ const bool overflow = flags & BAR_RL_FULLRETURN;
+ const bool echo = !(flags & BAR_RL_NOECHO);
+
+ assert(buf != NULL);
+ assert(bufSize > 0);
+ assert(input != NULL);
+
+ memset(buf, 0, bufSize);
+
+ if (timeout != INFINITE) {
+ // convert timeout to ms
+ timeout *= 1000;
+
+ // get time stamp, required for simulating non-locking input timeouts
+ timeStamp = GetTickCount();
+ }
+ else
+ timeStamp = 0;
+
+ while (true) {
+ if (timeout != INFINITE) {
+ DWORD now = GetTickCount();
+ if ((int)(now - timeStamp) < timeout) {
+ timeout -= (int)(now - timeStamp);
+ timeStamp = now;
}
- continue;
+ else
+ timeout = 0;
}
- switch (chr) {
- /* EOT */
- case 4:
- /* return */
- case 10:
- if (echo) {
- fputs ("\n", stdout);
- }
- buf[bufLen] = '\0';
- return bufLen;
- break;
-
- /* clear line */
- case 21:
- if (echo) {
- while (bufLen > 0) {
- const size_t moveSize = BarReadlinePrevUtf8 (&buf[bufLen]);
- assert (bufLen >= moveSize);
-
- /* move caret and delete character */
- fputs ("\033[D\033[K", stdout);
- bufLen -= moveSize;
- }
- fflush (stdout);
- }
- bufLen = 0;
- break;
-
- /* escape */
- case 27:
- escapeState = 1;
- break;
-
- /* del */
- case 126:
- break;
-
- /* backspace */
- case 8: /* ASCII BS */
- case 127: /* ASCII DEL */
- if (bufLen > 0) {
- size_t moveSize = BarReadlinePrevUtf8 (&buf[bufLen]);
- assert (bufLen >= moveSize);
- memmove (&buf[bufLen-moveSize], &buf[bufLen], moveSize);
-
- bufLen -= moveSize;
-
- /* move caret back and delete last character */
- if (echo) {
- fputs ("\033[D\033[K", stdout);
- fflush (stdout);
- }
- }
- break;
- default:
- /* ignore control/escape characters */
- if (chr <= 0x1F) {
- break;
- }
- if (escapeState == 2) {
- escapeState = 0;
- break;
- }
- if (escapeState == 1 && chr == '[') {
- escapeState = 2;
- break;
- }
- /* don't accept chars not in mask */
- if (mask != NULL && !strchr (mask, chr)) {
- break;
- }
- /* don't write beyond buffer's limits */
- if (bufLen < bufSize-1) {
- buf[bufLen] = chr;
- ++bufLen;
- if (echo) {
- putchar (chr);
- fflush (stdout);
- }
- /* buffer full => return if requested */
- if (bufLen >= bufSize-1 && (flags & BAR_RL_FULLRETURN)) {
+ waitResult = WaitForSingleObject(handle, timeout);
+
+ if (WAIT_OBJECT_0 == waitResult) {
+ INPUT_RECORD inputRecords[8];
+ INPUT_RECORD* record;
+ DWORD recordsRead, i;
+
+ ReadConsoleInput(handle, inputRecords, sizeof(inputRecords) / sizeof(*inputRecords), &recordsRead);
+
+ for (i = 0, record = inputRecords; i < recordsRead; ++i, ++record)
+ {
+ int codePoint, keyCode;
+
+ if ((record->EventType != KEY_EVENT) || !record->Event.KeyEvent.bKeyDown)
+ continue;
+
+ keyCode = record->Event.KeyEvent.wVirtualKeyCode;
+ codePoint = record->Event.KeyEvent.uChar.UnicodeChar;
+
+ switch (keyCode) {
+ case VK_LEFT:
+ if (bufPos > 0)
+ {
+ if (echo) {
+ BarConsoleMoveCursor(-1);
+ }
+ bufOut = BarReadlinePriorUtf8(bufOut);
+ --bufPos;
+ }
+ break;
+
+ case VK_RIGHT:
+ if (bufPos < bufLen)
+ {
+ if (echo) {
+ BarConsoleMoveCursor(1);
+ }
+ bufOut = BarReadlineNextUtf8(bufOut);
+ ++bufPos;
+ }
+ break;
+
+ case VK_HOME:
if (echo) {
- fputs ("\n", stdout);
+ BarConsoleMoveCursor(-bufPos);
+ }
+ bufPos = 0;
+ bufOut = buf;
+ break;
+
+ case VK_END:
+ if (echo) {
+ BarConsoleMoveCursor(bufLen - bufPos);
+ }
+ bufPos = bufLen;
+ bufOut = buf + strlen(buf);
+ break;
+
+ case VK_BACK:
+ if (bufPos > 0) {
+ int moveSize;
+
+ char* oldBufOut = bufOut;
+ bufOut = BarReadlinePriorUtf8(bufOut);
+ moveSize = strlen(bufOut) - (oldBufOut - bufOut);
+ memmove(bufOut, oldBufOut, moveSize);
+ bufOut[moveSize] = '\0';
+
+ if (echo) {
+ BarConsoleMoveCursor(-1);
+ BarConsoleEraseCharacter();
+ }
+
+ --bufPos;
+ --bufLen;
+ }
+ break;
+
+ case VK_DELETE:
+ if (bufPos < bufLen) {
+ int moveSize;
+
+ if (echo) {
+ BarConsoleEraseCharacter();
+ }
+
+ char* nextCharOut = BarReadlineNextUtf8(bufOut);
+ moveSize = strlen(bufOut) - (bufOut - nextCharOut);
+ memmove(bufOut, nextCharOut, moveSize);
+ bufOut[moveSize] = '\0';
+
+ --bufLen;
}
- buf[bufLen] = '\0';
+ break;
+
+ case VK_RETURN:
+ if (echo)
+ fputc('\n', stdout);
return bufLen;
- }
+
+ default: {
+ char encodedCodePoint[5];
+ int encodedCodePointLength;
+
+ /*
+ if (keyCode == VK_MEDIA_PLAY_PAUSE) {
+ codePoint = 'p';
+ PlaySoundA("SystemNotification", NULL, SND_ASYNC);
+ }
+ else if (keyCode == VK_MEDIA_NEXT_TRACK) {
+ codePoint = 'n';
+ PlaySoundA("SystemNotification", NULL, SND_ASYNC);
+ }
+ */
+
+ if (codePoint <= 0x1F)
+ break;
+
+ /* don't accept chars not in mask */
+ if (mask != NULL && (codePoint > 255 || !strchr(mask, (char)codePoint)))
+ break;
+
+ encodedCodePointLength = BarReadlineEncodeUtf8(codePoint, encodedCodePoint);
+ encodedCodePoint[encodedCodePointLength] = '\0';
+
+ if (bufLen + encodedCodePointLength < (int)bufSize)
+ {
+ strncpy(bufOut, encodedCodePoint, encodedCodePointLength);
+
+ if (echo) {
+ fputs(encodedCodePoint, stdout);
+ fflush(stdout);
+ }
+
+ bufOut += encodedCodePointLength;
+ ++bufPos;
+ ++bufLen;
+
+ if ((bufLen >= (int)(bufSize - 1)) && overflow)
+ {
+ if (echo)
+ fputc('\n', stdout);
+ return bufLen;
+ }
+ }
+ }
+ break;
}
- break;
- } /* end switch */
- } /* end while */
- buf[0] = '\0';
- return 0;
+ }
+ }
+ else if (WAIT_TIMEOUT == waitResult)
+ break;
+ else
+ /* TODO: Handle errors. */
+ break;
+ }
+
+ return bufLen;
}
/* Read string from stdin
@@ -200,7 +316,7 @@ size_t BarReadline (char *buf, const size_t bufSize, const char *mask,
* @return number of bytes read from stdin
*/
size_t BarReadlineStr (char *buf, const size_t bufSize,
- BarReadlineFds_t *input, const BarReadlineFlags_t flags) {
+ BarReadline_t input, const BarReadlineFlags_t flags) {
return BarReadline (buf, bufSize, NULL, input, flags, -1);
}
@@ -208,7 +324,7 @@ size_t BarReadlineStr (char *buf, const size_t bufSize,
* @param write result into this variable
* @return number of bytes read from stdin
*/
-size_t BarReadlineInt (int *ret, BarReadlineFds_t *input) {
+size_t BarReadlineInt (int *ret, BarReadline_t input) {
int rlRet = 0;
char buf[16];
@@ -222,7 +338,7 @@ size_t BarReadlineInt (int *ret, BarReadlineFds_t *input) {
/* Yes/No?
* @param default (user presses enter)
*/
-bool BarReadlineYesNo (bool def, BarReadlineFds_t *input) {
+bool BarReadlineYesNo (bool def, BarReadline_t input) {
char buf[2];
BarReadline (buf, sizeof (buf), "yYnN", input, BAR_RL_FULLRETURN, -1);
if (*buf == 'y' || *buf == 'Y' || (def == true && *buf == '\0')) {
diff --git a/src/ui_readline.h b/src/ui_readline.h
index cf8ed52..9343e1a 100644
--- a/src/ui_readline.h
+++ b/src/ui_readline.h
@@ -24,8 +24,10 @@ THE SOFTWARE.
#ifndef SRC_UI_READLINE_H_IFRX74VM
#define SRC_UI_READLINE_H_IFRX74VM
+#include "config.h"
+
#include <stdbool.h>
-#include <sys/select.h>
+#include <stdlib.h>
typedef enum {
BAR_RL_DEFAULT = 0,
@@ -33,18 +35,16 @@ typedef enum {
BAR_RL_NOECHO = 2, /* don't echo to stdout */
} BarReadlineFlags_t;
-typedef struct {
- fd_set set;
- int maxfd;
- int fds[2];
-} BarReadlineFds_t;
+typedef struct _BarReadline_t *BarReadline_t;
+void BarReadlineInit(BarReadline_t*);
+void BarReadlineDestroy(BarReadline_t);
size_t BarReadline (char *, const size_t, const char *,
- BarReadlineFds_t *, const BarReadlineFlags_t, int);
+ BarReadline_t, const BarReadlineFlags_t, int);
size_t BarReadlineStr (char *, const size_t,
- BarReadlineFds_t *, const BarReadlineFlags_t);
-size_t BarReadlineInt (int *, BarReadlineFds_t *);
-bool BarReadlineYesNo (bool, BarReadlineFds_t *);
+ BarReadline_t, const BarReadlineFlags_t);
+size_t BarReadlineInt (int *, BarReadline_t);
+bool BarReadlineYesNo (bool, BarReadline_t);
#endif /* SRC_UI_READLINE_H_IFRX74VM */