chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / sound.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #if HAVE_SOUND
26 #include "dialog.h"
27 #include "e16-ecore_list.h"
28 #include "emodule.h"
29 #include "settings.h"
30 #include "sound.h"
31 #include "sounds.h"
32
33 #if HAVE_SOUND_ESD
34 #define SOUND_SERVER_NAME "esd"
35 #elif HAVE_SOUND_PA
36 #define SOUND_SERVER_NAME "pulseaudio"
37 #else
38 #error Invalid sound configuration
39 #endif
40
41 #define N_SOUNDS (SOUND_NOT_USED - 1)
42
43 typedef struct {
44    char               *name;
45    char               *file;
46    Sample             *sample;
47 } SoundClass;
48
49 #define SC_NAME(sc) ((sc) ? (sc)->name : "(none)")
50
51 static struct {
52    char                enable;
53    char               *theme;
54    unsigned int        mask1, mask2;
55 } Conf_sound;
56
57 static struct {
58    char                cfg_loaded;
59    char               *theme_path;
60 } Mode_sound;
61
62 #define SOUND_THEME_PATH ((Mode_sound.theme_path) ? Mode_sound.theme_path : Mode.theme.path)
63
64 static Ecore_List  *sound_list = NULL;
65
66 #if USE_MODULES
67 static const SoundOps *ops = NULL;
68 #else
69 #if HAVE_SOUND_ESD
70 extern const SoundOps SoundOps_esd;
71 static const SoundOps *ops = &SoundOps_esd;
72 #elif HAVE_SOUND_PA
73 extern const SoundOps SoundOps_pa;
74 static const SoundOps *ops = &SoundOps_pa;
75 #endif
76 #endif
77
78 static void         _SoundConfigLoad(void);
79
80 static const char  *const sound_names[N_SOUNDS] = {
81    "SOUND_ALERT",               /*  0 0x00000001 */
82    "SOUND_BUTTON_CLICK",
83    "SOUND_BUTTON_RAISE",
84    "SOUND_DEICONIFY",
85    "SOUND_DESKTOP_LOWER",       /*  4 0x00000010 */
86    "SOUND_DESKTOP_RAISE",
87    "SOUND_DESKTOP_SHUT",
88    "SOUND_ERROR_IPC",
89    "SOUND_EXIT",                /*  8 0x00000100 */
90    "SOUND_FOCUS_SET",
91    "SOUND_ICONIFY",
92    "SOUND_INSERT_KEYS",
93    "SOUND_LOGOUT",              /* 12 0x00001000 */
94    "SOUND_LOWER",
95    "SOUND_MENU_SHOW",
96    "SOUND_MOVE_AREA_DOWN",
97    "SOUND_MOVE_AREA_LEFT",      /* 16 0x00010000 */
98    "SOUND_MOVE_AREA_RIGHT",
99    "SOUND_MOVE_AREA_UP",
100    "SOUND_MOVE_RESIST",
101    "SOUND_MOVE_START",          /* 20 0x00100000 */
102    "SOUND_MOVE_STOP",
103    "SOUND_RAISE",
104    "SOUND_RESIZE_START",
105    "SOUND_RESIZE_STOP",         /* 24 0x01000000 */
106    "SOUND_SCANNING",
107    "SOUND_SETTINGS_ACTIVE",
108    "SOUND_SETTINGS_ALL",
109    "SOUND_SETTINGS_AREA",       /* 28 0x10000000 */
110    "SOUND_SETTINGS_AUDIO",
111    "SOUND_SETTINGS_AUTORAISE",
112    "SOUND_SETTINGS_BG",
113    "SOUND_SETTINGS_COMPOSITE",  /*  0 0x00000001 */
114    "SOUND_SETTINGS_DESKTOPS",
115    "SOUND_SETTINGS_FOCUS",
116    "SOUND_SETTINGS_FX",
117    "SOUND_SETTINGS_GROUP",      /*  4 0x00000010 */
118    "SOUND_SETTINGS_ICONBOX",
119    "SOUND_SETTINGS_MENUS",
120    "SOUND_SETTINGS_MISCELLANEOUS",
121    "SOUND_SETTINGS_MOVERESIZE", /*  8 0x00000100 */
122    "SOUND_SETTINGS_PAGER",
123    "SOUND_SETTINGS_PLACEMENT",
124    "SOUND_SETTINGS_SESSION",
125    "SOUND_SETTINGS_TOOLTIPS",   /* 12 0x00001000 */
126    "SOUND_SETTINGS_TRANS",
127    "SOUND_SHADE",
128    "SOUND_SLIDEOUT_SHOW",
129    "SOUND_STARTUP",             /* 16 0x00010000 */
130    "SOUND_UNSHADE",
131    "SOUND_WAIT",
132    "SOUND_WINDOW_BORDER_CHANGE",
133    "SOUND_WINDOW_CHANGE_LAYER_DOWN",    /* 20 0x00100000 */
134    "SOUND_WINDOW_CHANGE_LAYER_UP",
135    "SOUND_WINDOW_CLOSE",
136    "SOUND_WINDOW_SLIDE",
137    "SOUND_WINDOW_SLIDE_END",    /* 24 0x01000000 */
138    "SOUND_WINDOW_STICK",
139    "SOUND_WINDOW_UNSTICK",
140 };
141
142 static void
143 _SclassSampleDestroy(void *data, void *user_data __UNUSED__)
144 {
145    SoundClass         *sclass = (SoundClass *) data;
146
147    if (!sclass || !sclass->sample)
148       return;
149
150    if (ops)
151       ops->SampleDestroy(sclass->sample);
152    sclass->sample = NULL;
153 }
154
155 static SoundClass  *
156 SclassCreate(const char *name, const char *file)
157 {
158    SoundClass         *sclass;
159
160    sclass = EMALLOC(SoundClass, 1);
161    if (!sclass)
162       return NULL;
163
164    if (!sound_list)
165       sound_list = ecore_list_new();
166    ecore_list_prepend(sound_list, sclass);
167
168    sclass->name = Estrdup(name);
169    sclass->file = Estrdup(file);
170    sclass->sample = NULL;
171
172    return sclass;
173 }
174
175 static void
176 SclassDestroy(SoundClass * sclass)
177 {
178    if (!sclass)
179       return;
180
181    ecore_list_node_remove(sound_list, sclass);
182    _SclassSampleDestroy(sclass, NULL);
183    Efree(sclass->name);
184    Efree(sclass->file);
185
186    Efree(sclass);
187 }
188
189 static void
190 _SclassDestroy(void *data, void *user_data __UNUSED__)
191 {
192    SclassDestroy((SoundClass *) data);
193 }
194
195 static void
196 SclassApply(SoundClass * sclass)
197 {
198    if (!sclass || !Conf_sound.enable)
199       return;
200
201    if (!sclass->sample)
202      {
203         char               *file;
204
205         file = FindFile(sclass->file, SOUND_THEME_PATH);
206         if (file)
207           {
208              sclass->sample = ops->SampleLoad(file);
209              Efree(file);
210           }
211         if (!sclass->sample)
212           {
213              DialogOK(_("Error finding sound file"),
214                       _("Warning!  Enlightenment was unable to load the\n"
215                         "following sound file:\n%s\n"
216                         "Enlightenment will continue to operate, but you\n"
217                         "may wish to check your configuration settings.\n"),
218                       sclass->file);
219              SclassDestroy(sclass);
220              return;
221           }
222      }
223
224    ops->SamplePlay(sclass->sample);
225 }
226
227 static int
228 _SclassMatchName(const void *data, const void *match)
229 {
230    return strcmp(((const SoundClass *)data)->name, (const char *)match);
231 }
232
233 static SoundClass  *
234 SclassFind(const char *name)
235 {
236    return (SoundClass *) ecore_list_find(sound_list, _SclassMatchName, name);
237 }
238
239 static void
240 _SoundPlayByName(const char *name)
241 {
242    SoundClass         *sclass;
243
244    if (!Conf_sound.enable)
245       return;
246
247    if (!name || !*name)
248       return;
249
250    sclass = SclassFind(name);
251
252    if (EDebug(EDBUG_TYPE_SOUND))
253       Eprintf("%s: %s file=%s\n", "SclassApply", name, SC_NAME(sclass));
254
255    SclassApply(sclass);
256 }
257
258 #define _SoundMasked(i) \
259     (((i) <= 32) ? Conf_sound.mask1 & (1 << ((i) - 1)) : \
260                    Conf_sound.mask2 & (1 << ((i) - 1)))
261 void
262 SoundPlay(int sound)
263 {
264    if (!Conf_sound.enable)
265       return;
266
267    if (sound <= 0 || sound > N_SOUNDS)
268       return;
269
270    if (_SoundMasked(sound))
271       return;
272
273    _SoundPlayByName(sound_names[sound - 1]);
274 }
275
276 static int
277 SoundFree(const char *name)
278 {
279    SoundClass         *sclass;
280
281    sclass = SclassFind(name);
282    SclassDestroy(sclass);
283
284    return sclass != NULL;
285 }
286
287 static void
288 SoundInit(void)
289 {
290    int                 err;
291
292    if (!Conf_sound.enable)
293       return;
294
295    err = -1;
296 #if USE_MODULES
297    if (!ops)
298 #if HAVE_SOUND_ESD
299       ops = ModLoadSym("sound", "SoundOps", "esd");
300 #elif HAVE_SOUND_PA
301       ops = ModLoadSym("sound", "SoundOps", "pa");
302 #endif
303 #endif
304    if (ops && ops->Init)
305       err = ops->Init();
306
307    if (err)
308      {
309         Conf_sound.enable = 0;
310         AlertX(_("Error initialising sound"), _("OK"), NULL, NULL,
311                _("Audio was enabled for Enlightenment but there was an error\n"
312                  "communicating with the audio server (%s).\n"
313                  "Audio will now be disabled.\n"), SOUND_SERVER_NAME);
314      }
315
316    _SoundConfigLoad();
317 }
318
319 static void
320 SoundExit(void)
321 {
322    ecore_list_for_each(sound_list, _SclassSampleDestroy, NULL);
323
324    if (ops)
325       ops->Exit();
326
327    Conf_sound.enable = 0;
328 }
329
330 /*
331  * Configuration load/save
332  */
333
334 static int
335 _SoundConfigParse(FILE * fs)
336 {
337    int                 err = 0;
338    SoundClass         *sc;
339    char                s[FILEPATH_LEN_MAX];
340    char                s1[FILEPATH_LEN_MAX];
341    char                s2[FILEPATH_LEN_MAX];
342    int                 i1, fields;
343
344    while (GetLine(s, sizeof(s), fs))
345      {
346         i1 = -1;
347         fields = sscanf(s, "%d", &i1);
348         if (fields == 1)        /* Just skip the numeric config stuff */
349            continue;
350
351         s1[0] = s2[0] = '\0';
352         fields = sscanf(s, "%4000s %4000s", s1, s2);
353         if (fields != 2)
354           {
355              Eprintf("*** Ignoring line: %s\n", s);
356              continue;
357           }
358         sc = SclassCreate(s1, s2);
359      }
360 #if 0                           /* Errors here are just ignored */
361    if (err)
362       ConfigAlertLoad("Sound");
363 #endif
364
365    return err;
366 }
367
368 static void
369 _SoundConfigLoad(void)
370 {
371    if (Mode_sound.cfg_loaded)
372       return;
373    Mode_sound.cfg_loaded = 1;
374
375    Efree(Mode_sound.theme_path);
376    if (Conf_sound.theme)
377       Mode_sound.theme_path = ThemeFind(Conf_sound.theme);
378    else
379       Mode_sound.theme_path = NULL;
380
381    ConfigFileLoad("sound.cfg", SOUND_THEME_PATH, _SoundConfigParse, 1);
382 }
383
384 static void
385 _SoundConfigUnload(void)
386 {
387    ecore_list_for_each(sound_list, _SclassDestroy, NULL);
388    Mode_sound.cfg_loaded = 0;
389 }
390
391 static void
392 _SoundThemeChange(void *item __UNUSED__, const char *theme)
393 {
394    if (*theme == '\0')
395       theme = NULL;
396    _SoundConfigUnload();
397    _EFDUP(Conf_sound.theme, theme);
398    _SoundConfigLoad();
399 }
400
401 /*
402  * Sound module
403  */
404
405 static void
406 SoundSighan(int sig, void *prm __UNUSED__)
407 {
408    switch (sig)
409      {
410      case ESIGNAL_INIT:
411         memset(&Mode_sound, 0, sizeof(Mode_sound));
412         break;
413      case ESIGNAL_CONFIGURE:
414         SoundInit();
415         break;
416      case ESIGNAL_START:
417         if (!Conf_sound.enable)
418            break;
419         SoundPlay(SOUND_STARTUP);
420         SoundFree("SOUND_STARTUP");
421         break;
422      case ESIGNAL_EXIT:
423 /*      if (Mode.wm.master) */
424         SoundExit();
425         break;
426      }
427 }
428
429 #if ENABLE_DIALOGS
430 /*
431  * Configuration dialog
432  */
433 static char         tmp_audio;
434
435 static void
436 CB_ConfigureAudio(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
437 {
438    if (val < 2)
439      {
440         Conf_sound.enable = tmp_audio;
441         if (Conf_sound.enable)
442            SoundInit();
443         else
444            SoundExit();
445      }
446    autosave();
447 }
448
449 static void
450 _DlgFillSound(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
451 {
452    DItem              *di;
453
454    tmp_audio = Conf_sound.enable;
455
456    DialogItemTableSetOptions(table, 2, 0, 0, 0);
457
458    di = DialogAddItem(table, DITEM_CHECKBUTTON);
459    DialogItemSetColSpan(di, 2);
460    DialogItemSetText(di, _("Enable sounds"));
461    DialogItemCheckButtonSetPtr(di, &tmp_audio);
462 }
463
464 const DialogDef     DlgSound = {
465    "CONFIGURE_AUDIO",
466    N_("Sound"),
467    N_("Audio Settings"),
468    SOUND_SETTINGS_AUDIO,
469    "pix/sound.png",
470    N_("Enlightenment Audio\n" "Settings Dialog\n"),
471    _DlgFillSound,
472    DLG_OAC, CB_ConfigureAudio,
473 };
474 #endif /* ENABLE_DIALOGS */
475
476 /*
477  * IPC functions
478  */
479
480 static void
481 SoundIpc(const char *params)
482 {
483    const char         *p;
484    char                cmd[128], prm[4096];
485    int                 len;
486    SoundClass         *sc;
487
488    cmd[0] = prm[0] = '\0';
489    p = params;
490    if (p)
491      {
492         len = 0;
493         sscanf(p, "%100s %4000s %n", cmd, prm, &len);
494         p += len;
495      }
496
497    if (!strncmp(cmd, "del", 3))
498      {
499         SoundFree(prm);
500      }
501    else if (!strncmp(cmd, "list", 2))
502      {
503         ECORE_LIST_FOR_EACH(sound_list, sc) IpcPrintf("%s\n", sc->name);
504      }
505    else if (!strncmp(cmd, "new", 3))
506      {
507         SclassCreate(prm, p);
508      }
509    else if (!strncmp(cmd, "off", 2))
510      {
511         SoundExit();
512         autosave();
513      }
514    else if (!strncmp(cmd, "on", 2))
515      {
516         Conf_sound.enable = 1;
517         SoundInit();
518         autosave();
519      }
520    else if (!strncmp(cmd, "play", 2))
521      {
522         _SoundPlayByName(prm);
523      }
524 }
525
526 static const IpcItem SoundIpcArray[] = {
527    {
528     SoundIpc,
529     "sound", "snd",
530     "Sound functions",
531     "  sound add <classname> <filename> Create soundclass\n"
532     "  sound del <classname>            Delete soundclass\n"
533     "  sound list                       Show all sounds\n"
534     "  sound off                        Disable sounds\n"
535     "  sound on                         Enable sounds\n"
536     "  sound play <classname>           Play sounds\n"}
537 };
538 #define N_IPC_FUNCS (sizeof(SoundIpcArray)/sizeof(IpcItem))
539
540 static const CfgItem SoundCfgItems[] = {
541    CFG_ITEM_BOOL(Conf_sound, enable, 0),
542    CFG_FUNC_STR(Conf_sound, theme, _SoundThemeChange),
543    CFG_ITEM_HEX(Conf_sound, mask1, 0),
544    CFG_ITEM_HEX(Conf_sound, mask2, 0),
545 };
546 #define N_CFG_ITEMS (sizeof(SoundCfgItems)/sizeof(CfgItem))
547
548 /*
549  * Module descriptor
550  */
551 extern const EModule ModSound;
552 const EModule       ModSound = {
553    "sound", "audio",
554    SoundSighan,
555    {N_IPC_FUNCS, SoundIpcArray},
556    {N_CFG_ITEMS, SoundCfgItems}
557 };
558
559 #endif /* HAVE_SOUND */