comparison src/alarm/alarm.c @ 121:b59bca84e3cd trunk

[svn] - add xmms-alarm port
author nenolod
date Thu, 26 Oct 2006 00:10:28 -0700
parents
children 96fc1ef32c99
comparison
equal deleted inserted replaced
120:7ce296248b5c 121:b59bca84e3cd
1 /*
2 * Copyright (C) Adam Feakin <adamf@snika.uklinux.net>
3 *
4 * modified by Daniel Stodden <stodden@in.tum.de>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 /*
22 * xmms-alarm.c - a XMMS plugin which allows you to set a time for it to
23 * start playing a playlist with the volume fading up and more things
24 * the next time I get bored
25 */
26
27 /* this file really should get split/cleaned up sometime ;) */
28 #include "config.h"
29
30 #if STDC_HEADERS
31 # include <stdlib.h>
32 #endif
33
34 #include <time.h>
35 #if TM_IN_SYS_TIME
36 # include <sys/time.h>
37 #endif
38
39 #include <string.h>
40 #include <stdio.h>
41 #include <audacious/plugin.h>
42 #include <audacious/beepctrl.h>
43 #include <audacious/configdb.h>
44 #include <gdk/gdk.h>
45 #include <gtk/gtk.h>
46 #include <pthread.h>
47 #include <assert.h>
48 #include <math.h>
49
50 #include "alarm.h"
51 #include "interface.h"
52 #include "callbacks.h"
53
54 static pthread_t start_tid; /* thread id of alarm loop */
55 static pthread_t stop_tid; /* thread id of stop loop */
56 static pthread_mutex_t fader_lock = PTHREAD_MUTEX_INITIALIZER;
57
58 static GeneralPlugin alarm_plugin;
59
60 /* string tokens to allow loops and shorten code */
61 static char day_cb[7][7] = {"sun_cb", "mon_cb", "tue_cb",
62 "wed_cb", "thu_cb", "fri_cb", "sat_cb"};
63
64 static char day_flags[7][10] = {"sun_flags", "mon_flags", "tue_flags",
65 "wed_flags", "thu_flags", "fri_flags", "sat_flags"};
66
67 static char day_h[7][6] = {"sun_h", "mon_h", "tue_h",
68 "wed_h", "thu_h", "fri_h", "sat_h"};
69
70 static char day_m[7][6] = {"sun_m", "mon_m", "tue_m",
71 "wed_m", "thu_m", "fri_m", "sat_m"};
72
73 static char day_def[7][8] = {"sun_def", "mon_def", "tue_def",
74 "wed_def", "thu_def", "fri_def", "sat_def"};
75
76 static struct
77 {
78 GtkSpinButton *alarm_h;
79 GtkSpinButton *alarm_m;
80
81 GtkToggleButton *stop_on;
82 GtkSpinButton *stop_h;
83 GtkSpinButton *stop_m;
84
85 GtkRange *volume;
86 GtkRange *quietvol;
87
88 GtkSpinButton *fading;
89
90 GtkEntry *cmdstr;
91 GtkToggleButton *cmd_on;
92
93 GtkEntry *playlist;
94
95 int default_hour;
96 int default_min;
97
98 // array allows looping of days
99 alarmday day[7];
100
101
102 GtkEntry *reminder;
103 GtkToggleButton *reminder_cb;
104 gchar *reminder_msg;
105 gboolean reminder_on;
106 }
107 alarm_conf;
108
109 static gint alarm_h, alarm_m;
110
111 static gboolean stop_on;
112 static gint stop_h, stop_m;
113
114 static gint volume, quietvol;
115
116 static gint fading;
117
118 static gchar *cmdstr = NULL;
119 static gboolean cmd_on;
120
121 static gchar *playlist = NULL;
122
123 static GtkWidget *config_dialog = NULL;
124 static GtkWidget *alarm_dialog = NULL;
125
126 static GtkWidget *lookup_widget(GtkWidget *w, const gchar *name)
127 {
128 GtkWidget *widget;
129
130 widget = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(w),
131 name);
132 g_return_val_if_fail(widget != NULL, NULL);
133
134 return widget;
135 }
136
137 static void dialog_destroyed(GtkWidget *dialog, gpointer data)
138 {
139 DEBUG("dialog destroyed\n");
140 *(GtkObject**)data = NULL;
141 }
142
143 static inline gboolean dialog_visible(GtkWidget *dialog)
144 {
145 return(((dialog != NULL) && GTK_WIDGET_VISIBLE(dialog)));
146 }
147
148 /*
149 * tell the user about that bug
150 */
151 static void alarm_warning(void)
152 {
153
154 static GtkWidget *warning_dialog = NULL;
155
156 if(dialog_visible(warning_dialog))
157 return;
158
159 warning_dialog = create_warning_dialog();
160
161 gtk_signal_connect(GTK_OBJECT(warning_dialog), "destroy",
162 GTK_SIGNAL_FUNC(dialog_destroyed), &warning_dialog);
163
164 gtk_widget_show_all(warning_dialog);
165
166 return;
167 }
168
169 /*
170 * the callback function that is called when the save button is
171 * pressed saves configuration to ~/.bmp/alarmconfig
172 */
173 void alarm_save(GtkButton *w, gpointer data)
174 {
175 int daynum = 0; // used to identify day number
176 ConfigDb *conf;
177
178 DEBUG("alarm_save\n");
179
180 conf = bmp_cfg_db_open();
181
182 /*
183 * update the live values and write them out
184 */
185 alarm_h = alarm_conf.default_hour =
186 gtk_spin_button_get_value_as_int(alarm_conf.alarm_h);
187 bmp_cfg_db_set_int(conf, "alarm", "alarm_h", alarm_h);
188
189 alarm_m = alarm_conf.default_min =
190 gtk_spin_button_get_value_as_int(alarm_conf.alarm_m);
191 bmp_cfg_db_set_int(conf, "alarm", "alarm_m", alarm_m);
192
193
194 stop_h =
195 gtk_spin_button_get_value_as_int( alarm_conf.stop_h);
196
197 stop_m =
198 gtk_spin_button_get_value_as_int(alarm_conf.stop_m);
199
200 stop_on =
201 gtk_toggle_button_get_active(alarm_conf.stop_on);
202
203 /* days of the week */
204 for(; daynum < 7; daynum++)
205 {
206 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(alarm_conf.day[daynum].cb)))
207 alarm_conf.day[daynum].flags = 0;
208 else
209 alarm_conf.day[daynum].flags = ALARM_OFF;
210
211 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(alarm_conf.day[daynum].cb_def)))
212 alarm_conf.day[daynum].flags |= ALARM_DEFAULT;
213
214 alarm_conf.day[daynum].hour =
215 gtk_spin_button_get_value_as_int(alarm_conf.day[daynum].spin_hr);
216 alarm_conf.day[daynum].min =
217 gtk_spin_button_get_value_as_int(alarm_conf.day[daynum].spin_min);
218
219 bmp_cfg_db_set_int(conf, "alarm", day_flags[daynum], alarm_conf.day[daynum].flags);
220 bmp_cfg_db_set_int(conf, "alarm", day_h[daynum], alarm_conf.day[daynum].hour);
221 bmp_cfg_db_set_int(conf, "alarm", day_m[daynum], alarm_conf.day[daynum].min);
222 }
223
224 /* END: days of week */
225
226 volume =
227 gtk_range_get_adjustment(alarm_conf.volume)->value;
228 bmp_cfg_db_set_int(conf, "alarm", "volume", volume);
229
230 quietvol =
231 gtk_range_get_adjustment(alarm_conf.quietvol)->value;
232 bmp_cfg_db_set_int(conf, "alarm", "quietvol", quietvol);
233
234 fading =
235 gtk_spin_button_get_value_as_int(alarm_conf.fading);
236 //xmms_cfg_write_int(conf, "alarm", "fading", fading);
237
238 /* lets check to see if we need to show the bug warning */
239 if((stop_on == TRUE) &&
240 ((((stop_h * 60) + stop_m) * 60) < (fading + 65)))
241 {
242 DEBUG("Displaying bug warning, stop %dh %dm, fade %d\n",
243 stop_h, stop_m, fading);
244 alarm_warning();
245 }
246 else if((stop_on == TRUE) && (fading < 10))
247 {
248 DEBUG("Displaying bug warning, stop %dh %dm, fade %d\n",
249 stop_h, stop_m, fading);
250 alarm_warning();
251 }
252 else
253 {
254 /* write the new values */
255 bmp_cfg_db_set_int(conf, "alarm", "stop_h", stop_h);
256 bmp_cfg_db_set_int(conf, "alarm", "stop_m", stop_m);
257 bmp_cfg_db_set_int(conf, "alarm", "fading", fading);
258 bmp_cfg_db_set_bool(conf, "alarm", "stop_on", stop_on);
259 }
260
261
262 g_free(cmdstr);
263 cmdstr = gtk_editable_get_chars(GTK_EDITABLE(alarm_conf.cmdstr),
264 0, -1);
265 bmp_cfg_db_set_string(conf, "alarm", "cmdstr", cmdstr);
266
267 cmd_on =
268 gtk_toggle_button_get_active(alarm_conf.cmd_on);
269 bmp_cfg_db_set_bool(conf, "alarm", "cmd_on", cmd_on);
270
271 g_free(playlist);
272 playlist = gtk_editable_get_chars(GTK_EDITABLE(alarm_conf.playlist),
273 0, -1);
274 bmp_cfg_db_set_string(conf, "alarm", "playlist", playlist);
275
276 /* reminder */
277 g_free(alarm_conf.reminder_msg);
278 alarm_conf.reminder_msg = gtk_editable_get_chars(GTK_EDITABLE(alarm_conf.reminder),
279 0, -1);
280 bmp_cfg_db_set_string(conf, "alarm", "reminder_msg", alarm_conf.reminder_msg);
281
282 alarm_conf.reminder_on =
283 gtk_toggle_button_get_active(alarm_conf.reminder_cb);
284 bmp_cfg_db_set_bool(conf, "alarm", "reminder_on", alarm_conf.reminder_on);
285
286 bmp_cfg_db_close(conf);
287 }
288
289 /*
290 * read the current configuration from the file
291 */
292 static void alarm_read_config()
293 {
294 int daynum = 0; // used for day number
295 ConfigDb *conf;
296
297 DEBUG("alarm_read_config\n");
298
299 conf = bmp_cfg_db_open();
300
301 if(!bmp_cfg_db_get_int(conf, "alarm", "alarm_h", &alarm_h))
302 alarm_h = DEFAULT_ALARM_HOUR;
303 if(!bmp_cfg_db_get_int(conf, "alarm", "alarm_m", &alarm_m))
304 alarm_m = DEFAULT_ALARM_MIN;
305
306 /* save them here too */
307 alarm_conf.default_hour = alarm_h;
308 alarm_conf.default_min = alarm_m;
309
310 if(!bmp_cfg_db_get_int( conf, "alarm", "stop_h", &stop_h))
311 stop_h = DEFAULT_STOP_HOURS;
312 if(!bmp_cfg_db_get_int( conf, "alarm", "stop_m", &stop_m))
313 stop_m = DEFAULT_STOP_MINS;
314 if(!bmp_cfg_db_get_bool(conf, "alarm", "stop_on", &stop_on))
315 stop_on = TRUE;
316
317 if(!bmp_cfg_db_get_int(conf, "alarm", "volume", &volume))
318 volume = DEFAULT_VOLUME;
319 if(!bmp_cfg_db_get_int(conf, "alarm", "quietvol", &quietvol))
320 quietvol = DEFAULT_QUIET_VOL;
321
322 if(!bmp_cfg_db_get_int(conf, "alarm", "fading", &fading))
323 fading = DEFAULT_FADING;
324
325 if(!bmp_cfg_db_get_string(conf, "alarm", "cmdstr", &cmdstr))
326 cmdstr = g_strdup("");
327 if(!bmp_cfg_db_get_bool(conf, "alarm", "cmd_on", &cmd_on))
328 cmd_on = FALSE;
329
330 if(!bmp_cfg_db_get_string(conf, "alarm", "playlist", &playlist))
331 playlist = g_strdup("");
332
333 if(!bmp_cfg_db_get_string(conf, "alarm", "reminder_msg", &alarm_conf.reminder_msg))
334 alarm_conf.reminder_msg = g_strdup("");
335 if(!bmp_cfg_db_get_bool(conf, "alarm", "reminder_on", &alarm_conf.reminder_on))
336 alarm_conf.reminder_on = FALSE;
337
338 /* day flags and times */
339 for(; daynum < 7; daynum++)
340 {
341 /* read the flags */
342 if(!bmp_cfg_db_get_int(conf, "alarm", day_flags[daynum], &alarm_conf.day[daynum].flags)) {
343 // only turn alarm off by default on a sunday
344 if(daynum != 0)
345 alarm_conf.day[daynum].flags = DEFAULT_FLAGS;
346 else
347 alarm_conf.day[daynum].flags = DEFAULT_FLAGS | ALARM_OFF;
348 }
349
350 /* read the times */
351 if(!bmp_cfg_db_get_int(conf, "alarm", day_h[daynum], &alarm_conf.day[daynum].hour))
352 alarm_conf.day[daynum].hour = DEFAULT_ALARM_HOUR;
353
354 if(!bmp_cfg_db_get_int(conf, "alarm", day_m[daynum], &alarm_conf.day[daynum].min))
355 alarm_conf.day[daynum].min = DEFAULT_ALARM_MIN;
356 }
357
358 DEBUG("END alarm_read_config\n");
359 }
360
361 /*
362 * display an about box
363 */
364 static void alarm_about()
365 {
366 static GtkWidget *about_dialog = NULL;
367
368 DEBUG("alarm_about\n");
369
370 if(dialog_visible(about_dialog))
371 return;
372
373 about_dialog = create_about_dialog();
374
375 gtk_signal_connect(GTK_OBJECT(about_dialog), "destroy",
376 GTK_SIGNAL_FUNC(dialog_destroyed), &about_dialog);
377
378 gtk_widget_show_all(about_dialog);
379
380 return;
381 }
382
383 /*
384 * create a playlist file selection dialog
385 */
386 static void alarm_playlist_browse(GtkButton *button, gpointer data)
387 {
388 GtkWidget *fs;
389 gchar *dirname, *path;
390
391 dirname = g_dirname(playlist);
392 DEBUG("dirname = %s\n", dirname);
393 path = g_strdup_printf("%s/", dirname);
394 DEBUG("path = %s\n", path);
395 g_free(dirname);
396
397 fs = create_playlist_fileselection();
398
399 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), path);
400 g_free(path);
401
402 gtk_widget_show_all(fs);
403 }
404
405 /*
406 * save selected playlist to the corresponding text entry
407 */
408 void alarm_store_playlistname(GtkButton *button, gpointer data)
409 {
410 GtkFileSelection *fs = GTK_FILE_SELECTION(data);
411 gchar *plist;
412
413 DEBUG("alarm_store_playlistname\n");
414
415 plist = gtk_file_selection_get_filename(fs);
416
417 gtk_entry_set_text(alarm_conf.playlist, plist);
418 }
419
420 /*
421 * displays the configuration window and opens the config file.
422 */
423 static void alarm_configure(void)
424 {
425 int daynum = 0; // used to loop days
426 GtkWidget *w;
427
428 DEBUG("alarm_configure\n");
429
430 /*
431 * dont want to show more than one config window
432 */
433 if(dialog_visible(config_dialog))
434 return;
435
436 alarm_read_config();
437
438 /*
439 * Create the widgets
440 */
441 config_dialog = create_config_dialog();
442
443 w = lookup_widget(config_dialog, "alarm_h_spin");
444 alarm_conf.alarm_h = GTK_SPIN_BUTTON(w);
445 gtk_spin_button_set_value(alarm_conf.alarm_h, alarm_h);
446
447 w = lookup_widget(config_dialog, "alarm_m_spin");
448 alarm_conf.alarm_m = GTK_SPIN_BUTTON(w);
449 gtk_spin_button_set_value(alarm_conf.alarm_m, alarm_m);
450
451 w = lookup_widget(config_dialog, "stop_h_spin");
452 alarm_conf.stop_h = GTK_SPIN_BUTTON(w);
453 gtk_spin_button_set_value(alarm_conf.stop_h, stop_h);
454
455 w = lookup_widget(config_dialog, "stop_m_spin");
456 alarm_conf.stop_m = GTK_SPIN_BUTTON(w);
457 gtk_spin_button_set_value(alarm_conf.stop_m, stop_m);
458
459 w = lookup_widget(config_dialog, "stop_checkb");
460 alarm_conf.stop_on = GTK_TOGGLE_BUTTON(w);
461 gtk_toggle_button_set_active(alarm_conf.stop_on, stop_on);
462
463 w = lookup_widget(config_dialog, "vol_scale");
464 alarm_conf.volume = GTK_RANGE(w);
465 gtk_range_set_adjustment(alarm_conf.volume,
466 GTK_ADJUSTMENT(gtk_adjustment_new(volume,
467 0,
468 100, 1,
469 5, 0)));
470
471 w = lookup_widget(config_dialog, "quiet_vol_scale");
472 alarm_conf.quietvol = GTK_RANGE(w);
473 gtk_range_set_adjustment(alarm_conf.quietvol,
474 GTK_ADJUSTMENT(gtk_adjustment_new(quietvol,
475 0,
476 100, 1,
477 5, 0)));
478
479 /* days of week */
480 for(; daynum < 7; daynum++)
481 {
482 w = lookup_widget(config_dialog, day_cb[daynum]);
483 alarm_conf.day[daynum].cb = GTK_CHECK_BUTTON(w);
484 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(alarm_conf.day[daynum].cb),
485 !(alarm_conf.day[daynum].flags & ALARM_OFF));
486
487 w = lookup_widget(config_dialog, day_def[daynum]);
488 alarm_conf.day[daynum].cb_def = GTK_CHECK_BUTTON(w);
489 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(alarm_conf.day[daynum].cb_def),
490 alarm_conf.day[daynum].flags & ALARM_DEFAULT);
491
492
493 /* Changed to show default time instead of set time when ALARM_DEFAULT set,
494 * as suggested by Mark Brown
495 */
496 /* w = lookup_widget(config_dialog, day_h[daynum]);
497 alarm_conf.day[daynum].spin_hr = GTK_SPIN_BUTTON(w);
498 gtk_spin_button_set_value(alarm_conf.day[daynum].spin_hr, alarm_conf.day[daynum].hour);
499
500 w = lookup_widget(config_dialog, day_m[daynum]);
501 alarm_conf.day[daynum].spin_min = GTK_SPIN_BUTTON(w);
502 gtk_spin_button_set_value(alarm_conf.day[daynum].spin_min, alarm_conf.day[daynum].min);
503 */
504 if(alarm_conf.day[daynum].flags & ALARM_DEFAULT)
505 {
506 w = lookup_widget(config_dialog, day_h[daynum]);
507 alarm_conf.day[daynum].spin_hr = GTK_SPIN_BUTTON(w);
508 gtk_spin_button_set_value(alarm_conf.day[daynum].spin_hr, alarm_conf.default_hour);
509
510 w = lookup_widget(config_dialog, day_m[daynum]);
511 alarm_conf.day[daynum].spin_min = GTK_SPIN_BUTTON(w);
512 gtk_spin_button_set_value(alarm_conf.day[daynum].spin_min, alarm_conf.default_min);
513
514 gtk_widget_set_sensitive((GtkWidget *)alarm_conf.day[daynum].spin_hr, FALSE);
515 gtk_widget_set_sensitive((GtkWidget *)alarm_conf.day[daynum].spin_min, FALSE);
516 }
517 else
518 {
519 w = lookup_widget(config_dialog, day_h[daynum]);
520 alarm_conf.day[daynum].spin_hr = GTK_SPIN_BUTTON(w);
521 gtk_spin_button_set_value(alarm_conf.day[daynum].spin_hr, alarm_conf.day[daynum].hour);
522
523 w = lookup_widget(config_dialog, day_m[daynum]);
524 alarm_conf.day[daynum].spin_min = GTK_SPIN_BUTTON(w);
525 gtk_spin_button_set_value(alarm_conf.day[daynum].spin_min, alarm_conf.day[daynum].min);
526
527 gtk_widget_set_sensitive((GtkWidget *)alarm_conf.day[daynum].spin_hr, TRUE);
528 gtk_widget_set_sensitive((GtkWidget *)alarm_conf.day[daynum].spin_min, TRUE);
529 }
530 }
531
532 /* END: days of week */
533
534 w = lookup_widget(config_dialog,"fading_spin");
535 alarm_conf.fading = GTK_SPIN_BUTTON(w);
536 gtk_spin_button_set_value(alarm_conf.fading, fading);
537
538 w = lookup_widget(config_dialog, "cmd_entry");
539 alarm_conf.cmdstr = GTK_ENTRY(w);
540 gtk_entry_set_text(alarm_conf.cmdstr, cmdstr);
541
542 w = lookup_widget(config_dialog, "cmd_checkb");
543 alarm_conf.cmd_on = GTK_TOGGLE_BUTTON(w);
544 gtk_toggle_button_set_active(alarm_conf.cmd_on, cmd_on);
545
546 w = lookup_widget(config_dialog, "playlist");
547 alarm_conf.playlist = GTK_ENTRY(w);
548 gtk_entry_set_text(alarm_conf.playlist, playlist);
549
550 w = lookup_widget(config_dialog, "reminder_text");
551 alarm_conf.reminder = GTK_ENTRY(w);
552 gtk_entry_set_text(alarm_conf.reminder, alarm_conf.reminder_msg);
553
554 w = lookup_widget(config_dialog, "reminder_cb");
555 alarm_conf.reminder_cb = GTK_TOGGLE_BUTTON(w);
556 gtk_toggle_button_set_active(alarm_conf.reminder_cb, alarm_conf.reminder_on);
557
558 w = lookup_widget(config_dialog, "playlist_browse_button");
559 gtk_signal_connect(GTK_OBJECT(w), "clicked",
560 GTK_SIGNAL_FUNC(alarm_playlist_browse), NULL);
561
562 gtk_signal_connect(GTK_OBJECT(config_dialog), "destroy",
563 GTK_SIGNAL_FUNC(dialog_destroyed), &config_dialog);
564
565 gtk_widget_show_all(config_dialog);
566
567 DEBUG("END alarm_configure\n");
568 }
569
570 /* functions for greying out the time for days */
571 void on_day_def_toggled(GtkToggleButton *togglebutton, gpointer user_data, int daynum)
572 {
573 GtkWidget *w;
574
575 /* change the time shown too */
576 w = lookup_widget(config_dialog, day_h[daynum]);
577 if(w == NULL)
578 return;
579
580 if(gtk_toggle_button_get_active(togglebutton) == TRUE)
581 {
582 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), alarm_conf.default_hour);
583 gtk_widget_set_sensitive(w, FALSE);
584 }
585 else
586 {
587 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), alarm_conf.day[daynum].hour);
588 gtk_widget_set_sensitive(w, TRUE);
589 }
590
591 w = lookup_widget(config_dialog, day_m[daynum]);
592 if(gtk_toggle_button_get_active(togglebutton) == TRUE)
593 {
594 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), alarm_conf.default_min);
595 gtk_widget_set_sensitive(w, FALSE);
596 }
597 else
598 {
599 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), alarm_conf.day[daynum].min);
600 gtk_widget_set_sensitive(w, TRUE);
601 }
602 }
603
604 void on_sun_def_toggled(GtkToggleButton *togglebutton, gpointer user_data)
605 {
606 on_day_def_toggled(togglebutton, user_data, 0);
607 }
608
609 void on_mon_def_toggled(GtkToggleButton *togglebutton, gpointer user_data)
610 {
611 on_day_def_toggled(togglebutton, user_data, 1);
612 }
613
614 void on_tue_def_toggled(GtkToggleButton *togglebutton, gpointer user_data)
615 {
616 on_day_def_toggled(togglebutton, user_data, 2);
617 }
618
619 void on_wed_def_toggled(GtkToggleButton *togglebutton, gpointer user_data)
620 {
621 on_day_def_toggled(togglebutton, user_data, 3);
622 }
623
624 void on_thu_def_toggled(GtkToggleButton *togglebutton, gpointer user_data)
625 {
626 on_day_def_toggled(togglebutton, user_data, 4);
627 }
628
629 void on_fri_def_toggled(GtkToggleButton *togglebutton, gpointer user_data)
630 {
631 on_day_def_toggled(togglebutton, user_data, 5);
632 }
633
634 void on_sat_def_toggled(GtkToggleButton *togglebutton, gpointer user_data)
635 {
636 on_day_def_toggled(togglebutton, user_data, 6);
637 }
638
639 /* END: greying things */
640
641 void alarm_current_volume(GtkButton *button, gpointer data)
642 {
643 gint vol;
644 GtkAdjustment *adj;
645
646 DEBUG("on_current_button_clicked\n");
647
648 vol = xmms_remote_get_main_volume(alarm_plugin.xmms_session);
649
650 adj = gtk_range_get_adjustment(alarm_conf.volume);
651 gtk_adjustment_set_value(adj, (gfloat)vol);
652 }
653
654 GeneralPlugin *get_gplugin_info()
655 {
656 return &alarm_plugin;
657 }
658
659 /*
660 * a thread safe sleeping function -
661 * and it even works in solaris (I think)
662 */
663 static void threadsleep(float x)
664 {
665 struct timespec rqtp, rmtp;
666
667 DEBUG("threadsleep: waiting %f seconds\n", x);
668
669 rqtp.tv_sec = x;
670 rqtp.tv_nsec = (int)((float)(x - (int)x) * 1000000000.0);
671
672 nanosleep(&rqtp, &rmtp);
673
674 return;
675 }
676
677 static inline pthread_t alarm_thread_create(void *(*start_routine)(void *), void *args, unsigned int detach)
678 {
679 pthread_t tid;
680 pthread_attr_t attr;
681
682 pthread_attr_init(&attr);
683
684 if(detach != 0)
685 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
686
687 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
688 pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
689 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
690
691 pthread_create(&tid, &attr, start_routine, args);
692
693 return(tid);
694 }
695
696 static void *alarm_fade(void *arg)
697 {
698 fader *vols = (fader *)arg;
699 guint i;
700 gint inc, diff, adiff;
701
702 /* lock */
703 pthread_mutex_lock(&fader_lock);
704
705 /* slide volume */
706 /* the Kaspar Giger way of fading, check the current mixer volume and
707 * increment from there so that if you have some other app lowering the
708 * volume at the same time xmms-alarm will not ignore it. If you have some
709 * other app increasing the volume, then it could get louder that you expect
710 * though - because the loop does not recalculate the difference each time.
711 */
712
713 /* difference between the 2 volumes */
714 diff = vols->end - vols->start;
715 adiff = abs(diff);
716
717 /* Are we going up or down? */
718 if(diff < 0)
719 inc = -1;
720 else
721 inc = 1;
722
723 xmms_remote_set_main_volume(alarm_plugin.xmms_session, (gint)vols->start);
724 //for(i=0;i<(vols->end - vols->start);i++)
725 for(i=0;i<adiff;i++)
726 {
727 //threadsleep((gfloat)fading / (vols->end - vols->start));
728 threadsleep((gfloat)fading / (gfloat)adiff);
729 xmms_remote_set_main_volume(alarm_plugin.xmms_session,
730 (gint)(xmms_remote_get_main_volume(alarm_plugin.xmms_session) + inc));
731 }
732 /* Setting the volume to the end volume sort of defeats the point if having
733 * the code in there to allow other apps to control volume too :)
734 */
735 //xmms_remote_set_main_volume(alarm_plugin.xmms_session, (gint)vols->end);
736
737 /* and */
738 pthread_mutex_unlock(&fader_lock);
739
740 DEBUG("volume = %f%%\n", (gdouble)vols->end);
741 return(0);
742 }
743
744 static void *alarm_stop_thread( void *args )
745 {
746 gint currvol;
747 fader fade_vols;
748 pthread_t f_tid;
749
750 DEBUG("alarm_stop_thread\n");
751
752
753 /* sleep for however long we are meant to be sleeping for until
754 * its time to shut up
755 */
756 threadsleep(((stop_h * 60) + stop_m) * 60);
757
758 DEBUG("alarm_stop triggered\n");
759
760 if (dialog_visible(alarm_dialog))
761 gtk_widget_destroy(alarm_dialog);
762
763 currvol = xmms_remote_get_main_volume(alarm_plugin.xmms_session),
764
765 /* fade back to zero */
766 fade_vols.start = currvol;
767 fade_vols.end = 0;
768
769 /* The fader thread locks the fader_mutex now */
770 f_tid = alarm_thread_create(alarm_fade, &fade_vols, 0);
771
772 pthread_join(f_tid, NULL);
773 xmms_remote_stop(alarm_plugin.xmms_session);
774
775 /* might as well set the volume to something higher than zero so we
776 * dont confuse the poor people who just woke up and cant work out why
777 * theres no music playing when they press the little play button :)
778 */
779 xmms_remote_set_main_volume(alarm_plugin.xmms_session, currvol);
780
781 DEBUG("alarm_stop done\n");
782 return(NULL);
783 }
784
785 void alarm_stop_cancel(GtkButton *w, gpointer data)
786 {
787 DEBUG("alarm_stop_cancel\n");
788 pthread_cancel(stop_tid);
789 }
790
791 /* the main alarm thread */
792 static void *alarm_start_thread(void *args)
793 {
794 struct tm *currtime;
795 time_t timenow;
796 unsigned int play_start = 0;
797 guint today;
798
799 /* give it time to set start_tid to something */
800 threadsleep(1);
801
802 while(start_tid != 0)
803 {
804 /* sit around and wait for the faders to not be doing anything */
805 DEBUG("Waiting for fader to be unlocked..");
806 pthread_mutex_lock(&fader_lock);
807 DEBUG("Ok\n");
808 pthread_mutex_unlock(&fader_lock);
809
810 DEBUG("Getting time\n");
811 timenow = time(NULL);
812 currtime = localtime(&timenow);
813 today = currtime->tm_wday;
814 DEBUG("Today is %d\n", today);
815
816 /* see if its time to do something */
817 DEBUG("Checking Day\n");
818
819 /* Had to put something here so I put the hour string.
820 ** Its only debug stuff anyway */
821 DEBUG(day_h[today]);
822
823 if(alarm_conf.day[today].flags & ALARM_OFF)
824 {
825 threadsleep(8.5);
826 continue;
827 }
828 else
829 {
830 /* set the alarm_h and alarm_m for today, if not default */
831 if(!(alarm_conf.day[today].flags & ALARM_DEFAULT))
832 {
833 alarm_h = alarm_conf.day[today].hour;
834 alarm_m = alarm_conf.day[today].min;
835 }
836 else
837 {
838 alarm_h = alarm_conf.default_hour;
839 alarm_m = alarm_conf.default_min;
840 }
841 }
842
843 DEBUG("Alarm time is %d:%d (def: %d:%d)\n", alarm_h, alarm_m,
844 alarm_conf.default_hour, alarm_conf.default_min);
845
846 DEBUG("Checking time (%d:%d)\n", currtime->tm_hour, currtime->tm_min);
847 if((currtime->tm_hour != alarm_h) || (currtime->tm_min != alarm_m))
848 {
849 threadsleep(8.5);
850 continue;
851 }
852
853 if(cmd_on == TRUE)
854 {
855 DEBUG("Executing %s, cmd_on is true\n", cmdstr);
856 system(cmdstr);
857 }
858
859 DEBUG("strcmp playlist, playlist is [%s]\n", playlist);
860 if(strcmp(playlist, ""))
861 {
862 DEBUG("playlist is not blank, aparently\n");
863 /* Is this a url? */
864 /* Thanks Thomer */
865 if(!strncmp(playlist, "http://", 7))
866 {
867 /* Yes */
868 DEBUG("This looks like a URL to me...\n");
869 /* If I just add the url and it turns out to be a playlist then xmms
870 * will sort that out.. It should also work for radio streams, I guess
871 */
872 xmms_remote_playlist_clear(alarm_plugin.xmms_session);
873 xmms_remote_playlist_add_url_string(alarm_plugin.xmms_session, playlist);
874 }
875 else
876 {
877 /* No, its probably a local file */
878 /* Does that mean we want to go through it and add files to the list
879 * properly, or just use this semi-hack to let xmms do the playlist
880 * parsing?
881 */
882 xmms_remote_playlist_clear(alarm_plugin.xmms_session);
883 xmms_remote_playlist(alarm_plugin.xmms_session,
884 &playlist, 1, TRUE);
885 }
886 }
887
888 if(fading)
889 {
890 fader fade_vols;
891
892 DEBUG("Fading is true\n");
893 xmms_remote_set_main_volume(alarm_plugin.xmms_session, quietvol);
894
895 /* start playing */
896 play_start = time(NULL);
897 xmms_remote_play(alarm_plugin.xmms_session);
898
899 /* fade volume */
900 fade_vols.start = quietvol;
901 fade_vols.end = volume;
902
903 //alarm_fade(quietvol, volume);
904 alarm_thread_create(alarm_fade, &fade_vols, 0);
905 }
906 else
907 {
908 /* no fading */
909
910 /* set volume */
911 xmms_remote_set_main_volume(alarm_plugin.xmms_session, volume);
912
913 /* start playing */
914 play_start = time(NULL);
915 xmms_remote_play(alarm_plugin.xmms_session);
916 }
917
918 if(alarm_conf.reminder_on == TRUE)
919 {
920 GtkWidget *reminder_dialog;
921 DEBUG("Showing reminder '%s'\n", alarm_conf.reminder_msg);
922
923 GDK_THREADS_ENTER();
924 reminder_dialog = create_reminder_dialog(alarm_conf.reminder_msg);
925 gtk_signal_connect(GTK_OBJECT(reminder_dialog), "destroy",
926 GTK_SIGNAL_FUNC(dialog_destroyed), &reminder_dialog);
927 gtk_widget_show_all(reminder_dialog);
928 GDK_THREADS_LEAVE();
929 }
930
931 /* bring up the wakeup call dialog if stop_on is set TRUE, this
932 * has been moved to after making xmms play so that it doesnt
933 * get in the way for people with manual window placement turned on
934 *
935 * this means that the dialog doesnt get shown until the volume has
936 * finished fading though !, so thats something else to fix
937 */
938 if(stop_on == TRUE)
939 {
940 /* ok, so when we want to open dialogs in threaded programs
941 * we use this do we?
942 * anyone?
943 */
944 GDK_THREADS_ENTER();
945 {
946 DEBUG("stop_on is true\n");
947 alarm_dialog = create_alarm_dialog();
948 DEBUG("created alarm dialog, %p\n", alarm_dialog);
949
950 gtk_signal_connect(GTK_OBJECT(alarm_dialog), "destroy",
951 GTK_SIGNAL_FUNC(dialog_destroyed), &alarm_dialog);
952 DEBUG("attached destroy signal to alarm dialog, %p\n", alarm_dialog);
953 gtk_widget_show_all(alarm_dialog);
954 DEBUG("dialog now showing\n");
955
956 DEBUG("now starting stop thread\n");
957 stop_tid = alarm_thread_create(alarm_stop_thread, NULL, 0);
958 DEBUG("Created wakeup dialog and started stop thread(%d)\n", (int)stop_tid);
959
960 }
961 GDK_THREADS_LEAVE();
962
963 /* now wait for the stop thread */
964 DEBUG("Waiting for stop to stop.... (%d)", (int)stop_tid);
965 pthread_join(stop_tid, NULL);
966 /* loop until we are out of the starting minute */
967 while(time(NULL) < (play_start + 61))
968 {
969 DEBUG("Waiting until out of starting minute\n");
970 threadsleep(5.0);
971 }
972 DEBUG("OK\n");
973 }
974 /* loop until we are out of the starting minute */
975 while(time(NULL) < (play_start + 61))
976 {
977 threadsleep(5.0);
978 }
979 threadsleep(fading);
980 }
981
982 DEBUG("Main thread has gone...\n");
983 return NULL;
984 }
985
986 /*
987 * initialization
988 * opens the config file and reads the value, creates a new
989 * config in memory if the file doesnt exist and sets default vals
990 */
991 static void alarm_init()
992 {
993 DEBUG("alarm_init\n");
994
995 alarm_read_config();
996
997 /* start the main thread running */
998 start_tid = alarm_thread_create(alarm_start_thread, NULL, 1);
999 }
1000
1001 /*
1002 * kill the main thread
1003 */
1004 static void alarm_cleanup()
1005 {
1006 DEBUG("alarm_cleanup\n");
1007
1008 pthread_cancel(start_tid);
1009 start_tid = 0;
1010 if(stop_tid)
1011 pthread_cancel(stop_tid);
1012 }
1013
1014 /*
1015 * for xmms to get the function names
1016 */
1017 static GeneralPlugin alarm_plugin =
1018 {
1019 NULL,
1020 NULL,
1021 -1,
1022 "Alarm "VERSION,
1023 alarm_init,
1024 alarm_about,
1025 alarm_configure,
1026 alarm_cleanup,
1027 };
1028
1029 /*
1030 * vi:ai:expandtab:ts=2 sts=2 shiftwidth=2:nowrap:
1031 */