1 /* xscreensaver, Copyright (c) 2006-2014 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 (Display *dpy, Window window, state *st, const char *file)
85 NSImage *image = [[NSImage alloc]
86 initWithContentsOfFile:
87 [NSString stringWithCString: file
88 encoding: NSUTF8StringEncoding]];
91 fprintf (stderr, "webcollage: failed to load \"%s\"\n", file);
95 CGFloat w = [image size].width;
96 CGFloat h = [image size].height;
97 if (w <= 1 || h <= 1) {
98 fprintf (stderr, "webcollage: unparsable image \"%s\"\n", file);
102 jwxyz_draw_NSImage_or_CGImage (dpy, window, True, image, 0, 0);
108 open_pipe (state *st)
110 /* This mess is because popen() doesn't give us the pid.
121 int timeout = get_integer_resource (st->dpy, "timeout", "Timeout");
122 int delay = get_integer_resource (st->dpy, "delay", "Delay");
123 float opacity = get_float_resource (st->dpy, "opacity", "Opacity");
124 char *filter = get_string_resource (st->dpy, "filter", "Filter");
125 char *filter2 = get_string_resource (st->dpy, "filter2", "Filter2");
127 av[ac++] = "webcollage";
131 sprintf (buf, "%dx%d", st->xgwa.width, st->xgwa.height);
132 av[ac++] = strdup (buf);
134 av[ac++] = "-timeout"; sprintf (buf, "%d", timeout);
135 av[ac++] = strdup (buf);
136 av[ac++] = "-delay"; sprintf (buf, "%d", delay);
137 av[ac++] = strdup (buf);
138 av[ac++] = "-opacity"; sprintf (buf, "%.2f", opacity);
139 av[ac++] = strdup (buf);
141 if (filter && *filter) {
142 av[ac++] = "-filter";
145 if (filter2 && *filter2) {
146 av[ac++] = "-filter2";
154 fprintf (stderr, "webcollage: launching:");
156 for (i = 0; i < ac; i++)
157 fprintf (stderr, " %s", av[i]);
158 fprintf (stderr, "\n");
164 perror ("webcollage: error creating pipe");
171 switch ((int) (forked = fork ()))
175 perror ("webcollage: couldn't fork");
182 close (in); /* don't need this one */
184 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
186 perror ("could not dup() a new stdout:");
190 execvp (av[0], av); /* shouldn't return. */
194 /* Ignore "no such file or directory" errors, unless verbose.
195 Issue all other exec errors, though. */
196 sprintf (buf, "webcollage: %s", av[0]);
200 exit (1); /* exits fork */
205 st->pipe_fd = fdopen (in, "r");
206 close (out); /* don't need this one */
210 if (! st->pipe_fd) abort();
214 XtAppAddInput (XtDisplayToApplicationContext (st->dpy),
215 fileno (st->pipe_fd),
216 (XtPointer) (XtInputReadMask | XtInputExceptMask),
217 subproc_cb, (XtPointer) st);
220 fprintf (stderr, "webcollage: subprocess pid: %d\n", st->pid);
225 webcollage_init (Display *dpy, Window window)
227 state *st = (state *) calloc (1, sizeof(*st));
231 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
233 st->delay = 1000000; /* check once a second */
235 // Log to syslog when FPS is turned on.
236 st->verbose_p = get_boolean_resource (dpy, "doFPS", "DoFPS");
239 static int done_once = 0;
243 if (atexit (webcollage_atexit)) { // catch calls to exit()
244 perror ("webcollage: atexit");
248 int sigs[] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT,
249 SIGFPE, SIGBUS, SIGSEGV, SIGSYS, /*SIGPIPE,*/
251 for (i = 0; i < sizeof(sigs)/sizeof(*sigs); i++) {
252 if (signal (sigs[i], signal_handler)) {
253 perror ("webcollage: signal");
263 while (all_states[i]) i++;
271 webcollage_draw (Display *dpy, Window window, void *closure)
273 state *st = (state *) closure;
278 if (! input_available_p (fileno (st->pipe_fd)))
282 int n = read (fileno (st->pipe_fd),
287 XtRemoveInput (st->pipe_id);
289 // #### sometimes hangs -- pclose (st->pipe_fd);
293 fprintf (stderr, "webcollage: subprocess has exited: bailing.\n");
295 return st->delay * 10;
299 char *s = strchr (buf, '\n');
302 const char *target = "COCOA LOAD ";
303 if (!strncmp (target, buf, strlen(target))) {
304 const char *file = buf + strlen(target);
305 display_image (dpy, window, st, file);
313 webcollage_reshape (Display *dpy, Window window, void *closure,
314 unsigned int w, unsigned int h)
320 webcollage_event (Display *dpy, Window window, void *closure, XEvent *event)
327 webcollage_atexit (void)
331 if (all_states[0] && all_states[0]->verbose_p)
332 fprintf (stderr, "webcollage: atexit handler\n");
334 while (all_states[i]) {
335 state *st = all_states[i];
338 fprintf (stderr, "webcollage: kill %d\n", st->pid);
339 if (kill (st->pid, SIGTERM) < 0) {
340 fprintf (stderr, "webcollage: kill (%d, TERM): ", st->pid);
341 perror ("webcollage: kill");
352 signal_handler (int sig)
354 if (all_states[0] && all_states[0]->verbose_p)
355 fprintf (stderr, "webcollage: signal %d\n", sig);
356 webcollage_atexit ();
361 /* This is important because OSX doesn't actually kill the screen savers!
362 It just sends them a [ScreenSaverView stopAnimation] method. Were
363 they to actually exit, the resultant SIGPIPE should reap the children,
364 but instead, we need to do it here.
366 On top of that, there's an atexit() handler because otherwise the
367 inferior perl script process was failing to die when SaverTester or
368 System Preferences exited. I don't pretend to understand.
370 It still fails to clean up when I hit the stop button in Xcode.
374 webcollage_free (Display *dpy, Window window, void *closure)
376 state *st = (state *) closure;
379 fprintf (stderr, "webcollage: free cb\n");
381 // Dammit dammit dammit! Why won't this shit die when we pclose it!
382 // killpg (0, SIGTERM);
387 XtRemoveInput (st->pipe_id);
390 fclose (st->pipe_fd);
396 waitpid (-1, &wait_status, 0);
402 static const char *webcollage_defaults [] = {
403 ".background: black",
404 ".foreground: white",
414 static XrmOptionDescRec webcollage_options [] = {
415 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
416 { "-delay", ".delay", XrmoptionSepArg, 0 },
417 { "-opacity", ".opacity", XrmoptionSepArg, 0 },
418 { "-filter", ".filter", XrmoptionSepArg, 0 },
419 { "-filter2", ".filter2", XrmoptionSepArg, 0 },
424 XSCREENSAVER_MODULE ("WebCollage", webcollage)