-/*
- * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
/** \file gui.c Curses-based interface for paraslash. */
#include <curses.h>
#include <locale.h>
#include <sys/time.h>
+#include <lopsub.h>
-#include "gui.cmdline.h"
+#include "gui.lsg.h"
#include "para.h"
#include "gui.h"
+#include "lsu.h"
#include "string.h"
#include "ringbuffer.h"
#include "fd.h"
#include "list.h"
#include "sched.h"
#include "signal.h"
-#include "ggo.h"
#include "version.h"
-/** define the array of error lists needed by para_gui */
-INIT_GUI_ERRLISTS;
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
+static struct lls_parse_result *cmdline_lpr, *lpr;
+
+#define CMD_PTR (lls_cmd(0, gui_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_GUI_PARA_GUI_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define FOR_EACH_KEY_MAP(_i) for (_i = 0; _i < OPT_GIVEN(KEY_MAP); _i++)
+
static char *stat_content[NUM_STAT_ITEMS];
static struct gui_window {
static struct ringbuffer *bot_win_rb;
static unsigned scroll_position;
-
static pid_t exec_pid;
-
static int exec_fds[2] = {-1, -1};
-static struct gui_args_info conf;
static int loglevel;
/** Type of the process currently being executed. */
}
/* taken from mutt */
-static char *km_keyname(int c)
+static const char *km_keyname(int c)
{
static char buf[10];
}
/* Print given number of spaces to curses window. */
-static void add_spaces(WINDOW* win, unsigned int num)
+static void add_spaces(WINDOW *win, unsigned int num)
{
const char space[] = " ";
const unsigned sz = sizeof(space) - 1; /* number of spaces */
}
/*
- * print aligned string to curses window. This function always prints
+ * Print aligned string to curses window. This function always prints
* exactly len chars.
*/
-static int align_str(WINDOW* win, const char *str, unsigned int len,
+static int align_str(WINDOW *win, const char *str, unsigned int len,
unsigned int align)
{
int ret, num; /* of spaces */
waddstr(bot.win, msg);
}
-/*
- * print formated output to bot win and refresh
- */
-__printf_2_3 static void outputf(int color, const char* fmt,...)
+/* Print formatted output to bot win and refresh. */
+__printf_2_3 static void outputf(int color, const char *fmt,...)
{
char *msg;
va_list ap;
vfprintf(stderr, fmt, ap);
va_end(ap);
}
+
/** The log function of para_gui, always set to curses_log(). */
-__printf_2_3 void (*para_log)(int, const char*, ...) = curses_log;
+__printf_2_3 void (*para_log)(int, const char *, ...) = curses_log;
/* Call endwin() to reset the terminal into non-visual mode. */
static void shutdown_curses(void)
endwin();
}
-/* disable curses, print a message, kill running processes and exit */
-__noreturn __printf_2_3 static void die(int exit_code, const char* fmt, ...)
+/* Disable curses, print a message, kill running processes and exit. */
+__noreturn __printf_2_3 static void die(int exit_code, const char *fmt, ...)
{
va_list argp;
exit(exit_code);
}
-/*
- * Print stat item #i to curses window
- */
+/* Print stat item #i to curses window. */
static void print_stat_item(int i)
{
char *tmp;
if (buf && buf[0])
goto dup;
switch (item_num) {
- case SI_ARTIST:
+ case SI_artist:
*c = para_strdup("(artist tag not set)");
goto print;
- case SI_TITLE:
+ case SI_title:
*c = para_strdup("(title tag not set)");
goto print;
- case SI_YEAR:
+ case SI_year:
*c = para_strdup("????");
goto print;
- case SI_ALBUM:
+ case SI_album:
*c = para_strdup("(album tag not set)");
goto print;
- case SI_COMMENT:
+ case SI_comment:
*c = para_strdup("(comment tag not set)");
goto print;
}
if (tv_diff(&st->next_exec, now, NULL) > 0)
return 0;
st->next_exec.tv_sec = now->tv_sec + 2;
- ret = para_exec_cmdline_pid(&st->pid, conf.stat_cmd_arg, fds);
+ ret = para_exec_cmdline_pid(&st->pid,
+ OPT_STRING_VAL(STAT_CMD), fds);
if (ret < 0)
return 0;
ret = mark_fd_nonblocking(fds[1]);
close(st->fd);
st->fd = -1;
clear_all_items();
- free(stat_content[SI_BASENAME]);
- stat_content[SI_BASENAME] =
+ free(stat_content[SI_basename]);
+ stat_content[SI_basename] =
para_strdup("stat command terminated!?");
print_all_items();
return 0;
return 0;
}
-/*
- * init all windows
- */
+/* Initialize all windows. */
static void init_wins(int top_lines)
{
int top_y = 0, bot_y = top_lines + 1, sb_y = LINES - 2,
{
int i;
char *tmp = NULL;
+ const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
- for (i = 0; i < conf.key_map_given; ++i) {
+ FOR_EACH_KEY_MAP(i) {
char *handler, *arg;
free(tmp);
- tmp = para_strdup(conf.key_map_arg[i]);
+ tmp = para_strdup(lls_string_val(i, lor));
if (!split_key_map(tmp, &handler, &arg))
break;
if (strlen(handler) != 1)
if (find_cmd_byname(arg) < 0)
break;
}
- if (i != conf.key_map_given)
- die(EXIT_FAILURE, "invalid key map: %s\n", conf.key_map_arg[i]);
+ if (i != OPT_GIVEN(KEY_MAP))
+ die(EXIT_FAILURE, "invalid key map: %s\n",
+ lls_string_val(i, lor));
free(tmp);
}
-static void parse_config_file_or_die(bool override)
-{
- bool err;
- char *config_file;
- struct gui_cmdline_parser_params params = {
- .override = override,
- .initialize = 0,
- .check_required = !override,
- .check_ambiguity = 0,
- .print_errors = 1,
- };
-
- if (conf.config_file_given)
- config_file = para_strdup(conf.config_file_arg);
- else {
- char *home = para_homedir();
- config_file = make_message("%s/.paraslash/gui.conf", home);
- free(home);
- }
- if (!file_exists(config_file)) {
- if (!conf.config_file_given)
- err = false;
- else {
- PARA_EMERG_LOG("config file %s does not exist\n",
- config_file);
- err = true;
- }
- goto out;
- }
- /*
- * When the gengetopt config file parser is called more than once, any
- * key map arguments found in the config file are _appended_ to the old
- * values, even though we turn on ->override. We want the new arguments
- * to replace the old ones, so we must empty the key_map_arg array
- * first. Unfortunately, this also clears any key map arguments given
- * at the command line.
- */
- if (override) {
- int i;
- for (i = 0; i < conf.key_map_given; i++) {
- free(conf.key_map_arg[i]);
- conf.key_map_arg[i] = NULL;
- }
- conf.key_map_given = 0;
+static void parse_config_file_or_die(bool reload)
+{
+ int ret;
+ unsigned flags = MCF_DONT_FREE;
+
+ if (lpr != cmdline_lpr)
+ lls_free_parse_result(lpr, CMD_PTR);
+ lpr = cmdline_lpr;
+ if (reload)
+ flags |= MCF_OVERRIDE;
+ ret = lsu_merge_config_file_options(OPT_STRING_VAL(CONFIG_FILE),
+ "gui.conf", &lpr, CMD_PTR, gui_suite, flags);
+ if (ret < 0) {
+ PARA_EMERG_LOG("failed to parse config file: %s\n",
+ para_strerror(-ret));
+ exit(EXIT_FAILURE);
}
-
- gui_cmdline_parser_config_file(config_file, &conf, ¶ms);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
check_key_map_args_or_die();
- err = false;
-out:
- free(config_file);
- if (err)
- exit(EXIT_FAILURE);
- theme_init(conf.theme_arg, &theme);
+ theme_init(OPT_STRING_VAL(THEME), &theme);
}
-/* reread configuration, terminate on errors */
+/* Reread configuration, terminate on errors. */
static void reread_conf(void)
{
/*
- * gengetopt might print to stderr and exit on errors. So we have to
- * shutdown curses first.
+ * If the reload of the config file fails, we are about to exit. In
+ * this case we print the error message to stderr rather than to the
+ * curses window. So we have to shutdown curses first.
*/
shutdown_curses();
- parse_config_file_or_die(true /* override */);
+ parse_config_file_or_die(true);
init_curses();
print_in_bar(COLOR_MSG, "config file reloaded\n");
}
-/*
- * React to various signal-related events
- */
+/* React to various signal-related events. */
static int signal_post_select(struct sched *s, __a_unused void *context)
{
int ret = para_next_signal(&s->rfds);
switch (ret) {
case SIGTERM:
die(EXIT_FAILURE, "only the good die young (caught SIGTERM)\n");
+ case SIGWINCH:
+ PARA_NOTICE_LOG("got SIGWINCH\n");
+ if (curses_active()) {
+ shutdown_curses();
+ init_curses();
+ redraw_bot_win();
+ }
+ return 1;
case SIGINT:
return 1;
case SIGUSR1:
sched_min_delay(s);
}
-/* read from command pipe and print data to bot window */
+/* Read from command pipe and print data to bot window. */
static void exec_and_display(const char *file_and_args)
{
int ret, fds[3] = {0, 1, 1};
free(file_and_args);
}
-/*
- * shutdown curses and stat pipe before executing external commands
- */
+/* Shutdown curses and stat pipe before executing external commands. */
static void exec_external(char *file_and_args)
{
int fds[3] = {-1, -1, -1};
static void handle_command(int c)
{
int i;
+ const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
+ const char *keyname = km_keyname(c);
/* first check user-defined key bindings */
- for (i = 0; i < conf.key_map_given; ++i) {
+ FOR_EACH_KEY_MAP(i) {
char *tmp, *handler, *arg;
- tmp = para_strdup(conf.key_map_arg[i]);
+ tmp = para_strdup(lls_string_val(i, lor));
if (!split_key_map(tmp, &handler, &arg)) {
free(tmp);
return;
}
- if (strcmp(tmp, km_keyname(c))) {
+ if (strcmp(tmp, keyname)) {
free(tmp);
continue;
}
}
/* not found, check internal key bindings */
for (i = 0; command_list[i].handler; i++) {
- if (!strcmp(km_keyname(c), command_list[i].key)) {
+ if (!strcmp(keyname, command_list[i].key)) {
command_list[i].handler();
return;
}
}
print_in_bar(COLOR_ERRMSG, "key '%s' is not bound, press ? for help",
- km_keyname(c));
+ keyname);
}
-static int input_post_select(__a_unused struct sched *s, __a_unused void *context)
+static int input_post_select(__a_unused struct sched *s,
+ __a_unused void *context)
{
int ret;
enum exec_status exs = exec_status();
ret = wgetch(top.win);
if (ret == ERR)
return 0;
- if (ret == KEY_RESIZE) {
- if (curses_active()) {
- shutdown_curses();
- init_curses();
- redraw_bot_win();
- }
+ if (ret == KEY_RESIZE) /* already handled in signal_post_select() */
return 0;
- }
if (exs == EXEC_IDLE)
handle_command(ret);
else if (exec_pid > 0)
unsigned lines_total, filled = ringbuffer_filled(bot_win_rb);
int first_rbe = first_visible_rbe(&lines_total);
- print_in_bar(COLOR_MSG, "scrolled view: %d-%d/%d\n", filled - first_rbe,
+ print_in_bar(COLOR_MSG, "scrolled view: %u-%u/%u\n", filled - first_rbe,
filled - scroll_position, ringbuffer_filled(bot_win_rb));
}
static void com_help(void)
{
int i;
+ const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
- for (i = 0; i < conf.key_map_given; ++i) {
- char *handler, *arg, *tmp = para_strdup(conf.key_map_arg[i]);
+ FOR_EACH_KEY_MAP(i) {
+ char *handler, *arg, *tmp = para_strdup(lls_string_val(i, lor));
const char *handler_text = "???", *desc = NULL;
if (!split_key_map(tmp, &handler, &arg)) {
com_refresh();
}
-__noreturn static void print_help_and_die(void)
-{
- struct ggo_help h = DEFINE_GGO_HELP(gui);
- bool d = conf.detailed_help_given;
-
- ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
- exit(0);
-}
-
static int setup_tasks_and_schedule(void)
{
int ret;
struct status_task status_task = {.fd = -1};
struct input_task input_task = {.task = NULL};
struct signal_task *signal_task;
- struct sched sched = {
- .default_timeout = {
- .tv_sec = conf.timeout_arg / 1000,
- .tv_usec = (conf.timeout_arg % 1000) * 1000,
- },
- };
+ struct sched sched = {.default_timeout = {.tv_sec = 1}};
exec_task.task = task_register(&(struct task_info) {
.name = "exec",
para_install_sighandler(SIGTERM);
para_install_sighandler(SIGCHLD);
para_install_sighandler(SIGUSR1);
+ para_install_sighandler(SIGWINCH);
signal_task->task = task_register(&(struct task_info) {
.name = "signal",
.pre_select = signal_pre_select,
return ret;
}
+static void handle_help_flags(void)
+{
+ char *help;
+
+ if (OPT_GIVEN(DETAILED_HELP))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ exit(EXIT_SUCCESS);
+}
+
/**
* The main function of para_gui.
*
*/
int main(int argc, char *argv[])
{
- gui_cmdline_parser(argc, argv, &conf); /* exits on errors */
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
- version_handle_flag("gui", conf.version_given);
- if (conf.help_given || conf.detailed_help_given)
- print_help_and_die();
- parse_config_file_or_die(false /* override */);
+ int ret;
+ char *errctx;
+
+ ret = lls(lls_parse(argc, argv, CMD_PTR, &cmdline_lpr, &errctx));
+ if (ret < 0)
+ goto out;
+ lpr = cmdline_lpr;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ version_handle_flag("gui", OPT_GIVEN(VERSION));
+ handle_help_flags();
+ parse_config_file_or_die(false);
bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
setlocale(LC_CTYPE, "");
initscr(); /* needed only once, always successful */
init_curses();
- return setup_tasks_and_schedule() < 0? EXIT_FAILURE : EXIT_SUCCESS;
+ ret = setup_tasks_and_schedule();
+out:
+ lls_free_parse_result(lpr, CMD_PTR);
+ if (lpr != cmdline_lpr)
+ lls_free_parse_result(cmdline_lpr, CMD_PTR);
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ }
+ return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
}