Adding upstream version 0.3.5.2+svn20080509.
[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"))
80 return FALSE;
81
82
83 snd_mixer_selem_get_playback_volume_range(vol->master_element, &vol->alsa_min_vol, &vol->alsa_max_vol);
84
85 snd_mixer_selem_set_playback_volume_range(vol->master_element, 0, 100);
86
87 return TRUE;
88}
89
90static int asound_read(volume_t *vol)
91{
92 long aleft, aright;
93 snd_mixer_handle_events(vol->mixer);
94 /* Left */
95 snd_mixer_selem_get_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_LEFT, &aleft);
96 /* Right */
97 snd_mixer_selem_get_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_RIGHT, &aright);
98
99 return (aleft + aright) >> 1;
100}
101
102static void asound_write(volume_t *vol, int volume)
103{
104 snd_mixer_selem_set_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_LEFT, volume);
105 snd_mixer_selem_set_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_RIGHT, volume);
106}
107
108static gboolean focus_out_event(GtkWidget *widget, GdkEvent *event, volume_t *vol)
109{
110 gtk_widget_hide(vol->dlg);
111 vol->show = 0;
112 return FALSE;
113}
114
115static gboolean tray_icon_press(GtkWidget *widget, GdkEventButton *event, volume_t *vol)
116{
117 if( event->button == 3 ) /* right button */
118 {
119 GtkMenu* popup = lxpanel_get_panel_menu( vol->plugin->panel, vol->plugin, FALSE );
120 gtk_menu_popup( popup, NULL, NULL, NULL, NULL, event->button, event->time );
121 return TRUE;
122 }
123
124 if (vol->show==0) {
125 gtk_window_set_position(GTK_WINDOW(vol->dlg), GTK_WIN_POS_MOUSE);
126 gtk_scale_set_digits(GTK_SCALE(vol->vscale), asound_read(vol));
127 gtk_widget_show_all(vol->dlg);
128 vol->show = 1;
129 } else {
130 gtk_widget_hide(vol->dlg);
131 vol->show = 0;
132 }
133 return TRUE;
134}
135
136static void on_vscale_value_changed(GtkRange *range, volume_t *vol)
137{
138 asound_write(vol, gtk_range_get_value(range));
139}
140
141static void click_mute(GtkWidget *widget, volume_t *vol)
142{
143 int chn;
144
145 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
146 gtk_image_set_from_file(vol->tray_icon, ICONS_MUTE);
147 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
148 snd_mixer_selem_set_playback_switch(vol->master_element, chn, 0);
149 }
150 } else {
151 gtk_image_set_from_file(vol->tray_icon, ICONS_VOLUME);
152 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
153 snd_mixer_selem_set_playback_switch(vol->master_element, chn, 1);
154 }
155 }
156}
157
158static void panel_init(Plugin *p)
159{
160 volume_t *vol = p->priv;
161 GtkWidget *scrolledwindow;
162 GtkWidget *viewport;
163 GtkWidget *box;
164 GtkWidget *frame;
165 GtkWidget *checkbutton;
166
167 /* set show flags */
168 vol->show = 0;
169
170 /* create a new window */
171 vol->dlg = gtk_window_new(GTK_WINDOW_TOPLEVEL);
172 gtk_window_set_decorated(GTK_WINDOW(vol->dlg), FALSE);
173 gtk_container_set_border_width(GTK_CONTAINER(vol->dlg), 5);
174 gtk_window_set_default_size(GTK_WINDOW(vol->dlg), 80, 140);
175 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(vol->dlg), TRUE);
176 gtk_window_set_skip_pager_hint(GTK_WINDOW(vol->dlg), TRUE);
177 gtk_window_set_type_hint(GTK_WINDOW(vol->dlg), GDK_WINDOW_TYPE_HINT_DIALOG);
178
179 /* setting background to default */
180 //gtk_widget_set_style(vol->dlg, p->panel->defstyle);
181
182 /* Focus-out signal */
183 g_signal_connect (G_OBJECT (vol->dlg), "focus_out_event",
184 G_CALLBACK (focus_out_event), vol);
185
186 scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
187 gtk_container_set_border_width (GTK_CONTAINER (scrolledwindow), 0);
188 gtk_widget_show (scrolledwindow);
189 gtk_container_add (GTK_CONTAINER (vol->dlg), scrolledwindow);
190 GTK_WIDGET_UNSET_FLAGS (scrolledwindow, GTK_CAN_FOCUS);
191 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
192 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_SHADOW_NONE);
193
194 viewport = gtk_viewport_new (NULL, NULL);
195 gtk_container_add (GTK_CONTAINER (scrolledwindow), viewport);
196 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
197 gtk_widget_show(viewport);
198
199 /* create frame */
200 frame = gtk_frame_new(_("Volume"));
201 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
202 gtk_container_add(GTK_CONTAINER(viewport), frame);
203
204 /* create box */
205 box = gtk_vbox_new(FALSE, 0);
206
207 /* create controller */
208 vol->vscale = gtk_vscale_new(GTK_ADJUSTMENT(gtk_adjustment_new(asound_read(vol), 0, 100, 0, 0, 0)));
209 gtk_scale_set_draw_value(GTK_SCALE(vol->vscale), FALSE);
210 gtk_range_set_inverted(GTK_RANGE(vol->vscale), TRUE);
211
212 g_signal_connect ((gpointer) vol->vscale, "value_changed",
213 G_CALLBACK (on_vscale_value_changed),
214 vol);
215
216 checkbutton = gtk_check_button_new_with_label(_("Mute"));
217 snd_mixer_selem_get_playback_switch(vol->master_element, 0, &vol->mute);
218
219 if (!vol->mute)
220 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), TRUE);
221
222 g_signal_connect ((gpointer) checkbutton, "toggled",
223 G_CALLBACK (click_mute),
224 vol);
225
226 gtk_box_pack_start(GTK_BOX(box), vol->vscale, TRUE, TRUE, 0);
227 gtk_box_pack_end(GTK_BOX(box), checkbutton, FALSE, FALSE, 0);
228 gtk_container_add(GTK_CONTAINER(frame), box);
229
230 /* setting background to default */
231 gtk_widget_set_style(viewport, p->panel->defstyle);
232}
233
234static void
235volumealsa_destructor(Plugin *p)
236{
237 volume_t *vol = (volume_t *) p->priv;
238
239 ENTER;
240 if (vol->dlg)
241 gtk_widget_destroy(vol->dlg);
242 g_object_unref( vol->tooltips );
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
291 vol->tooltips = gtk_tooltips_new ();
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};