interactive: kill i9ep->line_handler_running.
[paraslash.git] / fade.c
1 /*
2  * Copyright (C) 1998-2012 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file fade.c A volume fader and alarm clock for OSS. */
8
9 #include <regex.h>
10
11 #include "fade.cmdline.h"
12 #include "para.h"
13 #include "fd.h"
14 #include "string.h"
15 #include "mix.h"
16 #include "error.h"
17 #include "version.h"
18
19 INIT_FADE_ERRLISTS;
20 static struct fade_args_info conf;
21
22 enum mixer_id {MIXER_ENUM};
23 static char *mixer_name[] = {MIXER_NAMES};
24 DECLARE_MIXER_INITS;
25 static struct mixer supported_mixer[] = {MIXER_ARRAY};
26 #define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
27
28 static int loglevel;
29 __printf_2_3 void date_log(int ll, const char *fmt, ...)
30 {
31         va_list argp;
32         time_t t1;
33         struct tm *tm;
34
35         if (ll < loglevel)
36                 return;
37         time(&t1);
38         tm = localtime(&t1);
39         printf("%d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec);
40         va_start(argp, fmt);
41         vprintf(fmt, argp);
42         va_end(argp);
43 }
44 __printf_2_3 void (*para_log)(int, const char*, ...) = date_log;
45
46 /* Fade to new volume in fade_time seconds. */
47 static int fade(struct mixer *m, struct mixer_handle *h, int new_vol, int fade_time)
48 {
49         int vol, diff, incr, ret;
50         unsigned secs;
51         struct timespec ts;
52         unsigned long long tmp, tmp2; /* Careful with that axe, Eugene! */
53
54         if (fade_time <= 0)
55                 return m->set(h, new_vol);
56         secs = fade_time;
57         PARA_NOTICE_LOG("fading to %d in %d seconds\n", new_vol, secs);
58         ret = m->get(h);
59         if (ret < 0)
60                 goto out;
61         vol = ret;
62         diff = new_vol - vol;
63         if (!diff) {
64                 sleep(secs);
65                 goto out;
66         }
67         incr = diff > 0? 1: -1;
68         diff = diff > 0? diff: -diff;
69         tmp = secs * 1000 / diff;
70         tmp2 = tmp % 1000;
71         while ((new_vol - vol) * incr > 0) {
72                 ts.tv_nsec = tmp2 * 1000000; /* really nec ?*/
73                 ts.tv_sec = tmp / 1000; /* really nec ?*/
74                 //printf("ts.tv_sec: %i\n", ts.tv_nsec);
75                 vol += incr;
76                 ret = m->set(h, vol);
77                 if (ret < 0)
78                         goto out;
79                 //printf("vol = %i\n", vol);
80                 nanosleep(&ts, NULL);
81         }
82 out:
83         return ret;
84 }
85
86 static void client_cmd(const char *cmd)
87 {
88         int ret, status, fds[3] = {0, 0, 0};
89         pid_t pid;
90         char *cmdline = make_message(BINDIR "/para_client %s", cmd);
91
92         PARA_NOTICE_LOG("%s\n", cmdline);
93         ret = para_exec_cmdline_pid(&pid, cmdline, fds);
94         free(cmdline);
95         if (ret < 0) {
96                 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
97                 goto fail;
98         }
99         do
100                 pid = waitpid(pid, &status, 0);
101         while (pid == -1 && errno == EINTR);
102         if (pid < 0) {
103                 PARA_ERROR_LOG("%s\n", strerror(errno));
104                 goto fail;
105         }
106         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
107                 goto fail;
108         return;
109 fail:
110         PARA_EMERG_LOG("command \"%s\" failed\n", cmd);
111         exit(EXIT_FAILURE);
112 }
113
114 static void change_afs_mode_and_play(char *afs_mode)
115 {
116         char *cmd;
117
118         client_cmd("stop");
119         if (!afs_mode)
120                 return;
121         cmd = make_message("select %s", afs_mode);
122         client_cmd(cmd);
123         free(cmd);
124         client_cmd("play");
125 }
126
127 static int sweet_dreams(struct mixer *m, struct mixer_handle *h)
128 {
129         time_t t1, wake_time_epoch;
130         unsigned int delay;
131         struct tm *tm;
132         int ret, min = conf.wake_min_arg;
133         char *fo_mood = conf.fo_mood_arg;
134         char *fi_mood = conf.fi_mood_arg;
135         char *sleep_mood = conf.sleep_mood_arg;
136         int fit = conf.fi_time_arg;
137         int fot = conf.fo_time_arg;
138         int fiv = conf.fi_vol_arg;
139         int fov = conf.fo_vol_arg;
140         int iv = conf.ivol_arg;
141
142         /* calculate wake time */
143         time(&t1);
144         if (conf.wake_hour_given) {
145                 int hour = conf.wake_hour_arg;
146                 tm = localtime(&t1);
147                 if (tm->tm_hour > hour || (tm->tm_hour == hour && tm->tm_min> min)) {
148                         t1 += 86400; /* wake time is tomorrow */
149                         tm = localtime(&t1);
150                 }
151                 tm->tm_hour = hour;
152                 tm->tm_min = min;
153                 tm->tm_sec = 0;
154         } else {
155                 t1 += 9 * 60 * 60; /* nine hours from now */
156                 PARA_INFO_LOG("default wake time: %lu\n", (long unsigned)t1);
157                 tm = localtime(&t1);
158         }
159         wake_time_epoch = mktime(tm);
160         PARA_INFO_LOG("waketime: %s", asctime(tm));
161         client_cmd("stop");
162         sleep(1);
163         if (fot) {
164                 PARA_INFO_LOG("initial volume: %d\n", iv);
165                 ret = m->set(h, iv);
166                 if (ret < 0)
167                         return ret;
168                 change_afs_mode_and_play(fo_mood);
169                 ret = fade(m, h, fov, fot);
170                 if (ret < 0)
171                         return ret;
172         } else {
173                 ret = m->set(h, fov);
174                 if (ret < 0)
175                         return ret;
176         }
177         if (conf.sleep_mood_given)
178                 change_afs_mode_and_play(sleep_mood);
179         else
180                 client_cmd("stop");
181         if (!fit)
182                 return 1;
183         for (;;) {
184                 time(&t1);
185                 if (wake_time_epoch <= t1 + fit)
186                         break;
187                 delay = wake_time_epoch - t1 - fit;
188                 PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
189                         delay, delay / 3600,
190                         (delay % 3600) / 60);
191                 sleep(delay);
192         }
193         change_afs_mode_and_play(fi_mood);
194         ret = fade(m, h, fiv, fit);
195         PARA_INFO_LOG("fade complete, returning\n");
196         return ret;
197 }
198
199 static int snooze(struct mixer *m, struct mixer_handle *h)
200 {
201         int ret, val;
202
203         if (conf.so_time_arg <= 0)
204                 return 1;
205         ret = m->get(h);
206         if (ret < 0)
207                 return ret;
208         val = ret;
209         if (val < conf.so_vol_arg)
210                 ret = m->set(h, conf.so_vol_arg);
211         else
212                 ret = fade(m, h, conf.so_vol_arg, conf.so_time_arg);
213         if (ret < 0)
214                 return ret;
215         client_cmd("pause");
216         PARA_NOTICE_LOG("%d seconds snooze time...\n", conf.snooze_time_arg);
217         sleep(conf.snooze_time_arg);
218         client_cmd("play");
219         return fade(m, h, conf.si_vol_arg, conf.si_time_arg);
220 }
221
222 static int configfile_exists(void)
223 {
224         static char *config_file;
225
226         if (!conf.config_file_given) {
227                 char *home = para_homedir();
228                 free(config_file);
229                 config_file = make_message("%s/.paraslash/fade.conf", home);
230                 free(home);
231                 conf.config_file_arg = config_file;
232         }
233         return file_exists(conf.config_file_arg);
234 }
235
236 static void init_mixers(void)
237 {
238         int i;
239
240         FOR_EACH_MIXER(i) {
241                 struct mixer *m = &supported_mixer[i];
242                 PARA_DEBUG_LOG("initializing mixer API #%d (%s)\n",
243                         i, mixer_name[i]);
244                 m->init(m);
245         }
246 }
247
248 static int set_channel(struct mixer *m, struct mixer_handle *h)
249 {
250         char *channels;
251         int ret;
252
253         ret = m->set_channel(h, conf.mixer_channel_arg);
254         if (ret >= 0) {
255                 PARA_NOTICE_LOG("using %s mixer channel\n",
256                         conf.mixer_channel_arg?  conf.mixer_channel_arg
257                                 : "default");
258                 return ret;
259         }
260         channels = m->get_channels(h);
261         printf("Available channels: %s\n", channels);
262         free(channels);
263         return ret;
264 }
265
266 static struct mixer *get_mixer_or_die(void)
267 {
268         int i;
269
270         if (!conf.mixer_api_given)
271                 i = DEFAULT_MIXER;
272         else
273                 FOR_EACH_MIXER(i)
274                         if (!strcmp(mixer_name[i], conf.mixer_api_arg))
275                                 break;
276         if (i < NUM_SUPPORTED_MIXERS) {
277                 PARA_NOTICE_LOG("using %s mixer API\n", mixer_name[i]);
278                 return supported_mixer + i;
279         }
280         printf("available mixer APIs: ");
281         FOR_EACH_MIXER(i) {
282                 int d = (i == DEFAULT_MIXER);
283                 printf("%s%s%s ", d? "[" : "", mixer_name[i], d? "]" : "");
284         }
285         printf("\n");
286         exit(EXIT_FAILURE);
287 }
288
289 int main(int argc, char *argv[])
290 {
291         int ret;
292         struct mixer *m;
293         struct mixer_handle *h = NULL;
294
295         if (fade_cmdline_parser(argc, argv, &conf))
296                 exit(EXIT_FAILURE);
297         HANDLE_VERSION_FLAG("fade", conf);
298         ret = configfile_exists();
299         if (!ret && conf.config_file_given) {
300                 PARA_EMERG_LOG("can not read config file %s\n",
301                         conf.config_file_arg);
302                 exit(EXIT_FAILURE);
303         }
304         if (ret) {
305                 struct fade_cmdline_parser_params params = {
306                         .override = 0,
307                         .initialize = 0,
308                         .check_required = 0,
309                         .check_ambiguity = 0,
310                         .print_errors = 1
311                 };
312                 fade_cmdline_parser_config_file(conf.config_file_arg,
313                         &conf, &params);
314         }
315         loglevel = get_loglevel_by_name(conf.loglevel_arg);
316         init_mixers();
317         m = get_mixer_or_die();
318         ret = m->open(conf.mixer_device_arg, &h);
319         if (ret < 0)
320                 goto out;
321         ret = set_channel(m, h);
322         if (ret < 0)
323                 goto out;
324         switch (conf.mode_arg) {
325         case mode_arg_fade:
326                 ret = fade(m, h, conf.fade_vol_arg, conf.fade_time_arg);
327                 break;
328         case mode_arg_snooze:
329                 ret = snooze(m, h);
330                 break;
331         default: /* sleep mode */
332                 ret = sweet_dreams(m, h);
333                 break;
334         }
335 out:
336         m->close(&h);
337         if (ret < 0)
338                 PARA_EMERG_LOG("%s\n", para_strerror(-ret));
339         return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
340 }