1 /*
2  * $Id: alarms.c,v 1.1 2005-09-18 22:05:41 dhmunro Exp $
3  * alarm event functions, implemented using play interface
4  */
5 /* Copyright (c) 2005, The Regents of the University of California.
6  * All rights reserved.
7  * This file is part of yorick (http://yorick.sourceforge.net).
8  * Read the accompanying LICENSE file for details.
9  */
10 
11 #include "config.h"
12 #include "play.h"
13 #include "pstdlib.h"
14 
15 typedef struct p_alarm p_alarm;
16 struct p_alarm {
17   p_alarm *next;
18   double time;
19   void (*on_alarm)(void *c);
20   void *context;
21 };
22 
23 static p_alarm *alarm_next = 0;
24 static p_alarm *alarm_free = 0;
25 static double alarm_query(void);
26 static int idle_eligible = 1;
27 
28 static int p_dflt_idle(void);
29 static int (*p_app_idle)(void)= &p_dflt_idle;
30 
31 void
p_idler(int (* on_idle)(void))32 p_idler(int (*on_idle)(void))
33 {
34   p_app_idle = on_idle;
35 }
36 
37 static int
p_dflt_idle(void)38 p_dflt_idle(void)
39 {
40   return 0;
41 }
42 
43 void
p_on_idle(int reset)44 p_on_idle(int reset)
45 {
46   if (!reset) {
47     if (alarm_next && !alarm_query()) {
48       /* alarm has rung - unlink it and call its on_alarm */
49       p_alarm *next = alarm_next;
50       alarm_next = next->next;
51       next->next = alarm_free;
52       alarm_free = next;
53       next->on_alarm(next->context);
54       idle_eligible = 1;
55     } else {
56       idle_eligible = p_app_idle();
57     }
58   } else {
59     idle_eligible = 1;
60   }
61 }
62 
63 double
p_timeout(void)64 p_timeout(void)
65 {
66   int eligible = idle_eligible;
67   idle_eligible = 1;
68   return eligible? 0.0 : (alarm_next? alarm_query() : -1.0);
69 }
70 
71 void
p_set_alarm(double secs,void (* on_alarm)(void * c),void * context)72 p_set_alarm(double secs, void (*on_alarm)(void *c), void *context)
73 {
74   p_alarm *me;
75   p_alarm *next = alarm_next;
76   p_alarm **prev = &alarm_next;
77   double time;
78   if (!alarm_free) {
79     int n = 8;
80     alarm_free = p_malloc(sizeof(p_alarm)*n);
81     alarm_free[--n].next = 0;
82     while (n--) alarm_free[n].next = &alarm_free[n+1];
83   }
84   me = alarm_free;
85   me->time = time = p_wall_secs() + secs;
86   me->on_alarm = on_alarm;
87   me->context = context;
88   /* insert me into alarm_next list, kept in order of time */
89   while (next && next->time<=time) {
90     prev = &next->next;
91     next = next->next;
92   }
93   alarm_free = alarm_free->next;
94   me->next = next;
95   *prev = me;
96 }
97 
98 void
p_clr_alarm(void (* on_alarm)(void * c),void * context)99 p_clr_alarm(void (*on_alarm)(void *c), void *context)
100 {
101   p_alarm *next, **prev = &alarm_next;
102   for (next=alarm_next ; next ; next=*prev) {
103     if ((!on_alarm || on_alarm==next->on_alarm) &&
104         (!context || context==next->context)) {
105       *prev = next->next;
106       next->next = alarm_free;
107       alarm_free = next;
108     } else {
109       prev = &next->next;
110     }
111   }
112 }
113 
114 static double
alarm_query(void)115 alarm_query(void)
116 {
117   if (alarm_next->time != -1.e35) {
118     double time = p_wall_secs();
119     p_alarm *next = alarm_next;
120     /* if no alarms need to ring yet, return earliest */
121     if (next->time > time)
122       return next->time - time;
123     do {
124       next->time = -1.e35;   /* mark all alarms that need to ring */
125       next = next->next;
126     } while (next && next->time<=time);
127   }
128   return 0.0;
129 }
130