From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / driver / stderr.c
index 0c999e2fcfd55cdef991c2712069e15b3243b902..84fa697290d6268c34846fb36fb548ea56b8503a 100644 (file)
@@ -1,5 +1,5 @@
 /* stderr.c --- capturing stdout/stderr output onto the screensaver window.
- * xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@netscape.com>
+ * xscreensaver, Copyright (c) 1991-2016 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -47,9 +47,14 @@ static char stderr_buffer [4096];
 static char *stderr_tail = 0;
 static time_t stderr_last_read = 0;
 
+static int stderr_stdout_read_fd = -1;
+
 static void make_stderr_overlay_window (saver_screen_info *);
 
 
+/* Recreates the stderr window or GCs: do this when the xscreensaver window
+   on a screen has been re-created.
+ */
 void
 reset_stderr (saver_screen_info *ssi)
 {
@@ -57,7 +62,7 @@ reset_stderr (saver_screen_info *ssi)
 
   if (si->prefs.debug_p)
     fprintf ((real_stderr ? real_stderr : stderr),
-            "%s: resetting stderr\n", progname);
+            "%s: resetting stderr\n", blurb());
 
   ssi->stderr_text_x = 0;
   ssi->stderr_text_y = 0;
@@ -75,6 +80,10 @@ reset_stderr (saver_screen_info *ssi)
   ssi->stderr_cmap = 0;
 }
 
+/* Erases any stderr text overlaying the screen (if possible) and resets
+   the stderr output cursor to the upper left.  Do this when the xscreensaver
+   window is cleared.
+ */
 void
 clear_stderr (saver_screen_info *ssi)
 {
@@ -86,6 +95,8 @@ clear_stderr (saver_screen_info *ssi)
 }
 
 
+/* Draws the string on the screen's window.
+ */
 static void
 print_stderr_1 (saver_screen_info *ssi, char *string)
 {
@@ -102,12 +113,13 @@ print_stderr_1 (saver_screen_info *ssi, char *string)
 
   if (! ssi->stderr_font)
     {
-      char *font_name = get_string_resource ("font", "Font");
-      if (!font_name) font_name = "fixed";
+      char *font_name = get_string_resource (dpy, "font", "Font");
+      if (!font_name) font_name = strdup ("fixed");
       ssi->stderr_font = XLoadQueryFont (dpy, font_name);
       if (! ssi->stderr_font) ssi->stderr_font = XLoadQueryFont (dpy, "fixed");
       ssi->stderr_line_height = (ssi->stderr_font->ascent +
                                 ssi->stderr_font->descent);
+      free (font_name);
     }
 
   if (! ssi->stderr_gc)
@@ -117,7 +129,7 @@ print_stderr_1 (saver_screen_info *ssi, char *string)
       Colormap cmap = ssi->cmap;
 
       if (!ssi->stderr_overlay_window &&
-         get_boolean_resource("overlayStderr", "Boolean"))
+         get_boolean_resource(dpy, "overlayStderr", "Boolean"))
        {
          make_stderr_overlay_window (ssi);
          if (ssi->stderr_overlay_window)
@@ -126,8 +138,8 @@ print_stderr_1 (saver_screen_info *ssi, char *string)
            cmap = ssi->stderr_cmap;
        }
 
-      fg = get_pixel_resource ("overlayTextForeground","Foreground",dpy,cmap);
-      bg = get_pixel_resource ("overlayTextBackground","Background",dpy,cmap);
+      fg = get_pixel_resource (dpy,cmap,"overlayTextForeground","Foreground");
+      bg = get_pixel_resource (dpy,cmap,"overlayTextBackground","Background");
       gcv.font = ssi->stderr_font->fid;
       gcv.foreground = fg;
       gcv.background = bg;
@@ -163,15 +175,17 @@ print_stderr_1 (saver_screen_info *ssi, char *string)
              ssi->stderr_text_y = 0;
 #else
              int offset = ssi->stderr_line_height * 5;
