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