static char old_text[16];
static GtkWidget *win, *hbox1, *vbox1, *timebox, *namebox, *togglebutton1;
static time_t alarm_time;
-static char *run_cmd;
+static char *run_notify;
static char *default_name = "Tea";
static int expired;
+static int notify_pid = 0;
+static gint notify_watch = 0;
+static int kill_notify_by = 0;
static void
+kill_notify(void)
+{
+ if (kill_notify_by && notify_pid)
+ {
+ kill(notify_pid, kill_notify_by);
+ notify_pid = 0;
+ }
+ if (notify_watch)
+ {
+ g_source_remove(notify_watch);
+ notify_watch = 0;
+ }
+}
+
+static void
+quit(void)
+{
+ kill_notify();
+ gtk_main_quit();
+}
+
+static int // return pid of new process or 0 if failed
expand_and_exec(char *cmd)
{
GString *expanded_cmd = g_string_new("");
+ if (!expanded_cmd)
+ return 0;
for (int i=0; cmd[i]; i++)
{
if (cmd[i]=='%' && cmd[i+1]=='%')
}
GError *err = NULL;
- g_spawn_command_line_async(expanded_cmd->str, &err);
+ gint argc;
+ gchar ** argv = NULL;
+ int pid;
+
+ g_shell_parse_argv(expanded_cmd->str, &argc, &argv, &err);
g_string_free(expanded_cmd, 1);
if (err)
{
fprintf(stderr, "teatimer: Unable to run command: %s\n", err->message);
g_error_free(err);
+ return 0;
+ }
+ g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, &err);
+ g_strfreev(argv);
+ if (err)
+ {
+ fprintf(stderr, "teatimer: Unable to run command: %s\n", err->message);
+ g_error_free(err);
+ return 0;
}
+ return pid;
+}
+
+static void
+notify_exit(GPid pid, gint status UNUSED, gpointer data UNUSED)
+{
+ if (notify_pid == pid)
+ notify_pid = 0;
}
static void
it_tolls_for_thee(void)
{
- if (run_cmd)
+ if (run_notify)
{
if (!expired)
{
- expand_and_exec(run_cmd);
+ if(notify_watch)
+ g_source_remove(notify_watch);
+ notify_pid = expand_and_exec(run_notify);
+ notify_watch = g_child_watch_add(notify_pid, notify_exit, NULL);
expired = 1;
}
}
if (!strcmp(ev->string, "\r"))
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton1), !GTK_TOGGLE_BUTTON(togglebutton1)->active);
else if (!strcmp(ev->string, "\033"))
- gtk_main_quit();
+ quit();
return FALSE;
}
gtk_timeout_remove(second_timer);
second_timer = 0;
}
+ kill_notify();
gtk_entry_set_text(GTK_ENTRY(timebox), old_text);
gtk_entry_set_editable(GTK_ENTRY(timebox), 1);
gtk_widget_grab_focus(timebox);
static void
on_window_remove(GtkContainer *container UNUSED, GtkWidget *widget UNUSED, gpointer user_data UNUSED)
{
- gtk_main_quit();
+ quit();
}
static void
gtk_widget_show(win);
}
-static const char short_opts[] = "r:n:";
+static const char short_opts[] = "r:n:k:";
static const struct option long_opts[] = {
{ "run", required_argument, NULL, 'r' },
+ { "kill", required_argument, NULL, 'k' },
{ "timer-name", required_argument, NULL, 'n' },
{ NULL, 0, NULL, 0 },
};
-r, --run=<cmd>\t\tRun a given program when the tea is ready\n\
\t\t\t\t%%d will be expanded to timer name\n\
\t\t\t\t%%%% will be expanded to %%\n\
+-k, --kill=<int>\tKill run program by a given signal when the timer is stopped\n\
-n, --timer-name=<str>\tFill name box with <str>\n\
");
exit(1);
}
+void sig_handler(int signo)
+{
+ if (signo == SIGINT || signo == SIGTERM)
+ {
+ kill_notify();
+ exit(0);
+ }
+}
+
int
main(int argc, char **argv)
{
gtk_set_locale();
gtk_init(&argc, &argv);
+ signal(SIGINT, sig_handler);
+ signal(SIGTERM, sig_handler);
+
int opt;
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
switch (opt)
{
case 'r':
- run_cmd = optarg;
+ run_notify = optarg;
+ break;
+ case 'k':
+ kill_notify_by = atoi(optarg);
break;
case 'n':
default_name = optarg;