Blame alsamixer/mixer_widget.c

Packit Service a9274b
/*
Packit Service a9274b
 * mixer_widget.c - mixer widget and keys handling
Packit Service a9274b
 * Copyright (c) 1998,1999 Tim Janik
Packit Service a9274b
 *                         Jaroslav Kysela <perex@perex.cz>
Packit Service a9274b
 * Copyright (c) 2009      Clemens Ladisch <clemens@ladisch.de>
Packit Service a9274b
 *
Packit Service a9274b
 * This program is free software: you can redistribute it and/or modify
Packit Service a9274b
 * it under the terms of the GNU General Public License as published by
Packit Service a9274b
 * the Free Software Foundation, either version 2 of the License, or
Packit Service a9274b
 * (at your option) any later version.
Packit Service a9274b
 *
Packit Service a9274b
 * This program is distributed in the hope that it will be useful,
Packit Service a9274b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a9274b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a9274b
 * GNU General Public License for more details.
Packit Service a9274b
 *
Packit Service a9274b
 * You should have received a copy of the GNU General Public License
Packit Service a9274b
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service a9274b
 */
Packit Service a9274b
Packit Service a9274b
#include "aconfig.h"
Packit Service a9274b
#include <stdlib.h>
Packit Service a9274b
#include <string.h>
Packit Service a9274b
#include <errno.h>
Packit Service a9274b
#include <alsa/asoundlib.h>
Packit Service a9274b
#include "gettext_curses.h"
Packit Service a9274b
#include "version.h"
Packit Service a9274b
#include "utils.h"
Packit Service a9274b
#include "die.h"
Packit Service a9274b
#include "mem.h"
Packit Service a9274b
#include "colors.h"
Packit Service a9274b
#include "widget.h"
Packit Service a9274b
#include "textbox.h"
Packit Service a9274b
#include "proc_files.h"
Packit Service a9274b
#include "card_select.h"
Packit Service a9274b
#include "volume_mapping.h"
Packit Service a9274b
#include "mixer_clickable.h"
Packit Service a9274b
#include "mixer_controls.h"
Packit Service a9274b
#include "mixer_display.h"
Packit Service a9274b
#include "mixer_widget.h"
Packit Service a9274b
#include "bindings.h"
Packit Service a9274b
Packit Service a9274b
snd_mixer_t *mixer;
Packit Service a9274b
char *mixer_device_name;
Packit Service a9274b
bool unplugged;
Packit Service a9274b
Packit Service a9274b
struct widget mixer_widget;
Packit Service a9274b
Packit Service a9274b
enum view_mode view_mode;
Packit Service a9274b
Packit Service a9274b
int focus_control_index;
Packit Service a9274b
snd_mixer_selem_id_t *current_selem_id;
Packit Service a9274b
unsigned int current_control_flags;
Packit Service a9274b
Packit Service a9274b
bool control_values_changed;
Packit Service a9274b
bool controls_changed;
Packit Service a9274b
Packit Service a9274b
unsigned int mouse_wheel_step = 1;
Packit Service a9274b
bool mouse_wheel_focuses_control = 1;
Packit Service a9274b
Packit Service a9274b
static int elem_callback(snd_mixer_elem_t *elem, unsigned int mask)
Packit Service a9274b
{
Packit Service a9274b
	if (mask == SND_CTL_EVENT_MASK_REMOVE) {
Packit Service a9274b
		controls_changed = TRUE;
Packit Service a9274b
	} else {
Packit Service a9274b
		if (mask & SND_CTL_EVENT_MASK_VALUE)
Packit Service a9274b
			control_values_changed = TRUE;
Packit Service a9274b
Packit Service a9274b
		if (mask & SND_CTL_EVENT_MASK_INFO)
Packit Service a9274b
			controls_changed = TRUE;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int mixer_callback(snd_mixer_t *mixer, unsigned int mask, snd_mixer_elem_t *elem)
Packit Service a9274b
{
Packit Service a9274b
	if (mask & SND_CTL_EVENT_MASK_ADD) {
Packit Service a9274b
		snd_mixer_elem_set_callback(elem, elem_callback);
Packit Service a9274b
		controls_changed = TRUE;
Packit Service a9274b
	}
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
void create_mixer_object(struct snd_mixer_selem_regopt *selem_regopt)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	err = snd_mixer_open(&mixer, 0);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		fatal_alsa_error(_("cannot open mixer"), err);
Packit Service a9274b
Packit Service a9274b
	mixer_device_name = cstrdup(selem_regopt->device);
Packit Service a9274b
	err = snd_mixer_selem_register(mixer, selem_regopt, NULL);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		fatal_alsa_error(_("cannot open mixer"), err);
Packit Service a9274b
Packit Service a9274b
	snd_mixer_set_callback(mixer, mixer_callback);
Packit Service a9274b
Packit Service a9274b
	err = snd_mixer_load(mixer);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		fatal_alsa_error(_("cannot load mixer controls"), err);
Packit Service a9274b
Packit Service a9274b
	err = snd_mixer_selem_id_malloc(&current_selem_id);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		fatal_error("out of memory");
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void set_view_mode(enum view_mode m)
Packit Service a9274b
{
Packit Service a9274b
	view_mode = m;
Packit Service a9274b
	create_controls();
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void close_hctl(void)
Packit Service a9274b
{
Packit Service a9274b
	free_controls();
Packit Service a9274b
	if (mixer_device_name) {
Packit Service a9274b
		snd_mixer_detach(mixer, mixer_device_name);
Packit Service a9274b
		free(mixer_device_name);
Packit Service a9274b
		mixer_device_name = NULL;
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void check_unplugged(void)
Packit Service a9274b
{
Packit Service a9274b
	snd_hctl_t *hctl;
Packit Service a9274b
	snd_ctl_t *ctl;
Packit Service a9274b
	unsigned int state;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	unplugged = FALSE;
Packit Service a9274b
	if (mixer_device_name) {
Packit Service a9274b
		err = snd_mixer_get_hctl(mixer, mixer_device_name, &hctl);
Packit Service a9274b
		if (err >= 0) {
Packit Service a9274b
			ctl = snd_hctl_ctl(hctl);
Packit Service a9274b
			/* just any random function that does an ioctl() */
Packit Service a9274b
			err = snd_ctl_get_power_state(ctl, &state);
Packit Service a9274b
			if (err == -ENODEV)
Packit Service a9274b
				unplugged = TRUE;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
void close_mixer_device(void)
Packit Service a9274b
{
Packit Service a9274b
	check_unplugged();
Packit Service a9274b
	close_hctl();
Packit Service a9274b
Packit Service a9274b
	display_card_info();
Packit Service a9274b
	set_view_mode(view_mode);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
bool select_card_by_name(const char *device_name)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
	bool opened;
Packit Service a9274b
	char *msg;
Packit Service a9274b
Packit Service a9274b
	close_hctl();
Packit Service a9274b
	unplugged = FALSE;
Packit Service a9274b
Packit Service a9274b
	opened = FALSE;
Packit Service a9274b
	if (device_name) {
Packit Service a9274b
		err = snd_mixer_attach(mixer, device_name);
Packit Service a9274b
		if (err >= 0)
Packit Service a9274b
			opened = TRUE;
Packit Service a9274b
		else {
Packit Service a9274b
			msg = casprintf(_("Cannot open mixer device '%s'."), device_name);
Packit Service a9274b
			show_alsa_error(msg, err);
Packit Service a9274b
			free(msg);
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
	if (opened) {
Packit Service a9274b
		mixer_device_name = cstrdup(device_name);
Packit Service a9274b
Packit Service a9274b
		err = snd_mixer_load(mixer);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			fatal_alsa_error(_("cannot load mixer controls"), err);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	display_card_info();
Packit Service a9274b
	set_view_mode(view_mode);
Packit Service a9274b
	return opened;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void show_help(void)
Packit Service a9274b
{
Packit Service a9274b
	const char *help[] = {
Packit Service a9274b
		_("Esc     Exit"),
Packit Service a9274b
		_("F1 ? H  Help"),
Packit Service a9274b
		_("F2 /    System information"),
Packit Service a9274b
		_("F3      Show playback controls"),
Packit Service a9274b
		_("F4      Show capture controls"),
Packit Service a9274b
		_("F5      Show all controls"),
Packit Service a9274b
		_("Tab     Toggle view mode (F3/F4/F5)"),
Packit Service a9274b
		_("F6 S    Select sound card"),
Packit Service a9274b
		_("L       Redraw screen"),
Packit Service a9274b
		"",
Packit Service a9274b
		_("Left    Move to the previous control"),
Packit Service a9274b
		_("Right   Move to the next control"),
Packit Service a9274b
		"",
Packit Service a9274b
		_("Up/Down    Change volume"),
Packit Service a9274b
		_("+ -        Change volume"),
Packit Service a9274b
		_("Page Up/Dn Change volume in big steps"),
Packit Service a9274b
		_("End        Set volume to 0%"),
Packit Service a9274b
		_("0-9        Set volume to 0%-90%"),
Packit Service a9274b
		_("Q W E      Increase left/both/right volumes"),
Packit Service a9274b
		/* TRANSLATORS: or Y instead of Z */
Packit Service a9274b
		_("Z X C      Decrease left/both/right volumes"),
Packit Service a9274b
		_("B          Balance left and right volumes"),
Packit Service a9274b
		"",
Packit Service a9274b
		_("M          Toggle mute"),
Packit Service a9274b
		/* TRANSLATORS: or , . */
Packit Service a9274b
		_("< >        Toggle left/right mute"),
Packit Service a9274b
		"",
Packit Service a9274b
		_("Space      Toggle capture"),
Packit Service a9274b
		/* TRANSLATORS: or Insert Delete */
Packit Service a9274b
		_("; '        Toggle left/right capture"),
Packit Service a9274b
		"",
Packit Service a9274b
		_("Authors:"),
Packit Service a9274b
		_("  Tim Janik"),
Packit Service a9274b
		_("  Jaroslav Kysela <perex@perex.cz>"),
Packit Service a9274b
		_("  Clemens Ladisch <clemens@ladisch.de>"),
Packit Service a9274b
	};
Packit Service a9274b
	show_text(help, ARRAY_SIZE(help), _("Help"));
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
void refocus_control(void)
Packit Service a9274b
{
Packit Service a9274b
	if (focus_control_index < controls_count) {
Packit Service a9274b
		snd_mixer_selem_get_id(controls[focus_control_index].elem, current_selem_id);
Packit Service a9274b
		current_control_flags = controls[focus_control_index].flags;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	display_controls();
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static struct control *get_focus_control(unsigned int type)
Packit Service a9274b
{
Packit Service a9274b
	if (focus_control_index >= 0 &&
Packit Service a9274b
	    focus_control_index < controls_count &&
Packit Service a9274b
	    (controls[focus_control_index].flags & IS_ACTIVE) &&
Packit Service a9274b
	    (controls[focus_control_index].flags & type))
Packit Service a9274b
		return &controls[focus_control_index];
Packit Service a9274b
	else
Packit Service a9274b
		return NULL;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void change_enum_to_percent(struct control *control, int value)
Packit Service a9274b
{
Packit Service a9274b
	unsigned int i;
Packit Service a9274b
	unsigned int index;
Packit Service a9274b
	unsigned int new_index;
Packit Service a9274b
	int items;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	i = ffs(control->enum_channel_bits) - 1;
Packit Service a9274b
	err = snd_mixer_selem_get_enum_item(control->elem, i, &index);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return;
Packit Service a9274b
	new_index = index;
Packit Service a9274b
	if (value == 0) {
Packit Service a9274b
		new_index = 0;
Packit Service a9274b
	} else if (value == 100) {
Packit Service a9274b
		items = snd_mixer_selem_get_enum_items(control->elem);
Packit Service a9274b
		if (items < 1)
Packit Service a9274b
			return;
Packit Service a9274b
		new_index = items - 1;
Packit Service a9274b
	}
Packit Service a9274b
	if (new_index == index)
Packit Service a9274b
		return;
Packit Service a9274b
	for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i)
Packit Service a9274b
		if (control->enum_channel_bits & (1 << i))
Packit Service a9274b
			snd_mixer_selem_set_enum_item(control->elem, i, new_index);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void change_enum_relative(struct control *control, int delta)
Packit Service a9274b
{
Packit Service a9274b
	int items;
Packit Service a9274b
	unsigned int i;
Packit Service a9274b
	unsigned int index;
Packit Service a9274b
	int new_index;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	items = snd_mixer_selem_get_enum_items(control->elem);
Packit Service a9274b
	if (items < 1)
Packit Service a9274b
		return;
Packit Service a9274b
	err = snd_mixer_selem_get_enum_item(control->elem, 0, &index);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return;
Packit Service a9274b
	new_index = (int)index + delta;
Packit Service a9274b
	if (new_index < 0)
Packit Service a9274b
		new_index = 0;
Packit Service a9274b
	else if (new_index >= items)
Packit Service a9274b
		new_index = items - 1;
Packit Service a9274b
	if (new_index == index)
Packit Service a9274b
		return;
Packit Service a9274b
	for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i)
Packit Service a9274b
		if (control->enum_channel_bits & (1 << i))
Packit Service a9274b
			snd_mixer_selem_set_enum_item(control->elem, i, new_index);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void change_volume_to_percent(struct control *control, int value, unsigned int channels)
Packit Service a9274b
{
Packit Service a9274b
	int (*set_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, double, int);
Packit Service a9274b
Packit Service a9274b
	if (!(control->flags & HAS_VOLUME_1))
Packit Service a9274b
		channels = LEFT;
Packit Service a9274b
	if (control->flags & TYPE_PVOLUME)
Packit Service a9274b
		set_func = set_normalized_playback_volume;
Packit Service a9274b
	else
Packit Service a9274b
		set_func = set_normalized_capture_volume;
Packit Service a9274b
	if (channels & LEFT)
Packit Service a9274b
		set_func(control->elem, control->volume_channels[0], value / 100.0, 0);
Packit Service a9274b
	if (channels & RIGHT)
Packit Service a9274b
		set_func(control->elem, control->volume_channels[1], value / 100.0, 0);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static double clamp_volume(double v)
Packit Service a9274b
{
Packit Service a9274b
	if (v < 0)
Packit Service a9274b
		return 0;
Packit Service a9274b
	if (v > 1)
Packit Service a9274b
		return 1;
Packit Service a9274b
	return v;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void change_volume_relative(struct control *control, int delta, unsigned int channels)
Packit Service a9274b
{
Packit Service a9274b
	double (*get_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t);
Packit Service a9274b
	int (*set_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, double, int);
Packit Service a9274b
	double left, right;
Packit Service a9274b
	int dir;
Packit Service a9274b
Packit Service a9274b
	if (!(control->flags & HAS_VOLUME_1))
Packit Service a9274b
		channels = LEFT;
Packit Service a9274b
	if (control->flags & TYPE_PVOLUME) {
Packit Service a9274b
		get_func = get_normalized_playback_volume;
Packit Service a9274b
		set_func = set_normalized_playback_volume;
Packit Service a9274b
	} else {
Packit Service a9274b
		get_func = get_normalized_capture_volume;
Packit Service a9274b
		set_func = set_normalized_capture_volume;
Packit Service a9274b
	}
Packit Service a9274b
	if (channels & LEFT)
Packit Service a9274b
		left = get_func(control->elem, control->volume_channels[0]);
Packit Service a9274b
	if (channels & RIGHT)
Packit Service a9274b
		right = get_func(control->elem, control->volume_channels[1]);
Packit Service a9274b
	dir = delta > 0 ? 1 : -1;
Packit Service a9274b
	if (channels & LEFT) {
Packit Service a9274b
		left = clamp_volume(left + delta / 100.0);
Packit Service a9274b
		set_func(control->elem, control->volume_channels[0], left, dir);
Packit Service a9274b
	}
Packit Service a9274b
	if (channels & RIGHT) {
Packit Service a9274b
		right = clamp_volume(right + delta / 100.0);
Packit Service a9274b
		set_func(control->elem, control->volume_channels[1], right, dir);
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void change_control_to_percent(int value, unsigned int channels)
Packit Service a9274b
{
Packit Service a9274b
	struct control *control;
Packit Service a9274b
Packit Service a9274b
	control = get_focus_control(TYPE_PVOLUME | TYPE_CVOLUME | TYPE_ENUM);
Packit Service a9274b
	if (!control)
Packit Service a9274b
		return;
Packit Service a9274b
	if (control->flags & TYPE_ENUM)
Packit Service a9274b
		change_enum_to_percent(control, value);
Packit Service a9274b
	else
Packit Service a9274b
		change_volume_to_percent(control, value, channels);
Packit Service a9274b
	display_controls();
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void change_control_relative(int delta, unsigned int channels)
Packit Service a9274b
{
Packit Service a9274b
	struct control *control;
Packit Service a9274b
Packit Service a9274b
	control = get_focus_control(TYPE_PVOLUME | TYPE_CVOLUME | TYPE_ENUM);
Packit Service a9274b
	if (!control)
Packit Service a9274b
		return;
Packit Service a9274b
	if (control->flags & TYPE_ENUM)
Packit Service a9274b
		change_enum_relative(control, delta);
Packit Service a9274b
	else
Packit Service a9274b
		change_volume_relative(control, delta, channels);
Packit Service a9274b
	display_controls();
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void toggle_switches(unsigned int type, unsigned int channels)
Packit Service a9274b
{
Packit Service a9274b
	struct control *control;
Packit Service a9274b
	unsigned int switch_1_mask;
Packit Service a9274b
	int (*get_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, int *);
Packit Service a9274b
	int (*set_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, int);
Packit Service a9274b
	snd_mixer_selem_channel_id_t channel_ids[2];
Packit Service a9274b
	int left, right;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	control = get_focus_control(type);
Packit Service a9274b
	if (!control)
Packit Service a9274b
		return;
Packit Service a9274b
	if (type == TYPE_PSWITCH) {
Packit Service a9274b
		switch_1_mask = HAS_PSWITCH_1;
Packit Service a9274b
		get_func = snd_mixer_selem_get_playback_switch;
Packit Service a9274b
		set_func = snd_mixer_selem_set_playback_switch;
Packit Service a9274b
		channel_ids[0] = control->pswitch_channels[0];
Packit Service a9274b
		channel_ids[1] = control->pswitch_channels[1];
Packit Service a9274b
	} else {
Packit Service a9274b
		switch_1_mask = HAS_CSWITCH_1;
Packit Service a9274b
		get_func = snd_mixer_selem_get_capture_switch;
Packit Service a9274b
		set_func = snd_mixer_selem_set_capture_switch;
Packit Service a9274b
		channel_ids[0] = control->cswitch_channels[0];
Packit Service a9274b
		channel_ids[1] = control->cswitch_channels[1];
Packit Service a9274b
	}
Packit Service a9274b
	if (!(control->flags & switch_1_mask))
Packit Service a9274b
		channels = LEFT;
Packit Service a9274b
	if (channels & LEFT) {
Packit Service a9274b
		err = get_func(control->elem, channel_ids[0], &left);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return;
Packit Service a9274b
	}
Packit Service a9274b
	if (channels & RIGHT) {
Packit Service a9274b
		err = get_func(control->elem, channel_ids[1], &right);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return;
Packit Service a9274b
	}
Packit Service a9274b
	if (channels & LEFT)
Packit Service a9274b
		set_func(control->elem, channel_ids[0], !left);
Packit Service a9274b
	if (channels & RIGHT)
Packit Service a9274b
		set_func(control->elem, channel_ids[1], !right);
Packit Service a9274b
	display_controls();
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void toggle_mute(unsigned int channels)
Packit Service a9274b
{
Packit Service a9274b
	toggle_switches(TYPE_PSWITCH, channels);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void toggle_capture(unsigned int channels)
Packit Service a9274b
{
Packit Service a9274b
	toggle_switches(TYPE_CSWITCH, channels);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void balance_volumes(void)
Packit Service a9274b
{
Packit Service a9274b
	struct control *control;
Packit Service a9274b
	double left, right;
Packit Service a9274b
Packit Service a9274b
	control = get_focus_control(TYPE_PVOLUME | TYPE_CVOLUME);
Packit Service a9274b
	if (!control || !(control->flags & HAS_VOLUME_1))
Packit Service a9274b
		return;
Packit Service a9274b
	if (control->flags & TYPE_PVOLUME) {
Packit Service a9274b
		left = get_normalized_playback_volume(control->elem, control->volume_channels[0]);
Packit Service a9274b
		right = get_normalized_playback_volume(control->elem, control->volume_channels[1]);
Packit Service a9274b
	} else {
Packit Service a9274b
		left = get_normalized_capture_volume(control->elem, control->volume_channels[0]);
Packit Service a9274b
		right = get_normalized_capture_volume(control->elem, control->volume_channels[1]);
Packit Service a9274b
	}
Packit Service a9274b
	left = (left + right) / 2;
Packit Service a9274b
	if (control->flags & TYPE_PVOLUME) {
Packit Service a9274b
		set_normalized_playback_volume(control->elem, control->volume_channels[0], left, 0);
Packit Service a9274b
		set_normalized_playback_volume(control->elem, control->volume_channels[1], left, 0);
Packit Service a9274b
	} else {
Packit Service a9274b
		set_normalized_capture_volume(control->elem, control->volume_channels[0], left, 0);
Packit Service a9274b
		set_normalized_capture_volume(control->elem, control->volume_channels[1], left, 0);
Packit Service a9274b
	}
Packit Service a9274b
	display_controls();
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int on_mouse_key() {
Packit Service a9274b
	MEVENT m;
Packit Service a9274b
	command_enum cmd = 0;
Packit Service a9274b
	unsigned int channels = LEFT | RIGHT;
Packit Service a9274b
	unsigned int index;
Packit Service a9274b
	struct control *control;
Packit Service a9274b
	struct clickable_rect *rect;
Packit Service a9274b
Packit Service a9274b
	if (getmouse(&m) == ERR)
Packit Service a9274b
		return 0;
Packit Service a9274b
Packit Service a9274b
	if (m.bstate & (
Packit Service a9274b
				BUTTON1_PRESSED|BUTTON1_RELEASED|
Packit Service a9274b
				BUTTON2_PRESSED|BUTTON2_RELEASED|
Packit Service a9274b
				BUTTON3_PRESSED|BUTTON3_RELEASED))
Packit Service a9274b
		return 0;
Packit Service a9274b
Packit Service a9274b
	rect = clickable_find(m.y, m.x);
Packit Service a9274b
	if (rect)
Packit Service a9274b
		cmd = rect->command;
Packit Service a9274b
Packit Service a9274b
#if NCURSES_MOUSE_VERSION > 1
Packit Service a9274b
	if (m.bstate & (BUTTON4_CLICKED|BUTTON4_PRESSED|BUTTON5_CLICKED|BUTTON5_PRESSED)) {
Packit Service a9274b
		switch (cmd) {
Packit Service a9274b
			case CMD_MIXER_MOUSE_CLICK_CONTROL_ENUM:
Packit Service a9274b
				focus_control_index = rect->arg1;
Packit Service a9274b
				return CMD_WITH_ARG((
Packit Service a9274b
							m.bstate & (BUTTON4_CLICKED|BUTTON4_PRESSED)
Packit Service a9274b
							? CMD_MIXER_CONTROL_UP
Packit Service a9274b
							: CMD_MIXER_CONTROL_DOWN
Packit Service a9274b
						), 1);
Packit Service a9274b
Packit Service a9274b
			case CMD_MIXER_MOUSE_CLICK_VOLUME_BAR:
Packit Service a9274b
				if (mouse_wheel_focuses_control)
Packit Service a9274b
					focus_control_index = rect->arg1;
Packit Service a9274b
Packit Service a9274b
			default:
Packit Service a9274b
				return CMD_WITH_ARG((
Packit Service a9274b
							m.bstate & (BUTTON4_CLICKED|BUTTON4_PRESSED)
Packit Service a9274b
							? CMD_MIXER_CONTROL_UP
Packit Service a9274b
							: CMD_MIXER_CONTROL_DOWN
Packit Service a9274b
						), mouse_wheel_step);
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
#endif
Packit Service a9274b
Packit Service a9274b
	/* If the rectangle has got an argument (value != -1) it is used for
Packit Service a9274b
	 * setting `focus_control_index` */
Packit Service a9274b
	if (rect && rect->arg1 >= 0)
Packit Service a9274b
		focus_control_index = rect->arg1;
Packit Service a9274b
Packit Service a9274b
	switch (cmd) {
Packit Service a9274b
	case CMD_MIXER_MOUSE_CLICK_VOLUME_BAR:
Packit Service a9274b
		if (m.bstate & (BUTTON3_CLICKED|BUTTON3_DOUBLE_CLICKED|BUTTON3_TRIPLE_CLICKED))
Packit Service a9274b
			channels = m.x - rect->x1 + 1;
Packit Service a9274b
		return CMD_WITH_ARG(CMD_MIXER_CONTROL_SET_PERCENT_LEFT + channels - 1,
Packit Service a9274b
			(100 * (rect->y2 - m.y) / (rect->y2 - rect->y1)) // volume
Packit Service a9274b
		);
Packit Service a9274b
Packit Service a9274b
	case CMD_MIXER_MOUSE_CLICK_MUTE:
Packit Service a9274b
		if (m.bstate & (BUTTON3_CLICKED|BUTTON3_DOUBLE_CLICKED|BUTTON3_TRIPLE_CLICKED))
Packit Service a9274b
			channels = m.x - rect->x1 + 1;
Packit Service a9274b
		return CMD_WITH_ARG(CMD_MIXER_TOGGLE_MUTE, channels);
Packit Service a9274b
Packit Service a9274b
	case CMD_MIXER_MOUSE_CLICK_CONTROL_ENUM:
Packit Service a9274b
		control = get_focus_control(TYPE_ENUM);
Packit Service a9274b
		if (control &&
Packit Service a9274b
			(snd_mixer_selem_get_enum_item(control->elem, 0, &index) >= 0)) {
Packit Service a9274b
				return (index == 0
Packit Service a9274b
					? CMD_WITH_ARG(CMD_MIXER_CONTROL_UP, 100)
Packit Service a9274b
					: CMD_WITH_ARG(CMD_MIXER_CONTROL_DOWN, 1));
Packit Service a9274b
		}
Packit Service a9274b
		break;
Packit Service a9274b
Packit Service a9274b
	default:
Packit Service a9274b
		return cmd; // non-mouse command
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0; // failed mouse command
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void on_handle_key(int key)
Packit Service a9274b
{
Packit Service a9274b
	int arg;
Packit Service a9274b
	command_enum cmd;
Packit Service a9274b
Packit Service a9274b
	if (key == KEY_MOUSE)
Packit Service a9274b
		cmd = on_mouse_key();
Packit Service a9274b
	else if (key < ARRAY_SIZE(mixer_bindings))
Packit Service a9274b
		cmd = mixer_bindings[key];
Packit Service a9274b
	else
Packit Service a9274b
		return;
Packit Service a9274b
Packit Service a9274b
	arg = CMD_GET_ARGUMENT(cmd);
Packit Service a9274b
	cmd = CMD_GET_COMMAND(cmd);
Packit Service a9274b
Packit Service a9274b
	switch (cmd) {
Packit Service a9274b
	case CMD_MIXER_CONTROL_DOWN_LEFT:
Packit Service a9274b
	case CMD_MIXER_CONTROL_DOWN_RIGHT:
Packit Service a9274b
	case CMD_MIXER_CONTROL_DOWN:
Packit Service a9274b
		arg = (-arg);
Packit Service a9274b
	case CMD_MIXER_CONTROL_UP_LEFT:
Packit Service a9274b
	case CMD_MIXER_CONTROL_UP_RIGHT:
Packit Service a9274b
	case CMD_MIXER_CONTROL_UP:
Packit Service a9274b
		change_control_relative(arg, cmd % 4);
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_CONTROL_SET_PERCENT_LEFT:
Packit Service a9274b
	case CMD_MIXER_CONTROL_SET_PERCENT_RIGHT:
Packit Service a9274b
	case CMD_MIXER_CONTROL_SET_PERCENT:
Packit Service a9274b
		change_control_to_percent(arg, cmd % 4);
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_CLOSE:
Packit Service a9274b
		mixer_widget.close();
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_HELP:
Packit Service a9274b
		show_help();
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_SYSTEM_INFORMATION:
Packit Service a9274b
		create_proc_files_list();
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_TOGGLE_VIEW_MODE:
Packit Service a9274b
		arg = (view_mode + 1) % VIEW_MODE_COUNT;
Packit Service a9274b
	case CMD_MIXER_SET_VIEW_MODE:
Packit Service a9274b
		set_view_mode((enum view_mode)(arg));
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_SELECT_CARD:
Packit Service a9274b
		create_card_select_list();
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_REFRESH:
Packit Service a9274b
		clearok(mixer_widget.window, TRUE);
Packit Service a9274b
		display_controls();
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_PREVIOUS:
Packit Service a9274b
		arg = (-arg);
Packit Service a9274b
	case CMD_MIXER_NEXT:
Packit Service a9274b
		arg = focus_control_index + arg;
Packit Service a9274b
	case CMD_MIXER_FOCUS_CONTROL:
Packit Service a9274b
		focus_control_index = arg;
Packit Service a9274b
		if (focus_control_index < 0)
Packit Service a9274b
			focus_control_index = 0;
Packit Service a9274b
		else if (focus_control_index >= controls_count)
Packit Service a9274b
			focus_control_index = controls_count - 1;
Packit Service a9274b
		refocus_control();
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_TOGGLE_MUTE:
Packit Service a9274b
		toggle_mute(arg);
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_TOGGLE_CAPTURE:
Packit Service a9274b
		toggle_capture(arg);
Packit Service a9274b
		break;
Packit Service a9274b
	case CMD_MIXER_BALANCE_CONTROL:
Packit Service a9274b
		balance_volumes();
Packit Service a9274b
		break;
Packit Service a9274b
	}
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void create(void)
Packit Service a9274b
{
Packit Service a9274b
	static const char title[] = " AlsaMixer v" SND_UTIL_VERSION_STR " ";
Packit Service a9274b
Packit Service a9274b
	widget_init(&mixer_widget, screen_lines, screen_cols, 0, 0,
Packit Service a9274b
		    attrs.mixer_frame, WIDGET_BORDER);
Packit Service a9274b
	if (screen_cols >= (sizeof(title) - 1) + 2) {
Packit Service a9274b
		wattrset(mixer_widget.window, attrs.mixer_active);
Packit Service a9274b
		mvwaddstr(mixer_widget.window, 0, (screen_cols - (sizeof(title) - 1)) / 2, title);
Packit Service a9274b
	}
Packit Service a9274b
	init_mixer_layout();
Packit Service a9274b
	display_card_info();
Packit Service a9274b
	set_view_mode(view_mode);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void on_window_size_changed(void)
Packit Service a9274b
{
Packit Service a9274b
	create();
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void on_close(void)
Packit Service a9274b
{
Packit Service a9274b
	widget_free(&mixer_widget);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
void mixer_shutdown(void)
Packit Service a9274b
{
Packit Service a9274b
	free_controls();
Packit Service a9274b
	if (mixer)
Packit Service a9274b
		snd_mixer_close(mixer);
Packit Service a9274b
	if (current_selem_id)
Packit Service a9274b
		snd_mixer_selem_id_free(current_selem_id);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
struct widget mixer_widget = {
Packit Service a9274b
	.handle_key = on_handle_key,
Packit Service a9274b
	.window_size_changed = on_window_size_changed,
Packit Service a9274b
	.close = on_close,
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
void create_mixer_widget(void)
Packit Service a9274b
{
Packit Service a9274b
	create();
Packit Service a9274b
}