1 //##############################################################################
2 // volumeicon
3 //
4 // config.c - a singleton providing configuration values/functions
5 //
6 // Copyright 2011 Maato
7 //
8 // Authors:
9 //    Maato <maato@softwarebakery.com>
10 //
11 // This program is free software: you can redistribute it and/or modify it
12 // under the terms of the GNU General Public License version 3, as published
13 // by the Free Software Foundation.
14 //
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranties of
17 // MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
18 // PURPOSE.  See the GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License along
21 // with this program.  If not, see <http://www.gnu.org/licenses/>.
22 //##############################################################################
23 
24 #include <glib/gstdio.h>
25 #include <assert.h>
26 
27 #include "config.h"
28 
29 //##############################################################################
30 // Definitions
31 //##############################################################################
32 #define CONFIG_DIRNAME "volumeicon"
33 #define CONFIG_FILENAME "volumeicon"
34 
35 //##############################################################################
36 // Static variables
37 //##############################################################################
38 static struct config {
39     gchar *path;
40 
41     // Alsa
42     gchar *card; // TODO: Rename this to device.
43     gchar *channel;
44 
45     // Notifications
46     gboolean show_notification;
47     gint notification_type;
48 
49     // Status icon
50     int stepsize; // TODO: Rename this to volume_stepsize.
51     gchar *helper_program;
52     gchar *theme;
53     gboolean use_panel_specific_icons;
54 
55     // Left mouse button action
56     gboolean lmb_slider;
57 
58     // Middle mouse button action
59     gboolean mmb_mute;
60 
61     // Layout
62     gboolean use_horizontal_slider;
63     gboolean show_sound_level;
64     gboolean use_transparent_background;
65 
66     // Hotkeys
67     gboolean hotkey_up_enabled;
68     gboolean hotkey_down_enabled;
69     gboolean hotkey_mute_enabled;
70     gchar *hotkey_up;
71     gchar *hotkey_down;
72     gchar *hotkey_mute;
73 } m_config = {
74     .path = NULL,
75 
76     // Alsa
77     .card = NULL,
78     .channel = NULL,
79 
80     // Notifications
81     .show_notification = TRUE,
82     .notification_type = 0,
83 
84     // Status icon
85     .stepsize = 0,
86     .helper_program = NULL,
87     .theme = NULL,
88     .use_panel_specific_icons = FALSE,
89 
90     // Left mouse button action
91     .lmb_slider = FALSE,
92 
93     // Middle mouse button action
94     .mmb_mute = FALSE,
95 
96     // Layout
97     .use_horizontal_slider = FALSE,
98     .show_sound_level = FALSE,
99     .use_transparent_background = FALSE,
100 
101     // Hotkeys
102     .hotkey_up_enabled = FALSE,
103     .hotkey_down_enabled = FALSE,
104     .hotkey_mute_enabled = FALSE,
105     .hotkey_up = NULL,
106     .hotkey_down = NULL,
107     .hotkey_mute = NULL
108 };
109 
110 //##############################################################################
111 // Static functions
112 //##############################################################################
config_load_default(void)113 static void config_load_default(void)
114 {
115     if(!m_config.helper_program)
116         config_set_helper(DEFAULT_MIXERAPP);
117     if(!m_config.channel)
118         config_set_channel(NULL);
119     if(!m_config.card)
120         config_set_card("default");
121     if(!m_config.stepsize)
122         config_set_stepsize(5);
123     if(!m_config.theme)
124         config_set_theme("Default");
125     if(!m_config.hotkey_up)
126         config_set_hotkey_up("XF86AudioRaiseVolume");
127     if(!m_config.hotkey_down)
128         config_set_hotkey_down("XF86AudioLowerVolume");
129     if(!m_config.hotkey_mute)
130         config_set_hotkey_mute("XF86AudioMute");
131 }
132 
config_read(void)133 static void config_read(void)
134 {
135     // Clean up previously loaded configuration values
136     m_config.stepsize = 0;
137     g_free(m_config.helper_program);
138     g_free(m_config.channel);
139     g_free(m_config.theme);
140 
141     // Load keys from keyfile
142     GKeyFile *kf = g_key_file_new();
143     g_key_file_load_from_file(kf, m_config.path, G_KEY_FILE_NONE, NULL);
144 
145 #define GET_VALUE(type, section, key) \
146     g_key_file_get_##type(kf, section, key, NULL)
147 #define GET_STRING(s, k) GET_VALUE(value, s, k)
148 #define GET_BOOL(s, k) GET_VALUE(boolean, s, k)
149 #define GET_INT(s, k) GET_VALUE(integer, s, k)
150 
151     // Alsa
152     m_config.card = GET_STRING("Alsa", "card");
153     m_config.channel = GET_STRING("Alsa", "channel");
154 
155     // Notifications
156     m_config.show_notification = GET_BOOL("Notification", "show_notification");
157     m_config.notification_type = GET_INT("Notification", "notification_type");
158 
159     // Status icon
160     m_config.stepsize = GET_INT("StatusIcon", "stepsize");
161     m_config.helper_program = GET_STRING("StatusIcon", "onclick");
162     m_config.theme = GET_STRING("StatusIcon", "theme");
163     m_config.use_panel_specific_icons = GET_BOOL(
164         "StatusIcon", "use_panel_specific_icons");
165 
166     // Left mouse button action
167     m_config.lmb_slider = GET_BOOL("StatusIcon", "lmb_slider");
168 
169     // Middle mouse button action
170     m_config.mmb_mute = GET_BOOL("StatusIcon", "mmb_mute");
171 
172     // Layout
173     m_config.use_horizontal_slider = GET_BOOL(
174         "StatusIcon", "use_horizontal_slider");
175     m_config.show_sound_level = GET_BOOL("StatusIcon", "show_sound_level");
176     m_config.use_transparent_background = GET_BOOL(
177         "StatusIcon", "use_transparent_background");
178 
179     // Hotkeys
180     m_config.hotkey_up_enabled = GET_BOOL("Hotkeys", "up_enabled");
181     m_config.hotkey_down_enabled = GET_BOOL("Hotkeys", "down_enabled");
182     m_config.hotkey_mute_enabled = GET_BOOL("Hotkeys", "mute_enabled");
183     m_config.hotkey_up = GET_STRING("Hotkeys", "up");
184     m_config.hotkey_down = GET_STRING("Hotkeys", "down");
185     m_config.hotkey_mute = GET_STRING("Hotkeys", "mute");
186 
187     g_key_file_free(kf);
188 
189 #undef GET_VALUE
190 #undef GET_STRING
191 #undef GET_BOOL
192 #undef GET_INT
193 
194     // Load default values for unset keys
195     config_load_default();
196 }
197 
198 //##############################################################################
199 // Exported setter functions
200 //##############################################################################
201 
202 // Alsa
config_set_card(const gchar * card)203 void config_set_card(const gchar *card)
204 {
205     g_free(m_config.card);
206     m_config.card = g_strdup(card);
207 }
208 
config_set_channel(const gchar * channel)209 void config_set_channel(const gchar *channel)
210 {
211     g_free(m_config.channel);
212     m_config.channel = g_strdup(channel);
213 }
214 
215 // Notifications
config_set_show_notification(gboolean active)216 void config_set_show_notification(gboolean active)
217 {
218     m_config.show_notification = active;
219 }
220 
config_set_notification_type(gint type)221 void config_set_notification_type(gint type)
222 {
223     m_config.notification_type = type;
224 }
225 
226 // Status icon
config_set_stepsize(int stepsize)227 void config_set_stepsize(int stepsize)
228 {
229     m_config.stepsize = stepsize;
230 }
231 
config_set_helper(const gchar * helper)232 void config_set_helper(const gchar *helper)
233 {
234     g_free(m_config.helper_program);
235     m_config.helper_program = g_strdup(helper);
236 }
237 
config_set_theme(const gchar * theme)238 void config_set_theme(const gchar *theme)
239 {
240     g_free(m_config.theme);
241     m_config.theme = g_strdup(theme);
242 }
243 
config_set_use_panel_specific_icons(gboolean active)244 void config_set_use_panel_specific_icons(gboolean active)
245 {
246     m_config.use_panel_specific_icons = active;
247 }
248 
249 // Left mouse button action
config_set_left_mouse_slider(gboolean active)250 void config_set_left_mouse_slider(gboolean active)
251 {
252     m_config.lmb_slider = active;
253 }
254 
255 // Middle mouse button action
config_set_middle_mouse_mute(gboolean active)256 void config_set_middle_mouse_mute(gboolean active)
257 {
258     m_config.mmb_mute = active;
259 }
260 
261 // Layout
config_set_use_horizontal_slider(gboolean active)262 void config_set_use_horizontal_slider(gboolean active)
263 {
264     m_config.use_horizontal_slider = active;
265 }
266 
config_set_show_sound_level(gboolean active)267 void config_set_show_sound_level(gboolean active)
268 {
269     m_config.show_sound_level = active;
270 }
271 
config_set_use_transparent_background(gboolean active)272 void config_set_use_transparent_background(gboolean active)
273 {
274     m_config.use_transparent_background = active;
275 }
276 
277 // Hotkey
config_set_hotkey_up_enabled(gboolean enabled)278 void config_set_hotkey_up_enabled(gboolean enabled)
279 {
280     m_config.hotkey_up_enabled = enabled;
281 }
282 
config_set_hotkey_down_enabled(gboolean enabled)283 void config_set_hotkey_down_enabled(gboolean enabled)
284 {
285     m_config.hotkey_down_enabled = enabled;
286 }
287 
config_set_hotkey_mute_enabled(gboolean enabled)288 void config_set_hotkey_mute_enabled(gboolean enabled)
289 {
290     m_config.hotkey_mute_enabled = enabled;
291 }
292 
config_set_hotkey_up(const gchar * up)293 void config_set_hotkey_up(const gchar *up)
294 {
295     g_free(m_config.hotkey_up);
296     m_config.hotkey_up = g_strdup(up);
297 }
298 
config_set_hotkey_down(const gchar * down)299 void config_set_hotkey_down(const gchar *down)
300 {
301     g_free(m_config.hotkey_down);
302     m_config.hotkey_down = g_strdup(down);
303 }
304 
config_set_hotkey_mute(const gchar * mute)305 void config_set_hotkey_mute(const gchar *mute)
306 {
307     g_free(m_config.hotkey_mute);
308     m_config.hotkey_mute = g_strdup(mute);
309 }
310 
311 //##############################################################################
312 // Exported getter functions
313 //##############################################################################
314 
315 // Alsa
config_get_card(void)316 const gchar *config_get_card(void)
317 {
318     return m_config.card;
319 }
320 
config_get_channel(void)321 const gchar *config_get_channel(void)
322 {
323     return m_config.channel;
324 }
325 
326 // Notifications
config_get_show_notification(void)327 gboolean config_get_show_notification(void)
328 {
329     return m_config.show_notification;
330 }
331 
config_get_notification_type(void)332 gint config_get_notification_type(void)
333 {
334     return m_config.notification_type;
335 }
336 
337 // Status icon
config_get_stepsize(void)338 int config_get_stepsize(void)
339 {
340     return m_config.stepsize;
341 }
342 
config_get_helper(void)343 const gchar *config_get_helper(void)
344 {
345     return m_config.helper_program;
346 }
347 
config_get_theme(void)348 const gchar *config_get_theme(void)
349 {
350     return m_config.theme;
351 }
352 
config_get_use_gtk_theme(void)353 gboolean config_get_use_gtk_theme(void)
354 {
355     return g_strcmp0(m_config.theme, "Default") == 0 ? TRUE : FALSE;
356 }
357 
config_get_use_panel_specific_icons(void)358 gboolean config_get_use_panel_specific_icons(void)
359 {
360     return m_config.use_panel_specific_icons;
361 }
362 
363 // Left mouse button action
config_get_left_mouse_slider(void)364 gboolean config_get_left_mouse_slider(void)
365 {
366     return m_config.lmb_slider;
367 }
368 
369 // Middle mouse button action
config_get_middle_mouse_mute(void)370 gboolean config_get_middle_mouse_mute(void)
371 {
372     return m_config.mmb_mute;
373 }
374 
375 // Layout
config_get_use_horizontal_slider(void)376 gboolean config_get_use_horizontal_slider(void)
377 {
378     return m_config.use_horizontal_slider;
379 }
380 
config_get_show_sound_level(void)381 gboolean config_get_show_sound_level(void)
382 {
383     return m_config.show_sound_level;
384 }
385 
config_get_use_transparent_background(void)386 gboolean config_get_use_transparent_background(void)
387 {
388     return m_config.use_transparent_background;
389 }
390 
391 // Hotkeys
config_get_hotkey_up_enabled(void)392 gboolean config_get_hotkey_up_enabled(void)
393 {
394     return m_config.hotkey_up_enabled;
395 }
396 
config_get_hotkey_down_enabled(void)397 gboolean config_get_hotkey_down_enabled(void)
398 {
399     return m_config.hotkey_down_enabled;
400 }
401 
config_get_hotkey_mute_enabled(void)402 gboolean config_get_hotkey_mute_enabled(void)
403 {
404     return m_config.hotkey_mute_enabled;
405 }
406 
config_get_hotkey_up(void)407 const gchar *config_get_hotkey_up(void)
408 {
409     return m_config.hotkey_up;
410 }
411 
config_get_hotkey_down(void)412 const gchar *config_get_hotkey_down(void)
413 {
414     return m_config.hotkey_down;
415 }
416 
config_get_hotkey_mute(void)417 const gchar *config_get_hotkey_mute(void)
418 {
419     return m_config.hotkey_mute;
420 }
421 
422 //##############################################################################
423 // Exported miscellaneous functions
424 //##############################################################################
config_write(void)425 void config_write(void)
426 {
427     assert(m_config.path);
428 
429     GKeyFile *kf = g_key_file_new();
430 
431 #define SET_VALUE(type, section, key, value) \
432     g_key_file_set_##type(kf, section, key, value)
433 #define SET_STRING(s, k, v) SET_VALUE(value, s, k, v)
434 #define SET_BOOL(s, k, v) SET_VALUE(boolean, s, k, v)
435 #define SET_INT(s, k, v) SET_VALUE(integer, s, k, v)
436 
437     // Alsa
438     if(m_config.card)
439         SET_STRING("Alsa", "card", m_config.card);
440     if(m_config.channel)
441         SET_STRING("Alsa", "channel", m_config.channel);
442 
443     // Notifications
444     SET_BOOL("Notification", "show_notification", m_config.show_notification);
445     SET_INT("Notification", "notification_type", m_config.notification_type);
446 
447     // Status icon
448     SET_INT("StatusIcon", "stepsize", m_config.stepsize);
449     if(m_config.helper_program)
450         SET_STRING("StatusIcon", "onclick", m_config.helper_program);
451     if(m_config.theme)
452         SET_STRING("StatusIcon", "theme", m_config.theme);
453     SET_BOOL("StatusIcon", "use_panel_specific_icons",
454              m_config.use_panel_specific_icons);
455 
456     // Left mouse button action
457     SET_BOOL("StatusIcon", "lmb_slider", m_config.lmb_slider);
458 
459     // Middle mouse button action
460     SET_BOOL("StatusIcon", "mmb_mute", m_config.mmb_mute);
461 
462     // Layout
463     SET_BOOL("StatusIcon", "use_horizontal_slider",
464              m_config.use_horizontal_slider);
465     SET_BOOL("StatusIcon", "show_sound_level", m_config.show_sound_level);
466     SET_BOOL("StatusIcon", "use_transparent_background",
467              m_config.use_transparent_background);
468 
469     // Hotkeys
470     SET_BOOL("Hotkeys", "up_enabled", m_config.hotkey_up_enabled);
471     SET_BOOL("Hotkeys", "down_enabled", m_config.hotkey_down_enabled);
472     SET_BOOL("Hotkeys", "mute_enabled", m_config.hotkey_mute_enabled);
473     if(m_config.hotkey_up)
474         SET_STRING("Hotkeys", "up", m_config.hotkey_up);
475     if(m_config.hotkey_down)
476         SET_STRING("Hotkeys", "down", m_config.hotkey_down);
477     if(m_config.hotkey_mute)
478         SET_STRING("Hotkeys", "mute", m_config.hotkey_mute);
479 
480     gchar *data = g_key_file_to_data(kf, NULL, NULL);
481     g_key_file_free(kf);
482     g_file_set_contents(m_config.path, data, -1, NULL);
483     g_free(data);
484 
485 #undef SET_VALUE
486 #undef SET_STRING
487 #undef SET_BOOL
488 #undef SET_INT
489 }
490 
config_initialize(gchar * config_name)491 void config_initialize(gchar *config_name)
492 {
493     // Build config directory name
494     gchar *config_dir = g_build_filename(g_get_user_config_dir(),
495         CONFIG_DIRNAME, NULL);
496     m_config.path = g_build_filename(
497         config_dir, config_name ? config_name : CONFIG_FILENAME, NULL);
498 
499     // Make sure config directory exists
500     if(!g_file_test(config_dir, G_FILE_TEST_IS_DIR))
501         g_mkdir(config_dir, 0777);
502 
503     // If a config file doesn't exist, create one with defaults otherwise
504     // read the existing one.
505     if(!g_file_test(m_config.path, G_FILE_TEST_EXISTS))
506     {
507         config_load_default();
508         config_write();
509     }
510     else
511     {
512         config_read();
513     }
514 
515     g_free(config_dir);
516 }
517 
518