1 /* xscreensaver, Copyright (c) 2006-2008 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 Cocoa shim for webcollage.
14 It runs the webcollage perl script
15 (in "WebCollage.saver/Contents/Resources/webcollage")
16 at the end of a pipe; each time that script updates the image file on
17 disk it prints the file name, and this program loads and displays that
20 The script uses "WebCollage.saver/Contents/Resources/webcollage-helper"
21 to paste the images together in the usual way.
28 #import <Cocoa/Cocoa.h>
30 #include "screenhack.h"
36 XWindowAttributes xgwa;
45 /* Violating the cardinal rule of "don't use global variables",
46 but we need to get at these from the atexit() handler, and
47 the callback doesn't take a closure arg. Because apparently
48 those hadn't been invented yet in the seventies.
50 static state *all_states[50] = { 0, };
52 static void webcollage_atexit (void);
53 static void signal_handler (int sig);
57 subproc_cb (XtPointer closure, int *source, XtInputId *id)
59 /* state *st = (state *) closure; */
60 /* st->input_available_p = True; */
64 /* whether there is data available to be read on the file descriptor
67 input_available_p (int fd)
69 struct timeval tv = { 0, };
72 /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */
75 memset (&fds, 0, sizeof(fds));
78 return select (fd+1, &fds, NULL, NULL, &tv);
83 display_image (state *st, const char *file)
85 NSImage *image = [[NSImage alloc]
86 initWithContentsOfFile:
87 [NSString stringWithCString: file
88 encoding: kCFStringEncodingUTF8]];
91 fprintf (stderr, "webcollage: failed to load \"%s\"\n", file);
95 [image drawAtPoint: NSMakePoint (0, 0)
96 fromRect: NSMakeRect (0, 0, [image size].width, [image size].height)
97 operation: NSCompositeCopy
104 open_pipe (state *st)
106 /* This mess is because popen() doesn't give us the pid.
117 int timeout = get_integer_resource (st->dpy, "timeout", "Timeout");
118 int delay = get_integer_resource (st->dpy, "delay", "Delay");
119 float opacity = get_float_resource (st->dpy, "opacity", "Opacity");
120 char *filter = get_string_resource (st->dpy, "filter", "Filter");
121 char *filter2 = get_string_resource (st->dpy, "filter2", "Filter2");
123 av[ac++] = "webcollage";
127 sprintf (buf, "%dx%d", st->xgwa.width, st->xgwa.height);
128 av[ac++] = strdup (buf);
130 av[ac++] = "-timeout"; sprintf (buf, "%d", timeout);
131 av[ac++] = strdup (buf);
132 av[ac++] = "-delay"; sprintf (buf, "%d", delay);
133 av[ac++] = strdup (buf);
134 av[ac++] = "-opacity"; sprintf (buf, "%.2f", opacity);
135 av[ac++] = strdup (buf);
137 if (filter && *filter) {
138 av[ac++] = "-filter";
141 if (filter2 && *filter2) {
142 av[ac++] = "-filter2";
150 fprintf (stderr, "webcollage: launching:");
152 for (i = 0; i < ac; i++)
153 fprintf (stderr, " %s", av[i]);
154 fprintf (stderr, "\n");
160 perror ("webcollage: error creating pipe");
167 switch ((int) (forked = fork ()))
171 perror ("webcollage: couldn't fork");
178 close (in); /* don't need this one */
180 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
182 perror ("could not dup() a new stdout:");
186 execvp (av[0], av); /* shouldn't return. */
190 /* Ignore "no such file or directory" errors, unless verbose.
191 Issue all other exec errors, though. */
192 sprintf (buf, "webcollage: %s", av[0]);
196 exit (1); /* exits fork */
201 st->pipe_fd = fdopen (in, "r");
202 close (out); /* don't need this one */
206 if (! st->pipe_fd) abort();
210 XtAppAddInput (XtDisplayToApplicationContext (st->dpy),
211 fileno (st->pipe_fd),
212 (XtPointer) (XtInputReadMask | XtInputExceptMask),
213 subproc_cb, (XtPointer) st);
216 fprintf (stderr, "webcollage: subprocess pid: %d\n", st->pid);
221 webcollage_init (Display *dpy, Window window)
223 state *st = (state *) calloc (1, sizeof(*st));
227 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
229 st->delay = 1000000; /* check once a second */
231 // Log to syslog when FPS is turned on.
232 st->verbose_p = get_boolean_resource (dpy, "doFPS", "DoFPS");
235 static int done_once = 0;
239 if (atexit (webcollage_atexit)) { // catch calls to exit()
240 perror ("webcollage: atexit");
244 int sigs[] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT,
245 SIGFPE, SIGBUS, SIGSEGV, SIGSYS, /*SIGPIPE,*/
247 for (i = 0; i < sizeof(sigs)/sizeof(*sigs); i++) {
248 if (signal (sigs[i], signal_handler)) {
249 perror ("webcollage: signal");
259 while (all_states[i]) i++;
267 webcollage_draw (Display *dpy, Window window, void *closure)
269 state *st = (state *) closure;
274 if (! input_available_p (fileno (st->pipe_fd)))
278 int n = read (fileno (st->pipe_fd),
283 XtRemoveInput (st->pipe_id);
285 // #### sometimes hangs -- pclose (st->pipe_fd);
289 fprintf (stderr, "webcollage: subprocess has exited: bailing.\n");
291 return st->delay * 10;
295 char *s = strchr (buf, '\n');
298 const char *target = "COCOA LOAD ";
299 if (!strncmp (target, buf, strlen(target))) {
300 const char *file = buf + strlen(target);
301 display_image (st, file);
309 webcollage_reshape (Display *dpy, Window window, void *closure,
310 unsigned int w, unsigned int h)
316 webcollage_event (Display *dpy, Window window, void *closure, XEvent *event)
323 webcollage_atexit (void)
327 if (all_states[0] && all_states[0]->verbose_p)
328 fprintf (stderr, "webcollage: atexit handler\n");
330 while (all_states[i]) {
331 state *st = all_states[i];
334 fprintf (stderr, "webcollage: kill %d\n", st->pid);
335 if (kill (st->pid, SIGTERM) < 0) {
336 fprintf (stderr, "webcollage: kill (%d, TERM): ", st->pid);
337 perror ("webcollage: kill");
348 signal_handler (int sig)
350 if (all_states[0] && all_states[0]->verbose_p)
351 fprintf (stderr, "webcollage: signal %d\n", sig);
352 webcollage_atexit ();
357 /* This is important because OSX doesn't actually kill the screen savers!
358 It just sends them a [ScreenSaverView stopAnimation] method. Were
359 they to actually exit, the resultant SIGPIPE should reap the children,
360 but instead, we need to do it here.
362 On top of that, there's an atexit() handler because otherwise the
363 inferior perl script process was failing to die when SaverTester or
364 System Preferences exited. I don't pretend to understand.
366 It still fails to clean up when I hit the stop button in Xcode.
370 webcollage_free (Display *dpy, Window window, void *closure)
372 state *st = (state *) closure;
375 fprintf (stderr, "webcollage: free cb\n");
377 // Dammit dammit dammit! Why won't this shit die when we pclose it!
378 // killpg (0, SIGTERM);
383 XtRemoveInput (st->pipe_id);
386 fclose (st->pipe_fd);
392 waitpid (-1, &wait_status, 0);
398 static const char *webcollage_defaults [] = {
399 ".background: black",
400 ".foreground: white",
410 static XrmOptionDescRec webcollage_options [] = {
411 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
412 { "-delay", ".delay", XrmoptionSepArg, 0 },
413 { "-opacity", ".opacity", XrmoptionSepArg, 0 },
414 { "-filter", ".filter", XrmoptionSepArg, 0 },
415 { "-filter2", ".filter2", XrmoptionSepArg, 0 },
420 XSCREENSAVER_MODULE ("WebCollage", webcollage)