1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2  * xscreensaver, Copyright (c) 1993-2020 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or
10  * implied warranty.
11  */
12 
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16 
17 #ifdef HAVE_GTK /* whole file */
18 
19 #include <xscreensaver-intl.h>
20 
21 #include <stdlib.h>
22 
23 #ifdef HAVE_UNISTD_H
24 # include <unistd.h>
25 #endif
26 
27 # ifdef __GNUC__
28 #  define STFU __extension__  /* ignore gcc -pendantic warnings in next sexp */
29 # else
30 #  define STFU /* */
31 # endif
32 
33 
34 #ifdef ENABLE_NLS
35 # include <locale.h>
36 #endif /* ENABLE_NLS */
37 
38 #ifndef VMS
39 # include <pwd.h>		/* for getpwuid() */
40 #else /* VMS */
41 # include "vms-pwd.h"
42 #endif /* VMS */
43 
44 #ifdef HAVE_UNAME
45 # include <sys/utsname.h>	/* for uname() */
46 #endif /* HAVE_UNAME */
47 
48 #include <stdio.h>
49 #include <time.h>
50 #include <sys/stat.h>
51 #include <sys/time.h>
52 
53 
54 #include <signal.h>
55 #include <errno.h>
56 #ifdef HAVE_SYS_WAIT_H
57 # include <sys/wait.h>		/* for waitpid() and associated macros */
58 #endif
59 
60 
61 #include <X11/Xproto.h>		/* for CARD32 */
62 #include <X11/Xatom.h>		/* for XA_INTEGER */
63 #include <X11/Intrinsic.h>
64 #include <X11/StringDefs.h>
65 
66 /* We don't actually use any widget internals, but these are included
67    so that gdb will have debug info for the widgets... */
68 #include <X11/IntrinsicP.h>
69 #include <X11/ShellP.h>
70 
71 #ifdef HAVE_XMU
72 # ifndef VMS
73 #  include <X11/Xmu/Error.h>
74 # else /* VMS */
75 #  include <Xmu/Error.h>
76 # endif
77 #else
78 # include "xmu.h"
79 #endif
80 
81 #ifdef HAVE_XINERAMA
82 # include <X11/extensions/Xinerama.h>
83 #endif /* HAVE_XINERAMA */
84 
85 #include <gtk/gtk.h>
86 
87 #ifdef HAVE_CRAPPLET
88 # include <gnome.h>
89 # include <capplet-widget.h>
90 #endif
91 
92 #include <gdk/gdkx.h>
93 
94 #ifdef HAVE_GTK2
95 # include <glade/glade-xml.h>
96 # include <gmodule.h>
97 #else  /* !HAVE_GTK2 */
98 # define G_MODULE_EXPORT /**/
99 #endif /* !HAVE_GTK2 */
100 
101 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
102 # define GLADE_DIR DEFAULT_ICONDIR
103 #endif
104 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
105 # define DEFAULT_ICONDIR GLADE_DIR
106 #endif
107 
108 #ifndef HAVE_XML
109  /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
110     It is unused otherwise, so in that case, stub it out. */
111  static const char *hack_configuration_path = 0;
112 #endif
113 
114 
115 
116 #include "version.h"
117 #include "prefs.h"
118 #include "resources.h"		/* for parse_time() */
119 #include "visual.h"		/* for has_writable_cells() */
120 #include "remote.h"		/* for xscreensaver_command() */
121 #include "usleep.h"
122 
123 #include "logo-50.xpm"
124 #include "logo-180.xpm"
125 
126 #include "demo-Gtk-conf.h"
127 
128 #include <stdio.h>
129 #include <string.h>
130 #include <ctype.h>
131 
132 #ifdef HAVE_GTK2
133 enum {
134   COL_ENABLED,
135   COL_NAME,
136   COL_LAST
137 };
138 #endif /* HAVE_GTK2 */
139 
140 /* Deal with deprecation of direct access to struct fields on the way to GTK3
141    See http://live.gnome.org/GnomeGoals/UseGseal
142  */
143 #if GTK_CHECK_VERSION(2,14,0)
144 # define GET_PARENT(w)          gtk_widget_get_parent (w)
145 # define GET_WINDOW(w)          gtk_widget_get_window (w)
146 # define GET_ACTION_AREA(d)     gtk_dialog_get_action_area (d)
147 # define GET_CONTENT_AREA(d)    gtk_dialog_get_content_area (d)
148 # define GET_ADJ_VALUE(a)       gtk_adjustment_get_value (a)
149 # define SET_ADJ_VALUE(a,v)     gtk_adjustment_set_value (a, v)
150 # define SET_ADJ_UPPER(a,v)     gtk_adjustment_set_upper (a, v)
151 #else
152 # define GET_PARENT(w)          ((w)->parent)
153 # define GET_WINDOW(w)          ((w)->window)
154 # define GET_ACTION_AREA(d)     ((d)->action_area)
155 # define GET_CONTENT_AREA(d)    ((d)->vbox)
156 # define GET_ADJ_VALUE(a)       ((a)->value)
157 # define SET_ADJ_VALUE(a,v)     (a)->value = v
158 # define SET_ADJ_UPPER(a,v)     (a)->upper = v
159 #endif
160 
161 #if GTK_CHECK_VERSION(2,18,0)
162 # define SET_CAN_DEFAULT(w)     gtk_widget_set_can_default ((w), TRUE)
163 # define GET_SENSITIVE(w)       gtk_widget_get_sensitive (w)
164 #else
165 # define SET_CAN_DEFAULT(w)     GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT)
166 # define GET_SENSITIVE(w)       GTK_WIDGET_IS_SENSITIVE (w)
167 #endif
168 
169 #if GTK_CHECK_VERSION(2,20,0)
170 # define GET_REALIZED(w)        gtk_widget_get_realized (w)
171 #else
172 # define GET_REALIZED(w)        GTK_WIDGET_REALIZED (w)
173 #endif
174 
175 /* from exec.c */
176 extern void exec_command (const char *shell, const char *command, int nice);
177 extern int on_path_p (const char *program);
178 
179 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
180 
181 #undef countof
182 #define countof(x) (sizeof((x))/sizeof((*x)))
183 
184 
185 /* You might think that to read an array of 32-bit quantities out of a
186    server-side property, you would pass an array of 32-bit data quantities
187    into XGetWindowProperty().  You would be wrong.  You have to use an array
188    of longs, even if long is 64 bits (using 32 of each 64.)
189  */
190 typedef long PROP32;
191 
192 char *progname = 0;
193 char *progclass = "XScreenSaver";
194 XrmDatabase db;
195 
196 /* The order of the items in the mode menu. */
197 static int mode_menu_order[] = {
198   DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
199 
200 
201 typedef struct {
202 
203   char *short_version;		/* version number of this xscreensaver build */
204 
205   GtkWidget *toplevel_widget;	/* the main window */
206   GtkWidget *base_widget;	/* root of our hierarchy (for name lookups) */
207   GtkWidget *popup_widget;	/* the "Settings" dialog */
208   conf_data *cdata;		/* private data for per-hack configuration */
209 
210 #ifdef HAVE_GTK2
211   GladeXML *glade_ui;           /* Glade UI file */
212 #endif /* HAVE_GTK2 */
213 
214   Bool debug_p;			/* whether to print diagnostics */
215   Bool initializing_p;		/* flag for breaking recursion loops */
216   Bool saving_p;		/* flag for breaking recursion loops */
217 
218   char *desired_preview_cmd;	/* subprocess we intend to run */
219   char *running_preview_cmd;	/* subprocess we are currently running */
220   pid_t running_preview_pid;	/* pid of forked subproc (might be dead) */
221   Bool running_preview_error_p;	/* whether the pid died abnormally */
222 
223   Bool preview_suppressed_p;	/* flag meaning "don't launch subproc" */
224   int subproc_timer_id;		/* timer to delay subproc launch */
225   int subproc_check_timer_id;	/* timer to check whether it started up */
226   int subproc_check_countdown;  /* how many more checks left */
227 
228   int *list_elt_to_hack_number;	/* table for sorting the hack list */
229   int *hack_number_to_list_elt;	/* the inverse table */
230   Bool *hacks_available_p;	/* whether hacks are on $PATH */
231   int total_available;		/* how many are on $PATH */
232   int list_count;		/* how many items are in the list: this may be
233                                    less than p->screenhacks_count, if some are
234                                    suppressed. */
235 
236   int _selected_list_element;	/* don't use this: call
237                                    selected_list_element() instead */
238 
239   int nscreens;			/* How many X or Xinerama screens there are */
240 
241   saver_preferences prefs;
242 
243 } state;
244 
245 
246 /* Total fucking evilness due to the fact that it's rocket science to get
247    a closure object of our own down into the various widget callbacks. */
248 static state *global_state_kludge;
249 
250 Atom XA_VROOT;
251 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
252 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
253 Atom XA_ACTIVATE, XA_SUSPEND, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
254 Atom XA_NEXT, XA_PREV;
255 
256 
257 static void populate_demo_window (state *, int list_elt);
258 static void populate_prefs_page (state *);
259 static void populate_popup_window (state *);
260 
261 static Bool flush_dialog_changes_and_save (state *);
262 static Bool flush_popup_changes_and_save (state *);
263 
264 static int maybe_reload_init_file (state *);
265 static void await_xscreensaver (state *);
266 static Bool xscreensaver_running_p (state *);
267 static void sensitize_menu_items (state *s, Bool force_p);
268 static void force_dialog_repaint (state *s);
269 
270 static void schedule_preview (state *, const char *cmd);
271 static void kill_preview_subproc (state *, Bool reset_p);
272 static void schedule_preview_check (state *);
273 
274 
275 /* Prototypes of functions used by the Glade-generated code,
276    to avoid warnings.
277  */
278 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
279 void about_menu_cb (GtkMenuItem *, gpointer user_data);
280 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
281 void file_menu_cb (GtkMenuItem *, gpointer user_data);
282 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
283 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
284 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
285 void restart_menu_cb (GtkWidget *, gpointer user_data);
286 void run_this_cb (GtkButton *, gpointer user_data);
287 void manual_cb (GtkButton *, gpointer user_data);
288 void run_next_cb (GtkButton *, gpointer user_data);
289 void run_prev_cb (GtkButton *, gpointer user_data);
290 void pref_changed_cb (GtkWidget *, gpointer user_data);
291 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
292 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
293 void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
294                      gint page_num, gpointer user_data);
295 void browse_image_dir_cb (GtkButton *, gpointer user_data);
296 void browse_text_file_cb (GtkButton *, gpointer user_data);
297 void browse_text_program_cb (GtkButton *, gpointer user_data);
298 void settings_cb (GtkButton *, gpointer user_data);
299 void settings_adv_cb (GtkButton *, gpointer user_data);
300 void settings_std_cb (GtkButton *, gpointer user_data);
301 void settings_reset_cb (GtkButton *, gpointer user_data);
302 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
303                               gint page_num, gpointer user_data);
304 void settings_cancel_cb (GtkButton *, gpointer user_data);
305 void settings_ok_cb (GtkButton *, gpointer user_data);
306 
307 static void kill_gnome_screensaver (void);
308 static void kill_kde_screensaver (void);
309 
310 
311 /* Some random utility functions
312  */
313 
314 const char *blurb (void);
315 
316 const char *
blurb(void)317 blurb (void)
318 {
319   time_t now = time ((time_t *) 0);
320   char *ct = (char *) ctime (&now);
321   static char buf[255];
322   int n = strlen(progname);
323   if (n > 100) n = 99;
324   strncpy(buf, progname, n);
325   buf[n++] = ':';
326   buf[n++] = ' ';
327   strncpy(buf+n, ct+11, 8);
328   strcpy(buf+n+9, ": ");
329   return buf;
330 }
331 
332 
333 static GtkWidget *
name_to_widget(state * s,const char * name)334 name_to_widget (state *s, const char *name)
335 {
336   GtkWidget *w;
337   if (!s) abort();
338   if (!name) abort();
339   if (!*name) abort();
340 
341 #ifdef HAVE_GTK2
342   if (!s->glade_ui)
343     {
344       /* First try to load the Glade file from the current directory;
345          if there isn't one there, check the installed directory.
346        */
347 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
348       const char * const files[] = { GLADE_FILE_NAME,
349                                      GLADE_DIR "/" GLADE_FILE_NAME };
350       int i;
351       for (i = 0; i < countof (files); i++)
352         {
353           struct stat st;
354           if (!stat (files[i], &st))
355             {
356               s->glade_ui = glade_xml_new (files[i], NULL, NULL);
357               break;
358             }
359         }
360       if (!s->glade_ui)
361 	{
362 	  fprintf (stderr,
363                    "%s: could not load \"" GLADE_FILE_NAME "\"\n"
364                    "\tfrom " GLADE_DIR "/ or current directory.\n",
365                    blurb());
366           exit (-1);
367 	}
368 # undef GLADE_FILE_NAME
369 
370       glade_xml_signal_autoconnect (s->glade_ui);
371     }
372 
373   w = glade_xml_get_widget (s->glade_ui, name);
374 
375 #else /* !HAVE_GTK2 */
376 
377   w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
378                                          name);
379   if (w) return w;
380   w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
381                                          name);
382 #endif /* HAVE_GTK2 */
383   if (w) return w;
384 
385   fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
386            blurb(), name);
387   abort();
388 }
389 
390 
391 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
392    Takes a scroller, viewport, or list as an argument.
393  */
394 static void
ensure_selected_item_visible(GtkWidget * widget)395 ensure_selected_item_visible (GtkWidget *widget)
396 {
397 #ifdef HAVE_GTK2
398   GtkTreePath *path;
399   GtkTreeSelection *selection;
400   GtkTreeIter iter;
401   GtkTreeModel *model;
402 
403   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
404   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
405 	path = gtk_tree_path_new_first ();
406   else
407 	path = gtk_tree_model_get_path (model, &iter);
408 
409   gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
410 
411   gtk_tree_path_free (path);
412 
413 #else /* !HAVE_GTK2 */
414 
415   GtkScrolledWindow *scroller = 0;
416   GtkViewport *vp = 0;
417   GtkList *list_widget = 0;
418   GList *slist;
419   GList *kids;
420   int nkids = 0;
421   GtkWidget *selected = 0;
422   int list_elt = -1;
423   GtkAdjustment *adj;
424   gint parent_h, child_y, child_h, children_h, ignore;
425   double ratio_t, ratio_b;
426 
427   if (GTK_IS_SCROLLED_WINDOW (widget))
428     {
429       scroller = GTK_SCROLLED_WINDOW (widget);
430       vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
431       list_widget = GTK_LIST (GTK_BIN(vp)->child);
432     }
433   else if (GTK_IS_VIEWPORT (widget))
434     {
435       vp = GTK_VIEWPORT (widget);
436       scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
437       list_widget = GTK_LIST (GTK_BIN(vp)->child);
438     }
439   else if (GTK_IS_LIST (widget))
440     {
441       list_widget = GTK_LIST (widget);
442       vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
443       scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
444     }
445   else
446     abort();
447 
448   slist = list_widget->selection;
449   selected = (slist ? GTK_WIDGET (slist->data) : 0);
450   if (!selected)
451     return;
452 
453   list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
454 
455   for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
456        kids; kids = kids->next)
457     nkids++;
458 
459   adj = gtk_scrolled_window_get_vadjustment (scroller);
460 
461   gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (vp)),
462                            &ignore, &ignore, &ignore, &parent_h, &ignore);
463   gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (selected)),
464                            &ignore, &child_y, &ignore, &child_h, &ignore);
465   children_h = nkids * child_h;
466 
467   ratio_t = ((double) child_y) / ((double) children_h);
468   ratio_b = ((double) child_y + child_h) / ((double) children_h);
469 
470   if (adj->upper == 0.0)  /* no items in list */
471     return;
472 
473   if (ratio_t < (adj->value / adj->upper) ||
474       ratio_b > ((adj->value + adj->page_size) / adj->upper))
475     {
476       double target;
477       int slop = parent_h * 0.75; /* how much to overshoot by */
478 
479       if (ratio_t < (adj->value / adj->upper))
480         {
481           double ratio_w = ((double) parent_h) / ((double) children_h);
482           double ratio_l = (ratio_b - ratio_t);
483           target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
484           target += slop;
485         }
486       else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
487         {
488           target = ratio_t * adj->upper;
489           target -= slop;
490         }
491 
492       if (target > adj->upper - adj->page_size)
493         target = adj->upper - adj->page_size;
494       if (target < 0)
495         target = 0;
496 
497       gtk_adjustment_set_value (adj, target);
498     }
499 #endif /* !HAVE_GTK2 */
500 }
501 
502 static void
warning_dialog_dismiss_cb(GtkWidget * widget,gpointer user_data)503 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
504 {
505   GtkWidget *shell = GTK_WIDGET (user_data);
506   while (GET_PARENT (shell))
507     shell = GET_PARENT (shell);
508   gtk_widget_destroy (GTK_WIDGET (shell));
509 }
510 
511 
512 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
513 
warning_dialog_restart_cb(GtkWidget * widget,gpointer user_data)514 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
515 {
516   restart_menu_cb (widget, user_data);
517   warning_dialog_dismiss_cb (widget, user_data);
518 }
519 
warning_dialog_killg_cb(GtkWidget * widget,gpointer user_data)520 static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
521 {
522   kill_gnome_screensaver ();
523   warning_dialog_dismiss_cb (widget, user_data);
524 }
525 
warning_dialog_killk_cb(GtkWidget * widget,gpointer user_data)526 static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
527 {
528   kill_kde_screensaver ();
529   warning_dialog_dismiss_cb (widget, user_data);
530 }
531 
532 typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
533 
534 static Bool
warning_dialog(GtkWidget * parent,const char * message,dialog_button button_type,int center)535 warning_dialog (GtkWidget *parent, const char *message,
536                 dialog_button button_type, int center)
537 {
538   char *msg = strdup (message);
539   char *head;
540 
541   GtkWidget *dialog = gtk_dialog_new ();
542   GtkWidget *label = 0;
543   GtkWidget *ok = 0;
544   GtkWidget *cancel = 0;
545   int i = 0;
546 
547   while (parent && !GET_WINDOW (parent))
548     parent = GET_PARENT (parent);
549 
550   if (!parent ||
551       !GET_WINDOW (parent)) /* too early to pop up transient dialogs */
552     {
553       fprintf (stderr,
554                "%s: too early for warning dialog?"
555                "\n\n\t%s\n\n",
556                progname, message);
557       free(msg);
558       return False;
559     }
560 
561   head = msg;
562   while (head)
563     {
564       char name[20];
565       char *s = strchr (head, '\n');
566       if (s) *s = 0;
567 
568       sprintf (name, "label%d", i++);
569 
570       {
571         label = gtk_label_new (head);
572 #ifdef HAVE_GTK2
573 	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
574 #endif /* HAVE_GTK2 */
575 
576 #ifndef HAVE_GTK2
577         if (i == 1)
578           {
579             GTK_WIDGET (label)->style =
580               gtk_style_copy (GTK_WIDGET (label)->style);
581             GTK_WIDGET (label)->style->font =
582               gdk_font_load (get_string_resource("warning_dialog.headingFont",
583                                                  "Dialog.Font"));
584             gtk_widget_set_style (GTK_WIDGET (label),
585                                   GTK_WIDGET (label)->style);
586           }
587 #endif /* !HAVE_GTK2 */
588         if (center <= 0)
589           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
590         gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
591                             label, TRUE, TRUE, 0);
592         gtk_widget_show (label);
593       }
594 
595       if (s)
596 	head = s+1;
597       else
598 	head = 0;
599 
600       center--;
601     }
602 
603   label = gtk_label_new ("");
604   gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
605                       label, TRUE, TRUE, 0);
606   gtk_widget_show (label);
607 
608   label = gtk_hbutton_box_new ();
609   gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
610                       label, TRUE, TRUE, 0);
611 
612 #ifdef HAVE_GTK2
613   if (button_type != D_NONE)
614     {
615       cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
616       gtk_container_add (GTK_CONTAINER (label), cancel);
617     }
618 
619   ok = gtk_button_new_from_stock (GTK_STOCK_OK);
620   gtk_container_add (GTK_CONTAINER (label), ok);
621 
622 #else /* !HAVE_GTK2 */
623 
624   ok = gtk_button_new_with_label ("OK");
625   gtk_container_add (GTK_CONTAINER (label), ok);
626 
627   if (button_type != D_NONE)
628     {
629       cancel = gtk_button_new_with_label ("Cancel");
630       gtk_container_add (GTK_CONTAINER (label), cancel);
631     }
632 
633 #endif /* !HAVE_GTK2 */
634 
635   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
636   gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
637   gtk_window_set_title (GTK_WINDOW (dialog), progclass);
638   SET_CAN_DEFAULT (ok);
639   gtk_widget_show (ok);
640   gtk_widget_grab_focus (ok);
641 
642   if (cancel)
643     {
644       SET_CAN_DEFAULT (cancel);
645       gtk_widget_show (cancel);
646     }
647   gtk_widget_show (label);
648   gtk_widget_show (dialog);
649 
650   if (button_type != D_NONE)
651     {
652       GtkSignalFunc fn;
653       switch (button_type) {
654       case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
655       case D_GNOME:  fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb);   break;
656       case D_KDE:    fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb);   break;
657       default: abort(); break;
658       }
659       gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn,
660                                  (gpointer) dialog);
661       gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
662                                  GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
663                                  (gpointer) dialog);
664     }
665   else
666     {
667       gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
668                                  GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
669                                  (gpointer) dialog);
670     }
671 
672   gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
673                                 GET_WINDOW (GTK_WIDGET (parent)));
674 
675 #ifdef HAVE_GTK2
676   gtk_window_present (GTK_WINDOW (dialog));
677 #else  /* !HAVE_GTK2 */
678   gdk_window_show (GTK_WIDGET (dialog)->window);
679   gdk_window_raise (GTK_WIDGET (dialog)->window);
680 #endif /* !HAVE_GTK2 */
681 
682   free (msg);
683   return True;
684 }
685 
686 
687 static void
run_cmd(state * s,Atom command,int arg)688 run_cmd (state *s, Atom command, int arg)
689 {
690   char *err = 0;
691   int status;
692 
693   flush_dialog_changes_and_save (s);
694   status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
695 
696   /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
697   if (status < 0 && err && strstr (err, "unexpectedly deleted"))
698     status = 0;
699 
700   if (status < 0)
701     {
702       char buf [255];
703       if (err)
704         sprintf (buf, "Error:\n\n%s", err);
705       else
706         strcpy (buf, "Unknown error!");
707       warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
708     }
709   if (err) free (err);
710 
711   sensitize_menu_items (s, True);
712   force_dialog_repaint (s);
713 }
714 
715 
716 static void
run_hack(state * s,int list_elt,Bool report_errors_p)717 run_hack (state *s, int list_elt, Bool report_errors_p)
718 {
719   int hack_number;
720   char *err = 0;
721   int status;
722 
723   if (list_elt < 0) return;
724   hack_number = s->list_elt_to_hack_number[list_elt];
725 
726   flush_dialog_changes_and_save (s);
727   schedule_preview (s, 0);
728 
729   status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
730                                  False, &err);
731 
732   if (status < 0 && report_errors_p)
733     {
734       if (xscreensaver_running_p (s))
735         {
736           /* Kludge: ignore the spurious "window unexpectedly deleted"
737              errors... */
738           if (err && strstr (err, "unexpectedly deleted"))
739             status = 0;
740 
741           if (status < 0)
742             {
743               char buf [255];
744               if (err)
745                 sprintf (buf, "Error:\n\n%s", err);
746               else
747                 strcpy (buf, "Unknown error!");
748               warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
749             }
750         }
751       else
752         {
753           /* The error is that the daemon isn't running;
754              offer to restart it.
755            */
756           const char *d = DisplayString (GDK_DISPLAY());
757           char msg [1024];
758           sprintf (msg,
759                    _("Warning:\n\n"
760                      "The XScreenSaver daemon doesn't seem to be running\n"
761                      "on display \"%s\".  Launch it now?"),
762                    d);
763           warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
764         }
765     }
766 
767   if (err) free (err);
768 
769   sensitize_menu_items (s, False);
770 }
771 
772 
773 
774 /* Button callbacks
775 
776    According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
777    libglade work on Cygwin; apparently all Glade callbacks need this magic
778    extra declaration.  I do not pretend to understand.
779  */
780 
781 G_MODULE_EXPORT void
exit_menu_cb(GtkMenuItem * menuitem,gpointer user_data)782 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
783 {
784   state *s = global_state_kludge;  /* I hate C so much... */
785   flush_dialog_changes_and_save (s);
786   kill_preview_subproc (s, False);
787   gtk_main_quit ();
788 }
789 
790 static gboolean
wm_toplevel_close_cb(GtkWidget * widget,GdkEvent * event,gpointer data)791 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
792 {
793   state *s = (state *) data;
794   flush_dialog_changes_and_save (s);
795   gtk_main_quit ();
796   return TRUE;
797 }
798 
799 
800 G_MODULE_EXPORT void
about_menu_cb(GtkMenuItem * menuitem,gpointer user_data)801 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
802 {
803   char msg [2048];
804   char *vers = strdup (screensaver_id + 4);
805   char *s, *s2;
806   char copy[1024];
807   char year[5];
808   char *desc = _("For updates, check https://www.jwz.org/xscreensaver/");
809 
810   s = strchr (vers, ',');
811   *s = 0;
812   s += 2;
813 
814   s2 = vers;
815   s2 = strrchr (vers, '-');
816   s2++;
817   strncpy (year, s2, 4);
818   year[4] = 0;
819 
820   /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
821      non-ASCII characters aren't allowed in localizable string keys."
822      (I don't want to just use (c) instead of � because that doesn't
823      look as good in the plain-old default Latin1 "C" locale.)
824    */
825 #ifdef HAVE_GTK2
826   sprintf(copy, ("Copyright \xC2\xA9 1991-%s %s"), year, s);
827 #else  /* !HAVE_GTK2 */
828   sprintf(copy, ("Copyright \251 1991-%s %s"), year, s);
829 #endif /* !HAVE_GTK2 */
830 
831   sprintf (msg, "%s\n\n%s", copy, desc);
832 
833   /* I can't make gnome_about_new() work here -- it starts dying in
834      gdk_imlib_get_visual() under gnome_about_new().  If this worked,
835      then this might be the thing to do:
836 
837      #ifdef HAVE_CRAPPLET
838      {
839        const gchar *auth[] = { 0 };
840        GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
841                                            "xscreensaver.xpm");
842        gtk_widget_show (about);
843      }
844      #else / * GTK but not GNOME * /
845       ...
846    */
847   {
848     GdkColormap *colormap;
849     GdkPixmap *gdkpixmap;
850     GdkBitmap *mask;
851 
852     GtkWidget *dialog = gtk_dialog_new ();
853     GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
854     GtkWidget *parent = GTK_WIDGET (menuitem);
855     while (GET_PARENT (parent))
856       parent = GET_PARENT (parent);
857 
858     hbox = gtk_hbox_new (FALSE, 20);
859     gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
860                         hbox, TRUE, TRUE, 0);
861 
862     colormap = gtk_widget_get_colormap (parent);
863     gdkpixmap =
864       gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
865                                              (gchar **) logo_180_xpm);
866     icon = gtk_pixmap_new (gdkpixmap, mask);
867     gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
868 
869     gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
870 
871     vbox = gtk_vbox_new (FALSE, 0);
872     gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
873 
874     label1 = gtk_label_new (vers);
875     gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
876     gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
877     gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
878 
879 #ifndef HAVE_GTK2
880     GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
881     GTK_WIDGET (label1)->style->font =
882       gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
883     gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
884 #endif /* HAVE_GTK2 */
885 
886     label2 = gtk_label_new (msg);
887     gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
888     gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
889     gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
890 
891 #ifndef HAVE_GTK2
892     GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
893     GTK_WIDGET (label2)->style->font =
894       gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
895     gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
896 #endif /* HAVE_GTK2 */
897 
898     hb = gtk_hbutton_box_new ();
899 
900     gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
901                         hb, TRUE, TRUE, 0);
902 
903 #ifdef HAVE_GTK2
904     ok = gtk_button_new_from_stock (GTK_STOCK_OK);
905 #else /* !HAVE_GTK2 */
906     ok = gtk_button_new_with_label (_("OK"));
907 #endif /* !HAVE_GTK2 */
908     gtk_container_add (GTK_CONTAINER (hb), ok);
909 
910     gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
911     gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
912     gtk_window_set_title (GTK_WINDOW (dialog), progclass);
913 
914     gtk_widget_show (hbox);
915     gtk_widget_show (icon);
916     gtk_widget_show (vbox);
917     gtk_widget_show (label1);
918     gtk_widget_show (label2);
919     gtk_widget_show (hb);
920     gtk_widget_show (ok);
921     gtk_widget_show (dialog);
922 
923     gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
924                                GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
925                                (gpointer) dialog);
926     gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
927                                   GET_WINDOW (GTK_WIDGET (parent)));
928     gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
929     gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
930   }
931 }
932 
933 
934 G_MODULE_EXPORT void
doc_menu_cb(GtkMenuItem * menuitem,gpointer user_data)935 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
936 {
937   state *s = global_state_kludge;  /* I hate C so much... */
938   saver_preferences *p = &s->prefs;
939   char *help_command;
940 
941   if (!p->help_url || !*p->help_url)
942     {
943       warning_dialog (s->toplevel_widget,
944                       _("Error:\n\n"
945 			"No Help URL has been specified.\n"), D_NONE, 100);
946       return;
947     }
948 
949   help_command = (char *) malloc (strlen (p->load_url_command) +
950 				  (strlen (p->help_url) * 4) + 20);
951   strcpy (help_command, "( ");
952   sprintf (help_command + strlen(help_command),
953            p->load_url_command,
954            p->help_url, p->help_url, p->help_url, p->help_url);
955   strcat (help_command, " ) &");
956   if (system (help_command) < 0)
957     fprintf (stderr, "%s: fork error\n", blurb());
958   free (help_command);
959 }
960 
961 
962 G_MODULE_EXPORT void
file_menu_cb(GtkMenuItem * menuitem,gpointer user_data)963 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
964 {
965   state *s = global_state_kludge;  /* I hate C so much... */
966   sensitize_menu_items (s, False);
967 }
968 
969 
970 G_MODULE_EXPORT void
activate_menu_cb(GtkMenuItem * menuitem,gpointer user_data)971 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
972 {
973   state *s = global_state_kludge;  /* I hate C so much... */
974   run_cmd (s, XA_ACTIVATE, 0);
975 }
976 
977 
978 G_MODULE_EXPORT void
lock_menu_cb(GtkMenuItem * menuitem,gpointer user_data)979 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
980 {
981   state *s = global_state_kludge;  /* I hate C so much... */
982   run_cmd (s, XA_LOCK, 0);
983 }
984 
985 
986 G_MODULE_EXPORT void
kill_menu_cb(GtkMenuItem * menuitem,gpointer user_data)987 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
988 {
989   state *s = global_state_kludge;  /* I hate C so much... */
990   run_cmd (s, XA_EXIT, 0);
991 }
992 
993 
994 G_MODULE_EXPORT void
restart_menu_cb(GtkWidget * widget,gpointer user_data)995 restart_menu_cb (GtkWidget *widget, gpointer user_data)
996 {
997   state *s = global_state_kludge;  /* I hate C so much... */
998   flush_dialog_changes_and_save (s);
999   xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
1000   sleep (1);
1001   if (system ("xscreensaver -nosplash &") < 0)
1002     fprintf (stderr, "%s: fork error\n", blurb());
1003 
1004   await_xscreensaver (s);
1005 }
1006 
1007 static Bool
xscreensaver_running_p(state * s)1008 xscreensaver_running_p (state *s)
1009 {
1010   Display *dpy = GDK_DISPLAY();
1011   char *rversion = 0;
1012   server_xscreensaver_version (dpy, &rversion, 0, 0);
1013   if (!rversion)
1014     return False;
1015   free (rversion);
1016   return True;
1017 }
1018 
1019 static void
await_xscreensaver(state * s)1020 await_xscreensaver (state *s)
1021 {
1022   int countdown = 5;
1023   Bool ok = False;
1024 
1025   while (!ok && (--countdown > 0))
1026     if (xscreensaver_running_p (s))
1027       ok = True;
1028     else
1029       sleep (1);    /* If it's not there yet, wait a second... */
1030 
1031   sensitize_menu_items (s, True);
1032 
1033   if (! ok)
1034     {
1035       /* Timed out, no screensaver running. */
1036 
1037       char buf [1024];
1038       Bool root_p = (geteuid () == 0);
1039 
1040       strcpy (buf,
1041               _("Error:\n\n"
1042 		"The xscreensaver daemon did not start up properly.\n"
1043 		"\n"));
1044 
1045       if (root_p)
1046         strcat (buf, STFU
1047 	  _("You are running as root.  This usually means that xscreensaver\n"
1048             "was unable to contact your X server because access control is\n"
1049             "turned on."
1050 /*
1051             "  Try running this command:\n"
1052             "\n"
1053             "                        xhost +localhost\n"
1054             "\n"
1055             "and then selecting `File / Restart Daemon'.\n"
1056             "\n"
1057             "Note that turning off access control will allow anyone logged\n"
1058             "on to this machine to access your screen, which might be\n"
1059             "considered a security problem.  Please read the xscreensaver\n"
1060             "manual and FAQ for more information.\n"
1061  */
1062             "\n"
1063             "You shouldn't run X as root. Instead, you should log in as a\n"
1064             "normal user, and `sudo' as necessary."));
1065       else
1066         strcat (buf, _("Please check your $PATH and permissions."));
1067 
1068       warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1069     }
1070 
1071   force_dialog_repaint (s);
1072 }
1073 
1074 
1075 static int
selected_list_element(state * s)1076 selected_list_element (state *s)
1077 {
1078   return s->_selected_list_element;
1079 }
1080 
1081 
1082 static int
demo_write_init_file(state * s,saver_preferences * p)1083 demo_write_init_file (state *s, saver_preferences *p)
1084 {
1085   Display *dpy = GDK_DISPLAY();
1086 
1087 #if 0
1088   /* #### try to figure out why shit keeps getting reordered... */
1089   if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1090     abort();
1091 #endif
1092 
1093   if (!write_init_file (dpy, p, s->short_version, False))
1094     {
1095       if (s->debug_p)
1096         fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1097       return 0;
1098     }
1099   else
1100     {
1101       const char *f = init_file_name();
1102       if (!f || !*f)
1103         warning_dialog (s->toplevel_widget,
1104                         _("Error:\n\nCouldn't determine init file name!\n"),
1105                         D_NONE, 100);
1106       else
1107         {
1108           char *b = (char *) malloc (strlen(f) + 1024);
1109           sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1110           warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1111           free (b);
1112         }
1113       return -1;
1114     }
1115 }
1116 
1117 
1118 G_MODULE_EXPORT void
run_this_cb(GtkButton * button,gpointer user_data)1119 run_this_cb (GtkButton *button, gpointer user_data)
1120 {
1121   state *s = global_state_kludge;  /* I hate C so much... */
1122   int list_elt = selected_list_element (s);
1123   if (list_elt < 0) return;
1124   if (!flush_dialog_changes_and_save (s))
1125     run_hack (s, list_elt, True);
1126 }
1127 
1128 
1129 G_MODULE_EXPORT void
manual_cb(GtkButton * button,gpointer user_data)1130 manual_cb (GtkButton *button, gpointer user_data)
1131 {
1132   Display *dpy = GDK_DISPLAY();
1133   state *s = global_state_kludge;  /* I hate C so much... */
1134   saver_preferences *p = &s->prefs;
1135   GtkWidget *list_widget = name_to_widget (s, "list");
1136   int list_elt = selected_list_element (s);
1137   int hack_number;
1138   char *name, *name2, *cmd, *str;
1139   char *oname = 0;
1140   if (list_elt < 0) return;
1141   hack_number = s->list_elt_to_hack_number[list_elt];
1142 
1143   flush_dialog_changes_and_save (s);
1144   ensure_selected_item_visible (list_widget);
1145 
1146   name = strdup (p->screenhacks[hack_number]->command);
1147   name2 = name;
1148   oname = name;
1149   while (isspace (*name2)) name2++;
1150   str = name2;
1151   while (*str && !isspace (*str)) str++;
1152   *str = 0;
1153   str = strrchr (name2, '/');
1154   if (str) name2 = str+1;
1155 
1156   cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1157   if (cmd)
1158     {
1159       char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1160       strcpy (cmd2, "( ");
1161       sprintf (cmd2 + strlen (cmd2),
1162                cmd,
1163                name2, name2, name2, name2);
1164       strcat (cmd2, " ) &");
1165       if (system (cmd2) < 0)
1166         fprintf (stderr, "%s: fork error\n", blurb());
1167       free (cmd2);
1168     }
1169   else
1170     {
1171       warning_dialog (GTK_WIDGET (button),
1172                       _("Error:\n\nno `manualCommand' resource set."),
1173                       D_NONE, 100);
1174     }
1175 
1176   free (oname);
1177 }
1178 
1179 
1180 static void
force_list_select_item(state * s,GtkWidget * list,int list_elt,Bool scroll_p)1181 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1182 {
1183   GtkWidget *parent = name_to_widget (s, "scroller");
1184   gboolean was = GET_SENSITIVE (parent);
1185 #ifdef HAVE_GTK2
1186   GtkTreeIter iter;
1187   GtkTreeModel *model;
1188   GtkTreeSelection *selection;
1189 #endif /* HAVE_GTK2 */
1190 
1191   if (!was) gtk_widget_set_sensitive (parent, True);
1192 #ifdef HAVE_GTK2
1193   model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1194   g_assert (model);
1195   if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1196     {
1197       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1198       gtk_tree_selection_select_iter (selection, &iter);
1199     }
1200 #else  /* !HAVE_GTK2 */
1201   gtk_list_select_item (GTK_LIST (list), list_elt);
1202 #endif /* !HAVE_GTK2 */
1203   if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1204   if (!was) gtk_widget_set_sensitive (parent, False);
1205 }
1206 
1207 
1208 G_MODULE_EXPORT void
run_next_cb(GtkButton * button,gpointer user_data)1209 run_next_cb (GtkButton *button, gpointer user_data)
1210 {
1211   state *s = global_state_kludge;  /* I hate C so much... */
1212   /* saver_preferences *p = &s->prefs; */
1213   Bool ops = s->preview_suppressed_p;
1214 
1215   GtkWidget *list_widget = name_to_widget (s, "list");
1216   int list_elt = selected_list_element (s);
1217 
1218   if (list_elt < 0)
1219     list_elt = 0;
1220   else
1221     list_elt++;
1222 
1223   if (list_elt >= s->list_count)
1224     list_elt = 0;
1225 
1226   s->preview_suppressed_p = True;
1227 
1228   flush_dialog_changes_and_save (s);
1229   force_list_select_item (s, list_widget, list_elt, True);
1230   populate_demo_window (s, list_elt);
1231   run_hack (s, list_elt, False);
1232 
1233   s->preview_suppressed_p = ops;
1234 }
1235 
1236 
1237 G_MODULE_EXPORT void
run_prev_cb(GtkButton * button,gpointer user_data)1238 run_prev_cb (GtkButton *button, gpointer user_data)
1239 {
1240   state *s = global_state_kludge;  /* I hate C so much... */
1241   /* saver_preferences *p = &s->prefs; */
1242   Bool ops = s->preview_suppressed_p;
1243 
1244   GtkWidget *list_widget = name_to_widget (s, "list");
1245   int list_elt = selected_list_element (s);
1246 
1247   if (list_elt < 0)
1248     list_elt = s->list_count - 1;
1249   else
1250     list_elt--;
1251 
1252   if (list_elt < 0)
1253     list_elt = s->list_count - 1;
1254 
1255   s->preview_suppressed_p = True;
1256 
1257   flush_dialog_changes_and_save (s);
1258   force_list_select_item (s, list_widget, list_elt, True);
1259   populate_demo_window (s, list_elt);
1260   run_hack (s, list_elt, False);
1261 
1262   s->preview_suppressed_p = ops;
1263 }
1264 
1265 
1266 /* Writes the given settings into prefs.
1267    Returns true if there was a change, False otherwise.
1268    command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1269  */
1270 static Bool
flush_changes(state * s,int list_elt,int enabled_p,const char * command,const char * visual)1271 flush_changes (state *s,
1272                int list_elt,
1273                int enabled_p,
1274                const char *command,
1275                const char *visual)
1276 {
1277   saver_preferences *p = &s->prefs;
1278   Bool changed = False;
1279   screenhack *hack;
1280   int hack_number;
1281   if (list_elt < 0 || list_elt >= s->list_count)
1282     abort();
1283 
1284   hack_number = s->list_elt_to_hack_number[list_elt];
1285   hack = p->screenhacks[hack_number];
1286 
1287   if (enabled_p != -1 &&
1288       enabled_p != hack->enabled_p)
1289     {
1290       hack->enabled_p = enabled_p;
1291       changed = True;
1292       if (s->debug_p)
1293         fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1294                  blurb(), hack->name, enabled_p);
1295     }
1296 
1297   if (command)
1298     {
1299       if (!hack->command || !!strcmp (command, hack->command))
1300         {
1301           if (hack->command) free (hack->command);
1302           hack->command = strdup (command);
1303           changed = True;
1304           if (s->debug_p)
1305             fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1306                      blurb(), hack->name, command);
1307         }
1308     }
1309 
1310   if (visual)
1311     {
1312       const char *ov = hack->visual;
1313       if (!ov || !*ov) ov = "any";
1314       if (!*visual) visual = "any";
1315       if (!!strcasecmp (visual, ov))
1316         {
1317           if (hack->visual) free (hack->visual);
1318           hack->visual = strdup (visual);
1319           changed = True;
1320           if (s->debug_p)
1321             fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1322                      blurb(), hack->name, visual);
1323         }
1324     }
1325 
1326   return changed;
1327 }
1328 
1329 
1330 /* Helper for the text fields that contain time specifications:
1331    this parses the text, and does error checking.
1332  */
1333 static void
hack_time_text(state * s,const char * line,Time * store,Bool sec_p)1334 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1335 {
1336   if (*line)
1337     {
1338       int value;
1339       if (!sec_p || strchr (line, ':'))
1340         value = parse_time ((char *) line, sec_p, True);
1341       else
1342         {
1343           char c;
1344           if (sscanf (line, "%d%c", &value, &c) != 1)
1345             value = -1;
1346           if (!sec_p)
1347             value *= 60;
1348         }
1349 
1350       value *= 1000;	/* Time measures in microseconds */
1351       if (value < 0)
1352 	{
1353 	  char b[255];
1354 	  sprintf (b,
1355 		   _("Error:\n\n"
1356 		     "Unparsable time format: \"%s\"\n"),
1357 		   line);
1358 	  warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1359 	}
1360       else
1361 	*store = value;
1362     }
1363 }
1364 
1365 
1366 static Bool
directory_p(const char * path)1367 directory_p (const char *path)
1368 {
1369   struct stat st;
1370   if (!path || !*path)
1371     return False;
1372   else if (stat (path, &st))
1373     return False;
1374   else if (!S_ISDIR (st.st_mode))
1375     return False;
1376   else
1377     return True;
1378 }
1379 
1380 static Bool
file_p(const char * path)1381 file_p (const char *path)
1382 {
1383   struct stat st;
1384   if (!path || !*path)
1385     return False;
1386   else if (stat (path, &st))
1387     return False;
1388   else if (S_ISDIR (st.st_mode))
1389     return False;
1390   else
1391     return True;
1392 }
1393 
1394 static char *
normalize_directory(const char * path)1395 normalize_directory (const char *path)
1396 {
1397   int L;
1398   char *p2, *s;
1399   if (!path || !*path) return 0;
1400   L = strlen (path);
1401   p2 = (char *) malloc (L + 2);
1402   strcpy (p2, path);
1403   if (p2[L-1] == '/')  /* remove trailing slash */
1404     p2[--L] = 0;
1405 
1406   for (s = p2; s && *s; s++)
1407     {
1408       if (*s == '/' &&
1409           (!strncmp (s, "/../", 4) ||			/* delete "XYZ/../" */
1410            !strncmp (s, "/..\000", 4)))			/* delete "XYZ/..$" */
1411         {
1412           char *s0 = s;
1413           while (s0 > p2 && s0[-1] != '/')
1414             s0--;
1415           if (s0 > p2)
1416             {
1417               s0--;
1418               s += 3;
1419               /* strcpy (s0, s); */
1420               memmove(s0, s, strlen(s) + 1);
1421               s = s0-1;
1422             }
1423         }
1424       else if (*s == '/' && !strncmp (s, "/./", 3)) {	/* delete "/./" */
1425         /* strcpy (s, s+2), s--; */
1426         memmove(s, s+2, strlen(s+2) + 1);
1427         s--;
1428        }
1429       else if (*s == '/' && !strncmp (s, "/.\000", 3))	/* delete "/.$" */
1430         *s = 0, s--;
1431     }
1432 
1433   /*
1434     Normalize consecutive slashes.
1435     Ignore doubled slashes after ":" to avoid mangling URLs.
1436   */
1437 
1438   for (s = p2; s && *s; s++){
1439     if (*s == ':') continue;
1440     if (!s[1] || !s[2]) continue;
1441     while (s[1] == '/' && s[2] == '/')
1442       /* strcpy (s+1, s+2); */
1443       memmove (s+1, s+2, strlen(s+2) + 1);
1444   }
1445 
1446   /* and strip trailing whitespace for good measure. */
1447   L = strlen(p2);
1448   while (isspace(p2[L-1]))
1449     p2[--L] = 0;
1450 
1451   return p2;
1452 }
1453 
1454 
1455 #ifdef HAVE_GTK2
1456 
1457 typedef struct {
1458   state *s;
1459   int i;
1460   Bool *changed;
1461 } FlushForeachClosure;
1462 
1463 static gboolean
flush_checkbox(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)1464 flush_checkbox  (GtkTreeModel *model,
1465 		 GtkTreePath *path,
1466 		 GtkTreeIter *iter,
1467 		 gpointer data)
1468 {
1469   FlushForeachClosure *closure = data;
1470   gboolean checked;
1471 
1472   gtk_tree_model_get (model, iter,
1473 		      COL_ENABLED, &checked,
1474 		      -1);
1475 
1476   if (flush_changes (closure->s, closure->i,
1477 		     checked, 0, 0))
1478     *closure->changed = True;
1479 
1480   closure->i++;
1481 
1482   /* don't remove row */
1483   return FALSE;
1484 }
1485 
1486 #endif /* HAVE_GTK2 */
1487 
1488 /* Flush out any changes made in the main dialog window (where changes
1489    take place immediately: clicking on a checkbox causes the init file
1490    to be written right away.)
1491  */
1492 static Bool
flush_dialog_changes_and_save(state * s)1493 flush_dialog_changes_and_save (state *s)
1494 {
1495   saver_preferences *p = &s->prefs;
1496   saver_preferences P2, *p2 = &P2;
1497 #ifdef HAVE_GTK2
1498   GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1499   GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1500   FlushForeachClosure closure;
1501 #else /* !HAVE_GTK2 */
1502   GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1503   GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1504   int i;
1505 #endif /* !HAVE_GTK2 */
1506   static Bool already_warned_about_missing_image_directory = False; /* very long name... */
1507 
1508   Bool changed = False;
1509   GtkWidget *w;
1510 
1511   if (s->saving_p) return False;
1512   s->saving_p = True;
1513 
1514   *p2 = *p;
1515 
1516   /* Flush any checkbox changes in the list down into the prefs struct.
1517    */
1518 #ifdef HAVE_GTK2
1519   closure.s = s;
1520   closure.changed = &changed;
1521   closure.i = 0;
1522   gtk_tree_model_foreach (model, flush_checkbox, &closure);
1523 
1524 #else /* !HAVE_GTK2 */
1525 
1526   for (i = 0; kids; kids = kids->next, i++)
1527     {
1528       GtkWidget *line = GTK_WIDGET (kids->data);
1529       GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1530       GtkWidget *line_check =
1531         GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1532       Bool checked =
1533         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1534 
1535       if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1536         changed = True;
1537     }
1538 #endif /* ~HAVE_GTK2 */
1539 
1540   /* Flush the non-hack-specific settings down into the prefs struct.
1541    */
1542 
1543 # define SECONDS(FIELD,NAME) \
1544     w = name_to_widget (s, (NAME)); \
1545     hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1546 
1547 # define MINUTES(FIELD,NAME) \
1548     w = name_to_widget (s, (NAME)); \
1549     hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1550 
1551 # define CHECKBOX(FIELD,NAME) \
1552     w = name_to_widget (s, (NAME)); \
1553     (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1554 
1555 # define PATHNAME(FIELD,NAME) \
1556     w = name_to_widget (s, (NAME)); \
1557     (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1558 
1559 # define TEXT(FIELD,NAME) \
1560     w = name_to_widget (s, (NAME)); \
1561     (FIELD) = (char *) g_strdup(gtk_entry_get_text (GTK_ENTRY (w)))
1562 
1563   MINUTES  (&p2->timeout,         "timeout_spinbutton");
1564   MINUTES  (&p2->cycle,           "cycle_spinbutton");
1565   CHECKBOX (p2->lock_p,           "lock_button");
1566   MINUTES  (&p2->lock_timeout,    "lock_spinbutton");
1567 
1568   CHECKBOX (p2->dpms_enabled_p,   "dpms_button");
1569   CHECKBOX (p2->dpms_quickoff_p,  "dpms_quickoff_button");
1570   MINUTES  (&p2->dpms_standby,    "dpms_standby_spinbutton");
1571   MINUTES  (&p2->dpms_suspend,    "dpms_suspend_spinbutton");
1572   MINUTES  (&p2->dpms_off,        "dpms_off_spinbutton");
1573 
1574   CHECKBOX (p2->grab_desktop_p,   "grab_desk_button");
1575   CHECKBOX (p2->grab_video_p,     "grab_video_button");
1576   CHECKBOX (p2->random_image_p,   "grab_image_button");
1577   PATHNAME (p2->image_directory,  "image_text");
1578 
1579 #if 0
1580   CHECKBOX (p2->verbose_p,        "verbose_button");
1581   CHECKBOX (p2->capture_stderr_p, "capture_button");
1582   CHECKBOX (p2->splash_p,         "splash_button");
1583 #endif
1584 
1585   {
1586     Bool v = False;
1587     CHECKBOX (v, "text_host_radio");     if (v) p2->tmode = TEXT_DATE;
1588     CHECKBOX (v, "text_radio");          if (v) p2->tmode = TEXT_LITERAL;
1589     CHECKBOX (v, "text_file_radio");     if (v) p2->tmode = TEXT_FILE;
1590     CHECKBOX (v, "text_program_radio");  if (v) p2->tmode = TEXT_PROGRAM;
1591     CHECKBOX (v, "text_url_radio");      if (v) p2->tmode = TEXT_URL;
1592     TEXT     (p2->text_literal, "text_entry");
1593     PATHNAME (p2->text_file,    "text_file_entry");
1594     PATHNAME (p2->text_program, "text_program_entry");
1595     PATHNAME (p2->text_program, "text_program_entry");
1596     TEXT     (p2->text_url,     "text_url_entry");
1597   }
1598 
1599   CHECKBOX (p2->install_cmap_p,   "install_button");
1600   CHECKBOX (p2->fade_p,           "fade_button");
1601   CHECKBOX (p2->unfade_p,         "unfade_button");
1602   SECONDS  (&p2->fade_seconds,    "fade_spinbutton");
1603 
1604 # undef SECONDS
1605 # undef MINUTES
1606 # undef CHECKBOX
1607 # undef PATHNAME
1608 # undef TEXT
1609 
1610   /* Warn if the image directory doesn't exist, when:
1611      - not being warned before
1612      - image directory is changed and the directory doesn't exist
1613      - image directory does not begin with http://
1614    */
1615   if (p2->image_directory &&
1616       *p2->image_directory &&
1617       !directory_p (p2->image_directory) &&
1618        strncmp(p2->image_directory, "http://", 6) &&
1619         ( !already_warned_about_missing_image_directory ||
1620           ( p->image_directory &&
1621             *p->image_directory &&
1622             strcmp(p->image_directory, p2->image_directory)
1623           )
1624         )
1625       )
1626     {
1627       char b[255];
1628       sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
1629                p2->image_directory);
1630       if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
1631         already_warned_about_missing_image_directory = True;
1632     }
1633 
1634 
1635   /* Map the mode menu to `saver_mode' enum values. */
1636   {
1637     GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1638     GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1639     GtkWidget *selected = gtk_menu_get_active (menu);
1640     GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1641     int menu_elt = g_list_index (kids, (gpointer) selected);
1642     if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1643     p2->mode = mode_menu_order[menu_elt];
1644   }
1645 
1646   if (p2->mode == ONE_HACK)
1647     {
1648       int list_elt = selected_list_element (s);
1649       p2->selected_hack = (list_elt >= 0
1650                            ? s->list_elt_to_hack_number[list_elt]
1651                            : -1);
1652     }
1653 
1654 # define COPY(field, name) \
1655   if (p->field != p2->field) { \
1656     changed = True; \
1657     if (s->debug_p) \
1658       fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1659   } \
1660   p->field = p2->field
1661 
1662   COPY(mode,             "mode");
1663   COPY(selected_hack,    "selected_hack");
1664 
1665   COPY(timeout,        "timeout");
1666   COPY(cycle,          "cycle");
1667   COPY(lock_p,         "lock_p");
1668   COPY(lock_timeout,   "lock_timeout");
1669 
1670   COPY(dpms_enabled_p,  "dpms_enabled_p");
1671   COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p");
1672   COPY(dpms_standby,    "dpms_standby");
1673   COPY(dpms_suspend,    "dpms_suspend");
1674   COPY(dpms_off,        "dpms_off");
1675 
1676 #if 0
1677   COPY(verbose_p,        "verbose_p");
1678   COPY(capture_stderr_p, "capture_stderr_p");
1679   COPY(splash_p,         "splash_p");
1680 #endif
1681 
1682   COPY(tmode,            "tmode");
1683 
1684   COPY(install_cmap_p,   "install_cmap_p");
1685   COPY(fade_p,           "fade_p");
1686   COPY(unfade_p,         "unfade_p");
1687   COPY(fade_seconds,     "fade_seconds");
1688 
1689   COPY(grab_desktop_p, "grab_desktop_p");
1690   COPY(grab_video_p,   "grab_video_p");
1691   COPY(random_image_p, "random_image_p");
1692 
1693 # undef COPY
1694 
1695 # define COPYSTR(FIELD,NAME) \
1696   if (!p->FIELD || \
1697       !p2->FIELD || \
1698       strcmp(p->FIELD, p2->FIELD)) \
1699     { \
1700       changed = True; \
1701       if (s->debug_p) \
1702         fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1703     } \
1704   if (p->FIELD && p->FIELD != p2->FIELD) \
1705     free (p->FIELD); \
1706   p->FIELD = p2->FIELD; \
1707   p2->FIELD = 0
1708 
1709   COPYSTR(image_directory, "image_directory");
1710   COPYSTR(text_literal,    "text_literal");
1711   COPYSTR(text_file,       "text_file");
1712   COPYSTR(text_program,    "text_program");
1713   COPYSTR(text_url,        "text_url");
1714 # undef COPYSTR
1715 
1716   populate_prefs_page (s);
1717 
1718   if (changed)
1719     {
1720       Display *dpy = GDK_DISPLAY();
1721       Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1722       sync_server_dpms_settings (dpy, enabled_p, p->dpms_quickoff_p,
1723                                  p->dpms_standby / 1000,
1724                                  p->dpms_suspend / 1000,
1725                                  p->dpms_off / 1000,
1726                                  False);
1727 
1728       changed = demo_write_init_file (s, p);
1729     }
1730 
1731   s->saving_p = False;
1732   return changed;
1733 }
1734 
1735 
1736 /* Flush out any changes made in the popup dialog box (where changes
1737    take place only when the OK button is clicked.)
1738  */
1739 static Bool
flush_popup_changes_and_save(state * s)1740 flush_popup_changes_and_save (state *s)
1741 {
1742   Bool changed = False;
1743   saver_preferences *p = &s->prefs;
1744   int list_elt = selected_list_element (s);
1745 
1746   GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1747   GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1748 
1749   const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1750   const char *command = gtk_entry_get_text (cmd);
1751 
1752   char c;
1753   unsigned long id;
1754 
1755   if (s->saving_p) return False;
1756   s->saving_p = True;
1757 
1758   if (list_elt < 0)
1759     goto DONE;
1760 
1761   if (maybe_reload_init_file (s) != 0)
1762     {
1763       changed = True;
1764       goto DONE;
1765     }
1766 
1767   /* Sanity-check and canonicalize whatever the user typed into the combo box.
1768    */
1769   if      (!strcasecmp (visual, ""))                   visual = "";
1770   else if (!strcasecmp (visual, "any"))                visual = "";
1771   else if (!strcasecmp (visual, "default"))            visual = "Default";
1772   else if (!strcasecmp (visual, "default-n"))          visual = "Default-N";
1773   else if (!strcasecmp (visual, "default-i"))          visual = "Default-I";
1774   else if (!strcasecmp (visual, "best"))               visual = "Best";
1775   else if (!strcasecmp (visual, "mono"))               visual = "Mono";
1776   else if (!strcasecmp (visual, "monochrome"))         visual = "Mono";
1777   else if (!strcasecmp (visual, "gray"))               visual = "Gray";
1778   else if (!strcasecmp (visual, "grey"))               visual = "Gray";
1779   else if (!strcasecmp (visual, "color"))              visual = "Color";
1780   else if (!strcasecmp (visual, "gl"))                 visual = "GL";
1781   else if (!strcasecmp (visual, "staticgray"))         visual = "StaticGray";
1782   else if (!strcasecmp (visual, "staticcolor"))        visual = "StaticColor";
1783   else if (!strcasecmp (visual, "truecolor"))          visual = "TrueColor";
1784   else if (!strcasecmp (visual, "grayscale"))          visual = "GrayScale";
1785   else if (!strcasecmp (visual, "greyscale"))          visual = "GrayScale";
1786   else if (!strcasecmp (visual, "pseudocolor"))        visual = "PseudoColor";
1787   else if (!strcasecmp (visual, "directcolor"))        visual = "DirectColor";
1788   else if (1 == sscanf (visual, " %lu %c", &id, &c))   ;
1789   else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1790   else
1791     {
1792       gdk_beep ();				  /* unparsable */
1793       visual = "";
1794       gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1795     }
1796 
1797   changed = flush_changes (s, list_elt, -1, command, visual);
1798   if (changed)
1799     {
1800       changed = demo_write_init_file (s, p);
1801 
1802       /* Do this to re-launch the hack if (and only if) the command line
1803          has changed. */
1804       populate_demo_window (s, selected_list_element (s));
1805     }
1806 
1807  DONE:
1808   s->saving_p = False;
1809   return changed;
1810 }
1811 
1812 
1813 G_MODULE_EXPORT void
pref_changed_cb(GtkWidget * widget,gpointer user_data)1814 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1815 {
1816   state *s = global_state_kludge;  /* I hate C so much... */
1817   if (! s->initializing_p)
1818     {
1819       s->initializing_p = True;
1820       flush_dialog_changes_and_save (s);
1821       s->initializing_p = False;
1822     }
1823 }
1824 
1825 G_MODULE_EXPORT gboolean
pref_changed_event_cb(GtkWidget * widget,GdkEvent * event,gpointer user_data)1826 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1827 {
1828   pref_changed_cb (widget, user_data);
1829   return FALSE;
1830 }
1831 
1832 /* Callback on menu items in the "mode" options menu.
1833  */
1834 G_MODULE_EXPORT void
mode_menu_item_cb(GtkWidget * widget,gpointer user_data)1835 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1836 {
1837   state *s = (state *) user_data;
1838   saver_preferences *p = &s->prefs;
1839   GtkWidget *list = name_to_widget (s, "list");
1840   int list_elt;
1841 
1842   GList *menu_items =
1843     gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1844   int menu_index = 0;
1845   saver_mode new_mode;
1846 
1847   while (menu_items)
1848     {
1849       if (menu_items->data == widget)
1850         break;
1851       menu_index++;
1852       menu_items = menu_items->next;
1853     }
1854   if (!menu_items) abort();
1855 
1856   new_mode = mode_menu_order[menu_index];
1857 
1858   /* Keep the same list element displayed as before; except if we're
1859      switching *to* "one screensaver" mode from any other mode, set
1860      "the one" to be that which is currently selected.
1861    */
1862   list_elt = selected_list_element (s);
1863   if (new_mode == ONE_HACK)
1864     p->selected_hack = s->list_elt_to_hack_number[list_elt];
1865 
1866   {
1867     saver_mode old_mode = p->mode;
1868     p->mode = new_mode;
1869     populate_demo_window (s, list_elt);
1870     force_list_select_item (s, list, list_elt, True);
1871     p->mode = old_mode;  /* put it back, so the init file gets written */
1872   }
1873 
1874   pref_changed_cb (widget, user_data);
1875 }
1876 
1877 
1878 G_MODULE_EXPORT void
switch_page_cb(GtkNotebook * notebook,GtkNotebookPage * page,gint page_num,gpointer user_data)1879 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1880                 gint page_num, gpointer user_data)
1881 {
1882   state *s = global_state_kludge;  /* I hate C so much... */
1883   pref_changed_cb (GTK_WIDGET (notebook), user_data);
1884 
1885   /* If we're switching to page 0, schedule the current hack to be run.
1886      Otherwise, schedule it to stop. */
1887   if (page_num == 0)
1888     populate_demo_window (s, selected_list_element (s));
1889   else
1890     schedule_preview (s, 0);
1891 }
1892 
1893 #ifdef HAVE_GTK2
1894 static void
list_activated_cb(GtkTreeView * list,GtkTreePath * path,GtkTreeViewColumn * column,gpointer data)1895 list_activated_cb (GtkTreeView       *list,
1896 		   GtkTreePath       *path,
1897 		   GtkTreeViewColumn *column,
1898 		   gpointer           data)
1899 {
1900   state *s = data;
1901   char *str;
1902   int list_elt;
1903 
1904   g_return_if_fail (!gdk_pointer_is_grabbed ());
1905 
1906   str = gtk_tree_path_to_string (path);
1907   list_elt = strtol (str, NULL, 10);
1908   g_free (str);
1909 
1910   if (list_elt >= 0)
1911     run_hack (s, list_elt, True);
1912 }
1913 
1914 static void
list_select_changed_cb(GtkTreeSelection * selection,gpointer data)1915 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1916 {
1917   state *s = (state *)data;
1918   GtkTreeModel *model;
1919   GtkTreeIter iter;
1920   GtkTreePath *path;
1921   char *str;
1922   int list_elt;
1923 
1924   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1925     return;
1926 
1927   path = gtk_tree_model_get_path (model, &iter);
1928   str = gtk_tree_path_to_string (path);
1929   list_elt = strtol (str, NULL, 10);
1930 
1931   gtk_tree_path_free (path);
1932   g_free (str);
1933 
1934   populate_demo_window (s, list_elt);
1935   flush_dialog_changes_and_save (s);
1936 
1937   /* Re-populate the Settings window any time a new item is selected
1938      in the list, in case both windows are currently visible.
1939    */
1940   populate_popup_window (s);
1941 }
1942 
1943 #else /* !HAVE_GTK2 */
1944 
1945 static time_t last_doubleclick_time = 0;   /* FMH!  This is to suppress the
1946                                               list_select_cb that comes in
1947                                               *after* we've double-clicked.
1948                                             */
1949 
1950 static gint
list_doubleclick_cb(GtkWidget * button,GdkEventButton * event,gpointer data)1951 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1952                      gpointer data)
1953 {
1954   state *s = (state *) data;
1955   if (event->type == GDK_2BUTTON_PRESS)
1956     {
1957       GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1958       int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1959 
1960       last_doubleclick_time = time ((time_t *) 0);
1961 
1962       if (list_elt >= 0)
1963         run_hack (s, list_elt, True);
1964     }
1965 
1966   return FALSE;
1967 }
1968 
1969 
1970 static void
list_select_cb(GtkList * list,GtkWidget * child,gpointer data)1971 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1972 {
1973   state *s = (state *) data;
1974   time_t now = time ((time_t *) 0);
1975 
1976   if (now >= last_doubleclick_time + 2)
1977     {
1978       int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1979       populate_demo_window (s, list_elt);
1980       flush_dialog_changes_and_save (s);
1981     }
1982 }
1983 
1984 static void
list_unselect_cb(GtkList * list,GtkWidget * child,gpointer data)1985 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1986 {
1987   state *s = (state *) data;
1988   populate_demo_window (s, -1);
1989   flush_dialog_changes_and_save (s);
1990 }
1991 
1992 #endif /* !HAVE_GTK2 */
1993 
1994 
1995 /* Called when the checkboxes that are in the left column of the
1996    scrolling list are clicked.  This both populates the right pane
1997    (just as clicking on the label (really, listitem) does) and
1998    also syncs this checkbox with  the right pane Enabled checkbox.
1999  */
2000 static void
list_checkbox_cb(GtkCellRendererToggle * toggle,gchar * path_string,gpointer data)2001 list_checkbox_cb (
2002 #ifdef HAVE_GTK2
2003 		  GtkCellRendererToggle *toggle,
2004 		  gchar                 *path_string,
2005 #else  /* !HAVE_GTK2 */
2006 		  GtkWidget *cb,
2007 #endif /* !HAVE_GTK2 */
2008 		  gpointer               data)
2009 {
2010   state *s = (state *) data;
2011 
2012 #ifdef HAVE_GTK2
2013   GtkScrolledWindow *scroller =
2014     GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
2015   GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2016   GtkTreeModel *model = gtk_tree_view_get_model (list);
2017   GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
2018   GtkTreeIter iter;
2019   gboolean active;
2020 #else /* !HAVE_GTK2 */
2021   GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
2022   GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
2023 
2024   GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
2025   GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
2026   GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
2027 #endif /* !HAVE_GTK2 */
2028   GtkAdjustment *adj;
2029   double scroll_top;
2030 
2031   int list_elt;
2032 
2033 #ifdef HAVE_GTK2
2034   if (!gtk_tree_model_get_iter (model, &iter, path))
2035     {
2036       g_warning ("bad path: %s", path_string);
2037       return;
2038     }
2039   gtk_tree_path_free (path);
2040 
2041   gtk_tree_model_get (model, &iter,
2042 		      COL_ENABLED, &active,
2043 		      -1);
2044 
2045   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2046 		      COL_ENABLED, !active,
2047 		      -1);
2048 
2049   list_elt = strtol (path_string, NULL, 10);
2050 #else  /* !HAVE_GTK2 */
2051   list_elt = gtk_list_child_position (list, line);
2052 #endif /* !HAVE_GTK2 */
2053 
2054   /* remember previous scroll position of the top of the list */
2055   adj = gtk_scrolled_window_get_vadjustment (scroller);
2056   scroll_top = GET_ADJ_VALUE (adj);
2057 
2058   flush_dialog_changes_and_save (s);
2059   force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2060   populate_demo_window (s, list_elt);
2061 
2062   /* restore the previous scroll position of the top of the list.
2063      this is weak, but I don't really know why it's moving... */
2064   gtk_adjustment_set_value (adj, scroll_top);
2065 }
2066 
2067 
2068 typedef struct {
2069   state *state;
2070   GtkFileSelection *widget;
2071 } file_selection_data;
2072 
2073 
2074 
2075 static void
store_image_directory(GtkWidget * button,gpointer user_data)2076 store_image_directory (GtkWidget *button, gpointer user_data)
2077 {
2078   file_selection_data *fsd = (file_selection_data *) user_data;
2079   state *s = fsd->state;
2080   GtkFileSelection *selector = fsd->widget;
2081   GtkWidget *top = s->toplevel_widget;
2082   saver_preferences *p = &s->prefs;
2083   const char *path = gtk_file_selection_get_filename (selector);
2084 
2085   if (p->image_directory && !strcmp(p->image_directory, path))
2086     return;  /* no change */
2087 
2088   /* No warning for URLs. */
2089   if ((!directory_p (path)) && strncmp(path, "http://", 6))
2090     {
2091       char b[255];
2092       sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2093       warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2094       return;
2095     }
2096 
2097   if (p->image_directory) free (p->image_directory);
2098   p->image_directory = normalize_directory (path);
2099 
2100   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2101                       (p->image_directory ? p->image_directory : ""));
2102   demo_write_init_file (s, p);
2103 }
2104 
2105 
2106 static void
store_text_file(GtkWidget * button,gpointer user_data)2107 store_text_file (GtkWidget *button, gpointer user_data)
2108 {
2109   file_selection_data *fsd = (file_selection_data *) user_data;
2110   state *s = fsd->state;
2111   GtkFileSelection *selector = fsd->widget;
2112   GtkWidget *top = s->toplevel_widget;
2113   saver_preferences *p = &s->prefs;
2114   const char *path = gtk_file_selection_get_filename (selector);
2115 
2116   if (p->text_file && !strcmp(p->text_file, path))
2117     return;  /* no change */
2118 
2119   if (!file_p (path))
2120     {
2121       char b[255];
2122       sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2123       warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2124       return;
2125     }
2126 
2127   if (p->text_file) free (p->text_file);
2128   p->text_file = normalize_directory (path);
2129 
2130   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2131                       (p->text_file ? p->text_file : ""));
2132   demo_write_init_file (s, p);
2133 }
2134 
2135 
2136 static void
store_text_program(GtkWidget * button,gpointer user_data)2137 store_text_program (GtkWidget *button, gpointer user_data)
2138 {
2139   file_selection_data *fsd = (file_selection_data *) user_data;
2140   state *s = fsd->state;
2141   GtkFileSelection *selector = fsd->widget;
2142   /*GtkWidget *top = s->toplevel_widget;*/
2143   saver_preferences *p = &s->prefs;
2144   const char *path = gtk_file_selection_get_filename (selector);
2145 
2146   if (p->text_program && !strcmp(p->text_program, path))
2147     return;  /* no change */
2148 
2149 # if 0
2150   if (!file_p (path))
2151     {
2152       char b[255];
2153       sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2154       warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2155       return;
2156     }
2157 # endif
2158 
2159   if (p->text_program) free (p->text_program);
2160   p->text_program = normalize_directory (path);
2161 
2162   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2163                       (p->text_program ? p->text_program : ""));
2164   demo_write_init_file (s, p);
2165 }
2166 
2167 
2168 
2169 static void
browse_image_dir_cancel(GtkWidget * button,gpointer user_data)2170 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2171 {
2172   file_selection_data *fsd = (file_selection_data *) user_data;
2173   gtk_widget_hide (GTK_WIDGET (fsd->widget));
2174 }
2175 
2176 static void
browse_image_dir_ok(GtkWidget * button,gpointer user_data)2177 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2178 {
2179   browse_image_dir_cancel (button, user_data);
2180   store_image_directory (button, user_data);
2181 }
2182 
2183 static void
browse_text_file_ok(GtkWidget * button,gpointer user_data)2184 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2185 {
2186   browse_image_dir_cancel (button, user_data);
2187   store_text_file (button, user_data);
2188 }
2189 
2190 static void
browse_text_program_ok(GtkWidget * button,gpointer user_data)2191 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2192 {
2193   browse_image_dir_cancel (button, user_data);
2194   store_text_program (button, user_data);
2195 }
2196 
2197 static void
browse_image_dir_close(GtkWidget * widget,GdkEvent * event,gpointer user_data)2198 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2199 {
2200   browse_image_dir_cancel (widget, user_data);
2201 }
2202 
2203 
2204 G_MODULE_EXPORT void
browse_image_dir_cb(GtkButton * button,gpointer user_data)2205 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2206 {
2207   state *s = global_state_kludge;  /* I hate C so much... */
2208   saver_preferences *p = &s->prefs;
2209   static file_selection_data *fsd = 0;
2210 
2211   GtkFileSelection *selector = GTK_FILE_SELECTION(
2212     gtk_file_selection_new ("Please select the image directory."));
2213 
2214   if (!fsd)
2215     fsd = (file_selection_data *) malloc (sizeof (*fsd));
2216 
2217   fsd->widget = selector;
2218   fsd->state = s;
2219 
2220   if (p->image_directory && *p->image_directory)
2221     gtk_file_selection_set_filename (selector, p->image_directory);
2222 
2223   gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2224                       "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2225                       (gpointer *) fsd);
2226   gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2227                       "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2228                       (gpointer *) fsd);
2229   gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2230                       GTK_SIGNAL_FUNC (browse_image_dir_close),
2231                       (gpointer *) fsd);
2232 
2233   gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2234 
2235   gtk_window_set_modal (GTK_WINDOW (selector), True);
2236   gtk_widget_show (GTK_WIDGET (selector));
2237 }
2238 
2239 
2240 G_MODULE_EXPORT void
browse_text_file_cb(GtkButton * button,gpointer user_data)2241 browse_text_file_cb (GtkButton *button, gpointer user_data)
2242 {
2243   state *s = global_state_kludge;  /* I hate C so much... */
2244   saver_preferences *p = &s->prefs;
2245   static file_selection_data *fsd = 0;
2246 
2247   GtkFileSelection *selector = GTK_FILE_SELECTION(
2248     gtk_file_selection_new ("Please select a text file."));
2249 
2250   if (!fsd)
2251     fsd = (file_selection_data *) malloc (sizeof (*fsd));
2252 
2253   fsd->widget = selector;
2254   fsd->state = s;
2255 
2256   if (p->text_file && *p->text_file)
2257     gtk_file_selection_set_filename (selector, p->text_file);
2258 
2259   gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2260                       "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2261                       (gpointer *) fsd);
2262   gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2263                       "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2264                       (gpointer *) fsd);
2265   gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2266                       GTK_SIGNAL_FUNC (browse_image_dir_close),
2267                       (gpointer *) fsd);
2268 
2269   gtk_window_set_modal (GTK_WINDOW (selector), True);
2270   gtk_widget_show (GTK_WIDGET (selector));
2271 }
2272 
2273 
2274 G_MODULE_EXPORT void
browse_text_program_cb(GtkButton * button,gpointer user_data)2275 browse_text_program_cb (GtkButton *button, gpointer user_data)
2276 {
2277   state *s = global_state_kludge;  /* I hate C so much... */
2278   saver_preferences *p = &s->prefs;
2279   static file_selection_data *fsd = 0;
2280 
2281   GtkFileSelection *selector = GTK_FILE_SELECTION(
2282     gtk_file_selection_new ("Please select a text-generating program."));
2283 
2284   if (!fsd)
2285     fsd = (file_selection_data *) malloc (sizeof (*fsd));
2286 
2287   fsd->widget = selector;
2288   fsd->state = s;
2289 
2290   if (p->text_program && *p->text_program)
2291     gtk_file_selection_set_filename (selector, p->text_program);
2292 
2293   gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2294                       "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2295                       (gpointer *) fsd);
2296   gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2297                       "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2298                       (gpointer *) fsd);
2299   gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2300                       GTK_SIGNAL_FUNC (browse_image_dir_close),
2301                       (gpointer *) fsd);
2302 
2303   gtk_window_set_modal (GTK_WINDOW (selector), True);
2304   gtk_widget_show (GTK_WIDGET (selector));
2305 }
2306 
2307 
2308 
2309 
2310 
2311 G_MODULE_EXPORT void
settings_cb(GtkButton * button,gpointer user_data)2312 settings_cb (GtkButton *button, gpointer user_data)
2313 {
2314   state *s = global_state_kludge;  /* I hate C so much... */
2315   int list_elt = selected_list_element (s);
2316 
2317   populate_demo_window (s, list_elt);   /* reset the widget */
2318   populate_popup_window (s);		/* create UI on popup window */
2319   gtk_widget_show (s->popup_widget);
2320 }
2321 
2322 static void
settings_sync_cmd_text(state * s)2323 settings_sync_cmd_text (state *s)
2324 {
2325 # ifdef HAVE_XML
2326   GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2327   char *cmd_line = get_configurator_command_line (s->cdata, False);
2328   gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2329   gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2330   free (cmd_line);
2331 # endif /* HAVE_XML */
2332 }
2333 
2334 G_MODULE_EXPORT void
settings_adv_cb(GtkButton * button,gpointer user_data)2335 settings_adv_cb (GtkButton *button, gpointer user_data)
2336 {
2337   state *s = global_state_kludge;  /* I hate C so much... */
2338   GtkNotebook *notebook =
2339     GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2340 
2341   settings_sync_cmd_text (s);
2342   gtk_notebook_set_page (notebook, 1);
2343 }
2344 
2345 G_MODULE_EXPORT void
settings_std_cb(GtkButton * button,gpointer user_data)2346 settings_std_cb (GtkButton *button, gpointer user_data)
2347 {
2348   state *s = global_state_kludge;  /* I hate C so much... */
2349   GtkNotebook *notebook =
2350     GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2351 
2352   /* Re-create UI to reflect the in-progress command-line settings. */
2353   populate_popup_window (s);
2354 
2355   gtk_notebook_set_page (notebook, 0);
2356 }
2357 
2358 G_MODULE_EXPORT void
settings_reset_cb(GtkButton * button,gpointer user_data)2359 settings_reset_cb (GtkButton *button, gpointer user_data)
2360 {
2361 # ifdef HAVE_XML
2362   state *s = global_state_kludge;  /* I hate C so much... */
2363   GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2364   char *cmd_line = get_configurator_command_line (s->cdata, True);
2365   gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2366   gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2367   free (cmd_line);
2368   populate_popup_window (s);
2369 # endif /* HAVE_XML */
2370 }
2371 
2372 G_MODULE_EXPORT void
settings_switch_page_cb(GtkNotebook * notebook,GtkNotebookPage * page,gint page_num,gpointer user_data)2373 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2374                          gint page_num, gpointer user_data)
2375 {
2376   state *s = global_state_kludge;  /* I hate C so much... */
2377   GtkWidget *adv = name_to_widget (s, "adv_button");
2378   GtkWidget *std = name_to_widget (s, "std_button");
2379 
2380   if (page_num == 0)
2381     {
2382       gtk_widget_show (adv);
2383       gtk_widget_hide (std);
2384     }
2385   else if (page_num == 1)
2386     {
2387       gtk_widget_hide (adv);
2388       gtk_widget_show (std);
2389     }
2390   else
2391     abort();
2392 }
2393 
2394 
2395 
2396 G_MODULE_EXPORT void
settings_cancel_cb(GtkButton * button,gpointer user_data)2397 settings_cancel_cb (GtkButton *button, gpointer user_data)
2398 {
2399   state *s = global_state_kludge;  /* I hate C so much... */
2400   gtk_widget_hide (s->popup_widget);
2401 }
2402 
2403 G_MODULE_EXPORT void
settings_ok_cb(GtkButton * button,gpointer user_data)2404 settings_ok_cb (GtkButton *button, gpointer user_data)
2405 {
2406   state *s = global_state_kludge;  /* I hate C so much... */
2407   GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2408   int page = gtk_notebook_get_current_page (notebook);
2409 
2410   if (page == 0)
2411     /* Regenerate the command-line from the widget contents before saving.
2412        But don't do this if we're looking at the command-line page already,
2413        or we will blow away what they typed... */
2414     settings_sync_cmd_text (s);
2415 
2416   flush_popup_changes_and_save (s);
2417   gtk_widget_hide (s->popup_widget);
2418 }
2419 
2420 static gboolean
wm_popup_close_cb(GtkWidget * widget,GdkEvent * event,gpointer data)2421 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2422 {
2423   state *s = (state *) data;
2424   settings_cancel_cb (0, (gpointer) s);
2425   return TRUE;
2426 }
2427 
2428 
2429 
2430 /* Populating the various widgets
2431  */
2432 
2433 
2434 /* Returns the number of the last hack run by the server.
2435  */
2436 static int
server_current_hack(void)2437 server_current_hack (void)
2438 {
2439   Atom type;
2440   int format;
2441   unsigned long nitems, bytesafter;
2442   unsigned char *dataP = 0;
2443   Display *dpy = GDK_DISPLAY();
2444   int hack_number = -1;
2445 
2446   if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2447                           XA_SCREENSAVER_STATUS,
2448                           0, 3, False, XA_INTEGER,
2449                           &type, &format, &nitems, &bytesafter,
2450                           &dataP)
2451       == Success
2452       && type == XA_INTEGER
2453       && nitems >= 3
2454       && dataP)
2455     {
2456       PROP32 *data = (PROP32 *) dataP;
2457       hack_number = (int) data[2] - 1;
2458     }
2459 
2460   if (dataP) XFree (dataP);
2461 
2462   return hack_number;
2463 }
2464 
2465 
2466 /* Finds the number of the last hack that was run, and makes that item be
2467    selected by default.
2468  */
2469 static void
scroll_to_current_hack(state * s)2470 scroll_to_current_hack (state *s)
2471 {
2472   saver_preferences *p = &s->prefs;
2473   int hack_number = -1;
2474 
2475   if (p->mode == ONE_HACK)		   /* in "one" mode, use the one */
2476     hack_number = p->selected_hack;
2477   if (hack_number < 0)			   /* otherwise, use the last-run */
2478     hack_number = server_current_hack ();
2479   if (hack_number < 0)			   /* failing that, last "one mode" */
2480     hack_number = p->selected_hack;
2481   if (hack_number < 0)			   /* failing that, newest hack. */
2482     {
2483       /* We should only get here if the user does not have a .xscreensaver
2484          file, and the screen has not been blanked with a hack since X
2485          started up: in other words, this is probably a fresh install.
2486 
2487          Instead of just defaulting to hack #0 (in either "programs" or
2488          "alphabetical" order) let's try to default to the last runnable
2489          hack in the "programs" list: this is probably the hack that was
2490          most recently added to the xscreensaver distribution (and so
2491          it's probably the currently-coolest one!)
2492        */
2493       hack_number = p->screenhacks_count-1;
2494       while (hack_number > 0 &&
2495              ! (s->hacks_available_p[hack_number] &&
2496                 p->screenhacks[hack_number]->enabled_p))
2497         hack_number--;
2498     }
2499 
2500   if (hack_number >= 0 && hack_number < p->screenhacks_count)
2501     {
2502       int list_elt = s->hack_number_to_list_elt[hack_number];
2503       GtkWidget *list = name_to_widget (s, "list");
2504       force_list_select_item (s, list, list_elt, True);
2505       populate_demo_window (s, list_elt);
2506     }
2507 }
2508 
2509 
2510 static void
populate_hack_list(state * s)2511 populate_hack_list (state *s)
2512 {
2513   Display *dpy = GDK_DISPLAY();
2514 #ifdef HAVE_GTK2
2515   saver_preferences *p = &s->prefs;
2516   GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2517   GtkListStore *model;
2518   GtkTreeSelection *selection;
2519   GtkCellRenderer *ren;
2520   GtkTreeIter iter;
2521   int i;
2522 
2523   g_object_get (G_OBJECT (list),
2524 		"model", &model,
2525 		NULL);
2526   if (!model)
2527     {
2528       model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2529       g_object_set (G_OBJECT (list), "model", model, NULL);
2530       g_object_unref (model);
2531 
2532       ren = gtk_cell_renderer_toggle_new ();
2533       gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2534 						   _("Use"), ren,
2535 						   "active", COL_ENABLED,
2536 						   NULL);
2537 
2538       g_signal_connect (ren, "toggled",
2539 			G_CALLBACK (list_checkbox_cb),
2540 			s);
2541 
2542       ren = gtk_cell_renderer_text_new ();
2543       gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2544 						   _("Screen Saver"), ren,
2545 						   "markup", COL_NAME,
2546 						   NULL);
2547 
2548       g_signal_connect_after (list, "row_activated",
2549 			      G_CALLBACK (list_activated_cb),
2550 			      s);
2551 
2552       selection = gtk_tree_view_get_selection (list);
2553       g_signal_connect (selection, "changed",
2554 			G_CALLBACK (list_select_changed_cb),
2555 			s);
2556 
2557     }
2558 
2559   for (i = 0; i < s->list_count; i++)
2560     {
2561       int hack_number = s->list_elt_to_hack_number[i];
2562       screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2563       char *pretty_name;
2564       Bool available_p = (hack && s->hacks_available_p [hack_number]);
2565 
2566       if (!hack) continue;
2567 
2568       /* If we're to suppress uninstalled hacks, check $PATH now. */
2569       if (p->ignore_uninstalled_p && !available_p)
2570         continue;
2571 
2572       pretty_name = (hack->name
2573                      ? strdup (hack->name)
2574                      : make_hack_name (dpy, hack->command));
2575 
2576       if (!available_p)
2577         {
2578           /* Make the text foreground be the color of insensitive widgets
2579              (but don't actually make it be insensitive, since we still
2580              want to be able to click on it.)
2581            */
2582           GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2583           GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2584        /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2585           char *buf = (char *) malloc (strlen (pretty_name) + 100);
2586 
2587           sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2588                       /*     " background=\"#%02X%02X%02X\""  */
2589                         ">%s</span>",
2590                    fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2591                 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2592                    pretty_name);
2593           free (pretty_name);
2594           pretty_name = buf;
2595         }
2596 
2597       gtk_list_store_append (model, &iter);
2598       gtk_list_store_set (model, &iter,
2599 			  COL_ENABLED, hack->enabled_p,
2600 			  COL_NAME, pretty_name,
2601 			  -1);
2602       free (pretty_name);
2603     }
2604 
2605 #else /* !HAVE_GTK2 */
2606 
2607   saver_preferences *p = &s->prefs;
2608   GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2609   int i;
2610   for (i = 0; i < s->list_count; i++)
2611     {
2612       int hack_number = s->list_elt_to_hack_number[i];
2613       screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2614 
2615       /* A GtkList must contain only GtkListItems, but those can contain
2616          an arbitrary widget.  We add an Hbox, and inside that, a Checkbox
2617          and a Label.  We handle single and double click events on the
2618          line itself, for clicking on the text, but the interior checkbox
2619          also handles its own events.
2620        */
2621       GtkWidget *line;
2622       GtkWidget *line_hbox;
2623       GtkWidget *line_check;
2624       GtkWidget *line_label;
2625       char *pretty_name;
2626       Bool available_p = (hack && s->hacks_available_p [hack_number]);
2627 
2628       if (!hack) continue;
2629 
2630       /* If we're to suppress uninstalled hacks, check $PATH now. */
2631       if (p->ignore_uninstalled_p && !available_p)
2632         continue;
2633 
2634       pretty_name = (hack->name
2635                      ? strdup (hack->name)
2636                      : make_hack_name (hack->command));
2637 
2638       line = gtk_list_item_new ();
2639       line_hbox = gtk_hbox_new (FALSE, 0);
2640       line_check = gtk_check_button_new ();
2641       line_label = gtk_label_new (pretty_name);
2642 
2643       gtk_container_add (GTK_CONTAINER (line), line_hbox);
2644       gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2645       gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2646 
2647       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2648                                     hack->enabled_p);
2649       gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2650 
2651       gtk_widget_show (line_check);
2652       gtk_widget_show (line_label);
2653       gtk_widget_show (line_hbox);
2654       gtk_widget_show (line);
2655 
2656       free (pretty_name);
2657 
2658       gtk_container_add (GTK_CONTAINER (list), line);
2659       gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2660                           GTK_SIGNAL_FUNC (list_doubleclick_cb),
2661                           (gpointer) s);
2662 
2663       gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2664                           GTK_SIGNAL_FUNC (list_checkbox_cb),
2665                           (gpointer) s);
2666 
2667       gtk_widget_show (line);
2668 
2669       if (!available_p)
2670         {
2671           /* Make the widget be colored like insensitive widgets
2672              (but don't actually make it be insensitive, since we
2673              still want to be able to click on it.)
2674            */
2675           GtkRcStyle *rc_style;
2676           GdkColor fg, bg;
2677 
2678           gtk_widget_realize (GTK_WIDGET (line_label));
2679 
2680           fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2681           bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2682 
2683           rc_style = gtk_rc_style_new ();
2684           rc_style->fg[GTK_STATE_NORMAL] = fg;
2685           rc_style->bg[GTK_STATE_NORMAL] = bg;
2686           rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2687 
2688           gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2689           gtk_rc_style_unref (rc_style);
2690         }
2691     }
2692 
2693   gtk_signal_connect (GTK_OBJECT (list), "select_child",
2694                       GTK_SIGNAL_FUNC (list_select_cb),
2695                       (gpointer) s);
2696   gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2697                       GTK_SIGNAL_FUNC (list_unselect_cb),
2698                       (gpointer) s);
2699 #endif /* !HAVE_GTK2 */
2700 }
2701 
2702 static void
update_list_sensitivity(state * s)2703 update_list_sensitivity (state *s)
2704 {
2705   saver_preferences *p = &s->prefs;
2706   Bool sensitive = (p->mode == RANDOM_HACKS ||
2707                     p->mode == RANDOM_HACKS_SAME ||
2708                     p->mode == ONE_HACK);
2709   Bool checkable = (p->mode == RANDOM_HACKS ||
2710                     p->mode == RANDOM_HACKS_SAME);
2711   Bool blankable = (p->mode != DONT_BLANK);
2712 
2713 #ifndef HAVE_GTK2
2714   GtkWidget *head     = name_to_widget (s, "col_head_hbox");
2715   GtkWidget *use      = name_to_widget (s, "use_col_frame");
2716 #endif /* HAVE_GTK2 */
2717   GtkWidget *scroller = name_to_widget (s, "scroller");
2718   GtkWidget *buttons  = name_to_widget (s, "next_prev_hbox");
2719   GtkWidget *blanker  = name_to_widget (s, "blanking_table");
2720 
2721 #ifdef HAVE_GTK2
2722   GtkTreeView *list      = GTK_TREE_VIEW (name_to_widget (s, "list"));
2723   GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2724 #else /* !HAVE_GTK2 */
2725   GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2726   GList *kids   = gtk_container_children (GTK_CONTAINER (list));
2727 
2728   gtk_widget_set_sensitive (GTK_WIDGET (head),     sensitive);
2729 #endif /* !HAVE_GTK2 */
2730   gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2731   gtk_widget_set_sensitive (GTK_WIDGET (buttons),  sensitive);
2732 
2733   gtk_widget_set_sensitive (GTK_WIDGET (blanker),  blankable);
2734 
2735 #ifdef HAVE_GTK2
2736   gtk_tree_view_column_set_visible (use, checkable);
2737 #else  /* !HAVE_GTK2 */
2738   if (checkable)
2739     gtk_widget_show (use);   /* the "Use" column header */
2740   else
2741     gtk_widget_hide (use);
2742 
2743   while (kids)
2744     {
2745       GtkBin *line = GTK_BIN (kids->data);
2746       GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2747       GtkWidget *line_check =
2748         GTK_WIDGET (gtk_container_children (line_hbox)->data);
2749 
2750       if (checkable)
2751         gtk_widget_show (line_check);
2752       else
2753         gtk_widget_hide (line_check);
2754 
2755       kids = kids->next;
2756     }
2757 #endif /* !HAVE_GTK2 */
2758 }
2759 
2760 
2761 static void
populate_prefs_page(state * s)2762 populate_prefs_page (state *s)
2763 {
2764   saver_preferences *p = &s->prefs;
2765 
2766   Bool can_lock_p = True;
2767 
2768   /* Disable all the "lock" controls if locking support was not provided
2769      at compile-time, or if running on MacOS. */
2770 # if defined(NO_LOCKING) || defined(__APPLE__)
2771   can_lock_p = False;
2772 # endif
2773 
2774 
2775   /* If there is only one screen, the mode menu contains
2776      "random" but not "random-same".
2777    */
2778   if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2779     p->mode = RANDOM_HACKS;
2780 
2781 
2782   /* The file supports timeouts of less than a minute, but the GUI does
2783      not, so throttle the values to be at least one minute (since "0" is
2784      a bad rounding choice...)
2785    */
2786 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2787   THROTTLE (timeout);
2788   THROTTLE (cycle);
2789   /* THROTTLE (passwd_timeout); */  /* GUI doesn't set this; leave it alone */
2790 # undef THROTTLE
2791 
2792 # define FMT_MINUTES(NAME,N) \
2793     gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2794 
2795 # define FMT_SECONDS(NAME,N) \
2796     gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2797 
2798   FMT_MINUTES ("timeout_spinbutton",      p->timeout);
2799   FMT_MINUTES ("cycle_spinbutton",        p->cycle);
2800   FMT_MINUTES ("lock_spinbutton",         p->lock_timeout);
2801   FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2802   FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2803   FMT_MINUTES ("dpms_off_spinbutton",     p->dpms_off);
2804   FMT_SECONDS ("fade_spinbutton",         p->fade_seconds);
2805 
2806 # undef FMT_MINUTES
2807 # undef FMT_SECONDS
2808 
2809 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2810   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2811                                 (ACTIVEP))
2812 
2813   TOGGLE_ACTIVE ("lock_button",       p->lock_p);
2814 #if 0
2815   TOGGLE_ACTIVE ("verbose_button",    p->verbose_p);
2816   TOGGLE_ACTIVE ("capture_button",    p->capture_stderr_p);
2817   TOGGLE_ACTIVE ("splash_button",     p->splash_p);
2818 #endif
2819   TOGGLE_ACTIVE ("dpms_button",       p->dpms_enabled_p);
2820   TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p);
2821   TOGGLE_ACTIVE ("grab_desk_button",  p->grab_desktop_p);
2822   TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2823   TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2824   TOGGLE_ACTIVE ("install_button",    p->install_cmap_p);
2825   TOGGLE_ACTIVE ("fade_button",       p->fade_p);
2826   TOGGLE_ACTIVE ("unfade_button",     p->unfade_p);
2827 
2828   switch (p->tmode)
2829     {
2830     case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio",         True); break;
2831     case TEXT_FILE:    TOGGLE_ACTIVE ("text_file_radio",    True); break;
2832     case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2833     case TEXT_URL:     TOGGLE_ACTIVE ("text_url_radio",     True); break;
2834     default:           TOGGLE_ACTIVE ("text_host_radio",    True); break;
2835     }
2836 
2837 # undef TOGGLE_ACTIVE
2838 
2839   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2840                       (p->image_directory ? p->image_directory : ""));
2841   gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2842                             p->random_image_p);
2843   gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2844                             p->random_image_p);
2845 
2846   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2847                       (p->text_literal ? p->text_literal : ""));
2848   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2849                       (p->text_file ? p->text_file : ""));
2850   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2851                       (p->text_program ? p->text_program : ""));
2852   gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2853                       (p->text_url ? p->text_url : ""));
2854 
2855   gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2856                             p->tmode == TEXT_LITERAL);
2857   gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2858                             p->tmode == TEXT_FILE);
2859   gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2860                             p->tmode == TEXT_FILE);
2861   gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2862                             p->tmode == TEXT_PROGRAM);
2863   gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2864                             p->tmode == TEXT_PROGRAM);
2865   gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2866                             p->tmode == TEXT_URL);
2867 
2868 
2869   /* Map the `saver_mode' enum to mode menu to values. */
2870   {
2871     GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2872 
2873     int i;
2874     for (i = 0; i < countof(mode_menu_order); i++)
2875       if (mode_menu_order[i] == p->mode)
2876         break;
2877     gtk_option_menu_set_history (opt, i);
2878     update_list_sensitivity (s);
2879   }
2880 
2881   {
2882     Bool found_any_writable_cells = False;
2883     Bool fading_possible = False;
2884     Bool dpms_supported = False;
2885 
2886     Display *dpy = GDK_DISPLAY();
2887     int nscreens = ScreenCount(dpy);  /* real screens, not Xinerama */
2888     int i;
2889     for (i = 0; i < nscreens; i++)
2890       {
2891 	Screen *s = ScreenOfDisplay (dpy, i);
2892 	if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2893 	  {
2894 	    found_any_writable_cells = True;
2895 	    break;
2896 	  }
2897       }
2898 
2899     fading_possible = found_any_writable_cells;
2900 #ifdef HAVE_XF86VMODE_GAMMA
2901     fading_possible = True;
2902 #endif
2903 
2904 #ifdef HAVE_DPMS_EXTENSION
2905     {
2906       int op = 0, event = 0, error = 0;
2907       if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2908         dpms_supported = True;
2909     }
2910 #endif /* HAVE_DPMS_EXTENSION */
2911 
2912 
2913 # define SENSITIZE(NAME,SENSITIVEP) \
2914     gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2915 
2916     /* Blanking and Locking
2917      */
2918     SENSITIZE ("lock_button",     can_lock_p);
2919     SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2920     SENSITIZE ("lock_mlabel",     can_lock_p && p->lock_p);
2921 
2922     /* DPMS
2923      */
2924     SENSITIZE ("dpms_frame",              dpms_supported);
2925     SENSITIZE ("dpms_button",             dpms_supported);
2926     SENSITIZE ("dpms_quickoff_button",    dpms_supported);
2927 
2928     SENSITIZE ("dpms_standby_label",      dpms_supported && p->dpms_enabled_p);
2929     SENSITIZE ("dpms_standby_mlabel",     dpms_supported && p->dpms_enabled_p);
2930     SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2931     SENSITIZE ("dpms_suspend_label",      dpms_supported && p->dpms_enabled_p);
2932     SENSITIZE ("dpms_suspend_mlabel",     dpms_supported && p->dpms_enabled_p);
2933     SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2934     SENSITIZE ("dpms_off_label",          dpms_supported && p->dpms_enabled_p);
2935     SENSITIZE ("dpms_off_mlabel",         dpms_supported && p->dpms_enabled_p);
2936     SENSITIZE ("dpms_off_spinbutton",     dpms_supported && p->dpms_enabled_p);
2937 
2938     /* Colormaps
2939      */
2940     SENSITIZE ("cmap_frame",      found_any_writable_cells || fading_possible);
2941     SENSITIZE ("install_button",  found_any_writable_cells);
2942     SENSITIZE ("fade_button",     fading_possible);
2943     SENSITIZE ("unfade_button",   fading_possible);
2944 
2945     SENSITIZE ("fade_label",      (fading_possible &&
2946                                    (p->fade_p || p->unfade_p)));
2947     SENSITIZE ("fade_spinbutton", (fading_possible &&
2948                                    (p->fade_p || p->unfade_p)));
2949 
2950 # undef SENSITIZE
2951   }
2952 }
2953 
2954 
2955 static void
populate_popup_window(state * s)2956 populate_popup_window (state *s)
2957 {
2958   GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2959   char *doc_string = 0;
2960 
2961   /* #### not in Gtk 1.2
2962   gtk_label_set_selectable (doc);
2963    */
2964 
2965 # ifdef HAVE_XML
2966   if (s->cdata)
2967     {
2968       free_conf_data (s->cdata);
2969       s->cdata = 0;
2970     }
2971 
2972   {
2973     saver_preferences *p = &s->prefs;
2974     int list_elt = selected_list_element (s);
2975     int hack_number = (list_elt >= 0 && list_elt < s->list_count
2976                        ? s->list_elt_to_hack_number[list_elt]
2977                        : -1);
2978     screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2979     if (hack)
2980       {
2981         GtkWidget *parent = name_to_widget (s, "settings_vbox");
2982         GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2983         const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2984         s->cdata = load_configurator (cmd_line, s->debug_p);
2985         if (s->cdata && s->cdata->widget)
2986           gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2987                               TRUE, TRUE, 0);
2988       }
2989   }
2990 
2991   doc_string = (s->cdata
2992                 ? s->cdata->description
2993                 : 0);
2994 # else  /* !HAVE_XML */
2995   doc_string = _("Descriptions not available: no XML support compiled in.");
2996 # endif /* !HAVE_XML */
2997 
2998   gtk_label_set_text (doc, (doc_string
2999                             ? _(doc_string)
3000                             : _("No description available.")));
3001 }
3002 
3003 
3004 static void
sensitize_demo_widgets(state * s,Bool sensitive_p)3005 sensitize_demo_widgets (state *s, Bool sensitive_p)
3006 {
3007   const char *names[] = { "demo", "settings",
3008                           "cmd_label", "cmd_text", "manual",
3009                           "visual", "visual_combo" };
3010   int i;
3011   for (i = 0; i < countof(names); i++)
3012     {
3013       GtkWidget *w = name_to_widget (s, names[i]);
3014       gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
3015     }
3016 }
3017 
3018 
3019 static void
sensitize_menu_items(state * s,Bool force_p)3020 sensitize_menu_items (state *s, Bool force_p)
3021 {
3022   static Bool running_p = False;
3023   static time_t last_checked = 0;
3024   time_t now = time ((time_t *) 0);
3025   const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
3026                           /* "demo" */ };
3027   int i;
3028 
3029   if (force_p || now > last_checked + 10)   /* check every 10 seconds */
3030     {
3031       running_p = xscreensaver_running_p (s);
3032       last_checked = time ((time_t *) 0);
3033     }
3034 
3035   for (i = 0; i < countof(names); i++)
3036     {
3037       GtkWidget *w = name_to_widget (s, names[i]);
3038       gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3039     }
3040 }
3041 
3042 
3043 /* When the File menu is de-posted after a "Restart Daemon" command,
3044    the window underneath doesn't repaint for some reason.  I guess this
3045    is a bug in exposure handling in GTK or GDK.  This works around it.
3046  */
3047 static void
force_dialog_repaint(state * s)3048 force_dialog_repaint (state *s)
3049 {
3050 #if 1
3051   /* Tell GDK to invalidate and repaint the whole window.
3052    */
3053   GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3054   GdkRegion *region = gdk_region_new ();
3055   GdkRectangle rect;
3056   rect.x = rect.y = 0;
3057   rect.width = rect.height = 32767;
3058   gdk_region_union_with_rect (region, &rect);
3059   gdk_window_invalidate_region (w, region, True);
3060   gdk_region_destroy (region);
3061   gdk_window_process_updates (w, True);
3062 #else
3063   /* Force the server to send an exposure event by creating and then
3064      destroying a window as a child of the top level shell.
3065    */
3066   Display *dpy = GDK_DISPLAY();
3067   Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3068   Window w;
3069   XWindowAttributes xgwa;
3070   XGetWindowAttributes (dpy, parent, &xgwa);
3071   w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3072   XMapRaised (dpy, w);
3073   XDestroyWindow (dpy, w);
3074   XSync (dpy, False);
3075 #endif
3076 }
3077 
3078 
3079 /* Even though we've given these text fields a maximum number of characters,
3080    their default size is still about 30 characters wide -- so measure out
3081    a string in their font, and resize them to just fit that.
3082  */
3083 static void
fix_text_entry_sizes(state * s)3084 fix_text_entry_sizes (state *s)
3085 {
3086   GtkWidget *w;
3087 
3088 # if 0   /* appears no longer necessary with Gtk 1.2.10 */
3089   const char * const spinbuttons[] = {
3090     "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3091     "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3092     "dpms_off_spinbutton",
3093     "-fade_spinbutton" };
3094   int i;
3095   int width = 0;
3096 
3097   for (i = 0; i < countof(spinbuttons); i++)
3098     {
3099       const char *n = spinbuttons[i];
3100       int cols = 4;
3101       while (*n == '-') n++, cols--;
3102       w = GTK_WIDGET (name_to_widget (s, n));
3103       width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3104       gtk_widget_set_usize (w, width, -2);
3105     }
3106 
3107   /* Now fix the width of the combo box.
3108    */
3109   w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3110   w = GTK_COMBO (w)->entry;
3111   width = gdk_string_width (w->style->font, "PseudoColor___");
3112   gtk_widget_set_usize (w, width, -2);
3113 
3114   /* Now fix the width of the file entry text.
3115    */
3116   w = GTK_WIDGET (name_to_widget (s, "image_text"));
3117   width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3118   gtk_widget_set_usize (w, width, -2);
3119 
3120   /* Now fix the width of the command line text.
3121    */
3122   w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3123   width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3124   gtk_widget_set_usize (w, width, -2);
3125 
3126 # endif /* 0 */
3127 
3128   /* Now fix the height of the list widget:
3129      make it default to being around 10 text-lines high instead of 4.
3130    */
3131   w = GTK_WIDGET (name_to_widget (s, "list"));
3132   {
3133     int lines = 10;
3134     int height;
3135     int leading = 3;  /* approximate is ok... */
3136     int border = 2;
3137 
3138 #ifdef HAVE_GTK2
3139     PangoFontMetrics *pain =
3140       pango_context_get_metrics (gtk_widget_get_pango_context (w),
3141                                  gtk_widget_get_style (w)->font_desc,
3142                                  gtk_get_default_language ());
3143     height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3144                            pango_font_metrics_get_descent (pain));
3145 #else  /* !HAVE_GTK2 */
3146     height = w->style->font->ascent + w->style->font->descent;
3147 #endif /* !HAVE_GTK2 */
3148 
3149     height += leading;
3150     height *= lines;
3151     height += border * 2;
3152     w = GTK_WIDGET (name_to_widget (s, "scroller"));
3153     gtk_widget_set_usize (w, -2, height);
3154   }
3155 }
3156 
3157 
3158 #ifndef HAVE_GTK2
3159 
3160 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3161  */
3162 
3163 static char *up_arrow_xpm[] = {
3164   "15 15 4 1",
3165   " 	c None",
3166   "-	c #FFFFFF",
3167   "+	c #D6D6D6",
3168   "@	c #000000",
3169 
3170   "       @       ",
3171   "       @       ",
3172   "      -+@      ",
3173   "      -+@      ",
3174   "     -+++@     ",
3175   "     -+++@     ",
3176   "    -+++++@    ",
3177   "    -+++++@    ",
3178   "   -+++++++@   ",
3179   "   -+++++++@   ",
3180   "  -+++++++++@  ",
3181   "  -+++++++++@  ",
3182   " -+++++++++++@ ",
3183   " @@@@@@@@@@@@@ ",
3184   "               ",
3185 
3186   /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3187      the end of the array (Gtk 1.2.5.) */
3188   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3189   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3190 };
3191 
3192 static char *down_arrow_xpm[] = {
3193   "15 15 4 1",
3194   " 	c None",
3195   "-	c #FFFFFF",
3196   "+	c #D6D6D6",
3197   "@	c #000000",
3198 
3199   "               ",
3200   " ------------- ",
3201   " -+++++++++++@ ",
3202   "  -+++++++++@  ",
3203   "  -+++++++++@  ",
3204   "   -+++++++@   ",
3205   "   -+++++++@   ",
3206   "    -+++++@    ",
3207   "    -+++++@    ",
3208   "     -+++@     ",
3209   "     -+++@     ",
3210   "      -+@      ",
3211   "      -+@      ",
3212   "       @       ",
3213   "       @       ",
3214 
3215   /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3216      the end of the array (Gtk 1.2.5.) */
3217   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3218   "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3219 };
3220 
3221 static void
pixmapify_button(state * s,int down_p)3222 pixmapify_button (state *s, int down_p)
3223 {
3224   GdkPixmap *pixmap;
3225   GdkBitmap *mask;
3226   GtkWidget *pixmapwid;
3227   GtkStyle *style;
3228   GtkWidget *w;
3229 
3230   w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3231   style = gtk_widget_get_style (w);
3232   mask = 0;
3233   pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3234                                          &style->bg[GTK_STATE_NORMAL],
3235                                          (down_p
3236                                           ? (gchar **) down_arrow_xpm
3237                                           : (gchar **) up_arrow_xpm));
3238   pixmapwid = gtk_pixmap_new (pixmap, mask);
3239   gtk_widget_show (pixmapwid);
3240   gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3241   gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3242 }
3243 
3244 static void
map_next_button_cb(GtkWidget * w,gpointer user_data)3245 map_next_button_cb (GtkWidget *w, gpointer user_data)
3246 {
3247   state *s = (state *) user_data;
3248   pixmapify_button (s, 1);
3249 }
3250 
3251 static void
map_prev_button_cb(GtkWidget * w,gpointer user_data)3252 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3253 {
3254   state *s = (state *) user_data;
3255   pixmapify_button (s, 0);
3256 }
3257 #endif /* !HAVE_GTK2 */
3258 
3259 
3260 #ifndef HAVE_GTK2
3261 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3262  */
3263 
3264 static void
you_are_not_a_unique_or_beautiful_snowflake(GtkWidget * label,GtkAllocation * allocation,void * foo)3265 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3266                                              GtkAllocation *allocation,
3267 					     void *foo)
3268 {
3269   GtkRequisition req;
3270   GtkWidgetAuxInfo *aux_info;
3271 
3272   aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3273 
3274   aux_info->width = allocation->width;
3275   aux_info->height = -2;
3276   aux_info->x = -1;
3277   aux_info->y = -1;
3278 
3279   gtk_widget_size_request (label, &req);
3280 }
3281 
3282 /* Feel the love.  Thanks to Nat Friedman for finding this workaround.
3283  */
3284 static void
eschew_gtk_lossage(GtkLabel * label)3285 eschew_gtk_lossage (GtkLabel *label)
3286 {
3287   GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3288   aux_info->width = GTK_WIDGET (label)->allocation.width;
3289   aux_info->height = -2;
3290   aux_info->x = -1;
3291   aux_info->y = -1;
3292 
3293   gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3294 
3295   gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3296                       GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3297                       0);
3298 
3299   gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3300 
3301   gtk_widget_queue_resize (GTK_WIDGET (label));
3302 }
3303 #endif /* !HAVE_GTK2 */
3304 
3305 
3306 static void
populate_demo_window(state * s,int list_elt)3307 populate_demo_window (state *s, int list_elt)
3308 {
3309   Display *dpy = GDK_DISPLAY();
3310   saver_preferences *p = &s->prefs;
3311   screenhack *hack;
3312   char *pretty_name;
3313   GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3314   GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3315   GtkEntry *cmd    = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3316   GtkCombo *vis    = GTK_COMBO (name_to_widget (s, "visual_combo"));
3317   GtkWidget *list  = GTK_WIDGET (name_to_widget (s, "list"));
3318 
3319   if (p->mode == BLANK_ONLY)
3320     {
3321       hack = 0;
3322       pretty_name = strdup (_("Blank Screen"));
3323       schedule_preview (s, 0);
3324     }
3325   else if (p->mode == DONT_BLANK)
3326     {
3327       hack = 0;
3328       pretty_name = strdup (_("Screen Saver Disabled"));
3329       schedule_preview (s, 0);
3330     }
3331   else
3332     {
3333       int hack_number = (list_elt >= 0 && list_elt < s->list_count
3334                          ? s->list_elt_to_hack_number[list_elt]
3335                          : -1);
3336       hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3337 
3338       pretty_name = (hack
3339                      ? (hack->name
3340                         ? strdup (hack->name)
3341                         : make_hack_name (dpy, hack->command))
3342                      : 0);
3343 
3344       if (hack)
3345         schedule_preview (s, hack->command);
3346       else
3347         schedule_preview (s, 0);
3348     }
3349 
3350   if (!pretty_name)
3351     pretty_name = strdup (_("Preview"));
3352 
3353   gtk_frame_set_label (frame1, _(pretty_name));
3354   gtk_frame_set_label (frame2, _(pretty_name));
3355 
3356   gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3357   gtk_entry_set_position (cmd, 0);
3358 
3359   {
3360     char title[255];
3361     sprintf (title, _("%s: %.100s Settings"),
3362              progclass, (pretty_name ? pretty_name : "???"));
3363     gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3364   }
3365 
3366   gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3367                       (hack
3368                        ? (hack->visual && *hack->visual
3369                           ? hack->visual
3370                           : _("Any"))
3371                        : ""));
3372 
3373   sensitize_demo_widgets (s, (hack ? True : False));
3374 
3375   if (pretty_name) free (pretty_name);
3376 
3377   ensure_selected_item_visible (list);
3378 
3379   s->_selected_list_element = list_elt;
3380 }
3381 
3382 
3383 static void
widget_deleter(GtkWidget * widget,gpointer data)3384 widget_deleter (GtkWidget *widget, gpointer data)
3385 {
3386   /* #### Well, I want to destroy these widgets, but if I do that, they get
3387      referenced again, and eventually I get a SEGV.  So instead of
3388      destroying them, I'll just hide them, and leak a bunch of memory
3389      every time the disk file changes.  Go go go Gtk!
3390 
3391      #### Ok, that's a lie, I get a crash even if I just hide the widget
3392      and don't ever delete it.  Fuck!
3393    */
3394 #if 0
3395   gtk_widget_destroy (widget);
3396 #else
3397   gtk_widget_hide (widget);
3398 #endif
3399 }
3400 
3401 
3402 static char **sort_hack_cmp_names_kludge;
3403 static int
sort_hack_cmp(const void * a,const void * b)3404 sort_hack_cmp (const void *a, const void *b)
3405 {
3406   if (a == b)
3407     return 0;
3408   else
3409     {
3410       int aa = *(int *) a;
3411       int bb = *(int *) b;
3412       const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3413       return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3414                      (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3415     }
3416 }
3417 
3418 
3419 static void
initialize_sort_map(state * s)3420 initialize_sort_map (state *s)
3421 {
3422   Display *dpy = GDK_DISPLAY();
3423   saver_preferences *p = &s->prefs;
3424   int i, j;
3425 
3426   if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3427   if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3428   if (s->hacks_available_p) free (s->hacks_available_p);
3429 
3430   s->list_elt_to_hack_number = (int *)
3431     calloc (sizeof(int), p->screenhacks_count + 1);
3432   s->hack_number_to_list_elt = (int *)
3433     calloc (sizeof(int), p->screenhacks_count + 1);
3434   s->hacks_available_p = (Bool *)
3435     calloc (sizeof(Bool), p->screenhacks_count + 1);
3436   s->total_available = 0;
3437 
3438   /* Check which hacks actually exist on $PATH
3439    */
3440   for (i = 0; i < p->screenhacks_count; i++)
3441     {
3442       screenhack *hack = p->screenhacks[i];
3443       int on = on_path_p (hack->command) ? 1 : 0;
3444       s->hacks_available_p[i] = on;
3445       s->total_available += on;
3446     }
3447 
3448   /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3449      hacks, if desired.
3450    */
3451   j = 0;
3452   for (i = 0; i < p->screenhacks_count; i++)
3453     {
3454       if (!p->ignore_uninstalled_p ||
3455           s->hacks_available_p[i])
3456         s->list_elt_to_hack_number[j++] = i;
3457     }
3458   s->list_count = j;
3459 
3460   for (; j < p->screenhacks_count; j++)
3461     s->list_elt_to_hack_number[j] = -1;
3462 
3463 
3464   /* Generate list of sortable names (once)
3465    */
3466   sort_hack_cmp_names_kludge = (char **)
3467     calloc (sizeof(char *), p->screenhacks_count);
3468   for (i = 0; i < p->screenhacks_count; i++)
3469     {
3470       screenhack *hack = p->screenhacks[i];
3471       char *name = (hack->name && *hack->name
3472                     ? strdup (hack->name)
3473                     : make_hack_name (dpy, hack->command));
3474       char *str;
3475       for (str = name; *str; str++)
3476         *str = tolower(*str);
3477       sort_hack_cmp_names_kludge[i] = name;
3478     }
3479 
3480   /* Sort list->hack map alphabetically
3481    */
3482   qsort (s->list_elt_to_hack_number,
3483          p->screenhacks_count,
3484          sizeof(*s->list_elt_to_hack_number),
3485          sort_hack_cmp);
3486 
3487   /* Free names
3488    */
3489   for (i = 0; i < p->screenhacks_count; i++)
3490     free (sort_hack_cmp_names_kludge[i]);
3491   free (sort_hack_cmp_names_kludge);
3492   sort_hack_cmp_names_kludge = 0;
3493 
3494   /* Build inverse table */
3495   for (i = 0; i < p->screenhacks_count; i++)
3496     {
3497       int n = s->list_elt_to_hack_number[i];
3498       if (n != -1)
3499         s->hack_number_to_list_elt[n] = i;
3500     }
3501 }
3502 
3503 
3504 static int
maybe_reload_init_file(state * s)3505 maybe_reload_init_file (state *s)
3506 {
3507   Display *dpy = GDK_DISPLAY();
3508   saver_preferences *p = &s->prefs;
3509   int status = 0;
3510 
3511   static Bool reentrant_lock = False;
3512   if (reentrant_lock) return 0;
3513   reentrant_lock = True;
3514 
3515   if (init_file_changed_p (p))
3516     {
3517       const char *f = init_file_name();
3518       char *b;
3519       int list_elt;
3520       GtkWidget *list;
3521 
3522       if (!f || !*f) return 0;
3523       b = (char *) malloc (strlen(f) + 1024);
3524       sprintf (b,
3525                _("Warning:\n\n"
3526 		 "file \"%s\" has changed, reloading.\n"),
3527                f);
3528       warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3529       free (b);
3530 
3531       load_init_file (dpy, p);
3532       initialize_sort_map (s);
3533 
3534       list_elt = selected_list_element (s);
3535       list = name_to_widget (s, "list");
3536       gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3537       populate_hack_list (s);
3538       force_list_select_item (s, list, list_elt, True);
3539       populate_prefs_page (s);
3540       populate_demo_window (s, list_elt);
3541       ensure_selected_item_visible (list);
3542 
3543       status = 1;
3544     }
3545 
3546   reentrant_lock = False;
3547   return status;
3548 }
3549 
3550 
3551 
3552 /* Making the preview window have the right X visual (so that GL works.)
3553  */
3554 
3555 static Visual *get_best_gl_visual (state *);
3556 
3557 static GdkVisual *
x_visual_to_gdk_visual(Visual * xv)3558 x_visual_to_gdk_visual (Visual *xv)
3559 {
3560   GList *gvs = gdk_list_visuals();
3561   if (!xv) return gdk_visual_get_system();
3562   for (; gvs; gvs = gvs->next)
3563     {
3564       GdkVisual *gv = (GdkVisual *) gvs->data;
3565       if (xv == GDK_VISUAL_XVISUAL (gv))
3566         return gv;
3567     }
3568   fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3569            blurb(), (unsigned long) xv->visualid);
3570   abort();
3571 }
3572 
3573 static void
clear_preview_window(state * s)3574 clear_preview_window (state *s)
3575 {
3576   GtkWidget *p;
3577   GdkWindow *window;
3578   GtkStyle  *style;
3579 
3580   if (!s->toplevel_widget) return;  /* very early */
3581   p = name_to_widget (s, "preview");
3582   window = GET_WINDOW (p);
3583 
3584   if (!window) return;
3585 
3586   /* Flush the widget background down into the window, in case a subproc
3587      has changed it. */
3588   style = gtk_widget_get_style (p);
3589   gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3590   gdk_window_clear (window);
3591 
3592   {
3593     int list_elt = selected_list_element (s);
3594     int hack_number = (list_elt >= 0
3595                        ? s->list_elt_to_hack_number[list_elt]
3596                        : -1);
3597     Bool available_p = (hack_number >= 0
3598                         ? s->hacks_available_p [hack_number]
3599                         : True);
3600     Bool nothing_p = (s->total_available < 5);
3601 
3602 #ifdef HAVE_GTK2
3603     GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3604     gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3605 			   (s->running_preview_error_p
3606                             ? (available_p ? 1 :
3607                                nothing_p ? 3 : 2)
3608                             : 0));
3609 #else /* !HAVE_GTK2 */
3610     if (s->running_preview_error_p)
3611       {
3612         const char * const lines1[] = { N_("No Preview"), N_("Available") };
3613         const char * const lines2[] = { N_("Not"), N_("Installed") };
3614         int nlines = countof(lines1);
3615         int lh = p->style->font->ascent + p->style->font->descent;
3616         int y, i;
3617         gint w, h;
3618 
3619         const char * const *lines = (available_p ? lines1 : lines2);
3620 
3621         gdk_window_get_size (window, &w, &h);
3622         y = (h - (lh * nlines)) / 2;
3623         y += p->style->font->ascent;
3624         for (i = 0; i < nlines; i++)
3625           {
3626             int sw = gdk_string_width (p->style->font, _(lines[i]));
3627             int x = (w - sw) / 2;
3628             gdk_draw_string (window, p->style->font,
3629                              p->style->fg_gc[GTK_STATE_NORMAL],
3630                              x, y, _(lines[i]));
3631             y += lh;
3632           }
3633       }
3634 #endif /* !HAVE_GTK2 */
3635   }
3636 
3637   gdk_flush ();
3638 }
3639 
3640 
3641 static void
reset_preview_window(state * s)3642 reset_preview_window (state *s)
3643 {
3644   /* On some systems (most recently, MacOS X) OpenGL programs get confused
3645      when you kill one and re-start another on the same window.  So maybe
3646      it's best to just always destroy and recreate the preview window
3647      when changing hacks, instead of always trying to reuse the same one?
3648    */
3649   GtkWidget *pr = name_to_widget (s, "preview");
3650   if (GET_REALIZED (pr))
3651     {
3652       GdkWindow *window = GET_WINDOW (pr);
3653       Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3654       Window id;
3655       gtk_widget_hide (pr);
3656       gtk_widget_unrealize (pr);
3657       gtk_widget_realize (pr);
3658       gtk_widget_show (pr);
3659       id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3660       if (s->debug_p)
3661         fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3662                  (unsigned int) oid,
3663                  (unsigned int) id);
3664     }
3665 }
3666 
3667 
3668 static void
fix_preview_visual(state * s)3669 fix_preview_visual (state *s)
3670 {
3671   GtkWidget *widget = name_to_widget (s, "preview");
3672   Visual *xvisual = get_best_gl_visual (s);
3673   GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3674   GdkVisual *dvisual = gdk_visual_get_system();
3675   GdkColormap *cmap = (visual == dvisual
3676                        ? gdk_colormap_get_system ()
3677                        : gdk_colormap_new (visual, False));
3678 
3679   if (s->debug_p)
3680     fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3681              (visual == dvisual ? "default" : "non-default"),
3682              (xvisual ? (unsigned long) xvisual->visualid : 0L));
3683 
3684   if (!GET_REALIZED (widget) ||
3685       gtk_widget_get_visual (widget) != visual)
3686     {
3687       gtk_widget_unrealize (widget);
3688       gtk_widget_set_visual (widget, visual);
3689       gtk_widget_set_colormap (widget, cmap);
3690       gtk_widget_realize (widget);
3691     }
3692 
3693   /* Set the Widget colors to be white-on-black. */
3694   {
3695     GdkWindow *window = GET_WINDOW (widget);
3696     GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3697     GdkColormap *cmap = gtk_widget_get_colormap (widget);
3698     GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3699     GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3700     GdkGC *fgc = gdk_gc_new(window);
3701     GdkGC *bgc = gdk_gc_new(window);
3702     if (!gdk_color_white (cmap, fg)) abort();
3703     if (!gdk_color_black (cmap, bg)) abort();
3704     gdk_gc_set_foreground (fgc, fg);
3705     gdk_gc_set_background (fgc, bg);
3706     gdk_gc_set_foreground (bgc, bg);
3707     gdk_gc_set_background (bgc, fg);
3708     style->fg_gc[GTK_STATE_NORMAL] = fgc;
3709     style->bg_gc[GTK_STATE_NORMAL] = fgc;
3710     gtk_widget_set_style (widget, style);
3711 
3712     /* For debugging purposes, put a title on the window (so that
3713        it can be easily found in the output of "xwininfo -tree".)
3714      */
3715     gdk_window_set_title (window, "Preview");
3716   }
3717 
3718   gtk_widget_show (widget);
3719 }
3720 
3721 
3722 /* Subprocesses
3723  */
3724 
3725 static char *
subproc_pretty_name(state * s)3726 subproc_pretty_name (state *s)
3727 {
3728   if (s->running_preview_cmd)
3729     {
3730       char *ps = strdup (s->running_preview_cmd);
3731       char *ss = strchr (ps, ' ');
3732       if (ss) *ss = 0;
3733       ss = strrchr (ps, '/');
3734       if (!ss)
3735         ss = ps;
3736       else
3737         {
3738           ss = strdup (ss+1);
3739           free (ps);
3740         }
3741       return ss;
3742     }
3743   else
3744     return strdup ("???");
3745 }
3746 
3747 
3748 static void
reap_zombies(state * s)3749 reap_zombies (state *s)
3750 {
3751   int wait_status = 0;
3752   pid_t pid;
3753   while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3754     {
3755       if (s->debug_p)
3756         {
3757           if (pid == s->running_preview_pid)
3758             {
3759               char *ss = subproc_pretty_name (s);
3760               fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3761                        (unsigned long) pid, ss);
3762               free (ss);
3763             }
3764           else
3765             fprintf (stderr, "%s: pid %lu died\n", blurb(),
3766                      (unsigned long) pid);
3767         }
3768     }
3769 }
3770 
3771 
3772 /* Mostly lifted from driver/subprocs.c */
3773 static Visual *
get_best_gl_visual(state * s)3774 get_best_gl_visual (state *s)
3775 {
3776   Display *dpy = GDK_DISPLAY();
3777   pid_t forked;
3778   int fds [2];
3779   int in, out;
3780   char buf[1024];
3781 
3782   char *av[10];
3783   int ac = 0;
3784 
3785   av[ac++] = "xscreensaver-gl-helper";
3786   av[ac] = 0;
3787 
3788   if (pipe (fds))
3789     {
3790       perror ("error creating pipe:");
3791       return 0;
3792     }
3793 
3794   in = fds [0];
3795   out = fds [1];
3796 
3797   switch ((int) (forked = fork ()))
3798     {
3799     case -1:
3800       {
3801         sprintf (buf, "%s: couldn't fork", blurb());
3802         perror (buf);
3803         exit (1);
3804       }
3805     case 0:
3806       {
3807         int stdout_fd = 1;
3808 
3809         close (in);  /* don't need this one */
3810         close (ConnectionNumber (dpy));		/* close display fd */
3811 
3812         if (dup2 (out, stdout_fd) < 0)		/* pipe stdout */
3813           {
3814             perror ("could not dup() a new stdout:");
3815             return 0;
3816           }
3817 
3818         execvp (av[0], av);			/* shouldn't return. */
3819 
3820         if (errno != ENOENT)
3821           {
3822             /* Ignore "no such file or directory" errors, unless verbose.
3823                Issue all other exec errors, though. */
3824             sprintf (buf, "%s: running %s", blurb(), av[0]);
3825             perror (buf);
3826           }
3827 
3828         /* Note that one must use _exit() instead of exit() in procs forked
3829            off of Gtk programs -- Gtk installs an atexit handler that has a
3830            copy of the X connection (which we've already closed, for safety.)
3831            If one uses exit() instead of _exit(), then one sometimes gets a
3832            spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3833         */
3834         _exit (1);                              /* exits fork */
3835         break;
3836       }
3837     default:
3838       {
3839         int result = 0;
3840         int wait_status = 0;
3841 
3842         FILE *f = fdopen (in, "r");
3843         unsigned int v = 0;
3844         char c;
3845 
3846         close (out);  /* don't need this one */
3847 
3848         *buf = 0;
3849         if (!fgets (buf, sizeof(buf)-1, f))
3850           *buf = 0;
3851         fclose (f);
3852 
3853         /* Wait for the child to die. */
3854         waitpid (-1, &wait_status, 0);
3855 
3856         if (1 == sscanf (buf, "0x%x %c", &v, &c))
3857           result = (int) v;
3858 
3859         if (result == 0)
3860           {
3861             if (s->debug_p)
3862               fprintf (stderr, "%s: %s did not report a GL visual!\n",
3863                        blurb(), av[0]);
3864             return 0;
3865           }
3866         else
3867           {
3868             Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3869             if (s->debug_p)
3870               fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3871                        blurb(), av[0], result);
3872             if (!v) abort();
3873             return v;
3874           }
3875       }
3876     }
3877 
3878   abort();
3879 }
3880 
3881 
3882 static void
kill_preview_subproc(state * s,Bool reset_p)3883 kill_preview_subproc (state *s, Bool reset_p)
3884 {
3885   s->running_preview_error_p = False;
3886 
3887   reap_zombies (s);
3888   clear_preview_window (s);
3889 
3890   if (s->subproc_check_timer_id)
3891     {
3892       gtk_timeout_remove (s->subproc_check_timer_id);
3893       s->subproc_check_timer_id = 0;
3894       s->subproc_check_countdown = 0;
3895     }
3896 
3897   if (s->running_preview_pid)
3898     {
3899       int status = kill (s->running_preview_pid, SIGTERM);
3900       char *ss = subproc_pretty_name (s);
3901 
3902       if (status < 0)
3903         {
3904           if (errno == ESRCH)
3905             {
3906               if (s->debug_p)
3907                 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3908                          blurb(), (unsigned long) s->running_preview_pid, ss);
3909             }
3910           else
3911             {
3912               char buf [1024];
3913               sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3914                        blurb(), (unsigned long) s->running_preview_pid, ss);
3915               perror (buf);
3916             }
3917         }
3918       else {
3919 	int endstatus;
3920 	waitpid(s->running_preview_pid, &endstatus, 0);
3921 	if (s->debug_p)
3922 	  fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3923 		   (unsigned long) s->running_preview_pid, ss);
3924       }
3925 
3926       free (ss);
3927       s->running_preview_pid = 0;
3928       if (s->running_preview_cmd) free (s->running_preview_cmd);
3929       s->running_preview_cmd = 0;
3930     }
3931 
3932   reap_zombies (s);
3933 
3934   if (reset_p)
3935     {
3936       reset_preview_window (s);
3937       clear_preview_window (s);
3938     }
3939 }
3940 
3941 
3942 /* Immediately and unconditionally launches the given process,
3943    after appending the -window-id option; sets running_preview_pid.
3944  */
3945 static void
launch_preview_subproc(state * s)3946 launch_preview_subproc (state *s)
3947 {
3948   saver_preferences *p = &s->prefs;
3949   Window id;
3950   char *new_cmd = 0;
3951   pid_t forked;
3952   const char *cmd = s->desired_preview_cmd;
3953 
3954   GtkWidget *pr = name_to_widget (s, "preview");
3955   GdkWindow *window;
3956 
3957   reset_preview_window (s);
3958 
3959   window = GET_WINDOW (pr);
3960 
3961   s->running_preview_error_p = False;
3962 
3963   if (s->preview_suppressed_p)
3964     {
3965       kill_preview_subproc (s, False);
3966       goto DONE;
3967     }
3968 
3969   new_cmd = malloc (strlen (cmd) + 40);
3970 
3971   id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3972   if (id == 0)
3973     {
3974       /* No window id?  No command to run. */
3975       free (new_cmd);
3976       new_cmd = 0;
3977     }
3978   else
3979     {
3980       /* We do this instead of relying on $XSCREENSAVER_WINDOW specifically
3981          so that third-party savers that don't implement -window-id will fail:
3982          otherwise we might have full-screen windows popping up when we were
3983          just trying to get a preview thumbnail.
3984        */
3985       strcpy (new_cmd, cmd);
3986       sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3987                (unsigned int) id);
3988     }
3989 
3990   kill_preview_subproc (s, False);
3991   if (! new_cmd)
3992     {
3993       s->running_preview_error_p = True;
3994       clear_preview_window (s);
3995       goto DONE;
3996     }
3997 
3998   switch ((int) (forked = fork ()))
3999     {
4000     case -1:
4001       {
4002         char buf[255];
4003         sprintf (buf, "%s: couldn't fork", blurb());
4004         perror (buf);
4005         s->running_preview_error_p = True;
4006         goto DONE;
4007         break;
4008       }
4009     case 0:
4010       {
4011         close (ConnectionNumber (GDK_DISPLAY()));
4012 
4013         hack_subproc_environment (id, s->debug_p);
4014 
4015         usleep (250000);  /* pause for 1/4th second before launching, to give
4016                              the previous program time to die and flush its X
4017                              buffer, so we don't get leftover turds on the
4018                              window. */
4019 
4020         exec_command (p->shell, new_cmd, p->nice_inferior);
4021         /* Don't bother printing an error message when we are unable to
4022            exec subprocesses; we handle that by polling the pid later.
4023 
4024            Note that one must use _exit() instead of exit() in procs forked
4025            off of Gtk programs -- Gtk installs an atexit handler that has a
4026            copy of the X connection (which we've already closed, for safety.)
4027            If one uses exit() instead of _exit(), then one sometimes gets a
4028            spurious "Gdk-ERROR: Fatal IO error on X server" error message.
4029         */
4030         _exit (1);  /* exits child fork */
4031         break;
4032 
4033       default:
4034 
4035         if (s->running_preview_cmd) free (s->running_preview_cmd);
4036         s->running_preview_cmd = strdup (s->desired_preview_cmd);
4037         s->running_preview_pid = forked;
4038 
4039         if (s->debug_p)
4040           {
4041             char *ss = subproc_pretty_name (s);
4042             fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4043                      (unsigned long) forked, ss);
4044             free (ss);
4045           }
4046         break;
4047       }
4048     }
4049 
4050   schedule_preview_check (s);
4051 
4052  DONE:
4053   if (new_cmd) free (new_cmd);
4054   new_cmd = 0;
4055 }
4056 
4057 
4058 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4059  */
4060 static void
hack_environment(state * s)4061 hack_environment (state *s)
4062 {
4063   static const char *def_path =
4064 # ifdef DEFAULT_PATH_PREFIX
4065     DEFAULT_PATH_PREFIX;
4066 # else
4067     "";
4068 # endif
4069 
4070   Display *dpy = GDK_DISPLAY();
4071   const char *odpy = DisplayString (dpy);
4072   char *ndpy = (char *) malloc(strlen(odpy) + 20);
4073   strcpy (ndpy, "DISPLAY=");
4074   strcat (ndpy, odpy);
4075   if (putenv (ndpy))
4076     abort ();
4077 
4078   if (s->debug_p)
4079     fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4080 
4081   /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4082      2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4083      So we must leak it (and/or the previous setting).  Yay.
4084    */
4085 
4086   if (def_path && *def_path)
4087     {
4088       const char *opath = getenv("PATH");
4089       char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4090       strcpy (npath, "PATH=");
4091       strcat (npath, def_path);
4092       strcat (npath, ":");
4093       strcat (npath, opath);
4094 
4095       if (putenv (npath))
4096 	abort ();
4097       /* do not free(npath) -- see above */
4098 
4099       if (s->debug_p)
4100         fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4101     }
4102 }
4103 
4104 
4105 static void
hack_subproc_environment(Window preview_window_id,Bool debug_p)4106 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4107 {
4108   /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4109      necessary yet, but it will make programs work if we had invoked
4110      them with "-root" and not with "-window-id" -- which, of course,
4111      doesn't happen.
4112    */
4113   char *nssw = (char *) malloc (40);
4114   sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4115 
4116   /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4117      any more, right?  It's not Posix, but everyone seems to have it. */
4118   if (putenv (nssw))
4119     abort ();
4120 
4121   if (debug_p)
4122     fprintf (stderr, "%s: %s\n", blurb(), nssw);
4123 
4124   /* do not free(nssw) -- see above */
4125 }
4126 
4127 
4128 /* Called from a timer:
4129    Launches the currently-chosen subprocess, if it's not already running.
4130    If there's a different process running, kills it.
4131  */
4132 static int
update_subproc_timer(gpointer data)4133 update_subproc_timer (gpointer data)
4134 {
4135   state *s = (state *) data;
4136   if (! s->desired_preview_cmd)
4137     kill_preview_subproc (s, True);
4138   else if (!s->running_preview_cmd ||
4139            !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4140     launch_preview_subproc (s);
4141 
4142   s->subproc_timer_id = 0;
4143   return FALSE;  /* do not re-execute timer */
4144 }
4145 
4146 static int
settings_timer(gpointer data)4147 settings_timer (gpointer data)
4148 {
4149   settings_cb (0, 0);
4150   return FALSE;
4151 }
4152 
4153 
4154 /* Call this when you think you might want a preview process running.
4155    It will set a timer that will actually launch that program a second
4156    from now, if you haven't changed your mind (to avoid double-click
4157    spazzing, etc.)  `cmd' may be null meaning "no process".
4158  */
4159 static void
schedule_preview(state * s,const char * cmd)4160 schedule_preview (state *s, const char *cmd)
4161 {
4162   int delay = 1000 * 0.5;   /* 1/2 second hysteresis */
4163 
4164   if (s->debug_p)
4165     {
4166       if (cmd)
4167         fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4168       else
4169         fprintf (stderr, "%s: scheduling preview death\n", blurb());
4170     }
4171 
4172   if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4173   s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4174 
4175   if (s->subproc_timer_id)
4176     gtk_timeout_remove (s->subproc_timer_id);
4177   s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4178 }
4179 
4180 
4181 /* Called from a timer:
4182    Checks to see if the subproc that should be running, actually is.
4183  */
4184 static int
check_subproc_timer(gpointer data)4185 check_subproc_timer (gpointer data)
4186 {
4187   state *s = (state *) data;
4188   Bool again_p = True;
4189 
4190   if (s->running_preview_error_p ||   /* already dead */
4191       s->running_preview_pid <= 0)
4192     {
4193       again_p = False;
4194     }
4195   else
4196     {
4197       int status;
4198       reap_zombies (s);
4199       status = kill (s->running_preview_pid, 0);
4200       if (status < 0 && errno == ESRCH)
4201         s->running_preview_error_p = True;
4202 
4203       if (s->debug_p)
4204         {
4205           char *ss = subproc_pretty_name (s);
4206           fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4207                    (unsigned long) s->running_preview_pid, ss,
4208                    (s->running_preview_error_p ? "dead" : "alive"));
4209           free (ss);
4210         }
4211 
4212       if (s->running_preview_error_p)
4213         {
4214           clear_preview_window (s);
4215           again_p = False;
4216         }
4217     }
4218 
4219   /* Otherwise, it's currently alive.  We might be checking again, or we
4220      might be satisfied. */
4221 
4222   if (--s->subproc_check_countdown <= 0)
4223     again_p = False;
4224 
4225   if (again_p)
4226     return TRUE;     /* re-execute timer */
4227   else
4228     {
4229       s->subproc_check_timer_id = 0;
4230       s->subproc_check_countdown = 0;
4231       return FALSE;  /* do not re-execute timer */
4232     }
4233 }
4234 
4235 
4236 /* Call this just after launching a subprocess.
4237    This sets a timer that will, five times a second for two seconds,
4238    check whether the program is still running.  The assumption here
4239    is that if the process didn't stay up for more than a couple of
4240    seconds, then either the program doesn't exist, or it doesn't
4241    take a -window-id argument.
4242  */
4243 static void
schedule_preview_check(state * s)4244 schedule_preview_check (state *s)
4245 {
4246   int seconds = 2;
4247   int ticks = 5;
4248 
4249   if (s->debug_p)
4250     fprintf (stderr, "%s: scheduling check\n", blurb());
4251 
4252   if (s->subproc_check_timer_id)
4253     gtk_timeout_remove (s->subproc_check_timer_id);
4254   s->subproc_check_timer_id =
4255     gtk_timeout_add (1000 / ticks,
4256                      check_subproc_timer, (gpointer) s);
4257   s->subproc_check_countdown = ticks * seconds;
4258 }
4259 
4260 
4261 static Bool
screen_blanked_p(void)4262 screen_blanked_p (void)
4263 {
4264   Atom type;
4265   int format;
4266   unsigned long nitems, bytesafter;
4267   unsigned char *dataP = 0;
4268   Display *dpy = GDK_DISPLAY();
4269   Bool blanked_p = False;
4270 
4271   if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4272                           XA_SCREENSAVER_STATUS,
4273                           0, 3, False, XA_INTEGER,
4274                           &type, &format, &nitems, &bytesafter,
4275                           &dataP)
4276       == Success
4277       && type == XA_INTEGER
4278       && nitems >= 3
4279       && dataP)
4280     {
4281       Atom *data = (Atom *) dataP;
4282       blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4283     }
4284 
4285   if (dataP) XFree (dataP);
4286 
4287   return blanked_p;
4288 }
4289 
4290 /* Wake up every now and then and see if the screen is blanked.
4291    If it is, kill off the small-window demo -- no point in wasting
4292    cycles by running two screensavers at once...
4293  */
4294 static int
check_blanked_timer(gpointer data)4295 check_blanked_timer (gpointer data)
4296 {
4297   state *s = (state *) data;
4298   Bool blanked_p = screen_blanked_p ();
4299   if (blanked_p && s->running_preview_pid)
4300     {
4301       if (s->debug_p)
4302         fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4303       kill_preview_subproc (s, True);
4304     }
4305 
4306   return True;  /* re-execute timer */
4307 }
4308 
4309 
4310 /* How many screens are there (including Xinerama.)
4311  */
4312 static int
screen_count(Display * dpy)4313 screen_count (Display *dpy)
4314 {
4315   int nscreens = ScreenCount(dpy);
4316 # ifdef HAVE_XINERAMA
4317   if (nscreens <= 1)
4318     {
4319       int event_number, error_number;
4320       if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4321           XineramaIsActive (dpy))
4322         {
4323           XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4324           if (xsi) XFree (xsi);
4325         }
4326     }
4327 # endif /* HAVE_XINERAMA */
4328 
4329   return nscreens;
4330 }
4331 
4332 
4333 /* Setting window manager icon
4334  */
4335 
4336 static void
init_icon(GdkWindow * window)4337 init_icon (GdkWindow *window)
4338 {
4339   GdkBitmap *mask = 0;
4340   GdkPixmap *pixmap =
4341     gdk_pixmap_create_from_xpm_d (window, &mask, 0,
4342                                   (gchar **) logo_50_xpm);
4343   if (pixmap)
4344     gdk_window_set_icon (window, 0, pixmap, mask);
4345 }
4346 
4347 
4348 /* The main demo-mode command loop.
4349  */
4350 
4351 #if 0
4352 static Bool
4353 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4354 	XrmRepresentation *type, XrmValue *value, XPointer closure)
4355 {
4356   int i;
4357   for (i = 0; quarks[i]; i++)
4358     {
4359       if (bindings[i] == XrmBindTightly)
4360 	fprintf (stderr, (i == 0 ? "" : "."));
4361       else if (bindings[i] == XrmBindLoosely)
4362 	fprintf (stderr, "*");
4363       else
4364 	fprintf (stderr, " ??? ");
4365       fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4366     }
4367 
4368   fprintf (stderr, ": %s\n", (char *) value->addr);
4369 
4370   return False;
4371 }
4372 #endif
4373 
4374 
4375 static Window
gnome_screensaver_window(Screen * screen)4376 gnome_screensaver_window (Screen *screen)
4377 {
4378   Display *dpy = DisplayOfScreen (screen);
4379   Window root = RootWindowOfScreen (screen);
4380   Window parent, *kids;
4381   unsigned int nkids;
4382   Window gnome_window = 0;
4383   int i;
4384 
4385   if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4386     abort ();
4387   for (i = 0; i < nkids; i++)
4388     {
4389       Atom type;
4390       int format;
4391       unsigned long nitems, bytesafter;
4392       unsigned char *name;
4393       if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4394                               False, XA_STRING, &type, &format, &nitems,
4395                               &bytesafter, &name)
4396           == Success
4397           && type != None
4398           && !strcmp ((char *) name, "gnome-screensaver"))
4399 	{
4400 	  gnome_window = kids[i];
4401           break;
4402 	}
4403     }
4404 
4405   if (kids) XFree ((char *) kids);
4406   return gnome_window;
4407 }
4408 
4409 static Bool
gnome_screensaver_active_p(void)4410 gnome_screensaver_active_p (void)
4411 {
4412   Display *dpy = GDK_DISPLAY();
4413   Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4414   return (w ? True : False);
4415 }
4416 
4417 static void
kill_gnome_screensaver(void)4418 kill_gnome_screensaver (void)
4419 {
4420   Display *dpy = GDK_DISPLAY();
4421   Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4422   if (w) XKillClient (dpy, (XID) w);
4423 }
4424 
4425 static Bool
kde_screensaver_active_p(void)4426 kde_screensaver_active_p (void)
4427 {
4428   FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4429                    "r");
4430   char buf[255];
4431   if (!p) return False;
4432   if (!fgets (buf, sizeof(buf)-1, p)) return False;
4433   pclose (p);
4434   if (!strcmp (buf, "true\n"))
4435     return True;
4436   else
4437     return False;
4438 }
4439 
4440 static void
kill_kde_screensaver(void)4441 kill_kde_screensaver (void)
4442 {
4443   /* Use empty body to kill warning from gcc -Wall with
4444      "warning: ignoring return value of 'system',
4445       declared with attribute warn_unused_result"
4446   */
4447   if (system ("dcop kdesktop KScreensaverIface enable false")) {}
4448 }
4449 
4450 
4451 static void
the_network_is_not_the_computer(state * s)4452 the_network_is_not_the_computer (state *s)
4453 {
4454   Display *dpy = GDK_DISPLAY();
4455   char *rversion = 0, *ruser = 0, *rhost = 0;
4456   char *luser, *lhost;
4457   char *msg = 0;
4458   struct passwd *p = getpwuid (getuid ());
4459   const char *d = DisplayString (dpy);
4460 
4461 # if defined(HAVE_UNAME)
4462   struct utsname uts;
4463   if (uname (&uts) < 0)
4464     lhost = "<UNKNOWN>";
4465   else
4466     lhost = uts.nodename;
4467 # elif defined(VMS)
4468   strcpy (lhost, getenv("SYS$NODE"));
4469 # else  /* !HAVE_UNAME && !VMS */
4470   strcat (lhost, "<UNKNOWN>");
4471 # endif /* !HAVE_UNAME && !VMS */
4472 
4473   if (p && p->pw_name)
4474     luser = p->pw_name;
4475   else
4476     luser = "???";
4477 
4478   server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4479 
4480   /* Make a buffer that's big enough for a number of copies of all the
4481      strings, plus some. */
4482   msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4483 			       (ruser ? strlen(ruser) : 0) +
4484 			       (rhost ? strlen(rhost) : 0) +
4485 			       strlen(lhost) +
4486 			       strlen(luser) +
4487 			       strlen(d) +
4488 			       1024));
4489   *msg = 0;
4490 
4491   if (!rversion || !*rversion)
4492     {
4493       sprintf (msg,
4494 	       _("Warning:\n\n"
4495 		 "The XScreenSaver daemon doesn't seem to be running\n"
4496 		 "on display \"%s\".  Launch it now?"),
4497 	       d);
4498     }
4499   else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4500     {
4501       /* Warn that the two processes are running as different users.
4502        */
4503       sprintf(msg,
4504 	    _("Warning:\n\n"
4505 	      "%s is running as user \"%s\" on host \"%s\".\n"
4506 	      "But the xscreensaver managing display \"%s\"\n"
4507 	      "is running as user \"%s\" on host \"%s\".\n"
4508 	      "\n"
4509 	      "Since they are different users, they won't be reading/writing\n"
4510 	      "the same ~/.xscreensaver file, so %s isn't\n"
4511 	      "going to work right.\n"
4512 	      "\n"
4513 	      "You should either re-run %s as \"%s\", or re-run\n"
4514 	      "xscreensaver as \"%s\".\n"
4515               "\n"
4516               "Restart the xscreensaver daemon now?\n"),
4517 	      progname, luser, lhost,
4518 	      d,
4519 	      (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4520 	      progname,
4521 	      progname, (ruser ? ruser : "???"),
4522 	      luser);
4523     }
4524   else if (rhost && *rhost && !!strcmp (rhost, lhost))
4525     {
4526       /* Warn that the two processes are running on different hosts.
4527        */
4528       sprintf (msg,
4529 	      _("Warning:\n\n"
4530 	       "%s is running as user \"%s\" on host \"%s\".\n"
4531 	       "But the xscreensaver managing display \"%s\"\n"
4532 	       "is running as user \"%s\" on host \"%s\".\n"
4533 	       "\n"
4534 	       "If those two machines don't share a file system (that is,\n"
4535 	       "if they don't see the same ~%s/.xscreensaver file) then\n"
4536 	       "%s won't work right.\n"
4537                "\n"
4538                "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4539 	       progname, luser, lhost,
4540 	       d,
4541 	       (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4542 	       luser,
4543 	       progname,
4544                lhost, luser);
4545     }
4546   else if (!!strcmp (rversion, s->short_version))
4547     {
4548       /* Warn that the version numbers don't match.
4549        */
4550       sprintf (msg,
4551 	     _("Warning:\n\n"
4552 	       "This is %s version %s.\n"
4553 	       "But the xscreensaver managing display \"%s\"\n"
4554 	       "is version %s.  This could cause problems.\n"
4555 	       "\n"
4556 	       "Restart the xscreensaver daemon now?\n"),
4557 	       progname, s->short_version,
4558 	       d,
4559 	       rversion);
4560     }
4561 
4562 
4563   if (*msg)
4564     warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4565 
4566   if (rversion) free (rversion);
4567   if (ruser) free (ruser);
4568   if (rhost) free (rhost);
4569   free (msg);
4570   msg = 0;
4571 
4572   /* Note: since these dialogs are not modal, they will stack up.
4573      So we do this check *after* popping up the "xscreensaver is not
4574      running" dialog so that these are on top.  Good enough.
4575    */
4576 
4577   if (gnome_screensaver_active_p ())
4578     warning_dialog (s->toplevel_widget,
4579                     _("Warning:\n\n"
4580                       "The GNOME screensaver daemon appears to be running.\n"
4581                       "It must be stopped for XScreenSaver to work properly.\n"
4582                       "\n"
4583                       "Stop the GNOME screen saver daemon now?\n"),
4584                     D_GNOME, 1);
4585 
4586   if (kde_screensaver_active_p ())
4587     warning_dialog (s->toplevel_widget,
4588                     _("Warning:\n\n"
4589                       "The KDE screen saver daemon appears to be running.\n"
4590                       "It must be stopped for XScreenSaver to work properly.\n"
4591                       "\n"
4592                       "Stop the KDE screen saver daemon now?\n"),
4593                     D_KDE, 1);
4594 }
4595 
4596 
4597 /* We use this error handler so that X errors are preceeded by the name
4598    of the program that generated them.
4599  */
4600 static int
demo_ehandler(Display * dpy,XErrorEvent * error)4601 demo_ehandler (Display *dpy, XErrorEvent *error)
4602 {
4603   state *s = global_state_kludge;  /* I hate C so much... */
4604   fprintf (stderr, "\nX error in %s:\n", blurb());
4605   XmuPrintDefaultErrorMessage (dpy, error, stderr);
4606   kill_preview_subproc (s, False);
4607   exit (-1);
4608   return 0;
4609 }
4610 
4611 
4612 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4613    of the program that generated them; and also that we can ignore one
4614    particular bogus error message that Gdk madly spews.
4615  */
4616 static void
g_log_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)4617 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4618                const gchar *message, gpointer user_data)
4619 {
4620   /* Ignore the message "Got event for unknown window: 0x...".
4621      Apparently some events are coming in for the xscreensaver window
4622      (presumably reply events related to the ClientMessage) and Gdk
4623      feels the need to complain about them.  So, just suppress any
4624      messages that look like that one.
4625    */
4626   if (strstr (message, "unknown window"))
4627     return;
4628 
4629   fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4630            (log_domain ? log_domain : progclass),
4631            (log_level == G_LOG_LEVEL_ERROR    ? "error" :
4632             log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4633             log_level == G_LOG_LEVEL_WARNING  ? "warning" :
4634             log_level == G_LOG_LEVEL_MESSAGE  ? "message" :
4635             log_level == G_LOG_LEVEL_INFO     ? "info" :
4636             log_level == G_LOG_LEVEL_DEBUG    ? "debug" : "???"),
4637            message,
4638            ((!*message || message[strlen(message)-1] != '\n')
4639             ? "\n" : ""));
4640 }
4641 
4642 
4643 STFU
4644 static char *defaults[] = {
4645 #include "XScreenSaver_ad.h"
4646  0
4647 };
4648 
4649 #if 0
4650 #ifdef HAVE_CRAPPLET
4651 static struct poptOption crapplet_options[] = {
4652   {NULL, '\0', 0, NULL, 0}
4653 };
4654 #endif /* HAVE_CRAPPLET */
4655 #endif /* 0 */
4656 
4657 const char *usage = "[--display dpy] [--prefs | --settings]"
4658 # ifdef HAVE_CRAPPLET
4659                     " [--crapplet]"
4660 # endif
4661             "\n\t\t   [--debug] [--sync] [--no-xshm] [--configdir dir]";
4662 
4663 static void
map_popup_window_cb(GtkWidget * w,gpointer user_data)4664 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4665 {
4666   state *s = (state *) user_data;
4667   Boolean oi = s->initializing_p;
4668 #ifndef HAVE_GTK2
4669   GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4670 #endif
4671   s->initializing_p = True;
4672 #ifndef HAVE_GTK2
4673   eschew_gtk_lossage (label);
4674 #endif
4675   s->initializing_p = oi;
4676 }
4677 
4678 
4679 #if 0
4680 static void
4681 print_widget_tree (GtkWidget *w, int depth)
4682 {
4683   int i;
4684   for (i = 0; i < depth; i++)
4685     fprintf (stderr, "  ");
4686   fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4687 
4688   if (GTK_IS_LIST (w))
4689     {
4690       for (i = 0; i < depth+1; i++)
4691         fprintf (stderr, "  ");
4692       fprintf (stderr, "...list kids...\n");
4693     }
4694   else if (GTK_IS_CONTAINER (w))
4695     {
4696       GList *kids = gtk_container_children (GTK_CONTAINER (w));
4697       while (kids)
4698         {
4699           print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4700           kids = kids->next;
4701         }
4702     }
4703 }
4704 #endif /* 0 */
4705 
4706 static int
delayed_scroll_kludge(gpointer data)4707 delayed_scroll_kludge (gpointer data)
4708 {
4709   state *s = (state *) data;
4710   GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4711   ensure_selected_item_visible (w);
4712 
4713   /* Oh, this is just fucking lovely, too. */
4714   w = GTK_WIDGET (name_to_widget (s, "preview"));
4715   gtk_widget_hide (w);
4716   gtk_widget_show (w);
4717 
4718   return FALSE;  /* do not re-execute timer */
4719 }
4720 
4721 #ifdef HAVE_GTK2
4722 
4723 static GtkWidget *
create_xscreensaver_demo(void)4724 create_xscreensaver_demo (void)
4725 {
4726   GtkWidget *nb;
4727 
4728   nb = name_to_widget (global_state_kludge, "preview_notebook");
4729   gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4730 
4731   return name_to_widget (global_state_kludge, "xscreensaver_demo");
4732 }
4733 
4734 static GtkWidget *
create_xscreensaver_settings_dialog(void)4735 create_xscreensaver_settings_dialog (void)
4736 {
4737   GtkWidget *w, *box;
4738 
4739   box = name_to_widget (global_state_kludge, "dialog_action_area");
4740 
4741   w = name_to_widget (global_state_kludge, "adv_button");
4742   gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4743 
4744   w = name_to_widget (global_state_kludge, "std_button");
4745   gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4746 
4747   return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4748 }
4749 
4750 #endif /* HAVE_GTK2 */
4751 
4752 int
main(int argc,char ** argv)4753 main (int argc, char **argv)
4754 {
4755   XtAppContext app;
4756   state S, *s;
4757   saver_preferences *p;
4758   Bool prefs_p = False;
4759   Bool settings_p = False;
4760   int i;
4761   Display *dpy;
4762   Widget toplevel_shell;
4763   char *real_progname = argv[0];
4764   char *window_title;
4765   char *geom = 0;
4766   Bool crapplet_p = False;
4767   char *str;
4768 
4769 #ifdef ENABLE_NLS
4770   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4771   textdomain (GETTEXT_PACKAGE);
4772 
4773 # ifdef HAVE_GTK2
4774   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4775 # else  /* !HAVE_GTK2 */
4776   if (!setlocale (LC_ALL, ""))
4777     fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4778 # endif /* !HAVE_GTK2 */
4779 
4780 #endif /* ENABLE_NLS */
4781 
4782   str = strrchr (real_progname, '/');
4783   if (str) real_progname = str+1;
4784 
4785   s = &S;
4786   memset (s, 0, sizeof(*s));
4787   s->initializing_p = True;
4788   p = &s->prefs;
4789 
4790   global_state_kludge = s;  /* I hate C so much... */
4791 
4792   progname = real_progname;
4793 
4794   s->short_version = (char *) malloc (5);
4795   memcpy (s->short_version, screensaver_id + 17, 4);
4796   s->short_version [4] = 0;
4797 
4798 
4799   /* Register our error message logger for every ``log domain'' known.
4800      There's no way to do this globally, so I grepped the Gtk/Gdk sources
4801      for all of the domains that seem to be in use.
4802   */
4803   {
4804     const char * const domains[] = { 0,
4805                                      "Gtk", "Gdk", "GLib", "GModule",
4806                                      "GThread", "Gnome", "GnomeUI" };
4807     for (i = 0; i < countof(domains); i++)
4808       g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4809   }
4810 
4811 #ifdef DEFAULT_ICONDIR  /* from -D on compile line */
4812 # ifndef HAVE_GTK2
4813   {
4814     const char *dir = DEFAULT_ICONDIR;
4815     if (*dir) add_pixmap_directory (dir);
4816   }
4817 # endif /* !HAVE_GTK2 */
4818 #endif /* DEFAULT_ICONDIR */
4819 
4820   /* This is gross, but Gtk understands --display and not -display...
4821    */
4822   for (i = 1; i < argc; i++)
4823     if (argv[i][0] && argv[i][1] &&
4824         !strncmp(argv[i], "-display", strlen(argv[i])))
4825       argv[i] = "--display";
4826 
4827 
4828   /* We need to parse this arg really early... Sigh. */
4829   for (i = 1; i < argc; i++)
4830     {
4831       if (argv[i] &&
4832           (!strcmp(argv[i], "--crapplet") ||
4833            !strcmp(argv[i], "--capplet")))
4834         {
4835 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4836           int j;
4837           crapplet_p = True;
4838           for (j = i; j < argc; j++)  /* remove it from the list */
4839             argv[j] = argv[j+1];
4840           argc--;
4841 # else  /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4842           fprintf (stderr, "%s: not compiled with --crapplet support\n",
4843                    real_progname);
4844           fprintf (stderr, "%s: %s\n", real_progname, usage);
4845           exit (1);
4846 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4847         }
4848       else if (argv[i] &&
4849                (!strcmp(argv[i], "--debug") ||
4850                 !strcmp(argv[i], "-debug") ||
4851                 !strcmp(argv[i], "-d")))
4852         {
4853           int j;
4854           s->debug_p = True;
4855           for (j = i; j < argc; j++)  /* remove it from the list */
4856             argv[j] = argv[j+1];
4857           argc--;
4858           i--;
4859         }
4860       else if (argv[i] &&
4861                argc > i+1 &&
4862                *argv[i+1] &&
4863                (!strcmp(argv[i], "-geometry") ||
4864                 !strcmp(argv[i], "-geom") ||
4865                 !strcmp(argv[i], "-geo") ||
4866                 !strcmp(argv[i], "-g")))
4867         {
4868           int j;
4869           geom = argv[i+1];
4870           for (j = i; j < argc; j++)  /* remove them from the list */
4871             argv[j] = argv[j+2];
4872           argc -= 2;
4873           i -= 2;
4874         }
4875       else if (argv[i] &&
4876                argc > i+1 &&
4877                *argv[i+1] &&
4878                (!strcmp(argv[i], "--configdir")))
4879         {
4880           int j;
4881           struct stat st;
4882           hack_configuration_path = argv[i+1];
4883           for (j = i; j < argc; j++)  /* remove them from the list */
4884             argv[j] = argv[j+2];
4885           argc -= 2;
4886           i -= 2;
4887 
4888           if (0 != stat (hack_configuration_path, &st))
4889             {
4890               char buf[255];
4891               sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4892               perror (buf);
4893               exit (1);
4894             }
4895           else if (!S_ISDIR (st.st_mode))
4896             {
4897               fprintf (stderr, "%s: not a directory: %s\n",
4898                        blurb(), hack_configuration_path);
4899               exit (1);
4900             }
4901         }
4902     }
4903 
4904 
4905   if (s->debug_p)
4906     fprintf (stderr, "%s: using config directory \"%s\"\n",
4907              progname, hack_configuration_path);
4908 
4909 
4910   /* Let Gtk open the X connection, then initialize Xt to use that
4911      same connection.  Doctor Frankenstein would be proud.
4912    */
4913 # ifdef HAVE_CRAPPLET
4914   if (crapplet_p)
4915     {
4916       GnomeClient *client;
4917       GnomeClientFlags flags = 0;
4918 
4919       int init_results = gnome_capplet_init ("screensaver-properties",
4920                                              s->short_version,
4921                                              argc, argv, NULL, 0, NULL);
4922       /* init_results is:
4923          0 upon successful initialization;
4924          1 if --init-session-settings was passed on the cmdline;
4925          2 if --ignore was passed on the cmdline;
4926         -1 on error.
4927 
4928          So the 1 signifies just to init the settings, and quit, basically.
4929          (Meaning launch the xscreensaver daemon.)
4930        */
4931 
4932       if (init_results < 0)
4933         {
4934 #  if 0
4935           g_error ("An initialization error occurred while "
4936                    "starting xscreensaver-capplet.\n");
4937 #  else  /* !0 */
4938           fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4939                    real_progname, init_results);
4940           exit (1);
4941 #  endif /* !0 */
4942         }
4943 
4944       client = gnome_master_client ();
4945 
4946       if (client)
4947         flags = gnome_client_get_flags (client);
4948 
4949       if (flags & GNOME_CLIENT_IS_CONNECTED)
4950         {
4951           int token =
4952             gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4953                                          gnome_client_get_id (client));
4954           if (token)
4955             {
4956               char *session_args[20];
4957               int i = 0;
4958               session_args[i++] = real_progname;
4959               session_args[i++] = "--capplet";
4960               session_args[i++] = "--init-session-settings";
4961               session_args[i] = 0;
4962               gnome_client_set_priority (client, 20);
4963               gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4964               gnome_client_set_restart_command (client, i, session_args);
4965             }
4966           else
4967             {
4968               gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4969             }
4970 
4971           gnome_client_flush (client);
4972         }
4973 
4974       if (init_results == 1)
4975 	{
4976 	  system ("xscreensaver -nosplash &");
4977 	  return 0;
4978 	}
4979 
4980     }
4981   else
4982 # endif /* HAVE_CRAPPLET */
4983     {
4984       gtk_init (&argc, &argv);
4985     }
4986 
4987 
4988   /* We must read exactly the same resources as xscreensaver.
4989      That means we must have both the same progclass *and* progname,
4990      at least as far as the resource database is concerned.  So,
4991      put "xscreensaver" in argv[0] while initializing Xt.
4992    */
4993   argv[0] = "xscreensaver";
4994   progname = argv[0];
4995 
4996 
4997   /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4998    */
4999   XtToolkitInitialize ();
5000   app = XtCreateApplicationContext ();
5001   dpy = GDK_DISPLAY();
5002   XtAppSetFallbackResources (app, defaults);
5003   XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
5004   toplevel_shell = XtAppCreateShell (progname, progclass,
5005                                      applicationShellWidgetClass,
5006                                      dpy, 0, 0);
5007 
5008   dpy = XtDisplay (toplevel_shell);
5009   db = XtDatabase (dpy);
5010   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
5011   XSetErrorHandler (demo_ehandler);
5012 
5013   /* Let's just ignore these.  They seem to confuse Irix Gtk... */
5014   signal (SIGPIPE, SIG_IGN);
5015 
5016   /* After doing Xt-style command-line processing, complain about any
5017      unrecognized command-line arguments.
5018    */
5019   for (i = 1; i < argc; i++)
5020     {
5021       char *str = argv[i];
5022       if (str[0] == '-' && str[1] == '-')
5023 	str++;
5024       if (!strcmp (str, "-prefs"))
5025 	prefs_p = True;
5026       else if (!strcmp (str, "-settings"))
5027 	settings_p = True;
5028       else if (crapplet_p)
5029         /* There are lots of random args that we don't care about when we're
5030            started as a crapplet, so just ignore unknown args in that case. */
5031         ;
5032       else
5033 	{
5034 	  fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
5035                    argv[i]);
5036           fprintf (stderr, "%s: %s\n", real_progname, usage);
5037           exit (1);
5038 	}
5039     }
5040 
5041   /* Load the init file, which may end up consulting the X resource database
5042      and the site-wide app-defaults file.  Note that at this point, it's
5043      important that `progname' be "xscreensaver", rather than whatever
5044      was in argv[0].
5045    */
5046   p->db = db;
5047   s->nscreens = screen_count (dpy);
5048 
5049   hack_environment (s);  /* must be before initialize_sort_map() */
5050 
5051   load_init_file (dpy, p);
5052   initialize_sort_map (s);
5053 
5054   /* Now that Xt has been initialized, and the resources have been read,
5055      we can set our `progname' variable to something more in line with
5056      reality.
5057    */
5058   progname = real_progname;
5059 
5060 
5061 #if 0
5062   /* Print out all the resources we read. */
5063   {
5064     XrmName name = { 0 };
5065     XrmClass class = { 0 };
5066     int count = 0;
5067     XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5068 			  (POINTER) &count);
5069   }
5070 #endif
5071 
5072 
5073   /* Intern the atoms that xscreensaver_command() needs.
5074    */
5075   XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5076   XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5077   XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5078   XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5079   XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5080   XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5081   XA_SELECT = XInternAtom (dpy, "SELECT", False);
5082   XA_DEMO = XInternAtom (dpy, "DEMO", False);
5083   XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5084   XA_SUSPEND = XInternAtom (dpy, "SUSPEND", False);
5085   XA_BLANK = XInternAtom (dpy, "BLANK", False);
5086   XA_LOCK = XInternAtom (dpy, "LOCK", False);
5087   XA_NEXT = XInternAtom (dpy, "NEXT", False);
5088   XA_PREV = XInternAtom (dpy, "PREV", False);
5089   XA_EXIT = XInternAtom (dpy, "EXIT", False);
5090   XA_RESTART = XInternAtom (dpy, "RESTART", False);
5091 
5092 
5093   /* Create the window and all its widgets.
5094    */
5095   s->base_widget     = create_xscreensaver_demo ();
5096   s->popup_widget    = create_xscreensaver_settings_dialog ();
5097   s->toplevel_widget = s->base_widget;
5098 
5099 
5100   /* Set the main window's title. */
5101   {
5102     char *base_title = _("Screensaver Preferences");
5103     char *v = (char *) strdup(strchr(screensaver_id, ' '));
5104     char *s1, *s2, *s3, *s4;
5105     s1 = (char *) strchr(v,  ' '); s1++;
5106     s2 = (char *) strchr(s1, ' ');
5107     s3 = (char *) strchr(v,  '('); s3++;
5108     s4 = (char *) strchr(s3, ')');
5109     *s2 = 0;
5110     *s4 = 0;
5111 
5112     window_title = (char *) malloc (strlen (base_title) +
5113                                     strlen (progclass) +
5114                                     strlen (s1) + strlen (s3) +
5115                                     100);
5116     sprintf (window_title, "%s  (%s %s, %s)", base_title, progclass, s1, s3);
5117     gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5118     gtk_window_set_title (GTK_WINDOW (s->popup_widget),    window_title);
5119     free (v);
5120   }
5121 
5122   /* Adjust the (invisible) notebooks on the popup dialog... */
5123   {
5124     GtkNotebook *notebook =
5125       GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5126     GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5127     int page = 0;
5128 
5129 # ifdef HAVE_XML
5130     gtk_widget_hide (std);
5131 # else  /* !HAVE_XML */
5132     /* Make the advanced page be the only one available. */
5133     gtk_widget_set_sensitive (std, False);
5134     std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5135     gtk_widget_hide (std);
5136     std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5137     gtk_widget_hide (std);
5138     page = 1;
5139 # endif /* !HAVE_XML */
5140 
5141     gtk_notebook_set_page (notebook, page);
5142     gtk_notebook_set_show_tabs (notebook, False);
5143   }
5144 
5145   /* Various other widget initializations...
5146    */
5147   gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5148                       GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5149                       (gpointer) s);
5150   gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5151                       GTK_SIGNAL_FUNC (wm_popup_close_cb),
5152                       (gpointer) s);
5153 
5154   populate_hack_list (s);
5155   populate_prefs_page (s);
5156   sensitize_demo_widgets (s, False);
5157   fix_text_entry_sizes (s);
5158   scroll_to_current_hack (s);
5159 
5160   gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5161                       "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5162                       (gpointer) s);
5163 
5164 #ifndef HAVE_GTK2
5165   gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5166                       "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5167                       (gpointer) s);
5168   gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5169                       "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5170                       (gpointer) s);
5171 #endif /* !HAVE_GTK2 */
5172 
5173   /* Hook up callbacks to the items on the mode menu. */
5174   {
5175     GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5176     GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5177     GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5178     int i;
5179     for (i = 0; kids; kids = kids->next, i++)
5180       {
5181         gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5182                             GTK_SIGNAL_FUNC (mode_menu_item_cb),
5183                             (gpointer) s);
5184 
5185         /* The "random-same" mode menu item does not appear unless
5186            there are multple screens.
5187          */
5188         if (s->nscreens <= 1 &&
5189             mode_menu_order[i] == RANDOM_HACKS_SAME)
5190           gtk_widget_hide (GTK_WIDGET (kids->data));
5191       }
5192 
5193     if (s->nscreens <= 1)   /* recompute option-menu size */
5194       {
5195         gtk_widget_unrealize (GTK_WIDGET (menu));
5196         gtk_widget_realize (GTK_WIDGET (menu));
5197       }
5198   }
5199 
5200 
5201   /* Handle the -prefs command-line argument. */
5202   if (prefs_p)
5203     {
5204       GtkNotebook *notebook =
5205         GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5206       gtk_notebook_set_page (notebook, 1);
5207     }
5208 
5209 # ifdef HAVE_CRAPPLET
5210   if (crapplet_p)
5211     {
5212       GtkWidget *capplet;
5213       GtkWidget *outer_vbox;
5214 
5215       gtk_widget_hide (s->toplevel_widget);
5216 
5217       capplet = capplet_widget_new ();
5218 
5219       /* Make there be a "Close" button instead of "OK" and "Cancel" */
5220 # ifdef HAVE_CRAPPLET_IMMEDIATE
5221       capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5222 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5223       /* In crapplet-mode, take off the menubar. */
5224       gtk_widget_hide (name_to_widget (s, "menubar"));
5225 
5226       /* Reparent our top-level container to be a child of the capplet
5227          window.
5228        */
5229       outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5230       gtk_widget_ref (outer_vbox);
5231       gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5232                             outer_vbox);
5233       STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5234       gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5235 
5236       /* Find the window above us, and set the title and close handler. */
5237       {
5238         GtkWidget *window = capplet;
5239         while (window && !GTK_IS_WINDOW (window))
5240           window = GET_PARENT (window);
5241         if (window)
5242           {
5243             gtk_window_set_title (GTK_WINDOW (window), window_title);
5244             gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5245                                 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5246                                 (gpointer) s);
5247           }
5248       }
5249 
5250       s->toplevel_widget = capplet;
5251     }
5252 # endif /* HAVE_CRAPPLET */
5253 
5254 
5255   /* The Gnome folks hate the menubar.  I think it's important to have access
5256      to the commands on the File menu (Restart Daemon, etc.) and to the
5257      About and Documentation commands on the Help menu.
5258    */
5259 #if 0
5260 #ifdef HAVE_GTK2
5261   gtk_widget_hide (name_to_widget (s, "menubar"));
5262 #endif
5263 #endif
5264 
5265   free (window_title);
5266   window_title = 0;
5267 
5268 #ifdef HAVE_GTK2
5269   /* After picking the default size, allow -geometry to override it. */
5270   if (geom)
5271     gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5272 #endif
5273 
5274   gtk_widget_show (s->toplevel_widget);
5275   init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget)));  /* after `show' */
5276   fix_preview_visual (s);
5277 
5278   /* Realize page zero, so that we can diddle the scrollbar when the
5279      user tabs back to it -- otherwise, the current hack isn't scrolled
5280      to the first time they tab back there, when started with "-prefs".
5281      (Though it is if they then tab away, and back again.)
5282 
5283      #### Bah!  This doesn't work.  Gtk eats my ass!  Someone who
5284      #### understands this crap, explain to me how to make this work.
5285   */
5286   gtk_widget_realize (name_to_widget (s, "demos_table"));
5287 
5288 
5289   gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5290 
5291 
5292   /* Handle the --settings command-line argument. */
5293   if (settings_p)
5294     gtk_timeout_add (500, settings_timer, 0);
5295 
5296 
5297   /* Issue any warnings about the running xscreensaver daemon. */
5298   if (! s->debug_p)
5299     the_network_is_not_the_computer (s);
5300 
5301 
5302   if (senesculent_p())
5303     warning_dialog (s->toplevel_widget,
5304       _("Warning:\n\n"
5305         "This version of xscreensaver is VERY OLD!\n"
5306         "Please upgrade!\n"
5307         "\n"
5308         "https://www.jwz.org/xscreensaver/\n"
5309         "\n"
5310         "(If this is the latest version that your distro ships, then\n"
5311         "your distro is doing you a disservice. Build from source.)\n"
5312         ),
5313       D_NONE, 7);
5314 
5315 
5316   /* Run the Gtk event loop, and not the Xt event loop.  This means that
5317      if there were Xt timers or fds registered, they would never get serviced,
5318      and if there were any Xt widgets, they would never have events delivered.
5319      Fortunately, we're using Gtk for all of the UI, and only initialized
5320      Xt so that we could process the command line and use the X resource
5321      manager.
5322    */
5323   s->initializing_p = False;
5324 
5325   /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5326      after we start up.  Otherwise, it always appears scrolled to the top
5327      when in crapplet-mode. */
5328   gtk_timeout_add (500, delayed_scroll_kludge, s);
5329 
5330 
5331 #if 1
5332   /* Load every configurator in turn, to scan them for errors all at once. */
5333   if (s->debug_p)
5334     {
5335       int i;
5336       for (i = 0; i < p->screenhacks_count; i++)
5337         {
5338           screenhack *hack = p->screenhacks[i];
5339           conf_data *d = load_configurator (hack->command, s->debug_p);
5340           if (d) free_conf_data (d);
5341         }
5342     }
5343 #endif
5344 
5345 
5346 # ifdef HAVE_CRAPPLET
5347   if (crapplet_p)
5348     capplet_gtk_main ();
5349   else
5350 # endif /* HAVE_CRAPPLET */
5351     gtk_main ();
5352 
5353   kill_preview_subproc (s, False);
5354   exit (0);
5355 }
5356 
5357 #endif /* HAVE_GTK -- whole file */
5358