Rename panel, plugin, and plugin_class to Panel, Plugin, and
[lxde/lxpanel.git] / src / plugins / volumealsa / volumealsa.c
CommitLineData
c044bccf
JH
1/**
2 * Copyright (c) 2008 LxDE Developers, see the file AUTHORS for details.
49db0a37
FC
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
d5d373e7
FC
32#define ICONS_VOLUME PACKAGE_DATA_DIR "/lxpanel/images/volume.png"
33#define ICONS_MUTE PACKAGE_DATA_DIR "/lxpanel/images/mute.png"
49db0a37
FC
34
35typedef struct {
4542c20d
HJYP
36 GtkWidget *mainw;
37 GtkWidget *tray_icon;
38 GtkWidget *dlg;
39 GtkTooltips* tooltips;
40 GtkWidget *vscale;
41 snd_mixer_t *mixer;
42 snd_mixer_selem_id_t *sid;
43 snd_mixer_elem_t *master_element;
44 long alsa_min_vol, alsa_max_vol;
45 int mute;
46 int show;
49db0a37
FC
47} volume_t;
48
49
50
51/* ALSA */
52static gboolean find_element(volume_t *vol, const char *ename)
53{
4542c20d
HJYP
54 for (vol->master_element=snd_mixer_first_elem(vol->mixer);vol->master_element;vol->master_element=snd_mixer_elem_next(vol->master_element)) {
55 snd_mixer_selem_get_id(vol->master_element, vol->sid);
56 if (!snd_mixer_selem_is_active(vol->master_element))
57 continue;
49db0a37 58
4542c20d
HJYP
59 if (strcmp(ename, snd_mixer_selem_id_get_name(vol->sid))==0) {
60 return TRUE;
61 }
62 }
49db0a37 63
4542c20d 64 return FALSE;
49db0a37
FC
65}
66
9864f98f 67static gboolean asound_init(volume_t *vol)
49db0a37 68{
4542c20d
HJYP
69 snd_mixer_selem_id_alloca(&vol->sid);
70 snd_mixer_open(&vol->mixer, 0);
71 snd_mixer_attach(vol->mixer, "default");
72 snd_mixer_selem_register(vol->mixer, NULL, NULL);
73 snd_mixer_load(vol->mixer);
74
75 /* Find Master element */
76 if (!find_element(vol, "Master"))
77 if (!find_element(vol, "Front"))
78 if (!find_element(vol, "PCM"))
9864f98f 79 return FALSE;
4542c20d
HJYP
80
81
82 snd_mixer_selem_get_playback_volume_range(vol->master_element, &vol->alsa_min_vol, &vol->alsa_max_vol);
83
84 snd_mixer_selem_set_playback_volume_range(vol->master_element, 0, 100);
9864f98f
FC
85
86 return TRUE;
49db0a37
FC
87}
88
89static int asound_read(volume_t *vol)
90{
4542c20d
HJYP
91 long aleft, aright;
92 snd_mixer_handle_events(vol->mixer);
93 /* Left */
94 snd_mixer_selem_get_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_LEFT, &aleft);
95 /* Right */
96 snd_mixer_selem_get_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_RIGHT, &aright);
97
98 return (aleft + aright) >> 1;
49db0a37
FC
99}
100
101static void asound_write(volume_t *vol, int volume)
102{
4542c20d
HJYP
103 snd_mixer_selem_set_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_LEFT, volume);
104 snd_mixer_selem_set_playback_volume(vol->master_element, SND_MIXER_SCHN_FRONT_RIGHT, volume);
49db0a37
FC
105}
106
107static gboolean focus_out_event(GtkWidget *widget, GdkEvent *event, volume_t *vol)
108{
4542c20d
HJYP
109 gtk_widget_hide(vol->dlg);
110 vol->show = 0;
111 return FALSE;
49db0a37
FC
112}
113
114static void tray_icon_press(GtkWidget *widget, GdkEvent *event, volume_t *vol)
115{
4542c20d
HJYP
116 if (vol->show==0) {
117 gtk_window_set_position(GTK_WINDOW(vol->dlg), GTK_WIN_POS_MOUSE);
118 gtk_scale_set_digits(GTK_SCALE(vol->vscale), asound_read(vol));
119 gtk_widget_show_all(vol->dlg);
120 vol->show = 1;
121 } else {
122 gtk_widget_hide(vol->dlg);
123 vol->show = 0;
124 }
49db0a37
FC
125}
126
127static void on_vscale_value_changed(GtkRange *range, volume_t *vol)
128{
4542c20d 129 asound_write(vol, gtk_range_get_value(range));
49db0a37
FC
130}
131
132static void click_mute(GtkWidget *widget, volume_t *vol)
133{
4542c20d
HJYP
134 int chn;
135
136 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
137 gtk_image_set_from_file(vol->tray_icon, ICONS_MUTE);
138 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
139 snd_mixer_selem_set_playback_switch(vol->master_element, chn, 0);
140 }
141 } else {
142 gtk_image_set_from_file(vol->tray_icon, ICONS_VOLUME);
143 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
144 snd_mixer_selem_set_playback_switch(vol->master_element, chn, 1);
145 }
146 }
49db0a37
FC
147}
148
22242ed4 149static void panel_init(Plugin *p)
49db0a37 150{
4542c20d
HJYP
151 volume_t *vol = p->priv;
152 GtkWidget *scrolledwindow;
153 GtkWidget *viewport;
154 GtkWidget *box;
155 GtkWidget *frame;
156 GtkWidget *checkbutton;
157
158 /* set show flags */
159 vol->show = 0;
160
161 /* create a new window */
162 vol->dlg = gtk_window_new(GTK_WINDOW_TOPLEVEL);
163 gtk_window_set_decorated(GTK_WINDOW(vol->dlg), FALSE);
63de7afd 164 gtk_container_set_border_width(GTK_CONTAINER(vol->dlg), 5);
4542c20d
HJYP
165 gtk_window_set_default_size(GTK_WINDOW(vol->dlg), 80, 140);
166 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(vol->dlg), TRUE);
167 gtk_window_set_skip_pager_hint(GTK_WINDOW(vol->dlg), TRUE);
168 gtk_window_set_type_hint(GTK_WINDOW(vol->dlg), GDK_WINDOW_TYPE_HINT_DIALOG);
169
170 /* setting background to default */
ddae3a55 171 //gtk_widget_set_style(vol->dlg, p->panel->defstyle);
4542c20d
HJYP
172
173 /* Focus-out signal */
174 g_signal_connect (G_OBJECT (vol->dlg), "focus_out_event",
175 G_CALLBACK (focus_out_event), vol);
176
177 scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
63de7afd 178 gtk_container_set_border_width (GTK_CONTAINER (scrolledwindow), 0);
4542c20d
HJYP
179 gtk_widget_show (scrolledwindow);
180 gtk_container_add (GTK_CONTAINER (vol->dlg), scrolledwindow);
181 GTK_WIDGET_UNSET_FLAGS (scrolledwindow, GTK_CAN_FOCUS);
182 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
63de7afd 183 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_SHADOW_NONE);
4542c20d
HJYP
184
185 viewport = gtk_viewport_new (NULL, NULL);
4542c20d
HJYP
186 gtk_container_add (GTK_CONTAINER (scrolledwindow), viewport);
187 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
188 gtk_widget_show(viewport);
189
190 /* create frame */
191 frame = gtk_frame_new(_("Volume"));
192 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
193 gtk_container_add(GTK_CONTAINER(viewport), frame);
194
195 /* create box */
196 box = gtk_vbox_new(FALSE, 0);
197
198 /* create controller */
199 vol->vscale = gtk_vscale_new(GTK_ADJUSTMENT(gtk_adjustment_new(asound_read(vol), 0, 100, 0, 0, 0)));
200 gtk_scale_set_draw_value(GTK_SCALE(vol->vscale), FALSE);
201 gtk_range_set_inverted(GTK_RANGE(vol->vscale), TRUE);
202
203 g_signal_connect ((gpointer) vol->vscale, "value_changed",
49db0a37
FC
204 G_CALLBACK (on_vscale_value_changed),
205 vol);
206
4542c20d
HJYP
207 checkbutton = gtk_check_button_new_with_label(_("Mute"));
208 snd_mixer_selem_get_playback_switch(vol->master_element, 0, &vol->mute);
49db0a37 209
4542c20d
HJYP
210 if (!vol->mute)
211 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), TRUE);
49db0a37 212
4542c20d 213 g_signal_connect ((gpointer) checkbutton, "toggled",
49db0a37
FC
214 G_CALLBACK (click_mute),
215 vol);
216
4542c20d
HJYP
217 gtk_box_pack_start(GTK_BOX(box), vol->vscale, TRUE, TRUE, 0);
218 gtk_box_pack_end(GTK_BOX(box), checkbutton, FALSE, FALSE, 0);
219 gtk_container_add(GTK_CONTAINER(frame), box);
49db0a37 220
4542c20d
HJYP
221 /* setting background to default */
222 gtk_widget_set_style(viewport, p->panel->defstyle);
49db0a37
FC
223}
224
225static void
22242ed4 226volumealsa_destructor(Plugin *p)
49db0a37 227{
4542c20d
HJYP
228 volume_t *vol = (volume_t *) p->priv;
229
230 ENTER;
231 if (vol->dlg)
232 gtk_widget_destroy(vol->dlg);
233 g_object_unref( vol->tooltips );
234
235 g_free(vol);
236 RET();
49db0a37
FC
237}
238
239static int
22242ed4 240volumealsa_constructor(Plugin *p, char **fp)
49db0a37 241{
4542c20d
HJYP
242 volume_t *vol;
243 line s;
244 GdkPixbuf *icon;
245 GtkWidget *image;
246 GtkIconTheme* theme;
247 GtkIconInfo* info;
248
249 ENTER;
250 s.len = 256;
251 vol = g_new0(volume_t, 1);
252 g_return_val_if_fail(vol != NULL, 0);
253 p->priv = vol;
254
255 /* initializing */
9864f98f
FC
256 if (!asound_init(vol))
257 RET(1);
258
4542c20d
HJYP
259 panel_init(p);
260
261 /* main */
262 vol->mainw = gtk_event_box_new();
263
264 gtk_widget_add_events(vol->mainw, GDK_BUTTON_PRESS_MASK);
265 gtk_widget_set_size_request( vol->mainw, 24, 24 );
266
267 g_signal_connect(G_OBJECT(vol->mainw), "button-press-event",
49db0a37
FC
268 G_CALLBACK(tray_icon_press), vol);
269
4542c20d
HJYP
270 /* tray icon */
271 snd_mixer_selem_get_playback_switch(vol->master_element, 0, &vol->mute);
272 if (vol->mute==0)
273 vol->tray_icon = gtk_image_new_from_file(ICONS_MUTE);
274 else
275 vol->tray_icon = gtk_image_new_from_file(ICONS_VOLUME);
49db0a37 276
4542c20d 277 gtk_container_add(GTK_CONTAINER(vol->mainw), vol->tray_icon);
1abc8a6a 278
4542c20d 279 gtk_widget_show_all(vol->mainw);
49db0a37 280
4542c20d 281 vol->tooltips = gtk_tooltips_new ();
49db0a37
FC
282#if GLIB_CHECK_VERSION( 2, 10, 0 )
283 g_object_ref_sink( vol->tooltips );
284#else
285 g_object_ref( vol->tooltips );
286 gtk_object_sink( vol->tooltips );
287#endif
288
4542c20d
HJYP
289 /* FIXME: display current level in tooltip. ex: "Volume Control: 80%" */
290 gtk_tooltips_set_tip (vol->tooltips, vol->mainw, _("Volume control"), NULL);
291
292 /* store the created plugin widget in plugin->pwid */
293 p->pwid = vol->mainw;
49db0a37 294
4542c20d 295 RET(1);
49db0a37
FC
296}
297
298
22242ed4 299PluginClass volumealsa_plugin_class = {
4542c20d
HJYP
300 fname: NULL,
301 count: 0,
302
303 type : "volumealsa",
304 name : N_("Volume Control"),
305 version: "1.0",
306 description : "Display and control volume for ALSA",
307
308 constructor : volumealsa_constructor,
309 destructor : volumealsa_destructor,
310 config : NULL,
311 save : NULL
49db0a37 312};