b45e60c9cf88fd62da740f7d9822534ebde368a1
[debian/lxpanel.git] / plugins / volume / volume.c
1 /**
2 * Copyright (c) 2006, 2008, 2014 LxDE Developers,
3 * see the file AUTHORS for details.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <gtk/gtk.h>
21 #include <stdlib.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24
25 #include <glib/gi18n.h>
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27
28 #include "plugin.h"
29
30 #include "dbg.h"
31
32 #include "volume-impl.h"
33
34 #include "volume_xpm.h"
35
36 int mixer_fd;
37
38 GtkSpinButton *vol_spin;
39 static gdouble vol_before_mute;
40 static gdouble curr_volume;
41 static GtkWidget *curr_image;
42 static gboolean skip_botton1_event;
43
44 typedef struct {
45 LXPanel *panel;
46 GtkWidget *dlg;
47 } volume_t;
48
49 static void
50 volume_destructor(gpointer user_data)
51 {
52 volume_t *vol = (volume_t *) user_data;
53
54 ENTER;
55 if (vol->dlg)
56 gtk_widget_destroy(vol->dlg);
57 if (mixer_fd)
58 close(mixer_fd);
59 g_free(vol);
60 RET();
61 }
62
63 static void update_icon (GtkWidget *p, volume_t *vol)
64 {
65 GdkPixbuf *icon;
66 GtkWidget *image;
67 GtkIconTheme* theme;
68 GtkIconInfo* info;
69 int icon_size;
70
71 theme = panel_get_icon_theme(vol->panel);
72 icon_size = panel_get_icon_size(vol->panel);
73
74 if (curr_volume <= 0) {
75 info = gtk_icon_theme_lookup_icon( theme, "stock_volume-mute", icon_size, 0 );
76 }
77 else if (curr_volume > 0 && curr_volume <= 50) {
78 info = gtk_icon_theme_lookup_icon( theme, "stock_volume-min", icon_size, 0 );
79 }
80 else if (curr_volume > 50 && curr_volume <= 75) {
81 info = gtk_icon_theme_lookup_icon( theme, "stock_volume-med", icon_size, 0 );
82 }
83 else /* curr_volume > 75 */ {
84 info = gtk_icon_theme_lookup_icon( theme, "stock_volume-max", icon_size, 0 );
85 }
86
87 if (info ) {
88 icon = gdk_pixbuf_new_from_file_at_size(
89 gtk_icon_info_get_filename( info ),
90 icon_size, icon_size, NULL );
91 gtk_icon_info_free( info );
92 }
93 else {
94 icon = gdk_pixbuf_new_from_xpm_data((const char **) volume_xpm);
95 }
96
97 if (icon) {
98 if (curr_image) {
99 gtk_widget_destroy(curr_image);
100 curr_image = NULL;
101 }
102 image = gtk_image_new_from_pixbuf(icon);
103 gtk_container_add(GTK_CONTAINER(p), image);
104
105 curr_image = image;
106 }
107 gtk_widget_show_all(p);
108 return;
109 }
110
111 static void on_volume_focus (GtkWidget* dlg, GdkEventFocus *event, GtkWidget *p)
112 {
113 volume_t *vol = lxpanel_plugin_get_data(p);
114
115 if (! vol_spin) return;
116 GtkAdjustment *vol_adjustment = gtk_spin_button_get_adjustment (vol_spin);
117 if (! vol_adjustment) return;
118 curr_volume = gtk_adjustment_get_value (vol_adjustment);
119
120 update_icon(p, vol);
121
122 /* FIXME: use smarter method */
123 gtk_widget_destroy( dlg );
124 vol->dlg = NULL;
125 }
126
127 static void on_mouse_scroll (GtkWidget* widget, GdkEventScroll* evt, volume_t *vol)
128 {
129 if ( ! vol->dlg ) {
130
131 vol->dlg = create_volume_window();
132 g_signal_connect( vol->dlg, "delete-event",
133 G_CALLBACK(on_volume_focus), widget );
134
135 }
136 else {
137 if (! vol_spin) return;
138 GtkAdjustment *vol_adjustment =
139 gtk_spin_button_get_adjustment (vol_spin);
140 if (! vol_adjustment) return;
141
142 curr_volume = gtk_adjustment_get_value (vol_adjustment);
143
144 if (evt->direction == GDK_SCROLL_UP) {
145 curr_volume += 2;
146 }
147 else /*if (evt->direction == GDK_SCROLL_DOWN)*/ {
148 curr_volume -= 2;
149 }
150 update_icon(widget, vol);
151 gtk_adjustment_set_value (vol_adjustment, curr_volume);
152 gtk_spin_button_set_adjustment(vol_spin, vol_adjustment);
153 skip_botton1_event = TRUE;
154 }
155 }
156
157 static gboolean on_button_press (GtkWidget* widget, GdkEventButton* evt, LXPanel* p)
158 {
159 volume_t *vol = lxpanel_plugin_get_data(widget);
160
161 /* for scroll correction */
162 if (skip_botton1_event) {
163 gtk_widget_destroy(vol->dlg);
164 vol->dlg = NULL;
165 skip_botton1_event = FALSE;
166 }
167
168 switch ( evt->button ) {
169 case 1: { /* Left click */
170 if ( ! vol->dlg ) {
171 vol->dlg = create_volume_window();
172
173 /* setting background to default */
174 gtk_widget_set_style(vol->dlg, panel_get_defstyle(p));
175
176 g_signal_connect( vol->dlg, "focus-out-event",
177 G_CALLBACK(on_volume_focus), widget );
178
179 gtk_window_present( GTK_WINDOW(vol->dlg) );
180 }
181 else {
182 /* update icon */
183 if (! vol_spin) return FALSE;
184 GtkAdjustment *vol_adjustment =
185 gtk_spin_button_get_adjustment (vol_spin);
186 if (! vol_adjustment) return FALSE;
187 curr_volume = gtk_adjustment_get_value (vol_adjustment);
188 update_icon(widget, vol);
189
190 gtk_widget_destroy(vol->dlg);
191 vol->dlg = NULL;
192 }
193 break;
194 }
195
196 case 2: { /* middle mouse button */
197 if ( ! vol->dlg ) {
198 vol->dlg = create_volume_window();
199 }
200
201 if (! vol_spin) return FALSE;
202 GtkAdjustment *vol_adjustment =
203 gtk_spin_button_get_adjustment (vol_spin);
204 if (! vol_adjustment) return FALSE;
205
206 curr_volume = gtk_adjustment_get_value (vol_adjustment);
207
208 if (curr_volume > 0) {
209 /* turning to mute */
210 vol_before_mute = curr_volume;
211 curr_volume = 0;
212 }
213 else {
214 curr_volume = vol_before_mute;
215 }
216
217 gtk_adjustment_set_value (vol_adjustment, curr_volume);
218 gtk_spin_button_set_adjustment(vol_spin, vol_adjustment);
219
220 update_icon(widget, vol);
221
222 gtk_widget_destroy( vol->dlg );
223 vol->dlg = NULL;
224 break;
225 }
226 default: /* never here */
227 break;
228 }
229 return FALSE;
230 }
231
232 static GtkWidget *volume_constructor(LXPanel *panel, config_setting_t *settings)
233 {
234 volume_t *vol;
235 GtkWidget *p;
236 GtkAdjustment *vol_adjustment;
237
238 vol_before_mute = 1;
239 curr_volume = 0;
240 curr_image = NULL;
241 skip_botton1_event = FALSE;
242
243 ENTER;
244 /* check if OSS mixer device could be open */
245 mixer_fd = open ("/dev/mixer", O_RDWR, 0);
246 if (mixer_fd < 0) {
247 RET(NULL);
248 }
249 /* try to obtain current volume */
250 p = create_volume_window(); /* use pointer */
251 if (! vol_spin)
252 goto _error;
253 vol_adjustment = gtk_spin_button_get_adjustment (vol_spin);
254 if (! vol_adjustment)
255 {
256 _error:
257 gtk_widget_destroy(p);
258 RET(NULL);
259 }
260 curr_volume = gtk_adjustment_get_value (vol_adjustment);
261
262 vol = g_new0(volume_t, 1);
263 vol->dlg = p; /* it was reused */
264
265 p = gtk_event_box_new();
266 lxpanel_plugin_set_data(p, vol, volume_destructor);
267 vol->panel = panel;
268
269 gtk_widget_add_events(p, GDK_BUTTON_PRESS_MASK);
270 g_signal_connect(p, "scroll-event", G_CALLBACK(on_mouse_scroll), vol);
271 gtk_widget_set_size_request(p, panel_get_icon_size(panel), panel_get_icon_size(panel));
272
273 update_icon(p, vol);
274 gtk_widget_destroy( vol->dlg );
275 vol->dlg = NULL;
276
277 /* FIXME: display current level in tooltip. ex: "Volume Control: 80%" */
278 gtk_widget_set_tooltip_text(p, _("Volume control"));
279
280 RET(p);
281 }
282
283 FM_DEFINE_MODULE(lxpanel_gtk, volume)
284
285 LXPanelPluginInit fm_module_init_lxpanel_gtk = {
286 .name = N_("Volume Control"),
287 .description = "Display and control volume",
288
289 .new_instance = volume_constructor,
290 .button_press_event = on_button_press
291 };