1 /* xscreensaver, Copyright (c) 2006-2012 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.
15 //#define DEBUG_TIMERS
16 //#define DEBUG_SOURCES
18 /* If this is defined, we implement timers in terms of CFRunLoopTimerCreate.
19 But I couldn't get that to work right: when the left window was accepting
20 input, the right window would starve. So I implemented them in terms of
21 select() instead. This is more efficient anyway, since instead of the
22 event loop telling us that input is ready constantly, we only check once
23 just before the draw method is called.
25 #undef USE_COCOA_SOURCES
30 #ifdef USE_COCOA_SOURCES
31 # import <Cocoa/Cocoa.h>
35 #import "jwxyz-timers.h"
40 # define LOGT(str,arg1,arg2) NSLog(str,arg1,arg2)
42 # define LOGT(str,arg1,arg2)
46 # define LOGI(str,arg1,arg2,arg3) NSLog(str,arg1,arg2,arg3)
48 # define LOGI(str,arg1,arg2,arg3)
53 XtDisplayToApplicationContext (Display *dpy)
55 return (XtAppContext) dpy;
58 #define app_to_display(APP) ((Display *) (APP))
61 struct jwxyz_XtIntervalId {
62 CFRunLoopTimerRef cftimer;
65 XtTimerCallbackProc cb;
69 struct jwxyz_XtInputId {
70 # ifdef USE_COCOA_SOURCES
71 CFRunLoopSourceRef cfsource;
78 XtInputCallbackProc cb;
85 jwxyz_timer_retain (const void *arg)
87 struct jwxyz_XtIntervalId *data = (struct jwxyz_XtIntervalId *) arg;
89 LOGT(@"timer 0x%08X: retain %d", (unsigned int) data, data->refcount);
94 jwxyz_timer_release (const void *arg)
96 struct jwxyz_XtIntervalId *data = (struct jwxyz_XtIntervalId *) arg;
98 LOGT(@"timer 0x%08X: release %d", (unsigned int) data, data->refcount);
99 if (data->refcount < 0) abort();
100 if (data->refcount == 0) free (data);
104 jwxyz_source_retain (const void *arg)
106 struct jwxyz_XtInputId *data = (struct jwxyz_XtInputId *) arg;
108 LOGI(@"source 0x%08X %2d: retain %d", (unsigned int) data, data->fd,
114 jwxyz_source_release (const void *arg)
116 struct jwxyz_XtInputId *data = (struct jwxyz_XtInputId *) arg;
118 LOGI(@"source 0x%08X %2d: release %d", (unsigned int) data, data->fd,
120 if (data->refcount < 0) abort();
121 if (data->refcount == 0) {
122 # ifdef USE_COCOA_SOURCES
124 CFRelease (data->socket);
126 CFRelease (data->cfsource);
127 # endif /* USE_COCOA_SOURCES */
128 memset (data, 0xA1, sizeof(*data));
136 jwxyz_timer_cb (CFRunLoopTimerRef timer, void *arg)
138 struct jwxyz_XtIntervalId *data = (struct jwxyz_XtIntervalId *) arg;
139 LOGT(@"timer 0x%08X: fire", (unsigned int) data, 0);
140 data->cb (data->closure, &data);
142 // Our caller (__CFRunLoopDoTimer) will now call CFRunLoopTimerInvalidate,
143 // which will call jwxyz_timer_release.
147 #ifdef USE_COCOA_SOURCES
149 /* whether there is data available to be read on the file descriptor
152 input_available_p (int fd)
154 struct timeval tv = { 0, };
158 return select (fd+1, &fds, NULL, NULL, &tv);
163 jwxyz_source_cb (CFSocketRef s, CFSocketCallBackType type,
164 CFDataRef address, const void *call_data, void *info)
166 struct jwxyz_XtInputId *data = (struct jwxyz_XtInputId *) info;
168 if (type != kCFSocketReadCallBack) abort();
169 if (call_data != 0) abort(); // not used for kCFSocketRead
171 // We are sometimes called when there is not, in fact, data available!
172 // So don't call data->cb if we're being fed a pack of lies.
174 if (! input_available_p (data->fd)) {
175 LOGI(@"source 0x%08X %2d: false alarm!", (unsigned int) data, data->fd, 0);
179 LOGI(@"source 0x%08X %2d: fire", (unsigned int) data, data->fd, 0);
181 data->cb (data->closure, &data->fd, &data);
184 #endif /* USE_COCOA_SOURCES */
188 XtAppAddTimeOut (XtAppContext app, unsigned long msecs,
189 XtTimerCallbackProc cb, XtPointer closure)
191 struct jwxyz_XtIntervalId *data = (struct jwxyz_XtIntervalId *)
192 calloc (1, sizeof(*data));
194 data->closure = closure;
196 LOGT(@"timer 0x%08X: alloc %d", (unsigned int) data, msecs);
198 CFRunLoopTimerContext ctx = { 0, };
200 ctx.retain = jwxyz_timer_retain;
201 ctx.release = jwxyz_timer_release;
203 CFAbsoluteTime time = CFAbsoluteTimeGetCurrent() + (msecs / 1000.0);
206 CFRunLoopTimerCreate (NULL, // allocator
207 time, 0, 0, 0, // interval, flags, order
208 jwxyz_timer_cb, &ctx);
209 // CFRunLoopTimerCreate called jwxyz_timer_retain.
211 CFRunLoopAddTimer (CFRunLoopGetCurrent(), data->cftimer,
212 kCFRunLoopCommonModes);
218 XtRemoveTimeOut (XtIntervalId id)
220 LOGT(@"timer 0x%08X: remove", (unsigned int) id, 0);
221 if (id->refcount <= 0) abort();
222 if (!id->cftimer) abort();
224 CFRunLoopRemoveTimer (CFRunLoopGetCurrent(), id->cftimer,
225 kCFRunLoopCommonModes);
226 CFRunLoopTimerInvalidate (id->cftimer);
227 // CFRunLoopTimerInvalidate called jwxyz_timer_release.
231 #ifndef USE_COCOA_SOURCES
233 struct jwxyz_sources_data {
235 XtInputId ids[FD_SETSIZE];
239 jwxyz_sources_init (XtAppContext app)
241 jwxyz_sources_data *td = (jwxyz_sources_data *) calloc (1, sizeof (*td));
246 jwxyz_sources_free (jwxyz_sources_data *td)
253 jwxyz_source_select (XtInputId id)
255 jwxyz_sources_data *td = display_sources_data (app_to_display (id->app));
256 if (id->fd <= 0 || id->fd >= FD_SETSIZE) abort();
257 if (td->ids[id->fd]) abort();
258 td->ids[id->fd] = id;
263 jwxyz_source_deselect (XtInputId id)
265 jwxyz_sources_data *td = display_sources_data (app_to_display (id->app));
266 if (td->count <= 0) abort();
267 if (id->fd <= 0 || id->fd >= FD_SETSIZE) abort();
268 if (td->ids[id->fd] != id) abort();
274 jwxyz_sources_run (jwxyz_sources_data *td)
276 if (td->count == 0) return;
278 struct timeval tv = { 0, };
284 for (i = 0; i < FD_SETSIZE; i++) {
293 if (0 < select (max+1, &fds, NULL, NULL, &tv)) {
294 for (i = 0; i < FD_SETSIZE; i++) {
295 if (FD_ISSET (i, &fds)) {
296 XtInputId id = td->ids[i];
297 if (!id || !id->cb) abort();
298 if (id->fd != i) abort();
299 id->cb (id->closure, &id->fd, &id);
305 #endif /* !USE_COCOA_SOURCES */
309 XtAppAddInput (XtAppContext app, int fd, XtPointer flags,
310 XtInputCallbackProc cb, XtPointer closure)
312 struct jwxyz_XtInputId *data = (struct jwxyz_XtInputId *)
313 calloc (1, sizeof(*data));
316 data->closure = closure;
318 LOGI(@"source 0x%08X %2d: alloc", (unsigned int) data, data->fd, 0);
320 # ifdef USE_COCOA_SOURCES
322 CFSocketContext ctx = { 0, };
324 ctx.retain = jwxyz_source_retain;
325 ctx.release = jwxyz_source_release;
327 data->socket = CFSocketCreateWithNative (NULL, fd, kCFSocketReadCallBack,
328 jwxyz_source_cb, &ctx);
329 // CFSocketCreateWithNative called jwxyz_source_retain.
331 CFSocketSetSocketFlags (data->socket,
332 kCFSocketAutomaticallyReenableReadCallBack
334 // not kCFSocketCloseOnInvalidate.
336 data->cfsource = CFSocketCreateRunLoopSource (NULL, data->socket, 0);
338 CFRunLoopAddSource (CFRunLoopGetCurrent(), data->cfsource,
339 kCFRunLoopCommonModes);
341 # else /* !USE_COCOA_SOURCES */
344 jwxyz_source_retain (data);
345 jwxyz_source_select (data);
347 # endif /* !USE_COCOA_SOURCES */
353 XtRemoveInput (XtInputId id)
355 LOGI(@"source 0x%08X %2d: remove", (unsigned int) id, id->fd, 0);
356 if (id->refcount <= 0) abort();
357 # ifdef USE_COCOA_SOURCES
358 if (! id->cfsource) abort();
359 if (! id->socket) abort();
361 CFRunLoopRemoveSource (CFRunLoopGetCurrent(), id->cfsource,
362 kCFRunLoopCommonModes);
363 CFSocketInvalidate (id->socket);
364 // CFSocketInvalidate called jwxyz_source_release.
366 # else /* !USE_COCOA_SOURCES */
368 jwxyz_source_deselect (id);
369 jwxyz_source_release (id);
371 # endif /* !USE_COCOA_SOURCES */
375 jwxyz_XtRemoveInput_all (Display *dpy)
377 # ifdef USE_COCOA_SOURCES
379 # else /* !USE_COCOA_SOURCES */
381 jwxyz_sources_data *td = display_sources_data (dpy);
383 for (i = 0; i < FD_SETSIZE; i++) {
384 XtInputId id = td->ids[i];
385 if (id) XtRemoveInput (id);
388 # endif /* !USE_COCOA_SOURCES */
393 XtAppPending (XtAppContext app)
395 return XtIMAlternateInput; /* just always say yes */
399 XtAppProcessEvent (XtAppContext app, XtInputMask mask)
401 jwxyz_sources_data *td = display_sources_data (app_to_display (app));
402 jwxyz_sources_run (td);