Adding upstream version 0.3.7.
[debian/lxpanel.git] / src / plugins / volumealsa / volumealsa.c
CommitLineData
6cc5e1a6
DB
1/**
2 * Copyright (c) 2008 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#include <gtk/gtk.h>
20#include <stdlib.h>
21#include <fcntl.h>
22#include <unistd.h>
23#include <glib.h>
24#include <glib/gi18n.h>
25#include <gdk-pixbuf/gdk-pixbuf.h>
26#include <alsa/asoundlib.h>
27#include "panel.h"
28#include "misc.h"
29#include "plugin.h"
30#include "dbg.h"
31
32#define ICONS_VOLUME PACKAGE_DATA_DIR "/lxpanel/images/volume.png"
33#define ICONS_MUTE PACKAGE_DATA_DIR "/lxpanel/images/mute.png"
34
35typedef struct {
36 Plugin* plugin;
37 GtkWidget *mainw;
38 GtkWidget *tray_icon;
39 GtkWidget *dlg;
40 GtkTooltips* tooltips;
41 GtkWidget *vscale;
42 snd_mixer_t *mixer;
43 snd_mixer_selem_id_t *sid;
44 snd_mixer_elem_t *master_element;
45 long alsa_min_vol, alsa_max_vol;
46 int mute;
47 int show;
48} volume_t;
49
50
51
52/* ALSA */
53static gboolean find_element(volume_t *vol, const char *ename)
54{
55 for (vol->master_element=snd_mixer_first_elem(vol->mixer);vol->master_element;vol->master_element=snd_mixer_elem_next(vol->master_element)) {
56 snd_mixer_selem_get_id(vol->master_element, vol->sid);
57 if (!snd_mixer_selem_is_active(vol->master_element))
58 continue;
59
60 if (strcmp(ename, snd_mixer_selem_id_get_name(vol->sid))==0) {
61 return TRUE;
62 }
63 }
64
65 return FALSE;
66}
67
68static gboolean asound_init(volume_t *vol)
69{
70 snd_mixer_selem_id_alloca(&vol->sid);
71 snd_mixer_open(&vol->mixer, 0);
72 snd_mixer_attach(vol->mixer, "default");
73 snd_mixer_selem_register(vol->mixer, NULL, NULL);
74 snd_mixer_load(vol->mixer);
75
76 /* Find Master element */
77 if (!find_element(vol, "Master"))
78 if (!find_element(vol, "Front"))
79 if (!find_element(vol, "PCM"))
39c13576
DB
80 if (!find_element(vol, "LineOut"))
81 return FALSE;
6cc5e1a6
DB
82
83
84 snd_mixer_selem_get_playback_volume_range(vol->master_element, &vol->alsa_min_vol, &vol->alsa_max_vol);
85
86 snd_mixer_selem_set_playback_volume_range(vol->master_element, 0, 100);
87
88 return TRUE;
89}
90
91static int asound_read(volume_t *vol)
92{
93 long aleft, aright;
94 snd_mixer_handle_events(vol->mixer);
95 /* Left */
96 snd_mixer_selem_get_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_LEFT, &aleft);
97 /* Right */
98 snd_mixer_selem_get_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_RIGHT, &aright);
99
100 return (aleft + aright) >> 1;
101}
102
103static void asound_write(volume_t *vol, int volume)
104{
105 snd_mixer_selem_set_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_LEFT, volume);
106 snd_mixer_selem_set_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_RIGHT, volume);
107}
108
109static gboolean focus_out_event(GtkWidget *widget, GdkEvent *event, volume_t *vol)
110{
111 gtk_widget_hide(vol->dlg);
112 vol->show = 0;
113 return FALSE;
114}
115
116static gboolean tray_icon_press(GtkWidget *widget, GdkEventButton *event, volume_t *vol)
117{
118 if( event->button == 3 ) /* right button */
119 {
120 GtkMenu* popup = lxpanel_get_panel_menu( vol->plugin->panel, vol->plugin, FALSE );
121 gtk_menu_popup( popup, NULL, NULL, NULL, NULL, event->button, event->time );
122 return TRUE;
123 }
124
125 if (vol->show==0) {
126 gtk_window_set_position(GTK_WINDOW(vol->dlg), GTK_WIN_POS_MOUSE);
127 gtk_scale_set_digits(GTK_SCALE(vol->vscale), asound_read(vol));
128 gtk_widget_show_all(vol->dlg);
129 vol->show = 1;
130 } else {
131 gtk_widget_hide(vol->dlg);
132 vol->show = 0;
133 }
134 return TRUE;
135}
136
137static void on_vscale_value_changed(GtkRange *range, volume_t *vol)
138{
139 asound_write(vol, gtk_range_get_value(range));
140}
141
142static void click_mute(GtkWidget *widget, volume_t *vol)
143{
144 int chn;
145
146 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
147 gtk_image_set_from_file(vol->tray_icon, ICONS_MUTE);
148 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
149 snd_mixer_selem_set_playback_switch(vol->master_element, chn, 0);
150 }
151 } else {
152 gtk_image_set_from_file(vol->tray_icon, ICONS_VOLUME);
153 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
154 snd_mixer_selem_set_playback_switch(vol->master_element, chn, 1);
155 }
156 }
157}
158
159static void panel_init(Plugin *p)
160{
161 volume_t *vol = p->priv;
162 GtkWidget *scrolledwindow;
163 GtkWidget *viewport;
164 GtkWidget *box;
165 GtkWidget *frame;
166 GtkWidget *checkbutton;
167
168 /* set show flags */
169 vol->show = 0;
170
171 /* create a new window */
172 vol->dlg = gtk_window_new(GTK_WINDOW_TOPLEVEL);
173 gtk_window_set_decorated(GTK_WINDOW(vol->dlg), FALSE);
174 gtk_container_set_border_width(GTK_CONTAINER(vol->dlg), 5);
175 gtk_window_set_default_size(GTK_WINDOW(vol->dlg), 80, 140);
176 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(vol->dlg), TRUE);
177 gtk_window_set_skip_pager_hint(GTK_WINDOW(vol->dlg), TRUE);
178 gtk_window_set_type_hint(GTK_WINDOW(vol->dlg), GDK_WINDOW_TYPE_HINT_DIALOG);
179
180 /* setting background to default */
181 //gtk_widget_set_style(vol->dlg, p->panel->defstyle);
182
183 /* Focus-out signal */
184 g_signal_connect (G_OBJECT (vol->dlg), "focus_out_event",
185 G_CALLBACK (focus_out_event), vol);
186
187 scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
188 gtk_container_set_border_width (GTK_CONTAINER (scrolledwindow), 0);
189 gtk_widget_show (scrolledwindow);
190 gtk_container_add (GTK_CONTAINER (vol->dlg), scrolledwindow);
191 GTK_WIDGET_UNSET_FLAGS (scrolledwindow, GTK_CAN_FOCUS);
192 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
193 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_SHADOW_NONE);
194
195 viewport = gtk_viewport_new (NULL, NULL);
196 gtk_container_add (GTK_CONTAINER (scrolledwindow), viewport);
197 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
198 gtk_widget_show(viewport);
199
200 /* create frame */
201 frame = gtk_frame_new(_("Volume"));
202 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
203 gtk_container_add(GTK_CONTAINER(viewport), frame);
204
205 /* create box */
206 box = gtk_vbox_new(FALSE, 0);
207
208 /* create controller */
209 vol->vscale = gtk_vscale_new(GTK_ADJUSTMENT(gtk_adjustment_new(asound_read(vol), 0, 100, 0, 0, 0)));
210 gtk_scale_set_draw_value(GTK_SCALE(vol->vscale), FALSE);
211 gtk_range_set_inverted(GTK_RANGE(vol->vscale), TRUE);
212
213 g_signal_connect ((gpointer) vol->vscale, "value_changed",
214 G_CALLBACK (on_vscale_value_changed),
215 vol);
216
217 checkbutton = gtk_check_button_new_with_label(_("Mute"));
218 snd_mixer_selem_get_playback_switch(vol->master_element, 0, &vol->mute);
219
220 if (!vol->mute)
221 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), TRUE);
222
223 g_signal_connect ((gpointer) checkbutton, "toggled",
224 G_CALLBACK (click_mute),
225 vol);
226
227 gtk_box_pack_start(GTK_BOX(box), vol->vscale, TRUE, TRUE, 0);
228 gtk_box_pack_end(GTK_BOX(box), checkbutton, FALSE, FALSE, 0);
229 gtk_container_add(GTK_CONTAINER(frame), box);
230
231 /* setting background to default */
232 gtk_widget_set_style(viewport, p->panel->defstyle);
233}
234
235static void
236volumealsa_destructor(Plugin *p)
237{
238 volume_t *vol = (volume_t *) p->priv;
239
240 ENTER;
241 if (vol->dlg)
242 gtk_widget_destroy(vol->dlg);
6cc5e1a6
DB
243
244 g_free(vol);
245 RET();
246}
247
248static int
249volumealsa_constructor(Plugin *p, char **fp)
250{
251 volume_t *vol;
252 line s;
253 GdkPixbuf *icon;
254 GtkWidget *image;
255 GtkIconTheme* theme;
256 GtkIconInfo* info;
257
258 ENTER;
259 s.len = 256;
260 vol = g_new0(volume_t, 1);
261 vol->plugin = p;
262 g_return_val_if_fail(vol != NULL, 0);
263 p->priv = vol;
264
265 /* initializing */
266 if (!asound_init(vol))
267 RET(1);
268
269 panel_init(p);
270
271 /* main */
272 vol->mainw = gtk_event_box_new();
273
274 gtk_widget_add_events(vol->mainw, GDK_BUTTON_PRESS_MASK);
275 gtk_widget_set_size_request( vol->mainw, 24, 24 );
276
277 g_signal_connect(G_OBJECT(vol->mainw), "button-press-event",
278 G_CALLBACK(tray_icon_press), vol);
279
280 /* tray icon */
281 snd_mixer_selem_get_playback_switch(vol->master_element, 0, &vol->mute);
282 if (vol->mute==0)
283 vol->tray_icon = gtk_image_new_from_file(ICONS_MUTE);
284 else
285 vol->tray_icon = gtk_image_new_from_file(ICONS_VOLUME);
286
287 gtk_container_add(GTK_CONTAINER(vol->mainw), vol->tray_icon);
288
289 gtk_widget_show_all(vol->mainw);
290
3f7f0eb5 291 vol->tooltips = p->panel->tooltips;;
6cc5e1a6
DB
292#if GLIB_CHECK_VERSION( 2, 10, 0 )
293 g_object_ref_sink( vol->tooltips );
294#else
295 g_object_ref( vol->tooltips );
296 gtk_object_sink( vol->tooltips );
297#endif
298
299 /* FIXME: display current level in tooltip. ex: "Volume Control: 80%" */
300 gtk_tooltips_set_tip (vol->tooltips, vol->mainw, _("Volume control"), NULL);
301
302 /* store the created plugin widget in plugin->pwid */
303 p->pwid = vol->mainw;
304
305 RET(1);
306}
307
308
309PluginClass volumealsa_plugin_class = {
310 fname: NULL,
311 count: 0,
312
313 type : "volumealsa",
314 name : N_("Volume Control"),
315 version: "1.0",
316 description : "Display and control volume for ALSA",
317
318 constructor : volumealsa_constructor,
319 destructor : volumealsa_destructor,
320 config : NULL,
321 save : NULL
322};