mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

src/mn-sound-player.gob (5922B) - raw

      1 /*
      2  * Mail Notification
      3  * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
      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 3 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 along
     16  * with this program; if not, write to the Free Software Foundation, Inc.,
     17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     18  */
     19 
     20 %h{
     21 #include <gtk/gtk.h>
     22 %}
     23 
     24 %{
     25 #include <sys/types.h>
     26 #include <signal.h>
     27 #include <glib/gi18n.h>
     28 #include <gnome.h>
     29 #include "mn-conf.h"
     30 #include "mn-locked-callback.h"
     31 #include "mn-util.h"
     32 %}
     33 
     34 class MN:Sound:Player from G:Object
     35 {
     36   private gboolean can_play;
     37   property BOOLEAN can_play (export)
     38     get
     39     {
     40       g_value_set_boolean(VAL, selfp->can_play);
     41     };
     42 
     43   property BOOLEAN can_stop (export)
     44     get
     45     {
     46       g_value_set_boolean(VAL, selfp->pid != 0);
     47     };
     48 
     49   private GPid pid;
     50   private GSource *watch_source;
     51 
     52   init (self)
     53   {
     54     selfp->can_play = self_can_play(self);
     55 
     56     mn_g_object_gconf_notification_add_gdk_locked(self,
     57 						  MN_CONF_SOUNDS_PLAY_COMMAND,
     58 						  self_notify_play_command_cb,
     59 						  self);
     60   }
     61 
     62   dispose (self)
     63   {
     64     /* do not emit notify::can-stop at finalization time (pass FALSE) */
     65     self_stop_real(self, FALSE);
     66   }
     67 
     68   private void
     69     notify_play_command_cb (GConfClient *client,
     70 			    unsigned int cnxn_id,
     71 			    GConfEntry *entry,
     72 			    gpointer user_data)
     73   {
     74     Self *self = user_data;
     75     gboolean can_play;
     76 
     77     can_play = self_can_play(self);
     78     if (can_play != selfp->can_play)
     79       {
     80 	selfp->can_play = can_play;
     81 	g_object_notify(G_OBJECT(self), "can-play");
     82       }
     83   }
     84 
     85   private gboolean
     86     can_play (self)
     87   {
     88     char *command;
     89     gboolean can;
     90 
     91     command = mn_conf_get_string(MN_CONF_SOUNDS_PLAY_COMMAND);
     92     can = command && *command;
     93     g_free(command);
     94 
     95     return can;
     96   }
     97 
     98   public void
     99     play (self, const char *filename (check null), GtkWindow *parent)
    100   {
    101     const char *error_message;
    102     char *command = NULL;
    103     char *subst_command;
    104     char *shell;
    105     char *argv[4];
    106     GError *err = NULL;
    107 
    108     g_return_if_fail(parent == NULL || GTK_IS_WINDOW(parent));
    109     g_return_if_fail(selfp->can_play);
    110 
    111     self_stop(self);
    112 
    113     /*
    114      * If a parent window is provided, we are normally called because
    115      * the user interacted with the UI. Otherwise, we are called because
    116      * a background event has occurred, and for clarity we should
    117      * mention the originating application in the error message.
    118      */
    119     if (parent)
    120       error_message = N_("Unable to play sound");
    121     else
    122       error_message = N_("A command error has occurred in Mail Notification");
    123 
    124     command = mn_conf_get_string(MN_CONF_SOUNDS_PLAY_COMMAND);
    125     if (! command || ! *command)
    126       goto end;
    127 
    128     subst_command = mn_subst_command(command, self_subst_cb, (gpointer) filename, &err);
    129     if (! subst_command)
    130       {
    131 	mn_show_error_dialog(parent,
    132 			     _(error_message),
    133 			     _("Unable to execute \"%s\": %s."),
    134 			     command,
    135 			     err->message);
    136 	g_error_free(err);
    137 	goto end;
    138       }
    139 
    140     /*
    141      * We cannot use gnome_execute_child() because it does not reap the
    142      * child, which is needed for GChildWatch.
    143      */
    144 
    145     shell = gnome_util_user_shell();
    146 
    147     argv[0] = shell;
    148     argv[1] = "-c";
    149     argv[2] = subst_command;
    150     argv[3] = NULL;
    151 
    152     if (g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &selfp->pid, &err))
    153       {
    154 	GSource *source;
    155 
    156 	g_object_notify(G_OBJECT(self), "can-stop");
    157 
    158 	g_assert(selfp->watch_source == NULL);
    159 
    160 	source = g_child_watch_source_new(selfp->pid);
    161 	g_source_set_callback(source, (GSourceFunc) self_watch_cb, self, NULL);
    162 	g_source_attach(source, NULL);
    163 	selfp->watch_source = source;
    164       }
    165     else
    166       {
    167 	mn_show_error_dialog(parent,
    168 			     _(error_message),
    169 			     _("Unable to execute \"%s\": %s."),
    170 			     command,
    171 			     err->message);
    172 	g_error_free(err);
    173       }
    174 
    175     g_free(subst_command);
    176     g_free(shell);
    177 
    178   end:
    179     g_free(command);
    180   }
    181 
    182   private gboolean
    183     subst_cb (const char *name, char **value, gpointer data)
    184   {
    185     const char *filename = data;
    186 
    187     if (! strcmp(name, "file"))
    188       {
    189 	*value = g_strdup(filename);
    190 	return TRUE;
    191       }
    192 
    193     return FALSE;
    194   }
    195 
    196   public void
    197     stop (self)
    198   {
    199     self_stop_real(self, TRUE);
    200   }
    201 
    202   private void
    203     stop_real (self, gboolean notify_can_stop)
    204   {
    205     /*
    206      * If we remove the watch, a zombie will be left since GLib will
    207      * not have waited for the process, so we just abandon the watch.
    208      */
    209     if (selfp->watch_source)
    210       {
    211 	g_source_set_callback(selfp->watch_source, (GSourceFunc) self_watch_abandoned_cb, NULL, NULL);
    212 	g_source_unref(selfp->watch_source);
    213 	selfp->watch_source = NULL;
    214       }
    215 
    216     if (selfp->pid)
    217       {
    218 	kill(selfp->pid, SIGTERM);
    219 	selfp->pid = 0;
    220 	if (notify_can_stop)
    221 	  g_object_notify(G_OBJECT(self), "can-stop");
    222       }
    223   }
    224 
    225   private void
    226     watch_abandoned_cb (GPid pid, int status, gpointer data)
    227   {
    228     /* no op */
    229   }
    230 
    231   private void
    232     watch_cb (GPid pid, int status, gpointer data)
    233   {
    234     Self *self = data;
    235 
    236     GDK_THREADS_ENTER();
    237 
    238     g_assert(selfp->watch_source != NULL);
    239 
    240     g_source_unref(selfp->watch_source);
    241     selfp->watch_source = NULL;
    242     selfp->pid = 0;
    243 
    244     g_object_notify(G_OBJECT(self), "can-stop");
    245 
    246     /* do not call gdk_flush(), we're normally in the main thread */
    247     GDK_THREADS_LEAVE();
    248   }
    249 
    250   public MNSoundPlayer *
    251     new (void)
    252   {
    253     return GET_NEW;
    254   }
    255 }