]> mj.ucw.cz Git - teatimer.git/blob - teatimer.c
881a8c0fbf2e9c37666b42fd6a36102229ad64c8
[teatimer.git] / teatimer.c
1 /*
2  *      Trivial Tea Timer
3  *
4  *      (c) 2002, 2010 Martin Mares <mj@ucw.cz>
5  *
6  *      GPL'ed
7  */
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <time.h>
13 #include <getopt.h>
14
15 #include <glib.h>
16 #include <gtk/gtk.h>
17
18 #define UNUSED __attribute__((unused))
19
20 static guint second_timer;
21 static char old_text[8];
22 static GtkWidget *win, *hbox1, *timebox, *togglebutton1;
23 static time_t alarm_time;
24 static char *run_cmd;
25 static int expired;
26
27 static void
28 it_tolls_for_thee(void)
29 {
30   if (run_cmd)
31     {
32       if (!expired)
33         {
34           GError *err = NULL;
35           g_spawn_command_line_async(run_cmd, &err);
36           if (err)
37             {
38               fprintf(stderr, "teatimer: Unable to run command: %s\n", err->message);
39               g_error_free(err);
40             }
41           expired = 1;
42         }
43     }
44   else
45     gdk_beep();
46 }
47
48 static gint
49 on_second_timeout(gpointer data UNUSED)
50 {
51   char buf[8];
52   time_t now = time(NULL);
53   int delta = alarm_time - now;
54   char *sign = "";
55
56   if (delta < 0)
57     {
58       sign = "-";
59       delta = -delta;
60     }
61   if (delta >= 6000)
62     delta = 5999;
63   sprintf(buf, "%s%02d:%02d", sign, delta/60, delta%60);
64   gtk_entry_set_text(GTK_ENTRY(timebox), buf);
65   if (now >= alarm_time)
66     it_tolls_for_thee();
67   else
68     expired = 0;
69   return 1;
70 }
71
72 static gint
73 on_timebox_key(GtkWidget *widget UNUSED, GdkEventKey *ev, gpointer user_data UNUSED)
74 {
75   if (!strcmp(ev->string, "\r"))
76     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton1), !GTK_TOGGLE_BUTTON(togglebutton1)->active);
77   else if (!strcmp(ev->string, "\033"))
78     gtk_main_quit();
79   return FALSE;
80 }
81
82 static int
83 parse_time(char *c)
84 {
85   int t = 0;
86
87   while (*c && *c != ':')
88     {
89       if (*c >= '0' && *c <= '9')
90         t = 10*t + *c++ - '0';
91       else
92         return -1;
93     }
94   if (*c)
95     {
96       int m = 0;
97       c++;
98       while (*c)
99         {
100           if (*c >= '0' && *c <= '9')
101             m = 10*m + *c++ - '0';
102           else
103             return -1;
104         }
105       t = 60*t + m;
106     }
107   if (t >= 6000)
108     return -1;
109   return t;
110 }
111
112 static void
113 on_togglebutton1_toggled(GtkToggleButton *togglebutton, gpointer user_data UNUSED)
114 {
115   if (togglebutton->active)
116     {
117       int t;
118       strcpy(old_text, gtk_entry_get_text(GTK_ENTRY(timebox)));
119       t = parse_time(old_text);
120       if (t < 0)
121         {
122           gtk_toggle_button_set_active(togglebutton, 0);
123           return;
124         }
125       alarm_time = time(NULL) + t;
126       gtk_entry_set_editable(GTK_ENTRY(timebox), 0);
127       on_second_timeout(NULL);
128       second_timer = gtk_timeout_add(1000, on_second_timeout, NULL);
129     }
130   else
131     {
132       if (second_timer)
133         {
134           gtk_timeout_remove(second_timer);
135           second_timer = 0;
136         }
137       gtk_entry_set_text(GTK_ENTRY(timebox), old_text);
138       gtk_entry_set_editable(GTK_ENTRY(timebox), 1);
139       gtk_widget_grab_focus (timebox);
140     }
141 }
142
143 static void
144 on_window_remove(GtkContainer *container UNUSED, GtkWidget *widget UNUSED, gpointer user_data UNUSED)
145 {
146   gtk_main_quit();
147 }
148
149 static void
150 open_window(void)
151 {
152   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
153   gtk_window_set_title (GTK_WINDOW (win), "Tea Timer");
154   gtk_window_set_policy (GTK_WINDOW (win), TRUE, TRUE, TRUE);
155
156   hbox1 = gtk_hbox_new (FALSE, 0);
157   gtk_widget_show (hbox1);
158   gtk_container_add (GTK_CONTAINER (win), hbox1);
159
160   timebox = gtk_entry_new_with_max_length (6);
161   gtk_widget_show (timebox);
162   gtk_box_pack_start (GTK_BOX (hbox1), timebox, TRUE, TRUE, 0);
163   gtk_entry_set_text (GTK_ENTRY (timebox), "00:00");
164
165   togglebutton1 = gtk_toggle_button_new_with_label ("Run");
166   gtk_widget_show (togglebutton1);
167   gtk_box_pack_start (GTK_BOX (hbox1), togglebutton1, FALSE, FALSE, 0);
168
169   gtk_signal_connect(GTK_OBJECT (win), "remove", GTK_SIGNAL_FUNC (on_window_remove), NULL);
170   gtk_signal_connect(GTK_OBJECT (timebox), "key_press_event", GTK_SIGNAL_FUNC (on_timebox_key), NULL);
171   gtk_signal_connect(GTK_OBJECT (togglebutton1), "toggled", GTK_SIGNAL_FUNC (on_togglebutton1_toggled), NULL);
172
173   gtk_widget_grab_focus (timebox);
174
175   gtk_widget_show(win);
176 }
177
178 static const char short_opts[] = "r:";
179
180 static const struct option long_opts[] = {
181   { "run",              required_argument,      NULL,   'r' },
182   { NULL,               0,                      NULL,   0   },
183 };
184
185 static void
186 usage(void)
187 {
188   fprintf(stderr, "Usage: teatimer [<options>] [<mm:ss>]\n\n\
189 Options:\n\
190 -r, --run=<cmd>\t\tRun a given program when the tea is ready\n\
191 ");
192   exit(1);
193 }
194
195 int
196 main(int argc, char **argv)
197 {
198   gtk_set_locale();
199   gtk_init(&argc, &argv);
200
201   int opt;
202   while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
203     switch (opt)
204       {
205       case 'r':
206         run_cmd = optarg;
207         break;
208       default:
209         usage();
210       }
211   if (optind != argc && optind+1 != argc)
212     usage();
213
214   open_window();
215   if (optind < argc)
216     {
217       gtk_entry_set_text(GTK_ENTRY(timebox), argv[optind]);
218       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton1), 1);
219     }
220   gtk_main();
221   return 0;
222 }