+              XWindowAttributes xgwa;
+              XGetWindowAttributes (dpy, window, &xgwa);
+
              XCopyArea (dpy, window, window, ssi->stderr_gc,
                         0, v_border + offset,
-                        WidthOfScreen (screen),
-                        (HeightOfScreen (screen) - v_border - v_border
-                         - offset),
+                        xgwa.width,
+                        (xgwa.height - v_border - v_border - offset),
                         0, v_border);
              XClearArea (dpy, window,
-                         0, HeightOfScreen (screen) - v_border - offset,
-                         WidthOfScreen (screen), offset, False);
+                         0, xgwa.height - v_border - offset,
+                         xgwa.width, offset, False);
              ssi->stderr_text_y -= offset;
 #endif
            }
@@ -202,12 +216,14 @@ make_stderr_overlay_window (saver_screen_info *ssi)
     {
       int depth = visual_depth (ssi->screen, visual);
       XSetWindowAttributes attrs;
+      XWindowAttributes xgwa;
       unsigned long attrmask;
+      XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa);
 
       if (si->prefs.debug_p)
        fprintf(real_stderr,
                "%s: using overlay visual 0x%0x for stderr text layer.\n",
-               progname, (int) XVisualIDFromVisual (visual));
+               blurb(), (int) XVisualIDFromVisual (visual));
 
       ssi->stderr_cmap = XCreateColormap(si->dpy,
                                         RootWindowOfScreen(ssi->screen),
@@ -224,13 +240,15 @@ make_stderr_overlay_window (saver_screen_info *ssi)
 
       ssi->stderr_overlay_window =
        XCreateWindow(si->dpy, ssi->screensaver_window, 0, 0,
-                     WidthOfScreen(ssi->screen), HeightOfScreen(ssi->screen),
+                     xgwa.width, xgwa.height,
                      0, depth, InputOutput, visual, attrmask, &attrs);
       XMapRaised(si->dpy, ssi->stderr_overlay_window);
     }
 }
 
 
+/* Draws the string on each screen's window as error text.
+ */
 static void
 print_stderr (saver_info *si, char *string)
 {
@@ -246,6 +264,9 @@ print_stderr (saver_info *si, char *string)
 }
 
 
+/* Polls the stderr buffer every few seconds and if it finds any text,
+   writes it on all screens.
+ */
 static void
 stderr_popup_timer_fn (XtPointer closure, XtIntervalId *id)
 {
@@ -268,6 +289,9 @@ stderr_popup_timer_fn (XtPointer closure, XtIntervalId *id)
 }
 
 
+/* Called when data becomes available on the stderr pipe.  Copies it into
+   stderr_buffer where stderr_popup_timer_fn() can find it later.
+ */
 static void
 stderr_callback (XtPointer closure, int *fd, XtIntervalId *id)
 {
@@ -277,6 +301,9 @@ stderr_callback (XtPointer closure, int *fd, XtIntervalId *id)
   int size;
   int read_this_time = 0;
 
+  if (!fd || *fd < 0 || *fd != stderr_stdout_read_fd)
+    abort();
+
   if (stderr_tail == 0)
     stderr_tail = stderr_buffer;
 
@@ -329,6 +356,10 @@ stderr_callback (XtPointer closure, int *fd, XtIntervalId *id)
     }
 }
 
