#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 "string.h"
#include "list.h"
#include "sched.h"
#include "signal.h"
-#include "ggo.h"
#include "version.h"
/** 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 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. */
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]);
{
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)
+static void parse_config_file_or_die(bool reload)
{
- bool err;
- char *config_file;
- struct gui_cmdline_parser_params params = {
- .override = override,
- .initialize = 0,
- .check_required = !override,
- .check_ambiguity = 0,
- .print_errors = 1,
- };
+ int ret;
+ char *cf = NULL, *errctx = NULL;
+ void *map;
+ size_t sz;
+ int cf_argc;
+ char **cf_argv;
+ struct lls_parse_result *cf_lpr, *merged_lpr;
- if (conf.config_file_given)
- config_file = para_strdup(conf.config_file_arg);
+ if (OPT_GIVEN(CONFIG_FILE))
+ cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
else {
char *home = para_homedir();
- config_file = make_message("%s/.paraslash/gui.conf", home);
+ cf = 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;
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret < 0) {
+ if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+ goto free_cf;
+ if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+ goto free_cf;
+ ret = 0;
+ lpr = cmdline_lpr;
+ goto success;
}
-
- gui_cmdline_parser_config_file(config_file, &conf, ¶ms);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+ para_munmap(map, sz);
+ if (ret < 0)
+ goto free_cf;
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0)
+ goto free_cf;
+ if (reload) /* config file overrides command line */
+ ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr,
+ &errctx));
+ else /* command line options overrride config file options */
+ ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr,
+ &errctx));
+ lls_free_parse_result(cf_lpr, CMD_PTR);
+ if (ret < 0)
+ goto free_cf;
+ if (lpr != cmdline_lpr)
+ lls_free_parse_result(lpr, CMD_PTR);
+ lpr = merged_lpr;
+success:
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
check_key_map_args_or_die();
- err = false;
-out:
- free(config_file);
- if (err)
+ theme_init(OPT_STRING_VAL(THEME), &theme);
+free_cf:
+ free(cf);
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
exit(EXIT_FAILURE);
- theme_init(conf.theme_arg, &theme);
+ }
}
/* 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");
}
static void handle_command(int c)
{
int i;
+ const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
/* 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;
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;
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;
}
--- /dev/null
+m4_define(PROGRAM, para_gui)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/gui.conf)
+[suite gui]
+version-string = GIT_VERSION()
+[supercommand para_gui]
+ purpose = show para_audiod status in a curses window
+ [description]
+ para_gui is a curses program which displays status information
+ about para_server and para_audiod in a terminal. Keys may be
+ mapped to user-defined commands which are executed when the key is
+ pressed. Command output is shown in a simple pager.
+ [/description]
+ m4_include(common-option-section.m4)
+ m4_include(help.m4)
+ m4_include(detailed-help.m4)
+ m4_include(version.m4)
+ m4_include(config-file.m4)
+ m4_include(loglevel.m4)
+ m4_include(per-command-options-section.m4)
+ [option theme]
+ short_opt = T
+ summary = select startup theme
+ arg_info = required_arg
+ arg_type = string
+ typestr = name
+ [help]
+ If this option is not given the default theme is used. If the given
+ name is not a valid theme name, the list of available themes is
+ printed and the program terminates.
+ [/help]
+ [option stat-cmd]
+ short_opt = s
+ summary = command to read status items from
+ arg_info = required_arg
+ arg_type = string
+ typestr = command
+ default_val = para_audioc -- stat -p
+ [help]
+ On a host on which the para_audiod service is not is running, the
+ default command will fail. An alternative is
+
+ para_client -- stat -p
+
+ This command connects para_server instead of para_audiod. However,
+ no timing information about the current audio file is printed.
+ [/help]
+ [option key-map]
+ short_opt = k
+ summary = map a key to a command using one of three possible modes
+ arg_info = required_arg
+ arg_type = string
+ typestr = k:m:c
+ flag multiple
+ [help]
+ Mode may be d, x or p for display, external and paraslash commands,
+ respectively. Of course, this option may be given multiple times,
+ one for each key mapping. See the manual for more information.
+ [/help]