1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2002 Jamie Zawinski <jwz@jwz.org>
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
17 #ifdef HAVE_GTK /* whole file */
26 # include <pwd.h> /* for getpwuid() */
32 # include <sys/utsname.h> /* for uname() */
33 #endif /* HAVE_UNAME */
43 #ifdef HAVE_SYS_WAIT_H
44 # include <sys/wait.h> /* for waitpid() and associated macros */
48 #include <X11/Xproto.h> /* for CARD32 */
49 #include <X11/Xatom.h> /* for XA_INTEGER */
50 #include <X11/Intrinsic.h>
51 #include <X11/StringDefs.h>
53 /* We don't actually use any widget internals, but these are included
54 so that gdb will have debug info for the widgets... */
55 #include <X11/IntrinsicP.h>
56 #include <X11/ShellP.h>
60 # include <X11/Xmu/Error.h>
62 # include <Xmu/Error.h>
72 # include <capplet-widget.h>
79 #include "resources.h" /* for parse_time() */
80 #include "visual.h" /* for has_writable_cells() */
81 #include "remote.h" /* for xscreensaver_command() */
84 #include "logo-50.xpm"
85 #include "logo-180.xpm"
87 #include "demo-Gtk-widgets.h"
88 #include "demo-Gtk-support.h"
89 #include "demo-Gtk-conf.h"
97 extern void exec_command (const char *shell, const char *command, int nice);
100 #define countof(x) (sizeof((x))/sizeof((*x)))
104 char *progclass = "XScreenSaver";
107 /* The order of the items in the mode menu. */
108 static int mode_menu_order[] = {
109 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
114 char *short_version; /* version number of this xscreensaver build */
116 GtkWidget *toplevel_widget; /* the main window */
117 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
118 GtkWidget *popup_widget; /* the "Settings" dialog */
119 conf_data *cdata; /* private data for per-hack configuration */
121 Bool debug_p; /* whether to print diagnostics */
122 Bool initializing_p; /* flag for breaking recursion loops */
123 Bool saving_p; /* flag for breaking recursion loops */
125 char *desired_preview_cmd; /* subprocess we intend to run */
126 char *running_preview_cmd; /* subprocess we are currently running */
127 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
128 Bool running_preview_error_p; /* whether the pid died abnormally */
130 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
131 int subproc_timer_id; /* timer to delay subproc launch */
132 int subproc_check_timer_id; /* timer to check whether it started up */
133 int subproc_check_countdown; /* how many more checks left */
135 int *list_elt_to_hack_number; /* table for sorting the hack list */
136 int *hack_number_to_list_elt; /* the inverse table */
138 int _selected_list_element; /* don't use this: call
139 selected_list_element() instead */
141 saver_preferences prefs;
146 /* Total fucking evilness due to the fact that it's rocket science to get
147 a closure object of our own down into the various widget callbacks. */
148 static state *global_state_kludge;
151 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
152 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
153 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
156 static void populate_demo_window (state *, int list_elt);
157 static void populate_prefs_page (state *);
158 static void populate_popup_window (state *);
160 static Bool flush_dialog_changes_and_save (state *);
161 static Bool flush_popup_changes_and_save (state *);
163 static int maybe_reload_init_file (state *);
164 static void await_xscreensaver (state *);
166 static void schedule_preview (state *, const char *cmd);
167 static void kill_preview_subproc (state *);
168 static void schedule_preview_check (state *);
172 /* Some random utility functions
178 time_t now = time ((time_t *) 0);
179 char *ct = (char *) ctime (&now);
180 static char buf[255];
181 int n = strlen(progname);
183 strncpy(buf, progname, n);
186 strncpy(buf+n, ct+11, 8);
187 strcpy(buf+n+9, ": ");
193 name_to_widget (state *s, const char *name)
200 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
203 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
207 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
212 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
213 Takes a scroller, viewport, or list as an argument.
216 ensure_selected_item_visible (GtkWidget *widget)
218 GtkScrolledWindow *scroller = 0;
220 GtkList *list_widget = 0;
224 GtkWidget *selected = 0;
227 gint parent_h, child_y, child_h, children_h, ignore;
228 double ratio_t, ratio_b;
230 if (GTK_IS_SCROLLED_WINDOW (widget))
232 scroller = GTK_SCROLLED_WINDOW (widget);
233 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
234 list_widget = GTK_LIST (GTK_BIN(vp)->child);
236 else if (GTK_IS_VIEWPORT (widget))
238 vp = GTK_VIEWPORT (widget);
239 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
240 list_widget = GTK_LIST (GTK_BIN(vp)->child);
242 else if (GTK_IS_LIST (widget))
244 list_widget = GTK_LIST (widget);
245 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
246 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
251 slist = list_widget->selection;
252 selected = (slist ? GTK_WIDGET (slist->data) : 0);
256 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
258 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
259 kids; kids = kids->next)
262 adj = gtk_scrolled_window_get_vadjustment (scroller);
264 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
265 &ignore, &ignore, &ignore, &parent_h, &ignore);
266 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
267 &ignore, &child_y, &ignore, &child_h, &ignore);
268 children_h = nkids * child_h;
270 ratio_t = ((double) child_y) / ((double) children_h);
271 ratio_b = ((double) child_y + child_h) / ((double) children_h);
273 if (adj->upper == 0.0) /* no items in list */
276 if (ratio_t < (adj->value / adj->upper) ||
277 ratio_b > ((adj->value + adj->page_size) / adj->upper))
280 int slop = parent_h * 0.75; /* how much to overshoot by */
282 if (ratio_t < (adj->value / adj->upper))
284 double ratio_w = ((double) parent_h) / ((double) children_h);
285 double ratio_l = (ratio_b - ratio_t);
286 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
289 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
291 target = ratio_t * adj->upper;
295 if (target > adj->upper - adj->page_size)
296 target = adj->upper - adj->page_size;
300 gtk_adjustment_set_value (adj, target);
305 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
307 GtkWidget *shell = GTK_WIDGET (user_data);
308 while (shell->parent)
309 shell = shell->parent;
310 gtk_widget_destroy (GTK_WIDGET (shell));
314 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
316 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
318 restart_menu_cb (widget, user_data);
319 warning_dialog_dismiss_cb (widget, user_data);
323 warning_dialog (GtkWidget *parent, const char *message,
324 Boolean restart_button_p, int center)
326 char *msg = strdup (message);
329 GtkWidget *dialog = gtk_dialog_new ();
330 GtkWidget *label = 0;
332 GtkWidget *cancel = 0;
335 while (parent && !parent->window)
336 parent = parent->parent;
338 if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
340 fprintf (stderr, "%s: too early for dialog?\n", progname);
348 char *s = strchr (head, '\n');
351 sprintf (name, "label%d", i++);
354 label = gtk_label_new (head);
358 GTK_WIDGET (label)->style =
359 gtk_style_copy (GTK_WIDGET (label)->style);
360 GTK_WIDGET (label)->style->font =
361 gdk_font_load (get_string_resource("warning_dialog.headingFont",
363 gtk_widget_set_style (GTK_WIDGET (label),
364 GTK_WIDGET (label)->style);
368 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
369 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
370 label, TRUE, TRUE, 0);
371 gtk_widget_show (label);
382 label = gtk_label_new ("");
383 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
384 label, TRUE, TRUE, 0);
385 gtk_widget_show (label);
387 label = gtk_hbutton_box_new ();
388 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
389 label, TRUE, TRUE, 0);
391 ok = gtk_button_new_with_label ("OK");
392 gtk_container_add (GTK_CONTAINER (label), ok);
394 if (restart_button_p)
396 cancel = gtk_button_new_with_label ("Cancel");
397 gtk_container_add (GTK_CONTAINER (label), cancel);
400 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
401 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
402 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
403 gtk_widget_show (ok);
405 gtk_widget_show (cancel);
406 gtk_widget_show (label);
407 gtk_widget_show (dialog);
408 /* gtk_window_set_default (GTK_WINDOW (dialog), ok);*/
410 if (restart_button_p)
412 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
413 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
415 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
416 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
421 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
422 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
426 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
427 GTK_WIDGET (parent)->window);
429 gdk_window_show (GTK_WIDGET (dialog)->window);
430 gdk_window_raise (GTK_WIDGET (dialog)->window);
437 run_cmd (state *s, Atom command, int arg)
442 flush_dialog_changes_and_save (s);
443 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
448 sprintf (buf, "Error:\n\n%s", err);
450 strcpy (buf, "Unknown error!");
451 warning_dialog (s->toplevel_widget, buf, False, 100);
458 run_hack (state *s, int list_elt, Bool report_errors_p)
461 if (list_elt < 0) return;
462 hack_number = s->list_elt_to_hack_number[list_elt];
464 flush_dialog_changes_and_save (s);
465 schedule_preview (s, 0);
467 run_cmd (s, XA_DEMO, hack_number + 1);
471 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
483 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
485 state *s = global_state_kludge; /* I hate C so much... */
486 flush_dialog_changes_and_save (s);
487 kill_preview_subproc (s);
492 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
494 state *s = (state *) data;
495 flush_dialog_changes_and_save (s);
501 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
504 char *vers = strdup (screensaver_id + 4);
507 char *desc = "For updates, check http://www.jwz.org/xscreensaver/";
509 s = strchr (vers, ',');
513 sprintf(copy, "Copyright \251 1991-2002 %s", s);
515 sprintf (msg, "%s\n\n%s", copy, desc);
517 /* I can't make gnome_about_new() work here -- it starts dying in
518 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
519 then this might be the thing to do:
523 const gchar *auth[] = { 0 };
524 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
526 gtk_widget_show (about);
528 #else / * GTK but not GNOME * /
532 GdkColormap *colormap;
533 GdkPixmap *gdkpixmap;
536 GtkWidget *dialog = gtk_dialog_new ();
537 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
538 GtkWidget *parent = GTK_WIDGET (menuitem);
539 while (parent->parent)
540 parent = parent->parent;
542 hbox = gtk_hbox_new (FALSE, 20);
543 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
544 hbox, TRUE, TRUE, 0);
546 colormap = gtk_widget_get_colormap (parent);
548 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
549 (gchar **) logo_180_xpm);
550 icon = gtk_pixmap_new (gdkpixmap, mask);
551 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
553 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
555 vbox = gtk_vbox_new (FALSE, 0);
556 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
558 label1 = gtk_label_new (vers);
559 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
560 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
561 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
563 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
564 GTK_WIDGET (label1)->style->font =
565 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
566 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
568 label2 = gtk_label_new (msg);
569 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
570 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
571 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
573 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
574 GTK_WIDGET (label2)->style->font =
575 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
576 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
578 hb = gtk_hbutton_box_new ();
580 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
583 ok = gtk_button_new_with_label ("OK");
584 gtk_container_add (GTK_CONTAINER (hb), ok);
586 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
587 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
588 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
590 gtk_widget_show (hbox);
591 gtk_widget_show (icon);
592 gtk_widget_show (vbox);
593 gtk_widget_show (label1);
594 gtk_widget_show (label2);
595 gtk_widget_show (hb);
596 gtk_widget_show (ok);
597 gtk_widget_show (dialog);
599 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
600 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
602 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
603 GTK_WIDGET (parent)->window);
604 gdk_window_show (GTK_WIDGET (dialog)->window);
605 gdk_window_raise (GTK_WIDGET (dialog)->window);
611 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
613 state *s = global_state_kludge; /* I hate C so much... */
614 saver_preferences *p = &s->prefs;
617 if (!p->help_url || !*p->help_url)
619 warning_dialog (s->toplevel_widget,
621 "No Help URL has been specified.\n", False, 100);
625 help_command = (char *) malloc (strlen (p->load_url_command) +
626 (strlen (p->help_url) * 2) + 20);
627 strcpy (help_command, "( ");
628 sprintf (help_command + strlen(help_command),
629 p->load_url_command, p->help_url, p->help_url);
630 strcat (help_command, " ) &");
631 system (help_command);
637 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
639 state *s = global_state_kludge; /* I hate C so much... */
640 run_cmd (s, XA_ACTIVATE, 0);
645 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
647 state *s = global_state_kludge; /* I hate C so much... */
648 run_cmd (s, XA_LOCK, 0);
653 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
655 state *s = global_state_kludge; /* I hate C so much... */
656 run_cmd (s, XA_EXIT, 0);
661 restart_menu_cb (GtkWidget *widget, gpointer user_data)
663 state *s = global_state_kludge; /* I hate C so much... */
664 flush_dialog_changes_and_save (s);
665 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
667 system ("xscreensaver -nosplash &");
669 await_xscreensaver (s);
673 await_xscreensaver (state *s)
677 Display *dpy = GDK_DISPLAY();
678 /* GtkWidget *dialog = 0;*/
681 while (!rversion && (--countdown > 0))
683 /* Check for the version of the running xscreensaver... */
684 server_xscreensaver_version (dpy, &rversion, 0, 0);
686 /* If it's not there yet, wait a second... */
691 /* if (dialog) gtk_widget_destroy (dialog);*/
700 /* Timed out, no screensaver running. */
703 Bool root_p = (geteuid () == 0);
707 "The xscreensaver daemon did not start up properly.\n"
712 "You are running as root. This usually means that xscreensaver\n"
713 "was unable to contact your X server because access control is\n"
714 "turned on. Try running this command:\n"
716 " xhost +localhost\n"
718 "and then selecting `File / Restart Daemon'.\n"
720 "Note that turning off access control will allow anyone logged\n"
721 "on to this machine to access your screen, which might be\n"
722 "considered a security problem. Please read the xscreensaver\n"
723 "manual and FAQ for more information.\n"
725 "You shouldn't run X as root. Instead, you should log in as a\n"
726 "normal user, and `su' as necessary.");
728 strcat (buf, "Please check your $PATH and permissions.");
730 warning_dialog (s->toplevel_widget, buf, False, 1);
736 selected_list_element (state *s)
738 return s->_selected_list_element;
743 demo_write_init_file (state *s, saver_preferences *p)
747 /* #### try to figure out why shit keeps getting reordered... */
748 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
752 if (!write_init_file (p, s->short_version, False))
755 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
760 const char *f = init_file_name();
762 warning_dialog (s->toplevel_widget,
763 "Error:\n\nCouldn't determine init file name!\n",
767 char *b = (char *) malloc (strlen(f) + 1024);
768 sprintf (b, "Error:\n\nCouldn't write %s\n", f);
769 warning_dialog (s->toplevel_widget, b, False, 100);
778 run_this_cb (GtkButton *button, gpointer user_data)
780 state *s = global_state_kludge; /* I hate C so much... */
781 int list_elt = selected_list_element (s);
782 if (list_elt < 0) return;
783 if (!flush_dialog_changes_and_save (s))
784 run_hack (s, list_elt, True);
789 manual_cb (GtkButton *button, gpointer user_data)
791 state *s = global_state_kludge; /* I hate C so much... */
792 saver_preferences *p = &s->prefs;
793 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
794 int list_elt = selected_list_element (s);
796 char *name, *name2, *cmd, *str;
797 if (list_elt < 0) return;
798 hack_number = s->list_elt_to_hack_number[list_elt];
800 flush_dialog_changes_and_save (s);
801 ensure_selected_item_visible (GTK_WIDGET (list_widget));
803 name = strdup (p->screenhacks[hack_number]->command);
805 while (isspace (*name2)) name2++;
807 while (*str && !isspace (*str)) str++;
809 str = strrchr (name2, '/');
810 if (str) name = str+1;
812 cmd = get_string_resource ("manualCommand", "ManualCommand");
815 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
817 sprintf (cmd2 + strlen (cmd2),
819 name2, name2, name2, name2);
820 strcat (cmd2, " ) &");
826 warning_dialog (GTK_WIDGET (button),
827 "Error:\n\nno `manualCommand' resource set.",
836 force_list_select_item (state *s, GtkList *list, int list_elt, Bool scroll_p)
838 GtkWidget *parent = name_to_widget (s, "scroller");
839 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
841 if (!was) gtk_widget_set_sensitive (parent, True);
842 gtk_list_select_item (GTK_LIST (list), list_elt);
843 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
844 if (!was) gtk_widget_set_sensitive (parent, False);
849 run_next_cb (GtkButton *button, gpointer user_data)
851 state *s = global_state_kludge; /* I hate C so much... */
852 saver_preferences *p = &s->prefs;
853 Bool ops = s->preview_suppressed_p;
855 GtkList *list_widget =
856 GTK_LIST (name_to_widget (s, "list"));
857 int list_elt = selected_list_element (s);
864 if (list_elt >= p->screenhacks_count)
867 s->preview_suppressed_p = True;
869 flush_dialog_changes_and_save (s);
870 force_list_select_item (s, GTK_LIST (list_widget), list_elt, True);
871 populate_demo_window (s, list_elt);
872 run_hack (s, list_elt, False);
874 s->preview_suppressed_p = ops;
879 run_prev_cb (GtkButton *button, gpointer user_data)
881 state *s = global_state_kludge; /* I hate C so much... */
882 saver_preferences *p = &s->prefs;
883 Bool ops = s->preview_suppressed_p;
885 GtkList *list_widget =
886 GTK_LIST (name_to_widget (s, "list"));
887 int list_elt = selected_list_element (s);
890 list_elt = p->screenhacks_count - 1;
895 list_elt = p->screenhacks_count - 1;
897 s->preview_suppressed_p = True;
899 flush_dialog_changes_and_save (s);
900 force_list_select_item (s, GTK_LIST (list_widget), list_elt, True);
901 populate_demo_window (s, list_elt);
902 run_hack (s, list_elt, False);
904 s->preview_suppressed_p = ops;
908 /* Writes the given settings into prefs.
909 Returns true if there was a change, False otherwise.
910 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
913 flush_changes (state *s,
919 saver_preferences *p = &s->prefs;
920 Bool changed = False;
923 if (list_elt < 0 || list_elt >= p->screenhacks_count)
926 hack_number = s->list_elt_to_hack_number[list_elt];
927 hack = p->screenhacks[hack_number];
929 if (enabled_p != -1 &&
930 enabled_p != hack->enabled_p)
932 hack->enabled_p = enabled_p;
935 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
936 blurb(), hack->name, enabled_p);
941 if (!hack->command || !!strcmp (command, hack->command))
943 if (hack->command) free (hack->command);
944 hack->command = strdup (command);
947 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
948 blurb(), hack->name, command);
954 const char *ov = hack->visual;
955 if (!ov || !*ov) ov = "any";
956 if (!*visual) visual = "any";
957 if (!!strcasecmp (visual, ov))
959 if (hack->visual) free (hack->visual);
960 hack->visual = strdup (visual);
963 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
964 blurb(), hack->name, visual);
972 /* Helper for the text fields that contain time specifications:
973 this parses the text, and does error checking.
976 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
981 if (!sec_p || strchr (line, ':'))
982 value = parse_time ((char *) line, sec_p, True);
986 if (sscanf (line, "%u%c", &value, &c) != 1)
992 value *= 1000; /* Time measures in microseconds */
998 "Unparsable time format: \"%s\"\n",
1000 warning_dialog (s->toplevel_widget, b, False, 100);
1009 directory_p (const char *path)
1012 if (!path || !*path)
1014 else if (stat (path, &st))
1016 else if (!S_ISDIR (st.st_mode))
1023 normalize_directory (const char *path)
1027 if (!path) return 0;
1029 p2 = (char *) malloc (L + 2);
1031 if (p2[L-1] == '/') /* remove trailing slash */
1034 for (s = p2; s && *s; s++)
1037 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1038 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1041 while (s0 > p2 && s0[-1] != '/')
1051 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1052 strcpy (s, s+2), s--;
1053 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1057 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1058 while (s[0] == '/' && s[1] == '/')
1061 /* and strip trailing whitespace for good measure. */
1063 while (isspace(p2[L-1]))
1070 /* Flush out any changes made in the main dialog window (where changes
1071 take place immediately: clicking on a checkbox causes the init file
1072 to be written right away.)
1075 flush_dialog_changes_and_save (state *s)
1077 saver_preferences *p = &s->prefs;
1078 saver_preferences P2, *p2 = &P2;
1079 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1080 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1081 Bool changed = False;
1085 if (s->saving_p) return False;
1090 /* Flush any checkbox changes in the list down into the prefs struct.
1092 for (i = 0; kids; kids = kids->next, i++)
1094 GtkWidget *line = GTK_WIDGET (kids->data);
1095 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1096 GtkWidget *line_check =
1097 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1099 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1101 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1106 /* Flush the non-hack-specific settings down into the prefs struct.
1109 # define SECONDS(FIELD,NAME) \
1110 w = name_to_widget (s, (NAME)); \
1111 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1113 # define MINUTES(FIELD,NAME) \
1114 w = name_to_widget (s, (NAME)); \
1115 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1117 # define CHECKBOX(FIELD,NAME) \
1118 w = name_to_widget (s, (NAME)); \
1119 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1121 # define PATHNAME(FIELD,NAME) \
1122 w = name_to_widget (s, (NAME)); \
1123 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1125 MINUTES (&p2->timeout, "timeout_spinbutton");
1126 MINUTES (&p2->cycle, "cycle_spinbutton");
1127 CHECKBOX (p2->lock_p, "lock_button");
1128 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1130 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1131 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1132 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1133 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1135 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1136 CHECKBOX (p2->grab_video_p, "grab_video_button");
1137 CHECKBOX (p2->random_image_p, "grab_image_button");
1138 PATHNAME (p2->image_directory, "image_text");
1140 CHECKBOX (p2->verbose_p, "verbose_button");
1141 CHECKBOX (p2->capture_stderr_p, "capture_button");
1142 CHECKBOX (p2->splash_p, "splash_button");
1144 CHECKBOX (p2->install_cmap_p, "install_button");
1145 CHECKBOX (p2->fade_p, "fade_button");
1146 CHECKBOX (p2->unfade_p, "unfade_button");
1147 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1154 /* Warn if the image directory doesn't exist.
1156 if (p2->image_directory &&
1157 *p2->image_directory &&
1158 !directory_p (p2->image_directory))
1161 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1162 p2->image_directory);
1163 warning_dialog (s->toplevel_widget, b, False, 100);
1167 /* Map the mode menu to `saver_mode' enum values. */
1169 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1170 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1171 GtkWidget *selected = gtk_menu_get_active (menu);
1172 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1173 int menu_elt = g_list_index (kids, (gpointer) selected);
1174 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1175 p2->mode = mode_menu_order[menu_elt];
1178 if (p2->mode == ONE_HACK)
1180 int list_elt = selected_list_element (s);
1181 p2->selected_hack = (list_elt >= 0
1182 ? s->list_elt_to_hack_number[list_elt]
1186 # define COPY(field, name) \
1187 if (p->field != p2->field) { \
1190 fprintf (stderr, "%s: %s => %d\n", blurb(), name, p2->field); \
1192 p->field = p2->field
1195 COPY(selected_hack, "selected_hack");
1197 COPY(timeout, "timeout");
1198 COPY(cycle, "cycle");
1199 COPY(lock_p, "lock_p");
1200 COPY(lock_timeout, "lock_timeout");
1202 COPY(dpms_enabled_p, "dpms_enabled_p");
1203 COPY(dpms_standby, "dpms_standby");
1204 COPY(dpms_suspend, "dpms_suspend");
1205 COPY(dpms_off, "dpms_off");
1207 COPY(verbose_p, "verbose_p");
1208 COPY(capture_stderr_p, "capture_stderr_p");
1209 COPY(splash_p, "splash_p");
1211 COPY(install_cmap_p, "install_cmap_p");
1212 COPY(fade_p, "fade_p");
1213 COPY(unfade_p, "unfade_p");
1214 COPY(fade_seconds, "fade_seconds");
1216 COPY(grab_desktop_p, "grab_desktop_p");
1217 COPY(grab_video_p, "grab_video_p");
1218 COPY(random_image_p, "random_image_p");
1222 if (!p->image_directory ||
1223 !p2->image_directory ||
1224 strcmp(p->image_directory, p2->image_directory))
1228 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1229 blurb(), p2->image_directory);
1231 if (p->image_directory && p->image_directory != p2->image_directory)
1232 free (p->image_directory);
1233 p->image_directory = p2->image_directory;
1234 p2->image_directory = 0;
1236 populate_prefs_page (s);
1240 Display *dpy = GDK_DISPLAY();
1241 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1242 sync_server_dpms_settings (dpy, enabled_p,
1243 p->dpms_standby / 1000,
1244 p->dpms_suspend / 1000,
1248 changed = demo_write_init_file (s, p);
1251 s->saving_p = False;
1256 /* Flush out any changes made in the popup dialog box (where changes
1257 take place only when the OK button is clicked.)
1260 flush_popup_changes_and_save (state *s)
1262 Bool changed = False;
1263 saver_preferences *p = &s->prefs;
1264 int list_elt = selected_list_element (s);
1266 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1267 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1269 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1270 const char *command = gtk_entry_get_text (cmd);
1275 if (s->saving_p) return False;
1281 if (maybe_reload_init_file (s) != 0)
1287 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1289 if (!strcasecmp (visual, "")) visual = "";
1290 else if (!strcasecmp (visual, "any")) visual = "";
1291 else if (!strcasecmp (visual, "default")) visual = "Default";
1292 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1293 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1294 else if (!strcasecmp (visual, "best")) visual = "Best";
1295 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1296 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1297 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1298 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1299 else if (!strcasecmp (visual, "color")) visual = "Color";
1300 else if (!strcasecmp (visual, "gl")) visual = "GL";
1301 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1302 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1303 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1304 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1305 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1306 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1307 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1308 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
1309 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1312 gdk_beep (); /* unparsable */
1314 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");
1317 changed = flush_changes (s, list_elt, -1, command, visual);
1320 changed = demo_write_init_file (s, p);
1322 /* Do this to re-launch the hack if (and only if) the command line
1324 populate_demo_window (s, selected_list_element (s));
1328 s->saving_p = False;
1336 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1338 state *s = global_state_kludge; /* I hate C so much... */
1339 if (! s->initializing_p)
1341 s->initializing_p = True;
1342 flush_dialog_changes_and_save (s);
1343 s->initializing_p = False;
1348 /* Callback on menu items in the "mode" options menu.
1351 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1353 state *s = (state *) user_data;
1354 saver_preferences *p = &s->prefs;
1355 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1358 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1360 saver_mode new_mode;
1361 int old_selected = p->selected_hack;
1365 if (menu_items->data == widget)
1368 menu_items = menu_items->next;
1370 if (!menu_items) abort();
1372 new_mode = mode_menu_order[menu_index];
1374 /* Keep the same list element displayed as before; except if we're
1375 switching *to* "one screensaver" mode from any other mode, scroll
1376 to and select "the one".
1379 if (new_mode == ONE_HACK)
1380 list_elt = (p->selected_hack >= 0
1381 ? s->hack_number_to_list_elt[p->selected_hack]
1385 list_elt = selected_list_element (s);
1388 saver_mode old_mode = p->mode;
1390 populate_demo_window (s, list_elt);
1391 force_list_select_item (s, list, list_elt, True);
1392 p->mode = old_mode; /* put it back, so the init file gets written */
1395 pref_changed_cb (widget, user_data);
1397 if (old_selected != p->selected_hack)
1398 abort(); /* dammit, not again... */
1403 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1404 gint page_num, gpointer user_data)
1406 state *s = global_state_kludge; /* I hate C so much... */
1407 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1409 /* If we're switching to page 0, schedule the current hack to be run.
1410 Otherwise, schedule it to stop. */
1412 populate_demo_window (s, selected_list_element (s));
1414 schedule_preview (s, 0);
1418 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1419 list_select_cb that comes in
1420 *after* we've double-clicked.
1424 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1427 state *s = (state *) data;
1428 if (event->type == GDK_2BUTTON_PRESS)
1430 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1431 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1433 last_doubleclick_time = time ((time_t *) 0);
1436 run_hack (s, list_elt, True);
1444 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1446 state *s = (state *) data;
1447 time_t now = time ((time_t *) 0);
1449 if (now >= last_doubleclick_time + 2)
1451 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1452 populate_demo_window (s, list_elt);
1453 flush_dialog_changes_and_save (s);
1458 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1460 state *s = (state *) data;
1461 populate_demo_window (s, -1);
1462 flush_dialog_changes_and_save (s);
1466 /* Called when the checkboxes that are in the left column of the
1467 scrolling list are clicked. This both populates the right pane
1468 (just as clicking on the label (really, listitem) does) and
1469 also syncs this checkbox with the right pane Enabled checkbox.
1472 list_checkbox_cb (GtkWidget *cb, gpointer data)
1474 state *s = (state *) data;
1476 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1477 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1479 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1480 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1481 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1485 int list_elt = gtk_list_child_position (list, line);
1487 /* remember previous scroll position of the top of the list */
1488 adj = gtk_scrolled_window_get_vadjustment (scroller);
1489 scroll_top = adj->value;
1491 flush_dialog_changes_and_save (s);
1492 force_list_select_item (s, list, list_elt, False);
1493 populate_demo_window (s, list_elt);
1495 /* restore the previous scroll position of the top of the list.
1496 this is weak, but I don't really know why it's moving... */
1497 gtk_adjustment_set_value (adj, scroll_top);
1503 GtkFileSelection *widget;
1504 } file_selection_data;
1509 store_image_directory (GtkWidget *button, gpointer user_data)
1511 file_selection_data *fsd = (file_selection_data *) user_data;
1512 state *s = fsd->state;
1513 GtkFileSelection *selector = fsd->widget;
1514 GtkWidget *top = s->toplevel_widget;
1515 saver_preferences *p = &s->prefs;
1516 char *path = gtk_file_selection_get_filename (selector);
1518 if (p->image_directory && !strcmp(p->image_directory, path))
1519 return; /* no change */
1521 if (!directory_p (path))
1524 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n", path);
1525 warning_dialog (GTK_WIDGET (top), b, False, 100);
1529 if (p->image_directory) free (p->image_directory);
1530 p->image_directory = normalize_directory (path);
1532 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1533 (p->image_directory ? p->image_directory : ""));
1534 demo_write_init_file (s, p);
1539 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1541 file_selection_data *fsd = (file_selection_data *) user_data;
1542 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1546 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1548 browse_image_dir_cancel (button, user_data);
1549 store_image_directory (button, user_data);
1553 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1555 browse_image_dir_cancel (widget, user_data);
1560 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1562 state *s = global_state_kludge; /* I hate C so much... */
1563 saver_preferences *p = &s->prefs;
1564 static file_selection_data *fsd = 0;
1566 GtkFileSelection *selector = GTK_FILE_SELECTION(
1567 gtk_file_selection_new ("Please select the image directory."));
1570 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1572 fsd->widget = selector;
1575 if (p->image_directory && *p->image_directory)
1576 gtk_file_selection_set_filename (selector, p->image_directory);
1578 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1579 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1581 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1582 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1584 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1585 GTK_SIGNAL_FUNC (browse_image_dir_close),
1588 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1590 gtk_window_set_modal (GTK_WINDOW (selector), True);
1591 gtk_widget_show (GTK_WIDGET (selector));
1596 settings_cb (GtkButton *button, gpointer user_data)
1598 state *s = global_state_kludge; /* I hate C so much... */
1599 int list_elt = selected_list_element (s);
1601 populate_demo_window (s, list_elt); /* reset the widget */
1602 populate_popup_window (s); /* create UI on popup window */
1603 gtk_widget_show (s->popup_widget);
1607 settings_sync_cmd_text (state *s)
1610 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1611 char *cmd_line = get_configurator_command_line (s->cdata);
1612 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1613 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1615 # endif /* HAVE_XML */
1619 settings_adv_cb (GtkButton *button, gpointer user_data)
1621 state *s = global_state_kludge; /* I hate C so much... */
1622 GtkNotebook *notebook =
1623 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1625 settings_sync_cmd_text (s);
1626 gtk_notebook_set_page (notebook, 1);
1630 settings_std_cb (GtkButton *button, gpointer user_data)
1632 state *s = global_state_kludge; /* I hate C so much... */
1633 GtkNotebook *notebook =
1634 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1636 /* Re-create UI to reflect the in-progress command-line settings. */
1637 populate_popup_window (s);
1639 gtk_notebook_set_page (notebook, 0);
1643 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1644 gint page_num, gpointer user_data)
1646 state *s = global_state_kludge; /* I hate C so much... */
1647 GtkWidget *adv = name_to_widget (s, "adv_button");
1648 GtkWidget *std = name_to_widget (s, "std_button");
1652 gtk_widget_show (adv);
1653 gtk_widget_hide (std);
1655 else if (page_num == 1)
1657 gtk_widget_hide (adv);
1658 gtk_widget_show (std);
1667 settings_cancel_cb (GtkButton *button, gpointer user_data)
1669 state *s = global_state_kludge; /* I hate C so much... */
1670 gtk_widget_hide (s->popup_widget);
1674 settings_ok_cb (GtkButton *button, gpointer user_data)
1676 state *s = global_state_kludge; /* I hate C so much... */
1677 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1678 int page = gtk_notebook_get_current_page (notebook);
1681 /* Regenerate the command-line from the widget contents before saving.
1682 But don't do this if we're looking at the command-line page already,
1683 or we will blow away what they typed... */
1684 settings_sync_cmd_text (s);
1686 flush_popup_changes_and_save (s);
1687 gtk_widget_hide (s->popup_widget);
1691 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1693 state *s = (state *) data;
1694 settings_cancel_cb (0, (gpointer) s);
1699 /* Populating the various widgets
1703 /* Returns the number of the last hack run by the server.
1706 server_current_hack (void)
1710 unsigned long nitems, bytesafter;
1712 Display *dpy = GDK_DISPLAY();
1713 int hack_number = -1;
1715 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1716 XA_SCREENSAVER_STATUS,
1717 0, 3, False, XA_INTEGER,
1718 &type, &format, &nitems, &bytesafter,
1719 (unsigned char **) &data)
1721 && type == XA_INTEGER
1724 hack_number = (int) data[2] - 1;
1726 if (data) free (data);
1732 /* Finds the number of the last hack to run, and makes that item be
1733 selected by default.
1736 scroll_to_current_hack (state *s)
1738 saver_preferences *p = &s->prefs;
1741 if (p->mode == ONE_HACK)
1742 hack_number = p->selected_hack;
1744 hack_number = server_current_hack ();
1746 if (hack_number >= 0 && hack_number < p->screenhacks_count)
1748 int list_elt = s->hack_number_to_list_elt[hack_number];
1749 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1750 force_list_select_item (s, list, list_elt, True);
1751 populate_demo_window (s, list_elt);
1757 populate_hack_list (state *s)
1759 saver_preferences *p = &s->prefs;
1760 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1762 for (i = 0; i < p->screenhacks_count; i++)
1764 screenhack *hack = p->screenhacks[s->list_elt_to_hack_number[i]];
1766 /* A GtkList must contain only GtkListItems, but those can contain
1767 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
1768 and a Label. We handle single and double click events on the
1769 line itself, for clicking on the text, but the interior checkbox
1770 also handles its own events.
1773 GtkWidget *line_hbox;
1774 GtkWidget *line_check;
1775 GtkWidget *line_label;
1777 char *pretty_name = (hack->name
1778 ? strdup (hack->name)
1779 : make_hack_name (hack->command));
1781 line = gtk_list_item_new ();
1782 line_hbox = gtk_hbox_new (FALSE, 0);
1783 line_check = gtk_check_button_new ();
1784 line_label = gtk_label_new (pretty_name);
1786 gtk_container_add (GTK_CONTAINER (line), line_hbox);
1787 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
1788 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
1790 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1792 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
1794 gtk_widget_show (line_check);
1795 gtk_widget_show (line_label);
1796 gtk_widget_show (line_hbox);
1797 gtk_widget_show (line);
1801 gtk_container_add (GTK_CONTAINER (list), line);
1802 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
1803 GTK_SIGNAL_FUNC (list_doubleclick_cb),
1806 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
1807 GTK_SIGNAL_FUNC (list_checkbox_cb),
1811 GTK_WIDGET (GTK_BIN(line)->child)->style =
1812 gtk_style_copy (GTK_WIDGET (text_line)->style);
1814 gtk_widget_show (line);
1817 gtk_signal_connect (GTK_OBJECT (list), "select_child",
1818 GTK_SIGNAL_FUNC (list_select_cb),
1820 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
1821 GTK_SIGNAL_FUNC (list_unselect_cb),
1827 update_list_sensitivity (state *s)
1829 saver_preferences *p = &s->prefs;
1830 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
1831 Bool checkable = (p->mode == RANDOM_HACKS);
1832 Bool blankable = (p->mode != DONT_BLANK);
1834 GtkWidget *head = name_to_widget (s, "col_head_hbox");
1835 GtkWidget *use = name_to_widget (s, "use_col_frame");
1836 GtkWidget *scroller = name_to_widget (s, "scroller");
1837 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
1838 GtkWidget *blanker = name_to_widget (s, "blanking_table");
1840 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1841 GList *kids = gtk_container_children (GTK_CONTAINER (list));
1843 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
1844 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
1845 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
1847 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
1850 gtk_widget_show (use); /* the "Use" column header */
1852 gtk_widget_hide (use);
1856 GtkBin *line = GTK_BIN (kids->data);
1857 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
1858 GtkWidget *line_check =
1859 GTK_WIDGET (gtk_container_children (line_hbox)->data);
1862 gtk_widget_show (line_check);
1864 gtk_widget_hide (line_check);
1872 populate_prefs_page (state *s)
1874 saver_preferences *p = &s->prefs;
1877 /* The file supports timeouts of less than a minute, but the GUI does
1878 not, so throttle the values to be at least one minute (since "0" is
1879 a bad rounding choice...)
1881 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
1884 THROTTLE (passwd_timeout);
1887 # define FMT_MINUTES(NAME,N) \
1888 sprintf (str, "%d", (((N / 1000) + 59) / 60)); \
1889 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, (NAME))), str)
1891 # define FMT_SECONDS(NAME,N) \
1892 sprintf (str, "%d", ((N) / 1000)); \
1893 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, (NAME))), str)
1895 FMT_MINUTES ("timeout_spinbutton", p->timeout);
1896 FMT_MINUTES ("cycle_spinbutton", p->cycle);
1897 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
1898 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
1899 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
1900 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
1901 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
1906 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
1907 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
1910 TOGGLE_ACTIVE ("lock_button", p->lock_p);
1911 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
1912 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
1913 TOGGLE_ACTIVE ("splash_button", p->splash_p);
1914 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
1915 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
1916 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
1917 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
1918 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
1919 TOGGLE_ACTIVE ("fade_button", p->fade_p);
1920 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
1922 # undef TOGGLE_ACTIVE
1924 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1925 (p->image_directory ? p->image_directory : ""));
1926 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
1928 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
1931 /* Map the `saver_mode' enum to mode menu to values. */
1933 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1936 for (i = 0; i < countof(mode_menu_order); i++)
1937 if (mode_menu_order[i] == p->mode)
1939 gtk_option_menu_set_history (opt, i);
1940 update_list_sensitivity (s);
1944 Bool found_any_writable_cells = False;
1945 Bool dpms_supported = False;
1947 Display *dpy = GDK_DISPLAY();
1948 int nscreens = ScreenCount(dpy);
1950 for (i = 0; i < nscreens; i++)
1952 Screen *s = ScreenOfDisplay (dpy, i);
1953 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1955 found_any_writable_cells = True;
1960 #ifdef HAVE_XF86VMODE_GAMMA
1961 found_any_writable_cells = True; /* if we can gamma fade, go for it */
1964 #ifdef HAVE_DPMS_EXTENSION
1966 int op = 0, event = 0, error = 0;
1967 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
1968 dpms_supported = True;
1970 #endif /* HAVE_DPMS_EXTENSION */
1973 # define SENSITIZE(NAME,SENSITIVEP) \
1974 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
1976 /* Blanking and Locking
1978 SENSITIZE ("lock_spinbutton", p->lock_p);
1979 SENSITIZE ("lock_mlabel", p->lock_p);
1983 SENSITIZE ("dpms_frame", dpms_supported);
1984 SENSITIZE ("dpms_button", dpms_supported);
1985 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
1986 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
1987 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
1988 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
1989 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
1990 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
1991 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
1992 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
1993 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
1997 SENSITIZE ("cmap_frame", found_any_writable_cells);
1998 SENSITIZE ("install_button", found_any_writable_cells);
1999 SENSITIZE ("fade_button", found_any_writable_cells);
2000 SENSITIZE ("unfade_button", found_any_writable_cells);
2002 SENSITIZE ("fade_label", (found_any_writable_cells &&
2003 (p->fade_p || p->unfade_p)));
2004 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2005 (p->fade_p || p->unfade_p)));
2013 populate_popup_window (state *s)
2015 saver_preferences *p = &s->prefs;
2016 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2017 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2018 int list_elt = selected_list_element (s);
2019 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2020 ? s->list_elt_to_hack_number[list_elt]
2022 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2023 char *doc_string = 0;
2025 /* #### not in Gtk 1.2
2026 gtk_label_set_selectable (doc);
2032 free_conf_data (s->cdata);
2038 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2039 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2040 s->cdata = load_configurator (cmd_line, s->debug_p);
2041 if (s->cdata && s->cdata->widget)
2042 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget, TRUE, TRUE, 0);
2045 doc_string = (s->cdata
2046 ? s->cdata->description
2048 # else /* !HAVE_XML */
2049 doc_string = "Descriptions not available: no XML support compiled in.";
2050 # endif /* !HAVE_XML */
2052 gtk_label_set_text (doc, (doc_string
2054 : "No description available."));
2059 sensitize_demo_widgets (state *s, Bool sensitive_p)
2061 const char *names1[] = { "demo", "settings" };
2062 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2063 "visual", "visual_combo" };
2065 for (i = 0; i < countof(names1); i++)
2067 GtkWidget *w = name_to_widget (s, names1[i]);
2068 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2070 for (i = 0; i < countof(names2); i++)
2072 GtkWidget *w = name_to_widget (s, names2[i]);
2073 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2078 /* Even though we've given these text fields a maximum number of characters,
2079 their default size is still about 30 characters wide -- so measure out
2080 a string in their font, and resize them to just fit that.
2083 fix_text_entry_sizes (state *s)
2085 const char * const spinbuttons[] = {
2086 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2087 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2088 "dpms_off_spinbutton",
2089 "-fade_spinbutton" };
2094 for (i = 0; i < countof(spinbuttons); i++)
2096 const char *n = spinbuttons[i];
2098 while (*n == '-') n++, cols--;
2099 w = GTK_WIDGET (name_to_widget (s, n));
2100 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2101 gtk_widget_set_usize (w, width, -2);
2104 /* Now fix the width of the combo box.
2106 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2107 w = GTK_COMBO (w)->entry;
2108 width = gdk_string_width (w->style->font, "PseudoColor___");
2109 gtk_widget_set_usize (w, width, -2);
2111 /* Now fix the width of the file entry text.
2113 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2114 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2115 gtk_widget_set_usize (w, width, -2);
2117 /* Now fix the width of the command line text.
2119 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2120 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2121 gtk_widget_set_usize (w, width, -2);
2123 /* Now fix the height of the list.
2128 int leading = 3; /* approximate is ok... */
2130 w = GTK_WIDGET (name_to_widget (s, "list"));
2131 height = w->style->font->ascent + w->style->font->descent;
2134 height += border * 2;
2135 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2136 gtk_widget_set_usize (w, -2, height);
2143 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2146 static char *up_arrow_xpm[] = {
2169 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2170 the end of the array (Gtk 1.2.5.) */
2171 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2172 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2175 static char *down_arrow_xpm[] = {
2198 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2199 the end of the array (Gtk 1.2.5.) */
2200 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2201 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2205 pixmapify_button (state *s, int down_p)
2209 GtkWidget *pixmapwid;
2213 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2214 style = gtk_widget_get_style (w);
2216 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2217 &style->bg[GTK_STATE_NORMAL],
2219 ? (gchar **) down_arrow_xpm
2220 : (gchar **) up_arrow_xpm));
2221 pixmapwid = gtk_pixmap_new (pixmap, mask);
2222 gtk_widget_show (pixmapwid);
2223 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2224 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2228 map_next_button_cb (GtkWidget *w, gpointer user_data)
2230 state *s = (state *) user_data;
2231 pixmapify_button (s, 1);
2235 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2237 state *s = (state *) user_data;
2238 pixmapify_button (s, 0);
2243 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2247 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2248 GtkAllocation *allocation,
2252 GtkWidgetAuxInfo *aux_info;
2254 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2256 aux_info->width = allocation->width;
2257 aux_info->height = -2;
2261 gtk_widget_size_request (label, &req);
2265 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2268 eschew_gtk_lossage (GtkLabel *label)
2270 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2271 aux_info->width = GTK_WIDGET (label)->allocation.width;
2272 aux_info->height = -2;
2276 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2278 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2279 you_are_not_a_unique_or_beautiful_snowflake,
2282 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2284 gtk_widget_queue_resize (GTK_WIDGET (label));
2289 populate_demo_window (state *s, int list_elt)
2291 saver_preferences *p = &s->prefs;
2294 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2295 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2296 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2297 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2298 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2300 if (p->mode == BLANK_ONLY)
2303 pretty_name = strdup ("Blank Screen");
2304 schedule_preview (s, 0);
2306 else if (p->mode == DONT_BLANK)
2309 pretty_name = strdup ("Screen Saver Disabled");
2310 schedule_preview (s, 0);
2314 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2315 ? s->list_elt_to_hack_number[list_elt]
2317 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2321 ? strdup (hack->name)
2322 : make_hack_name (hack->command))
2326 schedule_preview (s, hack->command);
2328 schedule_preview (s, 0);
2332 pretty_name = strdup ("Preview");
2334 gtk_frame_set_label (frame1, pretty_name);
2335 gtk_frame_set_label (frame2, pretty_name);
2337 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2338 gtk_entry_set_position (cmd, 0);
2342 sprintf (title, "%s: %.100s Settings",
2343 progclass, (pretty_name ? pretty_name : "???"));
2344 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2347 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2349 ? (hack->visual && *hack->visual
2354 sensitize_demo_widgets (s, (hack ? True : False));
2356 if (pretty_name) free (pretty_name);
2358 ensure_selected_item_visible (list);
2360 s->_selected_list_element = list_elt;
2365 widget_deleter (GtkWidget *widget, gpointer data)
2367 /* #### Well, I want to destroy these widgets, but if I do that, they get
2368 referenced again, and eventually I get a SEGV. So instead of
2369 destroying them, I'll just hide them, and leak a bunch of memory
2370 every time the disk file changes. Go go go Gtk!
2372 #### Ok, that's a lie, I get a crash even if I just hide the widget
2373 and don't ever delete it. Fuck!
2376 gtk_widget_destroy (widget);
2378 gtk_widget_hide (widget);
2383 static char **sort_hack_cmp_names_kludge;
2385 sort_hack_cmp (const void *a, const void *b)
2390 return strcmp (sort_hack_cmp_names_kludge[*(int *) a],
2391 sort_hack_cmp_names_kludge[*(int *) b]);
2396 initialize_sort_map (state *s)
2398 saver_preferences *p = &s->prefs;
2401 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2402 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2404 s->list_elt_to_hack_number = (int *)
2405 calloc (sizeof(int), p->screenhacks_count + 1);
2406 s->hack_number_to_list_elt = (int *)
2407 calloc (sizeof(int), p->screenhacks_count + 1);
2409 /* Initialize table to 1:1 mapping */
2410 for (i = 0; i < p->screenhacks_count; i++)
2411 s->list_elt_to_hack_number[i] = i;
2413 /* Generate list of names (once)
2415 sort_hack_cmp_names_kludge = (char **)
2416 calloc (sizeof(char *), p->screenhacks_count);
2417 for (i = 0; i < p->screenhacks_count; i++)
2419 screenhack *hack = p->screenhacks[i];
2420 char *name = (hack->name && *hack->name
2421 ? strdup (hack->name)
2422 : make_hack_name (hack->command));
2424 for (str = name; *str; str++)
2425 *str = tolower(*str);
2426 sort_hack_cmp_names_kludge[i] = name;
2429 /* Sort alphabetically
2431 qsort (s->list_elt_to_hack_number,
2432 p->screenhacks_count,
2433 sizeof(*s->list_elt_to_hack_number),
2438 for (i = 0; i < p->screenhacks_count; i++)
2439 free (sort_hack_cmp_names_kludge[i]);
2440 free (sort_hack_cmp_names_kludge);
2441 sort_hack_cmp_names_kludge = 0;
2443 /* Build inverse table */
2444 for (i = 0; i < p->screenhacks_count; i++)
2445 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2450 maybe_reload_init_file (state *s)
2452 saver_preferences *p = &s->prefs;
2455 static Bool reentrant_lock = False;
2456 if (reentrant_lock) return 0;
2457 reentrant_lock = True;
2459 if (init_file_changed_p (p))
2461 const char *f = init_file_name();
2466 if (!f || !*f) return 0;
2467 b = (char *) malloc (strlen(f) + 1024);
2470 "file \"%s\" has changed, reloading.\n",
2472 warning_dialog (s->toplevel_widget, b, False, 100);
2476 initialize_sort_map (s);
2478 list_elt = selected_list_element (s);
2479 list = GTK_LIST (name_to_widget (s, "list"));
2480 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
2481 populate_hack_list (s);
2482 force_list_select_item (s, list, list_elt, True);
2483 populate_prefs_page (s);
2484 populate_demo_window (s, list_elt);
2485 ensure_selected_item_visible (GTK_WIDGET (list));
2490 reentrant_lock = False;
2496 /* Making the preview window have the right X visual (so that GL works.)
2499 static Visual *get_best_gl_visual (state *);
2502 x_visual_to_gdk_visual (Visual *xv)
2504 GList *gvs = gdk_list_visuals();
2505 if (!xv) return gdk_visual_get_system();
2506 for (; gvs; gvs = gvs->next)
2508 GdkVisual *gv = (GdkVisual *) gvs->data;
2509 if (xv == GDK_VISUAL_XVISUAL (gv))
2512 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
2513 blurb(), (unsigned long) xv->visualid);
2518 clear_preview_window (state *s)
2523 if (!s->toplevel_widget) return; /* very early */
2524 p = name_to_widget (s, "preview");
2527 if (!window) return;
2529 /* Flush the widget background down into the window, in case a subproc
2531 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
2532 gdk_window_clear (window);
2534 if (s->running_preview_error_p)
2536 const char * const lines[] = { "No Preview", "Available" };
2537 int lh = p->style->font->ascent + p->style->font->descent;
2540 gdk_window_get_size (window, &w, &h);
2541 y = (h - (lh * countof(lines))) / 2;
2542 y += p->style->font->ascent;
2543 for (i = 0; i < countof(lines); i++)
2545 int sw = gdk_string_width (p->style->font, lines[i]);
2546 int x = (w - sw) / 2;
2547 gdk_draw_string (window, p->style->font,
2548 p->style->fg_gc[GTK_STATE_NORMAL],
2556 /* Is there a GDK way of doing this? */
2557 XSync (GDK_DISPLAY(), False);
2562 fix_preview_visual (state *s)
2564 GtkWidget *widget = name_to_widget (s, "preview");
2565 Visual *xvisual = get_best_gl_visual (s);
2566 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
2567 GdkVisual *dvisual = gdk_visual_get_system();
2568 GdkColormap *cmap = (visual == dvisual
2569 ? gdk_colormap_get_system ()
2570 : gdk_colormap_new (visual, False));
2573 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
2574 (visual == dvisual ? "default" : "non-default"),
2575 (xvisual ? (unsigned long) xvisual->visualid : 0L));
2577 if (!GTK_WIDGET_REALIZED (widget) ||
2578 gtk_widget_get_visual (widget) != visual)
2580 gtk_widget_unrealize (widget);
2581 gtk_widget_set_visual (widget, visual);
2582 gtk_widget_set_colormap (widget, cmap);
2583 gtk_widget_realize (widget);
2586 /* Set the Widget colors to be white-on-black. */
2588 GdkWindow *window = widget->window;
2589 GtkStyle *style = gtk_style_copy (widget->style);
2590 GdkColormap *cmap = gtk_widget_get_colormap (widget);
2591 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
2592 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
2593 GdkGC *fgc = gdk_gc_new(window);
2594 GdkGC *bgc = gdk_gc_new(window);
2595 if (!gdk_color_white (cmap, fg)) abort();
2596 if (!gdk_color_black (cmap, bg)) abort();
2597 gdk_gc_set_foreground (fgc, fg);
2598 gdk_gc_set_background (fgc, bg);
2599 gdk_gc_set_foreground (bgc, bg);
2600 gdk_gc_set_background (bgc, fg);
2601 style->fg_gc[GTK_STATE_NORMAL] = fgc;
2602 style->bg_gc[GTK_STATE_NORMAL] = fgc;
2603 gtk_widget_set_style (widget, style);
2606 gtk_widget_show (widget);
2614 subproc_pretty_name (state *s)
2616 if (s->running_preview_cmd)
2618 char *ps = strdup (s->running_preview_cmd);
2619 char *ss = strchr (ps, ' ');
2621 ss = strrchr (ps, '/');
2627 return strdup ("???");
2632 reap_zombies (state *s)
2634 int wait_status = 0;
2636 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
2640 if (pid == s->running_preview_pid)
2642 char *ss = subproc_pretty_name (s);
2643 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(), pid, ss);
2647 fprintf (stderr, "%s: pid %lu died\n", blurb(), pid);
2653 /* Mostly lifted from driver/subprocs.c */
2655 get_best_gl_visual (state *s)
2657 Display *dpy = GDK_DISPLAY();
2666 av[ac++] = "xscreensaver-gl-helper";
2671 perror ("error creating pipe:");
2678 switch ((int) (forked = fork ()))
2682 sprintf (buf, "%s: couldn't fork", blurb());
2690 close (in); /* don't need this one */
2691 close (ConnectionNumber (dpy)); /* close display fd */
2693 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
2695 perror ("could not dup() a new stdout:");
2699 execvp (av[0], av); /* shouldn't return. */
2701 if (errno != ENOENT)
2703 /* Ignore "no such file or directory" errors, unless verbose.
2704 Issue all other exec errors, though. */
2705 sprintf (buf, "%s: running %s", blurb(), av[0]);
2708 exit (1); /* exits fork */
2714 int wait_status = 0;
2716 FILE *f = fdopen (in, "r");
2717 unsigned long v = 0;
2720 close (out); /* don't need this one */
2723 fgets (buf, sizeof(buf)-1, f);
2726 /* Wait for the child to die. */
2727 waitpid (-1, &wait_status, 0);
2729 if (1 == sscanf (buf, "0x%x %c", &v, &c))
2735 fprintf (stderr, "%s: %s did not report a GL visual!\n",
2741 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
2743 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
2744 blurb(), av[0], result);
2756 kill_preview_subproc (state *s)
2758 s->running_preview_error_p = False;
2761 clear_preview_window (s);
2763 if (s->subproc_check_timer_id)
2765 gtk_timeout_remove (s->subproc_check_timer_id);
2766 s->subproc_check_timer_id = 0;
2767 s->subproc_check_countdown = 0;
2770 if (s->running_preview_pid)
2772 int status = kill (s->running_preview_pid, SIGTERM);
2773 char *ss = subproc_pretty_name (s);
2780 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
2781 blurb(), s->running_preview_pid, ss);
2786 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
2787 blurb(), s->running_preview_pid, ss);
2791 else if (s->debug_p)
2792 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
2793 s->running_preview_pid, ss);
2796 s->running_preview_pid = 0;
2797 if (s->running_preview_cmd) free (s->running_preview_cmd);
2798 s->running_preview_cmd = 0;
2805 /* Immediately and unconditionally launches the given process,
2806 after appending the -window-id option; sets running_preview_pid.
2809 launch_preview_subproc (state *s)
2811 saver_preferences *p = &s->prefs;
2815 const char *cmd = s->desired_preview_cmd;
2817 GtkWidget *pr = name_to_widget (s, "preview");
2818 GdkWindow *window = pr->window;
2820 s->running_preview_error_p = False;
2822 if (s->preview_suppressed_p)
2824 kill_preview_subproc (s);
2828 new_cmd = malloc (strlen (cmd) + 40);
2830 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
2833 /* No window id? No command to run. */
2839 strcpy (new_cmd, cmd);
2840 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X", id);
2843 kill_preview_subproc (s);
2846 s->running_preview_error_p = True;
2847 clear_preview_window (s);
2851 switch ((int) (forked = fork ()))
2856 sprintf (buf, "%s: couldn't fork", blurb());
2858 s->running_preview_error_p = True;
2864 close (ConnectionNumber (GDK_DISPLAY()));
2866 usleep (250000); /* pause for 1/4th second before launching, to give
2867 the previous program time to die and flush its X
2868 buffer, so we don't get leftover turds on the
2871 exec_command (p->shell, new_cmd, p->nice_inferior);
2872 /* Don't bother printing an error message when we are unable to
2873 exec subprocesses; we handle that by polling the pid later. */
2874 exit (1); /* exits child fork */
2879 if (s->running_preview_cmd) free (s->running_preview_cmd);
2880 s->running_preview_cmd = strdup (s->desired_preview_cmd);
2881 s->running_preview_pid = forked;
2885 char *ss = subproc_pretty_name (s);
2886 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(), forked, ss);
2893 schedule_preview_check (s);
2896 if (new_cmd) free (new_cmd);
2901 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
2904 hack_environment (state *s)
2906 static const char *def_path =
2907 # ifdef DEFAULT_PATH_PREFIX
2908 DEFAULT_PATH_PREFIX;
2913 Display *dpy = GDK_DISPLAY();
2914 const char *odpy = DisplayString (dpy);
2915 char *ndpy = (char *) malloc(strlen(odpy) + 20);
2916 strcpy (ndpy, "DISPLAY=");
2917 strcat (ndpy, odpy);
2922 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
2924 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
2925 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
2926 So we must leak it (and/or the previous setting). Yay.
2929 if (def_path && *def_path)
2931 const char *opath = getenv("PATH");
2932 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
2933 strcpy (npath, "PATH=");
2934 strcat (npath, def_path);
2935 strcat (npath, ":");
2936 strcat (npath, opath);
2940 /* do not free(npath) -- see above */
2943 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
2948 /* Called from a timer:
2949 Launches the currently-chosen subprocess, if it's not already running.
2950 If there's a different process running, kills it.
2953 update_subproc_timer (gpointer data)
2955 state *s = (state *) data;
2956 if (! s->desired_preview_cmd)
2957 kill_preview_subproc (s);
2958 else if (!s->running_preview_cmd ||
2959 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
2960 launch_preview_subproc (s);
2962 s->subproc_timer_id = 0;
2963 return FALSE; /* do not re-execute timer */
2967 /* Call this when you think you might want a preview process running.
2968 It will set a timer that will actually launch that program a second
2969 from now, if you haven't changed your mind (to avoid double-click
2970 spazzing, etc.) `cmd' may be null meaning "no process".
2973 schedule_preview (state *s, const char *cmd)
2975 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
2980 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
2982 fprintf (stderr, "%s: scheduling preview death\n", blurb());
2985 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
2986 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
2988 if (s->subproc_timer_id)
2989 gtk_timeout_remove (s->subproc_timer_id);
2990 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
2994 /* Called from a timer:
2995 Checks to see if the subproc that should be running, actually is.
2998 check_subproc_timer (gpointer data)
3000 state *s = (state *) data;
3001 Bool again_p = True;
3003 if (s->running_preview_error_p || /* already dead */
3004 s->running_preview_pid <= 0)
3012 status = kill (s->running_preview_pid, 0);
3013 if (status < 0 && errno == ESRCH)
3014 s->running_preview_error_p = True;
3018 char *ss = subproc_pretty_name (s);
3019 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3020 s->running_preview_pid, ss,
3021 (s->running_preview_error_p ? "dead" : "alive"));
3025 if (s->running_preview_error_p)
3027 clear_preview_window (s);
3032 /* Otherwise, it's currently alive. We might be checking again, or we
3033 might be satisfied. */
3035 if (--s->subproc_check_countdown <= 0)
3039 return TRUE; /* re-execute timer */
3042 s->subproc_check_timer_id = 0;
3043 s->subproc_check_countdown = 0;
3044 return FALSE; /* do not re-execute timer */
3049 /* Call this just after launching a subprocess.
3050 This sets a timer that will, five times a second for two seconds,
3051 check whether the program is still running. The assumption here
3052 is that if the process didn't stay up for more than a couple of
3053 seconds, then either the program doesn't exist, or it doesn't
3054 take a -window-id argument.
3057 schedule_preview_check (state *s)
3063 fprintf (stderr, "%s: scheduling check\n", blurb());
3065 if (s->subproc_check_timer_id)
3066 gtk_timeout_remove (s->subproc_check_timer_id);
3067 s->subproc_check_timer_id =
3068 gtk_timeout_add (1000 / ticks,
3069 check_subproc_timer, (gpointer) s);
3070 s->subproc_check_countdown = ticks * seconds;
3075 screen_blanked_p (void)
3079 unsigned long nitems, bytesafter;
3081 Display *dpy = GDK_DISPLAY();
3082 Bool blanked_p = False;
3084 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3085 XA_SCREENSAVER_STATUS,
3086 0, 3, False, XA_INTEGER,
3087 &type, &format, &nitems, &bytesafter,
3088 (unsigned char **) &data)
3090 && type == XA_INTEGER
3093 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3095 if (data) free (data);
3100 /* Wake up every now and then and see if the screen is blanked.
3101 If it is, kill off the small-window demo -- no point in wasting
3102 cycles by running two screensavers at once...
3105 check_blanked_timer (gpointer data)
3107 state *s = (state *) data;
3108 Bool blanked_p = screen_blanked_p ();
3109 if (blanked_p && s->running_preview_pid)
3112 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3113 kill_preview_subproc (s);
3116 return True; /* re-execute timer */
3120 /* Setting window manager icon
3124 init_icon (GdkWindow *window)
3126 GdkBitmap *mask = 0;
3129 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3130 (gchar **) logo_50_xpm);
3132 gdk_window_set_icon (window, 0, pixmap, mask);
3136 /* The main demo-mode command loop.
3141 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3142 XrmRepresentation *type, XrmValue *value, XPointer closure)
3145 for (i = 0; quarks[i]; i++)
3147 if (bindings[i] == XrmBindTightly)
3148 fprintf (stderr, (i == 0 ? "" : "."));
3149 else if (bindings[i] == XrmBindLoosely)
3150 fprintf (stderr, "*");
3152 fprintf (stderr, " ??? ");
3153 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3156 fprintf (stderr, ": %s\n", (char *) value->addr);
3164 the_network_is_not_the_computer (state *s)
3166 Display *dpy = GDK_DISPLAY();
3167 char *rversion = 0, *ruser = 0, *rhost = 0;
3168 char *luser, *lhost;
3170 struct passwd *p = getpwuid (getuid ());
3171 const char *d = DisplayString (dpy);
3173 # if defined(HAVE_UNAME)
3175 if (uname (&uts) < 0)
3176 lhost = "<UNKNOWN>";
3178 lhost = uts.nodename;
3180 strcpy (lhost, getenv("SYS$NODE"));
3181 # else /* !HAVE_UNAME && !VMS */
3182 strcat (lhost, "<UNKNOWN>");
3183 # endif /* !HAVE_UNAME && !VMS */
3185 if (p && p->pw_name)
3190 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3192 /* Make a buffer that's big enough for a number of copies of all the
3193 strings, plus some. */
3194 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3195 (ruser ? strlen(ruser) : 0) +
3196 (rhost ? strlen(rhost) : 0) +
3203 if (!rversion || !*rversion)
3207 "The XScreenSaver daemon doesn't seem to be running\n"
3208 "on display \"%s\". Launch it now?",
3211 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3213 /* Warn that the two processes are running as different users.
3217 "%s is running as user \"%s\" on host \"%s\".\n"
3218 "But the xscreensaver managing display \"%s\"\n"
3219 "is running as user \"%s\" on host \"%s\".\n"
3221 "Since they are different users, they won't be reading/writing\n"
3222 "the same ~/.xscreensaver file, so %s isn't\n"
3223 "going to work right.\n"
3225 "You should either re-run %s as \"%s\", or re-run\n"
3226 "xscreensaver as \"%s\".\n"
3228 "Restart the xscreensaver daemon now?\n",
3229 blurb(), luser, lhost,
3231 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3233 blurb(), (ruser ? ruser : "???"),
3236 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3238 /* Warn that the two processes are running on different hosts.
3242 "%s is running as user \"%s\" on host \"%s\".\n"
3243 "But the xscreensaver managing display \"%s\"\n"
3244 "is running as user \"%s\" on host \"%s\".\n"
3246 "If those two machines don't share a file system (that is,\n"
3247 "if they don't see the same ~%s/.xscreensaver file) then\n"
3248 "%s won't work right.\n"
3250 "Restart the daemon on \"%s\" as \"%s\" now?\n",
3251 blurb(), luser, lhost,
3253 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3258 else if (!!strcmp (rversion, s->short_version))
3260 /* Warn that the version numbers don't match.
3264 "This is %s version %s.\n"
3265 "But the xscreensaver managing display \"%s\"\n"
3266 "is version %s. This could cause problems.\n"
3268 "Restart the xscreensaver daemon now?\n",
3269 blurb(), s->short_version,
3276 warning_dialog (s->toplevel_widget, msg, True, 1);
3278 if (rversion) free (rversion);
3279 if (ruser) free (ruser);
3280 if (rhost) free (rhost);
3285 /* We use this error handler so that X errors are preceeded by the name
3286 of the program that generated them.
3289 demo_ehandler (Display *dpy, XErrorEvent *error)
3291 state *s = global_state_kludge; /* I hate C so much... */
3292 fprintf (stderr, "\nX error in %s:\n", blurb());
3293 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
3295 kill_preview_subproc (s);
3299 fprintf (stderr, " (nonfatal.)\n");
3304 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3305 of the program that generated them; and also that we can ignore one
3306 particular bogus error message that Gdk madly spews.
3309 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3310 const gchar *message, gpointer user_data)
3312 /* Ignore the message "Got event for unknown window: 0x...".
3313 Apparently some events are coming in for the xscreensaver window
3314 (presumably reply events related to the ClientMessage) and Gdk
3315 feels the need to complain about them. So, just suppress any
3316 messages that look like that one.
3318 if (strstr (message, "unknown window"))
3321 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3322 (log_domain ? log_domain : progclass),
3323 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3324 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3325 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3326 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3327 log_level == G_LOG_LEVEL_INFO ? "info" :
3328 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3330 ((!*message || message[strlen(message)-1] != '\n')
3335 static char *defaults[] = {
3336 #include "XScreenSaver_ad.h"
3341 #ifdef HAVE_CRAPPLET
3342 static struct poptOption crapplet_options[] = {
3343 {NULL, '\0', 0, NULL, 0}
3345 #endif /* HAVE_CRAPPLET */
3348 const char *usage = "[--display dpy] [--prefs]"
3349 # ifdef HAVE_CRAPPLET
3356 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3358 state *s = (state *) user_data;
3359 Boolean oi = s->initializing_p;
3360 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3361 s->initializing_p = True;
3362 eschew_gtk_lossage (label);
3363 s->initializing_p = oi;
3369 print_widget_tree (GtkWidget *w, int depth)
3372 for (i = 0; i < depth; i++)
3373 fprintf (stderr, " ");
3374 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3376 if (GTK_IS_LIST (w))
3378 for (i = 0; i < depth+1; i++)
3379 fprintf (stderr, " ");
3380 fprintf (stderr, "...list kids...\n");
3382 else if (GTK_IS_CONTAINER (w))
3384 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3387 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3395 delayed_scroll_kludge (gpointer data)
3397 state *s = (state *) data;
3398 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3399 ensure_selected_item_visible (w);
3401 /* Oh, this is just fucking lovely, too. */
3402 w = GTK_WIDGET (name_to_widget (s, "preview"));
3403 gtk_widget_hide (w);
3404 gtk_widget_show (w);
3406 return FALSE; /* do not re-execute timer */
3411 main (int argc, char **argv)
3415 saver_preferences *p;
3419 Widget toplevel_shell;
3420 char *real_progname = argv[0];
3421 char window_title[255];
3422 Bool crapplet_p = False;
3425 str = strrchr (real_progname, '/');
3426 if (str) real_progname = str+1;
3429 memset (s, 0, sizeof(*s));
3430 s->initializing_p = True;
3433 global_state_kludge = s; /* I hate C so much... */
3435 progname = real_progname;
3437 s->short_version = (char *) malloc (5);
3438 memcpy (s->short_version, screensaver_id + 17, 4);
3439 s->short_version [4] = 0;
3442 /* Register our error message logger for every ``log domain'' known.
3443 There's no way to do this globally, so I grepped the Gtk/Gdk sources
3444 for all of the domains that seem to be in use.
3447 const char * const domains[] = { 0,
3448 "Gtk", "Gdk", "GLib", "GModule",
3449 "GThread", "Gnome", "GnomeUI" };
3450 for (i = 0; i < countof(domains); i++)
3451 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
3454 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
3456 const char *dir = DEFAULT_ICONDIR;
3457 if (*dir) add_pixmap_directory (dir);
3461 /* This is gross, but Gtk understands --display and not -display...
3463 for (i = 1; i < argc; i++)
3464 if (argv[i][0] && argv[i][1] &&
3465 !strncmp(argv[i], "-display", strlen(argv[i])))
3466 argv[i] = "--display";
3469 /* We need to parse this arg really early... Sigh. */
3470 for (i = 1; i < argc; i++)
3472 (!strcmp(argv[i], "--crapplet") ||
3473 !strcmp(argv[i], "--capplet")))
3475 # ifdef HAVE_CRAPPLET
3478 for (j = i; j < argc; j++) /* remove it from the list */
3479 argv[j] = argv[j+1];
3482 # else /* !HAVE_CRAPPLET */
3483 fprintf (stderr, "%s: not compiled with --crapplet support\n",
3485 fprintf (stderr, "%s: %s\n", real_progname, usage);
3487 # endif /* !HAVE_CRAPPLET */
3490 (!strcmp(argv[i], "--debug") ||
3491 !strcmp(argv[i], "-debug") ||
3492 !strcmp(argv[i], "-d")))
3496 for (j = i; j < argc; j++) /* remove it from the list */
3497 argv[j] = argv[j+1];
3501 /* Let Gtk open the X connection, then initialize Xt to use that
3502 same connection. Doctor Frankenstein would be proud.
3504 # ifdef HAVE_CRAPPLET
3507 GnomeClient *client;
3508 GnomeClientFlags flags = 0;
3510 int init_results = gnome_capplet_init ("screensaver-properties",
3512 argc, argv, NULL, 0, NULL);
3514 0 upon successful initialization;
3515 1 if --init-session-settings was passed on the cmdline;
3516 2 if --ignore was passed on the cmdline;
3519 So the 1 signifies just to init the settings, and quit, basically.
3520 (Meaning launch the xscreensaver daemon.)
3523 if (init_results < 0)
3526 g_error ("An initialization error occurred while "
3527 "starting xscreensaver-capplet.\n");
3529 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
3530 real_progname, init_results);
3535 client = gnome_master_client ();
3538 flags = gnome_client_get_flags (client);
3540 if (flags & GNOME_CLIENT_IS_CONNECTED)
3543 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
3544 gnome_client_get_id (client));
3547 char *session_args[20];
3549 session_args[i++] = real_progname;
3550 session_args[i++] = "--capplet";
3551 session_args[i++] = "--init-session-settings";
3552 session_args[i] = 0;
3553 gnome_client_set_priority (client, 20);
3554 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
3555 gnome_client_set_restart_command (client, i, session_args);
3559 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
3562 gnome_client_flush (client);
3565 if (init_results == 1)
3567 system ("xscreensaver -nosplash &");
3573 # endif /* HAVE_CRAPPLET */
3575 gtk_init (&argc, &argv);
3579 /* We must read exactly the same resources as xscreensaver.
3580 That means we must have both the same progclass *and* progname,
3581 at least as far as the resource database is concerned. So,
3582 put "xscreensaver" in argv[0] while initializing Xt.
3584 argv[0] = "xscreensaver";
3588 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
3590 XtToolkitInitialize ();
3591 app = XtCreateApplicationContext ();
3592 dpy = GDK_DISPLAY();
3593 XtAppSetFallbackResources (app, defaults);
3594 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
3595 toplevel_shell = XtAppCreateShell (progname, progclass,
3596 applicationShellWidgetClass,
3599 dpy = XtDisplay (toplevel_shell);
3600 db = XtDatabase (dpy);
3601 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
3602 XSetErrorHandler (demo_ehandler);
3604 /* Let's just ignore these. They seem to confuse Irix Gtk... */
3605 signal (SIGPIPE, SIG_IGN);
3607 /* After doing Xt-style command-line processing, complain about any
3608 unrecognized command-line arguments.
3610 for (i = 1; i < argc; i++)
3612 char *str = argv[i];
3613 if (str[0] == '-' && str[1] == '-')
3615 if (!strcmp (str, "-prefs"))
3617 else if (crapplet_p)
3618 /* There are lots of random args that we don't care about when we're
3619 started as a crapplet, so just ignore unknown args in that case. */
3623 fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]);
3624 fprintf (stderr, "%s: %s\n", real_progname, usage);
3629 /* Load the init file, which may end up consulting the X resource database
3630 and the site-wide app-defaults file. Note that at this point, it's
3631 important that `progname' be "xscreensaver", rather than whatever
3636 initialize_sort_map (s);
3638 /* Now that Xt has been initialized, and the resources have been read,
3639 we can set our `progname' variable to something more in line with
3642 progname = real_progname;
3646 /* Print out all the resources we read. */
3648 XrmName name = { 0 };
3649 XrmClass class = { 0 };
3651 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
3657 /* Intern the atoms that xscreensaver_command() needs.
3659 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
3660 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
3661 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
3662 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
3663 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
3664 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
3665 XA_SELECT = XInternAtom (dpy, "SELECT", False);
3666 XA_DEMO = XInternAtom (dpy, "DEMO", False);
3667 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
3668 XA_BLANK = XInternAtom (dpy, "BLANK", False);
3669 XA_LOCK = XInternAtom (dpy, "LOCK", False);
3670 XA_EXIT = XInternAtom (dpy, "EXIT", False);
3671 XA_RESTART = XInternAtom (dpy, "RESTART", False);
3674 /* Create the window and all its widgets.
3676 s->base_widget = create_xscreensaver_demo ();
3677 s->popup_widget = create_xscreensaver_settings_dialog ();
3678 s->toplevel_widget = s->base_widget;
3681 /* Set the main window's title. */
3683 char *v = (char *) strdup(strchr(screensaver_id, ' '));
3684 char *s1, *s2, *s3, *s4;
3685 s1 = (char *) strchr(v, ' '); s1++;
3686 s2 = (char *) strchr(s1, ' ');
3687 s3 = (char *) strchr(v, '('); s3++;
3688 s4 = (char *) strchr(s3, ')');
3691 sprintf (window_title, "%.50s %.50s, %.50s", progclass, s1, s3);
3692 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
3693 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
3697 /* Adjust the (invisible) notebooks on the popup dialog... */
3699 GtkNotebook *notebook =
3700 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
3701 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
3705 gtk_widget_hide (std);
3706 # else /* !HAVE_XML */
3707 /* Make the advanced page be the only one available. */
3708 gtk_widget_set_sensitive (std, False);
3709 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
3710 gtk_widget_hide (std);
3712 # endif /* !HAVE_XML */
3714 gtk_notebook_set_page (notebook, page);
3715 gtk_notebook_set_show_tabs (notebook, False);
3718 /* Various other widget initializations...
3720 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
3721 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
3723 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
3724 GTK_SIGNAL_FUNC (wm_popup_close_cb),
3727 populate_hack_list (s);
3728 populate_prefs_page (s);
3729 sensitize_demo_widgets (s, False);
3730 fix_text_entry_sizes (s);
3731 scroll_to_current_hack (s);
3733 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
3734 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
3737 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
3738 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
3740 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
3741 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
3745 /* Hook up callbacks to the items on the mode menu. */
3747 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
3748 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
3749 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
3750 for (; kids; kids = kids->next)
3751 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
3752 GTK_SIGNAL_FUNC (mode_menu_item_cb),
3757 /* Handle the -prefs command-line argument. */
3760 GtkNotebook *notebook =
3761 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
3762 gtk_notebook_set_page (notebook, 1);
3765 # ifdef HAVE_CRAPPLET
3769 GtkWidget *outer_vbox;
3771 gtk_widget_hide (s->toplevel_widget);
3773 capplet = capplet_widget_new ();
3775 /* Make there be a "Close" button instead of "OK" and "Cancel" */
3776 # ifdef HAVE_CRAPPLET_IMMEDIATE
3777 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
3778 # endif /* HAVE_CRAPPLET_IMMEDIATE */
3780 /* In crapplet-mode, take off the menubar. */
3781 gtk_widget_hide (name_to_widget (s, "menubar"));
3783 /* Reparent our top-level container to be a child of the capplet
3786 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
3787 gtk_widget_ref (outer_vbox);
3788 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
3790 GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
3791 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
3793 /* Find the window above us, and set the title and close handler. */
3795 GtkWidget *window = capplet;
3796 while (window && !GTK_IS_WINDOW (window))
3797 window = window->parent;
3800 gtk_window_set_title (GTK_WINDOW (window), window_title);
3801 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
3802 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
3807 s->toplevel_widget = capplet;
3809 # endif /* HAVE_CRAPPLET */
3812 gtk_widget_show (s->toplevel_widget);
3813 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
3814 hack_environment (s);
3815 fix_preview_visual (s);
3817 /* Realize page zero, so that we can diddle the scrollbar when the
3818 user tabs back to it -- otherwise, the current hack isn't scrolled
3819 to the first time they tab back there, when started with "-prefs".
3820 (Though it is if they then tab away, and back again.)
3822 #### Bah! This doesn't work. Gtk eats my ass! Someone who
3823 #### understands this crap, explain to me how to make this work.
3825 gtk_widget_realize (name_to_widget (s, "demos_table"));
3828 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
3831 /* Issue any warnings about the running xscreensaver daemon. */
3832 the_network_is_not_the_computer (s);
3835 /* Run the Gtk event loop, and not the Xt event loop. This means that
3836 if there were Xt timers or fds registered, they would never get serviced,
3837 and if there were any Xt widgets, they would never have events delivered.
3838 Fortunately, we're using Gtk for all of the UI, and only initialized
3839 Xt so that we could process the command line and use the X resource
3842 s->initializing_p = False;
3844 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
3845 after we start up. Otherwise, it always appears scrolled to the top
3846 when in crapplet-mode. */
3847 gtk_timeout_add (500, delayed_scroll_kludge, s);
3851 /* Load every configurator in turn, to scan them for errors all at once. */
3854 for (i = 0; i < p->screenhacks_count; i++)
3856 screenhack *hack = p->screenhacks[s->hack_number_to_list_elt[i]];
3857 conf_data *d = load_configurator (hack->command, False);
3858 if (d) free_conf_data (d);
3864 # ifdef HAVE_CRAPPLET
3866 capplet_gtk_main ();
3868 # endif /* HAVE_CRAPPLET */
3871 kill_preview_subproc (s);
3875 #endif /* HAVE_GTK -- whole file */