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 # define FMT_MINUTES(NAME,N) \
1878 sprintf (str, "%d", ((N) + 59) / (60 * 1000)); \
1879 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, (NAME))), str)
1881 # define FMT_SECONDS(NAME,N) \
1882 sprintf (str, "%d", ((N) / 1000)); \
1883 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, (NAME))), str)
1885 FMT_MINUTES ("timeout_spinbutton", p->timeout);
1886 FMT_MINUTES ("cycle_spinbutton", p->cycle);
1887 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
1888 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
1889 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
1890 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
1891 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
1896 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
1897 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
1900 TOGGLE_ACTIVE ("lock_button", p->lock_p);
1901 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
1902 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
1903 TOGGLE_ACTIVE ("splash_button", p->splash_p);
1904 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
1905 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
1906 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
1907 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
1908 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
1909 TOGGLE_ACTIVE ("fade_button", p->fade_p);
1910 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
1912 # undef TOGGLE_ACTIVE
1914 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1915 (p->image_directory ? p->image_directory : ""));
1916 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
1918 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
1921 /* Map the `saver_mode' enum to mode menu to values. */
1923 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1926 for (i = 0; i < countof(mode_menu_order); i++)
1927 if (mode_menu_order[i] == p->mode)
1929 gtk_option_menu_set_history (opt, i);
1930 update_list_sensitivity (s);
1934 Bool found_any_writable_cells = False;
1935 Bool dpms_supported = False;
1937 Display *dpy = GDK_DISPLAY();
1938 int nscreens = ScreenCount(dpy);
1940 for (i = 0; i < nscreens; i++)
1942 Screen *s = ScreenOfDisplay (dpy, i);
1943 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1945 found_any_writable_cells = True;
1950 #ifdef HAVE_XF86VMODE_GAMMA
1951 found_any_writable_cells = True; /* if we can gamma fade, go for it */
1954 #ifdef HAVE_DPMS_EXTENSION
1956 int op = 0, event = 0, error = 0;
1957 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
1958 dpms_supported = True;
1960 #endif /* HAVE_DPMS_EXTENSION */
1963 # define SENSITIZE(NAME,SENSITIVEP) \
1964 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
1966 /* Blanking and Locking
1968 SENSITIZE ("lock_spinbutton", p->lock_p);
1969 SENSITIZE ("lock_mlabel", p->lock_p);
1973 SENSITIZE ("dpms_frame", dpms_supported);
1974 SENSITIZE ("dpms_button", dpms_supported);
1975 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
1976 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
1977 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
1978 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
1979 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
1980 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
1981 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
1982 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
1983 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
1987 SENSITIZE ("cmap_frame", found_any_writable_cells);
1988 SENSITIZE ("install_button", found_any_writable_cells);
1989 SENSITIZE ("fade_button", found_any_writable_cells);
1990 SENSITIZE ("unfade_button", found_any_writable_cells);
1992 SENSITIZE ("fade_label", (found_any_writable_cells &&
1993 (p->fade_p || p->unfade_p)));
1994 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
1995 (p->fade_p || p->unfade_p)));
2003 populate_popup_window (state *s)
2005 saver_preferences *p = &s->prefs;
2006 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2007 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2008 int list_elt = selected_list_element (s);
2009 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2010 ? s->list_elt_to_hack_number[list_elt]
2012 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2013 char *doc_string = 0;
2018 free_conf_data (s->cdata);
2024 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2025 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2026 s->cdata = load_configurator (cmd_line, s->debug_p);
2027 if (s->cdata && s->cdata->widget)
2028 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget, TRUE, TRUE, 0);
2031 doc_string = (s->cdata
2032 ? s->cdata->description
2034 # else /* !HAVE_XML */
2035 doc_string = "Descriptions not available: no XML support compiled in.";
2036 # endif /* !HAVE_XML */
2038 gtk_label_set_text (doc, (doc_string
2040 : "No description available."));
2045 sensitize_demo_widgets (state *s, Bool sensitive_p)
2047 const char *names1[] = { "demo", "settings" };
2048 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2049 "visual", "visual_combo" };
2051 for (i = 0; i < countof(names1); i++)
2053 GtkWidget *w = name_to_widget (s, names1[i]);
2054 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2056 for (i = 0; i < countof(names2); i++)
2058 GtkWidget *w = name_to_widget (s, names2[i]);
2059 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2064 /* Even though we've given these text fields a maximum number of characters,
2065 their default size is still about 30 characters wide -- so measure out
2066 a string in their font, and resize them to just fit that.
2069 fix_text_entry_sizes (state *s)
2071 const char * const spinbuttons[] = {
2072 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2073 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2074 "dpms_off_spinbutton",
2075 "-fade_spinbutton" };
2080 for (i = 0; i < countof(spinbuttons); i++)
2082 const char *n = spinbuttons[i];
2084 while (*n == '-') n++, cols--;
2085 w = GTK_WIDGET (name_to_widget (s, n));
2086 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2087 gtk_widget_set_usize (w, width, -2);
2090 /* Now fix the width of the combo box.
2092 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2093 w = GTK_COMBO (w)->entry;
2094 width = gdk_string_width (w->style->font, "PseudoColor___");
2095 gtk_widget_set_usize (w, width, -2);
2097 /* Now fix the width of the file entry text.
2099 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2100 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2101 gtk_widget_set_usize (w, width, -2);
2103 /* Now fix the width of the command line text.
2105 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2106 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2107 gtk_widget_set_usize (w, width, -2);
2109 /* Now fix the height of the list.
2114 int leading = 3; /* approximate is ok... */
2116 w = GTK_WIDGET (name_to_widget (s, "list"));
2117 height = w->style->font->ascent + w->style->font->descent;
2120 height += border * 2;
2121 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2122 gtk_widget_set_usize (w, -2, height);
2129 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2132 static char *up_arrow_xpm[] = {
2155 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2156 the end of the array (Gtk 1.2.5.) */
2157 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2158 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2161 static char *down_arrow_xpm[] = {
2184 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2185 the end of the array (Gtk 1.2.5.) */
2186 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2187 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2191 pixmapify_button (state *s, int down_p)
2195 GtkWidget *pixmapwid;
2199 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2200 style = gtk_widget_get_style (w);
2202 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2203 &style->bg[GTK_STATE_NORMAL],
2205 ? (gchar **) down_arrow_xpm
2206 : (gchar **) up_arrow_xpm));
2207 pixmapwid = gtk_pixmap_new (pixmap, mask);
2208 gtk_widget_show (pixmapwid);
2209 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2210 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2214 map_next_button_cb (GtkWidget *w, gpointer user_data)
2216 state *s = (state *) user_data;
2217 pixmapify_button (s, 1);
2221 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2223 state *s = (state *) user_data;
2224 pixmapify_button (s, 0);
2229 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2233 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2234 GtkAllocation *allocation,
2238 GtkWidgetAuxInfo *aux_info;
2240 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2242 aux_info->width = allocation->width;
2243 aux_info->height = -2;
2247 gtk_widget_size_request (label, &req);
2251 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2254 eschew_gtk_lossage (GtkLabel *label)
2256 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2257 aux_info->width = GTK_WIDGET (label)->allocation.width;
2258 aux_info->height = -2;
2262 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2264 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2265 you_are_not_a_unique_or_beautiful_snowflake,
2268 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2270 gtk_widget_queue_resize (GTK_WIDGET (label));
2275 populate_demo_window (state *s, int list_elt)
2277 saver_preferences *p = &s->prefs;
2280 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2281 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2282 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2283 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2284 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2286 if (p->mode == BLANK_ONLY)
2289 pretty_name = strdup ("Blank Screen");
2290 schedule_preview (s, 0);
2292 else if (p->mode == DONT_BLANK)
2295 pretty_name = strdup ("Screen Saver Disabled");
2296 schedule_preview (s, 0);
2300 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2301 ? s->list_elt_to_hack_number[list_elt]
2303 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2307 ? strdup (hack->name)
2308 : make_hack_name (hack->command))
2312 schedule_preview (s, hack->command);
2314 schedule_preview (s, 0);
2318 pretty_name = strdup ("Preview");
2320 gtk_frame_set_label (frame1, pretty_name);
2321 gtk_frame_set_label (frame2, pretty_name);
2323 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2324 gtk_entry_set_position (cmd, 0);
2328 sprintf (title, "%s: %.100s Settings",
2329 progclass, (pretty_name ? pretty_name : "???"));
2330 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2333 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2335 ? (hack->visual && *hack->visual
2340 sensitize_demo_widgets (s, (hack ? True : False));
2342 if (pretty_name) free (pretty_name);
2344 ensure_selected_item_visible (list);
2346 s->_selected_list_element = list_elt;
2351 widget_deleter (GtkWidget *widget, gpointer data)
2353 /* #### Well, I want to destroy these widgets, but if I do that, they get
2354 referenced again, and eventually I get a SEGV. So instead of
2355 destroying them, I'll just hide them, and leak a bunch of memory
2356 every time the disk file changes. Go go go Gtk!
2358 #### Ok, that's a lie, I get a crash even if I just hide the widget
2359 and don't ever delete it. Fuck!
2362 gtk_widget_destroy (widget);
2364 gtk_widget_hide (widget);
2369 static char **sort_hack_cmp_names_kludge;
2371 sort_hack_cmp (const void *a, const void *b)
2376 return strcmp (sort_hack_cmp_names_kludge[*(int *) a],
2377 sort_hack_cmp_names_kludge[*(int *) b]);
2382 initialize_sort_map (state *s)
2384 saver_preferences *p = &s->prefs;
2387 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2388 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2390 s->list_elt_to_hack_number = (int *)
2391 calloc (sizeof(int), p->screenhacks_count + 1);
2392 s->hack_number_to_list_elt = (int *)
2393 calloc (sizeof(int), p->screenhacks_count + 1);
2395 /* Initialize table to 1:1 mapping */
2396 for (i = 0; i < p->screenhacks_count; i++)
2397 s->list_elt_to_hack_number[i] = i;
2399 /* Generate list of names (once)
2401 sort_hack_cmp_names_kludge = (char **)
2402 calloc (sizeof(char *), p->screenhacks_count);
2403 for (i = 0; i < p->screenhacks_count; i++)
2405 screenhack *hack = p->screenhacks[i];
2406 char *name = (hack->name && *hack->name
2407 ? strdup (hack->name)
2408 : make_hack_name (hack->command));
2410 for (str = name; *str; str++)
2411 *str = tolower(*str);
2412 sort_hack_cmp_names_kludge[i] = name;
2415 /* Sort alphabetically
2417 qsort (s->list_elt_to_hack_number,
2418 p->screenhacks_count,
2419 sizeof(*s->list_elt_to_hack_number),
2424 for (i = 0; i < p->screenhacks_count; i++)
2425 free (sort_hack_cmp_names_kludge[i]);
2426 free (sort_hack_cmp_names_kludge);
2427 sort_hack_cmp_names_kludge = 0;
2429 /* Build inverse table */
2430 for (i = 0; i < p->screenhacks_count; i++)
2431 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2436 maybe_reload_init_file (state *s)
2438 saver_preferences *p = &s->prefs;
2441 static Bool reentrant_lock = False;
2442 if (reentrant_lock) return 0;
2443 reentrant_lock = True;
2445 if (init_file_changed_p (p))
2447 const char *f = init_file_name();
2452 if (!f || !*f) return 0;
2453 b = (char *) malloc (strlen(f) + 1024);
2456 "file \"%s\" has changed, reloading.\n",
2458 warning_dialog (s->toplevel_widget, b, False, 100);
2462 initialize_sort_map (s);
2464 list_elt = selected_list_element (s);
2465 list = GTK_LIST (name_to_widget (s, "list"));
2466 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
2467 populate_hack_list (s);
2468 force_list_select_item (s, list, list_elt, True);
2469 populate_prefs_page (s);
2470 populate_demo_window (s, list_elt);
2471 ensure_selected_item_visible (GTK_WIDGET (list));
2476 reentrant_lock = False;
2482 /* Making the preview window have the right X visual (so that GL works.)
2485 static Visual *get_best_gl_visual (state *);
2488 x_visual_to_gdk_visual (Visual *xv)
2490 GList *gvs = gdk_list_visuals();
2491 if (!xv) return gdk_visual_get_system();
2492 for (; gvs; gvs = gvs->next)
2494 GdkVisual *gv = (GdkVisual *) gvs->data;
2495 if (xv == GDK_VISUAL_XVISUAL (gv))
2498 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
2499 blurb(), (unsigned long) xv->visualid);
2504 clear_preview_window (state *s)
2509 if (!s->toplevel_widget) return; /* very early */
2510 p = name_to_widget (s, "preview");
2513 if (!window) return;
2515 /* Flush the widget background down into the window, in case a subproc
2517 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
2518 gdk_window_clear (window);
2520 if (s->running_preview_error_p)
2522 const char * const lines[] = { "No Preview", "Available" };
2523 int lh = p->style->font->ascent + p->style->font->descent;
2526 gdk_window_get_size (window, &w, &h);
2527 y = (h - (lh * countof(lines))) / 2;
2528 y += p->style->font->ascent;
2529 for (i = 0; i < countof(lines); i++)
2531 int sw = gdk_string_width (p->style->font, lines[i]);
2532 int x = (w - sw) / 2;
2533 gdk_draw_string (window, p->style->font,
2534 p->style->fg_gc[GTK_STATE_NORMAL],
2542 /* Is there a GDK way of doing this? */
2543 XSync (GDK_DISPLAY(), False);
2548 fix_preview_visual (state *s)
2550 GtkWidget *widget = name_to_widget (s, "preview");
2551 Visual *xvisual = get_best_gl_visual (s);
2552 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
2553 GdkVisual *dvisual = gdk_visual_get_system();
2554 GdkColormap *cmap = (visual == dvisual
2555 ? gdk_colormap_get_system ()
2556 : gdk_colormap_new (visual, False));
2559 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
2560 (visual == dvisual ? "default" : "non-default"),
2561 (xvisual ? (unsigned long) xvisual->visualid : 0L));
2563 if (!GTK_WIDGET_REALIZED (widget) ||
2564 gtk_widget_get_visual (widget) != visual)
2566 gtk_widget_unrealize (widget);
2567 gtk_widget_set_visual (widget, visual);
2568 gtk_widget_set_colormap (widget, cmap);
2569 gtk_widget_realize (widget);
2572 /* Set the Widget colors to be white-on-black. */
2574 GdkWindow *window = widget->window;
2575 GtkStyle *style = gtk_style_copy (widget->style);
2576 GdkColormap *cmap = gtk_widget_get_colormap (widget);
2577 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
2578 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
2579 GdkGC *fgc = gdk_gc_new(window);
2580 GdkGC *bgc = gdk_gc_new(window);
2581 if (!gdk_color_white (cmap, fg)) abort();
2582 if (!gdk_color_black (cmap, bg)) abort();
2583 gdk_gc_set_foreground (fgc, fg);
2584 gdk_gc_set_background (fgc, bg);
2585 gdk_gc_set_foreground (bgc, bg);
2586 gdk_gc_set_background (bgc, fg);
2587 style->fg_gc[GTK_STATE_NORMAL] = fgc;
2588 style->bg_gc[GTK_STATE_NORMAL] = fgc;
2589 gtk_widget_set_style (widget, style);
2592 gtk_widget_show (widget);
2600 subproc_pretty_name (state *s)
2602 if (s->running_preview_cmd)
2604 char *ps = strdup (s->running_preview_cmd);
2605 char *ss = strchr (ps, ' ');
2607 ss = strrchr (ps, '/');
2613 return strdup ("???");
2618 reap_zombies (state *s)
2620 int wait_status = 0;
2622 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
2626 if (pid == s->running_preview_pid)
2628 char *ss = subproc_pretty_name (s);
2629 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(), pid, ss);
2633 fprintf (stderr, "%s: pid %lu died\n", blurb(), pid);
2639 /* Mostly lifted from driver/subprocs.c */
2641 get_best_gl_visual (state *s)
2643 Display *dpy = GDK_DISPLAY();
2652 av[ac++] = "xscreensaver-gl-helper";
2657 perror ("error creating pipe:");
2664 switch ((int) (forked = fork ()))
2668 sprintf (buf, "%s: couldn't fork", blurb());
2676 close (in); /* don't need this one */
2677 close (ConnectionNumber (dpy)); /* close display fd */
2679 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
2681 perror ("could not dup() a new stdout:");
2685 execvp (av[0], av); /* shouldn't return. */
2687 if (errno != ENOENT)
2689 /* Ignore "no such file or directory" errors, unless verbose.
2690 Issue all other exec errors, though. */
2691 sprintf (buf, "%s: running %s", blurb(), av[0]);
2694 exit (1); /* exits fork */
2700 int wait_status = 0;
2702 FILE *f = fdopen (in, "r");
2703 unsigned long v = 0;
2706 close (out); /* don't need this one */
2709 fgets (buf, sizeof(buf)-1, f);
2712 /* Wait for the child to die. */
2713 waitpid (-1, &wait_status, 0);
2715 if (1 == sscanf (buf, "0x%x %c", &v, &c))
2721 fprintf (stderr, "%s: %s did not report a GL visual!\n",
2727 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
2729 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
2730 blurb(), av[0], result);
2742 kill_preview_subproc (state *s)
2744 s->running_preview_error_p = False;
2747 clear_preview_window (s);
2749 if (s->subproc_check_timer_id)
2751 gtk_timeout_remove (s->subproc_check_timer_id);
2752 s->subproc_check_timer_id = 0;
2753 s->subproc_check_countdown = 0;
2756 if (s->running_preview_pid)
2758 int status = kill (s->running_preview_pid, SIGTERM);
2759 char *ss = subproc_pretty_name (s);
2766 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
2767 blurb(), s->running_preview_pid, ss);
2772 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
2773 blurb(), s->running_preview_pid, ss);
2777 else if (s->debug_p)
2778 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
2779 s->running_preview_pid, ss);
2782 s->running_preview_pid = 0;
2783 if (s->running_preview_cmd) free (s->running_preview_cmd);
2784 s->running_preview_cmd = 0;
2791 /* Immediately and unconditionally launches the given process,
2792 after appending the -window-id option; sets running_preview_pid.
2795 launch_preview_subproc (state *s)
2797 saver_preferences *p = &s->prefs;
2801 const char *cmd = s->desired_preview_cmd;
2803 GtkWidget *pr = name_to_widget (s, "preview");
2804 GdkWindow *window = pr->window;
2806 s->running_preview_error_p = False;
2808 if (s->preview_suppressed_p)
2810 kill_preview_subproc (s);
2814 new_cmd = malloc (strlen (cmd) + 40);
2816 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
2819 /* No window id? No command to run. */
2825 strcpy (new_cmd, cmd);
2826 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X", id);
2829 kill_preview_subproc (s);
2832 s->running_preview_error_p = True;
2833 clear_preview_window (s);
2837 switch ((int) (forked = fork ()))
2842 sprintf (buf, "%s: couldn't fork", blurb());
2844 s->running_preview_error_p = True;
2849 close (ConnectionNumber (GDK_DISPLAY()));
2851 usleep (250000); /* pause for 1/4th second before launching, to give
2852 the previous program time to die and flush its X
2853 buffer, so we don't get leftover turds on the
2856 exec_command (p->shell, new_cmd, p->nice_inferior);
2857 /* Don't bother printing an error message when we are unable to
2858 exec subprocesses; we handle that by polling the pid later. */
2859 exit (1); /* exits child fork */
2864 if (s->running_preview_cmd) free (s->running_preview_cmd);
2865 s->running_preview_cmd = strdup (s->desired_preview_cmd);
2866 s->running_preview_pid = forked;
2870 char *ss = subproc_pretty_name (s);
2871 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(), forked, ss);
2878 schedule_preview_check (s);
2882 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
2885 hack_environment (state *s)
2887 static const char *def_path =
2888 # ifdef DEFAULT_PATH_PREFIX
2889 DEFAULT_PATH_PREFIX;
2894 Display *dpy = GDK_DISPLAY();
2895 const char *odpy = DisplayString (dpy);
2896 char *ndpy = (char *) malloc(strlen(odpy) + 20);
2897 strcpy (ndpy, "DISPLAY=");
2898 strcat (ndpy, odpy);
2903 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
2905 if (def_path && *def_path)
2907 const char *opath = getenv("PATH");
2908 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
2909 strcpy (npath, "PATH=");
2910 strcat (npath, def_path);
2911 strcat (npath, ":");
2912 strcat (npath, opath);
2918 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
2923 /* Called from a timer:
2924 Launches the currently-chosen subprocess, if it's not already running.
2925 If there's a different process running, kills it.
2928 update_subproc_timer (gpointer data)
2930 state *s = (state *) data;
2931 if (! s->desired_preview_cmd)
2932 kill_preview_subproc (s);
2933 else if (!s->running_preview_cmd ||
2934 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
2935 launch_preview_subproc (s);
2937 s->subproc_timer_id = 0;
2938 return FALSE; /* do not re-execute timer */
2942 /* Call this when you think you might want a preview process running.
2943 It will set a timer that will actually launch that program a second
2944 from now, if you haven't changed your mind (to avoid double-click
2945 spazzing, etc.) `cmd' may be null meaning "no process".
2948 schedule_preview (state *s, const char *cmd)
2950 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
2955 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
2957 fprintf (stderr, "%s: scheduling preview death\n", blurb());
2960 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
2961 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
2963 if (s->subproc_timer_id)
2964 gtk_timeout_remove (s->subproc_timer_id);
2965 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
2969 /* Called from a timer:
2970 Checks to see if the subproc that should be running, actually is.
2973 check_subproc_timer (gpointer data)
2975 state *s = (state *) data;
2976 Bool again_p = True;
2978 if (s->running_preview_error_p || /* already dead */
2979 s->running_preview_pid <= 0)
2987 status = kill (s->running_preview_pid, 0);
2988 if (status < 0 && errno == ESRCH)
2989 s->running_preview_error_p = True;
2993 char *ss = subproc_pretty_name (s);
2994 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
2995 s->running_preview_pid, ss,
2996 (s->running_preview_error_p ? "dead" : "alive"));
3000 if (s->running_preview_error_p)
3002 clear_preview_window (s);
3007 /* Otherwise, it's currently alive. We might be checking again, or we
3008 might be satisfied. */
3010 if (--s->subproc_check_countdown <= 0)
3014 return TRUE; /* re-execute timer */
3017 s->subproc_check_timer_id = 0;
3018 s->subproc_check_countdown = 0;
3019 return FALSE; /* do not re-execute timer */
3024 /* Call this just after launching a subprocess.
3025 This sets a timer that will, five times a second for two seconds,
3026 check whether the program is still running. The assumption here
3027 is that if the process didn't stay up for more than a couple of
3028 seconds, then either the program doesn't exist, or it doesn't
3029 take a -window-id argument.
3032 schedule_preview_check (state *s)
3038 fprintf (stderr, "%s: scheduling check\n", blurb());
3040 if (s->subproc_check_timer_id)
3041 gtk_timeout_remove (s->subproc_check_timer_id);
3042 s->subproc_check_timer_id =
3043 gtk_timeout_add (1000 / ticks,
3044 check_subproc_timer, (gpointer) s);
3045 s->subproc_check_countdown = ticks * seconds;
3050 screen_blanked_p (void)
3054 unsigned long nitems, bytesafter;
3056 Display *dpy = GDK_DISPLAY();
3057 Bool blanked_p = False;
3059 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3060 XA_SCREENSAVER_STATUS,
3061 0, 3, False, XA_INTEGER,
3062 &type, &format, &nitems, &bytesafter,
3063 (unsigned char **) &data)
3065 && type == XA_INTEGER
3068 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3070 if (data) free (data);
3075 /* Wake up every now and then and see if the screen is blanked.
3076 If it is, kill off the small-window demo -- no point in wasting
3077 cycles by running two screensavers at once...
3080 check_blanked_timer (gpointer data)
3082 state *s = (state *) data;
3083 Bool blanked_p = screen_blanked_p ();
3084 if (blanked_p && s->running_preview_pid)
3087 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3088 kill_preview_subproc (s);
3091 return True; /* re-execute timer */
3095 /* Setting window manager icon
3099 init_icon (GdkWindow *window)
3101 GdkBitmap *mask = 0;
3104 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3105 (gchar **) logo_50_xpm);
3107 gdk_window_set_icon (window, 0, pixmap, mask);
3111 /* The main demo-mode command loop.
3116 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3117 XrmRepresentation *type, XrmValue *value, XPointer closure)
3120 for (i = 0; quarks[i]; i++)
3122 if (bindings[i] == XrmBindTightly)
3123 fprintf (stderr, (i == 0 ? "" : "."));
3124 else if (bindings[i] == XrmBindLoosely)
3125 fprintf (stderr, "*");
3127 fprintf (stderr, " ??? ");
3128 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3131 fprintf (stderr, ": %s\n", (char *) value->addr);
3139 the_network_is_not_the_computer (state *s)
3141 Display *dpy = GDK_DISPLAY();
3142 char *rversion, *ruser, *rhost;
3143 char *luser, *lhost;
3145 struct passwd *p = getpwuid (getuid ());
3146 const char *d = DisplayString (dpy);
3148 # if defined(HAVE_UNAME)
3150 if (uname (&uts) < 0)
3151 lhost = "<UNKNOWN>";
3153 lhost = uts.nodename;
3155 strcpy (lhost, getenv("SYS$NODE"));
3156 # else /* !HAVE_UNAME && !VMS */
3157 strcat (lhost, "<UNKNOWN>");
3158 # endif /* !HAVE_UNAME && !VMS */
3160 if (p && p->pw_name)
3165 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3167 /* Make a buffer that's big enough for a number of copies of all the
3168 strings, plus some. */
3169 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3170 (ruser ? strlen(ruser) : 0) +
3171 (rhost ? strlen(rhost) : 0) +
3178 if (!rversion || !*rversion)
3182 "The XScreenSaver daemon doesn't seem to be running\n"
3183 "on display \"%s\". Launch it now?",
3186 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3188 /* Warn that the two processes are running as different users.
3192 "%s is running as user \"%s\" on host \"%s\".\n"
3193 "But the xscreensaver managing display \"%s\"\n"
3194 "is running as user \"%s\" on host \"%s\".\n"
3196 "Since they are different users, they won't be reading/writing\n"
3197 "the same ~/.xscreensaver file, so %s isn't\n"
3198 "going to work right.\n"
3200 "You should either re-run %s as \"%s\", or re-run\n"
3201 "xscreensaver as \"%s\".\n"
3203 "Restart the xscreensaver daemon now?\n",
3204 blurb(), luser, lhost,
3206 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3208 blurb(), (ruser ? ruser : "???"),
3211 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3213 /* Warn that the two processes are running on different hosts.
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 "If those two machines don't share a file system (that is,\n"
3222 "if they don't see the same ~%s/.xscreensaver file) then\n"
3223 "%s won't work right.\n"
3225 "Restart the daemon on \"%s\" as \"%s\" now?\n",
3226 blurb(), luser, lhost,
3228 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3233 else if (!!strcmp (rversion, s->short_version))
3235 /* Warn that the version numbers don't match.
3239 "This is %s version %s.\n"
3240 "But the xscreensaver managing display \"%s\"\n"
3241 "is version %s. This could cause problems.\n"
3243 "Restart the xscreensaver daemon now?\n",
3244 blurb(), s->short_version,
3251 warning_dialog (s->toplevel_widget, msg, True, 1);
3257 /* We use this error handler so that X errors are preceeded by the name
3258 of the program that generated them.
3261 demo_ehandler (Display *dpy, XErrorEvent *error)
3263 state *s = global_state_kludge; /* I hate C so much... */
3264 fprintf (stderr, "\nX error in %s:\n", blurb());
3265 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
3267 kill_preview_subproc (s);
3271 fprintf (stderr, " (nonfatal.)\n");
3276 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3277 of the program that generated them; and also that we can ignore one
3278 particular bogus error message that Gdk madly spews.
3281 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3282 const gchar *message, gpointer user_data)
3284 /* Ignore the message "Got event for unknown window: 0x...".
3285 Apparently some events are coming in for the xscreensaver window
3286 (presumably reply events related to the ClientMessage) and Gdk
3287 feels the need to complain about them. So, just suppress any
3288 messages that look like that one.
3290 if (strstr (message, "unknown window"))
3293 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3294 (log_domain ? log_domain : progclass),
3295 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3296 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3297 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3298 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3299 log_level == G_LOG_LEVEL_INFO ? "info" :
3300 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3302 ((!*message || message[strlen(message)-1] != '\n')
3307 static char *defaults[] = {
3308 #include "XScreenSaver_ad.h"
3313 #ifdef HAVE_CRAPPLET
3314 static struct poptOption crapplet_options[] = {
3315 {NULL, '\0', 0, NULL, 0}
3317 #endif /* HAVE_CRAPPLET */
3320 const char *usage = "[--display dpy] [--prefs]"
3321 # ifdef HAVE_CRAPPLET
3328 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3330 state *s = (state *) user_data;
3331 Boolean oi = s->initializing_p;
3332 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3333 s->initializing_p = True;
3334 eschew_gtk_lossage (label);
3335 s->initializing_p = oi;
3341 print_widget_tree (GtkWidget *w, int depth)
3344 for (i = 0; i < depth; i++)
3345 fprintf (stderr, " ");
3346 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3348 if (GTK_IS_LIST (w))
3350 for (i = 0; i < depth+1; i++)
3351 fprintf (stderr, " ");
3352 fprintf (stderr, "...list kids...\n");
3354 else if (GTK_IS_CONTAINER (w))
3356 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3359 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3367 delayed_scroll_kludge (gpointer data)
3369 state *s = (state *) data;
3370 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3371 ensure_selected_item_visible (w);
3373 /* Oh, this is just fucking lovely, too. */
3374 w = GTK_WIDGET (name_to_widget (s, "preview"));
3375 gtk_widget_hide (w);
3376 gtk_widget_show (w);
3378 return FALSE; /* do not re-execute timer */
3383 main (int argc, char **argv)
3387 saver_preferences *p;
3391 Widget toplevel_shell;
3392 char *real_progname = argv[0];
3393 char window_title[255];
3394 Bool crapplet_p = False;
3397 str = strrchr (real_progname, '/');
3398 if (str) real_progname = str+1;
3401 memset (s, 0, sizeof(*s));
3402 s->initializing_p = True;
3405 global_state_kludge = s; /* I hate C so much... */
3407 progname = real_progname;
3409 s->short_version = (char *) malloc (5);
3410 memcpy (s->short_version, screensaver_id + 17, 4);
3411 s->short_version [4] = 0;
3414 /* Register our error message logger for every ``log domain'' known.
3415 There's no way to do this globally, so I grepped the Gtk/Gdk sources
3416 for all of the domains that seem to be in use.
3419 const char * const domains[] = { 0,
3420 "Gtk", "Gdk", "GLib", "GModule",
3421 "GThread", "Gnome", "GnomeUI" };
3422 for (i = 0; i < countof(domains); i++)
3423 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
3426 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
3427 add_pixmap_directory (DEFAULT_ICONDIR);
3430 /* This is gross, but Gtk understands --display and not -display...
3432 for (i = 1; i < argc; i++)
3433 if (argv[i][0] && argv[i][1] &&
3434 !strncmp(argv[i], "-display", strlen(argv[i])))
3435 argv[i] = "--display";
3438 /* We need to parse this arg really early... Sigh. */
3439 for (i = 1; i < argc; i++)
3441 (!strcmp(argv[i], "--crapplet") ||
3442 !strcmp(argv[i], "--capplet")))
3444 # ifdef HAVE_CRAPPLET
3447 for (j = i; j < argc; j++) /* remove it from the list */
3448 argv[j] = argv[j+1];
3451 # else /* !HAVE_CRAPPLET */
3452 fprintf (stderr, "%s: not compiled with --crapplet support\n",
3454 fprintf (stderr, "%s: %s\n", real_progname, usage);
3456 # endif /* !HAVE_CRAPPLET */
3459 (!strcmp(argv[i], "--debug") ||
3460 !strcmp(argv[i], "-debug") ||
3461 !strcmp(argv[i], "-d")))
3465 for (j = i; j < argc; j++) /* remove it from the list */
3466 argv[j] = argv[j+1];
3470 /* Let Gtk open the X connection, then initialize Xt to use that
3471 same connection. Doctor Frankenstein would be proud.
3473 # ifdef HAVE_CRAPPLET
3476 GnomeClient *client;
3477 GnomeClientFlags flags = 0;
3479 int init_results = gnome_capplet_init ("screensaver-properties",
3481 argc, argv, NULL, 0, NULL);
3483 0 upon successful initialization;
3484 1 if --init-session-settings was passed on the cmdline;
3485 2 if --ignore was passed on the cmdline;
3488 So the 1 signifies just to init the settings, and quit, basically.
3489 (Meaning launch the xscreensaver daemon.)
3492 if (init_results < 0)
3495 g_error ("An initialization error occurred while "
3496 "starting xscreensaver-capplet.\n");
3498 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
3499 real_progname, init_results);
3504 client = gnome_master_client ();
3507 flags = gnome_client_get_flags (client);
3509 if (flags & GNOME_CLIENT_IS_CONNECTED)
3512 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
3513 gnome_client_get_id (client));
3516 char *session_args[20];
3518 session_args[i++] = real_progname;
3519 session_args[i++] = "--capplet";
3520 session_args[i++] = "--init-session-settings";
3521 session_args[i] = 0;
3522 gnome_client_set_priority (client, 20);
3523 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
3524 gnome_client_set_restart_command (client, i, session_args);
3528 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
3531 gnome_client_flush (client);
3534 if (init_results == 1)
3536 system ("xscreensaver -nosplash &");
3542 # endif /* HAVE_CRAPPLET */
3544 gtk_init (&argc, &argv);
3548 /* We must read exactly the same resources as xscreensaver.
3549 That means we must have both the same progclass *and* progname,
3550 at least as far as the resource database is concerned. So,
3551 put "xscreensaver" in argv[0] while initializing Xt.
3553 argv[0] = "xscreensaver";
3557 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
3559 XtToolkitInitialize ();
3560 app = XtCreateApplicationContext ();
3561 dpy = GDK_DISPLAY();
3562 XtAppSetFallbackResources (app, defaults);
3563 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
3564 toplevel_shell = XtAppCreateShell (progname, progclass,
3565 applicationShellWidgetClass,
3568 dpy = XtDisplay (toplevel_shell);
3569 db = XtDatabase (dpy);
3570 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
3571 XSetErrorHandler (demo_ehandler);
3573 /* Let's just ignore these. They seem to confuse Irix Gtk... */
3574 signal (SIGPIPE, SIG_IGN);
3576 /* After doing Xt-style command-line processing, complain about any
3577 unrecognized command-line arguments.
3579 for (i = 1; i < argc; i++)
3581 char *str = argv[i];
3582 if (str[0] == '-' && str[1] == '-')
3584 if (!strcmp (str, "-prefs"))
3586 else if (crapplet_p)
3587 /* There are lots of random args that we don't care about when we're
3588 started as a crapplet, so just ignore unknown args in that case. */
3592 fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]);
3593 fprintf (stderr, "%s: %s\n", real_progname, usage);
3598 /* Load the init file, which may end up consulting the X resource database
3599 and the site-wide app-defaults file. Note that at this point, it's
3600 important that `progname' be "xscreensaver", rather than whatever
3605 initialize_sort_map (s);
3607 /* Now that Xt has been initialized, and the resources have been read,
3608 we can set our `progname' variable to something more in line with
3611 progname = real_progname;
3615 /* Print out all the resources we read. */
3617 XrmName name = { 0 };
3618 XrmClass class = { 0 };
3620 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
3626 /* Intern the atoms that xscreensaver_command() needs.
3628 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
3629 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
3630 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
3631 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
3632 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
3633 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
3634 XA_SELECT = XInternAtom (dpy, "SELECT", False);
3635 XA_DEMO = XInternAtom (dpy, "DEMO", False);
3636 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
3637 XA_BLANK = XInternAtom (dpy, "BLANK", False);
3638 XA_LOCK = XInternAtom (dpy, "LOCK", False);
3639 XA_EXIT = XInternAtom (dpy, "EXIT", False);
3640 XA_RESTART = XInternAtom (dpy, "RESTART", False);
3643 /* Create the window and all its widgets.
3645 s->base_widget = create_xscreensaver_demo ();
3646 s->popup_widget = create_xscreensaver_settings_dialog ();
3647 s->toplevel_widget = s->base_widget;
3650 /* Set the main window's title. */
3652 char *v = (char *) strdup(strchr(screensaver_id, ' '));
3653 char *s1, *s2, *s3, *s4;
3654 s1 = (char *) strchr(v, ' '); s1++;
3655 s2 = (char *) strchr(s1, ' ');
3656 s3 = (char *) strchr(v, '('); s3++;
3657 s4 = (char *) strchr(s3, ')');
3660 sprintf (window_title, "%.50s %.50s, %.50s", progclass, s1, s3);
3661 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
3662 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
3666 /* Adjust the (invisible) notebooks on the popup dialog... */
3668 GtkNotebook *notebook =
3669 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
3670 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
3674 gtk_widget_hide (std);
3675 # else /* !HAVE_XML */
3676 /* Make the advanced page be the only one available. */
3677 gtk_widget_set_sensitive (std, False);
3678 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
3679 gtk_widget_hide (std);
3681 # endif /* !HAVE_XML */
3683 gtk_notebook_set_page (notebook, page);
3684 gtk_notebook_set_show_tabs (notebook, False);
3687 /* Various other widget initializations...
3689 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
3690 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
3692 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
3693 GTK_SIGNAL_FUNC (wm_popup_close_cb),
3696 populate_hack_list (s);
3697 populate_prefs_page (s);
3698 sensitize_demo_widgets (s, False);
3699 fix_text_entry_sizes (s);
3700 scroll_to_current_hack (s);
3702 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
3703 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
3706 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
3707 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
3709 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
3710 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
3714 /* Hook up callbacks to the items on the mode menu. */
3716 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
3717 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
3718 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
3719 for (; kids; kids = kids->next)
3720 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
3721 GTK_SIGNAL_FUNC (mode_menu_item_cb),
3726 /* Handle the -prefs command-line argument. */
3729 GtkNotebook *notebook =
3730 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
3731 gtk_notebook_set_page (notebook, 1);
3734 # ifdef HAVE_CRAPPLET
3738 GtkWidget *outer_vbox;
3740 gtk_widget_hide (s->toplevel_widget);
3742 capplet = capplet_widget_new ();
3744 /* Make there be a "Close" button instead of "OK" and "Cancel" */
3745 # ifdef HAVE_CRAPPLET_IMMEDIATE
3746 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
3747 # endif /* HAVE_CRAPPLET_IMMEDIATE */
3749 /* In crapplet-mode, take off the menubar. */
3750 gtk_widget_hide (name_to_widget (s, "menubar"));
3752 /* Reparent our top-level container to be a child of the capplet
3755 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
3756 gtk_widget_ref (outer_vbox);
3757 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
3759 GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
3760 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
3762 /* Find the window above us, and set the title and close handler. */
3764 GtkWidget *window = capplet;
3765 while (window && !GTK_IS_WINDOW (window))
3766 window = window->parent;
3769 gtk_window_set_title (GTK_WINDOW (window), window_title);
3770 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
3771 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
3776 s->toplevel_widget = capplet;
3778 # endif /* HAVE_CRAPPLET */
3781 gtk_widget_show (s->toplevel_widget);
3782 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
3783 hack_environment (s);
3784 fix_preview_visual (s);
3786 /* Realize page zero, so that we can diddle the scrollbar when the
3787 user tabs back to it -- otherwise, the current hack isn't scrolled
3788 to the first time they tab back there, when started with "-prefs".
3789 (Though it is if they then tab away, and back again.)
3791 #### Bah! This doesn't work. Gtk eats my ass! Someone who
3792 #### understands this crap, explain to me how to make this work.
3794 gtk_widget_realize (name_to_widget (s, "demos_table"));
3797 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
3800 /* Issue any warnings about the running xscreensaver daemon. */
3801 the_network_is_not_the_computer (s);
3804 /* Run the Gtk event loop, and not the Xt event loop. This means that
3805 if there were Xt timers or fds registered, they would never get serviced,
3806 and if there were any Xt widgets, they would never have events delivered.
3807 Fortunately, we're using Gtk for all of the UI, and only initialized
3808 Xt so that we could process the command line and use the X resource
3811 s->initializing_p = False;
3813 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
3814 after we start up. Otherwise, it always appears scrolled to the top
3815 when in crapplet-mode. */
3816 gtk_timeout_add (500, delayed_scroll_kludge, s);
3820 /* Load every configurator in turn, to scan them for errors all at once. */
3823 for (i = 0; i < p->screenhacks_count; i++)
3825 screenhack *hack = p->screenhacks[s->hack_number_to_list_elt[i]];
3826 conf_data *d = load_configurator (hack->command, False);
3827 if (d) free_conf_data (d);
3833 # ifdef HAVE_CRAPPLET
3835 capplet_gtk_main ();
3837 # endif /* HAVE_CRAPPLET */
3840 kill_preview_subproc (s);
3844 #endif /* HAVE_GTK -- whole file */