mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-09 17:05:41 +01:00
alsamixer: added configuration file parser
Added configparser.c and curskey.c: - Lines starting with arbitrary whitespace + '#' are comments - Words in a command name don't have a fixed order (toggle_mute is the same as mute_toggle) Moved read_file() from textbox.c to utils.c, so configparser.c can make use of it. Added command line options: -f/-F to specify/disable configuration file -m/-M to enable/disable mouse Signed-off-by: Benjamin Abendroth <braph93@gmx.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
32dc22c2bf
commit
89e746368c
11 changed files with 990 additions and 40 deletions
|
@ -6,6 +6,8 @@ alsamixer_SOURCES = card_select.c card_select.h \
|
|||
bindings.c bindings.h \
|
||||
cli.c \
|
||||
colors.c colors.h \
|
||||
curskey.c curskey.h \
|
||||
configparser.c configparser.h \
|
||||
device_name.c device_name.h \
|
||||
die.c die.h \
|
||||
mainloop.c mainloop.h \
|
||||
|
|
|
@ -27,8 +27,11 @@
|
|||
#include "gettext_curses.h"
|
||||
#include "mixer_widget.h"
|
||||
#include "mainloop.h"
|
||||
#include "configparser.h"
|
||||
|
||||
static int use_color = 1;
|
||||
static int use_mouse = 1;
|
||||
static const char* config_file = CONFIG_DEFAULT;
|
||||
static struct snd_mixer_selem_regopt selem_regopt = {
|
||||
.ver = 1,
|
||||
.abstract = SND_MIXER_SABSTRACT_NONE,
|
||||
|
@ -42,6 +45,10 @@ static void show_help(void)
|
|||
" -h, --help this help\n"
|
||||
" -c, --card=NUMBER sound card number or id\n"
|
||||
" -D, --device=NAME mixer device name\n"
|
||||
" -m, --mouse enable mouse\n"
|
||||
" -M, --no-mouse disable mouse\n"
|
||||
" -f, --config=FILE configuration file\n"
|
||||
" -F, --no-config do not load configuration file\n"
|
||||
" -V, --view=MODE starting view mode: playback/capture/all"));
|
||||
puts(_("Debugging options:\n"
|
||||
" -g, --no-color toggle using of colors\n"
|
||||
|
@ -50,11 +57,15 @@ static void show_help(void)
|
|||
|
||||
static void parse_options(int argc, char *argv[])
|
||||
{
|
||||
static const char short_options[] = "hc:D:V:gsa:";
|
||||
static const char short_options[] = "hc:D:f:FmMV:gsa:";
|
||||
static const struct option long_options[] = {
|
||||
{ .name = "help", .val = 'h' },
|
||||
{ .name = "card", .has_arg = 1, .val = 'c' },
|
||||
{ .name = "config", .has_arg = 1, .val = 'f' },
|
||||
{ .name = "no-config", .val = 'F' },
|
||||
{ .name = "device", .has_arg = 1, .val = 'D' },
|
||||
{ .name = "mouse", .val = 'm' },
|
||||
{ .name = "no-mouse", .val = 'M' },
|
||||
{ .name = "view", .has_arg = 1, .val = 'V' },
|
||||
{ .name = "no-color", .val = 'g' },
|
||||
{ .name = "abstraction", .has_arg = 1, .val = 'a' },
|
||||
|
@ -83,6 +94,18 @@ static void parse_options(int argc, char *argv[])
|
|||
case 'D':
|
||||
selem_regopt.device = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
config_file = optarg;
|
||||
break;
|
||||
case 'F':
|
||||
config_file = NULL;
|
||||
break;
|
||||
case 'm':
|
||||
use_mouse = 1;
|
||||
break;
|
||||
case 'M':
|
||||
use_mouse = 0;
|
||||
break;
|
||||
case 'V':
|
||||
if (*optarg == 'p' || *optarg == 'P')
|
||||
view_mode = VIEW_MODE_PLAYBACK;
|
||||
|
@ -127,7 +150,12 @@ int main(int argc, char *argv[])
|
|||
|
||||
create_mixer_object(&selem_regopt);
|
||||
|
||||
initialize_curses(use_color);
|
||||
initialize_curses(use_color, use_mouse);
|
||||
|
||||
if (config_file == CONFIG_DEFAULT)
|
||||
parse_default_config_file();
|
||||
else if (config_file)
|
||||
parse_config_file(config_file);
|
||||
|
||||
create_mixer_widget();
|
||||
|
||||
|
|
592
alsamixer/configparser.c
Normal file
592
alsamixer/configparser.c
Normal file
|
@ -0,0 +1,592 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include CURSESINC
|
||||
#include "colors.h"
|
||||
#include "gettext_curses.h"
|
||||
#include "utils.h"
|
||||
#include "curskey.h"
|
||||
#include "bindings.h"
|
||||
#include "mixer_widget.h"
|
||||
|
||||
#define ERROR_CONFIG (-1)
|
||||
#define ERROR_MISSING_ARGUMENTS (-2)
|
||||
#define ERROR_TOO_MUCH_ARGUMENTS (-3)
|
||||
|
||||
static const char *error_message;
|
||||
static const char *error_cause;
|
||||
|
||||
static int strlist_index(const char *haystack, unsigned int itemlen, const char *needle) {
|
||||
unsigned int needle_len;
|
||||
unsigned int pos;
|
||||
const char *found;
|
||||
|
||||
needle_len = strlen(needle);
|
||||
if (needle_len <= itemlen && needle[needle_len - 1] != ' ') {
|
||||
found = strstr(haystack, needle);
|
||||
if (found) {
|
||||
pos = (found - haystack);
|
||||
if (pos % itemlen == 0 && (needle_len == itemlen || haystack[pos+needle_len] == ' '))
|
||||
return pos / itemlen;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int color_by_name(const char *name) {
|
||||
return strlist_index(
|
||||
"default"
|
||||
"black "
|
||||
"red "
|
||||
"green "
|
||||
"yellow "
|
||||
"blue "
|
||||
"magenta"
|
||||
"cyan "
|
||||
"white ", 7, name) - 1;
|
||||
};
|
||||
|
||||
static int attr_by_name(const char *name) {
|
||||
return (int[]) {
|
||||
-1,
|
||||
A_BOLD,
|
||||
A_REVERSE,
|
||||
A_STANDOUT,
|
||||
A_DIM,
|
||||
A_UNDERLINE,
|
||||
#ifdef A_ITALIC
|
||||
A_ITALIC,
|
||||
#endif
|
||||
A_NORMAL,
|
||||
A_BLINK,
|
||||
}[strlist_index(
|
||||
"bold "
|
||||
"reverse "
|
||||
"standout "
|
||||
"dim "
|
||||
"underline"
|
||||
#ifdef A_ITALIC
|
||||
"italic "
|
||||
#endif
|
||||
"normal "
|
||||
"blink ", 9, name) + 1];
|
||||
};
|
||||
|
||||
#define W_NUMBER (1U << 0)
|
||||
|
||||
enum textbox_word {
|
||||
TW_BOTTOM = (1U << 1),
|
||||
TW_CLOSE = (1U << 2),
|
||||
TW_DOWN = (1U << 3),
|
||||
TW_LEFT = (1U << 4),
|
||||
TW_PAGE = (1U << 5),
|
||||
TW_RIGHT = (1U << 6),
|
||||
TW_TOP = (1U << 7),
|
||||
TW_UP = (1U << 8),
|
||||
};
|
||||
|
||||
const char *textbox_words =
|
||||
"bottom"
|
||||
"close "
|
||||
"down "
|
||||
"left "
|
||||
"page "
|
||||
"right "
|
||||
"top "
|
||||
"up ";
|
||||
|
||||
enum mixer_word {
|
||||
MW_ALL = (1U << 1),
|
||||
MW_BALANCE = (1U << 2),
|
||||
MW_CAPTURE = (1U << 3),
|
||||
MW_CARD = (1U << 4),
|
||||
MW_CLOSE = (1U << 5),
|
||||
MW_CONTROL = (1U << 6),
|
||||
MW_DOWN = (1U << 7),
|
||||
MW_FOCUS = (1U << 8),
|
||||
MW_HELP = (1U << 9),
|
||||
MW_INFORMATION = (1U << 10),
|
||||
MW_LEFT = (1U << 11),
|
||||
MW_MODE = (1U << 12),
|
||||
MW_MUTE = (1U << 13),
|
||||
MW_NEXT = (1U << 14),
|
||||
MW_PLAYBACK = (1U << 15),
|
||||
MW_PREVIOUS = (1U << 16),
|
||||
MW_REFRESH = (1U << 17),
|
||||
MW_RIGHT = (1U << 18),
|
||||
MW_SELECT = (1U << 19),
|
||||
MW_SET = (1U << 20),
|
||||
MW_SYSTEM = (1U << 21),
|
||||
MW_TOGGLE = (1U << 22),
|
||||
MW_UP = (1U << 23),
|
||||
};
|
||||
|
||||
const char *mixer_words =
|
||||
"all "
|
||||
"balance "
|
||||
"capture "
|
||||
"card "
|
||||
"close "
|
||||
"control "
|
||||
"down "
|
||||
"focus "
|
||||
"help "
|
||||
"information"
|
||||
"left "
|
||||
"mode "
|
||||
"mute "
|
||||
"next "
|
||||
"playback "
|
||||
"previous "
|
||||
"refresh "
|
||||
"right "
|
||||
"select "
|
||||
"set "
|
||||
"system "
|
||||
"toggle "
|
||||
"up ";
|
||||
|
||||
static unsigned int parse_words(const char *name, const char* wordlist, unsigned int itemlen, unsigned int *number) {
|
||||
unsigned int words = 0;
|
||||
unsigned int word;
|
||||
unsigned int i;
|
||||
char buf[16];
|
||||
char *endptr;
|
||||
|
||||
while (*name) {
|
||||
for (i = 0; i < sizeof(buf) - 1; ++i) {
|
||||
if (*name == '\0')
|
||||
break;
|
||||
if (*name == '_') {
|
||||
++name;
|
||||
break;
|
||||
}
|
||||
buf[i] = *name;
|
||||
++name;
|
||||
}
|
||||
buf[i] = '\0';
|
||||
|
||||
if (buf[0] >= '0' && buf[0] <= '9') {
|
||||
if (number) {
|
||||
*number = strtoumax(buf, &endptr, 10);
|
||||
if (*endptr != '\0')
|
||||
return 0;
|
||||
}
|
||||
word = W_NUMBER;
|
||||
}
|
||||
else if ((i = strlist_index(wordlist, itemlen, buf)) >= 0)
|
||||
word = 2U << i;
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (words & word) // no duplicate words
|
||||
return 0;
|
||||
words |= word;
|
||||
}
|
||||
|
||||
return words;
|
||||
}
|
||||
|
||||
static int textbox_command_by_name(const char *name) {
|
||||
switch (parse_words(name, textbox_words, 6, NULL)) {
|
||||
case TW_TOP: return CMD_TEXTBOX_TOP;
|
||||
case TW_BOTTOM: return CMD_TEXTBOX_BOTTOM;
|
||||
case TW_CLOSE: return CMD_TEXTBOX_CLOSE;
|
||||
case TW_UP: return CMD_TEXTBOX_UP;
|
||||
case TW_DOWN: return CMD_TEXTBOX_DOWN;
|
||||
case TW_LEFT: return CMD_TEXTBOX_LEFT;
|
||||
case TW_RIGHT: return CMD_TEXTBOX_RIGHT;
|
||||
case TW_PAGE|TW_UP: return CMD_TEXTBOX_PAGE_UP;
|
||||
case TW_PAGE|TW_DOWN: return CMD_TEXTBOX_PAGE_DOWN;
|
||||
case TW_PAGE|TW_LEFT: return CMD_TEXTBOX_PAGE_LEFT;
|
||||
case TW_PAGE|TW_RIGHT: return CMD_TEXTBOX_PAGE_RIGHT;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int mixer_command_by_name(const char *name) {
|
||||
unsigned int channel = 0;
|
||||
unsigned int number = 1; // default numeric arg
|
||||
unsigned int words = parse_words(name, mixer_words, 11, &number);
|
||||
|
||||
switch (words) {
|
||||
case MW_HELP: return CMD_MIXER_HELP;
|
||||
case MW_CLOSE: return CMD_MIXER_CLOSE;
|
||||
case MW_REFRESH: return CMD_MIXER_REFRESH;
|
||||
case MW_SELECT|MW_CARD: return CMD_MIXER_SELECT_CARD;
|
||||
case MW_SYSTEM|MW_INFORMATION: return CMD_MIXER_SYSTEM_INFORMATION;
|
||||
case MW_MODE|MW_ALL: return CMD_WITH_ARG(CMD_MIXER_SET_VIEW_MODE, VIEW_MODE_ALL);
|
||||
case MW_MODE|MW_CAPTURE: return CMD_WITH_ARG(CMD_MIXER_SET_VIEW_MODE, VIEW_MODE_CAPTURE);
|
||||
case MW_MODE|MW_PLAYBACK: return CMD_WITH_ARG(CMD_MIXER_SET_VIEW_MODE, VIEW_MODE_PLAYBACK);
|
||||
case MW_MODE|MW_TOGGLE: return CMD_MIXER_TOGGLE_VIEW_MODE;
|
||||
case MW_CONTROL|MW_BALANCE: return CMD_MIXER_BALANCE_CONTROL;
|
||||
case MW_NEXT:
|
||||
case MW_NEXT|W_NUMBER:
|
||||
case MW_PREVIOUS:
|
||||
case MW_PREVIOUS|W_NUMBER:
|
||||
return ((number < 1 || number > 511) ? 0 :
|
||||
CMD_WITH_ARG((words & MW_NEXT
|
||||
? CMD_MIXER_NEXT
|
||||
: CMD_MIXER_PREVIOUS), number));
|
||||
case MW_CONTROL|MW_FOCUS|W_NUMBER:
|
||||
return ((number < 1 || number > 512) ? 0 :
|
||||
CMD_WITH_ARG(CMD_MIXER_FOCUS_CONTROL, number - 1));
|
||||
}
|
||||
|
||||
if (words & MW_LEFT)
|
||||
channel |= LEFT;
|
||||
if (words & MW_RIGHT)
|
||||
channel |= RIGHT;
|
||||
if (!channel)
|
||||
channel = LEFT|RIGHT;
|
||||
|
||||
switch (words & ~(MW_LEFT|MW_RIGHT)) {
|
||||
case MW_CONTROL|MW_UP:
|
||||
case MW_CONTROL|MW_UP|W_NUMBER:
|
||||
case MW_CONTROL|MW_DOWN:
|
||||
case MW_CONTROL|MW_DOWN|W_NUMBER:
|
||||
return ((number < 1 || number > 100) ? 0 :
|
||||
CMD_WITH_ARG((words & MW_UP
|
||||
? CMD_MIXER_CONTROL_UP_LEFT
|
||||
: CMD_MIXER_CONTROL_DOWN_LEFT) + channel - 1, number));
|
||||
case MW_CONTROL|MW_SET|W_NUMBER:
|
||||
return (number > 100 ? 0 :
|
||||
CMD_WITH_ARG(CMD_MIXER_CONTROL_SET_PERCENT_LEFT + channel - 1, number));
|
||||
case MW_TOGGLE|MW_MUTE:
|
||||
return CMD_WITH_ARG(CMD_MIXER_TOGGLE_MUTE, channel);
|
||||
case MW_TOGGLE|MW_CAPTURE:
|
||||
return CMD_WITH_ARG(CMD_MIXER_TOGGLE_CAPTURE, channel);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int* element_by_name(const char *name) {
|
||||
int idx = strlist_index(
|
||||
#ifdef TRICOLOR_VOLUME_BAR
|
||||
"ctl_bar_hi "
|
||||
#endif
|
||||
"ctl_bar_lo "
|
||||
#ifdef TRICOLOR_VOLUME_BAR
|
||||
"ctl_bar_mi "
|
||||
#endif
|
||||
"ctl_capture "
|
||||
"ctl_frame "
|
||||
"ctl_inactive "
|
||||
"ctl_label "
|
||||
"ctl_label_focus "
|
||||
"ctl_label_inactive"
|
||||
"ctl_mark_focus "
|
||||
"ctl_mute "
|
||||
"ctl_nocapture "
|
||||
"ctl_nomute "
|
||||
"errormsg "
|
||||
"infomsg "
|
||||
"menu "
|
||||
"menu_selected "
|
||||
"mixer_active "
|
||||
"mixer_frame "
|
||||
"mixer_text "
|
||||
"textbox "
|
||||
"textfield ", 18, name);
|
||||
|
||||
if (idx < 0) {
|
||||
#ifndef TRICOLOR_VOLUME_BAR
|
||||
if (strlist_index(
|
||||
"ctl_bar_hi"
|
||||
"ctl_bar_mi", 10, name) >= 0)
|
||||
return &errno; // dummy element
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &( ((int*) &attrs)[idx] );
|
||||
}
|
||||
|
||||
static int cfg_bind(char **argv, unsigned int argc) {
|
||||
const char *command_name;
|
||||
command_enum command = 0;
|
||||
unsigned int i;
|
||||
int keys[3] = { -1, -1, -1 };
|
||||
union {
|
||||
command_enum *mixer_bindings;
|
||||
uint8_t *textbox_bindings;
|
||||
} bind_to = {
|
||||
.mixer_bindings = mixer_bindings
|
||||
};
|
||||
|
||||
if (argc == 2)
|
||||
command_name = argv[1];
|
||||
else if (argc == 3) {
|
||||
command_name = argv[2];
|
||||
|
||||
if (! strcmp(argv[1], "textbox")) {
|
||||
bind_to.textbox_bindings = textbox_bindings;
|
||||
}
|
||||
else if (! strcmp(argv[1], "mixer"))
|
||||
; // bind_to.mixer_bindings = mixer_bindings
|
||||
else {
|
||||
error_message = _("invalid widget");
|
||||
error_cause = argv[1];
|
||||
return ERROR_CONFIG;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (argc < 2 ? ERROR_MISSING_ARGUMENTS : ERROR_TOO_MUCH_ARGUMENTS);
|
||||
}
|
||||
|
||||
keys[0] = curskey_parse(argv[0]);
|
||||
if (keys[0] < 0 || keys[0] >= ARRAY_SIZE(mixer_bindings)) {
|
||||
error_message = _("invalid key");
|
||||
error_cause = argv[0];
|
||||
return ERROR_CONFIG;
|
||||
}
|
||||
|
||||
if (keys[0] == KEY_ENTER || keys[0] == '\n' || keys[0] == '\r') {
|
||||
keys[0] = KEY_ENTER;
|
||||
keys[1] = '\n';
|
||||
keys[2] = '\r';
|
||||
}
|
||||
|
||||
if (bind_to.textbox_bindings == textbox_bindings)
|
||||
command = textbox_command_by_name(command_name);
|
||||
else
|
||||
command = mixer_command_by_name(command_name);
|
||||
|
||||
if (!command) {
|
||||
if (!strcmp(command_name, "none"))
|
||||
; // command = 0
|
||||
else {
|
||||
error_message = _("invalid command");
|
||||
error_cause = command_name;
|
||||
return ERROR_CONFIG;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(keys) && keys[i] != -1; ++i) {
|
||||
if (bind_to.textbox_bindings == textbox_bindings)
|
||||
bind_to.textbox_bindings[keys[i]] = command;
|
||||
else
|
||||
bind_to.mixer_bindings[keys[i]] = command;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfg_color(char **argv, unsigned int argc)
|
||||
{
|
||||
short fg_color, bg_color;
|
||||
unsigned int i;
|
||||
int *element;
|
||||
int attr;
|
||||
|
||||
if (argc < 3)
|
||||
return ERROR_MISSING_ARGUMENTS;
|
||||
|
||||
if (NULL == (element = element_by_name(argv[0]))) {
|
||||
error_message = _("unknown theme element");
|
||||
error_cause = argv[0];
|
||||
return ERROR_CONFIG;
|
||||
}
|
||||
|
||||
if (-2 == (fg_color = color_by_name(argv[1]))) {
|
||||
error_message = _("unknown color");
|
||||
error_cause = argv[1];
|
||||
return ERROR_CONFIG;
|
||||
}
|
||||
|
||||
if (-2 == (bg_color = color_by_name(argv[2]))) {
|
||||
error_message = _("unknown color");
|
||||
error_cause = argv[2];
|
||||
return ERROR_CONFIG;
|
||||
}
|
||||
|
||||
*element = get_color_pair(fg_color, bg_color);
|
||||
|
||||
for (i = 3; i < argc; ++i) {
|
||||
if (-1 == (attr = attr_by_name(argv[i]))) {
|
||||
error_message = _("unknown color attribute");
|
||||
error_cause = argv[i];
|
||||
return ERROR_CONFIG;
|
||||
}
|
||||
else
|
||||
*element |= attr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfg_set(char **argv, unsigned int argc)
|
||||
{
|
||||
char *endptr;
|
||||
|
||||
if (argc == 2) {
|
||||
if (! strcmp(argv[0], "mouse_wheel_step")) {
|
||||
mouse_wheel_step = strtoumax(argv[1], &endptr, 10);
|
||||
if (mouse_wheel_step > 100 || *endptr != '\0') {
|
||||
mouse_wheel_step = 1;
|
||||
error_message = _("invalid value");
|
||||
error_cause = argv[1];
|
||||
return ERROR_CONFIG;
|
||||
}
|
||||
}
|
||||
else if (! strcmp(argv[0], "mouse_wheel_focuses_control")) {
|
||||
if ((argv[1][0] == '0' || argv[1][0] == '1') && argv[1][1] == '\0')
|
||||
mouse_wheel_focuses_control = argv[1][0] - '0';
|
||||
else {
|
||||
error_message = _("invalid value");
|
||||
error_cause = argv[1];
|
||||
return ERROR_CONFIG;
|
||||
}
|
||||
}
|
||||
else {
|
||||
error_message = _("unknown option");
|
||||
error_cause = argv[0];
|
||||
return ERROR_CONFIG;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (argc < 2 ? ERROR_MISSING_ARGUMENTS : ERROR_TOO_MUCH_ARGUMENTS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Split $line on whitespace, store it in $args, return the argument count.
|
||||
* Return 0 for commented lines ('\s*#').
|
||||
*
|
||||
* This will modify contents of $line.
|
||||
*/
|
||||
static unsigned int parse_line(char *line, char **args, unsigned int args_size)
|
||||
{
|
||||
unsigned int count;
|
||||
|
||||
for (count = 0; count < args_size; ++count) {
|
||||
while (*line && isspace(*line))
|
||||
++line;
|
||||
|
||||
if (*line == '\0')
|
||||
break;
|
||||
|
||||
if (*line == '#' && count == 0)
|
||||
break;
|
||||
|
||||
args[count] = line;
|
||||
|
||||
while (*line && !isspace(*line))
|
||||
++line;
|
||||
|
||||
if (*line != '\0') {
|
||||
*line = '\0';
|
||||
++line;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int process_line(char *line) {
|
||||
char *args[16];
|
||||
unsigned int argc = parse_line(line, args, ARRAY_SIZE(args));
|
||||
int ret = 0;
|
||||
|
||||
if (argc >= 1) {
|
||||
error_cause = NULL;
|
||||
//error_message = _("unknown error");
|
||||
|
||||
if (argc >= ARRAY_SIZE(args))
|
||||
ret = ERROR_TOO_MUCH_ARGUMENTS;
|
||||
else {
|
||||
ret = strlist_index(
|
||||
"bind "
|
||||
"color"
|
||||
"set ", 5, args[0]);
|
||||
switch (ret) {
|
||||
case 0: ret = cfg_bind(args + 1, argc - 1); break;
|
||||
case 1: ret = cfg_color(args + 1, argc - 1); break;
|
||||
case 2: ret = cfg_set(args + 1, argc - 1); break;
|
||||
default: error_message = _("unknown command");
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == ERROR_MISSING_ARGUMENTS)
|
||||
error_message = _("missing arguments");
|
||||
else if (ret == ERROR_TOO_MUCH_ARGUMENTS)
|
||||
error_message = _("too much arguments");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void parse_config_file(const char *file_name)
|
||||
{
|
||||
char *buf;
|
||||
unsigned int file_size;
|
||||
unsigned int lineno;
|
||||
unsigned int i;
|
||||
char *line;
|
||||
|
||||
endwin(); // print warnings to stderr
|
||||
|
||||
buf = read_file(file_name, &file_size);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "%s: %s\n", file_name, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
curskey_init();
|
||||
curskey_define_meta_keys(128);
|
||||
|
||||
lineno = 0;
|
||||
line = buf;
|
||||
for (i = 0; i < file_size; ++i) {
|
||||
if (buf[i] == '\n') {
|
||||
buf[i] = '\0';
|
||||
++lineno;
|
||||
if (process_line(line) < 0) {
|
||||
if (error_cause)
|
||||
fprintf(stderr, "%s:%d: %s: %s: %s\n", file_name, lineno, line, error_message, error_cause);
|
||||
else
|
||||
fprintf(stderr, "%s:%d: %s: %s\n", file_name, lineno, line, error_message);
|
||||
}
|
||||
line = &buf[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
curskey_destroy();
|
||||
}
|
||||
|
||||
void parse_default_config_file() {
|
||||
char file[4096];
|
||||
const char *home;
|
||||
|
||||
home = getenv("XDG_CONFIG_HOME");
|
||||
if (home && *home) {
|
||||
snprintf(file, sizeof(file), "%s/alsamixer.rc", home);
|
||||
if (! access(file, F_OK))
|
||||
return parse_config_file(file);
|
||||
}
|
||||
|
||||
home = getenv("HOME");
|
||||
if (!home || !*home) {
|
||||
struct passwd *pwd = getpwuid(getuid());
|
||||
if (pwd)
|
||||
home = pwd->pw_dir;
|
||||
}
|
||||
|
||||
if (home && *home) {
|
||||
snprintf(file, sizeof(file), "%s/.config/alsamixer.rc", home);
|
||||
if (! access(file, F_OK))
|
||||
return parse_config_file(file);
|
||||
|
||||
snprintf(file, sizeof(file), "%s/.alsamixer.rc", home);
|
||||
if (! access(file, F_OK))
|
||||
return parse_config_file(file);
|
||||
}
|
||||
}
|
9
alsamixer/configparser.h
Normal file
9
alsamixer/configparser.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef CONFIGPARSER_H_INCLUDED
|
||||
#define CONFIGPARSER_H_INCLUDED
|
||||
|
||||
#define CONFIG_DEFAULT ((const char*) 1)
|
||||
|
||||
void parse_config_file(const char *file);
|
||||
void parse_default_config_file();
|
||||
|
||||
#endif
|
274
alsamixer/curskey.c
Normal file
274
alsamixer/curskey.c
Normal file
|
@ -0,0 +1,274 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "curskey.h"
|
||||
#include "utils.h"
|
||||
#include "mem.h"
|
||||
|
||||
struct curskey_key {
|
||||
char *keyname;
|
||||
int keycode;
|
||||
};
|
||||
|
||||
static struct curskey_key *keynames;
|
||||
static unsigned int keynames_count;
|
||||
|
||||
unsigned int meta_keycode_start;
|
||||
static uint64_t invalid_meta_char_mask[2];
|
||||
|
||||
// Names for non-printable/whitespace characters and aliases for existing keys
|
||||
static const struct curskey_key keyname_aliases[] = {
|
||||
// Sorted by `keyname`
|
||||
{ "DEL", KEY_DEL },
|
||||
{ "DELETE", KEY_DC },
|
||||
{ "ENTER", '\n' },
|
||||
{ "ENTER", '\r' },
|
||||
{ "ESCAPE", KEY_ESCAPE },
|
||||
{ "INSERT", KEY_IC },
|
||||
{ "PAGEDOWN", KEY_NPAGE },
|
||||
{ "PAGEUP", KEY_PPAGE },
|
||||
{ "SPACE", KEY_SPACE },
|
||||
{ "TAB", KEY_TAB }
|
||||
};
|
||||
|
||||
#define STARTSWITH_KEY(S) \
|
||||
((name[0] == 'K' || name[0] == 'k') && \
|
||||
(name[1] == 'E' || name[1] == 'e') && \
|
||||
(name[2] == 'Y' || name[2] == 'y') && \
|
||||
(name[3] == '_'))
|
||||
|
||||
#define IS_META(S) \
|
||||
((S[0] == 'M' || S[0] == 'm' || S[0] == 'A' || S[0] == 'a') && S[1] == '-')
|
||||
|
||||
static int curskey_key_cmp(const void *a, const void *b) {
|
||||
return strcmp(((struct curskey_key*) a)->keyname,
|
||||
((struct curskey_key*) b)->keyname);
|
||||
}
|
||||
|
||||
static int curskey_find(const struct curskey_key *table, unsigned int size, const char *name) {
|
||||
unsigned int start = 0;
|
||||
unsigned int end = size;
|
||||
unsigned int i;
|
||||
int cmp;
|
||||
|
||||
while (1) {
|
||||
i = (start+end) / 2;
|
||||
cmp = strcasecmp(name, table[i].keyname);
|
||||
|
||||
if (cmp == 0)
|
||||
return table[i].keycode;
|
||||
else if (end == start + 1)
|
||||
return ERR;
|
||||
else if (cmp > 0)
|
||||
start = i;
|
||||
else
|
||||
end = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Translate the name of a ncurses KEY_ constant to its value.
|
||||
* "KEY_DOWN" -> 258
|
||||
*
|
||||
* Return ERR on failure.
|
||||
*/
|
||||
int curskey_keycode(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (! name)
|
||||
return ERR;
|
||||
|
||||
if (STARTSWITH_KEY(name))
|
||||
name += 4;
|
||||
|
||||
if (name[0] == 'F' || name[0] == 'f') {
|
||||
i = (name[1] == '(' ? 2 : 1);
|
||||
|
||||
if (name[i] >= '0' && name[i] <= '9') {
|
||||
i = atoi(name + i);
|
||||
if (i >= 1 && i <= 63)
|
||||
return KEY_F(i);
|
||||
}
|
||||
}
|
||||
|
||||
i = curskey_find(keyname_aliases, ARRAY_SIZE(keyname_aliases), name);
|
||||
if (i != ERR)
|
||||
return i;
|
||||
|
||||
return curskey_find(keynames, keynames_count, name);
|
||||
}
|
||||
|
||||
static void free_ncurses_keynames() {
|
||||
if (keynames) {
|
||||
while (keynames_count)
|
||||
free(keynames[--keynames_count].keyname);
|
||||
free(keynames);
|
||||
keynames = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the list of ncurses KEY_ constants and their names.
|
||||
* Returns OK on success, ERR on failure.
|
||||
*/
|
||||
int create_ncurses_keynames() {
|
||||
int key;
|
||||
char *name;
|
||||
|
||||
free_ncurses_keynames();
|
||||
keynames = ccalloc(sizeof(struct curskey_key), (KEY_MAX - KEY_MIN));
|
||||
|
||||
for (key = KEY_MIN; key != KEY_MAX; ++key) {
|
||||
name = (char*) keyname(key);
|
||||
|
||||
if (!name || !STARTSWITH_KEY(name))
|
||||
continue;
|
||||
|
||||
name += 4;
|
||||
if (name[0] == 'F' && name[1] == '(')
|
||||
continue; // ignore KEY_F(1),...
|
||||
|
||||
keynames[keynames_count].keycode = key;
|
||||
keynames[keynames_count].keyname = cstrdup(name);
|
||||
++keynames_count;
|
||||
}
|
||||
|
||||
keynames = crealloc(keynames, keynames_count * sizeof(struct curskey_key));
|
||||
qsort(keynames, keynames_count, sizeof(struct curskey_key), curskey_key_cmp);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Defines meta escape sequences in ncurses.
|
||||
*
|
||||
* Some combinations with meta/alt may not be available since they collide
|
||||
* with the prefix of a pre-defined key.
|
||||
* For example, keys F1 - F4 begin with "\eO", so ALT-O cannot be defined.
|
||||
*
|
||||
* Returns OK if meta keys are available, ERR otherwise.
|
||||
*/
|
||||
int curskey_define_meta_keys(unsigned int keycode_start) {
|
||||
#ifdef NCURSES_VERSION
|
||||
int ch;
|
||||
int keycode;
|
||||
int new_keycode = keycode_start;
|
||||
char key_sequence[3] = "\e ";
|
||||
|
||||
invalid_meta_char_mask[0] = 0;
|
||||
invalid_meta_char_mask[1] = 0;
|
||||
|
||||
for (ch = 0; ch <= CURSKEY_MAX_META_CHAR; ++ch) {
|
||||
key_sequence[1] = ch;
|
||||
keycode = key_defined(key_sequence);
|
||||
if (! keycode) {
|
||||
define_key(key_sequence, new_keycode);
|
||||
}
|
||||
else if (keycode == new_keycode)
|
||||
;
|
||||
else
|
||||
invalid_meta_char_mask[ch/65] |= (1UL << (ch % 64));
|
||||
|
||||
++new_keycode;
|
||||
}
|
||||
|
||||
meta_keycode_start = keycode_start;
|
||||
return OK;
|
||||
#endif
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/* Return the keycode for a key with modifiers applied.
|
||||
*
|
||||
* Available modifiers are:
|
||||
* - CURSKEY_MOD_META / CURSKEY_MOD_ALT
|
||||
* - CURSKEY_MOD_CNTRL
|
||||
*
|
||||
* See also the macros curskey_meta_key(), curskey_cntrl_key().
|
||||
*
|
||||
* Returns ERR if the modifiers cannot be applied to this key.
|
||||
*/
|
||||
int curskey_mod_key(int key, unsigned int modifiers) {
|
||||
if (modifiers & CURSKEY_MOD_CNTRL) {
|
||||
if ((key >= 'A' && key <= '_') || (key >= 'a' && key <= 'z') || key == ' ')
|
||||
key = key % 32;
|
||||
else
|
||||
return ERR;
|
||||
}
|
||||
|
||||
if (modifiers & CURSKEY_MOD_META) {
|
||||
if (meta_keycode_start &&
|
||||
(key >= 0 && key <= CURSKEY_MAX_META_CHAR) &&
|
||||
! (invalid_meta_char_mask[key/65] & (1UL << (key % 64)))) {
|
||||
key = meta_keycode_start + key;
|
||||
}
|
||||
else
|
||||
return ERR;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/* Return the ncurses keycode for a key definition.
|
||||
*
|
||||
* Key definition may be:
|
||||
* - Single character (a, z, ...)
|
||||
* - Character with control-modifier (^x, C-x, c-x, ...)
|
||||
* - Character with meta/alt-modifier (M-x, m-x, A-x, a-x, ...)
|
||||
* - Character with both modifiers (C-M-x, M-C-x, M-^x, ...)
|
||||
* - Curses keyname, no modifiers allowed (KEY_HOME, HOME, F1, F(1), ...)
|
||||
*
|
||||
* Returns ERR if either
|
||||
* - The key definition is NULL or empty
|
||||
* - The key could not be found ("KEY_FOO")
|
||||
* - The key combination is invalid in general ("C-TAB", "C-RETURN")
|
||||
* - The key is invalid because of compile time options (the
|
||||
* `define_key()` function was not available.)
|
||||
* - The key is invalid because it could not be defined by
|
||||
* curskey_define_meta_keys()
|
||||
*/
|
||||
int curskey_parse(const char *def) {
|
||||
int c;
|
||||
unsigned int mod = 0;
|
||||
|
||||
if (! def)
|
||||
return ERR;
|
||||
|
||||
for (;;) {
|
||||
if (def[0] == '^' && def[1] != '\0') {
|
||||
++def;
|
||||
mod |= CURSKEY_MOD_CNTRL;
|
||||
}
|
||||
else if ((def[0] == 'C' || def[0] == 'c') && def[1] == '-') {
|
||||
def += 2;
|
||||
mod |= CURSKEY_MOD_CNTRL;
|
||||
}
|
||||
else if (IS_META(def)) {
|
||||
if (! meta_keycode_start)
|
||||
return ERR;
|
||||
def += 2;
|
||||
mod |= CURSKEY_MOD_ALT;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (*def == '\0')
|
||||
return ERR;
|
||||
else if (*(def+1) == '\0')
|
||||
c = *def;
|
||||
else
|
||||
c = curskey_keycode(def);
|
||||
|
||||
return curskey_mod_key(c, mod);
|
||||
}
|
||||
|
||||
/* Initialize curskey.
|
||||
* Returns OK on success, ERR on failure. */
|
||||
int curskey_init() {
|
||||
keypad(stdscr, TRUE);
|
||||
return create_ncurses_keynames();
|
||||
}
|
||||
|
||||
/* Destroy curskey. */
|
||||
void curskey_destroy() {
|
||||
free_ncurses_keynames();
|
||||
}
|
37
alsamixer/curskey.h
Normal file
37
alsamixer/curskey.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef CURSKEY_H_INCLUDED
|
||||
#define CURSKEY_H_INCLUDED
|
||||
|
||||
#include CURSESINC
|
||||
|
||||
/* Additional KEY_ constants */
|
||||
#define KEY_SPACE ' '
|
||||
#define KEY_TAB '\t'
|
||||
#define KEY_DEL 127
|
||||
#define KEY_ESCAPE 27
|
||||
#define KEY_INSERT KEY_IC
|
||||
#define KEY_DELETE KEY_DC
|
||||
#define KEY_PAGEUP KEY_PPAGE
|
||||
#define KEY_PAGEDOWN KEY_NPAGE
|
||||
|
||||
/* Modifiers */
|
||||
#define CURSKEY_MOD_CNTRL 1U
|
||||
#define CURSKEY_MOD_META 2U
|
||||
#define CURSKEY_MOD_ALT CURSKEY_MOD_META
|
||||
|
||||
/* Defines the range of characters which should be "meta-able" */
|
||||
#define CURSKEY_MAX_META_CHAR 127
|
||||
|
||||
int curskey_init();
|
||||
void curskey_destroy();
|
||||
int curskey_define_meta_keys(unsigned int keycode_start);
|
||||
|
||||
int curskey_parse(const char *keydef);
|
||||
int curskey_mod_key(int key, unsigned int modifiers);
|
||||
|
||||
#define curskey_meta_key(KEY) \
|
||||
curskey_mod_key(KEY, CURSKEY_MOD_META)
|
||||
|
||||
#define curskey_cntrl_key(KEY) \
|
||||
curskey_mod_key(KEY, CURSKEY_MOD_CNTRL)
|
||||
|
||||
#endif
|
|
@ -40,7 +40,7 @@ static void black_hole_error_handler(const char *file, int line,
|
|||
{
|
||||
}
|
||||
|
||||
void initialize_curses(bool use_color)
|
||||
void initialize_curses(bool use_color, bool use_mouse)
|
||||
{
|
||||
curses_initialized = initscr();
|
||||
cbreak();
|
||||
|
@ -50,7 +50,8 @@ void initialize_curses(bool use_color)
|
|||
#endif
|
||||
window_size_changed(); /* update screen_lines/cols */
|
||||
init_colors(use_color);
|
||||
mousemask(ALL_MOUSE_EVENTS, NULL);
|
||||
if (use_mouse)
|
||||
mousemask(ALL_MOUSE_EVENTS, NULL);
|
||||
snd_lib_error_set_handler(black_hole_error_handler);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include CURSESINC
|
||||
|
||||
void initialize_curses(bool use_color);
|
||||
void initialize_curses(bool use_color, bool use_mouse);
|
||||
void mainloop(void);
|
||||
void app_shutdown(void);
|
||||
|
||||
|
|
|
@ -34,8 +34,6 @@
|
|||
#include "textbox.h"
|
||||
#include "bindings.h"
|
||||
|
||||
#define MAX_FILE_SIZE 1048576
|
||||
|
||||
static void create_text_box(const char *const *lines, unsigned int count,
|
||||
const char *title, int attrs);
|
||||
|
||||
|
@ -67,38 +65,6 @@ void show_alsa_error(const char *msg, int err)
|
|||
create_text_box(lines, count, _("Error"), attrs.errormsg);
|
||||
}
|
||||
|
||||
static char *read_file(const char *file_name, unsigned int *file_size)
|
||||
{
|
||||
FILE *f;
|
||||
int err;
|
||||
char *buf;
|
||||
unsigned int allocated = 2048;
|
||||
unsigned int bytes_read;
|
||||
|
||||
f = fopen(file_name, "r");
|
||||
if (!f) {
|
||||
err = errno;
|
||||
buf = casprintf(_("Cannot open file \"%s\"."), file_name);
|
||||
show_error(buf, err);
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
*file_size = 0;
|
||||
buf = NULL;
|
||||
do {
|
||||
allocated *= 2;
|
||||
buf = crealloc(buf, allocated);
|
||||
bytes_read = fread(buf + *file_size, 1, allocated - *file_size, f);
|
||||
*file_size += bytes_read;
|
||||
} while (*file_size == allocated && allocated < MAX_FILE_SIZE);
|
||||
fclose(f);
|
||||
if (*file_size > 0 && buf[*file_size - 1] != '\n' && *file_size < allocated) {
|
||||
buf[*file_size] = '\n';
|
||||
++*file_size;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void show_textfile(const char *file_name)
|
||||
{
|
||||
char *buf;
|
||||
|
@ -109,8 +75,12 @@ void show_textfile(const char *file_name)
|
|||
const char *start_line;
|
||||
|
||||
buf = read_file(file_name, &file_size);
|
||||
if (!buf)
|
||||
if (!buf) {
|
||||
int err = errno;
|
||||
buf = casprintf(_("Cannot open file \"%s\"."), file_name);
|
||||
show_error(buf, err);
|
||||
return;
|
||||
}
|
||||
line_count = 0;
|
||||
for (i = 0; i < file_size; ++i)
|
||||
line_count += buf[i] == '\n';
|
||||
|
|
|
@ -24,7 +24,10 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "utils.h"
|
||||
#include "mem.h"
|
||||
|
||||
/*
|
||||
* mbs_at_width - compute screen position in a string
|
||||
|
@ -111,3 +114,36 @@ unsigned int get_max_mbs_width(const char *const *s, unsigned int count)
|
|||
}
|
||||
return max_width;
|
||||
}
|
||||
|
||||
#define MAX_FILE_SIZE 1048576
|
||||
char *read_file(const char *file_name, unsigned int *file_size)
|
||||
{
|
||||
FILE *f;
|
||||
int err;
|
||||
char *buf;
|
||||
unsigned int allocated = 2048;
|
||||
unsigned int bytes_read;
|
||||
|
||||
f = fopen(file_name, "r");
|
||||
if (!f) {
|
||||
err = errno;
|
||||
free(buf);
|
||||
errno = err;
|
||||
return NULL;
|
||||
}
|
||||
*file_size = 0;
|
||||
buf = NULL;
|
||||
do {
|
||||
allocated *= 2;
|
||||
buf = crealloc(buf, allocated);
|
||||
bytes_read = fread(buf + *file_size, 1, allocated - *file_size, f);
|
||||
*file_size += bytes_read;
|
||||
} while (*file_size == allocated && allocated < MAX_FILE_SIZE);
|
||||
fclose(f);
|
||||
if (*file_size > 0 && buf[*file_size - 1] != '\n' && *file_size < allocated) {
|
||||
buf[*file_size] = '\n';
|
||||
++*file_size;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
unsigned int get_mbs_width(const char *s);
|
||||
unsigned int get_max_mbs_width(const char *const *s, unsigned int count);
|
||||
const char *mbs_at_width(const char *s, int *width, int dir);
|
||||
char *read_file(const char *file_name, unsigned int *file_size);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue