1 /* xscreensaver, Copyright (c) 2006-2017 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* This is the portable implementation of Xt timers and inputs, for libjwxyz.
19 #ifdef HAVE_JWXYZ /* whole file */
28 #include <sys/select.h>
30 #include "jwxyz-timers.h"
33 extern void Log(const char *format, ...);
37 # define Log(S, ...) fprintf(stderr, "xscreensaver: " S "\n", __VA_ARGS__)
41 # define LOGT(...) Log(__VA_ARGS__)
47 # define LOGI(...) Log(__VA_ARGS__)
52 #define ASSERT_RET(C,S) do { \
54 jwxyz_abort ("jwxyz-timers: %s",(S)); \
58 #define ASSERT_RET0(C,S) do { \
60 jwxyz_abort ("jwxyz-timers: %s",(S)); \
66 XtDisplayToApplicationContext (Display *dpy)
68 return (XtAppContext) dpy;
71 #define app_to_display(APP) ((Display *) (APP))
73 #define DISPLAY_SOURCES_DATA(APP) \
74 JWXYZ_VTBL(app_to_display (APP))->display_sources_data (app_to_display (APP))
77 struct jwxyz_sources_data {
79 XtInputId ids[FD_SETSIZE];
80 XtIntervalId all_timers;
83 struct jwxyz_XtIntervalId {
88 XtTimerCallbackProc cb;
94 struct jwxyz_XtInputId {
98 XtInputCallbackProc cb;
108 # ifdef GETTIMEOFDAY_TWO_ARGS
110 gettimeofday(&now, &tzp);
115 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
120 jwxyz_sources_init (XtAppContext app)
122 jwxyz_sources_data *td = (jwxyz_sources_data *) calloc (1, sizeof (*td));
127 XtAppAddTimeOut (XtAppContext app, unsigned long msecs,
128 XtTimerCallbackProc cb, XtPointer closure)
130 jwxyz_sources_data *td = DISPLAY_SOURCES_DATA (app);
131 XtIntervalId data = (XtIntervalId) calloc (1, sizeof(*data));
132 double now = double_time();
135 data->closure = closure;
137 data->run_at = now + (msecs / 1000.0);
139 data->next = td->all_timers;
140 td->all_timers = data;
142 LOGT("timer 0x%08lX: alloc %lu %.2f", (unsigned long) data, msecs,
149 /* This is called both by the user to manually kill a timer,
150 and by the run loop after a timer has fired.
153 XtRemoveTimeOut (XtIntervalId data)
155 jwxyz_sources_data *td = DISPLAY_SOURCES_DATA (data->app);
157 LOGT("timer 0x%08lX: remove", (unsigned long) data);
158 ASSERT_RET (data->refcount > 0, "already freed");
161 LOGT("timer 0x%08lX: release %d", (unsigned long) data, data->refcount);
162 ASSERT_RET (data->refcount >= 0, "double free");
164 if (data->refcount == 0) {
166 /* Remove it from the list of live timers. */
167 XtIntervalId prev, timer;
169 for (timer = td->all_timers, prev = 0;
171 prev = timer, timer = timer->next) {
173 ASSERT_RET (!hit, "circular timer list");
175 prev->next = timer->next;
177 td->all_timers = timer->next;
181 ASSERT_RET (timer->refcount > 0, "timer list corrupted");
191 XtAppAddInput (XtAppContext app, int fd, XtPointer flags,
192 XtInputCallbackProc cb, XtPointer closure)
194 jwxyz_sources_data *td = DISPLAY_SOURCES_DATA (app);
195 XtInputId data = (XtInputId) calloc (1, sizeof(*data));
198 data->closure = closure;
202 LOGI("source 0x%08lX %2d: alloc", (unsigned long) data, data->fd);
204 ASSERT_RET0 (fd > 0 && fd < FD_SETSIZE, "fd out of range");
205 ASSERT_RET0 (td->ids[fd] == 0, "sources corrupted");
214 XtRemoveInput (XtInputId id)
216 jwxyz_sources_data *td = DISPLAY_SOURCES_DATA (id->app);
218 LOGI("source 0x%08lX %2d: remove", (unsigned long) id, id->fd);
219 ASSERT_RET (id->refcount > 0, "sources corrupted");
220 ASSERT_RET (td->fd_count > 0, "sources corrupted");
221 ASSERT_RET (id->fd > 0 && id->fd < FD_SETSIZE, "fd out of range");
222 ASSERT_RET (td->ids[id->fd] == id, "sources corrupted");
228 LOGI("source 0x%08lX %2d: release %d", (unsigned long) id, id->fd,
230 ASSERT_RET (id->refcount >= 0, "double free");
231 if (id->refcount == 0) {
232 memset (id, 0xA1, sizeof(*id));
240 jwxyz_timers_run (jwxyz_sources_data *td)
242 /* Iterate the timer list, being careful because XtRemoveTimeOut removes
243 the current item from that list. */
244 if (td->all_timers) {
245 XtIntervalId timer, next;
246 double now = double_time();
249 for (timer = td->all_timers, next = timer->next;
251 timer = next, next = (timer ? timer->next : 0)) {
252 if (timer->run_at <= now) {
253 LOGT("timer 0x%08lX: fire %.02f", (unsigned long) timer,
254 now - timer->run_at);
255 timer->cb (timer->closure, &timer);
256 XtRemoveTimeOut (timer);
258 ASSERT_RET (count < 10000, "way too many timers to run");
266 jwxyz_sources_run (jwxyz_sources_data *td)
268 if (td->fd_count == 0) return;
270 struct timeval tv = { 0, };
276 for (i = 0; i < FD_SETSIZE; i++) {
283 ASSERT_RET (max > 0, "no fds");
285 if (0 < select (max+1, &fds, NULL, NULL, &tv)) {
286 for (i = 0; i < FD_SETSIZE; i++) {
287 if (FD_ISSET (i, &fds)) {
288 XtInputId id = td->ids[i];
289 ASSERT_RET (id && id->cb, "sources corrupted");
290 ASSERT_RET (id->fd == i, "sources corrupted");
291 id->cb (id->closure, &id->fd, &id);
299 jwxyz_XtRemoveInput_all (jwxyz_sources_data *td)
302 for (i = 0; i < FD_SETSIZE; i++) {
303 XtInputId id = td->ids[i];
304 if (id) XtRemoveInput (id);
310 jwxyz_XtRemoveTimeOut_all (jwxyz_sources_data *td)
312 XtIntervalId timer, next;
315 /* Iterate the timer list, being careful because XtRemoveTimeOut removes
316 the current item from that list. */
317 if (td->all_timers) {
318 for (timer = td->all_timers, next = timer->next;
320 timer = next, next = (timer ? timer->next : 0)) {
321 XtRemoveTimeOut (timer);
323 ASSERT_RET (count < 10000, "way too many timers to free");
325 ASSERT_RET (!td->all_timers, "timer list didn't empty");
331 jwxyz_sources_free (jwxyz_sources_data *td)
333 jwxyz_XtRemoveInput_all (td);
334 jwxyz_XtRemoveTimeOut_all (td);
335 memset (td, 0xA1, sizeof(*td));
341 XtAppPending (XtAppContext app)
343 return XtIMAlternateInput; /* just always say yes */
347 XtAppProcessEvent (XtAppContext app, XtInputMask mask)
349 jwxyz_sources_data *td = DISPLAY_SOURCES_DATA (app);
350 if (mask & XtIMAlternateInput)
351 jwxyz_sources_run (td);
352 if (mask & XtIMTimer)
353 jwxyz_timers_run (td);
356 #endif /* HAVE_JWXYZ */