1 /* xscreensaver, Copyright (c) 2006 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 OSX implementation of Xt timers, for libjwxyz.
16 #import <Cocoa/Cocoa.h>
18 #import "jwxyz-timers.h"
20 //#define DEBUG_TIMERS
21 //#define DEBUG_SOURCES
23 /* If this is defined, we implement timers in terms of CFRunLoopTimerCreate.
24 But I couldn't get that to work right: when the left window was accepting
25 input, the right window would starve. So I implemented them in terms of
26 select() instead. This is more efficient anyway, since instead of the
27 event loop telling us that input is ready constantly, we only check once
28 just before the draw method is called.
30 #undef USE_COCOA_SOURCES
34 # define LOGT(str,arg1,arg2) NSLog(str,arg1,arg2)
36 # define LOGT(str,arg1,arg2)
40 # define LOGI(str,arg1,arg2,arg3) NSLog(str,arg1,arg2,arg3)
42 # define LOGI(str,arg1,arg2,arg3)
47 XtDisplayToApplicationContext (Display *dpy)
49 return (XtAppContext) dpy;
52 #define app_to_display(APP) ((Display *) (APP))
55 struct jwxyz_XtIntervalId {
56 CFRunLoopTimerRef cftimer;
59 XtTimerCallbackProc cb;
63 struct jwxyz_XtInputId {
64 # ifdef USE_COCOA_SOURCES
65 CFRunLoopSourceRef cfsource;
72 XtInputCallbackProc cb;
79 jwxyz_timer_retain (const void *arg)
81 struct jwxyz_XtIntervalId *data = (struct jwxyz_XtIntervalId *) arg;
83 LOGT(@"timer 0x%08X: retain %d", (unsigned int) data, data->refcount);
88 jwxyz_timer_release (const void *arg)
90 struct jwxyz_XtIntervalId *data = (struct jwxyz_XtIntervalId *) arg;
92 LOGT(@"timer 0x%08X: release %d", (unsigned int) data, data->refcount);
93 if (data->refcount < 0) abort();
94 if (data->refcount == 0) free (data);
98 jwxyz_source_retain (const void *arg)
100 struct jwxyz_XtInputId *data = (struct jwxyz_XtInputId *) arg;
102 LOGI(@"source 0x%08X %2d: retain %d", (unsigned int) data, data->fd,
108 jwxyz_source_release (const void *arg)
110 struct jwxyz_XtInputId *data = (struct jwxyz_XtInputId *) arg;
112 LOGI(@"source 0x%08X %2d: release %d", (unsigned int) data, data->fd,
114 if (data->refcount < 0) abort();
115 if (data->refcount == 0) {
116 # ifdef USE_COCOA_SOURCES
118 CFRelease (data->socket);
120 CFRelease (data->cfsource);
121 # endif /* USE_COCOA_SOURCES */
122 memset (data, 0xA1, sizeof(*data));
130 jwxyz_timer_cb (CFRunLoopTimerRef timer, void *arg)
132 struct jwxyz_XtIntervalId *data = (struct jwxyz_XtIntervalId *) arg;
133 LOGT(@"timer 0x%08X: fire", (unsigned int) data, 0);
134 data->cb (data->closure, &data);
136 // Our caller (__CFRunLoopDoTimer) will now call CFRunLoopTimerInvalidate,
137 // which will call jwxyz_timer_release.
141 #ifdef USE_COCOA_SOURCES
143 /* whether there is data available to be read on the file descriptor
146 input_available_p (int fd)
148 struct timeval tv = { 0, };
152 return select (fd+1, &fds, NULL, NULL, &tv);
157 jwxyz_source_cb (CFSocketRef s, CFSocketCallBackType type,
158 CFDataRef address, const void *call_data, void *info)
160 struct jwxyz_XtInputId *data = (struct jwxyz_XtInputId *) info;
162 if (type != kCFSocketReadCallBack) abort();
163 if (call_data != 0) abort(); // not used for kCFSocketRead
165 // We are sometimes called when there is not, in fact, data available!
166 // So don't call data->cb if we're being fed a pack of lies.
168 if (! input_available_p (data->fd)) {
169 LOGI(@"source 0x%08X %2d: false alarm!", (unsigned int) data, data->fd, 0);
173 LOGI(@"source 0x%08X %2d: fire", (unsigned int) data, data->fd, 0);
175 data->cb (data->closure, &data->fd, &data);
178 #endif /* USE_COCOA_SOURCES */
182 XtAppAddTimeOut (XtAppContext app, unsigned long msecs,
183 XtTimerCallbackProc cb, XtPointer closure)
185 struct jwxyz_XtIntervalId *data = (struct jwxyz_XtIntervalId *)
186 calloc (1, sizeof(*data));
188 data->closure = closure;
190 LOGT(@"timer 0x%08X: alloc %d", (unsigned int) data, msecs);
192 CFRunLoopTimerContext ctx = { 0, };
194 ctx.retain = jwxyz_timer_retain;
195 ctx.release = jwxyz_timer_release;
197 CFAbsoluteTime time = CFAbsoluteTimeGetCurrent() + (msecs / 1000.0);
200 CFRunLoopTimerCreate (NULL, // allocator
201 time, 0, 0, 0, // interval, flags, order
202 jwxyz_timer_cb, &ctx);
203 // CFRunLoopTimerCreate called jwxyz_timer_retain.
205 CFRunLoopAddTimer (CFRunLoopGetCurrent(), data->cftimer,
206 kCFRunLoopCommonModes);
212 XtRemoveTimeOut (XtIntervalId id)
214 LOGT(@"timer 0x%08X: remove", (unsigned int) id, 0);
215 if (id->refcount <= 0) abort();
216 if (!id->cftimer) abort();
218 CFRunLoopRemoveTimer (CFRunLoopGetCurrent(), id->cftimer,
219 kCFRunLoopCommonModes);
220 CFRunLoopTimerInvalidate (id->cftimer);
221 // CFRunLoopTimerInvalidate called jwxyz_timer_release.
225 #ifndef USE_COCOA_SOURCES
227 struct jwxyz_sources_data {
229 XtInputId ids[FD_SETSIZE];
233 jwxyz_sources_init (XtAppContext app)
235 jwxyz_sources_data *td = (jwxyz_sources_data *) calloc (1, sizeof (*td));
240 jwxyz_sources_free (jwxyz_sources_data *td)
247 jwxyz_source_select (XtInputId id)
249 jwxyz_sources_data *td = display_sources_data (app_to_display (id->app));
250 if (id->fd <= 0 || id->fd >= FD_SETSIZE) abort();
251 if (td->ids[id->fd]) abort();
252 td->ids[id->fd] = id;
257 jwxyz_source_deselect (XtInputId id)
259 jwxyz_sources_data *td = display_sources_data (app_to_display (id->app));
260 if (td->count <= 0) abort();
261 if (id->fd <= 0 || id->fd >= FD_SETSIZE) abort();
262 if (td->ids[id->fd] != id) abort();
268 jwxyz_sources_run (jwxyz_sources_data *td)
270 if (td->count == 0) return;
272 struct timeval tv = { 0, };
278 for (i = 0; i < FD_SETSIZE; i++) {
287 if (0 < select (max+1, &fds, NULL, NULL, &tv)) {
288 for (i = 0; i < FD_SETSIZE; i++) {
289 if (FD_ISSET (i, &fds)) {
290 XtInputId id = td->ids[i];
291 if (!id || !id->cb) abort();
292 if (id->fd != i) abort();
293 id->cb (id->closure, &id->fd, &id);
299 #endif /* !USE_COCOA_SOURCES */
303 XtAppAddInput (XtAppContext app, int fd, XtPointer flags,
304 XtInputCallbackProc cb, XtPointer closure)
306 struct jwxyz_XtInputId *data = (struct jwxyz_XtInputId *)
307 calloc (1, sizeof(*data));
310 data->closure = closure;
312 LOGI(@"source 0x%08X %2d: alloc", (unsigned int) data, data->fd, 0);
314 # ifdef USE_COCOA_SOURCES
316 CFSocketContext ctx = { 0, };
318 ctx.retain = jwxyz_source_retain;
319 ctx.release = jwxyz_source_release;
321 data->socket = CFSocketCreateWithNative (NULL, fd, kCFSocketReadCallBack,
322 jwxyz_source_cb, &ctx);
323 // CFSocketCreateWithNative called jwxyz_source_retain.
325 CFSocketSetSocketFlags (data->socket,
326 kCFSocketAutomaticallyReenableReadCallBack
328 // not kCFSocketCloseOnInvalidate.
330 data->cfsource = CFSocketCreateRunLoopSource (NULL, data->socket, 0);
332 CFRunLoopAddSource (CFRunLoopGetCurrent(), data->cfsource,
333 kCFRunLoopCommonModes);
335 # else /* !USE_COCOA_SOURCES */
338 jwxyz_source_retain (data);
339 jwxyz_source_select (data);
341 # endif /* !USE_COCOA_SOURCES */
347 XtRemoveInput (XtInputId id)
349 LOGI(@"source 0x%08X %2d: remove", (unsigned int) id, id->fd, 0);
350 if (id->refcount <= 0) abort();
351 # ifdef USE_COCOA_SOURCES
352 if (! id->cfsource) abort();
353 if (! id->socket) abort();
355 CFRunLoopRemoveSource (CFRunLoopGetCurrent(), id->cfsource,
356 kCFRunLoopCommonModes);
357 CFSocketInvalidate (id->socket);
358 // CFSocketInvalidate called jwxyz_source_release.
360 # else /* !USE_COCOA_SOURCES */
362 jwxyz_source_deselect (id);
363 jwxyz_source_release (id);
365 # endif /* !USE_COCOA_SOURCES */
369 jwxyz_XtRemoveInput_all (Display *dpy)
371 # ifdef USE_COCOA_SOURCES
373 # else /* !USE_COCOA_SOURCES */
375 jwxyz_sources_data *td = display_sources_data (dpy);
377 for (i = 0; i < FD_SETSIZE; i++) {
378 XtInputId id = td->ids[i];
379 if (id) XtRemoveInput (id);
382 # endif /* !USE_COCOA_SOURCES */