/*
- * Copyright (C) 1998-2009 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1998-2010 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
/** \file gui.c Curses-based interface for paraslash. */
+#include <regex.h>
#include <signal.h>
#include <sys/types.h>
#include <dirent.h>
+#include <curses.h>
+
#include "gui.cmdline.h"
#include "para.h"
#include "gui.h"
#include "string.h"
-#include <curses.h>
#include "ringbuffer.h"
#include "fd.h"
#include "error.h"
size_t len;
int color;
};
-struct ringbuffer *bot_win_rb;
+static struct ringbuffer *bot_win_rb;
#define NUM_LINES(len) (1 + (len) / bot.cols)
static unsigned scroll_position;
static pid_t cmd_pid;
static int command_pipe = -1;
-static int audiod_pipe = -1;
+static int stat_pipe = -1;
static struct gui_args_info conf;
enum {GETCH_MODE, COMMAND_MODE, EXTERNAL_MODE};
static struct gui_theme theme;
-int _argc;
-char **_argv;
+static int _argc;
+static char **_argv;
static void com_help(void);
static void com_reread_conf(void);
static void com_page_up(void);
static void com_page_down(void);
-struct gui_command command_list[] = {
+static struct gui_command command_list[] = {
{
.key = "?",
.name = "help",
}
};
-static int para_open_audiod_pipe(char *cmd)
-{
- int fds[3] = {0, 1, 0};
- pid_t pid;
- int ret = para_exec_cmdline_pid(&pid, cmd, fds);
- if (ret < 0)
- return ret;
- ret = mark_fd_nonblocking(fds[1]);
- if (ret > 0)
- return fds[1];
- close(fds[1]);
- return ret;
-}
-
-static int read_audiod_pipe(int fd, line_handler_t *line_handler)
-{
- static char buf[4096];
- const ssize_t bufsize = sizeof(buf) - 1;
- static ssize_t loaded;
- ssize_t ret;
-
- if (loaded >= bufsize)
- loaded = 0;
- ret = read(fd, buf + loaded, bufsize - loaded);
- if (ret > 0) {
- loaded += ret;
- buf[loaded] = '\0';
- loaded = for_each_line(buf, loaded, line_handler, NULL);
- }
- return ret;
-}
-
static int find_cmd_byname(char *name)
{
int i;
* print aligned string to curses window. This function always prints
* exactly len chars.
*/
-static int align_str(WINDOW* win, const char *string, unsigned int len,
+static int align_str(WINDOW* win, char *str, unsigned int len,
unsigned int align)
{
- int num; /* of spaces */
- char *str;
+ int i, num; /* of spaces */
- if (!win || !string)
+ if (!win || !str)
return -1;
- num = len - strlen(string);
- str = para_strdup(string);
+ num = len - strlen(str);
if (num < 0) {
str[len] = '\0';
num = 0;
}
+ /* replace control characters by spaces */
+ for (i = 0; i < len && str[i]; i++) {
+ if (str[i] == '\n' || str[i] == '\r' || str[i] == '\f')
+ str[i] = ' ';
+ }
if (align == LEFT) {
waddstr(win, str);
add_spaces(win, num);
waddstr(win, str[0]? str: "");
add_spaces(win, num - num / 2);
}
- free(str);
return 1;
}
*/
static void print_status_bar(void)
{
+ char *tmp;
+
if (!curses_active)
return;
+ tmp = para_strdup(STANDARD_STATUS_BAR);
wmove(sb.win, 0, 0);
- align_str(sb.win, STANDARD_STATUS_BAR, sb.cols, CENTER);
+ align_str(sb.win, tmp, sb.cols, CENTER);
+ free(tmp);
wrefresh(sb.win);
}
wrefresh(top.win);
}
+static int update_item(int item_num, char *buf)
+{
+ char **c = stat_content + item_num;
+
+ free(*c);
+ if (buf && buf[0])
+ goto dup;
+ switch (item_num) {
+ case SI_ARTIST:
+ *c = para_strdup("(artist tag not set)");
+ goto print;
+ case SI_TITLE:
+ *c = para_strdup("(title tag not set)");
+ goto print;
+ case SI_YEAR:
+ *c = para_strdup("????");
+ goto print;
+ case SI_ALBUM:
+ *c = para_strdup("(album tag not set)");
+ goto print;
+ case SI_COMMENT:
+ *c = para_strdup("(comment tag not set)");
+ goto print;
+ }
+dup:
+ *c = para_strdup(buf);
+print:
+ print_stat_item(item_num);
+ return 1;
+}
+
+static int read_stat_pipe(fd_set *rfds)
+{
+ static char *buf;
+ static int bufsize, loaded;
+ int ret, ret2;
+ size_t sz;
+
+ if (stat_pipe < 0)
+ return 0;
+ if (loaded >= bufsize) {
+ if (bufsize > 1000 * 1000) {
+ loaded = 0;
+ return 0;
+ }
+ bufsize += bufsize + 1000;
+ buf = para_realloc(buf, bufsize);
+ }
+ assert(loaded < bufsize);
+ ret = read_nonblock(stat_pipe, buf + loaded, bufsize - loaded,
+ rfds, &sz);
+ loaded += sz;
+ ret2 = for_each_stat_item(buf, loaded, update_item);
+ if (ret < 0 || ret2 < 0) {
+ loaded = 0;
+ return ret2 < 0? ret2 : ret;
+ }
+ sz = ret2; /* what is left */
+ if (sz > 0 && sz < loaded)
+ memmove(buf, buf + loaded - sz, sz);
+ loaded = sz;
+ return 1;
+}
+
static void print_all_items(void)
{
int i;
goto reap_next_child;
}
-/*
- * print status line if line starts with known command.
- */
-static int check_stat_line(char *line, __a_unused void *data)
-{
- int i;
-
-// PARA_INFO_LOG("%s: checking: %s\n", __func__, line);
- i = stat_line_valid(line);
- if (i >= 0) {
- line += strlen(status_item_list[i]) + 1;
- if (*line == ' ')
- line++;
- free(stat_content[i]);
- stat_content[i] = para_strdup(line);
- print_stat_item(i);
- }
- return 1;
-}
-
/*
* This sucker modifies its first argument. *handler and *arg are
* pointers to 0-terminated strings (inside line). Crap.
return;
case SIGINT:
PARA_WARNING_LOG("caught SIGINT, reset");
- /* Nothing to do. SIGINT killed our child, para_client stat.
- * This get noticed by do_select which resets everything
+ /* Nothing to do. SIGINT killed our child which gets noticed
+ * by do_select and resets everything.
*/
return;
case SIGUSR1:
}
}
-static int open_audiod_pipe(void)
+static int open_stat_pipe(void)
{
static int init = 1;
+ int ret, fds[3] = {0, 1, 0};
+ pid_t pid;
if (init)
init = 0;
else
- sleep(1);
- return para_open_audiod_pipe(conf.stat_cmd_arg);
+ /*
+ * Sleep a bit to avoid a busy loop. As the call to sleep() may
+ * be interrupted by SIGCHLD, we simply wait until the call
+ * succeeds.
+ */
+ while (sleep(2))
+ ; /* nothing */
+ ret = para_exec_cmdline_pid(&pid, conf.stat_cmd_arg, fds);
+ if (ret < 0)
+ return ret;
+ ret = mark_fd_nonblocking(fds[1]);
+ if (ret >= 0)
+ return fds[1];
+ close(fds[1]);
+ return ret;
}
/*
{
fd_set rfds;
int ret;
- int max_fileno, cp_numread = 1;
+ int max_fileno;
char command_buf[4096] = "";
int cbo = 0; /* command buf offset */
struct timeval tv;
// ret = refresh_status();
FD_ZERO(&rfds);
max_fileno = 0;
- /* audiod pipe */
- if (audiod_pipe < 0)
- audiod_pipe = open_audiod_pipe();
- if (audiod_pipe >= 0)
- para_fd_set(audiod_pipe, &rfds, &max_fileno);
+ if (stat_pipe < 0)
+ stat_pipe = open_stat_pipe();
+ if (stat_pipe >= 0)
+ para_fd_set(stat_pipe, &rfds, &max_fileno);
/* signal pipe */
para_fd_set(signal_pipe, &rfds, &max_fileno);
/* command pipe only for COMMAND_MODE */
if (ret <= 0)
goto check_return; /* skip fd checks */
/* signals */
- if (FD_ISSET(signal_pipe, &rfds)) {
- int sig_nr = para_next_signal();
- if (sig_nr > 0)
- handle_signal(sig_nr);
- }
+ ret = para_next_signal(&rfds);
+ if (ret > 0)
+ handle_signal(ret);
/* read command pipe if ready */
- if (command_pipe >= 0 && mode == COMMAND_MODE &&
- FD_ISSET(command_pipe, &rfds)) {
- cp_numread = read(command_pipe, command_buf + cbo,
- sizeof(command_buf) - 1 - cbo);
- if (cp_numread >= 0)
- cbo += cp_numread;
- else {
- if (cp_numread < 0)
- PARA_ERROR_LOG("read error (%d)", cp_numread);
+ if (command_pipe >= 0 && mode == COMMAND_MODE) {
+ size_t sz;
+ ret = read_nonblock(command_pipe, command_buf + cbo,
+ sizeof(command_buf) - 1 - cbo, &rfds, &sz);
+ cbo += sz;
+ sz = cbo;
+ cbo = for_each_line(command_buf, cbo, &add_output_line, NULL);
+ if (sz != cbo)
+ wrefresh(bot.win);
+ if (ret < 0) {
+ PARA_NOTICE_LOG("closing command pipe: %s",
+ para_strerror(-ret));
close(command_pipe);
command_pipe = -1;
+ return 0;
}
}
- if (audiod_pipe >= 0 && FD_ISSET(audiod_pipe, &rfds))
- if (read_audiod_pipe(audiod_pipe, check_stat_line) <= 0) {
- close(audiod_pipe);
- audiod_pipe = -1;
- clear_all_items();
- free(stat_content[SI_BASENAME]);
- stat_content[SI_BASENAME] =
- para_strdup("audiod not running!?");
- print_all_items();
- }
+ ret = read_stat_pipe(&rfds);
+ if (ret < 0) {
+ PARA_NOTICE_LOG("closing stat pipe: %s\n", para_strerror(-ret));
+ close(stat_pipe);
+ stat_pipe = -1;
+ clear_all_items();
+ free(stat_content[SI_BASENAME]);
+ stat_content[SI_BASENAME] =
+ para_strdup("stat command terminated!?");
+ print_all_items();
+ }
check_return:
switch (mode) {
case COMMAND_MODE:
- if (cp_numread <= 0 && !cbo) /* command complete */
- return 0;
- if (cbo)
- cbo = for_each_line(command_buf, cbo,
- &add_output_line, NULL);
- if (cp_numread <= 0)
- cbo = 0;
- wrefresh(bot.win);
ret = wgetch(top.win);
if (ret != ERR && ret != KEY_RESIZE) {
if (command_pipe) {
/* first check user's key bindings */
for (i = 0; i < conf.key_map_given; ++i) {
- char tmp[MAXLINE], *handler, *arg;
+ char *tmp, *handler, *arg;
- strcpy(tmp, conf.key_map_arg[i]);
- if (!split_key_map(tmp, &handler, &arg))
+ tmp = para_strdup(conf.key_map_arg[i]);
+ if (!split_key_map(tmp, &handler, &arg)) {
+ free(tmp);
return;
- if (!strcmp(tmp, km_keyname(c))) {
- if (*handler == 'd') {
- display_cmd(arg);
- return;
- }
- if (*handler == 'x') {
- external_cmd(arg);
- return;
- }
- if (*handler == 'p') {
- client_cmd_cmdline(arg);
- return;
- }
- if (*handler == 'i') {
- int num = find_cmd_byname(arg);
- if (num >= 0)
- command_list[num].handler();
- return;
- }
}
+ if (strcmp(tmp, km_keyname(c))) {
+ free(tmp);
+ continue;
+ }
+ if (*handler == 'd')
+ display_cmd(arg);
+ else if (*handler == 'x')
+ external_cmd(arg);
+ else if (*handler == 'p')
+ client_cmd_cmdline(arg);
+ else if (*handler == 'i') {
+ int num = find_cmd_byname(arg);
+ if (num >= 0)
+ command_list[num].handler();
+ }
+ free(tmp);
+ return;
}
/* not found, check internal key bindings */
for (i = 0; command_list[i].handler; i++) {