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 */
41 #ifdef HAVE_SYS_WAIT_H
42 # include <sys/wait.h> /* for waitpid() and associated macros */
46 #include <X11/Xproto.h> /* for CARD32 */
47 #include <X11/Xatom.h> /* for XA_INTEGER */
48 #include <X11/Intrinsic.h>
49 #include <X11/StringDefs.h>
51 /* We don't actually use any widget internals, but these are included
52 so that gdb will have debug info for the widgets... */
53 #include <X11/IntrinsicP.h>
54 #include <X11/ShellP.h>
58 # include <X11/Xmu/Error.h>
60 # include <Xmu/Error.h>
70 # include <capplet-widget.h>
77 #include "resources.h" /* for parse_time() */
78 #include "visual.h" /* for has_writable_cells() */
79 #include "remote.h" /* for xscreensaver_command() */
82 #include "logo-50.xpm"
83 #include "logo-180.xpm"
85 #include "demo-Gtk-widgets.h"
86 #include "demo-Gtk-support.h"
87 #include "demo-Gtk-conf.h"
94 #define countof(x) (sizeof((x))/sizeof((*x)))
98 char *progclass = "XScreenSaver";
101 /* The order of the items in the mode menu. */
102 static int mode_menu_order[] = {
103 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS };
108 char *short_version; /* version number of this xscreensaver build */
110 GtkWidget *toplevel_widget; /* the main window */
111 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
112 GtkWidget *popup_widget; /* the "Settings" dialog */
113 conf_data *cdata; /* private data for per-hack configuration */
115 Bool debug_p; /* whether to print diagnostics */
116 Bool initializing_p; /* flag for breaking recursion loops */
117 Bool saving_p; /* flag for breaking recursion loops */
119 char *desired_preview_cmd; /* subprocess we intend to run */
120 char *running_preview_cmd; /* subprocess we are currently running */
121 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
122 Bool running_preview_error_p; /* whether the pid died abnormally */
124 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
125 int subproc_timer_id; /* timer to delay subproc launch */
126 int subproc_check_timer_id; /* timer to check whether it started up */
127 int subproc_check_countdown; /* how many more checks left */
128 int preview_nice_level;
130 int *list_elt_to_hack_number; /* table for sorting the hack list */
131 int *hack_number_to_list_elt; /* the inverse table */
133 int _selected_list_element; /* don't use this: call
134 selected_list_element() instead */
136 saver_preferences prefs;
141 /* Total fucking evilness due to the fact that it's rocket science to get
142 a closure object of our own down into the various widget callbacks. */
143 static state *global_state_kludge;
146 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
147 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
148 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
151 static void populate_demo_window (state *, int list_elt);
152 static void populate_prefs_page (state *);
153 static void populate_popup_window (state *);
155 static Bool flush_dialog_changes_and_save (state *);
156 static Bool flush_popup_changes_and_save (state *);
158 static int maybe_reload_init_file (state *);
159 static void await_xscreensaver (state *);
161 static void schedule_preview (state *, const char *cmd);
162 static void kill_preview_subproc (state *);
163 static void schedule_preview_check (state *);
167 /* Some random utility functions
173 time_t now = time ((time_t *) 0);
174 char *ct = (char *) ctime (&now);
175 static char buf[255];
176 int n = strlen(progname);
178 strncpy(buf, progname, n);
181 strncpy(buf+n, ct+11, 8);
182 strcpy(buf+n+9, ": ");
188 name_to_widget (state *s, const char *name)
195 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
198 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
202 fprintf (stderr, "%s: no widget \"%s\"\n", blurb(), name);
207 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
208 Takes a scroller, viewport, or list as an argument.
211 ensure_selected_item_visible (GtkWidget *widget)
213 GtkScrolledWindow *scroller = 0;
215 GtkList *list_widget = 0;
219 GtkWidget *selected = 0;
222 gint parent_h, child_y, child_h, children_h, ignore;
223 double ratio_t, ratio_b;
225 if (GTK_IS_SCROLLED_WINDOW (widget))
227 scroller = GTK_SCROLLED_WINDOW (widget);
228 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
229 list_widget = GTK_LIST (GTK_BIN(vp)->child);
231 else if (GTK_IS_VIEWPORT (widget))
233 vp = GTK_VIEWPORT (widget);
234 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
235 list_widget = GTK_LIST (GTK_BIN(vp)->child);
237 else if (GTK_IS_LIST (widget))
239 list_widget = GTK_LIST (widget);
240 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
241 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
246 slist = list_widget->selection;
247 selected = (slist ? GTK_WIDGET (slist->data) : 0);
251 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
253 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
254 kids; kids = kids->next)
257 adj = gtk_scrolled_window_get_vadjustment (scroller);
259 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
260 &ignore, &ignore, &ignore, &parent_h, &ignore);
261 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
262 &ignore, &child_y, &ignore, &child_h, &ignore);
263 children_h = nkids * child_h;
265 ratio_t = ((double) child_y) / ((double) children_h);
266 ratio_b = ((double) child_y + child_h) / ((double) children_h);
268 if (adj->upper == 0.0) /* no items in list */
271 if (ratio_t < (adj->value / adj->upper) ||
272 ratio_b > ((adj->value + adj->page_size) / adj->upper))
275 int slop = parent_h * 0.75; /* how much to overshoot by */
277 if (ratio_t < (adj->value / adj->upper))
279 double ratio_w = ((double) parent_h) / ((double) children_h);
280 double ratio_l = (ratio_b - ratio_t);
281 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
284 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
286 target = ratio_t * adj->upper;
290 if (target > adj->upper - adj->page_size)
291 target = adj->upper - adj->page_size;
295 gtk_adjustment_set_value (adj, target);
300 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
302 GtkWidget *shell = GTK_WIDGET (user_data);
303 while (shell->parent)
304 shell = shell->parent;
305 gtk_widget_destroy (GTK_WIDGET (shell));
309 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
311 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
313 restart_menu_cb (widget, user_data);
314 warning_dialog_dismiss_cb (widget, user_data);
318 warning_dialog (GtkWidget *parent, const char *message,
319 Boolean restart_button_p, int center)
321 char *msg = strdup (message);
324 GtkWidget *dialog = gtk_dialog_new ();
325 GtkWidget *label = 0;
327 GtkWidget *cancel = 0;
330 while (parent && !parent->window)
331 parent = parent->parent;
333 if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
335 fprintf (stderr, "%s: too early for dialog?\n", progname);
343 char *s = strchr (head, '\n');
346 sprintf (name, "label%d", i++);
349 label = gtk_label_new (head);
353 GTK_WIDGET (label)->style =
354 gtk_style_copy (GTK_WIDGET (label)->style);
355 GTK_WIDGET (label)->style->font =
356 gdk_font_load (get_string_resource("warning_dialog.headingFont",
358 gtk_widget_set_style (GTK_WIDGET (label),
359 GTK_WIDGET (label)->style);
363 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
364 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
365 label, TRUE, TRUE, 0);
366 gtk_widget_show (label);
377 label = gtk_label_new ("");
378 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
379 label, TRUE, TRUE, 0);
380 gtk_widget_show (label);
382 label = gtk_hbutton_box_new ();
383 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
384 label, TRUE, TRUE, 0);
386 ok = gtk_button_new_with_label ("OK");
387 gtk_container_add (GTK_CONTAINER (label), ok);
389 if (restart_button_p)
391 cancel = gtk_button_new_with_label ("Cancel");
392 gtk_container_add (GTK_CONTAINER (label), cancel);
395 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
396 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
397 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
398 gtk_widget_show (ok);
400 gtk_widget_show (cancel);
401 gtk_widget_show (label);
402 gtk_widget_show (dialog);
403 /* gtk_window_set_default (GTK_WINDOW (dialog), ok);*/
405 if (restart_button_p)
407 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
408 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
410 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
411 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
416 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
417 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
421 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
422 GTK_WIDGET (parent)->window);
424 gdk_window_show (GTK_WIDGET (dialog)->window);
425 gdk_window_raise (GTK_WIDGET (dialog)->window);
432 run_cmd (state *s, Atom command, int arg)
437 flush_dialog_changes_and_save (s);
438 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
443 sprintf (buf, "Error:\n\n%s", err);
445 strcpy (buf, "Unknown error!");
446 warning_dialog (s->toplevel_widget, buf, False, 100);
453 run_hack (state *s, int list_elt, Bool report_errors_p)
456 if (list_elt < 0) return;
457 hack_number = s->list_elt_to_hack_number[list_elt];
459 flush_dialog_changes_and_save (s);
460 schedule_preview (s, 0);
462 run_cmd (s, XA_DEMO, hack_number + 1);
466 xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
478 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
480 state *s = global_state_kludge; /* I hate C so much... */
481 flush_dialog_changes_and_save (s);
482 kill_preview_subproc (s);
487 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
489 state *s = (state *) data;
490 flush_dialog_changes_and_save (s);
496 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
499 char *vers = strdup (screensaver_id + 4);
502 char *desc = "For updates, check http://www.jwz.org/xscreensaver/";
504 s = strchr (vers, ',');
508 sprintf(copy, "Copyright \251 1991-2002 %s", s);
510 sprintf (msg, "%s\n\n%s", copy, desc);
512 /* I can't make gnome_about_new() work here -- it starts dying in
513 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
514 then this might be the thing to do:
518 const gchar *auth[] = { 0 };
519 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
521 gtk_widget_show (about);
523 #else / * GTK but not GNOME * /
527 GdkColormap *colormap;
528 GdkPixmap *gdkpixmap;
531 GtkWidget *dialog = gtk_dialog_new ();
532 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
533 GtkWidget *parent = GTK_WIDGET (menuitem);
534 while (parent->parent)
535 parent = parent->parent;
537 hbox = gtk_hbox_new (FALSE, 20);
538 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
539 hbox, TRUE, TRUE, 0);
541 colormap = gtk_widget_get_colormap (parent);
543 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
544 (gchar **) logo_180_xpm);
545 icon = gtk_pixmap_new (gdkpixmap, mask);
546 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
548 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
550 vbox = gtk_vbox_new (FALSE, 0);
551 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
553 label1 = gtk_label_new (vers);
554 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
555 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
556 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
558 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
559 GTK_WIDGET (label1)->style->font =
560 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
561 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
563 label2 = gtk_label_new (msg);
564 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
565 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
566 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
568 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
569 GTK_WIDGET (label2)->style->font =
570 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
571 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
573 hb = gtk_hbutton_box_new ();
575 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
578 ok = gtk_button_new_with_label ("OK");
579 gtk_container_add (GTK_CONTAINER (hb), ok);
581 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
582 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
583 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
585 gtk_widget_show (hbox);
586 gtk_widget_show (icon);
587 gtk_widget_show (vbox);
588 gtk_widget_show (label1);
589 gtk_widget_show (label2);
590 gtk_widget_show (hb);
591 gtk_widget_show (ok);
592 gtk_widget_show (dialog);
594 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
595 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
597 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
598 GTK_WIDGET (parent)->window);
599 gdk_window_show (GTK_WIDGET (dialog)->window);
600 gdk_window_raise (GTK_WIDGET (dialog)->window);
606 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
608 state *s = global_state_kludge; /* I hate C so much... */
609 saver_preferences *p = &s->prefs;
612 if (!p->help_url || !*p->help_url)
614 warning_dialog (s->toplevel_widget,
616 "No Help URL has been specified.\n", False, 100);
620 help_command = (char *) malloc (strlen (p->load_url_command) +
621 (strlen (p->help_url) * 2) + 20);
622 strcpy (help_command, "( ");
623 sprintf (help_command + strlen(help_command),
624 p->load_url_command, p->help_url, p->help_url);
625 strcat (help_command, " ) &");
626 system (help_command);
632 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
634 state *s = global_state_kludge; /* I hate C so much... */
635 run_cmd (s, XA_ACTIVATE, 0);
640 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
642 state *s = global_state_kludge; /* I hate C so much... */
643 run_cmd (s, XA_LOCK, 0);
648 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
650 state *s = global_state_kludge; /* I hate C so much... */
651 run_cmd (s, XA_EXIT, 0);
656 restart_menu_cb (GtkWidget *widget, gpointer user_data)
658 state *s = global_state_kludge; /* I hate C so much... */
659 flush_dialog_changes_and_save (s);
660 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
662 system ("xscreensaver -nosplash &");
664 await_xscreensaver (s);
668 await_xscreensaver (state *s)
672 Display *dpy = GDK_DISPLAY();
673 /* GtkWidget *dialog = 0;*/
676 while (!rversion && (--countdown > 0))
678 /* Check for the version of the running xscreensaver... */
679 server_xscreensaver_version (dpy, &rversion, 0, 0);
681 /* If it's not there yet, wait a second... */
686 /* if (dialog) gtk_widget_destroy (dialog);*/
695 /* Timed out, no screensaver running. */
698 Bool root_p = (geteuid () == 0);
702 "The xscreensaver daemon did not start up properly.\n"
707 "You are running as root. This usually means that xscreensaver\n"
708 "was unable to contact your X server because access control is\n"
709 "turned on. Try running this command:\n"
711 " xhost +localhost\n"
713 "and then selecting `File / Restart Daemon'.\n"
715 "Note that turning off access control will allow anyone logged\n"
716 "on to this machine to access your screen, which might be\n"
717 "considered a security problem. Please read the xscreensaver\n"
718 "manual and FAQ for more information.\n"
720 "You shouldn't run X as root. Instead, you should log in as a\n"
721 "normal user, and `su' as necessary.");
723 strcat (buf, "Please check your $PATH and permissions.");
725 warning_dialog (s->toplevel_widget, buf, False, 1);
731 selected_list_element (state *s)
733 return s->_selected_list_element;
738 demo_write_init_file (state *s, saver_preferences *p)
742 /* #### try to figure out why shit keeps getting reordered... */
743 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
747 if (!write_init_file (p, s->short_version, False))
750 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
755 const char *f = init_file_name();
757 warning_dialog (s->toplevel_widget,
758 "Error:\n\nCouldn't determine init file name!\n",
762 char *b = (char *) malloc (strlen(f) + 1024);
763 sprintf (b, "Error:\n\nCouldn't write %s\n", f);
764 warning_dialog (s->toplevel_widget, b, False, 100);
773 run_this_cb (GtkButton *button, gpointer user_data)
775 state *s = global_state_kludge; /* I hate C so much... */
776 int list_elt = selected_list_element (s);
777 if (list_elt < 0) return;
778 if (!flush_dialog_changes_and_save (s))
779 run_hack (s, list_elt, True);
784 manual_cb (GtkButton *button, gpointer user_data)
786 state *s = global_state_kludge; /* I hate C so much... */
787 saver_preferences *p = &s->prefs;
788 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
789 int list_elt = selected_list_element (s);
791 char *name, *name2, *cmd, *str;
792 if (list_elt < 0) return;
793 hack_number = s->list_elt_to_hack_number[list_elt];
795 flush_dialog_changes_and_save (s);
796 ensure_selected_item_visible (GTK_WIDGET (list_widget));
798 name = strdup (p->screenhacks[hack_number]->command);
800 while (isspace (*name2)) name2++;
802 while (*str && !isspace (*str)) str++;
804 str = strrchr (name2, '/');
805 if (str) name = str+1;
807 cmd = get_string_resource ("manualCommand", "ManualCommand");
810 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
812 sprintf (cmd2 + strlen (cmd2),
814 name2, name2, name2, name2);
815 strcat (cmd2, " ) &");
821 warning_dialog (GTK_WIDGET (button),
822 "Error:\n\nno `manualCommand' resource set.",
831 force_list_select_item (state *s, GtkList *list, int list_elt, Bool scroll_p)
833 GtkWidget *parent = name_to_widget (s, "scroller");
834 Bool was = GTK_WIDGET_IS_SENSITIVE (parent);
836 if (!was) gtk_widget_set_sensitive (parent, True);
837 gtk_list_select_item (GTK_LIST (list), list_elt);
838 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
839 if (!was) gtk_widget_set_sensitive (parent, False);
844 run_next_cb (GtkButton *button, gpointer user_data)
846 state *s = global_state_kludge; /* I hate C so much... */
847 saver_preferences *p = &s->prefs;
848 Bool ops = s->preview_suppressed_p;
850 GtkList *list_widget =
851 GTK_LIST (name_to_widget (s, "list"));
852 int list_elt = selected_list_element (s);
859 if (list_elt >= p->screenhacks_count)
862 s->preview_suppressed_p = True;
864 flush_dialog_changes_and_save (s);
865 force_list_select_item (s, GTK_LIST (list_widget), list_elt, True);
866 populate_demo_window (s, list_elt);
867 run_hack (s, list_elt, False);
869 s->preview_suppressed_p = ops;
874 run_prev_cb (GtkButton *button, gpointer user_data)
876 state *s = global_state_kludge; /* I hate C so much... */
877 saver_preferences *p = &s->prefs;
878 Bool ops = s->preview_suppressed_p;
880 GtkList *list_widget =
881 GTK_LIST (name_to_widget (s, "list"));
882 int list_elt = selected_list_element (s);
885 list_elt = p->screenhacks_count - 1;
890 list_elt = p->screenhacks_count - 1;
892 s->preview_suppressed_p = True;
894 flush_dialog_changes_and_save (s);
895 force_list_select_item (s, GTK_LIST (list_widget), list_elt, True);
896 populate_demo_window (s, list_elt);
897 run_hack (s, list_elt, False);
899 s->preview_suppressed_p = ops;
903 /* Writes the given settings into prefs.
904 Returns true if there was a change, False otherwise.
905 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
908 flush_changes (state *s,
914 saver_preferences *p = &s->prefs;
915 Bool changed = False;
918 if (list_elt < 0 || list_elt >= p->screenhacks_count)
921 hack_number = s->list_elt_to_hack_number[list_elt];
922 hack = p->screenhacks[hack_number];
924 if (enabled_p != -1 &&
925 enabled_p != hack->enabled_p)
927 hack->enabled_p = enabled_p;
930 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
931 blurb(), hack->name, enabled_p);
936 if (!hack->command || !!strcmp (command, hack->command))
938 if (hack->command) free (hack->command);
939 hack->command = strdup (command);
942 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
943 blurb(), hack->name, command);
949 const char *ov = hack->visual;
950 if (!ov || !*ov) ov = "any";
951 if (!*visual) visual = "any";
952 if (!!strcasecmp (visual, ov))
954 if (hack->visual) free (hack->visual);
955 hack->visual = strdup (visual);
958 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
959 blurb(), hack->name, visual);
967 /* Helper for the text fields that contain time specifications:
968 this parses the text, and does error checking.
971 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
976 if (!sec_p || strchr (line, ':'))
977 value = parse_time ((char *) line, sec_p, True);
981 if (sscanf (line, "%u%c", &value, &c) != 1)
987 value *= 1000; /* Time measures in microseconds */
993 "Unparsable time format: \"%s\"\n",
995 warning_dialog (s->toplevel_widget, b, False, 100);
1004 directory_p (const char *path)
1007 if (!path || !*path)
1009 else if (stat (path, &st))
1011 else if (!S_ISDIR (st.st_mode))
1018 normalize_directory (const char *path)
1022 if (!path) return 0;
1024 p2 = (char *) malloc (L + 2);
1026 if (p2[L-1] == '/') /* remove trailing slash */
1029 for (s = p2; s && *s; s++)
1032 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1033 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1036 while (s0 > p2 && s0[-1] != '/')
1046 else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
1047 strcpy (s, s+2), s--;
1048 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1052 for (s = p2; s && *s; s++) /* normalize consecutive slashes */
1053 while (s[0] == '/' && s[1] == '/')
1056 /* and strip trailing whitespace for good measure. */
1058 while (isspace(p2[L-1]))
1065 /* Flush out any changes made in the main dialog window (where changes
1066 take place immediately: clicking on a checkbox causes the init file
1067 to be written right away.)
1070 flush_dialog_changes_and_save (state *s)
1072 saver_preferences *p = &s->prefs;
1073 saver_preferences P2, *p2 = &P2;
1074 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1075 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1076 Bool changed = False;
1080 if (s->saving_p) return False;
1085 /* Flush any checkbox changes in the list down into the prefs struct.
1087 for (i = 0; kids; kids = kids->next, i++)
1089 GtkWidget *line = GTK_WIDGET (kids->data);
1090 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1091 GtkWidget *line_check =
1092 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1094 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1096 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1101 /* Flush the non-hack-specific settings down into the prefs struct.
1104 # define SECONDS(FIELD,NAME) \
1105 w = name_to_widget (s, (NAME)); \
1106 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1108 # define MINUTES(FIELD,NAME) \
1109 w = name_to_widget (s, (NAME)); \
1110 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1112 # define CHECKBOX(FIELD,NAME) \
1113 w = name_to_widget (s, (NAME)); \
1114 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1116 # define PATHNAME(FIELD,NAME) \
1117 w = name_to_widget (s, (NAME)); \
1118 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1120 MINUTES (&p2->timeout, "timeout_spinbutton");
1121 MINUTES (&p2->cycle, "cycle_spinbutton");
1122 CHECKBOX (p2->lock_p, "lock_button");
1123 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1125 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1126 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1127 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1128 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1130 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1131 CHECKBOX (p2->grab_video_p, "grab_video_button");
1132 CHECKBOX (p2->random_image_p, "grab_image_button");
1133 PATHNAME (p2->image_directory, "image_text");
1135 CHECKBOX (p2->verbose_p, "verbose_button");
1136 CHECKBOX (p2->capture_stderr_p, "capture_button");
1137 CHECKBOX (p2->splash_p, "splash_button");
1139 CHECKBOX (p2->install_cmap_p, "install_button");
1140 CHECKBOX (p2->fade_p, "fade_button");
1141 CHECKBOX (p2->unfade_p, "unfade_button");
1142 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1149 /* Warn if the image directory doesn't exist.
1151 if (p2->image_directory &&
1152 *p2->image_directory &&
1153 !directory_p (p2->image_directory))
1156 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n",
1157 p2->image_directory);
1158 warning_dialog (s->toplevel_widget, b, False, 100);
1162 /* Map the mode menu to `saver_mode' enum values. */
1164 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1165 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1166 GtkWidget *selected = gtk_menu_get_active (menu);
1167 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1168 int menu_elt = g_list_index (kids, (gpointer) selected);
1169 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1170 p2->mode = mode_menu_order[menu_elt];
1173 if (p2->mode == ONE_HACK)
1175 int list_elt = selected_list_element (s);
1176 p2->selected_hack = (list_elt >= 0
1177 ? s->list_elt_to_hack_number[list_elt]
1181 # define COPY(field, name) \
1182 if (p->field != p2->field) { \
1185 fprintf (stderr, "%s: %s => %d\n", blurb(), name, p2->field); \
1187 p->field = p2->field
1190 COPY(selected_hack, "selected_hack");
1192 COPY(timeout, "timeout");
1193 COPY(cycle, "cycle");
1194 COPY(lock_p, "lock_p");
1195 COPY(lock_timeout, "lock_timeout");
1197 COPY(dpms_enabled_p, "dpms_enabled_p");
1198 COPY(dpms_standby, "dpms_standby");
1199 COPY(dpms_suspend, "dpms_suspend");
1200 COPY(dpms_off, "dpms_off");
1202 COPY(verbose_p, "verbose_p");
1203 COPY(capture_stderr_p, "capture_stderr_p");
1204 COPY(splash_p, "splash_p");
1206 COPY(install_cmap_p, "install_cmap_p");
1207 COPY(fade_p, "fade_p");
1208 COPY(unfade_p, "unfade_p");
1209 COPY(fade_seconds, "fade_seconds");
1211 COPY(grab_desktop_p, "grab_desktop_p");
1212 COPY(grab_video_p, "grab_video_p");
1213 COPY(random_image_p, "random_image_p");
1217 if (!p->image_directory ||
1218 !p2->image_directory ||
1219 strcmp(p->image_directory, p2->image_directory))
1223 fprintf (stderr, "%s: image_directory => \"%s\"\n",
1224 blurb(), p2->image_directory);
1226 if (p->image_directory && p->image_directory != p2->image_directory)
1227 free (p->image_directory);
1228 p->image_directory = p2->image_directory;
1229 p2->image_directory = 0;
1231 populate_prefs_page (s);
1235 Display *dpy = GDK_DISPLAY();
1236 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1237 sync_server_dpms_settings (dpy, enabled_p,
1238 p->dpms_standby / 1000,
1239 p->dpms_suspend / 1000,
1243 changed = demo_write_init_file (s, p);
1246 s->saving_p = False;
1251 /* Flush out any changes made in the popup dialog box (where changes
1252 take place only when the OK button is clicked.)
1255 flush_popup_changes_and_save (state *s)
1257 Bool changed = False;
1258 saver_preferences *p = &s->prefs;
1259 int list_elt = selected_list_element (s);
1261 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1262 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1264 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1265 const char *command = gtk_entry_get_text (cmd);
1270 if (s->saving_p) return False;
1276 if (maybe_reload_init_file (s) != 0)
1282 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1284 if (!strcasecmp (visual, "")) visual = "";
1285 else if (!strcasecmp (visual, "any")) visual = "";
1286 else if (!strcasecmp (visual, "default")) visual = "Default";
1287 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1288 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1289 else if (!strcasecmp (visual, "best")) visual = "Best";
1290 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1291 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1292 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1293 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1294 else if (!strcasecmp (visual, "color")) visual = "Color";
1295 else if (!strcasecmp (visual, "gl")) visual = "GL";
1296 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1297 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1298 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1299 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1300 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1301 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1302 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1303 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
1304 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1307 gdk_beep (); /* unparsable */
1309 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");
1312 changed = flush_changes (s, list_elt, -1, command, visual);
1315 changed = demo_write_init_file (s, p);
1317 /* Do this to re-launch the hack if (and only if) the command line
1319 populate_demo_window (s, selected_list_element (s));
1323 s->saving_p = False;
1331 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1333 state *s = global_state_kludge; /* I hate C so much... */
1334 if (! s->initializing_p)
1336 s->initializing_p = True;
1337 flush_dialog_changes_and_save (s);
1338 s->initializing_p = False;
1343 /* Callback on menu items in the "mode" options menu.
1346 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1348 state *s = (state *) user_data;
1349 saver_preferences *p = &s->prefs;
1350 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1353 GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
1355 saver_mode new_mode;
1356 int old_selected = p->selected_hack;
1360 if (menu_items->data == widget)
1363 menu_items = menu_items->next;
1365 if (!menu_items) abort();
1367 new_mode = mode_menu_order[menu_index];
1369 /* Keep the same list element displayed as before; except if we're
1370 switching *to* "one screensaver" mode from any other mode, scroll
1371 to and select "the one".
1374 if (new_mode == ONE_HACK)
1375 list_elt = (p->selected_hack >= 0
1376 ? s->hack_number_to_list_elt[p->selected_hack]
1380 list_elt = selected_list_element (s);
1383 saver_mode old_mode = p->mode;
1385 populate_demo_window (s, list_elt);
1386 force_list_select_item (s, list, list_elt, True);
1387 p->mode = old_mode; /* put it back, so the init file gets written */
1390 pref_changed_cb (widget, user_data);
1392 if (old_selected != p->selected_hack)
1393 abort(); /* dammit, not again... */
1398 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1399 gint page_num, gpointer user_data)
1401 state *s = global_state_kludge; /* I hate C so much... */
1402 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1404 /* If we're switching to page 0, schedule the current hack to be run.
1405 Otherwise, schedule it to stop. */
1407 populate_demo_window (s, selected_list_element (s));
1409 schedule_preview (s, 0);
1413 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1414 list_select_cb that comes in
1415 *after* we've double-clicked.
1419 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1422 state *s = (state *) data;
1423 if (event->type == GDK_2BUTTON_PRESS)
1425 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1426 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1428 last_doubleclick_time = time ((time_t *) 0);
1431 run_hack (s, list_elt, True);
1439 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1441 state *s = (state *) data;
1442 time_t now = time ((time_t *) 0);
1444 if (now >= last_doubleclick_time + 2)
1446 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1447 populate_demo_window (s, list_elt);
1448 flush_dialog_changes_and_save (s);
1453 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1455 state *s = (state *) data;
1456 populate_demo_window (s, -1);
1457 flush_dialog_changes_and_save (s);
1461 /* Called when the checkboxes that are in the left column of the
1462 scrolling list are clicked. This both populates the right pane
1463 (just as clicking on the label (really, listitem) does) and
1464 also syncs this checkbox with the right pane Enabled checkbox.
1467 list_checkbox_cb (GtkWidget *cb, gpointer data)
1469 state *s = (state *) data;
1471 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1472 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1474 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1475 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1476 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1480 int list_elt = gtk_list_child_position (list, line);
1482 /* remember previous scroll position of the top of the list */
1483 adj = gtk_scrolled_window_get_vadjustment (scroller);
1484 scroll_top = adj->value;
1486 flush_dialog_changes_and_save (s);
1487 force_list_select_item (s, list, list_elt, False);
1488 populate_demo_window (s, list_elt);
1490 /* restore the previous scroll position of the top of the list.
1491 this is weak, but I don't really know why it's moving... */
1492 gtk_adjustment_set_value (adj, scroll_top);
1498 GtkFileSelection *widget;
1499 } file_selection_data;
1504 store_image_directory (GtkWidget *button, gpointer user_data)
1506 file_selection_data *fsd = (file_selection_data *) user_data;
1507 state *s = fsd->state;
1508 GtkFileSelection *selector = fsd->widget;
1509 GtkWidget *top = s->toplevel_widget;
1510 saver_preferences *p = &s->prefs;
1511 char *path = gtk_file_selection_get_filename (selector);
1513 if (p->image_directory && !strcmp(p->image_directory, path))
1514 return; /* no change */
1516 if (!directory_p (path))
1519 sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n", path);
1520 warning_dialog (GTK_WIDGET (top), b, False, 100);
1524 if (p->image_directory) free (p->image_directory);
1525 p->image_directory = normalize_directory (path);
1527 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1528 (p->image_directory ? p->image_directory : ""));
1529 demo_write_init_file (s, p);
1534 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
1536 file_selection_data *fsd = (file_selection_data *) user_data;
1537 gtk_widget_hide (GTK_WIDGET (fsd->widget));
1541 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
1543 browse_image_dir_cancel (button, user_data);
1544 store_image_directory (button, user_data);
1548 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1550 browse_image_dir_cancel (widget, user_data);
1555 browse_image_dir_cb (GtkButton *button, gpointer user_data)
1557 state *s = global_state_kludge; /* I hate C so much... */
1558 saver_preferences *p = &s->prefs;
1559 static file_selection_data *fsd = 0;
1561 GtkFileSelection *selector = GTK_FILE_SELECTION(
1562 gtk_file_selection_new ("Please select the image directory."));
1565 fsd = (file_selection_data *) malloc (sizeof (*fsd));
1567 fsd->widget = selector;
1570 if (p->image_directory && *p->image_directory)
1571 gtk_file_selection_set_filename (selector, p->image_directory);
1573 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
1574 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
1576 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
1577 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
1579 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
1580 GTK_SIGNAL_FUNC (browse_image_dir_close),
1583 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
1585 gtk_window_set_modal (GTK_WINDOW (selector), True);
1586 gtk_widget_show (GTK_WIDGET (selector));
1591 settings_cb (GtkButton *button, gpointer user_data)
1593 state *s = global_state_kludge; /* I hate C so much... */
1594 int list_elt = selected_list_element (s);
1596 populate_demo_window (s, list_elt); /* reset the widget */
1597 populate_popup_window (s); /* create UI on popup window */
1598 gtk_widget_show (s->popup_widget);
1602 settings_sync_cmd_text (state *s)
1605 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
1606 char *cmd_line = get_configurator_command_line (s->cdata);
1607 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
1608 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
1610 # endif /* HAVE_XML */
1614 settings_adv_cb (GtkButton *button, gpointer user_data)
1616 state *s = global_state_kludge; /* I hate C so much... */
1617 GtkNotebook *notebook =
1618 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1620 settings_sync_cmd_text (s);
1621 gtk_notebook_set_page (notebook, 1);
1625 settings_std_cb (GtkButton *button, gpointer user_data)
1627 state *s = global_state_kludge; /* I hate C so much... */
1628 GtkNotebook *notebook =
1629 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1631 /* Re-create UI to reflect the in-progress command-line settings. */
1632 populate_popup_window (s);
1634 gtk_notebook_set_page (notebook, 0);
1638 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1639 gint page_num, gpointer user_data)
1641 state *s = global_state_kludge; /* I hate C so much... */
1642 GtkWidget *adv = name_to_widget (s, "adv_button");
1643 GtkWidget *std = name_to_widget (s, "std_button");
1647 gtk_widget_show (adv);
1648 gtk_widget_hide (std);
1650 else if (page_num == 1)
1652 gtk_widget_hide (adv);
1653 gtk_widget_show (std);
1662 settings_cancel_cb (GtkButton *button, gpointer user_data)
1664 state *s = global_state_kludge; /* I hate C so much... */
1665 gtk_widget_hide (s->popup_widget);
1669 settings_ok_cb (GtkButton *button, gpointer user_data)
1671 state *s = global_state_kludge; /* I hate C so much... */
1672 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
1673 int page = gtk_notebook_get_current_page (notebook);
1676 /* Regenerate the command-line from the widget contents before saving.
1677 But don't do this if we're looking at the command-line page already,
1678 or we will blow away what they typed... */
1679 settings_sync_cmd_text (s);
1681 flush_popup_changes_and_save (s);
1682 gtk_widget_hide (s->popup_widget);
1686 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
1688 state *s = (state *) data;
1689 settings_cancel_cb (0, (gpointer) s);
1694 /* Populating the various widgets
1698 /* Returns the number of the last hack run by the server.
1701 server_current_hack (void)
1705 unsigned long nitems, bytesafter;
1707 Display *dpy = GDK_DISPLAY();
1708 int hack_number = -1;
1710 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1711 XA_SCREENSAVER_STATUS,
1712 0, 3, False, XA_INTEGER,
1713 &type, &format, &nitems, &bytesafter,
1714 (unsigned char **) &data)
1716 && type == XA_INTEGER
1719 hack_number = (int) data[2] - 1;
1721 if (data) free (data);
1727 /* Finds the number of the last hack to run, and makes that item be
1728 selected by default.
1731 scroll_to_current_hack (state *s)
1733 saver_preferences *p = &s->prefs;
1736 if (p->mode == ONE_HACK)
1737 hack_number = p->selected_hack;
1739 hack_number = server_current_hack ();
1741 if (hack_number >= 0 && hack_number < p->screenhacks_count)
1743 int list_elt = s->hack_number_to_list_elt[hack_number];
1744 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1745 force_list_select_item (s, list, list_elt, True);
1746 populate_demo_window (s, list_elt);
1752 populate_hack_list (state *s)
1754 saver_preferences *p = &s->prefs;
1755 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1757 for (i = 0; i < p->screenhacks_count; i++)
1759 screenhack *hack = p->screenhacks[s->list_elt_to_hack_number[i]];
1761 /* A GtkList must contain only GtkListItems, but those can contain
1762 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
1763 and a Label. We handle single and double click events on the
1764 line itself, for clicking on the text, but the interior checkbox
1765 also handles its own events.
1768 GtkWidget *line_hbox;
1769 GtkWidget *line_check;
1770 GtkWidget *line_label;
1772 char *pretty_name = (hack->name
1773 ? strdup (hack->name)
1774 : make_hack_name (hack->command));
1776 line = gtk_list_item_new ();
1777 line_hbox = gtk_hbox_new (FALSE, 0);
1778 line_check = gtk_check_button_new ();
1779 line_label = gtk_label_new (pretty_name);
1781 gtk_container_add (GTK_CONTAINER (line), line_hbox);
1782 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
1783 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
1785 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1787 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
1789 gtk_widget_show (line_check);
1790 gtk_widget_show (line_label);
1791 gtk_widget_show (line_hbox);
1792 gtk_widget_show (line);
1796 gtk_container_add (GTK_CONTAINER (list), line);
1797 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
1798 GTK_SIGNAL_FUNC (list_doubleclick_cb),
1801 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
1802 GTK_SIGNAL_FUNC (list_checkbox_cb),
1806 GTK_WIDGET (GTK_BIN(line)->child)->style =
1807 gtk_style_copy (GTK_WIDGET (text_line)->style);
1809 gtk_widget_show (line);
1812 gtk_signal_connect (GTK_OBJECT (list), "select_child",
1813 GTK_SIGNAL_FUNC (list_select_cb),
1815 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
1816 GTK_SIGNAL_FUNC (list_unselect_cb),
1822 update_list_sensitivity (state *s)
1824 saver_preferences *p = &s->prefs;
1825 Bool sensitive = (p->mode == RANDOM_HACKS || p->mode == ONE_HACK);
1826 Bool checkable = (p->mode == RANDOM_HACKS);
1827 Bool blankable = (p->mode != DONT_BLANK);
1829 GtkWidget *head = name_to_widget (s, "col_head_hbox");
1830 GtkWidget *use = name_to_widget (s, "use_col_frame");
1831 GtkWidget *scroller = name_to_widget (s, "scroller");
1832 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
1833 GtkWidget *blanker = name_to_widget (s, "blanking_table");
1835 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1836 GList *kids = gtk_container_children (GTK_CONTAINER (list));
1838 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
1839 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
1840 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
1842 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
1845 gtk_widget_show (use); /* the "Use" column header */
1847 gtk_widget_hide (use);
1851 GtkBin *line = GTK_BIN (kids->data);
1852 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
1853 GtkWidget *line_check =
1854 GTK_WIDGET (gtk_container_children (line_hbox)->data);
1857 gtk_widget_show (line_check);
1859 gtk_widget_hide (line_check);
1867 populate_prefs_page (state *s)
1869 saver_preferences *p = &s->prefs;
1872 # define FMT_MINUTES(NAME,N) \
1873 sprintf (str, "%d", ((N) + 59) / (60 * 1000)); \
1874 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, (NAME))), str)
1876 # define FMT_SECONDS(NAME,N) \
1877 sprintf (str, "%d", ((N) / 1000)); \
1878 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, (NAME))), str)
1880 FMT_MINUTES ("timeout_spinbutton", p->timeout);
1881 FMT_MINUTES ("cycle_spinbutton", p->cycle);
1882 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
1883 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
1884 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
1885 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
1886 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
1891 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
1892 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
1895 TOGGLE_ACTIVE ("lock_button", p->lock_p);
1896 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
1897 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
1898 TOGGLE_ACTIVE ("splash_button", p->splash_p);
1899 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
1900 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
1901 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
1902 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
1903 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
1904 TOGGLE_ACTIVE ("fade_button", p->fade_p);
1905 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
1907 # undef TOGGLE_ACTIVE
1909 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
1910 (p->image_directory ? p->image_directory : ""));
1911 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
1913 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
1916 /* Map the `saver_mode' enum to mode menu to values. */
1918 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1921 for (i = 0; i < countof(mode_menu_order); i++)
1922 if (mode_menu_order[i] == p->mode)
1924 gtk_option_menu_set_history (opt, i);
1925 update_list_sensitivity (s);
1929 Bool found_any_writable_cells = False;
1930 Bool dpms_supported = False;
1932 Display *dpy = GDK_DISPLAY();
1933 int nscreens = ScreenCount(dpy);
1935 for (i = 0; i < nscreens; i++)
1937 Screen *s = ScreenOfDisplay (dpy, i);
1938 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1940 found_any_writable_cells = True;
1945 #ifdef HAVE_XF86VMODE_GAMMA
1946 found_any_writable_cells = True; /* if we can gamma fade, go for it */
1949 #ifdef HAVE_DPMS_EXTENSION
1951 int op = 0, event = 0, error = 0;
1952 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
1953 dpms_supported = True;
1955 #endif /* HAVE_DPMS_EXTENSION */
1958 # define SENSITIZE(NAME,SENSITIVEP) \
1959 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
1961 /* Blanking and Locking
1963 SENSITIZE ("lock_spinbutton", p->lock_p);
1964 SENSITIZE ("lock_mlabel", p->lock_p);
1968 SENSITIZE ("dpms_frame", dpms_supported);
1969 SENSITIZE ("dpms_button", dpms_supported);
1970 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
1971 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
1972 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
1973 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
1974 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
1975 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
1976 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
1977 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
1978 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
1982 SENSITIZE ("cmap_frame", found_any_writable_cells);
1983 SENSITIZE ("install_button", found_any_writable_cells);
1984 SENSITIZE ("fade_button", found_any_writable_cells);
1985 SENSITIZE ("unfade_button", found_any_writable_cells);
1987 SENSITIZE ("fade_label", (found_any_writable_cells &&
1988 (p->fade_p || p->unfade_p)));
1989 SENSITIZE ("fade_spinbutton", (found_any_writable_cells &&
1990 (p->fade_p || p->unfade_p)));
1998 populate_popup_window (state *s)
2000 saver_preferences *p = &s->prefs;
2001 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2002 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2003 int list_elt = selected_list_element (s);
2004 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2005 ? s->list_elt_to_hack_number[list_elt]
2007 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2008 char *doc_string = 0;
2013 free_conf_data (s->cdata);
2019 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2020 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2021 s->cdata = load_configurator (cmd_line, s->debug_p);
2022 if (s->cdata && s->cdata->widget)
2023 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget, TRUE, TRUE, 0);
2026 doc_string = (s->cdata
2027 ? s->cdata->description
2029 # else /* !HAVE_XML */
2030 doc_string = "Descriptions not available: no XML support compiled in.";
2031 # endif /* !HAVE_XML */
2033 gtk_label_set_text (doc, (doc_string
2035 : "No description available."));
2040 sensitize_demo_widgets (state *s, Bool sensitive_p)
2042 const char *names1[] = { "demo", "settings" };
2043 const char *names2[] = { "cmd_label", "cmd_text", "manual",
2044 "visual", "visual_combo" };
2046 for (i = 0; i < countof(names1); i++)
2048 GtkWidget *w = name_to_widget (s, names1[i]);
2049 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2051 for (i = 0; i < countof(names2); i++)
2053 GtkWidget *w = name_to_widget (s, names2[i]);
2054 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
2059 /* Even though we've given these text fields a maximum number of characters,
2060 their default size is still about 30 characters wide -- so measure out
2061 a string in their font, and resize them to just fit that.
2064 fix_text_entry_sizes (state *s)
2066 const char * const spinbuttons[] = {
2067 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
2068 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
2069 "dpms_off_spinbutton",
2070 "-fade_spinbutton" };
2075 for (i = 0; i < countof(spinbuttons); i++)
2077 const char *n = spinbuttons[i];
2079 while (*n == '-') n++, cols--;
2080 w = GTK_WIDGET (name_to_widget (s, n));
2081 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
2082 gtk_widget_set_usize (w, width, -2);
2085 /* Now fix the width of the combo box.
2087 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
2088 w = GTK_COMBO (w)->entry;
2089 width = gdk_string_width (w->style->font, "PseudoColor___");
2090 gtk_widget_set_usize (w, width, -2);
2092 /* Now fix the width of the file entry text.
2094 w = GTK_WIDGET (name_to_widget (s, "image_text"));
2095 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
2096 gtk_widget_set_usize (w, width, -2);
2098 /* Now fix the width of the command line text.
2100 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2101 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
2102 gtk_widget_set_usize (w, width, -2);
2104 /* Now fix the height of the list.
2109 int leading = 3; /* approximate is ok... */
2111 w = GTK_WIDGET (name_to_widget (s, "list"));
2112 height = w->style->font->ascent + w->style->font->descent;
2115 height += border * 2;
2116 w = GTK_WIDGET (name_to_widget (s, "scroller"));
2117 gtk_widget_set_usize (w, -2, height);
2124 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
2127 static char *up_arrow_xpm[] = {
2150 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2151 the end of the array (Gtk 1.2.5.) */
2152 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2153 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2156 static char *down_arrow_xpm[] = {
2179 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
2180 the end of the array (Gtk 1.2.5.) */
2181 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
2182 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
2186 pixmapify_button (state *s, int down_p)
2190 GtkWidget *pixmapwid;
2194 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
2195 style = gtk_widget_get_style (w);
2197 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
2198 &style->bg[GTK_STATE_NORMAL],
2200 ? (gchar **) down_arrow_xpm
2201 : (gchar **) up_arrow_xpm));
2202 pixmapwid = gtk_pixmap_new (pixmap, mask);
2203 gtk_widget_show (pixmapwid);
2204 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
2205 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
2209 map_next_button_cb (GtkWidget *w, gpointer user_data)
2211 state *s = (state *) user_data;
2212 pixmapify_button (s, 1);
2216 map_prev_button_cb (GtkWidget *w, gpointer user_data)
2218 state *s = (state *) user_data;
2219 pixmapify_button (s, 0);
2224 /* Work around a Gtk bug that causes label widgets to wrap text too early.
2228 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
2229 GtkAllocation *allocation,
2233 GtkWidgetAuxInfo *aux_info;
2235 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
2237 aux_info->width = allocation->width;
2238 aux_info->height = -2;
2242 gtk_widget_size_request (label, &req);
2246 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
2249 eschew_gtk_lossage (GtkLabel *label)
2251 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
2252 aux_info->width = GTK_WIDGET (label)->allocation.width;
2253 aux_info->height = -2;
2257 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
2259 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
2260 you_are_not_a_unique_or_beautiful_snowflake,
2263 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
2265 gtk_widget_queue_resize (GTK_WIDGET (label));
2270 populate_demo_window (state *s, int list_elt)
2272 saver_preferences *p = &s->prefs;
2275 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
2276 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "doc_frame"));
2277 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
2278 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
2279 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
2281 if (p->mode == BLANK_ONLY)
2284 pretty_name = strdup ("Blank Screen");
2285 schedule_preview (s, 0);
2287 else if (p->mode == DONT_BLANK)
2290 pretty_name = strdup ("Screen Saver Disabled");
2291 schedule_preview (s, 0);
2295 int hack_number = (list_elt >= 0 && list_elt < p->screenhacks_count
2296 ? s->list_elt_to_hack_number[list_elt]
2298 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2302 ? strdup (hack->name)
2303 : make_hack_name (hack->command))
2307 schedule_preview (s, hack->command);
2309 schedule_preview (s, 0);
2313 pretty_name = strdup ("Preview");
2315 gtk_frame_set_label (frame1, pretty_name);
2316 gtk_frame_set_label (frame2, pretty_name);
2318 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
2319 gtk_entry_set_position (cmd, 0);
2323 sprintf (title, "%s: %.100s Settings",
2324 progclass, (pretty_name ? pretty_name : "???"));
2325 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
2328 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
2330 ? (hack->visual && *hack->visual
2335 sensitize_demo_widgets (s, (hack ? True : False));
2337 if (pretty_name) free (pretty_name);
2339 ensure_selected_item_visible (list);
2341 s->_selected_list_element = list_elt;
2346 widget_deleter (GtkWidget *widget, gpointer data)
2348 /* #### Well, I want to destroy these widgets, but if I do that, they get
2349 referenced again, and eventually I get a SEGV. So instead of
2350 destroying them, I'll just hide them, and leak a bunch of memory
2351 every time the disk file changes. Go go go Gtk!
2353 #### Ok, that's a lie, I get a crash even if I just hide the widget
2354 and don't ever delete it. Fuck!
2357 gtk_widget_destroy (widget);
2359 gtk_widget_hide (widget);
2364 static char **sort_hack_cmp_names_kludge;
2366 sort_hack_cmp (const void *a, const void *b)
2371 return strcmp (sort_hack_cmp_names_kludge[*(int *) a],
2372 sort_hack_cmp_names_kludge[*(int *) b]);
2377 initialize_sort_map (state *s)
2379 saver_preferences *p = &s->prefs;
2382 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
2383 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
2385 s->list_elt_to_hack_number = (int *)
2386 calloc (sizeof(int), p->screenhacks_count + 1);
2387 s->hack_number_to_list_elt = (int *)
2388 calloc (sizeof(int), p->screenhacks_count + 1);
2390 /* Initialize table to 1:1 mapping */
2391 for (i = 0; i < p->screenhacks_count; i++)
2392 s->list_elt_to_hack_number[i] = i;
2394 /* Generate list of names (once)
2396 sort_hack_cmp_names_kludge = (char **)
2397 calloc (sizeof(char *), p->screenhacks_count);
2398 for (i = 0; i < p->screenhacks_count; i++)
2400 screenhack *hack = p->screenhacks[i];
2401 char *name = (hack->name && *hack->name
2402 ? strdup (hack->name)
2403 : make_hack_name (hack->command));
2405 for (str = name; *str; str++)
2406 *str = tolower(*str);
2407 sort_hack_cmp_names_kludge[i] = name;
2410 /* Sort alphabetically
2412 qsort (s->list_elt_to_hack_number,
2413 p->screenhacks_count,
2414 sizeof(*s->list_elt_to_hack_number),
2419 for (i = 0; i < p->screenhacks_count; i++)
2420 free (sort_hack_cmp_names_kludge[i]);
2421 free (sort_hack_cmp_names_kludge);
2422 sort_hack_cmp_names_kludge = 0;
2424 /* Build inverse table */
2425 for (i = 0; i < p->screenhacks_count; i++)
2426 s->hack_number_to_list_elt[s->list_elt_to_hack_number[i]] = i;
2431 maybe_reload_init_file (state *s)
2433 saver_preferences *p = &s->prefs;
2436 static Bool reentrant_lock = False;
2437 if (reentrant_lock) return 0;
2438 reentrant_lock = True;
2440 if (init_file_changed_p (p))
2442 const char *f = init_file_name();
2447 if (!f || !*f) return 0;
2448 b = (char *) malloc (strlen(f) + 1024);
2451 "file \"%s\" has changed, reloading.\n",
2453 warning_dialog (s->toplevel_widget, b, False, 100);
2457 initialize_sort_map (s);
2459 list_elt = selected_list_element (s);
2460 list = GTK_LIST (name_to_widget (s, "list"));
2461 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
2462 populate_hack_list (s);
2463 force_list_select_item (s, list, list_elt, True);
2464 populate_prefs_page (s);
2465 populate_demo_window (s, list_elt);
2466 ensure_selected_item_visible (GTK_WIDGET (list));
2471 reentrant_lock = False;
2477 /* Making the preview window have the right X visual (so that GL works.)
2480 static Visual *get_best_gl_visual (state *);
2483 x_visual_to_gdk_visual (Visual *xv)
2485 GList *gvs = gdk_list_visuals();
2486 if (!xv) return gdk_visual_get_system();
2487 for (; gvs; gvs = gvs->next)
2489 GdkVisual *gv = (GdkVisual *) gvs->data;
2490 if (xv == GDK_VISUAL_XVISUAL (gv))
2493 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
2494 blurb(), (unsigned long) xv->visualid);
2499 clear_preview_window (state *s)
2504 if (!s->toplevel_widget) return; /* very early */
2505 p = name_to_widget (s, "preview");
2508 if (!window) return;
2510 /* Flush the widget background down into the window, in case a subproc
2512 gdk_window_set_background (window, &p->style->bg[GTK_STATE_NORMAL]);
2513 gdk_window_clear (window);
2515 if (s->running_preview_error_p)
2517 const char * const lines[] = { "No Preview", "Available" };
2518 int lh = p->style->font->ascent + p->style->font->descent;
2521 gdk_window_get_size (window, &w, &h);
2522 y = (h - (lh * countof(lines))) / 2;
2523 y += p->style->font->ascent;
2524 for (i = 0; i < countof(lines); i++)
2526 int sw = gdk_string_width (p->style->font, lines[i]);
2527 int x = (w - sw) / 2;
2528 gdk_draw_string (window, p->style->font,
2529 p->style->fg_gc[GTK_STATE_NORMAL],
2537 /* Is there a GDK way of doing this? */
2538 XSync (GDK_DISPLAY(), False);
2543 fix_preview_visual (state *s)
2545 GtkWidget *widget = name_to_widget (s, "preview");
2546 Visual *xvisual = get_best_gl_visual (s);
2547 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
2548 GdkVisual *dvisual = gdk_visual_get_system();
2549 GdkColormap *cmap = (visual == dvisual
2550 ? gdk_colormap_get_system ()
2551 : gdk_colormap_new (visual, False));
2554 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
2555 (visual == dvisual ? "default" : "non-default"),
2556 (xvisual ? (unsigned long) xvisual->visualid : 0L));
2558 if (!GTK_WIDGET_REALIZED (widget) ||
2559 gtk_widget_get_visual (widget) != visual)
2561 gtk_widget_unrealize (widget);
2562 gtk_widget_set_visual (widget, visual);
2563 gtk_widget_set_colormap (widget, cmap);
2564 gtk_widget_realize (widget);
2567 /* Set the Widget colors to be white-on-black. */
2569 GdkWindow *window = widget->window;
2570 GtkStyle *style = gtk_style_copy (widget->style);
2571 GdkColormap *cmap = gtk_widget_get_colormap (widget);
2572 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
2573 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
2574 GdkGC *fgc = gdk_gc_new(window);
2575 GdkGC *bgc = gdk_gc_new(window);
2576 if (!gdk_color_white (cmap, fg)) abort();
2577 if (!gdk_color_black (cmap, bg)) abort();
2578 gdk_gc_set_foreground (fgc, fg);
2579 gdk_gc_set_background (fgc, bg);
2580 gdk_gc_set_foreground (bgc, bg);
2581 gdk_gc_set_background (bgc, fg);
2582 style->fg_gc[GTK_STATE_NORMAL] = fgc;
2583 style->bg_gc[GTK_STATE_NORMAL] = fgc;
2584 gtk_widget_set_style (widget, style);
2587 gtk_widget_show (widget);
2595 subproc_pretty_name (state *s)
2597 if (s->running_preview_cmd)
2599 char *ps = strdup (s->running_preview_cmd);
2600 char *ss = strchr (ps, ' ');
2602 ss = strrchr (ps, '/');
2608 return strdup ("???");
2613 reap_zombies (state *s)
2615 int wait_status = 0;
2617 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
2621 if (pid == s->running_preview_pid)
2623 char *ss = subproc_pretty_name (s);
2624 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(), pid, ss);
2628 fprintf (stderr, "%s: pid %lu died\n", blurb(), pid);
2634 /* Mostly lifted from driver/subprocs.c */
2636 get_best_gl_visual (state *s)
2638 Display *dpy = GDK_DISPLAY();
2647 av[ac++] = "xscreensaver-gl-helper";
2652 perror ("error creating pipe:");
2659 switch ((int) (forked = fork ()))
2663 sprintf (buf, "%s: couldn't fork", blurb());
2671 close (in); /* don't need this one */
2672 close (ConnectionNumber (dpy)); /* close display fd */
2674 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
2676 perror ("could not dup() a new stdout:");
2680 execvp (av[0], av); /* shouldn't return. */
2682 if (errno != ENOENT)
2684 /* Ignore "no such file or directory" errors, unless verbose.
2685 Issue all other exec errors, though. */
2686 sprintf (buf, "%s: running %s", blurb(), av[0]);
2689 exit (1); /* exits fork */
2695 int wait_status = 0;
2697 FILE *f = fdopen (in, "r");
2698 unsigned long v = 0;
2701 close (out); /* don't need this one */
2704 fgets (buf, sizeof(buf)-1, f);
2707 /* Wait for the child to die. */
2708 waitpid (-1, &wait_status, 0);
2710 if (1 == sscanf (buf, "0x%x %c", &v, &c))
2716 fprintf (stderr, "%s: %s did not report a GL visual!\n",
2722 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
2724 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
2725 blurb(), av[0], result);
2737 kill_preview_subproc (state *s)
2739 s->running_preview_error_p = False;
2742 clear_preview_window (s);
2744 if (s->subproc_check_timer_id)
2746 gtk_timeout_remove (s->subproc_check_timer_id);
2747 s->subproc_check_timer_id = 0;
2748 s->subproc_check_countdown = 0;
2751 if (s->running_preview_pid)
2753 int status = kill (s->running_preview_pid, SIGTERM);
2754 char *ss = subproc_pretty_name (s);
2761 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
2762 blurb(), s->running_preview_pid, ss);
2767 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
2768 blurb(), s->running_preview_pid, ss);
2772 else if (s->debug_p)
2773 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
2774 s->running_preview_pid, ss);
2777 s->running_preview_pid = 0;
2778 if (s->running_preview_cmd) free (s->running_preview_cmd);
2779 s->running_preview_cmd = 0;
2787 exec_program (const char *cmd, int nice_level)
2791 char *token = strtok (strdup(cmd), " \t");
2795 token = strtok(0, " \t");
2799 nice (nice_level - nice (0));
2801 usleep (250000); /* pause for 1/4th second before launching, to give the
2802 previous program time to die and flush its X buffer,
2803 so we don't get leftover turds on the window. */
2805 execvp (av[0], av); /* shouldn't return. */
2809 sprintf (buf, "%s: could not execute \"%s\"", blurb(), av[0]);
2814 exit (1); /* Note that this only exits a child fork. */
2818 /* Immediately and unconditionally launches the given process,
2819 after appending the -window-id option; sets running_preview_pid.
2822 launch_preview_subproc (state *s)
2824 saver_preferences *p = &s->prefs;
2829 int nice_level = nice (0) - p->nice_inferior;
2830 const char *cmd = s->desired_preview_cmd;
2832 GtkWidget *pr = name_to_widget (s, "preview");
2833 GdkWindow *window = pr->window;
2835 s->running_preview_error_p = False;
2837 if (s->preview_suppressed_p)
2839 kill_preview_subproc (s);
2843 new_cmd = malloc (strlen (cmd) + 40);
2845 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
2848 /* No window id? No command to run. */
2854 strcpy (new_cmd, cmd);
2855 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X", id);
2858 hairy_p = (new_cmd && !!strpbrk (new_cmd, "*?$&!<>[];`'\\\"="));
2861 /* Command requires a full shell? Forget it. */
2865 fprintf (stderr, "%s: command is hairy: not previewing\n", blurb());
2868 kill_preview_subproc (s);
2871 s->running_preview_error_p = True;
2872 clear_preview_window (s);
2876 switch ((int) (forked = fork ()))
2881 sprintf (buf, "%s: couldn't fork", blurb());
2883 s->running_preview_error_p = True;
2888 close (ConnectionNumber (GDK_DISPLAY()));
2889 exec_program (new_cmd, nice_level);
2895 if (s->running_preview_cmd) free (s->running_preview_cmd);
2896 s->running_preview_cmd = strdup (s->desired_preview_cmd);
2897 s->running_preview_pid = forked;
2901 char *ss = subproc_pretty_name (s);
2902 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(), forked, ss);
2909 schedule_preview_check (s);
2913 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
2916 hack_environment (state *s)
2918 static const char *def_path =
2919 # ifdef DEFAULT_PATH_PREFIX
2920 DEFAULT_PATH_PREFIX;
2925 Display *dpy = GDK_DISPLAY();
2926 const char *odpy = DisplayString (dpy);
2927 char *ndpy = (char *) malloc(strlen(odpy) + 20);
2928 strcpy (ndpy, "DISPLAY=");
2929 strcat (ndpy, odpy);
2934 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
2936 if (def_path && *def_path)
2938 const char *opath = getenv("PATH");
2939 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
2940 strcpy (npath, "PATH=");
2941 strcat (npath, def_path);
2942 strcat (npath, ":");
2943 strcat (npath, opath);
2949 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
2954 /* Called from a timer:
2955 Launches the currently-chosen subprocess, if it's not already running.
2956 If there's a different process running, kills it.
2959 update_subproc_timer (gpointer data)
2961 state *s = (state *) data;
2962 if (! s->desired_preview_cmd)
2963 kill_preview_subproc (s);
2964 else if (!s->running_preview_cmd ||
2965 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
2966 launch_preview_subproc (s);
2968 s->subproc_timer_id = 0;
2969 return FALSE; /* do not re-execute timer */
2973 /* Call this when you think you might want a preview process running.
2974 It will set a timer that will actually launch that program a second
2975 from now, if you haven't changed your mind (to avoid double-click
2976 spazzing, etc.) `cmd' may be null meaning "no process".
2979 schedule_preview (state *s, const char *cmd)
2981 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
2986 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
2988 fprintf (stderr, "%s: scheduling preview death\n", blurb());
2991 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
2992 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
2994 if (s->subproc_timer_id)
2995 gtk_timeout_remove (s->subproc_timer_id);
2996 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
3000 /* Called from a timer:
3001 Checks to see if the subproc that should be running, actually is.
3004 check_subproc_timer (gpointer data)
3006 state *s = (state *) data;
3007 Bool again_p = True;
3009 if (s->running_preview_error_p || /* already dead */
3010 s->running_preview_pid <= 0)
3018 status = kill (s->running_preview_pid, 0);
3019 if (status < 0 && errno == ESRCH)
3020 s->running_preview_error_p = True;
3024 char *ss = subproc_pretty_name (s);
3025 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
3026 s->running_preview_pid, ss,
3027 (s->running_preview_error_p ? "dead" : "alive"));
3031 if (s->running_preview_error_p)
3033 clear_preview_window (s);
3038 /* Otherwise, it's currently alive. We might be checking again, or we
3039 might be satisfied. */
3041 if (--s->subproc_check_countdown <= 0)
3045 return TRUE; /* re-execute timer */
3048 s->subproc_check_timer_id = 0;
3049 s->subproc_check_countdown = 0;
3050 return FALSE; /* do not re-execute timer */
3055 /* Call this just after launching a subprocess.
3056 This sets a timer that will, five times a second for two seconds,
3057 check whether the program is still running. The assumption here
3058 is that if the process didn't stay up for more than a couple of
3059 seconds, then either the program doesn't exist, or it doesn't
3060 take a -window-id argument.
3063 schedule_preview_check (state *s)
3069 fprintf (stderr, "%s: scheduling check\n", blurb());
3071 if (s->subproc_check_timer_id)
3072 gtk_timeout_remove (s->subproc_check_timer_id);
3073 s->subproc_check_timer_id =
3074 gtk_timeout_add (1000 / ticks,
3075 check_subproc_timer, (gpointer) s);
3076 s->subproc_check_countdown = ticks * seconds;
3081 screen_blanked_p (void)
3085 unsigned long nitems, bytesafter;
3087 Display *dpy = GDK_DISPLAY();
3088 Bool blanked_p = False;
3090 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
3091 XA_SCREENSAVER_STATUS,
3092 0, 3, False, XA_INTEGER,
3093 &type, &format, &nitems, &bytesafter,
3094 (unsigned char **) &data)
3096 && type == XA_INTEGER
3099 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
3101 if (data) free (data);
3106 /* Wake up every now and then and see if the screen is blanked.
3107 If it is, kill off the small-window demo -- no point in wasting
3108 cycles by running two screensavers at once...
3111 check_blanked_timer (gpointer data)
3113 state *s = (state *) data;
3114 Bool blanked_p = screen_blanked_p ();
3115 if (blanked_p && s->running_preview_pid)
3118 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
3119 kill_preview_subproc (s);
3122 return True; /* re-execute timer */
3126 /* Setting window manager icon
3130 init_icon (GdkWindow *window)
3132 GdkBitmap *mask = 0;
3135 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
3136 (gchar **) logo_50_xpm);
3138 gdk_window_set_icon (window, 0, pixmap, mask);
3142 /* The main demo-mode command loop.
3147 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
3148 XrmRepresentation *type, XrmValue *value, XPointer closure)
3151 for (i = 0; quarks[i]; i++)
3153 if (bindings[i] == XrmBindTightly)
3154 fprintf (stderr, (i == 0 ? "" : "."));
3155 else if (bindings[i] == XrmBindLoosely)
3156 fprintf (stderr, "*");
3158 fprintf (stderr, " ??? ");
3159 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
3162 fprintf (stderr, ": %s\n", (char *) value->addr);
3170 the_network_is_not_the_computer (state *s)
3172 Display *dpy = GDK_DISPLAY();
3173 char *rversion, *ruser, *rhost;
3174 char *luser, *lhost;
3176 struct passwd *p = getpwuid (getuid ());
3177 const char *d = DisplayString (dpy);
3179 # if defined(HAVE_UNAME)
3181 if (uname (&uts) < 0)
3182 lhost = "<UNKNOWN>";
3184 lhost = uts.nodename;
3186 strcpy (lhost, getenv("SYS$NODE"));
3187 # else /* !HAVE_UNAME && !VMS */
3188 strcat (lhost, "<UNKNOWN>");
3189 # endif /* !HAVE_UNAME && !VMS */
3191 if (p && p->pw_name)
3196 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
3198 /* Make a buffer that's big enough for a number of copies of all the
3199 strings, plus some. */
3200 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
3201 (ruser ? strlen(ruser) : 0) +
3202 (rhost ? strlen(rhost) : 0) +
3209 if (!rversion || !*rversion)
3213 "The XScreenSaver daemon doesn't seem to be running\n"
3214 "on display \"%s\". Launch it now?",
3217 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
3219 /* Warn that the two processes are running as different users.
3223 "%s is running as user \"%s\" on host \"%s\".\n"
3224 "But the xscreensaver managing display \"%s\"\n"
3225 "is running as user \"%s\" on host \"%s\".\n"
3227 "Since they are different users, they won't be reading/writing\n"
3228 "the same ~/.xscreensaver file, so %s isn't\n"
3229 "going to work right.\n"
3231 "You should either re-run %s as \"%s\", or re-run\n"
3232 "xscreensaver as \"%s\".\n"
3234 "Restart the xscreensaver daemon now?\n",
3235 blurb(), luser, lhost,
3237 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3239 blurb(), (ruser ? ruser : "???"),
3242 else if (rhost && *rhost && !!strcmp (rhost, lhost))
3244 /* Warn that the two processes are running on different hosts.
3248 "%s is running as user \"%s\" on host \"%s\".\n"
3249 "But the xscreensaver managing display \"%s\"\n"
3250 "is running as user \"%s\" on host \"%s\".\n"
3252 "If those two machines don't share a file system (that is,\n"
3253 "if they don't see the same ~%s/.xscreensaver file) then\n"
3254 "%s won't work right.\n"
3256 "Restart the daemon on \"%s\" as \"%s\" now?\n",
3257 blurb(), luser, lhost,
3259 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
3264 else if (!!strcmp (rversion, s->short_version))
3266 /* Warn that the version numbers don't match.
3270 "This is %s version %s.\n"
3271 "But the xscreensaver managing display \"%s\"\n"
3272 "is version %s. This could cause problems.\n"
3274 "Restart the xscreensaver daemon now?\n",
3275 blurb(), s->short_version,
3282 warning_dialog (s->toplevel_widget, msg, True, 1);
3288 /* We use this error handler so that X errors are preceeded by the name
3289 of the program that generated them.
3292 demo_ehandler (Display *dpy, XErrorEvent *error)
3294 state *s = global_state_kludge; /* I hate C so much... */
3295 fprintf (stderr, "\nX error in %s:\n", blurb());
3296 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
3298 kill_preview_subproc (s);
3302 fprintf (stderr, " (nonfatal.)\n");
3307 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
3308 of the program that generated them; and also that we can ignore one
3309 particular bogus error message that Gdk madly spews.
3312 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
3313 const gchar *message, gpointer user_data)
3315 /* Ignore the message "Got event for unknown window: 0x...".
3316 Apparently some events are coming in for the xscreensaver window
3317 (presumably reply events related to the ClientMessage) and Gdk
3318 feels the need to complain about them. So, just suppress any
3319 messages that look like that one.
3321 if (strstr (message, "unknown window"))
3324 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
3325 (log_domain ? log_domain : progclass),
3326 (log_level == G_LOG_LEVEL_ERROR ? "error" :
3327 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
3328 log_level == G_LOG_LEVEL_WARNING ? "warning" :
3329 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
3330 log_level == G_LOG_LEVEL_INFO ? "info" :
3331 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
3333 ((!*message || message[strlen(message)-1] != '\n')
3338 static char *defaults[] = {
3339 #include "XScreenSaver_ad.h"
3344 #ifdef HAVE_CRAPPLET
3345 static struct poptOption crapplet_options[] = {
3346 {NULL, '\0', 0, NULL, 0}
3348 #endif /* HAVE_CRAPPLET */
3351 const char *usage = "[--display dpy] [--prefs]"
3352 # ifdef HAVE_CRAPPLET
3359 map_popup_window_cb (GtkWidget *w, gpointer user_data)
3361 state *s = (state *) user_data;
3362 Boolean oi = s->initializing_p;
3363 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
3364 s->initializing_p = True;
3365 eschew_gtk_lossage (label);
3366 s->initializing_p = oi;
3372 print_widget_tree (GtkWidget *w, int depth)
3375 for (i = 0; i < depth; i++)
3376 fprintf (stderr, " ");
3377 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
3379 if (GTK_IS_LIST (w))
3381 for (i = 0; i < depth+1; i++)
3382 fprintf (stderr, " ");
3383 fprintf (stderr, "...list kids...\n");
3385 else if (GTK_IS_CONTAINER (w))
3387 GList *kids = gtk_container_children (GTK_CONTAINER (w));
3390 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
3398 delayed_scroll_kludge (gpointer data)
3400 state *s = (state *) data;
3401 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
3402 ensure_selected_item_visible (w);
3404 /* Oh, this is just fucking lovely, too. */
3405 w = GTK_WIDGET (name_to_widget (s, "preview"));
3406 gtk_widget_hide (w);
3407 gtk_widget_show (w);
3409 return FALSE; /* do not re-execute timer */
3414 main (int argc, char **argv)
3418 saver_preferences *p;
3422 Widget toplevel_shell;
3423 char *real_progname = argv[0];
3424 char window_title[255];
3425 Bool crapplet_p = False;
3428 str = strrchr (real_progname, '/');
3429 if (str) real_progname = str+1;
3432 memset (s, 0, sizeof(*s));
3433 s->initializing_p = True;
3436 global_state_kludge = s; /* I hate C so much... */
3438 progname = real_progname;
3440 s->short_version = (char *) malloc (5);
3441 memcpy (s->short_version, screensaver_id + 17, 4);
3442 s->short_version [4] = 0;
3445 /* Register our error message logger for every ``log domain'' known.
3446 There's no way to do this globally, so I grepped the Gtk/Gdk sources
3447 for all of the domains that seem to be in use.
3450 const char * const domains[] = { 0,
3451 "Gtk", "Gdk", "GLib", "GModule",
3452 "GThread", "Gnome", "GnomeUI" };
3453 for (i = 0; i < countof(domains); i++)
3454 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
3457 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
3458 add_pixmap_directory (DEFAULT_ICONDIR);
3461 /* This is gross, but Gtk understands --display and not -display...
3463 for (i = 1; i < argc; i++)
3464 if (argv[i][0] && argv[i][1] &&
3465 !strncmp(argv[i], "-display", strlen(argv[i])))
3466 argv[i] = "--display";
3469 /* We need to parse this arg really early... Sigh. */
3470 for (i = 1; i < argc; i++)
3472 (!strcmp(argv[i], "--crapplet") ||
3473 !strcmp(argv[i], "--capplet")))
3475 # ifdef HAVE_CRAPPLET
3478 for (j = i; j < argc; j++) /* remove it from the list */
3479 argv[j] = argv[j+1];
3482 # else /* !HAVE_CRAPPLET */
3483 fprintf (stderr, "%s: not compiled with --crapplet support\n",
3485 fprintf (stderr, "%s: %s\n", real_progname, usage);
3487 # endif /* !HAVE_CRAPPLET */
3490 (!strcmp(argv[i], "--debug") ||
3491 !strcmp(argv[i], "-debug") ||
3492 !strcmp(argv[i], "-d")))
3496 for (j = i; j < argc; j++) /* remove it from the list */
3497 argv[j] = argv[j+1];
3501 /* Let Gtk open the X connection, then initialize Xt to use that
3502 same connection. Doctor Frankenstein would be proud.
3504 # ifdef HAVE_CRAPPLET
3507 GnomeClient *client;
3508 GnomeClientFlags flags = 0;
3510 int init_results = gnome_capplet_init ("screensaver-properties",
3512 argc, argv, NULL, 0, NULL);
3514 0 upon successful initialization;
3515 1 if --init-session-settings was passed on the cmdline;
3516 2 if --ignore was passed on the cmdline;
3519 So the 1 signifies just to init the settings, and quit, basically.
3520 (Meaning launch the xscreensaver daemon.)
3523 if (init_results < 0)
3526 g_error ("An initialization error occurred while "
3527 "starting xscreensaver-capplet.\n");
3529 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
3530 real_progname, init_results);
3535 client = gnome_master_client ();
3538 flags = gnome_client_get_flags (client);
3540 if (flags & GNOME_CLIENT_IS_CONNECTED)
3543 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
3544 gnome_client_get_id (client));
3547 char *session_args[20];
3549 session_args[i++] = real_progname;
3550 session_args[i++] = "--capplet";
3551 session_args[i++] = "--init-session-settings";
3552 session_args[i] = 0;
3553 gnome_client_set_priority (client, 20);
3554 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
3555 gnome_client_set_restart_command (client, i, session_args);
3559 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
3562 gnome_client_flush (client);
3565 if (init_results == 1)
3567 system ("xscreensaver -nosplash &");
3573 # endif /* HAVE_CRAPPLET */
3575 gtk_init (&argc, &argv);
3579 /* We must read exactly the same resources as xscreensaver.
3580 That means we must have both the same progclass *and* progname,
3581 at least as far as the resource database is concerned. So,
3582 put "xscreensaver" in argv[0] while initializing Xt.
3584 argv[0] = "xscreensaver";
3588 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
3590 XtToolkitInitialize ();
3591 app = XtCreateApplicationContext ();
3592 dpy = GDK_DISPLAY();
3593 XtAppSetFallbackResources (app, defaults);
3594 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
3595 toplevel_shell = XtAppCreateShell (progname, progclass,
3596 applicationShellWidgetClass,
3599 dpy = XtDisplay (toplevel_shell);
3600 db = XtDatabase (dpy);
3601 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
3602 XSetErrorHandler (demo_ehandler);
3604 /* Let's just ignore these. They seem to confuse Irix Gtk... */
3605 signal (SIGPIPE, SIG_IGN);
3607 /* After doing Xt-style command-line processing, complain about any
3608 unrecognized command-line arguments.
3610 for (i = 1; i < argc; i++)
3612 char *str = argv[i];
3613 if (str[0] == '-' && str[1] == '-')
3615 if (!strcmp (str, "-prefs"))
3617 else if (crapplet_p)
3618 /* There are lots of random args that we don't care about when we're
3619 started as a crapplet, so just ignore unknown args in that case. */
3623 fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]);
3624 fprintf (stderr, "%s: %s\n", real_progname, usage);
3629 /* Load the init file, which may end up consulting the X resource database
3630 and the site-wide app-defaults file. Note that at this point, it's
3631 important that `progname' be "xscreensaver", rather than whatever
3636 initialize_sort_map (s);
3638 /* Now that Xt has been initialized, and the resources have been read,
3639 we can set our `progname' variable to something more in line with
3642 progname = real_progname;
3646 /* Print out all the resources we read. */
3648 XrmName name = { 0 };
3649 XrmClass class = { 0 };
3651 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
3657 /* Intern the atoms that xscreensaver_command() needs.
3659 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
3660 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
3661 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
3662 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
3663 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
3664 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
3665 XA_SELECT = XInternAtom (dpy, "SELECT", False);
3666 XA_DEMO = XInternAtom (dpy, "DEMO", False);
3667 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
3668 XA_BLANK = XInternAtom (dpy, "BLANK", False);
3669 XA_LOCK = XInternAtom (dpy, "LOCK", False);
3670 XA_EXIT = XInternAtom (dpy, "EXIT", False);
3671 XA_RESTART = XInternAtom (dpy, "RESTART", False);
3674 /* Create the window and all its widgets.
3676 s->base_widget = create_xscreensaver_demo ();
3677 s->popup_widget = create_xscreensaver_settings_dialog ();
3678 s->toplevel_widget = s->base_widget;
3681 /* Set the main window's title. */
3683 char *v = (char *) strdup(strchr(screensaver_id, ' '));
3684 char *s1, *s2, *s3, *s4;
3685 s1 = (char *) strchr(v, ' '); s1++;
3686 s2 = (char *) strchr(s1, ' ');
3687 s3 = (char *) strchr(v, '('); s3++;
3688 s4 = (char *) strchr(s3, ')');
3691 sprintf (window_title, "%.50s %.50s, %.50s", progclass, s1, s3);
3692 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
3693 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
3697 /* Adjust the (invisible) notebooks on the popup dialog... */
3699 GtkNotebook *notebook =
3700 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
3701 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
3705 gtk_widget_hide (std);
3706 # else /* !HAVE_XML */
3707 /* Make the advanced page be the only one available. */
3708 gtk_widget_set_sensitive (std, False);
3709 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
3710 gtk_widget_hide (std);
3712 # endif /* !HAVE_XML */
3714 gtk_notebook_set_page (notebook, page);
3715 gtk_notebook_set_show_tabs (notebook, False);
3718 /* Various other widget initializations...
3720 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
3721 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
3723 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
3724 GTK_SIGNAL_FUNC (wm_popup_close_cb),
3727 populate_hack_list (s);
3728 populate_prefs_page (s);
3729 sensitize_demo_widgets (s, False);
3730 fix_text_entry_sizes (s);
3731 scroll_to_current_hack (s);
3733 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
3734 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
3737 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
3738 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
3740 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
3741 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
3745 /* Hook up callbacks to the items on the mode menu. */
3747 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
3748 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
3749 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
3750 for (; kids; kids = kids->next)
3751 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
3752 GTK_SIGNAL_FUNC (mode_menu_item_cb),
3757 /* Handle the -prefs command-line argument. */
3760 GtkNotebook *notebook =
3761 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
3762 gtk_notebook_set_page (notebook, 1);
3765 # ifdef HAVE_CRAPPLET
3769 GtkWidget *outer_vbox;
3771 gtk_widget_hide (s->toplevel_widget);
3773 capplet = capplet_widget_new ();
3775 /* Make there be a "Close" button instead of "OK" and "Cancel" */
3776 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
3779 /* In crapplet-mode, take off the menubar. */
3780 gtk_widget_hide (name_to_widget (s, "menubar"));
3783 /* Reparent our top-level container to be a child of the capplet
3786 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
3787 gtk_widget_ref (outer_vbox);
3788 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
3790 GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
3791 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
3793 /* Find the window above us, and set the title and close handler. */
3795 GtkWidget *window = capplet;
3796 while (window && !GTK_IS_WINDOW (window))
3797 window = window->parent;
3800 gtk_window_set_title (GTK_WINDOW (window), window_title);
3801 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
3802 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
3807 s->toplevel_widget = capplet;
3809 # endif /* HAVE_CRAPPLET */
3812 gtk_widget_show (s->toplevel_widget);
3813 init_icon (GTK_WIDGET (s->toplevel_widget)->window); /* after `show' */
3814 hack_environment (s);
3815 fix_preview_visual (s);
3817 /* Realize page zero, so that we can diddle the scrollbar when the
3818 user tabs back to it -- otherwise, the current hack isn't scrolled
3819 to the first time they tab back there, when started with "-prefs".
3820 (Though it is if they then tab away, and back again.)
3822 #### Bah! This doesn't work. Gtk eats my ass! Someone who
3823 #### understands this crap, explain to me how to make this work.
3825 gtk_widget_realize (name_to_widget (s, "demos_table"));
3828 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
3831 /* Issue any warnings about the running xscreensaver daemon. */
3832 the_network_is_not_the_computer (s);
3835 /* Run the Gtk event loop, and not the Xt event loop. This means that
3836 if there were Xt timers or fds registered, they would never get serviced,
3837 and if there were any Xt widgets, they would never have events delivered.
3838 Fortunately, we're using Gtk for all of the UI, and only initialized
3839 Xt so that we could process the command line and use the X resource
3842 s->initializing_p = False;
3844 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
3845 after we start up. Otherwise, it always appears scrolled to the top
3846 when in crapplet-mode. */
3847 gtk_timeout_add (500, delayed_scroll_kludge, s);
3851 /* Load every configurator in turn, to scan them for errors all at once. */
3854 for (i = 0; i < p->screenhacks_count; i++)
3856 screenhack *hack = p->screenhacks[s->hack_number_to_list_elt[i]];
3857 conf_data *d = load_configurator (hack->command, False);
3858 if (d) free_conf_data (d);
3864 # ifdef HAVE_CRAPPLET
3866 capplet_gtk_main ();
3868 # endif /* HAVE_CRAPPLET */
3871 kill_preview_subproc (s);
3875 #endif /* HAVE_GTK -- whole file */