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 */
27 #endif /* ENABLE_NLS */
30 # include <pwd.h> /* for getpwuid() */
36 # include <sys/utsname.h> /* for uname() */
37 #endif /* HAVE_UNAME */
47 #ifdef HAVE_SYS_WAIT_H
48 # include <sys/wait.h> /* for waitpid() and associated macros */
52 #include <X11/Xproto.h> /* for CARD32 */
53 #include <X11/Xatom.h> /* for XA_INTEGER */
54 #include <X11/Intrinsic.h>
55 #include <X11/StringDefs.h>
57 /* We don't actually use any widget internals, but these are included
58 so that gdb will have debug info for the widgets... */
59 #include <X11/IntrinsicP.h>
60 #include <X11/ShellP.h>
64 # include <X11/Xmu/Error.h>
66 # include <Xmu/Error.h>
76 # include <capplet-widget.h>
83 #include "resources.h" /* for parse_time() */
84 #include "visual.h" /* for has_writable_cells() */
85 #include "remote.h" /* for xscreensaver_command() */
88 #include "logo-50.xpm"
89 #include "logo-180.xpm"
91 #include "demo-Gtk-widgets.h"
92 #include "demo-Gtk-support.h"
93 #include "demo-Gtk-conf.h"
101 extern void exec_command (const char *shell, const char *command, int nice);
104 #define countof(x) (sizeof((x))/sizeof((*x)))
108 char *progclass = "XScreenSaver";
111 /* The order of the items in the mode menu. */
112 static int mode_menu_order[] = {
113 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
118 char *short_version; /* version number of this xscreensaver build */
120 GtkWidget *toplevel_widget; /* the main window */
121 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
122 GtkWidget *popup_widget; /* the "Settings" dialog */
123 conf_data *cdata; /* private data for per-hack configuration */
125 Bool debug_p; /* whether to print diagnostics */
126 Bool initializing_p; /* flag for breaking recursion loops */
127 Bool saving_p; /* flag for breaking recursion loops */
129 char *desired_preview_cmd; /* subprocess we intend to run */
130 char *running_preview_cmd; /* subprocess we are currently running */
131 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
132 Bool running_preview_error_p; /* whether the pid died abnormally */
134 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
135 int subproc_timer_id; /* timer to delay subproc launch */
136 int subproc_check_timer_id; /* timer to check whether it started up */
137 int subproc_check_countdown; /* how many more checks left */
139 int *list_elt_to_hack_number; /* table for sorting the hack list */
140 int *hack_number_to_list_elt; /* the inverse table */
142 int _selected_list_element; /* don't use this: call
143 selected_list_element() instead */
145 saver_preferences prefs;
150 /* Total fucking evilness due to the fact that it's rocket science to get
151 a closure object of our own down into the various widget callbacks. */
152 static state *global_state_kludge;
155 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
156 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
157 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
160 static void populate_demo_window (state *, int list_elt);
161 static void populate_prefs_page (state *);
162 static void populate_popup_window (state *);
164 static Bool flush_dialog_changes_and_save (state *);
165 static Bool flush_popup_changes_and_save (state *);
167 static int maybe_reload_init_file (state *);
168 static void await_xscreensaver (state *);
170 static void schedule_preview (state *, const char *cmd);
171 static void kill_preview_subproc (state *);
172 static void schedule_preview_check (state *);
176 /* Some random utility functions
182 time_t now = time ((time_t *) 0);
183 char *ct = (char *) ctime (&now);
184 static char buf[255];
185 int n = strlen(progname);
187 strncpy(buf, progname, n);
190 strncpy(buf+n, ct+11, 8);
191 strcpy(buf+n+9, ": ");
197 name_to_widget (state *s, const char *name)
204 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
207 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
211 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
216 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
217 Takes a scroller, viewport, or list as an argument.
220 ensure_selected_item_visible (GtkWidget *widget)
222 GtkScrolledWindow *scroller = 0;
224 GtkList *list_widget = 0;
228 GtkWidget *selected = 0;
231 gint parent_h, child_y, child_h, children_h, ignore;
232 double ratio_t, ratio_b;
234 if (GTK_IS_SCROLLED_WINDOW (widget))
236 scroller = GTK_SCROLLED_WINDOW (widget);
237 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
238 list_widget = GTK_LIST (GTK_BIN(vp)->child);
240 else if (GTK_IS_VIEWPORT (widget))
242 vp = GTK_VIEWPORT (widget);
243 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
244 list_widget = GTK_LIST (GTK_BIN(vp)->child);
246 else if (GTK_IS_LIST (widget))
248 list_widget = GTK_LIST (widget);
249 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
250 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
255 slist = list_widget->selection;
256 selected = (slist ? GTK_WIDGET (slist->data) : 0);
260 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
262 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
263 kids; kids = kids->next)
266 adj = gtk_scrolled_window_get_vadjustment (scroller);
268 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
269 &ignore, &ignore, &ignore, &parent_h, &ignore);
270 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
271 &ignore, &child_y, &ignore, &child_h, &ignore);
272 children_h = nkids * child_h;
274 ratio_t = ((double) child_y) / ((double) children_h);
275 ratio_b = ((double) child_y + child_h) / ((double) children_h);
277 if (adj->upper == 0.0) /* no items in list */
280 if (ratio_t < (adj->value / adj->upper) ||
281 ratio_b > ((adj->value + adj->page_size) / adj->upper))
284 int slop = parent_h * 0.75; /* how much to overshoot by */
286 if (ratio_t < (adj->value / adj->upper))
288 double ratio_w = ((double) parent_h) / ((double) children_h);
289 double ratio_l = (ratio_b - ratio_t);
290 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
293 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
295 target = ratio_t * adj->upper;
299 if (target > adj->upper - adj->page_size)
300 target = adj->upper - adj->page_size;
304 gtk_adjustment_set_value (adj, target);
309 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
311 GtkWidget *shell = GTK_WIDGET (user_data);
312 while (shell->parent)
313 shell = shell->parent;
314 gtk_widget_destroy (GTK_WIDGET (shell));
318 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
320 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
322 restart_menu_cb (widget, user_data);
323 warning_dialog_dismiss_cb (widget, user_data);
327 warning_dialog (GtkWidget *parent, const char *message,
328 Boolean restart_button_p, int center)
330 char *msg = strdup (message);
333 GtkWidget *dialog = gtk_dialog_new ();
334 GtkWidget *label = 0;
336 GtkWidget *cancel = 0;
339 while (parent && !parent->window)
340 parent = parent->parent;
342 if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
344 fprintf (stderr, "%s: too early for dialog?\n", progname);
352 char *s = strchr (head, '\n');
355 sprintf (name, "label%d", i++);
358 label = gtk_label_new (head);
362 GTK_WIDGET (label)->style =
363 gtk_style_copy (GTK_WIDGET (label)->style);
364 GTK_WIDGET (label)->style->font =
365 gdk_font_load (get_string_resource("warning_dialog.headingFont",
367 gtk_widget_set_style (GTK_WIDGET (label),
368 GTK_WIDGET (label)->style);
372 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
373 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
374 label, TRUE, TRUE, 0);
375 gtk_widget_show (label);
386 label = gtk_label_new ("");
387 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
388 label, TRUE, TRUE, 0);
389 gtk_widget_show (label);
391 label = gtk_hbutton_box_new ();
392 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
393 label, TRUE, TRUE, 0);
395 ok = gtk_button_new_with_label ("OK");
396 gtk_container_add (GTK_CONTAINER (label), ok);
398 if (restart_button_p)
400 cancel = gtk_button_new_with_label ("Cancel");
401 gtk_container_add (GTK_CONTAINER (label), cancel);
404 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
405 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
406 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
407 gtk_widget_show (ok);
409 gtk_widget_show (cancel);
410 gtk_widget_show (label);
411 gtk_widget_show (dialog);
412 /* gtk_window_set_default (GTK_WINDOW (dialog), ok);*/
414 if (restart_button_p)
416 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
417 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
419 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
420 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
425 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
426 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
430 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
431 GTK_WIDGET (parent)->window);
433 gdk_window_show (GTK_WIDGET (dialog)->window);
434 gdk_window_raise (GTK_WIDGET (dialog)->window);
441 run_cmd (state *s, Atom command, int arg)
446 flush_dialog_changes_and_save (s);
447 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
452 sprintf (buf, "Error:\n\n%s", err);
454 strcpy (buf, "Unknown error!");
455 warning_dialog (s->toplevel_widget, buf, False, 100);
462 run_hack (state *s, int list_elt, Bool report_errors_p)
465 if (list_elt < 0) return;
466 hack_number = s->list_elt_to_hack_number[list_elt];
468 flush_dialog_changes_and_save (s);
469 schedule_preview (s, 0);
471 run_cmd (s, XA_DEMO, hack_number + 1);
475 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
487 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
489 state *s = global_state_kludge; /* I hate C so much... */
490 flush_dialog_changes_and_save (s);
491 kill_preview_subproc (s);
496 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
498 state *s = (state *) data;
499 flush_dialog_changes_and_save (s);
505 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
508 char *vers = strdup (screensaver_id + 4);
511 char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
513 s = strchr (vers, ',');
517 sprintf(copy, _("Copyright \251 1991-2002 %s"), s);
519 sprintf (msg, "%s\n\n%s", copy, desc);
521 /* I can't make gnome_about_new() work here -- it starts dying in
522 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
523 then this might be the thing to do:
527 const gchar *auth[] = { 0 };
528 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
530 gtk_widget_show (about);
532 #else / * GTK but not GNOME * /
536 GdkColormap *colormap;
537 GdkPixmap *gdkpixmap;
540 GtkWidget *dialog = gtk_dialog_new ();
541 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
542 GtkWidget *parent = GTK_WIDGET (menuitem);
543 while (parent->parent)
544 parent = parent->parent;
546 hbox = gtk_hbox_new (FALSE, 20);
547 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
548 hbox, TRUE, TRUE, 0);
550 colormap = gtk_widget_get_colormap (parent);
552 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
553 (gchar **) logo_180_xpm);
554 icon = gtk_pixmap_new (gdkpixmap, mask);
555 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
557 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
559 vbox = gtk_vbox_new (FALSE, 0);
560 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
562 label1 = gtk_label_new (vers);
563 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
564 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
565 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
567 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
568 GTK_WIDGET (label1)->style->font =
569 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
570 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
572 label2 = gtk_label_new (msg);
573 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
574 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
575 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
577 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
578 GTK_WIDGET (label2)->style->font =
579 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
580 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
582 hb = gtk_hbutton_box_new ();
584 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
587 ok = gtk_button_new_with_label (_("OK"));
588 gtk_container_add (GTK_CONTAINER (hb), ok);
590 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
591 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
592 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
594 gtk_widget_show (hbox);
595 gtk_widget_show (icon);
596 gtk_widget_show (vbox);
597 gtk_widget_show (label1);
598 gtk_widget_show (label2);
599 gtk_widget_show (hb);
600 gtk_widget_show (ok);
601 gtk_widget_show (dialog);
603 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
604 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
606 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
607 GTK_WIDGET (parent)->window);
608 gdk_window_show (GTK_WIDGET (dialog)->window);
609 gdk_window_raise (GTK_WIDGET (dialog)->window);
615 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
617 state *s = global_state_kludge; /* I hate C so much... */
618 saver_preferences *p = &s->prefs;
621 if (!p->help_url || !*p->help_url)
623 warning_dialog (s->toplevel_widget,
625 "No Help URL has been specified.\n"), False, 100);
629 help_command = (char *) malloc (strlen (p->load_url_command) +
630 (strlen (p->help_url) * 2) + 20);
631 strcpy (help_command, "( ");
632 sprintf (help_command + strlen(help_command),
633 p->load_url_command, p->help_url, p->help_url);
634 strcat (help_command, " ) &");
635 system (help_command);
641 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
643 state *s = global_state_kludge; /* I hate C so much... */
644 run_cmd (s, XA_ACTIVATE, 0);
649 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
651 state *s = global_state_kludge; /* I hate C so much... */
652 run_cmd (s, XA_LOCK, 0);
657 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
659 state *s = global_state_kludge; /* I hate C so much... */
660 run_cmd (s, XA_EXIT, 0);
665 restart_menu_cb (GtkWidget *widget, gpointer user_data)
667 state *s = global_state_kludge; /* I hate C so much... */
668 flush_dialog_changes_and_save (s);
669 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
671 system ("xscreensaver -nosplash &");
673 await_xscreensaver (s);
677 await_xscreensaver (state *s)
681 Display *dpy = GDK_DISPLAY();
682 /* GtkWidget *dialog = 0;*/
685 while (!rversion && (--countdown > 0))
687 /* Check for the version of the running xscreensaver... */
688 server_xscreensaver_version (dpy, &rversion, 0, 0);
690 /* If it's not there yet, wait a second... */
695 /* if (dialog) gtk_widget_destroy (dialog);*/
704 /* Timed out, no screensaver running. */
707 Bool root_p = (geteuid () == 0);
711 "The xscreensaver daemon did not start up properly.\n"
716 _("You are running as root. This usually means that xscreensaver\n"
717 "was unable to contact your X server because access control is\n"
718 "turned on. Try running this command:\n"
720 " xhost +localhost\n"
722 "and then selecting `File / Restart Daemon'.\n"
724 "Note that turning off access control will allow anyone logged\n"
725 "on to this machine to access your screen, which might be\n"
726 "considered a security problem. Please read the xscreensaver\n"
727 "manual and FAQ for more information.\n"
729 "You shouldn't run X as root. Instead, you should log in as a\n"
730 "normal user, and `su' as necessary."));
732 strcat (buf, _("Please check your $PATH and permissions."));
734 warning_dialog (s->toplevel_widget, buf, False, 1);
740 selected_list_element (state *s)
742 return s->_selected_list_element;
747 demo_write_init_file (state *s, saver_preferences *p)
751 /* #### try to figure out why shit keeps getting reordered... */
752 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
756 if (!write_init_file (p, s->short_version, False))
759 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
764 const char *f = init_file_name();
766 warning_dialog (s->toplevel_widget,
767 _("Error:\n\nCouldn't determine init file name!\n"),
771 char *b = (char *) malloc (strlen(f) + 1024);
772 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
773 warning_dialog (s->toplevel_widget, b, False, 100);
782 run_this_cb (GtkButton *button, gpointer user_data)
784 state *s = global_state_kludge; /* I hate C so much... */
785 int list_elt = selected_list_element (s);
786 if (list_elt < 0) return;
787 if (!flush_dialog_changes_and_save (s))
788 run_hack (s, list_elt, True);
793 manual_cb (GtkButton *button, gpointer user_data)
795 state *s = global_state_kludge; /* I hate C so much... */
796 saver_preferences *p = &s->prefs;
797 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
798 int list_elt = selected_list_element (s);
800 char *name, *name2, *cmd, *str;
801 if (list_elt < 0) return;
802 hack_number = s->list_elt_to_hack_number[list_elt];
804 flush_dialog_changes_and_save (s);
805 ensure_selected_item_visible (GTK_WIDGET (list_widget));
807 name = strdup (p->screenhacks[hack_number]->command);
809 while (isspace (*name2)) name2++;
811 while (*str && !isspace (*str)) str++;
813 str = strrchr (name2, '/');
814 if (str) name = str+1;
816 cmd = get_string_resource ("manualCommand", "ManualCommand");
819 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
821 sprintf (cmd2 + strlen (cmd2),
823 name2, name2, name2, name2);
824 strcat (cmd2, " ) &");
830 warning_dialog (GTK_WIDGET (button),
831 _("Error:\n\nno `manualCommand' resource set."),
840 force_list_select_item (state *s, GtkList *list, int list_elt, Bool scroll_p)
842 GtkWidget *parent = name_to_widget (s, "scroller");
843 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
845 if (!was) gtk_widget_set_sensitive (parent, True);
846 gtk_list_select_item (GTK_LIST (list), list_elt);
847 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
848 if (!was) gtk_widget_set_sensitive (parent, False);
853 run_next_cb (GtkButton *button, gpointer user_data)
855 state *s = global_state_kludge; /* I hate C so much... */
856 saver_preferences *p = &s->prefs;
857 Bool ops = s->preview_suppressed_p;
859 GtkList *list_widget =
860 GTK_LIST (name_to_widget (s, "list"));
861 int list_elt = selected_list_element (s);
868 if (list_elt >= p->screenhacks_count)
871 s->preview_suppressed_p = True;
873 flush_dialog_changes_and_save (s);
874 force_list_select_item (s, GTK_LIST (list_widget), list_elt, True);
875 populate_demo_window (s, list_elt);
876 run_hack (s, list_elt, False);
878 s->preview_suppressed_p = ops;
883 run_prev_cb (GtkButton *button, gpointer user_data)
885 state *s = global_state_kludge; /* I hate C so much... */
886 saver_preferences *p = &s->prefs;
887 Bool ops = s->preview_suppressed_p;
889 GtkList *list_widget =
890 GTK_LIST (name_to_widget (s, "list"));
891 int list_elt = selected_list_element (s);
894 list_elt = p->screenhacks_count - 1;
899 list_elt = p->screenhacks_count - 1;
901 s->preview_suppressed_p = True;
903 flush_dialog_changes_and_save (s);
904 force_list_select_item (s, GTK_LIST (list_widget), list_elt, True);
905 populate_demo_window (s, list_elt);
906 run_hack (s, list_elt, False);
908 s->preview_suppressed_p = ops;
912 /* Writes the given settings into prefs.
913 Returns true if there was a change, False otherwise.
914 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
917 flush_changes (state *s,
923 saver_preferences *p = &s->prefs;
924 Bool changed = False;
927 if (list_elt < 0 || list_elt >= p->screenhacks_count)
930 hack_number = s->list_elt_to_hack_number[list_elt];
931 hack = p->screenhacks[hack_number];
933 if (enabled_p != -1 &&
934 enabled_p != hack->enabled_p)
936 hack->enabled_p = enabled_p;
939 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
940 blurb(), hack->name, enabled_p);
945 if (!hack->command || !!strcmp (command, hack->command))
947 if (hack->command) free (hack->command);
948 hack->command = strdup (command);
951 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
952 blurb(), hack->name, command);
958 const char *ov = hack->visual;
959 if (!ov || !*ov) ov = "any";
960 if (!*visual) visual = "any";
961 if (!!strcasecmp (visual, ov))
963 if (hack->visual) free (hack->visual);
964 hack->visual = strdup (visual);
967 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
968 blurb(), hack->name, visual);
976 /* Helper for the text fields that contain time specifications:
977 this parses the text, and does error checking.
980 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
985 if (!sec_p || strchr (line, ':'))
986 value = parse_time ((char *) line, sec_p, True);
990 if (sscanf (line, "%u%c", &value, &c) != 1)
996 value *= 1000; /* Time measures in microseconds */
1002 "Unparsable time format: \"%s\"\n"),
1004 warning_dialog (s->toplevel_widget, b, False, 100);
1013 directory_p (const char *path)
1016 if (!path || !*path)
1018 else if (stat (path, &st))
1020 else if (!S_ISDIR (st.st_mode))
1027 normalize_directory (const char *path)
1031 if (!path) return 0;
1033 p2 = (char *) malloc (L + 2);
1035 if (p2[L-1] == '/') /* remove trailing slash */
1038 for (s = p2; s && *s; s++)
1041 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1042 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1045 while (s0 > p2 && s0[-1] != '/')
1055 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1056 strcpy (s, s+2), s--;
1057 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1061 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1062 while (s[0] == '/' && s[1] == '/')
1065 /* and strip trailing whitespace for good measure. */
1067 while (isspace(p2[L-1]))
1074 /* Flush out any changes made in the main dialog window (where changes
1075 take place immediately: clicking on a checkbox causes the init file
1076 to be written right away.)
1079 flush_dialog_changes_and_save (state *s)
1081 saver_preferences *p = &s->prefs;
1082 saver_preferences P2, *p2 = &P2;
1083 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1084 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1085 Bool changed = False;
1089 if (s->saving_p) return False;
1094 /* Flush any checkbox changes in the list down into the prefs struct.
1096 for (i = 0; kids; kids = kids->next, i++)
1098 GtkWidget *line = GTK_WIDGET (kids->data);
1099 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1100 GtkWidget *line_check =
1101 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1103 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1105 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1110 /* Flush the non-hack-specific settings down into the prefs struct.
1113 # define SECONDS(FIELD,NAME) \
1114 w = name_to_widget (s, (NAME)); \
1115 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1117 # define MINUTES(FIELD,NAME) \
1118 w = name_to_widget (s, (NAME)); \
1119 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1121 # define CHECKBOX(FIELD,NAME) \
1122 w = name_to_widget (s, (NAME)); \
1123 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1125 # define PATHNAME(FIELD,NAME) \
1126 w = name_to_widget (s, (NAME)); \
1127 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1129 MINUTES (&p2->timeout, "timeout_spinbutton");
1130 MINUTES (&p2->cycle, "cycle_spinbutton");
1131 CHECKBOX (p2->lock_p, "lock_button");
1132 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1134 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1135 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1136 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1137 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1139 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1140 CHECKBOX (p2->grab_video_p, "grab_video_button");
1141 CHECKBOX (p2->random_image_p, "grab_image_button");
1142 PATHNAME (p2->image_directory, "image_text");
1144 CHECKBOX (p2->verbose_p, "verbose_button");
1145 CHECKBOX (p2->capture_stderr_p, "capture_button");
1146 CHECKBOX (p2->splash_p, "splash_button");
1148 CHECKBOX (p2->install_cmap_p, "install_button");
1149 CHECKBOX (p2->fade_p, "fade_button");
1150 CHECKBOX (p2->unfade_p, "unfade_button");
1151 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1158 /* Warn if the image directory doesn't exist.
1160 if (p2->image_directory &&
1161 *p2->image_directory &&
1162 !directory_p (p2->image_directory))
1165 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1166 p2->image_directory);
1167 warning_dialog (s->toplevel_widget, b, False, 100);
1171 /* Map the mode menu to `saver_mode' enum values. */
1173 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1174 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1175 GtkWidget *selected = gtk_menu_get_active (menu);
1176 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1177 int menu_elt = g_list_index (kids, (gpointer) selected);
1178 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1179 p2->mode = mode_menu_order[menu_elt];
1182 if (p2->mode == ONE_HACK)
1184 int list_elt = selected_list_element (s);
1185 p2->selected_hack = (list_elt >= 0
1186 ? s->list_elt_to_hack_number[list_elt]
1190 # define COPY(field, name) \
1191 if (p->field != p2->field) { \
1194 fprintf (stderr, "%s: %s => %d\n", blurb(), name, p2->field); \
1196 p->field = p2->field
1199 COPY(selected_hack, "selected_hack");
1201 COPY(timeout, "timeout");
1202 COPY(cycle, "cycle");
1203 COPY(lock_p, "lock_p");
1204 COPY(lock_timeout, "lock_timeout");
1206 COPY(dpms_enabled_p, "dpms_enabled_p");
1207 COPY(dpms_standby, "dpms_standby");
1208 COPY(dpms_suspend, "dpms_suspend");
1209 COPY(dpms_off, "dpms_off");
1211 COPY(verbose_p, "verbose_p");
1212 COPY(capture_stderr_p, "capture_stderr_p");
1213 COPY(splash_p, "splash_p");
1215 COPY(install_cmap_p, "install_cmap_p");
1216 COPY(fade_p, "fade_p");
1217 COPY(unfade_p, "unfade_p");
1218 COPY(fade_seconds, "fade_seconds");
1220 COPY(grab_desktop_p, "grab_desktop_p");
1221 COPY(grab_video_p, "grab_video_p");
1222 COPY(random_image_p, "random_image_p");
1226 if (!p->image_directory ||
1227 !p2->image_directory ||
1228 strcmp(p->image_directory, p2->image_directory))
1232 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1233 blurb(), p2->image_directory);
1235 if (p->image_directory && p->image_directory != p2->image_directory)
1236 free (p->image_directory);
1237 p->image_directory = p2->image_directory;
1238 p2->image_directory = 0;
1240 populate_prefs_page (s);
1244 Display *dpy = GDK_DISPLAY();
1245 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1246 sync_server_dpms_settings (dpy, enabled_p,
1247 p->dpms_standby / 1000,
1248 p->dpms_suspend / 1000,
1252 changed = demo_write_init_file (s, p);
1255 s->saving_p = False;
1260 /* Flush out any changes made in the popup dialog box (where changes
1261 take place only when the OK button is clicked.)
1264 flush_popup_changes_and_save (state *s)
1266 Bool changed = False;
1267 saver_preferences *p = &s->prefs;
1268 int list_elt = selected_list_element (s);
1270 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1271 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1273 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1274 const char *command = gtk_entry_get_text (cmd);
1279 if (s->saving_p) return False;
1285 if (maybe_reload_init_file (s) != 0)
1291 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1293 if (!strcasecmp (visual, "")) visual = "";
1294 else if (!strcasecmp (visual, "any")) visual = "";
1295 else if (!strcasecmp (visual, "default")) visual = "Default";
1296 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1297 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1298 else if (!strcasecmp (visual, "best")) visual = "Best";
1299 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1300 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1301 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1302 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1303 else if (!strcasecmp (visual, "color")) visual = "Color";
1304 else if (!strcasecmp (visual, "gl")) visual = "GL";
1305 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1306 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1307 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1308 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1309 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1310 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1311 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1312 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
1313 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1316 gdk_beep (); /* unparsable */
1318 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1321 changed = flush_changes (s, list_elt, -1, command, visual);
1324 changed = demo_write_init_file (s, p);
1326 /* Do this to re-launch the hack if (and only if) the command line
1328 populate_demo_window (s, selected_list_element (s));
1332 s->saving_p = False;
1340 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1342 state *s = global_state_kludge; /* I hate C so much... */
1343 if (! s->initializing_p)
1345 s->initializing_p = True;
1346 flush_dialog_changes_and_save (s);
1347 s->initializing_p = False;
1352 /* Callback on menu items in the "mode" options menu.
1355 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1357 state *s = (state *) user_data;
1358 saver_preferences *p = &s->prefs;
1359 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1362 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1364 saver_mode new_mode;
1365 int old_selected = p->selected_hack;
1369 if (menu_items->data == widget)
1372 menu_items = menu_items->next;
1374 if (!menu_items) abort();
1376 new_mode = mode_menu_order[menu_index];
1378 /* Keep the same list element displayed as before; except if we're
1379 switching *to* "one screensaver" mode from any other mode, scroll
1380 to and select "the one".
1383 if (new_mode == ONE_HACK)
1384 list_elt = (p->selected_hack >= 0
1385 ? s->hack_number_to_list_elt[p->selected_hack]
1389 list_elt = selected_list_element (s);
1392 saver_mode old_mode = p->mode;
1394 populate_demo_window (s, list_elt);
1395 force_list_select_item (s, list, list_elt, True);
1396 p->mode = old_mode; /* put it back, so the init file gets written */
1399 pref_changed_cb (widget, user_data);
1401 if (old_selected != p->selected_hack)
1402 abort(); /* dammit, not again... */
1407 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1408 gint page_num, gpointer user_data)
1410 state *s = global_state_kludge; /* I hate C so much... */
1411 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1413 /* If we're switching to page 0, schedule the current hack to be run.
1414 Otherwise, schedule it to stop. */
1416 populate_demo_window (s, selected_list_element (s));
1418 schedule_preview (s, 0);
1422 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1423 list_select_cb that comes in
1424 *after* we've double-clicked.
1428 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1431 state *s = (state *) data;
1432 if (event->type == GDK_2BUTTON_PRESS)
1434 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1435 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1437 last_doubleclick_time = time ((time_t *) 0);
1440 run_hack (s, list_elt, True);
1448 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1450 state *s = (state *) data;
1451 time_t now = time ((time_t *) 0);
1453 if (now >= last_doubleclick_time + 2)
1455 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1456 populate_demo_window (s, list_elt);
1457 flush_dialog_changes_and_save (s);
1462 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1464 state *s = (state *) data;
1465 populate_demo_window (s, -1);
1466 flush_dialog_changes_and_save (s);
1470 /* Called when the checkboxes that are in the left column of the
1471 scrolling list are clicked. This both populates the right pane
1472 (just as clicking on the label (really, listitem) does) and
1473 also syncs this checkbox with the right pane Enabled checkbox.
1476 list_checkbox_cb (GtkWidget *cb, gpointer data)
1478 state *s = (state *) data;
1480 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1481 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1483 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1484 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1485 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1489 int list_elt = gtk_list_child_position (list, line);
1491 /* remember previous scroll position of the top of the list */
1492 adj = gtk_scrolled_window_get_vadjustment (scroller);
1493 scroll_top = adj->value;
1495 flush_dialog_changes_and_save (s);
1496 force_list_select_item (s, list, list_elt, False);
1497 populate_demo_window (s, list_elt);
1499 /* restore the previous scroll position of the top of the list.
1500 this is weak, but I don't really know why it's moving... */
1501 gtk_adjustment_set_value (adj, scroll_top);
1507 GtkFileSelection *widget;
1508 } file_selection_data;
1513 store_image_directory (GtkWidget *button, gpointer user_data)
1515 file_selection_data *fsd = (file_selection_data *) user_data;
1516 state *s = fsd->state;
1517 GtkFileSelection *selector = fsd->widget;
1518 GtkWidget *top = s->toplevel_widget;
1519 saver_preferences *p = &s->prefs;
1520 char *path = gtk_file_selection_get_filename (selector);
1522 if (p->image_directory && !strcmp(p->image_directory, path))
1523 return; /* no change */
1525 if (!directory_p (path))
1528 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
1529 warning_dialog (GTK_WIDGET (top), b, False, 100);
1533 if (p->image_directory) free (p->image_directory);
1534 p->image_directory = normalize_directory (path);
1536 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1537 (p->image_directory ? p->image_directory : ""));
1538 demo_write_init_file (s, p);
1543 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1545 file_selection_data *fsd = (file_selection_data *) user_data;
1546 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1550 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1552 browse_image_dir_cancel (button, user_data);
1553 store_image_directory (button, user_data);
1557 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1559 browse_image_dir_cancel (widget, user_data);
1564 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1566 state *s = global_state_kludge; /* I hate C so much... */
1567 saver_preferences *p = &s->prefs;
1568 static file_selection_data *fsd = 0;
1570 GtkFileSelection *selector = GTK_FILE_SELECTION(
1571 gtk_file_selection_new ("Please select the image directory."));
1574 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1576 fsd->widget = selector;
1579 if (p->image_directory && *p->image_directory)
1580 gtk_file_selection_set_filename (selector, p->image_directory);
1582 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1583 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1585 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1586 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1588 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1589 GTK_SIGNAL_FUNC (browse_image_dir_close),
1592 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1594 gtk_window_set_modal (GTK_WINDOW (selector), True);
1595 gtk_widget_show (GTK_WIDGET (selector));
1600 settings_cb (GtkButton *button, gpointer user_data)
1602 state *s = global_state_kludge; /* I hate C so much... */
1603 int list_elt = selected_list_element (s);
1605 populate_demo_window (s, list_elt); /* reset the widget */
1606 populate_popup_window (s); /* create UI on popup window */
1607 gtk_widget_show (s->popup_widget);
1611 settings_sync_cmd_text (state *s)
1614 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1615 char *cmd_line = get_configurator_command_line (s->cdata);
1616 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1617 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1619 # endif /* HAVE_XML */
1623 settings_adv_cb (GtkButton *button, gpointer user_data)
1625 state *s = global_state_kludge; /* I hate C so much... */
1626 GtkNotebook *notebook =
1627 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1629 settings_sync_cmd_text (s);
1630 gtk_notebook_set_page (notebook, 1);
1634 settings_std_cb (GtkButton *button, gpointer user_data)
1636 state *s = global_state_kludge; /* I hate C so much... */
1637 GtkNotebook *notebook =
1638 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1640 /* Re-create UI to reflect the in-progress command-line settings. */
1641 populate_popup_window (s);
1643 gtk_notebook_set_page (notebook, 0);
1647 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1648 gint page_num, gpointer user_data)
1650 state *s = global_state_kludge; /* I hate C so much... */
1651 GtkWidget *adv = name_to_widget (s, "adv_button");
1652 GtkWidget *std = name_to_widget (s, "std_button");
1656 gtk_widget_show (adv);
1657 gtk_widget_hide (std);
1659 else if (page_num == 1)
1661 gtk_widget_hide (adv);
1662 gtk_widget_show (std);
1671 settings_cancel_cb (GtkButton *button, gpointer user_data)
1673 state *s = global_state_kludge; /* I hate C so much... */
1674 gtk_widget_hide (s->popup_widget);
1678 settings_ok_cb (GtkButton *button, gpointer user_data)
1680 state *s = global_state_kludge; /* I hate C so much... */
1681 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1682 int page = gtk_notebook_get_current_page (notebook);
1685 /* Regenerate the command-line from the widget contents before saving.
1686 But don't do this if we're looking at the command-line page already,
1687 or we will blow away what they typed... */
1688 settings_sync_cmd_text (s);
1690 flush_popup_changes_and_save (s);
1691 gtk_widget_hide (s->popup_widget);
1695 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1697 state *s = (state *) data;
1698 settings_cancel_cb (0, (gpointer) s);
1703 /* Populating the various widgets
1707 /* Returns the number of the last hack run by the server.
1710 server_current_hack (void)
1714 unsigned long nitems, bytesafter;
1716 Display *dpy = GDK_DISPLAY();
1717 int hack_number = -1;
1719 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1720 XA_SCREENSAVER_STATUS,
1721 0, 3, False, XA_INTEGER,
1722 &type, &format, &nitems, &bytesafter,
1723 (unsigned char **) &data)
1725 && type == XA_INTEGER
1728 hack_number = (int) data[2] - 1;
1730 if (data) free (data);
1736 /* Finds the number of the last hack to run, and makes that item be
1737 selected by default.
1740 scroll_to_current_hack (state *s)
1742 saver_preferences *p = &s->prefs;
1745 if (p->mode == ONE_HACK)
1746 hack_number = p->selected_hack;
1748 hack_number = server_current_hack ();
1750 if (hack_number >= 0 && hack_number < p->screenhacks_count)
1752 int list_elt = s->hack_number_to_list_elt[hack_number];
1753 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1754 force_list_select_item (s, list, list_elt, True);
1755 populate_demo_window (s, list_elt);
1761 populate_hack_list (state *s)
1763 saver_preferences *p = &s->prefs;
1764 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1766 for (i = 0; i < p->screenhacks_count; i++)
1768 screenhack *hack = p->screenhacks[s->list_elt_to_hack_number[i]];
1770 /* A GtkList must contain only GtkListItems, but those can contain
1771 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
1772 and a Label. We handle single and double click events on the
1773 line itself, for clicking on the text, but the interior checkbox
1774 also handles its own events.
1777 GtkWidget *line_hbox;
1778 GtkWidget *line_check;
1779 GtkWidget *line_label;
1781 char *pretty_name = (hack->name
1782 ? strdup (hack->name)
1783 : make_hack_name (hack->command));
1785 line = gtk_list_item_new ();
1786 line_hbox = gtk_hbox_new (FALSE, 0);
1787 line_check = gtk_check_button_new ();
1788 line_label = gtk_label_new (pretty_name);
1790 gtk_container_add (GTK_CONTAINER (line), line_hbox);
1791 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
1792 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
1794 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1796 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
1798 gtk_widget_show (line_check);
1799 gtk_widget_show (line_label);
1800 gtk_widget_show (line_hbox);
1801 gtk_widget_show (line);
1805 gtk_container_add (GTK_CONTAINER (list), line);
1806 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
1807 GTK_SIGNAL_FUNC (list_doubleclick_cb),
1810 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
1811 GTK_SIGNAL_FUNC (list_checkbox_cb),
1815 GTK_WIDGET (GTK_BIN(line)->child)->style =
1816 gtk_style_copy (GTK_WIDGET (text_line)->style);
1818 gtk_widget_show (line);
1821 gtk_signal_connect (GTK_OBJECT (list), "select_child",
1822 GTK_SIGNAL_FUNC (list_select_cb),
1824 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
1825 GTK_SIGNAL_FUNC (list_unselect_cb),
1831 update_list_sensitivity (state *s)
1833 saver_preferences *p = &s->prefs;
1834 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
1835 Bool checkable = (p->mode == RANDOM_HACKS);
1836 Bool blankable = (p->mode != DONT_BLANK);
1838 GtkWidget *head = name_to_widget (s, "col_head_hbox");
1839 GtkWidget *use = name_to_widget (s, "use_col_frame");
1840 GtkWidget *scroller = name_to_widget (s, "scroller");
1841 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
1842 GtkWidget *blanker = name_to_widget (s, "blanking_table");
1844 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1845 GList *kids = gtk_container_children (GTK_CONTAINER (list));
1847 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
1848 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
1849 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
1851 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
1854 gtk_widget_show (use); /* the "Use" column header */
1856 gtk_widget_hide (use);
1860 GtkBin *line = GTK_BIN (kids->data);
1861 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
1862 GtkWidget *line_check =
1863 GTK_WIDGET (gtk_container_children (line_hbox)->data);
1866 gtk_widget_show (line_check);
1868 gtk_widget_hide (line_check);
1876 populate_prefs_page (state *s)
1878 saver_preferences *p = &s->prefs;
1881 /* The file supports timeouts of less than a minute, but the GUI does
1882 not, so throttle the values to be at least one minute (since "0" is
1883 a bad rounding choice...)
1885 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
1888 THROTTLE (passwd_timeout);
1891 # define FMT_MINUTES(NAME,N) \
1892 sprintf (str, "%d", (((N / 1000) + 59) / 60)); \
1893 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, (NAME))), str)
1895 # define FMT_SECONDS(NAME,N) \
1896 sprintf (str, "%d", ((N) / 1000)); \
1897 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, (NAME))), str)
1899 FMT_MINUTES ("timeout_spinbutton", p->timeout);
1900 FMT_MINUTES ("cycle_spinbutton", p->cycle);
1901 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
1902 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
1903 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
1904 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
1905 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
1910 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
1911 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
1914 TOGGLE_ACTIVE ("lock_button", p->lock_p);
1915 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
1916 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
1917 TOGGLE_ACTIVE ("splash_button", p->splash_p);
1918 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
1919 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
1920 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
1921 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
1922 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
1923 TOGGLE_ACTIVE ("fade_button", p->fade_p);
1924 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
1926 # undef TOGGLE_ACTIVE
1928 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1929 (p->image_directory ? p->image_directory : ""));
1930 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
1932 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
1935 /* Map the `saver_mode' enum to mode menu to values. */
1937 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1940 for (i = 0; i < countof(mode_menu_order); i++)
1941 if (mode_menu_order[i] == p->mode)
1943 gtk_option_menu_set_history (opt, i);
1944 update_list_sensitivity (s);
1948 Bool found_any_writable_cells = False;
1949 Bool dpms_supported = False;
1951 Display *dpy = GDK_DISPLAY();
1952 int nscreens = ScreenCount(dpy);
1954 for (i = 0; i < nscreens; i++)
1956 Screen *s = ScreenOfDisplay (dpy, i);
1957 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1959 found_any_writable_cells = True;
1964 #ifdef HAVE_XF86VMODE_GAMMA
1965 found_any_writable_cells = True; /* if we can gamma fade, go for it */
1968 #ifdef HAVE_DPMS_EXTENSION
1970 int op = 0, event = 0, error = 0;
1971 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
1972 dpms_supported = True;
1974 #endif /* HAVE_DPMS_EXTENSION */
1977 # define SENSITIZE(NAME,SENSITIVEP) \
1978 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
1980 /* Blanking and Locking
1982 SENSITIZE ("lock_spinbutton", p->lock_p);
1983 SENSITIZE ("lock_mlabel", p->lock_p);
1987 SENSITIZE ("dpms_frame", dpms_supported);
1988 SENSITIZE ("dpms_button", dpms_supported);
1989 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
1990 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
1991 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
1992 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
1993 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
1994 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
1995 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
1996 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
1997 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2001 SENSITIZE ("cmap_frame", found_any_writable_cells);
2002 SENSITIZE ("install_button", found_any_writable_cells);
2003 SENSITIZE ("fade_button", found_any_writable_cells);
2004 SENSITIZE ("unfade_button", found_any_writable_cells);
2006 SENSITIZE ("fade_label", (found_any_writable_cells &&
2007 (p->fade_p || p->unfade_p)));
2008 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
2009 (p->fade_p || p->unfade_p)));
2017 populate_popup_window (state *s)
2019 saver_preferences *p = &s->prefs;
2020 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2021 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2022 int list_elt = selected_list_element (s);
2023 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2024 ? s->list_elt_to_hack_number[list_elt]
2026 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2027 char *doc_string = 0;
2029 /* #### not in Gtk 1.2
2030 gtk_label_set_selectable (doc);
2036 free_conf_data (s->cdata);
2042 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2043 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2044 s->cdata = load_configurator (cmd_line, s->debug_p);
2045 if (s->cdata && s->cdata->widget)
2046 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget, TRUE, TRUE, 0);
2049 doc_string = (s->cdata
2050 ? s->cdata->description
2052 # else /* !HAVE_XML */
2053 doc_string = _("Descriptions not available: no XML support compiled in.");
2054 # endif /* !HAVE_XML */
2056 gtk_label_set_text (doc, (doc_string
2058 : _("No description available.")));
2063 sensitize_demo_widgets (state *s, Bool sensitive_p)
2065 const char *names1[] = { "demo", "settings" };
2066 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2067 "visual", "visual_combo" };
2069 for (i = 0; i < countof(names1); i++)
2071 GtkWidget *w = name_to_widget (s, names1[i]);
2072 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2074 for (i = 0; i < countof(names2); i++)
2076 GtkWidget *w = name_to_widget (s, names2[i]);
2077 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2082 /* Even though we've given these text fields a maximum number of characters,
2083 their default size is still about 30 characters wide -- so measure out
2084 a string in their font, and resize them to just fit that.
2087 fix_text_entry_sizes (state *s)
2089 const char * const spinbuttons[] = {
2090 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2091 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2092 "dpms_off_spinbutton",
2093 "-fade_spinbutton" };
2098 for (i = 0; i < countof(spinbuttons); i++)
2100 const char *n = spinbuttons[i];
2102 while (*n == '-') n++, cols--;
2103 w = GTK_WIDGET (name_to_widget (s, n));
2104 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2105 gtk_widget_set_usize (w, width, -2);
2108 /* Now fix the width of the combo box.
2110 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2111 w = GTK_COMBO (w)->entry;
2112 width = gdk_string_width (w->style->font, "PseudoColor___");
2113 gtk_widget_set_usize (w, width, -2);
2115 /* Now fix the width of the file entry text.
2117 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2118 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2119 gtk_widget_set_usize (w, width, -2);
2121 /* Now fix the width of the command line text.
2123 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2124 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2125 gtk_widget_set_usize (w, width, -2);
2127 /* Now fix the height of the list.
2132 int leading = 3; /* approximate is ok... */
2134 w = GTK_WIDGET (name_to_widget (s, "list"));
2135 height = w->style->font->ascent + w->style->font->descent;
2138 height += border * 2;
2139 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2140 gtk_widget_set_usize (w, -2, height);
2147 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2150 static char *up_arrow_xpm[] = {
2173 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2174 the end of the array (Gtk 1.2.5.) */
2175 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2176 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2179 static char *down_arrow_xpm[] = {
2202 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2203 the end of the array (Gtk 1.2.5.) */
2204 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2205 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2209 pixmapify_button (state *s, int down_p)
2213 GtkWidget *pixmapwid;
2217 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2218 style = gtk_widget_get_style (w);
2220 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2221 &style->bg[GTK_STATE_NORMAL],
2223 ? (gchar **) down_arrow_xpm
2224 : (gchar **) up_arrow_xpm));
2225 pixmapwid = gtk_pixmap_new (pixmap, mask);
2226 gtk_widget_show (pixmapwid);
2227 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2228 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2232 map_next_button_cb (GtkWidget *w, gpointer user_data)
2234 state *s = (state *) user_data;
2235 pixmapify_button (s, 1);
2239 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2241 state *s = (state *) user_data;
2242 pixmapify_button (s, 0);
2247 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2251 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2252 GtkAllocation *allocation,
2256 GtkWidgetAuxInfo *aux_info;
2258 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2260 aux_info->width = allocation->width;
2261 aux_info->height = -2;
2265 gtk_widget_size_request (label, &req);
2269 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2272 eschew_gtk_lossage (GtkLabel *label)
2274 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2275 aux_info->width = GTK_WIDGET (label)->allocation.width;
2276 aux_info->height = -2;
2280 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2282 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2283 you_are_not_a_unique_or_beautiful_snowflake,
2286 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2288 gtk_widget_queue_resize (GTK_WIDGET (label));
2293 populate_demo_window (state *s, int list_elt)
2295 saver_preferences *p = &s->prefs;
2298 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2299 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2300 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2301 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2302 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2304 if (p->mode == BLANK_ONLY)
2307 pretty_name = strdup (_("Blank Screen"));
2308 schedule_preview (s, 0);
2310 else if (p->mode == DONT_BLANK)
2313 pretty_name = strdup (_("Screen Saver Disabled"));
2314 schedule_preview (s, 0);
2318 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2319 ? s->list_elt_to_hack_number[list_elt]
2321 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2325 ? strdup (hack->name)
2326 : make_hack_name (hack->command))
2330 schedule_preview (s, hack->command);
2332 schedule_preview (s, 0);
2336 pretty_name = strdup (_("Preview"));
2338 gtk_frame_set_label (frame1, pretty_name);
2339 gtk_frame_set_label (frame2, pretty_name);
2341 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2342 gtk_entry_set_position (cmd, 0);
2346 sprintf (title, "%s: %.100s Settings",
2347 progclass, (pretty_name ? pretty_name : "???"));
2348 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2351 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2353 ? (hack->visual && *hack->visual
2358 sensitize_demo_widgets (s, (hack ? True : False));
2360 if (pretty_name) free (pretty_name);
2362 ensure_selected_item_visible (list);
2364 s->_selected_list_element = list_elt;
2369 widget_deleter (GtkWidget *widget, gpointer data)
2371 /* #### Well, I want to destroy these widgets, but if I do that, they get
2372 referenced again, and eventually I get a SEGV. So instead of
2373 destroying them, I'll just hide them, and leak a bunch of memory
2374 every time the disk file changes. Go go go Gtk!
2376 #### Ok, that's a lie, I get a crash even if I just hide the widget
2377 and don't ever delete it. Fuck!
2380 gtk_widget_destroy (widget);
2382 gtk_widget_hide (widget);
2387 static char **sort_hack_cmp_names_kludge;
2389 sort_hack_cmp (const void *a, const void *b)
2394 return strcmp (sort_hack_cmp_names_kludge[*(int *) a],
2395 sort_hack_cmp_names_kludge[*(int *) b]);
2400 initialize_sort_map (state *s)
2402 saver_preferences *p = &s->prefs;
2405 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2406 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2408 s->list_elt_to_hack_number = (int *)
2409 calloc (sizeof(int), p->screenhacks_count + 1);
2410 s->hack_number_to_list_elt = (int *)
2411 calloc (sizeof(int), p->screenhacks_count + 1);
2413 /* Initialize table to 1:1 mapping */
2414 for (i = 0; i < p->screenhacks_count; i++)
2415 s->list_elt_to_hack_number[i] = i;
2417 /* Generate list of names (once)
2419 sort_hack_cmp_names_kludge = (char **)
2420 calloc (sizeof(char *), p->screenhacks_count);
2421 for (i = 0; i < p->screenhacks_count; i++)
2423 screenhack *hack = p->screenhacks[i];
2424 char *name = (hack->name && *hack->name
2425 ? strdup (hack->name)
2426 : make_hack_name (hack->command));
2428 for (str = name; *str; str++)
2429 *str = tolower(*str);
2430 sort_hack_cmp_names_kludge[i] = name;
2433 /* Sort alphabetically
2435 qsort (s->list_elt_to_hack_number,
2436 p->screenhacks_count,
2437 sizeof(*s->list_elt_to_hack_number),
2442 for (i = 0; i < p->screenhacks_count; i++)
2443 free (sort_hack_cmp_names_kludge[i]);
2444 free (sort_hack_cmp_names_kludge);
2445 sort_hack_cmp_names_kludge = 0;
2447 /* Build inverse table */
2448 for (i = 0; i < p->screenhacks_count; i++)
2449 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2454 maybe_reload_init_file (state *s)
2456 saver_preferences *p = &s->prefs;
2459 static Bool reentrant_lock = False;
2460 if (reentrant_lock) return 0;
2461 reentrant_lock = True;
2463 if (init_file_changed_p (p))
2465 const char *f = init_file_name();
2470 if (!f || !*f) return 0;
2471 b = (char *) malloc (strlen(f) + 1024);
2474 "file \"%s\" has changed, reloading.\n"),
2476 warning_dialog (s->toplevel_widget, b, False, 100);
2480 initialize_sort_map (s);
2482 list_elt = selected_list_element (s);
2483 list = GTK_LIST (name_to_widget (s, "list"));
2484 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
2485 populate_hack_list (s);
2486 force_list_select_item (s, list, list_elt, True);
2487 populate_prefs_page (s);
2488 populate_demo_window (s, list_elt);
2489 ensure_selected_item_visible (GTK_WIDGET (list));
2494 reentrant_lock = False;
2500 /* Making the preview window have the right X visual (so that GL works.)
2503 static Visual *get_best_gl_visual (state *);
2506 x_visual_to_gdk_visual (Visual *xv)
2508 GList *gvs = gdk_list_visuals();
2509 if (!xv) return gdk_visual_get_system();
2510 for (; gvs; gvs = gvs->next)
2512 GdkVisual *gv = (GdkVisual *) gvs->data;
2513 if (xv == GDK_VISUAL_XVISUAL (gv))
2516 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
2517 blurb(), (unsigned long) xv->visualid);
2522 clear_preview_window (state *s)
2527 if (!s->toplevel_widget) return; /* very early */
2528 p = name_to_widget (s, "preview");
2531 if (!window) return;
2533 /* Flush the widget background down into the window, in case a subproc
2535 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
2536 gdk_window_clear (window);
2538 if (s->running_preview_error_p)
2540 const char * const lines[] = { N_("No Preview"), N_("Available") };
2541 int lh = p->style->font->ascent + p->style->font->descent;
2544 gdk_window_get_size (window, &w, &h);
2545 y = (h - (lh * countof(lines))) / 2;
2546 y += p->style->font->ascent;
2547 for (i = 0; i < countof(lines); i++)
2549 int sw = gdk_string_width (p->style->font, _(lines[i]));
2550 int x = (w - sw) / 2;
2551 gdk_draw_string (window, p->style->font,
2552 p->style->fg_gc[GTK_STATE_NORMAL],
2560 /* Is there a GDK way of doing this? */
2561 XSync (GDK_DISPLAY(), False);
2566 fix_preview_visual (state *s)
2568 GtkWidget *widget = name_to_widget (s, "preview");
2569 Visual *xvisual = get_best_gl_visual (s);
2570 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
2571 GdkVisual *dvisual = gdk_visual_get_system();
2572 GdkColormap *cmap = (visual == dvisual
2573 ? gdk_colormap_get_system ()
2574 : gdk_colormap_new (visual, False));
2577 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
2578 (visual == dvisual ? "default" : "non-default"),
2579 (xvisual ? (unsigned long) xvisual->visualid : 0L));
2581 if (!GTK_WIDGET_REALIZED (widget) ||
2582 gtk_widget_get_visual (widget) != visual)
2584 gtk_widget_unrealize (widget);
2585 gtk_widget_set_visual (widget, visual);
2586 gtk_widget_set_colormap (widget, cmap);
2587 gtk_widget_realize (widget);
2590 /* Set the Widget colors to be white-on-black. */
2592 GdkWindow *window = widget->window;
2593 GtkStyle *style = gtk_style_copy (widget->style);
2594 GdkColormap *cmap = gtk_widget_get_colormap (widget);
2595 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
2596 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
2597 GdkGC *fgc = gdk_gc_new(window);
2598 GdkGC *bgc = gdk_gc_new(window);
2599 if (!gdk_color_white (cmap, fg)) abort();
2600 if (!gdk_color_black (cmap, bg)) abort();
2601 gdk_gc_set_foreground (fgc, fg);
2602 gdk_gc_set_background (fgc, bg);
2603 gdk_gc_set_foreground (bgc, bg);
2604 gdk_gc_set_background (bgc, fg);
2605 style->fg_gc[GTK_STATE_NORMAL] = fgc;
2606 style->bg_gc[GTK_STATE_NORMAL] = fgc;
2607 gtk_widget_set_style (widget, style);
2610 gtk_widget_show (widget);
2618 subproc_pretty_name (state *s)
2620 if (s->running_preview_cmd)
2622 char *ps = strdup (s->running_preview_cmd);
2623 char *ss = strchr (ps, ' ');
2625 ss = strrchr (ps, '/');
2631 return strdup ("???");
2636 reap_zombies (state *s)
2638 int wait_status = 0;
2640 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
2644 if (pid == s->running_preview_pid)
2646 char *ss = subproc_pretty_name (s);
2647 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(), pid, ss);
2651 fprintf (stderr, "%s: pid %lu died\n", blurb(), pid);
2657 /* Mostly lifted from driver/subprocs.c */
2659 get_best_gl_visual (state *s)
2661 Display *dpy = GDK_DISPLAY();
2670 av[ac++] = "xscreensaver-gl-helper";
2675 perror ("error creating pipe:");
2682 switch ((int) (forked = fork ()))
2686 sprintf (buf, "%s: couldn't fork", blurb());
2694 close (in); /* don't need this one */
2695 close (ConnectionNumber (dpy)); /* close display fd */
2697 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
2699 perror ("could not dup() a new stdout:");
2703 execvp (av[0], av); /* shouldn't return. */
2705 if (errno != ENOENT)
2707 /* Ignore "no such file or directory" errors, unless verbose.
2708 Issue all other exec errors, though. */
2709 sprintf (buf, "%s: running %s", blurb(), av[0]);
2712 exit (1); /* exits fork */
2718 int wait_status = 0;
2720 FILE *f = fdopen (in, "r");
2721 unsigned long v = 0;
2724 close (out); /* don't need this one */
2727 fgets (buf, sizeof(buf)-1, f);
2730 /* Wait for the child to die. */
2731 waitpid (-1, &wait_status, 0);
2733 if (1 == sscanf (buf, "0x%x %c", &v, &c))
2739 fprintf (stderr, "%s: %s did not report a GL visual!\n",
2745 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
2747 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
2748 blurb(), av[0], result);
2760 kill_preview_subproc (state *s)
2762 s->running_preview_error_p = False;
2765 clear_preview_window (s);
2767 if (s->subproc_check_timer_id)
2769 gtk_timeout_remove (s->subproc_check_timer_id);
2770 s->subproc_check_timer_id = 0;
2771 s->subproc_check_countdown = 0;
2774 if (s->running_preview_pid)
2776 int status = kill (s->running_preview_pid, SIGTERM);
2777 char *ss = subproc_pretty_name (s);
2784 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
2785 blurb(), s->running_preview_pid, ss);
2790 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
2791 blurb(), s->running_preview_pid, ss);
2795 else if (s->debug_p)
2796 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
2797 s->running_preview_pid, ss);
2800 s->running_preview_pid = 0;
2801 if (s->running_preview_cmd) free (s->running_preview_cmd);
2802 s->running_preview_cmd = 0;
2809 /* Immediately and unconditionally launches the given process,
2810 after appending the -window-id option; sets running_preview_pid.
2813 launch_preview_subproc (state *s)
2815 saver_preferences *p = &s->prefs;
2819 const char *cmd = s->desired_preview_cmd;
2821 GtkWidget *pr = name_to_widget (s, "preview");
2822 GdkWindow *window = pr->window;
2824 s->running_preview_error_p = False;
2826 if (s->preview_suppressed_p)
2828 kill_preview_subproc (s);
2832 new_cmd = malloc (strlen (cmd) + 40);
2834 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
2837 /* No window id? No command to run. */
2843 strcpy (new_cmd, cmd);
2844 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X", id);
2847 kill_preview_subproc (s);
2850 s->running_preview_error_p = True;
2851 clear_preview_window (s);
2855 switch ((int) (forked = fork ()))
2860 sprintf (buf, "%s: couldn't fork", blurb());
2862 s->running_preview_error_p = True;
2868 close (ConnectionNumber (GDK_DISPLAY()));
2870 usleep (250000); /* pause for 1/4th second before launching, to give
2871 the previous program time to die and flush its X
2872 buffer, so we don't get leftover turds on the
2875 exec_command (p->shell, new_cmd, p->nice_inferior);
2876 /* Don't bother printing an error message when we are unable to
2877 exec subprocesses; we handle that by polling the pid later. */
2878 exit (1); /* exits child fork */
2883 if (s->running_preview_cmd) free (s->running_preview_cmd);
2884 s->running_preview_cmd = strdup (s->desired_preview_cmd);
2885 s->running_preview_pid = forked;
2889 char *ss = subproc_pretty_name (s);
2890 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(), forked, ss);
2897 schedule_preview_check (s);
2900 if (new_cmd) free (new_cmd);
2905 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
2908 hack_environment (state *s)
2910 static const char *def_path =
2911 # ifdef DEFAULT_PATH_PREFIX
2912 DEFAULT_PATH_PREFIX;
2917 Display *dpy = GDK_DISPLAY();
2918 const char *odpy = DisplayString (dpy);
2919 char *ndpy = (char *) malloc(strlen(odpy) + 20);
2920 strcpy (ndpy, "DISPLAY=");
2921 strcat (ndpy, odpy);
2926 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
2928 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
2929 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
2930 So we must leak it (and/or the previous setting). Yay.
2933 if (def_path && *def_path)
2935 const char *opath = getenv("PATH");
2936 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
2937 strcpy (npath, "PATH=");
2938 strcat (npath, def_path);
2939 strcat (npath, ":");
2940 strcat (npath, opath);
2944 /* do not free(npath) -- see above */
2947 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
2952 /* Called from a timer:
2953 Launches the currently-chosen subprocess, if it's not already running.
2954 If there's a different process running, kills it.
2957 update_subproc_timer (gpointer data)
2959 state *s = (state *) data;
2960 if (! s->desired_preview_cmd)
2961 kill_preview_subproc (s);
2962 else if (!s->running_preview_cmd ||
2963 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
2964 launch_preview_subproc (s);
2966 s->subproc_timer_id = 0;
2967 return FALSE; /* do not re-execute timer */
2971 /* Call this when you think you might want a preview process running.
2972 It will set a timer that will actually launch that program a second
2973 from now, if you haven't changed your mind (to avoid double-click
2974 spazzing, etc.) `cmd' may be null meaning "no process".
2977 schedule_preview (state *s, const char *cmd)
2979 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
2984 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
2986 fprintf (stderr, "%s: scheduling preview death\n", blurb());
2989 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
2990 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
2992 if (s->subproc_timer_id)
2993 gtk_timeout_remove (s->subproc_timer_id);
2994 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
2998 /* Called from a timer:
2999 Checks to see if the subproc that should be running, actually is.
3002 check_subproc_timer (gpointer data)
3004 state *s = (state *) data;
3005 Bool again_p = True;
3007 if (s->running_preview_error_p || /* already dead */
3008 s->running_preview_pid <= 0)
3016 status = kill (s->running_preview_pid, 0);
3017 if (status < 0 && errno == ESRCH)
3018 s->running_preview_error_p = True;
3022 char *ss = subproc_pretty_name (s);
3023 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3024 s->running_preview_pid, ss,
3025 (s->running_preview_error_p ? "dead" : "alive"));
3029 if (s->running_preview_error_p)
3031 clear_preview_window (s);
3036 /* Otherwise, it's currently alive. We might be checking again, or we
3037 might be satisfied. */
3039 if (--s->subproc_check_countdown <= 0)
3043 return TRUE; /* re-execute timer */
3046 s->subproc_check_timer_id = 0;
3047 s->subproc_check_countdown = 0;
3048 return FALSE; /* do not re-execute timer */
3053 /* Call this just after launching a subprocess.
3054 This sets a timer that will, five times a second for two seconds,
3055 check whether the program is still running. The assumption here
3056 is that if the process didn't stay up for more than a couple of
3057 seconds, then either the program doesn't exist, or it doesn't
3058 take a -window-id argument.
3061 schedule_preview_check (state *s)
3067 fprintf (stderr, "%s: scheduling check\n", blurb());
3069 if (s->subproc_check_timer_id)
3070 gtk_timeout_remove (s->subproc_check_timer_id);
3071 s->subproc_check_timer_id =
3072 gtk_timeout_add (1000 / ticks,
3073 check_subproc_timer, (gpointer) s);
3074 s->subproc_check_countdown = ticks * seconds;
3079 screen_blanked_p (void)
3083 unsigned long nitems, bytesafter;
3085 Display *dpy = GDK_DISPLAY();
3086 Bool blanked_p = False;
3088 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3089 XA_SCREENSAVER_STATUS,
3090 0, 3, False, XA_INTEGER,
3091 &type, &format, &nitems, &bytesafter,
3092 (unsigned char **) &data)
3094 && type == XA_INTEGER
3097 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3099 if (data) free (data);
3104 /* Wake up every now and then and see if the screen is blanked.
3105 If it is, kill off the small-window demo -- no point in wasting
3106 cycles by running two screensavers at once...
3109 check_blanked_timer (gpointer data)
3111 state *s = (state *) data;
3112 Bool blanked_p = screen_blanked_p ();
3113 if (blanked_p && s->running_preview_pid)
3116 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3117 kill_preview_subproc (s);
3120 return True; /* re-execute timer */
3124 /* Setting window manager icon
3128 init_icon (GdkWindow *window)
3130 GdkBitmap *mask = 0;
3133 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3134 (gchar **) logo_50_xpm);
3136 gdk_window_set_icon (window, 0, pixmap, mask);
3140 /* The main demo-mode command loop.
3145 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3146 XrmRepresentation *type, XrmValue *value, XPointer closure)
3149 for (i = 0; quarks[i]; i++)
3151 if (bindings[i] == XrmBindTightly)
3152 fprintf (stderr, (i == 0 ? "" : "."));
3153 else if (bindings[i] == XrmBindLoosely)
3154 fprintf (stderr, "*");
3156 fprintf (stderr, " ??? ");
3157 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3160 fprintf (stderr, ": %s\n", (char *) value->addr);
3168 the_network_is_not_the_computer (state *s)
3170 Display *dpy = GDK_DISPLAY();
3171 char *rversion = 0, *ruser = 0, *rhost = 0;
3172 char *luser, *lhost;
3174 struct passwd *p = getpwuid (getuid ());
3175 const char *d = DisplayString (dpy);
3177 # if defined(HAVE_UNAME)
3179 if (uname (&uts) < 0)
3180 lhost = "<UNKNOWN>";
3182 lhost = uts.nodename;
3184 strcpy (lhost, getenv("SYS$NODE"));
3185 # else /* !HAVE_UNAME && !VMS */
3186 strcat (lhost, "<UNKNOWN>");
3187 # endif /* !HAVE_UNAME && !VMS */
3189 if (p && p->pw_name)
3194 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3196 /* Make a buffer that's big enough for a number of copies of all the
3197 strings, plus some. */
3198 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3199 (ruser ? strlen(ruser) : 0) +
3200 (rhost ? strlen(rhost) : 0) +
3207 if (!rversion || !*rversion)
3211 "The XScreenSaver daemon doesn't seem to be running\n"
3212 "on display \"%s\". Launch it now?"),
3215 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3217 /* Warn that the two processes are running as different users.
3221 "%s is running as user \"%s\" on host \"%s\".\n"
3222 "But the xscreensaver managing display \"%s\"\n"
3223 "is running as user \"%s\" on host \"%s\".\n"
3225 "Since they are different users, they won't be reading/writing\n"
3226 "the same ~/.xscreensaver file, so %s isn't\n"
3227 "going to work right.\n"
3229 "You should either re-run %s as \"%s\", or re-run\n"
3230 "xscreensaver as \"%s\".\n"
3232 "Restart the xscreensaver daemon now?\n"),
3233 blurb(), luser, lhost,
3235 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3237 blurb(), (ruser ? ruser : "???"),
3240 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3242 /* Warn that the two processes are running on different hosts.
3246 "%s is running as user \"%s\" on host \"%s\".\n"
3247 "But the xscreensaver managing display \"%s\"\n"
3248 "is running as user \"%s\" on host \"%s\".\n"
3250 "If those two machines don't share a file system (that is,\n"
3251 "if they don't see the same ~%s/.xscreensaver file) then\n"
3252 "%s won't work right.\n"
3254 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
3255 blurb(), luser, lhost,
3257 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3262 else if (!!strcmp (rversion, s->short_version))
3264 /* Warn that the version numbers don't match.
3268 "This is %s version %s.\n"
3269 "But the xscreensaver managing display \"%s\"\n"
3270 "is version %s. This could cause problems.\n"
3272 "Restart the xscreensaver daemon now?\n"),
3273 progname, s->short_version,
3280 warning_dialog (s->toplevel_widget, msg, True, 1);
3282 if (rversion) free (rversion);
3283 if (ruser) free (ruser);
3284 if (rhost) free (rhost);
3289 /* We use this error handler so that X errors are preceeded by the name
3290 of the program that generated them.
3293 demo_ehandler (Display *dpy, XErrorEvent *error)
3295 state *s = global_state_kludge; /* I hate C so much... */
3296 fprintf (stderr, "\nX error in %s:\n", blurb());
3297 XmuPrintDefaultErrorMessage (dpy, error, stderr);
3298 kill_preview_subproc (s);
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
3352 "\n\t\t [--debug] [--sync] [--no-xshm]";
3355 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3357 state *s = (state *) user_data;
3358 Boolean oi = s->initializing_p;
3359 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3360 s->initializing_p = True;
3361 eschew_gtk_lossage (label);
3362 s->initializing_p = oi;
3368 print_widget_tree (GtkWidget *w, int depth)
3371 for (i = 0; i < depth; i++)
3372 fprintf (stderr, " ");
3373 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3375 if (GTK_IS_LIST (w))
3377 for (i = 0; i < depth+1; i++)
3378 fprintf (stderr, " ");
3379 fprintf (stderr, "...list kids...\n");
3381 else if (GTK_IS_CONTAINER (w))
3383 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3386 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3394 delayed_scroll_kludge (gpointer data)
3396 state *s = (state *) data;
3397 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3398 ensure_selected_item_visible (w);
3400 /* Oh, this is just fucking lovely, too. */
3401 w = GTK_WIDGET (name_to_widget (s, "preview"));
3402 gtk_widget_hide (w);
3403 gtk_widget_show (w);
3405 return FALSE; /* do not re-execute timer */
3410 main (int argc, char **argv)
3414 saver_preferences *p;
3418 Widget toplevel_shell;
3419 char *real_progname = argv[0];
3420 char window_title[255];
3421 Bool crapplet_p = False;
3425 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
3426 textdomain (GETTEXT_PACKAGE);
3429 bindtextdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3430 # else /* ! HAVE_GTK2 */
3431 if (!setlocale (LC_ALL, ""))
3432 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
3433 # endif /* ! HAVE_GTK2 */
3435 #endif /* ENABLE_NLS */
3437 str = strrchr (real_progname, '/');
3438 if (str) real_progname = str+1;
3441 memset (s, 0, sizeof(*s));
3442 s->initializing_p = True;
3445 global_state_kludge = s; /* I hate C so much... */
3447 progname = real_progname;
3449 s->short_version = (char *) malloc (5);
3450 memcpy (s->short_version, screensaver_id + 17, 4);
3451 s->short_version [4] = 0;
3454 /* Register our error message logger for every ``log domain'' known.
3455 There's no way to do this globally, so I grepped the Gtk/Gdk sources
3456 for all of the domains that seem to be in use.
3459 const char * const domains[] = { 0,
3460 "Gtk", "Gdk", "GLib", "GModule",
3461 "GThread", "Gnome", "GnomeUI" };
3462 for (i = 0; i < countof(domains); i++)
3463 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
3466 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
3468 const char *dir = DEFAULT_ICONDIR;
3469 if (*dir) add_pixmap_directory (dir);
3473 /* This is gross, but Gtk understands --display and not -display...
3475 for (i = 1; i < argc; i++)
3476 if (argv[i][0] && argv[i][1] &&
3477 !strncmp(argv[i], "-display", strlen(argv[i])))
3478 argv[i] = "--display";
3481 /* We need to parse this arg really early... Sigh. */
3482 for (i = 1; i < argc; i++)
3484 (!strcmp(argv[i], "--crapplet") ||
3485 !strcmp(argv[i], "--capplet")))
3487 # ifdef HAVE_CRAPPLET
3490 for (j = i; j < argc; j++) /* remove it from the list */
3491 argv[j] = argv[j+1];
3494 # else /* !HAVE_CRAPPLET */
3495 fprintf (stderr, "%s: not compiled with --crapplet support\n",
3497 fprintf (stderr, "%s: %s\n", real_progname, usage);
3499 # endif /* !HAVE_CRAPPLET */
3502 (!strcmp(argv[i], "--debug") ||
3503 !strcmp(argv[i], "-debug") ||
3504 !strcmp(argv[i], "-d")))
3508 for (j = i; j < argc; j++) /* remove it from the list */
3509 argv[j] = argv[j+1];
3513 /* Let Gtk open the X connection, then initialize Xt to use that
3514 same connection. Doctor Frankenstein would be proud.
3516 # ifdef HAVE_CRAPPLET
3519 GnomeClient *client;
3520 GnomeClientFlags flags = 0;
3522 int init_results = gnome_capplet_init ("screensaver-properties",
3524 argc, argv, NULL, 0, NULL);
3526 0 upon successful initialization;
3527 1 if --init-session-settings was passed on the cmdline;
3528 2 if --ignore was passed on the cmdline;
3531 So the 1 signifies just to init the settings, and quit, basically.
3532 (Meaning launch the xscreensaver daemon.)
3535 if (init_results < 0)
3538 g_error ("An initialization error occurred while "
3539 "starting xscreensaver-capplet.\n");
3541 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
3542 real_progname, init_results);
3547 client = gnome_master_client ();
3550 flags = gnome_client_get_flags (client);
3552 if (flags & GNOME_CLIENT_IS_CONNECTED)
3555 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
3556 gnome_client_get_id (client));
3559 char *session_args[20];
3561 session_args[i++] = real_progname;
3562 session_args[i++] = "--capplet";
3563 session_args[i++] = "--init-session-settings";
3564 session_args[i] = 0;
3565 gnome_client_set_priority (client, 20);
3566 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
3567 gnome_client_set_restart_command (client, i, session_args);
3571 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
3574 gnome_client_flush (client);
3577 if (init_results == 1)
3579 system ("xscreensaver -nosplash &");
3585 # endif /* HAVE_CRAPPLET */
3587 gtk_init (&argc, &argv);
3591 /* We must read exactly the same resources as xscreensaver.
3592 That means we must have both the same progclass *and* progname,
3593 at least as far as the resource database is concerned. So,
3594 put "xscreensaver" in argv[0] while initializing Xt.
3596 argv[0] = "xscreensaver";
3600 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
3602 XtToolkitInitialize ();
3603 app = XtCreateApplicationContext ();
3604 dpy = GDK_DISPLAY();
3605 XtAppSetFallbackResources (app, defaults);
3606 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
3607 toplevel_shell = XtAppCreateShell (progname, progclass,
3608 applicationShellWidgetClass,
3611 dpy = XtDisplay (toplevel_shell);
3612 db = XtDatabase (dpy);
3613 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
3614 XSetErrorHandler (demo_ehandler);
3616 /* Let's just ignore these. They seem to confuse Irix Gtk... */
3617 signal (SIGPIPE, SIG_IGN);
3619 /* After doing Xt-style command-line processing, complain about any
3620 unrecognized command-line arguments.
3622 for (i = 1; i < argc; i++)
3624 char *str = argv[i];
3625 if (str[0] == '-' && str[1] == '-')
3627 if (!strcmp (str, "-prefs"))
3629 else if (crapplet_p)
3630 /* There are lots of random args that we don't care about when we're
3631 started as a crapplet, so just ignore unknown args in that case. */
3635 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname, argv[i]);
3636 fprintf (stderr, "%s: %s\n", real_progname, usage);
3641 /* Load the init file, which may end up consulting the X resource database
3642 and the site-wide app-defaults file. Note that at this point, it's
3643 important that `progname' be "xscreensaver", rather than whatever
3648 initialize_sort_map (s);
3650 /* Now that Xt has been initialized, and the resources have been read,
3651 we can set our `progname' variable to something more in line with
3654 progname = real_progname;
3658 /* Print out all the resources we read. */
3660 XrmName name = { 0 };
3661 XrmClass class = { 0 };
3663 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
3669 /* Intern the atoms that xscreensaver_command() needs.
3671 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
3672 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
3673 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
3674 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
3675 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
3676 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
3677 XA_SELECT = XInternAtom (dpy, "SELECT", False);
3678 XA_DEMO = XInternAtom (dpy, "DEMO", False);
3679 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
3680 XA_BLANK = XInternAtom (dpy, "BLANK", False);
3681 XA_LOCK = XInternAtom (dpy, "LOCK", False);
3682 XA_EXIT = XInternAtom (dpy, "EXIT", False);
3683 XA_RESTART = XInternAtom (dpy, "RESTART", False);
3686 /* Create the window and all its widgets.
3688 s->base_widget = create_xscreensaver_demo ();
3689 s->popup_widget = create_xscreensaver_settings_dialog ();
3690 s->toplevel_widget = s->base_widget;
3693 /* Set the main window's title. */
3695 char *v = (char *) strdup(strchr(screensaver_id, ' '));
3696 char *s1, *s2, *s3, *s4;
3697 s1 = (char *) strchr(v, ' '); s1++;
3698 s2 = (char *) strchr(s1, ' ');
3699 s3 = (char *) strchr(v, '('); s3++;
3700 s4 = (char *) strchr(s3, ')');
3703 sprintf (window_title, "%.50s %.50s, %.50s", progclass, s1, s3);
3704 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
3705 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
3709 /* Adjust the (invisible) notebooks on the popup dialog... */
3711 GtkNotebook *notebook =
3712 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
3713 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
3717 gtk_widget_hide (std);
3718 # else /* !HAVE_XML */
3719 /* Make the advanced page be the only one available. */
3720 gtk_widget_set_sensitive (std, False);
3721 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
3722 gtk_widget_hide (std);
3724 # endif /* !HAVE_XML */
3726 gtk_notebook_set_page (notebook, page);
3727 gtk_notebook_set_show_tabs (notebook, False);
3730 /* Various other widget initializations...
3732 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
3733 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
3735 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
3736 GTK_SIGNAL_FUNC (wm_popup_close_cb),
3739 populate_hack_list (s);
3740 populate_prefs_page (s);
3741 sensitize_demo_widgets (s, False);
3742 fix_text_entry_sizes (s);
3743 scroll_to_current_hack (s);
3745 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
3746 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
3749 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
3750 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
3752 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
3753 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
3757 /* Hook up callbacks to the items on the mode menu. */
3759 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
3760 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
3761 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
3762 for (; kids; kids = kids->next)
3763 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
3764 GTK_SIGNAL_FUNC (mode_menu_item_cb),
3769 /* Handle the -prefs command-line argument. */
3772 GtkNotebook *notebook =
3773 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
3774 gtk_notebook_set_page (notebook, 1);
3777 # ifdef HAVE_CRAPPLET
3781 GtkWidget *outer_vbox;
3783 gtk_widget_hide (s->toplevel_widget);
3785 capplet = capplet_widget_new ();
3787 /* Make there be a "Close" button instead of "OK" and "Cancel" */
3788 # ifdef HAVE_CRAPPLET_IMMEDIATE
3789 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
3790 # endif /* HAVE_CRAPPLET_IMMEDIATE */
3792 /* In crapplet-mode, take off the menubar. */
3793 gtk_widget_hide (name_to_widget (s, "menubar"));
3795 /* Reparent our top-level container to be a child of the capplet
3798 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
3799 gtk_widget_ref (outer_vbox);
3800 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
3802 GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
3803 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
3805 /* Find the window above us, and set the title and close handler. */
3807 GtkWidget *window = capplet;
3808 while (window && !GTK_IS_WINDOW (window))
3809 window = window->parent;
3812 gtk_window_set_title (GTK_WINDOW (window), window_title);
3813 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
3814 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
3819 s->toplevel_widget = capplet;
3821 # endif /* HAVE_CRAPPLET */
3824 gtk_widget_show (s->toplevel_widget);
3825 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
3826 hack_environment (s);
3827 fix_preview_visual (s);
3829 /* Realize page zero, so that we can diddle the scrollbar when the
3830 user tabs back to it -- otherwise, the current hack isn't scrolled
3831 to the first time they tab back there, when started with "-prefs".
3832 (Though it is if they then tab away, and back again.)
3834 #### Bah! This doesn't work. Gtk eats my ass! Someone who
3835 #### understands this crap, explain to me how to make this work.
3837 gtk_widget_realize (name_to_widget (s, "demos_table"));
3840 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
3843 /* Issue any warnings about the running xscreensaver daemon. */
3844 the_network_is_not_the_computer (s);
3847 /* Run the Gtk event loop, and not the Xt event loop. This means that
3848 if there were Xt timers or fds registered, they would never get serviced,
3849 and if there were any Xt widgets, they would never have events delivered.
3850 Fortunately, we're using Gtk for all of the UI, and only initialized
3851 Xt so that we could process the command line and use the X resource
3854 s->initializing_p = False;
3856 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
3857 after we start up. Otherwise, it always appears scrolled to the top
3858 when in crapplet-mode. */
3859 gtk_timeout_add (500, delayed_scroll_kludge, s);
3863 /* Load every configurator in turn, to scan them for errors all at once. */
3866 for (i = 0; i < p->screenhacks_count; i++)
3868 screenhack *hack = p->screenhacks[s->hack_number_to_list_elt[i]];
3869 conf_data *d = load_configurator (hack->command, False);
3870 if (d) free_conf_data (d);
3876 # ifdef HAVE_CRAPPLET
3878 capplet_gtk_main ();
3880 # endif /* HAVE_CRAPPLET */
3883 kill_preview_subproc (s);
3887 #endif /* HAVE_GTK -- whole file */