1 /* xscreensaver, Copyright (c) 2006-2015 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);
103 jwxyz_draw_NSImage_or_CGImage (dpy, window, True, image, 0, 0);
109 open_pipe (state *st)
111 /* This mess is because popen() doesn't give us the pid.
122 int timeout = get_integer_resource (st->dpy, "timeout", "Timeout");
123 int delay = get_integer_resource (st->dpy, "delay", "Delay");
124 float opacity = get_float_resource (st->dpy, "opacity", "Opacity");
125 char *filter = get_string_resource (st->dpy, "filter", "Filter");
126 char *filter2 = get_string_resource (st->dpy, "filter2", "Filter2");
128 av[ac++] = strdup ("webcollage");
129 av[ac++] = strdup ("-cocoa");
131 av[ac++] = strdup ("-size");
132 sprintf (buf, "%dx%d", st->xgwa.width, st->xgwa.height);
133 av[ac++] = strdup (buf);
135 av[ac++] = strdup ("-timeout"); sprintf (buf, "%d", timeout);
136 av[ac++] = strdup (buf);
137 av[ac++] = strdup ("-delay"); sprintf (buf, "%d", delay);
138 av[ac++] = strdup (buf);
139 av[ac++] = strdup ("-opacity"); sprintf (buf, "%.2f", opacity);
140 av[ac++] = strdup (buf);
142 if (filter && *filter) {
143 av[ac++] = strdup ("-filter");
146 if (filter2 && *filter2) {
147 av[ac++] = strdup ("-filter2");
155 fprintf (stderr, "webcollage: launching:");
157 for (i = 0; i < ac; i++)
158 fprintf (stderr, " %s", av[i]);
159 fprintf (stderr, "\n");
165 perror ("webcollage: error creating pipe");
172 switch ((int) (forked = fork ()))
176 perror ("webcollage: couldn't fork");
183 close (in); /* don't need this one */
185 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
187 perror ("could not dup() a new stdout:");
191 execvp (av[0], av); /* shouldn't return. */
195 /* Ignore "no such file or directory" errors, unless verbose.
196 Issue all other exec errors, though. */
197 sprintf (buf, "webcollage: %s", av[0]);
201 exit (1); /* exits fork */
206 st->pipe_fd = fdopen (in, "r");
207 close (out); /* don't need this one */
214 if (! st->pipe_fd) abort();
218 XtAppAddInput (XtDisplayToApplicationContext (st->dpy),
219 fileno (st->pipe_fd),
220 (XtPointer) (XtInputReadMask | XtInputExceptMask),
221 subproc_cb, (XtPointer) st);
224 fprintf (stderr, "webcollage: subprocess pid: %d\n", st->pid);
229 webcollage_init (Display *dpy, Window window)
231 state *st = (state *) calloc (1, sizeof(*st));
235 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
237 st->delay = 1000000; /* check once a second */
239 // Log to syslog when FPS is turned on.
240 st->verbose_p = get_boolean_resource (dpy, "doFPS", "DoFPS");
243 static int done_once = 0;
247 if (atexit (webcollage_atexit)) { // catch calls to exit()
248 perror ("webcollage: atexit");
252 int sigs[] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT,
253 SIGFPE, SIGBUS, SIGSEGV, SIGSYS, /*SIGPIPE,*/
255 for (i = 0; i < sizeof(sigs)/sizeof(*sigs); i++) {
256 if (signal (sigs[i], signal_handler)) {
257 perror ("webcollage: signal");
267 while (all_states[i]) i++;
275 webcollage_draw (Display *dpy, Window window, void *closure)
277 state *st = (state *) closure;
282 if (! input_available_p (fileno (st->pipe_fd)))
286 int n = read (fileno (st->pipe_fd),
291 XtRemoveInput (st->pipe_id);
293 // #### sometimes hangs -- pclose (st->pipe_fd);
297 fprintf (stderr, "webcollage: subprocess has exited: bailing.\n");
299 return st->delay * 10;
303 char *s = strchr (buf, '\n');
306 const char *target = "COCOA LOAD ";
307 if (!strncmp (target, buf, strlen(target))) {
308 const char *file = buf + strlen(target);
309 display_image (dpy, window, st, file);
317 webcollage_reshape (Display *dpy, Window window, void *closure,
318 unsigned int w, unsigned int h)
324 webcollage_event (Display *dpy, Window window, void *closure, XEvent *event)
331 webcollage_atexit (void)
335 if (all_states[0] && all_states[0]->verbose_p)
336 fprintf (stderr, "webcollage: atexit handler\n");
338 while (all_states[i]) {
339 state *st = all_states[i];
342 fprintf (stderr, "webcollage: kill %d\n", st->pid);
343 if (kill (st->pid, SIGTERM) < 0) {
344 fprintf (stderr, "webcollage: kill (%d, TERM): ", st->pid);
345 perror ("webcollage: kill");
356 signal_handler (int sig)
358 if (all_states[0] && all_states[0]->verbose_p)
359 fprintf (stderr, "webcollage: signal %d\n", sig);
360 webcollage_atexit ();
365 /* This is important because OSX doesn't actually kill the screen savers!
366 It just sends them a [ScreenSaverView stopAnimation] method. Were
367 they to actually exit, the resultant SIGPIPE should reap the children,
368 but instead, we need to do it here.
370 On top of that, there's an atexit() handler because otherwise the
371 inferior perl script process was failing to die when SaverTester or
372 System Preferences exited. I don't pretend to understand.
374 It still fails to clean up when I hit the stop button in Xcode.
378 webcollage_free (Display *dpy, Window window, void *closure)
380 state *st = (state *) closure;
383 fprintf (stderr, "webcollage: free cb\n");
385 // Dammit dammit dammit! Why won't this shit die when we pclose it!
386 // killpg (0, SIGTERM);
391 XtRemoveInput (st->pipe_id);
394 fclose (st->pipe_fd);
400 waitpid (-1, &wait_status, 0);
406 static const char *webcollage_defaults [] = {
407 ".background: black",
408 ".foreground: white",
418 static XrmOptionDescRec webcollage_options [] = {
419 { "-timeout", ".timeout", XrmoptionSepArg, 0 },
420 { "-delay", ".delay", XrmoptionSepArg, 0 },
421 { "-opacity", ".opacity", XrmoptionSepArg, 0 },
422 { "-filter", ".filter", XrmoptionSepArg, 0 },
423 { "-filter2", ".filter2", XrmoptionSepArg, 0 },
428 XSCREENSAVER_MODULE ("WebCollage", webcollage)