1 /*
2 * Here is where the extensions can get timed on when they load and
3 * unload. All of the timing is done in here.
4 *
5 * Authors:
6 * Ted Gould <ted@gould.cx>
7 *
8 * Copyright (C) 2004 Authors
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
15 #include "extension.h"
16 #include "timer.h"
18 namespace Inkscape {
19 namespace Extension {
21 #define TIMER_SCALE_VALUE 20
23 ExpirationTimer * ExpirationTimer::timer_list = NULL;
24 ExpirationTimer * ExpirationTimer::idle_start = NULL;
25 long ExpirationTimer::timeout = 240;
26 bool ExpirationTimer::timer_started = false;
28 /** \brief Create a new expiration timer
29 \param in_extension Which extension this timer is related to
31 This function creates the timer, and sets the time to the current
32 time, plus what ever the current timeout is. Also, if this is
33 the first timer extension, the timer is kicked off. This function
34 also sets up the circularly linked list of all the timers.
35 */
36 ExpirationTimer::ExpirationTimer (Extension * in_extension):
37 locked(0),
38 extension(in_extension)
39 {
40 /* Fix Me! */
41 if (timer_list == NULL) {
42 next = this;
43 timer_list = this;
44 } else {
45 next = timer_list->next;
46 timer_list->next = this;
47 }
49 expiration.assign_current_time();
50 expiration += timeout;
52 if (!timer_started) {
53 Glib::signal_timeout().connect(sigc::ptr_fun(&timer_func), timeout * 1000 / TIMER_SCALE_VALUE);
54 timer_started = true;
55 }
57 return;
58 }
60 /** \brief Deletes a \c ExpirationTimer
62 The most complex thing that this function does is remove the timer
63 from the circularly linked list. If this is the only entry in the
64 list that is easy, otherwise all the entries must be found, and this
65 one removed from the list.
66 */
67 ExpirationTimer::~ExpirationTimer(void)
68 {
69 if (this != next) {
70 /* This will remove this entry from the circularly linked
71 list. */
72 ExpirationTimer * prev;
73 for (prev = timer_list;
74 prev->next != this;
75 prev = prev->next){};
76 prev->next = next;
78 if (idle_start == this)
79 idle_start = next;
81 /* This may occur more than you think, just because the guy
82 doing most of the deletions is the idle function, who tracks
83 where it is looking using the \c timer_list variable. */
84 if (timer_list == this)
85 timer_list = next;
86 } else {
87 /* If we're the only entry in the list, the list needs to go
88 to NULL */
89 timer_list = NULL;
90 idle_start = NULL;
91 }
93 return;
94 }
96 /** \brief Touches the timer to extend the length before it expires
98 Basically it adds more time to the timer. One thing that is kinda
99 tricky is that it adds half the time remaining back into the timer.
100 This allows for some extensions that are used regularly to having
101 extended expiration times. So, in the end, they stay loaded longer.
102 Extensions that are only used once will expire at a standard rate
103 set by \c timeout.
104 */
105 void
106 ExpirationTimer::touch (void)
107 {
108 Glib::TimeVal current;
109 current.assign_current_time();
111 long time_left = (long)(expiration.as_double() - current.as_double());
112 if (time_left < 0) time_left = 0;
113 time_left /= 2;
115 expiration = current + timeout + time_left;
116 return;
117 }
119 /** \brief Check to see if the timer has expired
121 Checks the time against the current time.
122 */
123 bool
124 ExpirationTimer::expired (void) const
125 {
126 if (locked > 0) return false;
128 Glib::TimeVal current;
129 current.assign_current_time();
130 return expiration < current;
131 }
133 // int idle_cnt = 0;
135 /** \brief This function goes in the idle loop to find expired extensions
136 \return Whether the function should be requeued or not
138 This function first insures that there is a timer list, and then checks
139 to see if the one on the top of the list has expired. If it has
140 expired it unloads the module. By unloading the module, the timer
141 gets deleted (happens in the unload function). If the list is
142 no empty, the function returns that it should be dequeued and sets
143 the \c timer_started variable so that the timer will be reissued when
144 a timer is added. If there is entries left, but the next one is
145 where this function started, then the timer is set up. The timer
146 will then re-add the idle loop function when it runs.
147 */
148 bool
149 ExpirationTimer::idle_func (void)
150 {
151 // std::cout << "Idle func pass: " << idle_cnt++ << " timer list: " << timer_list << std::endl;
153 /* see if this is the last */
154 if (timer_list == NULL) {
155 timer_started = false;
156 return false;
157 }
159 /* evalutate current */
160 if (timer_list->expired()) {
161 timer_list->extension->set_state(Extension::STATE_UNLOADED);
162 }
164 /* see if this is the last */
165 if (timer_list == NULL) {
166 timer_started = false;
167 return false;
168 }
170 if (timer_list->next == idle_start) {
171 /* if so, set up the timer and return FALSE */
172 /* Note: This may cause one to be missed on the evaluation if
173 the one before it expires and it is last in the list.
174 While this could be taken care of, it isn't worth the
175 complexity for this lazy removal that we're doing. It
176 should get picked up next time */
177 Glib::signal_timeout().connect(sigc::ptr_fun(&timer_func), timeout * 1000 / TIMER_SCALE_VALUE);
178 return false;
179 }
181 /* If nothing else, continue on */
182 timer_list = timer_list->next;
183 return true;
184 }
186 /** \brief A timer function to set up the idle function
187 \return Always false -- to disable the timer
189 This function sets up the idle loop when it runs. The idle loop is
190 the one that unloads all the extensions.
191 */
192 bool
193 ExpirationTimer::timer_func (void)
194 {
195 // std::cout << "Timer func" << std::endl;
196 idle_start = timer_list;
197 // idle_cnt = 0;
198 Glib::signal_idle().connect(sigc::ptr_fun(&idle_func));
199 return false;
200 }
202 }; }; /* namespace Inkscape, Extension */
204 /*
205 Local Variables:
206 mode:c++
207 c-file-style:"stroustrup"
208 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
209 indent-tabs-mode:nil
210 fill-column:99
211 End:
212 */
213 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :