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 teh circularly linked list of all the timers.
35 */
36 ExpirationTimer::ExpirationTimer (Extension * in_extension)
37 {
38 locked = false;
39 extension = in_extension;
41 /* Fix Me! */
42 if (timer_list == NULL) {
43 next = this;
44 timer_list = this;
45 } else {
46 next = timer_list->next;
47 timer_list->next = this;
48 }
50 expiration.assign_current_time();
51 expiration += timeout;
53 if (!timer_started) {
54 Glib::signal_timeout().connect(sigc::ptr_fun(&timer_func), timeout * 1000 / TIMER_SCALE_VALUE);
55 timer_started = true;
56 }
58 return;
59 }
61 /** \brief Deletes a \c ExpirationTimer
63 The most complex thing that this function does is remove the timer
64 from the circularly linked list. If this is the only entry in the
65 list that is easy, otherwise all the entries must be found, and this
66 one removed from the list.
67 */
68 ExpirationTimer::~ExpirationTimer(void)
69 {
70 if (this != next) {
71 /* This will remove this entry from the circularly linked
72 list. */
73 ExpirationTimer * prev;
74 for (prev = timer_list;
75 prev->next != this;
76 prev = prev->next);
77 prev->next = next;
79 if (idle_start == this)
80 idle_start = next;
82 /* This may occur more than you think, just because the guy
83 doing most of the deletions is the idle function, who tracks
84 where it is looking using the \c timer_list variable. */
85 if (timer_list == this)
86 timer_list = next;
87 } else {
88 /* If we're the only entry in the list, the list needs to go
89 to NULL */
90 timer_list = NULL;
91 idle_start = NULL;
92 }
94 return;
95 }
97 /** \brief Touches the timer to extend the length before it expires
99 Basically it adds more time to the timer. One thing that is kinda
100 tricky is that it adds half the time remaining back into the timer.
101 This allows for some extensions that are used regularly to having
102 extended expiration times. So, in the end, they stay loaded longer.
103 Extensions that are only used once will expire at a standard rate
104 set by \c timeout.
105 */
106 void
107 ExpirationTimer::touch (void)
108 {
109 Glib::TimeVal current;
110 current.assign_current_time();
112 long time_left = (long)(expiration.as_double() - current.as_double());
113 if (time_left < 0) time_left = 0;
114 time_left /= 2;
116 expiration = current + timeout + time_left;
117 return;
118 }
120 /** \brief Check to see if the timer has expired
122 Checks the time against the current time.
123 */
124 bool
125 ExpirationTimer::expired (void) const
126 {
127 if (locked) return false;
129 Glib::TimeVal current;
130 current.assign_current_time();
131 return expiration < current;
132 }
134 // int idle_cnt = 0;
136 /** \brief This function goes in the idle loop to find expired extensions
137 \return Whether the function should be requeued or not
139 This function first insures that there is a timer list, and then checks
140 to see if the one on the top of the list has expired. If it has
141 expired it unloads the module. By unloading the module, the timer
142 gets deleted (happens in the unload function). If the list is
143 no empty, the function returns that it should be dequeued and sets
144 the \c timer_started variable so that the timer will be reissued when
145 a timer is added. If there is entries left, but the next one is
146 where this function started, then the timer is set up. The timer
147 will then re-add the idle loop function when it runs.
148 */
149 bool
150 ExpirationTimer::idle_func (void)
151 {
152 // std::cout << "Idle func pass: " << idle_cnt++ << " timer list: " << timer_list << std::endl;
154 /* see if this is the last */
155 if (timer_list == NULL) {
156 timer_started = false;
157 return false;
158 }
160 /* evalutate current */
161 if (timer_list->expired()) {
162 timer_list->extension->set_state(Extension::STATE_UNLOADED);
163 }
165 /* see if this is the last */
166 if (timer_list == NULL) {
167 timer_started = false;
168 return false;
169 }
171 if (timer_list->next == idle_start) {
172 /* if so, set up the timer and return FALSE */
173 /* Note: This may cause one to be missed on the evaluation if
174 the one before it expires and it is last in the list.
175 While this could be taken care of, it isn't worth the
176 complexity for this lazy removal that we're doing. It
177 should get picked up next time */
178 Glib::signal_timeout().connect(sigc::ptr_fun(&timer_func), timeout * 1000 / TIMER_SCALE_VALUE);
179 return false;
180 }
182 /* If nothing else, continue on */
183 timer_list = timer_list->next;
184 return true;
185 }
187 /** \brief A timer function to set up the idle function
188 \return Always false -- to disable the timer
190 This function sets up the idle loop when it runs. The idle loop is
191 the one that unloads all the extensions.
192 */
193 bool
194 ExpirationTimer::timer_func (void)
195 {
196 // std::cout << "Timer func" << std::endl;
197 idle_start = timer_list;
198 // idle_cnt = 0;
199 Glib::signal_idle().connect(sigc::ptr_fun(&idle_func));
200 return false;
201 }
203 }; }; /* namespace Inkscape, Extension */
205 /*
206 Local Variables:
207 mode:c++
208 c-file-style:"stroustrup"
209 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
210 indent-tabs-mode:nil
211 fill-column:99
212 End:
213 */
214 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :