2
* This file is a part of the Cairo-Dock project
4
* Copyright : (C) see the 'copyright' file.
5
* E-mail : see the 'copyright' file.
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU General Public License
9
* as published by the Free Software Foundation; either version 3
10
* of the License, or (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20
/******************************************************************************
22
This file is a part of the cairo-dock program,
23
released under the terms of the GNU General Public License.
25
Written by Fabrice Rey (for any bug report, please mail me to fabounet@users.berlios.de)
27
******************************************************************************/
32
#include <glib/gi18n.h>
33
#include <alsa/asoundlib.h>
35
#include "applet-struct.h"
36
#include "applet-draw.h"
37
#include "applet-mixer.h"
40
static int mixer_level = 0;
41
static struct snd_mixer_selem_regopt mixer_options;
45
mixer_event (snd_mixer_t *mixer, unsigned int mask, snd_mixer_elem_t *elem)
51
void mixer_init (gchar *cCardID) // this function is taken from AlsaMixer.
53
snd_ctl_card_info_t *hw_info = NULL; // ne pas liberer.
54
snd_ctl_t *ctl_handle = NULL;
56
snd_ctl_card_info_alloca (&hw_info);
58
if ((err = snd_ctl_open (&ctl_handle, cCardID, 0)) < 0)
60
myData.cErrorMessage = g_strdup_printf (D_("I couldn't open card '%s'"), cCardID);
63
if ((err = snd_ctl_card_info (ctl_handle, hw_info)) < 0)
65
myData.cErrorMessage = g_strdup_printf (D_("Card '%s' opened but I couldn't get any info"), cCardID);
68
snd_ctl_close (ctl_handle);
71
if ((err = snd_mixer_open (&myData.mixer_handle, 0)) < 0)
73
myData.cErrorMessage = g_strdup (D_("I couldn't open the mixer"));
76
if (mixer_level == 0 && (err = snd_mixer_attach (myData.mixer_handle, cCardID)) < 0)
78
snd_mixer_free (myData.mixer_handle);
79
myData.mixer_handle = NULL;
80
myData.cErrorMessage = g_strdup (D_("I couldn't attach the mixer to the card"));
83
if ((err = snd_mixer_selem_register (myData.mixer_handle, mixer_level > 0 ? &mixer_options : NULL, NULL)) < 0)
85
snd_mixer_free (myData.mixer_handle);
86
myData.mixer_handle = NULL;
87
myData.cErrorMessage = g_strdup (D_("I couldn't register options"));
90
///snd_mixer_set_callback (myData.mixer_handle, mixer_event);
91
if ((err = snd_mixer_load (myData.mixer_handle)) < 0)
93
snd_mixer_free (myData.mixer_handle);
94
myData.mixer_handle = NULL;
95
myData.cErrorMessage = g_strdup (D_("I couldn't load the mixer"));
99
myData.mixer_card_name = g_strdup (snd_ctl_card_info_get_name(hw_info));
100
myData.mixer_device_name= g_strdup (snd_ctl_card_info_get_mixername(hw_info));
101
cd_debug ("myData.mixer_card_name : %s ; myData.mixer_device_name : %s", myData.mixer_card_name, myData.mixer_device_name);
104
void mixer_stop (void)
106
if (myData.mixer_handle != NULL)
108
snd_mixer_detach (myData.mixer_handle, myConfig.card_id);
109
snd_mixer_close (myData.mixer_handle);
110
myData.mixer_handle = NULL;
111
myData.pControledElement = NULL;
112
myData.pControledElement2 = NULL;
117
GList *mixer_get_elements_list (void)
119
snd_mixer_elem_t *elem;
120
if (myData.mixer_handle == NULL)
125
for (elem = snd_mixer_first_elem(myData.mixer_handle); elem; elem = snd_mixer_elem_next(elem))
127
if (snd_mixer_selem_is_active (elem) && snd_mixer_selem_has_playback_volume (elem))
128
pList = g_list_prepend (pList, snd_mixer_selem_get_name (elem));
134
static snd_mixer_elem_t *_mixer_get_element_by_name (gchar *cName)
136
if (myData.mixer_handle == NULL)
138
g_return_val_if_fail (cName != NULL, NULL);
140
snd_mixer_elem_t *elem;
141
for (elem = snd_mixer_first_elem(myData.mixer_handle); elem; elem = snd_mixer_elem_next(elem))
143
if (strcmp (cName, snd_mixer_selem_get_name (elem)) == 0)
146
myData.cErrorMessage = g_strdup_printf (D_("I couldn't find any audio channel named '%s'\nYou should try to open the configuration panel of the applet,\n and select the proper audio channel you want to control."), cName);
150
void mixer_get_controlled_element (void)
152
myData.pControledElement = _mixer_get_element_by_name (myConfig.cMixerElementName);
153
if (myData.pControledElement != NULL)
155
myData.bHasMuteSwitch = snd_mixer_selem_has_playback_switch (myData.pControledElement);
157
snd_mixer_selem_get_playback_volume_range (myData.pControledElement, &myData.iVolumeMin, &myData.iVolumeMax);
158
cd_debug ("volume range : %d - %d", myData.iVolumeMin, myData.iVolumeMax);
160
snd_mixer_elem_set_callback (myData.pControledElement, mixer_element_update_with_event);
162
if (myConfig.cMixerElementName2 != NULL)
164
myData.pControledElement2 = _mixer_get_element_by_name (myConfig.cMixerElementName2);
168
int mixer_get_mean_volume (void)
170
g_return_val_if_fail (myData.pControledElement != NULL, 0);
171
long iVolumeLeft=0, iVolumeRight=0;
172
gboolean bHasLeft = snd_mixer_selem_has_playback_channel (myData.pControledElement, SND_MIXER_SCHN_FRONT_LEFT);
173
gboolean bHasRight = snd_mixer_selem_has_playback_channel (myData.pControledElement, SND_MIXER_SCHN_FRONT_RIGHT);
176
snd_mixer_selem_get_playback_volume (myData.pControledElement, SND_MIXER_SCHN_FRONT_LEFT, &iVolumeLeft);
178
snd_mixer_selem_get_playback_volume (myData.pControledElement, SND_MIXER_SCHN_FRONT_RIGHT, &iVolumeRight);
179
cd_debug ("volume : %d;%d", iVolumeLeft, iVolumeRight);
181
g_return_val_if_fail (bHasLeft || bHasRight, 0);
183
int iMeanVolume = (iVolumeLeft + iVolumeRight) / (bHasLeft + bHasRight);
185
g_print ("myData.iVolumeMin : %d ; myData.iVolumeMax : %d ; iMeanVolume : %d\n", myData.iVolumeMin, myData.iVolumeMax, iMeanVolume);
186
return (100. * (iMeanVolume - myData.iVolumeMin) / (myData.iVolumeMax - myData.iVolumeMin));
189
void mixer_set_volume (int iNewVolume)
191
g_return_if_fail (myData.pControledElement != NULL);
192
int iVolume = ceil (myData.iVolumeMin + (myData.iVolumeMax - myData.iVolumeMin) * iNewVolume / 100.);
193
snd_mixer_selem_set_playback_volume_all (myData.pControledElement, iVolume);
194
if (myData.pControledElement2 != NULL)
195
snd_mixer_selem_set_playback_volume_all (myData.pControledElement2, iVolume);
196
myData.iCurrentVolume = iNewVolume;
197
mixer_element_update_with_event (myData.pControledElement, 0); // on ne recoit pas d'evenements pour nos actions.
200
gboolean mixer_is_mute (void)
203
g_return_val_if_fail (myData.pControledElement != NULL, FALSE);
204
if (snd_mixer_selem_has_playback_switch (myData.pControledElement))
206
int iSwitchLeft, iSwitchRight;
207
snd_mixer_selem_get_playback_switch (myData.pControledElement, SND_MIXER_SCHN_FRONT_LEFT, &iSwitchLeft);
208
snd_mixer_selem_get_playback_switch (myData.pControledElement, SND_MIXER_SCHN_FRONT_RIGHT, &iSwitchRight);
209
cd_debug ("%d;%d", iSwitchLeft, iSwitchRight);
210
return (iSwitchLeft == 0 && iSwitchRight == 0);
216
void mixer_switch_mute (void)
218
g_return_if_fail (myData.pControledElement != NULL);
219
gboolean bIsMute = mixer_is_mute ();
220
snd_mixer_selem_set_playback_switch_all (myData.pControledElement, bIsMute);
221
if (myData.pControledElement2 != NULL)
222
snd_mixer_selem_set_playback_switch_all (myData.pControledElement2, bIsMute);
223
myData.bIsMute = ! bIsMute;
224
mixer_element_update_with_event (myData.pControledElement, 0); // on ne recoit pas d'evenements pour nos actions.
229
static void on_change_volume (GtkRange *range, gpointer data)
231
int iNewVolume = (int) gtk_range_get_value (GTK_RANGE (range));
232
cd_debug ("%s (%d)", __func__, iNewVolume);
233
mixer_set_volume (iNewVolume);
235
GtkWidget *mixer_build_widget (gboolean bHorizontal)
237
g_return_val_if_fail (myData.pControledElement != NULL, NULL);
238
GtkWidget *pScale = (bHorizontal ? gtk_hscale_new_with_range (0., 100., .5*myConfig.iScrollVariation) : gtk_vscale_new_with_range (0., 100., .5*myConfig.iScrollVariation));
240
gtk_range_set_inverted (GTK_RANGE (pScale), TRUE); // de bas en haut.
242
myData.iCurrentVolume = mixer_get_mean_volume ();
243
gtk_range_set_value (GTK_RANGE (pScale), myData.iCurrentVolume);
245
g_signal_connect (G_OBJECT (pScale),
247
G_CALLBACK (on_change_volume),
254
void mixer_set_volume_with_no_callback (GtkWidget *pScale, int iVolume)
256
g_signal_handlers_block_matched (GTK_WIDGET(pScale),
258
0, 0, NULL, (void*)on_change_volume, NULL);
259
gtk_range_set_value (GTK_RANGE (pScale), (double) iVolume);
260
g_signal_handlers_unblock_matched (GTK_WIDGET(pScale),
262
0, 0, NULL, (void*)on_change_volume, NULL);
266
static gboolean on_button_press_dialog (GtkWidget *widget,
267
GdkEventButton *pButton,
268
CairoDialog *pDialog)
270
cairo_dock_dialog_unreference (pDialog);
271
myData.pDialog = NULL;
275
void mixer_show_hide_dialog (void)
279
if (myData.pDialog == NULL)
281
const gchar *cMessage;
282
GtkWidget *pScale = NULL;
283
if (myData.cErrorMessage != NULL)
284
cMessage = myData.cErrorMessage;
287
cMessage = D_("Set up volume :");
288
pScale = mixer_build_widget (TRUE);
291
CairoDialogAttribute attr;
292
memset (&attr, 0, sizeof (CairoDialogAttribute));
293
attr.cText = cMessage;
294
attr.pInteractiveWidget = pScale;
295
myData.pDialog = cairo_dock_build_dialog (&attr, myIcon, myContainer);
296
g_signal_connect (G_OBJECT (myData.pDialog->pWidget),
297
"button-press-event",
298
G_CALLBACK (on_button_press_dialog),
303
cairo_dock_dialog_unreference (myData.pDialog);
304
myData.pDialog = NULL;
308
gboolean mixer_check_events (gpointer data)
310
snd_mixer_handle_events (myData.mixer_handle); // ne renvoie pas d'evenements pour nos actions !