+/* If stderr capturing is desired, this replaces `stdout' and `stderr'
+   with a pipe, so that any output written to them will show up on the
+   screen as well as on the original value of those streams.
+ */
 void
 initialize_stderr (saver_info *si)
 {
@@ -339,7 +370,7 @@ initialize_stderr (saver_info *si)
   int stdout_fd = 1;
   int stderr_fd = 2;
   int flags = 0;
-  Boolean stderr_dialog_p, stdout_dialog_p;
+  Boolean stderr_dialog_p;
 
   if (done) return;
   done = True;
@@ -347,10 +378,9 @@ initialize_stderr (saver_info *si)
   real_stderr = stderr;
   real_stdout = stdout;
 
-  stderr_dialog_p = get_boolean_resource ("captureStderr", "Boolean");
-  stdout_dialog_p = get_boolean_resource ("captureStdout", "Boolean");
+  stderr_dialog_p = get_boolean_resource (si->dpy, "captureStderr", "Boolean");
 
-  if (!stderr_dialog_p && !stdout_dialog_p)
+  if (!stderr_dialog_p)
     return;
 
   if (pipe (fds))
@@ -392,6 +422,8 @@ initialize_stderr (saver_info *si)
   if (stderr_dialog_p)
     {
       FILE *new_stderr_file;
+      FILE *new_stdout_file;
+
       new_stderr = dup (stderr_fd);
       if (new_stderr < 0)
        {
@@ -411,11 +443,8 @@ initialize_stderr (saver_info *si)
          perror ("could not dup() a new stderr:");
          return;
        }
-    }
 
-  if (stdout_dialog_p)
-    {
-      FILE *new_stdout_file;
+
       new_stdout = dup (stdout_fd);
       if (new_stdout < 0)
        {
@@ -435,8 +464,97 @@ initialize_stderr (saver_info *si)
          perror ("could not dup() a new stdout:");
          return;
        }
+      close (out);
     }
 
+  stderr_stdout_read_fd = in;
   XtAppAddInput (si->app, in, (XtPointer) XtInputReadMask, stderr_callback,
                 (XtPointer) si);
 }
+
+
+/* If the "-log file" command-line option has been specified,
+   open the file for append, and redirect stdout/stderr there.
+   This is called very early, before initialize_stderr().
+ */
+void
+stderr_log_file (saver_info *si)
+{
+  int stdout_fd = 1;
+  int stderr_fd = 2;
+  const char *filename = get_string_resource (si->dpy, "logFile", "LogFile");
+  int fd;
+
+  if (!filename || !*filename) return;
+
+  fd = open (filename, O_WRONLY | O_APPEND | O_CREAT, 0666);
+
+  if (fd < 0)
+    {
+      char buf[255];
+    FAIL:
+      sprintf (buf, "%.100s: %.100s", blurb(), filename);
+      perror (buf);
+      fflush (stderr);
+      fflush (stdout);
+      exit (1);
+    }
+
+  fprintf (stderr, "%s: logging to file %s\n", blurb(), filename);
+
+  if (dup2 (fd, stdout_fd) < 0) goto FAIL;
+  if (dup2 (fd, stderr_fd) < 0) goto FAIL;
+
+  fprintf (stderr, "\n\n"
+ "##########################################################################\n"
+           "%s: logging to \"%s\" at %s\n"
+ "##########################################################################\n"
+           "\n",
+           blurb(), filename, timestring(0));
+}
+
+
+/* If there is anything in the stderr buffer, flush it to the real stderr.
+   This does no X operations.  Call this when exiting to make sure any
+   last words actually show up.
+ */
+void
+shutdown_stderr (saver_info *si)
+{
+  fflush (stdout);
+  fflush (stderr);
+
+  if (!real_stderr || stderr_stdout_read_fd < 0)
+    return;
+
+  stderr_callback ((XtPointer) si, &stderr_stdout_read_fd, 0);
+
+  if (stderr_tail &&
+      stderr_buffer < stderr_tail)
+    {
+      *stderr_tail = 0;
+      fprintf (real_stderr, "%s", stderr_buffer);
+      stderr_tail = stderr_buffer;
+    }
+
+  if (real_stdout) fflush (real_stdout);
+  if (real_stderr) fflush (real_stderr);
+
+  if (stdout != real_stdout)
+    {
+      dup2 (fileno(real_stdout), fileno(stdout));
+      fclose (real_stdout);
+      real_stdout = stdout;
+    }
+  if (stderr != real_stderr)
+    {
+      dup2 (fileno(real_stderr), fileno(stderr));
+      fclose (real_stderr);
+      real_stderr = stderr;
+    }
+  if (stderr_stdout_read_fd != -1)
+    {
+      close (stderr_stdout_read_fd);
+      stderr_stdout_read_fd = -1;
+    }
+}