+ const char *f = init_file_name();
+ char *b;
+ int list_elt;
+ GtkWidget *list;
+
+ if (!f || !*f) return 0;
+ b = (char *) malloc (strlen(f) + 1024);
+ sprintf (b,
+ _("Warning:\n\n"
+ "file \"%s\" has changed, reloading.\n"),
+ f);
+ warning_dialog (s->toplevel_widget, b, D_NONE, 100);
+ free (b);
+
+ load_init_file (dpy, p);
+ initialize_sort_map (s);
+
+ list_elt = selected_list_element (s);
+ list = name_to_widget (s, "list");
+ gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
+ populate_hack_list (s);
+ force_list_select_item (s, list, list_elt, True);
+ populate_prefs_page (s);
+ populate_demo_window (s, list_elt);
+ ensure_selected_item_visible (list);
+
+ status = 1;
+ }
+
+ reentrant_lock = False;
+ return status;
+}
+
+
+\f
+/* Making the preview window have the right X visual (so that GL works.)
+ */
+
+static Visual *get_best_gl_visual (state *);
+
+static GdkVisual *
+x_visual_to_gdk_visual (Visual *xv)
+{
+ GList *gvs = gdk_list_visuals();
+ if (!xv) return gdk_visual_get_system();
+ for (; gvs; gvs = gvs->next)
+ {
+ GdkVisual *gv = (GdkVisual *) gvs->data;
+ if (xv == GDK_VISUAL_XVISUAL (gv))
+ return gv;
+ }
+ fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
+ blurb(), (unsigned long) xv->visualid);
+ abort();
+}
+
+static void
+clear_preview_window (state *s)
+{
+ GtkWidget *p;
+ GdkWindow *window;
+ GtkStyle *style;
+
+ if (!s->toplevel_widget) return; /* very early */
+ p = name_to_widget (s, "preview");
+ window = GET_WINDOW (p);
+
+ if (!window) return;
+
+ /* Flush the widget background down into the window, in case a subproc
+ has changed it. */
+ style = gtk_widget_get_style (p);
+ gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
+ gdk_window_clear (window);
+
+ {
+ int list_elt = selected_list_element (s);
+ int hack_number = (list_elt >= 0
+ ? s->list_elt_to_hack_number[list_elt]
+ : -1);
+ Bool available_p = (hack_number >= 0
+ ? s->hacks_available_p [hack_number]
+ : True);
+ Bool nothing_p = (s->total_available < 5);
+
+#ifdef HAVE_GTK2
+ GtkWidget *notebook = name_to_widget (s, "preview_notebook");
+ gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
+ (s->running_preview_error_p
+ ? (available_p ? 1 :
+ nothing_p ? 3 : 2)
+ : 0));
+#else /* !HAVE_GTK2 */
+ if (s->running_preview_error_p)
+ {
+ const char * const lines1[] = { N_("No Preview"), N_("Available") };
+ const char * const lines2[] = { N_("Not"), N_("Installed") };
+ int nlines = countof(lines1);
+ int lh = p->style->font->ascent + p->style->font->descent;
+ int y, i;
+ gint w, h;
+
+ const char * const *lines = (available_p ? lines1 : lines2);
+
+ gdk_window_get_size (window, &w, &h);
+ y = (h - (lh * nlines)) / 2;
+ y += p->style->font->ascent;
+ for (i = 0; i < nlines; i++)
+ {
+ int sw = gdk_string_width (p->style->font, _(lines[i]));
+ int x = (w - sw) / 2;
+ gdk_draw_string (window, p->style->font,
+ p->style->fg_gc[GTK_STATE_NORMAL],
+ x, y, _(lines[i]));
+ y += lh;
+ }
+ }
+#endif /* !HAVE_GTK2 */
+ }
+
+ gdk_flush ();
+}
+
+
+static void
+reset_preview_window (state *s)
+{
+ /* On some systems (most recently, MacOS X) OpenGL programs get confused
+ when you kill one and re-start another on the same window. So maybe
+ it's best to just always destroy and recreate the preview window
+ when changing hacks, instead of always trying to reuse the same one?
+ */
+ GtkWidget *pr = name_to_widget (s, "preview");
+ if (GET_REALIZED (pr))
+ {
+ GdkWindow *window = GET_WINDOW (pr);
+ Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
+ Window id;
+ gtk_widget_hide (pr);
+ gtk_widget_unrealize (pr);
+ gtk_widget_realize (pr);
+ gtk_widget_show (pr);
+ id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
+ if (s->debug_p)
+ fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
+ (unsigned int) oid,
+ (unsigned int) id);
+ }
+}
+
+
+static void
+fix_preview_visual (state *s)
+{
+ GtkWidget *widget = name_to_widget (s, "preview");
+ Visual *xvisual = get_best_gl_visual (s);
+ GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
+ GdkVisual *dvisual = gdk_visual_get_system();
+ GdkColormap *cmap = (visual == dvisual
+ ? gdk_colormap_get_system ()
+ : gdk_colormap_new (visual, False));
+
+ if (s->debug_p)
+ fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
+ (visual == dvisual ? "default" : "non-default"),
+ (xvisual ? (unsigned long) xvisual->visualid : 0L));
+
+ if (!GET_REALIZED (widget) ||
+ gtk_widget_get_visual (widget) != visual)
+ {
+ gtk_widget_unrealize (widget);
+ gtk_widget_set_visual (widget, visual);
+ gtk_widget_set_colormap (widget, cmap);
+ gtk_widget_realize (widget);
+ }
+
+ /* Set the Widget colors to be white-on-black. */
+ {
+ GdkWindow *window = GET_WINDOW (widget);
+ GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
+ GdkColormap *cmap = gtk_widget_get_colormap (widget);
+ GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
+ GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
+ GdkGC *fgc = gdk_gc_new(window);
+ GdkGC *bgc = gdk_gc_new(window);
+ if (!gdk_color_white (cmap, fg)) abort();
+ if (!gdk_color_black (cmap, bg)) abort();
+ gdk_gc_set_foreground (fgc, fg);
+ gdk_gc_set_background (fgc, bg);
+ gdk_gc_set_foreground (bgc, bg);
+ gdk_gc_set_background (bgc, fg);
+ style->fg_gc[GTK_STATE_NORMAL] = fgc;
+ style->bg_gc[GTK_STATE_NORMAL] = fgc;
+ gtk_widget_set_style (widget, style);
+
+ /* For debugging purposes, put a title on the window (so that
+ it can be easily found in the output of "xwininfo -tree".)
+ */
+ gdk_window_set_title (window, "Preview");
+ }
+
+ gtk_widget_show (widget);
+}
+
+\f
+/* Subprocesses
+ */
+
+static char *
+subproc_pretty_name (state *s)
+{
+ if (s->running_preview_cmd)
+ {
+ char *ps = strdup (s->running_preview_cmd);
+ char *ss = strchr (ps, ' ');
+ if (ss) *ss = 0;
+ ss = strrchr (ps, '/');
+ if (!ss)
+ ss = ps;
+ else
+ {
+ ss = strdup (ss+1);
+ free (ps);
+ }
+ return ss;
+ }
+ else
+ return strdup ("???");
+}
+
+
+static void
+reap_zombies (state *s)
+{
+ int wait_status = 0;
+ pid_t pid;
+ while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
+ {
+ if (s->debug_p)
+ {
+ if (pid == s->running_preview_pid)
+ {
+ char *ss = subproc_pretty_name (s);
+ fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
+ (unsigned long) pid, ss);
+ free (ss);
+ }
+ else
+ fprintf (stderr, "%s: pid %lu died\n", blurb(),
+ (unsigned long) pid);
+ }
+ }
+}
+
+
+/* Mostly lifted from driver/subprocs.c */
+static Visual *
+get_best_gl_visual (state *s)
+{
+ Display *dpy = GDK_DISPLAY();
+ pid_t forked;
+ int fds [2];
+ int in, out;
+ char buf[1024];
+
+ char *av[10];
+ int ac = 0;
+
+ av[ac++] = "xscreensaver-gl-helper";
+ av[ac] = 0;
+
+ if (pipe (fds))
+ {
+ perror ("error creating pipe:");
+ return 0;
+ }
+
+ in = fds [0];
+ out = fds [1];
+
+ switch ((int) (forked = fork ()))
+ {
+ case -1:
+ {
+ sprintf (buf, "%s: couldn't fork", blurb());
+ perror (buf);
+ exit (1);
+ }
+ case 0:
+ {
+ int stdout_fd = 1;
+
+ close (in); /* don't need this one */
+ close (ConnectionNumber (dpy)); /* close display fd */
+
+ if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
+ {
+ perror ("could not dup() a new stdout:");
+ return 0;
+ }
+
+ execvp (av[0], av); /* shouldn't return. */
+
+ if (errno != ENOENT)
+ {
+ /* Ignore "no such file or directory" errors, unless verbose.
+ Issue all other exec errors, though. */
+ sprintf (buf, "%s: running %s", blurb(), av[0]);
+ perror (buf);
+ }
+
+ /* Note that one must use _exit() instead of exit() in procs forked
+ off of Gtk programs -- Gtk installs an atexit handler that has a
+ copy of the X connection (which we've already closed, for safety.)
+ If one uses exit() instead of _exit(), then one sometimes gets a
+ spurious "Gdk-ERROR: Fatal IO error on X server" error message.
+ */
+ _exit (1); /* exits fork */
+ break;
+ }
+ default:
+ {
+ int result = 0;
+ int wait_status = 0;
+
+ FILE *f = fdopen (in, "r");
+ unsigned int v = 0;
+ char c;
+
+ close (out); /* don't need this one */
+
+ *buf = 0;
+ if (!fgets (buf, sizeof(buf)-1, f))
+ *buf = 0;
+ fclose (f);
+
+ /* Wait for the child to die. */
+ waitpid (-1, &wait_status, 0);
+
+ if (1 == sscanf (buf, "0x%x %c", &v, &c))
+ result = (int) v;
+
+ if (result == 0)
+ {
+ if (s->debug_p)
+ fprintf (stderr, "%s: %s did not report a GL visual!\n",
+ blurb(), av[0]);
+ return 0;
+ }
+ else
+ {
+ Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
+ if (s->debug_p)
+ fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
+ blurb(), av[0], result);
+ if (!v) abort();
+ return v;
+ }
+ }
+ }
+
+ abort();
+}
+
+
+static void
+kill_preview_subproc (state *s, Bool reset_p)
+{
+ s->running_preview_error_p = False;
+
+ reap_zombies (s);
+ clear_preview_window (s);
+
+ if (s->subproc_check_timer_id)
+ {
+ gtk_timeout_remove (s->subproc_check_timer_id);
+ s->subproc_check_timer_id = 0;
+ s->subproc_check_countdown = 0;
+ }
+
+ if (s->running_preview_pid)
+ {
+ int status = kill (s->running_preview_pid, SIGTERM);
+ char *ss = subproc_pretty_name (s);
+
+ if (status < 0)