source: trunk/third/libgnome/libgnome/gnome-triggers.c @ 18320

Revision 18320, 14.7 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18319, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (C) 1997, 1998 Elliot Lee
3 * All rights reserved.
4 *
5 * This file is part of the Gnome Library.
6 *
7 * The Gnome Library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * The Gnome Library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
19 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22/*
23  @NOTATION@
24 */
25
26#include "config.h"
27
28/* By Elliot Lee */
29
30#include "gnome-triggers.h"
31#include "gnome-triggersP.h"
32#include "gnome-config.h"
33#include "gnome-util.h"
34#include "gnome-sound.h"
35#ifdef HAVE_ESD
36 #include <esd.h>
37#endif
38
39#include <unistd.h>
40#include <stdio.h>
41#include <string.h>
42#include <stdlib.h>
43#include <stdarg.h>
44#include <glib.h>
45#include <sys/types.h>
46#include <sys/wait.h>
47#include <dirent.h>
48
49/* TYPE DECLARATIONS */
50
51typedef void (*GnomeTriggerTypeFunction)(GnomeTrigger *t, char *msg, char *level, char *supinfo[]);
52
53/* PROTOTYPES */
54static GnomeTrigger* gnome_trigger_dup(GnomeTrigger *dupme);
55static GnomeTriggerList* gnome_triggerlist_new(char *nodename);
56static void gnome_triggerlist_free(GnomeTriggerList* t);
57static void gnome_trigger_free(GnomeTrigger* t);
58static void gnome_trigger_do(GnomeTrigger* t, const char *msg, const char *level,
59                             const char *supinfo[]);
60static void gnome_trigger_do_function(GnomeTrigger* t,
61                                      const char *msg, const char *level,
62                                      const char *supinfo[]);
63static void gnome_trigger_do_command(GnomeTrigger* t,
64                                     const char *msg, const char *level,
65                                     const char *supinfo[]);
66static void gnome_trigger_do_mediaplay(GnomeTrigger* t,
67                                       const char *msg,
68                                       const char *level,
69                                       const char *supinfo[]);
70
71/* FILEWIDE VARIABLES */
72
73static GnomeTriggerList* gnome_triggerlist_topnode = NULL;
74
75static const GnomeTriggerTypeFunction actiontypes[] =
76/* This list should have entries for all the trigger types in
77   gnome-triggers.h */
78{
79  (GnomeTriggerTypeFunction)NULL,
80  (GnomeTriggerTypeFunction)gnome_trigger_do_function,
81  (GnomeTriggerTypeFunction)gnome_trigger_do_command,
82  (GnomeTriggerTypeFunction)gnome_trigger_do_mediaplay,
83  (GnomeTriggerTypeFunction)NULL
84};
85
86/* IMPLEMENTATIONS */
87#if 0
88void
89gnome_triggers_init(void)
90{
91}
92#endif
93
94#if 0
95/* snarfed almost directly from sound-properties. */
96static gint
97gnome_triggers_read_path(const char *config_path)
98{
99  DIR *dirh;
100  char *sample_name, *sample_file, *ctmp;
101  gpointer event_iter;
102  struct dirent *dent;
103  GnomeTrigger nt;
104  GString *tmpstr;
105
106  nt.type = GTRIG_MEDIAPLAY;
107  nt.level = NULL;
108  nt.u.media.cache_id = -1;
109
110  dirh = opendir(config_path);
111  if(!dirh)
112    return -1;
113
114  tmpstr = g_string_new(NULL);
115
116  while((dent = readdir(dirh))) {
117    /* ignore no-good dir entries.
118       We ignore "gnome" because the system sounds are listed in there.
119    */
120    if (!strcmp(dent->d_name, ".")
121        || !strcmp(dent->d_name, "..")
122        || !strcmp(dent->d_name, "gnome")
123        || !strcmp(dent->d_name, "gnome.soundlist"))
124      continue;
125
126    g_string_printf(tmpstr, "=%s/%s=", config_path, dent->d_name);
127
128    gnome_config_push_prefix(tmpstr->str);
129
130    event_iter = gnome_config_init_iterator_sections(tmpstr->str);
131    while((event_iter = gnome_config_iterator_next(event_iter,
132                                                   &sample_name, NULL))) {
133      if(!strcmp(sample_name, "__section_info__"))
134        goto continue_loop;
135
136      g_string_printf(tmpstr, "%s/file", sample_name);
137      sample_file = gnome_config_get_string(tmpstr->str);
138
139      if(!sample_file || !*sample_file) {
140        g_free(sample_name);
141        continue;
142      }
143
144      if(*sample_file != '/') {
145        char *tmp = gnome_sound_file(sample_file);
146        g_free(sample_file);
147        sample_file = tmp;
148      }
149
150      ctmp = g_strdup(dent->d_name);
151      if(strstr(ctmp, ".soundlist"))
152        *strstr(ctmp, ".soundlist") = '\0';
153
154      nt.u.media.file = sample_file;
155      gnome_triggers_add_trigger(&nt, ctmp, sample_name, NULL);
156      g_print ("Added: %s\n", ctmp);
157     
158      g_free(ctmp);
159
160    continue_loop:
161      g_free(sample_name);
162    }
163
164    gnome_config_pop_prefix();
165  }
166  closedir(dirh);
167
168  g_string_free(tmpstr, TRUE);
169
170  return 0;
171}
172/**
173 * gnome_triggers_readfile:
174 * @infilename: A file listing triggers to install in the currently
175 * running program.
176 *
177 * The file should be of the format:
178 *
179 *    level section type params
180 *
181 * Where 'level' indicates the message severity at which this trigger
182 * should be activated, 'section' is a colon-separated list indicating
183 * which part of the "message classification tree" this trigger will
184 * be activated for, 'type' is either "command" (run the command
185 * specified in 'params') or 'play' (play the esd sound sample named
186 * 'params').
187 *
188 * Returns 0 on success.  1 otherwise.
189 *
190 */
191static gint
192gnome_triggers_readfile(const char *infilename)
193{
194  GnomeTrigger* nt;
195  char aline[512];
196  char **subnames = NULL;
197  char **parts = NULL;
198  FILE *infile;
199  int i;
200
201  infile = fopen(infilename, "r");
202  if(infile == NULL)
203    return 1;
204
205  nt = gnome_trigger_dup(NULL);
206  while(fgets(aline, sizeof(aline), infile)) {
207    i = strlen(aline) - 1;
208    while(isspace(aline[i])) aline[i--] = '\0';
209
210    if(aline[0] == '\0' || aline[0] == '#')
211      continue;
212
213    parts = g_strsplit(aline, " ", 4);
214    if(!parts || !parts[0] || !parts[1] || !parts[2] || !parts[3]) {
215      g_strfreev(parts);
216      g_warning("Invalid triggers line \'%s\'\n", aline);
217      continue;
218    }
219
220    if(!strcmp(parts[1], "NULL")) {
221      subnames = g_malloc(sizeof(gchar *));
222      subnames[0] = NULL;
223    } else
224      subnames = g_strsplit(parts[1], ":", -1);
225
226    if(!strcmp(parts[2], "command"))
227      nt->type = GTRIG_COMMAND;
228    else if(!strcmp(parts[2], "play"))
229      nt->type = GTRIG_MEDIAPLAY;
230    nt->u.command = parts[3];
231    if(!strcmp(parts[0], "NULL"))
232      nt->level = NULL;
233    else
234      nt->level = parts[0];
235    gnome_triggers_vadd_trigger(nt, subnames);
236
237    g_strfreev(subnames);
238    g_strfreev(parts);
239  }
240  g_free(nt);
241  fclose(infile);
242
243  return 0;
244}
245#endif
246
247/**
248 * gnome_triggers_add_trigger:
249 * @nt: Information on the new trigger to be added.
250 * @...: The section to add the trigger under.
251 *
252 * Adds a new #GnomeTrigger instance to the event hierarchy.
253 */
254void gnome_triggers_add_trigger(GnomeTrigger* nt, ...)
255{
256  va_list l;
257  gint nstrings, i;
258  gchar **strings;
259 
260  /* Count number of strings */
261 
262  va_start(l, nt);
263  for (nstrings = 0; va_arg(l, gchar *); nstrings++);
264  va_end(l);
265 
266  /* Build list */
267 
268  strings = g_new(gchar *, nstrings + 1);
269 
270  va_start(l, nt);
271 
272  for (i = 0; i < nstrings; i++)
273    strings[i] = va_arg(l, gchar *);
274  strings[i] = NULL;
275 
276  va_end(l);
277 
278  /* And pass them to the real function */
279 
280  gnome_triggers_vadd_trigger(nt, strings);
281
282  g_free (strings);
283}
284
285static GnomeTrigger*
286gnome_trigger_dup(GnomeTrigger* dupme)
287{
288  GnomeTrigger* retval;
289  retval = g_malloc(sizeof(struct _GnomeTrigger));
290  if(dupme) {
291    *retval = *dupme;
292    if(dupme->level)
293      retval->level = g_strdup(dupme->level);
294    else
295      retval->level = NULL;
296    switch(retval->type) {
297    case GTRIG_COMMAND:
298      retval->u.command = g_strdup(dupme->u.command);
299      break;
300    default:
301      break;
302    }
303  } else {
304    retval->level = NULL;
305    retval->type = GTRIG_NONE;
306    memset(&retval->u, 0, sizeof(retval->u));
307  }
308  return retval;
309}
310
311static GnomeTriggerList*
312gnome_triggerlist_new(char *nodename)
313{
314  GnomeTriggerList* retval;
315  retval = g_malloc0(sizeof(GnomeTriggerList));
316  retval->nodename = g_strdup(nodename);
317  return retval;
318}
319
320/**
321 * gnome_triggers_vadd_trigger:
322 * @nt: Information on the new trigger to be added.
323 * @supinfo: The section to add the trigger under.
324 *
325 * This does the same as gnome_triggers_add_trigger(), except the section is
326 * stored in the %NULL terminated array @supinfo instead of as a variable
327 * length argument list.
328 */
329void gnome_triggers_vadd_trigger(GnomeTrigger* nt,
330                                 char *supinfo[])
331{
332  g_return_if_fail(nt != NULL);
333  if(!gnome_triggerlist_topnode)
334    gnome_triggerlist_topnode = gnome_triggerlist_new(NULL);
335
336  if(supinfo == NULL || supinfo[0] == NULL) {
337    gnome_triggerlist_topnode->actions = g_realloc(gnome_triggerlist_topnode->actions, ++gnome_triggerlist_topnode->numactions);
338    gnome_triggerlist_topnode->actions[gnome_triggerlist_topnode->numactions - 1] = gnome_trigger_dup(nt);
339  } else {
340    int i, j;
341    GnomeTriggerList* curnode;
342
343    for(i = 0, curnode = gnome_triggerlist_topnode;
344        supinfo[i]; i++) {
345      for(j = 0;
346          j < curnode->numsubtrees
347            && strcmp(curnode->subtrees[j]->nodename, supinfo[i]);
348          j++) /* Do nothing */ ;
349
350      if(j < curnode->numsubtrees) {
351        curnode = curnode->subtrees[j];
352      } else {
353        curnode->subtrees = g_realloc(curnode->subtrees,
354                                      ++curnode->numsubtrees
355                                      * sizeof(GnomeTriggerList*));
356        curnode->subtrees[curnode->numsubtrees - 1] =
357          gnome_triggerlist_new(supinfo[i]);
358        curnode = curnode->subtrees[curnode->numsubtrees - 1];
359      } /* end for j */
360    } /* end for i */
361
362    curnode->actions = g_realloc(curnode->actions,
363                                 ++curnode->numactions
364                                 * sizeof(GnomeTrigger));
365    curnode->actions[curnode->numactions - 1] = gnome_trigger_dup(nt);
366  } /* end if */
367}
368
369/**
370 * gnome_triggers_do:
371 * @msg: The human-readable message describing the event (can be %NULL).
372 * @level: The level of severity of the event, or %NULL.
373 * @...: The classification of the event.
374 *
375 * Notifies GNOME about an event happening, so that any appropriate handlers
376 * can be run.
377 */
378void
379gnome_triggers_do(const char *msg, const char *level, ...)
380{
381  va_list l;
382  gint nstrings, i;
383  gchar **strings;
384 
385  /* Count number of strings */
386  va_start(l, level);
387  for (nstrings = 0; va_arg(l, gchar *); nstrings++);
388  va_end(l);
389 
390  /* Build list */
391 
392  strings = g_new (gchar *, nstrings + 1);
393 
394  va_start(l, level);
395 
396  for (i = 0; i < nstrings; i++)
397    strings[i] = va_arg(l, gchar *);
398  strings[i] = NULL;
399 
400  va_end(l);
401 
402  /* And pass them to the real function */
403 
404  gnome_triggers_vdo(msg, level, (const char **)strings);
405
406  g_free (strings);
407}
408
409/* The "add one to the sample ID" is because sample ID's start at 0,
410   and we need a way to distinguish between "not found in sound_ids"
411   and "sample #0" */
412static void
413gnome_triggers_play_sound(const char *sndname)
414{
415#ifdef HAVE_ESD
416  int sid;
417  static GHashTable *sound_ids = NULL;
418
419  if(gnome_sound_connection_get () < 0) return;
420
421  if(!sound_ids)
422    sound_ids = g_hash_table_new(g_str_hash, g_str_equal);
423
424  sid = GPOINTER_TO_INT(g_hash_table_lookup(sound_ids, sndname));
425
426  if(!sid) {
427    sid = esd_sample_getid(gnome_sound_connection_get (), sndname);
428    if(sid >= 0) sid++;
429    g_hash_table_insert(sound_ids, g_strdup(sndname), GINT_TO_POINTER(sid));
430  }
431
432  if(sid < 0) return;
433  sid--;
434  esd_sample_play(gnome_sound_connection_get (), sid);
435#endif
436  /* If there's no esound, this is just a no-op */
437}
438
439/**
440 * gnome_triggers_vdo:
441 * @msg: The human-readable message describing the event (can be %NULL).
442 * @level: The level of severity of the event, or %NULL.
443 * @supinfo: The classification of the event (%NULL terminated array).
444 *
445 * Notifies GNOME about an event happening, so that any appropriate handlers
446 * can be run. This does the same as gnome_trigger_do() except that it takes a
447 * %NULL terminated array instead of a varargs list.
448 */
449void
450gnome_triggers_vdo(const char *msg, const char *level, const char *supinfo[])
451{
452  GnomeTriggerList* curnode = gnome_triggerlist_topnode;
453  int i, j;
454  char buf[256], *ctmp;
455
456  if(level) {
457    g_snprintf(buf, sizeof(buf), "gnome/%s", level);
458    gnome_triggers_play_sound(buf);
459  }
460
461  if(!supinfo)
462    return;
463
464  ctmp = g_strjoinv("/", (char **)supinfo);
465  gnome_triggers_play_sound(ctmp);
466  g_free(ctmp);
467
468  for(i = 0; curnode && supinfo[i]; i++)
469    {
470
471    for(j = 0; j < curnode->numactions; j++)
472      {
473        if(!curnode->actions[j]->level
474           || !level
475           || !strcmp(level, curnode->actions[j]->level))
476          gnome_trigger_do(curnode->actions[j], msg, level, supinfo);
477      }
478   
479    for(j = 0;
480        j < curnode->numsubtrees
481          && strcmp(curnode->subtrees[j]->nodename,supinfo[i]);
482        j++)
483      /* Do nothing */ ;
484    if(j < curnode->numsubtrees)
485      curnode = curnode->subtrees[j];
486    else
487      curnode = NULL;
488  }
489  if(curnode)
490    {
491      for(j = 0; j < curnode->numactions; j++)
492        {
493          if(!curnode->actions[j]->level
494             || !level
495             || !strcmp(level, curnode->actions[j]->level))
496            gnome_trigger_do(curnode->actions[j], msg, level, supinfo);
497        }
498    }
499}
500
501static void
502gnome_trigger_free(GnomeTrigger* t)
503{
504  if(t->level)
505    g_free(t->level);
506  switch(t->type) {
507  case GTRIG_COMMAND:
508    g_free(t->u.command); break;
509  case GTRIG_MEDIAPLAY:
510    g_free(t->u.media.file); break;
511  default:
512    break;
513  }
514  g_free(t);
515}
516
517static void
518gnome_triggerlist_free(GnomeTriggerList* t)
519{
520  int i;
521
522  g_free(t->nodename);
523
524  for(i = 0; i < t->numsubtrees; i++) {
525    gnome_triggerlist_free(t->subtrees[i]);
526  }
527  g_free(t->subtrees);
528
529  for(i = 0; i < t->numactions; i++) {
530    gnome_trigger_free(t->actions[i]);
531  }
532  g_free(t->actions);
533
534  g_free(t);
535}
536
537static void
538gnome_trigger_do(GnomeTrigger* t,
539                 const char *msg,
540                 const char * level,
541                 const char *supinfo[])
542{
543  g_return_if_fail(t != NULL);
544
545  actiontypes[t->type](t, (char *)msg, (char *)level, (char **)supinfo);
546}
547
548static void
549gnome_trigger_do_function(GnomeTrigger* t,
550                          const char *msg,
551                          const char *level,
552                          const char *supinfo[])
553{
554  t->u.function((char *)msg, (char *)level, (char **)supinfo);
555}
556
557static void
558gnome_trigger_do_command(GnomeTrigger* t,
559                         const char *msg,
560                         const char *level,
561                         const char *supinfo[])
562{
563  char **argv;
564  int nsupinfos, i;
565
566  for(nsupinfos = 0; supinfo[nsupinfos]; nsupinfos++);
567
568  argv = g_malloc(sizeof(char *) * (nsupinfos + 4));
569  argv[0] = (char *)t->u.command;
570  argv[1] = (char *)msg;
571  argv[2] = (char *)level;
572
573  for(i = 0; supinfo[i]; i++) {
574    argv[i + 3] = (char *)supinfo[i];
575  }
576  argv[i + 3] = NULL;
577
578  /* We're all set, let's do it */
579  {
580    pid_t childpid;
581    int status;
582    childpid = fork();
583    if(childpid)
584      waitpid(childpid, &status, 0);
585    else
586      execv(t->u.command, argv);
587  }
588 
589  g_free(argv);
590}
591
592static void
593gnome_trigger_do_mediaplay(GnomeTrigger* t,
594                           const char *msg,
595                           const char *level,
596                           const char *supinfo[])
597{
598#if defined(HAVE_ESD)
599  if(gnome_sound_connection_get () == -1)
600    return;
601
602  if(t->u.media.cache_id >= 0)
603    esd_sample_play(gnome_sound_connection_get (), t->u.media.cache_id);
604  else if(t->u.media.cache_id == -1)
605    gnome_sound_play(t->u.media.file);
606#endif
607}
608
Note: See TracBrowser for help on using the repository browser.