aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/gui/alarm.c
diff options
context:
space:
mode:
Diffstat (limited to 'calendar/gui/alarm.c')
-rw-r--r--calendar/gui/alarm.c399
1 files changed, 227 insertions, 172 deletions
diff --git a/calendar/gui/alarm.c b/calendar/gui/alarm.c
index ae76a0ce73..20f64ffd75 100644
--- a/calendar/gui/alarm.c
+++ b/calendar/gui/alarm.c
@@ -1,10 +1,25 @@
-/*
- * Alarm handling for the GNOME Calendar.
+/* Evolution calendar - alarm notification support
*
- * (C) 1998 the Free Software Foundation
+ * Copyright (C) 2000 Helix Code, Inc.
*
- * Author: Miguel de Icaza (miguel@kernel.org)
+ * Authors: Miguel de Icaza <miguel@helixcode.com>
+ * Federico Mena-Quintero <federico@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
+
#include <config.h>
#include <time.h>
#include <gnome.h>
@@ -14,237 +29,277 @@
#include <cal-util/calobj.h>
#include "alarm.h"
+
+
/* The pipes used to notify about an alarm */
-int alarm_pipes [2];
+static int alarm_pipes [2];
/* The list of pending alarms */
static GList *alarms;
-static void *head_alarm;
-
+/* A queued alarm structure */
typedef struct {
- time_t activation_time;
- AlarmFunction fn;
- void *closure;
- CalendarAlarm *alarm;
+ time_t trigger;
+ AlarmFunction alarm_fn;
+ gpointer data;
+ AlarmDestroyNotify destroy_notify_fn;
} AlarmRecord;
-enum DebugAction {
- ALARM_ACTIVATED,
- ALARM_ADDED,
- ALARM_NOT_ADDED
-};
-
-void debug_alarm (AlarmRecord* ar, enum DebugAction action);
-void calendar_notify (time_t time, CalendarAlarm *which, void *data);
-extern int debug_alarms;
+
-/*
- * SIGALRM handler. Notifies the callback about the alarm
- */
+/* SIGALRM handler. Notifies the callback about the alarm. */
static void
-alarm_activate ()
+alarm_signal (int arg)
{
char c = 0;
write (alarm_pipes [1], &c, 1);
}
-/*
- * SIGUSR1 handler. Toggles debugging output
+/* Sets up an itimer and returns a success code */
+static gboolean
+setup_itimer (time_t diff)
+{
+ struct itimerval itimer;
+ int v;
+
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = 0;
+ itimer.it_value.tv_sec = diff;
+ itimer.it_value.tv_usec = 0;
+
+ v = setitimer (ITIMER_REAL, &itimer, NULL);
+
+ return (v == 0) ? TRUE : FALSE;
+}
+
+/* Removes the head alarm, returns it, and schedules the next alarm in the
+ * queue.
*/
-static void
-toggle_debugging ()
+static AlarmRecord *
+pop_alarm (void)
{
- debug_alarms = !debug_alarms;
+ AlarmRecord *ar;
+ GList *l;
+
+ if (!alarms)
+ return NULL;
+
+ ar = alarms->data;
+
+ l = alarms;
+ alarms = g_list_remove_link (alarms, l);
+ g_list_free_1 (l);
+
+ if (alarms) {
+ time_t now;
+ AlarmRecord *new_ar;
+
+ now = time (NULL);
+ new_ar = alarms->data;
+
+ if (!setup_itimer (new_ar->trigger)) {
+ g_message ("pop_alarm(): Could not reset the timer! "
+ "Weird things will happen.");
+
+ /* FIXME: should we free the alarm list? What
+ * about further alarm removal requests that
+ * will fail?
+ */
+ }
+ } else {
+ struct itimerval itimer;
+ int v;
+
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = 0;
+ itimer.it_value.tv_sec = 0;
+ itimer.it_value.tv_usec = 0;
+
+ v = setitimer (ITIMER_REAL, &itimer, NULL);
+ if (v != 0)
+ g_message ("pop_alarm(): Could not clear the timer! "
+ "Weird things may happen.");
+ }
+
+ return ar;
}
+/* Input handler for our own alarm notification pipe */
static void
-alarm_ready (void *closure, int fd, GdkInputCondition cond)
+alarm_ready (gpointer data, gint fd, GdkInputCondition cond)
{
- AlarmRecord *ar = head_alarm;
- time_t now = time (NULL);
+ AlarmRecord *ar;
char c;
- if (read (alarm_pipes [0], &c, 1) != 1)
- return;
-
- if (ar == NULL){
- g_warning ("Empty events. This should not happen\n");
+ if (read (alarm_pipes [0], &c, 1) != 1) {
+ g_message ("alarm_ready(): Uh? Could not read from notification pipe.");
return;
}
- while (head_alarm){
- if (debug_alarms)
- debug_alarm (ar, ALARM_ACTIVATED);
- (*ar->fn)(ar->activation_time, ar->alarm, ar->closure);
- alarms = g_list_remove (alarms, head_alarm);
-
- /* Schedule next alarm */
- if (alarms){
- AlarmRecord *next;
-
- head_alarm = alarms->data;
- next = head_alarm;
-
- if (next->activation_time > now){
- struct itimerval itimer;
-
- itimer.it_interval.tv_sec = 0;
- itimer.it_interval.tv_usec = 0;
- itimer.it_value.tv_sec = next->activation_time - now;
- itimer.it_value.tv_usec = 0;
- setitimer (ITIMER_REAL, &itimer, NULL);
- break;
- } else {
- g_free (ar);
- ar = next;
- }
- } else
- head_alarm = NULL;
- }
+ g_assert (alarms != NULL);
+ ar = pop_alarm ();
+
+ g_message ("alarm_ready(): Notifying about alarm on %s", ctime (&ar->trigger));
+
+ (* ar->alarm_fn) (ar, ar->trigger, ar->data);
+
+ if (ar->destroy_notify_fn)
+ (* ar->destroy_notify_fn) (ar->data);
+
g_free (ar);
}
static int
-alarm_compare_by_time (gconstpointer a, gconstpointer b)
+compare_alarm_by_time (gconstpointer a, gconstpointer b)
{
const AlarmRecord *ara = a;
const AlarmRecord *arb = b;
time_t diff;
-
- diff = ara->activation_time - arb->activation_time;
+
+ diff = ara->trigger - arb->trigger;
return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}
+/* Adds an alarm to the queue and sets up the timer */
+static gboolean
+queue_alarm (time_t now, AlarmRecord *ar)
+{
+ time_t diff;
+ AlarmRecord *old_head;
+
+ if (alarms)
+ old_head = alarms->data;
+ else
+ old_head = NULL;
+
+ alarms = g_list_insert_sorted (alarms, ar, compare_alarm_by_time);
+
+ if (old_head == alarms->data)
+ return TRUE;
+
+ /* Set the timer for removal upon activation */
+
+ diff = ar->trigger - now;
+ if (!setup_itimer (diff)) {
+ GList *l;
+
+ g_message ("queue_alarm(): Could not set up timer! Not queueing alarm.");
+
+ l = g_list_find (alarms, ar);
+ g_assert (l != NULL);
+
+ alarms = g_list_remove_link (alarms, l);
+ g_list_free_1 (l);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/**
* alarm_add:
+ * @trigger: Time at which alarm will trigger.
+ * @alarm_fn: Callback for trigger.
+ * @data: Closure data for callback.
*
- * Tries to schedule @alarm.
+ * Adds an alarm to trigger at the specified time. The @alarm_fn will be called
+ * with the provided data and the alarm will be removed from the trigger list.
*
- * Returns TRUE if the alarm was scheduled.
- */
-gboolean
-alarm_add (CalendarAlarm *alarm, AlarmFunction fn, void *closure)
+ * Return value: An identifier for this alarm; it can be used to remove the
+ * alarm later with alarm_remove(). If the trigger time occurs in the past, then
+ * the alarm will not be queued and the function will return NULL.
+ **/
+gpointer
+alarm_add (time_t trigger, AlarmFunction alarm_fn, gpointer data,
+ AlarmDestroyNotify destroy_notify_fn)
{
- time_t now = time (NULL);
+ time_t now;
AlarmRecord *ar;
- time_t alarm_time = alarm->trigger;
-
- ar = g_new0 (AlarmRecord, 1);
- ar->activation_time = alarm_time;
- ar->fn = fn;
- ar->closure = closure;
- ar->alarm = alarm;
-
- /* If it already expired, do not add it */
- if (alarm_time < now) {
- if (debug_alarms)
- debug_alarm (ar, ALARM_NOT_ADDED);
- return FALSE;
- }
- alarms = g_list_insert_sorted (alarms, ar, alarm_compare_by_time);
+ now = time (NULL);
+ if (trigger < now)
+ return NULL;
- /* If first alarm is not the previous first alarm, reschedule SIGALRM */
- if (head_alarm != alarms->data){
- struct itimerval itimer;
- int v;
-
- /* Set the timer to disable upon activation */
- itimer.it_interval.tv_sec = 0;
- itimer.it_interval.tv_usec = 0;
- itimer.it_value.tv_sec = alarm_time - now;
- itimer.it_value.tv_usec = 0;
- v = setitimer (ITIMER_REAL, &itimer, NULL);
- head_alarm = alarms->data;
+ ar = g_new (AlarmRecord, 1);
+ ar->trigger = trigger;
+ ar->alarm_fn = alarm_fn;
+ ar->data = data;
+ ar->destroy_notify_fn = destroy_notify_fn;
+
+ g_message ("alarm_add(): Adding alarm for %s", ctime (&trigger));
+
+ if (!queue_alarm (now, ar)) {
+ if (ar->destroy_notify_fn)
+ (* ar->destroy_notify_fn) (ar->data);
+
+ g_free (ar);
+ ar = NULL;
}
- if (debug_alarms)
- debug_alarm (ar, ALARM_ADDED);
- return TRUE;
+
+ return ar;
}
-int
-alarm_kill (void *closure_key)
+/**
+ * alarm_remove:
+ * @alarm: A queued alarm identifier.
+ *
+ * Removes an alarm from the alarm queue.
+ **/
+void
+alarm_remove (gpointer alarm)
{
- GList *p;
-
- for (p = alarms; p; p = p->next){
- AlarmRecord *ar = p->data;
-
- if (ar->closure == closure_key){
- alarms = g_list_remove (alarms, p->data);
- if (alarms)
- head_alarm = alarms->data;
- else
- head_alarm = NULL;
- return 1;
- }
+ AlarmRecord *ar;
+ AlarmRecord *old_head;
+ GList *l;
+
+ ar = alarm;
+
+ l = g_list_find (alarms, ar);
+ if (!l) {
+ g_message ("alarm_remove(): Requested removal of nonexistent alarm!");
+ return;
}
- return 0;
+
+ old_head = alarms->data;
+
+ if (old_head == ar)
+ pop_alarm ();
+ else {
+ alarms = g_list_remove_link (alarms, l);
+ g_list_free_1 (l);
+ }
+
+ if (ar->destroy_notify_fn)
+ (* ar->destroy_notify_fn) (ar->data);
+
+ g_free (ar);
}
+/**
+ * alarm_init:
+ * @void:
+ *
+ * Initializes the alarm notification system. This must be called near the
+ * beginning of the program.
+ **/
void
alarm_init (void)
{
struct sigaction sa;
- struct sigaction debug_sa;
- int flags = 0;
-
+ int flags;
+
pipe (alarm_pipes);
-
+
/* set non blocking mode */
+ flags = 0;
fcntl (alarm_pipes [0], F_GETFL, &flags);
fcntl (alarm_pipes [0], F_SETFL, flags | O_NONBLOCK);
- gdk_input_add (alarm_pipes [0], GDK_INPUT_READ, alarm_ready, 0);
+ gdk_input_add (alarm_pipes [0], GDK_INPUT_READ, alarm_ready, NULL);
/* Setup the signal handler */
- sa.sa_handler = alarm_activate;
+ sa.sa_handler = alarm_signal;
sigemptyset (&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
+ sa.sa_flags = SA_RESTART;
sigaction (SIGALRM, &sa, NULL);
-
- /* Setup a signal handler to toggle debugging */
- debug_sa.sa_handler = toggle_debugging;
- sigemptyset (&debug_sa.sa_mask);
- debug_sa.sa_flags = SA_RESTART;
- sigaction (SIGUSR1, &debug_sa, NULL);
-}
-
-void
-debug_alarm (AlarmRecord* ar, enum DebugAction action)
-{
- time_t now = time (NULL);
- iCalObject *ico = ar->closure;
- printf ("%s", ctime(&now));
- switch (action) {
- case ALARM_ADDED:
- printf ("Added alarm for %s", ctime(&ar->activation_time));
- break;
- case ALARM_NOT_ADDED:
- printf ("Alarm not added for %s", ctime(&ar->activation_time));
- break;
- case ALARM_ACTIVATED:
- printf ("Activated alarm\n");
- break;
- }
-
- if (ar->fn!=&calendar_notify) return;
- printf ("--- Summary: %s\n", ico->summary);
- switch (ar->alarm->type) {
- case ALARM_MAIL:
- printf ("--- Type: Mail\n");
- break;
- case ALARM_PROGRAM:
- printf ("--- Type: Program\n");
- break;
- case ALARM_DISPLAY:
- printf ("--- Type: Display\n");
- break;
- case ALARM_AUDIO:
- printf ("--- Type: Audio\n");
- break;
- }
}
-
-