1 /*
2  * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <glib.h>
21 #include <glib-object.h>
22 #include <gmodule.h>
23 
24 #include "matemixer.h"
25 #include "matemixer-private.h"
26 #include "matemixer-backend-module.h"
27 
28 /**
29  * SECTION:matemixer
30  * @short_description: Library initialization and support functions
31  * @include: libmatemixer/matemixer.h
32  * @see_also: #MateMixerContext
33  *
34  * The libmatemixer library must be initialized before it is used by an
35  * application. The initialization function loads dynamic modules which provide
36  * access to sound systems (also called backends) and it only succeeds if there
37  * is at least one usable module present on the target system.
38  *
39  * To connect to a sound system and access the mixer functionality after the
40  * library is initialized, create a #MateMixerContext using the
41  * mate_mixer_context_new() function.
42  */
43 
44 static void       load_modules     (void);
45 static gint       compare_modules  (gconstpointer a,
46                                     gconstpointer b);
47 
48 static GList     *modules = NULL;
49 static gboolean   initialized = FALSE;
50 
51 /**
52  * mate_mixer_init:
53  *
54  * Initializes the library. You must call this function before using any other
55  * function from the library.
56  *
57  * Returns: %TRUE on success or %FALSE if the library installation does not
58  * provide support for any sound system backends.
59  */
60 gboolean
mate_mixer_init(void)61 mate_mixer_init (void)
62 {
63     if (initialized == TRUE)
64         return TRUE;
65 
66     load_modules ();
67 
68     if (modules != NULL) {
69         GList *list = modules;
70 
71         while (list != NULL) {
72             GTypeModule *module = G_TYPE_MODULE (list->data);
73             GList       *next = list->next;
74 
75             /* Load the plugin and remove it from the list if it fails */
76             if (g_type_module_use (module) == FALSE) {
77                 g_object_unref (module);
78                 modules = g_list_delete_link (modules, list);
79             }
80             list = next;
81         }
82 
83         if (modules != NULL) {
84             /* Sort the usable modules by priority */
85             modules = g_list_sort (modules, compare_modules);
86             initialized = TRUE;
87         } else
88             g_critical ("No usable backend modules have been found");
89     } else
90         g_critical ("No backend modules have been found");
91 
92     return initialized;
93 }
94 
95 /**
96  * mate_mixer_is_initialized:
97  *
98  * Returns %TRUE if the library has been initialized.
99  *
100  * Returns: %TRUE or %FALSE.
101  */
102 gboolean
mate_mixer_is_initialized(void)103 mate_mixer_is_initialized (void)
104 {
105     return initialized;
106 }
107 
108 /**
109  * _mate_mixer_list_modules:
110  *
111  * Gets a list of loaded backend modules.
112  *
113  * Returns: a #GList.
114  */
115 const GList *
_mate_mixer_list_modules(void)116 _mate_mixer_list_modules (void)
117 {
118     return (const GList *) modules;
119 }
120 
121 /**
122  * _mate_mixer_create_channel_mask:
123  * @positions: an array of channel positions
124  * @n: number of channel positions in the array
125  *
126  * Creates a channel mask using the given list of channel positions.
127  *
128  * Returns: a channel mask.
129  */
130 guint32
_mate_mixer_create_channel_mask(MateMixerChannelPosition * positions,guint n)131 _mate_mixer_create_channel_mask (MateMixerChannelPosition *positions, guint n)
132 {
133     guint32 mask = 0;
134     guint   i = 0;
135 
136     for (i = 0; i < n; i++) {
137         if (positions[i] > MATE_MIXER_CHANNEL_UNKNOWN &&
138             positions[i] < MATE_MIXER_CHANNEL_MAX)
139             mask |= 1 << positions[i];
140     }
141     return mask;
142 }
143 
144 static void
load_modules(void)145 load_modules (void)
146 {
147     static gboolean loaded = FALSE;
148 
149     if (loaded == TRUE)
150         return;
151 
152     if (G_LIKELY (g_module_supported () == TRUE)) {
153         GDir   *dir;
154         GError *error = NULL;
155 
156         /* Read the directory which contains module libraries and create a list
157          * of those that are likely to be usable backend modules */
158         dir = g_dir_open (LIBMATEMIXER_BACKEND_DIR, 0, &error);
159         if (dir != NULL) {
160             const gchar *name;
161 
162             while ((name = g_dir_read_name (dir)) != NULL) {
163                 gchar *file;
164 
165                 if (g_str_has_suffix (name, "." G_MODULE_SUFFIX) == FALSE)
166                     continue;
167 
168                 file = g_build_filename (LIBMATEMIXER_BACKEND_DIR, name, NULL);
169                 modules = g_list_prepend (modules,
170                                           mate_mixer_backend_module_new (file));
171                 g_free (file);
172             }
173 
174             g_dir_close (dir);
175         } else {
176             g_critical ("%s", error->message);
177             g_error_free (error);
178         }
179     } else {
180         g_critical ("Unable to load backend modules: Not supported");
181     }
182 
183     loaded = TRUE;
184 }
185 
186 /* Backend modules sorting function, higher priority number means higher priority
187  * of the backend module */
188 static gint
compare_modules(gconstpointer a,gconstpointer b)189 compare_modules (gconstpointer a, gconstpointer b)
190 {
191     const MateMixerBackendInfo *info1, *info2;
192 
193     info1 = mate_mixer_backend_module_get_info (MATE_MIXER_BACKEND_MODULE (a));
194     info2 = mate_mixer_backend_module_get_info (MATE_MIXER_BACKEND_MODULE (b));
195 
196     return info2->priority - info1->priority;
197 }
198